0
Fork 0
mirror of https://github.com/penpot/penpot-exporter-figma-plugin.git synced 2025-03-11 15:21:26 -05:00

Autolayout (#151)

* wip

* added layout sizing

* layout positioning

* fixes

* fixes

* fixes

* fixes

* fixes

* fixes
This commit is contained in:
Alex Sánchez 2024-06-14 10:18:34 +02:00 committed by GitHub
parent 8d35f58f89
commit b85a4f7279
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 257 additions and 54 deletions

View file

@ -0,0 +1,5 @@
---
"penpot-exporter": minor
---
Added support for autolayout

View file

@ -15,7 +15,7 @@ export const findAllTextNodes = async () => {
extractMissingFonts(node, fonts); extractMissingFonts(node, fonts);
} }
sleep(0); await sleep(0);
} }
} }

View file

@ -6,6 +6,7 @@ export * from './transformDimensionAndPosition';
export * from './transformEffects'; export * from './transformEffects';
export * from './transformFigmaIds'; export * from './transformFigmaIds';
export * from './transformFills'; export * from './transformFills';
export * from './transformLayout';
export * from './transformProportion'; export * from './transformProportion';
export * from './transformRotationAndPosition'; export * from './transformRotationAndPosition';
export * from './transformSceneNode'; export * from './transformSceneNode';

View file

@ -0,0 +1,44 @@
import {
translateLayoutAlignContent,
translateLayoutAlignItems,
translateLayoutFlexDir,
translateLayoutGap,
translateLayoutJustifyContent,
translateLayoutJustifyItems,
translateLayoutPadding,
translateLayoutSizing,
translateLayoutWrapType
} from '@plugin/translators';
import { LayoutAttributes, LayoutChildAttributes } from '@ui/lib/types/shapes/layout';
export const transformAutoLayout = (node: BaseFrameMixin): LayoutAttributes => {
return {
layout: node.layoutMode !== 'NONE' ? 'flex' : undefined,
layoutFlexDir: translateLayoutFlexDir(node.layoutMode),
layoutGap: translateLayoutGap(
node.layoutMode,
node.itemSpacing,
node.primaryAxisAlignItems === 'SPACE_BETWEEN'
),
layoutWrapType: translateLayoutWrapType(node.layoutWrap),
layoutPadding: translateLayoutPadding(node),
layoutJustifyContent: translateLayoutJustifyContent(node),
layoutJustifyItems: translateLayoutJustifyItems(node),
layoutAlignContent: translateLayoutAlignContent(node),
layoutAlignItems: translateLayoutAlignItems(node)
};
};
export const transformLayoutAttributes = (
node: LayoutMixin
): Pick<
LayoutChildAttributes,
'layoutItemH-Sizing' | 'layoutItemV-Sizing' | 'layoutItemAbsolute'
> => {
return {
'layoutItemH-Sizing': translateLayoutSizing(node.layoutSizingHorizontal),
'layoutItemV-Sizing': translateLayoutSizing(node.layoutSizingVertical),
'layoutItemAbsolute': node.layoutPositioning === 'ABSOLUTE'
};
};

View file

@ -4,6 +4,7 @@ import {
transformBlend, transformBlend,
transformDimensionAndPositionFromVectorPath, transformDimensionAndPositionFromVectorPath,
transformEffects, transformEffects,
transformLayoutAttributes,
transformProportion, transformProportion,
transformSceneNode, transformSceneNode,
transformStrokesFromVector transformStrokesFromVector
@ -110,6 +111,7 @@ const transformVectorPath = (
...transformDimensionAndPositionFromVectorPath(vectorPath, baseX, baseY), ...transformDimensionAndPositionFromVectorPath(vectorPath, baseX, baseY),
...transformSceneNode(node), ...transformSceneNode(node),
...transformBlend(node), ...transformBlend(node),
...transformProportion(node) ...transformProportion(node),
...transformLayoutAttributes(node)
}; };
}; };

View file

