@seed-design/figma 0.0.0-alpha-20260324091316

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 (181) hide show
  1. package/lib/codegen/index.cjs +23543 -0
  2. package/lib/codegen/index.d.ts +2957 -0
  3. package/lib/codegen/index.d.ts.map +1 -0
  4. package/lib/codegen/index.js +23514 -0
  5. package/lib/codegen/targets/react/index.cjs +31980 -0
  6. package/lib/codegen/targets/react/index.d.ts +308 -0
  7. package/lib/codegen/targets/react/index.d.ts.map +1 -0
  8. package/lib/codegen/targets/react/index.js +31961 -0
  9. package/lib/index.cjs +26905 -0
  10. package/lib/index.d.ts +221 -0
  11. package/lib/index.d.ts.map +1 -0
  12. package/lib/index.js +26884 -0
  13. package/package.json +56 -0
  14. package/src/codegen/component-properties.archive.ts +1019 -0
  15. package/src/codegen/component-properties.ts +369 -0
  16. package/src/codegen/core/codegen.ts +112 -0
  17. package/src/codegen/core/component-handler.ts +23 -0
  18. package/src/codegen/core/component-type-helper.ts +35 -0
  19. package/src/codegen/core/element-transformer.ts +13 -0
  20. package/src/codegen/core/index.ts +19 -0
  21. package/src/codegen/core/infer-layout.test.ts +286 -0
  22. package/src/codegen/core/infer-layout.ts +416 -0
  23. package/src/codegen/core/jsx.ts +174 -0
  24. package/src/codegen/core/props-converter.ts +78 -0
  25. package/src/codegen/core/value-resolver.ts +381 -0
  26. package/src/codegen/default-services.ts +44 -0
  27. package/src/codegen/index.ts +3 -0
  28. package/src/codegen/skip-components.ts +7 -0
  29. package/src/codegen/targets/figma/frame.ts +38 -0
  30. package/src/codegen/targets/figma/index.ts +6 -0
  31. package/src/codegen/targets/figma/instance.ts +36 -0
  32. package/src/codegen/targets/figma/pipeline.ts +106 -0
  33. package/src/codegen/targets/figma/props.ts +262 -0
  34. package/src/codegen/targets/figma/shape.ts +65 -0
  35. package/src/codegen/targets/figma/text.ts +30 -0
  36. package/src/codegen/targets/figma/value-resolver.ts +75 -0
  37. package/src/codegen/targets/index.ts +2 -0
  38. package/src/codegen/targets/react/component/deps.interface.ts +7 -0
  39. package/src/codegen/targets/react/component/handlers/action-button.ts +149 -0
  40. package/src/codegen/targets/react/component/handlers/alert-dialog.ts +120 -0
  41. package/src/codegen/targets/react/component/handlers/app-bar.ts +169 -0
  42. package/src/codegen/targets/react/component/handlers/archive/action-button.ts +144 -0
  43. package/src/codegen/targets/react/component/handlers/archive/alert-dialog.ts +122 -0
  44. package/src/codegen/targets/react/component/handlers/archive/app-bar.ts +149 -0
  45. package/src/codegen/targets/react/component/handlers/archive/avatar-stack.ts +35 -0
  46. package/src/codegen/targets/react/component/handlers/archive/avatar.ts +55 -0
  47. package/src/codegen/targets/react/component/handlers/archive/badge.ts +18 -0
  48. package/src/codegen/targets/react/component/handlers/archive/bottom-sheet.ts +70 -0
  49. package/src/codegen/targets/react/component/handlers/archive/callout.ts +88 -0
  50. package/src/codegen/targets/react/component/handlers/archive/checkbox.ts +43 -0
  51. package/src/codegen/targets/react/component/handlers/archive/checkmark.ts +29 -0
  52. package/src/codegen/targets/react/component/handlers/archive/chip.ts +90 -0
  53. package/src/codegen/targets/react/component/handlers/archive/contextual-floating-button.ts +52 -0
  54. package/src/codegen/targets/react/component/handlers/archive/divider.ts +25 -0
  55. package/src/codegen/targets/react/component/handlers/archive/field-button.ts +197 -0
  56. package/src/codegen/targets/react/component/handlers/archive/field.ts +167 -0
  57. package/src/codegen/targets/react/component/handlers/archive/floating-action-button.ts +48 -0
  58. package/src/codegen/targets/react/component/handlers/archive/help-bubble.ts +73 -0
  59. package/src/codegen/targets/react/component/handlers/archive/identity-placeholder.ts +21 -0
  60. package/src/codegen/targets/react/component/handlers/archive/index.ts +40 -0
  61. package/src/codegen/targets/react/component/handlers/archive/legacy-select-box.ts +89 -0
  62. package/src/codegen/targets/react/component/handlers/archive/legacy-text-field.ts +198 -0
  63. package/src/codegen/targets/react/component/handlers/archive/list-header.ts +20 -0
  64. package/src/codegen/targets/react/component/handlers/archive/list-item.ts +162 -0
  65. package/src/codegen/targets/react/component/handlers/archive/manner-temp-badge.ts +21 -0
  66. package/src/codegen/targets/react/component/handlers/archive/manner-temp.ts +18 -0
  67. package/src/codegen/targets/react/component/handlers/archive/menu-sheet.ts +108 -0
  68. package/src/codegen/targets/react/component/handlers/archive/page-banner.ts +101 -0
  69. package/src/codegen/targets/react/component/handlers/archive/progress-circle.ts +55 -0
  70. package/src/codegen/targets/react/component/handlers/archive/radio-group.ts +31 -0
  71. package/src/codegen/targets/react/component/handlers/archive/radiomark.ts +27 -0
  72. package/src/codegen/targets/react/component/handlers/archive/reaction-button.ts +37 -0
  73. package/src/codegen/targets/react/component/handlers/archive/result-section.ts +67 -0
  74. package/src/codegen/targets/react/component/handlers/archive/segmented-control.ts +64 -0
  75. package/src/codegen/targets/react/component/handlers/archive/skeleton.ts +26 -0
  76. package/src/codegen/targets/react/component/handlers/archive/slider.ts +114 -0
  77. package/src/codegen/targets/react/component/handlers/archive/snackbar.ts +25 -0
  78. package/src/codegen/targets/react/component/handlers/archive/switch.ts +39 -0
  79. package/src/codegen/targets/react/component/handlers/archive/switchmark.ts +26 -0
  80. package/src/codegen/targets/react/component/handlers/archive/tabs.ts +297 -0
  81. package/src/codegen/targets/react/component/handlers/archive/tag-group.ts +86 -0
  82. package/src/codegen/targets/react/component/handlers/archive/text-field.ts +264 -0
  83. package/src/codegen/targets/react/component/handlers/archive/toggle-button.ts +43 -0
  84. package/src/codegen/targets/react/component/handlers/avatar-stack.ts +38 -0
  85. package/src/codegen/targets/react/component/handlers/avatar.ts +58 -0
  86. package/src/codegen/targets/react/component/handlers/badge.ts +18 -0
  87. package/src/codegen/targets/react/component/handlers/bottom-sheet.ts +74 -0
  88. package/src/codegen/targets/react/component/handlers/callout.ts +88 -0
  89. package/src/codegen/targets/react/component/handlers/checkbox.ts +129 -0
  90. package/src/codegen/targets/react/component/handlers/checkmark.ts +29 -0
  91. package/src/codegen/targets/react/component/handlers/chip.ts +93 -0
  92. package/src/codegen/targets/react/component/handlers/content-placeholder.ts +20 -0
  93. package/src/codegen/targets/react/component/handlers/contextual-floating-button.ts +52 -0
  94. package/src/codegen/targets/react/component/handlers/divider.ts +25 -0
  95. package/src/codegen/targets/react/component/handlers/field-button.ts +192 -0
  96. package/src/codegen/targets/react/component/handlers/field.ts +164 -0
  97. package/src/codegen/targets/react/component/handlers/floating-action-button.ts +45 -0
  98. package/src/codegen/targets/react/component/handlers/help-bubble.ts +73 -0
  99. package/src/codegen/targets/react/component/handlers/identity-placeholder.ts +20 -0
  100. package/src/codegen/targets/react/component/handlers/image-frame.ts +147 -0
  101. package/src/codegen/targets/react/component/handlers/index.ts +43 -0
  102. package/src/codegen/targets/react/component/handlers/legacy-select-box.ts +87 -0
  103. package/src/codegen/targets/react/component/handlers/legacy-text-field.ts +196 -0
  104. package/src/codegen/targets/react/component/handlers/list-header.ts +20 -0
  105. package/src/codegen/targets/react/component/handlers/list-item.ts +163 -0
  106. package/src/codegen/targets/react/component/handlers/manner-temp-badge.ts +21 -0
  107. package/src/codegen/targets/react/component/handlers/manner-temp.ts +18 -0
  108. package/src/codegen/targets/react/component/handlers/menu-sheet.ts +111 -0
  109. package/src/codegen/targets/react/component/handlers/page-banner.ts +106 -0
  110. package/src/codegen/targets/react/component/handlers/progress-circle.ts +55 -0
  111. package/src/codegen/targets/react/component/handlers/radio-group.ts +109 -0
  112. package/src/codegen/targets/react/component/handlers/radiomark.ts +27 -0
  113. package/src/codegen/targets/react/component/handlers/reaction-button.ts +37 -0
  114. package/src/codegen/targets/react/component/handlers/result-section.ts +67 -0
  115. package/src/codegen/targets/react/component/handlers/segmented-control.ts +63 -0
  116. package/src/codegen/targets/react/component/handlers/select-box.ts +333 -0
  117. package/src/codegen/targets/react/component/handlers/skeleton.ts +26 -0
  118. package/src/codegen/targets/react/component/handlers/slider.ts +117 -0
  119. package/src/codegen/targets/react/component/handlers/snackbar.ts +25 -0
  120. package/src/codegen/targets/react/component/handlers/switch.ts +35 -0
  121. package/src/codegen/targets/react/component/handlers/switchmark.ts +26 -0
  122. package/src/codegen/targets/react/component/handlers/tabs.ts +298 -0
  123. package/src/codegen/targets/react/component/handlers/tag-group.ts +90 -0
  124. package/src/codegen/targets/react/component/handlers/text-field.ts +253 -0
  125. package/src/codegen/targets/react/component/handlers/toggle-button.ts +43 -0
  126. package/src/codegen/targets/react/component/index.ts +24 -0
  127. package/src/codegen/targets/react/component/size.ts +22 -0
  128. package/src/codegen/targets/react/element-factories.ts +59 -0
  129. package/src/codegen/targets/react/frame.ts +96 -0
  130. package/src/codegen/targets/react/icon.ts +55 -0
  131. package/src/codegen/targets/react/index.ts +7 -0
  132. package/src/codegen/targets/react/instance.ts +82 -0
  133. package/src/codegen/targets/react/pipeline.ts +133 -0
  134. package/src/codegen/targets/react/props.ts +417 -0
  135. package/src/codegen/targets/react/shape.ts +47 -0
  136. package/src/codegen/targets/react/text.ts +31 -0
  137. package/src/codegen/targets/react/value-resolver.ts +93 -0
  138. package/src/entities/component.interface.ts +7 -0
  139. package/src/entities/component.repository.ts +16 -0
  140. package/src/entities/data/__generated__/archive/component-sets/index.d.ts +2074 -0
  141. package/src/entities/data/__generated__/archive/component-sets/index.mjs +2074 -0
  142. package/src/entities/data/__generated__/archive/components/index.d.ts +116 -0
  143. package/src/entities/data/__generated__/archive/components/index.mjs +116 -0
  144. package/src/entities/data/__generated__/archive/styles/index.d.ts +3 -0
  145. package/src/entities/data/__generated__/archive/styles/index.mjs +429 -0
  146. package/src/entities/data/__generated__/archive/variable-collections/index.d.ts +3 -0
  147. package/src/entities/data/__generated__/archive/variable-collections/index.mjs +501 -0
  148. package/src/entities/data/__generated__/archive/variables/index.d.ts +3 -0
  149. package/src/entities/data/__generated__/archive/variables/index.mjs +7019 -0
  150. package/src/entities/data/__generated__/component-sets/index.d.ts +4325 -0
  151. package/src/entities/data/__generated__/component-sets/index.mjs +4325 -0
  152. package/src/entities/data/__generated__/components/index.d.ts +378 -0
  153. package/src/entities/data/__generated__/components/index.mjs +378 -0
  154. package/src/entities/data/__generated__/icons/index.d.ts +3 -0
  155. package/src/entities/data/__generated__/icons/index.mjs +3476 -0
  156. package/src/entities/data/__generated__/styles/index.d.ts +3 -0
  157. package/src/entities/data/__generated__/styles/index.mjs +436 -0
  158. package/src/entities/data/__generated__/variable-collections/index.d.ts +3 -0
  159. package/src/entities/data/__generated__/variable-collections/index.mjs +479 -0
  160. package/src/entities/data/__generated__/variables/index.d.ts +3 -0
  161. package/src/entities/data/__generated__/variables/index.mjs +6969 -0
  162. package/src/entities/icon.interface.ts +5 -0
  163. package/src/entities/icon.repository.ts +11 -0
  164. package/src/entities/icon.service.ts +26 -0
  165. package/src/entities/index.ts +60 -0
  166. package/src/entities/style.interface.ts +5 -0
  167. package/src/entities/style.repository.ts +27 -0
  168. package/src/entities/style.service.ts +36 -0
  169. package/src/entities/variable.interface.ts +18 -0
  170. package/src/entities/variable.repository.ts +57 -0
  171. package/src/entities/variable.service.ts +101 -0
  172. package/src/index.ts +3 -0
  173. package/src/normalizer/from-plugin.ts +602 -0
  174. package/src/normalizer/from-rest.ts +577 -0
  175. package/src/normalizer/index.ts +3 -0
  176. package/src/normalizer/types.ts +208 -0
  177. package/src/utils/common.ts +38 -0
  178. package/src/utils/css.ts +19 -0
  179. package/src/utils/figma-gradient.ts +72 -0
  180. package/src/utils/figma-node.ts +95 -0
  181. package/src/utils/figma-variable.ts +49 -0
