diff --git a/.changeset/neat-badgers-dream.md b/.changeset/neat-badgers-dream.md new file mode 100644 index 0000000..2e95190 --- /dev/null +++ b/.changeset/neat-badgers-dream.md @@ -0,0 +1,5 @@ +--- +"penpot-exporter": minor +--- + +Added support for instances overrides diff --git a/plugin-src/OverridesLibrary.ts b/plugin-src/OverridesLibrary.ts new file mode 100644 index 0000000..c633d32 --- /dev/null +++ b/plugin-src/OverridesLibrary.ts @@ -0,0 +1,13 @@ +class OverridesLibrary { + private overrides: Map = new Map(); + + public register(nodeId: string, overrides: NodeChangeProperty[]): void { + this.overrides.set(nodeId, overrides); + } + + public get(nodeId: string): NodeChangeProperty[] | undefined { + return this.overrides.get(nodeId); + } +} + +export const overridesLibrary = new OverridesLibrary(); diff --git a/plugin-src/transformers/partials/index.ts b/plugin-src/transformers/partials/index.ts index f3a171b..5b40cc8 100644 --- a/plugin-src/transformers/partials/index.ts +++ b/plugin-src/transformers/partials/index.ts @@ -7,6 +7,7 @@ export * from './transformEffects'; export * from './transformFigmaIds'; export * from './transformFills'; export * from './transformLayout'; +export * from './transformOverrides'; export * from './transformProportion'; export * from './transformRotationAndPosition'; export * from './transformSceneNode'; diff --git a/plugin-src/transformers/partials/transformOverrides.ts b/plugin-src/transformers/partials/transformOverrides.ts new file mode 100644 index 0000000..dd9cbd0 --- /dev/null +++ b/plugin-src/transformers/partials/transformOverrides.ts @@ -0,0 +1,23 @@ +import { overridesLibrary } from '@plugin/OverridesLibrary'; +import { syncAttributes } from '@plugin/utils/syncAttributes'; + +import { SyncGroups } from '@ui/lib/types/utils/syncGroups'; + +export const transformOverrides = (node: SceneNode) => { + const overrides = overridesLibrary.get(node.id); + if (!overrides) { + return {}; + } + + const touched: SyncGroups[] = []; + + overrides.forEach(override => { + if (syncAttributes[override]) { + touched.push(...syncAttributes[override]); + } + }); + + return { + touched + }; +}; diff --git a/plugin-src/transformers/transformBooleanNode.ts b/plugin-src/transformers/transformBooleanNode.ts index 47bebbe..defb308 100644 --- a/plugin-src/transformers/transformBooleanNode.ts +++ b/plugin-src/transformers/transformBooleanNode.ts @@ -6,6 +6,7 @@ import { transformFigmaIds, transformFills, transformLayoutAttributes, + transformOverrides, transformProportion, transformRotationAndPosition, transformSceneNode, @@ -33,6 +34,7 @@ export const transformBooleanNode = async ( ...transformSceneNode(node), ...transformBlend(node), ...transformProportion(node), - ...transformLayoutAttributes(node) + ...transformLayoutAttributes(node), + ...transformOverrides(node) }; }; diff --git a/plugin-src/transformers/transformEllipseNode.ts b/plugin-src/transformers/transformEllipseNode.ts index 608b5c8..0675b41 100644 --- a/plugin-src/transformers/transformEllipseNode.ts +++ b/plugin-src/transformers/transformEllipseNode.ts @@ -6,6 +6,7 @@ import { transformFigmaIds, transformFills, transformLayoutAttributes, + transformOverrides, transformProportion, transformRotationAndPosition, transformSceneNode, @@ -28,6 +29,7 @@ export const transformEllipseNode = (node: EllipseNode, baseRotation: number): C ...transformBlend(node), ...transformProportion(node), ...transformLayoutAttributes(node), - ...transformConstraints(node) + ...transformConstraints(node), + ...transformOverrides(node) }; }; diff --git a/plugin-src/transformers/transformFrameNode.ts b/plugin-src/transformers/transformFrameNode.ts index c47e7cc..c5b34f0 100644 --- a/plugin-src/transformers/transformFrameNode.ts +++ b/plugin-src/transformers/transformFrameNode.ts @@ -9,6 +9,7 @@ import { transformFigmaIds, transformFills, transformLayoutAttributes, + transformOverrides, transformProportion, transformRotationAndPosition, transformSceneNode, @@ -63,6 +64,7 @@ export const transformFrameNode = async ( ...frameSpecificAttributes, ...transformDimension(node), ...(await transformChildren(node, rotation)), - ...transformSceneNode(node) + ...transformSceneNode(node), + ...transformOverrides(node) }; }; diff --git a/plugin-src/transformers/transformGroupNode.ts b/plugin-src/transformers/transformGroupNode.ts index bdb82ea..b5246f4 100644 --- a/plugin-src/transformers/transformGroupNode.ts +++ b/plugin-src/transformers/transformGroupNode.ts @@ -3,6 +3,7 @@ import { transformDimension, transformEffects, transformFigmaIds, + transformOverrides, transformRotationAndPosition, transformSceneNode } from '@plugin/transformers/partials'; @@ -19,7 +20,8 @@ export const transformGroupNode = async ( ...transformGroupNodeLike(node, baseRotation), ...transformEffects(node), ...transformBlend(node), - ...(await transformChildren(node, baseRotation)) + ...(await transformChildren(node, baseRotation)), + ...transformOverrides(node) }; }; diff --git a/plugin-src/transformers/transformInstanceNode.ts b/plugin-src/transformers/transformInstanceNode.ts index a610447..2d1639c 100644 --- a/plugin-src/transformers/transformInstanceNode.ts +++ b/plugin-src/transformers/transformInstanceNode.ts @@ -1,3 +1,4 @@ +import { overridesLibrary } from '@plugin/OverridesLibrary'; import { remoteComponentLibrary } from '@plugin/RemoteComponentLibrary'; import { transformAutoLayout, @@ -10,6 +11,7 @@ import { transformFigmaIds, transformFills, transformLayoutAttributes, + transformOverrides, transformProportion, transformRotationAndPosition, transformSceneNode, @@ -32,6 +34,12 @@ export const transformInstanceNode = async ( registerExternalComponents(mainComponent); } + if (node.overrides.length > 0) { + node.overrides.forEach(override => + overridesLibrary.register(override.id, override.overriddenFields) + ); + } + return { type: 'instance', name: node.name, @@ -51,7 +59,8 @@ export const transformInstanceNode = async ( ...transformRotationAndPosition(node, baseRotation), ...transformConstraints(node), ...transformAutoLayout(node), - ...(await transformChildren(node, node.rotation + baseRotation)) + ...(await transformChildren(node, node.rotation + baseRotation)), + ...transformOverrides(node) }; }; diff --git a/plugin-src/transformers/transformLineNode.ts b/plugin-src/transformers/transformLineNode.ts index aa8b653..260cc1a 100644 --- a/plugin-src/transformers/transformLineNode.ts +++ b/plugin-src/transformers/transformLineNode.ts @@ -4,6 +4,7 @@ import { transformEffects, transformFigmaIds, transformLayoutAttributes, + transformOverrides, transformProportion, transformSceneNode, transformStrokes @@ -30,6 +31,7 @@ export const transformLineNode = (node: LineNode, baseRotation: number): PathSha ...transformBlend(node), ...transformProportion(node), ...transformLayoutAttributes(node), - ...transformConstraints(node) + ...transformConstraints(node), + ...transformOverrides(node) }; }; diff --git a/plugin-src/transformers/transformPathNode.ts b/plugin-src/transformers/transformPathNode.ts index 21564ef..1eb7330 100644 --- a/plugin-src/transformers/transformPathNode.ts +++ b/plugin-src/transformers/transformPathNode.ts @@ -5,6 +5,7 @@ import { transformFigmaIds, transformFills, transformLayoutAttributes, + transformOverrides, transformProportion, transformSceneNode, transformStrokes @@ -29,6 +30,7 @@ export const transformPathNode = ( ...transformBlend(node), ...transformProportion(node), ...transformLayoutAttributes(node), - ...transformConstraints(node) + ...transformConstraints(node), + ...transformOverrides(node) }; }; diff --git a/plugin-src/transformers/transformRectangleNode.ts b/plugin-src/transformers/transformRectangleNode.ts index 74a4f88..604a582 100644 --- a/plugin-src/transformers/transformRectangleNode.ts +++ b/plugin-src/transformers/transformRectangleNode.ts @@ -7,6 +7,7 @@ import { transformFigmaIds, transformFills, transformLayoutAttributes, + transformOverrides, transformProportion, transformRotationAndPosition, transformSceneNode, @@ -30,6 +31,7 @@ export const transformRectangleNode = (node: RectangleNode, baseRotation: number ...transformProportion(node), ...transformLayoutAttributes(node), ...transformCornerRadius(node), - ...transformConstraints(node) + ...transformConstraints(node), + ...transformOverrides(node) }; }; diff --git a/plugin-src/transformers/transformTextNode.ts b/plugin-src/transformers/transformTextNode.ts index 3030772..727992b 100644 --- a/plugin-src/transformers/transformTextNode.ts +++ b/plugin-src/transformers/transformTextNode.ts @@ -5,6 +5,7 @@ import { transformEffects, transformFigmaIds, transformLayoutAttributes, + transformOverrides, transformProportion, transformRotationAndPosition, transformSceneNode, @@ -28,6 +29,7 @@ export const transformTextNode = (node: TextNode, baseRotation: number): TextSha ...transformProportion(node), ...transformLayoutAttributes(node), ...transformStrokes(node), - ...transformConstraints(node) + ...transformConstraints(node), + ...transformOverrides(node) }; }; diff --git a/plugin-src/transformers/transformVectorNode.ts b/plugin-src/transformers/transformVectorNode.ts index 61f839b..02e0e28 100644 --- a/plugin-src/transformers/transformVectorNode.ts +++ b/plugin-src/transformers/transformVectorNode.ts @@ -1,6 +1,7 @@ import { transformConstraints, transformFigmaIds, + transformOverrides, transformVectorPaths } from '@plugin/transformers/partials'; @@ -26,7 +27,8 @@ export const transformVectorNode = ( ...children[0], name: node.name, ...transformFigmaIds(node), - ...transformConstraints(node) + ...transformConstraints(node), + ...transformOverrides(node) }; } @@ -34,6 +36,7 @@ export const transformVectorNode = ( ...transformGroupNodeLike(node, baseRotation), ...transformFigmaIds(node), ...transformConstraints(node), + ...transformOverrides(node), children }; }; diff --git a/plugin-src/utils/syncAttributes.ts b/plugin-src/utils/syncAttributes.ts new file mode 100644 index 0000000..7a4e99f --- /dev/null +++ b/plugin-src/utils/syncAttributes.ts @@ -0,0 +1,150 @@ +import { SyncGroups } from '@ui/lib/types/utils/syncGroups'; + +export type SyncAttributes = { + [key in NodeChangeProperty]: SyncGroups[]; +}; + +export const syncAttributes: SyncAttributes = { + name: [':name-group'], + fills: [':fill-group'], + backgrounds: [':fill-group'], + fillStyleId: [':fill-group'], + backgroundStyleId: [':fill-group'], + textBackground: [':fill-group'], + visible: [':visibility-group'], + locked: [':modifiable-group'], + fontName: [':text-font-group', ':content-group'], + fontSize: [':text-font-group', ':content-group'], + textCase: [':text-font-group', ':content-group'], + textDecoration: [':text-font-group', ':content-group'], + textStyleId: [':text-font-group', ':content-group'], + characters: [':text-display-group', ':content-group'], + styledTextSegments: [':text-display-group', ':content-group'], + lineHeight: [':text-display-group', ':content-group'], + leadingTrim: [':text-display-group', ':content-group'], + paragraphIndent: [':text-display-group', ':content-group'], + paragraphSpacing: [':text-display-group', ':content-group'], + listSpacing: [':text-display-group', ':content-group'], + hangingPunctuation: [':text-display-group', ':content-group'], + hangingList: [':text-display-group', ':content-group'], + letterSpacing: [':text-display-group', ':content-group'], + textAlignHorizontal: [':text-display-group', ':content-group'], + textAlignVertical: [':text-display-group', ':content-group'], + textAutoResize: [':text-display-group', ':content-group'], + text: [':text-display-group', ':content-group'], + strokes: [':stroke-group'], + strokeWeight: [':stroke-group'], + strokeAlign: [':stroke-group'], + strokeCap: [':stroke-group'], + strokeJoin: [':stroke-group'], + strokeMiterLimit: [':stroke-group'], + dashPattern: [':stroke-group'], + strokeStyleId: [':stroke-group'], + stokeTopWeight: [':stroke-group'], + strokeBottomWeight: [':stroke-group'], + strokeLeftWeight: [':stroke-group'], + strokeRightWeight: [':stroke-group'], + connectorStartStrokeCap: [':stroke-group'], + connectorEndStrokeCap: [':stroke-group'], + innerRadius: [':radius-group'], + topLeftRadius: [':radius-group'], + topRightRadius: [':radius-group'], + bottomLeftRadius: [':radius-group'], + bottomRightRadius: [':radius-group'], + cornerRadius: [':radius-group'], + cornerSmoothing: [':radius-group'], + vectorNetwork: [':geometry-group'], + pointCount: [':geometry-group'], + width: [':geometry-group'], + height: [':geometry-group'], + guides: [':geometry-group'], + arcData: [':geometry-group'], + constrainProportions: [':geometry-group'], + handleMirroring: [':geometry-group'], + relativeTransform: [':geometry-group'], + x: [':geometry-group'], + y: [':geometry-group'], + rotation: [':geometry-group'], + type: [':geometry-group'], + shapeType: [':geometry-group'], + connectorStart: [':geometry-group'], + connectorEnd: [':geometry-group'], + connectorLineType: [':geometry-group'], + opacity: [':layer-effects-group'], + blendMode: [':layer-effects-group'], + effects: [':shadow-group', ':blur-group'], + effectStyleId: [':shadow-group', ':blur-group'], + isMask: [':mask-group'], + clipsContent: [':mask-group'], + maskType: [':mask-group'], + constraints: [':constraints-group'], + booleanOperation: [':bool-group'], + exportSettings: [':exports-group'], + gridStyleId: [':grids-group'], + layoutMode: [':layout-container', ':layout-flex-dir'], + layoutAlign: [':layout-align-content', ':layout-align-items'], + itemSpacing: [':layout-gap'], + paddingLeft: [':layout-padding'], + paddingTop: [':layout-padding'], + paddingRight: [':layout-padding'], + paddingBottom: [':layout-padding'], + layoutGrids: [ + ':layout-grid-cells', + ':layout-grid-columns', + ':layout-grid-dir', + ':layout-grid-rows' + ], + layoutWrap: [':layout-wrap-type'], + overflowDirection: [':layout-item-align-self'], + counterAxisSizingMode: [':layout-item-h-sizing'], + primaryAxisSizingMode: [':layout-item-v-sizing'], + primaryAxisAlignItems: [ + ':layout-item-align-self', + ':layout-justify-items', + ':layout-justify-content', + ':layout-gap' + ], + counterAxisAlignItems: [ + ':layout-item-align-self', + ':layout-align-content', + ':layout-align-items' + ], + layoutGrow: [':layout-item-h-sizing'], + layoutPositioning: [':layout-item-absolute'], + itemReverseZIndex: [':layout-item-z-index'], + + // @TODO: not supported yet + textTruncation: [], + minWidth: [], + minHeight: [], + maxWidth: [], + maxLines: [], + maxHeight: [], + counterAxisSpacing: [], + counterAxisAlignContent: [], + openTypeFeatures: [], + authorVisible: [], + parent: [], + pluginData: [], + autoRename: [], + overlayPositionType: [], + overlayBackgroundInteraction: [], + overlayBackground: [], + prototypeStartNode: [], + prototypeBackgrounds: [], + expanded: [], + description: [], + hyperlink: [], + mediaData: [], + reactions: [], + flowStartingPoints: [], + codeLanguage: [], + widgetSyncedState: [], + componentPropertyDefinitions: [], + componentPropertyReferences: [], + componentProperties: [], + embedData: [], + linkUnfurlData: [], + authorName: [], + code: [] +}; diff --git a/ui-src/lib/types/shapes/shape.ts b/ui-src/lib/types/shapes/shape.ts index 51bb6c5..2f3d7fe 100644 --- a/ui-src/lib/types/shapes/shape.ts +++ b/ui-src/lib/types/shapes/shape.ts @@ -9,6 +9,7 @@ import { Point } from '@ui/lib/types/utils/point'; import { Selrect } from '@ui/lib/types/utils/selrect'; import { Shadow } from '@ui/lib/types/utils/shadow'; import { Stroke } from '@ui/lib/types/utils/stroke'; +import { SyncGroups } from '@ui/lib/types/utils/syncGroups'; import { Uuid } from '@ui/lib/types/utils/uuid'; export type ShapeBaseAttributes = { @@ -74,6 +75,7 @@ export type ShapeAttributes = { shadow?: Shadow[]; blur?: Blur; growType?: GrowType; + touched?: SyncGroups[]; }; export type ShapeGeomAttributes = { diff --git a/ui-src/lib/types/utils/syncGroups.ts b/ui-src/lib/types/utils/syncGroups.ts new file mode 100644 index 0000000..6fce1e3 --- /dev/null +++ b/ui-src/lib/types/utils/syncGroups.ts @@ -0,0 +1,42 @@ +export type SyncGroups = + | ':name-group' + | ':fill-group' + | ':content-group' + | ':visibility-group' + | ':modifiable-group' + | ':text-font-group' + | ':text-display-group' + | ':stroke-group' + | ':radius-group' + | ':geometry-group' + | ':layer-effects-group' + | ':shadow-group' + | ':blur-group' + | ':mask-group' + | ':constraints-group' + | ':bool-group' + | ':exports-group' + | ':grids-group' + | ':layout-container' + | ':layout-align-content' + | ':layout-align-items' + | ':layout-flex-dir' + | ':layout-gap' + | ':layout-justify-content' + | ':layout-justify-items' + | ':layout-wrap-type' + | ':layout-padding' + | ':layout-grid-dir' + | ':layout-grid-rows' + | ':layout-grid-columns' + | ':layout-grid-cells' + | ':layout-item-margin' + | ':layout-item-h-sizing' + | ':layout-item-v-sizing' + | ':layout-item-max-h' + | ':layout-item-min-h' + | ':layout-item-max-w' + | ':layout-item-min-w' + | ':layout-item-absolute' + | ':layout-item-z-index' + | ':layout-item-align-self';