@uniai-fe/uds-primitives 0.1.13 → 0.2.1

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 (114) hide show
  1. package/README.md +2 -2
  2. package/dist/styles.css +1112 -385
  3. package/package.json +12 -15
  4. package/src/components/button/index.scss +1 -0
  5. package/src/components/button/markup/{ButtonRounded.tsx → Rounded.tsx} +1 -1
  6. package/src/components/button/markup/{ButtonText.tsx → Text.tsx} +1 -1
  7. package/src/components/button/markup/index.ts +3 -3
  8. package/src/components/button/styles/button.scss +113 -229
  9. package/src/components/button/styles/round-button.scss +11 -14
  10. package/src/components/button/styles/text-button.scss +23 -23
  11. package/src/components/button/styles/variables.scss +145 -0
  12. package/src/components/dropdown/index.tsx +3 -3
  13. package/src/components/dropdown/markup/Template.tsx +57 -0
  14. package/src/components/dropdown/markup/foundation/Container.tsx +125 -0
  15. package/src/components/dropdown/markup/foundation/MenuItem.tsx +107 -0
  16. package/src/components/dropdown/markup/foundation/MenuList.tsx +27 -0
  17. package/src/components/dropdown/markup/foundation/Provider.tsx +46 -0
  18. package/src/components/dropdown/markup/foundation/Root.tsx +30 -0
  19. package/src/components/dropdown/markup/foundation/Trigger.tsx +34 -0
  20. package/src/components/dropdown/markup/foundation/index.tsx +25 -0
  21. package/src/components/dropdown/markup/index.tsx +8 -2
  22. package/src/components/dropdown/styles/dropdown.scss +166 -0
  23. package/src/components/dropdown/styles/index.scss +2 -0
  24. package/src/components/dropdown/styles/variables.scss +40 -0
  25. package/src/components/dropdown/types/base.ts +31 -0
  26. package/src/components/dropdown/types/index.ts +2 -4
  27. package/src/components/dropdown/types/props.ts +170 -0
  28. package/src/components/dropdown/utils/index.ts +1 -4
  29. package/src/components/dropdown/utils/refs.ts +20 -0
  30. package/src/components/form/index.scss +1 -0
  31. package/src/components/form/index.tsx +18 -2
  32. package/src/components/form/markup/form-field/Body.tsx +18 -0
  33. package/src/components/form/markup/form-field/Container.tsx +58 -0
  34. package/src/components/form/markup/form-field/Footer.tsx +21 -0
  35. package/src/components/form/markup/form-field/Header.tsx +39 -0
  36. package/src/components/form/markup/form-field/Template.tsx +56 -0
  37. package/src/components/form/markup/form-field/index.tsx +22 -0
  38. package/src/components/form/styles/form-field/layout.scss +67 -0
  39. package/src/components/form/styles/form-field/variables.scss +17 -0
  40. package/src/components/form/styles/index.scss +2 -0
  41. package/src/components/form/types/index.ts +1 -0
  42. package/src/components/form/types/props.ts +125 -0
  43. package/src/components/form/utils/form-field.ts +42 -0
  44. package/src/components/input/hooks/index.ts +1 -4
  45. package/src/components/input/hooks/useDigitField.ts +63 -0
  46. package/src/components/input/img/calendar/calendar.svg +7 -0
  47. package/src/components/input/img/calendar/chevron-down.svg +3 -0
  48. package/src/components/input/img/calendar/chevron-left.svg +3 -0
  49. package/src/components/input/img/calendar/chevron-right.svg +3 -0
  50. package/src/components/input/img/calendar/chevron-up.svg +3 -0
  51. package/src/components/input/index.tsx +2 -1
  52. package/src/components/input/markup/calendar/Base.tsx +329 -0
  53. package/src/components/input/markup/calendar/index.tsx +8 -0
  54. package/src/components/input/markup/{text/InputUtilityButton.tsx → foundation/Button.tsx} +5 -15
  55. package/src/components/input/markup/foundation/Input.tsx +245 -0
  56. package/src/components/input/markup/foundation/SideSlot.tsx +30 -0
  57. package/src/components/input/markup/foundation/StatusIcon.tsx +21 -0
  58. package/src/components/input/markup/foundation/Utility.tsx +103 -0
  59. package/src/components/input/markup/foundation/index.tsx +15 -0
  60. package/src/components/input/markup/index.tsx +11 -1
  61. package/src/components/input/markup/text/AuthCode.tsx +41 -59
  62. package/src/components/input/markup/text/Email.tsx +25 -115
  63. package/src/components/input/markup/text/Password.tsx +30 -39
  64. package/src/components/input/markup/text/Phone.tsx +35 -122
  65. package/src/components/input/markup/text/Search.tsx +17 -18
  66. package/src/components/input/markup/text/index.ts +15 -12
  67. package/src/components/input/styles/calendar.scss +110 -0
  68. package/src/components/input/styles/foundation.scss +345 -0
  69. package/src/components/input/styles/index.scss +4 -476
  70. package/src/components/input/styles/text.scss +89 -0
  71. package/src/components/input/styles/variables.scss +41 -0
  72. package/src/components/input/types/calendar.ts +208 -0
  73. package/src/components/input/types/foundation.ts +194 -0
  74. package/src/components/input/types/hooks.ts +43 -0
  75. package/src/components/input/types/index.ts +5 -87
  76. package/src/components/input/types/text.ts +203 -0
  77. package/src/components/input/types/verification.ts +23 -0
  78. package/src/components/input/utils/index.tsx +1 -0
  79. package/src/components/input/utils/verification.tsx +35 -0
  80. package/src/components/select/hooks/index.ts +43 -2
  81. package/src/components/select/img/chevron/primary/large.svg +3 -0
  82. package/src/components/select/img/chevron/primary/medium.svg +3 -0
  83. package/src/components/select/img/chevron/primary/small.svg +3 -0
  84. package/src/components/select/img/chevron/secondary/large.svg +3 -0
  85. package/src/components/select/img/chevron/secondary/medium.svg +3 -0
  86. package/src/components/select/img/chevron/secondary/small.svg +3 -0
  87. package/src/components/select/img/remove.svg +3 -0
  88. package/src/components/select/index.scss +2 -1
  89. package/src/components/select/index.tsx +5 -0
  90. package/src/components/select/markup/Default.tsx +154 -0
  91. package/src/components/select/markup/foundation/Base.tsx +90 -0
  92. package/src/components/select/markup/foundation/Container.tsx +30 -0
  93. package/src/components/select/markup/foundation/Icon.tsx +78 -0
  94. package/src/components/select/markup/foundation/Selected.tsx +34 -0
  95. package/src/components/select/markup/foundation/index.ts +2 -0
  96. package/src/components/select/markup/index.tsx +36 -2
  97. package/src/components/select/markup/multiple/Multiple.tsx +205 -0
  98. package/src/components/select/markup/multiple/SelectedChip.tsx +58 -0
  99. package/src/components/select/markup/multiple/index.ts +2 -0
  100. package/src/components/select/styles/select.scss +316 -0
  101. package/src/components/select/styles/variables.scss +91 -0
  102. package/src/components/select/types/base.ts +34 -0
  103. package/src/components/select/types/icon.ts +45 -0
  104. package/src/components/select/types/index.ts +6 -4
  105. package/src/components/select/types/multiple.ts +57 -0
  106. package/src/components/select/types/option.ts +43 -0
  107. package/src/components/select/types/props.ts +209 -0
  108. package/src/components/select/types/trigger.ts +196 -0
  109. package/src/index.scss +3 -2
  110. package/src/components/input/markup/text/Base.tsx +0 -454
  111. package/src/components/input/utils/index.ts +0 -60
  112. package/src/components/select/styles/index.scss +0 -0
  113. /package/src/components/button/markup/{ButtonDefault.tsx → Base.tsx} +0 -0
  114. /package/src/components/form/{Provider.tsx → markup/Provider.tsx} +0 -0
