mirror of
https://github.com/penpot/penpot-exporter-figma-plugin.git
synced 2024-12-22 05:33:02 -05:00
Add loaders when the file is being built so we can track the progress (#172)
* Add loaders when the file is being built so we can track the progress * add changelog * improve * refactor
This commit is contained in:
parent
199984d961
commit
8697902e08
14 changed files with 169 additions and 80 deletions
5
.changeset/tidy-pandas-enjoy.md
Normal file
5
.changeset/tidy-pandas-enjoy.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"penpot-exporter": minor
|
||||
---
|
||||
|
||||
Track better the progress on file creation
|
|
@ -45,6 +45,7 @@ export const transformComponentNode = async (
|
|||
|
||||
return {
|
||||
figmaId: node.id,
|
||||
type: 'component'
|
||||
type: 'component',
|
||||
name: node.name
|
||||
};
|
||||
};
|
||||
|
|
|
@ -12,18 +12,21 @@ import { transformPageNode } from '.';
|
|||
const downloadImages = async (): Promise<Record<string, Uint8Array>> => {
|
||||
const imageToDownload = Object.entries(imagesLibrary.all());
|
||||
const images: Record<string, Uint8Array> = {};
|
||||
let currentImage = 1;
|
||||
|
||||
figma.ui.postMessage({
|
||||
type: 'PROGRESS_STEP',
|
||||
data: 'images'
|
||||
});
|
||||
if (imageToDownload.length === 0) return images;
|
||||
|
||||
let currentImage = 1;
|
||||
|
||||
figma.ui.postMessage({
|
||||
type: 'PROGRESS_TOTAL_ITEMS',
|
||||
data: imageToDownload.length
|
||||
});
|
||||
|
||||
figma.ui.postMessage({
|
||||
type: 'PROGRESS_STEP',
|
||||
data: 'images'
|
||||
});
|
||||
|
||||
for (const [key, image] of imageToDownload) {
|
||||
const bytes = await image?.getBytesAsync();
|
||||
|
||||
|
|
|
@ -25,7 +25,15 @@ const stepMessages: Record<Steps, Messages> = {
|
|||
optimization: {
|
||||
total: 'images optimized 📸'
|
||||
},
|
||||
downloading: {
|
||||
building: {
|
||||
total: 'pages built 🏗️',
|
||||
current: 'Currently processing layer'
|
||||
},
|
||||
components: {
|
||||
total: 'components built 🏗️',
|
||||
current: 'Currently processing layer'
|
||||
},
|
||||
exporting: {
|
||||
total: 'Generating Penpot file 🚀',
|
||||
current: 'Please wait, this process might take a while...'
|
||||
}
|
||||
|
@ -51,6 +59,8 @@ const StepProgress = (): JSX.Element | null => {
|
|||
case 'remote':
|
||||
case 'images':
|
||||
case 'optimization':
|
||||
case 'building':
|
||||
case 'components':
|
||||
return (
|
||||
<>
|
||||
{processedItems} of {totalItems} {stepMessages[step].total}
|
||||
|
@ -64,7 +74,7 @@ const StepProgress = (): JSX.Element | null => {
|
|||
) : undefined}
|
||||
</>
|
||||
);
|
||||
case 'downloading':
|
||||
case 'exporting':
|
||||
return (
|
||||
<>
|
||||
{stepMessages[step].total}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { useEffect, useState } from 'react';
|
|||
import { FormValues } from '@ui/components/ExportForm';
|
||||
import { parse } from '@ui/parser';
|
||||
|
||||
import { MessageData } from '.';
|
||||
import { MessageData, sendMessage } from '.';
|
||||
|
||||
export type UseFigmaHook = {
|
||||
missingFonts: string[] | undefined;
|
||||
|
@ -19,7 +19,14 @@ export type UseFigmaHook = {
|
|||
exportPenpot: (data: FormValues) => void;
|
||||
};
|
||||
|
||||
export type Steps = 'processing' | 'remote' | 'images' | 'optimization' | 'downloading';
|
||||
export type Steps =
|
||||
| 'processing'
|
||||
| 'remote'
|
||||
| 'images'
|
||||
| 'optimization'
|
||||
| 'building'
|
||||
| 'components'
|
||||
| 'exporting';
|
||||
|
||||
export const useFigma = (): UseFigmaHook => {
|
||||
const [missingFonts, setMissingFonts] = useState<string[]>();
|
||||
|
@ -44,6 +51,12 @@ export const useFigma = (): UseFigmaHook => {
|
|||
switch (pluginMessage.type) {
|
||||
case 'PENPOT_DOCUMENT': {
|
||||
const file = await parse(pluginMessage.data);
|
||||
|
||||
sendMessage({
|
||||
type: 'PROGRESS_STEP',
|
||||
data: 'exporting'
|
||||
});
|
||||
|
||||
const blob = await file.export();
|
||||
|
||||
download(blob, `${pluginMessage.data.name}.zip`);
|
||||
|
|
|
@ -16,7 +16,6 @@ export type ShapeBaseAttributes = {
|
|||
id?: Uuid;
|
||||
figmaId?: string; // @TODO: move to any other place
|
||||
figmaRelatedId?: string; // @TODO: move to any other place
|
||||
name?: string;
|
||||
type?:
|
||||
| 'frame'
|
||||
| 'group'
|
||||
|
@ -29,8 +28,6 @@ export type ShapeBaseAttributes = {
|
|||
| 'image'
|
||||
| 'component'
|
||||
| 'instance';
|
||||
selrect?: Selrect;
|
||||
points?: Point[];
|
||||
transform?: Matrix;
|
||||
transformInverse?: Matrix;
|
||||
parentId?: Uuid;
|
||||
|
@ -39,7 +36,7 @@ export type ShapeBaseAttributes = {
|
|||
};
|
||||
|
||||
export type ShapeAttributes = {
|
||||
name?: string;
|
||||
name: string;
|
||||
componentId?: string;
|
||||
componentFile?: string;
|
||||
componentRoot?: boolean;
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
import { componentsLibrary } from '@plugin/ComponentLibrary';
|
||||
|
||||
import { PenpotFile } from '@ui/lib/types/penpotFile';
|
||||
import { symbolBlendMode, symbolFills, symbolStrokes } from '@ui/parser/creators/symbols';
|
||||
import { uiComponents } from '@ui/parser/libraries';
|
||||
|
||||
import { createItems } from '.';
|
||||
|
||||
export const createComponentLibrary = (file: PenpotFile) => {
|
||||
uiComponents.all().forEach(uiComponent => {
|
||||
const component = componentsLibrary.get(uiComponent.componentFigmaId);
|
||||
if (!component) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { children = [], fills, strokes, blendMode, ...rest } = component;
|
||||
|
||||
file.startComponent({
|
||||
...rest,
|
||||
fills: symbolFills(fills),
|
||||
strokes: symbolStrokes(strokes),
|
||||
blendMode: symbolBlendMode(blendMode),
|
||||
id: uiComponent.componentId,
|
||||
componentId: uiComponent.componentId,
|
||||
mainInstancePage: uiComponent.mainInstancePage,
|
||||
mainInstanceId: uiComponent.mainInstanceId,
|
||||
componentRoot: true,
|
||||
mainInstance: true,
|
||||
componentFile: file.getId()
|
||||
});
|
||||
|
||||
createItems(file, children);
|
||||
|
||||
file.finishComponent();
|
||||
});
|
||||
};
|
63
ui-src/parser/creators/createComponentsLibrary.ts
Normal file
63
ui-src/parser/creators/createComponentsLibrary.ts
Normal file
|
@ -0,0 +1,63 @@
|
|||
import { componentsLibrary } from '@plugin/ComponentLibrary';
|
||||
import { sleep } from '@plugin/utils/sleep';
|
||||
|
||||
import { sendMessage } from '@ui/context';
|
||||
import { PenpotFile } from '@ui/lib/types/penpotFile';
|
||||
import { symbolBlendMode, symbolFills, symbolStrokes } from '@ui/parser/creators/symbols';
|
||||
import { UiComponent, uiComponents } from '@ui/parser/libraries';
|
||||
|
||||
import { createItems } from '.';
|
||||
|
||||
export const createComponentsLibrary = async (file: PenpotFile) => {
|
||||
let componentsBuilt = 1;
|
||||
const components = uiComponents.all();
|
||||
|
||||
sendMessage({
|
||||
type: 'PROGRESS_TOTAL_ITEMS',
|
||||
data: components.length
|
||||
});
|
||||
|
||||
sendMessage({
|
||||
type: 'PROGRESS_STEP',
|
||||
data: 'components'
|
||||
});
|
||||
|
||||
for (const uiComponent of components) {
|
||||
createComponentLibrary(file, uiComponent);
|
||||
|
||||
sendMessage({
|
||||
type: 'PROGRESS_PROCESSED_ITEMS',
|
||||
data: componentsBuilt++
|
||||
});
|
||||
|
||||
await sleep(0);
|
||||
}
|
||||
};
|
||||
|
||||
const createComponentLibrary = async (file: PenpotFile, uiComponent: UiComponent) => {
|
||||
const component = componentsLibrary.get(uiComponent.componentFigmaId);
|
||||
|
||||
if (!component) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { children = [], fills, strokes, blendMode, ...rest } = component;
|
||||
|
||||
file.startComponent({
|
||||
...rest,
|
||||
fills: symbolFills(fills),
|
||||
strokes: symbolStrokes(strokes),
|
||||
blendMode: symbolBlendMode(blendMode),
|
||||
id: uiComponent.componentId,
|
||||
componentId: uiComponent.componentId,
|
||||
mainInstancePage: uiComponent.mainInstancePage,
|
||||
mainInstanceId: uiComponent.mainInstanceId,
|
||||
componentRoot: true,
|
||||
mainInstance: true,
|
||||
componentFile: file.getId()
|
||||
});
|
||||
|
||||
createItems(file, children);
|
||||
|
||||
file.finishComponent();
|
||||
};
|
41
ui-src/parser/creators/createFile.ts
Normal file
41
ui-src/parser/creators/createFile.ts
Normal file
|
@ -0,0 +1,41 @@
|
|||
import { sleep } from '@plugin/utils/sleep';
|
||||
|
||||
import { sendMessage } from '@ui/context';
|
||||
import { createFile as createPenpotFile } from '@ui/lib/penpot';
|
||||
import { PenpotPage } from '@ui/lib/types/penpotPage';
|
||||
import { idLibrary } from '@ui/parser';
|
||||
import { createComponentsLibrary, createPage } from '@ui/parser/creators';
|
||||
import { uiComponents } from '@ui/parser/libraries';
|
||||
|
||||
export const createFile = async (name: string, children: PenpotPage[]) => {
|
||||
const file = createPenpotFile(name);
|
||||
let pagesBuilt = 1;
|
||||
|
||||
uiComponents.init();
|
||||
idLibrary.init();
|
||||
|
||||
sendMessage({
|
||||
type: 'PROGRESS_TOTAL_ITEMS',
|
||||
data: children.length
|
||||
});
|
||||
|
||||
sendMessage({
|
||||
type: 'PROGRESS_STEP',
|
||||
data: 'building'
|
||||
});
|
||||
|
||||
for (const page of children) {
|
||||
await createPage(file, page);
|
||||
|
||||
sendMessage({
|
||||
type: 'PROGRESS_PROCESSED_ITEMS',
|
||||
data: pagesBuilt++
|
||||
});
|
||||
|
||||
await sleep(0);
|
||||
}
|
||||
|
||||
await createComponentsLibrary(file);
|
||||
|
||||
return file;
|
||||
};
|
|
@ -1,3 +1,4 @@
|
|||
import { sendMessage } from '@ui/context';
|
||||
import { PenpotFile } from '@ui/lib/types/penpotFile';
|
||||
import { PenpotNode } from '@ui/types';
|
||||
|
||||
|
@ -20,6 +21,11 @@ export const createItems = (file: PenpotFile, nodes: PenpotNode[]) => {
|
|||
};
|
||||
|
||||
const createItem = (file: PenpotFile, node: PenpotNode) => {
|
||||
sendMessage({
|
||||
type: 'PROGRESS_CURRENT_ITEM',
|
||||
data: node.name
|
||||
});
|
||||
|
||||
switch (node.type) {
|
||||
case 'rect':
|
||||
return createRectangle(file, node);
|
||||
|
|
|
@ -3,7 +3,8 @@ export * from './createBool';
|
|||
export * from './createCircle';
|
||||
export * from './createComponent';
|
||||
export * from './createComponentInstance';
|
||||
export * from './createComponentLibrary';
|
||||
export * from './createComponentsLibrary';
|
||||
export * from './createFile';
|
||||
export * from './createGroup';
|
||||
export * from './createItems';
|
||||
export * from './createPage';
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Uuid } from '@ui/lib/types/utils/uuid';
|
||||
|
||||
type UiComponent = {
|
||||
export type UiComponent = {
|
||||
componentId: Uuid;
|
||||
mainInstancePage?: Uuid;
|
||||
mainInstanceId: Uuid;
|
||||
|
|
|
@ -3,27 +3,29 @@ import { componentsLibrary } from '@plugin/ComponentLibrary';
|
|||
import { sleep } from '@plugin/utils/sleep';
|
||||
|
||||
import { sendMessage } from '@ui/context';
|
||||
import { createFile } from '@ui/lib/penpot';
|
||||
import { createComponentLibrary, createPage } from '@ui/parser/creators';
|
||||
import { uiComponents, uiImages } from '@ui/parser/libraries';
|
||||
import { createFile } from '@ui/parser/creators';
|
||||
import { uiImages } from '@ui/parser/libraries';
|
||||
import { PenpotDocument } from '@ui/types';
|
||||
|
||||
import { idLibrary, parseImage } from '.';
|
||||
import { parseImage } from '.';
|
||||
|
||||
const optimizeImages = async (images: Record<string, Uint8Array>) => {
|
||||
const imagesToOptimize = Object.entries(images);
|
||||
let imagesOptimized = 1;
|
||||
|
||||
sendMessage({
|
||||
type: 'PROGRESS_STEP',
|
||||
data: 'optimization'
|
||||
});
|
||||
if (imagesToOptimize.length === 0) return;
|
||||
|
||||
let imagesOptimized = 1;
|
||||
|
||||
sendMessage({
|
||||
type: 'PROGRESS_TOTAL_ITEMS',
|
||||
data: imagesToOptimize.length
|
||||
});
|
||||
|
||||
sendMessage({
|
||||
type: 'PROGRESS_STEP',
|
||||
data: 'optimization'
|
||||
});
|
||||
|
||||
for (const [key, bytes] of imagesToOptimize) {
|
||||
if (bytes) {
|
||||
uiImages.register(key, await parseImage(bytes));
|
||||
|
@ -43,23 +45,5 @@ export const parse = async ({ name, children = [], components, images }: PenpotD
|
|||
|
||||
await optimizeImages(images);
|
||||
|
||||
sendMessage({
|
||||
type: 'PROGRESS_STEP',
|
||||
data: 'downloading'
|
||||
});
|
||||
|
||||
await sleep(20);
|
||||
|
||||
uiComponents.init();
|
||||
idLibrary.init();
|
||||
|
||||
const file = createFile(name);
|
||||
|
||||
for (const page of children) {
|
||||
await createPage(file, page);
|
||||
}
|
||||
|
||||
createComponentLibrary(file);
|
||||
|
||||
return file;
|
||||
return createFile(name, children);
|
||||
};
|
||||
|
|
|
@ -5,6 +5,7 @@ import { Children } from '@ui/lib/types/utils/children';
|
|||
export type ComponentRoot = {
|
||||
figmaId: string;
|
||||
type: 'component';
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type ComponentTextPropertyOverride = {
|
||||
|
|
Loading…
Reference in a new issue