better-ts-stack 2.1.2 → 2.2.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/README.md +16 -8
- package/dist/args.js +2 -1
- package/dist/builder/buildPlanner.js +7 -1
- package/dist/builder/configGenerator.js +28 -3
- package/dist/builder/index.js +2 -1
- package/dist/builder/readmeGenerator.js +2 -2
- package/dist/output/nextSteps.js +2 -4
- package/dist/prompts/stack.js +3 -5
- package/dist/types/index.js +1 -1
- package/package.json +1 -1
- package/templates/backend/express/src/env.ts.hbs +13 -2
- package/templates/backend/express/src/index.ts.hbs +31 -2
- package/templates/backend/nestjs/src/app.module.ts.hbs +4 -1
- package/templates/backend/nestjs/src/env.ts.hbs +13 -2
- package/templates/backend/nestjs/src/health/health.controller.ts.hbs +7 -0
- package/templates/backend/nestjs/src/{main.ts → main.ts.hbs} +11 -0
- package/templates/frontend/nextjs/app/globals.css +119 -79
- package/templates/frontend/nextjs/app/layout.tsx +13 -11
- package/templates/frontend/nextjs/app/page.tsx.hbs +63 -43
- package/templates/frontend/nextjs/components/ui/alert.tsx +75 -0
- package/templates/frontend/nextjs/components/ui/badge.tsx +51 -0
- package/templates/frontend/nextjs/components/ui/button.tsx +33 -27
- package/templates/frontend/nextjs/components/ui/card.tsx +76 -34
- package/templates/frontend/nextjs/components/ui/field.tsx +203 -32
- package/templates/frontend/nextjs/components/ui/input.tsx +8 -7
- package/templates/frontend/nextjs/components/ui/label.tsx +19 -0
- package/templates/frontend/nextjs/components/ui/separator.tsx +24 -0
- package/templates/frontend/nextjs/components/ui/skeleton.tsx +13 -0
- package/templates/frontend/nextjs/components/ui/spinner.tsx +10 -0
- package/templates/frontend/nextjs/components.json +11 -5
- package/templates/frontend/nextjs/config.json +4 -3
- package/templates/frontend/nextjs/lib/utils.ts +1 -6
- package/templates/frontend/tanstack/components.json +12 -5
- package/templates/frontend/tanstack/config.json +5 -2
- package/templates/frontend/tanstack/src/components/ui/alert.tsx +75 -0
- package/templates/frontend/tanstack/src/components/ui/badge.tsx +51 -0
- package/templates/frontend/tanstack/src/components/ui/button.tsx +33 -27
- package/templates/frontend/tanstack/src/components/ui/card.tsx +76 -34
- package/templates/frontend/tanstack/src/components/ui/field.tsx +201 -32
- package/templates/frontend/tanstack/src/components/ui/input.tsx +8 -7
- package/templates/frontend/tanstack/src/components/ui/label.tsx +17 -0
- package/templates/frontend/tanstack/src/components/ui/separator.tsx +24 -0
- package/templates/frontend/tanstack/src/components/ui/skeleton.tsx +13 -0
- package/templates/frontend/tanstack/src/components/ui/spinner.tsx +10 -0
- package/templates/frontend/tanstack/src/lib/utils.ts +1 -6
- package/templates/frontend/tanstack/src/routes/index.tsx.hbs +64 -45
- package/templates/frontend/tanstack/src/styles/app.css +100 -55
- package/templates/modules/agents/AGENTS.md.hbs +29 -21
- package/templates/modules/auth/express/config.json +12 -11
- package/templates/modules/auth/express/src/lib/auth.ts.hbs +143 -0
- package/templates/modules/auth/express/src/lib/email.ts +37 -0
- package/templates/modules/auth/express/src/middleware/requireAuth.ts +29 -0
- package/templates/modules/auth/express/src/routes/me.ts +13 -0
- package/templates/modules/auth/nestjs/config.json +13 -11
- package/templates/modules/auth/nestjs/src/auth/auth.module.ts.hbs +64 -14
- package/templates/modules/auth/nestjs/src/auth/auth.ts.hbs +152 -0
- package/templates/modules/auth/nestjs/src/auth/email.ts +37 -0
- package/templates/modules/auth/nestjs/src/me/me.controller.ts +15 -0
- package/templates/modules/auth/nextjs/app/dashboard/page.tsx +101 -45
- package/templates/modules/auth/nextjs/app/forgot-password/page.tsx +11 -0
- package/templates/modules/auth/nextjs/app/reset-password/page.tsx +17 -0
- package/templates/modules/auth/nextjs/app/sign-in/page.tsx +23 -17
- package/templates/modules/auth/nextjs/app/sign-up/page.tsx +25 -19
- package/templates/modules/auth/nextjs/components/auth/change-password-form.tsx +125 -0
- package/templates/modules/auth/nextjs/components/auth/email-verification-card.tsx +91 -0
- package/templates/modules/auth/nextjs/components/auth/forgot-password-form.tsx +133 -0
- package/templates/modules/auth/nextjs/components/auth/reset-password-form.tsx +155 -0
- package/templates/modules/auth/nextjs/components/auth/sign-in-form.tsx +46 -32
- package/templates/modules/auth/nextjs/components/auth/sign-out-button.tsx +6 -5
- package/templates/modules/auth/nextjs/components/auth/sign-up-form.tsx +36 -30
- package/templates/modules/auth/nextjs/components/auth/social-buttons.tsx +73 -0
- package/templates/modules/auth/nextjs/config.json +8 -2
- package/templates/modules/auth/nextjs/lib/auth-schema.ts +27 -2
- package/templates/modules/auth/nextjs/lib/auth.ts.hbs +65 -0
- package/templates/modules/auth/nextjs/lib/email.ts +43 -0
- package/templates/modules/auth/nextjs/lib/session.ts +12 -0
- package/templates/modules/auth/tanstack/config.json +9 -3
- package/templates/modules/auth/tanstack/src/components/auth/change-password-form.tsx +123 -0
- package/templates/modules/auth/tanstack/src/components/auth/email-verification-card.tsx +89 -0
- package/templates/modules/auth/tanstack/src/components/auth/forgot-password-form.tsx +131 -0
- package/templates/modules/auth/tanstack/src/components/auth/reset-password-form.tsx +149 -0
- package/templates/modules/auth/tanstack/src/components/auth/sign-in-form.tsx +44 -35
- package/templates/modules/auth/tanstack/src/components/auth/sign-out-button.tsx +7 -6
- package/templates/modules/auth/tanstack/src/components/auth/sign-up-form.tsx +37 -34
- package/templates/modules/auth/tanstack/src/components/auth/social-buttons.tsx +71 -0
- package/templates/modules/auth/tanstack/src/lib/auth-functions.ts +7 -1
- package/templates/modules/auth/tanstack/src/lib/auth-schema.ts +27 -2
- package/templates/modules/auth/tanstack/src/lib/auth.ts.hbs +61 -1
- package/templates/modules/auth/tanstack/src/lib/email.ts +43 -0
- package/templates/modules/auth/tanstack/src/routeTree.gen.ts +61 -3
- package/templates/modules/auth/tanstack/src/routes/dashboard.tsx +40 -32
- package/templates/modules/auth/tanstack/src/routes/forgot-password.tsx +17 -0
- package/templates/modules/auth/tanstack/src/routes/reset-password.tsx +25 -0
- package/templates/modules/auth/tanstack/src/routes/sign-in.tsx +19 -12
- package/templates/modules/auth/tanstack/src/routes/sign-up.tsx +21 -14
- package/templates/modules/drizzle/express/src/lib/schema.ts.hbs +90 -0
- package/templates/modules/drizzle/nestjs/src/database/schema.ts.hbs +90 -0
- package/templates/modules/mongoose/nestjs/config.json +2 -1
- package/templates/modules/prisma/express/prisma/schema.prisma.hbs +92 -0
- package/templates/modules/prisma/nestjs/prisma/schema.prisma.hbs +91 -0
- package/templates/modules/rate-limit/express/src/middleware/rateLimit.ts +0 -8
- package/templates/modules/auth/express/src/controllers/authController.ts.hbs +0 -116
- package/templates/modules/auth/express/src/lib/jwt.ts.hbs +0 -27
- package/templates/modules/auth/express/src/middleware/requireAuth.ts.hbs +0 -25
- package/templates/modules/auth/express/src/routes/auth.ts.hbs +0 -27
- package/templates/modules/auth/express/src/services/userStore.ts.hbs +0 -117
- package/templates/modules/auth/nestjs/src/auth/auth.controller.ts.hbs +0 -61
- package/templates/modules/auth/nestjs/src/auth/auth.guard.ts +0 -36
- package/templates/modules/auth/nestjs/src/auth/auth.schemas.ts +0 -35
- package/templates/modules/auth/nestjs/src/auth/auth.service.ts +0 -70
- package/templates/modules/auth/nestjs/src/auth/jwt.ts +0 -27
- package/templates/modules/auth/nestjs/src/auth/users.service.ts.hbs +0 -144
- package/templates/modules/auth/nestjs/src/auth/zod-validation.pipe.ts +0 -16
- package/templates/modules/drizzle/express/src/lib/schema.ts +0 -12
- package/templates/modules/drizzle/nestjs/src/database/schema.ts +0 -12
- package/templates/modules/prisma/express/prisma/schema.prisma +0 -24
- package/templates/modules/prisma/nestjs/prisma/schema.prisma +0 -23
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import type { Metadata } from "next";
|
|
2
|
-
import {
|
|
2
|
+
import { Geist_Mono, Inter } from "next/font/google";
|
|
3
|
+
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
|
|
3
6
|
import "./globals.css";
|
|
4
7
|
|
|
5
|
-
const
|
|
6
|
-
variable: "--font-
|
|
8
|
+
const fontSans = Inter({
|
|
9
|
+
variable: "--font-sans",
|
|
7
10
|
subsets: ["latin"],
|
|
8
11
|
});
|
|
9
12
|
|
|
10
|
-
const
|
|
11
|
-
variable: "--font-
|
|
13
|
+
const fontMono = Geist_Mono({
|
|
14
|
+
variable: "--font-mono",
|
|
12
15
|
subsets: ["latin"],
|
|
13
16
|
});
|
|
14
17
|
|
|
@@ -23,12 +26,11 @@ export default function RootLayout({
|
|
|
23
26
|
children: React.ReactNode;
|
|
24
27
|
}>) {
|
|
25
28
|
return (
|
|
26
|
-
<html
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
</body>
|
|
29
|
+
<html
|
|
30
|
+
lang="en"
|
|
31
|
+
className={cn("font-sans antialiased", fontSans.variable, fontMono.variable)}
|
|
32
|
+
>
|
|
33
|
+
<body>{children}</body>
|
|
32
34
|
</html>
|
|
33
35
|
);
|
|
34
36
|
}
|
|
@@ -2,14 +2,21 @@ import Link from "next/link";
|
|
|
2
2
|
|
|
3
3
|
import { ArrowRight } from "lucide-react";
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
5
|
+
import { Alert, AlertDescription } from "@/components/ui/alert";
|
|
6
|
+
import { Badge } from "@/components/ui/badge";
|
|
7
|
+
import { Button } from "@/components/ui/button";
|
|
8
|
+
import {
|
|
9
|
+
Card,
|
|
10
|
+
CardContent,
|
|
11
|
+
CardFooter,
|
|
12
|
+
CardHeader,
|
|
13
|
+
CardTitle,
|
|
14
|
+
} from "@/components/ui/card";
|
|
8
15
|
|
|
9
16
|
const featureList = [
|
|
10
17
|
"Next.js 16 App Router",
|
|
11
18
|
"React 19 and TypeScript",
|
|
12
|
-
"Tailwind CSS v4",
|
|
19
|
+
"Tailwind CSS v4 and shadcn/ui on Base UI",
|
|
13
20
|
{{#if useAuth}}"Better Auth-ready session flow",{{/if}}
|
|
14
21
|
{{#if (eq database "prisma")}}"Prisma database tooling",{{/if}}
|
|
15
22
|
{{#if (eq database "drizzle")}}"Drizzle database tooling",{{/if}}
|
|
@@ -18,66 +25,74 @@ const featureList = [
|
|
|
18
25
|
|
|
19
26
|
export default function Home() {
|
|
20
27
|
return (
|
|
21
|
-
<main className="min-h-
|
|
22
|
-
<div className="mx-auto flex min-h-
|
|
28
|
+
<main className="min-h-svh">
|
|
29
|
+
<div className="mx-auto flex min-h-svh max-w-6xl flex-col justify-center gap-12 px-6 py-16 sm:px-10 lg:px-12">
|
|
23
30
|
<section className="grid gap-10 lg:grid-cols-[1.2fr_0.8fr] lg:items-center">
|
|
24
|
-
<div className="
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
</span>
|
|
28
|
-
<div className="space-y-4">
|
|
31
|
+
<div className="flex flex-col gap-6">
|
|
32
|
+
<Badge variant="outline">Created with better-ts-stack</Badge>
|
|
33
|
+
<div className="flex flex-col gap-4">
|
|
29
34
|
<h1 className="max-w-3xl text-4xl font-semibold tracking-tight sm:text-5xl">
|
|
30
35
|
Ship your stack faster with a polished TypeScript foundation.
|
|
31
36
|
</h1>
|
|
32
37
|
<p className="max-w-2xl text-base leading-7 text-muted-foreground sm:text-lg">
|
|
33
38
|
Your project already includes the core app shell, database wiring,
|
|
34
|
-
and
|
|
35
|
-
|
|
39
|
+
and shadcn/ui components so you can keep building instead of
|
|
40
|
+
setting up boilerplate.
|
|
36
41
|
</p>
|
|
37
42
|
</div>
|
|
38
43
|
<div className="flex flex-col gap-3 sm:flex-row">
|
|
39
44
|
{{#if useAuth}}
|
|
40
|
-
<Link
|
|
41
|
-
href="/sign-up"
|
|
42
|
-
className={cn(buttonVariants({ size: "lg" }), "gap-2")}
|
|
43
|
-
>
|
|
45
|
+
<Button size="lg" nativeButton={false} render={<Link href="/sign-up" />}>
|
|
44
46
|
Create an account
|
|
45
|
-
<ArrowRight
|
|
46
|
-
</
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
<ArrowRight data-icon="inline-end" />
|
|
48
|
+
</Button>
|
|
49
|
+
<Button
|
|
50
|
+
size="lg"
|
|
51
|
+
variant="outline"
|
|
52
|
+
nativeButton={false}
|
|
53
|
+
render={<Link href="/sign-in" />}
|
|
50
54
|
>
|
|
51
55
|
Login
|
|
52
|
-
</
|
|
56
|
+
</Button>
|
|
53
57
|
{{else}}
|
|
54
|
-
<
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
<Button
|
|
59
|
+
size="lg"
|
|
60
|
+
nativeButton={false}
|
|
61
|
+
render={
|
|
62
|
+
<Link
|
|
63
|
+
href="https://better-ts-stack.abdullahtech.me/docs"
|
|
64
|
+
target="_blank"
|
|
65
|
+
rel="noreferrer"
|
|
66
|
+
/>
|
|
67
|
+
}
|
|
59
68
|
>
|
|
60
69
|
Explore the docs
|
|
61
|
-
<ArrowRight
|
|
62
|
-
</
|
|
63
|
-
<
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
70
|
+
<ArrowRight data-icon="inline-end" />
|
|
71
|
+
</Button>
|
|
72
|
+
<Button
|
|
73
|
+
size="lg"
|
|
74
|
+
variant="outline"
|
|
75
|
+
nativeButton={false}
|
|
76
|
+
render={
|
|
77
|
+
<Link
|
|
78
|
+
href="https://nextjs.org/docs"
|
|
79
|
+
target="_blank"
|
|
80
|
+
rel="noreferrer"
|
|
81
|
+
/>
|
|
82
|
+
}
|
|
68
83
|
>
|
|
69
84
|
Next.js guide
|
|
70
|
-
</
|
|
85
|
+
</Button>
|
|
71
86
|
{{/if}}
|
|
72
87
|
</div>
|
|
73
88
|
</div>
|
|
74
89
|
|
|
75
|
-
<Card
|
|
90
|
+
<Card>
|
|
76
91
|
<CardHeader>
|
|
77
92
|
<CardTitle>Included in this starter</CardTitle>
|
|
78
93
|
</CardHeader>
|
|
79
|
-
<CardContent
|
|
80
|
-
<ul className="
|
|
94
|
+
<CardContent>
|
|
95
|
+
<ul className="flex flex-col gap-3 text-muted-foreground">
|
|
81
96
|
{featureList.map((feature) => (
|
|
82
97
|
<li key={feature} className="flex items-center gap-3">
|
|
83
98
|
<span className="size-2 rounded-full bg-primary" />
|
|
@@ -85,11 +100,16 @@ export default function Home() {
|
|
|
85
100
|
</li>
|
|
86
101
|
))}
|
|
87
102
|
</ul>
|
|
88
|
-
<div className="rounded-2xl border border-border bg-muted px-4 py-3 text-sm text-muted-foreground">
|
|
89
|
-
Start editing <code className="font-mono">app/page.tsx</code> and
|
|
90
|
-
shape the project around your product instead of setup chores.
|
|
91
|
-
</div>
|
|
92
103
|
</CardContent>
|
|
104
|
+
<CardFooter>
|
|
105
|
+
<Alert>
|
|
106
|
+
<AlertDescription>
|
|
107
|
+
Start editing <code className="font-mono">app/page.tsx</code>{" "}
|
|
108
|
+
and shape the project around your product instead of setup
|
|
109
|
+
chores.
|
|
110
|
+
</AlertDescription>
|
|
111
|
+
</Alert>
|
|
112
|
+
</CardFooter>
|
|
93
113
|
</Card>
|
|
94
114
|
</section>
|
|
95
115
|
</div>
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
+
import { cn } from "cn"
|
|
4
|
+
|
|
5
|
+
const alertVariants = cva(
|
|
6
|
+
"group/alert relative grid w-full 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",
|
|
7
|
+
{
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
default: "bg-card text-card-foreground",
|
|
11
|
+
destructive:
|
|
12
|
+
"bg-card text-destructive *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current",
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
defaultVariants: {
|
|
16
|
+
variant: "default",
|
|
17
|
+
},
|
|
18
|
+
}
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
function Alert({
|
|
22
|
+
className,
|
|
23
|
+
variant,
|
|
24
|
+
...props
|
|
25
|
+
}: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
|
|
26
|
+
return (
|
|
27
|
+
<div
|
|
28
|
+
data-slot="alert"
|
|
29
|
+
role="alert"
|
|
30
|
+
className={cn(alertVariants({ variant }), className)}
|
|
31
|
+
{...props}
|
|
32
|
+
/>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|
37
|
+
return (
|
|
38
|
+
<div
|
|
39
|
+
data-slot="alert-title"
|
|
40
|
+
className={cn(
|
|
41
|
+
"font-medium group-has-[>svg]/alert:col-start-2 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground",
|
|
42
|
+
className
|
|
43
|
+
)}
|
|
44
|
+
{...props}
|
|
45
|
+
/>
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function AlertDescription({
|
|
50
|
+
className,
|
|
51
|
+
...props
|
|
52
|
+
}: React.ComponentProps<"div">) {
|
|
53
|
+
return (
|
|
54
|
+
<div
|
|
55
|
+
data-slot="alert-description"
|
|
56
|
+
className={cn(
|
|
57
|
+
"text-sm text-balance text-muted-foreground md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4",
|
|
58
|
+
className
|
|
59
|
+
)}
|
|
60
|
+
{...props}
|
|
61
|
+
/>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function AlertAction({ className, ...props }: React.ComponentProps<"div">) {
|
|
66
|
+
return (
|
|
67
|
+
<div
|
|
68
|
+
data-slot="alert-action"
|
|
69
|
+
className={cn("absolute top-2.5 right-3", className)}
|
|
70
|
+
{...props}
|
|
71
|
+
/>
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export { Alert, AlertTitle, AlertDescription, AlertAction }
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { mergeProps } from "@base-ui/react/merge-props"
|
|
2
|
+
import { useRender } from "@base-ui/react/use-render"
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
4
|
+
import { cn } from "cn"
|
|
5
|
+
|
|
6
|
+
const badgeVariants = cva(
|
|
7
|
+
"group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
variant: {
|
|
11
|
+
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
|
|
12
|
+
secondary:
|
|
13
|
+
"bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
|
|
14
|
+
destructive:
|
|
15
|
+
"bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20",
|
|
16
|
+
outline:
|
|
17
|
+
"border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
|
|
18
|
+
ghost:
|
|
19
|
+
"hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
|
|
20
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
defaultVariants: {
|
|
24
|
+
variant: "default",
|
|
25
|
+
},
|
|
26
|
+
}
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
function Badge({
|
|
30
|
+
className,
|
|
31
|
+
variant = "default",
|
|
32
|
+
render,
|
|
33
|
+
...props
|
|
34
|
+
}: useRender.ComponentProps<"span"> & VariantProps<typeof badgeVariants>) {
|
|
35
|
+
return useRender({
|
|
36
|
+
defaultTagName: "span",
|
|
37
|
+
props: mergeProps<"span">(
|
|
38
|
+
{
|
|
39
|
+
className: cn(badgeVariants({ variant }), className),
|
|
40
|
+
},
|
|
41
|
+
props
|
|
42
|
+
),
|
|
43
|
+
render,
|
|
44
|
+
state: {
|
|
45
|
+
slot: "badge",
|
|
46
|
+
variant,
|
|
47
|
+
},
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export { Badge, badgeVariants }
|
|
@@ -1,24 +1,35 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
-
|
|
4
|
-
import { cn } from "@/lib/utils";
|
|
1
|
+
import { Button as ButtonPrimitive } from "@base-ui/react/button"
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
+
import { cn } from "cn"
|
|
5
4
|
|
|
6
5
|
const buttonVariants = cva(
|
|
7
|
-
"inline-flex items-center justify-center
|
|
6
|
+
"group/button inline-flex shrink-0 items-center justify-center rounded-md border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
8
7
|
{
|
|
9
8
|
variants: {
|
|
10
9
|
variant: {
|
|
11
|
-
default:
|
|
12
|
-
"bg-slate-950 text-white shadow-sm hover:bg-slate-800",
|
|
10
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/80",
|
|
13
11
|
outline:
|
|
14
|
-
"border
|
|
15
|
-
|
|
12
|
+
"border-border bg-background shadow-xs hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
|
|
13
|
+
secondary:
|
|
14
|
+
"bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
|
|
15
|
+
ghost:
|
|
16
|
+
"hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
|
|
17
|
+
destructive:
|
|
18
|
+
"bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
|
|
19
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
16
20
|
},
|
|
17
21
|
size: {
|
|
18
|
-
default:
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
default:
|
|
23
|
+
"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",
|
|
24
|
+
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",
|
|
25
|
+
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",
|
|
26
|
+
lg: "h-10 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
|
27
|
+
icon: "size-9",
|
|
28
|
+
"icon-xs":
|
|
29
|
+
"size-6 rounded-[min(var(--radius-md),8px)] in-data-[slot=button-group]:rounded-md [&_svg:not([class*='size-'])]:size-3",
|
|
30
|
+
"icon-sm":
|
|
31
|
+
"size-8 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-md",
|
|
32
|
+
"icon-lg": "size-10",
|
|
22
33
|
},
|
|
23
34
|
},
|
|
24
35
|
defaultVariants: {
|
|
@@ -26,26 +37,21 @@ const buttonVariants = cva(
|
|
|
26
37
|
size: "default",
|
|
27
38
|
},
|
|
28
39
|
}
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
export interface ButtonProps
|
|
32
|
-
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
33
|
-
VariantProps<typeof buttonVariants> {}
|
|
40
|
+
)
|
|
34
41
|
|
|
35
42
|
function Button({
|
|
36
43
|
className,
|
|
37
|
-
variant,
|
|
38
|
-
size,
|
|
39
|
-
type = "button",
|
|
44
|
+
variant = "default",
|
|
45
|
+
size = "default",
|
|
40
46
|
...props
|
|
41
|
-
}:
|
|
47
|
+
}: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) {
|
|
42
48
|
return (
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
className={cn(buttonVariants({ variant, size
|
|
49
|
+
<ButtonPrimitive
|
|
50
|
+
data-slot="button"
|
|
51
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
46
52
|
{...props}
|
|
47
53
|
/>
|
|
48
|
-
)
|
|
54
|
+
)
|
|
49
55
|
}
|
|
50
56
|
|
|
51
|
-
export { Button, buttonVariants }
|
|
57
|
+
export { Button, buttonVariants }
|
|
@@ -1,60 +1,102 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
|
|
3
|
-
import { cn } from "@/lib/utils";
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cn } from "cn"
|
|
4
3
|
|
|
5
4
|
function Card({
|
|
6
5
|
className,
|
|
6
|
+
size = "default",
|
|
7
7
|
...props
|
|
8
|
-
}: React.
|
|
8
|
+
}: React.ComponentProps<"div"> & { size?: "default" | "sm" }) {
|
|
9
9
|
return (
|
|
10
10
|
<div
|
|
11
|
+
data-slot="card"
|
|
12
|
+
data-size={size}
|
|
11
13
|
className={cn(
|
|
12
|
-
"
|
|
14
|
+
"group/card flex flex-col gap-(--card-spacing) overflow-hidden rounded-xl bg-card py-(--card-spacing) text-sm text-card-foreground shadow-xs ring-1 ring-foreground/10 [--card-spacing:--spacing(6)] has-[>img:first-child]:pt-0 data-[size=sm]:[--card-spacing:--spacing(4)] *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
|
|
13
15
|
className
|
|
14
16
|
)}
|
|
15
17
|
{...props}
|
|
16
18
|
/>
|
|
17
|
-
)
|
|
19
|
+
)
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
function CardHeader({
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
23
|
+
return (
|
|
24
|
+
<div
|
|
25
|
+
data-slot="card-header"
|
|
26
|
+
className={cn(
|
|
27
|
+
"group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-(--card-spacing) has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-(--card-spacing)",
|
|
28
|
+
className
|
|
29
|
+
)}
|
|
30
|
+
{...props}
|
|
31
|
+
/>
|
|
32
|
+
)
|
|
25
33
|
}
|
|
26
34
|
|
|
27
|
-
function CardTitle({
|
|
28
|
-
className,
|
|
29
|
-
...props
|
|
30
|
-
}: React.HTMLAttributes<HTMLHeadingElement>) {
|
|
35
|
+
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|
31
36
|
return (
|
|
32
|
-
<
|
|
33
|
-
|
|
37
|
+
<div
|
|
38
|
+
data-slot="card-title"
|
|
39
|
+
className={cn(
|
|
40
|
+
"font-heading text-base leading-normal font-medium group-data-[size=sm]/card:text-sm",
|
|
41
|
+
className
|
|
42
|
+
)}
|
|
34
43
|
{...props}
|
|
35
44
|
/>
|
|
36
|
-
)
|
|
45
|
+
)
|
|
37
46
|
}
|
|
38
47
|
|
|
39
|
-
function CardDescription({
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
48
|
+
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
|
|
49
|
+
return (
|
|
50
|
+
<div
|
|
51
|
+
data-slot="card-description"
|
|
52
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
53
|
+
{...props}
|
|
54
|
+
/>
|
|
55
|
+
)
|
|
44
56
|
}
|
|
45
57
|
|
|
46
|
-
function
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
58
|
+
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
|
|
59
|
+
return (
|
|
60
|
+
<div
|
|
61
|
+
data-slot="card-action"
|
|
62
|
+
className={cn(
|
|
63
|
+
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
|
|
64
|
+
className
|
|
65
|
+
)}
|
|
66
|
+
{...props}
|
|
67
|
+
/>
|
|
68
|
+
)
|
|
51
69
|
}
|
|
52
70
|
|
|
53
|
-
function
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
71
|
+
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
72
|
+
return (
|
|
73
|
+
<div
|
|
74
|
+
data-slot="card-content"
|
|
75
|
+
className={cn("flex flex-col gap-3 px-(--card-spacing)", className)}
|
|
76
|
+
{...props}
|
|
77
|
+
/>
|
|
78
|
+
)
|
|
58
79
|
}
|
|
59
80
|
|
|
60
|
-
|
|
81
|
+
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|
82
|
+
return (
|
|
83
|
+
<div
|
|
84
|
+
data-slot="card-footer"
|
|
85
|
+
className={cn(
|
|
86
|
+
"flex items-center rounded-b-xl px-(--card-spacing) [.border-t]:pt-(--card-spacing)",
|
|
87
|
+
className
|
|
88
|
+
)}
|
|
89
|
+
{...props}
|
|
90
|
+
/>
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export {
|
|
95
|
+
Card,
|
|
96
|
+
CardHeader,
|
|
97
|
+
CardFooter,
|
|
98
|
+
CardTitle,
|
|
99
|
+
CardAction,
|
|
100
|
+
CardDescription,
|
|
101
|
+
CardContent,
|
|
102
|
+
}
|