@seed-design/figma 0.0.2 → 0.0.4

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 (53) hide show
  1. package/lib/index.cjs +556 -471
  2. package/lib/index.d.ts +29 -4
  3. package/lib/index.js +556 -471
  4. package/package.json +2 -2
  5. package/src/component/handlers/action-button.ts +66 -0
  6. package/src/component/handlers/action-chip.ts +71 -0
  7. package/src/component/handlers/action-sheet.ts +74 -0
  8. package/src/component/handlers/app-bar.ts +183 -0
  9. package/src/component/handlers/avatar-stack.ts +35 -0
  10. package/src/component/handlers/avatar.ts +37 -0
  11. package/src/component/handlers/badge.ts +20 -0
  12. package/src/component/handlers/callout.ts +87 -0
  13. package/src/component/handlers/checkbox.ts +32 -0
  14. package/src/component/handlers/chip-tabs.ts +51 -0
  15. package/src/component/handlers/control-chip.ts +75 -0
  16. package/src/component/handlers/error-state.ts +37 -0
  17. package/src/component/handlers/extended-action-sheet.ts +86 -0
  18. package/src/component/handlers/extended-fab.ts +24 -0
  19. package/src/component/handlers/fab.ts +17 -0
  20. package/src/component/handlers/help-bubble.ts +66 -0
  21. package/src/component/handlers/identity-placeholder.ts +16 -0
  22. package/src/component/handlers/inline-banner.ts +83 -0
  23. package/src/component/handlers/manner-temp-badge.ts +17 -0
  24. package/src/component/handlers/multiline-text-field.ts +80 -0
  25. package/src/component/handlers/progress-circle.ts +49 -0
  26. package/src/component/handlers/reaction-button.ts +36 -0
  27. package/src/component/handlers/segmented-control.ts +51 -0
  28. package/src/component/handlers/select-box.ts +76 -0
  29. package/src/component/handlers/skeleton.ts +51 -0
  30. package/src/component/handlers/snackbar.ts +21 -0
  31. package/src/component/handlers/switch.ts +29 -0
  32. package/src/component/handlers/tabs.ts +107 -0
  33. package/src/component/handlers/text-button.ts +57 -0
  34. package/src/component/handlers/text-field.ts +108 -0
  35. package/src/component/handlers/toggle-button.ts +44 -0
  36. package/src/component/index.ts +32 -1644
  37. package/src/component/type-helper.ts +11 -0
  38. package/src/generate-code.ts +183 -145
  39. package/src/icon.ts +2 -2
  40. package/src/index.ts +1 -2
  41. package/src/jsx.ts +1 -1
  42. package/src/layout.ts +23 -281
  43. package/src/normalizer/from-plugin.ts +24 -4
  44. package/src/normalizer/from-rest.ts +22 -4
  45. package/src/normalizer/index.ts +3 -0
  46. package/src/normalizer/types.ts +3 -1
  47. package/src/{color.ts → props/color.ts} +1 -1
  48. package/src/props/layout.ts +292 -0
  49. package/src/{sizing.ts → props/sizing.ts} +17 -17
  50. package/src/{text.ts → props/text.ts} +2 -1
  51. package/src/{variable.ts → props/variable.ts} +1 -1
  52. package/src/{util.ts → utils/common.ts} +0 -2
  53. package/src/{node-util.ts → utils/figma-node.ts} +1 -1
