- Add case, justification, vertical alignment, truncation, max-lines, and OpenType controls - Apply visual text case and measured vertical offsets in CanvasKit rendering - Preserve nullable preview values, shared text styles, font demand, and .fig metadata - Add panel, engine, and canvas visual coverage
74 lines
2.2 KiB
TypeScript
74 lines
2.2 KiB
TypeScript
import type { SceneGraph, SceneNode, SharedStyle, SharedStyleKind, SharedStyleType } from './index'
|
|
|
|
const STYLE_REF_KEYS = {
|
|
fill: 'fillStyleId',
|
|
stroke: 'strokeStyleId',
|
|
text: 'textStyleId',
|
|
effect: 'effectStyleId',
|
|
grid: 'gridStyleId'
|
|
} as const satisfies Record<SharedStyleKind, keyof SceneNode>
|
|
|
|
const STYLE_TYPES = {
|
|
fill: 'FILL',
|
|
stroke: 'FILL',
|
|
text: 'TEXT',
|
|
effect: 'EFFECT',
|
|
grid: 'GRID'
|
|
} as const satisfies Record<SharedStyleKind, SharedStyleType>
|
|
|
|
const TEXT_STYLE_KEYS = new Set<keyof SceneNode>([
|
|
'fontFamily',
|
|
'fontWeight',
|
|
'italic',
|
|
'fontSize',
|
|
'lineHeight',
|
|
'letterSpacing',
|
|
'textDecoration',
|
|
'textCase',
|
|
'fontFeatures'
|
|
])
|
|
|
|
export function sharedStyleRefKey(kind: SharedStyleKind): (typeof STYLE_REF_KEYS)[SharedStyleKind] {
|
|
return STYLE_REF_KEYS[kind]
|
|
}
|
|
|
|
export function sharedStyleTypeForKind(kind: SharedStyleKind): SharedStyleType {
|
|
return STYLE_TYPES[kind]
|
|
}
|
|
|
|
export function getSharedStyles(graph: SceneGraph, kind: SharedStyleKind): SharedStyle[] {
|
|
const type = sharedStyleTypeForKind(kind)
|
|
const styles: SharedStyle[] = []
|
|
for (const node of graph.getAllNodes()) {
|
|
if (node.sharedStyleType !== type || !node.source.id) continue
|
|
styles.push({ id: node.source.id, nodeId: node.id, name: node.name, type })
|
|
}
|
|
return styles.sort((left, right) => left.name.localeCompare(right.name))
|
|
}
|
|
|
|
export function styleDetachmentChanges(
|
|
node: SceneNode,
|
|
changes: Partial<SceneNode>
|
|
): Partial<SceneNode> {
|
|
const next = { ...changes }
|
|
if ('fills' in changes && !('fillStyleId' in changes) && node.fillStyleId) {
|
|
next.fillStyleId = null
|
|
}
|
|
if ('strokes' in changes && !('strokeStyleId' in changes) && node.strokeStyleId) {
|
|
next.strokeStyleId = null
|
|
}
|
|
if ('effects' in changes && !('effectStyleId' in changes) && node.effectStyleId) {
|
|
next.effectStyleId = null
|
|
}
|
|
if ('layoutGrids' in changes && !('gridStyleId' in changes) && node.gridStyleId) {
|
|
next.gridStyleId = null
|
|
}
|
|
const changesTextStyle = (Object.keys(changes) as (keyof SceneNode)[]).some((key) =>
|
|
TEXT_STYLE_KEYS.has(key)
|
|
)
|
|
if (changesTextStyle && !('textStyleId' in changes) && node.textStyleId) {
|
|
next.textStyleId = null
|
|
}
|
|
return next
|
|
}
|