From 170db518e9f196af5d4454b8e00ed0afdec52c8c Mon Sep 17 00:00:00 2001 From: Danila Poyarkov Date: Mon, 18 May 2026 00:08:31 +0300 Subject: [PATCH] refactor(fig): simplify override prop application --- .../core/src/kiwi/instance-overrides/props.ts | 136 +++++++++++++----- 1 file changed, 97 insertions(+), 39 deletions(-) diff --git a/packages/core/src/kiwi/instance-overrides/props.ts b/packages/core/src/kiwi/instance-overrides/props.ts index 74427a53c..e0b531bcf 100644 --- a/packages/core/src/kiwi/instance-overrides/props.ts +++ b/packages/core/src/kiwi/instance-overrides/props.ts @@ -1,3 +1,4 @@ +import type { GUID } from '#core/kiwi/binary/codec' import { applyOverridePatch } from '#core/kiwi/instance-overrides/patches' import { guidToString } from '#core/kiwi/node-change/convert' @@ -94,6 +95,11 @@ function assignmentsToValueMap( * Recursively apply prop assignments to children of a parent node. * Handles VISIBLE toggles and OVERRIDDEN_SYMBOL_ID (instance swap). */ +function stringToGuidParts(value: string): GUID { + const [sessionID, localID] = value.split(':').map(Number) + return { sessionID, localID } +} + function fallbackRefsForChild( ctx: OverrideContext, childName: string, @@ -104,15 +110,74 @@ function fallbackRefsForChild( for (const defId of valueByDef.keys()) { const propName = ctx.propNames.get(defId) if (propName && normalizePropName(propName) === normalizedChildName) { - refs.push({ - defID: { sessionID: Number(defId.split(':')[0]), localID: Number(defId.split(':')[1]) }, - componentPropNodeField: 'VISIBLE' - }) + refs.push({ defID: stringToGuidParts(defId), componentPropNodeField: 'VISIBLE' }) } } return refs.length > 0 ? refs : undefined } +function applyPatchAndMark( + ctx: OverrideContext, + childId: string, + patch: Parameters[1], + modified?: Set +): void { + if (applyOverridePatch(ctx, patch)) modified?.add(childId) +} + +function applyVisibleProp( + ctx: OverrideContext, + childId: string, + val: ComponentPropValue, + modified?: Set +): void { + if (val.boolValue === undefined) return + applyPatchAndMark( + ctx, + childId, + { targetId: childId, source: 'component-prop', props: { visible: val.boolValue } }, + modified + ) +} + +function applyTextProp( + ctx: OverrideContext, + childId: string, + val: ComponentPropValue, + modified?: Set +): void { + const child = ctx.graph.getNode(childId) + const text = propTextCharacters(val) + if (text === undefined || child?.type !== 'TEXT') return + applyPatchAndMark( + ctx, + childId, + { targetId: childId, source: 'component-prop', props: { text } }, + modified + ) +} + +function applySwapProp( + ctx: OverrideContext, + childId: string, + val: ComponentPropValue, + modified?: Set +): void { + const swapId = propTextCharacters(val) ?? (val.guidValue ? guidToString(val.guidValue) : undefined) + const newCompId = swapId ? ctx.guidToNodeId.get(swapId) : undefined + if (!newCompId) return + applyPatchAndMark( + ctx, + childId, + { + targetId: childId, + source: 'component-prop', + swapComponentId: getComponentRoot(ctx, newCompId) + }, + modified + ) +} + function applyComponentPropRef( ctx: OverrideContext, childId: string, @@ -120,27 +185,17 @@ function applyComponentPropRef( val: ComponentPropValue, modified?: Set ): void { - const child = ctx.graph.getNode(childId) - if (!child) return - - if (ref.componentPropNodeField === 'VISIBLE' && val.boolValue !== undefined) { - if (applyOverridePatch(ctx, { targetId: childId, source: 'component-prop', props: { visible: val.boolValue } })) modified?.add(childId) - return + switch (ref.componentPropNodeField) { + case 'VISIBLE': + applyVisibleProp(ctx, childId, val, modified) + break + case 'TEXT_DATA': + applyTextProp(ctx, childId, val, modified) + break + case 'OVERRIDDEN_SYMBOL_ID': + applySwapProp(ctx, childId, val, modified) + break } - - if (ref.componentPropNodeField === 'TEXT_DATA') { - const text = propTextCharacters(val) - if (text === undefined || child.type !== 'TEXT') return - if (applyOverridePatch(ctx, { targetId: childId, source: 'component-prop', props: { text } })) modified?.add(childId) - return - } - - if (ref.componentPropNodeField !== 'OVERRIDDEN_SYMBOL_ID') return - const swapId = propTextCharacters(val) ?? (val.guidValue ? guidToString(val.guidValue) : undefined) - if (!swapId) return - const newCompId = ctx.guidToNodeId.get(swapId) - if (!newCompId) return - if (applyOverridePatch(ctx, { targetId: childId, source: 'component-prop', swapComponentId: getComponentRoot(ctx, newCompId) })) modified?.add(childId) } function applyChildPropRefs( @@ -257,25 +312,28 @@ function applyOverrideAssignments( * Returns the set of modified node IDs so the caller can run a second * transitive sync to propagate the changes to deeper clones. */ +function collectPropRefsMap(ctx: OverrideContext): Map { + const result = new Map() + for (const [figmaId, nc] of ctx.changeMap) { + if (nc.componentPropRefs?.length) result.set(figmaId, nc.componentPropRefs) + } + return result +} + +function collectAssignmentsMap(ctx: OverrideContext): Map { + const result = new Map() + for (const [figmaId, nc] of ctx.changeMap) { + if (nc.componentPropAssignments?.length) result.set(figmaId, nc.componentPropAssignments) + } + return result +} + export function applyComponentProperties(ctx: OverrideContext): Set { const modified = new Set() - - const propRefsMap = new Map() - for (const [figmaId, nc] of ctx.changeMap) { - if (nc.componentPropRefs?.length) { - propRefsMap.set(figmaId, nc.componentPropRefs) - } - } + const propRefsMap = collectPropRefsMap(ctx) if (propRefsMap.size === 0) return modified - const assignmentSources = new Map() - for (const [figmaId, nc] of ctx.changeMap) { - if (nc.componentPropAssignments?.length) { - assignmentSources.set(figmaId, nc.componentPropAssignments) - } - } - - applyInstanceDirectAssignments(ctx, assignmentSources, propRefsMap, modified) + applyInstanceDirectAssignments(ctx, collectAssignmentsMap(ctx), propRefsMap, modified) applyOverrideAssignments(ctx, propRefsMap, modified) return modified