@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,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";
|