@seed-design/figma 0.1.7 → 0.1.8

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 (165) hide show
  1. package/lib/codegen/index.cjs +746 -646
  2. package/lib/codegen/index.d.ts +707 -627
  3. package/lib/codegen/index.d.ts.map +1 -1
  4. package/lib/codegen/index.js +746 -646
  5. package/lib/codegen/targets/react/index.cjs +1170 -1180
  6. package/lib/codegen/targets/react/index.d.ts +20 -9
  7. package/lib/codegen/targets/react/index.d.ts.map +1 -1
  8. package/lib/codegen/targets/react/index.js +1170 -1180
  9. package/lib/index.cjs +883 -668
  10. package/lib/index.d.ts +3 -1
  11. package/lib/index.d.ts.map +1 -1
  12. package/lib/index.js +883 -668
  13. package/package.json +3 -3
  14. package/src/codegen/component-properties.ts +283 -146
  15. package/src/codegen/core/component-handler.ts +9 -3
  16. package/src/codegen/core/jsx.ts +7 -2
  17. package/src/codegen/core/value-resolver.ts +35 -21
  18. package/src/codegen/targets/figma/pipeline.ts +4 -2
  19. package/src/codegen/targets/figma/value-resolver.ts +38 -2
  20. package/src/codegen/targets/react/component/handlers/action-button.ts +69 -5
  21. package/src/codegen/targets/react/component/handlers/alert-dialog.ts +81 -0
  22. package/src/codegen/targets/react/component/handlers/app-bar.ts +93 -128
  23. package/src/codegen/targets/react/component/handlers/avatar.ts +17 -8
  24. package/src/codegen/targets/react/component/handlers/badge.ts +0 -1
  25. package/src/codegen/targets/react/component/handlers/bottom-sheet.ts +71 -0
  26. package/src/codegen/targets/react/component/handlers/callout.ts +13 -17
  27. package/src/codegen/targets/react/component/handlers/chip.ts +66 -0
  28. package/src/codegen/targets/react/component/handlers/contextual-floating-button.ts +52 -0
  29. package/src/codegen/targets/react/component/handlers/divider.ts +19 -0
  30. package/src/codegen/targets/react/component/handlers/error-state.ts +26 -23
  31. package/src/codegen/targets/react/component/handlers/floating-action-button.ts +48 -0
  32. package/src/codegen/targets/react/component/handlers/help-bubble.ts +4 -5
  33. package/src/codegen/targets/react/component/handlers/identity-placeholder.ts +3 -2
  34. package/src/codegen/targets/react/component/handlers/inline-banner.ts +7 -14
  35. package/src/codegen/targets/react/component/handlers/manner-temp.ts +18 -0
  36. package/src/codegen/targets/react/component/handlers/menu-sheet.ts +106 -0
  37. package/src/codegen/targets/react/component/handlers/multiline-text-field.ts +1 -1
  38. package/src/codegen/targets/react/component/handlers/progress-circle.ts +3 -1
  39. package/src/codegen/targets/react/component/handlers/segmented-control.ts +4 -2
  40. package/src/codegen/targets/react/component/handlers/select-box.ts +7 -4
  41. package/src/codegen/targets/react/component/handlers/snackbar.ts +2 -2
  42. package/src/codegen/targets/react/component/handlers/tabs.ts +8 -125
  43. package/src/codegen/targets/react/component/index.ts +22 -19
  44. package/src/codegen/targets/react/instance.ts +1 -1
  45. package/src/codegen/targets/react/pipeline.ts +4 -2
  46. package/src/codegen/targets/react/props.ts +11 -4
  47. package/src/codegen/targets/react/value-resolver.ts +38 -3
  48. package/src/entities/data/__generated__/component-sets/action-button.d.ts +5 -5
  49. package/src/entities/data/__generated__/component-sets/action-button.mjs +5 -5
  50. package/src/entities/data/__generated__/component-sets/alert-dialog.d.ts +26 -0
  51. package/src/entities/data/__generated__/component-sets/alert-dialog.mjs +26 -0
  52. package/src/entities/data/__generated__/component-sets/avatar-stack.d.ts +5 -18
  53. package/src/entities/data/__generated__/component-sets/avatar-stack.mjs +5 -18
  54. package/src/entities/data/__generated__/component-sets/avatar.d.ts +11 -5
  55. package/src/entities/data/__generated__/component-sets/avatar.mjs +11 -5
  56. package/src/entities/data/__generated__/component-sets/badge.d.ts +0 -7
  57. package/src/entities/data/__generated__/component-sets/badge.mjs +0 -7
  58. package/src/entities/data/__generated__/component-sets/bottom-navigation-global.d.ts +3 -0
  59. package/src/entities/data/__generated__/component-sets/bottom-navigation-global.mjs +3 -0
  60. package/src/entities/data/__generated__/component-sets/bottom-navigation-kr.d.ts +3 -0
  61. package/src/entities/data/__generated__/component-sets/bottom-navigation-kr.mjs +3 -0
  62. package/src/entities/data/__generated__/component-sets/bottom-sheet.d.ts +53 -4
  63. package/src/entities/data/__generated__/component-sets/bottom-sheet.mjs +53 -4
  64. package/src/entities/data/__generated__/component-sets/callout.d.ts +13 -22
  65. package/src/entities/data/__generated__/component-sets/callout.mjs +13 -22
  66. package/src/entities/data/__generated__/component-sets/checkbox.d.ts +3 -3
  67. package/src/entities/data/__generated__/component-sets/checkbox.mjs +3 -3
  68. package/src/entities/data/__generated__/component-sets/checkmark.d.ts +34 -0
  69. package/src/entities/data/__generated__/component-sets/checkmark.mjs +34 -0
  70. package/src/entities/data/__generated__/component-sets/chip.d.ts +86 -0
  71. package/src/entities/data/__generated__/component-sets/chip.mjs +86 -0
  72. package/src/entities/data/__generated__/component-sets/chlid.d.ts +14 -0
  73. package/src/entities/data/__generated__/component-sets/chlid.mjs +14 -0
  74. package/src/entities/data/__generated__/component-sets/{extended-floating-action-button.d.ts → contextual-floating-button.d.ts} +12 -10
  75. package/src/entities/data/__generated__/component-sets/{extended-floating-action-button.mjs → contextual-floating-button.mjs} +12 -10
  76. package/src/entities/data/__generated__/component-sets/floating-action-button.d.ts +4 -8
  77. package/src/entities/data/__generated__/component-sets/floating-action-button.mjs +4 -8
  78. package/src/entities/data/__generated__/component-sets/help-bubble.d.ts +3 -10
  79. package/src/entities/data/__generated__/component-sets/help-bubble.mjs +3 -10
  80. package/src/entities/data/__generated__/component-sets/index.d.ts +13 -13
  81. package/src/entities/data/__generated__/component-sets/index.mjs +13 -13
  82. package/src/entities/data/__generated__/component-sets/inline-banner.d.ts +3 -3
  83. package/src/entities/data/__generated__/component-sets/inline-banner.mjs +3 -3
  84. package/src/entities/data/__generated__/component-sets/main-tab-navigation-global.d.ts +2 -2
  85. package/src/entities/data/__generated__/component-sets/main-tab-navigation-global.mjs +2 -2
  86. package/src/entities/data/__generated__/component-sets/manner-temp-badge.d.ts +2 -2
  87. package/src/entities/data/__generated__/component-sets/manner-temp-badge.mjs +2 -2
  88. package/src/entities/data/__generated__/component-sets/manner-temp.d.ts +2 -2
  89. package/src/entities/data/__generated__/component-sets/manner-temp.mjs +2 -2
  90. package/src/entities/data/__generated__/component-sets/{extended-action-sheet.d.ts → menu-sheet.d.ts} +17 -13
  91. package/src/entities/data/__generated__/component-sets/{extended-action-sheet.mjs → menu-sheet.mjs} +17 -13
  92. package/src/entities/data/__generated__/component-sets/multiline-text-field.d.ts +18 -18
  93. package/src/entities/data/__generated__/component-sets/multiline-text-field.mjs +18 -18
  94. package/src/entities/data/__generated__/component-sets/progress-circle.d.ts +2 -1
  95. package/src/entities/data/__generated__/component-sets/progress-circle.mjs +2 -1
  96. package/src/entities/data/__generated__/component-sets/radio-mark.d.ts +34 -0
  97. package/src/entities/data/__generated__/component-sets/radio-mark.mjs +34 -0
  98. package/src/entities/data/__generated__/component-sets/radio.d.ts +2 -2
  99. package/src/entities/data/__generated__/component-sets/radio.mjs +2 -2
  100. package/src/entities/data/__generated__/component-sets/reaction-button.d.ts +6 -6
  101. package/src/entities/data/__generated__/component-sets/reaction-button.mjs +6 -6
  102. package/src/entities/data/__generated__/component-sets/resizable-child.d.ts +18 -0
  103. package/src/entities/data/__generated__/component-sets/resizable-child.mjs +18 -0
  104. package/src/entities/data/__generated__/component-sets/resizable-icon.d.ts +18 -0
  105. package/src/entities/data/__generated__/component-sets/resizable-icon.mjs +18 -0
  106. package/src/entities/data/__generated__/component-sets/select-box.d.ts +4 -4
  107. package/src/entities/data/__generated__/component-sets/select-box.mjs +4 -4
  108. package/src/entities/data/__generated__/component-sets/skeleton.d.ts +7 -0
  109. package/src/entities/data/__generated__/component-sets/skeleton.mjs +7 -0
  110. package/src/entities/data/__generated__/component-sets/snackbar.d.ts +4 -4
  111. package/src/entities/data/__generated__/component-sets/snackbar.mjs +4 -4
  112. package/src/entities/data/__generated__/component-sets/switch.d.ts +1 -1
  113. package/src/entities/data/__generated__/component-sets/switch.mjs +1 -1
  114. package/src/entities/data/__generated__/component-sets/tabs.d.ts +13 -0
  115. package/src/entities/data/__generated__/component-sets/tabs.mjs +13 -0
  116. package/src/entities/data/__generated__/component-sets/template-button-group.d.ts +9 -33
  117. package/src/entities/data/__generated__/component-sets/template-button-group.mjs +9 -33
  118. package/src/entities/data/__generated__/component-sets/template-chip-group.d.ts +15 -12
  119. package/src/entities/data/__generated__/component-sets/template-chip-group.mjs +15 -12
  120. package/src/entities/data/__generated__/component-sets/template-completion.d.ts +28 -0
  121. package/src/entities/data/__generated__/component-sets/template-completion.mjs +28 -0
  122. package/src/entities/data/__generated__/component-sets/{error-state.d.ts → template-error-state.d.ts} +5 -5
  123. package/src/entities/data/__generated__/component-sets/{error-state.mjs → template-error-state.mjs} +5 -5
  124. package/src/entities/data/__generated__/component-sets/template-top-navigation.d.ts +9 -7
  125. package/src/entities/data/__generated__/component-sets/template-top-navigation.mjs +9 -7
  126. package/src/entities/data/__generated__/component-sets/text-field.d.ts +35 -36
  127. package/src/entities/data/__generated__/component-sets/text-field.mjs +35 -36
  128. package/src/entities/data/__generated__/component-sets/toggle-button.d.ts +7 -7
  129. package/src/entities/data/__generated__/component-sets/toggle-button.mjs +7 -7
  130. package/src/entities/data/__generated__/component-sets/top-navigation.d.ts +42 -0
  131. package/src/entities/data/__generated__/component-sets/top-navigation.mjs +42 -0
  132. package/src/entities/data/styles.ts +94 -0
  133. package/src/entities/index.ts +5 -2
  134. package/src/normalizer/from-plugin.ts +104 -44
  135. package/src/normalizer/types.ts +3 -1
  136. package/src/utils/figma-gradient.ts +72 -0
  137. package/src/utils/figma-node.ts +4 -3
  138. package/src/codegen/targets/react/component/handlers/action-chip.ts +0 -72
  139. package/src/codegen/targets/react/component/handlers/action-sheet.ts +0 -82
  140. package/src/codegen/targets/react/component/handlers/chip-tabs.ts +0 -57
  141. package/src/codegen/targets/react/component/handlers/control-chip.ts +0 -81
  142. package/src/codegen/targets/react/component/handlers/extended-action-sheet.ts +0 -98
  143. package/src/codegen/targets/react/component/handlers/extended-fab.ts +0 -25
  144. package/src/codegen/targets/react/component/handlers/fab.ts +0 -22
  145. package/src/codegen/targets/react/component/handlers/text-button.ts +0 -49
  146. package/src/entities/data/__generated__/component-sets/action-chip.d.ts +0 -57
  147. package/src/entities/data/__generated__/component-sets/action-chip.mjs +0 -57
  148. package/src/entities/data/__generated__/component-sets/action-sheet.d.ts +0 -40
  149. package/src/entities/data/__generated__/component-sets/action-sheet.mjs +0 -40
  150. package/src/entities/data/__generated__/component-sets/chip-tablist.d.ts +0 -24
  151. package/src/entities/data/__generated__/component-sets/chip-tablist.mjs +0 -24
  152. package/src/entities/data/__generated__/component-sets/control-chip.d.ts +0 -60
  153. package/src/entities/data/__generated__/component-sets/control-chip.mjs +0 -60
  154. package/src/entities/data/__generated__/component-sets/identity-placeholder.d.ts +0 -13
  155. package/src/entities/data/__generated__/component-sets/identity-placeholder.mjs +0 -13
  156. package/src/entities/data/__generated__/component-sets/manner-temp-bar.d.ts +0 -23
  157. package/src/entities/data/__generated__/component-sets/manner-temp-bar.mjs +0 -23
  158. package/src/entities/data/__generated__/component-sets/standard-navigation.d.ts +0 -23
  159. package/src/entities/data/__generated__/component-sets/standard-navigation.mjs +0 -23
  160. package/src/entities/data/__generated__/component-sets/tablist.d.ts +0 -29
  161. package/src/entities/data/__generated__/component-sets/tablist.mjs +0 -29
  162. package/src/entities/data/__generated__/component-sets/template-bottom-fixed-bar.d.ts +0 -42
  163. package/src/entities/data/__generated__/component-sets/template-bottom-fixed-bar.mjs +0 -42
  164. package/src/entities/data/__generated__/component-sets/text-button.d.ts +0 -45
  165. package/src/entities/data/__generated__/component-sets/text-button.mjs +0 -45