@@ -0,0 +1,46 @@
1
+ "use client";
2
+
3
+ import {
4
+ createContext,
5
+ useContext,
6
+ useMemo,
7
+ useRef,
8
+ type ReactNode,
9
+ } from "react";
10
+
11
+ import type { DropdownContextValue } from "../../types";
12
+
13
+ /**
14
+ * Dropdown 전용 context; trigger ref를 공유한다.
15
+ */
16
+ const DropdownContext = createContext<DropdownContextValue | null>(null);
17
+
18
+ /**
19
+ * Dropdown context provider
20
+ * @component
21
+ * @param {ReactNode} props.children dropdown 하위 노드
22
+ */
23
+ const DropdownProvider = ({ children }: { children: ReactNode }) => {
24
+ const triggerRef = useRef<HTMLElement | null>(null);
25
+ const value = useMemo<DropdownContextValue>(() => ({ triggerRef }), []);
26
+
27
+ return (
28
+ <DropdownContext.Provider value={value}>
29
+ {children}
30
+ </DropdownContext.Provider>
31
+ );
32
+ };
33
+
34
+ /**
35
+ * Dropdown context hook; scope 내에서만 호출해야 한다.
36
+ * @param {string} componentName 오류 메시지용 컴포넌트 명
37
+ */
38
+ const useDropdownContext = (componentName: string) => {
39
+ const context = useContext(DropdownContext);
40
+ if (!context) {
41
+ throw new Error(`${componentName} must be used within Dropdown.Container`);
42
+ }
43
+ return context;
44
+ };
45
+
46
+ export { DropdownContext, DropdownProvider, useDropdownContext };
@@ -0,0 +1,30 @@
1
+ "use client";
2
+
3
+ import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
4
+ import type { ReactNode } from "react";
5
+
6
+ import type { DropdownRootProps } from "../../types/base";
7
+ import { DropdownProvider } from "./Provider";
8
+
9
+ /**
10
+ * Dropdown root; Provider와 Radix Root를 래핑한다.
11
+ * @component
12
+ * @param {DropdownRootProps} props Dropdown Root props
13
+ * @param {ReactNode} props.children Dropdown 하위 node
14
+ * @param {boolean} [props.modal=false] Radix modal 모드
15
+ */
16
+ const DropdownRoot = ({
17
+ children,
18
+ modal = false,
19
+ ...rootProps
20
+ }: DropdownRootProps & { children: ReactNode }) => {
21
+ return (
22
+ <DropdownProvider>
23
+ <DropdownMenu.Root modal={modal} {...rootProps}>
24
+ {children}
25
+ </DropdownMenu.Root>
26
+ </DropdownProvider>
27
+ );
28
+ };
29
+
30
+ export default DropdownRoot;
@@ -0,0 +1,34 @@
1
+ "use client";
2
+
3
+ import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
4
+ import { forwardRef } from "react";
5
+
6
+ import type { DropdownTriggerProps } from "../../types/props";
7
+ import { mergeRefs } from "../../utils";
8
+ import { useDropdownContext } from "./Provider";
9
+
10
+ /**
11
+ * Dropdown trigger; trigger ref를 context에 공유한다.
12
+ * @component
13
+ * @param {DropdownTriggerProps} props Dropdown trigger props
14
+ * @param {boolean} [props.asChild=true] asChild 패턴 유지 여부
15
+ */
16
+ const DropdownTrigger = forwardRef<HTMLElement, DropdownTriggerProps>(
17
+ ({ asChild = true, children, ...rest }, ref) => {
18
+ const { triggerRef } = useDropdownContext("Dropdown.Trigger");
19
+
20
+ return (
21
+ <DropdownMenu.Trigger
22
+ {...rest}
23
+ asChild={asChild}
24
+ ref={mergeRefs(ref, triggerRef)}
25
+ >
26
+ {children}
27
+ </DropdownMenu.Trigger>
28
+ );
29
+ },
30
+ );
31
+
32
+ DropdownTrigger.displayName = "DropdownTrigger";
33
+
34
+ export default DropdownTrigger;
@@ -0,0 +1,25 @@
1
+ import DropdownContainer from "./Container";
2
+ import DropdownMenuItem from "./MenuItem";
3
+ import DropdownMenuList from "./MenuList";
4
+ import DropdownRoot from "./Root";
5
+ import { DropdownProvider } from "./Provider";
6
+ import DropdownTrigger from "./Trigger";
7
+
8
+ /**
9
+ * Dropdown; 기초 컴포넌트
10
+ * - Provider
11
+ * - Root
12
+ * - Container
13
+ * - Menu
14
+ * - Item
15
+ * - List
16
+ * - Panel
17
+ * - Trigger
18
+ */
19
+ export const DropdownFoundation = {
20
+ Provider: DropdownProvider,
21
+ Root: DropdownRoot,
22
+ Container: DropdownContainer,
23
+ Menu: { Item: DropdownMenuItem, List: DropdownMenuList },
24
+ Trigger: DropdownTrigger,
25
+ };
@@ -1,4 +1,10 @@
1
+ import { DropdownFoundation } from "./foundation";
2
+ import DropdownTemplate from "./Template";
3
+
1
4
  /**
2
- * TODO(dropdown): SOT 및 사용자 제약에 따라 컴포넌트를 구현한다.
5
+ * Dropdown namespace export
3
6
  */
