fix(fig): sync lazy link component overrides
- Carry resolved text styling and glyph data through component-property text clones - Align lazy cloned child placement and hidden chevrons with their resolved source - Cover the gold-preview dropzone link lazy import regression
This commit is contained in:
parent
11495ee2ce
commit
e6da2736fe
|
|
@ -6,6 +6,7 @@ import type {
|
|||
OverrideContext
|
||||
} from '#core/kiwi/instance-overrides/types'
|
||||
import { guidToString } from '#core/kiwi/node-change/convert'
|
||||
import { copyFills, copyStyleRuns } from '#core/scene-graph/copy'
|
||||
|
||||
import { propTextCharacters } from './values'
|
||||
|
||||
|
|
@ -42,12 +43,18 @@ function applyTextProp(
|
|||
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
|
||||
)
|
||||
const source = child.componentId ? ctx.graph.getNode(child.componentId) : null
|
||||
const props: Parameters<typeof applyPatchAndMark>[2]['props'] = { text }
|
||||
if (source?.type === 'TEXT' && source.text === text) {
|
||||
props.width = source.width
|
||||
props.height = source.height
|
||||
props.fills = copyFills(source.fills)
|
||||
props.styleRuns = copyStyleRuns(source.styleRuns)
|
||||
props.figmaDerivedTextGlyphs = source.figmaDerivedTextGlyphs
|
||||
? structuredClone(source.figmaDerivedTextGlyphs)
|
||||
: undefined
|
||||
}
|
||||
applyPatchAndMark(ctx, childId, { targetId: childId, source: 'component-prop', props }, modified)
|
||||
}
|
||||
|
||||
function applySwapProp(
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ export type {
|
|||
} from './types'
|
||||
|
||||
import { guidToString } from '#core/kiwi/node-change/convert'
|
||||
import type { SceneGraph } from '#core/scene-graph'
|
||||
import { copyFills } from '#core/scene-graph/copy'
|
||||
import type { SceneGraph, SceneNode } from '#core/scene-graph'
|
||||
import { copyFills, copyStyleRuns } from '#core/scene-graph/copy'
|
||||
|
||||
import { applyConstraintScaling } from './constraints'
|
||||
import { applyDerivedSymbolData } from './derived-symbol-data'
|
||||
|
|
@ -89,6 +89,55 @@ function propagateResolvedFills(
|
|||
}
|
||||
}
|
||||
|
||||
function propagateResolvedChildPlacementClones(graph: SceneGraph): void {
|
||||
for (let pass = 0; pass < 10; pass++) {
|
||||
let changed = false
|
||||
for (const node of graph.getAllNodes()) {
|
||||
if (node.type !== 'INSTANCE' || !node.componentId) continue
|
||||
const source = graph.getNode(node.componentId)
|
||||
if (!source || source.childIds.length !== node.childIds.length) continue
|
||||
for (let i = 0; i < node.childIds.length; i++) {
|
||||
const sourceChild = graph.getNode(source.childIds[i])
|
||||
const child = graph.getNode(node.childIds[i])
|
||||
if (!sourceChild || !child) continue
|
||||
if (sourceChild.overrideKey && child.overrideKey && sourceChild.overrideKey !== child.overrideKey) {
|
||||
continue
|
||||
}
|
||||
const updates: Partial<SceneNode> = {}
|
||||
if (!sourceChild.visible && child.visible) updates.visible = false
|
||||
if (sourceChild.x !== child.x) updates.x = sourceChild.x
|
||||
if (sourceChild.y !== child.y) updates.y = sourceChild.y
|
||||
if (Object.keys(updates).length === 0) continue
|
||||
graph.updateNode(child.id, updates)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
if (!changed) return
|
||||
}
|
||||
}
|
||||
|
||||
function propagateResolvedTextClones(graph: SceneGraph): void {
|
||||
for (let pass = 0; pass < 10; pass++) {
|
||||
let changed = false
|
||||
for (const node of graph.getAllNodes()) {
|
||||
if (node.type !== 'TEXT' || !node.componentId) continue
|
||||
const source = graph.getNode(node.componentId)
|
||||
if (source?.type !== 'TEXT' || source.text !== node.text) continue
|
||||
graph.updateNode(node.id, {
|
||||
width: source.width,
|
||||
height: source.height,
|
||||
fills: copyFills(source.fills),
|
||||
styleRuns: copyStyleRuns(source.styleRuns),
|
||||
figmaDerivedTextGlyphs: source.figmaDerivedTextGlyphs
|
||||
? structuredClone(source.figmaDerivedTextGlyphs)
|
||||
: undefined
|
||||
})
|
||||
changed = true
|
||||
}
|
||||
if (!changed) return
|
||||
}
|
||||
}
|
||||
|
||||
function buildOverrideContext(
|
||||
graph: SceneGraph,
|
||||
changeMap: Map<string, InstanceNodeChange>,
|
||||
|
|
@ -213,6 +262,7 @@ export function populateAndApplyOverrides(
|
|||
ctx.protectedFields
|
||||
)
|
||||
}
|
||||
propagateResolvedChildPlacementClones(graph)
|
||||
}
|
||||
|
||||
applyDerivedSymbolData(ctx)
|
||||
|
|
@ -221,5 +271,6 @@ export function populateAndApplyOverrides(
|
|||
new Set([...ctx.kiwiPropertyNodes, ...overriddenNodes]),
|
||||
ctx.activeNodeIds
|
||||
)
|
||||
propagateResolvedTextClones(graph)
|
||||
applyConstraintScaling(ctx)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ describe('derived instance layout regressions', () => {
|
|||
}
|
||||
})
|
||||
|
||||
test('propagates static icon color overrides with lazy first-page import', async () => {
|
||||
test('propagates nested component overrides with lazy first-page import', async () => {
|
||||
const graph = await parseFigFile(readFixtureBytes('gold-preview.fig').buffer as ArrayBuffer, {
|
||||
populate: 'first-page'
|
||||
})
|
||||
|
|
@ -144,6 +144,27 @@ describe('derived instance layout regressions', () => {
|
|||
|
||||
expect(vector?.strokes[0]?.color).toMatchObject({ r: 1, g: 1, b: 1, a: 1 })
|
||||
}
|
||||
|
||||
const dropzone = previewChild(graph, nodes, 'Drag’ n’ Drop File Uploads')
|
||||
const dropzoneRoot = childNamed(graph, dropzone, '_drag-n-drop-file-upload')
|
||||
const content = childNamed(graph, dropzoneRoot, 'Content')
|
||||
const titleRow = childNamed(graph, content, 'Title')
|
||||
const links = childNamed(graph, titleRow, 'Links')
|
||||
const linkRoot = childNamed(graph, links, '_link-default')
|
||||
const chevronLeft = childNamed(graph, linkRoot, 'chevron-left')
|
||||
const placeholder = childNamed(graph, linkRoot, 'Placeholder')
|
||||
const linkText = childNamed(graph, placeholder, 'Link')
|
||||
|
||||
expect(chevronLeft?.visible).toBe(false)
|
||||
expect(placeholder?.x).toBeCloseTo(0, 3)
|
||||
expect(linkText?.text).toBe('browse')
|
||||
expect(linkText?.fills[0]?.color).toMatchObject({
|
||||
r: 0.14509804546833038,
|
||||
g: 0.38823530077934265,
|
||||
b: 0.9215686321258545,
|
||||
a: 1
|
||||
})
|
||||
expect(linkText?.figmaDerivedTextGlyphs?.length).toBeGreaterThan(0)
|
||||
})
|
||||
|
||||
test('preserves WYSIWYG toolbar padding', () => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue