mirror of
https://github.com/penpot/penpot-exporter-figma-plugin.git
synced 2024-12-22 05:33:02 -05:00
Remote components (External design systems) (#140)
* remote components processing * changeset * fixes * fixes * fixes * fixes * fix everything * revert for now * fixes * change delete nodes flag --------- Co-authored-by: Jordi Sala Morales <jordism91@gmail.com>
This commit is contained in:
parent
3094f05e98
commit
be5ff3be8e
10 changed files with 96 additions and 8 deletions
5
.changeset/early-lamps-report.md
Normal file
5
.changeset/early-lamps-report.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"penpot-exporter": minor
|
||||
---
|
||||
|
||||
Remote components processing
|
30
plugin-src/RemoteComponentLibrary.ts
Normal file
30
plugin-src/RemoteComponentLibrary.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
class RemoteComponentsLibrary {
|
||||
private components: Record<string, ComponentNode | ComponentSetNode> = {};
|
||||
private queue: string[] = [];
|
||||
|
||||
public register(id: string, component: ComponentNode | ComponentSetNode) {
|
||||
if (!Object.prototype.hasOwnProperty.call(this.components, id)) {
|
||||
this.queue.push(id);
|
||||
}
|
||||
|
||||
this.components[id] = component;
|
||||
}
|
||||
|
||||
public get(id: string): ComponentNode | ComponentSetNode | undefined {
|
||||
return this.components[id];
|
||||
}
|
||||
|
||||
public next(): ComponentNode | ComponentSetNode {
|
||||
const lastKey = this.queue.pop();
|
||||
|
||||
if (!lastKey) throw new Error('No components to pop');
|
||||
|
||||
return this.components[lastKey];
|
||||
}
|
||||
|
||||
public remaining(): number {
|
||||
return this.queue.length;
|
||||
}
|
||||
}
|
||||
|
||||
export const remoteComponentLibrary = new RemoteComponentsLibrary();
|
|
@ -23,6 +23,7 @@ export const transformComponentNode = async (
|
|||
type: 'component',
|
||||
name: node.name,
|
||||
path: node.parent?.type === 'COMPONENT_SET' ? node.parent.name : '',
|
||||
showContent: !node.clipsContent,
|
||||
...transformFigmaIds(node),
|
||||
...transformFills(node),
|
||||
...transformEffects(node),
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { componentsLibrary } from '@plugin/ComponentLibrary';
|
||||
import { imagesLibrary } from '@plugin/ImageLibrary';
|
||||
import { remoteComponentLibrary } from '@plugin/RemoteComponentLibrary';
|
||||
import { translateRemoteChildren } from '@plugin/translators';
|
||||
import { sleep } from '@plugin/utils';
|
||||
|
||||
import { PenpotDocument } from '@ui/types';
|
||||
|
@ -28,6 +30,13 @@ export const transformDocumentNode = async (node: DocumentNode): Promise<PenpotD
|
|||
await sleep(0);
|
||||
}
|
||||
|
||||
if (remoteComponentLibrary.remaining() > 0) {
|
||||
children.push({
|
||||
name: 'External Components',
|
||||
children: await translateRemoteChildren()
|
||||
});
|
||||
}
|
||||
|
||||
const images: Record<string, Uint8Array> = {};
|
||||
|
||||
for (const [key, image] of Object.entries(imagesLibrary.all())) {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { remoteComponentLibrary } from '@plugin/RemoteComponentLibrary';
|
||||
import {
|
||||
transformBlend,
|
||||
transformChildren,
|
||||
|
@ -24,11 +25,16 @@ export const transformInstanceNode = async (
|
|||
return;
|
||||
}
|
||||
|
||||
if (isExternalComponent(mainComponent)) {
|
||||
await registerExternalComponents(mainComponent);
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'instance',
|
||||
name: node.name,
|
||||
mainComponentFigmaId: mainComponent.id,
|
||||
isComponentRoot: isComponentRoot(node),
|
||||
showContent: !node.clipsContent,
|
||||
...transformFigmaIds(node),
|
||||
...transformFills(node),
|
||||
...transformEffects(node),
|
||||
|
@ -42,19 +48,39 @@ export const transformInstanceNode = async (
|
|||
};
|
||||
};
|
||||
|
||||
const registerExternalComponents = async (mainComponent: ComponentNode): Promise<void> => {
|
||||
let component: ComponentSetNode | ComponentNode = mainComponent;
|
||||
|
||||
if (component.parent?.type === 'COMPONENT_SET') {
|
||||
component = component.parent;
|
||||
}
|
||||
|
||||
if (remoteComponentLibrary.get(component.id) !== undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
remoteComponentLibrary.register(component.id, component);
|
||||
};
|
||||
|
||||
const isExternalComponent = (mainComponent: ComponentNode): boolean => {
|
||||
return (
|
||||
mainComponent.remote ||
|
||||
(mainComponent.parent?.type === 'COMPONENT_SET' && mainComponent.parent.remote)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* We do not want to process component instances in the following scenarios:
|
||||
*
|
||||
* 1. If the component comes from an external design system.
|
||||
* 2. If the component does not have a parent. (it's been removed)
|
||||
* 3. Main component can be in a ComponentSet (the same logic applies to the parent).
|
||||
* 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 = (mainComponent: ComponentNode): boolean => {
|
||||
return (
|
||||
mainComponent.remote ||
|
||||
mainComponent.parent === null ||
|
||||
(mainComponent.parent === null && !mainComponent.remote) ||
|
||||
(mainComponent.parent?.type === 'COMPONENT_SET' &&
|
||||
(mainComponent.parent.parent === null || mainComponent.parent.remote))
|
||||
mainComponent.parent.parent === null &&
|
||||
!mainComponent.parent.remote)
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { remoteComponentLibrary } from '@plugin/RemoteComponentLibrary';
|
||||
import { transformGroupNodeLike, transformSceneNode } from '@plugin/transformers';
|
||||
import { transformMaskFigmaIds } from '@plugin/transformers/partials';
|
||||
import { sleep } from '@plugin/utils';
|
||||
|
@ -50,3 +51,19 @@ export const translateChildren = async (
|
|||
|
||||
return transformedChildren;
|
||||
};
|
||||
|
||||
export const translateRemoteChildren = async (): Promise<PenpotNode[]> => {
|
||||
const transformedChildren: PenpotNode[] = [];
|
||||
|
||||
while (remoteComponentLibrary.remaining() > 0) {
|
||||
const child = remoteComponentLibrary.next();
|
||||
|
||||
const penpotNode = await transformSceneNode(child);
|
||||
|
||||
if (penpotNode) transformedChildren.push(penpotNode);
|
||||
|
||||
await sleep(0);
|
||||
}
|
||||
|
||||
return transformedChildren;
|
||||
};
|
||||
|
|
|
@ -19,6 +19,7 @@ export type ComponentAttributes = {
|
|||
type?: 'component';
|
||||
name: string;
|
||||
path: string;
|
||||
showContent?: boolean;
|
||||
mainInstanceId?: Uuid;
|
||||
mainInstancePage?: Uuid;
|
||||
};
|
||||
|
|
|
@ -17,7 +17,6 @@ export const createComponent = (file: PenpotFile, { figmaId }: ComponentRoot) =>
|
|||
|
||||
const frameId = createArtboard(file, {
|
||||
...component,
|
||||
showContent: true,
|
||||
componentFile: file.getId(),
|
||||
componentId,
|
||||
componentRoot: true,
|
||||
|
|
|
@ -34,7 +34,6 @@ export const createComponentInstance = (
|
|||
|
||||
createArtboard(file, {
|
||||
...rest,
|
||||
showContent: true,
|
||||
shapeRef: uiComponent.mainInstanceId,
|
||||
componentFile: file.getId(),
|
||||
componentRoot: isComponentRoot,
|
||||
|
|
|
@ -16,5 +16,6 @@ export type ComponentInstance = ShapeGeomAttributes &
|
|||
figmaId?: string;
|
||||
figmaRelatedId?: string;
|
||||
isComponentRoot: boolean;
|
||||
showContent?: boolean;
|
||||
type: 'instance';
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue