@seed-design/figma 0.1.7 → 0.1.8

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 (165) hide show
  1. package/lib/codegen/index.cjs +746 -646
  2. package/lib/codegen/index.d.ts +707 -627
  3. package/lib/codegen/index.d.ts.map +1 -1
  4. package/lib/codegen/index.js +746 -646
  5. package/lib/codegen/targets/react/index.cjs +1170 -1180
  6. package/lib/codegen/targets/react/index.d.ts +20 -9
  7. package/lib/codegen/targets/react/index.d.ts.map +1 -1
  8. package/lib/codegen/targets/react/index.js +1170 -1180
  9. package/lib/index.cjs +883 -668
  10. package/lib/index.d.ts +3 -1
  11. package/lib/index.d.ts.map +1 -1
  12. package/lib/index.js +883 -668
  13. package/package.json +3 -3
  14. package/src/codegen/component-properties.ts +283 -146
  15. package/src/codegen/core/component-handler.ts +9 -3
  16. package/src/codegen/core/jsx.ts +7 -2
  17. package/src/codegen/core/value-resolver.ts +35 -21
  18. package/src/codegen/targets/figma/pipeline.ts +4 -2
  19. package/src/codegen/targets/figma/value-resolver.ts +38 -2
  20. package/src/codegen/targets/react/component/handlers/action-button.ts +69 -5
  21. package/src/codegen/targets/react/component/handlers/alert-dialog.ts +81 -0
  22. package/src/codegen/targets/react/component/handlers/app-bar.ts +93 -128
  23. package/src/codegen/targets/react/component/handlers/avatar.ts +17 -8
  24. package/src/codegen/targets/react/component/handlers/badge.ts +0 -1
  25. package/src/codegen/targets/react/component/handlers/bottom-sheet.ts +71 -0
  26. package/src/codegen/targets/react/component/handlers/callout.ts +13 -17
  27. package/src/codegen/targets/react/component/handlers/chip.ts +66 -0
  28. package/src/codegen/targets/react/component/handlers/contextual-floating-button.ts +52 -0
  29. package/src/codegen/targets/react/component/handlers/divider.ts +19 -0
  30. package/src/codegen/targets/react/component/handlers/error-state.ts +26 -23
  31. package/src/codegen/targets/react/component/handlers/floating-action-button.ts +48 -0
  32. package/src/codegen/targets/react/component/handlers/help-bubble.ts +4 -5
  33. package/src/codegen/targets/react/component/handlers/identity-placeholder.ts +3 -2
  34. package/src/codegen/targets/react/component/handlers/inline-banner.ts +7 -14
  35. package/src/codegen/targets/react/component/handlers/manner-temp.ts +18 -0
  36. package/src/codegen/targets/react/component/handlers/menu-sheet.ts +106 -0
  37. package/src/codegen/targets/react/component/handlers/multiline-text-field.ts +1 -1
  38. package/src/codegen/targets/react/component/handlers/progress-circle.ts +3 -1
  39. package/src/codegen/targets/react/component/handlers/segmented-control.ts +4 -2
  40. package/src/codegen/targets/react/component/handlers/select-box.ts +7 -4
  41. package/src/codegen/targets/react/component/handlers/snackbar.ts +2 -2
  42. package/src/codegen/targets/react/component/handlers/tabs.ts +8 -125
  43. package/src/codegen/targets/react/component/index.ts +22 -19
  44. package/src/codegen/targets/react/instance.ts +1 -1
  45. package/src/codegen/targets/react/pipeline.ts +4 -2
  46. package/src/codegen/targets/react/props.ts +11 -4
  47. package/src/codegen/targets/react/value-resolver.ts +38 -3
  48. package/src/entities/data/__generated__/component-sets/action-button.d.ts +5 -5
  49. package/src/entities/data/__generated__/component-sets/action-button.mjs +5 -5
  50. package/src/entities/data/__generated__/component-sets/alert-dialog.d.ts +26 -0
  51. package/src/entities/data/__generated__/component-sets/alert-dialog.mjs +26 -0
  52. package/src/entities/data/__generated__/component-sets/avatar-stack.d.ts +5 -18
  53. package/src/entities/data/__generated__/component-sets/avatar-stack.mjs +5 -18
  54. package/src/entities/data/__generated__/component-sets/avatar.d.ts +11 -5
  55. package/src/entities/data/__generated__/component-sets/avatar.mjs +11 -5
  56. package/src/entities/data/__generated__/component-sets/badge.d.ts +0 -7
  57. package/src/entities/data/__generated__/component-sets/badge.mjs +0 -7
  58. package/src/entities/data/__generated__/component-sets/bottom-navigation-global.d.ts +3 -0
  59. package/src/entities/data/__generated__/component-sets/bottom-navigation-global.mjs +3 -0
  60. package/src/entities/data/__generated__/component-sets/bottom-navigation-kr.d.ts +3 -0
  61. package/src/entities/data/__generated__/component-sets/bottom-navigation-kr.mjs +3 -0
  62. package/src/entities/data/__generated__/component-sets/bottom-sheet.d.ts +53 -4
  63. package/src/entities/data/__generated__/component-sets/bottom-sheet.mjs +53 -4
  64. package/src/entities/data/__generated__/component-sets/callout.d.ts +13 -22
  65. package/src/entities/data/__generated__/component-sets/callout.mjs +13 -22
  66. package/src/entities/data/__generated__/component-sets/checkbox.d.ts +3 -3
  67. package/src/entities/data/__generated__/component-sets/checkbox.mjs +3 -3
  68. package/src/entities/data/__generated__/component-sets/checkmark.d.ts +34 -0
  69. package/src/entities/data/__generated__/component-sets/checkmark.mjs +34 -0
  70. package/src/entities/data/__generated__/component-sets/chip.d.ts +86 -0
  71. package/src/entities/data/__generated__/component-sets/chip.mjs +86 -0
  72. package/src/entities/data/__generated__/component-sets/chlid.d.ts +14 -0
  73. package/src/entities/data/__generated__/component-sets/chlid.mjs +14 -0
  74. package/src/entities/data/__generated__/component-sets/{extended-floating-action-button.d.ts → contextual-floating-button.d.ts} +12 -10
  75. package/src/entities/data/__generated__/component-sets/{extended-floating-action-button.mjs → contextual-floating-button.mjs} +12 -10
  76. package/src/entities/data/__generated__/component-sets/floating-action-button.d.ts +4 -8
  77. package/src/entities/data/__generated__/component-sets/floating-action-button.mjs +4 -8
  78. package/src/entities/data/__generated__/component-sets/help-bubble.d.ts +3 -10
  79. package/src/entities/data/__generated__/component-sets/help-bubble.mjs +3 -10
  80. package/src/entities/data/__generated__/component-sets/index.d.ts +13 -13
  81. package/src/entities/data/__generated__/component-sets/index.mjs +13 -13
  82. package/src/entities/data/__generated__/component-sets/inline-banner.d.ts +3 -3
  83. package/src/entities/data/__generated__/component-sets/inline-banner.mjs +3 -3
  84. package/src/entities/data/__generated__/component-sets/main-tab-navigation-global.d.ts +2 -2
  85. package/src/entities/data/__generated__/component-sets/main-tab-navigation-global.mjs +2 -2
  86. package/src/entities/data/__generated__/component-sets/manner-temp-badge.d.ts +2 -2
  87. package/src/entities/data/__generated__/component-sets/manner-temp-badge.mjs +2 -2
  88. package/src/entities/data/__generated__/component-sets/manner-temp.d.ts +2 -2
  89. package/src/entities/data/__generated__/component-sets/manner-temp.mjs +2 -2
  90. package/src/entities/data/__generated__/component-sets/{extended-action-sheet.d.ts → menu-sheet.d.ts} +17 -13
  91. package/src/entities/data/__generated__/component-sets/{extended-action-sheet.mjs → menu-sheet.mjs} +17 -13
  92. package/src/entities/data/__generated__/component-sets/multiline-text-field.d.ts +18 -18
  93. package/src/entities/data/__generated__/component-sets/multiline-text-field.mjs +18 -18
  94. package/src/entities/data/__generated__/component-sets/progress-circle.d.ts +2 -1
  95. package/src/entities/data/__generated__/component-sets/progress-circle.mjs +2 -1
  96. package/src/entities/data/__generated__/component-sets/radio-mark.d.ts +34 -0
  97. package/src/entities/data/__generated__/component-sets/radio-mark.mjs +34 -0
  98. package/src/entities/data/__generated__/component-sets/radio.d.ts +2 -2
  99. package/src/entities/data/__generated__/component-sets/radio.mjs +2 -2
  100. package/src/entities/data/__generated__/component-sets/reaction-button.d.ts +6 -6
  101. package/src/entities/data/__generated__/component-sets/reaction-button.mjs +6 -6
  102. package/src/entities/data/__generated__/component-sets/resizable-child.d.ts +18 -0
  103. package/src/entities/data/__generated__/component-sets/resizable-child.mjs +18 -0
  104. package/src/entities/data/__generated__/component-sets/resizable-icon.d.ts +18 -0
  105. package/src/entities/data/__generated__/component-sets/resizable-icon.mjs +18 -0
  106. package/src/entities/data/__generated__/component-sets/select-box.d.ts +4 -4
  107. package/src/entities/data/__generated__/component-sets/select-box.mjs +4 -4
  108. package/src/entities/data/__generated__/component-sets/skeleton.d.ts +7 -0
  109. package/src/entities/data/__generated__/component-sets/skeleton.mjs +7 -0
  110. package/src/entities/data/__generated__/component-sets/snackbar.d.ts +4 -4
  111. package/src/entities/data/__generated__/component-sets/snackbar.mjs +4 -4
  112. package/src/entities/data/__generated__/component-sets/switch.d.ts +1 -1
  113. package/src/entities/data/__generated__/component-sets/switch.mjs +1 -1
  114. package/src/entities/data/__generated__/component-sets/tabs.d.ts +13 -0
  115. package/src/entities/data/__generated__/component-sets/tabs.mjs +13 -0
  116. package/src/entities/data/__generated__/component-sets/template-button-group.d.ts +9 -33
  117. package/src/entities/data/__generated__/component-sets/template-button-group.mjs +9 -33
  118. package/src/entities/data/__generated__/component-sets/template-chip-group.d.ts +15 -12
  119. package/src/entities/data/__generated__/component-sets/template-chip-group.mjs +15 -12
  120. package/src/entities/data/__generated__/component-sets/template-completion.d.ts +28 -0
  121. package/src/entities/data/__generated__/component-sets/template-completion.mjs +28 -0
  122. package/src/entities/data/__generated__/component-sets/{error-state.d.ts → template-error-state.d.ts} +5 -5
  123. package/src/entities/data/__generated__/component-sets/{error-state.mjs → template-error-state.mjs} +5 -5
  124. package/src/entities/data/__generated__/component-sets/template-top-navigation.d.ts +9 -7
  125. package/src/entities/data/__generated__/component-sets/template-top-navigation.mjs +9 -7
  126. package/src/entities/data/__generated__/component-sets/text-field.d.ts +35 -36
  127. package/src/entities/data/__generated__/component-sets/text-field.mjs +35 -36
  128. package/src/entities/data/__generated__/component-sets/toggle-button.d.ts +7 -7
  129. package/src/entities/data/__generated__/component-sets/toggle-button.mjs +7 -7
  130. package/src/entities/data/__generated__/component-sets/top-navigation.d.ts +42 -0
  131. package/src/entities/data/__generated__/component-sets/top-navigation.mjs +42 -0
  132. package/src/entities/data/styles.ts +94 -0
  133. package/src/entities/index.ts +5 -2
  134. package/src/normalizer/from-plugin.ts +104 -44
  135. package/src/normalizer/types.ts +3 -1
  136. package/src/utils/figma-gradient.ts +72 -0
  137. package/src/utils/figma-node.ts +4 -3
  138. package/src/codegen/targets/react/component/handlers/action-chip.ts +0 -72
  139. package/src/codegen/targets/react/component/handlers/action-sheet.ts +0 -82
  140. package/src/codegen/targets/react/component/handlers/chip-tabs.ts +0 -57
  141. package/src/codegen/targets/react/component/handlers/control-chip.ts +0 -81
  142. package/src/codegen/targets/react/component/handlers/extended-action-sheet.ts +0 -98
  143. package/src/codegen/targets/react/component/handlers/extended-fab.ts +0 -25
  144. package/src/codegen/targets/react/component/handlers/fab.ts +0 -22
  145. package/src/codegen/targets/react/component/handlers/text-button.ts +0 -49
  146. package/src/entities/data/__generated__/component-sets/action-chip.d.ts +0 -57
  147. package/src/entities/data/__generated__/component-sets/action-chip.mjs +0 -57
  148. package/src/entities/data/__generated__/component-sets/action-sheet.d.ts +0 -40
  149. package/src/entities/data/__generated__/component-sets/action-sheet.mjs +0 -40
  150. package/src/entities/data/__generated__/component-sets/chip-tablist.d.ts +0 -24
  151. package/src/entities/data/__generated__/component-sets/chip-tablist.mjs +0 -24
  152. package/src/entities/data/__generated__/component-sets/control-chip.d.ts +0 -60
  153. package/src/entities/data/__generated__/component-sets/control-chip.mjs +0 -60
  154. package/src/entities/data/__generated__/component-sets/identity-placeholder.d.ts +0 -13
  155. package/src/entities/data/__generated__/component-sets/identity-placeholder.mjs +0 -13
  156. package/src/entities/data/__generated__/component-sets/manner-temp-bar.d.ts +0 -23
  157. package/src/entities/data/__generated__/component-sets/manner-temp-bar.mjs +0 -23
  158. package/src/entities/data/__generated__/component-sets/standard-navigation.d.ts +0 -23
  159. package/src/entities/data/__generated__/component-sets/standard-navigation.mjs +0 -23
  160. package/src/entities/data/__generated__/component-sets/tablist.d.ts +0 -29
  161. package/src/entities/data/__generated__/component-sets/tablist.mjs +0 -29
  162. package/src/entities/data/__generated__/component-sets/template-bottom-fixed-bar.d.ts +0 -42
  163. package/src/entities/data/__generated__/component-sets/template-bottom-fixed-bar.mjs +0 -42
  164. package/src/entities/data/__generated__/component-sets/text-button.d.ts +0 -45
  165. package/src/entities/data/__generated__/component-sets/text-button.mjs +0 -45
