@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.
Files changed (53) hide show
  1. package/lib/index.cjs +556 -471
  2. package/lib/index.d.ts +29 -4
  3. package/lib/index.js +556 -471
  4. package/package.json +2 -2
  5. package/src/component/handlers/action-button.ts +66 -0
  6. package/src/component/handlers/action-chip.ts +71 -0
  7. package/src/component/handlers/action-sheet.ts +74 -0
  8. package/src/component/handlers/app-bar.ts +183 -0
  9. package/src/component/handlers/avatar-stack.ts +35 -0
  10. package/src/component/handlers/avatar.ts +37 -0
  11. package/src/component/handlers/badge.ts +20 -0
  12. package/src/component/handlers/callout.ts +87 -0
  13. package/src/component/handlers/checkbox.ts +32 -0
  14. package/src/component/handlers/chip-tabs.ts +51 -0
  15. package/src/component/handlers/control-chip.ts +75 -0
  16. package/src/component/handlers/error-state.ts +37 -0
  17. package/src/component/handlers/extended-action-sheet.ts +86 -0
  18. package/src/component/handlers/extended-fab.ts +24 -0
  19. package/src/component/handlers/fab.ts +17 -0
  20. package/src/component/handlers/help-bubble.ts +66 -0
  21. package/src/component/handlers/identity-placeholder.ts +16 -0
  22. package/src/component/handlers/inline-banner.ts +83 -0
  23. package/src/component/handlers/manner-temp-badge.ts +17 -0
  24. package/src/component/handlers/multiline-text-field.ts +80 -0
  25. package/src/component/handlers/progress-circle.ts +49 -0
  26. package/src/component/handlers/reaction-button.ts +36 -0
  27. package/src/component/handlers/segmented-control.ts +51 -0
  28. package/src/component/handlers/select-box.ts +76 -0
  29. package/src/component/handlers/skeleton.ts +51 -0
  30. package/src/component/handlers/snackbar.ts +21 -0
  31. package/src/component/handlers/switch.ts +29 -0
  32. package/src/component/handlers/tabs.ts +107 -0
  33. package/src/component/handlers/text-button.ts +57 -0
  34. package/src/component/handlers/text-field.ts +108 -0
  35. package/src/component/handlers/toggle-button.ts +44 -0
  36. package/src/component/index.ts +32 -1644
  37. package/src/component/type-helper.ts +11 -0
  38. package/src/generate-code.ts +183 -145
  39. package/src/icon.ts +2 -2
  40. package/src/index.ts +1 -2
  41. package/src/jsx.ts +1 -1
  42. package/src/layout.ts +23 -281
  43. package/src/normalizer/from-plugin.ts +24 -4
  44. package/src/normalizer/from-rest.ts +22 -4
  45. package/src/normalizer/index.ts +3 -0
  46. package/src/normalizer/types.ts +3 -1
  47. package/src/{color.ts → props/color.ts} +1 -1
  48. package/src/props/layout.ts +292 -0
  49. package/src/{sizing.ts → props/sizing.ts} +17 -17
  50. package/src/{text.ts → props/text.ts} +2 -1
  51. package/src/{variable.ts → props/variable.ts} +1 -1
  52. package/src/{util.ts → utils/common.ts} +0 -2
  53. package/src/{node-util.ts → utils/figma-node.ts} +1 -1
