@seed-design/figma 0.0.5 → 0.0.15

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 (195) hide show
  1. package/lib/index.cjs +5548 -4901
  2. package/lib/index.d.ts +489 -189
  3. package/lib/index.js +5535 -4888
  4. package/package.json +3 -2
  5. package/src/codegen/core/codegen.ts +65 -0
  6. package/src/codegen/core/component.ts +15 -27
  7. package/src/codegen/core/component.types.ts +29 -0
  8. package/src/codegen/core/element.ts +13 -0
  9. package/src/codegen/core/index.ts +13 -8
  10. package/src/codegen/core/infer-layout.test.ts +285 -0
  11. package/src/codegen/core/infer-layout.ts +416 -0
  12. package/src/codegen/core/jsx.ts +12 -0
  13. package/src/codegen/core/props.ts +81 -0
  14. package/src/codegen/core/value.ts +289 -0
  15. package/src/codegen/index.ts +39 -6
  16. package/src/codegen/targets/figma/context.ts +139 -0
  17. package/src/codegen/targets/figma/frame.ts +37 -0
  18. package/src/codegen/targets/figma/index.ts +6 -0
  19. package/src/codegen/targets/figma/instance.ts +16 -0
  20. package/src/codegen/targets/figma/props.ts +244 -0
  21. package/src/codegen/targets/figma/shape.ts +62 -0
  22. package/src/codegen/targets/figma/text.ts +33 -0
  23. package/src/codegen/targets/index.ts +2 -0
  24. package/src/codegen/{domain/seed-component → targets/react/component}/deps.interface.ts +2 -2
  25. package/src/codegen/{domain/seed-component → targets/react/component}/index.ts +36 -34
  26. package/src/codegen/{domain/seed-component → targets/react/component}/properties.type.ts +2 -2
  27. package/src/codegen/{domain/seed-component → targets/react/component}/transformers/action-button.ts +4 -5
  28. package/src/codegen/{domain/seed-component → targets/react/component}/transformers/action-chip.ts +3 -4
  29. package/src/codegen/{domain/seed-component → targets/react/component}/transformers/action-sheet.ts +4 -5
  30. package/src/codegen/{domain/seed-component → targets/react/component}/transformers/app-bar.ts +5 -6
  31. package/src/codegen/{domain/seed-component → targets/react/component}/transformers/avatar-stack.ts +4 -5
  32. package/src/codegen/{domain/seed-component → targets/react/component}/transformers/avatar.ts +4 -5
  33. package/src/codegen/{domain/seed-component → targets/react/component}/transformers/badge.ts +4 -5
  34. package/src/codegen/{domain/seed-component → targets/react/component}/transformers/callout.ts +4 -5
  35. package/src/codegen/{domain/seed-component → targets/react/component}/transformers/checkbox.ts +4 -5
  36. package/src/codegen/{domain/seed-component → targets/react/component}/transformers/chip-tabs.ts +4 -5
  37. package/src/codegen/{domain/seed-component → targets/react/component}/transformers/control-chip.ts +4 -5
  38. package/src/codegen/{domain/seed-component → targets/react/component}/transformers/error-state.ts +4 -5
  39. package/src/codegen/{domain/seed-component → targets/react/component}/transformers/extended-action-sheet.ts +4 -5
  40. package/src/codegen/{domain/seed-component → targets/react/component}/transformers/extended-fab.ts +4 -5
  41. package/src/codegen/{domain/seed-component → targets/react/component}/transformers/fab.ts +2 -2
  42. package/src/codegen/{domain/seed-component → targets/react/component}/transformers/help-bubble.ts +2 -2
  43. package/src/codegen/{domain/seed-component → targets/react/component}/transformers/identity-placeholder.ts +2 -2
  44. package/src/codegen/{domain/seed-component → targets/react/component}/transformers/inline-banner.ts +5 -6
  45. package/src/codegen/{domain/seed-component → targets/react/component}/transformers/manner-temp-badge.ts +3 -4
  46. package/src/codegen/{domain/seed-component → targets/react/component}/transformers/multiline-text-field.ts +4 -5
  47. package/src/codegen/{domain/seed-component → targets/react/component}/transformers/progress-circle.ts +3 -4
  48. package/src/codegen/{domain/seed-component → targets/react/component}/transformers/reaction-button.ts +4 -5
  49. package/src/codegen/{domain/seed-component → targets/react/component}/transformers/segmented-control.ts +4 -5
  50. package/src/codegen/{domain/seed-component → targets/react/component}/transformers/select-box.ts +4 -5
  51. package/src/codegen/{domain/seed-component → targets/react/component}/transformers/skeleton.ts +3 -4
  52. package/src/codegen/{domain/seed-component → targets/react/component}/transformers/snackbar.ts +3 -4
  53. package/src/codegen/{domain/seed-component → targets/react/component}/transformers/switch.ts +4 -5
  54. package/src/codegen/{domain/seed-component → targets/react/component}/transformers/tabs.ts +5 -6
  55. package/src/codegen/{domain/seed-component → targets/react/component}/transformers/text-button.ts +6 -7
  56. package/src/codegen/{domain/seed-component → targets/react/component}/transformers/text-field.ts +4 -5
  57. package/src/codegen/{domain/seed-component → targets/react/component}/transformers/toggle-button.ts +4 -5
  58. package/src/codegen/targets/react/context.ts +170 -0
  59. package/src/codegen/targets/react/frame.ts +75 -0
  60. package/src/codegen/targets/react/index.ts +7 -0
  61. package/src/codegen/{domain/instance.service.ts → targets/react/instance.ts} +20 -33
  62. package/src/codegen/targets/react/props.ts +361 -0
  63. package/src/codegen/targets/react/shape.ts +36 -0
  64. package/src/codegen/targets/react/text.ts +33 -0
  65. package/src/{codegen → entities}/data/icons.ts +1 -1
  66. package/src/{codegen → entities}/data/styles.ts +1 -1
  67. package/src/{codegen → entities}/data/variable-collections.ts +1 -1
  68. package/src/{codegen → entities}/data/variables.ts +1 -1
  69. package/src/entities/index.ts +41 -0
  70. package/src/{codegen/domain → entities}/style.repository.ts +6 -2
  71. package/src/{codegen/domain → entities}/style.service.ts +1 -1
  72. package/src/{codegen/domain → entities}/variable.repository.ts +17 -4
  73. package/src/{codegen/domain → entities}/variable.service.ts +47 -9
  74. package/src/index.ts +1 -0
  75. package/src/normalizer/from-plugin.ts +3 -0
  76. package/src/normalizer/types.ts +28 -24
  77. package/src/utils/common.ts +4 -0
  78. package/src/utils/css.ts +10 -4
  79. package/src/utils/figma-node.ts +42 -2
  80. package/src/codegen/context.ts +0 -148
  81. package/src/codegen/core/transformer.ts +0 -40
  82. package/src/codegen/domain/codegen.service.ts +0 -69
  83. package/src/codegen/domain/figma-component.service.ts +0 -21
  84. package/src/codegen/domain/frame.service.ts +0 -108
  85. package/src/codegen/domain/index.ts +0 -22
  86. package/src/codegen/domain/props/container-layout-props.service.ts +0 -248
  87. package/src/codegen/domain/props/fill-props.service.ts +0 -75
  88. package/src/codegen/domain/props/radius-props.service.ts +0 -105
  89. package/src/codegen/domain/props/self-layout-props.service.ts +0 -127
  90. package/src/codegen/domain/props/stroke-props.service.ts +0 -45
  91. package/src/codegen/domain/props/type-style-props.service.ts +0 -31
  92. package/src/codegen/domain/rectangle.service.ts +0 -31
  93. package/src/codegen/domain/text.service.ts +0 -62
  94. /package/src/codegen/{domain/seed-component → targets/react/component}/size.ts +0 -0
  95. /package/src/{codegen → entities}/data/__generated__/component-sets/action-button.d.ts +0 -0
  96. /package/src/{codegen → entities}/data/__generated__/component-sets/action-button.mjs +0 -0
  97. /package/src/{codegen → entities}/data/__generated__/component-sets/action-chip.d.ts +0 -0
  98. /package/src/{codegen → entities}/data/__generated__/component-sets/action-chip.mjs +0 -0
  99. /package/src/{codegen → entities}/data/__generated__/component-sets/action-sheet.d.ts +0 -0
  100. /package/src/{codegen → entities}/data/__generated__/component-sets/action-sheet.mjs +0 -0
  101. /package/src/{codegen → entities}/data/__generated__/component-sets/avatar-stack.d.ts +0 -0
  102. /package/src/{codegen → entities}/data/__generated__/component-sets/avatar-stack.mjs +0 -0
  103. /package/src/{codegen → entities}/data/__generated__/component-sets/avatar.d.ts +0 -0
  104. /package/src/{codegen → entities}/data/__generated__/component-sets/avatar.mjs +0 -0
  105. /package/src/{codegen → entities}/data/__generated__/component-sets/badge.d.ts +0 -0
  106. /package/src/{codegen → entities}/data/__generated__/component-sets/badge.mjs +0 -0
  107. /package/src/{codegen → entities}/data/__generated__/component-sets/bottom-navigation-global.d.ts +0 -0
  108. /package/src/{codegen → entities}/data/__generated__/component-sets/bottom-navigation-global.mjs +0 -0
  109. /package/src/{codegen → entities}/data/__generated__/component-sets/bottom-navigation-kr.d.ts +0 -0
  110. /package/src/{codegen → entities}/data/__generated__/component-sets/bottom-navigation-kr.mjs +0 -0
  111. /package/src/{codegen → entities}/data/__generated__/component-sets/bottom-sheet.d.ts +0 -0
  112. /package/src/{codegen → entities}/data/__generated__/component-sets/bottom-sheet.mjs +0 -0
  113. /package/src/{codegen → entities}/data/__generated__/component-sets/callout.d.ts +0 -0
  114. /package/src/{codegen → entities}/data/__generated__/component-sets/callout.mjs +0 -0
  115. /package/src/{codegen → entities}/data/__generated__/component-sets/checkbox.d.ts +0 -0
  116. /package/src/{codegen → entities}/data/__generated__/component-sets/checkbox.mjs +0 -0
  117. /package/src/{codegen → entities}/data/__generated__/component-sets/chip-tablist.d.ts +0 -0
  118. /package/src/{codegen → entities}/data/__generated__/component-sets/chip-tablist.mjs +0 -0
  119. /package/src/{codegen → entities}/data/__generated__/component-sets/control-chip.d.ts +0 -0
  120. /package/src/{codegen → entities}/data/__generated__/component-sets/control-chip.mjs +0 -0
  121. /package/src/{codegen → entities}/data/__generated__/component-sets/divider.d.ts +0 -0
  122. /package/src/{codegen → entities}/data/__generated__/component-sets/divider.mjs +0 -0
  123. /package/src/{codegen → entities}/data/__generated__/component-sets/error-state.d.ts +0 -0
  124. /package/src/{codegen → entities}/data/__generated__/component-sets/error-state.mjs +0 -0
  125. /package/src/{codegen → entities}/data/__generated__/component-sets/extended-action-sheet.d.ts +0 -0
  126. /package/src/{codegen → entities}/data/__generated__/component-sets/extended-action-sheet.mjs +0 -0
  127. /package/src/{codegen → entities}/data/__generated__/component-sets/extended-floating-action-button.d.ts +0 -0
  128. /package/src/{codegen → entities}/data/__generated__/component-sets/extended-floating-action-button.mjs +0 -0
  129. /package/src/{codegen → entities}/data/__generated__/component-sets/floating-action-button.d.ts +0 -0
  130. /package/src/{codegen → entities}/data/__generated__/component-sets/floating-action-button.mjs +0 -0
  131. /package/src/{codegen → entities}/data/__generated__/component-sets/help-bubble.d.ts +0 -0
  132. /package/src/{codegen → entities}/data/__generated__/component-sets/help-bubble.mjs +0 -0
  133. /package/src/{codegen → entities}/data/__generated__/component-sets/identity-placeholder.d.ts +0 -0
  134. /package/src/{codegen → entities}/data/__generated__/component-sets/identity-placeholder.mjs +0 -0
  135. /package/src/{codegen → entities}/data/__generated__/component-sets/index.d.ts +0 -0
  136. /package/src/{codegen → entities}/data/__generated__/component-sets/index.mjs +0 -0
  137. /package/src/{codegen → entities}/data/__generated__/component-sets/inline-banner.d.ts +0 -0
  138. /package/src/{codegen → entities}/data/__generated__/component-sets/inline-banner.mjs +0 -0
  139. /package/src/{codegen → entities}/data/__generated__/component-sets/main-tab-navigation-global.d.ts +0 -0
  140. /package/src/{codegen → entities}/data/__generated__/component-sets/main-tab-navigation-global.mjs +0 -0
  141. /package/src/{codegen → entities}/data/__generated__/component-sets/main-tab-navigation-kr.d.ts +0 -0
  142. /package/src/{codegen → entities}/data/__generated__/component-sets/main-tab-navigation-kr.mjs +0 -0
  143. /package/src/{codegen → entities}/data/__generated__/component-sets/manner-temp-badge.d.ts +0 -0
  144. /package/src/{codegen → entities}/data/__generated__/component-sets/manner-temp-badge.mjs +0 -0
  145. /package/src/{codegen → entities}/data/__generated__/component-sets/manner-temp-bar.d.ts +0 -0
  146. /package/src/{codegen → entities}/data/__generated__/component-sets/manner-temp-bar.mjs +0 -0
  147. /package/src/{codegen → entities}/data/__generated__/component-sets/manner-temp.d.ts +0 -0
  148. /package/src/{codegen → entities}/data/__generated__/component-sets/manner-temp.mjs +0 -0
  149. /package/src/{codegen → entities}/data/__generated__/component-sets/multiline-text-field.d.ts +0 -0
  150. /package/src/{codegen → entities}/data/__generated__/component-sets/multiline-text-field.mjs +0 -0
  151. /package/src/{codegen → entities}/data/__generated__/component-sets/progress-circle.d.ts +0 -0
  152. /package/src/{codegen → entities}/data/__generated__/component-sets/progress-circle.mjs +0 -0
  153. /package/src/{codegen → entities}/data/__generated__/component-sets/radio.d.ts +0 -0
  154. /package/src/{codegen → entities}/data/__generated__/component-sets/radio.mjs +0 -0
  155. /package/src/{codegen → entities}/data/__generated__/component-sets/range-slider.d.ts +0 -0
  156. /package/src/{codegen → entities}/data/__generated__/component-sets/range-slider.mjs +0 -0
  157. /package/src/{codegen → entities}/data/__generated__/component-sets/reaction-button.d.ts +0 -0
  158. /package/src/{codegen → entities}/data/__generated__/component-sets/reaction-button.mjs +0 -0
  159. /package/src/{codegen → entities}/data/__generated__/component-sets/segmented-control.d.ts +0 -0
  160. /package/src/{codegen → entities}/data/__generated__/component-sets/segmented-control.mjs +0 -0
  161. /package/src/{codegen → entities}/data/__generated__/component-sets/select-box.d.ts +0 -0
  162. /package/src/{codegen → entities}/data/__generated__/component-sets/select-box.mjs +0 -0
  163. /package/src/{codegen → entities}/data/__generated__/component-sets/skeleton.d.ts +0 -0
  164. /package/src/{codegen → entities}/data/__generated__/component-sets/skeleton.mjs +0 -0
  165. /package/src/{codegen → entities}/data/__generated__/component-sets/slider.d.ts +0 -0
  166. /package/src/{codegen → entities}/data/__generated__/component-sets/slider.mjs +0 -0
  167. /package/src/{codegen → entities}/data/__generated__/component-sets/snackbar.d.ts +0 -0
  168. /package/src/{codegen → entities}/data/__generated__/component-sets/snackbar.mjs +0 -0
  169. /package/src/{codegen → entities}/data/__generated__/component-sets/standard-navigation.d.ts +0 -0
  170. /package/src/{codegen → entities}/data/__generated__/component-sets/standard-navigation.mjs +0 -0
  171. /package/src/{codegen → entities}/data/__generated__/component-sets/switch.d.ts +0 -0
  172. /package/src/{codegen → entities}/data/__generated__/component-sets/switch.mjs +0 -0
  173. /package/src/{codegen → entities}/data/__generated__/component-sets/tablist.d.ts +0 -0
  174. /package/src/{codegen → entities}/data/__generated__/component-sets/tablist.mjs +0 -0
  175. /package/src/{codegen → entities}/data/__generated__/component-sets/template-bottom-fixed-bar.d.ts +0 -0
  176. /package/src/{codegen → entities}/data/__generated__/component-sets/template-bottom-fixed-bar.mjs +0 -0
  177. /package/src/{codegen → entities}/data/__generated__/component-sets/template-button-group.d.ts +0 -0
  178. /package/src/{codegen → entities}/data/__generated__/component-sets/template-button-group.mjs +0 -0
  179. /package/src/{codegen → entities}/data/__generated__/component-sets/template-chip-group.d.ts +0 -0
  180. /package/src/{codegen → entities}/data/__generated__/component-sets/template-chip-group.mjs +0 -0
  181. /package/src/{codegen → entities}/data/__generated__/component-sets/template-select-box-group.d.ts +0 -0
  182. /package/src/{codegen → entities}/data/__generated__/component-sets/template-select-box-group.mjs +0 -0
  183. /package/src/{codegen → entities}/data/__generated__/component-sets/template-top-navigation.d.ts +0 -0
  184. /package/src/{codegen → entities}/data/__generated__/component-sets/template-top-navigation.mjs +0 -0
  185. /package/src/{codegen → entities}/data/__generated__/component-sets/text-button.d.ts +0 -0
  186. /package/src/{codegen → entities}/data/__generated__/component-sets/text-button.mjs +0 -0
  187. /package/src/{codegen → entities}/data/__generated__/component-sets/text-field.d.ts +0 -0
  188. /package/src/{codegen → entities}/data/__generated__/component-sets/text-field.mjs +0 -0
  189. /package/src/{codegen → entities}/data/__generated__/component-sets/toggle-button.d.ts +0 -0
  190. /package/src/{codegen → entities}/data/__generated__/component-sets/toggle-button.mjs +0 -0
  191. /package/src/{codegen/domain → entities}/icon.interface.ts +0 -0
  192. /package/src/{codegen/domain → entities}/icon.repository.ts +0 -0
  193. /package/src/{codegen/domain → entities}/icon.service.ts +0 -0
  194. /package/src/{codegen/domain → entities}/style.interface.ts +0 -0
  195. /package/src/{codegen/domain → entities}/variable.interface.ts +0 -0
