mirror of
https://github.com/penpot/penpot-exporter-figma-plugin.git
synced 2024-12-22 05:33:02 -05:00
Text libraries (#185)
* color library * fixes * wip * wip * wip * wip * working * improvements * changeset * changeset * changeset & cleaning * changeset & cleaning * improvements * fixes * rebase
This commit is contained in:
parent
a58f9e913d
commit
d3c144e5e9
42 changed files with 506 additions and 155 deletions
5
.changeset/heavy-timers-sit.md
Normal file
5
.changeset/heavy-timers-sit.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"penpot-exporter": minor
|
||||
---
|
||||
|
||||
Add support for typographies
|
5
.changeset/mean-clouds-jog.md
Normal file
5
.changeset/mean-clouds-jog.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"penpot-exporter": patch
|
||||
---
|
||||
|
||||
Improve font weight translation
|
5
.changeset/selfish-spies-cover.md
Normal file
5
.changeset/selfish-spies-cover.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"penpot-exporter": patch
|
||||
---
|
||||
|
||||
Fix letter spacing
|
21
plugin-src/TextLibrary.ts
Normal file
21
plugin-src/TextLibrary.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
class TextLibrary {
|
||||
private styles: Map<string, TextStyle | undefined> = new Map();
|
||||
|
||||
public register(id: string, styles?: TextStyle | undefined) {
|
||||
this.styles.set(id, styles);
|
||||
}
|
||||
|
||||
public get(id: string): TextStyle | undefined {
|
||||
return this.styles.get(id);
|
||||
}
|
||||
|
||||
public has(id: string): boolean {
|
||||
return this.styles.has(id);
|
||||
}
|
||||
|
||||
public all(): Record<string, TextStyle | undefined> {
|
||||
return Object.fromEntries(this.styles.entries());
|
||||
}
|
||||
}
|
||||
|
||||
export const textLibrary = new TextLibrary();
|
|
@ -1,15 +1,11 @@
|
|||
import { translateFillStyleId, translateFills } from '@plugin/translators/fills';
|
||||
import { StyleTextSegment } from '@plugin/translators/text/paragraph';
|
||||
import { TextSegment } from '@plugin/translators/text/paragraph';
|
||||
|
||||
import { ShapeAttributes } from '@ui/lib/types/shapes/shape';
|
||||
import { TextStyle } from '@ui/lib/types/shapes/textShape';
|
||||
|
||||
export const transformFills = (
|
||||
node:
|
||||
| (MinimalFillsMixin & DimensionAndPositionMixin)
|
||||
| VectorRegion
|
||||
| VectorNode
|
||||
| StyleTextSegment
|
||||
node: (MinimalFillsMixin & DimensionAndPositionMixin) | VectorRegion | VectorNode | TextSegment
|
||||
): Pick<ShapeAttributes, 'fills' | 'fillStyleId'> | Pick<TextStyle, 'fills' | 'fillStyleId'> => {
|
||||
if (hasFillStyle(node)) {
|
||||
return {
|
||||
|
@ -39,11 +35,7 @@ export const transformVectorFills = (
|
|||
};
|
||||
|
||||
const hasFillStyle = (
|
||||
node:
|
||||
| (MinimalFillsMixin & DimensionAndPositionMixin)
|
||||
| VectorRegion
|
||||
| VectorNode
|
||||
| StyleTextSegment
|
||||
node: (MinimalFillsMixin & DimensionAndPositionMixin) | VectorRegion | VectorNode | TextSegment
|
||||
): boolean => {
|
||||
return (
|
||||
node.fillStyleId !== figma.mixed &&
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { transformFills } from '@plugin/transformers/partials';
|
||||
import { transformTextStyle, translateStyleTextSegments } from '@plugin/translators/text';
|
||||
import { transformTextStyle, translateTextSegments } from '@plugin/translators/text';
|
||||
import { translateGrowType, translateVerticalAlign } from '@plugin/translators/text/properties';
|
||||
|
||||
import { TextAttributes, TextShape } from '@ui/lib/types/shapes/textShape';
|
||||
|
@ -16,7 +16,8 @@ export const transformText = (node: TextNode): TextAttributes & Pick<TextShape,
|
|||
'indentation',
|
||||
'listOptions',
|
||||
'fills',
|
||||
'fillStyleId'
|
||||
'fillStyleId',
|
||||
'textStyleId'
|
||||
]);
|
||||
|
||||
return {
|
||||
|
@ -30,7 +31,7 @@ export const transformText = (node: TextNode): TextAttributes & Pick<TextShape,
|
|||
children: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: translateStyleTextSegments(node, styledTextSegments),
|
||||
children: translateTextSegments(node, styledTextSegments),
|
||||
...transformTextStyle(node, styledTextSegments[0]),
|
||||
...transformFills(node)
|
||||
}
|
||||
|
|
|
@ -2,16 +2,26 @@ import { componentsLibrary } from '@plugin/ComponentLibrary';
|
|||
import { imagesLibrary } from '@plugin/ImageLibrary';
|
||||
import { remoteComponentLibrary } from '@plugin/RemoteComponentLibrary';
|
||||
import { styleLibrary } from '@plugin/StyleLibrary';
|
||||
import { textLibrary } from '@plugin/TextLibrary';
|
||||
import { translateRemoteChildren } from '@plugin/translators';
|
||||
import { translatePaintStyles } from '@plugin/translators/fills';
|
||||
import { translatePaintStyle, translateTextStyle } from '@plugin/translators/styles';
|
||||
import { sleep } from '@plugin/utils';
|
||||
|
||||
import { PenpotPage } from '@ui/lib/types/penpotPage';
|
||||
import { TypographyStyle } from '@ui/lib/types/shapes/textShape';
|
||||
import { FillStyle } from '@ui/lib/types/utils/fill';
|
||||
import { PenpotDocument } from '@ui/types';
|
||||
|
||||
import { transformPageNode } from '.';
|
||||
|
||||
const isPaintStyle = (style: BaseStyle): style is PaintStyle => {
|
||||
return style.type === 'PAINT';
|
||||
};
|
||||
|
||||
const isTextStyle = (style: BaseStyle): style is TextStyle => {
|
||||
return style.type === 'TEXT';
|
||||
};
|
||||
|
||||
const downloadImages = async (): Promise<Record<string, Uint8Array>> => {
|
||||
const imageToDownload = Object.entries(imagesLibrary.all());
|
||||
const images: Record<string, Uint8Array> = {};
|
||||
|
@ -71,7 +81,7 @@ const getFillStyles = async (): Promise<Record<string, FillStyle>> => {
|
|||
for (const [styleId, paintStyle] of stylesToFetch) {
|
||||
const figmaStyle = paintStyle ?? (await figma.getStyleByIdAsync(styleId));
|
||||
if (figmaStyle && isPaintStyle(figmaStyle)) {
|
||||
styles[styleId] = translatePaintStyles(figmaStyle);
|
||||
styles[styleId] = translatePaintStyle(figmaStyle);
|
||||
}
|
||||
|
||||
figma.ui.postMessage({
|
||||
|
@ -87,8 +97,41 @@ const getFillStyles = async (): Promise<Record<string, FillStyle>> => {
|
|||
return styles;
|
||||
};
|
||||
|
||||
const isPaintStyle = (style: BaseStyle): style is PaintStyle => {
|
||||
return style.type === 'PAINT';
|
||||
const getTextStyles = async (): Promise<Record<string, TypographyStyle>> => {
|
||||
const stylesToFetch = Object.entries(textLibrary.all());
|
||||
const styles: Record<string, TypographyStyle> = {};
|
||||
|
||||
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: 'typographies'
|
||||
});
|
||||
|
||||
for (const [styleId, style] of stylesToFetch) {
|
||||
const figmaStyle = style ?? (await figma.getStyleByIdAsync(styleId));
|
||||
if (figmaStyle && isTextStyle(figmaStyle)) {
|
||||
styles[styleId] = translateTextStyle(figmaStyle);
|
||||
}
|
||||
|
||||
figma.ui.postMessage({
|
||||
type: 'PROGRESS_PROCESSED_ITEMS',
|
||||
data: currentStyle++
|
||||
});
|
||||
|
||||
await sleep(0);
|
||||
}
|
||||
|
||||
await sleep(20);
|
||||
|
||||
return styles;
|
||||
};
|
||||
|
||||
const processPages = async (node: DocumentNode): Promise<PenpotPage[]> => {
|
||||
|
@ -122,6 +165,11 @@ export const transformDocumentNode = async (node: DocumentNode): Promise<PenpotD
|
|||
styleLibrary.register(style.id, style);
|
||||
});
|
||||
|
||||
const localTextStyles = await figma.getLocalTextStylesAsync();
|
||||
localTextStyles.forEach(style => {
|
||||
textLibrary.register(style.id, style);
|
||||
});
|
||||
|
||||
const children = await processPages(node);
|
||||
|
||||
if (remoteComponentLibrary.remaining() > 0) {
|
||||
|
@ -135,11 +183,14 @@ export const transformDocumentNode = async (node: DocumentNode): Promise<PenpotD
|
|||
|
||||
const images = await downloadImages();
|
||||
|
||||
const typographies = await getTextStyles();
|
||||
|
||||
return {
|
||||
name: node.name,
|
||||
children,
|
||||
components: componentsLibrary.all(),
|
||||
images,
|
||||
styles
|
||||
styles,
|
||||
typographies
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
export * from './translateFills';
|
||||
export * from './translateImageFill';
|
||||
export * from './translatePaintStyles';
|
||||
export * from './translateSolidFill';
|
||||
|
|
2
plugin-src/translators/styles/index.ts
Normal file
2
plugin-src/translators/styles/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export * from './translatePaintStyle';
|
||||
export * from './translateTextStyle';
|
|
@ -2,7 +2,7 @@ import { translateFill } from '@plugin/translators/fills/translateFills';
|
|||
|
||||
import { FillStyle } from '@ui/lib/types/utils/fill';
|
||||
|
||||
export const translatePaintStyles = (figmaStyle: PaintStyle): FillStyle => {
|
||||
export const translatePaintStyle = (figmaStyle: PaintStyle): FillStyle => {
|
||||
const fillStyle: FillStyle = {
|
||||
name: figmaStyle.name,
|
||||
fills: [],
|
||||
|
@ -10,7 +10,6 @@ export const translatePaintStyles = (figmaStyle: PaintStyle): FillStyle => {
|
|||
};
|
||||
|
||||
const colorName = (figmaStyle: PaintStyle, index: number): string => {
|
||||
// @TODO: Think something better
|
||||
return figmaStyle.paints.length > 1 ? `Color ${index + 1}` : figmaStyle.name;
|
||||
};
|
||||
|
32
plugin-src/translators/styles/translateTextStyle.ts
Normal file
32
plugin-src/translators/styles/translateTextStyle.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import { translateFontName } from '@plugin/translators/text/font';
|
||||
import {
|
||||
translateFontStyle,
|
||||
translateLetterSpacing,
|
||||
translateLineHeight,
|
||||
translateTextDecoration,
|
||||
translateTextTransform
|
||||
} from '@plugin/translators/text/properties';
|
||||
|
||||
import { TypographyStyle } from '@ui/lib/types/shapes/textShape';
|
||||
|
||||
export const translateTextStyle = (figmaStyle: TextStyle): TypographyStyle => {
|
||||
const path = figmaStyle.remote ? 'Remote / ' : '';
|
||||
|
||||
return {
|
||||
name: figmaStyle.name,
|
||||
textStyle: {
|
||||
...translateFontName(figmaStyle.fontName),
|
||||
fontFamily: figmaStyle.fontName.family,
|
||||
fontSize: figmaStyle.fontSize.toString(),
|
||||
fontStyle: translateFontStyle(figmaStyle.fontName.style),
|
||||
textDecoration: translateTextDecoration(figmaStyle),
|
||||
letterSpacing: translateLetterSpacing(figmaStyle),
|
||||
textTransform: translateTextTransform(figmaStyle),
|
||||
lineHeight: translateLineHeight(figmaStyle)
|
||||
},
|
||||
typography: {
|
||||
path,
|
||||
name: figmaStyle.name
|
||||
}
|
||||
};
|
||||
};
|
|
@ -1,10 +1,15 @@
|
|||
import { getCustomFontId, translateFontVariantId } from '@plugin/translators/text/font/custom';
|
||||
|
||||
import { FontId } from '@ui/lib/types/shapes/textShape';
|
||||
import { TextTypography } from '@ui/lib/types/shapes/textShape';
|
||||
|
||||
export const translateCustomFont = (fontName: FontName, fontWeight: number): FontId | undefined => {
|
||||
export const translateCustomFont = (
|
||||
fontName: FontName,
|
||||
fontWeight: string
|
||||
): Pick<TextTypography, 'fontId' | 'fontVariantId' | 'fontWeight'> | undefined => {
|
||||
const customFontId = getCustomFontId(fontName);
|
||||
return {
|
||||
fontId: `custom-${getCustomFontId(fontName)}`,
|
||||
fontVariantId: translateFontVariantId(fontName, fontWeight)
|
||||
fontId: customFontId ? `custom-${customFontId}` : '',
|
||||
fontVariantId: translateFontVariantId(fontName, fontWeight),
|
||||
fontWeight
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
export const translateFontVariantId = (fontName: FontName, fontWeight: number) => {
|
||||
export const translateFontVariantId = (fontName: FontName, fontWeight: string) => {
|
||||
const style = fontName.style.toLowerCase().includes('italic') ? 'italic' : 'normal';
|
||||
|
||||
return `${style}-${fontWeight.toString()}`;
|
||||
return `${style}-${fontWeight}`;
|
||||
};
|
||||
|
|
|
@ -3,7 +3,7 @@ import { GoogleFont } from './googleFont';
|
|||
export const translateFontVariantId = (
|
||||
googleFont: GoogleFont,
|
||||
fontName: FontName,
|
||||
fontWeight: number
|
||||
fontWeight: string
|
||||
) => {
|
||||
// check match directly by style
|
||||
const variant = googleFont.variants?.find(variant => variant === fontName.style.toLowerCase());
|
||||
|
@ -13,7 +13,7 @@ export const translateFontVariantId = (
|
|||
// check match by style and weight
|
||||
const italic = fontName.style.toLowerCase().includes('italic') ? 'italic' : '';
|
||||
const variantWithWeight = googleFont.variants?.find(
|
||||
variant => variant === `${fontWeight.toString()}${italic}`
|
||||
variant => variant === `${fontWeight}${italic}`
|
||||
);
|
||||
|
||||
if (variantWithWeight !== undefined) return variantWithWeight;
|
||||
|
|
|
@ -3,21 +3,25 @@ import slugify from 'slugify';
|
|||
import { Cache } from '@plugin/Cache';
|
||||
import { translateFontVariantId } from '@plugin/translators/text/font/gfonts';
|
||||
|
||||
import { FontId } from '@ui/lib/types/shapes/textShape';
|
||||
import { TextTypography } from '@ui/lib/types/shapes/textShape';
|
||||
|
||||
import { items as gfonts } from './gfonts.json';
|
||||
import { GoogleFont } from './googleFont';
|
||||
|
||||
const fontsCache = new Cache<string, GoogleFont>({ max: 30 });
|
||||
|
||||
export const translateGoogleFont = (fontName: FontName, fontWeight: number): FontId | undefined => {
|
||||
export const translateGoogleFont = (
|
||||
fontName: FontName,
|
||||
fontWeight: string
|
||||
): Pick<TextTypography, 'fontId' | 'fontVariantId' | 'fontWeight'> | undefined => {
|
||||
const googleFont = getGoogleFont(fontName);
|
||||
|
||||
if (googleFont === undefined) return;
|
||||
|
||||
return {
|
||||
fontId: `gfont-${slugify(fontName.family.toLowerCase())}`,
|
||||
fontVariantId: translateFontVariantId(googleFont, fontName, fontWeight)
|
||||
fontVariantId: translateFontVariantId(googleFont, fontName, fontWeight),
|
||||
fontWeight
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
export * from './translateFontId';
|
||||
export * from './translateFontName';
|
||||
|
|
|
@ -3,13 +3,12 @@ import { LocalFont } from './localFont';
|
|||
export const translateFontVariantId = (
|
||||
localFont: LocalFont,
|
||||
fontName: FontName,
|
||||
fontWeight: number
|
||||
fontWeight: string
|
||||
): string | undefined => {
|
||||
// check match by style and weight
|
||||
const italic = fontName.style.toLowerCase().includes('italic');
|
||||
const variantWithStyleWeight = localFont.variants?.find(
|
||||
variant =>
|
||||
variant.weight === fontWeight.toString() && variant.style === (italic ? 'italic' : 'normal')
|
||||
variant => variant.weight === fontWeight && variant.style === (italic ? 'italic' : 'normal')
|
||||
);
|
||||
|
||||
if (variantWithStyleWeight !== undefined) return variantWithStyleWeight.id;
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
import { LocalFont, translateFontVariantId } from '@plugin/translators/text/font/local';
|
||||
|
||||
import { FontId } from '@ui/lib/types/shapes/textShape';
|
||||
import { TextTypography } from '@ui/lib/types/shapes/textShape';
|
||||
|
||||
import { items as localFonts } from './localFonts.json';
|
||||
|
||||
export const translateLocalFont = (fontName: FontName, fontWeight: number): FontId | undefined => {
|
||||
export const translateLocalFont = (
|
||||
fontName: FontName,
|
||||
fontWeight: string
|
||||
): Pick<TextTypography, 'fontId' | 'fontVariantId' | 'fontWeight'> | undefined => {
|
||||
const localFont = getLocalFont(fontName);
|
||||
|
||||
if (localFont === undefined) return;
|
||||
|
||||
return {
|
||||
fontId: localFont.id,
|
||||
fontVariantId: translateFontVariantId(localFont, fontName, fontWeight)
|
||||
fontVariantId: translateFontVariantId(localFont, fontName, fontWeight),
|
||||
fontWeight
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
import { FontId } from '@ui/lib/types/shapes/textShape';
|
||||
|
||||
import { translateCustomFont } from './custom';
|
||||
import { translateGoogleFont } from './gfonts';
|
||||
import { translateLocalFont } from './local';
|
||||
|
||||
export const translateFontId = (fontName: FontName, fontWeight: number): FontId | undefined => {
|
||||
return (
|
||||
translateGoogleFont(fontName, fontWeight) ??
|
||||
translateLocalFont(fontName, fontWeight) ??
|
||||
translateCustomFont(fontName, fontWeight)
|
||||
);
|
||||
};
|
19
plugin-src/translators/text/font/translateFontName.ts
Normal file
19
plugin-src/translators/text/font/translateFontName.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { translateFontWeight } from '@plugin/translators/text/properties';
|
||||
|
||||
import { TextTypography } from '@ui/lib/types/shapes/textShape';
|
||||
|
||||
import { translateCustomFont } from './custom';
|
||||
import { translateGoogleFont } from './gfonts';
|
||||
import { translateLocalFont } from './local';
|
||||
|
||||
export const translateFontName = (
|
||||
fontName: FontName
|
||||
): Pick<TextTypography, 'fontId' | 'fontVariantId' | 'fontWeight'> | undefined => {
|
||||
const fontWeight = translateFontWeight(fontName);
|
||||
|
||||
return (
|
||||
translateGoogleFont(fontName, fontWeight) ??
|
||||
translateLocalFont(fontName, fontWeight) ??
|
||||
translateCustomFont(fontName, fontWeight)
|
||||
);
|
||||
};
|
|
@ -1 +1 @@
|
|||
export * from './translateStyleTextSegments';
|
||||
export * from './translateTextSegments';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { StyleTextSegment } from '@plugin/translators/text/paragraph/translateParagraphProperties';
|
||||
import { TextSegment } from '@plugin/translators/text/paragraph/translateParagraphProperties';
|
||||
|
||||
import { TextNode as PenpotTextNode } from '@ui/lib/types/shapes/textShape';
|
||||
|
||||
|
@ -18,7 +18,7 @@ export class List {
|
|||
protected counter: number[] = [];
|
||||
private listTypeFactory = new ListTypeFactory();
|
||||
|
||||
public update(textNode: PenpotTextNode, segment: StyleTextSegment): void {
|
||||
public update(textNode: PenpotTextNode, segment: TextSegment): void {
|
||||
if (segment.indentation < this.indentation) {
|
||||
for (let i = segment.indentation + 1; i <= this.indentation; i++) {
|
||||
this.levels.delete(i);
|
||||
|
@ -41,7 +41,7 @@ export class List {
|
|||
this.indentation = segment.indentation;
|
||||
}
|
||||
|
||||
public getCurrentList(textNode: PenpotTextNode, segment: StyleTextSegment): PenpotTextNode {
|
||||
public getCurrentList(textNode: PenpotTextNode, segment: TextSegment): PenpotTextNode {
|
||||
const level = this.levels.get(segment.indentation);
|
||||
if (level === undefined) {
|
||||
throw new Error('Levels not updated');
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { TextNode as PenpotTextNode } from '@ui/lib/types/shapes/textShape';
|
||||
|
||||
import { List } from './List';
|
||||
import { StyleTextSegment } from './translateParagraphProperties';
|
||||
import { TextSegment } from './translateParagraphProperties';
|
||||
|
||||
export class Paragraph {
|
||||
private isParagraphStarting = false;
|
||||
|
@ -9,11 +9,7 @@ export class Paragraph {
|
|||
private firstTextNode: PenpotTextNode | null = null;
|
||||
private list = new List();
|
||||
|
||||
public format(
|
||||
node: TextNode,
|
||||
textNode: PenpotTextNode,
|
||||
segment: StyleTextSegment
|
||||
): PenpotTextNode[] {
|
||||
public format(node: TextNode, textNode: PenpotTextNode, segment: TextSegment): PenpotTextNode[] {
|
||||
const textNodes: PenpotTextNode[] = [];
|
||||
|
||||
const spacing = this.applySpacing(segment, node);
|
||||
|
@ -32,7 +28,7 @@ export class Paragraph {
|
|||
|
||||
private applyIndentation(
|
||||
textNode: PenpotTextNode,
|
||||
segment: StyleTextSegment,
|
||||
segment: TextSegment,
|
||||
node: TextNode
|
||||
): PenpotTextNode | undefined {
|
||||
if (this.isParagraphStarting || this.isFirstTextNode(textNode)) {
|
||||
|
@ -44,7 +40,7 @@ export class Paragraph {
|
|||
}
|
||||
}
|
||||
|
||||
private applySpacing(segment: StyleTextSegment, node: TextNode): PenpotTextNode | undefined {
|
||||
private applySpacing(segment: TextSegment, node: TextNode): PenpotTextNode | undefined {
|
||||
if (this.isParagraphStarting) {
|
||||
const isList = segment.listOptions.type !== 'NONE';
|
||||
|
||||
|
@ -73,8 +69,8 @@ export class Paragraph {
|
|||
fontSize: '5',
|
||||
fontStyle: 'normal',
|
||||
fontWeight: '400',
|
||||
lineHeight: 1,
|
||||
letterSpacing: 0
|
||||
lineHeight: '1',
|
||||
letterSpacing: '0'
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -88,8 +84,8 @@ export class Paragraph {
|
|||
fontSize: paragraphSpacing.toString(),
|
||||
fontStyle: 'normal',
|
||||
fontWeight: '400',
|
||||
lineHeight: 1,
|
||||
letterSpacing: 0
|
||||
lineHeight: '1',
|
||||
letterSpacing: '0'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { TextNode as PenpotTextNode } from '@ui/lib/types/shapes/textShape';
|
|||
|
||||
import { Paragraph } from './Paragraph';
|
||||
|
||||
export type StyleTextSegment = Pick<
|
||||
export type TextSegment = Pick<
|
||||
StyledTextSegment,
|
||||
| 'characters'
|
||||
| 'start'
|
||||
|
@ -18,16 +18,17 @@ export type StyleTextSegment = Pick<
|
|||
| 'listOptions'
|
||||
| 'fills'
|
||||
| 'fillStyleId'
|
||||
| 'textStyleId'
|
||||
>;
|
||||
|
||||
type PartialTranslation = {
|
||||
textNodes: PenpotTextNode[];
|
||||
segment: StyleTextSegment;
|
||||
segment: TextSegment;
|
||||
};
|
||||
|
||||
export const translateParagraphProperties = (
|
||||
node: TextNode,
|
||||
partials: { textNode: PenpotTextNode; segment: StyleTextSegment }[]
|
||||
partials: { textNode: PenpotTextNode; segment: TextSegment }[]
|
||||
): PenpotTextNode[] => {
|
||||
const splitSegments: PartialTranslation[] = [];
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
export * from './translateFontStyle';
|
||||
export * from './translateFontWeight';
|
||||
export * from './translateGrowType';
|
||||
export * from './translateHorizontalAlign';
|
||||
export * from './translateLetterSpacing';
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
export const translateFontWeight = (fontName: FontName): string => {
|
||||
switch (fontName.style) {
|
||||
case 'Thin':
|
||||
case 'Thin Italic':
|
||||
return '100';
|
||||
case 'Extra Light':
|
||||
case 'ExtraLight':
|
||||
case 'Extra Light Italic':
|
||||
case 'ExtraLight Italic':
|
||||
return '200';
|
||||
case 'Light':
|
||||
case 'Light Italic':
|
||||
return '300';
|
||||
case 'Regular':
|
||||
case 'Italic':
|
||||
return '400';
|
||||
case 'Medium':
|
||||
case 'Medium Italic':
|
||||
return '500';
|
||||
case 'Semi Bold':
|
||||
case 'SemiBold':
|
||||
case 'Semi Bold Italic':
|
||||
case 'SemiBold Italic':
|
||||
return '600';
|
||||
case 'Bold':
|
||||
case 'Bold Italic':
|
||||
return '700';
|
||||
case 'ExtraBold':
|
||||
case 'Extra Bold':
|
||||
case 'ExtraBold Italic':
|
||||
case 'Extra Bold Italic':
|
||||
return '800';
|
||||
case 'Black':
|
||||
case 'Black Italic':
|
||||
return '900';
|
||||
default:
|
||||
return '400';
|
||||
}
|
||||
};
|
|
@ -1,12 +1,12 @@
|
|||
export const translateLetterSpacing = (
|
||||
segment: Pick<StyledTextSegment, 'letterSpacing' | 'fontSize'>
|
||||
): number => {
|
||||
): string => {
|
||||
switch (segment.letterSpacing.unit) {
|
||||
case 'PIXELS':
|
||||
return segment.letterSpacing.value;
|
||||
return segment.letterSpacing.value.toString();
|
||||
case 'PERCENT':
|
||||
return (segment.fontSize * segment.letterSpacing.value) / 100;
|
||||
return ((segment.fontSize * segment.letterSpacing.value) / 100).toString();
|
||||
default:
|
||||
return 0;
|
||||
return '0';
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
export const translateLineHeight = (
|
||||
segment: Pick<StyledTextSegment, 'lineHeight' | 'fontSize'>
|
||||
): number | undefined => {
|
||||
): string => {
|
||||
switch (segment.lineHeight.unit) {
|
||||
case 'PIXELS':
|
||||
return segment.lineHeight.value / segment.fontSize;
|
||||
return (segment.lineHeight.value / segment.fontSize).toString();
|
||||
case 'PERCENT':
|
||||
return segment.lineHeight.value / 100;
|
||||
return (segment.lineHeight.value / 100).toString();
|
||||
default:
|
||||
return '1.2';
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
import { transformFills } from '@plugin/transformers/partials';
|
||||
import { translateFontId } from '@plugin/translators/text/font';
|
||||
import { StyleTextSegment, translateParagraphProperties } from '@plugin/translators/text/paragraph';
|
||||
import {
|
||||
translateFontStyle,
|
||||
translateHorizontalAlign,
|
||||
translateLetterSpacing,
|
||||
translateLineHeight,
|
||||
translateTextDecoration,
|
||||
translateTextTransform
|
||||
} from '@plugin/translators/text/properties';
|
||||
|
||||
import { TextNode as PenpotTextNode, TextStyle } from '@ui/lib/types/shapes/textShape';
|
||||
|
||||
export const translateStyleTextSegments = (
|
||||
node: TextNode,
|
||||
segments: StyleTextSegment[]
|
||||
): PenpotTextNode[] => {
|
||||
const partials = segments.map(segment => ({
|
||||
textNode: translateStyleTextSegment(node, segment),
|
||||
segment
|
||||
}));
|
||||
|
||||
return translateParagraphProperties(node, partials);
|
||||
};
|
||||
|
||||
export const transformTextStyle = (node: TextNode, segment: StyleTextSegment): TextStyle => {
|
||||
return {
|
||||
...translateFontId(segment.fontName, segment.fontWeight),
|
||||
fontFamily: segment.fontName.family,
|
||||
fontSize: segment.fontSize.toString(),
|
||||
fontStyle: translateFontStyle(segment.fontName.style),
|
||||
fontWeight: segment.fontWeight.toString(),
|
||||
textAlign: translateHorizontalAlign(node.textAlignHorizontal),
|
||||
textDecoration: translateTextDecoration(segment),
|
||||
textTransform: translateTextTransform(segment),
|
||||
letterSpacing: translateLetterSpacing(segment),
|
||||
lineHeight: translateLineHeight(segment)
|
||||
};
|
||||
};
|
||||
|
||||
const translateStyleTextSegment = (node: TextNode, segment: StyleTextSegment): PenpotTextNode => {
|
||||
return {
|
||||
text: segment.characters,
|
||||
...transformTextStyle(node, segment),
|
||||
...transformFills(segment)
|
||||
};
|
||||
};
|
75
plugin-src/translators/text/translateTextSegments.ts
Normal file
75
plugin-src/translators/text/translateTextSegments.ts
Normal file
|
@ -0,0 +1,75 @@
|
|||
import { textLibrary } from '@plugin/TextLibrary';
|
||||
import { transformFills } from '@plugin/transformers/partials';
|
||||
import { translateFontName } from '@plugin/translators/text/font';
|
||||
import { TextSegment, translateParagraphProperties } from '@plugin/translators/text/paragraph';
|
||||
import {
|
||||
translateFontStyle,
|
||||
translateHorizontalAlign,
|
||||
translateLetterSpacing,
|
||||
translateLineHeight,
|
||||
translateTextDecoration,
|
||||
translateTextTransform
|
||||
} from '@plugin/translators/text/properties';
|
||||
|
||||
import { TextNode as PenpotTextNode, TextStyle } from '@ui/lib/types/shapes/textShape';
|
||||
|
||||
export const translateTextSegments = (
|
||||
node: TextNode,
|
||||
segments: TextSegment[]
|
||||
): PenpotTextNode[] => {
|
||||
const partials = segments.map(segment => ({
|
||||
textNode: translateStyleTextSegment(node, segment),
|
||||
segment
|
||||
}));
|
||||
|
||||
return translateParagraphProperties(node, partials);
|
||||
};
|
||||
|
||||
export const transformTextStyle = (node: TextNode, segment: TextSegment): TextStyle => {
|
||||
if (hasTextStyle(segment)) {
|
||||
return {
|
||||
...partialTransformTextStyle(node, segment),
|
||||
textStyleId: translateTextStyleId(segment.textStyleId)
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...partialTransformTextStyle(node, segment),
|
||||
fontFamily: segment.fontName.family,
|
||||
fontSize: segment.fontSize.toString(),
|
||||
fontStyle: translateFontStyle(segment.fontName.style),
|
||||
textDecoration: translateTextDecoration(segment),
|
||||
letterSpacing: translateLetterSpacing(segment),
|
||||
lineHeight: translateLineHeight(segment),
|
||||
textTransform: translateTextTransform(segment)
|
||||
};
|
||||
};
|
||||
|
||||
const partialTransformTextStyle = (node: TextNode, segment: TextSegment): TextStyle => {
|
||||
return {
|
||||
...translateFontName(segment.fontName),
|
||||
textAlign: translateHorizontalAlign(node.textAlignHorizontal)
|
||||
};
|
||||
};
|
||||
|
||||
const translateStyleTextSegment = (node: TextNode, segment: TextSegment): PenpotTextNode => {
|
||||
return {
|
||||
text: segment.characters,
|
||||
...transformTextStyle(node, segment),
|
||||
...transformFills(segment)
|
||||
};
|
||||
};
|
||||
|
||||
const hasTextStyle = (segment: TextSegment): boolean => {
|
||||
return segment.textStyleId !== undefined && segment.textStyleId.length > 0;
|
||||
};
|
||||
|
||||
const translateTextStyleId = (textStyleId: string | undefined): string | undefined => {
|
||||
if (textStyleId === undefined) return;
|
||||
|
||||
if (!textLibrary.has(textStyleId)) {
|
||||
textLibrary.register(textStyleId);
|
||||
}
|
||||
|
||||
return textStyleId;
|
||||
};
|
|
@ -45,6 +45,15 @@ const stepMessages: Record<Steps, Messages> = {
|
|||
exporting: {
|
||||
total: 'Generating Penpot file 🚀',
|
||||
current: 'Please wait, this process might take a while...'
|
||||
},
|
||||
typographies: {
|
||||
total: 'text styles fetched 📝'
|
||||
},
|
||||
typoFormat: {
|
||||
total: 'formatting text styles 📝'
|
||||
},
|
||||
typoLibraries: {
|
||||
total: 'text styles built 📝'
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -73,6 +82,9 @@ const StepProgress = (): JSX.Element | null => {
|
|||
case 'components':
|
||||
case 'format':
|
||||
case 'libraries':
|
||||
case 'typographies':
|
||||
case 'typoFormat':
|
||||
case 'typoLibraries':
|
||||
return (
|
||||
<>
|
||||
{processedItems} of {totalItems} {stepMessages[step].total}
|
||||
|
|
|
@ -29,7 +29,10 @@ export type Steps =
|
|||
| 'exporting'
|
||||
| 'fills'
|
||||
| 'format'
|
||||
| 'libraries';
|
||||
| 'libraries'
|
||||
| 'typographies'
|
||||
| 'typoFormat'
|
||||
| 'typoLibraries';
|
||||
|
||||
export const useFigma = (): UseFigmaHook => {
|
||||
const [missingFonts, setMissingFonts] = useState<string[]>();
|
||||
|
|
|
@ -8,6 +8,7 @@ import { PathShape } from '@ui/lib/types/shapes/pathShape';
|
|||
import { RectShape } from '@ui/lib/types/shapes/rectShape';
|
||||
import { TextShape } from '@ui/lib/types/shapes/textShape';
|
||||
import { Color } from '@ui/lib/types/utils/color';
|
||||
import { Typography } from '@ui/lib/types/utils/typography';
|
||||
import { Uuid } from '@ui/lib/types/utils/uuid';
|
||||
|
||||
export interface PenpotFile {
|
||||
|
@ -24,18 +25,11 @@ export interface PenpotFile {
|
|||
createPath(path: PathShape): Uuid;
|
||||
createText(options: TextShape): Uuid;
|
||||
addLibraryColor(color: Color): void;
|
||||
updateLibraryColor(color: Color): void;
|
||||
deleteLibraryColor(color: Color): void;
|
||||
// addLibraryTypography(typography: any): void;
|
||||
// deleteLibraryTypography(typography: any): void;
|
||||
addLibraryTypography(typography: Typography): void;
|
||||
startComponent(component: ComponentShape): Uuid;
|
||||
finishComponent(): void;
|
||||
// lookupShape(shapeId: string): PenpotNode;
|
||||
// updateObject(id: string, object: any): void;
|
||||
// deleteObject(id: string): void;
|
||||
getId(): Uuid;
|
||||
getCurrentPageId(): Uuid;
|
||||
newId(): Uuid;
|
||||
// asMap(): unknown;
|
||||
export(): Promise<Blob>;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import {
|
|||
ShapeGeomAttributes
|
||||
} from '@ui/lib/types/shapes/shape';
|
||||
import { Fill } from '@ui/lib/types/utils/fill';
|
||||
import { Typography } from '@ui/lib/types/utils/typography';
|
||||
|
||||
export type TextShape = ShapeBaseAttributes &
|
||||
ShapeGeomAttributes &
|
||||
|
@ -45,25 +46,35 @@ export type TextNode = {
|
|||
key?: string;
|
||||
} & TextStyle;
|
||||
|
||||
export type TextStyle = FontId & {
|
||||
fontFamily?: string;
|
||||
fontSize?: string;
|
||||
fontStyle?: TextFontStyle;
|
||||
fontWeight?: string;
|
||||
export type TextStyle = TextTypography & {
|
||||
textDecoration?: string;
|
||||
textTransform?: string;
|
||||
direction?: string;
|
||||
typographyRefId?: string;
|
||||
typographyRefFile?: string;
|
||||
lineHeight?: number;
|
||||
letterSpacing?: number;
|
||||
textAlign?: TextHorizontalAlign;
|
||||
textDirection?: 'ltr' | 'rtl' | 'auto';
|
||||
fills?: Fill[];
|
||||
fillStyleId?: string; // @TODO: move to any other place
|
||||
textStyleId?: string; // @TODO: move to any other place
|
||||
};
|
||||
|
||||
export type TextTypography = FontId & {
|
||||
fontFamily?: string;
|
||||
fontSize?: string;
|
||||
fontWeight?: string;
|
||||
fontStyle?: TextFontStyle;
|
||||
lineHeight?: string;
|
||||
letterSpacing?: string;
|
||||
textTransform?: string;
|
||||
};
|
||||
|
||||
export type FontId = {
|
||||
fontId?: string;
|
||||
fontVariantId?: string;
|
||||
};
|
||||
|
||||
export type TypographyStyle = {
|
||||
name: string;
|
||||
textStyle: TextStyle;
|
||||
typography: Typography;
|
||||
};
|
||||
|
|
8
ui-src/lib/types/utils/typography.ts
Normal file
8
ui-src/lib/types/utils/typography.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import { TextTypography } from '@ui/lib/types/shapes/textShape';
|
||||
import { Uuid } from '@ui/lib/types/utils/uuid';
|
||||
|
||||
export type Typography = TextTypography & {
|
||||
id?: Uuid;
|
||||
name?: string;
|
||||
path?: string;
|
||||
};
|
|
@ -4,7 +4,12 @@ import { sendMessage } from '@ui/context';
|
|||
import { PenpotFile } from '@ui/lib/types/penpotFile';
|
||||
import { PenpotPage } from '@ui/lib/types/penpotPage';
|
||||
import { idLibrary } from '@ui/parser';
|
||||
import { createColorsLibrary, createComponentsLibrary, createPage } from '@ui/parser/creators';
|
||||
import {
|
||||
createColorsLibrary,
|
||||
createComponentsLibrary,
|
||||
createPage,
|
||||
createTextLibrary
|
||||
} from '@ui/parser/creators';
|
||||
import { uiComponents } from '@ui/parser/libraries';
|
||||
|
||||
export const buildFile = async (file: PenpotFile, children: PenpotPage[]) => {
|
||||
|
@ -35,6 +40,7 @@ export const buildFile = async (file: PenpotFile, children: PenpotPage[]) => {
|
|||
}
|
||||
|
||||
await createColorsLibrary(file);
|
||||
await createTextLibrary(file);
|
||||
|
||||
await createComponentsLibrary(file);
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { PenpotFile } from '@ui/lib/types/penpotFile';
|
||||
import { TextContent, TextShape } from '@ui/lib/types/shapes/textShape';
|
||||
import { Paragraph, TextContent, TextNode, TextShape } from '@ui/lib/types/shapes/textShape';
|
||||
import { parseFigmaId } from '@ui/parser';
|
||||
import { symbolFills, symbolStrokes } from '@ui/parser/creators/symbols';
|
||||
import { uiTextLibraries } from '@ui/parser/libraries/UiTextLibraries';
|
||||
|
||||
export const createText = (
|
||||
file: PenpotFile,
|
||||
|
@ -18,15 +19,31 @@ export const createText = (
|
|||
const parseContent = (content: TextContent | undefined): TextContent | undefined => {
|
||||
if (!content) return;
|
||||
|
||||
content.children?.forEach(paragraphSet => {
|
||||
paragraphSet.children.forEach(paragraph => {
|
||||
paragraph.children.forEach(textNode => {
|
||||
textNode.fills = symbolFills(textNode.fillStyleId, textNode.fills);
|
||||
content.children = content.children?.map(paragraphSet => {
|
||||
paragraphSet.children = paragraphSet.children.map(paragraph => {
|
||||
paragraph.children = paragraph.children.map(textNode => {
|
||||
return parseTextStyle(textNode, textNode.textStyleId) as TextNode;
|
||||
});
|
||||
|
||||
paragraph.fills = symbolFills(paragraph.fillStyleId, paragraph.fills);
|
||||
return parseTextStyle(paragraph, paragraph.textStyleId) as Paragraph;
|
||||
});
|
||||
return paragraphSet;
|
||||
});
|
||||
|
||||
return content;
|
||||
};
|
||||
|
||||
const parseTextStyle = (text: Paragraph | TextNode, textStyleId?: string): Paragraph | TextNode => {
|
||||
let textStyle = text;
|
||||
textStyle.fills = symbolFills(text.fillStyleId, text.fills);
|
||||
|
||||
const libraryStyle = textStyleId ? uiTextLibraries.get(textStyleId) : undefined;
|
||||
|
||||
if (libraryStyle) {
|
||||
textStyle = {
|
||||
...libraryStyle.textStyle,
|
||||
...textStyle
|
||||
};
|
||||
}
|
||||
|
||||
return textStyle;
|
||||
};
|
||||
|
|
42
ui-src/parser/creators/createTextLibrary.ts
Normal file
42
ui-src/parser/creators/createTextLibrary.ts
Normal file
|
@ -0,0 +1,42 @@
|
|||
import { sleep } from '@plugin/utils/sleep';
|
||||
|
||||
import { sendMessage } from '@ui/context';
|
||||
import { PenpotFile } from '@ui/lib/types/penpotFile';
|
||||
import { uiTextLibraries } from '@ui/parser/libraries/UiTextLibraries';
|
||||
|
||||
export const createTextLibrary = async (file: PenpotFile) => {
|
||||
let librariesBuilt = 1;
|
||||
const libraries = uiTextLibraries.all();
|
||||
|
||||
sendMessage({
|
||||
type: 'PROGRESS_TOTAL_ITEMS',
|
||||
data: libraries.length
|
||||
});
|
||||
|
||||
sendMessage({
|
||||
type: 'PROGRESS_STEP',
|
||||
data: 'typoLibraries'
|
||||
});
|
||||
|
||||
for (const library of libraries) {
|
||||
file.addLibraryTypography({
|
||||
...library.typography,
|
||||
fontId: library.textStyle.fontId,
|
||||
fontVariantId: library.textStyle.fontVariantId,
|
||||
letterSpacing: library.textStyle.letterSpacing,
|
||||
fontWeight: library.textStyle.fontWeight,
|
||||
fontStyle: library.textStyle.fontStyle,
|
||||
fontFamily: library.textStyle.fontFamily,
|
||||
fontSize: library.textStyle.fontSize,
|
||||
textTransform: library.textStyle.textTransform,
|
||||
lineHeight: library.textStyle.lineHeight
|
||||
});
|
||||
|
||||
sendMessage({
|
||||
type: 'PROGRESS_PROCESSED_ITEMS',
|
||||
data: librariesBuilt++
|
||||
});
|
||||
|
||||
await sleep(0);
|
||||
}
|
||||
};
|
|
@ -12,3 +12,4 @@ export * from './createPage';
|
|||
export * from './createPath';
|
||||
export * from './createRectangle';
|
||||
export * from './createText';
|
||||
export * from './createTextLibrary';
|
||||
|
|
19
ui-src/parser/libraries/UiTextLibraries.ts
Normal file
19
ui-src/parser/libraries/UiTextLibraries.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { TypographyStyle } from '@ui/lib/types/shapes/textShape';
|
||||
|
||||
class UiTextLibraries {
|
||||
private libraries: Map<string, TypographyStyle> = new Map();
|
||||
|
||||
public register(id: string, textStyle: TypographyStyle) {
|
||||
this.libraries.set(id, textStyle);
|
||||
}
|
||||
|
||||
public get(id: string): TypographyStyle | undefined {
|
||||
return this.libraries.get(id);
|
||||
}
|
||||
|
||||
public all(): TypographyStyle[] {
|
||||
return Array.from(this.libraries.values());
|
||||
}
|
||||
}
|
||||
|
||||
export const uiTextLibraries = new UiTextLibraries();
|
|
@ -5,10 +5,11 @@ import { sleep } from '@plugin/utils/sleep';
|
|||
import { sendMessage } from '@ui/context';
|
||||
import { createFile } from '@ui/lib/penpot';
|
||||
import { PenpotFile } from '@ui/lib/types/penpotFile';
|
||||
import { TypographyStyle } from '@ui/lib/types/shapes/textShape';
|
||||
import { FillStyle } from '@ui/lib/types/utils/fill';
|
||||
import { buildFile } from '@ui/parser/creators';
|
||||
import { uiImages } from '@ui/parser/libraries';
|
||||
import { uiColorLibraries } from '@ui/parser/libraries';
|
||||
import { uiColorLibraries, uiImages } from '@ui/parser/libraries';
|
||||
import { uiTextLibraries } from '@ui/parser/libraries/UiTextLibraries';
|
||||
import { PenpotDocument } from '@ui/types';
|
||||
|
||||
import { parseImage } from '.';
|
||||
|
@ -44,6 +45,43 @@ const optimizeImages = async (images: Record<string, Uint8Array>) => {
|
|||
}
|
||||
};
|
||||
|
||||
const prepareTypographyLibraries = async (
|
||||
file: PenpotFile,
|
||||
styles: Record<string, TypographyStyle>
|
||||
) => {
|
||||
const stylesToRegister = Object.entries(styles);
|
||||
|
||||
if (stylesToRegister.length === 0) return;
|
||||
|
||||
let stylesRegistered = 1;
|
||||
|
||||
sendMessage({
|
||||
type: 'PROGRESS_TOTAL_ITEMS',
|
||||
data: stylesToRegister.length
|
||||
});
|
||||
|
||||
sendMessage({
|
||||
type: 'PROGRESS_STEP',
|
||||
data: 'typoFormat'
|
||||
});
|
||||
|
||||
for (const [key, style] of stylesToRegister) {
|
||||
const typographyId = file.newId();
|
||||
style.textStyle.typographyRefId = typographyId;
|
||||
style.textStyle.typographyRefFile = file.getId();
|
||||
style.typography.id = typographyId;
|
||||
|
||||
uiTextLibraries.register(key, style);
|
||||
|
||||
sendMessage({
|
||||
type: 'PROGRESS_PROCESSED_ITEMS',
|
||||
data: stylesRegistered++
|
||||
});
|
||||
|
||||
await sleep(0);
|
||||
}
|
||||
};
|
||||
|
||||
const prepareColorLibraries = async (file: PenpotFile, styles: Record<string, FillStyle>) => {
|
||||
const stylesToRegister = Object.entries(styles);
|
||||
|
||||
|
@ -86,7 +124,8 @@ export const parse = async ({
|
|||
children = [],
|
||||
components,
|
||||
images,
|
||||
styles
|
||||
styles,
|
||||
typographies
|
||||
}: PenpotDocument) => {
|
||||
componentsLibrary.init(components);
|
||||
|
||||
|
@ -94,6 +133,7 @@ export const parse = async ({
|
|||
|
||||
await optimizeImages(images);
|
||||
await prepareColorLibraries(file, styles);
|
||||
await prepareTypographyLibraries(file, typographies);
|
||||
|
||||
return buildFile(file, children);
|
||||
};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { PenpotPage } from '@ui/lib/types/penpotPage';
|
||||
import { ComponentShape } from '@ui/lib/types/shapes/componentShape';
|
||||
import { TypographyStyle } from '@ui/lib/types/shapes/textShape';
|
||||
import { FillStyle } from '@ui/lib/types/utils/fill';
|
||||
|
||||
export type PenpotDocument = {
|
||||
|
@ -8,4 +9,5 @@ export type PenpotDocument = {
|
|||
components: Record<string, ComponentShape>;
|
||||
images: Record<string, Uint8Array>;
|
||||
styles: Record<string, FillStyle>;
|
||||
typographies: Record<string, TypographyStyle>;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue