@seed-design/figma 0.1.7 → 0.1.9

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 (166) hide show
  1. package/lib/codegen/index.cjs +773 -660
  2. package/lib/codegen/index.d.ts +725 -625
  3. package/lib/codegen/index.d.ts.map +1 -1
  4. package/lib/codegen/index.js +773 -660
  5. package/lib/codegen/targets/react/index.cjs +1242 -1226
  6. package/lib/codegen/targets/react/index.d.ts +22 -11
  7. package/lib/codegen/targets/react/index.d.ts.map +1 -1
  8. package/lib/codegen/targets/react/index.js +1242 -1226
  9. package/lib/index.cjs +899 -671
  10. package/lib/index.d.ts +3 -1
  11. package/lib/index.d.ts.map +1 -1
  12. package/lib/index.js +899 -671
  13. package/package.json +3 -3
  14. package/src/codegen/component-properties.ts +289 -144
  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 +88 -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 +7 -4
  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 +11 -8
  41. package/src/codegen/targets/react/component/handlers/snackbar.ts +2 -2
  42. package/src/codegen/targets/react/component/handlers/switch.ts +2 -7
  43. package/src/codegen/targets/react/component/handlers/tabs.ts +8 -125
  44. package/src/codegen/targets/react/component/index.ts +22 -19
  45. package/src/codegen/targets/react/instance.ts +1 -1
  46. package/src/codegen/targets/react/pipeline.ts +4 -2
  47. package/src/codegen/targets/react/props.ts +16 -9
  48. package/src/codegen/targets/react/value-resolver.ts +38 -3
  49. package/src/entities/data/__generated__/component-sets/action-button.d.ts +5 -5
  50. package/src/entities/data/__generated__/component-sets/action-button.mjs +5 -5
  51. package/src/entities/data/__generated__/component-sets/alert-dialog.d.ts +26 -0
  52. package/src/entities/data/__generated__/component-sets/alert-dialog.mjs +26 -0
  53. package/src/entities/data/__generated__/component-sets/avatar-stack.d.ts +5 -18
  54. package/src/entities/data/__generated__/component-sets/avatar-stack.mjs +5 -18
  55. package/src/entities/data/__generated__/component-sets/avatar.d.ts +11 -5
  56. package/src/entities/data/__generated__/component-sets/avatar.mjs +11 -5
  57. package/src/entities/data/__generated__/component-sets/badge.d.ts +0 -7
  58. package/src/entities/data/__generated__/component-sets/badge.mjs +0 -7
  59. package/src/entities/data/__generated__/component-sets/bottom-navigation-global.d.ts +3 -0
  60. package/src/entities/data/__generated__/component-sets/bottom-navigation-global.mjs +3 -0
  61. package/src/entities/data/__generated__/component-sets/bottom-navigation-kr.d.ts +3 -0
  62. package/src/entities/data/__generated__/component-sets/bottom-navigation-kr.mjs +3 -0
  63. package/src/entities/data/__generated__/component-sets/bottom-sheet.d.ts +53 -4
  64. package/src/entities/data/__generated__/component-sets/bottom-sheet.mjs +53 -4
  65. package/src/entities/data/__generated__/component-sets/callout.d.ts +13 -22
  66. package/src/entities/data/__generated__/component-sets/callout.mjs +13 -22
  67. package/src/entities/data/__generated__/component-sets/checkbox.d.ts +3 -3
  68. package/src/entities/data/__generated__/component-sets/checkbox.mjs +3 -3
  69. package/src/entities/data/__generated__/component-sets/checkmark.d.ts +34 -0
  70. package/src/entities/data/__generated__/component-sets/checkmark.mjs +34 -0
  71. package/src/entities/data/__generated__/component-sets/chip.d.ts +90 -0
  72. package/src/entities/data/__generated__/component-sets/chip.mjs +90 -0
  73. package/src/entities/data/__generated__/component-sets/chlid.d.ts +14 -0
  74. package/src/entities/data/__generated__/component-sets/chlid.mjs +14 -0
  75. package/src/entities/data/__generated__/component-sets/{extended-floating-action-button.d.ts → contextual-floating-button.d.ts} +12 -10
  76. package/src/entities/data/__generated__/component-sets/{extended-floating-action-button.mjs → contextual-floating-button.mjs} +12 -10
  77. package/src/entities/data/__generated__/component-sets/floating-action-button.d.ts +4 -8
  78. package/src/entities/data/__generated__/component-sets/floating-action-button.mjs +4 -8
  79. package/src/entities/data/__generated__/component-sets/help-bubble.d.ts +3 -10
  80. package/src/entities/data/__generated__/component-sets/help-bubble.mjs +3 -10
  81. package/src/entities/data/__generated__/component-sets/index.d.ts +13 -13
  82. package/src/entities/data/__generated__/component-sets/index.mjs +13 -13
  83. package/src/entities/data/__generated__/component-sets/inline-banner.d.ts +3 -3
  84. package/src/entities/data/__generated__/component-sets/inline-banner.mjs +3 -3
  85. package/src/entities/data/__generated__/component-sets/main-tab-navigation-global.d.ts +2 -2
  86. package/src/entities/data/__generated__/component-sets/main-tab-navigation-global.mjs +2 -2
  87. package/src/entities/data/__generated__/component-sets/manner-temp-badge.d.ts +2 -2
  88. package/src/entities/data/__generated__/component-sets/manner-temp-badge.mjs +2 -2
  89. package/src/entities/data/__generated__/component-sets/manner-temp.d.ts +2 -2
  90. package/src/entities/data/__generated__/component-sets/manner-temp.mjs +2 -2
  91. package/src/entities/data/__generated__/component-sets/{extended-action-sheet.d.ts → menu-sheet.d.ts} +17 -13
  92. package/src/entities/data/__generated__/component-sets/{extended-action-sheet.mjs → menu-sheet.mjs} +17 -13
  93. package/src/entities/data/__generated__/component-sets/multiline-text-field.d.ts +18 -18
  94. package/src/entities/data/__generated__/component-sets/multiline-text-field.mjs +18 -18
  95. package/src/entities/data/__generated__/component-sets/progress-circle.d.ts +2 -1
  96. package/src/entities/data/__generated__/component-sets/progress-circle.mjs +2 -1
  97. package/src/entities/data/__generated__/component-sets/radio-mark.d.ts +34 -0
  98. package/src/entities/data/__generated__/component-sets/radio-mark.mjs +34 -0
  99. package/src/entities/data/__generated__/component-sets/radio.d.ts +2 -2
  100. package/src/entities/data/__generated__/component-sets/radio.mjs +2 -2
  101. package/src/entities/data/__generated__/component-sets/reaction-button.d.ts +6 -6
  102. package/src/entities/data/__generated__/component-sets/reaction-button.mjs +6 -6
  103. package/src/entities/data/__generated__/component-sets/resizable-child.d.ts +18 -0
  104. package/src/entities/data/__generated__/component-sets/resizable-child.mjs +18 -0
  105. package/src/entities/data/__generated__/component-sets/resizable-icon.d.ts +18 -0
  106. package/src/entities/data/__generated__/component-sets/resizable-icon.mjs +18 -0
  107. package/src/entities/data/__generated__/component-sets/select-box.d.ts +4 -4
  108. package/src/entities/data/__generated__/component-sets/select-box.mjs +4 -4
  109. package/src/entities/data/__generated__/component-sets/skeleton.d.ts +7 -0
  110. package/src/entities/data/__generated__/component-sets/skeleton.mjs +7 -0
  111. package/src/entities/data/__generated__/component-sets/snackbar.d.ts +4 -4
  112. package/src/entities/data/__generated__/component-sets/snackbar.mjs +4 -4
  113. package/src/entities/data/__generated__/component-sets/switch.d.ts +15 -6
  114. package/src/entities/data/__generated__/component-sets/switch.mjs +15 -6
  115. package/src/entities/data/__generated__/component-sets/tabs.d.ts +13 -0
  116. package/src/entities/data/__generated__/component-sets/tabs.mjs +13 -0
  117. package/src/entities/data/__generated__/component-sets/template-button-group.d.ts +9 -33
  118. package/src/entities/data/__generated__/component-sets/template-button-group.mjs +9 -33
  119. package/src/entities/data/__generated__/component-sets/template-chip-group.d.ts +15 -12
  120. package/src/entities/data/__generated__/component-sets/template-chip-group.mjs +15 -12
  121. package/src/entities/data/__generated__/component-sets/template-completion.d.ts +28 -0
  122. package/src/entities/data/__generated__/component-sets/template-completion.mjs +28 -0
  123. package/src/entities/data/__generated__/component-sets/{error-state.d.ts → template-error-state.d.ts} +5 -5
  124. package/src/entities/data/__generated__/component-sets/{error-state.mjs → template-error-state.mjs} +5 -5
  125. package/src/entities/data/__generated__/component-sets/template-top-navigation.d.ts +9 -7
  126. package/src/entities/data/__generated__/component-sets/template-top-navigation.mjs +9 -7
  127. package/src/entities/data/__generated__/component-sets/text-field.d.ts +35 -36
  128. package/src/entities/data/__generated__/component-sets/text-field.mjs +35 -36
  129. package/src/entities/data/__generated__/component-sets/toggle-button.d.ts +7 -7
  130. package/src/entities/data/__generated__/component-sets/toggle-button.mjs +7 -7
  131. package/src/entities/data/__generated__/component-sets/top-navigation.d.ts +42 -0
  132. package/src/entities/data/__generated__/component-sets/top-navigation.mjs +42 -0
  133. package/src/entities/data/styles.ts +94 -0
  134. package/src/entities/index.ts +5 -2
  135. package/src/normalizer/from-plugin.ts +104 -44
  136. package/src/normalizer/types.ts +3 -1
  137. package/src/utils/figma-gradient.ts +72 -0
  138. package/src/utils/figma-node.ts +4 -3
  139. package/src/codegen/targets/react/component/handlers/action-chip.ts +0 -72
  140. package/src/codegen/targets/react/component/handlers/action-sheet.ts +0 -82
  141. package/src/codegen/targets/react/component/handlers/chip-tabs.ts +0 -57
  142. package/src/codegen/targets/react/component/handlers/control-chip.ts +0 -81
  143. package/src/codegen/targets/react/component/handlers/extended-action-sheet.ts +0 -98
  144. package/src/codegen/targets/react/component/handlers/extended-fab.ts +0 -25
  145. package/src/codegen/targets/react/component/handlers/fab.ts +0 -22
  146. package/src/codegen/targets/react/component/handlers/text-button.ts +0 -49
  147. package/src/entities/data/__generated__/component-sets/action-chip.d.ts +0 -57
  148. package/src/entities/data/__generated__/component-sets/action-chip.mjs +0 -57
  149. package/src/entities/data/__generated__/component-sets/action-sheet.d.ts +0 -40
  150. package/src/entities/data/__generated__/component-sets/action-sheet.mjs +0 -40
  151. package/src/entities/data/__generated__/component-sets/chip-tablist.d.ts +0 -24
  152. package/src/entities/data/__generated__/component-sets/chip-tablist.mjs +0 -24
  153. package/src/entities/data/__generated__/component-sets/control-chip.d.ts +0 -60
  154. package/src/entities/data/__generated__/component-sets/control-chip.mjs +0 -60
  155. package/src/entities/data/__generated__/component-sets/identity-placeholder.d.ts +0 -13
  156. package/src/entities/data/__generated__/component-sets/identity-placeholder.mjs +0 -13
  157. package/src/entities/data/__generated__/component-sets/manner-temp-bar.d.ts +0 -23
  158. package/src/entities/data/__generated__/component-sets/manner-temp-bar.mjs +0 -23
  159. package/src/entities/data/__generated__/component-sets/standard-navigation.d.ts +0 -23
  160. package/src/entities/data/__generated__/component-sets/standard-navigation.mjs +0 -23
  161. package/src/entities/data/__generated__/component-sets/tablist.d.ts +0 -29
  162. package/src/entities/data/__generated__/component-sets/tablist.mjs +0 -29
  163. package/src/entities/data/__generated__/component-sets/template-bottom-fixed-bar.d.ts +0 -42
  164. package/src/entities/data/__generated__/component-sets/template-bottom-fixed-bar.mjs +0 -42
  165. package/src/entities/data/__generated__/component-sets/text-button.d.ts +0 -45
  166. package/src/entities/data/__generated__/component-sets/text-button.mjs +0 -45
