mirror of
https://github.com/penpot/penpot-exporter-figma-plugin.git
synced 2024-12-22 05:33:02 -05:00
Reworked remote components (#197)
* reworked remote components * changeset
This commit is contained in:
parent
6a5ec1b89e
commit
c5dd5d011e
10 changed files with 31 additions and 133 deletions
5
.changeset/seven-roses-sniff.md
Normal file
5
.changeset/seven-roses-sniff.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"penpot-exporter": minor
|
||||
---
|
||||
|
||||
Rework remote components
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { overrides, remoteComponents } from '@plugin/libraries';
|
||||
import { overrides } from '@plugin/libraries';
|
||||
import {
|
||||
transformAutoLayout,
|
||||
transformBlend,
|
||||
|
@ -28,19 +28,15 @@ export const transformInstanceNode = async (
|
|||
}
|
||||
|
||||
const primaryComponent = getPrimaryComponent(mainComponent);
|
||||
if (isUnprocessableComponent(primaryComponent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (primaryComponent.remote) {
|
||||
registerExternalComponents(primaryComponent);
|
||||
}
|
||||
|
||||
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 {
|
||||
type: 'instance',
|
||||
|
@ -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 => {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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':
|
||||
|
|
|
@ -21,7 +21,6 @@ export type UseFigmaHook = {
|
|||
|
||||
export type Steps =
|
||||
| 'processing'
|
||||
| 'remote'
|
||||
| 'images'
|
||||
| 'optimization'
|
||||
| 'building'
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -26,6 +26,7 @@ export type ComponentInstance = ShapeGeomAttributes &
|
|||
figmaRelatedId?: string;
|
||||
isComponentRoot: boolean;
|
||||
showContent?: boolean;
|
||||
isOrphan: boolean;
|
||||
type: 'instance';
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue