@seed-design/figma 0.0.2 → 0.0.5

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 (185) hide show
  1. package/lib/index.cjs +10622 -12209
  2. package/lib/index.d.ts +1734 -18
  3. package/lib/index.js +10598 -12210
  4. package/package.json +2 -2
  5. package/src/codegen/context.ts +148 -0
  6. package/src/{component/type-helper.ts → codegen/core/component.ts} +1 -1
  7. package/src/codegen/core/index.ts +14 -0
  8. package/src/{jsx.ts → codegen/core/jsx.ts} +13 -3
  9. package/src/codegen/core/transformer.ts +40 -0
  10. package/src/{data → codegen/data}/icons.ts +2 -6
  11. package/src/{data → codegen/data}/styles.ts +87 -29
  12. package/src/codegen/data/variable-collections.ts +310 -0
  13. package/src/{data → codegen/data}/variables.ts +2821 -5887
  14. package/src/codegen/domain/codegen.service.ts +69 -0
  15. package/src/codegen/domain/figma-component.service.ts +21 -0
  16. package/src/codegen/domain/frame.service.ts +108 -0
  17. package/src/codegen/domain/icon.interface.ts +5 -0
  18. package/src/codegen/domain/icon.repository.ts +11 -0
  19. package/src/codegen/domain/icon.service.ts +35 -0
  20. package/src/codegen/domain/index.ts +22 -0
  21. package/src/codegen/domain/instance.service.ts +91 -0
  22. package/src/codegen/domain/props/container-layout-props.service.ts +248 -0
  23. package/src/codegen/domain/props/fill-props.service.ts +75 -0
  24. package/src/codegen/domain/props/radius-props.service.ts +105 -0
  25. package/src/codegen/domain/props/self-layout-props.service.ts +127 -0
  26. package/src/codegen/domain/props/stroke-props.service.ts +45 -0
  27. package/src/codegen/domain/props/type-style-props.service.ts +31 -0
  28. package/src/codegen/domain/rectangle.service.ts +31 -0
  29. package/src/codegen/domain/seed-component/deps.interface.ts +6 -0
  30. package/src/codegen/domain/seed-component/index.ts +75 -0
  31. package/src/{component/type.ts → codegen/domain/seed-component/properties.type.ts} +2 -2
  32. package/src/{component/properties.ts → codegen/domain/seed-component/size.ts} +4 -2
  33. package/src/codegen/domain/seed-component/transformers/action-button.ts +69 -0
  34. package/src/codegen/domain/seed-component/transformers/action-chip.ts +82 -0
  35. package/src/codegen/domain/seed-component/transformers/action-sheet.ts +78 -0
  36. package/src/codegen/domain/seed-component/transformers/app-bar.ts +186 -0
  37. package/src/codegen/domain/seed-component/transformers/avatar-stack.ts +30 -0
  38. package/src/codegen/domain/seed-component/transformers/avatar.ts +39 -0
  39. package/src/codegen/domain/seed-component/transformers/badge.ts +22 -0
  40. package/src/codegen/domain/seed-component/transformers/callout.ts +90 -0
  41. package/src/codegen/domain/seed-component/transformers/checkbox.ts +34 -0
  42. package/src/codegen/domain/seed-component/transformers/chip-tabs.ts +55 -0
  43. package/src/codegen/domain/seed-component/transformers/control-chip.ts +86 -0
  44. package/src/codegen/domain/seed-component/transformers/error-state.ts +39 -0
  45. package/src/codegen/domain/seed-component/transformers/extended-action-sheet.ts +99 -0
  46. package/src/codegen/domain/seed-component/transformers/extended-fab.ts +25 -0
  47. package/src/codegen/domain/seed-component/transformers/fab.ts +18 -0
  48. package/src/codegen/domain/seed-component/transformers/help-bubble.ts +68 -0
  49. package/src/codegen/domain/seed-component/transformers/identity-placeholder.ts +18 -0
  50. package/src/codegen/domain/seed-component/transformers/inline-banner.ts +81 -0
  51. package/src/codegen/domain/seed-component/transformers/manner-temp-badge.ts +19 -0
  52. package/src/codegen/domain/seed-component/transformers/multiline-text-field.ts +82 -0
  53. package/src/codegen/domain/seed-component/transformers/progress-circle.ts +51 -0
  54. package/src/codegen/domain/seed-component/transformers/reaction-button.ts +37 -0
  55. package/src/codegen/domain/seed-component/transformers/segmented-control.ts +59 -0
  56. package/src/codegen/domain/seed-component/transformers/select-box.ts +82 -0
  57. package/src/codegen/domain/seed-component/transformers/skeleton.ts +52 -0
  58. package/src/codegen/domain/seed-component/transformers/snackbar.ts +23 -0
  59. package/src/codegen/domain/seed-component/transformers/switch.ts +31 -0
  60. package/src/codegen/domain/seed-component/transformers/tabs.ts +129 -0
  61. package/src/codegen/domain/seed-component/transformers/text-button.ts +55 -0
  62. package/src/codegen/domain/seed-component/transformers/text-field.ts +109 -0
  63. package/src/codegen/domain/seed-component/transformers/toggle-button.ts +47 -0
  64. package/src/codegen/domain/style.interface.ts +5 -0
  65. package/src/codegen/domain/style.repository.ts +23 -0
  66. package/src/codegen/domain/style.service.ts +38 -0
  67. package/src/codegen/domain/text.service.ts +62 -0
  68. package/src/codegen/domain/variable.interface.ts +18 -0
  69. package/src/codegen/domain/variable.repository.ts +44 -0
  70. package/src/codegen/domain/variable.service.ts +95 -0
  71. package/src/codegen/index.ts +13 -0
  72. package/src/index.ts +2 -3
  73. package/src/normalizer/from-plugin.ts +52 -31
  74. package/src/normalizer/from-rest.ts +27 -5
  75. package/src/normalizer/index.ts +3 -0
  76. package/src/normalizer/types.ts +81 -31
  77. package/src/utils/common.ts +34 -0
  78. package/src/utils/css.ts +13 -0
  79. package/src/{node-util.ts → utils/figma-node.ts} +1 -1
  80. package/src/utils/figma-variable.ts +13 -0
  81. package/src/color.ts +0 -78
  82. package/src/component/index.ts +0 -1688
  83. package/src/generate-code.ts +0 -213
  84. package/src/icon.ts +0 -46
  85. package/src/layout.ts +0 -289
  86. package/src/sizing.ts +0 -58
  87. package/src/text.ts +0 -20
  88. package/src/util.ts +0 -17
  89. package/src/variable.ts +0 -66
  90. /package/src/{data → codegen/data}/__generated__/component-sets/action-button.d.ts +0 -0
  91. /package/src/{data → codegen/data}/__generated__/component-sets/action-button.mjs +0 -0
  92. /package/src/{data → codegen/data}/__generated__/component-sets/action-chip.d.ts +0 -0
  93. /package/src/{data → codegen/data}/__generated__/component-sets/action-chip.mjs +0 -0
  94. /package/src/{data → codegen/data}/__generated__/component-sets/action-sheet.d.ts +0 -0
  95. /package/src/{data → codegen/data}/__generated__/component-sets/action-sheet.mjs +0 -0
  96. /package/src/{data → codegen/data}/__generated__/component-sets/avatar-stack.d.ts +0 -0
  97. /package/src/{data → codegen/data}/__generated__/component-sets/avatar-stack.mjs +0 -0
  98. /package/src/{data → codegen/data}/__generated__/component-sets/avatar.d.ts +0 -0
  99. /package/src/{data → codegen/data}/__generated__/component-sets/avatar.mjs +0 -0
  100. /package/src/{data → codegen/data}/__generated__/component-sets/badge.d.ts +0 -0
  101. /package/src/{data → codegen/data}/__generated__/component-sets/badge.mjs +0 -0
  102. /package/src/{data → codegen/data}/__generated__/component-sets/bottom-navigation-global.d.ts +0 -0
  103. /package/src/{data → codegen/data}/__generated__/component-sets/bottom-navigation-global.mjs +0 -0
  104. /package/src/{data → codegen/data}/__generated__/component-sets/bottom-navigation-kr.d.ts +0 -0
  105. /package/src/{data → codegen/data}/__generated__/component-sets/bottom-navigation-kr.mjs +0 -0
  106. /package/src/{data → codegen/data}/__generated__/component-sets/bottom-sheet.d.ts +0 -0
  107. /package/src/{data → codegen/data}/__generated__/component-sets/bottom-sheet.mjs +0 -0
  108. /package/src/{data → codegen/data}/__generated__/component-sets/callout.d.ts +0 -0
  109. /package/src/{data → codegen/data}/__generated__/component-sets/callout.mjs +0 -0
  110. /package/src/{data → codegen/data}/__generated__/component-sets/checkbox.d.ts +0 -0
  111. /package/src/{data → codegen/data}/__generated__/component-sets/checkbox.mjs +0 -0
  112. /package/src/{data → codegen/data}/__generated__/component-sets/chip-tablist.d.ts +0 -0
  113. /package/src/{data → codegen/data}/__generated__/component-sets/chip-tablist.mjs +0 -0
  114. /package/src/{data → codegen/data}/__generated__/component-sets/control-chip.d.ts +0 -0
  115. /package/src/{data → codegen/data}/__generated__/component-sets/control-chip.mjs +0 -0
  116. /package/src/{data → codegen/data}/__generated__/component-sets/divider.d.ts +0 -0
  117. /package/src/{data → codegen/data}/__generated__/component-sets/divider.mjs +0 -0
  118. /package/src/{data → codegen/data}/__generated__/component-sets/error-state.d.ts +0 -0
  119. /package/src/{data → codegen/data}/__generated__/component-sets/error-state.mjs +0 -0
  120. /package/src/{data → codegen/data}/__generated__/component-sets/extended-action-sheet.d.ts +0 -0
  121. /package/src/{data → codegen/data}/__generated__/component-sets/extended-action-sheet.mjs +0 -0
  122. /package/src/{data → codegen/data}/__generated__/component-sets/extended-floating-action-button.d.ts +0 -0
  123. /package/src/{data → codegen/data}/__generated__/component-sets/extended-floating-action-button.mjs +0 -0
  124. /package/src/{data → codegen/data}/__generated__/component-sets/floating-action-button.d.ts +0 -0
  125. /package/src/{data → codegen/data}/__generated__/component-sets/floating-action-button.mjs +0 -0
  126. /package/src/{data → codegen/data}/__generated__/component-sets/help-bubble.d.ts +0 -0
  127. /package/src/{data → codegen/data}/__generated__/component-sets/help-bubble.mjs +0 -0
  128. /package/src/{data → codegen/data}/__generated__/component-sets/identity-placeholder.d.ts +0 -0
  129. /package/src/{data → codegen/data}/__generated__/component-sets/identity-placeholder.mjs +0 -0
  130. /package/src/{data → codegen/data}/__generated__/component-sets/index.d.ts +0 -0
  131. /package/src/{data → codegen/data}/__generated__/component-sets/index.mjs +0 -0
  132. /package/src/{data → codegen/data}/__generated__/component-sets/inline-banner.d.ts +0 -0
  133. /package/src/{data → codegen/data}/__generated__/component-sets/inline-banner.mjs +0 -0
  134. /package/src/{data → codegen/data}/__generated__/component-sets/main-tab-navigation-global.d.ts +0 -0
  135. /package/src/{data → codegen/data}/__generated__/component-sets/main-tab-navigation-global.mjs +0 -0
  136. /package/src/{data → codegen/data}/__generated__/component-sets/main-tab-navigation-kr.d.ts +0 -0
  137. /package/src/{data → codegen/data}/__generated__/component-sets/main-tab-navigation-kr.mjs +0 -0
  138. /package/src/{data → codegen/data}/__generated__/component-sets/manner-temp-badge.d.ts +0 -0
  139. /package/src/{data → codegen/data}/__generated__/component-sets/manner-temp-badge.mjs +0 -0
  140. /package/src/{data → codegen/data}/__generated__/component-sets/manner-temp-bar.d.ts +0 -0
  141. /package/src/{data → codegen/data}/__generated__/component-sets/manner-temp-bar.mjs +0 -0
  142. /package/src/{data → codegen/data}/__generated__/component-sets/manner-temp.d.ts +0 -0
  143. /package/src/{data → codegen/data}/__generated__/component-sets/manner-temp.mjs +0 -0
  144. /package/src/{data → codegen/data}/__generated__/component-sets/multiline-text-field.d.ts +0 -0
  145. /package/src/{data → codegen/data}/__generated__/component-sets/multiline-text-field.mjs +0 -0
  146. /package/src/{data → codegen/data}/__generated__/component-sets/progress-circle.d.ts +0 -0
  147. /package/src/{data → codegen/data}/__generated__/component-sets/progress-circle.mjs +0 -0
  148. /package/src/{data → codegen/data}/__generated__/component-sets/radio.d.ts +0 -0
  149. /package/src/{data → codegen/data}/__generated__/component-sets/radio.mjs +0 -0
  150. /package/src/{data → codegen/data}/__generated__/component-sets/range-slider.d.ts +0 -0
  151. /package/src/{data → codegen/data}/__generated__/component-sets/range-slider.mjs +0 -0
  152. /package/src/{data → codegen/data}/__generated__/component-sets/reaction-button.d.ts +0 -0
  153. /package/src/{data → codegen/data}/__generated__/component-sets/reaction-button.mjs +0 -0
  154. /package/src/{data → codegen/data}/__generated__/component-sets/segmented-control.d.ts +0 -0
  155. /package/src/{data → codegen/data}/__generated__/component-sets/segmented-control.mjs +0 -0
  156. /package/src/{data → codegen/data}/__generated__/component-sets/select-box.d.ts +0 -0
  157. /package/src/{data → codegen/data}/__generated__/component-sets/select-box.mjs +0 -0
  158. /package/src/{data → codegen/data}/__generated__/component-sets/skeleton.d.ts +0 -0
  159. /package/src/{data → codegen/data}/__generated__/component-sets/skeleton.mjs +0 -0
  160. /package/src/{data → codegen/data}/__generated__/component-sets/slider.d.ts +0 -0
  161. /package/src/{data → codegen/data}/__generated__/component-sets/slider.mjs +0 -0
  162. /package/src/{data → codegen/data}/__generated__/component-sets/snackbar.d.ts +0 -0
  163. /package/src/{data → codegen/data}/__generated__/component-sets/snackbar.mjs +0 -0
  164. /package/src/{data → codegen/data}/__generated__/component-sets/standard-navigation.d.ts +0 -0
  165. /package/src/{data → codegen/data}/__generated__/component-sets/standard-navigation.mjs +0 -0
  166. /package/src/{data → codegen/data}/__generated__/component-sets/switch.d.ts +0 -0
  167. /package/src/{data → codegen/data}/__generated__/component-sets/switch.mjs +0 -0
  168. /package/src/{data → codegen/data}/__generated__/component-sets/tablist.d.ts +0 -0
  169. /package/src/{data → codegen/data}/__generated__/component-sets/tablist.mjs +0 -0
  170. /package/src/{data → codegen/data}/__generated__/component-sets/template-bottom-fixed-bar.d.ts +0 -0
  171. /package/src/{data → codegen/data}/__generated__/component-sets/template-bottom-fixed-bar.mjs +0 -0
  172. /package/src/{data → codegen/data}/__generated__/component-sets/template-button-group.d.ts +0 -0
  173. /package/src/{data → codegen/data}/__generated__/component-sets/template-button-group.mjs +0 -0
  174. /package/src/{data → codegen/data}/__generated__/component-sets/template-chip-group.d.ts +0 -0
  175. /package/src/{data → codegen/data}/__generated__/component-sets/template-chip-group.mjs +0 -0
  176. /package/src/{data → codegen/data}/__generated__/component-sets/template-select-box-group.d.ts +0 -0
  177. /package/src/{data → codegen/data}/__generated__/component-sets/template-select-box-group.mjs +0 -0
  178. /package/src/{data → codegen/data}/__generated__/component-sets/template-top-navigation.d.ts +0 -0
  179. /package/src/{data → codegen/data}/__generated__/component-sets/template-top-navigation.mjs +0 -0
  180. /package/src/{data → codegen/data}/__generated__/component-sets/text-button.d.ts +0 -0
  181. /package/src/{data → codegen/data}/__generated__/component-sets/text-button.mjs +0 -0
  182. /package/src/{data → codegen/data}/__generated__/component-sets/text-field.d.ts +0 -0
  183. /package/src/{data → codegen/data}/__generated__/component-sets/text-field.mjs +0 -0
  184. /package/src/{data → codegen/data}/__generated__/component-sets/toggle-button.d.ts +0 -0
  185. /package/src/{data → codegen/data}/__generated__/component-sets/toggle-button.mjs +0 -0
