@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,299 @@
1
+ import * as React from "react"
2
+ import * as MenubarPrimitive from "@radix-ui/react-menubar"
3
+ import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
4
+
5
+ import { cn } from "@/lib/utils"
6
+
7
+ /**
8
+ * 수평 메뉴바 컴포넌트입니다.
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * <Menubar>
13
+ * <MenubarMenu>
14
+ * <MenubarTrigger>파일</MenubarTrigger>
15
+ * <MenubarContent>
16
+ * <MenubarItem>새로 만들기</MenubarItem>
17
+ * <MenubarItem>열기</MenubarItem>
18
+ * </MenubarContent>
19
+ * </MenubarMenu>
20
+ * </Menubar>
21
+ * ```
22
+ */
23
+ function Menubar({
24
+ className,
25
+ ...props
26
+ }: React.ComponentProps<typeof MenubarPrimitive.Root>) {
27
+ return (
28
+ <MenubarPrimitive.Root
29
+ data-slot="menubar"
30
+ className={cn(
31
+ "bg-background flex h-9 items-center gap-1 rounded-md border p-1 shadow-xs",
32
+ className
33
+ )}
34
+ {...props}
35
+ />
36
+ )
37
+ }
38
+
39
+ /** 개별 메뉴 컨테이너입니다. */
40
+ function MenubarMenu({
41
+ ...props
42
+ }: React.ComponentProps<typeof MenubarPrimitive.Menu>) {
43
+ return <MenubarPrimitive.Menu data-slot="menubar-menu" {...props} />
44
+ }
45
+
46
+ function MenubarGroup({
47
+ ...props
48
+ }: React.ComponentProps<typeof MenubarPrimitive.Group>) {
49
+ return <MenubarPrimitive.Group data-slot="menubar-group" {...props} />
50
+ }
51
+
52
+ function MenubarPortal({
53
+ ...props
54
+ }: React.ComponentProps<typeof MenubarPrimitive.Portal>) {
55
+ return <MenubarPrimitive.Portal data-slot="menubar-portal" {...props} />
56
+ }
57
+
58
+ function MenubarRadioGroup({
59
+ ...props
60
+ }: React.ComponentProps<typeof MenubarPrimitive.RadioGroup>) {
61
+ return (
62
+ <MenubarPrimitive.RadioGroup data-slot="menubar-radio-group" {...props} />
63
+ )
64
+ }
65
+
66
+ /** 메뉴를 여는 트리거 버튼입니다. */
67
+ function MenubarTrigger({
68
+ className,
69
+ ...props
70
+ }: React.ComponentProps<typeof MenubarPrimitive.Trigger>) {
71
+ return (
72
+ <MenubarPrimitive.Trigger
73
+ data-slot="menubar-trigger"
74
+ className={cn(
75
+ "focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex items-center rounded-sm px-2 py-1 text-sm font-medium outline-hidden select-none",
76
+ className
77
+ )}
78
+ {...props}
79
+ />
80
+ )
81
+ }
82
+
83
+ /** 메뉴 콘텐츠 드롭다운입니다. */
84
+ function MenubarContent({
85
+ className,
86
+ align = "start",
87
+ alignOffset = -4,
88
+ sideOffset = 8,
89
+ ...props
90
+ }: React.ComponentProps<typeof MenubarPrimitive.Content>) {
91
+ return (
92
+ <MenubarPortal>
93
+ <MenubarPrimitive.Content
94
+ data-slot="menubar-content"
95
+ align={align}
96
+ alignOffset={alignOffset}
97
+ sideOffset={sideOffset}
98
+ className={cn(
99
+ "bg-popover text-popover-foreground data-[state=open]:animate-in 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-[12rem] origin-(--radix-menubar-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-md",
100
+ className
101
+ )}
102
+ {...props}
103
+ />
104
+ </MenubarPortal>
105
+ )
106
+ }
107
+
108
+ /** 메뉴 아이템 컴포넌트입니다. */
109
+ function MenubarItem({
110
+ className,
111
+ inset,
112
+ variant = "default",
113
+ ...props
114
+ }: React.ComponentProps<typeof MenubarPrimitive.Item> & {
115
+ inset?: boolean
116
+ variant?: "default" | "destructive"
117
+ }) {
118
+ return (
119
+ <MenubarPrimitive.Item
120
+ data-slot="menubar-item"
121
+ data-inset={inset}
122
+ data-variant={variant}
123
+ className={cn(
124
+ "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",
125
+ className
126
+ )}
127
+ {...props}
128
+ />
129
+ )
130
+ }
131
+
132
+ /** 체크박스 형태의 메뉴 아이템입니다. */
133
+ function MenubarCheckboxItem({
134
+ className,
135
+ children,
136
+ checked,
137
+ ...props
138
+ }: React.ComponentProps<typeof MenubarPrimitive.CheckboxItem>) {
139
+ return (
140
+ <MenubarPrimitive.CheckboxItem
141
+ data-slot="menubar-checkbox-item"
142
+ className={cn(
143
+ "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-xs 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",
144
+ className
145
+ )}
146
+ checked={checked}
147
+ {...props}
148
+ >
149
+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
150
+ <MenubarPrimitive.ItemIndicator>
151
+ <CheckIcon className="size-4" />
152
+ </MenubarPrimitive.ItemIndicator>
153
+ </span>
154
+ {children}
155
+ </MenubarPrimitive.CheckboxItem>
156
+ )
157
+ }
158
+
159
+ /** 라디오 형태의 메뉴 아이템입니다. */
160
+ function MenubarRadioItem({
161
+ className,
162
+ children,
163
+ ...props
164
+ }: React.ComponentProps<typeof MenubarPrimitive.RadioItem>) {
165
+ return (
166
+ <MenubarPrimitive.RadioItem
167
+ data-slot="menubar-radio-item"
168
+ className={cn(
169
+ "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-xs 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",
170
+ className
171
+ )}
172
+ {...props}
173
+ >
174
+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
175
+ <MenubarPrimitive.ItemIndicator>
176
+ <CircleIcon className="size-2 fill-current" />
177
+ </MenubarPrimitive.ItemIndicator>
178
+ </span>
179
+ {children}
180
+ </MenubarPrimitive.RadioItem>
181
+ )
182
+ }
183
+
184
+ /** 메뉴 섹션의 레이블입니다. */
185
+ function MenubarLabel({
186
+ className,
187
+ inset,
188
+ ...props
189
+ }: React.ComponentProps<typeof MenubarPrimitive.Label> & {
190
+ inset?: boolean
191
+ }) {
192
+ return (
193
+ <MenubarPrimitive.Label
194
+ data-slot="menubar-label"
195
+ data-inset={inset}
196
+ className={cn(
197
+ "px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
198
+ className
199
+ )}
200
+ {...props}
201
+ />
202
+ )
203
+ }
204
+
205
+ /** 메뉴 아이템 사이의 구분선입니다. */
206
+ function MenubarSeparator({
207
+ className,
208
+ ...props
209
+ }: React.ComponentProps<typeof MenubarPrimitive.Separator>) {
210
+ return (
211
+ <MenubarPrimitive.Separator
212
+ data-slot="menubar-separator"
213
+ className={cn("bg-border -mx-1 my-1 h-px", className)}
214
+ {...props}
215
+ />
216
+ )
217
+ }
218
+
219
+ /** 메뉴 아이템의 단축키 표시입니다. */
220
+ function MenubarShortcut({
221
+ className,
222
+ ...props
223
+ }: React.ComponentProps<"span">) {
224
+ return (
225
+ <span
226
+ data-slot="menubar-shortcut"
227
+ className={cn(
228
+ "text-muted-foreground ml-auto text-xs tracking-widest",
229
+ className
230
+ )}
231
+ {...props}
232
+ />
233
+ )
234
+ }
235
+
236
+ function MenubarSub({
237
+ ...props
238
+ }: React.ComponentProps<typeof MenubarPrimitive.Sub>) {
239
+ return <MenubarPrimitive.Sub data-slot="menubar-sub" {...props} />
240
+ }
241
+
242
+ function MenubarSubTrigger({
243
+ className,
244
+ inset,
245
+ children,
246
+ ...props
247
+ }: React.ComponentProps<typeof MenubarPrimitive.SubTrigger> & {
248
+ inset?: boolean
249
+ }) {
250
+ return (
251
+ <MenubarPrimitive.SubTrigger
252
+ data-slot="menubar-sub-trigger"
253
+ data-inset={inset}
254
+ className={cn(
255
+ "focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-none select-none data-[inset]:pl-8",
256
+ className
257
+ )}
258
+ {...props}
259
+ >
260
+ {children}
261
+ <ChevronRightIcon className="ml-auto h-4 w-4" />
262
+ </MenubarPrimitive.SubTrigger>
263
+ )
264
+ }
265
+
266
+ function MenubarSubContent({
267
+ className,
268
+ ...props
269
+ }: React.ComponentProps<typeof MenubarPrimitive.SubContent>) {
270
+ return (
271
+ <MenubarPrimitive.SubContent
272
+ data-slot="menubar-sub-content"
273
+ className={cn(
274
+ "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-menubar-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
275
+ className
276
+ )}
277
+ {...props}
278
+ />
279
+ )
280
+ }
281
+
282
+ export {
283
+ Menubar,
284
+ MenubarPortal,
285
+ MenubarMenu,
286
+ MenubarTrigger,
287
+ MenubarContent,
288
+ MenubarGroup,
289
+ MenubarSeparator,
290
+ MenubarLabel,
291
+ MenubarItem,
292
+ MenubarShortcut,
293
+ MenubarCheckboxItem,
294
+ MenubarRadioGroup,
295
+ MenubarRadioItem,
296
+ MenubarSub,
297
+ MenubarSubTrigger,
298
+ MenubarSubContent,
299
+ }
@@ -0,0 +1 @@
1
+ export * from "./navigation-menu";
@@ -0,0 +1,194 @@
1
+ import * as React from "react"
2
+ import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu"
3
+ import { cva } from "class-variance-authority"
4
+ import { ChevronDownIcon } from "lucide-react"
5
+
6
+ import { cn } from "@/lib/utils"
7
+
8
+ /**
9
+ * 수평 네비게이션 메뉴 컴포넌트입니다.
10
+ *
11
+ * @param props.viewport - 뷰포트 사용 여부
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * <NavigationMenu>
16
+ * <NavigationMenuList>
17
+ * <NavigationMenuItem>
18
+ * <NavigationMenuTrigger>시작하기</NavigationMenuTrigger>
19
+ * <NavigationMenuContent>
20
+ * <NavigationMenuLink>소개</NavigationMenuLink>
21
+ * </NavigationMenuContent>
22
+ * </NavigationMenuItem>
23
+ * </NavigationMenuList>
24
+ * </NavigationMenu>
25
+ * ```
26
+ */
27
+ function NavigationMenu({
28
+ className,
29
+ children,
30
+ viewport = true,
31
+ ...props
32
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Root> & {
33
+ viewport?: boolean
34
+ }) {
35
+ return (
36
+ <NavigationMenuPrimitive.Root
37
+ data-slot="navigation-menu"
38
+ data-viewport={viewport}
39
+ className={cn(
40
+ "group/navigation-menu relative flex max-w-max flex-1 items-center justify-center",
41
+ className
42
+ )}
43
+ {...props}
44
+ >
45
+ {children}
46
+ {viewport && <NavigationMenuViewport />}
47
+ </NavigationMenuPrimitive.Root>
48
+ )
49
+ }
50
+
51
+ /** 네비게이션 메뉴 아이템들의 목록입니다. */
52
+ function NavigationMenuList({
53
+ className,
54
+ ...props
55
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.List>) {
56
+ return (
57
+ <NavigationMenuPrimitive.List
58
+ data-slot="navigation-menu-list"
59
+ className={cn(
60
+ "group flex flex-1 list-none items-center justify-center gap-1",
61
+ className
62
+ )}
63
+ {...props}
64
+ />
65
+ )
66
+ }
67
+
68
+ /** 개별 네비게이션 메뉴 아이템입니다. */
69
+ function NavigationMenuItem({
70
+ className,
71
+ ...props
72
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Item>) {
73
+ return (
74
+ <NavigationMenuPrimitive.Item
75
+ data-slot="navigation-menu-item"
76
+ className={cn("relative", className)}
77
+ {...props}
78
+ />
79
+ )
80
+ }
81
+
82
+ const navigationMenuTriggerStyle = cva(
83
+ "group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=open]:hover:bg-accent data-[state=open]:text-accent-foreground data-[state=open]:focus:bg-accent data-[state=open]:bg-accent/50 focus-visible:ring-ring/50 outline-none transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1"
84
+ )
85
+
86
+ /** 드롭다운을 여는 트리거 버튼입니다. */
87
+ function NavigationMenuTrigger({
88
+ className,
89
+ children,
90
+ ...props
91
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Trigger>) {
92
+ return (
93
+ <NavigationMenuPrimitive.Trigger
94
+ data-slot="navigation-menu-trigger"
95
+ className={cn(navigationMenuTriggerStyle(), "group", className)}
96
+ {...props}
97
+ >
98
+ {children}{" "}
99
+ <ChevronDownIcon
100
+ className="relative top-[1px] ml-1 size-3 transition duration-300 group-data-[state=open]:rotate-180"
101
+ aria-hidden="true"
102
+ />
103
+ </NavigationMenuPrimitive.Trigger>
104
+ )
105
+ }
106
+
107
+ /** 네비게이션 메뉴의 드롭다운 콘텐츠입니다. */
108
+ function NavigationMenuContent({
109
+ className,
110
+ ...props
111
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Content>) {
112
+ return (
113
+ <NavigationMenuPrimitive.Content
114
+ data-slot="navigation-menu-content"
115
+ className={cn(
116
+ "data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 top-0 left-0 w-full p-2 pr-2.5 md:absolute md:w-auto",
117
+ "group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:data-[state=open]:animate-in group-data-[viewport=false]/navigation-menu:data-[state=closed]:animate-out group-data-[viewport=false]/navigation-menu:data-[state=closed]:zoom-out-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:fade-in-0 group-data-[viewport=false]/navigation-menu:data-[state=closed]:fade-out-0 group-data-[viewport=false]/navigation-menu:top-full group-data-[viewport=false]/navigation-menu:mt-1.5 group-data-[viewport=false]/navigation-menu:overflow-hidden group-data-[viewport=false]/navigation-menu:rounded-md group-data-[viewport=false]/navigation-menu:border group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:duration-200 **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none",
118
+ className
119
+ )}
120
+ {...props}
121
+ />
122
+ )
123
+ }
124
+
125
+ /** 드롭다운 콘텐츠를 표시하는 뷰포트입니다. */
126
+ function NavigationMenuViewport({
127
+ className,
128
+ ...props
129
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Viewport>) {
130
+ return (
131
+ <div
132
+ className={cn(
133
+ "absolute top-full left-0 isolate z-50 flex justify-center"
134
+ )}
135
+ >
136
+ <NavigationMenuPrimitive.Viewport
137
+ data-slot="navigation-menu-viewport"
138
+ className={cn(
139
+ "origin-top-center bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border shadow md:w-[var(--radix-navigation-menu-viewport-width)]",
140
+ className
141
+ )}
142
+ {...props}
143
+ />
144
+ </div>
145
+ )
146
+ }
147
+
148
+ /** 네비게이션 링크 컴포넌트입니다. */
149
+ function NavigationMenuLink({
150
+ className,
151
+ ...props
152
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Link>) {
153
+ return (
154
+ <NavigationMenuPrimitive.Link
155
+ data-slot="navigation-menu-link"
156
+ className={cn(
157
+ "data-[active=true]:focus:bg-accent data-[active=true]:hover:bg-accent data-[active=true]:bg-accent/50 data-[active=true]:text-accent-foreground hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus-visible:ring-ring/50 [&_svg:not([class*='text-'])]:text-muted-foreground flex flex-col gap-1 rounded-sm p-2 text-sm transition-all outline-none focus-visible:ring-[3px] focus-visible:outline-1 [&_svg:not([class*='size-'])]:size-4",
158
+ className
159
+ )}
160
+ {...props}
161
+ />
162
+ )
163
+ }
164
+
165
+ /** 현재 활성 메뉴를 가리키는 인디케이터입니다. */
166
+ function NavigationMenuIndicator({
167
+ className,
168
+ ...props
169
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Indicator>) {
170
+ return (
171
+ <NavigationMenuPrimitive.Indicator
172
+ data-slot="navigation-menu-indicator"
173
+ className={cn(
174
+ "data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden",
175
+ className
176
+ )}
177
+ {...props}
178
+ >
179
+ <div className="bg-border relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm shadow-md" />
180
+ </NavigationMenuPrimitive.Indicator>
181
+ )
182
+ }
183
+
184
+ export {
185
+ NavigationMenu,
186
+ NavigationMenuList,
187
+ NavigationMenuItem,
188
+ NavigationMenuContent,
189
+ NavigationMenuTrigger,
190
+ NavigationMenuLink,
191
+ NavigationMenuIndicator,
192
+ NavigationMenuViewport,
193
+ navigationMenuTriggerStyle,
194
+ }
@@ -0,0 +1 @@
1
+ export * from "./pagination";
@@ -0,0 +1,153 @@
1
+ import * as React from "react";
2
+ import {
3
+ ChevronLeftIcon,
4
+ ChevronRightIcon,
5
+ MoreHorizontalIcon,
6
+ } from "lucide-react";
7
+
8
+ import { cn } from "@/lib/utils";
9
+ import { buttonVariants, type Button } from "@/components/button";
10
+
11
+ /**
12
+ * 페이지 목록을 탐색하는 페이지네이션 컴포넌트입니다.
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * <Pagination>
17
+ * <PaginationContent>
18
+ * <PaginationItem>
19
+ * <PaginationPrevious href="#" />
20
+ * </PaginationItem>
21
+ * <PaginationItem>
22
+ * <PaginationLink href="#" isActive>1</PaginationLink>
23
+ * </PaginationItem>
24
+ * <PaginationItem>
25
+ * <PaginationNext href="#" />
26
+ * </PaginationItem>
27
+ * </PaginationContent>
28
+ * </Pagination>
29
+ * ```
30
+ */
31
+ function Pagination({ className, ...props }: React.ComponentProps<"nav">) {
32
+ return (
33
+ <nav
34
+ role="navigation"
35
+ aria-label="pagination"
36
+ data-slot="pagination"
37
+ className={cn("mx-auto flex w-full justify-center", className)}
38
+ {...props}
39
+ />
40
+ );
41
+ }
42
+
43
+ function PaginationContent({
44
+ className,
45
+ ...props
46
+ }: React.ComponentProps<"ul">) {
47
+ return (
48
+ <ul
49
+ data-slot="pagination-content"
50
+ className={cn("flex flex-row items-center gap-1", className)}
51
+ {...props}
52
+ />
53
+ );
54
+ }
55
+
56
+ function PaginationItem({ ...props }: React.ComponentProps<"li">) {
57
+ return <li data-slot="pagination-item" {...props} />;
58
+ }
59
+
60
+ type PaginationLinkProps = {
61
+ isActive?: boolean;
62
+ } & Pick<React.ComponentProps<typeof Button>, "size"> &
63
+ React.ComponentProps<"a">;
64
+
65
+ /**
66
+ * 페이지네이션 링크입니다.
67
+ * @param props.isActive - 현재 페이지 여부
68
+ */
69
+ function PaginationLink({
70
+ className,
71
+ isActive,
72
+ size = "icon",
73
+ ...props
74
+ }: PaginationLinkProps) {
75
+ return (
76
+ <a
77
+ aria-current={isActive ? "page" : undefined}
78
+ data-slot="pagination-link"
79
+ data-active={isActive}
80
+ className={cn(
81
+ buttonVariants({
82
+ variant: isActive ? "outline" : "ghost",
83
+ size,
84
+ }),
85
+ className
86
+ )}
87
+ {...props}
88
+ />
89
+ );
90
+ }
91
+
92
+ /** 이전 페이지로 이동하는 링크입니다. */
93
+ function PaginationPrevious({
94
+ className,
95
+ ...props
96
+ }: React.ComponentProps<typeof PaginationLink>) {
97
+ return (
98
+ <PaginationLink
99
+ aria-label="Go to previous page"
100
+ size="default"
101
+ className={cn("gap-1 px-2.5 sm:pl-2.5", className)}
102
+ {...props}
103
+ >
104
+ <ChevronLeftIcon />
105
+ <span className="hidden sm:block">Previous</span>
106
+ </PaginationLink>
107
+ );
108
+ }
109
+
110
+ /** 다음 페이지로 이동하는 링크입니다. */
111
+ function PaginationNext({
112
+ className,
113
+ ...props
114
+ }: React.ComponentProps<typeof PaginationLink>) {
115
+ return (
116
+ <PaginationLink
117
+ aria-label="Go to next page"
118
+ size="default"
119
+ className={cn("gap-1 px-2.5 sm:pr-2.5", className)}
120
+ {...props}
121
+ >
122
+ <span className="hidden sm:block">Next</span>
123
+ <ChevronRightIcon />
124
+ </PaginationLink>
125
+ );
126
+ }
127
+
128
+ function PaginationEllipsis({
129
+ className,
130
+ ...props
131
+ }: React.ComponentProps<"span">) {
132
+ return (
133
+ <span
134
+ aria-hidden
135
+ data-slot="pagination-ellipsis"
136
+ className={cn("flex size-9 items-center justify-center", className)}
137
+ {...props}
138
+ >
139
+ <MoreHorizontalIcon className="size-4" />
140
+ <span className="sr-only">More pages</span>
141
+ </span>
142
+ );
143
+ }
144
+
145
+ export {
146
+ Pagination,
147
+ PaginationContent,
148
+ PaginationLink,
149
+ PaginationItem,
150
+ PaginationPrevious,
151
+ PaginationNext,
152
+ PaginationEllipsis,
153
+ };
@@ -0,0 +1 @@
1
+ export * from "./popover";