@@ -0,0 +1,292 @@
1
+ import type {
2
+ NormalizedComponentNode,
3
+ NormalizedFrameNode,
4
+ NormalizedInstanceNode,
5
+ } from "../normalizer";
6
+ import { getLayoutVariableName, inferDimension, inferRadius } from "./variable";
7
+
8
+ // Basic handlers
9
+ type LayoutPropHandler = (
10
+ props: Pick<
11
+ NormalizedFrameNode,
12
+ | "layoutMode"
13
+ | "layoutWrap"
14
+ | "paddingLeft"
15
+ | "paddingRight"
16
+ | "paddingTop"
17
+ | "paddingBottom"
18
+ | "primaryAxisAlignItems"
19
+ | "counterAxisAlignItems"
20
+ | "primaryAxisSizingMode"
21
+ | "counterAxisSizingMode"
22
+ | "layoutGrow"
23
+ | "layoutAlign"
24
+ | "itemSpacing"
25
+ | "counterAxisSpacing"
26
+ | "boundVariables"
27
+ | "cornerRadius"
28
+ | "rectangleCornerRadii"
29
+ | "children"
30
+ >,
31
+ ) => string | number | boolean | undefined;
32
+
33
+ const layoutPropHandlers = {
34
+ flexDirection: ({ layoutMode }) => (layoutMode === "HORIZONTAL" ? "row" : "column"),
35
+ justifyContent: ({ primaryAxisAlignItems }) => {
36
+ switch (primaryAxisAlignItems) {
37
+ case "MIN":
38
+ return "flexStart";
39
+ case "CENTER":
40
+ return "center";
41
+ case "MAX":
42
+ return "flexEnd";
43
+ case "SPACE_BETWEEN":
44
+ return "spaceBetween";
45
+ }
46
+ },
47
+ alignItems: ({ counterAxisAlignItems, children }) => {
48
+ const isStretch = children.every((child) => {
49
+ if (!("layoutAlign" in child)) {
50
+ return false;
51
+ }
52
+
53
+ return child.layoutAlign === "STRETCH";
54
+ });
55
+
56
+ if (isStretch) {
57
+ return "stretch";
58
+ }
59
+
60
+ switch (counterAxisAlignItems) {
61
+ case "MIN":
62
+ return "flexStart";
63
+ case "CENTER":
64
+ return "center";
65
+ case "MAX":
66
+ return "flexEnd";
67
+ case "BASELINE":
68
+ return "baseline";
69
+ }
70
+ },
71
+ flexWrap: ({ layoutWrap }) => (layoutWrap === "WRAP" ? "wrap" : "nowrap"),
72
+ flexGrow: ({ layoutGrow }) => layoutGrow,
73
+ alignSelf: ({ layoutAlign }) => {
74
+ switch (layoutAlign) {
75
+ case "STRETCH":
76
+ return "stretch";
77
+ case "MIN":
78
+ return "flexStart";
79
+ case "CENTER":
80
+ return "center";
81
+ case "MAX":
82
+ return "flexEnd";
83
+ }
84
+ },
85
+ gap: ({ itemSpacing, boundVariables, primaryAxisAlignItems, children }) =>
86
+ children.length <= 1
87
+ ? 0
88
+ : primaryAxisAlignItems === "SPACE_BETWEEN"
89
+ ? 0
90
+ : boundVariables?.itemSpacing
91
+ ? getLayoutVariableName(boundVariables.itemSpacing.id)
92
+ : inferDimension(itemSpacing ?? 0),
93
+ paddingTop: ({ paddingTop, boundVariables }) =>
94
+ boundVariables?.paddingTop
95
+ ? getLayoutVariableName(boundVariables.paddingTop.id)
96
+ : inferDimension(paddingTop ?? 0),
97
+ paddingBottom: ({ paddingBottom, boundVariables }) =>
98
+ boundVariables?.paddingBottom
99
+ ? getLayoutVariableName(boundVariables.paddingBottom.id)
100
+ : inferDimension(paddingBottom ?? 0),
101
+ paddingLeft: ({ paddingLeft, boundVariables }) =>
102
+ boundVariables?.paddingLeft
103
+ ? getLayoutVariableName(boundVariables.paddingLeft.id)
104
+ : inferDimension(paddingLeft ?? 0),
105
+ paddingRight: ({ paddingRight, boundVariables }) =>
106
+ boundVariables?.paddingRight
107
+ ? getLayoutVariableName(boundVariables.paddingRight.id)
108
+ : inferDimension(paddingRight ?? 0),
109
+ borderRadius: ({ cornerRadius, boundVariables }) => {
110
+ // If all corner radii are the same, use the first one
111
+ if (
112
+ cornerRadius &&
113
+ boundVariables?.bottomLeftRadius === boundVariables?.bottomRightRadius &&
114
+ boundVariables?.bottomLeftRadius === boundVariables?.topLeftRadius &&
115
+ boundVariables?.bottomLeftRadius === boundVariables?.topRightRadius
116
+ ) {
117
+ return boundVariables?.bottomLeftRadius
118
+ ? getLayoutVariableName(boundVariables.bottomLeftRadius.id)
119
+ : inferRadius(cornerRadius ?? 0);
120
+ }
121
+
122
+ // TODO: handle individual corner radii
123
+ return undefined;
124
+ },
125
+ borderTopLeftRadius: ({ rectangleCornerRadii, boundVariables }) =>
126
+ boundVariables?.topLeftRadius
127
+ ? getLayoutVariableName(boundVariables.topLeftRadius.id)
128
+ : inferRadius(rectangleCornerRadii?.[0] ?? 0),
129
+ borderTopRightRadius: ({ rectangleCornerRadii, boundVariables }) =>
130
+ boundVariables?.topRightRadius
131
+ ? getLayoutVariableName(boundVariables.topRightRadius.id)
132
+ : inferRadius(rectangleCornerRadii?.[1] ?? 0),
133
+ borderBottomLeftRadius: ({ rectangleCornerRadii, boundVariables }) =>
134
+ boundVariables?.bottomLeftRadius
135
+ ? getLayoutVariableName(boundVariables.bottomLeftRadius.id)
136
+ : inferRadius(rectangleCornerRadii?.[2] ?? 0),
137
+ borderBottomRightRadius: ({ rectangleCornerRadii, boundVariables }) =>
138
+ boundVariables?.bottomRightRadius
139
+ ? getLayoutVariableName(boundVariables.bottomRightRadius.id)
140
+ : inferRadius(rectangleCornerRadii?.[3] ?? 0),
141
+ } satisfies Record<string, LayoutPropHandler>;
142
+
143
+ type LayoutProps = keyof typeof layoutPropHandlers;
144
+
145
+ // Shorthand handlers
146
+ type LayoutShorthandHandler = (props: Record<LayoutProps, string | number | boolean | undefined>) =>
147
+ | {
148
+ value: string | number | boolean | undefined;
149
+ exclude: LayoutProps[];
150
+ }
151
+ | undefined;
152
+
153
+ const layoutShorthandHandlers = {
154
+ paddingX: ({ paddingLeft, paddingRight, paddingTop, paddingBottom }) => {
155
+ if (
156
+ paddingLeft === paddingRight &&
157
+ paddingTop === paddingBottom &&
158
+ paddingLeft === paddingTop
159
+ ) {
160
+ return undefined;
161
+ }
162
+ if (paddingLeft === paddingRight) {
163
+ const value =
164
+ paddingLeft === "globalGutter" || paddingLeft === "betweenChips"
165
+ ? `spacingX.${paddingLeft}`
166
+ : paddingLeft;
167
+ return {
168
+ value,
169
+ exclude: ["paddingLeft", "paddingRight"],
170
+ };
171
+ }
172
+ return undefined;
173
+ },
174
+ paddingY: ({ paddingLeft, paddingRight, paddingTop, paddingBottom }) => {
175
+ if (
176
+ paddingLeft === paddingRight &&
177
+ paddingTop === paddingBottom &&
178
+ paddingLeft === paddingTop
179
+ ) {
180
+ return undefined;
181
+ }
182
+ if (paddingTop === paddingBottom) {
183
+ return {
184
+ value: paddingTop,
185
+ exclude: ["paddingTop", "paddingBottom"],
186
+ };
187
+ }
188
+ return undefined;
189
+ },
190
+ padding: ({ paddingLeft, paddingRight, paddingTop, paddingBottom }) => {
191
+ if (
192
+ paddingLeft === paddingRight &&
193
+ paddingTop === paddingBottom &&
194
+ paddingLeft === paddingTop
195
+ ) {
196
+ return {
197
+ value: paddingLeft,
198
+ exclude: ["paddingLeft", "paddingRight", "paddingTop", "paddingBottom"],
199
+ };
200
+ }
201
+ return undefined;
202
+ },
203
+ } satisfies Record<string, LayoutShorthandHandler>;
204
+
205
+ type LayoutShorthandProps = keyof typeof layoutShorthandHandlers;
206
+
207
+ // Default values
208
+ const layoutPropDefaults: Record<string, string | number | boolean> = {
209
+ flexDirection: "row",
210
+ justifyContent: "flexStart",
211
+ alignItems: "stretch",
212
+ flexWrap: "nowrap",
213
+ flexGrow: 0,
214
+ alignSelf: "auto",
215
+ gap: 0,
216
+ padding: 0,
217
+ paddingX: 0,
218
+ paddingY: 0,
219
+ paddingBottom: 0,
220
+ paddingLeft: 0,
221
+ paddingRight: 0,
222
+ paddingTop: 0,
223
+ borderRadius: 0,
224
+ borderTopLeftRadius: 0,
225
+ borderTopRightRadius: 0,
226
+ borderBottomLeftRadius: 0,
227
+ borderBottomRightRadius: 0,
228
+ } satisfies Record<LayoutProps | LayoutShorthandProps, string | number | boolean>;
229
+
230
+ type FrameLikeNode = NormalizedFrameNode | NormalizedComponentNode | NormalizedInstanceNode;
231
+
232
+ export function createLayoutProps(
233
+ node: FrameLikeNode,
234
+ ): Record<LayoutProps | LayoutShorthandProps, string | number | boolean> {
235
+ const boundVariables = node.boundVariables;
236
+ const children = node.children;
237
+
238
+ const autoLayoutProperties = {
239
+ layoutMode: node.layoutMode,
240
+ layoutWrap: node.layoutWrap,
241
+ paddingLeft: node.paddingLeft,
242
+ paddingRight: node.paddingRight,
243
+ paddingTop: node.paddingTop,
244
+ paddingBottom: node.paddingBottom,
245
+ primaryAxisAlignItems: node.primaryAxisAlignItems,
246
+ counterAxisAlignItems: node.counterAxisAlignItems,
247
+ primaryAxisSizingMode: node.primaryAxisSizingMode,
248
+ counterAxisSizingMode: node.counterAxisSizingMode,
249
+ layoutGrow: node.layoutGrow,
250
+ layoutAlign: node.layoutAlign,
251
+ itemSpacing: node.itemSpacing,
252
+ counterAxisSpacing: node.counterAxisSpacing,
253
+ };
254
+
255
+ const radiusProperties = {
256
+ cornerRadius: node.cornerRadius,
257
+ topLeftRadius: node.rectangleCornerRadii?.[0],
258
+ topRightRadius: node.rectangleCornerRadii?.[1],
259
+ bottomRightRadius: node.rectangleCornerRadii?.[2],
260
+ bottomLeftRadius: node.rectangleCornerRadii?.[3],
261
+ };
262
+
263
+ const result: Record<string, string | number | boolean> = {};
264
+
265
+ for (const [prop, handler] of Object.entries(layoutPropHandlers)) {
266
+ const value = handler({
267
+ ...autoLayoutProperties,
268
+ ...radiusProperties,
269
+ boundVariables,
270
+ children,
271
+ });
272
+ if (value !== undefined && value !== layoutPropDefaults[prop]) {
273
+ result[prop] = value;
274
+ }
275
+ }
276
+
277
+ for (const [prop, handler] of Object.entries(layoutShorthandHandlers)) {
278
+ const shorthandResult = handler(result);
279
+ if (shorthandResult === undefined) {
280
+ continue;
281
+ }
282
+ const { value, exclude } = shorthandResult;
283
+ if (value !== undefined && value !== layoutPropDefaults[prop]) {
284
+ result[prop] = value;
285
+ for (const excludedProp of exclude) {
286
+ delete result[excludedProp];
287
+ }
288
+ }
289
+ }
290
+
291
+ return result;
292
+ }
@@ -1,29 +1,30 @@
1
- import type { NormalizedFrameNode } from "./normalizer/types";
1
+ import type { NormalizedFrameNode } from "../normalizer";
2
2
  import { getLayoutVariableName, inferDimension } from "./variable";
