0
Fork 0
mirror of https://github.com/penpot/penpot-exporter-figma-plugin.git synced 2024-12-22 13:43:03 -05:00

Boolean Groups (#115)

* wip

* wip

* fixes

* changeset

* fixes

* fixes

* fixes

* fixes

* minor fixes

* minor fixes
This commit is contained in:
Alex Sánchez 2024-05-27 10:05:23 +02:00 committed by GitHub
parent aafb9cf342
commit 36afc6da55
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 139 additions and 8 deletions

View file

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

View file

@ -1,3 +1,4 @@
export * from './transformBooleanNode';
export * from './transformDocumentNode'; export * from './transformDocumentNode';
export * from './transformEllipseNode'; export * from './transformEllipseNode';
export * from './transformFrameNode'; export * from './transformFrameNode';

View file

@ -0,0 +1,33 @@
import {
transformBlend,
transformChildren,
transformDimensionAndPosition,
transformEffects,
transformFills,
transformProportion,
transformSceneNode,
transformStrokes
} from '@plugin/transformers/partials';
import { translateBoolType } from '@plugin/translators';
import { BoolShape } from '@ui/lib/types/shapes/boolShape';
export const transformBooleanNode = async (
node: BooleanOperationNode,
baseX: number,
baseY: number
): Promise<BoolShape> => {
return {
type: 'bool',
name: node.name,
boolType: translateBoolType(node.booleanOperation),
...(await transformChildren(node, baseX, baseY)),
...(await transformFills(node)),
...transformEffects(node),
...(await transformStrokes(node)),
...transformDimensionAndPosition(node, baseX, baseY),
...transformSceneNode(node),
...transformBlend(node),
...transformProportion(node)
};
};

View file

@ -1,6 +1,7 @@
import { PenpotNode } from '@ui/lib/types/penpotNode'; import { PenpotNode } from '@ui/lib/types/penpotNode';
import { import {
transformBooleanNode,
transformEllipseNode, transformEllipseNode,
transformFrameNode, transformFrameNode,
transformGroupNode, transformGroupNode,
@ -33,6 +34,8 @@ export const transformSceneNode = async (
case 'POLYGON': case 'POLYGON':
case 'LINE': case 'LINE':
return await transformPathNode(node, baseX, baseY); return await transformPathNode(node, baseX, baseY);
case 'BOOLEAN_OPERATION':
return await transformBooleanNode(node, baseX, baseY);
} }
console.error(`Unsupported node type: ${node.type}`); console.error(`Unsupported node type: ${node.type}`);

View file

@ -1,4 +1,5 @@
export * from './translateBlendMode'; export * from './translateBlendMode';
export * from './translateBlurEffects'; export * from './translateBlurEffects';
export * from './translateBoolType';
export * from './translateShadowEffects'; export * from './translateShadowEffects';
export * from './translateStrokes'; export * from './translateStrokes';

View file

@ -0,0 +1,15 @@
import { BoolOperations } from '@ui/lib/types/shapes/boolShape';
type BooleanOperation = 'UNION' | 'INTERSECT' | 'SUBTRACT' | 'EXCLUDE';
export const translateBoolType = (booleanOperation: BooleanOperation): BoolOperations => {
switch (booleanOperation) {
case 'EXCLUDE':
return 'exclude';
case 'INTERSECT':
return 'intersection';
case 'SUBTRACT':
return 'difference';
case 'UNION':
return 'union';
}
};

View file

@ -0,0 +1,22 @@
import { createPenpotItem } from '@ui/converters/createPenpotItem';
import { PenpotFile } from '@ui/lib/types/penpotFile';
import { BoolShape } from '@ui/lib/types/shapes/boolShape';
import { translateFillGradients, translateUiBlendMode, translateUiBoolType } from '@ui/translators';
export const createPenpotBool = (
file: PenpotFile,
{ type, fills, boolType, blendMode, children = [], ...rest }: BoolShape
) => {
file.addBool({
fills: translateFillGradients(fills),
blendMode: translateUiBlendMode(blendMode),
boolType: translateUiBoolType(boolType),
...rest
});
for (const child of children) {
createPenpotItem(file, child);
}
file.closeBool();
};

View file

@ -3,6 +3,7 @@ import { PenpotNode } from '@ui/lib/types/penpotNode';
import { import {
createPenpotArtboard, createPenpotArtboard,
createPenpotBool,
createPenpotCircle, createPenpotCircle,
createPenpotGroup, createPenpotGroup,
createPenpotPath, createPenpotPath,
@ -24,5 +25,7 @@ export const createPenpotItem = (file: PenpotFile, node: PenpotNode) => {
return createPenpotPath(file, node); return createPenpotPath(file, node);
case 'text': case 'text':
return createPenpotText(file, node); return createPenpotText(file, node);
case 'bool':
return createPenpotBool(file, node);
} }
}; };

View file

@ -1,4 +1,5 @@
export * from './createPenpotArtboard'; export * from './createPenpotArtboard';
export * from './createPenpotBool';
export * from './createPenpotCircle'; export * from './createPenpotCircle';
export * from './createPenpotFile'; export * from './createPenpotFile';
export * from './createPenpotGroup'; export * from './createPenpotGroup';

View file

@ -32,6 +32,6 @@ export interface PenpotFile {
// lookupShape(shapeId: string): void; // lookupShape(shapeId: string): void;
// updateObject(id: string, object: any): void; // updateObject(id: string, object: any): void;
// deleteObject(id: string): void; // deleteObject(id: string): void;
asMap(): unknown; // asMap(): unknown;
export(): void; export(): void;
} }

