Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Component Instances #124

Merged
merged 18 commits into from
May 30, 2024
5 changes: 5 additions & 0 deletions .changeset/stale-geckos-grab.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"penpot-exporter": minor
---

Implement component instances translation
1 change: 1 addition & 0 deletions plugin-src/transformers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from './transformDocumentNode';
export * from './transformEllipseNode';
export * from './transformFrameNode';
export * from './transformGroupNode';
export * from './transformInstanceNode';
export * from './transformPageNode';
export * from './transformPathNode';
export * from './transformRectangleNode';
Expand Down
1 change: 1 addition & 0 deletions plugin-src/transformers/partials/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export * from './transformChildren';
export * from './transformCornerRadius';
export * from './transformDimensionAndPosition';
export * from './transformEffects';
export * from './transformFigmaIds';
export * from './transformFills';
export * from './transformProportion';
export * from './transformRotationAndPosition';
Expand Down
22 changes: 22 additions & 0 deletions plugin-src/transformers/partials/transformFigmaIds.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ShapeBaseAttributes } from '@ui/lib/types/shapes/shape';

export const transformFigmaIds = (
node: SceneNode
): Pick<ShapeBaseAttributes, 'figmaId' | 'figmaRelatedId'> => {
return {
figmaId: normalizeNodeId(node.id),
figmaRelatedId: getRelatedNodeId(node.id)
};
};

const getRelatedNodeId = (nodeId: string): string | undefined => {
const ids = nodeId.split(';');

if (ids.length > 1) {
return ids.slice(1).join(';');
}
};