@@ -9,6 +9,7 @@ import type {
9
9
  NormalizedVectorNode,
10
10
  NormalizedBooleanOperationNode,
11
11
  } from "./types";
12
+ import { convertTransformToGradientHandles } from "@/utils/figma-gradient";
12
13
 
13
14
  export function createPluginNormalizer() {
14
15
  async function normalizeNodes(nodes: readonly SceneNode[]): Promise<NormalizedSceneNode[]> {
@@ -55,7 +56,7 @@ export function createPluginNormalizer() {
55
56
  name: node.name,
56
57
  boundVariables: await normalizeBoundVariables(node),
57
58
  ...normalizeRadiusProps(node),
58
- ...normalizeAutolayoutProps(node),
59
+ ...(await normalizeAutolayoutProps(node)),
59
60
  children: await normalizeNodes(node.children),
60
61
  };
61
62
  }
@@ -99,7 +100,7 @@ export function createPluginNormalizer() {
99
100
  name: node.name,
100
101
  boundVariables: await normalizeBoundVariables(node),
101
102
  ...normalizeRadiusProps(node),
102
- ...normalizeShapeProps(node),
103
+ ...(await normalizeShapeProps(node)),
103
104
  };
104
105
  }
105
106
 
@@ -109,7 +110,7 @@ export function createPluginNormalizer() {
109
110
  id: node.id,
110
111
  name: node.name,
111
112
  boundVariables: await normalizeBoundVariables(node),
112
- ...normalizeShapeProps(node),
113
+ ...(await normalizeShapeProps(node)),
113
114
  };
