@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,154 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Drawer as DrawerPrimitive } from "vaul"
5
+
6
+ import { cn } from "@/lib/utils"
7
+
8
+ /**
9
+ * 화면 하단에서 슬라이드업되는 모바일 친화적 드로어 컴포넌트입니다.
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * <Drawer>
14
+ * <DrawerTrigger>열기</DrawerTrigger>
15
+ * <DrawerContent>
16
+ * <DrawerHeader>
17
+ * <DrawerTitle>제목</DrawerTitle>
18
+ * </DrawerHeader>
19
+ * </DrawerContent>
20
+ * </Drawer>
21
+ * ```
22
+ */
23
+ function Drawer({
24
+ ...props
25
+ }: React.ComponentProps<typeof DrawerPrimitive.Root>) {
26
+ return <DrawerPrimitive.Root data-slot="drawer" {...props} />
27
+ }
28
+
29
+ function DrawerTrigger({
30
+ ...props
31
+ }: React.ComponentProps<typeof DrawerPrimitive.Trigger>) {
32
+ return <DrawerPrimitive.Trigger data-slot="drawer-trigger" {...props} />
33
+ }
34
+
35
+ function DrawerPortal({
36
+ ...props
37
+ }: React.ComponentProps<typeof DrawerPrimitive.Portal>) {
38
+ return <DrawerPrimitive.Portal data-slot="drawer-portal" {...props} />
39
+ }
40
+
41
+ function DrawerClose({
42
+ ...props
43
+ }: React.ComponentProps<typeof DrawerPrimitive.Close>) {
44
+ return <DrawerPrimitive.Close data-slot="drawer-close" {...props} />
45
+ }
46
+
47
+ function DrawerOverlay({
48
+ className,
49
+ ...props
50
+ }: React.ComponentProps<typeof DrawerPrimitive.Overlay>) {
51
+ return (
52
+ <DrawerPrimitive.Overlay
53
+ data-slot="drawer-overlay"
54
+ className={cn(
55
+ "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",
56
+ className
57
+ )}
58
+ {...props}
59
+ />
60
+ )
61
+ }
62
+
63
+ function DrawerContent({
64
+ className,
65
+ children,
66
+ ...props
67
+ }: React.ComponentProps<typeof DrawerPrimitive.Content>) {
68
+ return (
69
+ // @ts-expect-error - React 18/19 호환성: DrawerPortal children prop
70
+ <DrawerPortal data-slot="drawer-portal">
71
+ <DrawerOverlay />
72
+ <DrawerPrimitive.Content
73
+ data-slot="drawer-content"
74
+ className={cn(
75
+ "group/drawer-content bg-background fixed z-50 flex h-auto flex-col",
76
+ "data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-lg data-[vaul-drawer-direction=top]:border-b",
77
+ "data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-lg data-[vaul-drawer-direction=bottom]:border-t",
78
+ "data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=right]:sm:max-w-sm",
79
+ "data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=left]:sm:max-w-sm",
80
+ className
81
+ )}
82
+ {...props}
83
+ >
84
+ <div className="bg-muted mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block" />
85
+ {children}
86
+ </DrawerPrimitive.Content>
87
+ </DrawerPortal>
88
+ )
89
+ }
90
+
91
+ /** 드로어 헤더 영역입니다. */
92
+ function DrawerHeader({ className, ...props }: React.ComponentProps<"div">) {
93
+ return (
94
+ <div
95
+ data-slot="drawer-header"
96
+ className={cn(
97
+ "flex flex-col gap-0.5 p-4 group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center group-data-[vaul-drawer-direction=top]/drawer-content:text-center md:gap-1.5 md:text-left",
98
+ className
99
+ )}
100
+ {...props}
101
+ />
102
+ )
103
+ }
104
+
105
+ /** 드로어 푸터 영역입니다. */
106
+ function DrawerFooter({ className, ...props }: React.ComponentProps<"div">) {
107
+ return (
108
+ <div
109
+ data-slot="drawer-footer"
110
+ className={cn("mt-auto flex flex-col gap-2 p-4", className)}
111
+ {...props}
112
+ />
113
+ )
114
+ }
115
+
116
+ /** 드로어 제목 컴포넌트입니다. */
117
+ function DrawerTitle({
118
+ className,
119
+ ...props
120
+ }: React.ComponentProps<typeof DrawerPrimitive.Title>) {
121
+ return (
122
+ <DrawerPrimitive.Title
123
+ data-slot="drawer-title"
124
+ className={cn("text-foreground font-semibold", className)}
125
+ {...props}
126
+ />
127
+ )
128
+ }
129
+
130
+ function DrawerDescription({
131
+ className,
132
+ ...props
133
+ }: React.ComponentProps<typeof DrawerPrimitive.Description>) {
134
+ return (
135
+ <DrawerPrimitive.Description
136
+ data-slot="drawer-description"
137
+ className={cn("text-muted-foreground text-sm", className)}
138
+ {...props}
139
+ />
140
+ )
141
+ }
142
+
143
+ export {
144
+ Drawer,
145
+ DrawerPortal,
146
+ DrawerOverlay,
147
+ DrawerTrigger,
148
+ DrawerClose,
149
+ DrawerContent,
150
+ DrawerHeader,
151
+ DrawerFooter,
152
+ DrawerTitle,
153
+ DrawerDescription,
154
+ }
@@ -0,0 +1 @@
1
+ export * from "./drawer";
@@ -0,0 +1,276 @@
1
+ import * as React from "react"
2
+ import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
3
+ import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
4
+
5
+ import { cn } from "@/lib/utils"
6
+
7
+ /**
8
+ * 트리거를 클릭하면 표시되는 드롭다운 메뉴 컴포넌트입니다.
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * <DropdownMenu>
13
+ * <DropdownMenuTrigger>메뉴</DropdownMenuTrigger>
14
+ * <DropdownMenuContent>
15
+ * <DropdownMenuItem>항목 1</DropdownMenuItem>
16
+ * <DropdownMenuItem>항목 2</DropdownMenuItem>
17
+ * </DropdownMenuContent>
18
+ * </DropdownMenu>
19
+ * ```
20
+ */
21
+ function DropdownMenu({
22
+ ...props
23
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
24
+ return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />
25
+ }
26
+
27
+ function DropdownMenuPortal({
28
+ ...props
29
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
30
+ return (
31
+ <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
32
+ )
33
+ }
34
+
35
+ function DropdownMenuTrigger({
36
+ ...props
37
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
38
+ return (
39
+ <DropdownMenuPrimitive.Trigger
40
+ data-slot="dropdown-menu-trigger"
41
+ {...props}
42
+ />
43
+ )
44
+ }
45
+
46
+ function DropdownMenuContent({
47
+ className,
48
+ sideOffset = 4,
49
+ ...props
50
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
51
+ return (
52
+ <DropdownMenuPrimitive.Portal>
53
+ <DropdownMenuPrimitive.Content
54
+ data-slot="dropdown-menu-content"
55
+ sideOffset={sideOffset}
56
+ className={cn(
57
+ "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-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
58
+ className
59
+ )}
60
+ {...props}
61
+ />
62
+ </DropdownMenuPrimitive.Portal>
63
+ )
64
+ }
65
+
66
+ function DropdownMenuGroup({
67
+ ...props
68
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
69
+ return (
70
+ <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
71
+ )
72
+ }
73
+
74
+ /**
75
+ * 드롭다운 메뉴 아이템입니다.
76
+ * @param props.inset - 왼쪽 여백 추가 여부
77
+ * @param props.variant - 스타일 변형 ('default' | 'destructive')
78
+ */
79
+ function DropdownMenuItem({
80
+ className,
81
+ inset,
82
+ variant = "default",
83
+ ...props
84
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
85
+ inset?: boolean
86
+ variant?: "default" | "destructive"
87
+ }) {
88
+ return (
89
+ <DropdownMenuPrimitive.Item
90
+ data-slot="dropdown-menu-item"
91
+ data-inset={inset}
92
+ data-variant={variant}
93
+ className={cn(
94
+ "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",
95
+ className
96
+ )}
97
+ {...props}
98
+ />
99
+ )
100
+ }
101
+
102
+ /** 체크박스 형태의 메뉴 아이템입니다. */
103
+ function DropdownMenuCheckboxItem({
104
+ className,
105
+ children,
106
+ checked,
107
+ ...props
108
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
109
+ return (
110
+ <DropdownMenuPrimitive.CheckboxItem
111
+ data-slot="dropdown-menu-checkbox-item"
112
+ className={cn(
113
+ "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",
114
+ className
115
+ )}
116
+ checked={checked}
117
+ {...props}
118
+ >
119
+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
120
+ <DropdownMenuPrimitive.ItemIndicator>
121
+ <CheckIcon className="size-4" />
122
+ </DropdownMenuPrimitive.ItemIndicator>
123
+ </span>
124
+ {children}
125
+ </DropdownMenuPrimitive.CheckboxItem>
126
+ )
127
+ }
128
+
129
+ function DropdownMenuRadioGroup({
130
+ ...props
131
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
132
+ return (
133
+ <DropdownMenuPrimitive.RadioGroup
134
+ data-slot="dropdown-menu-radio-group"
135
+ {...props}
136
+ />
137
+ )
138
+ }
139
+
140
+ /** 라디오 버튼 형태의 메뉴 아이템입니다. */
141
+ function DropdownMenuRadioItem({
142
+ className,
143
+ children,
144
+ ...props
145
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
146
+ return (
147
+ <DropdownMenuPrimitive.RadioItem
148
+ data-slot="dropdown-menu-radio-item"
149
+ className={cn(
150
+ "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",
151
+ className
152
+ )}
153
+ {...props}
154
+ >
155
+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
156
+ <DropdownMenuPrimitive.ItemIndicator>
157
+ <CircleIcon className="size-2 fill-current" />
158
+ </DropdownMenuPrimitive.ItemIndicator>
159
+ </span>
160
+ {children}
161
+ </DropdownMenuPrimitive.RadioItem>
162
+ )
163
+ }
164
+
165
+ function DropdownMenuLabel({
166
+ className,
167
+ inset,
168
+ ...props
169
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
170
+ inset?: boolean
171
+ }) {
172
+ return (
173
+ <DropdownMenuPrimitive.Label
174
+ data-slot="dropdown-menu-label"
175
+ data-inset={inset}
176
+ className={cn(
177
+ "px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
178
+ className
179
+ )}
180
+ {...props}
181
+ />
182
+ )
183
+ }
184
+
185
+ function DropdownMenuSeparator({
186
+ className,
187
+ ...props
188
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
189
+ return (
190
+ <DropdownMenuPrimitive.Separator
191
+ data-slot="dropdown-menu-separator"
192
+ className={cn("bg-border -mx-1 my-1 h-px", className)}
193
+ {...props}
194
+ />
195
+ )
196
+ }
197
+
198
+ function DropdownMenuShortcut({
199
+ className,
200
+ ...props
201
+ }: React.ComponentProps<"span">) {
202
+ return (
203
+ <span
204
+ data-slot="dropdown-menu-shortcut"
205
+ className={cn(
206
+ "text-muted-foreground ml-auto text-xs tracking-widest",
207
+ className
208
+ )}
209
+ {...props}
210
+ />
211
+ )
212
+ }
213
+
214
+ function DropdownMenuSub({
215
+ ...props
216
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
217
+ return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />
218
+ }
219
+
220
+ function DropdownMenuSubTrigger({
221
+ className,
222
+ inset,
223
+ children,
224
+ ...props
225
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
226
+ inset?: boolean
227
+ }) {
228
+ return (
229
+ <DropdownMenuPrimitive.SubTrigger
230
+ data-slot="dropdown-menu-sub-trigger"
231
+ data-inset={inset}
232
+ className={cn(
233
+ "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 gap-2 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",
234
+ className
235
+ )}
236
+ {...props}
237
+ >
238
+ {children}
239
+ <ChevronRightIcon className="ml-auto size-4" />
240
+ </DropdownMenuPrimitive.SubTrigger>
241
+ )
242
+ }
243
+
244
+ function DropdownMenuSubContent({
245
+ className,
246
+ ...props
247
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
248
+ return (
249
+ <DropdownMenuPrimitive.SubContent
250
+ data-slot="dropdown-menu-sub-content"
251
+ className={cn(
252
+ "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-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
253
+ className
254
+ )}
255
+ {...props}
256
+ />
257
+ )
258
+ }
259
+
260
+ export {
261
+ DropdownMenu,
262
+ DropdownMenuPortal,
263
+ DropdownMenuTrigger,
264
+ DropdownMenuContent,
265
+ DropdownMenuGroup,
266
+ DropdownMenuLabel,
267
+ DropdownMenuItem,
268
+ DropdownMenuCheckboxItem,
269
+ DropdownMenuRadioGroup,
270
+ DropdownMenuRadioItem,
271
+ DropdownMenuSeparator,
272
+ DropdownMenuShortcut,
273
+ DropdownMenuSub,
274
+ DropdownMenuSubTrigger,
275
+ DropdownMenuSubContent,
276
+ }
@@ -0,0 +1 @@
1
+ export * from "./dropdown-menu";
@@ -0,0 +1,129 @@
1
+ import { cva, type VariantProps } from "class-variance-authority"
2
+
3
+ import { cn } from "@/lib/utils"
4
+
5
+ /**
6
+ * 콘텐츠가 없을 때 빈 상태를 표시하는 컴포넌트입니다.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * <Empty>
11
+ * <EmptyHeader>
12
+ * <EmptyMedia><InboxIcon /></EmptyMedia>
13
+ * <EmptyTitle>항목이 없습니다</EmptyTitle>
14
+ * <EmptyDescription>새 항목을 추가해보세요</EmptyDescription>
15
+ * </EmptyHeader>
16
+ * <EmptyContent>
17
+ * <Button>항목 추가</Button>
18
+ * </EmptyContent>
19
+ * </Empty>
20
+ * ```
21
+ */
22
+ function Empty({ className, ...props }: React.ComponentProps<"div">) {
23
+ return (
24
+ <div
25
+ data-slot="empty"
26
+ className={cn(
27
+ "flex min-w-0 flex-1 flex-col items-center justify-center gap-6 rounded-lg border-dashed p-6 text-center text-balance md:p-12",
28
+ className
29
+ )}
30
+ {...props}
31
+ />
32
+ )
33
+ }
34
+
35
+ /** Empty 컴포넌트의 헤더 영역입니다. */
36
+ function EmptyHeader({ className, ...props }: React.ComponentProps<"div">) {
37
+ return (
38
+ <div
39
+ data-slot="empty-header"
40
+ className={cn(
41
+ "flex max-w-sm flex-col items-center gap-2 text-center",
42
+ className
43
+ )}
44
+ {...props}
45
+ />
46
+ )
47
+ }
48
+
49
+ const emptyMediaVariants = cva(
50
+ "flex shrink-0 items-center justify-center mb-2 [&_svg]:pointer-events-none [&_svg]:shrink-0",
51
+ {
52
+ variants: {
53
+ variant: {
54
+ default: "bg-transparent",
55
+ icon: "bg-muted text-foreground flex size-10 shrink-0 items-center justify-center rounded-lg [&_svg:not([class*='size-'])]:size-6",
56
+ },
57
+ },
58
+ defaultVariants: {
59
+ variant: "default",
60
+ },
61
+ }
62
+ )
63
+
64
+ /**
65
+ * Empty 컴포넌트의 미디어(아이콘/이미지) 영역입니다.
66
+ * @param props.variant - 스타일 변형 ('default' | 'icon')
67
+ */
68
+ function EmptyMedia({
69
+ className,
70
+ variant = "default",
71
+ ...props
72
+ }: React.ComponentProps<"div"> & VariantProps<typeof emptyMediaVariants>) {
73
+ return (
74
+ <div
75
+ data-slot="empty-icon"
76
+ data-variant={variant}
77
+ className={cn(emptyMediaVariants({ variant, className }))}
78
+ {...props}
79
+ />
80
+ )
81
+ }
82
+
83
+ /** Empty 컴포넌트의 제목입니다. */
84
+ function EmptyTitle({ className, ...props }: React.ComponentProps<"div">) {
85
+ return (
86
+ <div
87
+ data-slot="empty-title"
88
+ className={cn("text-lg font-medium tracking-tight", className)}
89
+ {...props}
90
+ />
91
+ )
92
+ }
93
+
94
+ /** Empty 컴포넌트의 설명 텍스트입니다. */
95
+ function EmptyDescription({ className, ...props }: React.ComponentProps<"p">) {
96
+ return (
97
+ <div
98
+ data-slot="empty-description"
99
+ className={cn(
100
+ "text-muted-foreground [&>a:hover]:text-primary text-sm/relaxed [&>a]:underline [&>a]:underline-offset-4",
101
+ className
102
+ )}
103
+ {...props}
104
+ />
105
+ )
106
+ }
107
+
108
+ /** Empty 컴포넌트의 액션 영역입니다. */
109
+ function EmptyContent({ className, ...props }: React.ComponentProps<"div">) {
110
+ return (
111
+ <div
112
+ data-slot="empty-content"
113
+ className={cn(
114
+ "flex w-full max-w-sm min-w-0 flex-col items-center gap-4 text-sm text-balance",
115
+ className
116
+ )}
117
+ {...props}
118
+ />
119
+ )
120
+ }
121
+
122
+ export {
123
+ Empty,
124
+ EmptyHeader,
125
+ EmptyTitle,
126
+ EmptyDescription,
127
+ EmptyContent,
128
+ EmptyMedia,
129
+ }
@@ -0,0 +1 @@
1
+ export * from "./empty";