@greatapps/greatauth-ui 0.1.0 → 0.1.2
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/dist/index.d.ts +58 -1
- package/dist/index.js +1290 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
- package/src/components/app-header.tsx +55 -0
- package/src/components/app-shell.tsx +33 -0
- package/src/components/app-sidebar.tsx +185 -0
- package/src/components/index.ts +5 -0
- package/src/components/login-form.tsx +122 -0
- package/src/components/theme-toggle.tsx +21 -0
- package/src/components/ui/alert.tsx +72 -0
- package/src/components/ui/avatar.tsx +109 -0
- package/src/components/ui/badge.tsx +45 -0
- package/src/components/ui/breadcrumb.tsx +121 -0
- package/src/components/ui/button.tsx +60 -0
- package/src/components/ui/card.tsx +94 -0
- package/src/components/ui/collapsible.tsx +34 -0
- package/src/components/ui/dropdown-menu.tsx +261 -0
- package/src/components/ui/input.tsx +19 -0
- package/src/components/ui/label.tsx +24 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/sheet.tsx +133 -0
- package/src/components/ui/sidebar.tsx +701 -0
- package/src/components/ui/skeleton.tsx +15 -0
- package/src/components/ui/tooltip.tsx +57 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/index.ts +11 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { useRouter, useSearchParams } from "next/navigation";
|
|
5
|
+
import { Loader2 } from "lucide-react";
|
|
6
|
+
import type { LoginFormConfig } from "../types";
|
|
7
|
+
import { authClient } from "../auth";
|
|
8
|
+
import { Button } from "./ui/button";
|
|
9
|
+
import { Input } from "./ui/input";
|
|
10
|
+
import { Label } from "./ui/label";
|
|
11
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "./ui/card";
|
|
12
|
+
import { Alert, AlertDescription } from "./ui/alert";
|
|
13
|
+
import { Badge } from "./ui/badge";
|
|
14
|
+
|
|
15
|
+
interface LoginFormProps {
|
|
16
|
+
config: LoginFormConfig;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function LoginForm({ config }: LoginFormProps) {
|
|
20
|
+
const router = useRouter();
|
|
21
|
+
const searchParams = useSearchParams();
|
|
22
|
+
const callbackUrl = searchParams.get("callbackUrl") || config.callbackUrlDefault;
|
|
23
|
+
|
|
24
|
+
const [email, setEmail] = useState("");
|
|
25
|
+
const [password, setPassword] = useState("");
|
|
26
|
+
const [loading, setLoading] = useState(false);
|
|
27
|
+
const [error, setError] = useState("");
|
|
28
|
+
|
|
29
|
+
const handleSubmit = async (e: React.FormEvent) => {
|
|
30
|
+
e.preventDefault();
|
|
31
|
+
if (loading) return;
|
|
32
|
+
setLoading(true);
|
|
33
|
+
setError("");
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const { data, error: signInError } = await authClient.signIn.email({
|
|
37
|
+
email,
|
|
38
|
+
password,
|
|
39
|
+
callbackURL: callbackUrl,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
if (signInError) {
|
|
43
|
+
setError(signInError.message || "Erro ao entrar");
|
|
44
|
+
setLoading(false);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (config.onPostLoginSuccess) {
|
|
49
|
+
const gauthToken = (data as any)?.user?.gauthToken;
|
|
50
|
+
const redirectUrl = await config.onPostLoginSuccess(data?.user, gauthToken);
|
|
51
|
+
if (redirectUrl) {
|
|
52
|
+
router.push(redirectUrl);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
router.push(callbackUrl);
|
|
58
|
+
} catch {
|
|
59
|
+
setError("Erro ao entrar");
|
|
60
|
+
setLoading(false);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<div className="flex min-h-svh flex-col items-center justify-center p-6 md:p-10">
|
|
66
|
+
<div className="w-full max-w-sm">
|
|
67
|
+
<Card>
|
|
68
|
+
<CardHeader className="text-center">
|
|
69
|
+
<div className="flex items-center justify-center gap-2 mb-2">
|
|
70
|
+
{config.icon}
|
|
71
|
+
<CardTitle className="text-xl">
|
|
72
|
+
{config.appName}
|
|
73
|
+
</CardTitle>
|
|
74
|
+
{config.appBadge && (
|
|
75
|
+
<Badge variant={config.appBadge.variant}>
|
|
76
|
+
{config.appBadge.text}
|
|
77
|
+
</Badge>
|
|
78
|
+
)}
|
|
79
|
+
</div>
|
|
80
|
+
<CardDescription>{config.description}</CardDescription>
|
|
81
|
+
</CardHeader>
|
|
82
|
+
<CardContent>
|
|
83
|
+
<form onSubmit={handleSubmit}>
|
|
84
|
+
<div className="grid gap-4">
|
|
85
|
+
{error && (
|
|
86
|
+
<Alert variant="destructive">
|
|
87
|
+
<AlertDescription>{error}</AlertDescription>
|
|
88
|
+
</Alert>
|
|
89
|
+
)}
|
|
90
|
+
<div className="grid gap-2">
|
|
91
|
+
<Label htmlFor="email">Email</Label>
|
|
92
|
+
<Input
|
|
93
|
+
id="email"
|
|
94
|
+
type="email"
|
|
95
|
+
placeholder="email@exemplo.com"
|
|
96
|
+
value={email}
|
|
97
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
98
|
+
required
|
|
99
|
+
/>
|
|
100
|
+
</div>
|
|
101
|
+
<div className="grid gap-2">
|
|
102
|
+
<Label htmlFor="password">Senha</Label>
|
|
103
|
+
<Input
|
|
104
|
+
id="password"
|
|
105
|
+
type="password"
|
|
106
|
+
value={password}
|
|
107
|
+
onChange={(e) => setPassword(e.target.value)}
|
|
108
|
+
required
|
|
109
|
+
/>
|
|
110
|
+
</div>
|
|
111
|
+
<Button type="submit" className="w-full" disabled={loading}>
|
|
112
|
+
{loading && <Loader2 className="animate-spin" />}
|
|
113
|
+
Entrar
|
|
114
|
+
</Button>
|
|
115
|
+
</div>
|
|
116
|
+
</form>
|
|
117
|
+
</CardContent>
|
|
118
|
+
</Card>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useTheme } from "next-themes";
|
|
4
|
+
import { Sun, Moon } from "lucide-react";
|
|
5
|
+
import { Button } from "./ui/button";
|
|
6
|
+
|
|
7
|
+
export function ThemeToggle() {
|
|
8
|
+
const { theme, setTheme } = useTheme();
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<Button
|
|
12
|
+
variant="ghost"
|
|
13
|
+
size="icon"
|
|
14
|
+
onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
|
|
15
|
+
>
|
|
16
|
+
<Sun className="h-5 w-5 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
|
|
17
|
+
<Moon className="absolute h-5 w-5 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
|
|
18
|
+
<span className="sr-only">Alternar tema</span>
|
|
19
|
+
</Button>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
+
|
|
4
|
+
import { cn } from "../../lib/utils"
|
|
5
|
+
|
|
6
|
+
const alertVariants = cva("grid gap-0.5 rounded-lg border px-4 py-3 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2.5 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4 w-full relative group/alert", {
|
|
7
|
+
variants: {
|
|
8
|
+
variant: {
|
|
9
|
+
default: "bg-card text-card-foreground",
|
|
10
|
+
destructive: "text-destructive bg-card *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current",
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
defaultVariants: {
|
|
14
|
+
variant: "default",
|
|
15
|
+
},
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
function Alert({
|
|
19
|
+
className,
|
|
20
|
+
variant,
|
|
21
|
+
...props
|
|
22
|
+
}: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
|
|
23
|
+
return (
|
|
24
|
+
<div
|
|
25
|
+
data-slot="alert"
|
|
26
|
+
role="alert"
|
|
27
|
+
className={cn(alertVariants({ variant }), className)}
|
|
28
|
+
{...props}
|
|
29
|
+
/>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|
34
|
+
return (
|
|
35
|
+
<div
|
|
36
|
+
data-slot="alert-title"
|
|
37
|
+
className={cn(
|
|
38
|
+
"font-medium group-has-[>svg]/alert:col-start-2 [&_a]:hover:text-foreground [&_a]:underline [&_a]:underline-offset-3",
|
|
39
|
+
className
|
|
40
|
+
)}
|
|
41
|
+
{...props}
|
|
42
|
+
/>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function AlertDescription({
|
|
47
|
+
className,
|
|
48
|
+
...props
|
|
49
|
+
}: React.ComponentProps<"div">) {
|
|
50
|
+
return (
|
|
51
|
+
<div
|
|
52
|
+
data-slot="alert-description"
|
|
53
|
+
className={cn(
|
|
54
|
+
"text-muted-foreground text-sm text-balance md:text-pretty [&_p:not(:last-child)]:mb-4 [&_a]:hover:text-foreground [&_a]:underline [&_a]:underline-offset-3",
|
|
55
|
+
className
|
|
56
|
+
)}
|
|
57
|
+
{...props}
|
|
58
|
+
/>
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function AlertAction({ className, ...props }: React.ComponentProps<"div">) {
|
|
63
|
+
return (
|
|
64
|
+
<div
|
|
65
|
+
data-slot="alert-action"
|
|
66
|
+
className={cn("absolute top-2.5 right-3", className)}
|
|
67
|
+
{...props}
|
|
68
|
+
/>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export { Alert, AlertTitle, AlertDescription, AlertAction }
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Avatar as AvatarPrimitive } from "radix-ui"
|
|
5
|
+
|
|
6
|
+
import { cn } from "../../lib/utils"
|
|
7
|
+
|
|
8
|
+
function Avatar({
|
|
9
|
+
className,
|
|
10
|
+
size = "default",
|
|
11
|
+
...props
|
|
12
|
+
}: React.ComponentProps<typeof AvatarPrimitive.Root> & {
|
|
13
|
+
size?: "default" | "sm" | "lg"
|
|
14
|
+
}) {
|
|
15
|
+
return (
|
|
16
|
+
<AvatarPrimitive.Root
|
|
17
|
+
data-slot="avatar"
|
|
18
|
+
data-size={size}
|
|
19
|
+
className={cn(
|
|
20
|
+
"size-8 rounded-full after:rounded-full data-[size=lg]:size-10 data-[size=sm]:size-6 after:border-border group/avatar relative flex shrink-0 select-none after:absolute after:inset-0 after:border after:mix-blend-darken dark:after:mix-blend-lighten",
|
|
21
|
+
className
|
|
22
|
+
)}
|
|
23
|
+
{...props}
|
|
24
|
+
/>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function AvatarImage({
|
|
29
|
+
className,
|
|
30
|
+
...props
|
|
31
|
+
}: React.ComponentProps<typeof AvatarPrimitive.Image>) {
|
|
32
|
+
return (
|
|
33
|
+
<AvatarPrimitive.Image
|
|
34
|
+
data-slot="avatar-image"
|
|
35
|
+
className={cn(
|
|
36
|
+
"rounded-full aspect-square size-full object-cover",
|
|
37
|
+
className
|
|
38
|
+
)}
|
|
39
|
+
{...props}
|
|
40
|
+
/>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function AvatarFallback({
|
|
45
|
+
className,
|
|
46
|
+
...props
|
|
47
|
+
}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
|
|
48
|
+
return (
|
|
49
|
+
<AvatarPrimitive.Fallback
|
|
50
|
+
data-slot="avatar-fallback"
|
|
51
|
+
className={cn(
|
|
52
|
+
"bg-muted text-muted-foreground rounded-full flex size-full items-center justify-center text-sm group-data-[size=sm]/avatar:text-xs",
|
|
53
|
+
className
|
|
54
|
+
)}
|
|
55
|
+
{...props}
|
|
56
|
+
/>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) {
|
|
61
|
+
return (
|
|
62
|
+
<span
|
|
63
|
+
data-slot="avatar-badge"
|
|
64
|
+
className={cn(
|
|
65
|
+
"bg-primary text-primary-foreground ring-background absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-blend-color ring-2 select-none",
|
|
66
|
+
"group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden",
|
|
67
|
+
"group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2",
|
|
68
|
+
"group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2",
|
|
69
|
+
className
|
|
70
|
+
)}
|
|
71
|
+
{...props}
|
|
72
|
+
/>
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
77
|
+
return (
|
|
78
|
+
<div
|
|
79
|
+
data-slot="avatar-group"
|
|
80
|
+
className={cn(
|
|
81
|
+
"*:data-[slot=avatar]:ring-background group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2",
|
|
82
|
+
className
|
|
83
|
+
)}
|
|
84
|
+
{...props}
|
|
85
|
+
/>
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function AvatarGroupCount({
|
|
90
|
+
className,
|
|
91
|
+
...props
|
|
92
|
+
}: React.ComponentProps<"div">) {
|
|
93
|
+
return (
|
|
94
|
+
<div
|
|
95
|
+
data-slot="avatar-group-count"
|
|
96
|
+
className={cn("bg-muted text-muted-foreground size-8 rounded-full text-sm group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3 ring-background relative flex shrink-0 items-center justify-center ring-2", className)}
|
|
97
|
+
{...props}
|
|
98
|
+
/>
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export {
|
|
103
|
+
Avatar,
|
|
104
|
+
AvatarImage,
|
|
105
|
+
AvatarFallback,
|
|
106
|
+
AvatarGroup,
|
|
107
|
+
AvatarGroupCount,
|
|
108
|
+
AvatarBadge,
|
|
109
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
+
import { Slot } from "radix-ui"
|
|
4
|
+
|
|
5
|
+
import { cn } from "../../lib/utils"
|
|
6
|
+
|
|
7
|
+
const badgeVariants = cva(
|
|
8
|
+
"h-5 gap-1 rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium transition-all has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&>svg]:size-3! inline-flex items-center justify-center w-fit whitespace-nowrap shrink-0 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive overflow-hidden group/badge",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
|
|
13
|
+
secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
|
|
14
|
+
destructive: "bg-destructive/10 [a]:hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 text-destructive dark:bg-destructive/20",
|
|
15
|
+
outline: "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
|
|
16
|
+
ghost: "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
|
|
17
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
defaultVariants: {
|
|
21
|
+
variant: "default",
|
|
22
|
+
},
|
|
23
|
+
}
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
function Badge({
|
|
27
|
+
className,
|
|
28
|
+
variant = "default",
|
|
29
|
+
asChild = false,
|
|
30
|
+
...props
|
|
31
|
+
}: React.ComponentProps<"span"> &
|
|
32
|
+
VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
|
|
33
|
+
const Comp = asChild ? Slot.Root : "span"
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<Comp
|
|
37
|
+
data-slot="badge"
|
|
38
|
+
data-variant={variant}
|
|
39
|
+
className={cn(badgeVariants({ variant }), className)}
|
|
40
|
+
{...props}
|
|
41
|
+
/>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { Badge, badgeVariants }
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { Slot } from "radix-ui"
|
|
3
|
+
import { ChevronRight, MoreHorizontal } from "lucide-react"
|
|
4
|
+
|
|
5
|
+
import { cn } from "../../lib/utils"
|
|
6
|
+
|
|
7
|
+
function Breadcrumb({ className, ...props }: React.ComponentProps<"nav">) {
|
|
8
|
+
return (
|
|
9
|
+
<nav
|
|
10
|
+
aria-label="breadcrumb"
|
|
11
|
+
data-slot="breadcrumb"
|
|
12
|
+
className={cn(className)}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
|
|
19
|
+
return (
|
|
20
|
+
<ol
|
|
21
|
+
data-slot="breadcrumb-list"
|
|
22
|
+
className={cn(
|
|
23
|
+
"text-muted-foreground gap-1.5 text-sm sm:gap-2.5 flex flex-wrap items-center wrap-break-word",
|
|
24
|
+
className
|
|
25
|
+
)}
|
|
26
|
+
{...props}
|
|
27
|
+
/>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
|
|
32
|
+
return (
|
|
33
|
+
<li
|
|
34
|
+
data-slot="breadcrumb-item"
|
|
35
|
+
className={cn("gap-1.5 inline-flex items-center", className)}
|
|
36
|
+
{...props}
|
|
37
|
+
/>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function BreadcrumbLink({
|
|
42
|
+
asChild,
|
|
43
|
+
className,
|
|
44
|
+
...props
|
|
45
|
+
}: React.ComponentProps<"a"> & {
|
|
46
|
+
asChild?: boolean
|
|
47
|
+
}) {
|
|
48
|
+
const Comp = asChild ? Slot.Root : "a"
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<Comp
|
|
52
|
+
data-slot="breadcrumb-link"
|
|
53
|
+
className={cn("hover:text-foreground transition-colors", className)}
|
|
54
|
+
{...props}
|
|
55
|
+
/>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
|
|
60
|
+
return (
|
|
61
|
+
<span
|
|
62
|
+
data-slot="breadcrumb-page"
|
|
63
|
+
role="link"
|
|
64
|
+
aria-disabled="true"
|
|
65
|
+
aria-current="page"
|
|
66
|
+
className={cn("text-foreground font-normal", className)}
|
|
67
|
+
{...props}
|
|
68
|
+
/>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function BreadcrumbSeparator({
|
|
73
|
+
children,
|
|
74
|
+
className,
|
|
75
|
+
...props
|
|
76
|
+
}: React.ComponentProps<"li">) {
|
|
77
|
+
return (
|
|
78
|
+
<li
|
|
79
|
+
data-slot="breadcrumb-separator"
|
|
80
|
+
role="presentation"
|
|
81
|
+
aria-hidden="true"
|
|
82
|
+
className={cn("[&>svg]:size-3.5", className)}
|
|
83
|
+
{...props}
|
|
84
|
+
>
|
|
85
|
+
{children ?? (
|
|
86
|
+
<ChevronRight />
|
|
87
|
+
)}
|
|
88
|
+
</li>
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function BreadcrumbEllipsis({
|
|
93
|
+
className,
|
|
94
|
+
...props
|
|
95
|
+
}: React.ComponentProps<"span">) {
|
|
96
|
+
return (
|
|
97
|
+
<span
|
|
98
|
+
data-slot="breadcrumb-ellipsis"
|
|
99
|
+
role="presentation"
|
|
100
|
+
aria-hidden="true"
|
|
101
|
+
className={cn(
|
|
102
|
+
"size-5 [&>svg]:size-4 flex items-center justify-center",
|
|
103
|
+
className
|
|
104
|
+
)}
|
|
105
|
+
{...props}
|
|
106
|
+
>
|
|
107
|
+
<MoreHorizontal />
|
|
108
|
+
<span className="sr-only">More</span>
|
|
109
|
+
</span>
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export {
|
|
114
|
+
Breadcrumb,
|
|
115
|
+
BreadcrumbList,
|
|
116
|
+
BreadcrumbItem,
|
|
117
|
+
BreadcrumbLink,
|
|
118
|
+
BreadcrumbPage,
|
|
119
|
+
BreadcrumbSeparator,
|
|
120
|
+
BreadcrumbEllipsis,
|
|
121
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
+
import { Slot } from "radix-ui"
|
|
4
|
+
|
|
5
|
+
import { cn } from "../../lib/utils"
|
|
6
|
+
|
|
7
|
+
const buttonVariants = cva(
|
|
8
|
+
"focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-md border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-3 aria-invalid:ring-3 [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/80",
|
|
13
|
+
outline: "border-border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground shadow-xs",
|
|
14
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
|
|
15
|
+
ghost: "hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground",
|
|
16
|
+
destructive: "bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40 dark:hover:bg-destructive/30",
|
|
17
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
18
|
+
},
|
|
19
|
+
size: {
|
|
20
|
+
default: "h-9 gap-1.5 px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
|
21
|
+
xs: "h-6 gap-1 rounded-[min(var(--radius-md),8px)] px-2 text-xs in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
|
|
22
|
+
sm: "h-8 gap-1 rounded-[min(var(--radius-md),10px)] px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5",
|
|
23
|
+
lg: "h-10 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3",
|
|
24
|
+
icon: "size-9",
|
|
25
|
+
"icon-xs": "size-6 rounded-[min(var(--radius-md),8px)] in-data-[slot=button-group]:rounded-md [&_svg:not([class*='size-'])]:size-3",
|
|
26
|
+
"icon-sm": "size-8 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-md",
|
|
27
|
+
"icon-lg": "size-10",
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
defaultVariants: {
|
|
31
|
+
variant: "default",
|
|
32
|
+
size: "default",
|
|
33
|
+
},
|
|
34
|
+
}
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
function Button({
|
|
38
|
+
className,
|
|
39
|
+
variant = "default",
|
|
40
|
+
size = "default",
|
|
41
|
+
asChild = false,
|
|
42
|
+
...props
|
|
43
|
+
}: React.ComponentProps<"button"> &
|
|
44
|
+
VariantProps<typeof buttonVariants> & {
|
|
45
|
+
asChild?: boolean
|
|
46
|
+
}) {
|
|
47
|
+
const Comp = asChild ? Slot.Root : "button"
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<Comp
|
|
51
|
+
data-slot="button"
|
|
52
|
+
data-variant={variant}
|
|
53
|
+
data-size={size}
|
|
54
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
55
|
+
{...props}
|
|
56
|
+
/>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export { Button, buttonVariants }
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
import { cn } from "../../lib/utils"
|
|
4
|
+
|
|
5
|
+
function Card({
|
|
6
|
+
className,
|
|
7
|
+
size = "default",
|
|
8
|
+
...props
|
|
9
|
+
}: React.ComponentProps<"div"> & { size?: "default" | "sm" }) {
|
|
10
|
+
return (
|
|
11
|
+
<div
|
|
12
|
+
data-slot="card"
|
|
13
|
+
data-size={size}
|
|
14
|
+
className={cn("ring-foreground/10 bg-card text-card-foreground gap-6 overflow-hidden rounded-xl py-6 text-sm shadow-xs ring-1 has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl group/card flex flex-col", className)}
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
21
|
+
return (
|
|
22
|
+
<div
|
|
23
|
+
data-slot="card-header"
|
|
24
|
+
className={cn(
|
|
25
|
+
"gap-1 rounded-t-xl px-6 group-data-[size=sm]/card:px-4 [.border-b]:pb-6 group-data-[size=sm]/card:[.border-b]:pb-4 group/card-header @container/card-header grid auto-rows-min items-start has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto]",
|
|
26
|
+
className
|
|
27
|
+
)}
|
|
28
|
+
{...props}
|
|
29
|
+
/>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|
34
|
+
return (
|
|
35
|
+
<div
|
|
36
|
+
data-slot="card-title"
|
|
37
|
+
className={cn("text-base leading-normal font-medium group-data-[size=sm]/card:text-sm", className)}
|
|
38
|
+
{...props}
|
|
39
|
+
/>
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
|
|
44
|
+
return (
|
|
45
|
+
<div
|
|
46
|
+
data-slot="card-description"
|
|
47
|
+
className={cn("text-muted-foreground text-sm", className)}
|
|
48
|
+
{...props}
|
|
49
|
+
/>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
|
|
54
|
+
return (
|
|
55
|
+
<div
|
|
56
|
+
data-slot="card-action"
|
|
57
|
+
className={cn(
|
|
58
|
+
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
|
|
59
|
+
className
|
|
60
|
+
)}
|
|
61
|
+
{...props}
|
|
62
|
+
/>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
67
|
+
return (
|
|
68
|
+
<div
|
|
69
|
+
data-slot="card-content"
|
|
70
|
+
className={cn("px-6 group-data-[size=sm]/card:px-4", className)}
|
|
71
|
+
{...props}
|
|
72
|
+
/>
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|
77
|
+
return (
|
|
78
|
+
<div
|
|
79
|
+
data-slot="card-footer"
|
|
80
|
+
className={cn("rounded-b-xl px-6 group-data-[size=sm]/card:px-4 [.border-t]:pt-6 group-data-[size=sm]/card:[.border-t]:pt-4 flex items-center", className)}
|
|
81
|
+
{...props}
|
|
82
|
+
/>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export {
|
|
87
|
+
Card,
|
|
88
|
+
CardHeader,
|
|
89
|
+
CardFooter,
|
|
90
|
+
CardTitle,
|
|
91
|
+
CardAction,
|
|
92
|
+
CardDescription,
|
|
93
|
+
CardContent,
|
|
94
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { Collapsible as CollapsiblePrimitive } from "radix-ui"
|
|
5
|
+
|
|
6
|
+
function Collapsible({
|
|
7
|
+
...props
|
|
8
|
+
}: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
|
|
9
|
+
return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function CollapsibleTrigger({
|
|
13
|
+
...props
|
|
14
|
+
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {
|
|
15
|
+
return (
|
|
16
|
+
<CollapsiblePrimitive.CollapsibleTrigger
|
|
17
|
+
data-slot="collapsible-trigger"
|
|
18
|
+
{...props}
|
|
19
|
+
/>
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function CollapsibleContent({
|
|
24
|
+
...props
|
|
25
|
+
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {
|
|
26
|
+
return (
|
|
27
|
+
<CollapsiblePrimitive.CollapsibleContent
|
|
28
|
+
data-slot="collapsible-content"
|
|
29
|
+
{...props}
|
|
30
|
+
/>
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export { Collapsible, CollapsibleTrigger, CollapsibleContent }
|