@@ -0,0 +1,38 @@
1
+ import type {
2
+ NormalizedComponentNode,
3
+ NormalizedFrameNode,
4
+ NormalizedInstanceNode,
5
+ } from "@/normalizer";
6
+ import { createElement, defineElementTransformer, type ElementTransformer } from "../../core";
7
+ import type { PropsConverters } from "./props";
8
+
9
+ export interface FrameTransformerDeps {
10
+ propsConverters: PropsConverters;
11
+ }
12
+
13
+ export function createFrameTransformer({
14
+ propsConverters,
15
+ }: FrameTransformerDeps): ElementTransformer<
16
+ NormalizedFrameNode | NormalizedInstanceNode | NormalizedComponentNode
17
+ > {
18
+ return defineElementTransformer(
19
+ (node: NormalizedFrameNode | NormalizedInstanceNode | NormalizedComponentNode, traverse) => {
20
+ const children = node.children;
21
+
22
+ const props = {
23
+ ...propsConverters.radius(node),
24
+ ...propsConverters.containerLayout(node),
25
+ ...propsConverters.selfLayout(node),
26
+ ...propsConverters.frameFill(node),
27
+ ...propsConverters.stroke(node),
28
+ ...propsConverters.shadow(node),
29
+ };
30
+
31
+ return createElement(
32
+ "Frame",
33
+ props,
34
+ children.map((child) => traverse(child)),
35
+ );
36
+ },
37
+ );
38
+ }
@@ -0,0 +1,6 @@
1
+ export * from "./pipeline";
2
+ export * from "./frame";
3
+ export * from "./instance";
4
+ export * from "./props";
5
+ export * from "./shape";
6
+ export * from "./text";
@@ -0,0 +1,36 @@
1
+ import type { ComponentRepository } from "@/entities";
2
+ import type { NormalizedInstanceNode } from "@/normalizer";
3
+ import { camelCase } from "change-case";
4
+ import { createElement, defineElementTransformer, type ElementTransformer } from "../../core";
5
+
6
+ export interface InstanceTransformerDeps {
7
+ frameTransformer: ElementTransformer<NormalizedInstanceNode>;
8
+ componentRepository?: ComponentRepository;
9
+ }
10
+
11
+ export function createInstanceTransformer({
12
+ frameTransformer,
13
+ componentRepository,
14
+ }: InstanceTransformerDeps): ElementTransformer<NormalizedInstanceNode> {
15
+ const transform = defineElementTransformer((node: NormalizedInstanceNode, traverse) => {
16
+ const component = componentRepository?.getOne(node.componentSetKey ?? node.componentKey);
17
+
18
+ if (component) {
19
+ return createElement("Instance", {
20
+ componentName: component.name,
21
+ ...Object.fromEntries(
22
+ Object.entries(node.componentProperties)
23
+ .filter(([_, props]) => props.type === "VARIANT" || props.type === "TEXT")
24
+ .map(([key, props]) => [
25
+ camelCase(key.split("#")[0]),
26
+ camelCase(props.value as string),
27
+ ]),
28
+ ),
29
+ });
30
+ }
31
+
32
+ return frameTransformer(node, traverse);
33
+ });
34
+
35
+ return transform;
36
+ }
@@ -0,0 +1,106 @@
1
+ import { createCodeGenerator, createValueResolver } from "@/codegen/core";
2
+ import type { CodeGenerator } from "@/codegen/core/codegen";
3
+ import { styleService, variableService } from "@/codegen/default-services";
4
+ import { SKIP_COMPONENT_KEYS } from "@/codegen/skip-components";
5
+ import { componentRepository } from "@/entities";
6
+ import { createFrameTransformer } from "./frame";
7
+ import { createInstanceTransformer } from "./instance";
8
+ import {
9
+ createContainerLayoutPropsConverter,
10
+ createFrameFillPropsConverter,
11
+ createRadiusPropsConverter,
12
+ createSelfLayoutPropsConverter,
13
+ createShadowPropsConverter,
14
+ createShapeFillPropsConverter,
15
+ createStrokePropsConverter,
16
+ createTextFillPropsConverter,
17
+ createTypeStylePropsConverter,
18
+ } from "./props";
19
+ import {
20
+ createBooleanOperationTransformer,
21
+ createRectangleTransformer,
22
+ createVectorTransformer,
23
+ } from "./shape";
24
+ import { createTextTransformer } from "./text";
25
+ import {
26
+ defaultEffectStyleNameFormatter,
27
+ defaultFillStyleResolver,
28
+ defaultRawValueFormatters,
29
+ defaultTextStyleNameFormatter,
30
+ defaultVariableNameFormatter,
31
+ } from "./value-resolver";
32
+
33
+ export interface CreatePipelineConfig {
34
+ shouldInferAutoLayout?: boolean;
35
+ shouldInferVariableName?: boolean;
36
+ }
37
+
38
+ export function createPipeline(options: CreatePipelineConfig = {}): CodeGenerator {
39
+ const { shouldInferAutoLayout = true, shouldInferVariableName = true } = options;
40
+
41
+ const valueResolver = createValueResolver({
42
+ variableService,
43
+ variableNameFormatter: defaultVariableNameFormatter,
44
+ styleService,
45
+ textStyleNameFormatter: defaultTextStyleNameFormatter,
46
+ effectStyleNameFormatter: defaultEffectStyleNameFormatter,
47
+ fillStyleResolver: defaultFillStyleResolver,
48
+ rawValueFormatters: defaultRawValueFormatters,
49
+ shouldInferVariableName,
50
+ });
51
+
52
+ const containerLayoutPropsConverter = createContainerLayoutPropsConverter(valueResolver);
53
+ const selfLayoutPropsConverter = createSelfLayoutPropsConverter(valueResolver);
54
+ const frameFillPropsConverter = createFrameFillPropsConverter(valueResolver);
55
+ const shapeFillPropsConverter = createShapeFillPropsConverter(valueResolver);
56
+ const textFillPropsConverter = createTextFillPropsConverter(valueResolver);
57
+ const radiusPropsConverter = createRadiusPropsConverter(valueResolver);
58
+ const strokePropsConverter = createStrokePropsConverter(valueResolver);
59
+ const shadowPropsConverter = createShadowPropsConverter(valueResolver);
60
+ const typeStylePropsConverter = createTypeStylePropsConverter(valueResolver);
61
+
62
+ const propsConverters = {
63
+ containerLayout: containerLayoutPropsConverter,
64
+ selfLayout: selfLayoutPropsConverter,
65
+ frameFill: frameFillPropsConverter,
66
+ shapeFill: shapeFillPropsConverter,
67
+ textFill: textFillPropsConverter,
68
+ radius: radiusPropsConverter,
69
+ stroke: strokePropsConverter,
70
+ shadow: shadowPropsConverter,
71
+ typeStyle: typeStylePropsConverter,
72
+ };
73
+
74
+ const frameTransformer = createFrameTransformer({
75
+ propsConverters,
76
+ });
77
+ const instanceTransformer = createInstanceTransformer({
78
+ frameTransformer,
79
+ componentRepository,
80
+ });
81
+ const textTransformer = createTextTransformer({
82
+ propsConverters,
83
+ });
84
+ const rectangleTransformer = createRectangleTransformer({
85
+ propsConverters,
86
+ });
87
+ const vectorTransformer = createVectorTransformer({
88
+ propsConverters,
89
+ });
90
+ const booleanOperationTransformer = createBooleanOperationTransformer({
91
+ propsConverters,
92
+ });
93
+
94
+ const codegenTransformer = createCodeGenerator({
95
+ frameTransformer,
96
+ textTransformer,
97
+ rectangleTransformer,
98
+ instanceTransformer,
99
+ vectorTransformer,
100
+ booleanOperationTransformer,
101
+ shouldInferAutoLayout,
102
+ skipComponentKeys: SKIP_COMPONENT_KEYS,
103
+ });
104
+
105
+ return codegenTransformer;
106
+ }
@@ -0,0 +1,262 @@
1
+ import { createPropsConverter, definePropsConverter, type PropsConverter } from "@/codegen/core";
2
+ import type {
3
+ NormalizedCornerTrait,
4
+ NormalizedHasChildrenTrait,
5
+ NormalizedHasEffectsTrait,
6
+ NormalizedHasFramePropertiesTrait,
7
+ NormalizedHasGeometryTrait,
8
+ NormalizedHasLayoutTrait,
9
+ NormalizedIsLayerTrait,
10
+ NormalizedTypePropertiesTrait,
11
+ } from "@/normalizer";
12
+ import type { FigmaValueResolver } from "./value-resolver";
13
+
14
+ export interface PropsConverters {
15
+ containerLayout: PropsConverter<ContainerLayoutTrait, ContainerLayoutProps>;
16
+ selfLayout: PropsConverter<SelfLayoutTrait, SelfLayoutProps>;
17
+ radius: PropsConverter<RadiusTrait, RadiusProps>;
18
+ frameFill: PropsConverter<FillTrait, FillProps>;
19
+ shapeFill: PropsConverter<FillTrait, FillProps>;
20
+ textFill: PropsConverter<FillTrait, FillProps>;
21
+ stroke: PropsConverter<StrokeTrait, StrokeProps>;
22
+ shadow: PropsConverter<ShadowTrait, ShadowProps>;
23
+ typeStyle: PropsConverter<TypeStyleTrait, TypeStyleProps>;
24
+ }
25
+
26
+ export type ContainerLayoutTrait = NormalizedHasFramePropertiesTrait &
27
+ NormalizedHasChildrenTrait &
28
+ NormalizedHasLayoutTrait &
29
+ NormalizedIsLayerTrait;
30
+
31
+ export type SelfLayoutTrait = NormalizedIsLayerTrait & NormalizedHasLayoutTrait;
32
+
33
+ export type RadiusTrait = NormalizedCornerTrait & NormalizedIsLayerTrait;
34
+
35
+ export type FillTrait = NormalizedIsLayerTrait & NormalizedHasGeometryTrait;
36
+
37
+ export type StrokeTrait = NormalizedIsLayerTrait & NormalizedHasGeometryTrait;
38
+
39
+ export type ShadowTrait = NormalizedIsLayerTrait & NormalizedHasEffectsTrait;
40
+
41
+ export type TypeStyleTrait = NormalizedTypePropertiesTrait & NormalizedIsLayerTrait;
42
+
43
+ export interface ContainerLayoutProps {
44
+ layoutMode?: "HORIZONTAL" | "VERTICAL" | "NONE";
45
+ primaryAxisAlignItems?: "MIN" | "CENTER" | "MAX" | "SPACE_BETWEEN";
46
+ counterAxisAlignItems?: "MIN" | "CENTER" | "MAX" | "BASELINE";
47
+ layoutWrap?: "WRAP" | "NO_WRAP";
48
+ itemSpacing?: number | string; // string when variable
49
+ counterAxisSpacing?: number | string; // string when variable
50
+ paddingTop?: number | string; // string when variable
51
+ paddingBottom?: number | string; // string when variable
52
+ paddingLeft?: number | string; // string when variable
53
+ paddingRight?: number | string; // string when variable
54
+ horizontalPadding?: number | string; // string when variable
55
+ verticalPadding?: number | string; // string when variable
56
+ }
57
+
58
+ export function createContainerLayoutPropsConverter(
59
+ valueResolver: FigmaValueResolver,
60
+ ): PropsConverter<ContainerLayoutTrait, ContainerLayoutProps> {
61
+ return createPropsConverter({
62
+ _types: {
63
+ trait: {} as ContainerLayoutTrait,
64
+ props: {} as ContainerLayoutProps,
65
+ },
66
+ handlers: {
67
+ layoutMode: ({ layoutMode }) => (!layoutMode || layoutMode === "GRID" ? "NONE" : layoutMode),
68
+ primaryAxisAlignItems: ({ primaryAxisAlignItems }) => primaryAxisAlignItems,
69
+ counterAxisAlignItems: ({ counterAxisAlignItems }) => counterAxisAlignItems,
70
+ layoutWrap: ({ layoutWrap }) => layoutWrap,
71
+ itemSpacing: (node) => valueResolver.getFormattedValue.itemSpacing(node),
72
+ counterAxisSpacing: (node) => valueResolver.getFormattedValue.counterAxisSpacing(node),
73
+ paddingTop: (node) => valueResolver.getFormattedValue.paddingTop(node),
74
+ paddingBottom: (node) => valueResolver.getFormattedValue.paddingBottom(node),
75
+ paddingLeft: (node) => valueResolver.getFormattedValue.paddingLeft(node),
76
+ paddingRight: (node) => valueResolver.getFormattedValue.paddingRight(node),
77
+ },
78
+ shorthands: {
79
+ horizontalPadding: ["paddingLeft", "paddingRight"],
80
+ verticalPadding: ["paddingTop", "paddingBottom"],
81
+ },
82
+ defaults: {},
83
+ });
84
+ }
85
+
86
+ export interface SelfLayoutProps {
87
+ layoutGrow?: number;
88
+ layoutAlign?: "STRETCH" | "INHERIT" | "MIN" | "CENTER" | "MAX";
89
+ layoutSizingVertical?: "FIXED" | "HUG" | "FILL";
90
+ layoutSizingHorizontal?: "FIXED" | "HUG" | "FILL";
91
+ width?: string | number; // string when variable
92
+ height?: string | number; // string when variable
93
+ minWidth?: string | number; // string when variable
94
+ minHeight?: string | number; // string when variable
95
+ maxWidth?: string | number; // string when variable
96
+ maxHeight?: string | number; // string when variable
97
+ }
98
+
99
+ export function createSelfLayoutPropsConverter(
100
+ valueResolver: FigmaValueResolver,
101
+ ): PropsConverter<SelfLayoutTrait, SelfLayoutProps> {
102
+ return createPropsConverter({
103
+ _types: {
104
+ trait: {} as SelfLayoutTrait,
105
+ props: {} as SelfLayoutProps,
106
+ },
107
+ handlers: {
108
+ layoutGrow: ({ layoutGrow }) => layoutGrow,
109
+ layoutAlign: ({ layoutAlign }) => layoutAlign,
110
+ layoutSizingVertical: ({ layoutSizingVertical }) => layoutSizingVertical,
111
+ layoutSizingHorizontal: ({ layoutSizingHorizontal }) => layoutSizingHorizontal,
112
+ width: (node) => valueResolver.getFormattedValue.width(node),
113
+ height: (node) => valueResolver.getFormattedValue.height(node),
114
+ minWidth: (node) => valueResolver.getFormattedValue.minWidth(node),
115
+ minHeight: (node) => valueResolver.getFormattedValue.minHeight(node),
116
+ maxWidth: (node) => valueResolver.getFormattedValue.maxWidth(node),
117
+ maxHeight: (node) => valueResolver.getFormattedValue.maxHeight(node),
118
+ },
119
+ defaults: {},
120
+ });
121
+ }
122
+
123
+ export interface RadiusProps {
124
+ cornerRadius?: number | string; // string when variable
125
+ topLeftRadius?: number | string; // string when variable
126
+ topRightRadius?: number | string; // string when variable
127
+ bottomLeftRadius?: number | string; // string when variable
128
+ bottomRightRadius?: number | string; // string when variable
129
+ }
130
+
131
+ export function createRadiusPropsConverter(valueResolver: FigmaValueResolver) {
132
+ return createPropsConverter({
133
+ _types: {
134
+ trait: {} as RadiusTrait,
135
+ props: {} as RadiusProps,
136
+ },
137
+ handlers: {
138
+ topLeftRadius: (node) => valueResolver.getFormattedValue.topLeftRadius(node),
139
+ topRightRadius: (node) => valueResolver.getFormattedValue.topRightRadius(node),
140
+ bottomLeftRadius: (node) => valueResolver.getFormattedValue.bottomLeftRadius(node),
141
+ bottomRightRadius: (node) => valueResolver.getFormattedValue.bottomRightRadius(node),
142
+ },
143
+ shorthands: {
144
+ cornerRadius: ["topLeftRadius", "topRightRadius", "bottomLeftRadius", "bottomRightRadius"],
145
+ },
146
+ defaults: {
147
+ cornerRadius: 0,
148
+ topLeftRadius: 0,
149
+ topRightRadius: 0,
150
+ bottomLeftRadius: 0,
151
+ bottomRightRadius: 0,
152
+ },
153
+ });
154
+ }
155
+
156
+ export interface FillProps {
157
+ fill?: string | { value: string; direction?: string };
158
+ }
159
+
160
+ export function createFrameFillPropsConverter(valueResolver: FigmaValueResolver) {
161
+ return definePropsConverter<FillTrait, FillProps>((node: FillTrait) => {
162
+ const fill = valueResolver.getFormattedValue.frameFill(node);
163
+
164
+ return {
165
+ fill,
166
+ };
167
+ });
168
+ }
169
+
170
+ export function createShapeFillPropsConverter(valueResolver: FigmaValueResolver) {
171
+ return definePropsConverter<FillTrait, FillProps>((node: FillTrait) => {
172
+ const fill = valueResolver.getFormattedValue.shapeFill(node);
173
+
174
+ return {
175
+ fill,
176
+ };
177
+ });
178
+ }
179
+
180
+ export function createTextFillPropsConverter(valueResolver: FigmaValueResolver) {
181
+ return definePropsConverter<FillTrait, FillProps>((node: FillTrait) => {
182
+ const fill = valueResolver.getFormattedValue.textFill(node);
183
+
184
+ return {
185
+ fill,
186
+ };
187
+ });
188
+ }
189
+
190
+ export interface StrokeProps {
191
+ stroke?: string;
192
+ strokeWeight?: number;
193
+ }
194
+
195
+ export function createStrokePropsConverter(
196
+ valueResolver: FigmaValueResolver,
197
+ ): PropsConverter<StrokeTrait, StrokeProps> {
198
+ return definePropsConverter((node: StrokeTrait) => {
199
+ const stroke = valueResolver.getFormattedValue.stroke(node);
200
+ const strokeWeight = node.strokeWeight;
201
+
202
+ return {
203
+ stroke,
204
+ strokeWeight,
205
+ };
206
+ });
207
+ }
208
+
209
+ export interface TypeStyleProps {
210
+ textStyle?: string;
211
+ fontSize?: string | number;
212
+ fontWeight?: string | number;
213
+ lineHeight?: string | number;
214
+ maxLines?: number;
215
+ }
216
+
217
+ export function createTypeStylePropsConverter(
218
+ valueResolver: FigmaValueResolver,
219
+ ): PropsConverter<TypeStyleTrait, TypeStyleProps> {
220
+ return definePropsConverter((node) => {
221
+ const styleName = valueResolver.getTextStyleValue(node);
222
+ const maxLines =
223
+ node.style.textTruncation === "ENDING" ? (node.style.maxLines ?? undefined) : undefined;
224
+
225
+ if (styleName) {
226
+ return {
227
+ textStyle: styleName,
228
+ maxLines,
229
+ };
230
+ }
231
+
232
+ return {
233
+ fontSize: valueResolver.getFormattedValue.fontSize(node),
234
+ fontWeight: valueResolver.getFormattedValue.fontWeight(node),
235
+ lineHeight: valueResolver.getFormattedValue.lineHeight(node),
236
+ maxLines,
237
+ };
238
+ });
239
+ }
240
+
241
+ export interface ShadowProps {
242
+ boxShadowStyle?: string;
243
+ boxShadow?: string;
244
+ }
245
+
246
+ export function createShadowPropsConverter(
247
+ valueResolver: FigmaValueResolver,
248
+ ): PropsConverter<ShadowTrait, ShadowProps> {
249
+ return definePropsConverter((node: ShadowTrait) => {
250
+ const effectStyleName = valueResolver.getEffectStyleValue(node);
251
+ if (effectStyleName) {
252
+ return {
253
+ boxShadowStyle: effectStyleName,
254
+ };
255
+ }
256
+
257
+ const boxShadow = valueResolver.getFormattedValue.boxShadow(node);
258
+ return {
259
+ boxShadow,
260
+ };
261
+ });
262
+ }
@@ -0,0 +1,65 @@
1
+ import type {
2
+ NormalizedBooleanOperationNode,
3
+ NormalizedRectangleNode,
4
+ NormalizedVectorNode,
5
+ } from "@/normalizer";
6
+ import { createElement, defineElementTransformer, type ElementTransformer } from "../../core";
7
+ import type { PropsConverters } from "./props";
8
+
9
+ export interface RectangleTransformerDeps {
10
+ propsConverters: PropsConverters;
11
+ }
12
+
13
+ export function createRectangleTransformer({
14
+ propsConverters,
15
+ }: RectangleTransformerDeps): ElementTransformer<NormalizedRectangleNode> {
16
+ return defineElementTransformer((node: NormalizedRectangleNode) => {
17
+ return createElement("Rectangle", { ...propsConverters.selfLayout(node) });
18
+ });
19
+ }
20
+
21
+ export interface VectorTransformerDeps {
22
+ propsConverters: PropsConverters;
23
+ }
24
+
25
+ export function createVectorTransformer({
26
+ propsConverters,
27
+ }: VectorTransformerDeps): ElementTransformer<NormalizedVectorNode> {
28
+ return defineElementTransformer((node) => {
29
+ return createElement(
30
+ "Vector",
31
+ {
32
+ ...propsConverters.selfLayout(node),
33
+ ...propsConverters.radius(node),
34
+ ...propsConverters.stroke(node),
35
+ ...propsConverters.shapeFill(node),
36
+ ...propsConverters.shadow(node),
37
+ },
38
+ [],
39
+ {
40
+ comment: "Vector Node Placeholder",
41
+ },
42
+ );
43
+ });
44
+ }
45
+
46
+ export interface BooleanOperationTransformerDeps {
47
+ propsConverters: PropsConverters;
48
+ }
49
+
50
+ export function createBooleanOperationTransformer({
51
+ propsConverters,
52
+ }: BooleanOperationTransformerDeps): ElementTransformer<NormalizedBooleanOperationNode> {
53
+ return defineElementTransformer((node, traverse) => {
54
+ return createElement(
55
+ "BooleanOperation",
56
+ {
57
+ ...propsConverters.selfLayout(node),
58
+ ...propsConverters.radius(node),
59
+ ...propsConverters.stroke(node),
60
+ ...propsConverters.shapeFill(node),
61
+ },
62
+ node.children.map(traverse),
63
+ );
64
+ });
65
+ }
@@ -0,0 +1,30 @@
1
+ import type { NormalizedTextNode } from "@/normalizer";
2
+ import { compactObject } from "@/utils/common";
3
+ import { createElement, defineElementTransformer, type ElementTransformer } from "../../core";
4
+ import type { PropsConverters } from "./props";
5
+
6
+ export interface TextTransformerDeps {
7
+ propsConverters: PropsConverters;
8
+ }
9
+
10
+ export function createTextTransformer({
11
+ propsConverters,
12
+ }: TextTransformerDeps): ElementTransformer<NormalizedTextNode> {
13
+ return defineElementTransformer((node: NormalizedTextNode) => {
14
+ const hasMultipleFills = node.fills.length > 1;
15
+
16
+ const fillProps = propsConverters.textFill(node);
17
+ const typeStyleProps = propsConverters.typeStyle(node);
18
+
19
+ const props = compactObject({
20
+ ...typeStyleProps,
21
+ ...fillProps,
22
+ });
23
+
24
+ return createElement("Text", props, node.characters, {
25
+ comment: hasMultipleFills
26
+ ? "Multiple fills in Text node encountered, only the first fill is used."
27
+ : undefined,
28
+ });
29
+ });
30
+ }
@@ -0,0 +1,75 @@
1
+ import type { ValueResolver } from "@/codegen/core";
2
+ import { toCssRgba } from "@/utils/css";
3
+ import type { RGBA } from "@figma/rest-api-spec";
4
+ import { camelCase } from "change-case";
5
+
6
+ export type FigmaValueResolver = ValueResolver<
7
+ string,
8
+ { value: string; direction?: string },
9
+ number,
10
+ number,
11
+ number
12
+ >;
13
+
14
+ export const defaultVariableNameFormatter = ({ slug }: { slug: string[] }) =>
15
+ slug
16
+ .filter((s) => s !== "dimension")
17
+ .map((s) => s.replaceAll(",", "_"))
18
+ .join("/");
19
+
20
+ export const defaultTextStyleNameFormatter = ({ slug }: { slug: string[] }) =>
21
+ slug[slug.length - 1]!;
22
+
23
+ export const defaultEffectStyleNameFormatter = ({ slug }: { slug: string[] }) =>
24
+ slug[slug.length - 1]!;
25
+
26
+ export const defaultFillStyleResolver = ({ slug }: { slug: string[] }) => {
27
+ const [, ...rest] = slug;
28
+
29
+ if (rest.includes("fade")) {
30
+ // ["fade", "layer-default", "↓(to-bottom)"]
31
+
32
+ const last = rest[rest.length - 1];
33
+
34
+ const direction = (() => {
35
+ if (last.startsWith("↓")) return "to bottom";
36
+ if (last.startsWith("↑")) return "to top";
37
+ if (last.startsWith("→")) return "to right";
38
+ if (last.startsWith("←")) return "to left";
39
+
40
+ return "unknown";
41
+ })();
42
+
43
+ return {
44
+ value: camelCase(rest.slice(0, -1).join("-"), { mergeAmbiguousCharacters: true }),
45
+ direction,
46
+ };
47
+ }
48
+
49
+ return {
50
+ value: camelCase(rest.join("-"), { mergeAmbiguousCharacters: true }),
51
+ };
52
+ };
53
+
54
+ function formatBoxShadow(value: {
55
+ type: "DROP_SHADOW" | "INNER_SHADOW";
56
+ color: RGBA;
57
+ offset: { x: number; y: number };
58
+ radius: number;
59
+ spread?: number;
60
+ }): string {
61
+ const { type, color, offset, radius, spread } = value;
62
+ const inset = type === "INNER_SHADOW" ? "inset " : "";
63
+ const colorStr = toCssRgba(color);
64
+ const spreadStr = spread !== undefined ? ` ${spread}px` : "";
65
+
66
+ return `${inset}${offset.x}px ${offset.y}px ${radius}px${spreadStr} ${colorStr}`;
67
+ }
68
+
69
+ export const defaultRawValueFormatters = {
70
+ color: (value: RGBA) => toCssRgba(value),
71
+ dimension: (value: number) => value,
72
+ fontDimension: (value: number) => value,
73
+ fontWeight: (value: number) => value,
74
+ boxShadow: formatBoxShadow,
75
+ };
@@ -0,0 +1,2 @@
1
+ export * as figma from "./figma";
2
+ export * as react from "./react";
@@ -0,0 +1,7 @@
1
+ import type { IconHandler } from "../icon";
2
+ import type { ReactValueResolver } from "../value-resolver";
3
+
4
+ export interface ComponentHandlerDeps {
5
+ iconHandler: IconHandler;
6
+ valueResolver: ReactValueResolver;
7
+ }