diff --git a/plugin-src/transformers/partials/transformChildren.ts b/plugin-src/transformers/partials/transformChildren.ts index 6a94471..a516ed8 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 29f7245..41223a8 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 25428cc..9c2a865 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 ef414a3..fe76433 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 5d654be..84d1486 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 b2f165a..53e7b24 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 fb2b37f..59a775c 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 2edd699..c07a2b3 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 1157e33..30ba76f 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 4e3e444..8c665e4 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 21a6b47..c0157ef 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 b8e11a4..48dbe18 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 875fdf3..07cf9ce 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'