@@ -0,0 +1,76 @@
1
+ import * as metadata from "../../data/__generated__/component-sets";
2
+ import { createElement } from "../../jsx";
3
+ import { findAllInstances } from "../../utils/figma-node";
4
+ import type { ComponentHandler } from "../type-helper";
5
+ import type { SelectBoxGroupProperties, SelectBoxProperties } from "../type";
6
+
7
+ export const selectBoxGroupHandler: ComponentHandler<SelectBoxGroupProperties> = {
8
+ key: metadata.templateSelectBoxGroup.key,
9
+ codegen: async (node) => {
10
+ const props = node.componentProperties;
11
+
12
+ const tag = (() => {
13
+ switch (props.Control.value) {
14
+ case "Checkbox":
15
+ return "CheckSelectBoxGroup";
16
+ case "Radio":
17
+ return "RadioSelectBoxGroup";
18
+ }
19
+ })();
20
+
21
+ const selectBoxes = findAllInstances<SelectBoxProperties>({
22
+ node,
23
+ key: selectBoxHandler.key,
24
+ });
25
+
26
+ const selectedSelectBox = selectBoxes.find((selectBox) =>
27
+ selectBox.componentProperties.State.value.split("-").includes("Selected"),
28
+ );
29
+
30
+ const stack = createElement(
31
+ "Stack",
32
+ { gap: "spacingY.componentDefault" },
33
+ await Promise.all(selectBoxes.map(selectBoxHandler.codegen)),
34
+ );
35
+
36
+ const commonProps = {
37
+ ...(tag === "RadioSelectBoxGroup" && {
38
+ defaultValue: selectedSelectBox?.componentProperties["Label#3635:0"].value,
39
+ }),
40
+ };
41
+
42
+ return createElement(tag, commonProps, stack);
43
+ },
44
+ };
45
+
46
+ export const selectBoxHandler: ComponentHandler<SelectBoxProperties> = {
47
+ key: metadata.selectBox.key,
48
+ codegen: async ({ componentProperties: props }) => {
49
+ const tag = (() => {
50
+ switch (props.Control.value) {
51
+ case "Checkbox":
52
+ return "CheckSelectBox";
53
+ case "Radio":
54
+ return "RadioSelectBox";
55
+ }
56
+ })();
57
+
58
+ const states = props.State.value.split("-");
59
+
60
+ const commonProps = {
61
+ label: props["Label#3635:0"].value,
62
+ ...(props["Show Description#3033:0"].value && {
63
+ description: props["Description #3033:5"].value,
64
+ }),
65
+ ...(tag === "RadioSelectBox" && {
66
+ value: props["Label#3635:0"].value,
67
+ }),
68
+ ...(tag === "CheckSelectBox" &&
69
+ states.includes("Selected") && {
70
+ defaultChecked: true,
71
+ }),
72
+ };
73
+
74
+ return createElement(tag, commonProps);
75
+ },
76
+ };
@@ -0,0 +1,51 @@
1
+ import { camelCase } from "change-case";
2
+ import * as metadata from "../../data/__generated__/component-sets";
3
+ import { createElement } from "../../jsx";
4
+ import { getLayoutVariableName } from "../../props/variable";
5
+ import type { SkeletonProperties } from "../type";
6
+ import type { ComponentHandler } from "../type-helper";
7
+
8
+ export const skeletonHandler: ComponentHandler<SkeletonProperties> = {
9
+ key: metadata.skeleton.key,
10
+ codegen: async ({
11
+ componentProperties: props,
12
+ absoluteBoundingBox,
13
+ layoutSizingHorizontal,
14
+ layoutSizingVertical,
15
+ boundVariables,
16
+ }) => {
17
+ const commonProps = {
18
+ radius: camelCase(props.Radius.value),
19
+ width: (() => {
20
+ switch (layoutSizingHorizontal) {
21
+ case "FIXED": {
22
+ const variableId = boundVariables?.size?.x?.id;
23
+ if (variableId) return getLayoutVariableName(variableId);
24
+
25
+ return `${absoluteBoundingBox?.width}px`;
26
+ }
27
+ case "FILL":
28
+ return "full";
29
+ default:
30
+ return "full";
31
+ }
32
+ })(),
33
+ height: (() => {
34
+ switch (layoutSizingVertical) {
35
+ case "FIXED": {
36
+ const variableId = boundVariables?.size?.y?.id;
37
+ if (variableId) return getLayoutVariableName(variableId);
38
+
39
+ return `${absoluteBoundingBox?.height}px`;
40
+ }
41
+ case "FILL":
42
+ return "full";
43
+ default:
44
+ return "full";
45
+ }
46
+ })(),
47
+ };
48
+
49
+ return createElement("Skeleton", commonProps);
50
+ },
51
+ };
@@ -0,0 +1,21 @@
1
+ import { camelCase } from "change-case";
2
+ import * as metadata from "../../data/__generated__/component-sets";
3
+ import { createElement } from "../../jsx";
4
+ import type { SnackbarProperties } from "../type";
5
+ import type { ComponentHandler } from "../type-helper";
6
+
7
+ export const snackbarHandler: ComponentHandler<SnackbarProperties> = {
8
+ key: metadata.snackbar.key,
9
+ codegen: async ({ componentProperties: props }) => {
10
+ const commonProps = {
11
+ message: props["Message#1528:4"].value,
12
+ variant: camelCase(props.Variant.value),
13
+ ...(props["Show Action Button#1528:0"].value && {
14
+ actionLabel: props["Action Button Label#1528:8"].value,
15
+ }),
16
+ };
17
+
18
+ // TODO: adapter.create({ render })
19
+ return createElement("Snackbar", commonProps);
20
+ },
21
+ };
@@ -0,0 +1,29 @@
1
+ import * as metadata from "../../data/__generated__/component-sets";
2
+ import { createElement } from "../../jsx";
3
+ import { handleSize } from "../properties";
4
+ import type { SwitchProperties } from "../type";
5
+ import type { ComponentHandler } from "../type-helper";
6
+
7
+ export const switchHandler: ComponentHandler<SwitchProperties> = {
8
+ key: metadata.switch.key,
9
+ codegen: async ({ componentProperties: props }) => {
10
+ const states = props.State.value.split("-");
11
+
12
+ const size = handleSize(props.Size.value);
13
+
14
+ const commonProps = {
15
+ size,
16
+ ...(size === "small" && {
17
+ label: props["Label#15191:2"].value,
18
+ }),
19
+ ...(states.includes("Selected") && {
20
+ defaultChecked: true,
21
+ }),
22
+ ...(states.includes("Disabled") && {
23
+ disabled: true,
24
+ }),
25
+ };
26
+
27
+ return createElement("Switch", commonProps);
28
+ },
29
+ };
@@ -0,0 +1,107 @@
1
+ import { camelCase } from "change-case";
2
+ import * as metadata from "../../data/__generated__/component-sets";
3
+ import { createElement } from "../../jsx";
4
+ import type { NormalizedInstanceNode } from "../../normalizer";
5
+ import { handleSize } from "../properties";
6
+ import type { TabsFillItemProperties, TabsHugItemProperties, TabsProperties } from "../type";
7
+ import type { ComponentHandler } from "../type-helper";
8
+
9
+ export const tabsHandler: ComponentHandler<TabsProperties> = {
10
+ key: metadata.tablist.key,
11
+ codegen: async ({ componentProperties: props, children }) => {
12
+ const tabsItems = children
13
+ .map((child) => {
14
+ if (child.type !== "INSTANCE") return null;
15
+
16
+ const componentKey = child.componentSetKey ? child.componentSetKey : child.componentKey;
17
+
18
+ if (componentKey === tabsHugItemHandler.key)
19
+ return {
20
+ triggerLayout: "hug" as const,
21
+ node: child as NormalizedInstanceNode & { componentProperties: TabsHugItemProperties },
22
+ };
23
+
24
+ if (componentKey === tabsFillItemHandler.key)
25
+ return {
26
+ triggerLayout: "fill" as const,
27
+ node: child as NormalizedInstanceNode & { componentProperties: TabsFillItemProperties },
28
+ };
29
+
30
+ return null;
31
+ })
32
+ .filter((tabsItem) => tabsItem !== null);
33
+
34
+ const selectedTabsItem = tabsItems.find(({ node: { componentProperties } }) =>
35
+ componentProperties.State.value.split("-").includes("Selected"),
36
+ )?.node;
37
+
38
+ const tabTriggerList = createElement(
39
+ "TabsList",
40
+ undefined,
41
+ await Promise.all(
42
+ tabsItems.map(({ triggerLayout, node }) => {
43
+ switch (triggerLayout) {
44
+ case "hug":
45
+ return tabsHugItemHandler.codegen(node);
46
+ case "fill":
47
+ return tabsFillItemHandler.codegen(node);
48
+ }
49
+ }),
50
+ ),
51
+ );
52
+
53
+ const tabContents = tabsItems.map(({ node }) => {
54
+ const value = node.componentProperties["Label#4478:2"].value;
55
+
56
+ return createElement("TabsContent", { value }, "{/* TODO: 컨텐츠 추가 */}");
57
+ });
58
+
59
+ const commonProps = {
60
+ triggerLayout: camelCase(props.Layout.value),
61
+ size: handleSize(props.Size.value),
62
+ ...(selectedTabsItem && {
63
+ defaultValue: selectedTabsItem.componentProperties["Label#4478:2"].value,
64
+ }),
65
+ };
66
+
67
+ return createElement("TabsRoot", commonProps, [tabTriggerList, ...tabContents]);
68
+ },
69
+ };
70
+
71
+ const tabsHugItemHandler: ComponentHandler<TabsHugItemProperties> = {
72
+ key: "c242492543b327ceb84fa9933841512fc62a898c",
73
+ codegen: async ({ componentProperties: props }) => {
74
+ const states = props.State.value.split("-");
75
+
76
+ const commonProps = {
77
+ value: props["Label#4478:2"].value,
78
+ ...(props.Notification.value === "True" && {
79
+ alert: true,
80
+ }),
81
+ ...(states.includes("Disabled") && {
82
+ disabled: true,
83
+ }),
84
+ };
85
+
86
+ return createElement("TabsTrigger", commonProps, props["Label#4478:2"].value);
87
+ },
88
+ };
89
+
90
+ const tabsFillItemHandler: ComponentHandler<TabsFillItemProperties> = {
91
+ key: "7275293344efb40ee9a3f5248ba2659b94a0b305",
92
+ codegen: async ({ componentProperties: props }) => {
93
+ const states = props.State.value.split("-");
94
+
95
+ const commonProps = {
96
+ value: props["Label#4478:2"].value,
97
+ ...(props.Notification.value === "True" && {
98
+ alert: true,
99
+ }),
100
+ ...(states.includes("Disabled") && {
101
+ disabled: true,
102
+ }),
103
+ };
104
+
105
+ return createElement("TabsTrigger", commonProps, props["Label#4478:2"].value);
106
+ },
107
+ };
@@ -0,0 +1,57 @@
1
+ import { camelCase } from "change-case";
2
+ import { match } from "ts-pattern";
3
+ import * as metadata from "../../data/__generated__/component-sets";
4
+ import { createIconTagNameFromKey } from "../../icon";
5
+ import { createElement } from "../../jsx";
6
+ import { findOne } from "../../utils/figma-node";
7
+ import type { NormalizedInstanceNode } from "../../normalizer";
8
+ import { handleSize } from "../properties";
9
+ import type { TextButtonProperties } from "../type";
10
+ import type { ComponentHandler } from "../type-helper";
11
+
12
+ export const textButtonHandler: ComponentHandler<TextButtonProperties> = {
13
+ key: metadata.textButton.key,
14
+ codegen: async (node) => {
15
+ const { componentProperties: props } = node;
16
+
17
+ const states = props.State.value.split("-");
18
+
19
+ const { prefixIcon, suffixIcon, children } = await match(props.Layout.value)
20
+ .with("Icon First", async () => ({
21
+ prefixIcon: createElement(
22
+ createIconTagNameFromKey(props["Prefix Icon#7561:0"].componentKey),
23
+ ),
24
+ suffixIcon: undefined,
25
+ children: props["Label#6148:0"].value,
26
+ }))
27
+ .with("Icon Last", () => {
28
+ const suffixIconNode = findOne(
29
+ node,
30
+ (node) => node.type === "INSTANCE" && node.name === "Suffix Icon",
31
+ ) as NormalizedInstanceNode | null;
32
+
33
+ const suffixIconComponentKey = suffixIconNode?.componentKey;
34
+
35
+ return {
36
+ prefixIcon: undefined,
37
+ suffixIcon: suffixIconComponentKey
38
+ ? createElement(createIconTagNameFromKey(suffixIconComponentKey))
39
+ : undefined,
40
+ children: props["Label#6148:0"].value,
41
+ };
42
+ })
43
+ .exhaustive();
44
+
45
+ const commonProps = {
46
+ tone: camelCase(props.Tone.value),
47
+ size: handleSize(props.Size.value),
48
+ prefixIcon,
49
+ suffixIcon,
50
+ ...(states.includes("Disabled") && {
51
+ disabled: true,
52
+ }),
53
+ };
54
+
55
+ return createElement("TextButton", commonProps, children);
56
+ },
57
+ };
@@ -0,0 +1,108 @@
1
+ import * as metadata from "../../data/__generated__/component-sets";
2
+ import { createIconTagNameFromKey } from "../../icon";
3
+ import { createElement } from "../../jsx";
4
+ import { handleSize } from "../properties";
5
+ import type { TextFieldProperties } from "../type";
6
+ import type { ComponentHandler } from "../type-helper";
7
+
8
+ export const textFieldHandler: ComponentHandler<TextFieldProperties> = {
9
+ key: metadata.textField.key,
10
+ codegen: async ({ componentProperties: props }) => {
11
+ const {
12
+ Size: { value: size },
13
+ State: { value: state },
14
+ Filled: { value: filled },
15
+ "Show Header#870:0": { value: showHeader },
16
+ "Label#14964:0": { value: label },
17
+ "Show Indicator#1259:0": { value: showIndicator },
18
+ "Indicator#15327:249": { value: indicator },
19
+ "Show Prefix#958:125": { value: showPrefix },
20
+ "Show Prefix Icon#1267:50": { value: showPrefixIcon },
21
+ "Prefix Icon#1267:25": { value: prefixIcon },
22
+ "Show Prefix Text#1267:0": { value: showPrefixText },
23
+ "Prefix Text#15327:101": { value: prefix },
24
+ "Placeholder#958:0": { value: placeholder },
25
+ "Filled Text#1304:0": { value: defaultValue },
26
+ "Show Suffix#958:100": { value: showSuffix },
27
+ "Show Suffix Icon#1267:75": { value: showSuffixIcon },
28
+ "Suffix Icon #1267:100": { value: suffixIcon },
29
+ "Show Suffix Text#1267:125": { value: showSuffixText },
30
+ "Suffix Text#15327:138": { value: suffix },
31
+ "Show Footer#958:25": { value: showFooter },
32
+ "Show Description#958:50": { value: showDescription },
33
+ "Description#12626:5": { value: description },
34
+ "Show Character Count#958:75": { value: showCharacterCount },
35
+ "Character Count#15327:64": { value: _characterCount },
36
+ "Max Character Count#15327:27": { value: maxCharacterCount },
37
+ } = props;
38
+
39
+ const states = state.split("-");
40
+
41
+ const commonProps = {
42
+ size: handleSize(size),
43
+ // header
44
+ ...(showHeader && {
45
+ label,
46
+ }),
47
+ ...(showHeader &&
48
+ showIndicator && {
49
+ indicator,
50
+ }),
51
+ // input affixes
52
+ ...(showPrefix &&
53
+ showPrefixIcon && {
54
+ prefixIcon: createElement(createIconTagNameFromKey(prefixIcon)),
55
+ }),
56
+ ...(showPrefix &&
57
+ showPrefixText && {
58
+ prefix,
59
+ }),
60
+ ...(showSuffix &&
61
+ showSuffixIcon && {
62
+ suffixIcon: createElement(createIconTagNameFromKey(suffixIcon)),
63
+ }),
64
+ ...(showSuffix &&
65
+ showSuffixText && {
66
+ suffix,
67
+ }),
68
+ // input
69
+ ...(filled === "True" && {
70
+ defaultValue,
71
+ }),
72
+ ...(states.includes("Invalid") && {
73
+ invalid: true,
74
+ }),
75
+ ...(states.includes("Disabled") && {
76
+ disabled: true,
77
+ }),
78
+ ...(states.includes("Read Only") && {
79
+ readOnly: true,
80
+ }),
81
+ // footer
82
+ ...(showFooter &&
83
+ showDescription &&
84
+ states.includes("Invalid") && {
85
+ // invalid인 경우 description을 error로 사용
86
+ errorMessage: description,
87
+ }),
88
+ ...(showFooter &&
89
+ showDescription &&
90
+ !states.includes("Invalid") && {
91
+ // invalid가 아닌 경우 description을 description으로 사용
92
+ description,
93
+ }),
94
+ ...(showFooter &&
95
+ showCharacterCount && {
96
+ maxGraphemeCount: Number(maxCharacterCount),
97
+ }),
98
+ };
99
+
100
+ const inputProps = {
101
+ placeholder,
102
+ };
103
+
104
+ const TextFieldChildren = createElement("TextFieldInput", inputProps);
105
+
106
+ return createElement("TextField", commonProps, TextFieldChildren);
107
+ },
108
+ };
@@ -0,0 +1,44 @@
1
+ import { camelCase } from "change-case";
2
+ import * as metadata from "../../data/__generated__/component-sets";
3
+ import { createIconTagNameFromKey } from "../../icon";
4
+ import { createElement } from "../../jsx";
5
+ import { handleSize } from "../properties";
6
+ import type { ToggleButtonProperties } from "../type";
7
+ import type { ComponentHandler } from "../type-helper";
8
+
9
+ export const toggleButtonHandler: ComponentHandler<ToggleButtonProperties> = {
10
+ key: metadata.toggleButton.key,
11
+ codegen: async ({ componentProperties: props }) => {
12
+ const states = props.State.value.split("-");
13
+
14
+ const commonProps = {
15
+ variant: camelCase(props.Variant.value),
16
+ size: handleSize(props.Size.value),
17
+ ...(states.includes("Selected") && {
18
+ defaultPressed: true,
19
+ }),
20
+ ...(states.includes("Disabled") && {
21
+ disabled: true,
22
+ }),
23
+ ...(states.includes("Loading") && {
24
+ loading: true,
25
+ }),
26
+ };
27
+
28
+ return createElement("ToggleButton", commonProps, [
29
+ props["Show Prefix Icon#6122:392"].value
30
+ ? createElement("PrefixIcon", {
31
+ svg: createElement(createIconTagNameFromKey(props["Prefix Icon#6122:98"].componentKey)),
32
+ })
33
+ : undefined,
34
+ props["Label#6122:49"].value,
35
+ props["Show Suffix Icon#6122:147"].value
36
+ ? createElement("SuffixIcon", {
37
+ svg: createElement(
38
+ createIconTagNameFromKey(props["Suffix Icon#6122:343"].componentKey),
39
+ ),
40
+ })
41
+ : undefined,
42
+ ]);
43
+ },
44
+ };