fix(fig): apply text component props
This commit is contained in:
parent
a6050da061
commit
2072f2e916
|
|
@ -13,7 +13,17 @@ function normalizePropName(value: string): string {
|
|||
}
|
||||
|
||||
function isEmptyPropValue(v: ComponentPropValue): boolean {
|
||||
return v.boolValue === undefined && v.textValue === undefined && v.guidValue === undefined
|
||||
return (
|
||||
v.boolValue === undefined &&
|
||||
v.textValue === undefined &&
|
||||
v.textDataValue === undefined &&
|
||||
v.guidValue === undefined
|
||||
)
|
||||
}
|
||||
|
||||
function propTextCharacters(value: ComponentPropValue): string | undefined {
|
||||
if (typeof value.textValue === 'string') return value.textValue
|
||||
return value.textValue?.characters ?? value.textDataValue?.characters
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -60,6 +70,7 @@ function resolveAssignmentValue(
|
|||
if (variableValue?.symbolIdValue?.guid) return { guidValue: variableValue.symbolIdValue.guid }
|
||||
if (variableValue?.boolValue !== undefined) return { boolValue: variableValue.boolValue }
|
||||
if (variableValue?.textValue !== undefined) return { textValue: variableValue.textValue }
|
||||
if (variableValue?.textDataValue !== undefined) return { textDataValue: variableValue.textDataValue }
|
||||
|
||||
return resolveDefaults ? (ctx.propDefaults.get(key) ?? assignment.value) : assignment.value
|
||||
}
|
||||
|
|
@ -101,6 +112,54 @@ function fallbackRefsForChild(
|
|||
return refs.length > 0 ? refs : undefined
|
||||
}
|
||||
|
||||
function applyComponentPropRef(
|
||||
ctx: OverrideContext,
|
||||
childId: string,
|
||||
ref: ComponentPropRef,
|
||||
val: ComponentPropValue,
|
||||
modified?: Set<string>
|
||||
): void {
|
||||
const child = ctx.graph.getNode(childId)
|
||||
if (!child) return
|
||||
|
||||
if (ref.componentPropNodeField === 'VISIBLE' && val.boolValue !== undefined) {
|
||||
ctx.graph.updateNode(childId, { visible: val.boolValue })
|
||||
modified?.add(childId)
|
||||
return
|
||||
}
|
||||
|
||||
if (ref.componentPropNodeField === 'TEXT_DATA') {
|
||||
const text = propTextCharacters(val)
|
||||
if (text === undefined || child.type !== 'TEXT') return
|
||||
ctx.graph.updateNode(childId, { 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
|
||||
repopulateInstance(ctx, childId, newCompId)
|
||||
modified?.add(childId)
|
||||
}
|
||||
|
||||
function applyChildPropRefs(
|
||||
ctx: OverrideContext,
|
||||
childId: string,
|
||||
refs: ComponentPropRef[] | undefined,
|
||||
valueByDef: Map<string, ComponentPropValue>,
|
||||
modified?: Set<string>
|
||||
): void {
|
||||
if (!refs) return
|
||||
for (const ref of refs) {
|
||||
if (!ref.defID) continue
|
||||
const val = valueByDef.get(guidToString(ref.defID))
|
||||
if (val) applyComponentPropRef(ctx, childId, ref, val, modified)
|
||||
}
|
||||
}
|
||||
|
||||
function applyPropAssignments(
|
||||
ctx: OverrideContext,
|
||||
parentId: string,
|
||||
|
|
@ -121,27 +180,7 @@ function applyPropAssignments(
|
|||
const refs =
|
||||
findPropRefs(ctx, child.componentId, propRefsMap) ??
|
||||
fallbackRefsForChild(ctx, child.name, valueByDef)
|
||||
if (refs) {
|
||||
for (const ref of refs) {
|
||||
if (!ref.defID) continue
|
||||
const val = valueByDef.get(guidToString(ref.defID))
|
||||
if (!val) continue
|
||||
|
||||
if (ref.componentPropNodeField === 'VISIBLE' && val.boolValue !== undefined) {
|
||||
ctx.graph.updateNode(childId, { visible: val.boolValue })
|
||||
modified?.add(childId)
|
||||
} else if (ref.componentPropNodeField === 'OVERRIDDEN_SYMBOL_ID') {
|
||||
const swapId = val.textValue ?? (val.guidValue ? guidToString(val.guidValue) : undefined)
|
||||
if (!swapId) continue
|
||||
const newCompId = ctx.guidToNodeId.get(swapId)
|
||||
if (newCompId) {
|
||||
repopulateInstance(ctx, childId, newCompId)
|
||||
modified?.add(childId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
applyChildPropRefs(ctx, childId, refs, valueByDef, modified)
|
||||
applyPropAssignments(ctx, childId, valueByDef, propRefsMap, modified)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,9 +19,12 @@ export interface ComponentPropRef {
|
|||
componentPropNodeField: string
|
||||
}
|
||||
|
||||
export type ComponentPropTextValue = string | { characters?: string }
|
||||
|
||||
export type ComponentPropValue = {
|
||||
boolValue?: boolean
|
||||
textValue?: string
|
||||
textValue?: ComponentPropTextValue
|
||||
textDataValue?: { characters?: string }
|
||||
guidValue?: GUID
|
||||
}
|
||||
|
||||
|
|
@ -32,6 +35,7 @@ export interface ComponentPropAssignment {
|
|||
value?: {
|
||||
boolValue?: boolean
|
||||
textValue?: string
|
||||
textDataValue?: { characters?: string }
|
||||
symbolIdValue?: { guid?: GUID }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
70
tests/engine/io/fig/import/component-props.test.ts
Normal file
70
tests/engine/io/fig/import/component-props.test.ts
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
import { describe, expect, test } from 'bun:test'
|
||||
|
||||
import { importNodeChanges } from '#core/kiwi/fig/import'
|
||||
import type { NodeChange } from '#core/kiwi/binary/codec'
|
||||
|
||||
const documentGuid = { sessionID: 0, localID: 0 }
|
||||
const pageGuid = { sessionID: 0, localID: 1 }
|
||||
const componentGuid = { sessionID: 1, localID: 1 }
|
||||
const componentTextGuid = { sessionID: 1, localID: 2 }
|
||||
const instanceGuid = { sessionID: 2, localID: 1 }
|
||||
const textPropGuid = { sessionID: 3, localID: 1 }
|
||||
|
||||
function baseTextChange(): NodeChange {
|
||||
return {
|
||||
guid: componentTextGuid,
|
||||
phase: 'CREATED',
|
||||
parentIndex: { guid: componentGuid, position: '!' },
|
||||
type: 'TEXT',
|
||||
name: 'Label',
|
||||
size: { x: 100, y: 20 },
|
||||
transform: { m00: 1, m01: 0, m02: 0, m10: 0, m11: 1, m12: 0 },
|
||||
fontSize: 14,
|
||||
fontName: { family: 'Inter', style: 'Regular', postscript: '' },
|
||||
textData: { characters: 'Menu Item', lines: [{ lineType: 'PLAIN' }] },
|
||||
componentPropRefs: [{ defID: textPropGuid, componentPropNodeField: 'TEXT_DATA' }]
|
||||
}
|
||||
}
|
||||
|
||||
describe('Figma component property import', () => {
|
||||
test('applies text data component prop assignments', () => {
|
||||
const nodeChanges: NodeChange[] = [
|
||||
{ guid: documentGuid, phase: 'CREATED', type: 'DOCUMENT', name: 'Document' },
|
||||
{ guid: pageGuid, phase: 'CREATED', parentIndex: { guid: documentGuid, position: '!' }, type: 'CANVAS', name: 'Page' },
|
||||
{
|
||||
guid: componentGuid,
|
||||
phase: 'CREATED',
|
||||
parentIndex: { guid: pageGuid, position: '!' },
|
||||
type: 'SYMBOL',
|
||||
name: 'Menu item',
|
||||
size: { x: 100, y: 20 },
|
||||
transform: { m00: 1, m01: 0, m02: 0, m10: 0, m11: 1, m12: 0 },
|
||||
componentPropDefs: [
|
||||
{ id: textPropGuid, name: 'label', initialValue: { textValue: 'Menu Item' } }
|
||||
]
|
||||
},
|
||||
baseTextChange(),
|
||||
{
|
||||
guid: instanceGuid,
|
||||
phase: 'CREATED',
|
||||
parentIndex: { guid: pageGuid, position: '"' },
|
||||
type: 'INSTANCE',
|
||||
name: 'Menu item instance',
|
||||
symbolData: { symbolID: componentGuid },
|
||||
size: { x: 100, y: 20 },
|
||||
transform: { m00: 1, m01: 0, m02: 0, m10: 0, m11: 1, m12: 40 },
|
||||
componentPropAssignments: [
|
||||
{
|
||||
defID: textPropGuid,
|
||||
value: { textValue: { characters: 'Profile Item' } },
|
||||
varValue: { value: { textDataValue: { characters: 'Profile Item' } } }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
const graph = importNodeChanges(nodeChanges, [], undefined, { populate: 'all' })
|
||||
const labels = Array.from(graph.getAllNodes()).filter((node) => node.type === 'TEXT')
|
||||
expect(labels.map((node) => node.text).sort()).toEqual(['Menu Item', 'Profile Item'])
|
||||
})
|
||||
})
|
||||
Loading…
Reference in a new issue