127 lines
5.4 KiB
TypeScript
127 lines
5.4 KiB
TypeScript
import type { FigmaSourcePayload, SceneNode } from '@open-pencil/scene-graph'
|
|
|
|
interface FigmaSourceCarrier {
|
|
source: Omit<SceneNode['source'], 'editedFields'> &
|
|
Partial<Pick<SceneNode['source'], 'editedFields'>>
|
|
}
|
|
|
|
const RAW_SIZE_KEYS = new Set(['width', 'height'])
|
|
const RAW_TRANSFORM_KEYS = new Set(['x', 'y', 'rotation', 'flipX', 'flipY'])
|
|
|
|
const TEXT_DERIVED_RAW_FIELDS = [
|
|
'textData',
|
|
'derivedTextData',
|
|
'textUserLayoutVersion',
|
|
'textExplicitLayoutVersion'
|
|
] as const
|
|
const STROKE_GEOMETRY_RAW_FIELDS = ['strokeGeometry', 'vectorData'] as const
|
|
|
|
const EDITED_RAW_FIELDS: Partial<Record<string, readonly string[]>> = {
|
|
fillStyleId: ['styleIdForFill'],
|
|
strokeStyleId: ['styleIdForStrokeFill'],
|
|
textStyleId: ['styleIdForText'],
|
|
effectStyleId: ['styleIdForEffect'],
|
|
gridStyleId: ['styleIdForGrid'],
|
|
fills: ['fillPaints', 'backgroundPaints', 'backgroundColor'],
|
|
strokes: ['strokePaints'],
|
|
effects: ['effects'],
|
|
blendMode: ['blendMode'],
|
|
layoutGrids: ['layoutGrids'],
|
|
guides: ['guides'],
|
|
exportSettings: ['exportSettings'],
|
|
cornerRadius: ['cornerRadius'],
|
|
independentCorners: ['rectangleCornerRadiiIndependent'],
|
|
topLeftRadius: ['rectangleTopLeftCornerRadius', 'rectangleCornerRadiiIndependent'],
|
|
topRightRadius: ['rectangleTopRightCornerRadius', 'rectangleCornerRadiiIndependent'],
|
|
bottomLeftRadius: ['rectangleBottomLeftCornerRadius', 'rectangleCornerRadiiIndependent'],
|
|
bottomRightRadius: ['rectangleBottomRightCornerRadius', 'rectangleCornerRadiiIndependent'],
|
|
cornerSmoothing: ['cornerSmoothing'],
|
|
borderTopWeight: ['borderTopWeight', ...STROKE_GEOMETRY_RAW_FIELDS],
|
|
borderRightWeight: ['borderRightWeight', ...STROKE_GEOMETRY_RAW_FIELDS],
|
|
borderBottomWeight: ['borderBottomWeight', ...STROKE_GEOMETRY_RAW_FIELDS],
|
|
borderLeftWeight: ['borderLeftWeight', ...STROKE_GEOMETRY_RAW_FIELDS],
|
|
independentStrokeWeights: [
|
|
'borderStrokeWeightsIndependent',
|
|
'borderTopWeight',
|
|
'borderRightWeight',
|
|
'borderBottomWeight',
|
|
'borderLeftWeight',
|
|
...STROKE_GEOMETRY_RAW_FIELDS
|
|
],
|
|
strokeWeight: ['strokeWeight', ...STROKE_GEOMETRY_RAW_FIELDS],
|
|
strokeJoin: ['strokeJoin', ...STROKE_GEOMETRY_RAW_FIELDS],
|
|
strokeMiterLimit: ['miterLimit', ...STROKE_GEOMETRY_RAW_FIELDS],
|
|
strokeCap: [...STROKE_GEOMETRY_RAW_FIELDS],
|
|
dashPattern: [...STROKE_GEOMETRY_RAW_FIELDS],
|
|
text: [...TEXT_DERIVED_RAW_FIELDS],
|
|
styleRuns: [...TEXT_DERIVED_RAW_FIELDS],
|
|
fontSize: ['fontSize', ...TEXT_DERIVED_RAW_FIELDS],
|
|
fontFamily: ['fontName', 'fontVersion', ...TEXT_DERIVED_RAW_FIELDS],
|
|
fontWeight: ['semanticWeight', ...TEXT_DERIVED_RAW_FIELDS],
|
|
italic: ['semanticItalic', ...TEXT_DERIVED_RAW_FIELDS],
|
|
textAlignHorizontal: ['textAlignHorizontal', ...TEXT_DERIVED_RAW_FIELDS],
|
|
textAlignVertical: ['textAlignVertical', ...TEXT_DERIVED_RAW_FIELDS],
|
|
lineHeight: ['lineHeight', ...TEXT_DERIVED_RAW_FIELDS],
|
|
letterSpacing: ['letterSpacing', 'textTracking', ...TEXT_DERIVED_RAW_FIELDS],
|
|
textAutoResize: ['textAutoResize', ...TEXT_DERIVED_RAW_FIELDS],
|
|
textDecorationStyle: ['textDecorationStyle', ...TEXT_DERIVED_RAW_FIELDS],
|
|
textDecorationThickness: ['textDecorationThickness', ...TEXT_DERIVED_RAW_FIELDS],
|
|
textDecorationFills: ['textDecorationFillPaints', ...TEXT_DERIVED_RAW_FIELDS],
|
|
textUnderlineOffset: ['textUnderlineOffset', ...TEXT_DERIVED_RAW_FIELDS],
|
|
leadingTrim: ['leadingTrim', ...TEXT_DERIVED_RAW_FIELDS],
|
|
maxLines: ['maxLines', ...TEXT_DERIVED_RAW_FIELDS],
|
|
fontVariations: ['fontVariations', ...TEXT_DERIVED_RAW_FIELDS],
|
|
fontFeatures: [
|
|
'fontVariantCommonLigatures',
|
|
'fontVariantContextualLigatures',
|
|
'toggledOnOTFeatures',
|
|
'toggledOffOTFeatures',
|
|
...TEXT_DERIVED_RAW_FIELDS
|
|
],
|
|
minWidth: ['minSize'],
|
|
minHeight: ['minSize'],
|
|
maxWidth: ['maxSize'],
|
|
maxHeight: ['maxSize'],
|
|
vectorNetwork: ['vectorData', 'fillGeometry', 'strokeGeometry'],
|
|
fillGeometry: ['fillGeometry', 'vectorData'],
|
|
strokeGeometry: ['strokeGeometry', 'vectorData'],
|
|
isMask: ['mask'],
|
|
maskType: ['maskType'],
|
|
maskIsOutline: ['maskIsOutline'],
|
|
componentPropertyDefinitions: ['componentPropDefs'],
|
|
componentPropertyReferences: ['componentPropRefs'],
|
|
componentPropertyAssignments: ['componentPropAssignments'],
|
|
variantPropSpecs: ['variantPropSpecs']
|
|
}
|
|
|
|
export function staleFigmaRawFields(editedFields: readonly string[] = []): ReadonlySet<string> {
|
|
return new Set(editedFields.flatMap((key) => EDITED_RAW_FIELDS[key] ?? []))
|
|
}
|
|
|
|
export function effectiveFigmaRawNodeFields(node: FigmaSourceCarrier): Record<string, unknown> {
|
|
const staleFields = staleFigmaRawFields(node.source.editedFields ?? [])
|
|
if (staleFields.size === 0) return node.source.fig.rawNodeFields
|
|
return Object.fromEntries(
|
|
Object.entries(node.source.fig.rawNodeFields).filter(([key]) => !staleFields.has(key))
|
|
)
|
|
}
|
|
|
|
export function effectiveFigmaSourcePayload(node: FigmaSourceCarrier): FigmaSourcePayload {
|
|
const sourceEditedFields = node.source.editedFields ?? []
|
|
return {
|
|
...node.source.fig,
|
|
rawNodeFields: effectiveFigmaRawNodeFields(node),
|
|
rawSize: sourceEditedFields.some((key) => RAW_SIZE_KEYS.has(key))
|
|
? null
|
|
: node.source.fig.rawSize,
|
|
rawTransform: sourceEditedFields.some((key) => RAW_TRANSFORM_KEYS.has(key))
|
|
? null
|
|
: node.source.fig.rawTransform
|
|
}
|
|
}
|
|
|
|
export function readEffectiveFigmaRawField(node: FigmaSourceCarrier, field: string): unknown {
|
|
if (staleFigmaRawFields(node.source.editedFields ?? []).has(field)) return undefined
|
|
return node.source.fig.rawNodeFields[field]
|
|
}
|