114
115
  }
115
116
 
@@ -122,7 +123,7 @@ export function createPluginNormalizer() {
122
123
  name: node.name,
123
124
  boundVariables: await normalizeBoundVariables(node),
124
125
  children: await normalizeNodes(node.children),
125
- ...normalizeShapeProps(node),
126
+ ...(await normalizeShapeProps(node)),
126
127
  };
127
128
  }
128
129
  async function normalizeTextNode(node: TextNode): Promise<NormalizedTextNode> {
@@ -136,6 +137,7 @@ export function createPluginNormalizer() {
136
137
  "textStyleId",
137
138
  "fills",
138
139
  "boundVariables",
140
+ "textDecoration",
139
141
  ]);
140
142
  const first = segments[0]!;
141
143
 
@@ -175,7 +177,7 @@ export function createPluginNormalizer() {
175
177
  lineHeightPx: segment.lineHeight.unit === "PIXELS" ? segment.lineHeight.value : undefined,
176
178
  },
177
179
  })),
178
- ...normalizeShapeProps(node),
180
+ ...(await normalizeShapeProps(node)),
179
181
  };
180
182
  }
181
183
 
@@ -186,7 +188,7 @@ export function createPluginNormalizer() {
186
188
  name: node.name,
187
189
  boundVariables: await normalizeBoundVariables(node),
188
190
  ...normalizeRadiusProps(node),
189
- ...normalizeAutolayoutProps(node),
191
+ ...(await normalizeAutolayoutProps(node)),
190
192
  children: await normalizeNodes(node.children),
191
193
  };
