mirror of
https://github.com/penpot/penpot-exporter-figma-plugin.git
synced 2024-12-22 05:33:02 -05:00
Solid Fills (#39)
* structure for fills * structure for fills in text and improvements * fixes * improvements
This commit is contained in:
parent
ebdf3ad9c3
commit
e2b5b4a7ea
15 changed files with 145 additions and 111 deletions
|
@ -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';
|
||||
|
|
11
plugin-src/transformers/partials/transformFills.ts
Normal file
11
plugin-src/transformers/partials/transformFills.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import { translateFills } from '@plugin/translators';
|
||||
|
||||
import { ShapeAttributes } from '@ui/lib/types/shape/shapeAttributes';
|
||||
|
||||
export const transformFills = (
|
||||
node: MinimalFillsMixin & DimensionAndPositionMixin
|
||||
): Partial<ShapeAttributes> => {
|
||||
return {
|
||||
fills: translateFills(node.fills, node.width, node.height)
|
||||
};
|
||||
};
|
29
plugin-src/transformers/partials/transformTextStyle.ts
Normal file
29
plugin-src/transformers/partials/transformTextStyle.ts
Normal file
|
@ -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<TextStyle> => {
|
||||
return {
|
||||
fontFamily: node.fontName.family,
|
||||
fontSize: node.fontSize.toString(),
|
||||
fontStyle: node.fontName.style,
|
||||
fontWeight: node.fontWeight.toString(),
|
||||
textDecoration: translateTextDecoration(node),
|
||||
textTransform: translateTextTransform(node)
|
||||
};
|
||||
};
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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 figmaFills = fills === figma.mixed ? [] : fills;
|
||||
const penpotFills: Fill[] = [];
|
||||
|
||||
const penpotFills = [];
|
||||
|
||||
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
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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
|
||||
};
|
||||
};
|
|
@ -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
|
||||
};
|
||||
};
|
33
plugin-src/translators/translateStyledTextSegments.ts
Normal file
33
plugin-src/translators/translateStyledTextSegments.ts
Normal file
|
@ -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)
|
||||
};
|
||||
});
|
||||
};
|
|
@ -11,5 +11,5 @@ export type FrameAttributes = {
|
|||
hideFillOnExport?: boolean;
|
||||
showContent?: boolean;
|
||||
hideInViewer?: boolean;
|
||||
fills: Fill[];
|
||||
fills?: Fill[];
|
||||
};
|
||||
|
|
19
ui-src/lib/types/text/textContent.d.ts
vendored
19
ui-src/lib/types/text/textContent.d.ts
vendored
|
@ -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[];
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue