sh-ui-cli 0.52.1 → 0.52.2
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/data/changelog/versions.json +14 -0
- package/data/registry/react/components/_smoke/vanilla-extract.test.ts +33 -0
- package/data/registry/react/components/input/styles.css.ts +6 -6
- package/data/registry/react/registry.json +35 -852
- package/package.json +1 -1
- package/src/api.d.ts +3 -4
- package/src/constants.js +9 -5
- package/src/mcp.mjs +0 -1
- package/data/registry/react/components/accordion/index.vanilla-extract.tsx +0 -97
- package/data/registry/react/components/accordion/styles.css.ts +0 -131
- package/data/registry/react/components/avatar/index.vanilla-extract.tsx +0 -73
- package/data/registry/react/components/avatar/styles.css.ts +0 -68
- package/data/registry/react/components/badge/index.vanilla-extract.tsx +0 -40
- package/data/registry/react/components/badge/styles.css.ts +0 -71
- package/data/registry/react/components/breadcrumb/index.vanilla-extract.tsx +0 -152
- package/data/registry/react/components/breadcrumb/styles.css.ts +0 -95
- package/data/registry/react/components/calendar/index.vanilla-extract.tsx +0 -806
- package/data/registry/react/components/calendar/styles.css.ts +0 -250
- package/data/registry/react/components/carousel/index.vanilla-extract.tsx +0 -430
- package/data/registry/react/components/carousel/styles.css.ts +0 -169
- package/data/registry/react/components/checkbox/index.vanilla-extract.tsx +0 -96
- package/data/registry/react/components/checkbox/styles.css.ts +0 -74
- package/data/registry/react/components/code-editor/index.vanilla-extract.tsx +0 -230
- package/data/registry/react/components/code-editor/styles.css.ts +0 -97
- package/data/registry/react/components/code-panel/index.vanilla-extract.tsx +0 -191
- package/data/registry/react/components/code-panel/styles.css.ts +0 -151
- package/data/registry/react/components/color-picker/index.vanilla-extract.tsx +0 -467
- package/data/registry/react/components/color-picker/styles.css.ts +0 -169
- package/data/registry/react/components/combobox/index.vanilla-extract.tsx +0 -165
- package/data/registry/react/components/combobox/styles.css.ts +0 -174
- package/data/registry/react/components/context-menu/index.vanilla-extract.tsx +0 -251
- package/data/registry/react/components/context-menu/styles.css.ts +0 -167
- package/data/registry/react/components/date-picker/index.vanilla-extract.tsx +0 -520
- package/data/registry/react/components/date-picker/styles.css.ts +0 -111
- package/data/registry/react/components/dialog/index.vanilla-extract.tsx +0 -95
- package/data/registry/react/components/dialog/styles.css.ts +0 -140
- package/data/registry/react/components/dropdown-menu/index.vanilla-extract.tsx +0 -255
- package/data/registry/react/components/dropdown-menu/styles.css.ts +0 -175
- package/data/registry/react/components/file-upload/index.vanilla-extract.tsx +0 -487
- package/data/registry/react/components/file-upload/styles.css.ts +0 -193
- package/data/registry/react/components/form/index.vanilla-extract.tsx +0 -61
- package/data/registry/react/components/form/styles.css.ts +0 -56
- package/data/registry/react/components/header/index.vanilla-extract.tsx +0 -805
- package/data/registry/react/components/header/styles.css.ts +0 -413
- package/data/registry/react/components/label/index.vanilla-extract.tsx +0 -52
- package/data/registry/react/components/label/styles.css.ts +0 -141
- package/data/registry/react/components/markdown-editor/index.vanilla-extract.tsx +0 -119
- package/data/registry/react/components/markdown-editor/styles.css.ts +0 -231
- package/data/registry/react/components/menubar/index.vanilla-extract.tsx +0 -32
- package/data/registry/react/components/menubar/styles.css.ts +0 -53
- package/data/registry/react/components/numeric-input/index.vanilla-extract.tsx +0 -148
- package/data/registry/react/components/numeric-input/styles.css.ts +0 -65
- package/data/registry/react/components/page-toc/index.vanilla-extract.tsx +0 -174
- package/data/registry/react/components/page-toc/styles.css.ts +0 -97
- package/data/registry/react/components/pagination/index.vanilla-extract.tsx +0 -269
- package/data/registry/react/components/pagination/styles.css.ts +0 -113
- package/data/registry/react/components/popover/index.vanilla-extract.tsx +0 -113
- package/data/registry/react/components/popover/styles.css.ts +0 -78
- package/data/registry/react/components/progress/index.vanilla-extract.tsx +0 -54
- package/data/registry/react/components/progress/styles.css.ts +0 -53
- package/data/registry/react/components/radio/index.vanilla-extract.tsx +0 -65
- package/data/registry/react/components/radio/styles.css.ts +0 -79
- package/data/registry/react/components/rich-text-editor/index.vanilla-extract.tsx +0 -348
- package/data/registry/react/components/rich-text-editor/styles.css.ts +0 -243
- package/data/registry/react/components/select/index.vanilla-extract.tsx +0 -234
- package/data/registry/react/components/select/styles.css.ts +0 -225
- package/data/registry/react/components/separator/index.vanilla-extract.tsx +0 -46
- package/data/registry/react/components/separator/styles.css.ts +0 -24
- package/data/registry/react/components/sidebar/index.vanilla-extract.tsx +0 -1067
- package/data/registry/react/components/sidebar/styles.css.ts +0 -578
- package/data/registry/react/components/skeleton/index.vanilla-extract.tsx +0 -22
- package/data/registry/react/components/skeleton/styles.css.ts +0 -30
- package/data/registry/react/components/slider/index.vanilla-extract.tsx +0 -298
- package/data/registry/react/components/slider/styles.css.ts +0 -75
- package/data/registry/react/components/spinner/index.vanilla-extract.tsx +0 -38
- package/data/registry/react/components/spinner/styles.css.ts +0 -60
- package/data/registry/react/components/switch/index.vanilla-extract.tsx +0 -39
- package/data/registry/react/components/switch/styles.css.ts +0 -87
- package/data/registry/react/components/tabs/index.vanilla-extract.tsx +0 -91
- package/data/registry/react/components/tabs/styles.css.ts +0 -145
- package/data/registry/react/components/textarea/index.vanilla-extract.tsx +0 -23
- package/data/registry/react/components/textarea/styles.css.ts +0 -55
- package/data/registry/react/components/toast/index.vanilla-extract.tsx +0 -258
- package/data/registry/react/components/toast/styles.css.ts +0 -307
- package/data/registry/react/components/toggle/index.vanilla-extract.tsx +0 -131
- package/data/registry/react/components/toggle/styles.css.ts +0 -109
- package/data/registry/react/components/tooltip/index.vanilla-extract.tsx +0 -83
- package/data/registry/react/components/tooltip/styles.css.ts +0 -59
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import { style } from "@vanilla-extract/css";
|
|
2
|
-
|
|
3
|
-
export const datePickerTrigger = style({
|
|
4
|
-
display: "inline-flex",
|
|
5
|
-
alignItems: "center",
|
|
6
|
-
justifyContent: "space-between",
|
|
7
|
-
width: "100%",
|
|
8
|
-
height: "var(--control-md)",
|
|
9
|
-
padding: "0 var(--space-3)",
|
|
10
|
-
background: "var(--background)",
|
|
11
|
-
color: "var(--foreground)",
|
|
12
|
-
border: "1px solid var(--border)",
|
|
13
|
-
borderRadius: "var(--radius)",
|
|
14
|
-
fontFamily: "inherit",
|
|
15
|
-
fontSize: "var(--text-sm)",
|
|
16
|
-
lineHeight: 1,
|
|
17
|
-
cursor: "pointer",
|
|
18
|
-
transition: "border-color var(--duration-fast), box-shadow var(--duration-fast)",
|
|
19
|
-
WebkitTapHighlightColor: "transparent",
|
|
20
|
-
selectors: {
|
|
21
|
-
"&:hover:not(:disabled)": {
|
|
22
|
-
borderColor: "var(--border-strong)",
|
|
23
|
-
},
|
|
24
|
-
"&:focus-visible": {
|
|
25
|
-
outline: "none",
|
|
26
|
-
borderColor: "var(--foreground)",
|
|
27
|
-
boxShadow: "0 0 0 1px var(--foreground)",
|
|
28
|
-
},
|
|
29
|
-
"&:disabled": {
|
|
30
|
-
opacity: "var(--opacity-disabled)",
|
|
31
|
-
cursor: "not-allowed",
|
|
32
|
-
background: "var(--background-subtle)",
|
|
33
|
-
},
|
|
34
|
-
"&[aria-invalid="true"]": {
|
|
35
|
-
borderColor: "var(--danger)",
|
|
36
|
-
},
|
|
37
|
-
"&[aria-invalid="true"]:focus-visible": {
|
|
38
|
-
boxShadow: "0 0 0 1px var(--danger)",
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
"@media": {
|
|
42
|
-
"(hover: none) and (pointer: coarse)": {
|
|
43
|
-
height: "2.75rem",
|
|
44
|
-
fontSize: "var(--text-base)",
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
export const datePickerValue = style({
|
|
50
|
-
overflow: "hidden",
|
|
51
|
-
textOverflow: "ellipsis",
|
|
52
|
-
whiteSpace: "nowrap",
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
export const datePickerPlaceholder = style({
|
|
56
|
-
color: "var(--foreground-subtle)",
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
export const datePickerIcon = style({
|
|
60
|
-
flexShrink: 0,
|
|
61
|
-
display: "inline-flex",
|
|
62
|
-
color: "var(--foreground-muted)",
|
|
63
|
-
marginLeft: "var(--space-2)",
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
export const datePickerPositioner = style({
|
|
67
|
-
zIndex: "var(--z-popover)",
|
|
68
|
-
outline: "none",
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
export const datePickerPopup = style({
|
|
72
|
-
background: "var(--background)",
|
|
73
|
-
border: "1px solid var(--border)",
|
|
74
|
-
borderRadius: "var(--radius)",
|
|
75
|
-
boxShadow: "0 8px 24px rgba(0, 0, 0, 0.12)",
|
|
76
|
-
outline: "none",
|
|
77
|
-
padding: "var(--space-3)",
|
|
78
|
-
transformOrigin: "var(--transform-origin)",
|
|
79
|
-
transition: "opacity 140ms ease, transform 140ms ease",
|
|
80
|
-
selectors: {
|
|
81
|
-
"&[data-starting-style]": {
|
|
82
|
-
opacity: 0,
|
|
83
|
-
transform: "scale(0.96)",
|
|
84
|
-
},
|
|
85
|
-
"&[data-ending-style]": {
|
|
86
|
-
opacity: 0,
|
|
87
|
-
transform: "scale(0.96)",
|
|
88
|
-
},
|
|
89
|
-
},
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
export const datePickerFooter = style({
|
|
93
|
-
display: "flex",
|
|
94
|
-
alignItems: "center",
|
|
95
|
-
justifyContent: "flex-end",
|
|
96
|
-
gap: "var(--space-2)",
|
|
97
|
-
marginTop: "var(--space-2)",
|
|
98
|
-
paddingTop: "var(--space-2)",
|
|
99
|
-
borderTop: "1px solid var(--border)",
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
/** 동적 키로 클래스 참조용 — `byKey[\`badge--${variant}\`]` 같은 패턴 지원. */
|
|
103
|
-
export const byKey: Record<string, string> = {
|
|
104
|
-
"date-picker__trigger": datePickerTrigger,
|
|
105
|
-
"date-picker__value": datePickerValue,
|
|
106
|
-
"date-picker__placeholder": datePickerPlaceholder,
|
|
107
|
-
"date-picker__icon": datePickerIcon,
|
|
108
|
-
"date-picker__positioner": datePickerPositioner,
|
|
109
|
-
"date-picker__popup": datePickerPopup,
|
|
110
|
-
"date-picker__footer": datePickerFooter,
|
|
111
|
-
};
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import { Dialog as BaseDialog } from "@base-ui/react/dialog";
|
|
3
|
-
import { byKey, dialog__backdrop, dialog__content, dialog__title, dialog__description, dialog__footer, dialog__close } from "./styles.css";
|
|
4
|
-
|
|
5
|
-
import { cn } from "@SH_UI_UTILS@";
|
|
6
|
-
type WithStringClassName<T> = Omit<T, "className"> & { className?: string };
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* 모달 다이얼로그 루트. 열림 상태를 가지며 자식으로 Trigger·Content를 둔다.
|
|
11
|
-
* 주의를 강제하는 흐름(확인/입력)에 사용하고, 단순 안내는 Popover나 Toast를 권장.
|
|
12
|
-
*/
|
|
13
|
-
export const Dialog = BaseDialog.Root;
|
|
14
|
-
|
|
15
|
-
/** Dialog를 여는 트리거. 보통 Button을 감싸 사용. */
|
|
16
|
-
export const DialogTrigger = BaseDialog.Trigger;
|
|
17
|
-
|
|
18
|
-
/** 클릭 시 Dialog를 닫는 요소. footer의 취소 버튼 등에 사용. */
|
|
19
|
-
export const DialogClose = BaseDialog.Close;
|
|
20
|
-
|
|
21
|
-
/** 우상단에 배치되는 X 닫기 버튼. `aria-label="닫기"`가 자동 부여된다. */
|
|
22
|
-
export function DialogCloseX({ className, children, ...props }: React.ButtonHTMLAttributes<HTMLButtonElement>) {
|
|
23
|
-
return (
|
|
24
|
-
<BaseDialog.Close
|
|
25
|
-
className={cn(dialog__close, className)}
|
|
26
|
-
aria-label="닫기"
|
|
27
|
-
{...props}
|
|
28
|
-
>
|
|
29
|
-
{children ?? "×"}
|
|
30
|
-
</BaseDialog.Close>
|
|
31
|
-
);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/** Dialog 본문 하단의 액션 버튼 영역. 보통 [취소, 확인] 순서로 배치. */
|
|
35
|
-
export function DialogFooter({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
36
|
-
return <div className={cn(dialog__footer, className)} {...props} />;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface DialogContentProps
|
|
40
|
-
extends WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseDialog.Popup>> {
|
|
41
|
-
/**
|
|
42
|
-
* Portal이 마운트될 DOM 노드. 모달이 다른 stacking context에 갇혀야 할 때 지정한다.
|
|
43
|
-
* @default document.body
|
|
44
|
-
*/
|
|
45
|
-
container?: React.ComponentPropsWithoutRef<typeof BaseDialog.Portal>["container"];
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Dialog의 실제 콘텐츠. Portal로 body 끝에 마운트되며 backdrop·focus trap·ESC 닫힘 등이 자동 처리된다.
|
|
50
|
-
* 접근성: 안에 반드시 `DialogTitle`을 두고, 추가 설명은 `DialogDescription`으로 연결할 것.
|
|
51
|
-
*/
|
|
52
|
-
export const DialogContent = React.forwardRef<HTMLDivElement, DialogContentProps>(
|
|
53
|
-
function DialogContent({ className, children, container, ...props }, ref) {
|
|
54
|
-
return (
|
|
55
|
-
<BaseDialog.Portal container={container}>
|
|
56
|
-
<BaseDialog.Backdrop className={dialog__backdrop} />
|
|
57
|
-
<BaseDialog.Popup
|
|
58
|
-
ref={ref}
|
|
59
|
-
className={cn(dialog__content, className)}
|
|
60
|
-
{...props}
|
|
61
|
-
>
|
|
62
|
-
{children}
|
|
63
|
-
</BaseDialog.Popup>
|
|
64
|
-
</BaseDialog.Portal>
|
|
65
|
-
);
|
|
66
|
-
},
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
/** Dialog의 제목. 접근성을 위해 DialogContent 안에 항상 포함시킬 것. */
|
|
70
|
-
export const DialogTitle = React.forwardRef<
|
|
71
|
-
HTMLHeadingElement,
|
|
72
|
-
WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseDialog.Title>>
|
|
73
|
-
>(function DialogTitle({ className, ...props }, ref) {
|
|
74
|
-
return (
|
|
75
|
-
<BaseDialog.Title
|
|
76
|
-
ref={ref}
|
|
77
|
-
className={cn(dialog__title, className)}
|
|
78
|
-
{...props}
|
|
79
|
-
/>
|
|
80
|
-
);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
/** Dialog의 보조 설명. 제목만으로 맥락이 부족할 때 사용한다. */
|
|
84
|
-
export const DialogDescription = React.forwardRef<
|
|
85
|
-
HTMLParagraphElement,
|
|
86
|
-
WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseDialog.Description>>
|
|
87
|
-
>(function DialogDescription({ className, ...props }, ref) {
|
|
88
|
-
return (
|
|
89
|
-
<BaseDialog.Description
|
|
90
|
-
ref={ref}
|
|
91
|
-
className={cn(dialog__description, className)}
|
|
92
|
-
{...props}
|
|
93
|
-
/>
|
|
94
|
-
);
|
|
95
|
-
});
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
import { style } from "@vanilla-extract/css";
|
|
2
|
-
|
|
3
|
-
export const dialog__backdrop = style({
|
|
4
|
-
position: "fixed",
|
|
5
|
-
inset: 0,
|
|
6
|
-
zIndex: "var(--z-overlay)",
|
|
7
|
-
background: "rgba(0, 0, 0, 0.25)",
|
|
8
|
-
backdropFilter: "blur(8px)",
|
|
9
|
-
transition: "opacity var(--duration-slow) ease",
|
|
10
|
-
selectors: {
|
|
11
|
-
"&[data-starting-style]": {
|
|
12
|
-
opacity: 0,
|
|
13
|
-
},
|
|
14
|
-
"&[data-ending-style]": {
|
|
15
|
-
opacity: 0,
|
|
16
|
-
},
|
|
17
|
-
},
|
|
18
|
-
"@media": {
|
|
19
|
-
"(prefers-reduced-motion: reduce)": {
|
|
20
|
-
transition: "none",
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
export const dialog__content = style({
|
|
26
|
-
position: "fixed",
|
|
27
|
-
top: "50%",
|
|
28
|
-
left: "50%",
|
|
29
|
-
transform: "translate(-50%, -50%)",
|
|
30
|
-
zIndex: "var(--z-modal)",
|
|
31
|
-
display: "flex",
|
|
32
|
-
flexDirection: "column",
|
|
33
|
-
width: "calc(100% - 2rem)",
|
|
34
|
-
maxWidth: "28rem",
|
|
35
|
-
maxHeight: "calc(100dvh - 4rem)",
|
|
36
|
-
padding: "var(--space-6)",
|
|
37
|
-
background: "var(--background)",
|
|
38
|
-
color: "var(--foreground)",
|
|
39
|
-
border: "1px solid var(--border)",
|
|
40
|
-
borderRadius: "var(--radius)",
|
|
41
|
-
boxShadow: "var(--shadow-xl)",
|
|
42
|
-
outline: "none",
|
|
43
|
-
overflowY: "auto",
|
|
44
|
-
transition: "opacity var(--duration-slow) ease,\n transform var(--duration-slow) ease",
|
|
45
|
-
selectors: {
|
|
46
|
-
"&[data-starting-style]": {
|
|
47
|
-
opacity: 0,
|
|
48
|
-
transform: "translate(-50%, calc(-50% + 0.5rem)) scale(0.97)",
|
|
49
|
-
},
|
|
50
|
-
"&[data-ending-style]": {
|
|
51
|
-
opacity: 0,
|
|
52
|
-
transform: "translate(-50%, calc(-50% + 0.25rem)) scale(0.98)",
|
|
53
|
-
},
|
|
54
|
-
"&:focus-visible": {
|
|
55
|
-
outline: "var(--border-width-strong) solid var(--foreground)",
|
|
56
|
-
outlineOffset: "2px",
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
"@media": {
|
|
60
|
-
"(prefers-reduced-motion: reduce)": {
|
|
61
|
-
transition: "none",
|
|
62
|
-
selectors: {
|
|
63
|
-
"&[data-starting-style]": {
|
|
64
|
-
transform: "translate(-50%, -50%)",
|
|
65
|
-
},
|
|
66
|
-
"&[data-ending-style]": {
|
|
67
|
-
transform: "translate(-50%, -50%)",
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
},
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
export const dialog__title = style({
|
|
75
|
-
margin: "0 0 var(--space-1)",
|
|
76
|
-
fontWeight: "var(--weight-semibold)",
|
|
77
|
-
fontSize: "var(--text-lg)",
|
|
78
|
-
lineHeight: 1.4,
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
export const dialog__description = style({
|
|
82
|
-
margin: "0 0 var(--space-5)",
|
|
83
|
-
color: "var(--foreground-muted)",
|
|
84
|
-
fontSize: "var(--text-sm)",
|
|
85
|
-
lineHeight: 1.5,
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
export const dialog__footer = style({
|
|
89
|
-
display: "flex",
|
|
90
|
-
alignItems: "center",
|
|
91
|
-
justifyContent: "flex-end",
|
|
92
|
-
gap: "var(--space-2)",
|
|
93
|
-
paddingTop: "var(--space-4)",
|
|
94
|
-
borderTop: "1px solid var(--border)",
|
|
95
|
-
marginTop: "auto",
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
export const dialog__close = style({
|
|
99
|
-
position: "absolute",
|
|
100
|
-
top: "var(--space-3)",
|
|
101
|
-
right: "var(--space-3)",
|
|
102
|
-
display: "inline-flex",
|
|
103
|
-
alignItems: "center",
|
|
104
|
-
justifyContent: "center",
|
|
105
|
-
width: "2rem",
|
|
106
|
-
height: "2rem",
|
|
107
|
-
border: 0,
|
|
108
|
-
borderRadius: "calc(var(--radius) - 2px)",
|
|
109
|
-
background: "transparent",
|
|
110
|
-
color: "var(--foreground-muted)",
|
|
111
|
-
fontSize: "var(--text-lg)",
|
|
112
|
-
lineHeight: 1,
|
|
113
|
-
cursor: "pointer",
|
|
114
|
-
transition: "background-color var(--duration-fast), color var(--duration-fast)",
|
|
115
|
-
selectors: {
|
|
116
|
-
"&:hover": {
|
|
117
|
-
background: "var(--background-muted)",
|
|
118
|
-
color: "var(--foreground)",
|
|
119
|
-
},
|
|
120
|
-
"&:focus-visible": {
|
|
121
|
-
outline: "var(--border-width-strong) solid var(--foreground)",
|
|
122
|
-
outlineOffset: "2px",
|
|
123
|
-
},
|
|
124
|
-
},
|
|
125
|
-
"@media": {
|
|
126
|
-
"(prefers-reduced-motion: reduce)": {
|
|
127
|
-
transition: "none",
|
|
128
|
-
},
|
|
129
|
-
},
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
/** 동적 키로 클래스 참조용 — `byKey[\`badge--${variant}\`]` 같은 패턴 지원. */
|
|
133
|
-
export const byKey: Record<string, string> = {
|
|
134
|
-
"dialog__backdrop": dialog__backdrop,
|
|
135
|
-
"dialog__content": dialog__content,
|
|
136
|
-
"dialog__title": dialog__title,
|
|
137
|
-
"dialog__description": dialog__description,
|
|
138
|
-
"dialog__footer": dialog__footer,
|
|
139
|
-
"dialog__close": dialog__close,
|
|
140
|
-
};
|
|
@@ -1,255 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import * as React from "react";
|
|
4
|
-
import { Menu as BaseMenu } from "@base-ui/react/menu";
|
|
5
|
-
import { byKey, dm__trigger, dm__positioner, dm__content, dm__item, dmItemText, dmItemCheck, dmItemIndicator, dm__group, dm__label, dm__separator, dmSubArrow, dmSubTrigger } from "./styles.css";
|
|
6
|
-
|
|
7
|
-
import { cn } from "@SH_UI_UTILS@";
|
|
8
|
-
type WithStringClassName<T> = Omit<T, "className"> & { className?: string };
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
/* ───────── Root ───────── */
|
|
12
|
-
|
|
13
|
-
export const DropdownMenu = BaseMenu.Root;
|
|
14
|
-
|
|
15
|
-
/* ───────── Trigger ───────── */
|
|
16
|
-
|
|
17
|
-
export const DropdownMenuTrigger = React.forwardRef<
|
|
18
|
-
HTMLButtonElement,
|
|
19
|
-
WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseMenu.Trigger>>
|
|
20
|
-
>(function DropdownMenuTrigger({ className, ...props }, ref) {
|
|
21
|
-
return (
|
|
22
|
-
<BaseMenu.Trigger
|
|
23
|
-
ref={ref}
|
|
24
|
-
className={cn(dm__trigger, className)}
|
|
25
|
-
{...props}
|
|
26
|
-
/>
|
|
27
|
-
);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
/* ───────── Content ─────────
|
|
31
|
-
* Portal + Positioner + Popup을 한 컴포넌트로 묶는다. Popover와 동일한 관용.
|
|
32
|
-
*/
|
|
33
|
-
|
|
34
|
-
export interface DropdownMenuContentProps
|
|
35
|
-
extends WithStringClassName<
|
|
36
|
-
React.ComponentPropsWithoutRef<typeof BaseMenu.Popup>
|
|
37
|
-
> {
|
|
38
|
-
side?: "top" | "right" | "bottom" | "left";
|
|
39
|
-
align?: "start" | "center" | "end";
|
|
40
|
-
/** Trigger-Popup 간격(px). 기본 6. */
|
|
41
|
-
sideOffset?: number;
|
|
42
|
-
container?: React.ComponentPropsWithoutRef<
|
|
43
|
-
typeof BaseMenu.Portal
|
|
44
|
-
>["container"];
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export const DropdownMenuContent = React.forwardRef<
|
|
48
|
-
HTMLDivElement,
|
|
49
|
-
DropdownMenuContentProps
|
|
50
|
-
>(function DropdownMenuContent(
|
|
51
|
-
{ className, children, side, align, sideOffset = 6, container, ...props },
|
|
52
|
-
ref,
|
|
53
|
-
) {
|
|
54
|
-
return (
|
|
55
|
-
<BaseMenu.Portal container={container}>
|
|
56
|
-
<BaseMenu.Positioner
|
|
57
|
-
className={dm__positioner}
|
|
58
|
-
side={side}
|
|
59
|
-
align={align}
|
|
60
|
-
sideOffset={sideOffset}
|
|
61
|
-
>
|
|
62
|
-
<BaseMenu.Popup
|
|
63
|
-
ref={ref}
|
|
64
|
-
className={cn(dm__content, className)}
|
|
65
|
-
{...props}
|
|
66
|
-
>
|
|
67
|
-
{children}
|
|
68
|
-
</BaseMenu.Popup>
|
|
69
|
-
</BaseMenu.Positioner>
|
|
70
|
-
</BaseMenu.Portal>
|
|
71
|
-
);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
/* ───────── Item ───────── */
|
|
75
|
-
|
|
76
|
-
export const DropdownMenuItem = React.forwardRef<
|
|
77
|
-
HTMLDivElement,
|
|
78
|
-
WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseMenu.Item>>
|
|
79
|
-
>(function DropdownMenuItem({ className, ...props }, ref) {
|
|
80
|
-
return (
|
|
81
|
-
<BaseMenu.Item
|
|
82
|
-
ref={ref}
|
|
83
|
-
className={cn(dm__item, className)}
|
|
84
|
-
{...props}
|
|
85
|
-
/>
|
|
86
|
-
);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
/* ───────── CheckboxItem ───────── */
|
|
90
|
-
|
|
91
|
-
export interface DropdownMenuCheckboxItemProps
|
|
92
|
-
extends WithStringClassName<
|
|
93
|
-
React.ComponentPropsWithoutRef<typeof BaseMenu.CheckboxItem>
|
|
94
|
-
> {}
|
|
95
|
-
|
|
96
|
-
export const DropdownMenuCheckboxItem = React.forwardRef<
|
|
97
|
-
HTMLDivElement,
|
|
98
|
-
DropdownMenuCheckboxItemProps
|
|
99
|
-
>(function DropdownMenuCheckboxItem({ className, children, ...props }, ref) {
|
|
100
|
-
return (
|
|
101
|
-
<BaseMenu.CheckboxItem
|
|
102
|
-
ref={ref}
|
|
103
|
-
className={cn(dm__item, dmItemCheck, className)}
|
|
104
|
-
{...props}
|
|
105
|
-
>
|
|
106
|
-
<span className={dmItemIndicator} aria-hidden>
|
|
107
|
-
<BaseMenu.CheckboxItemIndicator>
|
|
108
|
-
<CheckIcon />
|
|
109
|
-
</BaseMenu.CheckboxItemIndicator>
|
|
110
|
-
</span>
|
|
111
|
-
<span className={dmItemText}>{children}</span>
|
|
112
|
-
</BaseMenu.CheckboxItem>
|
|
113
|
-
);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
/* ───────── RadioGroup / RadioItem ───────── */
|
|
117
|
-
|
|
118
|
-
export const DropdownMenuRadioGroup = BaseMenu.RadioGroup;
|
|
119
|
-
|
|
120
|
-
export interface DropdownMenuRadioItemProps
|
|
121
|
-
extends WithStringClassName<
|
|
122
|
-
React.ComponentPropsWithoutRef<typeof BaseMenu.RadioItem>
|
|
123
|
-
> {}
|
|
124
|
-
|
|
125
|
-
export const DropdownMenuRadioItem = React.forwardRef<
|
|
126
|
-
HTMLDivElement,
|
|
127
|
-
DropdownMenuRadioItemProps
|
|
128
|
-
>(function DropdownMenuRadioItem({ className, children, ...props }, ref) {
|
|
129
|
-
return (
|
|
130
|
-
<BaseMenu.RadioItem
|
|
131
|
-
ref={ref}
|
|
132
|
-
className={cn(dm__item, dmItemCheck, className)}
|
|
133
|
-
{...props}
|
|
134
|
-
>
|
|
135
|
-
<span className={dmItemIndicator} aria-hidden>
|
|
136
|
-
<BaseMenu.RadioItemIndicator>
|
|
137
|
-
<DotIcon />
|
|
138
|
-
</BaseMenu.RadioItemIndicator>
|
|
139
|
-
</span>
|
|
140
|
-
<span className={dmItemText}>{children}</span>
|
|
141
|
-
</BaseMenu.RadioItem>
|
|
142
|
-
);
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
/* ───────── Group / Label ───────── */
|
|
146
|
-
|
|
147
|
-
export const DropdownMenuGroup = React.forwardRef<
|
|
148
|
-
HTMLDivElement,
|
|
149
|
-
WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseMenu.Group>>
|
|
150
|
-
>(function DropdownMenuGroup({ className, ...props }, ref) {
|
|
151
|
-
return (
|
|
152
|
-
<BaseMenu.Group
|
|
153
|
-
ref={ref}
|
|
154
|
-
className={cn(dm__group, className)}
|
|
155
|
-
{...props}
|
|
156
|
-
/>
|
|
157
|
-
);
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
export const DropdownMenuLabel = React.forwardRef<
|
|
161
|
-
HTMLDivElement,
|
|
162
|
-
WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseMenu.GroupLabel>>
|
|
163
|
-
>(function DropdownMenuLabel({ className, ...props }, ref) {
|
|
164
|
-
return (
|
|
165
|
-
<BaseMenu.GroupLabel
|
|
166
|
-
ref={ref}
|
|
167
|
-
className={cn(dm__label, className)}
|
|
168
|
-
{...props}
|
|
169
|
-
/>
|
|
170
|
-
);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
/* ───────── Separator ─────────
|
|
174
|
-
* Base UI Menu에 전용 Separator가 없어 role="separator" div로 대체.
|
|
175
|
-
*/
|
|
176
|
-
|
|
177
|
-
export const DropdownMenuSeparator = React.forwardRef<
|
|
178
|
-
HTMLDivElement,
|
|
179
|
-
React.HTMLAttributes<HTMLDivElement>
|
|
180
|
-
>(function DropdownMenuSeparator({ className, ...props }, ref) {
|
|
181
|
-
return (
|
|
182
|
-
<div
|
|
183
|
-
ref={ref}
|
|
184
|
-
role="separator"
|
|
185
|
-
aria-orientation="horizontal"
|
|
186
|
-
className={cn(dm__separator, className)}
|
|
187
|
-
{...props}
|
|
188
|
-
/>
|
|
189
|
-
);
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
/* ───────── Submenu ───────── */
|
|
193
|
-
|
|
194
|
-
export const DropdownMenuSub = BaseMenu.SubmenuRoot;
|
|
195
|
-
|
|
196
|
-
export const DropdownMenuSubTrigger = React.forwardRef<
|
|
197
|
-
HTMLDivElement,
|
|
198
|
-
WithStringClassName<
|
|
199
|
-
React.ComponentPropsWithoutRef<typeof BaseMenu.SubmenuTrigger>
|
|
200
|
-
>
|
|
201
|
-
>(function DropdownMenuSubTrigger({ className, children, ...props }, ref) {
|
|
202
|
-
return (
|
|
203
|
-
<BaseMenu.SubmenuTrigger
|
|
204
|
-
ref={ref}
|
|
205
|
-
className={cn(dm__item, dmSubTrigger, className)}
|
|
206
|
-
{...props}
|
|
207
|
-
>
|
|
208
|
-
<span className={dmItemText}>{children}</span>
|
|
209
|
-
<span className={dmSubArrow} aria-hidden>
|
|
210
|
-
<ChevronRightIcon />
|
|
211
|
-
</span>
|
|
212
|
-
</BaseMenu.SubmenuTrigger>
|
|
213
|
-
);
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
/** Submenu 내용물 — 최상위 Content와 동일한 포털 구조. */
|
|
217
|
-
export const DropdownMenuSubContent = DropdownMenuContent;
|
|
218
|
-
|
|
219
|
-
/* ───────── 기본 아이콘 ───────── */
|
|
220
|
-
|
|
221
|
-
function CheckIcon() {
|
|
222
|
-
return (
|
|
223
|
-
<svg width="12" height="12" viewBox="0 0 16 16" fill="none" aria-hidden>
|
|
224
|
-
<path
|
|
225
|
-
d="M3.5 8.5l3 3 6-7"
|
|
226
|
-
stroke="currentColor"
|
|
227
|
-
strokeWidth="1.75"
|
|
228
|
-
strokeLinecap="round"
|
|
229
|
-
strokeLinejoin="round"
|
|
230
|
-
/>
|
|
231
|
-
</svg>
|
|
232
|
-
);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
function DotIcon() {
|
|
236
|
-
return (
|
|
237
|
-
<svg width="8" height="8" viewBox="0 0 8 8" fill="currentColor" aria-hidden>
|
|
238
|
-
<circle cx="4" cy="4" r="3" />
|
|
239
|
-
</svg>
|
|
240
|
-
);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
function ChevronRightIcon() {
|
|
244
|
-
return (
|
|
245
|
-
<svg width="12" height="12" viewBox="0 0 16 16" fill="none" aria-hidden>
|
|
246
|
-
<path
|
|
247
|
-
d="M6 4l4 4-4 4"
|
|
248
|
-
stroke="currentColor"
|
|
249
|
-
strokeWidth="1.5"
|
|
250
|
-
strokeLinecap="round"
|
|
251
|
-
strokeLinejoin="round"
|
|
252
|
-
/>
|
|
253
|
-
</svg>
|
|
254
|
-
);
|
|
255
|
-
}
|