From e2b5b4a7ea87e32e4f1ce5335a9c6fb054067db3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20S=C3=A1nchez?= Date: Wed, 17 Apr 2024 15:27:53 +0200 Subject: [PATCH] Solid Fills (#39) * structure for fills * structure for fills in text and improvements * fixes * improvements --- plugin-src/transformers/partials/index.ts | 2 + .../transformers/partials/transformFills.ts | 11 ++++ .../partials/transformTextStyle.ts | 29 +++++++++++ .../transformers/transformEllipseNode.ts | 4 +- plugin-src/transformers/transformFrameNode.ts | 6 +-- plugin-src/transformers/transformPathNode.ts | 8 ++- .../transformers/transformRectangleNode.ts | 4 +- plugin-src/transformers/transformTextNode.ts | 37 +++---------- plugin-src/translators/index.ts | 3 +- plugin-src/translators/translateFills.ts | 52 +++++++++++++++---- .../translateGradientLinearFill.ts | 36 ------------- plugin-src/translators/translateSolidFill.ts | 10 ---- .../translateStyledTextSegments.ts | 33 ++++++++++++ ui-src/lib/types/frame/frameAttributes.ts | 2 +- ui-src/lib/types/text/textContent.d.ts | 19 +++---- 15 files changed, 145 insertions(+), 111 deletions(-) create mode 100644 plugin-src/transformers/partials/transformFills.ts create mode 100644 plugin-src/transformers/partials/transformTextStyle.ts delete mode 100644 plugin-src/translators/translateGradientLinearFill.ts delete mode 100644 plugin-src/translators/translateSolidFill.ts create mode 100644 plugin-src/translators/translateStyledTextSegments.ts diff --git a/plugin-src/transformers/partials/index.ts b/plugin-src/transformers/partials/index.ts index 3140f8ef..0f58db1d 100644 --- a/plugin-src/transformers/partials/index.ts +++ b/plugin-src/transformers/partials/index.ts @@ -1,6 +1,8 @@ export * from './transformBlend'; export * from './transformChildren'; export * from './transformDimensionAndPosition'; +export * from './transformFills'; export * from './transformSceneNode'; export * from './transformStrokes'; +export * from './transformTextStyle'; export * from './transformVectorPaths'; diff --git a/plugin-src/transformers/partials/transformFills.ts b/plugin-src/transformers/partials/transformFills.ts new file mode 100644 index 00000000..982be650 --- /dev/null +++ b/plugin-src/transformers/partials/transformFills.ts @@ -0,0 +1,11 @@ +import { translateFills } from '@plugin/translators'; + +import { ShapeAttributes } from '@ui/lib/types/shape/shapeAttributes'; + +export const transformFills = ( + node: MinimalFillsMixin & DimensionAndPositionMixin +): Partial => { + return { + fills: translateFills(node.fills, node.width, node.height) + }; +}; diff --git a/plugin-src/transformers/partials/transformTextStyle.ts b/plugin-src/transformers/partials/transformTextStyle.ts new file mode 100644 index 00000000..1a28052c --- /dev/null +++ b/plugin-src/transformers/partials/transformTextStyle.ts @@ -0,0 +1,29 @@ +import { translateTextDecoration, translateTextTransform } from '@plugin/translators'; + +import { TextStyle } from '@ui/lib/types/text/textContent'; + +export const transformTextStyle = ( + node: Pick< + StyledTextSegment, + | 'characters' + | 'start' + | 'end' + | 'fontName' + | 'fontSize' + | 'fontWeight' + | 'lineHeight' + | 'letterSpacing' + | 'textCase' + | 'textDecoration' + | 'fills' + > +): Partial => { + return { + fontFamily: node.fontName.family, + fontSize: node.fontSize.toString(), + fontStyle: node.fontName.style, + fontWeight: node.fontWeight.toString(), + textDecoration: translateTextDecoration(node), + textTransform: translateTextTransform(node) + }; +}; diff --git a/plugin-src/transformers/transformEllipseNode.ts b/plugin-src/transformers/transformEllipseNode.ts index 64ca2ff9..5fa72b8e 100644 --- a/plugin-src/transformers/transformEllipseNode.ts +++ b/plugin-src/transformers/transformEllipseNode.ts @@ -1,10 +1,10 @@ import { transformBlend, transformDimensionAndPosition, + transformFills, transformSceneNode, transformStrokes } from '@plugin/transformers/partials'; -import { translateFills } from '@plugin/translators'; import { CircleShape } from '@ui/lib/types/circle/circleShape'; @@ -16,7 +16,7 @@ export const transformEllipseNode = ( return { type: 'circle', name: node.name, - fills: translateFills(node.fills, node.width, node.height), + ...transformFills(node), ...transformStrokes(node), ...transformDimensionAndPosition(node, baseX, baseY), ...transformSceneNode(node), diff --git a/plugin-src/transformers/transformFrameNode.ts b/plugin-src/transformers/transformFrameNode.ts index 87538579..d2b3c31e 100644 --- a/plugin-src/transformers/transformFrameNode.ts +++ b/plugin-src/transformers/transformFrameNode.ts @@ -1,11 +1,11 @@ import { transformBlend, + transformChildren, transformDimensionAndPosition, + transformFills, transformSceneNode, transformStrokes } from '@plugin/transformers/partials'; -import { transformChildren } from '@plugin/transformers/partials'; -import { translateFills } from '@plugin/translators'; import { FrameShape } from '@ui/lib/types/frame/frameShape'; @@ -21,7 +21,7 @@ export const transformFrameNode = async ( return { type: 'frame', name: node.name, - fills: translateFills(node.fills, node.width, node.height), + ...transformFills(node), // Figma API does not expose strokes for sections, // they plan to add it in the future. Refactor this when available. // @see: https://forum.figma.com/t/why-are-strokes-not-available-on-section-nodes/41658 diff --git a/plugin-src/transformers/transformPathNode.ts b/plugin-src/transformers/transformPathNode.ts index 0e0dda08..41aeacf6 100644 --- a/plugin-src/transformers/transformPathNode.ts +++ b/plugin-src/transformers/transformPathNode.ts @@ -1,14 +1,18 @@ import { transformBlend, transformDimensionAndPosition, + transformFills, transformSceneNode, transformStrokes, transformVectorPaths } from '@plugin/transformers/partials'; -import { translateFills } from '@plugin/translators'; import { PathShape } from '@ui/lib/types/path/pathShape'; +const hasFillGeometry = (node: VectorNode | StarNode | LineNode | PolygonNode): boolean => { + return 'fillGeometry' in node && node.fillGeometry.length > 0; +}; + export const transformPathNode = ( node: VectorNode | StarNode | LineNode | PolygonNode, baseX: number, @@ -16,7 +20,7 @@ export const transformPathNode = ( ): PathShape => { return { name: node.name, - fills: node.fillGeometry.length ? translateFills(node.fills, node.width, node.height) : [], + ...(hasFillGeometry(node) ? transformFills(node) : []), ...transformStrokes(node), ...transformVectorPaths(node, baseX, baseY), ...transformDimensionAndPosition(node, baseX, baseY), diff --git a/plugin-src/transformers/transformRectangleNode.ts b/plugin-src/transformers/transformRectangleNode.ts index cc042bf2..1616f1a7 100644 --- a/plugin-src/transformers/transformRectangleNode.ts +++ b/plugin-src/transformers/transformRectangleNode.ts @@ -1,10 +1,10 @@ import { transformBlend, transformDimensionAndPosition, + transformFills, transformSceneNode, transformStrokes } from '@plugin/transformers/partials'; -import { translateFills } from '@plugin/translators'; import { RectShape } from '@ui/lib/types/rect/rectShape'; @@ -16,7 +16,7 @@ export const transformRectangleNode = ( return { type: 'rect', name: node.name, - fills: translateFills(node.fills, node.width, node.height), + ...transformFills(node), ...transformStrokes(node), ...transformDimensionAndPosition(node, baseX, baseY), ...transformSceneNode(node), diff --git a/plugin-src/transformers/transformTextNode.ts b/plugin-src/transformers/transformTextNode.ts index c4f83cd7..1bad3ddd 100644 --- a/plugin-src/transformers/transformTextNode.ts +++ b/plugin-src/transformers/transformTextNode.ts @@ -1,15 +1,12 @@ import { transformBlend, transformDimensionAndPosition, - transformSceneNode + transformFills, + transformSceneNode, + transformTextStyle } from '@plugin/transformers/partials'; -import { - translateFills, - translateTextDecoration, - translateTextTransform -} from '@plugin/translators'; +import { translateStyledTextSegments } from '@plugin/translators'; -import { TextNode as PenpotTextNode } from '@ui/lib/types/text/textContent'; import { TextShape } from '@ui/lib/types/text/textShape'; export const transformTextNode = (node: TextNode, baseX: number, baseY: number): TextShape => { @@ -24,21 +21,6 @@ export const transformTextNode = (node: TextNode, baseX: number, baseY: number): 'fills' ]); - const children: PenpotTextNode[] = styledTextSegments.map(segment => { - figma.ui.postMessage({ type: 'FONT_NAME', data: segment.fontName.family }); - - return { - text: segment.characters, - fills: translateFills(segment.fills, node.width, node.height), - fontFamily: segment.fontName.family, - fontSize: segment.fontSize.toString(), - fontStyle: segment.fontName.style, - fontWeight: segment.fontWeight.toString(), - textDecoration: translateTextDecoration(segment), - textTransform: translateTextTransform(segment) - }; - }); - return { type: 'text', name: node.name, @@ -50,14 +32,9 @@ export const transformTextNode = (node: TextNode, baseX: number, baseY: number): children: [ { type: 'paragraph', - fills: translateFills(node.fills, node.width, node.height), - fontFamily: children[0].fontFamily, - fontSize: children[0].fontSize, - fontStyle: children[0].fontStyle, - fontWeight: children[0].fontWeight, - textDecoration: children[0].textDecoration, - textTransform: children[0].textTransform, - children: children + children: translateStyledTextSegments(styledTextSegments, node.width, node.height), + ...(styledTextSegments.length ? transformTextStyle(styledTextSegments[0]) : {}), + ...transformFills(node) } ] } diff --git a/plugin-src/translators/index.ts b/plugin-src/translators/index.ts index 10048294..953fd238 100644 --- a/plugin-src/translators/index.ts +++ b/plugin-src/translators/index.ts @@ -1,8 +1,7 @@ export * from './translateBlendMode'; export * from './translateFills'; -export * from './translateGradientLinearFill'; -export * from './translateSolidFill'; export * from './translateStrokes'; +export * from './translateStyledTextSegments'; export * from './translateTextDecoration'; export * from './translateTextTransform'; export * from './translateVectorPaths'; diff --git a/plugin-src/translators/translateFills.ts b/plugin-src/translators/translateFills.ts index 1cd97272..e582ef96 100644 --- a/plugin-src/translators/translateFills.ts +++ b/plugin-src/translators/translateFills.ts @@ -1,7 +1,7 @@ -import { Fill } from '@ui/lib/types/utils/fill'; +import { rgbToHex } from '@plugin/utils'; +import { calculateLinearGradient } from '@plugin/utils/calculateLinearGradient'; -import { translateGradientLinearFill } from './translateGradientLinearFill'; -import { translateSolidFill } from './translateSolidFill'; +import { Fill } from '@ui/lib/types/utils/fill'; export const translateFill = (fill: Paint, width: number, height: number): Fill | undefined => { switch (fill.type) { @@ -19,19 +19,51 @@ export const translateFills = ( width: number, height: number ): Fill[] => { - // @TODO: think better variable name - // @TODO: make it work with figma.mixed - const fills2 = fills === figma.mixed ? [] : fills; - - const penpotFills = []; + const figmaFills = fills === figma.mixed ? [] : fills; + const penpotFills: Fill[] = []; - for (const fill of fills2) { + for (const fill of figmaFills) { const penpotFill = translateFill(fill, width, height); - if (penpotFill) { + // colors are applied in reverse order in Figma, that's why we unshift penpotFills.unshift(penpotFill); } } return penpotFills; }; + +const translateSolidFill = (fill: SolidPaint): Fill => { + return { + fillColor: rgbToHex(fill.color), + fillOpacity: !fill.visible ? 0 : fill.opacity + }; +}; + +const translateGradientLinearFill = (fill: GradientPaint, width: number, height: number): Fill => { + const points = calculateLinearGradient(width, height, fill.gradientTransform); + + return { + fillColorGradient: { + type: 'linear', + startX: points.start[0] / width, + startY: points.start[1] / height, + endX: points.end[0] / width, + endY: points.end[1] / height, + width: 1, + stops: [ + { + color: rgbToHex(fill.gradientStops[0].color), + offset: fill.gradientStops[0].position, + opacity: fill.gradientStops[0].color.a * (fill.opacity ?? 1) + }, + { + color: rgbToHex(fill.gradientStops[1].color), + offset: fill.gradientStops[1].position, + opacity: fill.gradientStops[1].color.a * (fill.opacity ?? 1) + } + ] + }, + fillOpacity: fill.visible === false ? 0 : undefined + }; +}; diff --git a/plugin-src/translators/translateGradientLinearFill.ts b/plugin-src/translators/translateGradientLinearFill.ts deleted file mode 100644 index 0079c7b4..00000000 --- a/plugin-src/translators/translateGradientLinearFill.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { rgbToHex } from '@plugin/utils'; -import { calculateLinearGradient } from '@plugin/utils/calculateLinearGradient'; - -import { Fill } from '@ui/lib/types/utils/fill'; - -export const translateGradientLinearFill = ( - fill: GradientPaint, - width: number, - height: number -): Fill => { - const points = calculateLinearGradient(width, height, fill.gradientTransform); - - return { - fillColorGradient: { - type: 'linear', - startX: points.start[0] / width, - startY: points.start[1] / height, - endX: points.end[0] / width, - endY: points.end[1] / height, - width: 1, - stops: [ - { - color: rgbToHex(fill.gradientStops[0].color), - offset: fill.gradientStops[0].position, - opacity: fill.gradientStops[0].color.a * (fill.opacity ?? 1) - }, - { - color: rgbToHex(fill.gradientStops[1].color), - offset: fill.gradientStops[1].position, - opacity: fill.gradientStops[1].color.a * (fill.opacity ?? 1) - } - ] - }, - fillOpacity: fill.visible === false ? 0 : undefined - }; -}; diff --git a/plugin-src/translators/translateSolidFill.ts b/plugin-src/translators/translateSolidFill.ts deleted file mode 100644 index aebddab2..00000000 --- a/plugin-src/translators/translateSolidFill.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { rgbToHex } from '@plugin/utils'; - -import { Fill } from '@ui/lib/types/utils/fill'; - -export const translateSolidFill = (fill: SolidPaint): Fill => { - return { - fillColor: rgbToHex(fill.color), - fillOpacity: fill.visible === false ? 0 : fill.opacity - }; -}; diff --git a/plugin-src/translators/translateStyledTextSegments.ts b/plugin-src/translators/translateStyledTextSegments.ts new file mode 100644 index 00000000..2caf1e62 --- /dev/null +++ b/plugin-src/translators/translateStyledTextSegments.ts @@ -0,0 +1,33 @@ +import { transformTextStyle } from '@plugin/transformers/partials'; +import { translateFills } from '@plugin/translators/translateFills'; + +import { TextNode } from '@ui/lib/types/text/textContent'; + +export const translateStyledTextSegments = ( + segments: Pick< + StyledTextSegment, + | 'characters' + | 'start' + | 'end' + | 'fontName' + | 'fontSize' + | 'fontWeight' + | 'lineHeight' + | 'letterSpacing' + | 'textCase' + | 'textDecoration' + | 'fills' + >[], + width: number, + height: number +): TextNode[] => { + return segments.map(segment => { + figma.ui.postMessage({ type: 'FONT_NAME', data: segment.fontName.family }); + + return { + fills: translateFills(segment.fills, width, height), + text: segment.characters, + ...transformTextStyle(segment) + }; + }); +}; diff --git a/ui-src/lib/types/frame/frameAttributes.ts b/ui-src/lib/types/frame/frameAttributes.ts index d4121e35..86fc3a78 100644 --- a/ui-src/lib/types/frame/frameAttributes.ts +++ b/ui-src/lib/types/frame/frameAttributes.ts @@ -11,5 +11,5 @@ export type FrameAttributes = { hideFillOnExport?: boolean; showContent?: boolean; hideInViewer?: boolean; - fills: Fill[]; + fills?: Fill[]; }; diff --git a/ui-src/lib/types/text/textContent.d.ts b/ui-src/lib/types/text/textContent.d.ts index 4339680d..3519dc38 100644 --- a/ui-src/lib/types/text/textContent.d.ts +++ b/ui-src/lib/types/text/textContent.d.ts @@ -15,30 +15,23 @@ type ParagraphSet = { type Paragraph = { type: 'paragraph'; key?: string; - fills?: Fill[]; - fontFamily?: string; - fontSize?: string; - fontStyle?: string; - fontWeight?: string; - direction?: string; - textDecoration?: string; - textTransform?: string; - typographyRefId?: string; - typographyRefFile?: string; children: TextNode[]; -}; +} & TextStyle; type TextNode = { text: string; key?: string; - fills?: Fill[]; +} & TextStyle; + +type TextStyle = { fontFamily?: string; fontSize?: string; fontStyle?: string; fontWeight?: string; - direction?: string; textDecoration?: string; textTransform?: string; + direction?: string; typographyRefId?: string; typographyRefFile?: string; + fills?: Fill[]; };