fix(fig): apply shared text styles

This commit is contained in:
Danila Poyarkov 2026-05-17 23:02:20 +03:00
parent ff0f072aef
commit 4d758f26cb
3 changed files with 55 additions and 0 deletions

View file

@ -351,6 +351,8 @@ export interface NodeChange {
truncationStartIndex?: number
truncatedHeight?: number
}
styleType?: string
styleIdForText?: { guid?: GUID }
textUserLayoutVersion?: number
textExplicitLayoutVersion?: number
textBidiVersion?: number

View file

@ -349,6 +349,32 @@ function parseDocumentColorSpace(nodeChanges: NodeChange[]): 'srgb' | 'display-p
return documentNode?.documentColorProfile === 'DISPLAY_P3' ? 'display-p3' : 'srgb'
}
function applyTextStyleRefs(changeMap: Map<string, NodeChange>): void {
const textStyleFields = [
'fontSize',
'fontName',
'lineHeight',
'letterSpacing',
'textDecoration',
'textCase'
] as const
for (const nc of changeMap.values()) {
if (nc.type !== 'TEXT') continue
const styleGuid = nc.styleIdForText?.guid
if (!styleGuid) continue
const style = changeMap.get(guidToString(styleGuid))
if (style?.type !== 'TEXT' || style.styleType !== 'TEXT') continue
for (const field of textStyleFields) {
if (field === 'textDecoration') {
nc.textDecoration = style.textDecoration
} else if (style[field] !== undefined) {
nc[field] = style[field] as never
}
}
}
}
export interface FigImportOptions {
populate?: 'all' | 'first-page'
}
@ -373,6 +399,7 @@ export function importNodeChanges(
}
const { changeMap, parentMap, childrenMap } = buildChangeMaps(nodeChanges)
applyTextStyleRefs(changeMap)
const assetRefs = buildAssetRefMap(changeMap)
setVariableColorResolver(buildVariableColorResolver(changeMap, assetRefs))

View file

@ -49,6 +49,32 @@ describe('fig-import: text properties', () => {
expect(n.lineHeight).toBe(16.94)
})
test('applies shared text style refs', () => {
const graph = importNodeChanges([
doc(),
canvas(),
node('TEXT', 20, 1, {
name: 'Body style',
styleType: 'TEXT',
fontSize: 16,
fontName: { family: 'Inter', style: 'Regular' },
lineHeight: { value: 24, units: 'PIXELS' }
} as Partial<NodeChange>),
node('TEXT', 10, 1, {
textData: { characters: 'Styled text' },
fontSize: 48,
fontName: { family: 'Inter', style: 'Extra Bold' },
textDecoration: 'UNDERLINE',
styleIdForText: { guid: { sessionID: 1, localID: 20 } }
} as Partial<NodeChange>)
])
const styled = graph.getChildren(graph.getPages()[0].id).find((node) => node.text === 'Styled text')
expect(styled?.fontSize).toBe(16)
expect(styled?.fontWeight).toBe(400)
expect(styled?.lineHeight).toBe(24)
expect(styled?.textDecoration).toBe('NONE')
})
test('font weight mapping', () => {
const cases = [
['Bold', 700],