@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,149 @@
1
+ import { defineComponentHandler } from "@/codegen/core";
2
+ import * as metadata from "@/entities/data/__generated__/component-sets";
3
+ import { camelCase } from "change-case";
4
+ import { match } from "ts-pattern";
5
+ import type { ComponentHandlerDeps } from "../deps.interface";
6
+ import type {
7
+ ActionButtonGhostProperties,
8
+ ActionButtonProperties,
9
+ } from "@/codegen/component-properties";
10
+ import { handleSizeProp } from "../size";
11
+ import { createLocalSnippetHelper, createSeedReactElement } from "../../element-factories";
12
+ import { findOne } from "@/utils/figma-node";
13
+ import type { NormalizedTextNode } from "@/normalizer";
14
+
15
+ const { createLocalSnippetElement } = createLocalSnippetHelper("action-button");
16
+
17
+ export const createActionButtonHandler = (ctx: ComponentHandlerDeps) =>
18
+ defineComponentHandler<ActionButtonProperties>(
19
+ metadata.componentActionButton.key,
20
+ ({ componentProperties: props, layoutGrow }) => {
21
+ const states = props.State.value.split("-");
22
+
23
+ const { layout, children } = match(props.Layout.value)
24
+ .with("Icon Only", () => ({
25
+ layout: "iconOnly",
26
+ children: [
27
+ createSeedReactElement("Icon", {
28
+ svg: ctx.iconHandler.transform(props["Icon#7574:0"]),
29
+ }),
30
+ ],
31
+ }))
32
+ .with("Icon First", () => ({
33
+ layout: "withText",
34
+ children: [
35
+ createSeedReactElement("PrefixIcon", {
36
+ svg: ctx.iconHandler.transform(props["Prefix Icon#5987:305"]),
37
+ }),
38
+ props["Label#5987:61"].value,
39
+ ],
40
+ }))
41
+ .with("Icon Last", () => ({
42
+ layout: "withText",
43
+ children: [
44
+ props["Label#5987:61"].value,
45
+ createSeedReactElement("SuffixIcon", {
46
+ svg: ctx.iconHandler.transform(props["Suffix Icon#5987:244"]),
47
+ }),
48
+ ],
49
+ }))
50
+ .with("Text Only", () => ({
51
+ layout: "withText",
52
+ children: props["Label#5987:61"].value,
53
+ }))
54
+ .exhaustive();
55
+
56
+ const commonProps = {
57
+ ...(states.includes("Disabled") && {
58
+ disabled: true,
59
+ }),
60
+ ...(states.includes("Loading") && {
61
+ loading: true,
62
+ }),
63
+ size: handleSizeProp(props.Size.value),
64
+ variant: camelCase(props.Variant.value),
65
+ layout,
66
+ ...(layoutGrow === 1 && { flexGrow: true }),
67
+ };
68
+
69
+ return createLocalSnippetElement("ActionButton", commonProps, children);
70
+ },
71
+ );
72
+
73
+ export const createActionButtonGhostHandler = (ctx: ComponentHandlerDeps) =>
74
+ defineComponentHandler<ActionButtonGhostProperties>(
75
+ metadata.componentActionButtonGhostButton.key,
76
+ (node) => {
77
+ const props = node.componentProperties;
78
+
79
+ const states = props.State.value.split("-");
80
+
81
+ const buttonWrapperNode = findOne(
82
+ node,
83
+ (n) => n.type === "FRAME" && n.layoutPositioning !== "ABSOLUTE",
84
+ );
85
+
86
+ const labelNode = buttonWrapperNode
87
+ ? (findOne(buttonWrapperNode, (n) => n.type === "TEXT") as NormalizedTextNode | undefined)
88
+ : undefined;
89
+
90
+ const color = labelNode ? ctx.valueResolver.getFormattedValue.textFill(labelNode) : null;
91
+ const fontWeight = labelNode
92
+ ? ctx.valueResolver.getFormattedValue.fontWeight(labelNode)
93
+ : null;
94
+
95
+ const { layout, children } = match(props.Layout.value)
96
+ .with("Icon Only", () => ({
97
+ layout: "iconOnly",
98
+ children: [
99
+ createSeedReactElement("Icon", {
100
+ svg: ctx.iconHandler.transform(props["Icon#30525:15"]),
101
+ }),
102
+ ],
103
+ }))
104
+ .with("Icon First", () => ({
105
+ layout: "withText",
106
+ children: [
107
+ createSeedReactElement("PrefixIcon", {
108
+ svg: ctx.iconHandler.transform(props["Prefix Icon#30511:3"]),
109
+ }),
110
+ props["Label#30511:2"].value,
111
+ ],
112
+ }))
113
+ .with("Icon Last", () => ({
114
+ layout: "withText",
115
+ children: [
116
+ props["Label#30511:2"].value,
117
+ createSeedReactElement("SuffixIcon", {
118
+ svg: ctx.iconHandler.transform(props["Suffix Icon#30525:0"]),
119
+ }),
120
+ ],
121
+ }))
122
+ .with("Text Only", () => ({
123
+ layout: "withText",
124
+ children: props["Label#30511:2"].value,
125
+ }))
126
+ .exhaustive();
127
+
128
+ const commonProps = {
129
+ ...(states.includes("Disabled") && {
130
+ disabled: true,
131
+ }),
132
+ ...(states.includes("Loading") && {
133
+ loading: true,
134
+ }),
135
+ size: handleSizeProp(props.Size.value),
136
+ variant: "ghost",
137
+ layout,
138
+ ...(color && { color }),
139
+ ...(fontWeight && { fontWeight }),
140
+ ...(props.Bleed.value === "true" && {
141
+ bleedX: "asPadding",
142
+ bleedY: "asPadding",
143
+ }),
144
+ ...(node.layoutGrow === 1 && { flexGrow: true }),
145
+ };
146
+
147
+ return createLocalSnippetElement("ActionButton", commonProps, children);
148
+ },
149
+ );
@@ -0,0 +1,120 @@
1
+ import { defineComponentHandler } from "@/codegen/core";
2
+ import type {
3
+ ActionButtonGhostProperties,
4
+ ActionButtonProperties,
5
+ AlertDialogFooterProperties,
6
+ AlertDialogProperties,
7
+ } from "@/codegen/component-properties";
8
+ import * as metadata from "@/entities/data/__generated__/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/action-button";
17
+
18
+ const { createLocalSnippetElement } = createLocalSnippetHelper("alert-dialog");
19
+ const { createLocalSnippetElement: createLocalSnippetElementTrigger } =
20
+ createLocalSnippetHelper("action-button");
21
+
22
+ export const createAlertDialogHandler = (ctx: ComponentHandlerDeps) => {
23
+ const actionButtonHandler = createActionButtonHandler(ctx);
24
+ const actionButtonGhostHandler = createActionButtonGhostHandler(ctx);
25
+
26
+ return defineComponentHandler<AlertDialogProperties>(
27
+ metadata.componentAlertDialog.key,
28
+ (node, traverse) => {
29
+ const props = node.componentProperties;
30
+ const alertDialogHeader = createLocalSnippetElement("AlertDialogHeader", undefined, [
31
+ ...(props["Show Title#20361:14"].value
32
+ ? [
33
+ createLocalSnippetElement(
34
+ "AlertDialogTitle",
35
+ undefined,
36
+ props["Title Text#20361:0"].value,
37
+ ),
38
+ ]
39
+ : []),
40
+ createLocalSnippetElement(
41
+ "AlertDialogDescription",
42
+ undefined,
43
+ props["Description Text#20361:7"].value,
44
+ ),
45
+ ]);
46
+
47
+ const footerNodes = findAllInstances<AlertDialogFooterProperties>({
48
+ node,
49
+ key: metadata.privateComponentAlertDialogActions.key,
50
+ });
51
+
52
+ if (footerNodes.length === 0 || footerNodes.length > 1) {
53
+ return createLocalSnippetElement("AlertDialog", undefined, alertDialogHeader, {
54
+ comment: "Footer 영역을 확인해주세요.",
55
+ });
56
+ }
57
+
58
+ const footerNode = footerNodes[0];
59
+ const footerNodeProps = traverse(footerNode)?.props;
60
+
61
+ const buttons = footerNode.children.map(traverse);
62
+
63
+ const alertDialogFooterChildren = match(footerNode.componentProperties.Type.value)
64
+ .with("Single", () => buttons)
65
+ .with("Neutral", "Critical", () =>
66
+ createSeedReactElement("ResponsivePair", footerNodeProps, buttons),
67
+ )
68
+ .with("Neutral (Overflow)", "Critical (Overflow)", () =>
69
+ createSeedReactElement("VStack", footerNodeProps, buttons),
70
+ )
71
+ .with("Nonpreferred", () => {
72
+ const [actionButtonNode] = findAllInstances<ActionButtonProperties>({
73
+ node: footerNode,
74
+ key: actionButtonHandler.key,
75
+ });
76
+
77
+ const [ghostButtonNode] = findAllInstances<ActionButtonGhostProperties>({
78
+ node: footerNode,
79
+ key: actionButtonGhostHandler.key,
80
+ });
81
+
82
+ const actionButton = actionButtonHandler.transform(actionButtonNode, traverse);
83
+ const ghostButton = actionButtonGhostHandler.transform(ghostButtonNode, traverse);
84
+
85
+ const buttons = [
86
+ actionButton,
87
+ {
88
+ ...ghostButton,
89
+ props: {
90
+ ...ghostButton.props,
91
+ // nonpreferred shouldn't have bleedX, but in Figma it's not possible to represent that
92
+ bleedX: undefined,
93
+ },
94
+ },
95
+ ];
96
+
97
+ return createSeedReactElement("VStack", footerNodeProps, buttons);
98
+ })
99
+ .exhaustive();
100
+
101
+ const alertDialogFooter = createLocalSnippetElement(
102
+ "AlertDialogFooter",
103
+ undefined,
104
+ alertDialogFooterChildren,
105
+ );
106
+
107
+ return createLocalSnippetElement("AlertDialogRoot", { open: true }, [
108
+ createLocalSnippetElement(
109
+ "AlertDialogTrigger",
110
+ { asChild: true },
111
+ createLocalSnippetElementTrigger("ActionButton", {}, "AlertDialog 열기"),
112
+ ),
113
+ createLocalSnippetElement("AlertDialogContent", undefined, [
114
+ alertDialogHeader,
115
+ alertDialogFooter,
116
+ ]),
117
+ ]);
118
+ },
119
+ );
120
+ };
@@ -0,0 +1,169 @@
1
+ import type {
2
+ AppBarProperties,
3
+ AppBarMainProperties,
4
+ AppBarRightIconButtonProperties,
5
+ AppBarLeftIconButtonProperties,
6
+ } from "@/codegen/component-properties";
7
+ import { defineComponentHandler } from "@/codegen/core";
8
+ import * as metadata from "@/entities/data/__generated__/component-sets";
9
+ import * as components from "@/entities/data/__generated__/components";
10
+ import { findAllInstances } from "@/utils/figma-node";
11
+ import { match } from "ts-pattern";
12
+ import { createLocalSnippetHelper } from "../../element-factories";
13
+ import type { ComponentHandlerDeps } from "../deps.interface";
14
+
15
+ const { createLocalSnippetElement } = createLocalSnippetHelper("app-bar");
16
+
17
+ const createAppBarMainHandler = (_ctx: ComponentHandlerDeps) => {
18
+ return defineComponentHandler<AppBarMainProperties>(
19
+ metadata.privateComponentTopNavigationTitle.key,
20
+ ({ componentProperties: props }) => {
21
+ const { title, subtitle } = match(props.Type.value)
22
+ .with("Title", () => ({
23
+ title: props["Title#16944:0"].value,
24
+ subtitle: undefined,
25
+ }))
26
+ .with("Title-Subtitle", () => ({
27
+ title: props["Title#16944:0"].value,
28
+ subtitle: props["Subtitle#16958:9"].value,
29
+ }))
30
+ .with("Logo (Figma Only)", () => ({
31
+ title: undefined,
32
+ subtitle: undefined,
33
+ }))
34
+ .exhaustive();
35
+
36
+ if (title) {
37
+ return createLocalSnippetElement("AppBarMain", { title, subtitle });
38
+ }
39
+
40
+ return createLocalSnippetElement("AppBarMain", undefined, undefined, {
41
+ comment: "AppBarMain 내부를 직접 작성해주세요.",
42
+ });
43
+ },
44
+ );
45
+ };
46
+
47
+ export const createAppBarHandler = (ctx: ComponentHandlerDeps) => {
48
+ const appBarMainHandler = createAppBarMainHandler(ctx);
49
+
50
+ return defineComponentHandler<AppBarProperties>(
51
+ metadata.componentTopNavigation.key,
52
+ (node, traverse) => {
53
+ const props = node.componentProperties;
54
+
55
+ const { theme, tone } = {
56
+ theme: match(props["OS (Figma Only)"].value)
57
+ .with("Android", () => "android")
58
+ .with("iOS", () => "cupertino")
59
+ .exhaustive(),
60
+
61
+ tone: match(props.Variant.value)
62
+ .with("Layer Default", () => "layer")
63
+ .with("Transparent", () => "transparent")
64
+ .exhaustive(),
65
+ };
66
+
67
+ const main = (() => {
68
+ if (!props["Show Title#33588:82"].value) {
69
+ return undefined;
70
+ }
71
+
72
+ const [mainNode] = findAllInstances<AppBarMainProperties>({
73
+ node,
74
+ key: metadata.privateComponentTopNavigationTitle.key,
75
+ });
76
+
77
+ if (!mainNode) {
78
+ return undefined;
79
+ }
80
+
81
+ return appBarMainHandler.transform(mainNode, traverse);
82
+ })();
83
+
84
+ const leftChildren = match(props.Left.value)
85
+ .with("None", () => undefined)
86
+ .with("Back", () => [createLocalSnippetElement("AppBarBackButton")])
87
+ .with("Close", () => [createLocalSnippetElement("AppBarCloseButton")])
88
+ .with("Custom", () => {
89
+ const buttons = findAllInstances<AppBarLeftIconButtonProperties>({
90
+ node,
91
+ key: components.privateComponentTopNavigationLeftIconButton.key,
92
+ });
93
+
94
+ if (buttons.length > 0) {
95
+ return buttons.map((button) =>
96
+ createLocalSnippetElement(
97
+ "AppBarIconButton",
98
+ undefined,
99
+ ctx.iconHandler.transform(button.componentProperties["Icon#33580:0"]),
100
+ { comment: "AppBarIconButton에 aria-label 속성을 추가해주세요." },
101
+ ),
102
+ );
103
+ }
104
+
105
+ return undefined;
106
+ })
107
+ .exhaustive();
108
+
109
+ const left =
110
+ leftChildren && leftChildren.length > 0
111
+ ? createLocalSnippetElement("AppBarLeft", {}, leftChildren)
112
+ : undefined;
113
+
114
+ const rightChildren = match(props.Right.value)
115
+ .with("None", () => undefined)
116
+ .with("1 Icon Button", "2 Icon Button", "3 Icon Button", () => {
117
+ const buttons = findAllInstances<AppBarRightIconButtonProperties>({
118
+ node,
119
+ key: metadata.privateComponentTopNavigationRightIconButton.key,
120
+ });
121
+
122
+ return buttons.map((button) =>
123
+ createLocalSnippetElement(
124
+ "AppBarIconButton",
125
+ undefined,
126
+ ctx.iconHandler.transform(button.componentProperties["Icon#6406:3"]),
127
+ { comment: "AppBarIconButton에 aria-label 속성을 추가해주세요." },
128
+ ),
129
+ );
130
+ })
131
+ .with("Text Button", () => undefined)
132
+ .exhaustive();
133
+
134
+ const right =
135
+ rightChildren && rightChildren.length > 0
136
+ ? createLocalSnippetElement("AppBarRight", {}, rightChildren)
137
+ : undefined;
138
+
139
+ return createLocalSnippetElement(
140
+ "AppBar",
141
+ { theme, tone },
142
+ [left, main, right].filter(Boolean),
143
+ {
144
+ comment:
145
+ tone === "transparent"
146
+ ? '<AppScreen layerOffsetTop="none">으로 상단 패딩을 제거할 수 있습니다.'
147
+ : undefined,
148
+ },
149
+ );
150
+ },
151
+ );
152
+ };
153
+
154
+ export const createAppBarPresetHandler = (ctx: ComponentHandlerDeps) => {
155
+ const appBarHandler = createAppBarHandler(ctx);
156
+
157
+ return defineComponentHandler(metadata.templateTopNavigationPreset.key, (node, traverse) => {
158
+ const [appBarNode] = findAllInstances<AppBarProperties>({
159
+ node,
160
+ key: metadata.componentTopNavigation.key,
161
+ });
162
+
163
+ if (!appBarNode) {
164
+ return createLocalSnippetElement("AppBar");
165
+ }
166
+
167
+ return appBarHandler.transform(appBarNode, traverse);
168
+ });
169
+ };
@@ -0,0 +1,144 @@
1
+ import { defineComponentHandler } from "@/codegen/core";
2
+ import * as metadata from "@/entities/data/__generated__/archive/component-sets";
3
+ import { camelCase } from "change-case";
4
+ import { match } from "ts-pattern";
5
+ import type { ComponentHandlerDeps } from "../../deps.interface";
6
+ import type {
7
+ ActionButtonGhostProperties,
8
+ ActionButtonProperties,
9
+ } from "@/codegen/component-properties.archive";
10
+ import { handleSizeProp } from "../../size";
11
+ import { createLocalSnippetHelper, createSeedReactElement } from "../../../element-factories";
12
+ import { findOne } from "@/utils/figma-node";
13
+ import type { NormalizedTextNode } from "@/normalizer";
14
+
15
+ const { createLocalSnippetElement } = createLocalSnippetHelper("action-button");
16
+
17
+ export const createActionButtonHandler = (ctx: ComponentHandlerDeps) =>
18
+ defineComponentHandler<ActionButtonProperties>(
19
+ metadata.actionButton.key,
20
+ ({ componentProperties: props }) => {
21
+ const states = props.State.value.split("-");
22
+
23
+ const { layout, children } = match(props.Layout.value)
24
+ .with("Icon Only", () => ({
25
+ layout: "iconOnly",
26
+ children: [
27
+ createSeedReactElement("Icon", {
28
+ svg: ctx.iconHandler.transform(props["Icon#7574:0"]),
29
+ }),
30
+ ],
31
+ }))
32
+ .with("Icon First", () => ({
33
+ layout: "withText",
34
+ children: [
35
+ createSeedReactElement("PrefixIcon", {
36
+ svg: ctx.iconHandler.transform(props["Prefix Icon#5987:305"]),
37
+ }),
38
+ props["Label#5987:61"].value,
39
+ ],
40
+ }))
41
+ .with("Icon Last", () => ({
42
+ layout: "withText",
43
+ children: [
44
+ props["Label#5987:61"].value,
45
+ createSeedReactElement("SuffixIcon", {
46
+ svg: ctx.iconHandler.transform(props["Suffix Icon#5987:244"]),
47
+ }),
48
+ ],
49
+ }))
50
+ .with("Text Only", () => ({
51
+ layout: "withText",
52
+ children: props["Label#5987:61"].value,
53
+ }))
54
+ .exhaustive();
55
+
56
+ const commonProps = {
57
+ ...(states.includes("Disabled") && {
58
+ disabled: true,
59
+ }),
60
+ ...(states.includes("Loading") && {
61
+ loading: true,
62
+ }),
63
+ size: handleSizeProp(props.Size.value),
64
+ variant: camelCase(props.Variant.value),
65
+ layout,
66
+ };
67
+
68
+ return createLocalSnippetElement("ActionButton", commonProps, children);
69
+ },
70
+ );
71
+
72
+ const ACTION_BUTTON_GHOST_BUTTON_KEY = "ea69291fb4d76217419f3d9613ae16aadafb56a5";
73
+
74
+ export const createActionButtonGhostHandler = (ctx: ComponentHandlerDeps) =>
75
+ defineComponentHandler<ActionButtonGhostProperties>(ACTION_BUTTON_GHOST_BUTTON_KEY, (node) => {
76
+ const props = node.componentProperties;
77
+
78
+ const states = props.State.value.split("-");
79
+
80
+ const buttonWrapperNode = findOne(
81
+ node,
82
+ (n) => n.type === "FRAME" && n.layoutPositioning !== "ABSOLUTE",
83
+ );
84
+
85
+ const labelNode = buttonWrapperNode
86
+ ? (findOne(buttonWrapperNode, (n) => n.type === "TEXT") as NormalizedTextNode | undefined)
87
+ : undefined;
88
+
89
+ const color = labelNode ? ctx.valueResolver.getFormattedValue.textFill(labelNode) : null;
90
+ const fontWeight = labelNode ? ctx.valueResolver.getFormattedValue.fontWeight(labelNode) : null;
91
+
92
+ const { layout, children } = match(props.Layout.value)
93
+ .with("Icon Only", () => ({
94
+ layout: "iconOnly",
95
+ children: [
96
+ createSeedReactElement("Icon", {
97
+ svg: ctx.iconHandler.transform(props["Icon#30525:15"]),
98
+ }),
99
+ ],
100
+ }))
101
+ .with("Icon First", () => ({
102
+ layout: "withText",
103
+ children: [
104
+ createSeedReactElement("PrefixIcon", {
105
+ svg: ctx.iconHandler.transform(props["Prefix Icon#30511:3"]),
106
+ }),
107
+ props["Label#30511:2"].value,
108
+ ],
109
+ }))
110
+ .with("Icon Last", () => ({
111
+ layout: "withText",
112
+ children: [
113
+ props["Label#30511:2"].value,
114
+ createSeedReactElement("SuffixIcon", {
115
+ svg: ctx.iconHandler.transform(props["Suffix Icon#30525:0"]),
116
+ }),
117
+ ],
118
+ }))
119
+ .with("Text Only", () => ({
120
+ layout: "withText",
121
+ children: props["Label#30511:2"].value,
122
+ }))
123
+ .exhaustive();
124
+
125
+ const commonProps = {
126
+ ...(states.includes("Disabled") && {
127
+ disabled: true,
128
+ }),
129
+ ...(states.includes("Loading") && {
130
+ loading: true,
131
+ }),
132
+ size: handleSizeProp(props.Size.value),
133
+ variant: "ghost",
134
+ layout,
135
+ ...(color && { color }),
136
+ ...(fontWeight && { fontWeight }),
137
+ ...(props.Bleed.value === "true" && {
138
+ bleedX: "asPadding",
139
+ bleedY: "asPadding",
140
+ }),
141
+ };
142
+
143
+ return createLocalSnippetElement("ActionButton", commonProps, children);
144
+ });