diff --git a/packages/core/src/kiwi/instance-overrides/sync/clones.ts b/packages/core/src/kiwi/instance-overrides/sync/clones.ts index de8f9dfd3..1c772e393 100644 --- a/packages/core/src/kiwi/instance-overrides/sync/clones.ts +++ b/packages/core/src/kiwi/instance-overrides/sync/clones.ts @@ -38,11 +38,7 @@ export function syncChildrenDeep( const tgtNode = graph.getNode(tgt.childIds[i]) if (!srcNode || !tgtNode || srcNode.type !== tgtNode.type) continue - if ( - srcNode.type === 'INSTANCE' && - swappedInstances.has(src.childIds[i]) && - srcNode.componentId !== tgtNode.componentId - ) { + if (srcNode.type === 'INSTANCE' && srcNode.componentId !== tgtNode.componentId) { recloneChildren(graph, src.childIds[i], tgtNode, swappedInstances, protections) continue } diff --git a/packages/core/src/layout/apply.ts b/packages/core/src/layout/apply.ts index a1f2cb734..0f92be791 100644 --- a/packages/core/src/layout/apply.ts +++ b/packages/core/src/layout/apply.ts @@ -32,12 +32,12 @@ function applyFrameSize(graph: SceneGraph, frame: SceneNode, yogaNode: YogaNode) } function updateChildFromYoga(graph: SceneGraph, child: SceneNode, yogaChild: YogaNode): void { - if (!child.visible || child.layoutPositioning === 'ABSOLUTE' || child.type === 'INSTANCE') return + if (!child.visible || child.layoutPositioning === 'ABSOLUTE') return const derived = child.figmaDerivedLayout graph.updateNode(child.id, { - x: derived?.x ?? yogaChild.getComputedLeft(), - y: derived?.y ?? yogaChild.getComputedTop(), + x: child.type === 'INSTANCE' ? yogaChild.getComputedLeft() : (derived?.x ?? yogaChild.getComputedLeft()), + y: child.type === 'INSTANCE' ? yogaChild.getComputedTop() : (derived?.y ?? yogaChild.getComputedTop()), width: derived?.width ?? yogaChild.getComputedWidth(), height: derived?.height ?? yogaChild.getComputedHeight() }) diff --git a/tests/engine/kiwi/instance-overrides/sync-clones.test.ts b/tests/engine/kiwi/instance-overrides/sync-clones.test.ts new file mode 100644 index 000000000..0b984776c --- /dev/null +++ b/tests/engine/kiwi/instance-overrides/sync-clones.test.ts @@ -0,0 +1,34 @@ +import { describe, expect, test } from 'bun:test' + +import { SceneGraph } from '@open-pencil/core' + +import { syncChildrenDeep } from '#core/kiwi/instance-overrides/sync/clones' + +describe('instance override clone sync', () => { + test('reclones nested instance children when the source component changes', () => { + const graph = new SceneGraph() + const page = graph.getPages()[0] + const sourceParent = graph.createNode('FRAME', page.id) + const targetParent = graph.createNode('FRAME', page.id) + const sourceComponent = graph.createNode('COMPONENT', page.id, { name: 'source component' }) + const targetComponent = graph.createNode('COMPONENT', page.id, { name: 'target component' }) + graph.createNode('TEXT', sourceComponent.id, { name: 'label', text: 'Source' }) + graph.createNode('TEXT', targetComponent.id, { name: 'label', text: 'Target' }) + const sourceChild = graph.createNode('INSTANCE', sourceParent.id, { + name: 'nested', + componentId: sourceComponent.id + }) + graph.populateInstanceChildren(sourceChild.id, sourceComponent.id) + const targetChild = graph.createNode('INSTANCE', targetParent.id, { + name: 'nested', + componentId: targetComponent.id + }) + graph.populateInstanceChildren(targetChild.id, targetComponent.id) + + syncChildrenDeep(graph, sourceParent.id, targetParent.id, new Set()) + + const syncedChild = graph.getNode(targetChild.id) + expect(syncedChild?.componentId).toBe(sourceComponent.id) + expect(graph.getChildren(targetChild.id).map((child) => child.text)).toEqual(['Source']) + }) +}) diff --git a/tests/engine/layout/auto-layout/imported-derived-layout.test.ts b/tests/engine/layout/auto-layout/imported-derived-layout.test.ts index 598faaaf5..714c14f06 100644 --- a/tests/engine/layout/auto-layout/imported-derived-layout.test.ts +++ b/tests/engine/layout/auto-layout/imported-derived-layout.test.ts @@ -103,4 +103,29 @@ describe('imported auto-layout bounds', () => { expect(graph.getNode(field.id)).toMatchObject({ x: 0, y: 0, width: 276, height: 40 }) }) + + test('uses Yoga positions for imported instances while preserving imported size', () => { + const graph = new SceneGraph() + const page = graph.getPages()[0] + const row = graph.createNode('FRAME', page.id, { + width: 200, + height: 40, + layoutMode: 'HORIZONTAL', + primaryAxisSizing: 'FIXED', + counterAxisSizing: 'FIXED', + primaryAxisAlign: 'MAX', + counterAxisAlign: 'CENTER' + }) + const instance = graph.createNode('INSTANCE', row.id, { + x: 0, + y: 0, + width: 40, + height: 20, + figmaDerivedLayout: { x: 0, y: 0, width: 40, height: 20 } + }) + + computeAllLayouts(graph) + + expect(graph.getNode(instance.id)).toMatchObject({ x: 160, y: 10, width: 40, height: 20 }) + }) })