4
- export {};
7
+ export const Dropdown = {
8
+ ...DropdownFoundation,
9
+ Template: DropdownTemplate,
10
+ };
@@ -0,0 +1,166 @@
1
+ .dropdown-panel {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: var(--dropdown-panel-gap);
5
+ padding: var(--dropdown-panel-padding);
6
+ border-radius: var(--dropdown-panel-radius);
7
+ border: 1px solid var(--dropdown-panel-border-color);
8
+ background-color: var(--dropdown-panel-background);
9
+ box-shadow: var(--dropdown-panel-shadow);
10
+ max-height: var(--dropdown-panel-max-height);
11
+ overflow-y: auto;
12
+ }
13
+
14
+ .dropdown-panel-small {
15
+ --dropdown-option-height-current: var(--dropdown-option-height-small);
16
+ --dropdown-text-size-current: var(--dropdown-text-small-size);
17
+ --dropdown-text-line-height-current: var(--dropdown-text-small-line-height);
18
+ --dropdown-text-letter-spacing-current: var(
19
+ --dropdown-text-small-letter-spacing
20
+ );
21
+ }
22
+
23
+ .dropdown-panel-medium {
24
+ --dropdown-option-height-current: var(--dropdown-option-height-medium);
25
+ --dropdown-text-size-current: var(--dropdown-text-medium-size);
26
+ --dropdown-text-line-height-current: var(--dropdown-text-medium-line-height);
27
+ --dropdown-text-letter-spacing-current: var(
28
+ --dropdown-text-medium-letter-spacing
29
+ );
30
+ }
31
+
32
+ .dropdown-panel-large {
33
+ --dropdown-option-height-current: var(--dropdown-option-height-large);
34
+ --dropdown-text-size-current: var(--dropdown-text-large-size);
35
+ --dropdown-text-line-height-current: var(--dropdown-text-large-line-height);
36
+ --dropdown-text-letter-spacing-current: var(
37
+ --dropdown-text-large-letter-spacing
38
+ );
39
+ }
40
+
41
+ .dropdown-menu-list {
42
+ display: flex;
43
+ flex-direction: column;
44
+ gap: var(--dropdown-panel-gap);
45
+ list-style: none;
46
+ padding: 0;
47
+ margin: 0;
48
+ }
49
+
50
+ .dropdown-menu-item {
51
+ width: 100%;
52
+ }
53
+
54
+ .dropdown-menu-item-trigger {
55
+ display: flex;
56
+ align-items: center;
57
+ gap: var(--dropdown-option-gap-inline);
58
+ width: 100%;
59
+ min-height: var(
60
+ --dropdown-option-height-current,
61
+ var(--dropdown-option-height-medium)
62
+ );
63
+ padding: var(--dropdown-option-padding-block)
64
+ var(--dropdown-option-padding-inline);
65
+ border-radius: var(--dropdown-option-radius);
66
+ background-color: transparent;
67
+ color: var(--dropdown-option-color);
68
+ cursor: pointer;
69
+ font-size: var(--dropdown-option-font-size, var(--dropdown-text-medium-size));
70
+ font-weight: var(--dropdown-option-font-weight, var(--dropdown-text-weight));
71
+ line-height: var(
72
+ --dropdown-option-line-height,
73
+ var(--dropdown-text-medium-line-height)
74
+ );
75
+
76
+ &[data-state="selected"] {
77
+ background-color: var(--dropdown-option-bg-selected);
78
+ color: var(--dropdown-option-color-selected);
79
+ }
80
+
81
+ &[data-highlighted],
82
+ &:hover {
83
+ background-color: var(--dropdown-option-bg-hover);
84
+ color: var(--dropdown-option-color-hover);
85
+ }
86
+
87
+ &[data-state="selected"]:hover {
88
+ background-color: var(--dropdown-option-bg-selected);
89
+ color: var(--dropdown-option-color-selected);
90
+ }
91
+
92
+ &[data-disabled],
93
+ &:disabled {
94
+ color: var(--dropdown-option-color-disabled);
95
+ cursor: not-allowed;
96
+ background-color: var(--dropdown-option-bg-disabled);
97
+ }
98
+
99
+ &[data-disabled] .dropdown-menu-item-label,
100
+ &:disabled .dropdown-menu-item-label {
101
+ color: var(--dropdown-option-color-disabled);
102
+ }
103
+
104
+ &[data-disabled] .dropdown-menu-item-description,
105
+ &:disabled .dropdown-menu-item-description {
106
+ color: var(--dropdown-option-color-disabled);
107
+ }
108
+ }
109
+
110
+ .dropdown-menu-item-left,
111
+ .dropdown-menu-item-right {
112
+ display: inline-flex;
113
+ align-items: center;
114
+ color: inherit;
115
+ }
116
+
117
+ .dropdown-menu-item-body {
118
+ display: flex;
119
+ flex-direction: column;
120
+ gap: 0.2rem;
121
+ flex: 1 1 auto;
122
+ min-width: 0;
123
+ }
124
+
125
+ .dropdown-menu-item-label {
126
+ display: inline-flex;
127
+ align-items: center;
128
+ min-width: 0;
129
+ color: inherit;
130
+ font-size: var(
131
+ --dropdown-text-size-current,
132
+ var(--dropdown-text-medium-size)
133
+ );
134
+ line-height: var(
135
+ --dropdown-text-line-height-current,
136
+ var(--dropdown-text-medium-line-height)
137
+ );
138
+ letter-spacing: var(
139
+ --dropdown-text-letter-spacing-current,
140
+ var(--dropdown-text-medium-letter-spacing)
141
+ );
142
+ font-weight: var(--dropdown-text-weight);
143
+ overflow: hidden;
144
+ text-overflow: ellipsis;
145
+ white-space: nowrap;
146
+ }
147
+
148
+ .dropdown-menu-item-description {
149
+ font-size: var(--dropdown-description-size);
150
+ line-height: var(--dropdown-description-line-height);
151
+ color: var(--dropdown-description-color);
152
+ overflow: hidden;
153
+ text-overflow: ellipsis;
154
+ white-space: nowrap;
155
+ }
156
+ .dropdown-menu-item-trigger[data-state="selected"] .dropdown-menu-item-label {
157
+ font-weight: var(--dropdown-text-weight-selected);
158
+ }
159
+ .dropdown-menu-item-trigger[data-multiple="true"] {
160
+ align-items: center;
161
+ }
162
+ .dropdown-menu-item-checkbox {
163
+ width: var(--theme-checkbox-frame-size-medium);
164
+ height: var(--theme-checkbox-frame-size-medium);
165
+ padding: 0;
166
+ }
@@ -0,0 +1,2 @@
1
+ @use "./variables.scss";
2
+ @use "./dropdown.scss";
@@ -0,0 +1,40 @@
1
+ :root {
2
+ --dropdown-panel-background: var(--color-common-100);
3
+ --dropdown-panel-border-color: var(--color-border-standard-cool-gray);
4
+ --dropdown-panel-radius: var(--theme-radius-large-1);
5
+ --dropdown-panel-shadow: 0px 4px 22px rgba(19, 22, 32, 0.12);
6
+ --dropdown-panel-padding: var(--spacing-padding-3);
7
+ --dropdown-panel-gap: var(--spacing-gap-2);
8
+ --dropdown-panel-max-height: 32rem;
9
+
10
+ --dropdown-option-radius: var(--theme-radius-medium-1);
11
+ --dropdown-option-gap-inline: var(--spacing-gap-3);
12
+ --dropdown-option-padding-inline: var(--spacing-padding-6);
13
+ --dropdown-option-padding-block: var(--spacing-padding-2);
14
+ --dropdown-option-color: var(--color-label-standard);
15
+ --dropdown-option-color-hover: var(--color-label-strong);
16
+ --dropdown-option-color-selected: var(--color-primary-default);
17
+ --dropdown-option-color-disabled: var(--color-label-disabled);
18
+ --dropdown-option-bg-hover: var(--color-surface-standard);
19
+ --dropdown-option-bg-selected: var(--color-surface-static-blue);
20
+ --dropdown-option-bg-disabled: transparent;
21
+
22
+ --dropdown-text-small-size: 15px;
23
+ --dropdown-text-small-line-height: 24px;
24
+ --dropdown-text-small-letter-spacing: 0;
25
+ --dropdown-text-medium-size: 16px;
26
+ --dropdown-text-medium-line-height: 24px;
27
+ --dropdown-text-medium-letter-spacing: 0;
28
+ --dropdown-text-large-size: 19px;
29
+ --dropdown-text-large-line-height: 26px;
30
+ --dropdown-text-large-letter-spacing: 0;
31
+ --dropdown-text-weight: 400;
32
+ --dropdown-text-weight-selected: 600;
33
+ --dropdown-description-color: var(--color-label-neutral);
34
+ --dropdown-description-size: 14px;
35
+ --dropdown-description-line-height: 22px;
36
+
37
+ --dropdown-option-height-small: var(--theme-size-medium-1, 40px);
38
+ --dropdown-option-height-medium: var(--theme-size-medium-2, 48px);
39
+ --dropdown-option-height-large: var(--theme-size-medium-3, 56px);
40
+ }
@@ -0,0 +1,31 @@
1
+ import type { DropdownMenuProps } from "@radix-ui/react-dropdown-menu";
2
+
3
+ /**
4
+ * Dropdown size scale
5
+ * @typedef {"small" | "medium" | "large"} DropdownSize
6
+ */
7
+ export type DropdownSize = "small" | "medium" | "large";
8
+
9
+ /**
10
+ * Dropdown panel width 옵션
11
+ * - "match": trigger width 추적
12
+ * - "fit-content" | "max-content": 고정 넓이 프리셋
13
+ * - string/number: min-width 커스텀 값
14
+ */
15
+ export type DropdownPanelWidth =
16
+ | "match"
17
+ | "fit-content"
18
+ | "max-content"
19
+ | string
20
+ | number;
21
+
22
+ /**
23
+ * Dropdown root props
24
+ * @property {boolean} [modal] Radix modal 모드 여부
25
+ */
26
+ export interface DropdownRootProps extends DropdownMenuProps {
27
+ /**
28
+ * Radix modal 모드 여부
29
+ */
30
+ modal?: DropdownMenuProps["modal"];
31
+ }
@@ -1,4 +1,2 @@
1
- /**
2
- * TODO(dropdown): variant/slot 타입 정의를 작성한다.
3
- */
4
- export {};
1
+ export type * from "./base";
2
+ export type * from "./props";
@@ -0,0 +1,170 @@
1
+ import type {
2
+ DropdownMenuContentProps,
3
+ DropdownMenuItemProps as RadixDropdownMenuItemProps,
4
+ DropdownMenuTriggerProps,
5
+ } from "@radix-ui/react-dropdown-menu";
6
+ import type { HTMLAttributes, MutableRefObject, ReactNode } from "react";
7
+
8
+ import type { CheckboxProps } from "../../checkbox/types";
9
+ import type {
10
+ DropdownPanelWidth,
11
+ DropdownRootProps,
12
+ DropdownSize,
13
+ } from "./base";
14
+
15
+ /**
16
+ * Dropdown trigger props
17
+ * @property {boolean} [asChild=true] trigger를 asChild 패턴으로 감쌀지 여부
18
+ */
19
+ export type DropdownTriggerProps = DropdownMenuTriggerProps;
20
+
21
+ /**
22
+ * Dropdown Container props
23
+ * @property {DropdownSize} [size="medium"] option 높이 스케일
24
+ * @property {DropdownPanelWidth} [width="match"] dropdown panel width 옵션
25
+ * @property {HTMLElement | null} [portalContainer] portal을 렌더링할 DOM 컨테이너
26
+ */
27
+ export interface DropdownContainerProps extends DropdownMenuContentProps {
28
+ /**
29
+ * option 높이 스케일
30
+ */
31
+ size?: DropdownSize;
32
+ /**
33
+ * trigger 너비에 맞춰 dropdown 너비를 맞출지 여부
34
+ */
35
+ width?: DropdownPanelWidth;
36
+ /**
37
+ * portal을 렌더링할 DOM 컨테이너
38
+ */
39
+ portalContainer?: HTMLElement | null;
40
+ }
41
+
42
+ /**
43
+ * Dropdown menu item props
44
+ * @property {ReactNode} [label] 옵션 라벨
45
+ * @property {ReactNode} [description] 보조 텍스트
46
+ * @property {ReactNode} [left] 좌측 콘텐츠
47
+ * @property {ReactNode} [right] 우측 콘텐츠
48
+ * @property {boolean} [isSelected] 선택 상태 여부
49
+ * @property {boolean} [multiple] multi select 스타일 여부
50
+ * @property {CheckboxProps} [checkboxProps] multiple 시 Checkbox 커스터마이징 옵션
51
+ */
52
+ export interface DropdownMenuItemProps extends RadixDropdownMenuItemProps {
53
+ /**
54
+ * 옵션 라벨
55
+ */
56
+ label?: ReactNode;
57
+ /**
58
+ * 보조 텍스트
59
+ */
60
+ description?: ReactNode;
61
+ /**
62
+ * 좌측 콘텐츠
63
+ */
64
+ left?: ReactNode;
65
+ /**
66
+ * 우측 콘텐츠
67
+ */
68
+ right?: ReactNode;
69
+ /**
70
+ * 선택 상태 여부
71
+ */
72
+ isSelected?: boolean;
73
+ /**
74
+ * multi select 스타일 여부
75
+ */
76
+ multiple?: boolean;
77
+ /**
78
+ * Checkbox 커스터마이징 props
79
+ */
80
+ checkboxProps?: CheckboxProps;
81
+ }
82
+
83
+ /**
84
+ * Dropdown menu list props
85
+ */
86
+ export interface DropdownMenuListProps extends HTMLAttributes<HTMLUListElement> {}
87
+
88
+ /**
89
+ * Dropdown context value; trigger ref 공유용
90
+ * @property {React.MutableRefObject<HTMLElement | null>} triggerRef trigger DOM ref
91
+ */
92
+ export interface DropdownContextValue {
93
+ /**
94
+ * trigger DOM ref
95
+ */
96
+ triggerRef: MutableRefObject<HTMLElement | null>;
97
+ }
98
+
99
+ /**
100
+ * Dropdown template item
101
+ * @property {string} id 고유 식별자
102
+ * @property {ReactNode} label 옵션 라벨
103
+ * @property {ReactNode} [description] 보조 텍스트
104
+ * @property {boolean} [disabled] 비활성 여부
105
+ * @property {ReactNode} [left] 좌측 콘텐츠
106
+ * @property {ReactNode} [right] 우측 콘텐츠
107
+ * @property {boolean} [multiple] multi select 스타일 여부
108
+ */
109
+ export interface DropdownTemplateItem {
110
+ /**
111
+ * 고유 식별자
112
+ */
113
+ id: string;
114
+ /**
115
+ * 옵션 라벨
116
+ */
117
+ label: ReactNode;
118
+ /**
119
+ * 보조 텍스트
120
+ */
121
+ description?: ReactNode;
122
+ /**
123
+ * 비활성 여부
124
+ */
125
+ disabled?: boolean;
126
+ /**
127
+ * 좌측 콘텐츠
128
+ */
129
+ left?: ReactNode;
130
+ /**
131
+ * 우측 콘텐츠
132
+ */
133
+ right?: ReactNode;
134
+ /**
135
+ * multi select 스타일 여부
136
+ */
137
+ multiple?: boolean;
138
+ }
139
+
140
+ /**
141
+ * Dropdown template props
142
+ * @property {ReactNode} trigger trigger 요소
143
+ * @property {DropdownTemplateItem[]} items 렌더링할 menu item 리스트
144
+ * @property {string[]} [selectedIds] 선택된 item id 배열
145
+ * @property {DropdownSize} [size="medium"] surface height scale
146
+ * @property {DropdownPanelWidth} [width="match"] panel width 옵션
147
+ * @property {DropdownRootProps} [rootProps] Root 에 전달할 props
148
+ * @property {DropdownContainerProps} [containerProps] Container 에 전달할 props
149
+ * @property {DropdownMenuListProps} [menuListProps] MenuList 에 전달할 props
150
+ */
151
+ export interface DropdownTemplateProps {
152
+ trigger: ReactNode;
153
+ items: DropdownTemplateItem[];
154
+ selectedIds?: string[];
155
+ onSelect?: (item: DropdownTemplateItem) => void;
156
+ size?: DropdownSize;
157
+ width?: DropdownPanelWidth;
158
+ /**
159
+ * Root 에 전달할 props
160
+ */
161
+ rootProps?: DropdownRootProps;
162
+ /**
163
+ * Container 에 전달할 props
164
+ */
165
+ containerProps?: Omit<DropdownContainerProps, "children" | "size" | "width">;
166
+ /**
167
+ * MenuList 에 전달할 props
168
+ */
169
+ menuListProps?: DropdownMenuListProps;
170
+ }
@@ -1,4 +1 @@
1
- /**
2
- * TODO(dropdown): 토큰 매핑과 클래스명 유틸을 구현한다.
3
- */
4
- export {};
1
+ export { mergeRefs } from "./refs";
@@ -0,0 +1,20 @@
1
+ import type { ForwardedRef } from "react";
2
+
3
+ /**
4
+ * 다중 ref를 병합하는 헬퍼
5
+ * @param {ForwardedRef<T>[]} refs 연결할 refs
6
+ * @return {(node: T | null) => void} merged ref
7
+ */
8
+ const mergeRefs = <T>(...refs: (ForwardedRef<T> | null | undefined)[]) => {
9
+ return (node: T | null) => {
10
+ refs.forEach(ref => {
11
+ if (typeof ref === "function") {
12
+ ref(node);
13
+ } else if (ref && typeof ref === "object") {
14
+ (ref as { current: T | null }).current = node;
15
+ }
16
+ });
17
+ };
18
+ };
19
+
20
+ export { mergeRefs };
@@ -0,0 +1 @@
1
+ @use "./styles/index.scss";
@@ -1,3 +1,19 @@
1
- import FormProvider from "./Provider";
1
+ import FormProvider from "./markup/Provider";
2
+ import { FormField } from "./markup/form-field";
2
3
 
