@nocobase/portal-template-default 1.0.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/.env.example +10 -0
- package/Dockerfile +36 -0
- package/README.MD +50 -0
- package/components.json +25 -0
- package/eslint.config.js +28 -0
- package/index.html +39 -0
- package/package.json +84 -0
- package/pnpm-workspace.yaml +2 -0
- package/public/favicon.ico +0 -0
- package/public/logo-dark.png +0 -0
- package/public/logo-mark-dark.png +0 -0
- package/public/logo-mark.png +0 -0
- package/public/logo.png +0 -0
- package/src/App.css +158 -0
- package/src/App.tsx +151 -0
- package/src/components/app-shell/brand.tsx +70 -0
- package/src/components/app-shell/breadcrumb.tsx +81 -0
- package/src/components/app-shell/document-title-handler.tsx +169 -0
- package/src/components/app-shell/error-component.tsx +134 -0
- package/src/components/app-shell/header.tsx +127 -0
- package/src/components/app-shell/layout.tsx +39 -0
- package/src/components/app-shell/loading-overlay.tsx +36 -0
- package/src/components/app-shell/sidebar.tsx +351 -0
- package/src/components/app-shell/user-avatar.tsx +42 -0
- package/src/components/app-shell/user-info.tsx +53 -0
- package/src/components/auth/auth-layout.tsx +82 -0
- package/src/components/auth/forgot-password-form.tsx +58 -0
- package/src/components/auth/input-password.tsx +38 -0
- package/src/components/auth/sign-in-form.tsx +111 -0
- package/src/components/auth/sign-up-form.tsx +127 -0
- package/src/components/data-table/data-table-filter.tsx +994 -0
- package/src/components/data-table/data-table-pagination.tsx +146 -0
- package/src/components/data-table/data-table-sorter.tsx +47 -0
- package/src/components/data-table/data-table.tsx +321 -0
- package/src/components/notifications/toaster.tsx +23 -0
- package/src/components/notifications/undoable-notification.tsx +84 -0
- package/src/components/notifications/use-notification-provider.tsx +62 -0
- package/src/components/resources/buttons/clone.tsx +81 -0
- package/src/components/resources/buttons/create.tsx +70 -0
- package/src/components/resources/buttons/delete.tsx +113 -0
- package/src/components/resources/buttons/edit.tsx +81 -0
- package/src/components/resources/buttons/list.tsx +70 -0
- package/src/components/resources/buttons/refresh.tsx +81 -0
- package/src/components/resources/buttons/show.tsx +81 -0
- package/src/components/resources/status-badge.tsx +37 -0
- package/src/components/resources/views/create-view.tsx +81 -0
- package/src/components/resources/views/edit-view.tsx +104 -0
- package/src/components/resources/views/list-view.tsx +86 -0
- package/src/components/resources/views/show-view.tsx +108 -0
- package/src/components/theme/theme-provider.tsx +71 -0
- package/src/components/theme/theme-select.tsx +97 -0
- package/src/components/theme/theme-toggle.tsx +66 -0
- package/src/components/ui/accordion.tsx +72 -0
- package/src/components/ui/alert-dialog.tsx +185 -0
- package/src/components/ui/alert.tsx +76 -0
- package/src/components/ui/aspect-ratio.tsx +22 -0
- package/src/components/ui/avatar.tsx +107 -0
- package/src/components/ui/badge.tsx +52 -0
- package/src/components/ui/breadcrumb.tsx +125 -0
- package/src/components/ui/button.tsx +58 -0
- package/src/components/ui/calendar.tsx +221 -0
- package/src/components/ui/card.tsx +103 -0
- package/src/components/ui/carousel.tsx +240 -0
- package/src/components/ui/chart.tsx +373 -0
- package/src/components/ui/checkbox.tsx +29 -0
- package/src/components/ui/collapsible.tsx +19 -0
- package/src/components/ui/command.tsx +192 -0
- package/src/components/ui/context-menu.tsx +271 -0
- package/src/components/ui/dialog.tsx +160 -0
- package/src/components/ui/drawer.tsx +226 -0
- package/src/components/ui/dropdown-menu.tsx +268 -0
- package/src/components/ui/form.tsx +174 -0
- package/src/components/ui/hover-card.tsx +49 -0
- package/src/components/ui/input-group.tsx +158 -0
- package/src/components/ui/input-otp.tsx +87 -0
- package/src/components/ui/input.tsx +20 -0
- package/src/components/ui/label.tsx +18 -0
- package/src/components/ui/menubar.tsx +278 -0
- package/src/components/ui/navigation-menu.tsx +168 -0
- package/src/components/ui/pagination.tsx +130 -0
- package/src/components/ui/popover.tsx +91 -0
- package/src/components/ui/progress.tsx +81 -0
- package/src/components/ui/radio-group.tsx +38 -0
- package/src/components/ui/resizable.tsx +48 -0
- package/src/components/ui/scroll-area.tsx +55 -0
- package/src/components/ui/select.tsx +199 -0
- package/src/components/ui/separator.tsx +25 -0
- package/src/components/ui/sheet.tsx +136 -0
- package/src/components/ui/sidebar.tsx +723 -0
- package/src/components/ui/skeleton.tsx +13 -0
- package/src/components/ui/slider.tsx +52 -0
- package/src/components/ui/sonner.tsx +47 -0
- package/src/components/ui/switch.tsx +32 -0
- package/src/components/ui/table.tsx +114 -0
- package/src/components/ui/tabs.tsx +82 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/toggle-group.tsx +87 -0
- package/src/components/ui/toggle.tsx +43 -0
- package/src/components/ui/tooltip.tsx +66 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/index.tsx +17 -0
- package/src/lib/utils.ts +19 -0
- package/src/pages/blog-posts/create.tsx +177 -0
- package/src/pages/blog-posts/edit.tsx +197 -0
- package/src/pages/blog-posts/index.ts +4 -0
- package/src/pages/blog-posts/list.tsx +131 -0
- package/src/pages/blog-posts/show.tsx +81 -0
- package/src/pages/categories/create.tsx +73 -0
- package/src/pages/categories/edit.tsx +73 -0
- package/src/pages/categories/index.ts +4 -0
- package/src/pages/categories/list.tsx +86 -0
- package/src/pages/categories/show.tsx +37 -0
- package/src/pages/forgot-password/index.tsx +6 -0
- package/src/pages/forgotPassword/index.tsx +5 -0
- package/src/pages/login/index.tsx +5 -0
- package/src/pages/register/index.tsx +5 -0
- package/src/providers/auth.ts +204 -0
- package/src/providers/constants.ts +75 -0
- package/src/providers/data.ts +232 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.json +25 -0
- package/tsconfig.node.json +21 -0
- package/vite.config.ts +74 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { OTPInput, OTPInputContext } from "input-otp"
|
|
5
|
+
|
|
6
|
+
import { cn } from "@/lib/utils"
|
|
7
|
+
import { MinusIcon } from "lucide-react"
|
|
8
|
+
|
|
9
|
+
function InputOTP({
|
|
10
|
+
className,
|
|
11
|
+
containerClassName,
|
|
12
|
+
...props
|
|
13
|
+
}: React.ComponentProps<typeof OTPInput> & {
|
|
14
|
+
containerClassName?: string
|
|
15
|
+
}) {
|
|
16
|
+
return (
|
|
17
|
+
<OTPInput
|
|
18
|
+
data-slot="input-otp"
|
|
19
|
+
containerClassName={cn(
|
|
20
|
+
"cn-input-otp flex items-center has-disabled:opacity-50",
|
|
21
|
+
containerClassName
|
|
22
|
+
)}
|
|
23
|
+
spellCheck={false}
|
|
24
|
+
className={cn("disabled:cursor-not-allowed", className)}
|
|
25
|
+
{...props}
|
|
26
|
+
/>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function InputOTPGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
31
|
+
return (
|
|
32
|
+
<div
|
|
33
|
+
data-slot="input-otp-group"
|
|
34
|
+
className={cn(
|
|
35
|
+
"flex items-center rounded-lg has-aria-invalid:border-destructive has-aria-invalid:ring-3 has-aria-invalid:ring-destructive/20 dark:has-aria-invalid:ring-destructive/40",
|
|
36
|
+
className
|
|
37
|
+
)}
|
|
38
|
+
{...props}
|
|
39
|
+
/>
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function InputOTPSlot({
|
|
44
|
+
index,
|
|
45
|
+
className,
|
|
46
|
+
...props
|
|
47
|
+
}: React.ComponentProps<"div"> & {
|
|
48
|
+
index: number
|
|
49
|
+
}) {
|
|
50
|
+
const inputOTPContext = React.useContext(OTPInputContext)
|
|
51
|
+
const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {}
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<div
|
|
55
|
+
data-slot="input-otp-slot"
|
|
56
|
+
data-active={isActive}
|
|
57
|
+
className={cn(
|
|
58
|
+
"relative flex size-8 items-center justify-center border-y border-r border-input text-sm transition-all outline-none first:rounded-l-lg first:border-l last:rounded-r-lg aria-invalid:border-destructive data-[active=true]:z-10 data-[active=true]:border-ring data-[active=true]:ring-3 data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:border-destructive data-[active=true]:aria-invalid:ring-destructive/20 dark:bg-input/30 dark:data-[active=true]:aria-invalid:ring-destructive/40",
|
|
59
|
+
className
|
|
60
|
+
)}
|
|
61
|
+
{...props}
|
|
62
|
+
>
|
|
63
|
+
{char}
|
|
64
|
+
{hasFakeCaret && (
|
|
65
|
+
<div className="pointer-events-none absolute inset-0 flex items-center justify-center">
|
|
66
|
+
<div className="h-4 w-px animate-caret-blink bg-foreground duration-1000" />
|
|
67
|
+
</div>
|
|
68
|
+
)}
|
|
69
|
+
</div>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function InputOTPSeparator({ ...props }: React.ComponentProps<"div">) {
|
|
74
|
+
return (
|
|
75
|
+
<div
|
|
76
|
+
data-slot="input-otp-separator"
|
|
77
|
+
className="flex items-center [&_svg:not([class*='size-'])]:size-4"
|
|
78
|
+
role="separator"
|
|
79
|
+
{...props}
|
|
80
|
+
>
|
|
81
|
+
<MinusIcon
|
|
82
|
+
/>
|
|
83
|
+
</div>
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator }
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { Input as InputPrimitive } from "@base-ui/react/input"
|
|
3
|
+
|
|
4
|
+
import { cn } from "@/lib/utils"
|
|
5
|
+
|
|
6
|
+
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
|
|
7
|
+
return (
|
|
8
|
+
<InputPrimitive
|
|
9
|
+
type={type}
|
|
10
|
+
data-slot="input"
|
|
11
|
+
className={cn(
|
|
12
|
+
"h-9 w-full min-w-0 rounded-lg border border-input bg-transparent px-3 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/30 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
|
|
13
|
+
className
|
|
14
|
+
)}
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { Input }
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
import { cn } from "@/lib/utils"
|
|
4
|
+
|
|
5
|
+
function Label({ className, ...props }: React.ComponentProps<"label">) {
|
|
6
|
+
return (
|
|
7
|
+
<label
|
|
8
|
+
data-slot="label"
|
|
9
|
+
className={cn(
|
|
10
|
+
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
|
|
11
|
+
className
|
|
12
|
+
)}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { Label }
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { Menu as MenuPrimitive } from "@base-ui/react/menu"
|
|
3
|
+
import { Menubar as MenubarPrimitive } from "@base-ui/react/menubar"
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
6
|
+
import {
|
|
7
|
+
DropdownMenu,
|
|
8
|
+
DropdownMenuContent,
|
|
9
|
+
DropdownMenuGroup,
|
|
10
|
+
DropdownMenuItem,
|
|
11
|
+
DropdownMenuLabel,
|
|
12
|
+
DropdownMenuPortal,
|
|
13
|
+
DropdownMenuRadioGroup,
|
|
14
|
+
DropdownMenuSeparator,
|
|
15
|
+
DropdownMenuShortcut,
|
|
16
|
+
DropdownMenuSub,
|
|
17
|
+
DropdownMenuSubContent,
|
|
18
|
+
DropdownMenuSubTrigger,
|
|
19
|
+
DropdownMenuTrigger,
|
|
20
|
+
} from "@/components/ui/dropdown-menu"
|
|
21
|
+
import { CheckIcon } from "lucide-react"
|
|
22
|
+
|
|
23
|
+
function Menubar({ className, ...props }: MenubarPrimitive.Props) {
|
|
24
|
+
return (
|
|
25
|
+
<MenubarPrimitive
|
|
26
|
+
data-slot="menubar"
|
|
27
|
+
className={cn(
|
|
28
|
+
"flex h-8 items-center gap-0.5 rounded-lg border p-[3px]",
|
|
29
|
+
className
|
|
30
|
+
)}
|
|
31
|
+
{...props}
|
|
32
|
+
/>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function MenubarMenu({ ...props }: React.ComponentProps<typeof DropdownMenu>) {
|
|
37
|
+
return <DropdownMenu data-slot="menubar-menu" {...props} />
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function MenubarGroup({
|
|
41
|
+
...props
|
|
42
|
+
}: React.ComponentProps<typeof DropdownMenuGroup>) {
|
|
43
|
+
return <DropdownMenuGroup data-slot="menubar-group" {...props} />
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function MenubarPortal({
|
|
47
|
+
...props
|
|
48
|
+
}: React.ComponentProps<typeof DropdownMenuPortal>) {
|
|
49
|
+
return <DropdownMenuPortal data-slot="menubar-portal" {...props} />
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function MenubarTrigger({
|
|
53
|
+
className,
|
|
54
|
+
...props
|
|
55
|
+
}: React.ComponentProps<typeof DropdownMenuTrigger>) {
|
|
56
|
+
return (
|
|
57
|
+
<DropdownMenuTrigger
|
|
58
|
+
data-slot="menubar-trigger"
|
|
59
|
+
className={cn(
|
|
60
|
+
"flex items-center rounded-sm px-1.5 py-[2px] text-sm font-medium outline-hidden select-none hover:bg-muted aria-expanded:bg-muted",
|
|
61
|
+
className
|
|
62
|
+
)}
|
|
63
|
+
{...props}
|
|
64
|
+
/>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function MenubarContent({
|
|
69
|
+
className,
|
|
70
|
+
align = "start",
|
|
71
|
+
alignOffset = -4,
|
|
72
|
+
sideOffset = 8,
|
|
73
|
+
...props
|
|
74
|
+
}: React.ComponentProps<typeof DropdownMenuContent>) {
|
|
75
|
+
return (
|
|
76
|
+
<DropdownMenuContent
|
|
77
|
+
data-slot="menubar-content"
|
|
78
|
+
align={align}
|
|
79
|
+
alignOffset={alignOffset}
|
|
80
|
+
sideOffset={sideOffset}
|
|
81
|
+
className={cn("min-w-36 rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-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 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95", className )}
|
|
82
|
+
{...props}
|
|
83
|
+
/>
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function MenubarItem({
|
|
88
|
+
className,
|
|
89
|
+
inset,
|
|
90
|
+
variant = "default",
|
|
91
|
+
...props
|
|
92
|
+
}: React.ComponentProps<typeof DropdownMenuItem>) {
|
|
93
|
+
return (
|
|
94
|
+
<DropdownMenuItem
|
|
95
|
+
data-slot="menubar-item"
|
|
96
|
+
data-inset={inset}
|
|
97
|
+
data-variant={variant}
|
|
98
|
+
className={cn(
|
|
99
|
+
"group/menubar-item gap-1.5 rounded-md px-1.5 py-1 text-sm focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:opacity-50 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive!",
|
|
100
|
+
className
|
|
101
|
+
)}
|
|
102
|
+
{...props}
|
|
103
|
+
/>
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function MenubarCheckboxItem({
|
|
108
|
+
className,
|
|
109
|
+
children,
|
|
110
|
+
checked,
|
|
111
|
+
inset,
|
|
112
|
+
...props
|
|
113
|
+
}: MenuPrimitive.CheckboxItem.Props & {
|
|
114
|
+
inset?: boolean
|
|
115
|
+
}) {
|
|
116
|
+
return (
|
|
117
|
+
<MenuPrimitive.CheckboxItem
|
|
118
|
+
data-slot="menubar-checkbox-item"
|
|
119
|
+
data-inset={inset}
|
|
120
|
+
className={cn(
|
|
121
|
+
"relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-1.5 pl-7 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
122
|
+
className
|
|
123
|
+
)}
|
|
124
|
+
checked={checked}
|
|
125
|
+
{...props}
|
|
126
|
+
>
|
|
127
|
+
<span className="pointer-events-none absolute left-1.5 flex size-4 items-center justify-center [&_svg:not([class*='size-'])]:size-4">
|
|
128
|
+
<MenuPrimitive.CheckboxItemIndicator>
|
|
129
|
+
<CheckIcon
|
|
130
|
+
/>
|
|
131
|
+
</MenuPrimitive.CheckboxItemIndicator>
|
|
132
|
+
</span>
|
|
133
|
+
{children}
|
|
134
|
+
</MenuPrimitive.CheckboxItem>
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function MenubarRadioGroup({
|
|
139
|
+
...props
|
|
140
|
+
}: React.ComponentProps<typeof DropdownMenuRadioGroup>) {
|
|
141
|
+
return <DropdownMenuRadioGroup data-slot="menubar-radio-group" {...props} />
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function MenubarRadioItem({
|
|
145
|
+
className,
|
|
146
|
+
children,
|
|
147
|
+
inset,
|
|
148
|
+
...props
|
|
149
|
+
}: MenuPrimitive.RadioItem.Props & {
|
|
150
|
+
inset?: boolean
|
|
151
|
+
}) {
|
|
152
|
+
return (
|
|
153
|
+
<MenuPrimitive.RadioItem
|
|
154
|
+
data-slot="menubar-radio-item"
|
|
155
|
+
data-inset={inset}
|
|
156
|
+
className={cn(
|
|
157
|
+
"relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-1.5 pl-7 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
158
|
+
className
|
|
159
|
+
)}
|
|
160
|
+
{...props}
|
|
161
|
+
>
|
|
162
|
+
<span className="pointer-events-none absolute left-1.5 flex size-4 items-center justify-center [&_svg:not([class*='size-'])]:size-4">
|
|
163
|
+
<MenuPrimitive.RadioItemIndicator>
|
|
164
|
+
<CheckIcon
|
|
165
|
+
/>
|
|
166
|
+
</MenuPrimitive.RadioItemIndicator>
|
|
167
|
+
</span>
|
|
168
|
+
{children}
|
|
169
|
+
</MenuPrimitive.RadioItem>
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function MenubarLabel({
|
|
174
|
+
className,
|
|
175
|
+
inset,
|
|
176
|
+
...props
|
|
177
|
+
}: React.ComponentProps<typeof DropdownMenuLabel> & {
|
|
178
|
+
inset?: boolean
|
|
179
|
+
}) {
|
|
180
|
+
return (
|
|
181
|
+
<DropdownMenuLabel
|
|
182
|
+
data-slot="menubar-label"
|
|
183
|
+
data-inset={inset}
|
|
184
|
+
className={cn(
|
|
185
|
+
"px-1.5 py-1 text-sm font-medium data-inset:pl-7",
|
|
186
|
+
className
|
|
187
|
+
)}
|
|
188
|
+
{...props}
|
|
189
|
+
/>
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function MenubarSeparator({
|
|
194
|
+
className,
|
|
195
|
+
...props
|
|
196
|
+
}: React.ComponentProps<typeof DropdownMenuSeparator>) {
|
|
197
|
+
return (
|
|
198
|
+
<DropdownMenuSeparator
|
|
199
|
+
data-slot="menubar-separator"
|
|
200
|
+
className={cn("-mx-1 my-1 h-px bg-border", className)}
|
|
201
|
+
{...props}
|
|
202
|
+
/>
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function MenubarShortcut({
|
|
207
|
+
className,
|
|
208
|
+
...props
|
|
209
|
+
}: React.ComponentProps<typeof DropdownMenuShortcut>) {
|
|
210
|
+
return (
|
|
211
|
+
<DropdownMenuShortcut
|
|
212
|
+
data-slot="menubar-shortcut"
|
|
213
|
+
className={cn(
|
|
214
|
+
"ml-auto text-xs tracking-widest text-muted-foreground group-focus/menubar-item:text-accent-foreground",
|
|
215
|
+
className
|
|
216
|
+
)}
|
|
217
|
+
{...props}
|
|
218
|
+
/>
|
|
219
|
+
)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function MenubarSub({
|
|
223
|
+
...props
|
|
224
|
+
}: React.ComponentProps<typeof DropdownMenuSub>) {
|
|
225
|
+
return <DropdownMenuSub data-slot="menubar-sub" {...props} />
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function MenubarSubTrigger({
|
|
229
|
+
className,
|
|
230
|
+
inset,
|
|
231
|
+
...props
|
|
232
|
+
}: React.ComponentProps<typeof DropdownMenuSubTrigger> & {
|
|
233
|
+
inset?: boolean
|
|
234
|
+
}) {
|
|
235
|
+
return (
|
|
236
|
+
<DropdownMenuSubTrigger
|
|
237
|
+
data-slot="menubar-sub-trigger"
|
|
238
|
+
data-inset={inset}
|
|
239
|
+
className={cn(
|
|
240
|
+
"gap-1.5 rounded-md px-1.5 py-1 text-sm focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-open:bg-accent data-open:text-accent-foreground [&_svg:not([class*='size-'])]:size-4",
|
|
241
|
+
className
|
|
242
|
+
)}
|
|
243
|
+
{...props}
|
|
244
|
+
/>
|
|
245
|
+
)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function MenubarSubContent({
|
|
249
|
+
className,
|
|
250
|
+
...props
|
|
251
|
+
}: React.ComponentProps<typeof DropdownMenuSubContent>) {
|
|
252
|
+
return (
|
|
253
|
+
<DropdownMenuSubContent
|
|
254
|
+
data-slot="menubar-sub-content"
|
|
255
|
+
className={cn("min-w-32 rounded-lg bg-popover p-1 text-popover-foreground shadow-lg ring-1 ring-foreground/10 duration-100 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 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )}
|
|
256
|
+
{...props}
|
|
257
|
+
/>
|
|
258
|
+
)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export {
|
|
262
|
+
Menubar,
|
|
263
|
+
MenubarPortal,
|
|
264
|
+
MenubarMenu,
|
|
265
|
+
MenubarTrigger,
|
|
266
|
+
MenubarContent,
|
|
267
|
+
MenubarGroup,
|
|
268
|
+
MenubarSeparator,
|
|
269
|
+
MenubarLabel,
|
|
270
|
+
MenubarItem,
|
|
271
|
+
MenubarShortcut,
|
|
272
|
+
MenubarCheckboxItem,
|
|
273
|
+
MenubarRadioGroup,
|
|
274
|
+
MenubarRadioItem,
|
|
275
|
+
MenubarSub,
|
|
276
|
+
MenubarSubTrigger,
|
|
277
|
+
MenubarSubContent,
|
|
278
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { NavigationMenu as NavigationMenuPrimitive } from "@base-ui/react/navigation-menu"
|
|
2
|
+
import { cva } from "class-variance-authority"
|
|
3
|
+
|
|
4
|
+
import { cn } from "@/lib/utils"
|
|
5
|
+
import { ChevronDownIcon } from "lucide-react"
|
|
6
|
+
|
|
7
|
+
function NavigationMenu({
|
|
8
|
+
align = "start",
|
|
9
|
+
className,
|
|
10
|
+
children,
|
|
11
|
+
...props
|
|
12
|
+
}: NavigationMenuPrimitive.Root.Props &
|
|
13
|
+
Pick<NavigationMenuPrimitive.Positioner.Props, "align">) {
|
|
14
|
+
return (
|
|
15
|
+
<NavigationMenuPrimitive.Root
|
|
16
|
+
data-slot="navigation-menu"
|
|
17
|
+
className={cn(
|
|
18
|
+
"group/navigation-menu relative flex max-w-max flex-1 items-center justify-center",
|
|
19
|
+
className
|
|
20
|
+
)}
|
|
21
|
+
{...props}
|
|
22
|
+
>
|
|
23
|
+
{children}
|
|
24
|
+
<NavigationMenuPositioner align={align} />
|
|
25
|
+
</NavigationMenuPrimitive.Root>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function NavigationMenuList({
|
|
30
|
+
className,
|
|
31
|
+
...props
|
|
32
|
+
}: React.ComponentPropsWithRef<typeof NavigationMenuPrimitive.List>) {
|
|
33
|
+
return (
|
|
34
|
+
<NavigationMenuPrimitive.List
|
|
35
|
+
data-slot="navigation-menu-list"
|
|
36
|
+
className={cn(
|
|
37
|
+
"group flex flex-1 list-none items-center justify-center gap-0",
|
|
38
|
+
className
|
|
39
|
+
)}
|
|
40
|
+
{...props}
|
|
41
|
+
/>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function NavigationMenuItem({
|
|
46
|
+
className,
|
|
47
|
+
...props
|
|
48
|
+
}: React.ComponentPropsWithRef<typeof NavigationMenuPrimitive.Item>) {
|
|
49
|
+
return (
|
|
50
|
+
<NavigationMenuPrimitive.Item
|
|
51
|
+
data-slot="navigation-menu-item"
|
|
52
|
+
className={cn("relative", className)}
|
|
53
|
+
{...props}
|
|
54
|
+
/>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const navigationMenuTriggerStyle = cva(
|
|
59
|
+
"group/navigation-menu-trigger inline-flex h-9 w-max items-center justify-center rounded-lg px-2.5 py-1.5 text-sm font-medium transition-all outline-none hover:bg-muted focus:bg-muted focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-popup-open:bg-muted/50 data-popup-open:hover:bg-muted data-open:bg-muted/50 data-open:hover:bg-muted data-open:focus:bg-muted"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
function NavigationMenuTrigger({
|
|
63
|
+
className,
|
|
64
|
+
children,
|
|
65
|
+
...props
|
|
66
|
+
}: NavigationMenuPrimitive.Trigger.Props) {
|
|
67
|
+
return (
|
|
68
|
+
<NavigationMenuPrimitive.Trigger
|
|
69
|
+
data-slot="navigation-menu-trigger"
|
|
70
|
+
className={cn(navigationMenuTriggerStyle(), "group", className)}
|
|
71
|
+
{...props}
|
|
72
|
+
>
|
|
73
|
+
{children}{" "}
|
|
74
|
+
<ChevronDownIcon className="relative top-px ml-1 size-3 transition duration-300 group-data-popup-open/navigation-menu-trigger:rotate-180 group-data-open/navigation-menu-trigger:rotate-180" aria-hidden="true" />
|
|
75
|
+
</NavigationMenuPrimitive.Trigger>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function NavigationMenuContent({
|
|
80
|
+
className,
|
|
81
|
+
...props
|
|
82
|
+
}: NavigationMenuPrimitive.Content.Props) {
|
|
83
|
+
return (
|
|
84
|
+
<NavigationMenuPrimitive.Content
|
|
85
|
+
data-slot="navigation-menu-content"
|
|
86
|
+
className={cn(
|
|
87
|
+
"data-ending-style:data-activation-direction=left:translate-x-[50%] data-ending-style:data-activation-direction=right:translate-x-[-50%] data-starting-style:data-activation-direction=left:translate-x-[-50%] data-starting-style:data-activation-direction=right:translate-x-[50%] h-full w-auto p-1 transition-[opacity,transform,translate] duration-[0.35s] ease-[cubic-bezier(0.22,1,0.36,1)] group-data-[viewport=false]/navigation-menu:rounded-lg group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:ring-1 group-data-[viewport=false]/navigation-menu:ring-foreground/10 group-data-[viewport=false]/navigation-menu:duration-300 data-ending-style:opacity-0 data-starting-style:opacity-0 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 data-[motion^=from-]:animate-in data-[motion^=from-]:fade-in data-[motion^=to-]:animate-out data-[motion^=to-]:fade-out **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none group-data-[viewport=false]/navigation-menu:data-open:animate-in group-data-[viewport=false]/navigation-menu:data-open:fade-in-0 group-data-[viewport=false]/navigation-menu:data-open:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-closed:animate-out group-data-[viewport=false]/navigation-menu:data-closed:fade-out-0 group-data-[viewport=false]/navigation-menu:data-closed:zoom-out-95",
|
|
88
|
+
className
|
|
89
|
+
)}
|
|
90
|
+
{...props}
|
|
91
|
+
/>
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function NavigationMenuPositioner({
|
|
96
|
+
className,
|
|
97
|
+
side = "bottom",
|
|
98
|
+
sideOffset = 8,
|
|
99
|
+
align = "start",
|
|
100
|
+
alignOffset = 0,
|
|
101
|
+
...props
|
|
102
|
+
}: NavigationMenuPrimitive.Positioner.Props) {
|
|
103
|
+
return (
|
|
104
|
+
<NavigationMenuPrimitive.Portal>
|
|
105
|
+
<NavigationMenuPrimitive.Positioner
|
|
106
|
+
side={side}
|
|
107
|
+
sideOffset={sideOffset}
|
|
108
|
+
align={align}
|
|
109
|
+
alignOffset={alignOffset}
|
|
110
|
+
className={cn(
|
|
111
|
+
"isolate z-50 h-(--positioner-height) w-(--positioner-width) max-w-(--available-width) transition-[top,left,right,bottom] duration-[0.35s] ease-[cubic-bezier(0.22,1,0.36,1)] data-instant:transition-none data-[side=bottom]:before:top-[-10px] data-[side=bottom]:before:right-0 data-[side=bottom]:before:left-0",
|
|
112
|
+
className
|
|
113
|
+
)}
|
|
114
|
+
{...props}
|
|
115
|
+
>
|
|
116
|
+
<NavigationMenuPrimitive.Popup className="data-[ending-style]:easing-[ease] xs:w-(--popup-width) relative h-(--popup-height) w-(--popup-width) origin-(--transform-origin) rounded-lg bg-popover text-popover-foreground shadow ring-1 ring-foreground/10 transition-[opacity,transform,width,height,scale,translate] duration-[0.35s] ease-[cubic-bezier(0.22,1,0.36,1)] outline-none data-ending-style:scale-90 data-ending-style:opacity-0 data-ending-style:duration-150 data-starting-style:scale-90 data-starting-style:opacity-0">
|
|
117
|
+
<NavigationMenuPrimitive.Viewport className="relative size-full overflow-hidden" />
|
|
118
|
+
</NavigationMenuPrimitive.Popup>
|
|
119
|
+
</NavigationMenuPrimitive.Positioner>
|
|
120
|
+
</NavigationMenuPrimitive.Portal>
|
|
121
|
+
)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function NavigationMenuLink({
|
|
125
|
+
className,
|
|
126
|
+
...props
|
|
127
|
+
}: NavigationMenuPrimitive.Link.Props) {
|
|
128
|
+
return (
|
|
129
|
+
<NavigationMenuPrimitive.Link
|
|
130
|
+
data-slot="navigation-menu-link"
|
|
131
|
+
className={cn(
|
|
132
|
+
"flex items-center gap-2 rounded-lg p-2 text-sm transition-all outline-none hover:bg-muted focus:bg-muted focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:outline-1 in-data-[slot=navigation-menu-content]:rounded-md data-active:bg-muted/50 data-active:hover:bg-muted data-active:focus:bg-muted [&_svg:not([class*='size-'])]:size-4",
|
|
133
|
+
className
|
|
134
|
+
)}
|
|
135
|
+
{...props}
|
|
136
|
+
/>
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function NavigationMenuIndicator({
|
|
141
|
+
className,
|
|
142
|
+
...props
|
|
143
|
+
}: React.ComponentPropsWithRef<typeof NavigationMenuPrimitive.Icon>) {
|
|
144
|
+
return (
|
|
145
|
+
<NavigationMenuPrimitive.Icon
|
|
146
|
+
data-slot="navigation-menu-indicator"
|
|
147
|
+
className={cn(
|
|
148
|
+
"top-full z-1 flex h-1.5 items-end justify-center overflow-hidden data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:animate-in data-[state=visible]:fade-in",
|
|
149
|
+
className
|
|
150
|
+
)}
|
|
151
|
+
{...props}
|
|
152
|
+
>
|
|
153
|
+
<div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" />
|
|
154
|
+
</NavigationMenuPrimitive.Icon>
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export {
|
|
159
|
+
NavigationMenu,
|
|
160
|
+
NavigationMenuContent,
|
|
161
|
+
NavigationMenuIndicator,
|
|
162
|
+
NavigationMenuItem,
|
|
163
|
+
NavigationMenuLink,
|
|
164
|
+
NavigationMenuList,
|
|
165
|
+
NavigationMenuTrigger,
|
|
166
|
+
navigationMenuTriggerStyle,
|
|
167
|
+
NavigationMenuPositioner,
|
|
168
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
import { cn } from "@/lib/utils"
|
|
4
|
+
import { Button } from "@/components/ui/button"
|
|
5
|
+
import { ChevronLeftIcon, ChevronRightIcon, MoreHorizontalIcon } from "lucide-react"
|
|
6
|
+
|
|
7
|
+
function Pagination({ className, ...props }: React.ComponentProps<"nav">) {
|
|
8
|
+
return (
|
|
9
|
+
<nav
|
|
10
|
+
role="navigation"
|
|
11
|
+
aria-label="pagination"
|
|
12
|
+
data-slot="pagination"
|
|
13
|
+
className={cn("mx-auto flex w-full justify-center", className)}
|
|
14
|
+
{...props}
|
|
15
|
+
/>
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function PaginationContent({
|
|
20
|
+
className,
|
|
21
|
+
...props
|
|
22
|
+
}: React.ComponentProps<"ul">) {
|
|
23
|
+
return (
|
|
24
|
+
<ul
|
|
25
|
+
data-slot="pagination-content"
|
|
26
|
+
className={cn("flex items-center gap-0.5", className)}
|
|
27
|
+
{...props}
|
|
28
|
+
/>
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function PaginationItem({ ...props }: React.ComponentProps<"li">) {
|
|
33
|
+
return <li data-slot="pagination-item" {...props} />
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
type PaginationLinkProps = {
|
|
37
|
+
isActive?: boolean
|
|
38
|
+
} & Pick<React.ComponentProps<typeof Button>, "size"> &
|
|
39
|
+
React.ComponentProps<"a">
|
|
40
|
+
|
|
41
|
+
function PaginationLink({
|
|
42
|
+
className,
|
|
43
|
+
isActive,
|
|
44
|
+
size = "icon",
|
|
45
|
+
...props
|
|
46
|
+
}: PaginationLinkProps) {
|
|
47
|
+
return (
|
|
48
|
+
<Button
|
|
49
|
+
variant={isActive ? "outline" : "ghost"}
|
|
50
|
+
size={size}
|
|
51
|
+
className={cn(className)}
|
|
52
|
+
nativeButton={false}
|
|
53
|
+
render={
|
|
54
|
+
<a
|
|
55
|
+
aria-current={isActive ? "page" : undefined}
|
|
56
|
+
data-slot="pagination-link"
|
|
57
|
+
data-active={isActive}
|
|
58
|
+
{...props}
|
|
59
|
+
/>
|
|
60
|
+
}
|
|
61
|
+
/>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function PaginationPrevious({
|
|
66
|
+
className,
|
|
67
|
+
text = "Previous",
|
|
68
|
+
...props
|
|
69
|
+
}: React.ComponentProps<typeof PaginationLink> & { text?: string }) {
|
|
70
|
+
return (
|
|
71
|
+
<PaginationLink
|
|
72
|
+
aria-label="Go to previous page"
|
|
73
|
+
size="default"
|
|
74
|
+
className={cn("pl-1.5!", className)}
|
|
75
|
+
{...props}
|
|
76
|
+
>
|
|
77
|
+
<ChevronLeftIcon data-icon="inline-start" />
|
|
78
|
+
<span className="hidden sm:block">{text}</span>
|
|
79
|
+
</PaginationLink>
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function PaginationNext({
|
|
84
|
+
className,
|
|
85
|
+
text = "Next",
|
|
86
|
+
...props
|
|
87
|
+
}: React.ComponentProps<typeof PaginationLink> & { text?: string }) {
|
|
88
|
+
return (
|
|
89
|
+
<PaginationLink
|
|
90
|
+
aria-label="Go to next page"
|
|
91
|
+
size="default"
|
|
92
|
+
className={cn("pr-1.5!", className)}
|
|
93
|
+
{...props}
|
|
94
|
+
>
|
|
95
|
+
<span className="hidden sm:block">{text}</span>
|
|
96
|
+
<ChevronRightIcon data-icon="inline-end" />
|
|
97
|
+
</PaginationLink>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function PaginationEllipsis({
|
|
102
|
+
className,
|
|
103
|
+
...props
|
|
104
|
+
}: React.ComponentProps<"span">) {
|
|
105
|
+
return (
|
|
106
|
+
<span
|
|
107
|
+
aria-hidden
|
|
108
|
+
data-slot="pagination-ellipsis"
|
|
109
|
+
className={cn(
|
|
110
|
+
"flex size-8 items-center justify-center [&_svg:not([class*='size-'])]:size-4",
|
|
111
|
+
className
|
|
112
|
+
)}
|
|
113
|
+
{...props}
|
|
114
|
+
>
|
|
115
|
+
<MoreHorizontalIcon
|
|
116
|
+
/>
|
|
117
|
+
<span className="sr-only">More pages</span>
|
|
118
|
+
</span>
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export {
|
|
123
|
+
Pagination,
|
|
124
|
+
PaginationContent,
|
|
125
|
+
PaginationEllipsis,
|
|
126
|
+
PaginationItem,
|
|
127
|
+
PaginationLink,
|
|
128
|
+
PaginationNext,
|
|
129
|
+
PaginationPrevious,
|
|
130
|
+
}
|