View file

@ -1,3 +1,4 @@
import { BoolShape } from '@ui/lib/types/shapes/boolShape';
import { CircleShape } from '@ui/lib/types/shapes/circleShape'; import { CircleShape } from '@ui/lib/types/shapes/circleShape';
import { FrameShape } from '@ui/lib/types/shapes/frameShape'; import { FrameShape } from '@ui/lib/types/shapes/frameShape';
import { GroupShape } from '@ui/lib/types/shapes/groupShape'; import { GroupShape } from '@ui/lib/types/shapes/groupShape';
@ -5,4 +6,11 @@ import { PathShape } from '@ui/lib/types/shapes/pathShape';
import { RectShape } from '@ui/lib/types/shapes/rectShape'; import { RectShape } from '@ui/lib/types/shapes/rectShape';
import { TextShape } from '@ui/lib/types/shapes/textShape'; import { TextShape } from '@ui/lib/types/shapes/textShape';
export type PenpotNode = FrameShape | GroupShape | PathShape | RectShape | CircleShape | TextShape; export type PenpotNode =
| FrameShape
| GroupShape
| PathShape
| RectShape
| CircleShape
| TextShape
| BoolShape;

View file

@ -1,23 +1,39 @@
import { LayoutChildAttributes } from '@ui/lib/types/shapes/layout'; import { LayoutChildAttributes } from '@ui/lib/types/shapes/layout';
import { PathContent } from '@ui/lib/types/shapes/pathShape';
import { ShapeAttributes, ShapeBaseAttributes } from '@ui/lib/types/shapes/shape'; import { ShapeAttributes, ShapeBaseAttributes } from '@ui/lib/types/shapes/shape';
import { Children } from '@ui/lib/types/utils/children';
import { Point } from '@ui/lib/types/utils/point'; import { Point } from '@ui/lib/types/utils/point';
import { Uuid } from '@ui/lib/types/utils/uuid'; import { Uuid } from '@ui/lib/types/utils/uuid';
export const BOOL_DIFFERENCE: unique symbol = Symbol.for('difference');
export const BOOL_UNION: unique symbol = Symbol.for('union');
export const BOOL_INTERSECTION: unique symbol = Symbol.for('intersection');
export const BOOL_EXCLUDE: unique symbol = Symbol.for('exclude');
export type BoolOperations =
| 'difference'
| 'union'
| 'intersection'
| 'exclude'
| typeof BOOL_DIFFERENCE
| typeof BOOL_UNION
| typeof BOOL_INTERSECTION
| typeof BOOL_EXCLUDE;
export type BoolShape = ShapeBaseAttributes & export type BoolShape = ShapeBaseAttributes &
ShapeAttributes & ShapeAttributes &
BoolAttributes & BoolAttributes &
LayoutChildAttributes; LayoutChildAttributes &
Children;
type BoolAttributes = { type BoolAttributes = {
type?: 'bool'; type?: 'bool';
shapes?: Uuid[]; shapes?: Uuid[];
boolType: string; // @TODO: in Penpot this is of type :keyword. check if it makes sense boolType: BoolOperations;
boolContent: BoolContent[]; boolContent?: BoolContent[];
}; };
type BoolContent = { type BoolContent = {
command: string; // @TODO: in Penpot this is of type :keyword. check if it makes sense
relative?: boolean; relative?: boolean;
prevPos?: Point; prevPos?: Point;
params?: { [keyword: string]: number }; // @TODO: in Penpot this is of type :keyword. check if it makes sense } & PathContent;
};

View file

@ -1,3 +1,4 @@
export * from './translateFillGradients'; export * from './translateFillGradients';
export * from './translatePathContent'; export * from './translatePathContent';
export * from './translateUiBlendMode'; export * from './translateUiBlendMode';
export * from './translateUiBoolType';

View file

@ -0,0 +1,22 @@
import {
BOOL_DIFFERENCE,
BOOL_EXCLUDE,
BOOL_INTERSECTION,
BOOL_UNION,
BoolOperations
} from '@ui/lib/types/shapes/boolShape';
export const translateUiBoolType = (booleanOperation: BoolOperations): BoolOperations => {
switch (booleanOperation) {
case 'union':
return BOOL_UNION;
case 'exclude':
return BOOL_EXCLUDE;
case 'difference':
return BOOL_DIFFERENCE;
case 'intersection':
return BOOL_INTERSECTION;
}
throw new Error(`Unsupported boolean operation: ${String(booleanOperation)}`);
};