From 06d56ad3c46274d21d1fd0b68dc76be4f90062d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20S=C3=A1nchez?= Date: Fri, 21 Jun 2024 12:09:54 +0200 Subject: [PATCH 01/14] wip --- plugin-src/OverridesLibrary.ts | 2 +- plugin-src/transformers/transformInstanceNode.ts | 7 +++++++ plugin-src/utils/syncAttributes.ts | 2 +- ui-src/parser/creators/createComponentInstance.ts | 13 ++++--------- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/plugin-src/OverridesLibrary.ts b/plugin-src/OverridesLibrary.ts index c633d327..6437e332 100644 --- a/plugin-src/OverridesLibrary.ts +++ b/plugin-src/OverridesLibrary.ts @@ -2,7 +2,7 @@ class OverridesLibrary { private overrides: Map = new Map(); public register(nodeId: string, overrides: NodeChangeProperty[]): void { - this.overrides.set(nodeId, overrides); + this.overrides.set(nodeId, [...(this.overrides.get(nodeId) ?? []), ...overrides]); } public get(nodeId: string): NodeChangeProperty[] | undefined { diff --git a/plugin-src/transformers/transformInstanceNode.ts b/plugin-src/transformers/transformInstanceNode.ts index 2843d5c2..14c0727f 100644 --- a/plugin-src/transformers/transformInstanceNode.ts +++ b/plugin-src/transformers/transformInstanceNode.ts @@ -45,6 +45,13 @@ export const transformInstanceNode = async ( ); } + if (!node.visible) { + overridesLibrary.register(node.id, ['visible']); + } + if (node.locked) { + overridesLibrary.register(node.id, ['locked']); + } + return { type: 'instance', name: node.name, diff --git a/plugin-src/utils/syncAttributes.ts b/plugin-src/utils/syncAttributes.ts index 7a4e99fe..113374f5 100644 --- a/plugin-src/utils/syncAttributes.ts +++ b/plugin-src/utils/syncAttributes.ts @@ -118,8 +118,8 @@ export const syncAttributes: SyncAttributes = { minWidth: [], minHeight: [], maxWidth: [], - maxLines: [], maxHeight: [], + maxLines: [], counterAxisSpacing: [], counterAxisAlignContent: [], openTypeFeatures: [], diff --git a/ui-src/parser/creators/createComponentInstance.ts b/ui-src/parser/creators/createComponentInstance.ts index 44bcf9a7..feb39355 100644 --- a/ui-src/parser/creators/createComponentInstance.ts +++ b/ui-src/parser/creators/createComponentInstance.ts @@ -7,14 +7,7 @@ import { createArtboard } from '.'; export const createComponentInstance = ( file: PenpotFile, - { - type, - mainComponentFigmaId, - figmaId, - figmaRelatedId, - isComponentRoot, - ...shape - }: ComponentInstance + { type, mainComponentFigmaId, isComponentRoot, ...shape }: ComponentInstance ) => { const uiComponent = uiComponents.get(mainComponentFigmaId) ?? createUiComponent(file, mainComponentFigmaId); @@ -23,7 +16,9 @@ export const createComponentInstance = ( return; } - shape.shapeRef = uiComponent.mainInstanceId; + if (!shape.figmaRelatedId) { + shape.shapeRef = uiComponent.mainInstanceId; + } shape.componentFile = file.getId(); shape.componentRoot = isComponentRoot; shape.componentId = uiComponent.componentId; From b4e81a65c1f8464caac0797d68f3b86b93b7b6c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20S=C3=A1nchez?= Date: Fri, 21 Jun 2024 14:29:29 +0200 Subject: [PATCH 02/14] wip --- plugin-src/ComponentPropertiesLibrary.ts | 35 +++++++++++ .../partials/transformOverrides.ts | 23 +++---- .../transformers/partials/transformText.ts | 1 + .../transformers/transformComponentNode.ts | 13 ++++ .../transformers/transformDocumentNode.ts | 6 +- plugin-src/transformers/transformFrameNode.ts | 15 +++++ .../transformers/transformInstanceNode.ts | 60 ------------------- ui-src/lib/types/shapes/shape.ts | 2 + ui-src/lib/types/shapes/textShape.ts | 1 + ui-src/parser/creators/createArtboard.ts | 8 ++- ui-src/parser/creators/createBool.ts | 13 +++- ui-src/parser/creators/createCircle.ts | 8 ++- .../creators/createComponentInstance.ts | 7 +++ ui-src/parser/creators/createGroup.ts | 7 +++ ui-src/parser/creators/createPath.ts | 13 +++- ui-src/parser/creators/createRectangle.ts | 8 ++- ui-src/parser/creators/createText.ts | 10 +++- ui-src/parser/creators/symbols/index.ts | 1 + .../parser/creators/symbols/symbolTouched.ts | 32 ++++++++++ ui-src/parser/parse.ts | 5 +- ui-src/types/component.ts | 6 ++ ui-src/types/penpotDocument.ts | 3 + 22 files changed, 198 insertions(+), 79 deletions(-) create mode 100644 plugin-src/ComponentPropertiesLibrary.ts create mode 100644 ui-src/parser/creators/symbols/symbolTouched.ts diff --git a/plugin-src/ComponentPropertiesLibrary.ts b/plugin-src/ComponentPropertiesLibrary.ts new file mode 100644 index 00000000..5e5a1fa0 --- /dev/null +++ b/plugin-src/ComponentPropertiesLibrary.ts @@ -0,0 +1,35 @@ +export type ComponentProperty = { + type: ComponentPropertyType; + defaultValue: string | boolean; + preferredValues?: InstanceSwapPreferredValue[]; + variantOptions?: string[]; +}; +class ComponentPropertiesLibrary { + private properties: Map = new Map(); + + public register(id: string, property: ComponentProperty) { + this.properties.set(id, property); + } + + public registerAll(properties: Record) { + Object.entries(properties).forEach(([key, value]) => { + if (value.type === 'TEXT' || value.type === 'BOOLEAN') { + this.register(key, value); + } + }); + } + + public get(id: string): ComponentProperty | undefined { + return this.properties.get(id); + } + + public all(): Record { + return Object.fromEntries(this.properties.entries()); + } + + public init(properties: Record): void { + this.properties = new Map(Object.entries(properties)); + } +} + +export const componentPropertiesLibrary = new ComponentPropertiesLibrary(); diff --git a/plugin-src/transformers/partials/transformOverrides.ts b/plugin-src/transformers/partials/transformOverrides.ts index dd9cbd09..f1fb19d3 100644 --- a/plugin-src/transformers/partials/transformOverrides.ts +++ b/plugin-src/transformers/partials/transformOverrides.ts @@ -1,23 +1,26 @@ import { overridesLibrary } from '@plugin/OverridesLibrary'; import { syncAttributes } from '@plugin/utils/syncAttributes'; +import { ShapeAttributes } from '@ui/lib/types/shapes/shape'; import { SyncGroups } from '@ui/lib/types/utils/syncGroups'; -export const transformOverrides = (node: SceneNode) => { +export const transformOverrides = ( + node: SceneNode +): Pick => { const overrides = overridesLibrary.get(node.id); - if (!overrides) { - return {}; - } const touched: SyncGroups[] = []; - overrides.forEach(override => { - if (syncAttributes[override]) { - touched.push(...syncAttributes[override]); - } - }); + if (overrides) { + overrides.forEach(override => { + if (syncAttributes[override]) { + touched.push(...syncAttributes[override]); + } + }); + } return { - touched + touched, + componentPropertyReferences: node.componentPropertyReferences }; }; diff --git a/plugin-src/transformers/partials/transformText.ts b/plugin-src/transformers/partials/transformText.ts index 207945b4..622467d2 100644 --- a/plugin-src/transformers/partials/transformText.ts +++ b/plugin-src/transformers/partials/transformText.ts @@ -20,6 +20,7 @@ export const transformText = (node: TextNode): TextAttributes & Pick { + return node.parent?.type !== 'COMPONENT_SET'; +}; + export const transformComponentNode = async (node: ComponentNode): Promise => { componentsLibrary.register(node.id, { type: 'component', @@ -40,6 +45,14 @@ export const transformComponentNode = async (node: ComponentNode): Promise { + return node.type === 'COMPONENT_SET'; +}; + export const transformFrameNode = async ( node: FrameNode | SectionNode | ComponentSetNode ): Promise => { let frameSpecificAttributes: Partial = {}; let referencePoint: Point = { x: node.absoluteTransform[0][2], y: node.absoluteTransform[1][2] }; + if (isComponentSetNode(node)) { + try { + componentPropertiesLibrary.registerAll(node.componentPropertyDefinitions); + } catch (error) { + console.error('Error registering component properties', error); + } + } + if (!isSectionNode(node)) { const { x, y, ...transformAndRotation } = transformRotationAndPosition(node); diff --git a/plugin-src/transformers/transformInstanceNode.ts b/plugin-src/transformers/transformInstanceNode.ts index 14c0727f..a261a49f 100644 --- a/plugin-src/transformers/transformInstanceNode.ts +++ b/plugin-src/transformers/transformInstanceNode.ts @@ -37,21 +37,12 @@ export const transformInstanceNode = async ( registerExternalComponents(primaryComponent); } - registerTextVariableOverrides(node, primaryComponent); - if (node.overrides.length > 0) { node.overrides.forEach(override => overridesLibrary.register(override.id, override.overriddenFields) ); } - if (!node.visible) { - overridesLibrary.register(node.id, ['visible']); - } - if (node.locked) { - overridesLibrary.register(node.id, ['locked']); - } - return { type: 'instance', name: node.name, @@ -92,57 +83,6 @@ const registerExternalComponents = (primaryComponent: ComponentNode | ComponentS remoteComponentLibrary.register(primaryComponent.id, primaryComponent); }; -const getComponentTextPropertyOverrides = ( - node: InstanceNode, - primaryComponent: ComponentNode | ComponentSetNode -): ComponentTextPropertyOverride[] => { - try { - const componentPropertyDefinitions = Object.entries( - primaryComponent.componentPropertyDefinitions - ).filter(([, value]) => value.type === 'TEXT'); - - const instanceComponentProperties = new Map( - Object.entries(node.componentProperties).filter(([, value]) => value.type === 'TEXT') - ); - - return componentPropertyDefinitions - .map(([key, value]) => { - const nodeValue = instanceComponentProperties.get(key); - return { - id: key, - ...value, - value: nodeValue ? nodeValue.value : value.defaultValue - } as ComponentTextPropertyOverride; - }) - .filter(({ value, defaultValue }) => value !== defaultValue); - } catch (error) { - return []; - } -}; - -const registerTextVariableOverrides = ( - node: InstanceNode, - primaryComponent: ComponentNode | ComponentSetNode -) => { - const mergedOverridden = getComponentTextPropertyOverrides(node, primaryComponent); - - if (mergedOverridden.length > 0) { - const textNodes = node - .findChildren(child => child.type === 'TEXT') - .filter(textNode => { - const componentPropertyReference = textNode.componentPropertyReferences?.characters; - return ( - componentPropertyReference && - mergedOverridden.some(property => property.id === componentPropertyReference) - ); - }); - - textNodes.forEach(textNode => { - overridesLibrary.register(textNode.id, ['text']); - }); - } -}; - /** * We do not want to process component instances in the following scenarios: * diff --git a/ui-src/lib/types/shapes/shape.ts b/ui-src/lib/types/shapes/shape.ts index ed4ddcc0..351f0db5 100644 --- a/ui-src/lib/types/shapes/shape.ts +++ b/ui-src/lib/types/shapes/shape.ts @@ -11,6 +11,7 @@ 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'; +import { ComponentPropertyReference } from '@ui/types'; export type ShapeBaseAttributes = { id?: Uuid; @@ -74,6 +75,7 @@ export type ShapeAttributes = { blur?: Blur; growType?: GrowType; touched?: SyncGroups[]; + componentPropertyReferences?: ComponentPropertyReference; // @TODO: move to any other place }; export type ShapeGeomAttributes = { diff --git a/ui-src/lib/types/shapes/textShape.ts b/ui-src/lib/types/shapes/textShape.ts index c6f02b2f..90d73cf8 100644 --- a/ui-src/lib/types/shapes/textShape.ts +++ b/ui-src/lib/types/shapes/textShape.ts @@ -15,6 +15,7 @@ export type TextShape = ShapeBaseAttributes & export type TextAttributes = { type?: 'text'; content?: TextContent; + characters?: string; // @ TODO: move to any other place }; export type TextContent = { diff --git a/ui-src/parser/creators/createArtboard.ts b/ui-src/parser/creators/createArtboard.ts index ff714b98..3ce18895 100644 --- a/ui-src/parser/creators/createArtboard.ts +++ b/ui-src/parser/creators/createArtboard.ts @@ -2,7 +2,7 @@ import { PenpotFile } from '@ui/lib/types/penpotFile'; import { FrameShape } from '@ui/lib/types/shapes/frameShape'; import { Uuid } from '@ui/lib/types/utils/uuid'; import { parseFigmaId } from '@ui/parser'; -import { symbolFills, symbolStrokes } from '@ui/parser/creators/symbols'; +import { symbolFills, symbolStrokes, symbolTouched } from '@ui/parser/creators/symbols'; import { createItems } from '.'; @@ -16,6 +16,12 @@ export const createArtboard = ( shape.shapeRef ??= parseFigmaId(file, figmaRelatedId, true); shape.fills = symbolFills(shape.fillStyleId, shape.fills); shape.strokes = symbolStrokes(shape.strokes); + shape.touched = symbolTouched( + !shape.hidden, + undefined, + shape.touched, + shape.componentPropertyReferences + ); file.addArtboard(shape); diff --git a/ui-src/parser/creators/createBool.ts b/ui-src/parser/creators/createBool.ts index 2ff61995..f93a8dc1 100644 --- a/ui-src/parser/creators/createBool.ts +++ b/ui-src/parser/creators/createBool.ts @@ -1,7 +1,12 @@ import { PenpotFile } from '@ui/lib/types/penpotFile'; import { BoolShape } from '@ui/lib/types/shapes/boolShape'; import { parseFigmaId } from '@ui/parser'; -import { symbolBoolType, symbolFills, symbolStrokes } from '@ui/parser/creators/symbols'; +import { + symbolBoolType, + symbolFills, + symbolStrokes, + symbolTouched +} from '@ui/parser/creators/symbols'; import { createItems } from '.'; @@ -14,6 +19,12 @@ export const createBool = ( shape.fills = symbolFills(shape.fillStyleId, shape.fills); shape.strokes = symbolStrokes(shape.strokes); shape.boolType = symbolBoolType(shape.boolType); + shape.touched = symbolTouched( + !shape.hidden, + undefined, + shape.touched, + shape.componentPropertyReferences + ); file.addBool(shape); diff --git a/ui-src/parser/creators/createCircle.ts b/ui-src/parser/creators/createCircle.ts index 2fea9018..f8a18351 100644 --- a/ui-src/parser/creators/createCircle.ts +++ b/ui-src/parser/creators/createCircle.ts @@ -1,7 +1,7 @@ import { PenpotFile } from '@ui/lib/types/penpotFile'; import { CircleShape } from '@ui/lib/types/shapes/circleShape'; import { parseFigmaId } from '@ui/parser'; -import { symbolFills, symbolStrokes } from '@ui/parser/creators/symbols'; +import { symbolFills, symbolStrokes, symbolTouched } from '@ui/parser/creators/symbols'; export const createCircle = ( file: PenpotFile, @@ -11,6 +11,12 @@ export const createCircle = ( shape.shapeRef = parseFigmaId(file, figmaRelatedId, true); shape.fills = symbolFills(shape.fillStyleId, shape.fills); shape.strokes = symbolStrokes(shape.strokes); + shape.touched = symbolTouched( + !shape.hidden, + undefined, + shape.touched, + shape.componentPropertyReferences + ); file.createCircle(shape); }; diff --git a/ui-src/parser/creators/createComponentInstance.ts b/ui-src/parser/creators/createComponentInstance.ts index feb39355..0ddbe651 100644 --- a/ui-src/parser/creators/createComponentInstance.ts +++ b/ui-src/parser/creators/createComponentInstance.ts @@ -1,5 +1,6 @@ import { PenpotFile } from '@ui/lib/types/penpotFile'; import { parseFigmaId } from '@ui/parser'; +import { symbolTouched } from '@ui/parser/creators/symbols'; import { uiComponents } from '@ui/parser/libraries'; import { ComponentInstance } from '@ui/types'; @@ -22,6 +23,12 @@ export const createComponentInstance = ( shape.componentFile = file.getId(); shape.componentRoot = isComponentRoot; shape.componentId = uiComponent.componentId; + shape.touched = symbolTouched( + !shape.hidden, + undefined, + shape.touched, + shape.componentPropertyReferences + ); createArtboard(file, shape); }; diff --git a/ui-src/parser/creators/createGroup.ts b/ui-src/parser/creators/createGroup.ts index 4ac4a16c..9b0b5f63 100644 --- a/ui-src/parser/creators/createGroup.ts +++ b/ui-src/parser/creators/createGroup.ts @@ -1,6 +1,7 @@ import { PenpotFile } from '@ui/lib/types/penpotFile'; import { GroupShape } from '@ui/lib/types/shapes/groupShape'; import { parseFigmaId } from '@ui/parser'; +import { symbolTouched } from '@ui/parser/creators/symbols'; import { createItems } from '.'; @@ -10,6 +11,12 @@ export const createGroup = ( ) => { shape.id = parseFigmaId(file, figmaId); shape.shapeRef = parseFigmaId(file, figmaRelatedId, true); + shape.touched = symbolTouched( + !shape.hidden, + undefined, + shape.touched, + shape.componentPropertyReferences + ); file.addGroup(shape); diff --git a/ui-src/parser/creators/createPath.ts b/ui-src/parser/creators/createPath.ts index f5848f0f..e025f079 100644 --- a/ui-src/parser/creators/createPath.ts +++ b/ui-src/parser/creators/createPath.ts @@ -1,7 +1,12 @@ import { PenpotFile } from '@ui/lib/types/penpotFile'; import { PathShape } from '@ui/lib/types/shapes/pathShape'; import { parseFigmaId } from '@ui/parser'; -import { symbolFills, symbolPathContent, symbolStrokes } from '@ui/parser/creators/symbols'; +import { + symbolFills, + symbolPathContent, + symbolStrokes, + symbolTouched +} from '@ui/parser/creators/symbols'; export const createPath = ( file: PenpotFile, @@ -12,6 +17,12 @@ export const createPath = ( shape.fills = symbolFills(shape.fillStyleId, shape.fills); shape.strokes = symbolStrokes(shape.strokes); shape.content = symbolPathContent(shape.content); + shape.touched = symbolTouched( + !shape.hidden, + undefined, + shape.touched, + shape.componentPropertyReferences + ); file.createPath(shape); }; diff --git a/ui-src/parser/creators/createRectangle.ts b/ui-src/parser/creators/createRectangle.ts index f4bbb865..825420bf 100644 --- a/ui-src/parser/creators/createRectangle.ts +++ b/ui-src/parser/creators/createRectangle.ts @@ -1,7 +1,7 @@ import { PenpotFile } from '@ui/lib/types/penpotFile'; import { RectShape } from '@ui/lib/types/shapes/rectShape'; import { parseFigmaId } from '@ui/parser'; -import { symbolFills, symbolStrokes } from '@ui/parser/creators/symbols'; +import { symbolFills, symbolStrokes, symbolTouched } from '@ui/parser/creators/symbols'; export const createRectangle = ( file: PenpotFile, @@ -11,6 +11,12 @@ export const createRectangle = ( shape.shapeRef = parseFigmaId(file, figmaRelatedId, true); shape.fills = symbolFills(shape.fillStyleId, shape.fills); shape.strokes = symbolStrokes(shape.strokes); + shape.touched = symbolTouched( + !shape.hidden, + undefined, + shape.touched, + shape.componentPropertyReferences + ); file.createRect(shape); }; diff --git a/ui-src/parser/creators/createText.ts b/ui-src/parser/creators/createText.ts index cba45013..2626b340 100644 --- a/ui-src/parser/creators/createText.ts +++ b/ui-src/parser/creators/createText.ts @@ -1,16 +1,22 @@ import { PenpotFile } from '@ui/lib/types/penpotFile'; import { TextContent, TextShape } from '@ui/lib/types/shapes/textShape'; import { parseFigmaId } from '@ui/parser'; -import { symbolFills, symbolStrokes } from '@ui/parser/creators/symbols'; +import { symbolFills, symbolStrokes, symbolTouched } from '@ui/parser/creators/symbols'; export const createText = ( file: PenpotFile, - { type, figmaId, figmaRelatedId, ...shape }: TextShape + { type, figmaId, figmaRelatedId, characters, ...shape }: TextShape ) => { shape.id = parseFigmaId(file, figmaId); shape.shapeRef = parseFigmaId(file, figmaRelatedId, true); shape.content = parseContent(shape.content); shape.strokes = symbolStrokes(shape.strokes); + shape.touched = symbolTouched( + !shape.hidden, + characters, + shape.touched, + shape.componentPropertyReferences + ); file.createText(shape); }; diff --git a/ui-src/parser/creators/symbols/index.ts b/ui-src/parser/creators/symbols/index.ts index 08f1e0fa..dd382291 100644 --- a/ui-src/parser/creators/symbols/index.ts +++ b/ui-src/parser/creators/symbols/index.ts @@ -2,3 +2,4 @@ export * from './symbolBoolType'; export * from './symbolFills'; export * from './symbolPathContent'; export * from './symbolStrokes'; +export * from './symbolTouched'; diff --git a/ui-src/parser/creators/symbols/symbolTouched.ts b/ui-src/parser/creators/symbols/symbolTouched.ts new file mode 100644 index 00000000..72523c9f --- /dev/null +++ b/ui-src/parser/creators/symbols/symbolTouched.ts @@ -0,0 +1,32 @@ +import { componentPropertiesLibrary } from '@plugin/ComponentPropertiesLibrary'; + +import { SyncGroups } from '@ui/lib/types/utils/syncGroups'; +import { ComponentPropertyReference } from '@ui/types'; + +export const symbolTouched = ( + visible: boolean | undefined, + characters: string | undefined, + touched: SyncGroups[] | undefined, + componentPropertyReferences: ComponentPropertyReference | undefined +): SyncGroups[] | undefined => { + if (componentPropertyReferences) { + Object.entries(componentPropertyReferences).forEach(([key, value]) => { + switch (key) { + case 'visible': + if (visible !== componentPropertiesLibrary.get(value)?.defaultValue) { + touched?.push(':visibility-group'); + } + break; + case 'characters': + if (characters !== componentPropertiesLibrary.get(value)?.defaultValue) { + touched?.push(':content-group'); + } + break; + default: + break; + } + }); + } + + return touched; +}; diff --git a/ui-src/parser/parse.ts b/ui-src/parser/parse.ts index 4413738c..b26fec03 100644 --- a/ui-src/parser/parse.ts +++ b/ui-src/parser/parse.ts @@ -1,4 +1,5 @@ import { componentsLibrary } from '@plugin/ComponentLibrary'; +import { componentPropertiesLibrary } from '@plugin/ComponentPropertiesLibrary'; import { styleLibrary } from '@plugin/StyleLibrary'; // @TODO: Direct import on purpose, to avoid problems with the tsc linting import { sleep } from '@plugin/utils/sleep'; @@ -46,10 +47,12 @@ export const parse = async ({ children = [], components, images, - styles + styles, + componentProperties }: PenpotDocument) => { componentsLibrary.init(components); styleLibrary.init(styles); + componentPropertiesLibrary.init(componentProperties); await optimizeImages(images); diff --git a/ui-src/types/component.ts b/ui-src/types/component.ts index ed1f50a3..7f4db084 100644 --- a/ui-src/types/component.ts +++ b/ui-src/types/component.ts @@ -27,3 +27,9 @@ export type ComponentInstance = ShapeGeomAttributes & showContent?: boolean; type: 'instance'; }; + +export type ComponentPropertyReference = + | { + [nodeProperty in 'visible' | 'characters' | 'mainComponent']?: string; + } + | null; diff --git a/ui-src/types/penpotDocument.ts b/ui-src/types/penpotDocument.ts index b0cbadb3..ab1e84b1 100644 --- a/ui-src/types/penpotDocument.ts +++ b/ui-src/types/penpotDocument.ts @@ -1,3 +1,5 @@ +import { ComponentProperty } from '@plugin/ComponentPropertiesLibrary'; + import { PenpotPage } from '@ui/lib/types/penpotPage'; import { ComponentShape } from '@ui/lib/types/shapes/componentShape'; import { Fill } from '@ui/lib/types/utils/fill'; @@ -8,4 +10,5 @@ export type PenpotDocument = { components: Record; images: Record; styles: Record; + componentProperties: Record; }; From 25b93059f2a6e9bc73b4e01e9ff256bb19ee7321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20S=C3=A1nchez?= Date: Fri, 21 Jun 2024 14:32:36 +0200 Subject: [PATCH 03/14] wip --- plugin-src/transformers/transformInstanceNode.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin-src/transformers/transformInstanceNode.ts b/plugin-src/transformers/transformInstanceNode.ts index a261a49f..8d992684 100644 --- a/plugin-src/transformers/transformInstanceNode.ts +++ b/plugin-src/transformers/transformInstanceNode.ts @@ -18,7 +18,7 @@ import { transformStrokes } from '@plugin/transformers/partials'; -import { ComponentInstance, ComponentTextPropertyOverride } from '@ui/types'; +import { ComponentInstance } from '@ui/types'; export const transformInstanceNode = async ( node: InstanceNode From b2dd23d6afe8114ba5166ba3b29f05b3844758a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20S=C3=A1nchez?= Date: Mon, 24 Jun 2024 08:43:06 +0200 Subject: [PATCH 04/14] wip --- plugin-src/ComponentPropertiesLibrary.ts | 7 +++++-- plugin-src/transformers/transformInstanceNode.ts | 10 ++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/plugin-src/ComponentPropertiesLibrary.ts b/plugin-src/ComponentPropertiesLibrary.ts index 5e5a1fa0..6f593817 100644 --- a/plugin-src/ComponentPropertiesLibrary.ts +++ b/plugin-src/ComponentPropertiesLibrary.ts @@ -1,7 +1,10 @@ export type ComponentProperty = { - type: ComponentPropertyType; + type: 'BOOLEAN' | 'TEXT' | 'INSTANCE_SWAP' | 'VARIANT'; defaultValue: string | boolean; - preferredValues?: InstanceSwapPreferredValue[]; + preferredValues?: { + type: 'COMPONENT' | 'COMPONENT_SET'; + key: string; + }[]; variantOptions?: string[]; }; class ComponentPropertiesLibrary { diff --git a/plugin-src/transformers/transformInstanceNode.ts b/plugin-src/transformers/transformInstanceNode.ts index 8d992684..e6049b2f 100644 --- a/plugin-src/transformers/transformInstanceNode.ts +++ b/plugin-src/transformers/transformInstanceNode.ts @@ -43,6 +43,16 @@ export const transformInstanceNode = async ( ); } + if (node.visible !== mainComponent.visible) { + overridesLibrary.register(node.id, ['visible']); + } + if (node.locked !== mainComponent.locked) { + overridesLibrary.register(node.id, ['locked']); + } + if (node.id === 'I1:360;304:2177') { + console.log(node, mainComponent); + } + return { type: 'instance', name: node.name, From c2a718aa5c419b244f74119bbdb574d8c4ea062c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20S=C3=A1nchez?= Date: Mon, 24 Jun 2024 08:43:14 +0200 Subject: [PATCH 05/14] wip --- plugin-src/transformers/transformInstanceNode.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugin-src/transformers/transformInstanceNode.ts b/plugin-src/transformers/transformInstanceNode.ts index e6049b2f..38efc6d9 100644 --- a/plugin-src/transformers/transformInstanceNode.ts +++ b/plugin-src/transformers/transformInstanceNode.ts @@ -49,9 +49,6 @@ export const transformInstanceNode = async ( if (node.locked !== mainComponent.locked) { overridesLibrary.register(node.id, ['locked']); } - if (node.id === 'I1:360;304:2177') { - console.log(node, mainComponent); - } return { type: 'instance', From 17a8dc8df402ea8607ed7d502f6a5d80fb2e4267 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20S=C3=A1nchez?= Date: Thu, 27 Jun 2024 08:24:50 +0200 Subject: [PATCH 06/14] fixes --- .changeset/shy-plants-argue.md | 5 ++++ plugin-src/ComponentPropertiesLibrary.ts | 29 ------------------- plugin-src/libraries.ts | 6 ++-- .../transformers/transformComponentNode.ts | 6 +++- .../transformers/transformDocumentNode.ts | 2 +- plugin-src/transformers/transformFrameNode.ts | 6 +++- 6 files changed, 18 insertions(+), 36 deletions(-) create mode 100644 .changeset/shy-plants-argue.md delete mode 100644 plugin-src/ComponentPropertiesLibrary.ts diff --git a/.changeset/shy-plants-argue.md b/.changeset/shy-plants-argue.md new file mode 100644 index 00000000..6a67ee5c --- /dev/null +++ b/.changeset/shy-plants-argue.md @@ -0,0 +1,5 @@ +--- +"penpot-exporter": minor +--- + +Improvements in overrides management diff --git a/plugin-src/ComponentPropertiesLibrary.ts b/plugin-src/ComponentPropertiesLibrary.ts deleted file mode 100644 index a8b5632a..00000000 --- a/plugin-src/ComponentPropertiesLibrary.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { ComponentProperty } from '@ui/types'; - -export class ComponentPropertiesLibrary { - private properties: Map = new Map(); - - public register(id: string, property: ComponentProperty) { - this.properties.set(id, property); - } - - public registerAll(properties: Record) { - Object.entries(properties).forEach(([key, value]) => { - if (value.type === 'TEXT' || value.type === 'BOOLEAN') { - this.register(key, value); - } - }); - } - - public get(id: string): ComponentProperty | undefined { - return this.properties.get(id); - } - - public all(): Record { - return Object.fromEntries(this.properties.entries()); - } - - public init(properties: Record): void { - this.properties = new Map(Object.entries(properties)); - } -} diff --git a/plugin-src/libraries.ts b/plugin-src/libraries.ts index a0d5e1fa..07714633 100644 --- a/plugin-src/libraries.ts +++ b/plugin-src/libraries.ts @@ -1,6 +1,5 @@ -import { ComponentPropertiesLibrary } from '@plugin/ComponentPropertiesLibrary'; - import { ComponentShape } from '@ui/lib/types/shapes/componentShape'; +import { ComponentProperty } from '@ui/types'; import { RemoteComponentsLibrary } from './RemoteComponents'; @@ -9,6 +8,5 @@ export const paintStyles: Map = new Map(); export const overrides: Map = new Map(); export const images: Map = new Map(); export const components: Map = new Map(); +export const componentProperties: Map = new Map(); export const remoteComponents = new RemoteComponentsLibrary(); - -export const componentProperties = new ComponentPropertiesLibrary(); diff --git a/plugin-src/transformers/transformComponentNode.ts b/plugin-src/transformers/transformComponentNode.ts index 251130e4..2a7886fa 100644 --- a/plugin-src/transformers/transformComponentNode.ts +++ b/plugin-src/transformers/transformComponentNode.ts @@ -46,7 +46,11 @@ export const transformComponentNode = async (node: ComponentNode): Promise { + if (value.type === 'TEXT' || value.type === 'BOOLEAN') { + componentProperties.set(key, value); + } + }); } catch (error) { console.error('Error registering component properties', error); } diff --git a/plugin-src/transformers/transformDocumentNode.ts b/plugin-src/transformers/transformDocumentNode.ts index bdfb7c4f..91337010 100644 --- a/plugin-src/transformers/transformDocumentNode.ts +++ b/plugin-src/transformers/transformDocumentNode.ts @@ -20,7 +20,7 @@ export const transformDocumentNode = async (node: DocumentNode): Promise { + if (value.type === 'TEXT' || value.type === 'BOOLEAN') { + componentProperties.set(key, value); + } + }); } catch (error) { console.error('Error registering component properties', error); } From 31e018b747b3cc314105a12457e9513f91d30cb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20S=C3=A1nchez?= Date: Thu, 27 Jun 2024 08:27:34 +0200 Subject: [PATCH 07/14] fixes --- .../parser/creators/symbols/symbolTouched.ts | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/ui-src/parser/creators/symbols/symbolTouched.ts b/ui-src/parser/creators/symbols/symbolTouched.ts index fa503917..9bda1f52 100644 --- a/ui-src/parser/creators/symbols/symbolTouched.ts +++ b/ui-src/parser/creators/symbols/symbolTouched.ts @@ -8,24 +8,26 @@ export const symbolTouched = ( touched: SyncGroups[] | undefined, componentPropertyReferences: ComponentPropertyReference | undefined ): SyncGroups[] | undefined => { - if (componentPropertyReferences) { - Object.entries(componentPropertyReferences).forEach(([key, value]) => { - switch (key) { - case 'visible': - if (visible !== componentProperties.get(value)?.defaultValue) { - touched?.push(':visibility-group'); - } - break; - case 'characters': - if (characters !== componentProperties.get(value)?.defaultValue) { - touched?.push(':content-group'); - } - break; - default: - break; - } - }); + if (!componentPropertyReferences) { + return touched; } + Object.entries(componentPropertyReferences).forEach(([key, value]) => { + switch (key) { + case 'visible': + if (visible !== componentProperties.get(value)?.defaultValue) { + touched?.push(':visibility-group'); + } + break; + case 'characters': + if (characters !== componentProperties.get(value)?.defaultValue) { + touched?.push(':content-group'); + } + break; + default: + break; + } + }); + return touched; }; From 7067ad9cfc39c94618819254bee6a0f617911f26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20S=C3=A1nchez?= Date: Thu, 27 Jun 2024 08:30:24 +0200 Subject: [PATCH 08/14] fixes --- plugin-src/transformers/transformDocumentNode.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugin-src/transformers/transformDocumentNode.ts b/plugin-src/transformers/transformDocumentNode.ts index 91337010..d862751a 100644 --- a/plugin-src/transformers/transformDocumentNode.ts +++ b/plugin-src/transformers/transformDocumentNode.ts @@ -20,7 +20,6 @@ export const transformDocumentNode = async (node: DocumentNode): Promise Date: Fri, 28 Jun 2024 11:54:02 +0200 Subject: [PATCH 09/14] fixes --- .../partials/transformOverrides.ts | 19 ++--------- .../transformers/transformDocumentNode.ts | 4 +-- plugin-src/translators/index.ts | 1 + .../translateTouched.ts} | 22 ++++++++++-- .../parser/creators/symbols/symbolTouched.ts | 34 +++++++++++-------- 5 files changed, 45 insertions(+), 35 deletions(-) rename plugin-src/{utils/syncAttributes.ts => translators/translateTouched.ts} (90%) diff --git a/plugin-src/transformers/partials/transformOverrides.ts b/plugin-src/transformers/partials/transformOverrides.ts index 25d4af7e..c6a54b18 100644 --- a/plugin-src/transformers/partials/transformOverrides.ts +++ b/plugin-src/transformers/partials/transformOverrides.ts @@ -1,26 +1,13 @@ -import { overrides as overridesLibrary } from '@plugin/libraries'; -import { syncAttributes } from '@plugin/utils/syncAttributes'; +import { overrides } from '@plugin/libraries'; +import { translateTouched } from '@plugin/translators'; import { ShapeAttributes } from '@ui/lib/types/shapes/shape'; -import { SyncGroups } from '@ui/lib/types/utils/syncGroups'; export const transformOverrides = ( node: SceneNode ): Pick => { - const overrides = overridesLibrary.get(node.id); - - const touched: SyncGroups[] = []; - - if (overrides) { - overrides.forEach(override => { - if (syncAttributes[override]) { - touched.push(...syncAttributes[override]); - } - }); - } - return { - touched, + touched: translateTouched(overrides.get(node.id)), componentPropertyReferences: node.componentPropertyReferences }; }; diff --git a/plugin-src/transformers/transformDocumentNode.ts b/plugin-src/transformers/transformDocumentNode.ts index d862751a..0b2659f2 100644 --- a/plugin-src/transformers/transformDocumentNode.ts +++ b/plugin-src/transformers/transformDocumentNode.ts @@ -1,6 +1,6 @@ import { toObject } from '@common/map'; -import { componentProperties as componentPropertiesLib, components } from '@plugin/libraries'; +import { componentProperties, components } from '@plugin/libraries'; import { processImages, processPages, @@ -28,6 +28,6 @@ export const transformDocumentNode = async (node: DocumentNode): Promise { + const syncGroups: Set = new Set(); + + if (!changedProperties) return []; + + changedProperties.forEach(changedProperty => { + const syncGroup = syncAttributes[changedProperty]; + + if (syncGroup && syncGroup.length > 0) { + syncGroup.forEach(group => syncGroups.add(group)); + } + }); + + return Array.from(syncGroups); +}; diff --git a/ui-src/parser/creators/symbols/symbolTouched.ts b/ui-src/parser/creators/symbols/symbolTouched.ts index 9bda1f52..5da37674 100644 --- a/ui-src/parser/creators/symbols/symbolTouched.ts +++ b/ui-src/parser/creators/symbols/symbolTouched.ts @@ -12,22 +12,26 @@ export const symbolTouched = ( return touched; } - Object.entries(componentPropertyReferences).forEach(([key, value]) => { - switch (key) { - case 'visible': - if (visible !== componentProperties.get(value)?.defaultValue) { - touched?.push(':visibility-group'); - } - break; - case 'characters': - if (characters !== componentProperties.get(value)?.defaultValue) { - touched?.push(':content-group'); - } - break; - default: - break; + const propertyReferenceVisible = componentPropertyReferences.visible; + const propertyReferenceCharacters = componentPropertyReferences.characters; + + if (propertyReferenceVisible) { + if ( + visible !== componentProperties.get(propertyReferenceVisible)?.defaultValue && + !touched?.includes(':visibility-group') + ) { + touched?.push(':visibility-group'); } - }); + } + + if (propertyReferenceCharacters) { + if ( + characters !== componentProperties.get(propertyReferenceCharacters)?.defaultValue && + !touched?.includes(':content-group') + ) { + touched?.push(':content-group'); + } + } return touched; }; From 08a5a5dc4b0fbd1817684ff17e2dd00d51e4e5a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20S=C3=A1nchez?= Date: Fri, 28 Jun 2024 12:07:04 +0200 Subject: [PATCH 10/14] fixes --- plugin-src/transformers/transformInstanceNode.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugin-src/transformers/transformInstanceNode.ts b/plugin-src/transformers/transformInstanceNode.ts index 05b32518..d1c16489 100644 --- a/plugin-src/transformers/transformInstanceNode.ts +++ b/plugin-src/transformers/transformInstanceNode.ts @@ -35,12 +35,14 @@ export const transformInstanceNode = async ( nodeOverrides = transformOverrides(node); } + const fetchedOverrides = overrides.get(node.id) ?? []; if (node.visible !== mainComponent.visible) { - overrides.set(node.id, [...(overrides.get(node.id) ?? []), 'visible']); + fetchedOverrides.push('visible'); } if (node.locked !== mainComponent.locked) { - overrides.set(node.id, [...(overrides.get(node.id) ?? []), 'locked']); + fetchedOverrides.push('locked'); } + overrides.set(node.id, fetchedOverrides); return { type: 'instance', From c078f23d09ea6b4163396b3e4b31d40d6aa411db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20S=C3=A1nchez?= Date: Fri, 28 Jun 2024 12:09:51 +0200 Subject: [PATCH 11/14] fixes --- ui-src/parser/creators/createComponentInstance.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/ui-src/parser/creators/createComponentInstance.ts b/ui-src/parser/creators/createComponentInstance.ts index 18d47816..26f1d38f 100644 --- a/ui-src/parser/creators/createComponentInstance.ts +++ b/ui-src/parser/creators/createComponentInstance.ts @@ -25,12 +25,6 @@ export const createComponentInstance = ( shape.componentFile = shape.isOrphan ? getRemoteFileId(file) : file.getId(); shape.componentRoot = isComponentRoot; shape.componentId = uiComponent.componentId; - shape.touched = symbolTouched( - !shape.hidden, - undefined, - shape.touched, - shape.componentPropertyReferences - ); createArtboard(file, shape); }; From e6b59ea976e0052d5f890567dbfc009b20b29565 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20S=C3=A1nchez?= Date: Fri, 28 Jun 2024 12:11:49 +0200 Subject: [PATCH 12/14] fixes --- .../parser/creators/symbols/symbolTouched.ts | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/ui-src/parser/creators/symbols/symbolTouched.ts b/ui-src/parser/creators/symbols/symbolTouched.ts index 5da37674..a3593c40 100644 --- a/ui-src/parser/creators/symbols/symbolTouched.ts +++ b/ui-src/parser/creators/symbols/symbolTouched.ts @@ -15,22 +15,20 @@ export const symbolTouched = ( const propertyReferenceVisible = componentPropertyReferences.visible; const propertyReferenceCharacters = componentPropertyReferences.characters; - if (propertyReferenceVisible) { - if ( - visible !== componentProperties.get(propertyReferenceVisible)?.defaultValue && - !touched?.includes(':visibility-group') - ) { - touched?.push(':visibility-group'); - } + if ( + propertyReferenceVisible && + visible !== componentProperties.get(propertyReferenceVisible)?.defaultValue && + !touched?.includes(':visibility-group') + ) { + touched?.push(':visibility-group'); } - if (propertyReferenceCharacters) { - if ( - characters !== componentProperties.get(propertyReferenceCharacters)?.defaultValue && - !touched?.includes(':content-group') - ) { - touched?.push(':content-group'); - } + if ( + propertyReferenceCharacters && + characters !== componentProperties.get(propertyReferenceCharacters)?.defaultValue && + !touched?.includes(':content-group') + ) { + touched?.push(':content-group'); } return touched; From 9865ddaaa643a6567b66fe105a6c7173b5597aa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20S=C3=A1nchez?= Date: Fri, 28 Jun 2024 12:12:59 +0200 Subject: [PATCH 13/14] fixes --- ui-src/types/component.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/ui-src/types/component.ts b/ui-src/types/component.ts index eab772b8..64393479 100644 --- a/ui-src/types/component.ts +++ b/ui-src/types/component.ts @@ -47,6 +47,7 @@ export type ComponentProperty = { variantOptions?: string[]; }; +// This type comes directly from Figma. We have it here because we need to reference it from the UI export type ComponentPropertyReference = | { [nodeProperty in 'visible' | 'characters' | 'mainComponent']?: string; From d5dbd2e3be8eb64ee152426166273242d7725637 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20S=C3=A1nchez?= Date: Fri, 28 Jun 2024 12:15:10 +0200 Subject: [PATCH 14/14] fixes --- ui-src/parser/creators/createComponentInstance.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/ui-src/parser/creators/createComponentInstance.ts b/ui-src/parser/creators/createComponentInstance.ts index 26f1d38f..2b78b056 100644 --- a/ui-src/parser/creators/createComponentInstance.ts +++ b/ui-src/parser/creators/createComponentInstance.ts @@ -1,7 +1,6 @@ import { PenpotFile } from '@ui/lib/types/penpotFile'; import { Uuid } from '@ui/lib/types/utils/uuid'; import { components, parseFigmaId } from '@ui/parser'; -import { symbolTouched } from '@ui/parser/creators/symbols'; import { ComponentInstance } from '@ui/types'; import { createArtboard } from '.';