0
Fork 0
mirror of https://github.com/penpot/penpot-exporter-figma-plugin.git synced 2024-12-22 05:33:02 -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);
}
sleep(0);
await sleep(0);
}
}

View file

@ -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';

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,
transformDimensionAndPositionFromVectorPath,
transformEffects,
transformLayoutAttributes,
transformProportion,
transformSceneNode,
transformStrokesFromVector
@ -110,6 +111,7 @@ const transformVectorPath = (
...transformDimensionAndPositionFromVectorPath(vectorPath, baseX, baseY),
...transformSceneNode(node),
...transformBlend(node),
...transformProportion(node)
...transformProportion(node),
...transformLayoutAttributes(node)
};
};

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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';

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_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?:
'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?:
| '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;
layoutItemAlignSelf?:
'layoutItemMaxH'?: number;
'layoutItemMinH'?: number;
'layoutItemMaxW'?: number;
'layoutItemMinW'?: number;
'layoutItemH-Sizing'?: LayoutSizing;
'layoutItemV-Sizing'?: LayoutSizing;
'layoutItemAlignSelf'?:
| 'start'
| 'end'
| 'center'
@ -52,11 +45,11 @@ 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;
};
type JustifyAlignContent =
export type JustifyAlignContent =
| 'start'
| 'center'
| 'end'
@ -65,30 +58,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;