3
3
 
4
- type SizingPropHandler = (props: {
5
- boundVariables: NonNullable<NormalizedFrameNode["boundVariables"]>;
6
- layoutSizingHorizontal: FrameNode["layoutSizingHorizontal"];
7
- layoutSizingVertical: FrameNode["layoutSizingVertical"];
8
- width: FrameNode["width"];
9
- height: FrameNode["height"];
10
- }) => string | number | boolean | undefined;
4
+ type SizingPropHandler = (
5
+ props: Pick<
6
+ NormalizedFrameNode,
7
+ "boundVariables" | "layoutSizingHorizontal" | "layoutSizingVertical" | "absoluteBoundingBox"
8
+ >,
9
+ ) => string | number | boolean | undefined;
11
10
 
12
11
  const sizingPropHandlers = {
13
- height: ({ boundVariables, layoutSizingVertical, height }) =>
12
+ height: ({ boundVariables, layoutSizingVertical, absoluteBoundingBox }) =>
14
13
  layoutSizingVertical === "FIXED"
15
- ? boundVariables.size?.y
14
+ ? boundVariables?.size?.y
16
15
  ? getLayoutVariableName(boundVariables.size.y.id)
17
- : inferDimension(height)
16
+ : inferDimension(absoluteBoundingBox?.height ?? 0)
18
17
  : undefined,
19
- width: ({ boundVariables, layoutSizingHorizontal, width }) =>
18
+ width: ({ boundVariables, layoutSizingHorizontal, absoluteBoundingBox }) =>
20
19
  layoutSizingHorizontal === "FIXED"
21
- ? boundVariables.size?.x
20
+ ? boundVariables?.size?.x
22
21
  ? getLayoutVariableName(boundVariables.size.x.id)
23
- : inferDimension(width)
22
+ : inferDimension(absoluteBoundingBox?.width ?? 0)
24
23
  : undefined,
25
24
  } satisfies Record<string, SizingPropHandler>;
