@mzc-fe/design-system 0.0.5 → 0.0.7-rc.0
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/components/accordion/accordion.tsx +114 -0
- package/components/accordion/index.ts +1 -0
- package/components/alert/alert.tsx +97 -0
- package/components/alert/index.ts +1 -0
- package/components/alert-dialog/alert-dialog.tsx +190 -0
- package/components/alert-dialog/index.ts +1 -0
- package/components/aspect-ratio/aspect-ratio.tsx +23 -0
- package/components/aspect-ratio/index.ts +1 -0
- package/components/avatar/avatar.tsx +62 -0
- package/components/avatar/index.ts +1 -0
- package/components/badge/badge.tsx +58 -0
- package/components/badge/index.ts +1 -0
- package/components/breadcrumb/breadcrumb.tsx +132 -0
- package/components/breadcrumb/index.ts +1 -0
- package/components/button/button.tsx +77 -0
- package/components/button/index.ts +1 -0
- package/components/button-group/button-group.tsx +99 -0
- package/components/button-group/index.ts +1 -0
- package/components/calendar/calendar.tsx +235 -0
- package/components/calendar/index.ts +1 -0
- package/components/card/card.tsx +107 -0
- package/components/card/index.ts +1 -0
- package/components/carousel/carousel.tsx +263 -0
- package/components/carousel/index.ts +1 -0
- package/components/chart/chart.tsx +377 -0
- package/components/chart/index.ts +1 -0
- package/components/checkbox/checkbox.tsx +41 -0
- package/components/checkbox/index.ts +1 -0
- package/components/collapsible/collapsible.tsx +44 -0
- package/components/collapsible/index.ts +1 -0
- package/components/command/command.tsx +201 -0
- package/components/command/index.ts +1 -0
- package/components/context-menu/context-menu.tsx +270 -0
- package/components/context-menu/index.ts +1 -0
- package/components/dialog/dialog.tsx +166 -0
- package/components/dialog/index.ts +1 -0
- package/components/drawer/drawer.tsx +154 -0
- package/components/drawer/index.ts +1 -0
- package/components/dropdown-menu/dropdown-menu.tsx +276 -0
- package/components/dropdown-menu/index.ts +1 -0
- package/components/empty/empty.tsx +129 -0
- package/components/empty/index.ts +1 -0
- package/components/field/field.tsx +272 -0
- package/components/field/index.ts +1 -0
- package/components/form/form.tsx +197 -0
- package/components/form/index.ts +1 -0
- package/components/hover-card/hover-card.tsx +57 -0
- package/components/hover-card/index.ts +1 -0
- package/components/input/index.ts +1 -0
- package/components/input/input.tsx +31 -0
- package/components/input-group/index.ts +1 -0
- package/components/input-group/input-group.tsx +189 -0
- package/components/input-otp/index.ts +1 -0
- package/components/input-otp/input-otp.tsx +99 -0
- package/components/item/index.ts +1 -0
- package/components/item/item.tsx +225 -0
- package/components/kbd/index.ts +1 -0
- package/components/kbd/kbd.tsx +38 -0
- package/components/label/index.ts +1 -0
- package/components/label/label.tsx +33 -0
- package/components/menubar/index.ts +1 -0
- package/components/menubar/menubar.tsx +299 -0
- package/components/navigation-menu/index.ts +1 -0
- package/components/navigation-menu/navigation-menu.tsx +194 -0
- package/components/pagination/index.ts +1 -0
- package/components/pagination/pagination.tsx +153 -0
- package/components/popover/index.ts +1 -0
- package/components/popover/popover.tsx +106 -0
- package/components/progress/index.ts +1 -0
- package/components/progress/progress.tsx +39 -0
- package/components/radio-group/index.ts +1 -0
- package/components/radio-group/radio-group.tsx +57 -0
- package/components/resizable/index.ts +1 -0
- package/components/resizable/resizable.tsx +73 -0
- package/components/scroll-area/index.ts +1 -0
- package/components/scroll-area/scroll-area.tsx +72 -0
- package/components/select/index.ts +1 -0
- package/components/select/select.tsx +213 -0
- package/components/separator/index.ts +1 -0
- package/components/separator/separator.tsx +39 -0
- package/components/sheet/index.ts +1 -0
- package/components/sheet/sheet.tsx +160 -0
- package/components/sidebar/index.ts +1 -0
- package/components/sidebar/sidebar.tsx +776 -0
- package/components/skeleton/index.ts +1 -0
- package/components/skeleton/skeleton.tsx +21 -0
- package/components/slider/index.ts +1 -0
- package/components/slider/slider.tsx +75 -0
- package/components/sonner/index.ts +2 -0
- package/components/sonner/sonner.tsx +52 -0
- package/components/spinner/index.ts +1 -0
- package/components/spinner/spinner.tsx +26 -0
- package/components/switch/index.ts +1 -0
- package/components/switch/switch.tsx +39 -0
- package/components/table/index.ts +1 -0
- package/components/table/table.tsx +140 -0
- package/components/tabs/index.ts +1 -0
- package/components/tabs/tabs.tsx +94 -0
- package/components/textarea/index.ts +1 -0
- package/components/textarea/textarea.tsx +26 -0
- package/components/toggle/index.ts +1 -0
- package/components/toggle/toggle.tsx +58 -0
- package/components/toggle-group/index.ts +1 -0
- package/components/toggle-group/toggle-group.tsx +97 -0
- package/components/tooltip/index.ts +1 -0
- package/components/tooltip/tooltip.tsx +82 -0
- package/dist/components/accordion/accordion.d.ts +50 -0
- package/dist/components/alert/alert.d.ts +31 -0
- package/dist/components/alert-dialog/alert-dialog.d.ts +35 -0
- package/dist/components/aspect-ratio/aspect-ratio.d.ts +12 -0
- package/dist/components/avatar/avatar.d.ts +11 -0
- package/dist/components/badge/badge.d.ts +12 -0
- package/dist/components/breadcrumb/breadcrumb.d.ts +23 -0
- package/dist/components/button/button.d.ts +15 -0
- package/dist/components/button-group/button-group.d.ts +16 -0
- package/dist/components/calendar/calendar.d.ts +15 -0
- package/dist/components/card/card.d.ts +15 -0
- package/dist/components/carousel/carousel.d.ts +24 -0
- package/dist/components/chart/chart.d.ts +20 -0
- package/dist/components/checkbox/checkbox.d.ts +9 -0
- package/dist/components/collapsible/collapsible.d.ts +13 -0
- package/dist/components/command/command.d.ts +18 -0
- package/dist/components/context-menu/context-menu.d.ts +18 -0
- package/dist/components/dialog/dialog.d.ts +25 -0
- package/dist/components/drawer/drawer.d.ts +18 -0
- package/dist/components/dropdown-menu/dropdown-menu.d.ts +21 -0
- package/dist/components/empty/empty.d.ts +25 -0
- package/dist/components/field/field.d.ts +26 -0
- package/dist/components/form/form.d.ts +30 -1
- package/dist/components/hover-card/hover-card.d.ts +13 -0
- package/dist/components/input/input.d.ts +10 -0
- package/dist/components/input-group/input-group.d.ts +19 -0
- package/dist/components/input-otp/input-otp.d.ts +23 -0
- package/dist/components/item/item.d.ts +33 -1
- package/dist/components/kbd/kbd.d.ts +10 -0
- package/dist/components/label/label.d.ts +9 -0
- package/dist/components/menubar/menubar.d.ts +25 -0
- package/dist/components/navigation-menu/navigation-menu.d.ts +26 -0
- package/dist/components/pagination/pagination.d.ts +26 -0
- package/dist/components/popover/popover.d.ts +17 -0
- package/dist/components/progress/progress.d.ts +10 -0
- package/dist/components/radio-group/radio-group.d.ts +12 -0
- package/dist/components/resizable/resizable.d.ts +19 -0
- package/dist/components/scroll-area/scroll-area.d.ts +14 -0
- package/dist/components/select/select.d.ts +25 -0
- package/dist/components/separator/separator.d.ts +11 -0
- package/dist/components/sheet/sheet.d.ts +23 -0
- package/dist/components/sidebar/sidebar.d.ts +50 -0
- package/dist/components/skeleton/skeleton.d.ts +8 -0
- package/dist/components/slider/slider.d.ts +12 -0
- package/dist/components/sonner/sonner.d.ts +14 -0
- package/dist/components/spinner/spinner.d.ts +9 -0
- package/dist/components/switch/switch.d.ts +8 -0
- package/dist/components/table/table.d.ts +26 -0
- package/dist/components/tabs/tabs.d.ts +16 -6
- package/dist/components/textarea/textarea.d.ts +8 -0
- package/dist/components/toggle/toggle.d.ts +13 -0
- package/dist/components/toggle-group/toggle-group.d.ts +1 -0
- package/dist/components/tooltip/tooltip.d.ts +21 -0
- package/dist/design-system.css +1 -1
- package/dist/design-system.es.js +3493 -28470
- package/dist/design-system.umd.js +4 -257
- package/dist/index.d.ts +1 -1
- package/foundations/ThemeProvider.tsx +77 -0
- package/foundations/color.css +232 -0
- package/foundations/palette.css +249 -0
- package/foundations/spacing.css +8 -0
- package/foundations/typography.css +143 -0
- package/hooks/use-mobile.ts +19 -0
- package/index.css +173 -0
- package/index.ts +339 -0
- package/lib/utils.ts +6 -0
- package/package.json +40 -19
- package/README.md +0 -184
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 콘텐츠를 접고 펼 수 있는 컬랩서블 컴포넌트입니다.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```tsx
|
|
8
|
+
* <Collapsible>
|
|
9
|
+
* <CollapsibleTrigger>토글</CollapsibleTrigger>
|
|
10
|
+
* <CollapsibleContent>숨겨진 콘텐츠</CollapsibleContent>
|
|
11
|
+
* </Collapsible>
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
function Collapsible({
|
|
15
|
+
...props
|
|
16
|
+
}: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
|
|
17
|
+
return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** 컬랩서블을 열고 닫는 트리거입니다. */
|
|
21
|
+
function CollapsibleTrigger({
|
|
22
|
+
...props
|
|
23
|
+
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {
|
|
24
|
+
return (
|
|
25
|
+
<CollapsiblePrimitive.CollapsibleTrigger
|
|
26
|
+
data-slot="collapsible-trigger"
|
|
27
|
+
{...props}
|
|
28
|
+
/>
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** 접혔다 펼쳐지는 콘텐츠 영역입니다. */
|
|
33
|
+
function CollapsibleContent({
|
|
34
|
+
...props
|
|
35
|
+
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {
|
|
36
|
+
return (
|
|
37
|
+
<CollapsiblePrimitive.CollapsibleContent
|
|
38
|
+
data-slot="collapsible-content"
|
|
39
|
+
{...props}
|
|
40
|
+
/>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export { Collapsible, CollapsibleTrigger, CollapsibleContent }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./collapsible";
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Command as CommandPrimitive } from "cmdk";
|
|
3
|
+
import { SearchIcon } from "lucide-react";
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
import {
|
|
7
|
+
Dialog,
|
|
8
|
+
DialogContent,
|
|
9
|
+
DialogDescription,
|
|
10
|
+
DialogHeader,
|
|
11
|
+
DialogTitle,
|
|
12
|
+
} from "@/components/dialog";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 키보드 기반의 명령 팔레트/검색 컴포넌트입니다.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```tsx
|
|
19
|
+
* <Command>
|
|
20
|
+
* <CommandInput placeholder="검색..." />
|
|
21
|
+
* <CommandList>
|
|
22
|
+
* <CommandGroup heading="제안">
|
|
23
|
+
* <CommandItem>항목 1</CommandItem>
|
|
24
|
+
* </CommandGroup>
|
|
25
|
+
* </CommandList>
|
|
26
|
+
* </Command>
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
function Command({
|
|
30
|
+
className,
|
|
31
|
+
...props
|
|
32
|
+
}: React.ComponentProps<typeof CommandPrimitive>) {
|
|
33
|
+
return (
|
|
34
|
+
<CommandPrimitive
|
|
35
|
+
data-slot="command"
|
|
36
|
+
className={cn(
|
|
37
|
+
"bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md",
|
|
38
|
+
className
|
|
39
|
+
)}
|
|
40
|
+
{...props}
|
|
41
|
+
/>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** 다이얼로그 형태의 커맨드 컴포넌트입니다. */
|
|
46
|
+
function CommandDialog({
|
|
47
|
+
title = "Command Palette",
|
|
48
|
+
description = "Search for a command to run...",
|
|
49
|
+
children,
|
|
50
|
+
className,
|
|
51
|
+
showCloseButton = true,
|
|
52
|
+
...props
|
|
53
|
+
}: React.ComponentProps<typeof Dialog> & {
|
|
54
|
+
title?: string;
|
|
55
|
+
description?: string;
|
|
56
|
+
className?: string;
|
|
57
|
+
showCloseButton?: boolean;
|
|
58
|
+
}) {
|
|
59
|
+
return (
|
|
60
|
+
<Dialog {...props}>
|
|
61
|
+
<DialogHeader className="sr-only">
|
|
62
|
+
<DialogTitle>{title}</DialogTitle>
|
|
63
|
+
<DialogDescription>{description}</DialogDescription>
|
|
64
|
+
</DialogHeader>
|
|
65
|
+
<DialogContent
|
|
66
|
+
className={cn("overflow-hidden p-0", className)}
|
|
67
|
+
showCloseButton={showCloseButton}
|
|
68
|
+
>
|
|
69
|
+
<Command className="[&_[cmdk-group-heading]]:text-muted-foreground **:data-[slot=command-input-wrapper]:h-12 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
|
|
70
|
+
{/* @ts-expect-error - React 18/19 호환성: ReactNode 타입 불일치 */}
|
|
71
|
+
{children}
|
|
72
|
+
</Command>
|
|
73
|
+
</DialogContent>
|
|
74
|
+
</Dialog>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** 커맨드 검색 입력창입니다. */
|
|
79
|
+
function CommandInput({
|
|
80
|
+
className,
|
|
81
|
+
...props
|
|
82
|
+
}: React.ComponentProps<typeof CommandPrimitive.Input>) {
|
|
83
|
+
return (
|
|
84
|
+
<div
|
|
85
|
+
data-slot="command-input-wrapper"
|
|
86
|
+
className="flex h-9 items-center gap-2 border-b px-3"
|
|
87
|
+
>
|
|
88
|
+
<SearchIcon className="size-4 shrink-0 opacity-50" />
|
|
89
|
+
<CommandPrimitive.Input
|
|
90
|
+
data-slot="command-input"
|
|
91
|
+
className={cn(
|
|
92
|
+
"placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50",
|
|
93
|
+
className
|
|
94
|
+
)}
|
|
95
|
+
{...props}
|
|
96
|
+
/>
|
|
97
|
+
</div>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function CommandList({
|
|
102
|
+
className,
|
|
103
|
+
...props
|
|
104
|
+
}: React.ComponentProps<typeof CommandPrimitive.List>) {
|
|
105
|
+
return (
|
|
106
|
+
<CommandPrimitive.List
|
|
107
|
+
data-slot="command-list"
|
|
108
|
+
className={cn(
|
|
109
|
+
"max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto",
|
|
110
|
+
className
|
|
111
|
+
)}
|
|
112
|
+
{...props}
|
|
113
|
+
/>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function CommandEmpty({
|
|
118
|
+
...props
|
|
119
|
+
}: React.ComponentProps<typeof CommandPrimitive.Empty>) {
|
|
120
|
+
return (
|
|
121
|
+
<CommandPrimitive.Empty
|
|
122
|
+
data-slot="command-empty"
|
|
123
|
+
className="py-6 text-center text-sm"
|
|
124
|
+
{...props}
|
|
125
|
+
/>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function CommandGroup({
|
|
130
|
+
className,
|
|
131
|
+
...props
|
|
132
|
+
}: React.ComponentProps<typeof CommandPrimitive.Group>) {
|
|
133
|
+
return (
|
|
134
|
+
<CommandPrimitive.Group
|
|
135
|
+
data-slot="command-group"
|
|
136
|
+
className={cn(
|
|
137
|
+
"text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium",
|
|
138
|
+
className
|
|
139
|
+
)}
|
|
140
|
+
{...props}
|
|
141
|
+
/>
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function CommandSeparator({
|
|
146
|
+
className,
|
|
147
|
+
...props
|
|
148
|
+
}: React.ComponentProps<typeof CommandPrimitive.Separator>) {
|
|
149
|
+
return (
|
|
150
|
+
<CommandPrimitive.Separator
|
|
151
|
+
data-slot="command-separator"
|
|
152
|
+
className={cn("bg-border -mx-1 h-px", className)}
|
|
153
|
+
{...props}
|
|
154
|
+
/>
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** 선택 가능한 커맨드 아이템입니다. */
|
|
159
|
+
function CommandItem({
|
|
160
|
+
className,
|
|
161
|
+
...props
|
|
162
|
+
}: React.ComponentProps<typeof CommandPrimitive.Item>) {
|
|
163
|
+
return (
|
|
164
|
+
<CommandPrimitive.Item
|
|
165
|
+
data-slot="command-item"
|
|
166
|
+
className={cn(
|
|
167
|
+
"data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
168
|
+
className
|
|
169
|
+
)}
|
|
170
|
+
{...props}
|
|
171
|
+
/>
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function CommandShortcut({
|
|
176
|
+
className,
|
|
177
|
+
...props
|
|
178
|
+
}: React.ComponentProps<"span">) {
|
|
179
|
+
return (
|
|
180
|
+
<span
|
|
181
|
+
data-slot="command-shortcut"
|
|
182
|
+
className={cn(
|
|
183
|
+
"text-muted-foreground ml-auto text-xs tracking-widest",
|
|
184
|
+
className
|
|
185
|
+
)}
|
|
186
|
+
{...props}
|
|
187
|
+
/>
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export {
|
|
192
|
+
Command,
|
|
193
|
+
CommandDialog,
|
|
194
|
+
CommandInput,
|
|
195
|
+
CommandList,
|
|
196
|
+
CommandEmpty,
|
|
197
|
+
CommandGroup,
|
|
198
|
+
CommandItem,
|
|
199
|
+
CommandShortcut,
|
|
200
|
+
CommandSeparator,
|
|
201
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./command";
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import * as ContextMenuPrimitive from "@radix-ui/react-context-menu"
|
|
5
|
+
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
|
|
6
|
+
|
|
7
|
+
import { cn } from "@/lib/utils"
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 우클릭 시 표시되는 컨텍스트 메뉴 컴포넌트입니다.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```tsx
|
|
14
|
+
* <ContextMenu>
|
|
15
|
+
* <ContextMenuTrigger>우클릭하세요</ContextMenuTrigger>
|
|
16
|
+
* <ContextMenuContent>
|
|
17
|
+
* <ContextMenuItem>복사</ContextMenuItem>
|
|
18
|
+
* <ContextMenuItem>붙여넣기</ContextMenuItem>
|
|
19
|
+
* </ContextMenuContent>
|
|
20
|
+
* </ContextMenu>
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
function ContextMenu({
|
|
24
|
+
...props
|
|
25
|
+
}: React.ComponentProps<typeof ContextMenuPrimitive.Root>) {
|
|
26
|
+
return <ContextMenuPrimitive.Root data-slot="context-menu" {...props} />
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function ContextMenuTrigger({
|
|
30
|
+
...props
|
|
31
|
+
}: React.ComponentProps<typeof ContextMenuPrimitive.Trigger>) {
|
|
32
|
+
return (
|
|
33
|
+
<ContextMenuPrimitive.Trigger data-slot="context-menu-trigger" {...props} />
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function ContextMenuGroup({
|
|
38
|
+
...props
|
|
39
|
+
}: React.ComponentProps<typeof ContextMenuPrimitive.Group>) {
|
|
40
|
+
return (
|
|
41
|
+
<ContextMenuPrimitive.Group data-slot="context-menu-group" {...props} />
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function ContextMenuPortal({
|
|
46
|
+
...props
|
|
47
|
+
}: React.ComponentProps<typeof ContextMenuPrimitive.Portal>) {
|
|
48
|
+
return (
|
|
49
|
+
<ContextMenuPrimitive.Portal data-slot="context-menu-portal" {...props} />
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function ContextMenuSub({
|
|
54
|
+
...props
|
|
55
|
+
}: React.ComponentProps<typeof ContextMenuPrimitive.Sub>) {
|
|
56
|
+
return <ContextMenuPrimitive.Sub data-slot="context-menu-sub" {...props} />
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function ContextMenuRadioGroup({
|
|
60
|
+
...props
|
|
61
|
+
}: React.ComponentProps<typeof ContextMenuPrimitive.RadioGroup>) {
|
|
62
|
+
return (
|
|
63
|
+
<ContextMenuPrimitive.RadioGroup
|
|
64
|
+
data-slot="context-menu-radio-group"
|
|
65
|
+
{...props}
|
|
66
|
+
/>
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function ContextMenuSubTrigger({
|
|
71
|
+
className,
|
|
72
|
+
inset,
|
|
73
|
+
children,
|
|
74
|
+
...props
|
|
75
|
+
}: React.ComponentProps<typeof ContextMenuPrimitive.SubTrigger> & {
|
|
76
|
+
inset?: boolean
|
|
77
|
+
}) {
|
|
78
|
+
return (
|
|
79
|
+
<ContextMenuPrimitive.SubTrigger
|
|
80
|
+
data-slot="context-menu-sub-trigger"
|
|
81
|
+
data-inset={inset}
|
|
82
|
+
className={cn(
|
|
83
|
+
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
84
|
+
className
|
|
85
|
+
)}
|
|
86
|
+
{...props}
|
|
87
|
+
>
|
|
88
|
+
{children}
|
|
89
|
+
<ChevronRightIcon className="ml-auto" />
|
|
90
|
+
</ContextMenuPrimitive.SubTrigger>
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function ContextMenuSubContent({
|
|
95
|
+
className,
|
|
96
|
+
...props
|
|
97
|
+
}: React.ComponentProps<typeof ContextMenuPrimitive.SubContent>) {
|
|
98
|
+
return (
|
|
99
|
+
<ContextMenuPrimitive.SubContent
|
|
100
|
+
data-slot="context-menu-sub-content"
|
|
101
|
+
className={cn(
|
|
102
|
+
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
|
|
103
|
+
className
|
|
104
|
+
)}
|
|
105
|
+
{...props}
|
|
106
|
+
/>
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function ContextMenuContent({
|
|
111
|
+
className,
|
|
112
|
+
...props
|
|
113
|
+
}: React.ComponentProps<typeof ContextMenuPrimitive.Content>) {
|
|
114
|
+
return (
|
|
115
|
+
<ContextMenuPrimitive.Portal>
|
|
116
|
+
<ContextMenuPrimitive.Content
|
|
117
|
+
data-slot="context-menu-content"
|
|
118
|
+
className={cn(
|
|
119
|
+
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-context-menu-content-available-height) min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
|
|
120
|
+
className
|
|
121
|
+
)}
|
|
122
|
+
{...props}
|
|
123
|
+
/>
|
|
124
|
+
</ContextMenuPrimitive.Portal>
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 컨텍스트 메뉴 아이템입니다.
|
|
130
|
+
* @param props.variant - 스타일 변형 ('default' | 'destructive')
|
|
131
|
+
*/
|
|
132
|
+
function ContextMenuItem({
|
|
133
|
+
className,
|
|
134
|
+
inset,
|
|
135
|
+
variant = "default",
|
|
136
|
+
...props
|
|
137
|
+
}: React.ComponentProps<typeof ContextMenuPrimitive.Item> & {
|
|
138
|
+
inset?: boolean
|
|
139
|
+
variant?: "default" | "destructive"
|
|
140
|
+
}) {
|
|
141
|
+
return (
|
|
142
|
+
<ContextMenuPrimitive.Item
|
|
143
|
+
data-slot="context-menu-item"
|
|
144
|
+
data-inset={inset}
|
|
145
|
+
data-variant={variant}
|
|
146
|
+
className={cn(
|
|
147
|
+
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
148
|
+
className
|
|
149
|
+
)}
|
|
150
|
+
{...props}
|
|
151
|
+
/>
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function ContextMenuCheckboxItem({
|
|
156
|
+
className,
|
|
157
|
+
children,
|
|
158
|
+
checked,
|
|
159
|
+
...props
|
|
160
|
+
}: React.ComponentProps<typeof ContextMenuPrimitive.CheckboxItem>) {
|
|
161
|
+
return (
|
|
162
|
+
<ContextMenuPrimitive.CheckboxItem
|
|
163
|
+
data-slot="context-menu-checkbox-item"
|
|
164
|
+
className={cn(
|
|
165
|
+
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
166
|
+
className
|
|
167
|
+
)}
|
|
168
|
+
checked={checked}
|
|
169
|
+
{...props}
|
|
170
|
+
>
|
|
171
|
+
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
|
172
|
+
<ContextMenuPrimitive.ItemIndicator>
|
|
173
|
+
<CheckIcon className="size-4" />
|
|
174
|
+
</ContextMenuPrimitive.ItemIndicator>
|
|
175
|
+
</span>
|
|
176
|
+
{children}
|
|
177
|
+
</ContextMenuPrimitive.CheckboxItem>
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function ContextMenuRadioItem({
|
|
182
|
+
className,
|
|
183
|
+
children,
|
|
184
|
+
...props
|
|
185
|
+
}: React.ComponentProps<typeof ContextMenuPrimitive.RadioItem>) {
|
|
186
|
+
return (
|
|
187
|
+
<ContextMenuPrimitive.RadioItem
|
|
188
|
+
data-slot="context-menu-radio-item"
|
|
189
|
+
className={cn(
|
|
190
|
+
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
191
|
+
className
|
|
192
|
+
)}
|
|
193
|
+
{...props}
|
|
194
|
+
>
|
|
195
|
+
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
|
196
|
+
<ContextMenuPrimitive.ItemIndicator>
|
|
197
|
+
<CircleIcon className="size-2 fill-current" />
|
|
198
|
+
</ContextMenuPrimitive.ItemIndicator>
|
|
199
|
+
</span>
|
|
200
|
+
{children}
|
|
201
|
+
</ContextMenuPrimitive.RadioItem>
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function ContextMenuLabel({
|
|
206
|
+
className,
|
|
207
|
+
inset,
|
|
208
|
+
...props
|
|
209
|
+
}: React.ComponentProps<typeof ContextMenuPrimitive.Label> & {
|
|
210
|
+
inset?: boolean
|
|
211
|
+
}) {
|
|
212
|
+
return (
|
|
213
|
+
<ContextMenuPrimitive.Label
|
|
214
|
+
data-slot="context-menu-label"
|
|
215
|
+
data-inset={inset}
|
|
216
|
+
className={cn(
|
|
217
|
+
"text-foreground px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
|
|
218
|
+
className
|
|
219
|
+
)}
|
|
220
|
+
{...props}
|
|
221
|
+
/>
|
|
222
|
+
)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function ContextMenuSeparator({
|
|
226
|
+
className,
|
|
227
|
+
...props
|
|
228
|
+
}: React.ComponentProps<typeof ContextMenuPrimitive.Separator>) {
|
|
229
|
+
return (
|
|
230
|
+
<ContextMenuPrimitive.Separator
|
|
231
|
+
data-slot="context-menu-separator"
|
|
232
|
+
className={cn("bg-border -mx-1 my-1 h-px", className)}
|
|
233
|
+
{...props}
|
|
234
|
+
/>
|
|
235
|
+
)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function ContextMenuShortcut({
|
|
239
|
+
className,
|
|
240
|
+
...props
|
|
241
|
+
}: React.ComponentProps<"span">) {
|
|
242
|
+
return (
|
|
243
|
+
<span
|
|
244
|
+
data-slot="context-menu-shortcut"
|
|
245
|
+
className={cn(
|
|
246
|
+
"text-muted-foreground ml-auto text-xs tracking-widest",
|
|
247
|
+
className
|
|
248
|
+
)}
|
|
249
|
+
{...props}
|
|
250
|
+
/>
|
|
251
|
+
)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export {
|
|
255
|
+
ContextMenu,
|
|
256
|
+
ContextMenuTrigger,
|
|
257
|
+
ContextMenuContent,
|
|
258
|
+
ContextMenuItem,
|
|
259
|
+
ContextMenuCheckboxItem,
|
|
260
|
+
ContextMenuRadioItem,
|
|
261
|
+
ContextMenuLabel,
|
|
262
|
+
ContextMenuSeparator,
|
|
263
|
+
ContextMenuShortcut,
|
|
264
|
+
ContextMenuGroup,
|
|
265
|
+
ContextMenuPortal,
|
|
266
|
+
ContextMenuSub,
|
|
267
|
+
ContextMenuSubContent,
|
|
268
|
+
ContextMenuSubTrigger,
|
|
269
|
+
ContextMenuRadioGroup,
|
|
270
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./context-menu";
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import * as DialogPrimitive from "@radix-ui/react-dialog"
|
|
3
|
+
import { XIcon } from "lucide-react"
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 사용자의 주의를 끌거나 중요한 정보를 표시하는 모달 다이얼로그 컴포넌트입니다.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* <Dialog>
|
|
13
|
+
* <DialogTrigger>열기</DialogTrigger>
|
|
14
|
+
* <DialogContent>
|
|
15
|
+
* <DialogHeader>
|
|
16
|
+
* <DialogTitle>제목</DialogTitle>
|
|
17
|
+
* <DialogDescription>설명</DialogDescription>
|
|
18
|
+
* </DialogHeader>
|
|
19
|
+
* </DialogContent>
|
|
20
|
+
* </Dialog>
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
function Dialog({
|
|
24
|
+
...props
|
|
25
|
+
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
|
|
26
|
+
return <DialogPrimitive.Root data-slot="dialog" {...props} />
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** 다이얼로그를 열기 위한 트리거 요소입니다. */
|
|
30
|
+
function DialogTrigger({
|
|
31
|
+
...props
|
|
32
|
+
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
|
|
33
|
+
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function DialogPortal({
|
|
37
|
+
...props
|
|
38
|
+
}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
|
|
39
|
+
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function DialogClose({
|
|
43
|
+
...props
|
|
44
|
+
}: React.ComponentProps<typeof DialogPrimitive.Close>) {
|
|
45
|
+
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function DialogOverlay({
|
|
49
|
+
className,
|
|
50
|
+
...props
|
|
51
|
+
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
|
|
52
|
+
return (
|
|
53
|
+
<DialogPrimitive.Overlay
|
|
54
|
+
data-slot="dialog-overlay"
|
|
55
|
+
className={cn(
|
|
56
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
|
57
|
+
className
|
|
58
|
+
)}
|
|
59
|
+
{...props}
|
|
60
|
+
/>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 다이얼로그의 메인 콘텐츠 영역입니다.
|
|
66
|
+
* @param props.showCloseButton - 닫기 버튼 표시 여부 (기본값: true)
|
|
67
|
+
*/
|
|
68
|
+
function DialogContent({
|
|
69
|
+
className,
|
|
70
|
+
children,
|
|
71
|
+
showCloseButton = true,
|
|
72
|
+
...props
|
|
73
|
+
}: React.ComponentProps<typeof DialogPrimitive.Content> & {
|
|
74
|
+
showCloseButton?: boolean
|
|
75
|
+
}) {
|
|
76
|
+
return (
|
|
77
|
+
<DialogPortal data-slot="dialog-portal">
|
|
78
|
+
<DialogOverlay />
|
|
79
|
+
<DialogPrimitive.Content
|
|
80
|
+
data-slot="dialog-content"
|
|
81
|
+
className={cn(
|
|
82
|
+
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 outline-none sm:max-w-lg",
|
|
83
|
+
className
|
|
84
|
+
)}
|
|
85
|
+
{...props}
|
|
86
|
+
>
|
|
87
|
+
{children}
|
|
88
|
+
{showCloseButton && (
|
|
89
|
+
<DialogPrimitive.Close
|
|
90
|
+
data-slot="dialog-close"
|
|
91
|
+
className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
|
|
92
|
+
>
|
|
93
|
+
<XIcon />
|
|
94
|
+
<span className="sr-only">Close</span>
|
|
95
|
+
</DialogPrimitive.Close>
|
|
96
|
+
)}
|
|
97
|
+
</DialogPrimitive.Content>
|
|
98
|
+
</DialogPortal>
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** 다이얼로그 헤더 영역으로, 제목과 설명을 포함합니다. */
|
|
103
|
+
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
104
|
+
return (
|
|
105
|
+
<div
|
|
106
|
+
data-slot="dialog-header"
|
|
107
|
+
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
|
|
108
|
+
{...props}
|
|
109
|
+
/>
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** 다이얼로그 푸터 영역으로, 액션 버튼들을 포함합니다. */
|
|
114
|
+
function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|
115
|
+
return (
|
|
116
|
+
<div
|
|
117
|
+
data-slot="dialog-footer"
|
|
118
|
+
className={cn(
|
|
119
|
+
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
|
120
|
+
className
|
|
121
|
+
)}
|
|
122
|
+
{...props}
|
|
123
|
+
/>
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** 다이얼로그 제목 컴포넌트입니다. */
|
|
128
|
+
function DialogTitle({
|
|
129
|
+
className,
|
|
130
|
+
...props
|
|
131
|
+
}: React.ComponentProps<typeof DialogPrimitive.Title>) {
|
|
132
|
+
return (
|
|
133
|
+
<DialogPrimitive.Title
|
|
134
|
+
data-slot="dialog-title"
|
|
135
|
+
className={cn("text-lg leading-none font-semibold", className)}
|
|
136
|
+
{...props}
|
|
137
|
+
/>
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** 다이얼로그 설명 컴포넌트입니다. */
|
|
142
|
+
function DialogDescription({
|
|
143
|
+
className,
|
|
144
|
+
...props
|
|
145
|
+
}: React.ComponentProps<typeof DialogPrimitive.Description>) {
|
|
146
|
+
return (
|
|
147
|
+
<DialogPrimitive.Description
|
|
148
|
+
data-slot="dialog-description"
|
|
149
|
+
className={cn("text-muted-foreground text-sm", className)}
|
|
150
|
+
{...props}
|
|
151
|
+
/>
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export {
|
|
156
|
+
Dialog,
|
|
157
|
+
DialogClose,
|
|
158
|
+
DialogContent,
|
|
159
|
+
DialogDescription,
|
|
160
|
+
DialogFooter,
|
|
161
|
+
DialogHeader,
|
|
162
|
+
DialogOverlay,
|
|
163
|
+
DialogPortal,
|
|
164
|
+
DialogTitle,
|
|
165
|
+
DialogTrigger,
|
|
166
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./dialog";
|