@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,122 @@
1
+ import { defineComponentHandler } from "@/codegen/core";
2
+ import type {
3
+ ActionButtonGhostProperties,
4
+ ActionButtonProperties,
5
+ AlertDialogFooterProperties,
6
+ AlertDialogProperties,
7
+ } from "@/codegen/component-properties.archive";
8
+ import * as metadata from "@/entities/data/__generated__/archive/component-sets";
9
+ import { createLocalSnippetHelper, createSeedReactElement } from "../../../element-factories";
10
+ import type { ComponentHandlerDeps } from "../../deps.interface";
11
+ import { findAllInstances } from "@/utils/figma-node";
12
+ import { match } from "ts-pattern";
13
+ import {
14
+ createActionButtonGhostHandler,
15
+ createActionButtonHandler,
16
+ } from "@/codegen/targets/react/component/handlers/archive/action-button";
17
+
18
+ const { createLocalSnippetElement } = createLocalSnippetHelper("alert-dialog");
19
+ const { createLocalSnippetElement: createLocalSnippetElementTrigger } =
20
+ createLocalSnippetHelper("action-button");
21
+
22
+ const ALERT_DIALOG_FOOTER_KEY = "00b1b131d67edf2875a7a1df8dfa88098d7c04be";
23
+
24
+ export const createAlertDialogHandler = (ctx: ComponentHandlerDeps) => {
25
+ const actionButtonHandler = createActionButtonHandler(ctx);
26
+ const actionButtonGhostHandler = createActionButtonGhostHandler(ctx);
27
+
28
+ return defineComponentHandler<AlertDialogProperties>(
29
+ metadata.alertDialog.key,
30
+ (node, traverse) => {
31
+ const props = node.componentProperties;
32
+ const alertDialogHeader = createLocalSnippetElement("AlertDialogHeader", undefined, [
33
+ ...(props["Show Title#20361:14"].value
34
+ ? [
35
+ createLocalSnippetElement(
36
+ "AlertDialogTitle",
37
+ undefined,
38
+ props["Title Text#20361:0"].value,
39
+ ),
40
+ ]
41
+ : []),
42
+ createLocalSnippetElement(
43
+ "AlertDialogDescription",
44
+ undefined,
45
+ props["Description Text#20361:7"].value,
46
+ ),
47
+ ]);
48
+
49
+ const footerNodes = findAllInstances<AlertDialogFooterProperties>({
50
+ node,
51
+ key: ALERT_DIALOG_FOOTER_KEY,
52
+ });
53
+
54
+ if (footerNodes.length === 0 || footerNodes.length > 1) {
55
+ return createLocalSnippetElement("AlertDialog", undefined, alertDialogHeader, {
56
+ comment: "Footer 영역을 확인해주세요.",
57
+ });
58
+ }
59
+
60
+ const footerNode = footerNodes[0];
61
+ const footerNodeProps = traverse(footerNode)?.props;
62
+
63
+ const buttons = footerNode.children.map(traverse);
64
+
65
+ const alertDialogFooterChildren = match(footerNode.componentProperties.Type.value)
66
+ .with("Single", () => buttons)
67
+ .with("Neutral", "Critical", () =>
68
+ createSeedReactElement("ResponsivePair", footerNodeProps, buttons),
69
+ )
70
+ .with("Neutral (Overflow)", "Critical (Overflow)", () =>
71
+ createSeedReactElement("VStack", footerNodeProps, buttons),
72
+ )
73
+ .with("Nonpreferred", () => {
74
+ const [actionButtonNode] = findAllInstances<ActionButtonProperties>({
75
+ node: footerNode,
76
+ key: actionButtonHandler.key,
77
+ });
78
+
79
+ const [ghostButtonNode] = findAllInstances<ActionButtonGhostProperties>({
80
+ node: footerNode,
81
+ key: actionButtonGhostHandler.key,
82
+ });
83
+
84
+ const actionButton = actionButtonHandler.transform(actionButtonNode, traverse);
85
+ const ghostButton = actionButtonGhostHandler.transform(ghostButtonNode, traverse);
86
+
87
+ const buttons = [
88
+ actionButton,
89
+ {
90
+ ...ghostButton,
91
+ props: {
92
+ ...ghostButton.props,
93
+ // nonpreferred shouldn't have bleedX, but in Figma it's not possible to represent that
94
+ bleedX: undefined,
95
+ },
96
+ },
97
+ ];
98
+
99
+ return createSeedReactElement("VStack", footerNodeProps, buttons);
100
+ })
101
+ .exhaustive();
102
+
103
+ const alertDialogFooter = createLocalSnippetElement(
104
+ "AlertDialogFooter",
105
+ undefined,
106
+ alertDialogFooterChildren,
107
+ );
108
+
109
+ return createLocalSnippetElement("AlertDialogRoot", { open: true }, [
110
+ createLocalSnippetElement(
111
+ "AlertDialogTrigger",
112
+ { asChild: true },
113
+ createLocalSnippetElementTrigger("ActionButton", {}, "AlertDialog 열기"),
114
+ ),
115
+ createLocalSnippetElement("AlertDialogContent", undefined, [
116
+ alertDialogHeader,
117
+ alertDialogFooter,
118
+ ]),
119
+ ]);
120
+ },
121
+ );
122
+ };
@@ -0,0 +1,149 @@
1
+ import type {
2
+ AppBarProperties,
3
+ AppBarMainProperties,
4
+ AppBarRightIconButtonProperties,
5
+ AppBarLeftIconButtonProperties,
6
+ } from "@/codegen/component-properties.archive";
7
+ import { defineComponentHandler } from "@/codegen/core";
8
+ import * as metadata from "@/entities/data/__generated__/archive/component-sets";
9
+ import { findAllInstances } from "@/utils/figma-node";
10
+ import { match } from "ts-pattern";
11
+ import { createLocalSnippetHelper } from "../../../element-factories";
12
+ import type { ComponentHandlerDeps } from "../../deps.interface";
13
+
14
+ const { createLocalSnippetElement } = createLocalSnippetHelper("app-bar");
15
+
16
+ const APP_BAR_TITLE_KEY = "d2cc4f615b2b44098be89448ad1c573f94af0355";
17
+ const APP_BAR_LEFT_ICON_BUTTON_KEY = "5a953f7bafc0df744777517458396e9f6c915825";
18
+ const APP_BAR_RIGHT_ICON_BUTTON_KEY = "c08db793288077e53bd45ef11aa419a835e88fce";
19
+
20
+ const createAppBarMainHandler = (_ctx: ComponentHandlerDeps) => {
21
+ return defineComponentHandler<AppBarMainProperties>(
22
+ APP_BAR_TITLE_KEY,
23
+ ({ componentProperties: props }) => {
24
+ const { title, subtitle } = match(props.Type.value)
25
+ .with("Title", () => ({
26
+ title: props["Title#16944:0"].value,
27
+ subtitle: undefined,
28
+ }))
29
+ .with("Title-Subtitle", () => ({
30
+ title: props["Title#16944:0"].value,
31
+ subtitle: props["Subtitle#16958:9"].value,
32
+ }))
33
+ .with("Logo (Figma Only)", () => ({
34
+ title: undefined,
35
+ subtitle: undefined,
36
+ }))
37
+ .exhaustive();
38
+
39
+ if (title) {
40
+ return createLocalSnippetElement("AppBarMain", { title, subtitle });
41
+ }
42
+
43
+ return createLocalSnippetElement("AppBarMain", undefined, undefined, {
44
+ comment: "AppBarMain 내부를 직접 작성해주세요.",
45
+ });
46
+ },
47
+ );
48
+ };
49
+
50
+ export const createAppBarHandler = (ctx: ComponentHandlerDeps) => {
51
+ const appBarMainHandler = createAppBarMainHandler(ctx);
52
+
53
+ return defineComponentHandler<AppBarProperties>(metadata.topNavigation.key, (node, traverse) => {
54
+ const props = node.componentProperties;
55
+
56
+ const { theme, tone } = {
57
+ theme: match(props["OS (Figma Only)"].value)
58
+ .with("Android", () => "android")
59
+ .with("iOS", () => "cupertino")
60
+ .exhaustive(),
61
+
62
+ tone: match(props.Variant.value)
63
+ .with("Layer Default", () => "layer")
64
+ .with("Transparent", () => "transparent")
65
+ .exhaustive(),
66
+ };
67
+
68
+ const main = (() => {
69
+ if (!props["Show Title#33588:82"].value) {
70
+ return undefined;
71
+ }
72
+
73
+ const [mainNode] = findAllInstances<AppBarMainProperties>({ node, key: APP_BAR_TITLE_KEY });
74
+
75
+ if (!mainNode) {
76
+ return undefined;
77
+ }
78
+
79
+ return appBarMainHandler.transform(mainNode, traverse);
80
+ })();
81
+
82
+ const leftChildren = match(props.Left.value)
83
+ .with("None", () => undefined)
84
+ .with("Back", () => [createLocalSnippetElement("AppBarBackButton")])
85
+ .with("Close", () => [createLocalSnippetElement("AppBarCloseButton")])
86
+ .with("Custom", () => {
87
+ const buttons = findAllInstances<AppBarLeftIconButtonProperties>({
88
+ node,
89
+ key: APP_BAR_LEFT_ICON_BUTTON_KEY,
90
+ });
91
+
92
+ if (buttons.length > 0) {
93
+ return buttons.map((button) =>
94
+ createLocalSnippetElement(
95
+ "AppBarIconButton",
96
+ undefined,
97
+ ctx.iconHandler.transform(button.componentProperties["Icon#33580:0"]),
98
+ { comment: "AppBarIconButton에 aria-label 속성을 추가해주세요." },
99
+ ),
100
+ );
101
+ }
102
+
103
+ return undefined;
104
+ })
105
+ .exhaustive();
106
+
107
+ const left =
108
+ leftChildren && leftChildren.length > 0
109
+ ? createLocalSnippetElement("AppBarLeft", {}, leftChildren)
110
+ : undefined;
111
+
112
+ const rightChildren = match(props.Right.value)
113
+ .with("None", () => undefined)
114
+ .with("1 Icon Button", "2 Icon Button", "3 Icon Button", () => {
115
+ const buttons = findAllInstances<AppBarRightIconButtonProperties>({
116
+ node,
117
+ key: APP_BAR_RIGHT_ICON_BUTTON_KEY,
118
+ });
119
+
120
+ return buttons.map((button) =>
121
+ createLocalSnippetElement(
122
+ "AppBarIconButton",
123
+ undefined,
124
+ ctx.iconHandler.transform(button.componentProperties["Icon#6406:3"]),
125
+ { comment: "AppBarIconButton에 aria-label 속성을 추가해주세요." },
126
+ ),
127
+ );
128
+ })
129
+ .with("Text Button", () => undefined)
130
+ .exhaustive();
131
+
132
+ const right =
133
+ rightChildren && rightChildren.length > 0
134
+ ? createLocalSnippetElement("AppBarRight", {}, rightChildren)
135
+ : undefined;
136
+
137
+ return createLocalSnippetElement(
138
+ "AppBar",
139
+ { theme, tone },
140
+ [left, main, right].filter(Boolean),
141
+ {
142
+ comment:
143
+ tone === "transparent"
144
+ ? '<AppScreen layerOffsetTop="none">으로 상단 패딩을 제거할 수 있습니다.'
145
+ : undefined,
146
+ },
147
+ );
148
+ });
149
+ };
@@ -0,0 +1,35 @@
1
+ import type { AvatarProperties, AvatarStackProperties } from "@/codegen/component-properties.archive";
2
+ import { defineComponentHandler } from "@/codegen/core";
3
+ import * as metadata from "@/entities/data/__generated__/archive/component-sets";
4
+ import { findAllInstances } from "@/utils/figma-node";
5
+ import { createLocalSnippetHelper } from "../../../element-factories";
6
+ import type { ComponentHandlerDeps } from "../../deps.interface";
7
+ import { createAvatarHandler } from "./avatar";
8
+
9
+ const { createLocalSnippetElement } = createLocalSnippetHelper("avatar");
10
+
11
+ export const createAvatarStackHandler = (ctx: ComponentHandlerDeps) => {
12
+ const avatarHandler = createAvatarHandler(ctx);
13
+
14
+ return defineComponentHandler<AvatarStackProperties>(
15
+ metadata.avatarStack.key,
16
+ (node, traverse) => {
17
+ const avatarNodes = findAllInstances<AvatarProperties>({
18
+ node,
19
+ key: avatarHandler.key,
20
+ });
21
+
22
+ const { componentProperties: props } = node;
23
+
24
+ const commonProps = {
25
+ size: props.Size.value,
26
+ };
27
+
28
+ const avatarStackChildren = avatarNodes.map((avatarNode) =>
29
+ avatarHandler.transform(avatarNode, traverse),
30
+ );
31
+
32
+ return createLocalSnippetElement("AvatarStack", commonProps, avatarStackChildren);
33
+ },
34
+ );
35
+ };
@@ -0,0 +1,55 @@
1
+ import type {
2
+ AvatarProperties,
3
+ IdentityPlaceholderProperties,
4
+ } from "@/codegen/component-properties.archive";
5
+ import { createElement, defineComponentHandler } from "@/codegen/core";
6
+ import * as metadata from "@/entities/data/__generated__/archive/component-sets";
7
+ import { findAllInstances } from "@/utils/figma-node";
8
+ import { createLocalSnippetHelper } from "../../../element-factories";
9
+ import type { ComponentHandlerDeps } from "../../deps.interface";
10
+ import { createIdentityPlaceholderHandler } from "./identity-placeholder";
11
+ import { camelCase } from "change-case";
12
+
13
+ const { createLocalSnippetElement } = createLocalSnippetHelper("avatar");
14
+
15
+ export const createAvatarHandler = (ctx: ComponentHandlerDeps) => {
16
+ const identityPlaceholderHandler = createIdentityPlaceholderHandler(ctx);
17
+
18
+ return defineComponentHandler<AvatarProperties>(metadata.avatar.key, (node, traverse) => {
19
+ const [placeholder] = findAllInstances<IdentityPlaceholderProperties>({
20
+ node,
21
+ key: identityPlaceholderHandler.key,
22
+ });
23
+ const { componentProperties: props } = node;
24
+
25
+ const avatarHasSrc = props["Has Image Contents#33407:0"].value;
26
+
27
+ const commonProps = {
28
+ ...(avatarHasSrc && {
29
+ // Placeholder
30
+ src: `https://placehold.co/${props.Size.value}x${props.Size.value}`,
31
+ }),
32
+ ...(placeholder && {
33
+ fallback: identityPlaceholderHandler.transform(placeholder, traverse),
34
+ }),
35
+ ...(props["Badge"].value !== "None" && {
36
+ badgeMask: camelCase(props["Badge"].value),
37
+ }),
38
+ size: props.Size.value,
39
+ };
40
+
41
+ return createLocalSnippetElement(
42
+ "Avatar",
43
+ commonProps,
44
+ props["Badge"].value === "None"
45
+ ? undefined
46
+ : createLocalSnippetElement(
47
+ "AvatarBadge",
48
+ { asChild: true },
49
+ createElement("img", { src: "https://placehold.co/20x20" }),
50
+ { comment: "뱃지를 설명하는 alt 텍스트를 제공해야 합니다." },
51
+ ),
52
+ { comment: avatarHasSrc ? "alt 텍스트를 제공해야 합니다." : undefined },
53
+ );
54
+ });
55
+ };
@@ -0,0 +1,18 @@
1
+ import type { BadgeProperties } from "@/codegen/component-properties.archive";
2
+ import { defineComponentHandler } from "@/codegen/core";
3
+ import * as metadata from "@/entities/data/__generated__/archive/component-sets";
4
+ import { camelCase } from "change-case";
5
+ import { createSeedReactElement } from "../../../element-factories";
6
+ import type { ComponentHandlerDeps } from "../../deps.interface";
7
+ import { handleSizeProp } from "../../size";
8
+
9
+ export const createBadgeHandler = (_ctx: ComponentHandlerDeps) =>
10
+ defineComponentHandler<BadgeProperties>(metadata.badge.key, ({ componentProperties: props }) => {
11
+ const commonProps = {
12
+ size: handleSizeProp(props.Size.value),
13
+ tone: camelCase(props.Tone.value),
14
+ variant: camelCase(props.Variant.value),
15
+ };
16
+
17
+ return createSeedReactElement("Badge", commonProps, props["Label#1584:0"].value);
18
+ });
@@ -0,0 +1,70 @@
1
+ import type { BottomSheetProperties } from "@/codegen/component-properties.archive";
2
+ import { createElement, defineComponentHandler } from "@/codegen/core";
3
+ import * as metadata from "@/entities/data/__generated__/archive/component-sets";
4
+ import { match } from "ts-pattern";
5
+ import { createLocalSnippetHelper } from "../../../element-factories";
6
+ import type { ComponentHandlerDeps } from "../../deps.interface";
7
+ import { findAllInstances } from "@/utils/figma-node";
8
+
9
+ const { createLocalSnippetElement } = createLocalSnippetHelper("bottom-sheet");
10
+ const { createLocalSnippetElement: createLocalSnippetElementTrigger } =
11
+ createLocalSnippetHelper("action-button");
12
+
13
+ // TODO: Bottom Action Bar (WIP) handler의 키. 해당 컴포넌트(템플릿) 핸들러 작성 시 handler.transform()으로 대체
14
+ const BOTTOM_SHEET_FOOTER_KEY = "de6c65d34cc4a01c18c9a7c0ded34635170ca11f";
15
+ const BOTTOM_SHEET_BODY_KEY = "aa9ffdd60fbfdea42f0d11162cd64f4b4389ddcc";
16
+
17
+ export const createBottomSheetHandler = (_ctx: ComponentHandlerDeps) =>
18
+ defineComponentHandler<BottomSheetProperties>(metadata.bottomSheet.key, (node, traverse) => {
19
+ const props = node.componentProperties;
20
+ const headerAlign = match(props["Header Layout"].value)
21
+ .with("Bottom Left", "Top Left", () => "left")
22
+ .with("Bottom Center", "Top Center", () => "center")
23
+ .with("None", () => undefined)
24
+ .exhaustive();
25
+
26
+ const contentProps = {
27
+ title: props["Title#19787:3"].value,
28
+ ...(props["Show Description#25192:0"].value === true && {
29
+ description: props["Description#19787:7"].value,
30
+ }),
31
+ showHandle: props["Show Handle#49774:6"].value,
32
+ showCloseButton: props["Show Close Button#19787:11"].value,
33
+ };
34
+
35
+ const bodyNodes = findAllInstances({
36
+ node,
37
+ key: BOTTOM_SHEET_BODY_KEY,
38
+ });
39
+
40
+ const bottomSheetBody =
41
+ bodyNodes.length === 1
42
+ ? createLocalSnippetElement("BottomSheetBody", {}, bodyNodes[0].children.map(traverse))
43
+ : createLocalSnippetElement(
44
+ "BottomSheetBody",
45
+ {},
46
+ createElement("div", undefined, "No content available"),
47
+ );
48
+
49
+ const footerNodes = findAllInstances({
50
+ node,
51
+ key: BOTTOM_SHEET_FOOTER_KEY,
52
+ });
53
+
54
+ const bottomSheetFooter =
55
+ props["Show Footer#25162:14"].value && footerNodes.length === 1
56
+ ? createLocalSnippetElement("BottomSheetFooter", {}, footerNodes[0].children.map(traverse))
57
+ : undefined;
58
+
59
+ return createLocalSnippetElement("BottomSheetRoot", { defaultOpen: true, headerAlign }, [
60
+ createLocalSnippetElement(
61
+ "BottomSheetTrigger",
62
+ { asChild: true },
63
+ createLocalSnippetElementTrigger("ActionButton", {}, "BottomSheet 열기"),
64
+ ),
65
+ createLocalSnippetElement("BottomSheetContent", contentProps, [
66
+ bottomSheetBody,
67
+ bottomSheetFooter,
68
+ ]),
69
+ ]);
70
+ });
@@ -0,0 +1,88 @@
1
+ import type { CalloutProperties } from "@/codegen/component-properties.archive";
2
+ import { defineComponentHandler } from "@/codegen/core";
3
+ import * as metadata from "@/entities/data/__generated__/archive/component-sets";
4
+ import type { NormalizedTextNode } from "@/normalizer";
5
+ import { camelCase } from "change-case";
6
+ import { createLocalSnippetHelper } from "../../../element-factories";
7
+ import type { ComponentHandlerDeps } from "../../deps.interface";
8
+ import { match } from "ts-pattern";
9
+
10
+ const { createLocalSnippetElement } = createLocalSnippetHelper("callout");
11
+
12
+ export const createCalloutHandler = (ctx: ComponentHandlerDeps) =>
13
+ defineComponentHandler<CalloutProperties>(
14
+ metadata.callout.key,
15
+ ({ componentProperties: props, children }) => {
16
+ const tag = match(props.Interaction.value)
17
+ .with("Display", () => "Callout")
18
+ .with("Actionable", () => "ActionableCallout")
19
+ .with("Dismissible", () => "DismissibleCallout")
20
+ .exhaustive();
21
+
22
+ const textNode = children.find((child) => child.type === "TEXT") as NormalizedTextNode | null;
23
+
24
+ if (!textNode) {
25
+ return createLocalSnippetElement(tag, undefined, undefined, {
26
+ comment: "내용을 제공해주세요.",
27
+ });
28
+ }
29
+
30
+ const slices = textNode.segments;
31
+
32
+ let title: string | undefined;
33
+ let description: string | undefined;
34
+ let linkLabel: string | undefined;
35
+
36
+ switch (slices.length) {
37
+ case 1: {
38
+ description = slices[0]?.characters.trim();
39
+
40
+ break;
41
+ }
42
+ case 2: {
43
+ const firstSlice = slices[0];
44
+ const secondSlice = slices[1];
45
+
46
+ if (firstSlice?.style.fontWeight === 700) {
47
+ title = firstSlice?.characters.trim();
48
+ description = secondSlice?.characters.trim();
49
+ break;
50
+ }
51
+
52
+ description = firstSlice?.characters.trim();
53
+
54
+ if (tag !== "ActionableCallout") {
55
+ linkLabel = secondSlice?.characters.trim();
56
+ }
57
+
58
+ break;
59
+ }
60
+ case 3: {
61
+ title = slices[0]?.characters.trim();
62
+ description = slices[1]?.characters.trim();
63
+
64
+ if (tag !== "ActionableCallout") {
65
+ linkLabel = slices[2]?.characters.trim();
66
+ }
67
+
68
+ break;
69
+ }
70
+ }
71
+
72
+ const commonProps = {
73
+ tone: camelCase(props.Tone.value),
74
+ title,
75
+ description,
76
+ ...(linkLabel && {
77
+ linkProps: {
78
+ children: linkLabel,
79
+ },
80
+ }),
81
+ ...(props["Show Prefix Icon#35087:1"].value && {
82
+ prefixIcon: ctx.iconHandler.transform(props["Prefix Icon#35087:0"]),
83
+ }),
84
+ };
85
+
86
+ return createLocalSnippetElement(tag, commonProps);
87
+ },
88
+ );
@@ -0,0 +1,43 @@
1
+ import type { CheckboxProperties } from "@/codegen/component-properties.archive";
2
+ import { defineComponentHandler } from "@/codegen/core";
3
+ import * as metadata from "@/entities/data/__generated__/archive/component-sets";
4
+ import { camelCase } from "change-case";
5
+ import { createLocalSnippetHelper } from "../../../element-factories";
6
+ import type { ComponentHandlerDeps } from "../../deps.interface";
7
+ import { handleSizeProp } from "../../size";
8
+ import { match } from "ts-pattern";
9
+
10
+ const { createLocalSnippetElement } = createLocalSnippetHelper("checkbox");
11
+
12
+ export const createCheckboxHandler = (_ctx: ComponentHandlerDeps) =>
13
+ defineComponentHandler<CheckboxProperties>(
14
+ metadata.checkbox.key,
15
+ ({ componentProperties: props }) => {
16
+ const tone = match(props.Tone.value)
17
+ .with("Neutral", () => "neutral")
18
+ .with("🚫[Deprecated]Brand", () => "brand")
19
+ .exhaustive();
20
+
21
+ const commonProps = {
22
+ label: props["Label#49990:0"].value,
23
+ weight: camelCase(props.Weight.value),
24
+ tone,
25
+ variant: camelCase(props.Shape.value),
26
+ size: handleSizeProp(props.Size.value),
27
+ ...(props.Selected.value === "True" && {
28
+ defaultChecked: true,
29
+ }),
30
+ ...(props.Selected.value === "Indeterminate" && {
31
+ defaultChecked: true,
32
+ indeterminate: true,
33
+ }),
34
+ ...(props.State.value === "Disabled" && {
35
+ disabled: true,
36
+ }),
37
+ };
38
+
39
+ return createLocalSnippetElement("Checkbox", commonProps, undefined, {
40
+ comment: "CheckboxGroup으로 묶어서 사용하는 것을 권장합니다.",
41
+ });
42
+ },
43
+ );
@@ -0,0 +1,29 @@
1
+ import type { CheckmarkProperties } from "@/codegen/component-properties.archive";
2
+ import { defineComponentHandler } from "@/codegen/core";
3
+ import * as metadata from "@/entities/data/__generated__/archive/component-sets";
4
+ import { camelCase } from "change-case";
5
+ import { createLocalSnippetHelper } from "../../../element-factories";
6
+ import type { ComponentHandlerDeps } from "../../deps.interface";
7
+ import { handleSizeProp } from "../../size";
8
+ import { match } from "ts-pattern";
9
+
10
+ const { createLocalSnippetElement } = createLocalSnippetHelper("checkbox");
11
+
12
+ export const createCheckmarkHandler = (_ctx: ComponentHandlerDeps) =>
13
+ defineComponentHandler<CheckmarkProperties>(
14
+ metadata.checkmark.key,
15
+ ({ componentProperties: props }) => {
16
+ const tone = match(props.Tone.value)
17
+ .with("Neutral", () => "neutral")
18
+ .with("🚫[Deprecated]Brand", () => "brand")
19
+ .exhaustive();
20
+
21
+ const commonProps = {
22
+ tone,
23
+ variant: camelCase(props.Shape.value),
24
+ size: handleSizeProp(props.Size.value),
25
+ };
26
+
27
+ return createLocalSnippetElement("Checkmark", commonProps);
28
+ },
29
+ );