@ -5,6 +5,7 @@ import {
transformEffects, transformEffects,
transformFigmaIds, transformFigmaIds,
transformFills, transformFills,
transformLayoutAttributes,
transformProportion, transformProportion,
transformSceneNode, transformSceneNode,
transformStrokes transformStrokes
@ -30,6 +31,7 @@ export const transformBooleanNode = async (
...transformDimensionAndPosition(node, baseX, baseY), ...transformDimensionAndPosition(node, baseX, baseY),
...transformSceneNode(node), ...transformSceneNode(node),
...transformBlend(node), ...transformBlend(node),
...transformProportion(node) ...transformProportion(node),
...transformLayoutAttributes(node)
}; };
}; };

View file

@ -1,5 +1,6 @@
import { componentsLibrary } from '@plugin/ComponentLibrary'; import { componentsLibrary } from '@plugin/ComponentLibrary';
import { import {
transformAutoLayout,
transformBlend, transformBlend,
transformChildren, transformChildren,
transformConstraints, transformConstraints,
@ -8,6 +9,7 @@ import {
transformEffects, transformEffects,
transformFigmaIds, transformFigmaIds,
transformFills, transformFills,
transformLayoutAttributes,
transformProportion, transformProportion,
transformSceneNode, transformSceneNode,
transformStrokes transformStrokes
@ -32,10 +34,12 @@ export const transformComponentNode = async (
...transformSceneNode(node), ...transformSceneNode(node),
...transformBlend(node), ...transformBlend(node),
...transformProportion(node), ...transformProportion(node),
...transformLayoutAttributes(node),
...transformCornerRadius(node), ...transformCornerRadius(node),
...(await transformChildren(node, baseX + node.x, baseY + node.y)), ...(await transformChildren(node, baseX + node.x, baseY + node.y)),
...transformDimensionAndPosition(node, baseX, baseY), ...transformDimensionAndPosition(node, baseX, baseY),
...transformConstraints(node) ...transformConstraints(node),
...transformAutoLayout(node)
}); });
return { return {

View file

@ -5,6 +5,7 @@ import {
transformEffects, transformEffects,
transformFigmaIds, transformFigmaIds,
transformFills, transformFills,
transformLayoutAttributes,
transformProportion, transformProportion,
transformRotationAndPosition, transformRotationAndPosition,
transformSceneNode, transformSceneNode,
@ -30,6 +31,7 @@ export const transformEllipseNode = (
...transformSceneNode(node), ...transformSceneNode(node),
...transformBlend(node), ...transformBlend(node),
...transformProportion(node), ...transformProportion(node),
...transformLayoutAttributes(node),
...transformConstraints(node) ...transformConstraints(node)
}; };
}; };

View file

@ -1,4 +1,5 @@
import { import {
transformAutoLayout,
transformBlend, transformBlend,
transformChildren, transformChildren,
transformConstraints, transformConstraints,
@ -7,6 +8,7 @@ import {
transformEffects, transformEffects,
transformFigmaIds, transformFigmaIds,
transformFills, transformFills,
transformLayoutAttributes,
transformProportion, transformProportion,
transformSceneNode, transformSceneNode,
transformStrokes transformStrokes
@ -34,9 +36,11 @@ export const transformFrameNode = async (
// @see: https://forum.figma.com/t/add-a-blendmode-property-for-sectionnode/58560 // @see: https://forum.figma.com/t/add-a-blendmode-property-for-sectionnode/58560
...transformBlend(node), ...transformBlend(node),
...transformProportion(node), ...transformProportion(node),
...transformLayoutAttributes(node),
...transformCornerRadius(node), ...transformCornerRadius(node),
...transformEffects(node), ...transformEffects(node),
...transformConstraints(node) ...transformConstraints(node),
...transformAutoLayout(node)
}; };
} }

View file