192
194
  }
@@ -219,7 +221,7 @@ export function createPluginNormalizer() {
219
221
  name: node.name,
220
222
  boundVariables: await normalizeBoundVariables(node),
221
223
  ...normalizeRadiusProps(node),
222
- ...normalizeAutolayoutProps(node),
224
+ ...(await normalizeAutolayoutProps(node)),
223
225
  children: await normalizeNodes(node.children),
224
226
  componentKey: mainComponent.key,
225
227
  componentSetKey:
@@ -244,29 +246,44 @@ export function createPluginNormalizer() {
244
246
  }
245
247
 
246
248
  function normalizePaint(paint: Paint): FigmaRestSpec.Paint {
247
- if (paint.type === "SOLID") {
248
- return normalizeSolidPaint(paint);
249
+ switch (paint.type) {
250
+ case "SOLID":
251
+ return normalizeSolidPaint(paint);
252
+ case "IMAGE":
253
+ return {
254
+ type: "IMAGE",
255
+ scaleMode: paint.scaleMode === "CROP" ? "STRETCH" : paint.scaleMode,
256
+ imageTransform: paint.imageTransform,
257
+ scalingFactor: paint.scalingFactor,
258
+ filters: paint.filters,
259
+ rotation: paint.rotation,
260
+ imageRef: paint.imageHash ?? "",
261
+ blendMode: paint.blendMode ?? "NORMAL",
262
+ visible: paint.visible,
263
+ opacity: paint.opacity,
264
+ };
265
+ case "GRADIENT_LINEAR":
266
+ case "GRADIENT_RADIAL":
267
+ case "GRADIENT_ANGULAR":
268
+ case "GRADIENT_DIAMOND":
269
+ return {
270
+ type: paint.type,
271
+ gradientStops: [...paint.gradientStops],
272
+ visible: paint.visible,
273
+ opacity: paint.opacity,
274
+ blendMode: paint.blendMode ?? "NORMAL",
275
+ gradientHandlePositions: convertTransformToGradientHandles(paint.gradientTransform),
276
+ };
277
+ default:
278
+ throw new Error(`Unimplemented paint type: ${paint.type}`);
249
279
  }
250
- if (paint.type === "IMAGE") {
251
- return {
252
- type: "IMAGE",
253
- scaleMode: paint.scaleMode === "CROP" ? "STRETCH" : paint.scaleMode,
254
- imageTransform: paint.imageTransform,
255
- scalingFactor: paint.scalingFactor,
256
- filters: paint.filters,
257
- rotation: paint.rotation,
258
- imageRef: paint.imageHash ?? "",
259
- blendMode: paint.blendMode ?? "NORMAL",
260
- visible: paint.visible,
261
- opacity: paint.opacity,
262
- };
263
- }
264
- throw new Error(`Unimplemented paint type: ${paint.type}`);
265
280
  }
266
281
 
267
282
  function normalizePaints(fills: readonly Paint[] | PluginAPI["mixed"]): FigmaRestSpec.Paint[] {
268
283
  if (fills === figma.mixed) {
269
- throw new Error("Mixed fills are not supported");
284
+ console.warn("Mixed fills are not supported");
285
+
286
+ return [];
270
287
  }
271
288
 
272
289
  return fills.map(normalizePaint);
@@ -289,10 +306,11 @@ export function createPluginNormalizer() {
289
306
  };
290
307
  }
291
308
 
292
- function normalizeShapeProps(
309
+ async function normalizeShapeProps(
293
310
  node: Pick<
294
311
  RectangleNode,
295
312
  | "fills"
313
+ | "fillStyleId"
296
314
  | "strokes"
297
315
  | "strokeWeight"
298
316
  | "layoutGrow"
@@ -308,6 +326,11 @@ export function createPluginNormalizer() {
308
326
  > &
309
327
  Partial<Pick<FrameNode, "inferredAutoLayout">>,
310
328
  ) {
329
+ const fillStyleKey =
330
+ typeof node.fillStyleId === "string"
331
+ ? (await figma.getStyleByIdAsync(node.fillStyleId))?.key
332
+ : undefined;
333
+
311
334
  return {
312
335
  layoutGrow: (node.inferredAutoLayout?.layoutGrow ?? node.layoutGrow) as 0 | 1 | undefined,
313
336
  layoutAlign: node.inferredAutoLayout?.layoutAlign ?? node.layoutAlign,
@@ -316,6 +339,7 @@ export function createPluginNormalizer() {
316
339
  absoluteBoundingBox: node.absoluteBoundingBox,
317
340
  relativeTransform: node.relativeTransform,
318
341
  fills: normalizePaints(node.fills),
342
+ ...(fillStyleKey ? { fillStyleKey } : {}),
319
343
  strokes: normalizePaints(node.strokes),
320
344
  strokeWeight: node.strokeWeight === figma.mixed ? undefined : node.strokeWeight,
321
345
  minHeight: node.minHeight ?? undefined,
@@ -325,9 +349,9 @@ export function createPluginNormalizer() {
325
349
  };
326
350
  }
327
351
 
328
- function normalizeAutolayoutProps(node: Omit<FrameNode, "type" | "clone">) {
352
+ async function normalizeAutolayoutProps(node: Omit<FrameNode, "type" | "clone">) {
329
353
  return {
330
- ...normalizeShapeProps(node),
354
+ ...(await normalizeShapeProps(node)),
331
355
  layoutMode: node.inferredAutoLayout?.layoutMode ?? node.layoutMode,
332
356
  layoutWrap: node.inferredAutoLayout?.layoutWrap ?? node.layoutWrap,
333
357
  paddingLeft: node.inferredAutoLayout?.paddingLeft ?? node.paddingLeft,
@@ -348,23 +372,59 @@ export function createPluginNormalizer() {
348
372
  };
349
373
  }
350
374
 
351
- async function normalizeBoundVariables(node: Pick<FrameNode, "boundVariables">) {
375
+ async function normalizeBoundVariables({
376
+ boundVariables,
377
+ }: Pick<FrameNode, "boundVariables">): Promise<FigmaRestSpec.IsLayerTrait["boundVariables"]> {
378
+ if (!boundVariables) return undefined;
379
+
380
+ const { width, height, componentProperties: _componentProperties, ...rest } = boundVariables;
381
+
382
+ // replace VariableAlias' id with the actual variable key
383
+ const resolveVariableId = async (variable: VariableAlias): Promise<VariableAlias> => ({
384
+ ...variable,
385
+ id: (await figma.variables.getVariableByIdAsync(variable.id))?.key ?? variable.id,
386
+ });
387
+
388
+ const needsResolution = [
389
+ "fills",
390
+ "itemSpacing",
391
+ "counterAxisSpacing",
392
+ "bottomLeftRadius",
393
+ "bottomRightRadius",
394
+ "topLeftRadius",
395
+ "topRightRadius",
396
+ "paddingBottom",
397
+ "paddingLeft",
398
+ "paddingRight",
399
+ "paddingTop",
400
+ "maxHeight",
401
+ "minHeight",
402
+ "maxWidth",
403
+ "minWidth",
404
+ ];
405
+
406
+ // Process all properties in parallel
407
+ const resolvedEntries = await Promise.all(
408
+ Object.entries(rest).map(async ([key, value]) => {
409
+ if (!value || !needsResolution.includes(key)) return [key, value];
410
+
411
+ if (Array.isArray(value)) {
412
+ return [key, await Promise.all(value.map(resolveVariableId))];
413
+ }
414
+
415
+ return [key, await resolveVariableId(value)];
416
+ }),
417
+ );
418
+
352
419
  return {
353
- ...node.boundVariables,
354
- fills: await Promise.all(
355
- node.boundVariables?.fills?.map((fill) =>
356
- figma.variables.getVariableByIdAsync(fill.id).then((res) => {
357
- return {
358
- ...fill,
359
- id: res?.key ?? fill.id,
360
- };
361
- }),
362
- ) ?? [],
363
- ),
364
- size: {
365
- x: node.boundVariables?.width,
366
- y: node.boundVariables?.height,
367
- },
420
+ ...Object.fromEntries(resolvedEntries),
421
+ ...(width &&
422
+ height && {
423
+ size: {
424
+ x: width,
425
+ y: height,
426
+ },
427
+ }),
368
428
  };
369
429
  }
370
430
 
@@ -32,7 +32,9 @@ export type NormalizedHasLayoutTrait = Pick<
32
32
  export type NormalizedHasGeometryTrait = Pick<
33
33
  FigmaRestSpec.HasGeometryTrait,
34
34
  "fills" | "strokes" | "strokeWeight" | "styles"
35
- >;
35
+ > & {
36
+ fillStyleKey?: string;
37
+ };
36
38
 
37
39
  export type NormalizedHasFramePropertiesTrait = Pick<
38
40
  FigmaRestSpec.HasFramePropertiesTrait,
@@ -0,0 +1,72 @@
1
+ /**
2
+ * @see https://gist.github.com/yagudaev/0c2b89674c6aee8b38cd379752ef58d0
3
+ */
4
+
5
+ const identityMatrixHandlePositions = [
6
+ [0, 1, 0],
7
+ [0.5, 0.5, 1],
8
+ [1, 1, 1],
9
+ ];
10
+
11
+ /**
12
+ * Inverts a 2x3 affine transformation matrix
13
+ * For a 2x3 matrix [[a, b, c], [d, e, f]], we treat it as a 3x3 matrix with [0, 0, 1] as the third row
14
+ */
15
+ function inv(matrix: number[][]): number[][] {
16
+ const [[a, b, c], [d, e, f]] = matrix;
17
+
18
+ // Calculate determinant of the 2x2 linear part
19
+ const det = a * e - b * d;
20
+
21
+ if (Math.abs(det) < 1e-10) {
22
+ throw new Error("Matrix is not invertible");
23
+ }
24
+
25
+ // Invert the 2x2 linear part
26
+ const invDet = 1 / det;
27
+ const a_inv = e * invDet;
28
+ const b_inv = -b * invDet;
29
+ const d_inv = -d * invDet;
30
+ const e_inv = a * invDet;
31
+
32
+ // Calculate the inverted translation
33
+ const c_inv = -(a_inv * c + b_inv * f);
34
+ const f_inv = -(d_inv * c + e_inv * f);
35
+
36
+ return [
37
+ [a_inv, b_inv, c_inv],
38
+ [d_inv, e_inv, f_inv]
39
+ ];
40
+ }
41
+
42
+ /**
43
+ * Multiplies a 2x3 matrix with a 3x3 matrix
44
+ * Result is a 2x3 matrix
45
+ */
46
+ function multiply(matrix1: number[][], matrix2: number[][]): number[][] {
47
+ const [[a, b, c], [d, e, f]] = matrix1;
48
+ const result: number[][] = [[], []];
49
+
50
+ // For each column in matrix2
51
+ for (let col = 0; col < matrix2[0].length; col++) {
52
+ // First row of result
53
+ result[0][col] = a * matrix2[0][col] + b * matrix2[1][col] + c * matrix2[2][col];
54
+ // Second row of result
55
+ result[1][col] = d * matrix2[0][col] + e * matrix2[1][col] + f * matrix2[2][col];
56
+ }
57
+
58
+ return result;
59
+ }
60
+
61
+ export function convertTransformToGradientHandles(transform: number[][]) {
62
+ const inverseTransform = inv(transform);
63
+
64
+ // point matrix
65
+ const mp = multiply(inverseTransform, identityMatrixHandlePositions);
66
+
67
+ return [
68
+ { x: mp[0][0], y: mp[1][0] },
69
+ { x: mp[0][1], y: mp[1][1] },
70
+ { x: mp[0][2], y: mp[1][2] },
71
+ ];
72
+ }
@@ -5,14 +5,15 @@ import type {
5
5
  NormalizedIsLayerTrait,
6
6
  NormalizedSceneNode,
7
7
  } from "../normalizer";
8
+
8
9
  export function traverseNode(
9
10
  node: NormalizedSceneNode,
10
11
  callback: (node: NormalizedSceneNode) => void,
11
12
  ) {
13
+ callback(node);
14
+
12
15
  if ("children" in node) {
13
16
  node.children.forEach((child) => traverseNode(child, callback));
14
- } else {
15
- callback(node);
16
17
  }
17
18
  }
18
19
 
@@ -50,7 +51,7 @@ export function findAllInstances<T>({ node, key }: { node: NormalizedSceneNode;
50
51
  return findAll(
51
52
  node,
52
53
  (n) => n.type === "INSTANCE" && (n.componentKey === key || n.componentSetKey === key),
53
- ) as (NormalizedInstanceNode & { componentProperties: T })[];
54
+ ) as (Omit<NormalizedInstanceNode, "componentProperties"> & { componentProperties: T })[];
54
55
  }
55
56
 
56
57
  export function getFirstSolidFill(node: NormalizedHasGeometryTrait) {
@@ -1,72 +0,0 @@
1
- import type { ActionChipProperties } from "@/codegen/component-properties";
2
- import { defineComponentHandler } from "@/codegen/core";
3
- import * as metadata from "@/entities/data/__generated__/component-sets";
4
- import { match } from "ts-pattern";
5
- import { createSeedReactElement } from "../../element-factories";
6
- import type { ComponentHandlerDeps } from "../deps.interface";
7
- import { handleSizeProp } from "../size";
8
-
9
- export const createActionChipHandler = (ctx: ComponentHandlerDeps) =>
10
- defineComponentHandler<ActionChipProperties>(
11
- metadata.actionChip.key,
12
- ({ componentProperties: props }) => {
13
- const states = props.State.value.split("-");
14
-
15
- const { layout, children } = match(props.Layout.value)
16
- .with("Icon Only", () => ({
17
- layout: "iconOnly",
18
- children: [
19
- createSeedReactElement("Icon", {
20
- svg: ctx.iconHandler.transform(props["Icon#8714:0"]),
21
- }),
22
- ],
23
- }))
24
- .with("Icon First", () => ({
25
- layout: "withText",
26
- children: [
27
- createSeedReactElement("PrefixIcon", {
28
- svg: ctx.iconHandler.transform(props["Prefix Icon#8711:0"]),
29
- }),
30
- props["Label#7185:0"].value,
31
- ],
32
- }))
33
- .with("Icon Last", () => ({
34
- layout: "withText",
35
- children: [
36
- props["Label#7185:0"].value,
37
- createSeedReactElement("SuffixIcon", {
38
- svg: ctx.iconHandler.transform(props["Suffix Icon#8711:3"]),
39
- }),
40
- ],
41
- }))
42
- .with("Icon Both", () => ({
43
- layout: "withText",
44
- children: [
45
- createSeedReactElement("PrefixIcon", {
46
- svg: ctx.iconHandler.transform(props["Prefix Icon#8711:0"]),
47
- }),
48
- props["Label#7185:0"].value,
49
- createSeedReactElement("SuffixIcon", {
50
- svg: ctx.iconHandler.transform(props["Suffix Icon#8711:3"]),
51
- }),
52
- ],
53
- }))
54
- .with("Text Only", () => ({
55
- layout: "withText",
56
- children: props["Label#7185:0"].value,
57
- }))
58
- .exhaustive();
59
-
60
- const commonProps = {
61
- size: handleSizeProp(props.Size.value),
62
- layout,
63
- ...(states.includes("Disabled") && {
64
- disabled: true,
65
- }),
66
- ...(props["Show Count#7185:42"].value && {
67
- count: Number(props["Count#7185:21"].value),
68
- }),
69
- };
70
- return createSeedReactElement("ActionChip", commonProps, children);
71
- },
72
- );
@@ -1,82 +0,0 @@
1
- import type {
2
- ActionSheetItemProperties,
3
- ActionSheetProperties,
4
- } from "@/codegen/component-properties";
5
- import { createElement, defineComponentHandler } from "@/codegen/core";
6
- import * as metadata from "@/entities/data/__generated__/component-sets";
7
- import { findAllInstances } from "@/utils/figma-node";
8
- import { camelCase } from "change-case";
9
- import { match } from "ts-pattern";
10
- import { createLocalSnippetHelper } from "../../element-factories";
11
- import type { ComponentHandlerDeps } from "../deps.interface";
12
-
13
- const { createLocalSnippetElement } = createLocalSnippetHelper("action-sheet");
14
-
15
- const ACTION_SHEET_ITEM_KEY = "c3cafd3a3fdcd45fecb6971019d88eaf39a2e381";
16
- const createActionSheetItemHandler = (_ctx: ComponentHandlerDeps) =>
17
- defineComponentHandler<ActionSheetItemProperties>(
18
- ACTION_SHEET_ITEM_KEY,
19
- ({ componentProperties: props }) => {
20
- const states = props.State.value.split("-");
21
-
22
- const commonProps = {
23
- label: props["Label#15420:4"].value,
24
- tone: camelCase(props.Tone.value),
25
- ...(states.includes("Disabled") && {
26
- disabled: true,
27
- }),
28
- };
29
-
30
- return createLocalSnippetElement("ActionSheetItem", commonProps);
31
- },
32
- );
33
-
34
- export const createActionSheetHandler = (ctx: ComponentHandlerDeps) => {
35
- const actionSheetItemHandler = createActionSheetItemHandler(ctx);
36
-
37
- return defineComponentHandler<ActionSheetProperties>(metadata.actionSheet.key, (node) => {
38
- const { componentProperties: props } = node;
39
-
40
- const contentProps = match(props.Header.value)
41
- .with("None", () => ({
42
- title: undefined,
43
- description: undefined,
44
- }))
45
- .with("Title Only", () => ({
46
- title: props["Title#15641:37"].value,
47
- description: undefined,
48
- }))
49
- .with("Description Only", () => ({
50
- title: undefined,
51
- description: props["Description#15641:70"].value,
52
- }))
53
- .with("Title With Description", () => ({
54
- title: props["Title#15641:37"].value,
55
- description: props["Description#15641:70"].value,
56
- }))
57
- .exhaustive();
58
-
59
- const items = findAllInstances<ActionSheetItemProperties>({
60
- node,
61
- key: actionSheetItemHandler.key,
62
- });
63
-
64
- const contentChildren = items.map(actionSheetItemHandler.transform);
65
-
66
- const content = createLocalSnippetElement("ActionSheetContent", contentProps, contentChildren, {
67
- comment: contentProps.title
68
- ? undefined
69
- : "title을 제공하지 않는 경우 aria-label이나 aria-labelledby 중 하나를 제공해야 합니다.",
70
- });
71
-
72
- const trigger = createLocalSnippetElement(
73
- "ActionSheetTrigger",
74
- { asChild: true },
75
- createElement("button", undefined, "열기", {
76
- comment: "ActionSheet을 여는 요소를 제공해주세요.",
77
- }),
78
- );
79
-
80
- return createLocalSnippetElement("ActionSheet", undefined, [trigger, content]);
81
- });
82
- };
@@ -1,57 +0,0 @@
1
- import type { ChipTabsItemProperties, ChipTabsProperties } from "@/codegen/component-properties";
2
- import { defineComponentHandler } from "@/codegen/core";
3
- import * as metadata from "@/entities/data/__generated__/component-sets";
4
- import { findAllInstances } from "@/utils/figma-node";
5
- import { camelCase } from "change-case";
6
- import { createLocalSnippetHelper } from "../../element-factories";
7
- import type { ComponentHandlerDeps } from "../deps.interface";
8
-
9
- const { createLocalSnippetElement } = createLocalSnippetHelper("chip-tabs");
10
-
11
- const CHIP_TABS_ITEM_KEY = "fa80168b02051fbb0ba032238bd76d840dbe2e15";
12
- const createChipTabsItemHandler = (_ctx: ComponentHandlerDeps) =>
13
- defineComponentHandler<ChipTabsItemProperties>(
14
- CHIP_TABS_ITEM_KEY,
15
- ({ componentProperties: props }) => {
16
- const states = props.State.value.split("-");
17
-
18
- const commonProps = {
19
- value: props["Label#8876:0"].value,
20
- ...(states.includes("Disabled") && {
21
- disabled: true,
22
- }),
23
- };
24
-
25
- return createLocalSnippetElement("ChipTabsTrigger", commonProps, props["Label#8876:0"].value);
26
- },
27
- );
28
-
29
- export const createChipTabsHandler = (ctx: ComponentHandlerDeps) => {
30
- const chipTabsItemHandler = createChipTabsItemHandler(ctx);
31
-
32
- return defineComponentHandler<ChipTabsProperties>(metadata.chipTablist.key, (node) => {
33
- const chipTabsItems = findAllInstances<ChipTabsItemProperties>({
34
- node,
35
- key: chipTabsItemHandler.key,
36
- });
37
-
38
- const selectedChipTabsItem = chipTabsItems.find((chipTabsItem) =>
39
- chipTabsItem.componentProperties.State.value.split("-").includes("Selected"),
40
- );
41
-
42
- const chipTabsList = createLocalSnippetElement(
43
- "ChipTabsList",
44
- undefined,
45
- chipTabsItems.map(chipTabsItemHandler.transform),
46
- );
47
-
48
- const commonProps = {
49
- variant: camelCase(node.componentProperties.Variant.value),
50
- ...(selectedChipTabsItem && {
51
- defaultValue: selectedChipTabsItem.componentProperties["Label#8876:0"].value,
52
- }),
53
- };
54
-
55
- return createLocalSnippetElement("ChipTabs", commonProps, chipTabsList);
56
- });
57
- };
@@ -1,81 +0,0 @@
1
- import type { ControlChipProperties } from "@/codegen/component-properties";
2
- import { defineComponentHandler } from "@/codegen/core";
3
- import * as metadata from "@/entities/data/__generated__/component-sets";
4
- import { match } from "ts-pattern";
5
- import { createLocalSnippetHelper, createSeedReactElement } from "../../element-factories";
6
- import type { ComponentHandlerDeps } from "../deps.interface";
7
- import { handleSizeProp } from "../size";
8
-
9
- const { createLocalSnippetElement } = createLocalSnippetHelper("control-chip");
10
-
11
- export const createControlChipHandler = (ctx: ComponentHandlerDeps) =>
12
- defineComponentHandler<ControlChipProperties>(
13
- metadata.controlChip.key,
14
- ({ componentProperties: props }) => {
15
- const states = props.State.value.split("-");
16
-
17
- const count = props["Show Count#7185:42"].value ? props["Count#7185:21"].value : undefined;
18
-
19
- const { layout, children } = match(props.Layout.value)
20
- .with("Icon Only", () => ({
21
- layout: "iconOnly",
22
- children: [
23
- createSeedReactElement("Icon", {
24
- svg: ctx.iconHandler.transform(props["Icon#8722:41"]),
25
- }),
26
- ],
27
- }))
28
- .with("Icon First", () => ({
29
- layout: "withText",
30
- children: [
31
- createSeedReactElement("PrefixIcon", {
32
- svg: ctx.iconHandler.transform(props["Prefix Icon#8722:0"]),
33
- }),
34
- props["Label#7185:0"].value,
35
- count ? createSeedReactElement("Count", undefined, [count]) : undefined,
36
- ],
37
- }))
38
- .with("Icon Last", () => ({
39
- layout: "withText",
40
- children: [
41
- props["Label#7185:0"].value,
42
- createSeedReactElement("SuffixIcon", {
43
- svg: ctx.iconHandler.transform(props["Suffix Icon#8722:82"]),
44
- }),
45
- ],
46
- }))
47
- .with("Icon Both", () => ({
48
- layout: "withText",
49
- children: [
50
- createSeedReactElement("PrefixIcon", {
51
- svg: ctx.iconHandler.transform(props["Prefix Icon#8722:0"]),
52
- }),
53
- props["Label#7185:0"].value,
54
- createSeedReactElement("SuffixIcon", {
55
- svg: ctx.iconHandler.transform(props["Suffix Icon#8722:82"]),
56
- }),
57
- ],
58
- }))
59
- .with("Text Only", () => ({
60
- layout: "withText",
61
- children: props["Label#7185:0"].value,
62
- }))
63
- .exhaustive();
64
-
65
- const commonProps = {
66
- size: handleSizeProp(props.Size.value),
67
- layout,
68
- ...(states.includes("Selected") && {
69
- defaultChecked: true,
70
- }),
71
- ...(states.includes("Disabled") && {
72
- disabled: true,
73
- }),
74
- ...(props["Show Count#7185:42"].value && {
75
- count: Number(props["Count#7185:21"].value),
76
- }),
77
- };
78
-
79
- return createLocalSnippetElement("ControlChip.Toggle", commonProps, children);
80
- },
81
- );