sh-ui-cli 0.52.0 → 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.
Files changed (89) hide show
  1. package/data/changelog/versions.json +25 -0
  2. package/data/registry/react/components/_smoke/vanilla-extract.test.ts +33 -0
  3. package/data/registry/react/components/input/styles.css.ts +6 -6
  4. package/data/registry/react/registry.json +35 -852
  5. package/package.json +2 -2
  6. package/src/api.d.ts +3 -4
  7. package/src/constants.js +9 -5
  8. package/src/create/plugins/pluginSchema.js +5 -3
  9. package/src/mcp.mjs +4 -3
  10. package/data/registry/react/components/accordion/index.vanilla-extract.tsx +0 -97
  11. package/data/registry/react/components/accordion/styles.css.ts +0 -131
  12. package/data/registry/react/components/avatar/index.vanilla-extract.tsx +0 -73
  13. package/data/registry/react/components/avatar/styles.css.ts +0 -68
  14. package/data/registry/react/components/badge/index.vanilla-extract.tsx +0 -40
  15. package/data/registry/react/components/badge/styles.css.ts +0 -71
  16. package/data/registry/react/components/breadcrumb/index.vanilla-extract.tsx +0 -152
  17. package/data/registry/react/components/breadcrumb/styles.css.ts +0 -95
  18. package/data/registry/react/components/calendar/index.vanilla-extract.tsx +0 -806
  19. package/data/registry/react/components/calendar/styles.css.ts +0 -250
  20. package/data/registry/react/components/carousel/index.vanilla-extract.tsx +0 -430
  21. package/data/registry/react/components/carousel/styles.css.ts +0 -169
  22. package/data/registry/react/components/checkbox/index.vanilla-extract.tsx +0 -96
  23. package/data/registry/react/components/checkbox/styles.css.ts +0 -74
  24. package/data/registry/react/components/code-editor/index.vanilla-extract.tsx +0 -230
  25. package/data/registry/react/components/code-editor/styles.css.ts +0 -97
  26. package/data/registry/react/components/code-panel/index.vanilla-extract.tsx +0 -191
  27. package/data/registry/react/components/code-panel/styles.css.ts +0 -151
  28. package/data/registry/react/components/color-picker/index.vanilla-extract.tsx +0 -467
  29. package/data/registry/react/components/color-picker/styles.css.ts +0 -169
  30. package/data/registry/react/components/combobox/index.vanilla-extract.tsx +0 -165
  31. package/data/registry/react/components/combobox/styles.css.ts +0 -174
  32. package/data/registry/react/components/context-menu/index.vanilla-extract.tsx +0 -251
  33. package/data/registry/react/components/context-menu/styles.css.ts +0 -167
  34. package/data/registry/react/components/date-picker/index.vanilla-extract.tsx +0 -520
  35. package/data/registry/react/components/date-picker/styles.css.ts +0 -111
  36. package/data/registry/react/components/dialog/index.vanilla-extract.tsx +0 -95
  37. package/data/registry/react/components/dialog/styles.css.ts +0 -140
  38. package/data/registry/react/components/dropdown-menu/index.vanilla-extract.tsx +0 -255
  39. package/data/registry/react/components/dropdown-menu/styles.css.ts +0 -175
  40. package/data/registry/react/components/file-upload/index.vanilla-extract.tsx +0 -487
  41. package/data/registry/react/components/file-upload/styles.css.ts +0 -193
  42. package/data/registry/react/components/form/index.vanilla-extract.tsx +0 -61
  43. package/data/registry/react/components/form/styles.css.ts +0 -56
  44. package/data/registry/react/components/header/index.vanilla-extract.tsx +0 -805
  45. package/data/registry/react/components/header/styles.css.ts +0 -413
  46. package/data/registry/react/components/label/index.vanilla-extract.tsx +0 -52
  47. package/data/registry/react/components/label/styles.css.ts +0 -141
  48. package/data/registry/react/components/markdown-editor/index.vanilla-extract.tsx +0 -119
  49. package/data/registry/react/components/markdown-editor/styles.css.ts +0 -231
  50. package/data/registry/react/components/menubar/index.vanilla-extract.tsx +0 -32
  51. package/data/registry/react/components/menubar/styles.css.ts +0 -53
  52. package/data/registry/react/components/numeric-input/index.vanilla-extract.tsx +0 -148
  53. package/data/registry/react/components/numeric-input/styles.css.ts +0 -65
  54. package/data/registry/react/components/page-toc/index.vanilla-extract.tsx +0 -174
  55. package/data/registry/react/components/page-toc/styles.css.ts +0 -97
  56. package/data/registry/react/components/pagination/index.vanilla-extract.tsx +0 -269
  57. package/data/registry/react/components/pagination/styles.css.ts +0 -113
  58. package/data/registry/react/components/popover/index.vanilla-extract.tsx +0 -113
  59. package/data/registry/react/components/popover/styles.css.ts +0 -78
  60. package/data/registry/react/components/progress/index.vanilla-extract.tsx +0 -54
  61. package/data/registry/react/components/progress/styles.css.ts +0 -53
  62. package/data/registry/react/components/radio/index.vanilla-extract.tsx +0 -65
  63. package/data/registry/react/components/radio/styles.css.ts +0 -79
  64. package/data/registry/react/components/rich-text-editor/index.vanilla-extract.tsx +0 -348
  65. package/data/registry/react/components/rich-text-editor/styles.css.ts +0 -243
  66. package/data/registry/react/components/select/index.vanilla-extract.tsx +0 -234
  67. package/data/registry/react/components/select/styles.css.ts +0 -225
  68. package/data/registry/react/components/separator/index.vanilla-extract.tsx +0 -46
  69. package/data/registry/react/components/separator/styles.css.ts +0 -24
  70. package/data/registry/react/components/sidebar/index.vanilla-extract.tsx +0 -1067
  71. package/data/registry/react/components/sidebar/styles.css.ts +0 -578
  72. package/data/registry/react/components/skeleton/index.vanilla-extract.tsx +0 -22
  73. package/data/registry/react/components/skeleton/styles.css.ts +0 -30
  74. package/data/registry/react/components/slider/index.vanilla-extract.tsx +0 -298
  75. package/data/registry/react/components/slider/styles.css.ts +0 -75
  76. package/data/registry/react/components/spinner/index.vanilla-extract.tsx +0 -38
  77. package/data/registry/react/components/spinner/styles.css.ts +0 -60
  78. package/data/registry/react/components/switch/index.vanilla-extract.tsx +0 -39
  79. package/data/registry/react/components/switch/styles.css.ts +0 -87
  80. package/data/registry/react/components/tabs/index.vanilla-extract.tsx +0 -91
  81. package/data/registry/react/components/tabs/styles.css.ts +0 -145
  82. package/data/registry/react/components/textarea/index.vanilla-extract.tsx +0 -23
  83. package/data/registry/react/components/textarea/styles.css.ts +0 -55
  84. package/data/registry/react/components/toast/index.vanilla-extract.tsx +0 -258
  85. package/data/registry/react/components/toast/styles.css.ts +0 -307
  86. package/data/registry/react/components/toggle/index.vanilla-extract.tsx +0 -131
  87. package/data/registry/react/components/toggle/styles.css.ts +0 -109
  88. package/data/registry/react/components/tooltip/index.vanilla-extract.tsx +0 -83
  89. 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
- }