@@ -0,0 +1,361 @@
1
+ import {
2
+ createPropsTransformer,
3
+ definePropsTransformer,
4
+ type PropsTransformer,
5
+ type ValueTransformer,
6
+ } from "@/codegen/core";
7
+ import type { StyleService } from "@/entities";
8
+ import type {
9
+ NormalizedCornerTrait,
10
+ NormalizedHasChildrenTrait,
11
+ NormalizedHasFramePropertiesTrait,
12
+ NormalizedHasGeometryTrait,
13
+ NormalizedHasLayoutTrait,
14
+ NormalizedIsLayerTrait,
15
+ NormalizedTypePropertiesTrait,
16
+ } from "@/normalizer";
17
+ import { match } from "ts-pattern";
18
+
19
+ export interface PropsTransformers {
20
+ containerLayout: PropsTransformer<ContainerLayoutTrait, ContainerLayoutProps>;
21
+ selfLayout: PropsTransformer<SelfLayoutTrait, SelfLayoutProps>;
22
+ iconSelfLayout: PropsTransformer<SelfLayoutTrait, IconSelfLayoutProps>;
23
+ radius: PropsTransformer<RadiusTrait, RadiusProps>;
24
+ frameFill: PropsTransformer<FillTrait, FrameFillProps>;
25
+ shapeFill: PropsTransformer<FillTrait, ShapeFillProps>;
26
+ textFill: PropsTransformer<FillTrait, TextFillProps>;
27
+ stroke: PropsTransformer<StrokeTrait, StrokeProps>;
28
+ typeStyle: PropsTransformer<TypeStyleTrait, TypeStyleProps>;
29
+ }
30
+
31
+ export type ContainerLayoutTrait = NormalizedHasFramePropertiesTrait &
32
+ NormalizedHasChildrenTrait &
33
+ NormalizedHasLayoutTrait &
34
+ NormalizedIsLayerTrait;
35
+
36
+ export type SelfLayoutTrait = NormalizedIsLayerTrait & NormalizedHasLayoutTrait;
37
+
38
+ export type RadiusTrait = NormalizedCornerTrait & NormalizedIsLayerTrait;
39
+
40
+ export type FillTrait = NormalizedIsLayerTrait & NormalizedHasGeometryTrait;
41
+
42
+ export type StrokeTrait = NormalizedIsLayerTrait & NormalizedHasGeometryTrait;
43
+
44
+ export type TypeStyleTrait = NormalizedTypePropertiesTrait & NormalizedIsLayerTrait;
45
+
46
+ export interface ContainerLayoutProps {
47
+ direction?: "row" | "column";
48
+ justify?: "flex-start" | "center" | "flex-end" | "space-between";
49
+ align?: "stretch" | "flex-start" | "center" | "flex-end" | "baseline";
50
+ wrap?: "wrap" | "nowrap" | true;
51
+ gap?: string | 0;
52
+ pb?: string | 0;
53
+ pl?: string | 0;
54
+ pr?: string | 0;
55
+ pt?: string | 0;
56
+ px?: string | 0;
57
+ py?: string | 0;
58
+ p?: string | 0;
59
+ }
60
+
61
+ type ReactValueTransformer = ValueTransformer<string, string, string, number>;
62
+
63
+ export function createContainerLayoutPropsTransformer(
64
+ valueTransformer: ReactValueTransformer,
65
+ ): PropsTransformer<ContainerLayoutTrait, ContainerLayoutProps> {
66
+ return createPropsTransformer({
67
+ _types: {
68
+ trait: {} as ContainerLayoutTrait,
69
+ props: {} as ContainerLayoutProps,
70
+ },
71
+ handlers: {
72
+ direction: ({ layoutMode }) =>
73
+ match(layoutMode)
74
+ .with("HORIZONTAL", () => "row" as const)
75
+ .with("VERTICAL", () => "column" as const)
76
+ .with("NONE", () => undefined)
77
+ .with(undefined, () => undefined)
78
+ .exhaustive(),
79
+ justify: ({ primaryAxisAlignItems }) =>
80
+ match(primaryAxisAlignItems)
81
+ .with("MIN", () => "flex-start" as const)
82
+ .with("CENTER", () => "center" as const)
83
+ .with("MAX", () => "flex-end" as const)
84
+ .with("SPACE_BETWEEN", () => "space-between" as const)
85
+ .with(undefined, () => undefined)
86
+ .exhaustive(),
87
+ align: ({ counterAxisAlignItems, children }) => {
88
+ const isStretch = children.every((child) => {
89
+ if (!("layoutAlign" in child)) {
90
+ return false;
91
+ }
92
+
93
+ return child.layoutAlign === "STRETCH";
94
+ });
95
+
96
+ if (isStretch) {
97
+ return "stretch";
98
+ }
99
+
100
+ return match(counterAxisAlignItems)
101
+ .with("MIN", () => "flex-start" as const)
102
+ .with("CENTER", () => "center" as const)
103
+ .with("MAX", () => "flex-end" as const)
104
+ .with("BASELINE", () => "baseline" as const)
105
+ .with(undefined, () => undefined)
106
+ .exhaustive();
107
+ },
108
+ wrap: ({ layoutWrap }) =>
109
+ match(layoutWrap)
110
+ .with("WRAP", () => true as const)
111
+ .with("NO_WRAP", () => "nowrap" as const)
112
+ .with(undefined, () => undefined)
113
+ .exhaustive(),
114
+ gap: (node) => {
115
+ if (node.children.length <= 1) {
116
+ return undefined;
117
+ }
118
+
119
+ if (node.primaryAxisAlignItems === "SPACE_BETWEEN") {
120
+ return undefined;
121
+ }
122
+
123
+ return valueTransformer.getFormattedValue.itemSpacing(node);
124
+ },
125
+ pt: (node) => valueTransformer.getFormattedValue.paddingTop(node),
126
+ pb: (node) => valueTransformer.getFormattedValue.paddingBottom(node),
127
+ pl: (node) => valueTransformer.getFormattedValue.paddingLeft(node),
128
+ pr: (node) => valueTransformer.getFormattedValue.paddingRight(node),
129
+ },
130
+ shorthands: {
131
+ p: ["pt", "pb", "pl", "pr"],
132
+ px: ["pl", "pr"],
133
+ py: ["pt", "pb"],
134
+ },
135
+ defaults: {
136
+ justify: "flex-start",
137
+ align: "stretch",
138
+ wrap: "nowrap",
139
+ gap: 0,
140
+ p: 0,
141
+ px: 0,
142
+ py: 0,
143
+ pb: 0,
144
+ pl: 0,
145
+ pr: 0,
146
+ pt: 0,
147
+ },
148
+ });
149
+ }
150
+
151
+ export interface SelfLayoutProps {
152
+ grow?: 0 | 1 | true;
153
+ alignSelf?: "stretch";
154
+ width?: string | number;
155
+ height?: string | number;
156
+ minWidth?: string | number;
157
+ minHeight?: string | number;
158
+ maxWidth?: string | number;
159
+ maxHeight?: string | number;
160
+ }
161
+
162
+ export function createSelfLayoutPropsTransformer(
163
+ valueTransformer: ReactValueTransformer,
164
+ ): PropsTransformer<SelfLayoutTrait, SelfLayoutProps> {
165
+ return createPropsTransformer({
166
+ _types: {
167
+ trait: {} as SelfLayoutTrait,
168
+ props: {} as SelfLayoutProps,
169
+ },
170
+ handlers: {
171
+ grow: ({ layoutGrow }) => (layoutGrow === 1 ? true : layoutGrow),
172
+ alignSelf: ({ layoutAlign }) =>
173
+ match(layoutAlign)
174
+ .with("STRETCH", () => "stretch" as const)
175
+ .with("INHERIT", () => undefined)
176
+ .with("MIN", () => undefined) // Deprecated in Figma
177
+ .with("CENTER", () => undefined) // Deprecated in Figma
178
+ .with("MAX", () => undefined) // Deprecated in Figma
179
+ .with(undefined, () => undefined)
180
+ .exhaustive(),
181
+ height: (node) =>
182
+ node.layoutSizingVertical === "FIXED"
183
+ ? valueTransformer.getFormattedValue.height(node)
184
+ : undefined,
185
+ width: (node) =>
186
+ node.layoutSizingHorizontal === "FIXED"
187
+ ? valueTransformer.getFormattedValue.width(node)
188
+ : undefined,
189
+ minHeight: (node) =>
190
+ node.layoutSizingVertical === "HUG"
191
+ ? valueTransformer.getFormattedValue.minHeight(node)
192
+ : undefined,
193
+ maxHeight: (node) =>
194
+ node.layoutSizingVertical === "HUG"
195
+ ? valueTransformer.getFormattedValue.maxHeight(node)
196
+ : undefined,
197
+ minWidth: (node) =>
198
+ node.layoutSizingHorizontal === "HUG"
199
+ ? valueTransformer.getFormattedValue.minWidth(node)
200
+ : undefined,
201
+ maxWidth: (node) =>
202
+ node.layoutSizingHorizontal === "HUG"
203
+ ? valueTransformer.getFormattedValue.maxWidth(node)
204
+ : undefined,
205
+ },
206
+ defaults: {
207
+ grow: 0,
208
+ },
209
+ });
210
+ }
211
+
212
+ export interface IconSelfLayoutProps {
213
+ size?: string | number;
214
+ }
215
+
216
+ export function createIconSelfLayoutPropsTransformer(valueTransformer: ReactValueTransformer) {
217
+ return createPropsTransformer({
218
+ _types: {
219
+ trait: {} as SelfLayoutTrait,
220
+ props: {} as IconSelfLayoutProps,
221
+ },
222
+ handlers: {
223
+ size: (node) => valueTransformer.getFormattedValue.width(node),
224
+ },
225
+ });
226
+ }
227
+
228
+ export interface RadiusProps {
229
+ borderRadius?: string | 0;
230
+ borderTopLeftRadius?: string | 0;
231
+ borderTopRightRadius?: string | 0;
232
+ borderBottomLeftRadius?: string | 0;
233
+ borderBottomRightRadius?: string | 0;
234
+ }
235
+
236
+ export function createRadiusPropsTransformer(valueTransformer: ReactValueTransformer) {
237
+ return createPropsTransformer({
238
+ _types: {
239
+ trait: {} as RadiusTrait,
240
+ props: {} as RadiusProps,
241
+ },
242
+ handlers: {
243
+ borderTopLeftRadius: (node) => valueTransformer.getFormattedValue.topLeftRadius(node),
244
+ borderTopRightRadius: (node) => valueTransformer.getFormattedValue.topRightRadius(node),
245
+ borderBottomLeftRadius: (node) => valueTransformer.getFormattedValue.bottomLeftRadius(node),
246
+ borderBottomRightRadius: (node) => valueTransformer.getFormattedValue.bottomRightRadius(node),
247
+ },
248
+ shorthands: {
249
+ borderRadius: [
250
+ "borderTopLeftRadius",
251
+ "borderTopRightRadius",
252
+ "borderBottomLeftRadius",
253
+ "borderBottomRightRadius",
254
+ ],
255
+ },
256
+ defaults: {
257
+ borderRadius: 0,
258
+ borderTopLeftRadius: 0,
259
+ borderTopRightRadius: 0,
260
+ borderBottomLeftRadius: 0,
261
+ borderBottomRightRadius: 0,
262
+ },
263
+ });
264
+ }
265
+
266
+ export interface TypeStyleProps {
267
+ textStyle?: string;
268
+ fontSize?: string;
269
+ fontWeight?: string | number;
270
+ lineHeight?: string;
271
+ maxLines?: number;
272
+ }
273
+
274
+ export function createTypeStylePropsTransformer({
275
+ valueTransformer,
276
+ styleService,
277
+ }: {
278
+ valueTransformer: ReactValueTransformer;
279
+ styleService: StyleService;
280
+ }): PropsTransformer<TypeStyleTrait, TypeStyleProps> {
281
+ return definePropsTransformer((node) => {
282
+ const styleName = node.textStyleKey ? styleService.getStyleName(node.textStyleKey) : undefined;
283
+ const maxLines =
284
+ node.style.textTruncation === "ENDING" ? (node.style.maxLines ?? undefined) : undefined;
285
+
286
+ if (styleName) {
287
+ return {
288
+ textStyle: styleName,
289
+ maxLines,
290
+ };
291
+ }
292
+
293
+ return {
294
+ fontSize: valueTransformer.getFormattedValue.fontSize(node),
295
+ fontWeight: valueTransformer.getFormattedValue.fontWeight(node),
296
+ lineHeight: valueTransformer.getFormattedValue.lineHeight(node),
297
+ maxLines,
298
+ };
299
+ });
300
+ }
301
+
302
+ export interface FrameFillProps {
303
+ bg?: string;
304
+ }
305
+
306
+ export function createFrameFillPropsTransformer(valueTransformer: ReactValueTransformer) {
307
+ return definePropsTransformer<FillTrait, FrameFillProps>((node) => {
308
+ const bg = valueTransformer.getFormattedValue.frameFill(node);
309
+
310
+ return {
311
+ bg,
312
+ };
313
+ });
314
+ }
315
+
316
+ export interface ShapeFillProps {
317
+ color?: string;
318
+ }
319
+
320
+ export function createShapeFillPropsTransformer(valueTransformer: ReactValueTransformer) {
321
+ return definePropsTransformer<FillTrait, ShapeFillProps>((node) => {
322
+ const color = valueTransformer.getFormattedValue.shapeFill(node);
323
+
324
+ return {
325
+ color,
326
+ };
327
+ });
328
+ }
329
+
330
+ export interface TextFillProps {
331
+ color?: string;
332
+ }
333
+
334
+ export function createTextFillPropsTransformer(valueTransformer: ReactValueTransformer) {
335
+ return definePropsTransformer<FillTrait, TextFillProps>((node) => {
336
+ const color = valueTransformer.getFormattedValue.textFill(node);
337
+
338
+ return {
339
+ color,
340
+ };
341
+ });
342
+ }
343
+
344
+ export interface StrokeProps {
345
+ borderWidth?: number;
346
+ borderColor?: string;
347
+ }
348
+
349
+ export function createStrokePropsTransformer(
350
+ valueTransformer: ReactValueTransformer,
351
+ ): PropsTransformer<StrokeTrait, StrokeProps> {
352
+ return definePropsTransformer((node) => {
353
+ const borderColor = valueTransformer.getFormattedValue.stroke(node);
354
+ const borderWidth = borderColor ? node.strokeWeight : undefined;
355
+
356
+ return {
357
+ borderColor,
358
+ borderWidth,
359
+ };
360
+ });
361
+ }
@@ -0,0 +1,36 @@
1
+ import type {
2
+ NormalizedBooleanOperationNode,
3
+ NormalizedRectangleNode,
4
+ NormalizedVectorNode,
5
+ } from "@/normalizer";
6
+ import { createElement, defineElementTransformer, type ElementTransformer } from "../../core";
7
+ import type { PropsTransformers } from "./props";
8
+
9
+ export interface RectangleTransformerDeps {
10
+ propsTransformers: PropsTransformers;
11
+ }
12
+
13
+ export function createRectangleTransformer({
14
+ propsTransformers,
15
+ }: RectangleTransformerDeps): ElementTransformer<NormalizedRectangleNode> {
16
+ return defineElementTransformer((node: NormalizedRectangleNode, traverse) => {
17
+ return createElement(
18
+ "Box",
19
+ { ...propsTransformers.selfLayout(node, traverse), background: "palette.gray200" },
20
+ undefined,
21
+ "Rectangle Node Placeholder",
22
+ );
23
+ });
24
+ }
25
+
26
+ export function createVectorTransformer(): ElementTransformer<NormalizedVectorNode> {
27
+ return defineElementTransformer(() => {
28
+ return createElement("svg", {}, [], "Vector Node Placeholder");
29
+ });
30
+ }
31
+
32
+ export function createBooleanOperationTransformer(): ElementTransformer<NormalizedBooleanOperationNode> {
33
+ return defineElementTransformer(() => {
34
+ return createElement("svg", {}, [], "Boolean Operation Node Placeholder");
35
+ });
36
+ }
@@ -0,0 +1,33 @@
1
+ import type { NormalizedTextNode } from "@/normalizer";
2
+ import { compactObject } from "@/utils/common";
3
+ import { createElement, defineElementTransformer, type ElementTransformer } from "../../core";
4
+ import type { PropsTransformers } from "./props";
5
+
6
+ export interface TextTransformerDeps {
7
+ propsTransformers: PropsTransformers;
8
+ }
9
+
10
+ export function createTextTransformer({
11
+ propsTransformers,
12
+ }: TextTransformerDeps): ElementTransformer<NormalizedTextNode> {
13
+ return defineElementTransformer((node: NormalizedTextNode, traverse) => {
14
+ const hasMultipleFills = node.fills.length > 1;
15
+
16
+ const fillProps = propsTransformers.textFill(node, traverse);
17
+ const typeStyleProps = propsTransformers.typeStyle(node, traverse);
18
+
19
+ const props = compactObject({
20
+ ...typeStyleProps,
21
+ ...fillProps,
22
+ });
23
+
24
+ return createElement(
25
+ "Text",
26
+ props,
27
+ node.characters.replace(/\n/g, "<br />"),
28
+ hasMultipleFills
29
+ ? "Multiple fills in Text node encountered, only the first fill is used."
30
+ : "",
31
+ );
32
+ });
33
+ }
@@ -1,4 +1,4 @@
1
- import type { IconData } from "../domain";
1
+ import type { IconData } from "../icon.interface";
2
2
 
