From 9053ba9226f6db632c07013b6621a081102d0c3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20S=C3=A1nchez?= Date: Mon, 10 Jun 2024 16:20:04 +0200 Subject: [PATCH 1/8] wip --- plugin-src/transformers/partials/index.ts | 1 + .../transformers/partials/transformLayout.ts | 25 +++++++ plugin-src/transformers/transformFrameNode.ts | 4 +- plugin-src/translators/index.ts | 1 + plugin-src/translators/translateLayout.ts | 75 +++++++++++++++++++ ui-src/lib/types/shapes/layout.ts | 48 +++++++----- 6 files changed, 133 insertions(+), 21 deletions(-) create mode 100644 plugin-src/transformers/partials/transformLayout.ts create mode 100644 plugin-src/translators/translateLayout.ts diff --git a/plugin-src/transformers/partials/index.ts b/plugin-src/transformers/partials/index.ts index 0253b368..f3a171b5 100644 --- a/plugin-src/transformers/partials/index.ts +++ b/plugin-src/transformers/partials/index.ts @@ -6,6 +6,7 @@ export * from './transformDimensionAndPosition'; export * from './transformEffects'; export * from './transformFigmaIds'; export * from './transformFills'; +export * from './transformLayout'; export * from './transformProportion'; export * from './transformRotationAndPosition'; export * from './transformSceneNode'; diff --git a/plugin-src/transformers/partials/transformLayout.ts b/plugin-src/transformers/partials/transformLayout.ts new file mode 100644 index 00000000..29f72453 --- /dev/null +++ b/plugin-src/transformers/partials/transformLayout.ts @@ -0,0 +1,25 @@ +import { + translateLayoutFlexDir, + translateLayoutGap, + translateLayoutJustifyContent, + translateLayoutJustifyItems, + translateLayoutPadding, + translateLayoutWrapType +} from '@plugin/translators'; + +import { LayoutAttributes } from '@ui/lib/types/shapes/layout'; + +export const transformAutoLayout = (node: BaseFrameMixin): LayoutAttributes => { + console.log(node); + return { + layout: node.layoutMode !== 'NONE' ? 'flex' : undefined, + layoutFlexDir: translateLayoutFlexDir(node.layoutMode), + layoutGap: translateLayoutGap(node.layoutMode, node.itemSpacing), + layoutWrapType: translateLayoutWrapType(node.layoutWrap), + layoutPadding: translateLayoutPadding(node), + layoutJustifyContent: translateLayoutJustifyContent(node), + layoutJustifyItems: translateLayoutJustifyItems(node), + layoutAlignContent: translateLayoutJustifyContent(node), + layoutAlignItems: translateLayoutJustifyItems(node) + }; +}; diff --git a/plugin-src/transformers/transformFrameNode.ts b/plugin-src/transformers/transformFrameNode.ts index 57ba08f8..fb2b37f7 100644 --- a/plugin-src/transformers/transformFrameNode.ts +++ b/plugin-src/transformers/transformFrameNode.ts @@ -1,4 +1,5 @@ import { + transformAutoLayout, transformBlend, transformChildren, transformConstraints, @@ -36,7 +37,8 @@ export const transformFrameNode = async ( ...transformProportion(node), ...transformCornerRadius(node), ...transformEffects(node), - ...transformConstraints(node) + ...transformConstraints(node), + ...transformAutoLayout(node) }; } diff --git a/plugin-src/translators/index.ts b/plugin-src/translators/index.ts index 9d58aa65..4d824ccf 100644 --- a/plugin-src/translators/index.ts +++ b/plugin-src/translators/index.ts @@ -3,5 +3,6 @@ export * from './translateBlurEffects'; export * from './translateBoolType'; export * from './translateChildren'; export * from './translateConstraints'; +export * from './translateLayout'; export * from './translateShadowEffects'; export * from './translateStrokes'; diff --git a/plugin-src/translators/translateLayout.ts b/plugin-src/translators/translateLayout.ts new file mode 100644 index 00000000..b8e11a4d --- /dev/null +++ b/plugin-src/translators/translateLayout.ts @@ -0,0 +1,75 @@ +import { + JustifyAlignContent, + JustifyAlignItems, + LayoutFlexDir, + LayoutGap, + LayoutPadding, + LayoutWrapType +} from '@ui/lib/types/shapes/layout'; + +type FigmaLayoutMode = 'NONE' | 'HORIZONTAL' | 'VERTICAL'; + +type FigmaWrap = 'NO_WRAP' | 'WRAP'; +export const translateLayoutFlexDir = (layoutMode: FigmaLayoutMode): LayoutFlexDir | undefined => { + switch (layoutMode) { + case 'HORIZONTAL': + return 'row-reverse'; + case 'VERTICAL': + return 'column-reverse'; + default: + return; + } +}; + +export const translateLayoutGap = (layoutMode: FigmaLayoutMode, itemSpacing: number): LayoutGap => { + return { + rowGap: layoutMode === 'VERTICAL' ? itemSpacing : 0, + columnGap: layoutMode === 'HORIZONTAL' ? itemSpacing : 0 + }; +}; + +export const translateLayoutWrapType = (wrap: FigmaWrap): LayoutWrapType => { + switch (wrap) { + case 'NO_WRAP': + return 'nowrap'; + case 'WRAP': + return 'wrap'; + } +}; + +export const translateLayoutPadding = (node: BaseFrameMixin): LayoutPadding => { + return { + p1: node.paddingTop, + p2: node.paddingRight, + p3: node.paddingBottom, + p4: node.paddingLeft + }; +}; + +export const translateLayoutJustifyContent = (node: BaseFrameMixin): JustifyAlignContent => { + switch (node.primaryAxisAlignItems) { + case 'MIN': + return 'start'; + case 'CENTER': + return 'center'; + case 'MAX': + return 'end'; + case 'SPACE_BETWEEN': + return 'space-between'; + default: + return 'stretch'; + } +}; + +export const translateLayoutJustifyItems = (node: BaseFrameMixin): JustifyAlignItems => { + switch (node.counterAxisAlignItems) { + case 'MIN': + return 'start'; + case 'CENTER': + return 'center'; + case 'MAX': + return 'end'; + default: + return 'stretch'; + } +}; diff --git a/ui-src/lib/types/shapes/layout.ts b/ui-src/lib/types/shapes/layout.ts index c84408fd..875fdf3c 100644 --- a/ui-src/lib/types/shapes/layout.ts +++ b/ui-src/lib/types/shapes/layout.ts @@ -56,7 +56,7 @@ export type LayoutChildAttributes = { layoutItemZIndex?: number; }; -type JustifyAlignContent = +export type JustifyAlignContent = | 'start' | 'center' | 'end' @@ -65,30 +65,38 @@ type JustifyAlignContent = | 'space-evenly' | 'stretch'; -type JustifyAlignItems = 'start' | 'end' | 'center' | 'stretch'; +export type JustifyAlignItems = 'start' | 'end' | 'center' | 'stretch'; + +export type LayoutFlexDir = + | 'row' + | 'reverse-row' + | 'row-reverse' + | 'column' + | 'reverse-column' + | 'column-reverse'; + +export type LayoutGap = { + rowGap?: number; + columnGap?: number; +}; + +export type LayoutWrapType = 'wrap' | 'nowrap' | 'no-wrap'; + +export type LayoutPadding = { + p1?: number; + p2?: number; + p3?: number; + p4?: number; +}; export type LayoutAttributes = { layout?: 'flex' | 'grid'; - layoutFlexDir?: - | 'row' - | 'reverse-row' - | 'row-reverse' - | 'column' - | 'reverse-column' - | 'column-reverse'; - layoutGap?: { - rowGap?: number; - columnGap?: number; - }; + layoutFlexDir?: LayoutFlexDir; + layoutGap?: LayoutGap; layoutGapType?: 'simple' | 'multiple'; - layoutWrapType?: 'wrap' | 'nowrap' | 'no-wrap'; + layoutWrapType?: LayoutWrapType; layoutPaddingType?: 'simple' | 'multiple'; - layoutPadding?: { - p1?: number; - p2?: number; - p3?: number; - p4?: number; - }; + layoutPadding?: LayoutPadding; layoutJustifyContent?: JustifyAlignContent; layoutJustifyItems?: JustifyAlignItems; layoutAlignContent?: JustifyAlignContent; From da68e0ca3fde5a52b1b8a5969f73c468853a7531 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20S=C3=A1nchez?= Date: Tue, 11 Jun 2024 09:26:38 +0200 Subject: [PATCH 2/8] added layout sizing --- .../partials/transformChildren.ts | 8 ++-- .../transformers/partials/transformLayout.ts | 19 ++++++-- .../partials/transformVectorPaths.ts | 4 +- .../transformers/transformBooleanNode.ts | 4 +- .../transformers/transformComponentNode.ts | 2 + .../transformers/transformEllipseNode.ts | 2 + plugin-src/transformers/transformFrameNode.ts | 8 +++- .../transformers/transformInstanceNode.ts | 11 ++++- plugin-src/transformers/transformPathNode.ts | 2 + .../transformers/transformRectangleNode.ts | 2 + plugin-src/transformers/transformTextNode.ts | 2 + plugin-src/translators/translateLayout.ts | 45 ++++++++++++++++++- ui-src/lib/types/shapes/layout.ts | 33 ++++++-------- 13 files changed, 109 insertions(+), 33 deletions(-) diff --git a/plugin-src/transformers/partials/transformChildren.ts b/plugin-src/transformers/partials/transformChildren.ts index 6a944713..a516ed8e 100644 --- a/plugin-src/transformers/partials/transformChildren.ts +++ b/plugin-src/transformers/partials/transformChildren.ts @@ -9,14 +9,16 @@ const nodeActsAsMask = (node: SceneNode): boolean => { export const transformChildren = async ( node: ChildrenMixin, baseX: number = 0, - baseY: number = 0 + baseY: number = 0, + reverseChildrenOrder: boolean = false ): Promise => { const maskIndex = node.children.findIndex(nodeActsAsMask); const containsMask = maskIndex !== -1; + const children = reverseChildrenOrder ? node.children.slice().reverse() : node.children; return { children: containsMask - ? await translateMaskChildren(node.children, maskIndex, baseX, baseY) - : await translateChildren(node.children, baseX, baseY) + ? await translateMaskChildren(children, maskIndex, baseX, baseY) + : await translateChildren(children, baseX, baseY) }; }; diff --git a/plugin-src/transformers/partials/transformLayout.ts b/plugin-src/transformers/partials/transformLayout.ts index 29f72453..41223a8b 100644 --- a/plugin-src/transformers/partials/transformLayout.ts +++ b/plugin-src/transformers/partials/transformLayout.ts @@ -1,16 +1,18 @@ import { + translateLayoutAlignContent, + translateLayoutAlignItems, translateLayoutFlexDir, translateLayoutGap, translateLayoutJustifyContent, translateLayoutJustifyItems, translateLayoutPadding, + translateLayoutSizing, translateLayoutWrapType } from '@plugin/translators'; -import { LayoutAttributes } from '@ui/lib/types/shapes/layout'; +import { LayoutAttributes, LayoutChildAttributes } from '@ui/lib/types/shapes/layout'; export const transformAutoLayout = (node: BaseFrameMixin): LayoutAttributes => { - console.log(node); return { layout: node.layoutMode !== 'NONE' ? 'flex' : undefined, layoutFlexDir: translateLayoutFlexDir(node.layoutMode), @@ -19,7 +21,16 @@ export const transformAutoLayout = (node: BaseFrameMixin): LayoutAttributes => { layoutPadding: translateLayoutPadding(node), layoutJustifyContent: translateLayoutJustifyContent(node), layoutJustifyItems: translateLayoutJustifyItems(node), - layoutAlignContent: translateLayoutJustifyContent(node), - layoutAlignItems: translateLayoutJustifyItems(node) + layoutAlignContent: translateLayoutAlignContent(node), + layoutAlignItems: translateLayoutAlignItems(node) + }; +}; + +export const transformLayoutSizing = ( + node: LayoutMixin +): Pick => { + return { + layoutItemHSizing: translateLayoutSizing(node.layoutSizingHorizontal), + layoutItemVSizing: translateLayoutSizing(node.layoutSizingVertical) }; }; diff --git a/plugin-src/transformers/partials/transformVectorPaths.ts b/plugin-src/transformers/partials/transformVectorPaths.ts index 25428cc9..9c2a8652 100644 --- a/plugin-src/transformers/partials/transformVectorPaths.ts +++ b/plugin-src/transformers/partials/transformVectorPaths.ts @@ -4,6 +4,7 @@ import { transformBlend, transformDimensionAndPositionFromVectorPath, transformEffects, + transformLayoutSizing, transformProportion, transformSceneNode, transformStrokesFromVector @@ -110,6 +111,7 @@ const transformVectorPath = ( ...transformDimensionAndPositionFromVectorPath(vectorPath, baseX, baseY), ...transformSceneNode(node), ...transformBlend(node), - ...transformProportion(node) + ...transformProportion(node), + ...transformLayoutSizing(node) }; }; diff --git a/plugin-src/transformers/transformBooleanNode.ts b/plugin-src/transformers/transformBooleanNode.ts index ef414a30..fe76433e 100644 --- a/plugin-src/transformers/transformBooleanNode.ts +++ b/plugin-src/transformers/transformBooleanNode.ts @@ -5,6 +5,7 @@ import { transformEffects, transformFigmaIds, transformFills, + transformLayoutSizing, transformProportion, transformSceneNode, transformStrokes @@ -30,6 +31,7 @@ export const transformBooleanNode = async ( ...transformDimensionAndPosition(node, baseX, baseY), ...transformSceneNode(node), ...transformBlend(node), - ...transformProportion(node) + ...transformProportion(node), + ...transformLayoutSizing(node) }; }; diff --git a/plugin-src/transformers/transformComponentNode.ts b/plugin-src/transformers/transformComponentNode.ts index 5d654be1..84d14865 100644 --- a/plugin-src/transformers/transformComponentNode.ts +++ b/plugin-src/transformers/transformComponentNode.ts @@ -8,6 +8,7 @@ import { transformEffects, transformFigmaIds, transformFills, + transformLayoutSizing, transformProportion, transformSceneNode, transformStrokes @@ -32,6 +33,7 @@ export const transformComponentNode = async ( ...transformSceneNode(node), ...transformBlend(node), ...transformProportion(node), + ...transformLayoutSizing(node), ...transformCornerRadius(node), ...(await transformChildren(node, baseX + node.x, baseY + node.y)), ...transformDimensionAndPosition(node, baseX, baseY), diff --git a/plugin-src/transformers/transformEllipseNode.ts b/plugin-src/transformers/transformEllipseNode.ts index b2f165a9..53e7b249 100644 --- a/plugin-src/transformers/transformEllipseNode.ts +++ b/plugin-src/transformers/transformEllipseNode.ts @@ -5,6 +5,7 @@ import { transformEffects, transformFigmaIds, transformFills, + transformLayoutSizing, transformProportion, transformRotationAndPosition, transformSceneNode, @@ -30,6 +31,7 @@ export const transformEllipseNode = ( ...transformSceneNode(node), ...transformBlend(node), ...transformProportion(node), + ...transformLayoutSizing(node), ...transformConstraints(node) }; }; diff --git a/plugin-src/transformers/transformFrameNode.ts b/plugin-src/transformers/transformFrameNode.ts index fb2b37f7..59a775c1 100644 --- a/plugin-src/transformers/transformFrameNode.ts +++ b/plugin-src/transformers/transformFrameNode.ts @@ -8,6 +8,7 @@ import { transformEffects, transformFigmaIds, transformFills, + transformLayoutSizing, transformProportion, transformSceneNode, transformStrokes @@ -25,8 +26,12 @@ export const transformFrameNode = async ( baseY: number ): Promise => { let frameSpecificAttributes: Partial = {}; + let reverseChildrenOrder = false; if (!isSectionNode(node)) { + if (node.layoutMode !== 'NONE') { + reverseChildrenOrder = true; + } // Figma API does not expose strokes, blend modes, corner radius, or constraint proportions for sections, // they plan to add it in the future. Refactor this when available. frameSpecificAttributes = { @@ -35,6 +40,7 @@ export const transformFrameNode = async ( // @see: https://forum.figma.com/t/add-a-blendmode-property-for-sectionnode/58560 ...transformBlend(node), ...transformProportion(node), + ...transformLayoutSizing(node), ...transformCornerRadius(node), ...transformEffects(node), ...transformConstraints(node), @@ -49,7 +55,7 @@ export const transformFrameNode = async ( ...transformFigmaIds(node), ...transformFills(node), ...frameSpecificAttributes, - ...(await transformChildren(node, baseX + node.x, baseY + node.y)), + ...(await transformChildren(node, baseX + node.x, baseY + node.y, reverseChildrenOrder)), ...transformDimensionAndPosition(node, baseX, baseY), ...transformSceneNode(node) }; diff --git a/plugin-src/transformers/transformInstanceNode.ts b/plugin-src/transformers/transformInstanceNode.ts index 2edd6998..c07a2b31 100644 --- a/plugin-src/transformers/transformInstanceNode.ts +++ b/plugin-src/transformers/transformInstanceNode.ts @@ -1,5 +1,6 @@ import { remoteComponentLibrary } from '@plugin/RemoteComponentLibrary'; import { + transformAutoLayout, transformBlend, transformChildren, transformConstraints, @@ -8,6 +9,7 @@ import { transformEffects, transformFigmaIds, transformFills, + transformLayoutSizing, transformProportion, transformSceneNode, transformStrokes @@ -30,6 +32,11 @@ export const transformInstanceNode = async ( await registerExternalComponents(mainComponent); } + let reverseChildrenOrder = false; + if (node.layoutMode !== 'NONE') { + reverseChildrenOrder = true; + } + return { type: 'instance', name: node.name, @@ -43,10 +50,12 @@ export const transformInstanceNode = async ( ...transformSceneNode(node), ...transformBlend(node), ...transformProportion(node), + ...transformLayoutSizing(node), ...transformCornerRadius(node), ...transformDimensionAndPosition(node, baseX, baseY), ...transformConstraints(node), - ...(await transformChildren(node, baseX + node.x, baseY + node.y)) + ...transformAutoLayout(node), + ...(await transformChildren(node, baseX + node.x, baseY + node.y, reverseChildrenOrder)) }; }; diff --git a/plugin-src/transformers/transformPathNode.ts b/plugin-src/transformers/transformPathNode.ts index 1157e335..30ba76f2 100644 --- a/plugin-src/transformers/transformPathNode.ts +++ b/plugin-src/transformers/transformPathNode.ts @@ -5,6 +5,7 @@ import { transformEffects, transformFigmaIds, transformFills, + transformLayoutSizing, transformProportion, transformSceneNode, transformStrokes, @@ -34,6 +35,7 @@ export const transformPathNode = ( ...transformSceneNode(node), ...transformBlend(node), ...transformProportion(node), + ...transformLayoutSizing(node), ...transformConstraints(node) }; }; diff --git a/plugin-src/transformers/transformRectangleNode.ts b/plugin-src/transformers/transformRectangleNode.ts index 4e3e444a..8c665e4e 100644 --- a/plugin-src/transformers/transformRectangleNode.ts +++ b/plugin-src/transformers/transformRectangleNode.ts @@ -6,6 +6,7 @@ import { transformEffects, transformFigmaIds, transformFills, + transformLayoutSizing, transformProportion, transformRotationAndPosition, transformSceneNode, @@ -31,6 +32,7 @@ export const transformRectangleNode = ( ...transformSceneNode(node), ...transformBlend(node), ...transformProportion(node), + ...transformLayoutSizing(node), ...transformCornerRadius(node), ...transformConstraints(node) }; diff --git a/plugin-src/transformers/transformTextNode.ts b/plugin-src/transformers/transformTextNode.ts index 21a6b479..c0157ef5 100644 --- a/plugin-src/transformers/transformTextNode.ts +++ b/plugin-src/transformers/transformTextNode.ts @@ -4,6 +4,7 @@ import { transformDimensionAndPosition, transformEffects, transformFigmaIds, + transformLayoutSizing, transformProportion, transformSceneNode, transformStrokes, @@ -23,6 +24,7 @@ export const transformTextNode = (node: TextNode, baseX: number, baseY: number): ...transformSceneNode(node), ...transformBlend(node), ...transformProportion(node), + ...transformLayoutSizing(node), ...transformStrokes(node), ...transformConstraints(node) }; diff --git a/plugin-src/translators/translateLayout.ts b/plugin-src/translators/translateLayout.ts index b8e11a4d..48dbe181 100644 --- a/plugin-src/translators/translateLayout.ts +++ b/plugin-src/translators/translateLayout.ts @@ -4,18 +4,22 @@ import { LayoutFlexDir, LayoutGap, LayoutPadding, + LayoutSizing, LayoutWrapType } from '@ui/lib/types/shapes/layout'; type FigmaLayoutMode = 'NONE' | 'HORIZONTAL' | 'VERTICAL'; type FigmaWrap = 'NO_WRAP' | 'WRAP'; + +type FigmaLayoutSizing = 'FIXED' | 'HUG' | 'FILL'; + export const translateLayoutFlexDir = (layoutMode: FigmaLayoutMode): LayoutFlexDir | undefined => { switch (layoutMode) { case 'HORIZONTAL': - return 'row-reverse'; + return 'row'; case 'VERTICAL': - return 'column-reverse'; + return 'column'; default: return; } @@ -62,6 +66,19 @@ export const translateLayoutJustifyContent = (node: BaseFrameMixin): JustifyAlig }; export const translateLayoutJustifyItems = (node: BaseFrameMixin): JustifyAlignItems => { + switch (node.primaryAxisAlignItems) { + case 'MIN': + return 'start'; + case 'CENTER': + return 'center'; + case 'MAX': + return 'end'; + default: + return 'stretch'; + } +}; + +export const translateLayoutAlignContent = (node: BaseFrameMixin): JustifyAlignContent => { switch (node.counterAxisAlignItems) { case 'MIN': return 'start'; @@ -73,3 +90,27 @@ export const translateLayoutJustifyItems = (node: BaseFrameMixin): JustifyAlignI return 'stretch'; } }; + +export const translateLayoutAlignItems = (node: BaseFrameMixin): JustifyAlignItems => { + switch (node.counterAxisAlignItems) { + case 'MIN': + return 'start'; + case 'CENTER': + return 'center'; + case 'MAX': + return 'end'; + default: + return 'stretch'; + } +}; + +export const translateLayoutSizing = (sizing: FigmaLayoutSizing): LayoutSizing => { + switch (sizing) { + case 'FIXED': + return 'fix'; + case 'HUG': + return 'auto'; + case 'FILL': + return 'fill'; + } +}; diff --git a/ui-src/lib/types/shapes/layout.ts b/ui-src/lib/types/shapes/layout.ts index 875fdf3c..07cf9ce3 100644 --- a/ui-src/lib/types/shapes/layout.ts +++ b/ui-src/lib/types/shapes/layout.ts @@ -2,17 +2,22 @@ import { Uuid } from '@ui/lib/types/utils/uuid'; export const ITEM_MARGIN_SIMPLE_TYPE: unique symbol = Symbol.for('simple'); export const ITEM_MARGIN_MULTIPLE_TYPE: unique symbol = Symbol.for('multiple'); -export const ITEM_HSIZING_FILL: unique symbol = Symbol.for('fill'); -export const ITEM_HSIZING_FIX: unique symbol = Symbol.for('fix'); -export const ITEM_HSIZING_AUTO: unique symbol = Symbol.for('auto'); -export const ITEM_VSIZING_FILL: unique symbol = Symbol.for('fill'); -export const ITEM_VSIZING_FIX: unique symbol = Symbol.for('fix'); -export const ITEM_VSIZING_AUTO: unique symbol = Symbol.for('auto'); +export const ITEM_SIZING_FILL: unique symbol = Symbol.for('fill'); +export const ITEM_SIZING_FIX: unique symbol = Symbol.for('fix'); +export const ITEM_SIZING_AUTO: unique symbol = Symbol.for('auto'); export const ITEM_ALIGN_SELF_START: unique symbol = Symbol.for('start'); export const ITEM_ALIGN_SELF_END: unique symbol = Symbol.for('end'); export const ITEM_ALIGN_SELF_CENTER: unique symbol = Symbol.for('center'); export const ITEM_ALIGN_SELF_STRETCH: unique symbol = Symbol.for('stretch'); +export type LayoutSizing = + | 'fill' + | 'fix' + | 'auto' + | typeof ITEM_SIZING_FILL + | typeof ITEM_SIZING_FIX + | typeof ITEM_SIZING_AUTO; + export type LayoutChildAttributes = { layoutItemMarginType?: | 'simple' @@ -29,20 +34,8 @@ export type LayoutChildAttributes = { layoutItemMinH?: number; layoutItemMaxW?: number; layoutItemMinW?: number; - layoutItemHSizing?: - | 'fill' - | 'fix' - | 'auto' - | typeof ITEM_HSIZING_FILL - | typeof ITEM_HSIZING_FIX - | typeof ITEM_HSIZING_AUTO; - layoutItemVSizing?: - | 'fill' - | 'fix' - | 'auto' - | typeof ITEM_VSIZING_FILL - | typeof ITEM_VSIZING_FIX - | typeof ITEM_VSIZING_AUTO; + layoutItemHSizing?: LayoutSizing; + layoutItemVSizing?: LayoutSizing; layoutItemAlignSelf?: | 'start' | 'end' From e555463a8ffc253a79b9c2505643b194c0fde41f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20S=C3=A1nchez?= Date: Tue, 11 Jun 2024 10:41:06 +0200 Subject: [PATCH 3/8] layout positioning --- plugin-src/transformers/partials/transformLayout.ts | 8 ++++++++ plugin-src/transformers/partials/transformVectorPaths.ts | 4 +++- plugin-src/transformers/transformBooleanNode.ts | 4 +++- plugin-src/transformers/transformComponentNode.ts | 2 ++ plugin-src/transformers/transformEllipseNode.ts | 2 ++ plugin-src/transformers/transformFrameNode.ts | 2 ++ plugin-src/transformers/transformInstanceNode.ts | 2 ++ plugin-src/transformers/transformPathNode.ts | 2 ++ plugin-src/transformers/transformRectangleNode.ts | 2 ++ plugin-src/transformers/transformTextNode.ts | 2 ++ 10 files changed, 28 insertions(+), 2 deletions(-) diff --git a/plugin-src/transformers/partials/transformLayout.ts b/plugin-src/transformers/partials/transformLayout.ts index 41223a8b..cd8bb01d 100644 --- a/plugin-src/transformers/partials/transformLayout.ts +++ b/plugin-src/transformers/partials/transformLayout.ts @@ -34,3 +34,11 @@ export const transformLayoutSizing = ( layoutItemVSizing: translateLayoutSizing(node.layoutSizingVertical) }; }; + +export const transformAutoLayoutPosition = ( + node: AutoLayoutChildrenMixin +): Pick => { + return { + layoutItemAbsolute: node.layoutPositioning === 'ABSOLUTE' + }; +}; diff --git a/plugin-src/transformers/partials/transformVectorPaths.ts b/plugin-src/transformers/partials/transformVectorPaths.ts index 9c2a8652..0ebdfdf2 100644 --- a/plugin-src/transformers/partials/transformVectorPaths.ts +++ b/plugin-src/transformers/partials/transformVectorPaths.ts @@ -1,6 +1,7 @@ import { parseSVG } from 'svg-path-parser'; import { + transformAutoLayoutPosition, transformBlend, transformDimensionAndPositionFromVectorPath, transformEffects, @@ -112,6 +113,7 @@ const transformVectorPath = ( ...transformSceneNode(node), ...transformBlend(node), ...transformProportion(node), - ...transformLayoutSizing(node) + ...transformLayoutSizing(node), + ...transformAutoLayoutPosition(node) }; }; diff --git a/plugin-src/transformers/transformBooleanNode.ts b/plugin-src/transformers/transformBooleanNode.ts index fe76433e..4cb18e46 100644 --- a/plugin-src/transformers/transformBooleanNode.ts +++ b/plugin-src/transformers/transformBooleanNode.ts @@ -1,4 +1,5 @@ import { + transformAutoLayoutPosition, transformBlend, transformChildren, transformDimensionAndPosition, @@ -32,6 +33,7 @@ export const transformBooleanNode = async ( ...transformSceneNode(node), ...transformBlend(node), ...transformProportion(node), - ...transformLayoutSizing(node) + ...transformLayoutSizing(node), + ...transformAutoLayoutPosition(node) }; }; diff --git a/plugin-src/transformers/transformComponentNode.ts b/plugin-src/transformers/transformComponentNode.ts index 84d14865..336444ba 100644 --- a/plugin-src/transformers/transformComponentNode.ts +++ b/plugin-src/transformers/transformComponentNode.ts @@ -1,5 +1,6 @@ import { componentsLibrary } from '@plugin/ComponentLibrary'; import { + transformAutoLayoutPosition, transformBlend, transformChildren, transformConstraints, @@ -34,6 +35,7 @@ export const transformComponentNode = async ( ...transformBlend(node), ...transformProportion(node), ...transformLayoutSizing(node), + ...transformAutoLayoutPosition(node), ...transformCornerRadius(node), ...(await transformChildren(node, baseX + node.x, baseY + node.y)), ...transformDimensionAndPosition(node, baseX, baseY), diff --git a/plugin-src/transformers/transformEllipseNode.ts b/plugin-src/transformers/transformEllipseNode.ts index 53e7b249..09853b27 100644 --- a/plugin-src/transformers/transformEllipseNode.ts +++ b/plugin-src/transformers/transformEllipseNode.ts @@ -1,4 +1,5 @@ import { + transformAutoLayoutPosition, transformBlend, transformConstraints, transformDimension, @@ -32,6 +33,7 @@ export const transformEllipseNode = ( ...transformBlend(node), ...transformProportion(node), ...transformLayoutSizing(node), + ...transformAutoLayoutPosition(node), ...transformConstraints(node) }; }; diff --git a/plugin-src/transformers/transformFrameNode.ts b/plugin-src/transformers/transformFrameNode.ts index 59a775c1..68f6eb40 100644 --- a/plugin-src/transformers/transformFrameNode.ts +++ b/plugin-src/transformers/transformFrameNode.ts @@ -1,5 +1,6 @@ import { transformAutoLayout, + transformAutoLayoutPosition, transformBlend, transformChildren, transformConstraints, @@ -41,6 +42,7 @@ export const transformFrameNode = async ( ...transformBlend(node), ...transformProportion(node), ...transformLayoutSizing(node), + ...transformAutoLayoutPosition(node), ...transformCornerRadius(node), ...transformEffects(node), ...transformConstraints(node), diff --git a/plugin-src/transformers/transformInstanceNode.ts b/plugin-src/transformers/transformInstanceNode.ts index c07a2b31..44be36f8 100644 --- a/plugin-src/transformers/transformInstanceNode.ts +++ b/plugin-src/transformers/transformInstanceNode.ts @@ -1,6 +1,7 @@ import { remoteComponentLibrary } from '@plugin/RemoteComponentLibrary'; import { transformAutoLayout, + transformAutoLayoutPosition, transformBlend, transformChildren, transformConstraints, @@ -51,6 +52,7 @@ export const transformInstanceNode = async ( ...transformBlend(node), ...transformProportion(node), ...transformLayoutSizing(node), + ...transformAutoLayoutPosition(node), ...transformCornerRadius(node), ...transformDimensionAndPosition(node, baseX, baseY), ...transformConstraints(node), diff --git a/plugin-src/transformers/transformPathNode.ts b/plugin-src/transformers/transformPathNode.ts index 30ba76f2..04347a35 100644 --- a/plugin-src/transformers/transformPathNode.ts +++ b/plugin-src/transformers/transformPathNode.ts @@ -1,4 +1,5 @@ import { + transformAutoLayoutPosition, transformBlend, transformConstraints, transformDimensionAndPosition, @@ -36,6 +37,7 @@ export const transformPathNode = ( ...transformBlend(node), ...transformProportion(node), ...transformLayoutSizing(node), + ...transformAutoLayoutPosition(node), ...transformConstraints(node) }; }; diff --git a/plugin-src/transformers/transformRectangleNode.ts b/plugin-src/transformers/transformRectangleNode.ts index 8c665e4e..066221dd 100644 --- a/plugin-src/transformers/transformRectangleNode.ts +++ b/plugin-src/transformers/transformRectangleNode.ts @@ -1,4 +1,5 @@ import { + transformAutoLayoutPosition, transformBlend, transformConstraints, transformCornerRadius, @@ -33,6 +34,7 @@ export const transformRectangleNode = ( ...transformBlend(node), ...transformProportion(node), ...transformLayoutSizing(node), + ...transformAutoLayoutPosition(node), ...transformCornerRadius(node), ...transformConstraints(node) }; diff --git a/plugin-src/transformers/transformTextNode.ts b/plugin-src/transformers/transformTextNode.ts index c0157ef5..6eb6adb0 100644 --- a/plugin-src/transformers/transformTextNode.ts +++ b/plugin-src/transformers/transformTextNode.ts @@ -1,4 +1,5 @@ import { + transformAutoLayoutPosition, transformBlend, transformConstraints, transformDimensionAndPosition, @@ -25,6 +26,7 @@ export const transformTextNode = (node: TextNode, baseX: number, baseY: number): ...transformBlend(node), ...transformProportion(node), ...transformLayoutSizing(node), + ...transformAutoLayoutPosition(node), ...transformStrokes(node), ...transformConstraints(node) }; From 73f6ea5fb74db90b09a07215be49c1bf5d512b7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20S=C3=A1nchez?= Date: Tue, 11 Jun 2024 13:56:44 +0200 Subject: [PATCH 4/8] fixes --- .../transformers/partials/transformLayout.ts | 6 ++--- ui-src/lib/types/shapes/layout.ts | 22 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/plugin-src/transformers/partials/transformLayout.ts b/plugin-src/transformers/partials/transformLayout.ts index cd8bb01d..a2562073 100644 --- a/plugin-src/transformers/partials/transformLayout.ts +++ b/plugin-src/transformers/partials/transformLayout.ts @@ -28,10 +28,10 @@ export const transformAutoLayout = (node: BaseFrameMixin): LayoutAttributes => { export const transformLayoutSizing = ( node: LayoutMixin -): Pick => { +): Pick => { return { - layoutItemHSizing: translateLayoutSizing(node.layoutSizingHorizontal), - layoutItemVSizing: translateLayoutSizing(node.layoutSizingVertical) + 'layoutItemH-Sizing': translateLayoutSizing(node.layoutSizingHorizontal), + 'layoutItemV-Sizing': translateLayoutSizing(node.layoutSizingVertical) }; }; diff --git a/ui-src/lib/types/shapes/layout.ts b/ui-src/lib/types/shapes/layout.ts index 07cf9ce3..b966def2 100644 --- a/ui-src/lib/types/shapes/layout.ts +++ b/ui-src/lib/types/shapes/layout.ts @@ -19,24 +19,24 @@ export type LayoutSizing = | typeof ITEM_SIZING_AUTO; export type LayoutChildAttributes = { - layoutItemMarginType?: + 'layoutItemMarginType'?: | 'simple' | 'multiple' | typeof ITEM_MARGIN_SIMPLE_TYPE | typeof ITEM_MARGIN_MULTIPLE_TYPE; - layoutItemMargin?: { + 'layoutItemMargin'?: { m1?: number; m2?: number; m3?: number; m4?: number; }; - layoutItemMaxH?: number; - layoutItemMinH?: number; - layoutItemMaxW?: number; - layoutItemMinW?: number; - layoutItemHSizing?: LayoutSizing; - layoutItemVSizing?: LayoutSizing; - layoutItemAlignSelf?: + 'layoutItemMaxH'?: number; + 'layoutItemMinH'?: number; + 'layoutItemMaxW'?: number; + 'layoutItemMinW'?: number; + 'layoutItemH-Sizing'?: LayoutSizing; + 'layoutItemV-Sizing'?: LayoutSizing; + 'layoutItemAlignSelf'?: | 'start' | 'end' | 'center' @@ -45,8 +45,8 @@ export type LayoutChildAttributes = { | typeof ITEM_ALIGN_SELF_END | typeof ITEM_ALIGN_SELF_CENTER | typeof ITEM_ALIGN_SELF_STRETCH; - layoutItemAbsolute?: boolean; - layoutItemZIndex?: number; + 'layoutItemAbsolute'?: boolean; + 'layoutItemZIndex'?: number; }; export type JustifyAlignContent = From c4c0ad362c3bb163cb68c47cd1cfd0e1f81e8571 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20S=C3=A1nchez?= Date: Tue, 11 Jun 2024 18:52:03 +0200 Subject: [PATCH 5/8] fixes --- plugin-src/transformers/partials/transformChildren.ts | 9 +++------ plugin-src/transformers/transformComponentNode.ts | 4 +++- plugin-src/transformers/transformFrameNode.ts | 6 +----- plugin-src/transformers/transformInstanceNode.ts | 7 +------ plugin-src/translators/translateLayout.ts | 4 ++-- 5 files changed, 10 insertions(+), 20 deletions(-) diff --git a/plugin-src/transformers/partials/transformChildren.ts b/plugin-src/transformers/partials/transformChildren.ts index a516ed8e..02093c59 100644 --- a/plugin-src/transformers/partials/transformChildren.ts +++ b/plugin-src/transformers/partials/transformChildren.ts @@ -9,16 +9,13 @@ const nodeActsAsMask = (node: SceneNode): boolean => { export const transformChildren = async ( node: ChildrenMixin, baseX: number = 0, - baseY: number = 0, - reverseChildrenOrder: boolean = false + baseY: number = 0 ): Promise => { const maskIndex = node.children.findIndex(nodeActsAsMask); const containsMask = maskIndex !== -1; - const children = reverseChildrenOrder ? node.children.slice().reverse() : node.children; - return { children: containsMask - ? await translateMaskChildren(children, maskIndex, baseX, baseY) - : await translateChildren(children, baseX, baseY) + ? await translateMaskChildren(node.children, maskIndex, baseX, baseY) + : await translateChildren(node.children, baseX, baseY) }; }; diff --git a/plugin-src/transformers/transformComponentNode.ts b/plugin-src/transformers/transformComponentNode.ts index 336444ba..3c994468 100644 --- a/plugin-src/transformers/transformComponentNode.ts +++ b/plugin-src/transformers/transformComponentNode.ts @@ -1,5 +1,6 @@ import { componentsLibrary } from '@plugin/ComponentLibrary'; import { + transformAutoLayout, transformAutoLayoutPosition, transformBlend, transformChildren, @@ -39,7 +40,8 @@ export const transformComponentNode = async ( ...transformCornerRadius(node), ...(await transformChildren(node, baseX + node.x, baseY + node.y)), ...transformDimensionAndPosition(node, baseX, baseY), - ...transformConstraints(node) + ...transformConstraints(node), + ...transformAutoLayout(node) }); return { diff --git a/plugin-src/transformers/transformFrameNode.ts b/plugin-src/transformers/transformFrameNode.ts index 68f6eb40..4be6e0b4 100644 --- a/plugin-src/transformers/transformFrameNode.ts +++ b/plugin-src/transformers/transformFrameNode.ts @@ -27,12 +27,8 @@ export const transformFrameNode = async ( baseY: number ): Promise => { let frameSpecificAttributes: Partial = {}; - let reverseChildrenOrder = false; if (!isSectionNode(node)) { - if (node.layoutMode !== 'NONE') { - reverseChildrenOrder = true; - } // Figma API does not expose strokes, blend modes, corner radius, or constraint proportions for sections, // they plan to add it in the future. Refactor this when available. frameSpecificAttributes = { @@ -57,7 +53,7 @@ export const transformFrameNode = async ( ...transformFigmaIds(node), ...transformFills(node), ...frameSpecificAttributes, - ...(await transformChildren(node, baseX + node.x, baseY + node.y, reverseChildrenOrder)), + ...(await transformChildren(node, baseX + node.x, baseY + node.y)), ...transformDimensionAndPosition(node, baseX, baseY), ...transformSceneNode(node) }; diff --git a/plugin-src/transformers/transformInstanceNode.ts b/plugin-src/transformers/transformInstanceNode.ts index 44be36f8..e4943b57 100644 --- a/plugin-src/transformers/transformInstanceNode.ts +++ b/plugin-src/transformers/transformInstanceNode.ts @@ -33,11 +33,6 @@ export const transformInstanceNode = async ( await registerExternalComponents(mainComponent); } - let reverseChildrenOrder = false; - if (node.layoutMode !== 'NONE') { - reverseChildrenOrder = true; - } - return { type: 'instance', name: node.name, @@ -57,7 +52,7 @@ export const transformInstanceNode = async ( ...transformDimensionAndPosition(node, baseX, baseY), ...transformConstraints(node), ...transformAutoLayout(node), - ...(await transformChildren(node, baseX + node.x, baseY + node.y, reverseChildrenOrder)) + ...(await transformChildren(node, baseX + node.x, baseY + node.y)) }; }; diff --git a/plugin-src/translators/translateLayout.ts b/plugin-src/translators/translateLayout.ts index 48dbe181..d2b595ca 100644 --- a/plugin-src/translators/translateLayout.ts +++ b/plugin-src/translators/translateLayout.ts @@ -17,9 +17,9 @@ type FigmaLayoutSizing = 'FIXED' | 'HUG' | 'FILL'; export const translateLayoutFlexDir = (layoutMode: FigmaLayoutMode): LayoutFlexDir | undefined => { switch (layoutMode) { case 'HORIZONTAL': - return 'row'; + return 'row-reverse'; case 'VERTICAL': - return 'column'; + return 'column-reverse'; default: return; } From e8e145d6bd7145d4f1526e7568d2e37e5e110fa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20S=C3=A1nchez?= Date: Thu, 13 Jun 2024 15:17:17 +0200 Subject: [PATCH 6/8] fixes --- plugin-src/findAllTextnodes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin-src/findAllTextnodes.ts b/plugin-src/findAllTextnodes.ts index 827d5a16..3856e225 100644 --- a/plugin-src/findAllTextnodes.ts +++ b/plugin-src/findAllTextnodes.ts @@ -15,7 +15,7 @@ export const findAllTextNodes = async () => { extractMissingFonts(node, fonts); } - sleep(0); + await sleep(0); } } From 18d1a5dab7ed52b4263bee7006568e042f718148 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20S=C3=A1nchez?= Date: Thu, 13 Jun 2024 15:56:38 +0200 Subject: [PATCH 7/8] fixes --- plugin-src/transformers/partials/transformLayout.ts | 6 +++++- plugin-src/translators/translateLayout.ts | 13 ++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/plugin-src/transformers/partials/transformLayout.ts b/plugin-src/transformers/partials/transformLayout.ts index a2562073..2a0e7baf 100644 --- a/plugin-src/transformers/partials/transformLayout.ts +++ b/plugin-src/transformers/partials/transformLayout.ts @@ -16,7 +16,11 @@ export const transformAutoLayout = (node: BaseFrameMixin): LayoutAttributes => { return { layout: node.layoutMode !== 'NONE' ? 'flex' : undefined, layoutFlexDir: translateLayoutFlexDir(node.layoutMode), - layoutGap: translateLayoutGap(node.layoutMode, node.itemSpacing), + layoutGap: translateLayoutGap( + node.layoutMode, + node.itemSpacing, + node.primaryAxisAlignItems === 'SPACE_BETWEEN' + ), layoutWrapType: translateLayoutWrapType(node.layoutWrap), layoutPadding: translateLayoutPadding(node), layoutJustifyContent: translateLayoutJustifyContent(node), diff --git a/plugin-src/translators/translateLayout.ts b/plugin-src/translators/translateLayout.ts index d2b595ca..f24544db 100644 --- a/plugin-src/translators/translateLayout.ts +++ b/plugin-src/translators/translateLayout.ts @@ -25,7 +25,18 @@ export const translateLayoutFlexDir = (layoutMode: FigmaLayoutMode): LayoutFlexD } }; -export const translateLayoutGap = (layoutMode: FigmaLayoutMode, itemSpacing: number): LayoutGap => { +export const translateLayoutGap = ( + layoutMode: FigmaLayoutMode, + itemSpacing: number, + auto: boolean = false +): LayoutGap => { + if (auto) { + return { + rowGap: 0, + columnGap: 0 + }; + } + return { rowGap: layoutMode === 'VERTICAL' ? itemSpacing : 0, columnGap: layoutMode === 'HORIZONTAL' ? itemSpacing : 0 From 9f5f05d2a92e8bf1cdf0750aadbd258bc839b1f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20S=C3=A1nchez?= Date: Fri, 14 Jun 2024 08:15:49 +0200 Subject: [PATCH 8/8] zIndex --- .../partials/transformChildren.ts | 19 ++++++++++-- .../transformers/partials/transformLayout.ts | 18 ++++++----- .../partials/transformVectorPaths.ts | 25 +++++++++++----- .../transformers/transformBooleanNode.ts | 11 +++---- .../transformers/transformComponentNode.ts | 13 ++++---- .../transformers/transformEllipseNode.ts | 11 +++---- plugin-src/transformers/transformFrameNode.ts | 13 ++++---- plugin-src/transformers/transformGroupNode.ts | 10 +++++-- .../transformers/transformInstanceNode.ts | 13 ++++---- plugin-src/transformers/transformPathNode.ts | 11 +++---- .../transformers/transformRectangleNode.ts | 11 +++---- plugin-src/transformers/transformSceneNode.ts | 23 +++++++------- plugin-src/transformers/transformTextNode.ts | 15 ++++++---- .../transformers/transformVectorNode.ts | 9 ++++-- plugin-src/translators/translateChildren.ts | 30 +++++++++++++++---- ui-src/lib/types/shapes/groupShape.ts | 4 ++- ui-src/lib/types/shapes/layout.ts | 2 +- 17 files changed, 152 insertions(+), 86 deletions(-) diff --git a/plugin-src/transformers/partials/transformChildren.ts b/plugin-src/transformers/partials/transformChildren.ts index 02093c59..f5f4b976 100644 --- a/plugin-src/transformers/partials/transformChildren.ts +++ b/plugin-src/transformers/partials/transformChildren.ts @@ -6,16 +6,29 @@ const nodeActsAsMask = (node: SceneNode): boolean => { return 'isMask' in node && node.isMask; }; +const nodeIsLayoutWithItemReverseZIndex = (node: ChildrenMixin): boolean => { + return !!('itemReverseZIndex' in node && node.itemReverseZIndex); +}; + export const transformChildren = async ( node: ChildrenMixin, baseX: number = 0, - baseY: number = 0 + baseY: number = 0, + zIndex: number = 0 ): Promise => { const maskIndex = node.children.findIndex(nodeActsAsMask); const containsMask = maskIndex !== -1; + const itemReverseZIndex = nodeIsLayoutWithItemReverseZIndex(node); return { children: containsMask - ? await translateMaskChildren(node.children, maskIndex, baseX, baseY) - : await translateChildren(node.children, baseX, baseY) + ? await translateMaskChildren( + node.children, + maskIndex, + baseX, + baseY, + zIndex, + itemReverseZIndex + ) + : await translateChildren(node.children, baseX, baseY, zIndex, itemReverseZIndex) }; }; diff --git a/plugin-src/transformers/partials/transformLayout.ts b/plugin-src/transformers/partials/transformLayout.ts index 2a0e7baf..b61c423a 100644 --- a/plugin-src/transformers/partials/transformLayout.ts +++ b/plugin-src/transformers/partials/transformLayout.ts @@ -30,19 +30,23 @@ export const transformAutoLayout = (node: BaseFrameMixin): LayoutAttributes => { }; }; -export const transformLayoutSizing = ( +export const transformLayoutAttributes = ( node: LayoutMixin -): Pick => { +): Pick< + LayoutChildAttributes, + 'layoutItemH-Sizing' | 'layoutItemV-Sizing' | 'layoutItemAbsolute' +> => { return { 'layoutItemH-Sizing': translateLayoutSizing(node.layoutSizingHorizontal), - 'layoutItemV-Sizing': translateLayoutSizing(node.layoutSizingVertical) + 'layoutItemV-Sizing': translateLayoutSizing(node.layoutSizingVertical), + 'layoutItemAbsolute': node.layoutPositioning === 'ABSOLUTE' }; }; -export const transformAutoLayoutPosition = ( - node: AutoLayoutChildrenMixin -): Pick => { +export const transformLayoutItemZIndex = ( + zIndex: number +): Pick => { return { - layoutItemAbsolute: node.layoutPositioning === 'ABSOLUTE' + 'layoutItemZ-Index': zIndex }; }; diff --git a/plugin-src/transformers/partials/transformVectorPaths.ts b/plugin-src/transformers/partials/transformVectorPaths.ts index 0ebdfdf2..6e8d3add 100644 --- a/plugin-src/transformers/partials/transformVectorPaths.ts +++ b/plugin-src/transformers/partials/transformVectorPaths.ts @@ -1,11 +1,11 @@ import { parseSVG } from 'svg-path-parser'; import { - transformAutoLayoutPosition, transformBlend, transformDimensionAndPositionFromVectorPath, transformEffects, - transformLayoutSizing, + transformLayoutAttributes, + transformLayoutItemZIndex, transformProportion, transformSceneNode, transformStrokesFromVector @@ -35,7 +35,8 @@ export const transformVectorPathsAsContent = ( export const transformVectorPaths = ( node: VectorNode, baseX: number, - baseY: number + baseY: number, + zIndex: number ): PathShape[] => { const pathShapes = node.vectorPaths .filter((vectorPath, index) => { @@ -45,7 +46,14 @@ export const transformVectorPaths = ( ); }) .map((vectorPath, index) => - transformVectorPath(node, vectorPath, (node.vectorNetwork.regions ?? [])[index], baseX, baseY) + transformVectorPath( + node, + vectorPath, + (node.vectorNetwork.regions ?? [])[index], + baseX, + baseY, + zIndex + ) ); const geometryShapes = node.fillGeometry @@ -55,7 +63,7 @@ export const transformVectorPaths = ( vectorPath => normalizePath(vectorPath.data) === normalizePath(geometry.data) ) ) - .map(geometry => transformVectorPath(node, geometry, undefined, baseX, baseY)); + .map(geometry => transformVectorPath(node, geometry, undefined, baseX, baseY, zIndex)); return [...geometryShapes, ...pathShapes]; }; @@ -92,7 +100,8 @@ const transformVectorPath = ( vectorPath: VectorPath, vectorRegion: VectorRegion | undefined, baseX: number, - baseY: number + baseY: number, + zIndex: number ): PathShape => { const normalizedPaths = parseSVG(vectorPath.data); @@ -107,13 +116,13 @@ const transformVectorPath = ( }, constraintsH: 'scale', constraintsV: 'scale', + ...transformLayoutItemZIndex(zIndex), ...transformStrokesFromVector(node, normalizedPaths, vectorRegion), ...transformEffects(node), ...transformDimensionAndPositionFromVectorPath(vectorPath, baseX, baseY), ...transformSceneNode(node), ...transformBlend(node), ...transformProportion(node), - ...transformLayoutSizing(node), - ...transformAutoLayoutPosition(node) + ...transformLayoutAttributes(node) }; }; diff --git a/plugin-src/transformers/transformBooleanNode.ts b/plugin-src/transformers/transformBooleanNode.ts index 4cb18e46..f2c1b85c 100644 --- a/plugin-src/transformers/transformBooleanNode.ts +++ b/plugin-src/transformers/transformBooleanNode.ts @@ -1,12 +1,12 @@ import { - transformAutoLayoutPosition, transformBlend, transformChildren, transformDimensionAndPosition, transformEffects, transformFigmaIds, transformFills, - transformLayoutSizing, + transformLayoutAttributes, + transformLayoutItemZIndex, transformProportion, transformSceneNode, transformStrokes @@ -18,12 +18,14 @@ import { BoolShape } from '@ui/lib/types/shapes/boolShape'; export const transformBooleanNode = async ( node: BooleanOperationNode, baseX: number, - baseY: number + baseY: number, + zIndex: number ): Promise => { return { type: 'bool', name: node.name, boolType: translateBoolType(node.booleanOperation), + ...transformLayoutItemZIndex(zIndex), ...transformFigmaIds(node), ...(await transformChildren(node, baseX, baseY)), ...transformFills(node), @@ -33,7 +35,6 @@ export const transformBooleanNode = async ( ...transformSceneNode(node), ...transformBlend(node), ...transformProportion(node), - ...transformLayoutSizing(node), - ...transformAutoLayoutPosition(node) + ...transformLayoutAttributes(node) }; }; diff --git a/plugin-src/transformers/transformComponentNode.ts b/plugin-src/transformers/transformComponentNode.ts index 3c994468..e3a44d70 100644 --- a/plugin-src/transformers/transformComponentNode.ts +++ b/plugin-src/transformers/transformComponentNode.ts @@ -1,7 +1,6 @@ import { componentsLibrary } from '@plugin/ComponentLibrary'; import { transformAutoLayout, - transformAutoLayoutPosition, transformBlend, transformChildren, transformConstraints, @@ -10,7 +9,8 @@ import { transformEffects, transformFigmaIds, transformFills, - transformLayoutSizing, + transformLayoutAttributes, + transformLayoutItemZIndex, transformProportion, transformSceneNode, transformStrokes @@ -21,13 +21,15 @@ import { ComponentRoot } from '@ui/types'; export const transformComponentNode = async ( node: ComponentNode, baseX: number, - baseY: number + baseY: number, + zIndex: number ): Promise => { componentsLibrary.register(node.id, { type: 'component', name: node.name, path: node.parent?.type === 'COMPONENT_SET' ? node.parent.name : '', showContent: !node.clipsContent, + ...transformLayoutItemZIndex(zIndex), ...transformFigmaIds(node), ...transformFills(node), ...transformEffects(node), @@ -35,10 +37,9 @@ export const transformComponentNode = async ( ...transformSceneNode(node), ...transformBlend(node), ...transformProportion(node), - ...transformLayoutSizing(node), - ...transformAutoLayoutPosition(node), + ...transformLayoutAttributes(node), ...transformCornerRadius(node), - ...(await transformChildren(node, baseX + node.x, baseY + node.y)), + ...(await transformChildren(node, baseX + node.x, baseY + node.y, zIndex)), ...transformDimensionAndPosition(node, baseX, baseY), ...transformConstraints(node), ...transformAutoLayout(node) diff --git a/plugin-src/transformers/transformEllipseNode.ts b/plugin-src/transformers/transformEllipseNode.ts index 09853b27..eedabc55 100644 --- a/plugin-src/transformers/transformEllipseNode.ts +++ b/plugin-src/transformers/transformEllipseNode.ts @@ -1,12 +1,12 @@ import { - transformAutoLayoutPosition, transformBlend, transformConstraints, transformDimension, transformEffects, transformFigmaIds, transformFills, - transformLayoutSizing, + transformLayoutAttributes, + transformLayoutItemZIndex, transformProportion, transformRotationAndPosition, transformSceneNode, @@ -18,11 +18,13 @@ import { CircleShape } from '@ui/lib/types/shapes/circleShape'; export const transformEllipseNode = ( node: EllipseNode, baseX: number, - baseY: number + baseY: number, + zIndex: number ): CircleShape => { return { type: 'circle', name: node.name, + ...transformLayoutItemZIndex(zIndex), ...transformFigmaIds(node), ...transformFills(node), ...transformEffects(node), @@ -32,8 +34,7 @@ export const transformEllipseNode = ( ...transformSceneNode(node), ...transformBlend(node), ...transformProportion(node), - ...transformLayoutSizing(node), - ...transformAutoLayoutPosition(node), + ...transformLayoutAttributes(node), ...transformConstraints(node) }; }; diff --git a/plugin-src/transformers/transformFrameNode.ts b/plugin-src/transformers/transformFrameNode.ts index 4be6e0b4..1b04d490 100644 --- a/plugin-src/transformers/transformFrameNode.ts +++ b/plugin-src/transformers/transformFrameNode.ts @@ -1,6 +1,5 @@ import { transformAutoLayout, - transformAutoLayoutPosition, transformBlend, transformChildren, transformConstraints, @@ -9,7 +8,8 @@ import { transformEffects, transformFigmaIds, transformFills, - transformLayoutSizing, + transformLayoutAttributes, + transformLayoutItemZIndex, transformProportion, transformSceneNode, transformStrokes @@ -24,7 +24,8 @@ const isSectionNode = (node: FrameNode | SectionNode | ComponentSetNode): node i export const transformFrameNode = async ( node: FrameNode | SectionNode | ComponentSetNode, baseX: number, - baseY: number + baseY: number, + zIndex: number ): Promise => { let frameSpecificAttributes: Partial = {}; @@ -37,8 +38,7 @@ export const transformFrameNode = async ( // @see: https://forum.figma.com/t/add-a-blendmode-property-for-sectionnode/58560 ...transformBlend(node), ...transformProportion(node), - ...transformLayoutSizing(node), - ...transformAutoLayoutPosition(node), + ...transformLayoutAttributes(node), ...transformCornerRadius(node), ...transformEffects(node), ...transformConstraints(node), @@ -50,10 +50,11 @@ export const transformFrameNode = async ( type: 'frame', name: node.name, showContent: isSectionNode(node) ? true : !node.clipsContent, + ...transformLayoutItemZIndex(zIndex), ...transformFigmaIds(node), ...transformFills(node), ...frameSpecificAttributes, - ...(await transformChildren(node, baseX + node.x, baseY + node.y)), + ...(await transformChildren(node, baseX + node.x, baseY + node.y, zIndex)), ...transformDimensionAndPosition(node, baseX, baseY), ...transformSceneNode(node) }; diff --git a/plugin-src/transformers/transformGroupNode.ts b/plugin-src/transformers/transformGroupNode.ts index 2968ccd1..f341fab8 100644 --- a/plugin-src/transformers/transformGroupNode.ts +++ b/plugin-src/transformers/transformGroupNode.ts @@ -3,6 +3,7 @@ import { transformDimensionAndPosition, transformEffects, transformFigmaIds, + transformLayoutItemZIndex, transformSceneNode } from '@plugin/transformers/partials'; import { transformChildren } from '@plugin/transformers/partials'; @@ -12,11 +13,12 @@ import { GroupShape } from '@ui/lib/types/shapes/groupShape'; export const transformGroupNode = async ( node: GroupNode, baseX: number, - baseY: number + baseY: number, + zIndex: number ): Promise => { return { ...transformFigmaIds(node), - ...transformGroupNodeLike(node, baseX, baseY), + ...transformGroupNodeLike(node, baseX, baseY, zIndex), ...transformEffects(node), ...transformBlend(node), ...(await transformChildren(node, baseX, baseY)) @@ -26,11 +28,13 @@ export const transformGroupNode = async ( export const transformGroupNodeLike = ( node: BaseNodeMixin & DimensionAndPositionMixin & SceneNodeMixin, baseX: number, - baseY: number + baseY: number, + zIndex: number ): GroupShape => { return { type: 'group', name: node.name, + ...transformLayoutItemZIndex(zIndex), ...transformDimensionAndPosition(node, baseX, baseY), ...transformSceneNode(node) }; diff --git a/plugin-src/transformers/transformInstanceNode.ts b/plugin-src/transformers/transformInstanceNode.ts index 3500ebb9..4702175a 100644 --- a/plugin-src/transformers/transformInstanceNode.ts +++ b/plugin-src/transformers/transformInstanceNode.ts @@ -1,7 +1,6 @@ import { remoteComponentLibrary } from '@plugin/RemoteComponentLibrary'; import { transformAutoLayout, - transformAutoLayoutPosition, transformBlend, transformChildren, transformConstraints, @@ -10,7 +9,8 @@ import { transformEffects, transformFigmaIds, transformFills, - transformLayoutSizing, + transformLayoutAttributes, + transformLayoutItemZIndex, transformProportion, transformSceneNode, transformStrokes @@ -21,7 +21,8 @@ import { ComponentInstance } from '@ui/types'; export const transformInstanceNode = async ( node: InstanceNode, baseX: number, - baseY: number + baseY: number, + zIndex: number ): Promise => { const mainComponent = await node.getMainComponentAsync(); @@ -39,6 +40,7 @@ export const transformInstanceNode = async ( mainComponentFigmaId: mainComponent.id, isComponentRoot: isComponentRoot(node), showContent: !node.clipsContent, + ...transformLayoutItemZIndex(zIndex), ...transformFigmaIds(node), ...transformFills(node), ...transformEffects(node), @@ -46,13 +48,12 @@ export const transformInstanceNode = async ( ...transformSceneNode(node), ...transformBlend(node), ...transformProportion(node), - ...transformLayoutSizing(node), - ...transformAutoLayoutPosition(node), + ...transformLayoutAttributes(node), ...transformCornerRadius(node), ...transformDimensionAndPosition(node, baseX, baseY), ...transformConstraints(node), ...transformAutoLayout(node), - ...(await transformChildren(node, baseX + node.x, baseY + node.y)) + ...(await transformChildren(node, baseX + node.x, baseY + node.y, zIndex)) }; }; diff --git a/plugin-src/transformers/transformPathNode.ts b/plugin-src/transformers/transformPathNode.ts index 04347a35..60b83e03 100644 --- a/plugin-src/transformers/transformPathNode.ts +++ b/plugin-src/transformers/transformPathNode.ts @@ -1,12 +1,12 @@ import { - transformAutoLayoutPosition, transformBlend, transformConstraints, transformDimensionAndPosition, transformEffects, transformFigmaIds, transformFills, - transformLayoutSizing, + transformLayoutAttributes, + transformLayoutItemZIndex, transformProportion, transformSceneNode, transformStrokes, @@ -22,11 +22,13 @@ const hasFillGeometry = (node: StarNode | LineNode | PolygonNode): boolean => { export const transformPathNode = ( node: StarNode | LineNode | PolygonNode, baseX: number, - baseY: number + baseY: number, + zIndex: number ): PathShape => { return { type: 'path', name: node.name, + ...transformLayoutItemZIndex(zIndex), ...transformFigmaIds(node), ...(hasFillGeometry(node) ? transformFills(node) : []), ...transformStrokes(node), @@ -36,8 +38,7 @@ export const transformPathNode = ( ...transformSceneNode(node), ...transformBlend(node), ...transformProportion(node), - ...transformLayoutSizing(node), - ...transformAutoLayoutPosition(node), + ...transformLayoutAttributes(node), ...transformConstraints(node) }; }; diff --git a/plugin-src/transformers/transformRectangleNode.ts b/plugin-src/transformers/transformRectangleNode.ts index 066221dd..ebd3df6f 100644 --- a/plugin-src/transformers/transformRectangleNode.ts +++ b/plugin-src/transformers/transformRectangleNode.ts @@ -1,5 +1,4 @@ import { - transformAutoLayoutPosition, transformBlend, transformConstraints, transformCornerRadius, @@ -7,7 +6,8 @@ import { transformEffects, transformFigmaIds, transformFills, - transformLayoutSizing, + transformLayoutAttributes, + transformLayoutItemZIndex, transformProportion, transformRotationAndPosition, transformSceneNode, @@ -19,11 +19,13 @@ import { RectShape } from '@ui/lib/types/shapes/rectShape'; export const transformRectangleNode = ( node: RectangleNode, baseX: number, - baseY: number + baseY: number, + zIndex: number ): RectShape => { return { type: 'rect', name: node.name, + ...transformLayoutItemZIndex(zIndex), ...transformFigmaIds(node), ...transformFills(node), ...transformEffects(node), @@ -33,8 +35,7 @@ export const transformRectangleNode = ( ...transformSceneNode(node), ...transformBlend(node), ...transformProportion(node), - ...transformLayoutSizing(node), - ...transformAutoLayoutPosition(node), + ...transformLayoutAttributes(node), ...transformCornerRadius(node), ...transformConstraints(node) }; diff --git a/plugin-src/transformers/transformSceneNode.ts b/plugin-src/transformers/transformSceneNode.ts index 93af76e0..733684a3 100644 --- a/plugin-src/transformers/transformSceneNode.ts +++ b/plugin-src/transformers/transformSceneNode.ts @@ -16,7 +16,8 @@ import { export const transformSceneNode = async ( node: SceneNode, baseX: number = 0, - baseY: number = 0 + baseY: number = 0, + zIndex: number = 0 ): Promise => { let penpotNode: PenpotNode | undefined; @@ -27,38 +28,38 @@ export const transformSceneNode = async ( switch (node.type) { case 'RECTANGLE': - penpotNode = transformRectangleNode(node, baseX, baseY); + penpotNode = transformRectangleNode(node, baseX, baseY, zIndex); break; case 'ELLIPSE': - penpotNode = transformEllipseNode(node, baseX, baseY); + penpotNode = transformEllipseNode(node, baseX, baseY, zIndex); break; case 'SECTION': case 'FRAME': case 'COMPONENT_SET': - penpotNode = await transformFrameNode(node, baseX, baseY); + penpotNode = await transformFrameNode(node, baseX, baseY, zIndex); break; case 'GROUP': - penpotNode = await transformGroupNode(node, baseX, baseY); + penpotNode = await transformGroupNode(node, baseX, baseY, zIndex); break; case 'TEXT': - penpotNode = transformTextNode(node, baseX, baseY); + penpotNode = transformTextNode(node, baseX, baseY, zIndex); break; case 'VECTOR': - penpotNode = transformVectorNode(node, baseX, baseY); + penpotNode = transformVectorNode(node, baseX, baseY, zIndex); break; case 'STAR': case 'POLYGON': case 'LINE': - penpotNode = transformPathNode(node, baseX, baseY); + penpotNode = transformPathNode(node, baseX, baseY, zIndex); break; case 'BOOLEAN_OPERATION': - penpotNode = await transformBooleanNode(node, baseX, baseY); + penpotNode = await transformBooleanNode(node, baseX, baseY, zIndex); break; case 'COMPONENT': - penpotNode = await transformComponentNode(node, baseX, baseY); + penpotNode = await transformComponentNode(node, baseX, baseY, zIndex); break; case 'INSTANCE': - penpotNode = await transformInstanceNode(node, baseX, baseY); + penpotNode = await transformInstanceNode(node, baseX, baseY, zIndex); break; } diff --git a/plugin-src/transformers/transformTextNode.ts b/plugin-src/transformers/transformTextNode.ts index 6eb6adb0..351236ff 100644 --- a/plugin-src/transformers/transformTextNode.ts +++ b/plugin-src/transformers/transformTextNode.ts @@ -1,11 +1,11 @@ import { - transformAutoLayoutPosition, transformBlend, transformConstraints, transformDimensionAndPosition, transformEffects, transformFigmaIds, - transformLayoutSizing, + transformLayoutAttributes, + transformLayoutItemZIndex, transformProportion, transformSceneNode, transformStrokes, @@ -14,10 +14,16 @@ import { import { TextShape } from '@ui/lib/types/shapes/textShape'; -export const transformTextNode = (node: TextNode, baseX: number, baseY: number): TextShape => { +export const transformTextNode = ( + node: TextNode, + baseX: number, + baseY: number, + zIndex: number +): TextShape => { return { type: 'text', name: node.name, + ...transformLayoutItemZIndex(zIndex), ...transformFigmaIds(node), ...transformText(node), ...transformDimensionAndPosition(node, baseX, baseY), @@ -25,8 +31,7 @@ export const transformTextNode = (node: TextNode, baseX: number, baseY: number): ...transformSceneNode(node), ...transformBlend(node), ...transformProportion(node), - ...transformLayoutSizing(node), - ...transformAutoLayoutPosition(node), + ...transformLayoutAttributes(node), ...transformStrokes(node), ...transformConstraints(node) }; diff --git a/plugin-src/transformers/transformVectorNode.ts b/plugin-src/transformers/transformVectorNode.ts index 82a2c268..7b4b0110 100644 --- a/plugin-src/transformers/transformVectorNode.ts +++ b/plugin-src/transformers/transformVectorNode.ts @@ -1,6 +1,7 @@ import { transformConstraints, transformFigmaIds, + transformLayoutItemZIndex, transformVectorPaths } from '@plugin/transformers/partials'; @@ -18,21 +19,23 @@ import { transformGroupNodeLike } from '.'; export const transformVectorNode = ( node: VectorNode, baseX: number, - baseY: number + baseY: number, + zIndex: number ): GroupShape | PathShape => { - const children = transformVectorPaths(node, baseX, baseY); + const children = transformVectorPaths(node, baseX, baseY, zIndex); if (children.length === 1) { return { ...children[0], name: node.name, + ...transformLayoutItemZIndex(zIndex), ...transformFigmaIds(node), ...transformConstraints(node) }; } return { - ...transformGroupNodeLike(node, baseX, baseY), + ...transformGroupNodeLike(node, baseX, baseY, zIndex), ...transformFigmaIds(node), ...transformConstraints(node), children diff --git a/plugin-src/translators/translateChildren.ts b/plugin-src/translators/translateChildren.ts index f11ce553..e59c2e35 100644 --- a/plugin-src/translators/translateChildren.ts +++ b/plugin-src/translators/translateChildren.ts @@ -1,6 +1,6 @@ import { remoteComponentLibrary } from '@plugin/RemoteComponentLibrary'; import { transformGroupNodeLike, transformSceneNode } from '@plugin/transformers'; -import { transformMaskFigmaIds } from '@plugin/transformers/partials'; +import { transformLayoutItemZIndex, transformMaskFigmaIds } from '@plugin/transformers/partials'; import { sleep } from '@plugin/utils'; import { PenpotNode } from '@ui/types'; @@ -18,15 +18,26 @@ export const translateMaskChildren = async ( children: readonly SceneNode[], maskIndex: number, baseX: number, - baseY: number + baseY: number, + zIndex: number, + itemReverseZIndex: boolean = false ): Promise => { const maskChild = children[maskIndex]; - const unmaskedChildren = await translateChildren(children.slice(0, maskIndex), baseX, baseY); + const unmaskedChildren = await translateChildren( + children.slice(0, maskIndex), + baseX, + baseY, + zIndex, + itemReverseZIndex + ); const maskedChildren = await translateChildren(children.slice(maskIndex), baseX, baseY); + const lastZIndex = itemReverseZIndex ? zIndex - unmaskedChildren.length - 1 : zIndex; + const maskGroup = { ...transformMaskFigmaIds(maskChild), - ...transformGroupNodeLike(maskChild, baseX, baseY), + ...transformGroupNodeLike(maskChild, baseX, baseY, zIndex), + ...transformLayoutItemZIndex(lastZIndex), children: maskedChildren, maskedGroup: true }; @@ -37,12 +48,19 @@ export const translateMaskChildren = async ( export const translateChildren = async ( children: readonly SceneNode[], baseX: number = 0, - baseY: number = 0 + baseY: number = 0, + zIndex: number = 0, + itemReverseZIndex: boolean = false ): Promise => { const transformedChildren: PenpotNode[] = []; for (const child of children) { - const penpotNode = await transformSceneNode(child, baseX, baseY); + const penpotNode = await transformSceneNode( + child, + baseX, + baseY, + itemReverseZIndex ? zIndex-- : zIndex + ); if (penpotNode) transformedChildren.push(penpotNode); diff --git a/ui-src/lib/types/shapes/groupShape.ts b/ui-src/lib/types/shapes/groupShape.ts index 834a9d46..22bf05f1 100644 --- a/ui-src/lib/types/shapes/groupShape.ts +++ b/ui-src/lib/types/shapes/groupShape.ts @@ -1,3 +1,4 @@ +import { LayoutChildAttributes } from '@ui/lib/types/shapes/layout'; import { ShapeAttributes, ShapeBaseAttributes, @@ -10,7 +11,8 @@ export type GroupShape = ShapeBaseAttributes & ShapeGeomAttributes & ShapeAttributes & GroupAttributes & - Children; + Children & + LayoutChildAttributes; type GroupAttributes = { type?: 'group'; diff --git a/ui-src/lib/types/shapes/layout.ts b/ui-src/lib/types/shapes/layout.ts index b966def2..064b6aff 100644 --- a/ui-src/lib/types/shapes/layout.ts +++ b/ui-src/lib/types/shapes/layout.ts @@ -46,7 +46,7 @@ export type LayoutChildAttributes = { | typeof ITEM_ALIGN_SELF_CENTER | typeof ITEM_ALIGN_SELF_STRETCH; 'layoutItemAbsolute'?: boolean; - 'layoutItemZIndex'?: number; + 'layoutItemZ-Index'?: number; }; export type JustifyAlignContent =