@@ -16,11 +16,11 @@ import {
16
16
  import type { RGBA } from "@figma/rest-api-spec";
17
17
  import type { VariableService } from "../../entities/variable.service";
18
18
 
19
- export interface ValueResolver<TColor, TDimension, TFontDimension, TFontWeight> {
19
+ export interface ValueResolver<TColor, TGradient, TDimension, TFontDimension, TFontWeight> {
20
20
  getFormattedValue: {
21
21
  frameFill: (
22
22
  node: NormalizedHasGeometryTrait & NormalizedIsLayerTrait,
23
- ) => string | TColor | undefined;
23
+ ) => string | TColor | TGradient | undefined;
24
24
  shapeFill: (
25
25
  node: NormalizedHasGeometryTrait & NormalizedIsLayerTrait,
26
26
  ) => string | TColor | undefined;
@@ -90,11 +90,12 @@ export interface ValueResolver<TColor, TDimension, TFontDimension, TFontWeight>
90
90
  ) => string | undefined; // TODO: we might turn this into a generic; not sure yet
91
91
  }
92
92
 
93
- export interface ValueResolverDeps<TColor, TDimension, TFontDimension, TFontWeight> {
93
+ export interface ValueResolverDeps<TColor, TGradient, TDimension, TFontDimension, TFontWeight> {
94
94
  variableService: VariableService;
95
95
  variableNameFormatter: (props: { slug: string[] }) => string;
96
96
  styleService: StyleService;
97
- styleNameFormatter: (props: { slug: string[] }) => string;
97
+ textStyleNameFormatter: (props: { slug: string[] }) => string;
98
+ fillStyleResolver: (props: { slug: string[] }) => TGradient | undefined;
98
99
  rawValueFormatters: {
99
100
  color: (value: RGBA) => string | TColor;
100
101
  dimension: (value: number) => string | TDimension;
@@ -104,15 +105,17 @@ export interface ValueResolverDeps<TColor, TDimension, TFontDimension, TFontWeig
104
105
  shouldInferVariableName: boolean;
105
106
  }
106
107
 
107
- export function createValueResolver<TColor, TDimension, TFontDimension, TFontWeight>({
108
+ export function createValueResolver<TColor, TGradient, TDimension, TFontDimension, TFontWeight>({
108
109
  variableService,
109
110
  variableNameFormatter,
110
111
  styleService,
111
- styleNameFormatter,
112
+ textStyleNameFormatter,
113
+ fillStyleResolver,
112
114
  rawValueFormatters,
113
115
  shouldInferVariableName,
114
- }: ValueResolverDeps<TColor, TDimension, TFontDimension, TFontWeight>): ValueResolver<
116
+ }: ValueResolverDeps<TColor, TGradient, TDimension, TFontDimension, TFontWeight>): ValueResolver<
115
117
  TColor,
118
+ TGradient,
116
119
  TDimension,
117
120
  TFontDimension,
118
121
  TFontWeight
@@ -141,16 +144,6 @@ export function createValueResolver<TColor, TDimension, TFontDimension, TFontWei
141
144
  return getVariableName(inferred.key);
142
145
  }
143
146
 
144
- function getStyleName(key: string) {
145
- const slug = styleService.getSlug(key);
146
-
147
- if (!slug) {
148
- return undefined;
149
- }
150
-
151
- return styleNameFormatter({ slug });
152
- }
153
-
154
147
  function processColor(
155
148
  key: string | undefined,
156
149
  value: RGBA | undefined,
@@ -167,6 +160,16 @@ export function createValueResolver<TColor, TDimension, TFontDimension, TFontWei
167
160
  return undefined;
168
161
  }
169
162
 
163
+ function processFillStyle(key: string) {
164
+ const slug = styleService.getSlug(key);
165
+
166
+ if (!slug) {
167
+ return undefined;
168
+ }
169
+
170
+ return fillStyleResolver({ slug });
171
+ }
172
+
170
173
  function processDimension(
171
174
  key: string | undefined,
172
175
  value: number | undefined,
@@ -229,6 +232,7 @@ export function createValueResolver<TColor, TDimension, TFontDimension, TFontWei
229
232
 
230
233
  const getFormattedValue: ValueResolver<
231
234
  TColor,
235
+ TGradient,
232
236
  TDimension,
233
237
  TFontDimension,
234
238
  TFontWeight
@@ -264,7 +268,13 @@ export function createValueResolver<TColor, TDimension, TFontDimension, TFontWei
264
268
  itemSpacing: (node) =>
265
269
  processDimension(node.boundVariables?.itemSpacing?.id, node.itemSpacing, "GAP"),
266
270
  frameFill: (node) =>
267
- processColor(getFirstFillVariable(node)?.id, getFirstSolidFill(node)?.color, "FRAME_FILL"),
271
+ node.fillStyleKey
272
+ ? processFillStyle(node.fillStyleKey)
273
+ : processColor(
274
+ getFirstFillVariable(node)?.id,
275
+ getFirstSolidFill(node)?.color,
276
+ "FRAME_FILL",
277
+ ),
268
278
  shapeFill: (node) =>
269
279
  processColor(getFirstFillVariable(node)?.id, getFirstSolidFill(node)?.color, "SHAPE_FILL"),
270
280
  textFill: (node) =>
@@ -312,11 +322,15 @@ export function createValueResolver<TColor, TDimension, TFontDimension, TFontWei
312
322
  };
313
323
 
314
324
  function getTextStyleValue(node: NormalizedTypePropertiesTrait & NormalizedIsLayerTrait) {
315
- if (node.textStyleKey) {
316
- return getStyleName(node.textStyleKey);
325
+ if (!node.textStyleKey) return undefined;
326
+
327
+ const slug = styleService.getSlug(node.textStyleKey);
328
+
329
+ if (!slug) {
330
+ return undefined;
317
331
  }
318
332
 
319
- return undefined;
333
+ return textStyleNameFormatter({ slug });
320
334
  }
321
335
 
322
336
  return {
@@ -21,8 +21,9 @@ import {
21
21
  } from "./shape";
22
22
  import { createTextTransformer } from "./text";
23
23
  import {
24
+ defaultFillStyleResolver,
24
25
  defaultRawValueFormatters,
25
- defaultStyleNameFormatter,
26
+ defaultTextStyleNameFormatter,
26
27
  defaultVariableNameFormatter,
27
28
  } from "./value-resolver";
28
29
 
@@ -38,7 +39,8 @@ export function createPipeline(options: CreatePipelineConfig = {}): CodeGenerato
38
39
  variableService,
39
40
  variableNameFormatter: defaultVariableNameFormatter,
40
41
  styleService,
41
- styleNameFormatter: defaultStyleNameFormatter,
42
+ textStyleNameFormatter: defaultTextStyleNameFormatter,
43
+ fillStyleResolver: defaultFillStyleResolver,
42
44
  rawValueFormatters: defaultRawValueFormatters,
43
45
  shouldInferVariableName,
44
46
  });
@@ -1,7 +1,14 @@
1
1
  import type { ValueResolver } from "@/codegen/core";
2
2
  import { toCssRgba } from "@/utils/css";
3
+ import { camelCase } from "change-case";
3
4
 
4
- export type FigmaValueResolver = ValueResolver<string, number, number, number>;
5
+ export type FigmaValueResolver = ValueResolver<
6
+ string,
7
+ { value: string; direction?: string },
8
+ number,
9
+ number,
10
+ number
11
+ >;
5
12
 
6
13
  export const defaultVariableNameFormatter = ({ slug }: { slug: string[] }) =>
7
14
  slug
@@ -9,7 +16,36 @@ export const defaultVariableNameFormatter = ({ slug }: { slug: string[] }) =>
9
16
  .map((s) => s.replaceAll(",", "_"))
10
17
  .join("/");
11
18
 
12
- export const defaultStyleNameFormatter = ({ slug }: { slug: string[] }) => slug[slug.length - 1]!;
19
+ export const defaultTextStyleNameFormatter = ({ slug }: { slug: string[] }) =>
20
+ slug[slug.length - 1]!;
21
+
22
+ export const defaultFillStyleResolver = ({ slug }: { slug: string[] }) => {
23
+ const [, ...rest] = slug;
24
+
25
+ if (rest.includes("fade")) {
26
+ // ["fade", "layer-default", "↓(to-bottom)"]
27
+
28
+ const last = rest[rest.length - 1];
29
+
30
+ const direction = (() => {
31
+ if (last.startsWith("↓")) return "to bottom";
32
+ if (last.startsWith("↑")) return "to top";
33
+ if (last.startsWith("→")) return "to right";
34
+ if (last.startsWith("←")) return "to left";
35
+
36
+ return "unknown";
37
+ })();
38
+
39
+ return {
40
+ value: camelCase(rest.slice(0, -1).join("-"), { mergeAmbiguousCharacters: true }),
41
+ direction,
42
+ };
43
+ }
44
+
45
+ return {
46
+ value: camelCase(rest.join("-"), { mergeAmbiguousCharacters: true }),
47
+ };
48
+ };
13
49
 
14
50
  export const defaultRawValueFormatters = {
15
51
  color: (value: RGBA) => toCssRgba(value),
@@ -3,9 +3,12 @@ import * as metadata from "@/entities/data/__generated__/component-sets";
3
3
  import { camelCase } from "change-case";
4
4
  import { match } from "ts-pattern";
5
5
  import type { ComponentHandlerDeps } from "../deps.interface";
6
- import type { ActionButtonProperties } from "@/codegen/component-properties";
6
+ import type {
7
+ ActionButtonGhostProperties,
8
+ ActionButtonProperties,
9
+ } from "@/codegen/component-properties";
7
10
  import { handleSizeProp } from "../size";
8
- import { createLocalSnippetHelper } from "../../element-factories";
11
+ import { createLocalSnippetHelper, createSeedReactElement } from "../../element-factories";
9
12
 
10
13
  const { createLocalSnippetElement } = createLocalSnippetHelper("action-button");
11
14
 
@@ -19,7 +22,7 @@ export const createActionButtonHandler = (ctx: ComponentHandlerDeps) =>
19
22
  .with("Icon Only", () => ({
20
23
  layout: "iconOnly",
21
24
  children: [
22
- createLocalSnippetElement("Icon", {
25
+ createSeedReactElement("Icon", {
23
26
  svg: ctx.iconHandler.transform(props["Icon#7574:0"]),
24
27
  }),
25
28
  ],
@@ -27,7 +30,7 @@ export const createActionButtonHandler = (ctx: ComponentHandlerDeps) =>
27
30
  .with("Icon First", () => ({
28
31
  layout: "withText",
29
32
  children: [
30
- createLocalSnippetElement("PrefixIcon", {
33
+ createSeedReactElement("PrefixIcon", {
31
34
  svg: ctx.iconHandler.transform(props["Prefix Icon#5987:305"]),
32
35
  }),
33
36
  props["Label#5987:61"].value,
@@ -37,7 +40,7 @@ export const createActionButtonHandler = (ctx: ComponentHandlerDeps) =>
37
40
  layout: "withText",
38
41
  children: [
39
42
  props["Label#5987:61"].value,
40
- createLocalSnippetElement("SuffixIcon", {
43
+ createSeedReactElement("SuffixIcon", {
41
44
  svg: ctx.iconHandler.transform(props["Suffix Icon#5987:244"]),
42
45
  }),
43
46
  ],
@@ -63,3 +66,64 @@ export const createActionButtonHandler = (ctx: ComponentHandlerDeps) =>
63
66
  return createLocalSnippetElement("ActionButton", commonProps, children);
64
67
  },
65
68
  );
69
+
70
+ const ACTION_BUTTON_GHOST_BUTTON_KEY = "ea69291fb4d76217419f3d9613ae16aadafb56a5";
71
+
72
+ export const createActionButtonGhostHandler = (ctx: ComponentHandlerDeps) =>
73
+ defineComponentHandler<ActionButtonGhostProperties>(
74
+ ACTION_BUTTON_GHOST_BUTTON_KEY,
75
+ ({ componentProperties: props }) => {
76
+ const states = props.State.value.split("-");
77
+
78
+ const { layout, children } = match(props.Layout.value)
79
+ .with("Icon Only", () => ({
80
+ layout: "iconOnly",
81
+ children: [
82
+ createSeedReactElement("Icon", {
83
+ svg: ctx.iconHandler.transform(props["Icon#30525:15"]),
84
+ }),
85
+ ],
86
+ }))
87
+ .with("Icon First", () => ({
88
+ layout: "withText",
89
+ children: [
90
+ createSeedReactElement("PrefixIcon", {
91
+ svg: ctx.iconHandler.transform(props["Prefix Icon#30511:3"]),
92
+ }),
93
+ props["Label#30511:2"].value,
94
+ ],
95
+ }))
96
+ .with("Icon Last", () => ({
97
+ layout: "withText",
98
+ children: [
99
+ props["Label#30511:2"].value,
100
+ createSeedReactElement("SuffixIcon", {
101
+ svg: ctx.iconHandler.transform(props["Suffix Icon#30525:0"]),
102
+ }),
103
+ ],
104
+ }))
105
+ .with("Text Only", () => ({
106
+ layout: "withText",
107
+ children: props["Label#30511:2"].value,
108
+ }))
109
+ .exhaustive();
110
+
111
+ const commonProps = {
112
+ ...(states.includes("Disabled") && {
113
+ disabled: true,
114
+ }),
115
+ ...(states.includes("Loading") && {
116
+ loading: true,
117
+ }),
118
+ size: handleSizeProp(props.Size.value),
119
+ variant: "ghost",
120
+ layout,
121
+ ...(props.Bleed.value === "true" && {
122
+ bleedX: "asPadding",
123
+ bleedY: "asPadding",
124
+ }),
125
+ };
126
+
127
+ return createLocalSnippetElement("ActionButton", commonProps, children);
128
+ },
129
+ );
@@ -0,0 +1,81 @@
1
+ import { defineComponentHandler } from "@/codegen/core";
2
+ import type {
3
+ AlertDialogFooterProperties,
4
+ AlertDialogProperties,
5
+ } from "@/codegen/component-properties";
6
+ import * as metadata from "@/entities/data/__generated__/component-sets";
7
+ import { createLocalSnippetHelper, createSeedReactElement } from "../../element-factories";
8
+ import type { ComponentHandlerDeps } from "../deps.interface";
9
+ import { findAllInstances } from "@/utils/figma-node";
10
+ import { match } from "ts-pattern";
11
+
12
+ const { createLocalSnippetElement } = createLocalSnippetHelper("alert-dialog");
13
+ const { createLocalSnippetElement: createLocalSnippetElementTrigger } =
14
+ createLocalSnippetHelper("action-button");
15
+
16
+ const ALERT_DIALOG_FOOTER_KEY = "00b1b131d67edf2875a7a1df8dfa88098d7c04be";
17
+
18
+ export const createAlertDialogHandler = (_ctx: ComponentHandlerDeps) =>
19
+ defineComponentHandler<AlertDialogProperties>(metadata.alertDialog.key, (node, traverse) => {
20
+ const props = node.componentProperties;
21
+ const alertDialogHeader = createLocalSnippetElement("AlertDialogHeader", undefined, [
22
+ ...(props["Show Title#20361:14"].value
23
+ ? [
24
+ createLocalSnippetElement(
25
+ "AlertDialogTitle",
26
+ undefined,
27
+ props["Title Text#20361:0"].value,
28
+ ),
29
+ ]
30
+ : []),
31
+ createLocalSnippetElement(
32
+ "AlertDialogDescription",
33
+ undefined,
34
+ props["Description Text#20361:7"].value,
35
+ ),
36
+ ]);
37
+
38
+ const footerNodes = findAllInstances<AlertDialogFooterProperties>({
39
+ node,
40
+ key: ALERT_DIALOG_FOOTER_KEY,
41
+ });
42
+
43
+ if (footerNodes.length === 0 || footerNodes.length > 1) {
44
+ return createLocalSnippetElement("AlertDialog", undefined, alertDialogHeader, {
45
+ comment: "Footer 영역을 확인해주세요.",
46
+ });
47
+ }
48
+
49
+ const footerNode = footerNodes[0];
50
+ const footerNodeProps = traverse(footerNode)?.props;
51
+
52
+ const buttons = footerNode.children.map(traverse);
53
+
54
+ const alertDialogFooterChildren = match(footerNode.componentProperties.Type.value)
55
+ .with("Single", () => buttons)
56
+ .with("Neutral", "Critical", () =>
57
+ createSeedReactElement("ResponsivePair", footerNodeProps, buttons),
58
+ )
59
+ .with("Neutral (Overflow)", "Critical (Overflow)", "Nonpreferred", () =>
60
+ createSeedReactElement("VStack", footerNodeProps, buttons),
61
+ )
62
+ .exhaustive();
63
+
64
+ const alertDialogFooter = createLocalSnippetElement(
65
+ "AlertDialogFooter",
66
+ undefined,
67
+ alertDialogFooterChildren,
68
+ );
69
+
70
+ return createLocalSnippetElement("AlertDialogRoot", { open: true }, [
71
+ createLocalSnippetElement(
72
+ "AlertDialogTrigger",
73
+ { asChild: true },
74
+ createLocalSnippetElementTrigger("ActionButton", {}, "AlertDialog 열기"),
75
+ ),
76
+ createLocalSnippetElement("AlertDialogContent", undefined, [
77
+ alertDialogHeader,
78
+ alertDialogFooter,
79
+ ]),
80
+ ]);
81
+ });
@@ -1,173 +1,138 @@
1
1
  import type {
2
- AppBarLeftProperties,
3
- AppBarMainProperties,
4
2
  AppBarProperties,
5
- AppBarRightProperties,
3
+ AppBarMainProperties,
4
+ AppBarRightIconButtonProperties,
5
+ AppBarLeftIconButtonProperties,
6
6
  } from "@/codegen/component-properties";
7
7
  import { defineComponentHandler } from "@/codegen/core";
8
8
  import * as metadata from "@/entities/data/__generated__/component-sets";
9
- import type { NormalizedInstanceNode, NormalizedTextNode } from "@/normalizer";
10
- import { findAll, findAllInstances, findOne } from "@/utils/figma-node";
9
+ import { findAllInstances } from "@/utils/figma-node";
11
10
  import { match } from "ts-pattern";
12
11
  import { createLocalSnippetHelper } from "../../element-factories";
13
12
  import type { ComponentHandlerDeps } from "../deps.interface";
14
13
 
15
14
  const { createLocalSnippetElement } = createLocalSnippetHelper("app-bar");
16
15
 
17
- const APP_BAR_MAIN_KEY = "336b49b26c3933485d87cc460b06c390976ea58e";
18
- const createAppBarMainHandler = (_ctx: ComponentHandlerDeps) =>
19
- defineComponentHandler<AppBarMainProperties>(
20
- APP_BAR_MAIN_KEY,
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,
21
23
  ({ componentProperties: props }) => {
22
- const { title, subtitle, layout } = match(props.Type.value)
24
+ const { title, subtitle } = match(props.Type.value)
23
25
  .with("Title", () => ({
24
26
  title: props["Title#16944:0"].value,
25
27
  subtitle: undefined,
26
- layout: undefined,
27
28
  }))
28
29
  .with("Title-Subtitle", () => ({
29
30
  title: props["Title#16944:0"].value,
30
31
  subtitle: props["Subtitle#16958:9"].value,
31
- layout: "withSubtitle",
32
32
  }))
33
- .otherwise(() => ({
33
+ .with("Logo (Figma Only)", () => ({
34
34
  title: undefined,
35
35
  subtitle: undefined,
36
- layout: undefined,
37
- }));
36
+ }))
37
+ .exhaustive();
38
38
 
39
- const commonProps = {
40
- title,
41
- subtitle,
42
- layout,
43
- };
39
+ if (title) {
40
+ return createLocalSnippetElement("AppBarMain", { title, subtitle });
41
+ }
44
42
 
45
- return createLocalSnippetElement("AppBarMain", commonProps, undefined, {
46
- comment: title === undefined ? "Title을 제공해주세요." : undefined,
43
+ return createLocalSnippetElement("AppBarMain", undefined, undefined, {
44
+ comment: "AppBarMain 내부를 직접 작성해주세요.",
47
45
  });
48
46
  },
49
47
  );
48
+ };
50
49
 
51
- const APP_BAR_LEFT_KEY = "e5d2e47052a22395db79f195a0991a570dc1b6c9";
52
- const createAppBarLeftHandler = (ctx: ComponentHandlerDeps) =>
53
- defineComponentHandler<AppBarLeftProperties>(APP_BAR_LEFT_KEY, (node) => {
50
+ export const createAppBarHandler = (ctx: ComponentHandlerDeps) => {
51
+ const appBarMainHandler = createAppBarMainHandler(ctx);
52
+
53
+ return defineComponentHandler<AppBarProperties>(metadata.topNavigation.key, (node, traverse) => {
54
54
  const props = node.componentProperties;
55
55
 
56
- const children = (() => {
57
- switch (props.Action.value) {
58
- case "Back":
59
- return createLocalSnippetElement("AppBarBackButton");
60
- case "Close":
61
- return createLocalSnippetElement("AppBarCloseButton");
62
- case "Other": {
63
- const iconNode = findOne(
64
- node,
65
- (child) => child.type === "INSTANCE" && child.name === "Icon",
66
- ) as NormalizedInstanceNode | null;
67
-
68
- if (!iconNode) {
69
- return undefined;
70
- }
71
-
72
- return createLocalSnippetElement(
73
- "AppBarIconButton",
74
- undefined,
75
- ctx.iconHandler.transform(iconNode),
76
- {
77
- comment: "aria-label 또는 aria-labelledby를 제공해주세요.",
78
- },
79
- );
80
- }
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;
81
71
  }
82
- })();
83
72
 
84
- return createLocalSnippetElement("AppBarLeft", undefined, children);
85
- });
73
+ const [mainNode] = findAllInstances<AppBarMainProperties>({ node, key: APP_BAR_TITLE_KEY });
86
74
 
87
- const APP_BAR_RIGHT_KEY = "9e157fc2d1f89ffee938a5bc62f4a58064fec44e";
88
- const createAppBarRightHandler = (ctx: ComponentHandlerDeps) =>
89
- defineComponentHandler<AppBarRightProperties>(APP_BAR_RIGHT_KEY, (node) => {
90
- const props = node.componentProperties;
91
-
92
- const children = (() => {
93
- switch (props.Type.value) {
94
- case "1 Text": {
95
- const textNode = findOne(
96
- node,
97
- (child) => child.type === "TEXT",
98
- ) as NormalizedTextNode | null;
75
+ if (!mainNode) {
76
+ return undefined;
77
+ }
99
78
 
100
- return textNode?.characters;
101
- }
102
- default: {
103
- const iconNodes = findAll(
104
- node,
105
- (child) => child.type === "INSTANCE" && child.name === "Icon",
106
- ) as NormalizedInstanceNode[];
79
+ return appBarMainHandler.transform(mainNode, traverse);
80
+ })();
107
81
 
108
- return iconNodes.map((iconNode) =>
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) =>
109
94
  createLocalSnippetElement(
110
95
  "AppBarIconButton",
111
96
  undefined,
112
- ctx.iconHandler.transform(iconNode),
113
- {
114
- comment: "aria-label 또는 aria-labelledby를 제공해주세요.",
115
- },
97
+ ctx.iconHandler.transform(button.componentProperties["Icon#33580:0"]),
98
+ { comment: "AppBarIconButton에 aria-label 속성을 추가해주세요." },
116
99
  ),
117
100
  );
118
101
  }
119
- }
120
- })();
121
-
122
- return createLocalSnippetElement("AppBarRight", undefined, children);
123
- });
124
-
125
- export const createAppBarHandler = (ctx: ComponentHandlerDeps) => {
126
- const appBarMainHandler = createAppBarMainHandler(ctx);
127
- const appBarLeftHandler = createAppBarLeftHandler(ctx);
128
- const appBarRightHandler = createAppBarRightHandler(ctx);
129
-
130
- return defineComponentHandler<AppBarProperties>(metadata.standardNavigation.key, (node) => {
131
- const props = node.componentProperties;
132
102
 
133
- const theme = (() => {
134
- switch (props.OS.value) {
135
- case "Android":
136
- return "android";
137
- case "iOS":
138
- return "cupertino";
139
- }
140
- })();
141
-
142
- const tone = (() => {
143
- switch (props.Variant.value) {
144
- case "Layer Default":
145
- return "layer";
146
- case "Transparent":
147
- return "transparent";
148
- }
149
- })();
150
-
151
- const mainNode = findAllInstances<AppBarMainProperties>({
152
- key: appBarMainHandler.key,
153
- node,
154
- });
155
- const onlyMainNode = mainNode.length === 1 ? mainNode[0] : undefined;
156
- const main = onlyMainNode ? appBarMainHandler.transform(onlyMainNode) : undefined;
157
-
158
- const leftNode = findAllInstances<AppBarLeftProperties>({
159
- key: appBarLeftHandler.key,
160
- node,
161
- });
162
- const onlyLeftNode = leftNode.length === 1 ? leftNode[0] : undefined;
163
- const left = onlyLeftNode ? appBarLeftHandler.transform(onlyLeftNode) : undefined;
164
-
165
- const rightNode = findAllInstances<AppBarRightProperties>({
166
- key: appBarRightHandler.key,
167
- node,
168
- });
169
- const onlyRightNode = rightNode.length === 1 ? rightNode[0] : undefined;
170
- const right = onlyRightNode ? appBarRightHandler.transform(onlyRightNode) : undefined;
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;
171
136
 
172
137
  return createLocalSnippetElement(
173
138
  "AppBar",