26
25
 
26
+ export type SizingProps = keyof typeof sizingPropHandlers;
27
+
27
28
  export function createSizingProps(
28
29
  node: Pick<
29
30
  NormalizedFrameNode,
@@ -33,7 +34,7 @@ export function createSizingProps(
33
34
  const boundVariables = node.boundVariables;
34
35
  const layoutSizingHorizontal = node.layoutSizingHorizontal ?? "FIXED";
35
36
  const layoutSizingVertical = node.layoutSizingVertical ?? "FIXED";
36
- const { width, height } = node.absoluteBoundingBox ?? { width: 0, height: 0 };
37
+ const absoluteBoundingBox = node.absoluteBoundingBox;
37
38
 
38
39
  if (!boundVariables) {
39
40
  return {};
@@ -46,8 +47,7 @@ export function createSizingProps(
46
47
  boundVariables,
47
48
  layoutSizingHorizontal,
48
49
  layoutSizingVertical,
49
- width,
50
- height,
50
+ absoluteBoundingBox,
51
51
  });
52
52
  if (value !== undefined) {
53
53
  result[prop] = value;
@@ -1,6 +1,7 @@
1
- import type { NormalizedTextNode } from "./normalizer/types";
1
+ import type { NormalizedTextNode } from "../normalizer";
2
2
  import { getTypographyVariableName } from "./variable";
3
3
 
4
+ // TODO: handle raw values
4
5
  export function createTextProps(boundVariables: NormalizedTextNode["boundVariables"]) {
5
6
  const fontSizeBoundVariables = boundVariables?.fontSize?.[0];
6
7
  const fontStyleBoundVariables = boundVariables?.fontStyle?.[0];
@@ -1,6 +1,6 @@
1
1
  import { vars } from "@seed-design/css/vars";
2
2
  import { camelCase } from "change-case";
3
- import { FIGMA_VARIABLES } from "./data/variables";
3
+ import { FIGMA_VARIABLES } from "../data/variables";
4
4
 
5
5
  function sanitizeVariableId(id: string) {
6
6
  return id.replace("VariableID:", "").split("/")[0]!;
@@ -1,5 +1,3 @@
1
- import type { NormalizedSceneNode } from "./normalizer/types";
2
-
3
1
  export function ensureArray<T>(maybeArray: T | T[]): T[] {
4
2
  if (Array.isArray(maybeArray)) {
5
3
  return maybeArray;
@@ -1,4 +1,4 @@
1
- import type { NormalizedInstanceNode, NormalizedSceneNode } from "./normalizer/types";
1
+ import type { NormalizedInstanceNode, NormalizedSceneNode } from "../normalizer";
2
2
 
3
3
  export function traverseNode(
4
4
  node: NormalizedSceneNode,