@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.
Files changed (174) hide show
  1. package/components/accordion/accordion.tsx +114 -0
  2. package/components/accordion/index.ts +1 -0
  3. package/components/alert/alert.tsx +97 -0
  4. package/components/alert/index.ts +1 -0
  5. package/components/alert-dialog/alert-dialog.tsx +190 -0
  6. package/components/alert-dialog/index.ts +1 -0
  7. package/components/aspect-ratio/aspect-ratio.tsx +23 -0
  8. package/components/aspect-ratio/index.ts +1 -0
  9. package/components/avatar/avatar.tsx +62 -0
  10. package/components/avatar/index.ts +1 -0
  11. package/components/badge/badge.tsx +58 -0
  12. package/components/badge/index.ts +1 -0
  13. package/components/breadcrumb/breadcrumb.tsx +132 -0
  14. package/components/breadcrumb/index.ts +1 -0
  15. package/components/button/button.tsx +77 -0
  16. package/components/button/index.ts +1 -0
  17. package/components/button-group/button-group.tsx +99 -0
  18. package/components/button-group/index.ts +1 -0
  19. package/components/calendar/calendar.tsx +235 -0
  20. package/components/calendar/index.ts +1 -0
  21. package/components/card/card.tsx +107 -0
  22. package/components/card/index.ts +1 -0
  23. package/components/carousel/carousel.tsx +263 -0
  24. package/components/carousel/index.ts +1 -0
  25. package/components/chart/chart.tsx +377 -0
  26. package/components/chart/index.ts +1 -0
  27. package/components/checkbox/checkbox.tsx +41 -0
  28. package/components/checkbox/index.ts +1 -0
  29. package/components/collapsible/collapsible.tsx +44 -0
  30. package/components/collapsible/index.ts +1 -0
  31. package/components/command/command.tsx +201 -0
  32. package/components/command/index.ts +1 -0
  33. package/components/context-menu/context-menu.tsx +270 -0
  34. package/components/context-menu/index.ts +1 -0
  35. package/components/dialog/dialog.tsx +166 -0
  36. package/components/dialog/index.ts +1 -0
  37. package/components/drawer/drawer.tsx +154 -0
  38. package/components/drawer/index.ts +1 -0
  39. package/components/dropdown-menu/dropdown-menu.tsx +276 -0
  40. package/components/dropdown-menu/index.ts +1 -0
  41. package/components/empty/empty.tsx +129 -0
  42. package/components/empty/index.ts +1 -0
  43. package/components/field/field.tsx +272 -0
  44. package/components/field/index.ts +1 -0
  45. package/components/form/form.tsx +197 -0
  46. package/components/form/index.ts +1 -0
  47. package/components/hover-card/hover-card.tsx +57 -0
  48. package/components/hover-card/index.ts +1 -0
  49. package/components/input/index.ts +1 -0
  50. package/components/input/input.tsx +31 -0
  51. package/components/input-group/index.ts +1 -0
  52. package/components/input-group/input-group.tsx +189 -0
  53. package/components/input-otp/index.ts +1 -0
  54. package/components/input-otp/input-otp.tsx +99 -0
  55. package/components/item/index.ts +1 -0
  56. package/components/item/item.tsx +225 -0
  57. package/components/kbd/index.ts +1 -0
  58. package/components/kbd/kbd.tsx +38 -0
  59. package/components/label/index.ts +1 -0
  60. package/components/label/label.tsx +33 -0
  61. package/components/menubar/index.ts +1 -0
  62. package/components/menubar/menubar.tsx +299 -0
  63. package/components/navigation-menu/index.ts +1 -0
  64. package/components/navigation-menu/navigation-menu.tsx +194 -0
  65. package/components/pagination/index.ts +1 -0
  66. package/components/pagination/pagination.tsx +153 -0
  67. package/components/popover/index.ts +1 -0
  68. package/components/popover/popover.tsx +106 -0
  69. package/components/progress/index.ts +1 -0
  70. package/components/progress/progress.tsx +39 -0
  71. package/components/radio-group/index.ts +1 -0
  72. package/components/radio-group/radio-group.tsx +57 -0
  73. package/components/resizable/index.ts +1 -0
  74. package/components/resizable/resizable.tsx +73 -0
  75. package/components/scroll-area/index.ts +1 -0
  76. package/components/scroll-area/scroll-area.tsx +72 -0
  77. package/components/select/index.ts +1 -0
  78. package/components/select/select.tsx +213 -0
  79. package/components/separator/index.ts +1 -0
  80. package/components/separator/separator.tsx +39 -0
  81. package/components/sheet/index.ts +1 -0
  82. package/components/sheet/sheet.tsx +160 -0
  83. package/components/sidebar/index.ts +1 -0
  84. package/components/sidebar/sidebar.tsx +776 -0
  85. package/components/skeleton/index.ts +1 -0
  86. package/components/skeleton/skeleton.tsx +21 -0
  87. package/components/slider/index.ts +1 -0
  88. package/components/slider/slider.tsx +75 -0
  89. package/components/sonner/index.ts +2 -0
  90. package/components/sonner/sonner.tsx +52 -0
  91. package/components/spinner/index.ts +1 -0
  92. package/components/spinner/spinner.tsx +26 -0
  93. package/components/switch/index.ts +1 -0
  94. package/components/switch/switch.tsx +39 -0
  95. package/components/table/index.ts +1 -0
  96. package/components/table/table.tsx +140 -0
  97. package/components/tabs/index.ts +1 -0
  98. package/components/tabs/tabs.tsx +94 -0
  99. package/components/textarea/index.ts +1 -0
  100. package/components/textarea/textarea.tsx +26 -0
  101. package/components/toggle/index.ts +1 -0
  102. package/components/toggle/toggle.tsx +58 -0
  103. package/components/toggle-group/index.ts +1 -0
  104. package/components/toggle-group/toggle-group.tsx +97 -0
  105. package/components/tooltip/index.ts +1 -0
  106. package/components/tooltip/tooltip.tsx +82 -0
  107. package/dist/components/accordion/accordion.d.ts +50 -0
  108. package/dist/components/alert/alert.d.ts +31 -0
  109. package/dist/components/alert-dialog/alert-dialog.d.ts +35 -0
  110. package/dist/components/aspect-ratio/aspect-ratio.d.ts +12 -0
  111. package/dist/components/avatar/avatar.d.ts +11 -0
  112. package/dist/components/badge/badge.d.ts +12 -0
  113. package/dist/components/breadcrumb/breadcrumb.d.ts +23 -0
  114. package/dist/components/button/button.d.ts +15 -0
  115. package/dist/components/button-group/button-group.d.ts +16 -0
  116. package/dist/components/calendar/calendar.d.ts +15 -0
  117. package/dist/components/card/card.d.ts +15 -0
  118. package/dist/components/carousel/carousel.d.ts +24 -0
  119. package/dist/components/chart/chart.d.ts +20 -0
  120. package/dist/components/checkbox/checkbox.d.ts +9 -0
  121. package/dist/components/collapsible/collapsible.d.ts +13 -0
  122. package/dist/components/command/command.d.ts +18 -0
  123. package/dist/components/context-menu/context-menu.d.ts +18 -0
  124. package/dist/components/dialog/dialog.d.ts +25 -0
  125. package/dist/components/drawer/drawer.d.ts +18 -0
  126. package/dist/components/dropdown-menu/dropdown-menu.d.ts +21 -0
  127. package/dist/components/empty/empty.d.ts +25 -0
  128. package/dist/components/field/field.d.ts +26 -0
  129. package/dist/components/form/form.d.ts +30 -1
  130. package/dist/components/hover-card/hover-card.d.ts +13 -0
  131. package/dist/components/input/input.d.ts +10 -0
  132. package/dist/components/input-group/input-group.d.ts +19 -0
  133. package/dist/components/input-otp/input-otp.d.ts +23 -0
  134. package/dist/components/item/item.d.ts +33 -1
  135. package/dist/components/kbd/kbd.d.ts +10 -0
  136. package/dist/components/label/label.d.ts +9 -0
  137. package/dist/components/menubar/menubar.d.ts +25 -0
  138. package/dist/components/navigation-menu/navigation-menu.d.ts +26 -0
  139. package/dist/components/pagination/pagination.d.ts +26 -0
  140. package/dist/components/popover/popover.d.ts +17 -0
  141. package/dist/components/progress/progress.d.ts +10 -0
  142. package/dist/components/radio-group/radio-group.d.ts +12 -0
  143. package/dist/components/resizable/resizable.d.ts +19 -0
  144. package/dist/components/scroll-area/scroll-area.d.ts +14 -0
  145. package/dist/components/select/select.d.ts +25 -0
  146. package/dist/components/separator/separator.d.ts +11 -0
  147. package/dist/components/sheet/sheet.d.ts +23 -0
  148. package/dist/components/sidebar/sidebar.d.ts +50 -0
  149. package/dist/components/skeleton/skeleton.d.ts +8 -0
  150. package/dist/components/slider/slider.d.ts +12 -0
  151. package/dist/components/sonner/sonner.d.ts +14 -0
  152. package/dist/components/spinner/spinner.d.ts +9 -0
  153. package/dist/components/switch/switch.d.ts +8 -0
  154. package/dist/components/table/table.d.ts +26 -0
  155. package/dist/components/tabs/tabs.d.ts +16 -6
  156. package/dist/components/textarea/textarea.d.ts +8 -0
  157. package/dist/components/toggle/toggle.d.ts +13 -0
  158. package/dist/components/toggle-group/toggle-group.d.ts +1 -0
  159. package/dist/components/tooltip/tooltip.d.ts +21 -0
  160. package/dist/design-system.css +1 -1
  161. package/dist/design-system.es.js +3493 -28470
  162. package/dist/design-system.umd.js +4 -257
  163. package/dist/index.d.ts +1 -1
  164. package/foundations/ThemeProvider.tsx +77 -0
  165. package/foundations/color.css +232 -0
  166. package/foundations/palette.css +249 -0
  167. package/foundations/spacing.css +8 -0
  168. package/foundations/typography.css +143 -0
  169. package/hooks/use-mobile.ts +19 -0
  170. package/index.css +173 -0
  171. package/index.ts +339 -0
  172. package/lib/utils.ts +6 -0
  173. package/package.json +40 -19
  174. 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";