@@ -1,213 +0,0 @@
1
- import { camelCase } from "change-case";
2
- import { createBackgroundProps, createBorderProps } from "./color";
3
- import { componentHandlerMap, ignoredComponentKeys } from "./component";
4
- import { iconRecord } from "./data/icons";
5
- import { createIconTagNameFromKey, createMonochromeIconColorProps, isIconComponent } from "./icon";
6
- import type { ElementNode } from "./jsx";
7
- import { createElement, stringifyElement } from "./jsx";
8
- import { createLayoutProps } from "./layout";
9
- import type {
10
- NormalizedComponentNode,
11
- NormalizedFrameNode,
12
- NormalizedInstanceNode,
13
- NormalizedRectangleNode,
14
- NormalizedSceneNode,
15
- NormalizedTextNode,
16
- } from "./normalizer/types";
17
- import { createSizingProps } from "./sizing";
18
- import { createTextProps } from "./text";
19
- import { getColorVariableName, getLayoutVariableName, inferDimension } from "./variable";
20
- import { FIGMA_TEXT_STYLES } from "./data/styles";
21
- import { compactObject } from "./util";
22
-
23
- export async function generateCode(selection: NormalizedSceneNode) {
24
- async function handleFrameNode(
25
- node: NormalizedFrameNode | NormalizedComponentNode | NormalizedInstanceNode,
26
- ) {
27
- const children = node.children;
28
-
29
- const props = {
30
- ...createLayoutProps(node),
31
- ...createSizingProps(node),
32
- ...createBackgroundProps(node),
33
- ...createBorderProps(node),
34
- };
35
-
36
- if (
37
- props.flexDirection === "row" &&
38
- props.alignItems === "flexStart" &&
39
- props.justifyContent === "flexStart" &&
40
- props.flexWrap === "wrap"
41
- ) {
42
- const { flexDirection, flexWrap, alignItems, justifyContent, ...rest } = props;
43
-
44
- return createElement("Inline", rest, await Promise.all(children.map(traverse)));
45
- }
46
-
47
- if (
48
- props.flexDirection === "row" &&
49
- props.justifyContent === "flexStart" &&
50
- props.flexWrap === "nowrap"
51
- ) {
52
- const { flexDirection, flexWrap, justifyContent, ...rest } = props;
53
-
54
- const childrenResult = await Promise.all(children.map(traverse));
55
-
56
- return createElement(
57
- "Columns",
58
- rest,
59
- childrenResult.map((child) => createElement("Column", {}, child)),
60
- );
61
- }
62
-
63
- if (props.flexDirection === "column") {
64
- const { flexDirection, ...rest } = props;
65
-
66
- return createElement("Stack", rest, await Promise.all(children.map(traverse)));
67
- }
68
-
69
- return createElement("Flex", props, await Promise.all(children.map(traverse)));
70
- }
71
-
72
- function handleTextNode(node: NormalizedTextNode): ElementNode {
73
- const maxLines =
74
- node.style.textTruncation === "ENDING" ? (node.style.maxLines ?? undefined) : undefined;
75
-
76
- if (node.fills.length > 1) {
77
- throw new Error("Expected a single fill");
78
- }
79
-
80
- const onlyFill = node.fills.length === 1 ? node.fills[0] : null;
81
- const fillBoundVariableId =
82
- onlyFill && onlyFill.type === "SOLID" ? (onlyFill.boundVariables?.color?.id ?? null) : null;
83
- const color = fillBoundVariableId ? getColorVariableName(fillBoundVariableId) : undefined;
84
-
85
- const style = FIGMA_TEXT_STYLES.find((s) => s.key === node.textStyleKey);
86
-
87
- if (style) {
88
- const styleNameSlugs = style.name.split("/");
89
- const styleName = styleNameSlugs[styleNameSlugs.length - 1]!;
90
- return createElement(
91
- "Text",
92
- compactObject({
93
- textStyle: camelCase(styleName, { mergeAmbiguousCharacters: true }),
94
- maxLines,
95
- color,
96
- }),
97
- node.characters.replace(/\n/g, "<br />"),
98
- color ? "" : "color 프로퍼티는 반영되지 않았습니다.",
99
- );
100
- }
101
-
102
- const { fontSize, fontWeight, lineHeight } = createTextProps(node.boundVariables);
103
-
104
- return createElement(
105
- "Text",
106
- compactObject({
107
- fontSize,
108
- fontWeight,
109
- lineHeight,
110
- color,
111
- }),
112
- node.characters.replace(/\n/g, "<br />"),
113
- );
114
- }
115
-
116
- async function handleRectangleNode(node: NormalizedRectangleNode) {
117
- return createElement(
118
- "Box",
119
- { ...createSizingProps(node), background: "palette.gray200" },
120
- undefined,
121
- "Rectangle Node Placeholder",
122
- );
123
- }
124
-
125
- async function handleComponentNode(node: NormalizedComponentNode) {
126
- return await handleFrameNode(node);
127
- }
128
-
129
- async function handleInstanceNode(node: NormalizedInstanceNode) {
130
- const { componentKey, componentSetKey } = node;
131
-
132
- if (isIconComponent(componentKey)) {
133
- const iconElement = createElement(createIconTagNameFromKey(componentKey));
134
-
135
- switch (iconRecord[componentKey]?.type) {
136
- case "monochrome":
137
- return createElement("Icon", {
138
- size:
139
- getLayoutVariableName(node.boundVariables?.size?.x?.id) ??
140
- inferDimension(node.absoluteBoundingBox?.width ?? 0),
141
- ...createMonochromeIconColorProps(node),
142
- svg: iconElement,
143
- });
144
- case "multicolor":
145
- return iconElement;
146
- default:
147
- return createElement("Icon", {
148
- size:
149
- getLayoutVariableName(node.boundVariables?.size?.x?.id) ??
150
- inferDimension(node.absoluteBoundingBox?.width ?? 0),
151
- svg: iconElement,
152
- ...createMonochromeIconColorProps(node),
153
- });
154
- }
155
- }
156
-
157
- if (ignoredComponentKeys.has(componentSetKey ?? componentKey)) {
158
- return;
159
- }
160
-
161
- const componentData = componentSetKey
162
- ? componentHandlerMap.get(componentSetKey)
163
- : componentHandlerMap.get(componentKey);
164
-
165
- if (componentData) {
166
- return componentData.codegen(node);
167
- }
168
-
169
- // if (node.id === selection.id) {
170
- return await handleFrameNode(node);
171
- // }
172
-
173
- // const mainComponent = node.mainComponent;
174
-
175
- // return createElement(
176
- // mainComponent.parent?.type === "COMPONENT_SET"
177
- // ? mainComponent.parent.name
178
- // : mainComponent.name,
179
- // Object.fromEntries(
180
- // Object.entries(node.componentProperties)
181
- // .filter(([_, props]) => props.type === "VARIANT" || props.type === "TEXT")
182
- // .map(([key, props]) => [camelCase(key), camelCase(props.value as string)]),
183
- // ),
184
- // undefined,
185
- // "Custom Component",
186
- // );
187
- }
188
-
189
- async function traverse(node: NormalizedSceneNode): Promise<ElementNode | undefined> {
190
- if ("visible" in node && !node.visible) {
191
- return;
192
- }
193
-
194
- if (node.type === "FRAME") return await handleFrameNode(node);
195
- if (node.type === "TEXT") return handleTextNode(node);
196
- if (node.type === "RECTANGLE") return await handleRectangleNode(node);
197
- if (node.type === "COMPONENT") return await handleComponentNode(node);
198
- if (node.type === "INSTANCE") return await handleInstanceNode(node);
199
-
200
- return;
201
- }
202
-
203
- try {
204
- const rootEl = await traverse(selection);
205
- if (!rootEl) {
206
- return "";
207
- }
208
- return stringifyElement(rootEl);
209
- } catch (e) {
210
- console.error(e);
211
- return "";
212
- }
213
- }
package/src/icon.ts DELETED
@@ -1,46 +0,0 @@
1
- import { pascalCase } from "change-case";
2
-
3
- import { createColorProps } from "./color";
4
- import { iconRecord } from "./data/icons";
5
- import type { NormalizedInstanceNode } from "./normalizer/types";
6
-
7
- export function isIconComponent(componentKey: string) {
8
- return !!iconRecord[componentKey];
9
- }
10
-
11
- export function createIconTagNameFromKey(key?: string) {
12
- if (!key) {
13
- return "UnknownIcon";
14
- }
15
-
16
- const iconData = iconRecord[key];
17
- if (!iconData) {
18
- throw new Error(`Icon not found: ${key}`);
19
- }
20
-
21
- const { name, weight } = iconData;
22
-
23
- return pascalCase(`${name}${weight ? weight : ""}`);
24
- }
25
-
26
- export function createMonochromeIconColorProps(node: NormalizedInstanceNode) {
27
- if (node.children.length === 0) {
28
- throw new Error("Icon node has no children");
29
- }
30
-
31
- const icons = node.children.filter(
32
- (child) => child.type === "VECTOR" || child.type === "BOOLEAN_OPERATION",
33
- );
34
-
35
- const colorProps = icons.map(createColorProps);
36
-
37
- const fills = new Set(
38
- colorProps.map((props) => props.color).filter((color) => color !== undefined),
39
- );
40
-
41
- if (fills.size > 1) {
42
- throw new Error(`Children of the icon node ${node.name} has multiple colors`);
43
- }
44
-
45
- return { color: fills.values().next().value };
46
- }
package/src/layout.ts DELETED
@@ -1,289 +0,0 @@
1
- import type {
2
- NormalizedComponentNode,
3
- NormalizedFrameNode,
4
- NormalizedInstanceNode,
5
- NormalizedSceneNode,
6
- } from "./normalizer/types";
7
- import { getLayoutVariableName, inferDimension, inferRadius } from "./variable";
8
-
9
- interface FigmaLayoutProps {
10
- layoutMode?: NormalizedFrameNode["layoutMode"];
11
- layoutWrap?: NormalizedFrameNode["layoutWrap"];
12
- paddingLeft?: NormalizedFrameNode["paddingLeft"];
13
- paddingRight?: NormalizedFrameNode["paddingRight"];
14
- paddingTop?: NormalizedFrameNode["paddingTop"];
15
- paddingBottom?: NormalizedFrameNode["paddingBottom"];
16
- primaryAxisAlignItems?: NormalizedFrameNode["primaryAxisAlignItems"];
17
- counterAxisAlignItems?: NormalizedFrameNode["counterAxisAlignItems"];
18
- primaryAxisSizingMode?: NormalizedFrameNode["primaryAxisSizingMode"];
19
- counterAxisSizingMode?: NormalizedFrameNode["counterAxisSizingMode"];
20
- layoutGrow?: NormalizedFrameNode["layoutGrow"];
21
- layoutAlign?: NormalizedFrameNode["layoutAlign"];
22
- itemSpacing?: NormalizedFrameNode["itemSpacing"];
23
- counterAxisSpacing?: NormalizedFrameNode["counterAxisSpacing"];
24
- boundVariables?: NormalizedFrameNode["boundVariables"];
25
- cornerRadius?: NormalizedFrameNode["cornerRadius"];
26
- rectangleCornerRadii?: NormalizedFrameNode["rectangleCornerRadii"];
27
- children: NormalizedSceneNode[];
28
- }
29
-
30
- type LayoutPropHandler = (props: FigmaLayoutProps) => string | number | boolean | undefined;
31
-
32
- const layoutPropHandlers = {
33
- flexDirection: ({ layoutMode }) => (layoutMode === "HORIZONTAL" ? "row" : "column"),
34
- justifyContent: ({ primaryAxisAlignItems }) => {
35
- switch (primaryAxisAlignItems) {
36
- case "MIN":
37
- return "flexStart";
38
- case "CENTER":
39
- return "center";
40
- case "MAX":
41
- return "flexEnd";
42
- case "SPACE_BETWEEN":
43
- return "spaceBetween";
44
- }
45
- },
46
- alignItems: ({ counterAxisAlignItems, children }) => {
47
- const isStretch = children.every((child) => {
48
- if (!("layoutAlign" in child)) {
49
- return false;
50
- }
51
-
52
- return child.layoutAlign === "STRETCH";
53
- });
54
-
55
- if (isStretch) {
56
- return "stretch";
57
- }
58
-
59
- switch (counterAxisAlignItems) {
60
- case "MIN":
61
- return "flexStart";
62
- case "CENTER":
63
- return "center";
64
- case "MAX":
65
- return "flexEnd";
66
- case "BASELINE":
67
- return "baseline";
68
- }
69
- },
70
- flexWrap: ({ layoutWrap }) => (layoutWrap === "WRAP" ? "wrap" : "nowrap"),
71
- flexGrow: ({ layoutGrow }) => layoutGrow,
72
- alignSelf: ({ layoutAlign }) => {
73
- switch (layoutAlign) {
74
- case "STRETCH":
75
- return "stretch";
76
- case "MIN":
77
- return "flexStart";
78
- case "CENTER":
79
- return "center";
80
- case "MAX":
81
- return "flexEnd";
82
- }
83
- },
84
- gap: ({ itemSpacing, boundVariables, primaryAxisAlignItems, children }) =>
85
- children.length <= 1
86
- ? 0
87
- : primaryAxisAlignItems === "SPACE_BETWEEN"
88
- ? 0
89
- : boundVariables?.itemSpacing
90
- ? getLayoutVariableName(boundVariables.itemSpacing.id)
91
- : inferDimension(itemSpacing ?? 0),
92
- paddingTop: ({ paddingTop, boundVariables }) =>
93
- boundVariables?.paddingTop
94
- ? getLayoutVariableName(boundVariables.paddingTop.id)
95
- : inferDimension(paddingTop ?? 0),
96
- paddingBottom: ({ paddingBottom, boundVariables }) =>
97
- boundVariables?.paddingBottom
98
- ? getLayoutVariableName(boundVariables.paddingBottom.id)
99
- : inferDimension(paddingBottom ?? 0),
100
- paddingLeft: ({ paddingLeft, boundVariables }) =>
101
- boundVariables?.paddingLeft
102
- ? getLayoutVariableName(boundVariables.paddingLeft.id)
103
- : inferDimension(paddingLeft ?? 0),
104
- paddingRight: ({ paddingRight, boundVariables }) =>
105
- boundVariables?.paddingRight
106
- ? getLayoutVariableName(boundVariables.paddingRight.id)
107
- : inferDimension(paddingRight ?? 0),
108
- borderRadius: ({ cornerRadius, boundVariables }) => {
109
- // If all corner radii are the same, use the first one
110
- if (
111
- cornerRadius &&
112
- boundVariables?.bottomLeftRadius === boundVariables?.bottomRightRadius &&
113
- boundVariables?.bottomLeftRadius === boundVariables?.topLeftRadius &&
114
- boundVariables?.bottomLeftRadius === boundVariables?.topRightRadius
115
- ) {
116
- return boundVariables?.bottomLeftRadius
117
- ? getLayoutVariableName(boundVariables.bottomLeftRadius.id)
118
- : inferRadius(cornerRadius ?? 0);
119
- }
120
-
121
- // TODO: handle individual corner radii
122
- return undefined;
123
- },
124
- borderTopLeftRadius: ({ rectangleCornerRadii, boundVariables }) =>
125
- boundVariables?.topLeftRadius
126
- ? getLayoutVariableName(boundVariables.topLeftRadius.id)
127
- : inferRadius(rectangleCornerRadii?.[0] ?? 0),
128
- borderTopRightRadius: ({ rectangleCornerRadii, boundVariables }) =>
129
- boundVariables?.topRightRadius
130
- ? getLayoutVariableName(boundVariables.topRightRadius.id)
131
- : inferRadius(rectangleCornerRadii?.[1] ?? 0),
132
- borderBottomLeftRadius: ({ rectangleCornerRadii, boundVariables }) =>
133
- boundVariables?.bottomLeftRadius
134
- ? getLayoutVariableName(boundVariables.bottomLeftRadius.id)
135
- : inferRadius(rectangleCornerRadii?.[2] ?? 0),
136
- borderBottomRightRadius: ({ rectangleCornerRadii, boundVariables }) =>
137
- boundVariables?.bottomRightRadius
138
- ? getLayoutVariableName(boundVariables.bottomRightRadius.id)
139
- : inferRadius(rectangleCornerRadii?.[3] ?? 0),
140
- } satisfies Record<string, LayoutPropHandler>;
141
-
142
- type LayoutProps = keyof typeof layoutPropHandlers;
143
-
144
- type LayoutShorthandHandler = (props: Record<LayoutProps, string | number | boolean | undefined>) =>
145
- | {
146
- value: string | number | boolean | undefined;
147
- exclude: LayoutProps[];
148
- }
149
- | undefined;
150
-
151
- const layoutShorthandHandlers = {
152
- paddingX: ({ paddingLeft, paddingRight, paddingTop, paddingBottom }) => {
153
- if (
154
- paddingLeft === paddingRight &&
155
- paddingTop === paddingBottom &&
156
- paddingLeft === paddingTop
157
- ) {
158
- return undefined;
159
- }
160
- if (paddingLeft === paddingRight) {
161
- const value =
162
- paddingLeft === "globalGutter" || paddingLeft === "betweenChips"
163
- ? `spacingX.${paddingLeft}`
164
- : paddingLeft;
165
- return {
166
- value,
167
- exclude: ["paddingLeft", "paddingRight"],
168
- };
169
- }
170
- return undefined;
171
- },
172
- paddingY: ({ paddingLeft, paddingRight, paddingTop, paddingBottom }) => {
173
- if (
174
- paddingLeft === paddingRight &&
175
- paddingTop === paddingBottom &&
176
- paddingLeft === paddingTop
177
- ) {
178
- return undefined;
179
- }
180
- if (paddingTop === paddingBottom) {
181
- return {
182
- value: paddingTop,
183
- exclude: ["paddingTop", "paddingBottom"],
184
- };
185
- }
186
- return undefined;
187
- },
188
- padding: ({ paddingLeft, paddingRight, paddingTop, paddingBottom }) => {
189
- if (
190
- paddingLeft === paddingRight &&
191
- paddingTop === paddingBottom &&
192
- paddingLeft === paddingTop
193
- ) {
194
- return {
195
- value: paddingLeft,
196
- exclude: ["paddingLeft", "paddingRight", "paddingTop", "paddingBottom"],
197
- };
198
- }
199
- return undefined;
200
- },
201
- } satisfies Record<string, LayoutShorthandHandler>;
202
-
203
- type LayoutShorthandProps = keyof typeof layoutShorthandHandlers;
204
-
205
- const layoutPropDefaults: Record<string, string | number | boolean> = {
206
- flexDirection: "row",
207
- justifyContent: "flexStart",
208
- alignItems: "stretch",
209
- flexWrap: "nowrap",
210
- flexGrow: 0,
211
- alignSelf: "auto",
212
- gap: 0,
213
- padding: 0,
214
- paddingX: 0,
215
- paddingY: 0,
216
- paddingBottom: 0,
217
- paddingLeft: 0,
218
- paddingRight: 0,
219
- paddingTop: 0,
220
- borderRadius: 0,
221
- borderTopLeftRadius: 0,
222
- borderTopRightRadius: 0,
223
- borderBottomLeftRadius: 0,
224
- borderBottomRightRadius: 0,
225
- } satisfies Record<LayoutProps | LayoutShorthandProps, string | number | boolean>;
226
-
227
- type FrameLikeNode = NormalizedFrameNode | NormalizedComponentNode | NormalizedInstanceNode;
228
-
229
- export function createLayoutProps(
230
- node: FrameLikeNode,
231
- ): Record<LayoutProps | LayoutShorthandProps, string | number | boolean> {
232
- const boundVariables = node.boundVariables;
233
- const children = node.children;
234
-
235
- const autoLayoutProperties = {
236
- layoutMode: node.layoutMode,
237
- layoutWrap: node.layoutWrap,
238
- paddingLeft: node.paddingLeft,
239
- paddingRight: node.paddingRight,
240
- paddingTop: node.paddingTop,
241
- paddingBottom: node.paddingBottom,
242
- primaryAxisAlignItems: node.primaryAxisAlignItems,
243
- counterAxisAlignItems: node.counterAxisAlignItems,
244
- primaryAxisSizingMode: node.primaryAxisSizingMode,
245
- counterAxisSizingMode: node.counterAxisSizingMode,
246
- layoutGrow: node.layoutGrow,
247
- layoutAlign: node.layoutAlign,
248
- itemSpacing: node.itemSpacing,
249
- counterAxisSpacing: node.counterAxisSpacing,
250
- };
251
-
252
- const radiusProperties = {
253
- cornerRadius: node.cornerRadius,
254
- topLeftRadius: node.rectangleCornerRadii?.[0],
255
- topRightRadius: node.rectangleCornerRadii?.[1],
256
- bottomRightRadius: node.rectangleCornerRadii?.[2],
257
- bottomLeftRadius: node.rectangleCornerRadii?.[3],
258
- };
259
-
260
- const result: Record<string, string | number | boolean> = {};
261
-
262
- for (const [prop, handler] of Object.entries(layoutPropHandlers)) {
263
- const value = handler({
264
- ...autoLayoutProperties,
265
- ...radiusProperties,
266
- boundVariables,
267
- children,
268
- });
269
- if (value !== undefined && value !== layoutPropDefaults[prop]) {
270
- result[prop] = value;
271
- }
272
- }
273
-
274
- for (const [prop, handler] of Object.entries(layoutShorthandHandlers)) {
275
- const shorthandResult = handler(result);
276
- if (shorthandResult === undefined) {
277
- continue;
278
- }
279
- const { value, exclude } = shorthandResult;
280
- if (value !== undefined && value !== layoutPropDefaults[prop]) {
281
- result[prop] = value;
282
- for (const excludedProp of exclude) {
283
- delete result[excludedProp];
284
- }
285
- }
286
- }
287
-
288
- return result;
289
- }
package/src/sizing.ts DELETED
@@ -1,58 +0,0 @@
1
- import type { NormalizedFrameNode } from "./normalizer/types";
2
- import { getLayoutVariableName, inferDimension } from "./variable";
3
-
4
- type SizingPropHandler = (props: {
5
- boundVariables: NonNullable<NormalizedFrameNode["boundVariables"]>;
6
- layoutSizingHorizontal: FrameNode["layoutSizingHorizontal"];
7
- layoutSizingVertical: FrameNode["layoutSizingVertical"];
8
- width: FrameNode["width"];
9
- height: FrameNode["height"];
10
- }) => string | number | boolean | undefined;
11
-
12
- const sizingPropHandlers = {
13
- height: ({ boundVariables, layoutSizingVertical, height }) =>
14
- layoutSizingVertical === "FIXED"
15
- ? boundVariables.size?.y
16
- ? getLayoutVariableName(boundVariables.size.y.id)
17
- : inferDimension(height)
18
- : undefined,
19
- width: ({ boundVariables, layoutSizingHorizontal, width }) =>
20
- layoutSizingHorizontal === "FIXED"
21
- ? boundVariables.size?.x
22
- ? getLayoutVariableName(boundVariables.size.x.id)
23
- : inferDimension(width)
24
- : undefined,
25
- } satisfies Record<string, SizingPropHandler>;
26
-
27
- export function createSizingProps(
28
- node: Pick<
29
- NormalizedFrameNode,
30
- "boundVariables" | "layoutSizingHorizontal" | "layoutSizingVertical" | "absoluteBoundingBox"
31
- >,
32
- ): Record<string, string | number | boolean> {
33
- const boundVariables = node.boundVariables;
34
- const layoutSizingHorizontal = node.layoutSizingHorizontal ?? "FIXED";
35
- const layoutSizingVertical = node.layoutSizingVertical ?? "FIXED";
36
- const { width, height } = node.absoluteBoundingBox ?? { width: 0, height: 0 };
37
-
38
- if (!boundVariables) {
39
- return {};
40
- }
41
-
42
- const result: Record<string, string | number | boolean> = {};
43
-
44
- for (const [prop, handler] of Object.entries(sizingPropHandlers)) {
45
- const value = handler({
46
- boundVariables,
47
- layoutSizingHorizontal,
48
- layoutSizingVertical,
49
- width,
50
- height,
51
- });
52
- if (value !== undefined) {
53
- result[prop] = value;
54
- }
55
- }
56
-
57
- return result;
58
- }
package/src/text.ts DELETED
@@ -1,20 +0,0 @@
1
- import type { NormalizedTextNode } from "./normalizer/types";
2
- import { getTypographyVariableName } from "./variable";
3
-
4
- export function createTextProps(boundVariables: NormalizedTextNode["boundVariables"]) {
5
- const fontSizeBoundVariables = boundVariables?.fontSize?.[0];
6
- const fontStyleBoundVariables = boundVariables?.fontStyle?.[0];
7
- const lineHeightBoundVariables = boundVariables?.lineHeight?.[0];
8
-
9
- return {
10
- fontSize: fontSizeBoundVariables
11
- ? getTypographyVariableName(fontSizeBoundVariables.id)
12
- : undefined,
13
- fontWeight: fontStyleBoundVariables
14
- ? getTypographyVariableName(fontStyleBoundVariables.id)
15
- : undefined,
16
- lineHeight: lineHeightBoundVariables
17
- ? getTypographyVariableName(lineHeightBoundVariables.id)
18
- : undefined,
19
- };
20
- }
package/src/util.ts DELETED
@@ -1,17 +0,0 @@
1
- import type { NormalizedSceneNode } from "./normalizer/types";
2
-
3
- export function ensureArray<T>(maybeArray: T | T[]): T[] {
4
- if (Array.isArray(maybeArray)) {
5
- return maybeArray;
6
- }
7
-
8
- return [maybeArray];
9
- }
10
-
11
- export function exists<T>(value: T | null | undefined): value is T {
12
- return value != null;
13
- }
14
-
15
- export function compactObject<T extends Record<string, unknown>>(obj: T): T {
16
- return Object.fromEntries(Object.entries(obj).filter(([, value]) => value != null)) as T;
17
- }