@greatapps/greatauth-ui 0.1.0 → 0.1.4

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.
@@ -0,0 +1,5 @@
1
+ export { AppShell } from "./app-shell";
2
+ export { AppSidebar } from "./app-sidebar";
3
+ export { AppHeader } from "./app-header";
4
+ export { LoginForm } from "./login-form";
5
+ export { ThemeToggle } from "./theme-toggle";
@@ -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 }