@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.
- package/README.md +2 -2
- package/dist/styles.css +1112 -385
- package/package.json +12 -15
- package/src/components/button/index.scss +1 -0
- package/src/components/button/markup/{ButtonRounded.tsx → Rounded.tsx} +1 -1
- package/src/components/button/markup/{ButtonText.tsx → Text.tsx} +1 -1
- package/src/components/button/markup/index.ts +3 -3
- package/src/components/button/styles/button.scss +113 -229
- package/src/components/button/styles/round-button.scss +11 -14
- package/src/components/button/styles/text-button.scss +23 -23
- package/src/components/button/styles/variables.scss +145 -0
- package/src/components/dropdown/index.tsx +3 -3
- package/src/components/dropdown/markup/Template.tsx +57 -0
- package/src/components/dropdown/markup/foundation/Container.tsx +125 -0
- package/src/components/dropdown/markup/foundation/MenuItem.tsx +107 -0
- package/src/components/dropdown/markup/foundation/MenuList.tsx +27 -0
- package/src/components/dropdown/markup/foundation/Provider.tsx +46 -0
- package/src/components/dropdown/markup/foundation/Root.tsx +30 -0
- package/src/components/dropdown/markup/foundation/Trigger.tsx +34 -0
- package/src/components/dropdown/markup/foundation/index.tsx +25 -0
- package/src/components/dropdown/markup/index.tsx +8 -2
- package/src/components/dropdown/styles/dropdown.scss +166 -0
- package/src/components/dropdown/styles/index.scss +2 -0
- package/src/components/dropdown/styles/variables.scss +40 -0
- package/src/components/dropdown/types/base.ts +31 -0
- package/src/components/dropdown/types/index.ts +2 -4
- package/src/components/dropdown/types/props.ts +170 -0
- package/src/components/dropdown/utils/index.ts +1 -4
- package/src/components/dropdown/utils/refs.ts +20 -0
- package/src/components/form/index.scss +1 -0
- package/src/components/form/index.tsx +18 -2
- package/src/components/form/markup/form-field/Body.tsx +18 -0
- package/src/components/form/markup/form-field/Container.tsx +58 -0
- package/src/components/form/markup/form-field/Footer.tsx +21 -0
- package/src/components/form/markup/form-field/Header.tsx +39 -0
- package/src/components/form/markup/form-field/Template.tsx +56 -0
- package/src/components/form/markup/form-field/index.tsx +22 -0
- package/src/components/form/styles/form-field/layout.scss +67 -0
- package/src/components/form/styles/form-field/variables.scss +17 -0
- package/src/components/form/styles/index.scss +2 -0
- package/src/components/form/types/index.ts +1 -0
- package/src/components/form/types/props.ts +125 -0
- package/src/components/form/utils/form-field.ts +42 -0
- package/src/components/input/hooks/index.ts +1 -4
- package/src/components/input/hooks/useDigitField.ts +63 -0
- package/src/components/input/img/calendar/calendar.svg +7 -0
- package/src/components/input/img/calendar/chevron-down.svg +3 -0
- package/src/components/input/img/calendar/chevron-left.svg +3 -0
- package/src/components/input/img/calendar/chevron-right.svg +3 -0
- package/src/components/input/img/calendar/chevron-up.svg +3 -0
- package/src/components/input/index.tsx +2 -1
- package/src/components/input/markup/calendar/Base.tsx +329 -0
- package/src/components/input/markup/calendar/index.tsx +8 -0
- package/src/components/input/markup/{text/InputUtilityButton.tsx → foundation/Button.tsx} +5 -15
- package/src/components/input/markup/foundation/Input.tsx +245 -0
- package/src/components/input/markup/foundation/SideSlot.tsx +30 -0
- package/src/components/input/markup/foundation/StatusIcon.tsx +21 -0
- package/src/components/input/markup/foundation/Utility.tsx +103 -0
- package/src/components/input/markup/foundation/index.tsx +15 -0
- package/src/components/input/markup/index.tsx +11 -1
- package/src/components/input/markup/text/AuthCode.tsx +41 -59
- package/src/components/input/markup/text/Email.tsx +25 -115
- package/src/components/input/markup/text/Password.tsx +30 -39
- package/src/components/input/markup/text/Phone.tsx +35 -122
- package/src/components/input/markup/text/Search.tsx +17 -18
- package/src/components/input/markup/text/index.ts +15 -12
- package/src/components/input/styles/calendar.scss +110 -0
- package/src/components/input/styles/foundation.scss +345 -0
- package/src/components/input/styles/index.scss +4 -476
- package/src/components/input/styles/text.scss +89 -0
- package/src/components/input/styles/variables.scss +41 -0
- package/src/components/input/types/calendar.ts +208 -0
- package/src/components/input/types/foundation.ts +194 -0
- package/src/components/input/types/hooks.ts +43 -0
- package/src/components/input/types/index.ts +5 -87
- package/src/components/input/types/text.ts +203 -0
- package/src/components/input/types/verification.ts +23 -0
- package/src/components/input/utils/index.tsx +1 -0
- package/src/components/input/utils/verification.tsx +35 -0
- package/src/components/select/hooks/index.ts +43 -2
- package/src/components/select/img/chevron/primary/large.svg +3 -0
- package/src/components/select/img/chevron/primary/medium.svg +3 -0
- package/src/components/select/img/chevron/primary/small.svg +3 -0
- package/src/components/select/img/chevron/secondary/large.svg +3 -0
- package/src/components/select/img/chevron/secondary/medium.svg +3 -0
- package/src/components/select/img/chevron/secondary/small.svg +3 -0
- package/src/components/select/img/remove.svg +3 -0
- package/src/components/select/index.scss +2 -1
- package/src/components/select/index.tsx +5 -0
- package/src/components/select/markup/Default.tsx +154 -0
- package/src/components/select/markup/foundation/Base.tsx +90 -0
- package/src/components/select/markup/foundation/Container.tsx +30 -0
- package/src/components/select/markup/foundation/Icon.tsx +78 -0
- package/src/components/select/markup/foundation/Selected.tsx +34 -0
- package/src/components/select/markup/foundation/index.ts +2 -0
- package/src/components/select/markup/index.tsx +36 -2
- package/src/components/select/markup/multiple/Multiple.tsx +205 -0
- package/src/components/select/markup/multiple/SelectedChip.tsx +58 -0
- package/src/components/select/markup/multiple/index.ts +2 -0
- package/src/components/select/styles/select.scss +316 -0
- package/src/components/select/styles/variables.scss +91 -0
- package/src/components/select/types/base.ts +34 -0
- package/src/components/select/types/icon.ts +45 -0
- package/src/components/select/types/index.ts +6 -4
- package/src/components/select/types/multiple.ts +57 -0
- package/src/components/select/types/option.ts +43 -0
- package/src/components/select/types/props.ts +209 -0
- package/src/components/select/types/trigger.ts +196 -0
- package/src/index.scss +3 -2
- package/src/components/input/markup/text/Base.tsx +0 -454
- package/src/components/input/utils/index.ts +0 -60
- package/src/components/select/styles/index.scss +0 -0
- /package/src/components/button/markup/{ButtonDefault.tsx → Base.tsx} +0 -0
- /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
|
-
*
|
|
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,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
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -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
|
-
|
|
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
|
+
}
|