3
- export const Form = { Provider: FormProvider };
4
+ import "./index.scss";
5
+
6
+ export type * from "./types";
7
+
8
+ /**
9
+ * Form
10
+ * @component
11
+ * @desc
12
+ * - Form.Provider: Form 컨텍스트 제공 컴포넌트
13
+ * - Form.Field: Form 필드 컴포넌트
14
+ * - Form.Field.Container: Form 필드의 컨테이너 컴포넌트
15
+ * - Form.Field.Header: Form 필드의 헤더 컴포넌트
16
+ * - Form.Field.Body: Form 필드의 본문 컴포넌트
17
+ * - Form.Field.Footer: Form 필드의 푸터 컴포넌트
18
+ */
19
+ export const Form = { Provider: FormProvider, Field: FormField };
@@ -0,0 +1,18 @@
1
+ import clsx from "clsx";
2
+
3
+ /**
4
+ * Form; field body
5
+ * @component
6
+ * @param {object} props
7
+ * @property {string} [className]
8
+ * @property {React.ReactNode} [children]
9
+ */
10
+ export default function FormFieldBody({
11
+ className,
12
+ children,
13
+ }: {
14
+ className?: string;
15
+ children?: React.ReactNode;
16
+ }) {
17
+ return <div className={clsx("form-field-body", className)}>{children}</div>;
18
+ }