0
Fork 0
mirror of https://github.com/penpot/penpot-exporter-figma-plugin.git synced 2024-12-21 21:23:06 -05:00

Reworked remote components (#197)

* reworked remote components

* changeset
This commit is contained in:
Alex Sánchez 2024-06-28 11:40:33 +02:00 committed by GitHub
parent 6a5ec1b89e
commit c5dd5d011e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 31 additions and 133 deletions

View file

@ -0,0 +1,5 @@
---
"penpot-exporter": minor
---
Rework remote components

View file

@ -1,39 +0,0 @@
export class RemoteComponentsLibrary {
private components: Map<string, ComponentNode | ComponentSetNode> = new Map();
private queue: string[] = [];
public register(id: string, component: ComponentNode | ComponentSetNode) {
if (!this.components.has(id)) {
this.queue.push(id);
}
this.components.set(id, component);
}
public get(id: string): ComponentNode | ComponentSetNode | undefined {
return this.components.get(id);
}
public has(id: string): boolean {
return this.components.has(id);
}
public next(): ComponentNode | ComponentSetNode {
const lastKey = this.queue.pop();
if (!lastKey) throw new Error('No components to pop');
const component = this.components.get(lastKey);
if (!component) throw new Error('Component not found');
return component;
}
public remaining(): number {
return this.queue.length;
}
public total(): number {
return this.components.size;
}
}

View file

@ -1,10 +1,7 @@
import { ComponentShape } from '@ui/lib/types/shapes/componentShape';
import { RemoteComponentsLibrary } from './RemoteComponents';
export const textStyles: Map<string, TextStyle | undefined> = new Map();
export const paintStyles: Map<string, PaintStyle | undefined> = new Map();
export const overrides: Map<string, NodeChangeProperty[]> = new Map();
export const images: Map<string, Image | null> = new Map();
export const components: Map<string, ComponentShape> = new Map();
export const remoteComponents = new RemoteComponentsLibrary();

View file

@ -1,31 +1,10 @@
import { sleep } from '@common/sleep';
import { remoteComponents } from '@plugin/libraries';
import { transformPageNode } from '@plugin/transformers';
import { translateRemoteChildren } from '@plugin/translators';
import { PenpotPage } from '@ui/lib/types/penpotPage';
export const processPages = async (node: DocumentNode): Promise<PenpotPage[]> => {
const children = await processLocalDocument(node);
const remoteComponents = await processRemoteComponents();
if (remoteComponents) {
children.push(remoteComponents);
}
return children;
};
const processRemoteComponents = async (): Promise<PenpotPage | undefined> => {
if (remoteComponents.remaining() > 0) {
return {
name: 'External Components',
children: await translateRemoteChildren()
};
}
};
const processLocalDocument = async (node: DocumentNode): Promise<PenpotPage[]> => {
const children = [];
let currentPage = 1;

View file

@ -1,4 +1,4 @@
import { overrides, remoteComponents } from '@plugin/libraries';
import { overrides } from '@plugin/libraries';
import {
transformAutoLayout,
transformBlend,
@ -28,18 +28,14 @@ export const transformInstanceNode = async (
}
const primaryComponent = getPrimaryComponent(mainComponent);
if (isUnprocessableComponent(primaryComponent)) {
return;
}
if (primaryComponent.remote) {
registerExternalComponents(primaryComponent);
}
registerTextVariableOverrides(node, primaryComponent);
if (node.overrides.length > 0) {
node.overrides.forEach(override => overrides.set(override.id, override.overriddenFields));
const isOrphan = isOrphanInstance(primaryComponent);
let nodeOverrides = {};
if (!isOrphan) {
registerTextVariableOverrides(node, primaryComponent);
if (node.overrides.length > 0) {
node.overrides.forEach(override => overrides.set(override.id, override.overriddenFields));
}
nodeOverrides = transformOverrides(node);
}
return {
@ -48,6 +44,7 @@ export const transformInstanceNode = async (
mainComponentFigmaId: mainComponent.id,
isComponentRoot: isComponentRoot(node),
showContent: !node.clipsContent,
isOrphan,
...transformFigmaIds(node),
...transformFills(node),
...transformEffects(node),
@ -62,7 +59,7 @@ export const transformInstanceNode = async (
...transformConstraints(node),
...transformAutoLayout(node),
...(await transformChildren(node)),
...transformOverrides(node)
...nodeOverrides
};
};
@ -74,14 +71,6 @@ const getPrimaryComponent = (mainComponent: ComponentNode): ComponentNode | Comp
return mainComponent;
};
const registerExternalComponents = (primaryComponent: ComponentNode | ComponentSetNode): void => {
if (remoteComponents.has(primaryComponent.id)) {
return;
}
remoteComponents.register(primaryComponent.id, primaryComponent);
};
const getComponentTextPropertyOverrides = (
node: InstanceNode,
primaryComponent: ComponentNode | ComponentSetNode
@ -133,14 +122,8 @@ const registerTextVariableOverrides = (
}
};
/**
* We do not want to process component instances in the following scenarios:
*
* 1. If the component does not have a parent. (it's been removed)
* 2. Main component can be in a ComponentSet (the same logic applies to the parent).
*/
const isUnprocessableComponent = (primaryComponent: ComponentNode | ComponentSetNode): boolean => {
return primaryComponent.parent === null && !primaryComponent.remote;
const isOrphanInstance = (primaryComponent: ComponentNode | ComponentSetNode): boolean => {
return primaryComponent.parent === null || primaryComponent.remote;
};
const isComponentRoot = (node: InstanceNode): boolean => {

View file

@ -1,6 +1,5 @@
import { sleep } from '@common/sleep';
import { remoteComponents } from '@plugin/libraries';
import { transformGroupNodeLike, transformSceneNode } from '@plugin/transformers';
import { transformMaskFigmaIds } from '@plugin/transformers/partials';
@ -62,35 +61,3 @@ export const translateChildren = async (children: readonly SceneNode[]): Promise
return transformedChildren;
};
export const translateRemoteChildren = async (): Promise<PenpotNode[]> => {
const transformedChildren: PenpotNode[] = [];
let currentRemote = 1;
figma.ui.postMessage({
type: 'PROGRESS_STEP',
data: 'remote'
});
while (remoteComponents.remaining() > 0) {
figma.ui.postMessage({
type: 'PROGRESS_TOTAL_ITEMS',
data: remoteComponents.total()
});
const child = remoteComponents.next();
const penpotNode = await transformSceneNode(child);
if (penpotNode) transformedChildren.push(penpotNode);
figma.ui.postMessage({
type: 'PROGRESS_PROCESSED_ITEMS',
data: currentRemote++
});
await sleep(0);
}
return transformedChildren;
};

View file

@ -15,10 +15,6 @@ const stepMessages: Record<Steps, Messages> = {
total: 'pages processed 💪',
current: 'Currently processing layer'
},
remote: {
total: 'remote components processed 📦',
current: 'Currently processing layer'
},
images: {
total: 'images downloaded 📸'
},
@ -74,7 +70,6 @@ const StepProgress = (): JSX.Element | null => {
switch (step) {
case 'processing':
case 'remote':
case 'images':
case 'optimization':
case 'building':

View file

@ -21,7 +21,6 @@ export type UseFigmaHook = {
export type Steps =
| 'processing'
| 'remote'
| 'images'
| 'optimization'
| 'building'

View file

@ -1,9 +1,12 @@
import { PenpotFile } from '@ui/lib/types/penpotFile';
import { Uuid } from '@ui/lib/types/utils/uuid';
import { components, parseFigmaId } from '@ui/parser';
import { ComponentInstance } from '@ui/types';
import { createArtboard } from '.';
let remoteFileId: Uuid | undefined = undefined;
export const createComponentInstance = (
file: PenpotFile,
{
@ -23,7 +26,7 @@ export const createComponentInstance = (
}
shape.shapeRef = uiComponent.mainInstanceId;
shape.componentFile = file.getId();
shape.componentFile = shape.isOrphan ? getRemoteFileId(file) : file.getId();
shape.componentRoot = isComponentRoot;
shape.componentId = uiComponent.componentId;
@ -46,3 +49,11 @@ const createUiComponent = (file: PenpotFile, mainComponentFigmaId: string) => {
return uiComponent;
};
const getRemoteFileId = (file: PenpotFile): Uuid => {
if (!remoteFileId) {
remoteFileId = file.newId();
}
return remoteFileId;
};

View file

@ -26,6 +26,7 @@ export type ComponentInstance = ShapeGeomAttributes &
figmaRelatedId?: string;
isComponentRoot: boolean;
showContent?: boolean;
isOrphan: boolean;
type: 'instance';
};