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:
parent
8d35f58f89
commit
b85a4f7279
16 changed files with 257 additions and 54 deletions
5
.changeset/swift-parents-wait.md
Normal file
5
.changeset/swift-parents-wait.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"penpot-exporter": minor
|
||||
---
|
||||
|
||||
Added support for autolayout
|
|
@ -15,7 +15,7 @@ export const findAllTextNodes = async () => {
|
|||
extractMissingFonts(node, fonts);
|
||||
}
|
||||
|
||||
sleep(0);
|
||||
await sleep(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
|
44
plugin-src/transformers/partials/transformLayout.ts
Normal file
44
plugin-src/transformers/partials/transformLayout.ts
Normal 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'
|
||||
};
|
||||
};
|
|
@ -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)
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
};
|
||||
|
|
|
@ -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';
|
||||
|
|
127
plugin-src/translators/translateLayout.ts
Normal file
127
plugin-src/translators/translateLayout.ts
Normal 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';
|
||||
}
|
||||
};
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue