Skip to content

Commit

Permalink
Color libraries (#183)
Browse files Browse the repository at this point in the history
* wip

* color library

* fixes

* fixes

* fixes

* fixes

* changeset

* fixes

* fixes

* fixes

* fixes
  • Loading branch information
Cenadros authored Jun 25, 2024
1 parent b652af1 commit a58f9e9
Show file tree
Hide file tree
Showing 19 changed files with 248 additions and 35 deletions.
5 changes: 5 additions & 0 deletions .changeset/strange-gorillas-knock.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"penpot-exporter": minor
---

Support for color libraries
14 changes: 4 additions & 10 deletions plugin-src/StyleLibrary.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,21 @@
import { Fill } from '@ui/lib/types/utils/fill';

class StyleLibrary {
private styles: Map<string, Fill[]> = new Map();
private styles: Map<string, PaintStyle | undefined> = new Map();

public register(id: string, styles: Fill[]) {
public register(id: string, styles?: PaintStyle | undefined) {
this.styles.set(id, styles);
}

public get(id: string): Fill[] | undefined {
public get(id: string): PaintStyle | undefined {
return this.styles.get(id);
}

public has(id: string): boolean {
return this.styles.has(id);
}

public all(): Record<string, Fill[]> {
public all(): Record<string, PaintStyle | undefined> {
return Object.fromEntries(this.styles.entries());
}

public init(styles: Record<string, Fill[]>): void {
this.styles = new Map(Object.entries(styles));
}
}

export const styleLibrary = new StyleLibrary();
4 changes: 2 additions & 2 deletions plugin-src/transformers/partials/transformFills.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { translateFillStyle, translateFills } from '@plugin/translators/fills';
import { translateFillStyleId, translateFills } from '@plugin/translators/fills';
import { StyleTextSegment } from '@plugin/translators/text/paragraph';

import { ShapeAttributes } from '@ui/lib/types/shapes/shape';
Expand All @@ -14,7 +14,7 @@ export const transformFills = (
if (hasFillStyle(node)) {
return {
fills: [],
fillStyleId: translateFillStyle(node.fillStyleId, node.fills)
fillStyleId: translateFillStyleId(node.fillStyleId)
};
}

Expand Down
56 changes: 54 additions & 2 deletions plugin-src/transformers/transformDocumentNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import { imagesLibrary } from '@plugin/ImageLibrary';
import { remoteComponentLibrary } from '@plugin/RemoteComponentLibrary';
import { styleLibrary } from '@plugin/StyleLibrary';
import { translateRemoteChildren } from '@plugin/translators';
import { translatePaintStyles } from '@plugin/translators/fills';
import { sleep } from '@plugin/utils';

import { PenpotPage } from '@ui/lib/types/penpotPage';
import { FillStyle } from '@ui/lib/types/utils/fill';
import { PenpotDocument } from '@ui/types';

import { transformPageNode } from '.';
Expand Down Expand Up @@ -48,6 +50,47 @@ const downloadImages = async (): Promise<Record<string, Uint8Array>> => {
return images;
};

const getFillStyles = async (): Promise<Record<string, FillStyle>> => {
const stylesToFetch = Object.entries(styleLibrary.all());
const styles: Record<string, FillStyle> = {};

if (stylesToFetch.length === 0) return styles;

let currentStyle = 1;

figma.ui.postMessage({
type: 'PROGRESS_TOTAL_ITEMS',
data: stylesToFetch.length
});

figma.ui.postMessage({
type: 'PROGRESS_STEP',
data: 'fills'
});

for (const [styleId, paintStyle] of stylesToFetch) {
const figmaStyle = paintStyle ?? (await figma.getStyleByIdAsync(styleId));
if (figmaStyle && isPaintStyle(figmaStyle)) {
styles[styleId] = translatePaintStyles(figmaStyle);
}

figma.ui.postMessage({
type: 'PROGRESS_PROCESSED_ITEMS',
data: currentStyle++
});

await sleep(0);
}

await sleep(20);

return styles;
};

const isPaintStyle = (style: BaseStyle): style is PaintStyle => {
return style.type === 'PAINT';
};

const processPages = async (node: DocumentNode): Promise<PenpotPage[]> => {
const children = [];
let currentPage = 1;
Expand All @@ -74,6 +117,11 @@ const processPages = async (node: DocumentNode): Promise<PenpotPage[]> => {
};

export const transformDocumentNode = async (node: DocumentNode): Promise<PenpotDocument> => {
const localPaintStyles = await figma.getLocalPaintStylesAsync();
localPaintStyles.forEach(style => {
styleLibrary.register(style.id, style);
});

const children = await processPages(node);

if (remoteComponentLibrary.remaining() > 0) {
Expand All @@ -83,11 +131,15 @@ export const transformDocumentNode = async (node: DocumentNode): Promise<PenpotD
});
}

const styles = await getFillStyles();

const images = await downloadImages();

return {
name: node.name,
children,
components: componentsLibrary.all(),
images: await downloadImages(),
styles: styleLibrary.all()
images,
styles
};
};
1 change: 1 addition & 0 deletions plugin-src/translators/fills/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './translateFills';
export * from './translateImageFill';
export * from './translatePaintStyles';
export * from './translateSolidFill';
7 changes: 3 additions & 4 deletions plugin-src/translators/fills/translateFills.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,13 @@ export const translateFills = (
return penpotFills;
};

export const translateFillStyle = (
fillStyleId: string | typeof figma.mixed | undefined,
fills: readonly Paint[] | typeof figma.mixed | undefined
export const translateFillStyleId = (
fillStyleId: string | typeof figma.mixed | undefined
): string | undefined => {
if (fillStyleId === figma.mixed || fillStyleId === undefined) return;

if (!styleLibrary.has(fillStyleId)) {
styleLibrary.register(fillStyleId, translateFills(fills));
styleLibrary.register(fillStyleId);
}

return fillStyleId;
Expand Down
35 changes: 35 additions & 0 deletions plugin-src/translators/fills/translatePaintStyles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { translateFill } from '@plugin/translators/fills/translateFills';

import { FillStyle } from '@ui/lib/types/utils/fill';

export const translatePaintStyles = (figmaStyle: PaintStyle): FillStyle => {
const fillStyle: FillStyle = {
name: figmaStyle.name,
fills: [],
colors: []
};

const colorName = (figmaStyle: PaintStyle, index: number): string => {
// @TODO: Think something better
return figmaStyle.paints.length > 1 ? `Color ${index + 1}` : figmaStyle.name;
};

let index = 0;
const path =
(figmaStyle.remote ? 'Remote / ' : '') + (figmaStyle.paints.length > 1 ? figmaStyle.name : '');

for (const fill of figmaStyle.paints) {
const penpotFill = translateFill(fill);

if (penpotFill) {
fillStyle.fills.unshift(penpotFill);
fillStyle.colors.unshift({
path,
name: colorName(figmaStyle, index)
});
}
index++;
}

return fillStyle;
};
12 changes: 12 additions & 0 deletions ui-src/components/ExporterProgress.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ const stepMessages: Record<Steps, Messages> = {
total: 'pages built 🏗️',
current: 'Currently processing layer'
},
fills: {
total: 'color libraries fetched 🎨'
},
format: {
total: 'formatting color libraries 🎨'
},
libraries: {
total: 'color libraries built 🎨'
},
components: {
total: 'components built 🏗️',
current: 'Currently processing layer'
Expand Down Expand Up @@ -60,7 +69,10 @@ const StepProgress = (): JSX.Element | null => {
case 'images':
case 'optimization':
case 'building':
case 'fills':
case 'components':
case 'format':
case 'libraries':
return (
<>
{processedItems} of {totalItems} {stepMessages[step].total}
Expand Down
5 changes: 4 additions & 1 deletion ui-src/context/useFigma.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ export type Steps =
| 'optimization'
| 'building'
| 'components'
| 'exporting';
| 'exporting'
| 'fills'
| 'format'
| 'libraries';

export const useFigma = (): UseFigmaHook => {
const [missingFonts, setMissingFonts] = useState<string[]>();
Expand Down
4 changes: 2 additions & 2 deletions ui-src/lib/types/utils/color.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Gradient } from './gradient';
import { ImageColor } from './imageColor';
import { ImageColor, PartialImageColor } from './imageColor';
import { Uuid } from './uuid';

export type Color = {
Expand All @@ -13,5 +13,5 @@ export type Color = {
refId?: Uuid;
refFile?: Uuid;
gradient?: Gradient;
image?: ImageColor;
image?: ImageColor | PartialImageColor; // @TODO: move to any other place
};
8 changes: 8 additions & 0 deletions ui-src/lib/types/utils/fill.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Color } from '@ui/lib/types/utils/color';

import { Gradient } from './gradient';
import { ImageColor, PartialImageColor } from './imageColor';
import { Uuid } from './uuid';
Expand All @@ -10,3 +12,9 @@ export type Fill = {
fillColorRefId?: Uuid;
fillImage?: ImageColor | PartialImageColor; // @TODO: move to any other place
};

export type FillStyle = {
name: string;
fills: Fill[];
colors: Color[];
};
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { sleep } from '@plugin/utils/sleep';

import { sendMessage } from '@ui/context';
import { createFile as createPenpotFile } from '@ui/lib/penpot';
import { PenpotFile } from '@ui/lib/types/penpotFile';
import { PenpotPage } from '@ui/lib/types/penpotPage';
import { idLibrary } from '@ui/parser';
import { createComponentsLibrary, createPage } from '@ui/parser/creators';
import { createColorsLibrary, createComponentsLibrary, createPage } from '@ui/parser/creators';
import { uiComponents } from '@ui/parser/libraries';

export const createFile = async (name: string, children: PenpotPage[]) => {
const file = createPenpotFile(name);
export const buildFile = async (file: PenpotFile, children: PenpotPage[]) => {
let pagesBuilt = 1;

uiComponents.init();
Expand All @@ -35,6 +34,8 @@ export const createFile = async (name: string, children: PenpotPage[]) => {
await sleep(0);
}

await createColorsLibrary(file);

await createComponentsLibrary(file);

return file;
Expand Down
41 changes: 41 additions & 0 deletions ui-src/parser/creators/createColorsLibrary.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { sleep } from '@plugin/utils/sleep';

import { sendMessage } from '@ui/context';
import { PenpotFile } from '@ui/lib/types/penpotFile';
import { uiColorLibraries } from '@ui/parser/libraries/UiColorLibraries';

export const createColorsLibrary = async (file: PenpotFile) => {
let librariesBuilt = 1;
const libraries = uiColorLibraries.all();

sendMessage({
type: 'PROGRESS_TOTAL_ITEMS',
data: libraries.length
});

sendMessage({
type: 'PROGRESS_STEP',
data: 'libraries'
});

for (const library of libraries) {
for (let index = 0; index < library.fills.length; index++) {
file.addLibraryColor({
...library.colors[index],
id: library.fills[index].fillColorRefId,
refFile: library.fills[index].fillColorRefFile,
color: library.fills[index].fillColor,
opacity: library.fills[index].fillOpacity,
image: library.fills[index].fillImage,
gradient: library.fills[index].fillColorGradient
});

sendMessage({
type: 'PROGRESS_PROCESSED_ITEMS',
data: librariesBuilt++
});

await sleep(0);
}
}
};
3 changes: 2 additions & 1 deletion ui-src/parser/creators/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
export * from './createArtboard';
export * from './createBool';
export * from './createCircle';
export * from './createColorsLibrary';
export * from './createComponent';
export * from './createComponentInstance';
export * from './createComponentsLibrary';
export * from './createFile';
export * from './buildFile';
export * from './createGroup';
export * from './createItems';
export * from './createPage';
Expand Down
5 changes: 2 additions & 3 deletions ui-src/parser/creators/symbols/symbolFills.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { styleLibrary } from '@plugin/StyleLibrary';

import { Fill } from '@ui/lib/types/utils/fill';
import { ImageColor, PartialImageColor } from '@ui/lib/types/utils/imageColor';
import { uiImages } from '@ui/parser/libraries';
import { uiColorLibraries } from '@ui/parser/libraries/UiColorLibraries';

export const symbolFills = (fillStyleId?: string, fills?: Fill[]): Fill[] | undefined => {
const nodeFills = fillStyleId ? styleLibrary.get(fillStyleId) : fills;
const nodeFills = fillStyleId ? uiColorLibraries.get(fillStyleId)?.fills : fills;

if (!nodeFills) return;

Expand Down
19 changes: 19 additions & 0 deletions ui-src/parser/libraries/UiColorLibraries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { FillStyle } from '@ui/lib/types/utils/fill';

class UiColorLibraries {
private libraries: Map<string, FillStyle> = new Map();

public register(id: string, fillStyle: FillStyle) {
this.libraries.set(id, fillStyle);
}

public get(id: string): FillStyle | undefined {
return this.libraries.get(id);
}

public all(): FillStyle[] {
return Array.from(this.libraries.values());
}
}

export const uiColorLibraries = new UiColorLibraries();
1 change: 1 addition & 0 deletions ui-src/parser/libraries/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './UiComponents';
export * from './UiImages';
export * from './UiColorLibraries';
Loading

0 comments on commit a58f9e9

Please sign in to comment.