3
3
  export const FIGMA_ICONS: Record<string, IconData> = {
4
4
  "8b12671ecc2e0d9bd87c854fd10f7907bd06c54b": {
@@ -1,4 +1,4 @@
1
- import type { Style } from "../domain";
1
+ import type { Style } from "../style.interface";
2
2
 
3
3
  export const FIGMA_TEXT_STYLES: Style[] = [
4
4
  {
@@ -1,4 +1,4 @@
1
- import type { VariableCollection } from "../domain";
1
+ import type { VariableCollection } from "../variable.interface";
2
2
 
3
3
  export const FIGMA_VARIABLE_COLLECTIONS: Record<string, VariableCollection> = {
4
4
  "VariableCollectionId:1:3": {
@@ -1,4 +1,4 @@
1
- import type { Variable } from "../domain";
1
+ import type { Variable } from "../variable.interface";
2
2
 
3
3
  export const FIGMA_VARIABLES: Record<string, Variable> = {
4
4
  "VariableID:1:129": {
@@ -0,0 +1,41 @@
1
+ import { createStaticIconRepository } from "./icon.repository";
2
+ import { FIGMA_ICONS } from "./data/icons";
3
+ import { FIGMA_TEXT_STYLES } from "./data/styles";
4
+ import { FIGMA_VARIABLE_COLLECTIONS } from "./data/variable-collections";
5
+ import { FIGMA_VARIABLES } from "./data/variables";
6
+ import { createStaticStyleRepository } from "./style.repository";
7
+ import { createStaticVariableRepository } from "./variable.repository";
8
+
9
+ export * from "./icon.interface";
10
+ export * from "./icon.repository";
11
+ export * from "./icon.service";
12
+ export * from "./style.interface";
13
+ export * from "./style.repository";
14
+ export * from "./style.service";
15
+ export * from "./variable.interface";
16
+ export * from "./variable.repository";
17
+ export * from "./variable.service";
18
+
19
+ export const styleRepository = createStaticStyleRepository(FIGMA_TEXT_STYLES);
20
+ export const variableRepository = createStaticVariableRepository({
21
+ variables: FIGMA_VARIABLES,
22
+ variableCollections: FIGMA_VARIABLE_COLLECTIONS,
23
+ });
24
+ export const iconRepository = createStaticIconRepository(FIGMA_ICONS);
25
+
26
+ export function getFigmaVariableKey(name: string) {
27
+ return variableRepository.findVariableByName(name)?.key;
28
+ }
29
+
30
+ export function getFigmaStyleKey(name: string) {
31
+ return styleRepository.findOneByName(name)?.key;
32
+ }
33
+
34
+ export function getFigmaColorVariableNames(scopes: Array<"fg" | "bg" | "stroke" | "palette">) {
35
+ const variables = variableRepository.getVariableList();
36
+ return variables
37
+ .filter((variable) =>
38
+ scopes.includes(variable.name.split("/")[0] as "fg" | "bg" | "stroke" | "palette"),
39
+ )
40
+ .map((variable) => variable.name);
41
+ }
@@ -4,20 +4,24 @@ export interface StyleRepository {
4
4
  getAll(): Style[];
5
5
  getTextStyles(): Style[];
6
6
  getColorStyles(): Style[];
7
- getOne(key: string): Style | undefined;
7
+ findOneByKey(key: string): Style | undefined;
8
+ findOneByName(name: string): Style | undefined;
8
9
  }
9
10
 
10
11
  export function createStaticStyleRepository(styles: Style[]): StyleRepository {
11
12
  const stylesMap = new Map<string, Style>();
13
+ const stylesNameMap = new Map<string, Style>();
12
14
 
13
15
  for (const style of styles) {
14
16
  stylesMap.set(style.key, style);
17
+ stylesNameMap.set(style.name, style);
15
18
  }
16
19
 
17
20
  return {
18
21
  getAll: () => styles,
19
22
  getTextStyles: () => styles.filter((style) => style.styleType === "TEXT"),
20
23
  getColorStyles: () => styles.filter((style) => style.styleType === "FILL"),
21
- getOne: (key) => stylesMap.get(key),
24
+ findOneByKey: (key) => stylesMap.get(key),
25
+ findOneByName: (name) => stylesNameMap.get(name),
22
26
  };
23
27
  }
@@ -13,7 +13,7 @@ export function createStyleService({
13
13
  styleNameTransformer: ({ slug }: { slug: string[] }) => string;
14
14
  }): StyleService {
15
15
  function getFigmaStyleName(id: string) {
16
- const style = styleRepository.getOne(id);
16
+ const style = styleRepository.findOneByKey(id);
17
17
 
18
18
  if (!style) {
19
19
  throw new Error(`Style not found: ${id}`);
@@ -5,6 +5,7 @@ export interface VariableRepository {
5
5
  getVariableCollectionList(): VariableCollection[];
6
6
  findVariableByKey(key: string): Variable | undefined;
7
7
  findVariableById(id: string): Variable | undefined;
8
+ findVariableByName(name: string): Variable | undefined;
8
9
  findVariableCollectionByKey(key: string): VariableCollection | undefined;
9
10
  findVariableCollectionById(id: string): VariableCollection | undefined;
10
11
  }
@@ -18,24 +19,36 @@ export function createStaticVariableRepository({
18
19
  }): VariableRepository {
19
20
  const variablesKeyMap = new Map<string, Variable>();
20
21
  const variablesIdMap = new Map<string, Variable>();
22
+ const variablesNameMap = new Map<string, Variable>();
21
23
  const variableCollectionsKeyMap = new Map<string, VariableCollection>();
22
24
  const variableCollectionsIdMap = new Map<string, VariableCollection>();
23
- const variablesList = Object.values(variables);
24
- const variableCollectionsList = Object.values(variableCollections);
25
25
 
26
- for (const variable of variablesList) {
26
+ for (const variable of Object.values(variables)) {
27
+ if (variable.remote) {
28
+ continue;
29
+ }
30
+
27
31
  variablesKeyMap.set(variable.key, variable);
28
32
  variablesIdMap.set(variable.id, variable);
33
+ variablesNameMap.set(variable.name, variable);
29
34
  }
30
35
 
31
- for (const variableCollection of variableCollectionsList) {
36
+ for (const variableCollection of Object.values(variableCollections)) {
37
+ if (variableCollection.remote) {
38
+ continue;
39
+ }
40
+
32
41
  variableCollectionsKeyMap.set(variableCollection.key, variableCollection);
33
42
  variableCollectionsIdMap.set(variableCollection.id, variableCollection);
34
43
  }
35
44
 
45
+ const variablesList = [...variablesKeyMap.values()];
46
+ const variableCollectionsList = [...variableCollectionsKeyMap.values()];
47
+
36
48
  return {
37
49
  getVariableList: () => variablesList,
38
50
  getVariableCollectionList: () => variableCollectionsList,
51
+ findVariableByName: (name: string) => variablesNameMap.get(name),
39
52
  findVariableByKey: (key: string) => variablesKeyMap.get(key),
40
53
  findVariableById: (id: string) => variablesIdMap.get(id),
41
54
  findVariableCollectionByKey: (key: string) => variableCollectionsKeyMap.get(key),
@@ -4,17 +4,19 @@ import type { VariableRepository } from "./variable.repository";
4
4
 
5
5
  export interface VariableService {
6
6
  getVariableName: (id: string) => string;
7
- inferVariableName: (scope: VariableScope, value: number | string | boolean) => string | undefined;
7
+ inferVariableName: (value: VariableValueResolved, scope: VariableScope) => string | undefined;
8
8
  }
9
9
 
10
10
  export interface VariableServiceDeps {
11
11
  variableRepository: VariableRepository;
12
12
  variableNameTransformer: ({ slug }: { slug: string[] }) => string;
13
+ inferCompareFunction: (name1: string, name2: string) => number;
13
14
  }
14
15
 
15
16
  export function createVariableService({
16
17
  variableRepository,
17
18
  variableNameTransformer,
19
+ inferCompareFunction,
18
20
  }: VariableServiceDeps): VariableService {
19
21
  const variables = variableRepository.getVariableList();
20
22
 
@@ -67,25 +69,61 @@ export function createVariableService({
67
69
  return value;
68
70
  }
69
71
 
72
+ function isIdenticalVariableValue(value1: VariableValueResolved, value2: VariableValueResolved) {
73
+ if (typeof value1 !== typeof value2) {
74
+ return false;
75
+ }
76
+
77
+ if (typeof value1 === "string" || typeof value1 === "number" || typeof value1 === "boolean") {
78
+ return value1 === value2;
79
+ }
80
+
81
+ return (
82
+ value1.r === (value2 as RGBA).r &&
83
+ value1.g === (value2 as RGBA).g &&
84
+ value1.b === (value2 as RGBA).b &&
85
+ value1.a === (value2 as RGBA).a
86
+ );
87
+ }
88
+
89
+ function isInsideScope(variable: Variable, scope: VariableScope) {
90
+ if (variable.scopes.includes("ALL_SCOPES")) {
91
+ return true;
92
+ }
93
+
94
+ if (variable.scopes.includes("ALL_FILLS")) {
95
+ if (scope === "FRAME_FILL" || scope === "SHAPE_FILL" || scope === "TEXT_FILL") {
96
+ return true;
97
+ }
98
+ }
99
+
100
+ return variable.scopes.includes(scope);
101
+ }
102
+
70
103
  // public
71
104
  function getVariableName(key: string) {
72
105
  const slug = getFigmaVariableSlug(key);
73
106
  return variableNameTransformer({ slug });
74
107
  }
75
108
 
76
- function inferVariableName(scope: VariableScope, value: number | string | boolean) {
109
+ function inferVariableName(value: VariableValueResolved, scope: VariableScope) {
77
110
  // NOTE: We assume that the variable is in the default mode or value is equal between all modes for simplicity.
78
- const inferredVariable = variables.find(
111
+ const inferredVariables = variables.filter(
79
112
  (variable) =>
80
- variable.scopes.includes(scope) &&
81
- resolveVariableValue(variable.id, getDefaultModeId(variable)) === value,
113
+ isInsideScope(variable, scope) &&
114
+ isIdenticalVariableValue(
115
+ resolveVariableValue(variable.id, getDefaultModeId(variable)),
116
+ value,
117
+ ),
82
118
  );
83
119
 
84
- if (!inferredVariable) {
85
- return undefined;
86
- }
120
+ const inferredVariableNames = inferredVariables.map((variable) =>
121
+ getVariableName(variable.key),
122
+ );
123
+
124
+ const sortedVariableNames = inferredVariableNames.sort(inferCompareFunction);
87
125
 
88
- return getVariableName(inferredVariable.key);
126
+ return sortedVariableNames[0];
89
127
  }
90
128
 
91
129
  return {
package/src/index.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from "./normalizer";
2
2
  export * from "./codegen";
3
+ export * from "./entities";