fix(fig): preserve independent bound padding

- Treat leading-edge variable bindings as independent padding values

- Serialize trailing padding when raw layout metadata would collapse it
This commit is contained in:
Danila Poyarkov 2026-07-21 10:49:54 +03:00
parent b61957dff9
commit 072c159fdb
4 changed files with 74 additions and 8 deletions

View file

@ -423,14 +423,25 @@ function convertTextProps(nc: NodeChange, blobs: Uint8Array[]): TextProps {
}
}
function consumesVariableField(nc: NodeChange, field: string): boolean {
return nc.variableConsumptionMap?.entries?.some((entry) => entry.variableField === field) ?? false
}
function convertLayoutPadding(
nc: NodeChange
): Pick<SceneNode, 'paddingTop' | 'paddingBottom' | 'paddingLeft' | 'paddingRight'> {
const basePadding = nc.stackPadding ?? 0
const verticalPadding = nc.stackVerticalPadding ?? basePadding
const horizontalPadding = nc.stackHorizontalPadding ?? basePadding
return {
paddingTop: nc.stackVerticalPadding ?? nc.stackPadding ?? 0,
paddingBottom: nc.stackPaddingBottom ?? nc.stackVerticalPadding ?? nc.stackPadding ?? 0,
paddingLeft: nc.stackHorizontalPadding ?? nc.stackPadding ?? 0,
paddingRight: nc.stackPaddingRight ?? nc.stackHorizontalPadding ?? nc.stackPadding ?? 0
paddingTop: verticalPadding,
paddingBottom:
nc.stackPaddingBottom ??
(consumesVariableField(nc, 'STACK_PADDING_TOP') ? basePadding : verticalPadding),
paddingLeft: horizontalPadding,
paddingRight:
nc.stackPaddingRight ??
(consumesVariableField(nc, 'STACK_PADDING_LEFT') ? basePadding : horizontalPadding)
}
}

View file

@ -305,6 +305,17 @@ function normalizeStackCounterAlign(value: string | undefined): string | undefin
return value === 'SPACE_EVENLY' ? 'SPACE_BETWEEN' : value
}
function preserveTrailingPadding(
explicitValue: number | undefined,
leadingValue: number | undefined,
baseValue: number | undefined,
normalizedValue: number
): number | undefined {
if (explicitValue !== undefined) return explicitValue
const inheritedValue = leadingValue ?? baseValue ?? normalizedValue
return normalizedValue !== inheritedValue ? normalizedValue : undefined
}
function serializeLayoutProps(node: SceneNode, nc: KiwiNodeChange): void {
if (!node.source.id) upsertPluginData(node, LAYOUT_DIRECTION_PLUGIN_KEY, node.layoutDirection)
const figLayout = node.source.fig.layout
@ -312,8 +323,18 @@ function serializeLayoutProps(node: SceneNode, nc: KiwiNodeChange): void {
nc.stackMode = normalizeStackMode(figLayout.stackMode)
nc.stackSpacing = figLayout.stackSpacing
nc.stackPadding = figLayout.stackPadding
nc.stackPaddingRight = figLayout.stackPaddingRight
nc.stackPaddingBottom = figLayout.stackPaddingBottom
nc.stackPaddingRight = preserveTrailingPadding(
figLayout.stackPaddingRight,
figLayout.stackHorizontalPadding,
figLayout.stackPadding,
node.paddingRight
)
nc.stackPaddingBottom = preserveTrailingPadding(
figLayout.stackPaddingBottom,
figLayout.stackVerticalPadding,
figLayout.stackPadding,
node.paddingBottom
)
nc.stackCounterAlign = normalizeStackCounterAlign(figLayout.stackCounterAlign)
nc.stackJustify = normalizeStackJustify(figLayout.stackJustify)
nc.stackCounterAlignItems = normalizeStackCounterAlign(figLayout.stackCounterAlignItems)

View file

@ -1,10 +1,44 @@
import { describe, expect, test } from 'bun:test'
import { importNodeChanges } from '@open-pencil/core'
import { nodeChangeToProps } from '@open-pencil/fig/node-change'
import type { NodeChange } from '@open-pencil/kiwi/fig/codec'
import { canvas, doc, node } from '../helpers'
describe('fig-import: auto-layout alignment', () => {
test('keeps variable-bound leading padding independent', () => {
const props = nodeChangeToProps(
{
type: 'FRAME',
stackMode: 'VERTICAL',
stackVerticalPadding: 8,
stackHorizontalPadding: 6,
variableConsumptionMap: {
entries: [
{
variableField: 'STACK_PADDING_TOP',
variableData: {
value: { alias: { guid: { sessionID: 2, localID: 1 } } }
}
},
{
variableField: 'STACK_PADDING_LEFT',
variableData: {
value: { alias: { guid: { sessionID: 2, localID: 2 } } }
}
}
]
}
} as NodeChange,
[]
)
expect(props.paddingTop).toBe(8)
expect(props.paddingBottom).toBe(0)
expect(props.paddingLeft).toBe(6)
expect(props.paddingRight).toBe(0)
})
test('maps SPACE_EVENLY kiwi primary alignment to Figma space-between', () => {
const graph = importNodeChanges([
doc(),

View file

@ -64,8 +64,8 @@ const SPECS: FixtureSpec[] = [
thumbnailHeight: 239,
imageCount: 3,
figKiwiVersion: 101,
g1ExportSize: 596988,
g2ExportSize: 596988
g1ExportSize: 596992,
g2ExportSize: 596992
}
]