@ -1,5 +1,6 @@
import { remoteComponentLibrary } from '@plugin/RemoteComponentLibrary'; import { remoteComponentLibrary } from '@plugin/RemoteComponentLibrary';
import { import {
transformAutoLayout,
transformBlend, transformBlend,
transformChildren, transformChildren,
transformConstraints, transformConstraints,
@ -8,6 +9,7 @@ import {
transformEffects, transformEffects,
transformFigmaIds, transformFigmaIds,
transformFills, transformFills,
transformLayoutAttributes,
transformProportion, transformProportion,
transformSceneNode, transformSceneNode,
transformStrokes transformStrokes
@ -43,9 +45,11 @@ export const transformInstanceNode = async (
...transformSceneNode(node), ...transformSceneNode(node),
...transformBlend(node), ...transformBlend(node),
...transformProportion(node), ...transformProportion(node),
...transformLayoutAttributes(node),
...transformCornerRadius(node), ...transformCornerRadius(node),
...transformDimensionAndPosition(node, baseX, baseY), ...transformDimensionAndPosition(node, baseX, baseY),
...transformConstraints(node), ...transformConstraints(node),
...transformAutoLayout(node),
...(await transformChildren(node, baseX + node.x, baseY + node.y)) ...(await transformChildren(node, baseX + node.x, baseY + node.y))
}; };
}; };

View file

@ -5,6 +5,7 @@ import {
transformEffects, transformEffects,
transformFigmaIds, transformFigmaIds,
transformFills, transformFills,
transformLayoutAttributes,
transformProportion, transformProportion,
transformSceneNode, transformSceneNode,
transformStrokes, transformStrokes,
@ -34,6 +35,7 @@ export const transformPathNode = (
...transformSceneNode(node), ...transformSceneNode(node),
...transformBlend(node), ...transformBlend(node),
...transformProportion(node), ...transformProportion(node),
...transformLayoutAttributes(node),
...transformConstraints(node) ...transformConstraints(node)
}; };
}; };

View file

@ -6,6 +6,7 @@ import {
transformEffects, transformEffects,
transformFigmaIds, transformFigmaIds,
transformFills, transformFills,
transformLayoutAttributes,
transformProportion, transformProportion,
transformRotationAndPosition, transformRotationAndPosition,
transformSceneNode, transformSceneNode,
@ -31,6 +32,7 @@ export const transformRectangleNode = (
...transformSceneNode(node), ...transformSceneNode(node),
...transformBlend(node), ...transformBlend(node),
...transformProportion(node), ...transformProportion(node),
...transformLayoutAttributes(node),
...transformCornerRadius(node), ...transformCornerRadius(node),
...transformConstraints(node) ...transformConstraints(node)
}; };

View file

@ -4,6 +4,7 @@ import {
transformDimensionAndPosition, transformDimensionAndPosition,
transformEffects, transformEffects,
transformFigmaIds, transformFigmaIds,
transformLayoutAttributes,
transformProportion, transformProportion,
transformSceneNode, transformSceneNode,
transformStrokes, transformStrokes,
@ -23,6 +24,7 @@ export const transformTextNode = (node: TextNode, baseX: number, baseY: number):
...transformSceneNode(node), ...transformSceneNode(node),
...transformBlend(node), ...transformBlend(node),
...transformProportion(node), ...transformProportion(node),
...transformLayoutAttributes(node),
...transformStrokes(node), ...transformStrokes(node),
...transformConstraints(node) ...transformConstraints(node)
}; };

View file

@ -3,5 +3,6 @@ export * from './translateBlurEffects';
export * from './translateBoolType'; export * from './translateBoolType';
export * from './translateChildren'; export * from './translateChildren';
export * from './translateConstraints'; export * from './translateConstraints';
export * from './translateLayout';
export * from './translateShadowEffects'; export * from './translateShadowEffects';
export * from './translateStrokes'; export * from './translateStrokes';

View file

@ -0,0 +1,127 @@
import {
JustifyAlignContent,
JustifyAlignItems,
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';
case 'VERTICAL':
return 'column-reverse';
default:
return;
}
};
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
};
};
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.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';
case 'CENTER':
return 'center';
case 'MAX':
return 'end';
default:
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';
}
};

View file

