fix(fig): preserve auto-layout child transforms
- Stop zeroing transforms for auto-layout children during .fig export - Match real Figma files, which keep child transform offsets alongside layout data - Update transform regression coverage to prevent collapsed roundtrips
This commit is contained in:
parent
4fe2b75dc8
commit
1c655f34ce
|
|
@ -23,7 +23,7 @@ interface SceneNodeToKiwiContext {
|
|||
mapToFigmaType: (type: SceneNode['type']) => string
|
||||
fillToKiwiPaint: (fill: SceneNode['fills'][number]) => Paint
|
||||
safeColor: (color: Color) => Color
|
||||
computeExportTransform: (node: SceneNode, graph: SceneGraph) => Matrix
|
||||
computeExportTransform: (node: SceneNode) => Matrix
|
||||
serializeCornerRadii: (node: SceneNode, nc: KiwiNodeChange) => void
|
||||
serializeTextProps: (
|
||||
node: SceneNode,
|
||||
|
|
@ -250,7 +250,7 @@ export function sceneNodeToKiwiWithContext(
|
|||
opacity: node.opacity,
|
||||
phase: 'CREATED',
|
||||
size: { x: node.width, y: node.height },
|
||||
transform: context.computeExportTransform(node, context.graph),
|
||||
transform: context.computeExportTransform(node),
|
||||
strokeWeight: node.strokes[0]?.weight ?? DEFAULT_STROKE_WEIGHT,
|
||||
strokeAlign: node.strokes[0]?.align ?? 'INSIDE'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -410,20 +410,11 @@ function serializeVariableBindings(
|
|||
if (entries.length > 0) nc.variableConsumptionMap = { entries }
|
||||
}
|
||||
|
||||
function computeExportTransform(node: SceneNode, graph: SceneGraph): Matrix {
|
||||
function computeExportTransform(node: SceneNode): Matrix {
|
||||
const sx = node.flipX ? -1 : 1
|
||||
const cos = Math.cos((node.rotation * Math.PI) / 180)
|
||||
const sin = Math.sin((node.rotation * Math.PI) / 180)
|
||||
|
||||
// Auto-layout children should have (0,0) transform — Figma computes
|
||||
// their positions from the layout engine at render time.
|
||||
const parent = node.parentId ? graph.getNode(node.parentId) : undefined
|
||||
const isAutoLayoutChild =
|
||||
parent &&
|
||||
parent.layoutMode !== 'NONE' &&
|
||||
parent.layoutMode !== 'GRID' &&
|
||||
node.layoutPositioning !== 'ABSOLUTE'
|
||||
|
||||
const m00 = cos * sx
|
||||
const m01 = -sin
|
||||
const m10 = sin * sx
|
||||
|
|
@ -443,10 +434,10 @@ function computeExportTransform(node: SceneNode, graph: SceneGraph): Matrix {
|
|||
return {
|
||||
m00,
|
||||
m01,
|
||||
m02: isAutoLayoutChild ? 0 : node.x - offsetX,
|
||||
m02: node.x - offsetX,
|
||||
m10,
|
||||
m11,
|
||||
m12: isAutoLayoutChild ? 0 : node.y - offsetY
|
||||
m12: node.y - offsetY
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { expectDefined, getNodeOrThrow } from '#tests/helpers/assert'
|
|||
import { ROOT_GUID, pageId } from '../helpers'
|
||||
|
||||
describe('Fix 1: auto-layout child transforms', () => {
|
||||
test('auto-layout child gets zero transform regardless of its x/y', () => {
|
||||
test('auto-layout child preserves its stored transform offsets', () => {
|
||||
const graph = new SceneGraph()
|
||||
const parent = graph.createNode('FRAME', pageId(graph), {
|
||||
name: 'AutoLayout',
|
||||
|
|
@ -48,8 +48,8 @@ describe('Fix 1: auto-layout child transforms', () => {
|
|||
'child node change'
|
||||
)
|
||||
expect(childNc).toBeDefined()
|
||||
expect(childNc.transform.m02).toBe(0)
|
||||
expect(childNc.transform.m12).toBe(0)
|
||||
expect(childNc.transform.m02).toBe(50)
|
||||
expect(childNc.transform.m12).toBe(100)
|
||||
})
|
||||
|
||||
test('absolute-positioned child inside auto-layout keeps its real x/y', () => {
|
||||
|
|
@ -130,7 +130,7 @@ describe('Fix 1: auto-layout child transforms', () => {
|
|||
expect(childNc.transform.m12).toBe(45)
|
||||
})
|
||||
|
||||
test('horizontal auto-layout child also gets zero transform', () => {
|
||||
test('horizontal auto-layout child also preserves its stored transform offsets', () => {
|
||||
const graph = new SceneGraph()
|
||||
const parent = graph.createNode('FRAME', pageId(graph), {
|
||||
name: 'HorizontalLayout',
|
||||
|
|
@ -164,8 +164,8 @@ describe('Fix 1: auto-layout child transforms', () => {
|
|||
changes.find((nc) => nc.name === 'Item'),
|
||||
'item node change'
|
||||
)
|
||||
expect(itemNc.transform.m02).toBe(0)
|
||||
expect(itemNc.transform.m12).toBe(0)
|
||||
expect(itemNc.transform.m02).toBe(200)
|
||||
expect(itemNc.transform.m12).toBe(50)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue