diff --git a/packages/fig/src/node-change/convert.ts b/packages/fig/src/node-change/convert.ts index 6c1323e1e..3c53878bb 100644 --- a/packages/fig/src/node-change/convert.ts +++ b/packages/fig/src/node-change/convert.ts @@ -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 { + 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) } } diff --git a/packages/fig/src/node-change/serialize.ts b/packages/fig/src/node-change/serialize.ts index cb9c9d3b6..78e4e299f 100644 --- a/packages/fig/src/node-change/serialize.ts +++ b/packages/fig/src/node-change/serialize.ts @@ -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) diff --git a/tests/engine/io/fig/import/legacy/auto-layout/basic.test.ts b/tests/engine/io/fig/import/legacy/auto-layout/basic.test.ts index 8e07a5ade..92d27c603 100644 --- a/tests/engine/io/fig/import/legacy/auto-layout/basic.test.ts +++ b/tests/engine/io/fig/import/legacy/auto-layout/basic.test.ts @@ -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(), diff --git a/tests/engine/io/fig/roundtrip/exhaustive.test.ts b/tests/engine/io/fig/roundtrip/exhaustive.test.ts index e1216bebd..eda3c7306 100644 --- a/tests/engine/io/fig/roundtrip/exhaustive.test.ts +++ b/tests/engine/io/fig/roundtrip/exhaustive.test.ts @@ -64,8 +64,8 @@ const SPECS: FixtureSpec[] = [ thumbnailHeight: 239, imageCount: 3, figKiwiVersion: 101, - g1ExportSize: 596988, - g2ExportSize: 596988 + g1ExportSize: 596992, + g2ExportSize: 596992 } ]