@seed-design/figma 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.cjs +556 -471
- package/lib/index.d.ts +29 -4
- package/lib/index.js +556 -471
- package/package.json +2 -2
- package/src/component/handlers/action-button.ts +66 -0
- package/src/component/handlers/action-chip.ts +71 -0
- package/src/component/handlers/action-sheet.ts +74 -0
- package/src/component/handlers/app-bar.ts +183 -0
- package/src/component/handlers/avatar-stack.ts +35 -0
- package/src/component/handlers/avatar.ts +37 -0
- package/src/component/handlers/badge.ts +20 -0
- package/src/component/handlers/callout.ts +87 -0
- package/src/component/handlers/checkbox.ts +32 -0
- package/src/component/handlers/chip-tabs.ts +51 -0
- package/src/component/handlers/control-chip.ts +75 -0
- package/src/component/handlers/error-state.ts +37 -0
- package/src/component/handlers/extended-action-sheet.ts +86 -0
- package/src/component/handlers/extended-fab.ts +24 -0
- package/src/component/handlers/fab.ts +17 -0
- package/src/component/handlers/help-bubble.ts +66 -0
- package/src/component/handlers/identity-placeholder.ts +16 -0
- package/src/component/handlers/inline-banner.ts +83 -0
- package/src/component/handlers/manner-temp-badge.ts +17 -0
- package/src/component/handlers/multiline-text-field.ts +80 -0
- package/src/component/handlers/progress-circle.ts +49 -0
- package/src/component/handlers/reaction-button.ts +36 -0
- package/src/component/handlers/segmented-control.ts +51 -0
- package/src/component/handlers/select-box.ts +76 -0
- package/src/component/handlers/skeleton.ts +51 -0
- package/src/component/handlers/snackbar.ts +21 -0
- package/src/component/handlers/switch.ts +29 -0
- package/src/component/handlers/tabs.ts +107 -0
- package/src/component/handlers/text-button.ts +57 -0
- package/src/component/handlers/text-field.ts +108 -0
- package/src/component/handlers/toggle-button.ts +44 -0
- package/src/component/index.ts +32 -1644
- package/src/component/type-helper.ts +11 -0
- package/src/generate-code.ts +183 -145
- package/src/icon.ts +2 -2
- package/src/index.ts +1 -2
- package/src/jsx.ts +1 -1
- package/src/layout.ts +23 -281
- package/src/normalizer/from-plugin.ts +24 -4
- package/src/normalizer/from-rest.ts +22 -4
- package/src/normalizer/index.ts +3 -0
- package/src/normalizer/types.ts +3 -1
- package/src/{color.ts → props/color.ts} +1 -1
- package/src/props/layout.ts +292 -0
- package/src/{sizing.ts → props/sizing.ts} +17 -17
- package/src/{text.ts → props/text.ts} +2 -1
- package/src/{variable.ts → props/variable.ts} +1 -1
- package/src/{util.ts → utils/common.ts} +0 -2
- package/src/{node-util.ts → utils/figma-node.ts} +1 -1
package/src/layout.ts
CHANGED
|
@@ -1,289 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
} from "./normalizer/types";
|
|
7
|
-
import { getLayoutVariableName, inferDimension, inferRadius } from "./variable";
|
|
8
|
-
|
|
9
|
-
interface FigmaLayoutProps {
|
|
10
|
-
layoutMode?: NormalizedFrameNode["layoutMode"];
|
|
11
|
-
layoutWrap?: NormalizedFrameNode["layoutWrap"];
|
|
12
|
-
paddingLeft?: NormalizedFrameNode["paddingLeft"];
|
|
13
|
-
paddingRight?: NormalizedFrameNode["paddingRight"];
|
|
14
|
-
paddingTop?: NormalizedFrameNode["paddingTop"];
|
|
15
|
-
paddingBottom?: NormalizedFrameNode["paddingBottom"];
|
|
16
|
-
primaryAxisAlignItems?: NormalizedFrameNode["primaryAxisAlignItems"];
|
|
17
|
-
counterAxisAlignItems?: NormalizedFrameNode["counterAxisAlignItems"];
|
|
18
|
-
primaryAxisSizingMode?: NormalizedFrameNode["primaryAxisSizingMode"];
|
|
19
|
-
counterAxisSizingMode?: NormalizedFrameNode["counterAxisSizingMode"];
|
|
20
|
-
layoutGrow?: NormalizedFrameNode["layoutGrow"];
|
|
21
|
-
layoutAlign?: NormalizedFrameNode["layoutAlign"];
|
|
22
|
-
itemSpacing?: NormalizedFrameNode["itemSpacing"];
|
|
23
|
-
counterAxisSpacing?: NormalizedFrameNode["counterAxisSpacing"];
|
|
24
|
-
boundVariables?: NormalizedFrameNode["boundVariables"];
|
|
25
|
-
cornerRadius?: NormalizedFrameNode["cornerRadius"];
|
|
26
|
-
rectangleCornerRadii?: NormalizedFrameNode["rectangleCornerRadii"];
|
|
27
|
-
children: NormalizedSceneNode[];
|
|
1
|
+
export interface LayoutComponentProps {
|
|
2
|
+
flexDirection: string | number | boolean;
|
|
3
|
+
alignItems: string | number | boolean;
|
|
4
|
+
justifyContent: string | number | boolean;
|
|
5
|
+
flexWrap: string | number | boolean;
|
|
28
6
|
}
|
|
29
7
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return "center";
|
|
40
|
-
case "MAX":
|
|
41
|
-
return "flexEnd";
|
|
42
|
-
case "SPACE_BETWEEN":
|
|
43
|
-
return "spaceBetween";
|
|
44
|
-
}
|
|
45
|
-
},
|
|
46
|
-
alignItems: ({ counterAxisAlignItems, children }) => {
|
|
47
|
-
const isStretch = children.every((child) => {
|
|
48
|
-
if (!("layoutAlign" in child)) {
|
|
49
|
-
return false;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return child.layoutAlign === "STRETCH";
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
if (isStretch) {
|
|
56
|
-
return "stretch";
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
switch (counterAxisAlignItems) {
|
|
60
|
-
case "MIN":
|
|
61
|
-
return "flexStart";
|
|
62
|
-
case "CENTER":
|
|
63
|
-
return "center";
|
|
64
|
-
case "MAX":
|
|
65
|
-
return "flexEnd";
|
|
66
|
-
case "BASELINE":
|
|
67
|
-
return "baseline";
|
|
68
|
-
}
|
|
69
|
-
},
|
|
70
|
-
flexWrap: ({ layoutWrap }) => (layoutWrap === "WRAP" ? "wrap" : "nowrap"),
|
|
71
|
-
flexGrow: ({ layoutGrow }) => layoutGrow,
|
|
72
|
-
alignSelf: ({ layoutAlign }) => {
|
|
73
|
-
switch (layoutAlign) {
|
|
74
|
-
case "STRETCH":
|
|
75
|
-
return "stretch";
|
|
76
|
-
case "MIN":
|
|
77
|
-
return "flexStart";
|
|
78
|
-
case "CENTER":
|
|
79
|
-
return "center";
|
|
80
|
-
case "MAX":
|
|
81
|
-
return "flexEnd";
|
|
82
|
-
}
|
|
83
|
-
},
|
|
84
|
-
gap: ({ itemSpacing, boundVariables, primaryAxisAlignItems, children }) =>
|
|
85
|
-
children.length <= 1
|
|
86
|
-
? 0
|
|
87
|
-
: primaryAxisAlignItems === "SPACE_BETWEEN"
|
|
88
|
-
? 0
|
|
89
|
-
: boundVariables?.itemSpacing
|
|
90
|
-
? getLayoutVariableName(boundVariables.itemSpacing.id)
|
|
91
|
-
: inferDimension(itemSpacing ?? 0),
|
|
92
|
-
paddingTop: ({ paddingTop, boundVariables }) =>
|
|
93
|
-
boundVariables?.paddingTop
|
|
94
|
-
? getLayoutVariableName(boundVariables.paddingTop.id)
|
|
95
|
-
: inferDimension(paddingTop ?? 0),
|
|
96
|
-
paddingBottom: ({ paddingBottom, boundVariables }) =>
|
|
97
|
-
boundVariables?.paddingBottom
|
|
98
|
-
? getLayoutVariableName(boundVariables.paddingBottom.id)
|
|
99
|
-
: inferDimension(paddingBottom ?? 0),
|
|
100
|
-
paddingLeft: ({ paddingLeft, boundVariables }) =>
|
|
101
|
-
boundVariables?.paddingLeft
|
|
102
|
-
? getLayoutVariableName(boundVariables.paddingLeft.id)
|
|
103
|
-
: inferDimension(paddingLeft ?? 0),
|
|
104
|
-
paddingRight: ({ paddingRight, boundVariables }) =>
|
|
105
|
-
boundVariables?.paddingRight
|
|
106
|
-
? getLayoutVariableName(boundVariables.paddingRight.id)
|
|
107
|
-
: inferDimension(paddingRight ?? 0),
|
|
108
|
-
borderRadius: ({ cornerRadius, boundVariables }) => {
|
|
109
|
-
// If all corner radii are the same, use the first one
|
|
110
|
-
if (
|
|
111
|
-
cornerRadius &&
|
|
112
|
-
boundVariables?.bottomLeftRadius === boundVariables?.bottomRightRadius &&
|
|
113
|
-
boundVariables?.bottomLeftRadius === boundVariables?.topLeftRadius &&
|
|
114
|
-
boundVariables?.bottomLeftRadius === boundVariables?.topRightRadius
|
|
115
|
-
) {
|
|
116
|
-
return boundVariables?.bottomLeftRadius
|
|
117
|
-
? getLayoutVariableName(boundVariables.bottomLeftRadius.id)
|
|
118
|
-
: inferRadius(cornerRadius ?? 0);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// TODO: handle individual corner radii
|
|
122
|
-
return undefined;
|
|
123
|
-
},
|
|
124
|
-
borderTopLeftRadius: ({ rectangleCornerRadii, boundVariables }) =>
|
|
125
|
-
boundVariables?.topLeftRadius
|
|
126
|
-
? getLayoutVariableName(boundVariables.topLeftRadius.id)
|
|
127
|
-
: inferRadius(rectangleCornerRadii?.[0] ?? 0),
|
|
128
|
-
borderTopRightRadius: ({ rectangleCornerRadii, boundVariables }) =>
|
|
129
|
-
boundVariables?.topRightRadius
|
|
130
|
-
? getLayoutVariableName(boundVariables.topRightRadius.id)
|
|
131
|
-
: inferRadius(rectangleCornerRadii?.[1] ?? 0),
|
|
132
|
-
borderBottomLeftRadius: ({ rectangleCornerRadii, boundVariables }) =>
|
|
133
|
-
boundVariables?.bottomLeftRadius
|
|
134
|
-
? getLayoutVariableName(boundVariables.bottomLeftRadius.id)
|
|
135
|
-
: inferRadius(rectangleCornerRadii?.[2] ?? 0),
|
|
136
|
-
borderBottomRightRadius: ({ rectangleCornerRadii, boundVariables }) =>
|
|
137
|
-
boundVariables?.bottomRightRadius
|
|
138
|
-
? getLayoutVariableName(boundVariables.bottomRightRadius.id)
|
|
139
|
-
: inferRadius(rectangleCornerRadii?.[3] ?? 0),
|
|
140
|
-
} satisfies Record<string, LayoutPropHandler>;
|
|
141
|
-
|
|
142
|
-
type LayoutProps = keyof typeof layoutPropHandlers;
|
|
143
|
-
|
|
144
|
-
type LayoutShorthandHandler = (props: Record<LayoutProps, string | number | boolean | undefined>) =>
|
|
145
|
-
| {
|
|
146
|
-
value: string | number | boolean | undefined;
|
|
147
|
-
exclude: LayoutProps[];
|
|
148
|
-
}
|
|
149
|
-
| undefined;
|
|
150
|
-
|
|
151
|
-
const layoutShorthandHandlers = {
|
|
152
|
-
paddingX: ({ paddingLeft, paddingRight, paddingTop, paddingBottom }) => {
|
|
153
|
-
if (
|
|
154
|
-
paddingLeft === paddingRight &&
|
|
155
|
-
paddingTop === paddingBottom &&
|
|
156
|
-
paddingLeft === paddingTop
|
|
157
|
-
) {
|
|
158
|
-
return undefined;
|
|
159
|
-
}
|
|
160
|
-
if (paddingLeft === paddingRight) {
|
|
161
|
-
const value =
|
|
162
|
-
paddingLeft === "globalGutter" || paddingLeft === "betweenChips"
|
|
163
|
-
? `spacingX.${paddingLeft}`
|
|
164
|
-
: paddingLeft;
|
|
165
|
-
return {
|
|
166
|
-
value,
|
|
167
|
-
exclude: ["paddingLeft", "paddingRight"],
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
return undefined;
|
|
171
|
-
},
|
|
172
|
-
paddingY: ({ paddingLeft, paddingRight, paddingTop, paddingBottom }) => {
|
|
173
|
-
if (
|
|
174
|
-
paddingLeft === paddingRight &&
|
|
175
|
-
paddingTop === paddingBottom &&
|
|
176
|
-
paddingLeft === paddingTop
|
|
177
|
-
) {
|
|
178
|
-
return undefined;
|
|
179
|
-
}
|
|
180
|
-
if (paddingTop === paddingBottom) {
|
|
181
|
-
return {
|
|
182
|
-
value: paddingTop,
|
|
183
|
-
exclude: ["paddingTop", "paddingBottom"],
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
return undefined;
|
|
187
|
-
},
|
|
188
|
-
padding: ({ paddingLeft, paddingRight, paddingTop, paddingBottom }) => {
|
|
189
|
-
if (
|
|
190
|
-
paddingLeft === paddingRight &&
|
|
191
|
-
paddingTop === paddingBottom &&
|
|
192
|
-
paddingLeft === paddingTop
|
|
193
|
-
) {
|
|
194
|
-
return {
|
|
195
|
-
value: paddingLeft,
|
|
196
|
-
exclude: ["paddingLeft", "paddingRight", "paddingTop", "paddingBottom"],
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
return undefined;
|
|
200
|
-
},
|
|
201
|
-
} satisfies Record<string, LayoutShorthandHandler>;
|
|
202
|
-
|
|
203
|
-
type LayoutShorthandProps = keyof typeof layoutShorthandHandlers;
|
|
204
|
-
|
|
205
|
-
const layoutPropDefaults: Record<string, string | number | boolean> = {
|
|
206
|
-
flexDirection: "row",
|
|
207
|
-
justifyContent: "flexStart",
|
|
208
|
-
alignItems: "stretch",
|
|
209
|
-
flexWrap: "nowrap",
|
|
210
|
-
flexGrow: 0,
|
|
211
|
-
alignSelf: "auto",
|
|
212
|
-
gap: 0,
|
|
213
|
-
padding: 0,
|
|
214
|
-
paddingX: 0,
|
|
215
|
-
paddingY: 0,
|
|
216
|
-
paddingBottom: 0,
|
|
217
|
-
paddingLeft: 0,
|
|
218
|
-
paddingRight: 0,
|
|
219
|
-
paddingTop: 0,
|
|
220
|
-
borderRadius: 0,
|
|
221
|
-
borderTopLeftRadius: 0,
|
|
222
|
-
borderTopRightRadius: 0,
|
|
223
|
-
borderBottomLeftRadius: 0,
|
|
224
|
-
borderBottomRightRadius: 0,
|
|
225
|
-
} satisfies Record<LayoutProps | LayoutShorthandProps, string | number | boolean>;
|
|
226
|
-
|
|
227
|
-
type FrameLikeNode = NormalizedFrameNode | NormalizedComponentNode | NormalizedInstanceNode;
|
|
228
|
-
|
|
229
|
-
export function createLayoutProps(
|
|
230
|
-
node: FrameLikeNode,
|
|
231
|
-
): Record<LayoutProps | LayoutShorthandProps, string | number | boolean> {
|
|
232
|
-
const boundVariables = node.boundVariables;
|
|
233
|
-
const children = node.children;
|
|
234
|
-
|
|
235
|
-
const autoLayoutProperties = {
|
|
236
|
-
layoutMode: node.layoutMode,
|
|
237
|
-
layoutWrap: node.layoutWrap,
|
|
238
|
-
paddingLeft: node.paddingLeft,
|
|
239
|
-
paddingRight: node.paddingRight,
|
|
240
|
-
paddingTop: node.paddingTop,
|
|
241
|
-
paddingBottom: node.paddingBottom,
|
|
242
|
-
primaryAxisAlignItems: node.primaryAxisAlignItems,
|
|
243
|
-
counterAxisAlignItems: node.counterAxisAlignItems,
|
|
244
|
-
primaryAxisSizingMode: node.primaryAxisSizingMode,
|
|
245
|
-
counterAxisSizingMode: node.counterAxisSizingMode,
|
|
246
|
-
layoutGrow: node.layoutGrow,
|
|
247
|
-
layoutAlign: node.layoutAlign,
|
|
248
|
-
itemSpacing: node.itemSpacing,
|
|
249
|
-
counterAxisSpacing: node.counterAxisSpacing,
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
const radiusProperties = {
|
|
253
|
-
cornerRadius: node.cornerRadius,
|
|
254
|
-
topLeftRadius: node.rectangleCornerRadii?.[0],
|
|
255
|
-
topRightRadius: node.rectangleCornerRadii?.[1],
|
|
256
|
-
bottomRightRadius: node.rectangleCornerRadii?.[2],
|
|
257
|
-
bottomLeftRadius: node.rectangleCornerRadii?.[3],
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
const result: Record<string, string | number | boolean> = {};
|
|
8
|
+
export function inferLayoutComponent(props: LayoutComponentProps) {
|
|
9
|
+
if (
|
|
10
|
+
props.flexDirection === "row" &&
|
|
11
|
+
props.alignItems === "flexStart" &&
|
|
12
|
+
props.justifyContent === "flexStart" &&
|
|
13
|
+
props.flexWrap === "wrap"
|
|
14
|
+
) {
|
|
15
|
+
return "Inline";
|
|
16
|
+
}
|
|
261
17
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
});
|
|
269
|
-
if (value !== undefined && value !== layoutPropDefaults[prop]) {
|
|
270
|
-
result[prop] = value;
|
|
271
|
-
}
|
|
18
|
+
if (
|
|
19
|
+
props.flexDirection === "row" &&
|
|
20
|
+
props.justifyContent === "flexStart" &&
|
|
21
|
+
props.flexWrap === "nowrap"
|
|
22
|
+
) {
|
|
23
|
+
return "Columns";
|
|
272
24
|
}
|
|
273
25
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
if (shorthandResult === undefined) {
|
|
277
|
-
continue;
|
|
278
|
-
}
|
|
279
|
-
const { value, exclude } = shorthandResult;
|
|
280
|
-
if (value !== undefined && value !== layoutPropDefaults[prop]) {
|
|
281
|
-
result[prop] = value;
|
|
282
|
-
for (const excludedProp of exclude) {
|
|
283
|
-
delete result[excludedProp];
|
|
284
|
-
}
|
|
285
|
-
}
|
|
26
|
+
if (props.flexDirection === "column") {
|
|
27
|
+
return "Stack";
|
|
286
28
|
}
|
|
287
29
|
|
|
288
|
-
return
|
|
30
|
+
return "Flex";
|
|
289
31
|
}
|
|
@@ -7,9 +7,14 @@ import type {
|
|
|
7
7
|
NormalizedComponentNode,
|
|
8
8
|
NormalizedInstanceNode,
|
|
9
9
|
NormalizedVectorNode,
|
|
10
|
+
NormalizedBooleanOperationNode,
|
|
10
11
|
} from "./types";
|
|
11
12
|
|
|
12
13
|
export function createPluginNormalizer() {
|
|
14
|
+
async function normalizeNodes(nodes: readonly SceneNode[]): Promise<NormalizedSceneNode[]> {
|
|
15
|
+
return Promise.all(nodes.filter((node) => node.visible).map(normalizeNode));
|
|
16
|
+
}
|
|
17
|
+
|
|
13
18
|
async function normalizeNode(node: SceneNode): Promise<NormalizedSceneNode> {
|
|
14
19
|
if (node.type === "FRAME") {
|
|
15
20
|
return normalizeFrameNode(node);
|
|
@@ -23,6 +28,9 @@ export function createPluginNormalizer() {
|
|
|
23
28
|
if (node.type === "VECTOR") {
|
|
24
29
|
return normalizeVectorNode(node);
|
|
25
30
|
}
|
|
31
|
+
if (node.type === "BOOLEAN_OPERATION") {
|
|
32
|
+
return normalizeBooleanOperationNode(node);
|
|
33
|
+
}
|
|
26
34
|
if (node.type === "TEXT") {
|
|
27
35
|
return normalizeTextNode(node);
|
|
28
36
|
}
|
|
@@ -44,7 +52,7 @@ export function createPluginNormalizer() {
|
|
|
44
52
|
boundVariables: await normalizeBoundVariables(node),
|
|
45
53
|
...normalizeRadiusProps(node),
|
|
46
54
|
...normalizeAutolayoutProps(node),
|
|
47
|
-
children: await
|
|
55
|
+
children: await normalizeNodes(node.children),
|
|
48
56
|
};
|
|
49
57
|
}
|
|
50
58
|
|
|
@@ -75,7 +83,7 @@ export function createPluginNormalizer() {
|
|
|
75
83
|
counterAxisSpacing: node.inferredAutoLayout?.counterAxisSpacing ?? undefined,
|
|
76
84
|
fills: [],
|
|
77
85
|
strokes: [],
|
|
78
|
-
children: await
|
|
86
|
+
children: await normalizeNodes(node.children),
|
|
79
87
|
};
|
|
80
88
|
}
|
|
81
89
|
|
|
@@ -100,6 +108,18 @@ export function createPluginNormalizer() {
|
|
|
100
108
|
};
|
|
101
109
|
}
|
|
102
110
|
|
|
111
|
+
async function normalizeBooleanOperationNode(
|
|
112
|
+
node: BooleanOperationNode,
|
|
113
|
+
): Promise<NormalizedBooleanOperationNode> {
|
|
114
|
+
return {
|
|
115
|
+
type: node.type,
|
|
116
|
+
id: node.id,
|
|
117
|
+
name: node.name,
|
|
118
|
+
boundVariables: await normalizeBoundVariables(node),
|
|
119
|
+
children: await normalizeNodes(node.children),
|
|
120
|
+
fills: normalizePaints(node.fills),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
103
123
|
async function normalizeTextNode(node: TextNode): Promise<NormalizedTextNode> {
|
|
104
124
|
const segments = node.getStyledTextSegments([
|
|
105
125
|
"fontSize",
|
|
@@ -173,7 +193,7 @@ export function createPluginNormalizer() {
|
|
|
173
193
|
boundVariables: await normalizeBoundVariables(node),
|
|
174
194
|
...normalizeRadiusProps(node),
|
|
175
195
|
...normalizeAutolayoutProps(node),
|
|
176
|
-
children: await
|
|
196
|
+
children: await normalizeNodes(node.children),
|
|
177
197
|
};
|
|
178
198
|
}
|
|
179
199
|
|
|
@@ -203,7 +223,7 @@ export function createPluginNormalizer() {
|
|
|
203
223
|
boundVariables: await normalizeBoundVariables(node),
|
|
204
224
|
...normalizeRadiusProps(node),
|
|
205
225
|
...normalizeAutolayoutProps(node),
|
|
206
|
-
children: await
|
|
226
|
+
children: await normalizeNodes(node.children),
|
|
207
227
|
componentKey: mainComponent.key,
|
|
208
228
|
componentSetKey:
|
|
209
229
|
mainComponent.parent?.type === "COMPONENT_SET" ? mainComponent.parent.key : undefined,
|
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
NormalizedInstanceNode,
|
|
9
9
|
NormalizedTextSegment,
|
|
10
10
|
NormalizedVectorNode,
|
|
11
|
+
NormalizedBooleanOperationNode,
|
|
11
12
|
} from "./types";
|
|
12
13
|
|
|
13
14
|
export interface RestNormalizerContext {
|
|
@@ -17,6 +18,11 @@ export interface RestNormalizerContext {
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
export function createRestNormalizer(ctx: RestNormalizerContext) {
|
|
21
|
+
function normalizeNodes(nodes: readonly FigmaRestSpec.Node[]): NormalizedSceneNode[] {
|
|
22
|
+
// Figma REST API omits default values for some fields, "visible" is one of them
|
|
23
|
+
return nodes.filter((node) => !("visible" in node) || node.visible).map(normalizeNode);
|
|
24
|
+
}
|
|
25
|
+
|
|
20
26
|
function normalizeNode(node: FigmaRestSpec.Node): NormalizedSceneNode {
|
|
21
27
|
if (node.type === "FRAME") {
|
|
22
28
|
return normalizeFrameNode(node);
|
|
@@ -30,6 +36,9 @@ export function createRestNormalizer(ctx: RestNormalizerContext) {
|
|
|
30
36
|
if (node.type === "VECTOR") {
|
|
31
37
|
return normalizeVectorNode(node);
|
|
32
38
|
}
|
|
39
|
+
if (node.type === "BOOLEAN_OPERATION") {
|
|
40
|
+
return normalizeBooleanOperationNode(node);
|
|
41
|
+
}
|
|
33
42
|
if (node.type === "TEXT") {
|
|
34
43
|
return normalizeTextNode(node);
|
|
35
44
|
}
|
|
@@ -46,7 +55,7 @@ export function createRestNormalizer(ctx: RestNormalizerContext) {
|
|
|
46
55
|
function normalizeFrameNode(node: FigmaRestSpec.FrameNode): NormalizedFrameNode {
|
|
47
56
|
return {
|
|
48
57
|
...node,
|
|
49
|
-
children: node.children
|
|
58
|
+
children: normalizeNodes(node.children),
|
|
50
59
|
};
|
|
51
60
|
}
|
|
52
61
|
|
|
@@ -54,7 +63,7 @@ export function createRestNormalizer(ctx: RestNormalizerContext) {
|
|
|
54
63
|
return {
|
|
55
64
|
...node,
|
|
56
65
|
type: "FRAME",
|
|
57
|
-
children: node.children
|
|
66
|
+
children: normalizeNodes(node.children),
|
|
58
67
|
};
|
|
59
68
|
}
|
|
60
69
|
|
|
@@ -66,6 +75,15 @@ export function createRestNormalizer(ctx: RestNormalizerContext) {
|
|
|
66
75
|
return node;
|
|
67
76
|
}
|
|
68
77
|
|
|
78
|
+
function normalizeBooleanOperationNode(
|
|
79
|
+
node: FigmaRestSpec.BooleanOperationNode,
|
|
80
|
+
): NormalizedBooleanOperationNode {
|
|
81
|
+
return {
|
|
82
|
+
...node,
|
|
83
|
+
children: normalizeNodes(node.children),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
69
87
|
function normalizeTextNode(node: FigmaRestSpec.TextNode): NormalizedTextNode {
|
|
70
88
|
// Function to segment a text node based on style overrides
|
|
71
89
|
function segmentTextNode(textNode: FigmaRestSpec.TextNode): NormalizedTextSegment[] {
|
|
@@ -141,7 +159,7 @@ export function createRestNormalizer(ctx: RestNormalizerContext) {
|
|
|
141
159
|
function normalizeComponentNode(node: FigmaRestSpec.ComponentNode): NormalizedComponentNode {
|
|
142
160
|
return {
|
|
143
161
|
...node,
|
|
144
|
-
children: node.children
|
|
162
|
+
children: normalizeNodes(node.children),
|
|
145
163
|
};
|
|
146
164
|
}
|
|
147
165
|
|
|
@@ -167,7 +185,7 @@ export function createRestNormalizer(ctx: RestNormalizerContext) {
|
|
|
167
185
|
|
|
168
186
|
return {
|
|
169
187
|
...node,
|
|
170
|
-
children: node.children
|
|
188
|
+
children: normalizeNodes(node.children),
|
|
171
189
|
componentKey: mainComponent.key,
|
|
172
190
|
componentSetKey: componentSet?.key,
|
|
173
191
|
componentProperties,
|
package/src/normalizer/types.ts
CHANGED
|
@@ -82,7 +82,9 @@ export interface NormalizedVectorNode
|
|
|
82
82
|
extends Pick<FigmaRestSpec.VectorNode, CommonProps | ShapeProps> {}
|
|
83
83
|
|
|
84
84
|
export interface NormalizedBooleanOperationNode
|
|
85
|
-
extends Pick<FigmaRestSpec.BooleanOperationNode, CommonProps | "fills"> {
|
|
85
|
+
extends Pick<FigmaRestSpec.BooleanOperationNode, CommonProps | "fills"> {
|
|
86
|
+
children: NormalizedSceneNode[];
|
|
87
|
+
}
|
|
86
88
|
|
|
87
89
|
export type NormalizedSceneNode =
|
|
88
90
|
| NormalizedFrameNode
|