const normalizeNodeId = (nodeId: string): string => {
return nodeId.replace('I', '');
};
2 changes: 2 additions & 0 deletions plugin-src/transformers/transformBooleanNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
transformChildren,
transformDimensionAndPosition,
transformEffects,
transformFigmaIds,
transformFills,
transformProportion,
transformSceneNode,
Expand All @@ -21,6 +22,7 @@ export const transformBooleanNode = async (
type: 'bool',
name: node.name,
boolType: translateBoolType(node.booleanOperation),
...transformFigmaIds(node),
...(await transformChildren(node, baseX, baseY)),
...(await transformFills(node)),
...transformEffects(node),
Expand Down
2 changes: 2 additions & 0 deletions plugin-src/transformers/transformComponentNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
transformCornerRadius,
transformDimensionAndPosition,
transformEffects,
transformFigmaIds,
transformFills,
transformProportion,
transformSceneNode,
Expand All @@ -22,6 +23,7 @@ export const transformComponentNode = async (
type: 'component',
name: node.name,
path: '',
...transformFigmaIds(node),
...(await transformFills(node)),
...transformEffects(node),
...(await transformStrokes(node)),
Expand Down
2 changes: 2 additions & 0 deletions plugin-src/transformers/transformEllipseNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
transformBlend,
transformDimension,
transformEffects,
transformFigmaIds,
transformFills,
transformProportion,
transformRotationAndPosition,
Expand All @@ -19,6 +20,7 @@ export const transformEllipseNode = async (
return {
type: 'circle',
name: node.name,
...transformFigmaIds(node),
...(await transformFills(node)),
...transformEffects(node),
...(await transformStrokes(node)),
Expand Down
2 changes: 2 additions & 0 deletions plugin-src/transformers/transformFrameNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
transformCornerRadius,
transformDimensionAndPosition,
transformEffects,
transformFigmaIds,
transformFills,
transformProportion,
transformSceneNode,
Expand Down Expand Up @@ -41,6 +42,7 @@ export const transformFrameNode = async (
type: 'frame',
name: node.name,
showContent: isSectionNode(node) ? true : !node.clipsContent,
...transformFigmaIds(node),
...(await transformFills(node)),
...frameSpecificAttributes,
...(await transformChildren(node, baseX + node.x, baseY + node.y)),
Expand Down
2 changes: 2 additions & 0 deletions plugin-src/transformers/transformGroupNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
transformBlend,
transformDimensionAndPosition,
transformEffects,
transformFigmaIds,
transformSceneNode
} from '@plugin/transformers/partials';
import { transformChildren } from '@plugin/transformers/partials';
Expand All @@ -14,6 +15,7 @@ export const transformGroupNode = async (
baseY: number
): Promise<GroupShape> => {
return {
...transformFigmaIds(node),
...transformGroupNodeLike(node, baseX, baseY),
...transformEffects(node),
...transformBlend(node),
Expand Down
64 changes: 64 additions & 0 deletions plugin-src/transformers/transformInstanceNode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import {
transformBlend,
transformChildren,
transformCornerRadius,
transformDimensionAndPosition,
transformEffects,
transformFigmaIds,
transformFills,
transformProportion,
transformSceneNode,
transformStrokes
} from '@plugin/transformers/partials';

import { ComponentInstance } from '@ui/types';

export const transformInstanceNode = async (
node: InstanceNode,
baseX: number,
baseY: number
): Promise<ComponentInstance | undefined> => {
const mainComponent = await node.getMainComponentAsync();

/**
* We do not want to process component instances in the following scenarios:
*
* 1. If the component does not have a main component.
* 2. If the component does not have parent (it comes from an external design system).
* 3. If the component is inside a component set, (it is a variant component).
*/
if (
!mainComponent ||
mainComponent.parent === null ||
mainComponent.parent.type === 'COMPONENT_SET'
) {
return;
}

return {
type: 'instance',
mainComponentFigmaId: mainComponent.id,
isComponentRoot: isComponentRoot(node),
...transformFigmaIds(node),
...(await transformFills(node)),
...transformEffects(node),
...(await transformStrokes(node)),
...transformSceneNode(node),
...transformBlend(node),
...transformProportion(node),
...transformCornerRadius(node),
...transformDimensionAndPosition(node, baseX, baseY),
...(await transformChildren(node, baseX + node.x, baseY + node.y))
};
};

const isComponentRoot = (node: InstanceNode): boolean => {
let parent = node.parent;
while (parent !== null) {
if (parent.type === 'COMPONENT' || parent.type === 'INSTANCE') {
return false;
}
parent = parent.parent;
}
return true;
};
2 changes: 2 additions & 0 deletions plugin-src/transformers/transformPathNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
transformBlend,
transformDimensionAndPosition,
transformEffects,
transformFigmaIds,
transformFills,
transformProportion,
transformSceneNode,
Expand All @@ -23,6 +24,7 @@ export const transformPathNode = async (
return {
type: 'path',
name: node.name,
...transformFigmaIds(node),
...(hasFillGeometry(node) ? await transformFills(node) : []),
...(await transformStrokes(node)),
...transformEffects(node),
Expand Down
2 changes: 2 additions & 0 deletions plugin-src/transformers/transformRectangleNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
transformCornerRadius,
transformDimension,
transformEffects,
transformFigmaIds,
transformFills,
transformProportion,
transformRotationAndPosition,
Expand All @@ -20,6 +21,7 @@ export const transformRectangleNode = async (
return {
type: 'rect',
name: node.name,
...transformFigmaIds(node),
...(await transformFills(node)),
...transformEffects(node),
...(await transformStrokes(node)),
Expand Down
3 changes: 3 additions & 0 deletions plugin-src/transformers/transformSceneNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
transformEllipseNode,
transformFrameNode,
transformGroupNode,
transformInstanceNode,
transformPathNode,
transformRectangleNode,
transformTextNode,
Expand Down Expand Up @@ -39,6 +40,8 @@ export const transformSceneNode = async (
return await transformBooleanNode(node, baseX, baseY);
case 'COMPONENT':
return await transformComponentNode(node, baseX, baseY);
case 'INSTANCE':
return await transformInstanceNode(node, baseX, baseY);
}

console.error(`Unsupported node type: ${node.type}`);
Expand Down
2 changes: 2 additions & 0 deletions plugin-src/transformers/transformTextNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
transformBlend,
transformDimensionAndPosition,
transformEffects,
transformFigmaIds,
transformProportion,
transformSceneNode,
transformStrokes,
Expand All @@ -18,6 +19,7 @@ export const transformTextNode = async (
return {
type: 'text',
name: node.name,
...transformFigmaIds(node),
...(await transformText(node)),
...transformDimensionAndPosition(node, baseX, baseY),
...transformEffects(node),
Expand Down
6 changes: 4 additions & 2 deletions plugin-src/transformers/transformVectorNode.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { transformVectorPaths } from '@plugin/transformers/partials';
import { transformFigmaIds, transformVectorPaths } from '@plugin/transformers/partials';

import { GroupShape } from '@ui/lib/types/shapes/groupShape';
import { PathShape } from '@ui/lib/types/shapes/pathShape';
Expand All @@ -21,12 +21,14 @@ export const transformVectorNode = async (
if (children.length === 1) {
return {
...children[0],
name: node.name
name: node.name,
...transformFigmaIds(node)
};
}

return {
...transformGroupNodeLike(node, baseX, baseY),
...transformFigmaIds(node),
children
};
};
Loading