@@ -2,22 +2,22 @@ export const metadata = {
2
2
  "name": "🟢 Toggle Button",
3
3
  "key": "1d240ee5fd7a56879713e69cbea1b6f006f0ea22",
4
4
  "componentPropertyDefinitions": {
5
- "Suffix Icon#6122:343": {
5
+ "Label#6122:49": {
6
+ "type": "TEXT"
7
+ },
8
+ "Prefix Icon#6122:98": {
6
9
  "type": "INSTANCE_SWAP",
7
10
  "preferredValues": []
8
11
  },
9
- "Show Prefix Icon#6122:392": {
10
- "type": "BOOLEAN"
11
- },
12
12
  "Show Suffix Icon#6122:147": {
13
13
  "type": "BOOLEAN"
14
14
  },
15
- "Prefix Icon#6122:98": {
15
+ "Suffix Icon#6122:343": {
16
16
  "type": "INSTANCE_SWAP",
17
17
  "preferredValues": []
18
18
  },
19
- "Label#6122:49": {
20
- "type": "TEXT"
19
+ "Show Prefix Icon#6122:392": {
20
+ "type": "BOOLEAN"
21
21
  },
22
22
  "Size": {
23
23
  "type": "VARIANT",
@@ -0,0 +1,42 @@
1
+ export declare const metadata: {
2
+ "name": "🟢 Top Navigation",
3
+ "key": "f6d069d65f8ffc8b430fd8f3013910557f36e9da",
4
+ "componentPropertyDefinitions": {
5
+ "Show Title#33588:82": {
6
+ "type": "BOOLEAN"
7
+ },
8
+ "OS (Figma Only)": {
9
+ "type": "VARIANT",
10
+ "variantOptions": [
11
+ "iOS",
12
+ "Android"
13
+ ]
14
+ },
15
+ "Variant": {
16
+ "type": "VARIANT",
17
+ "variantOptions": [
18
+ "Layer Default",
19
+ "Transparent"
20
+ ]
21
+ },
22
+ "Left": {
23
+ "type": "VARIANT",
24
+ "variantOptions": [
25
+ "Back",
26
+ "Close",
27
+ "Custom",
28
+ "None"
29
+ ]
30
+ },
31
+ "Right": {
32
+ "type": "VARIANT",
33
+ "variantOptions": [
34
+ "1 Icon Button",
35
+ "2 Icon Button",
36
+ "3 Icon Button",
37
+ "Text Button",
38
+ "None"
39
+ ]
40
+ }
41
+ }
42
+ };
@@ -0,0 +1,42 @@
1
+ export const metadata = {
2
+ "name": "🟢 Top Navigation",
3
+ "key": "f6d069d65f8ffc8b430fd8f3013910557f36e9da",
4
+ "componentPropertyDefinitions": {
5
+ "Show Title#33588:82": {
6
+ "type": "BOOLEAN"
7
+ },
8
+ "OS (Figma Only)": {
9
+ "type": "VARIANT",
10
+ "variantOptions": [
11
+ "iOS",
12
+ "Android"
13
+ ]
14
+ },
15
+ "Variant": {
16
+ "type": "VARIANT",
17
+ "variantOptions": [
18
+ "Layer Default",
19
+ "Transparent"
20
+ ]
21
+ },
22
+ "Left": {
23
+ "type": "VARIANT",
24
+ "variantOptions": [
25
+ "Back",
26
+ "Close",
27
+ "Custom",
28
+ "None"
29
+ ]
30
+ },
31
+ "Right": {
32
+ "type": "VARIANT",
33
+ "variantOptions": [
34
+ "1 Icon Button",
35
+ "2 Icon Button",
36
+ "3 Icon Button",
37
+ "Text Button",
38
+ "None"
39
+ ]
40
+ }
41
+ }
42
+ };
@@ -1,5 +1,99 @@
1
1
  import type { Style } from "../style.interface";
2
2
 
3
+ export const FIGMA_FILL_STYLES: Style[] = [
4
+ {
5
+ styleType: "FILL",
6
+ key: "46186d7dd037e8e54983ee6677cf7915790d84cc",
7
+ name: "gradient/fade/layer-floating/↑(to-top)",
8
+ description: "스크롤이 가능하도록 그라디언트로 특정 영역을 덮는 요소로 사용해요.",
9
+ remote: false,
10
+ },
11
+ {
12
+ styleType: "FILL",
13
+ key: "b28d9c9adc816fdd4ecec3a0b3e25d8aec802b6a",
14
+ name: "gradient/fade/layer-floating/↓(to-bottom)",
15
+ description: "스크롤이 가능하도록 그라디언트로 특정 영역을 덮는 요소로 사용해요.",
16
+ remote: false,
17
+ },
18
+ {
19
+ styleType: "FILL",
20
+ key: "063c0dc05b438ac36499492e783520a722782d74",
21
+ name: "gradient/fade/layer-floating/→(to-right)",
22
+ description: "스크롤이 가능하도록 그라디언트로 특정 영역을 덮는 요소로 사용해요.",
23
+ remote: false,
24
+ },
25
+ {
26
+ styleType: "FILL",
27
+ key: "c1cee6b89c357c6a12d020654d73a4490cafdf51",
28
+ name: "gradient/fade/layer-default/←(to-left)",
29
+ description: "스크롤이 가능하도록 그라디언트로 특정 영역을 덮는 요소로 사용해요.",
30
+ remote: false,
31
+ },
32
+ {
33
+ styleType: "FILL",
34
+ key: "826457503b7ee3d472eab91581b1fcd499c47c00",
35
+ name: "gradient/fade/layer-default/↑(to-top)",
36
+ description: "스크롤이 가능하도록 그라디언트로 특정 영역을 덮는 요소로 사용해요.",
37
+ remote: false,
38
+ },
39
+ {
40
+ styleType: "FILL",
41
+ key: "df22450a8f8e9c323baf40d8981920d1b6376cbf",
42
+ name: "gradient/fade/layer-floating/←(to-left)",
43
+ description: "스크롤이 가능하도록 그라디언트로 특정 영역을 덮는 요소로 사용해요.",
44
+ remote: false,
45
+ },
46
+ {
47
+ styleType: "FILL",
48
+ key: "ff436e73cf9477a1006e6f8a1f9d2c9dbe0e5edd",
49
+ name: "gradient/fade/layer-default/→(to-right)",
50
+ description: "스크롤이 가능하도록 그라디언트로 특정 영역을 덮는 요소로 사용해요.",
51
+ remote: false,
52
+ },
53
+ {
54
+ styleType: "FILL",
55
+ key: "353baa82e0440385eb121cd0ae98d2fcb1a70b18",
56
+ name: "gradient/glow/magic",
57
+ description: "반짝이는 것처럼 느껴지는 배경에 쓰이는 ai 컬러입니다.",
58
+ remote: false,
59
+ },
60
+ {
61
+ styleType: "FILL",
62
+ key: "f83f54c1840aaf26742629e2c696c363b8df0a82",
63
+ name: "gradient/glow/magic-pressed",
64
+ description: "반짝이는 것처럼 느껴지는 배경에 쓰이는 ai 컬러의 pressed컬러입니다.",
65
+ remote: false,
66
+ },
67
+ {
68
+ styleType: "FILL",
69
+ key: "408cb970f1de672217f452152b0398a4bb4f4e12",
70
+ name: "gradient/fade/layer-default/↓(to-bottom)",
71
+ description: "스크롤이 가능하도록 그라디언트로 특정 영역을 덮는 요소로 사용해요.",
72
+ remote: false,
73
+ },
74
+ {
75
+ styleType: "FILL",
76
+ key: "71971dde923a11c615296a16f8172c0671d5a3a6",
77
+ name: "gradient/shimmer/neutral",
78
+ description: "Skeleton shimmer",
79
+ remote: false,
80
+ },
81
+ {
82
+ styleType: "FILL",
83
+ key: "2ed61bff1a187f39389698cf63b74d6997aed609",
84
+ name: "gradient/shimmer/magic",
85
+ description: "Skeleton AI shimmer",
86
+ remote: false,
87
+ },
88
+ {
89
+ styleType: "FILL",
90
+ key: "1101a7c6a505d97904c3a0ce6a9ee17fe15de879",
91
+ name: "gradient/highlight/magic",
92
+ description: "아이콘 및 shape 영역에서 AI 기능을 표현할 때 사용하는 컬러입니다.",
93
+ remote: false,
94
+ },
95
+ ];
96
+
3
97
  export const FIGMA_TEXT_STYLES: Style[] = [
4
98
  {
5
99
  styleType: "TEXT",
@@ -1,6 +1,6 @@
1
1
  import { createStaticIconRepository } from "./icon.repository";
2
2
  import { FIGMA_ICONS } from "./data/icons";
3
- import { FIGMA_TEXT_STYLES } from "./data/styles";
3
+ import { FIGMA_FILL_STYLES, FIGMA_TEXT_STYLES } from "./data/styles";
4
4
  import { FIGMA_VARIABLE_COLLECTIONS } from "./data/variable-collections";
5
5
  import { FIGMA_VARIABLES } from "./data/variables";
6
6
  import * as FIGMA_COMPONENTS from "./data/__generated__/component-sets";
@@ -20,7 +20,10 @@ export * from "./variable.service";
20
20
  export * from "./component.interface";
21
21
  export * from "./component.repository";
22
22
 
23
- export const styleRepository = createStaticStyleRepository(FIGMA_TEXT_STYLES);
23
+ export const styleRepository = createStaticStyleRepository([
24
+ ...FIGMA_TEXT_STYLES,
25
+ ...FIGMA_FILL_STYLES,
26
+ ]);
24
27
  export const variableRepository = createStaticVariableRepository({
25
28
  variables: FIGMA_VARIABLES,
26
29
  variableCollections: FIGMA_VARIABLE_COLLECTIONS,
@@ -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
- );