@ -2,48 +2,41 @@ import { Uuid } from '@ui/lib/types/utils/uuid';
export const ITEM_MARGIN_SIMPLE_TYPE: unique symbol = Symbol.for('simple'); 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_MARGIN_MULTIPLE_TYPE: unique symbol = Symbol.for('multiple');
export const ITEM_HSIZING_FILL: unique symbol = Symbol.for('fill'); export const ITEM_SIZING_FILL: unique symbol = Symbol.for('fill');
export const ITEM_HSIZING_FIX: unique symbol = Symbol.for('fix'); export const ITEM_SIZING_FIX: unique symbol = Symbol.for('fix');
export const ITEM_HSIZING_AUTO: unique symbol = Symbol.for('auto'); export const ITEM_SIZING_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_ALIGN_SELF_START: unique symbol = Symbol.for('start'); 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_END: unique symbol = Symbol.for('end');
export const ITEM_ALIGN_SELF_CENTER: unique symbol = Symbol.for('center'); export const ITEM_ALIGN_SELF_CENTER: unique symbol = Symbol.for('center');
export const ITEM_ALIGN_SELF_STRETCH: unique symbol = Symbol.for('stretch'); 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 = { export type LayoutChildAttributes = {
layoutItemMarginType?: 'layoutItemMarginType'?:
| 'simple' | 'simple'
| 'multiple' | 'multiple'
| typeof ITEM_MARGIN_SIMPLE_TYPE | typeof ITEM_MARGIN_SIMPLE_TYPE
| typeof ITEM_MARGIN_MULTIPLE_TYPE; | typeof ITEM_MARGIN_MULTIPLE_TYPE;
layoutItemMargin?: { 'layoutItemMargin'?: {
m1?: number; m1?: number;
m2?: number; m2?: number;
m3?: number; m3?: number;
m4?: number; m4?: number;
}; };
layoutItemMaxH?: number; 'layoutItemMaxH'?: number;
layoutItemMinH?: number; 'layoutItemMinH'?: number;
layoutItemMaxW?: number; 'layoutItemMaxW'?: number;
layoutItemMinW?: number; 'layoutItemMinW'?: number;
layoutItemHSizing?: 'layoutItemH-Sizing'?: LayoutSizing;
| 'fill' 'layoutItemV-Sizing'?: LayoutSizing;
| 'fix' 'layoutItemAlignSelf'?:
| '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;
layoutItemAlignSelf?:
| 'start' | 'start'
| 'end' | 'end'
| 'center' | 'center'
@ -52,11 +45,11 @@ export type LayoutChildAttributes = {
| typeof ITEM_ALIGN_SELF_END | typeof ITEM_ALIGN_SELF_END
| typeof ITEM_ALIGN_SELF_CENTER | typeof ITEM_ALIGN_SELF_CENTER
| typeof ITEM_ALIGN_SELF_STRETCH; | typeof ITEM_ALIGN_SELF_STRETCH;
layoutItemAbsolute?: boolean; 'layoutItemAbsolute'?: boolean;
layoutItemZIndex?: number; 'layoutItemZIndex'?: number;
}; };
type JustifyAlignContent = export type JustifyAlignContent =
| 'start' | 'start'
| 'center' | 'center'
| 'end' | 'end'
@ -65,30 +58,38 @@ type JustifyAlignContent =
| 'space-evenly' | 'space-evenly'
| 'stretch'; | '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 = { export type LayoutAttributes = {
layout?: 'flex' | 'grid'; layout?: 'flex' | 'grid';
layoutFlexDir?: layoutFlexDir?: LayoutFlexDir;
| 'row' layoutGap?: LayoutGap;
| 'reverse-row'
| 'row-reverse'
| 'column'
| 'reverse-column'
| 'column-reverse';
layoutGap?: {
rowGap?: number;
columnGap?: number;
};
layoutGapType?: 'simple' | 'multiple'; layoutGapType?: 'simple' | 'multiple';
layoutWrapType?: 'wrap' | 'nowrap' | 'no-wrap'; layoutWrapType?: LayoutWrapType;
layoutPaddingType?: 'simple' | 'multiple'; layoutPaddingType?: 'simple' | 'multiple';
layoutPadding?: { layoutPadding?: LayoutPadding;
p1?: number;
p2?: number;
p3?: number;
p4?: number;
};
layoutJustifyContent?: JustifyAlignContent; layoutJustifyContent?: JustifyAlignContent;
layoutJustifyItems?: JustifyAlignItems; layoutJustifyItems?: JustifyAlignItems;
layoutAlignContent?: JustifyAlignContent; layoutAlignContent?: JustifyAlignContent;