create-better-t-stack 0.1.0 → 1.0.1
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 +59 -49
- package/dist/index.js +128 -305
- package/package.json +19 -11
- package/template/base/_gitignore +2 -0
- package/template/base/package.json +18 -0
- package/template/base/packages/client/_gitignore +23 -0
- package/template/base/packages/client/components.json +21 -0
- package/template/base/packages/client/index.html +12 -0
- package/template/base/packages/client/package.json +49 -0
- package/template/base/packages/client/src/components/header.tsx +31 -0
- package/template/base/packages/client/src/components/loader.tsx +9 -0
- package/template/base/packages/client/src/components/mode-toggle.tsx +37 -0
- package/template/base/packages/client/src/components/theme-provider.tsx +73 -0
- package/template/base/packages/client/src/components/ui/button.tsx +57 -0
- package/template/base/packages/client/src/components/ui/card.tsx +92 -0
- package/template/base/packages/client/src/components/ui/checkbox.tsx +30 -0
- package/template/base/packages/client/src/components/ui/dropdown-menu.tsx +199 -0
- package/template/base/packages/client/src/components/ui/input.tsx +22 -0
- package/template/base/packages/client/src/components/ui/label.tsx +24 -0
- package/template/base/packages/client/src/components/ui/skeleton.tsx +15 -0
- package/template/base/packages/client/src/components/ui/sonner.tsx +29 -0
- package/template/base/packages/client/src/index.css +119 -0
- package/template/base/packages/client/src/lib/utils.ts +6 -0
- package/template/base/packages/client/src/main.tsx +72 -0
- package/template/base/packages/client/src/routes/__root.tsx +58 -0
- package/template/base/packages/client/src/routes/index.tsx +97 -0
- package/template/base/packages/client/src/utils/trpc.ts +4 -0
- package/template/base/packages/client/tsconfig.json +18 -0
- package/template/base/packages/client/vite.config.ts +14 -0
- package/template/base/packages/server/_gitignore +36 -0
- package/template/base/packages/server/package.json +27 -0
- package/template/base/packages/server/src/index.ts +41 -0
- package/template/base/packages/server/src/lib/context.ts +13 -0
- package/template/base/packages/server/src/lib/trpc.ts +8 -0
- package/template/base/packages/server/src/routers/index.ts +11 -0
- package/template/base/packages/server/tsconfig.json +18 -0
- package/template/base/turbo.json +27 -0
- package/template/examples/todo/packages/client/src/routes/todos.tsx +128 -0
- package/template/examples/todo/packages/server/src/routers/with-drizzle-todo.ts +44 -0
- package/template/examples/todo/packages/server/src/routers/with-prisma-todo.ts +55 -0
- package/template/with-auth/packages/client/src/components/auth-forms.tsx +13 -0
- package/template/with-auth/packages/client/src/components/header.tsx +34 -0
- package/template/with-auth/packages/client/src/components/sign-in-form.tsx +139 -0
- package/template/with-auth/packages/client/src/components/sign-up-form.tsx +164 -0
- package/template/with-auth/packages/client/src/components/user-menu.tsx +62 -0
- package/template/with-auth/packages/client/src/lib/auth-client.ts +5 -0
- package/template/with-auth/packages/client/src/main.tsx +78 -0
- package/template/with-auth/packages/client/src/routes/dashboard.tsx +36 -0
- package/template/with-auth/packages/client/src/routes/index.tsx +97 -0
- package/template/with-auth/packages/client/src/routes/login.tsx +11 -0
- package/template/with-auth/packages/server/src/index.ts +46 -0
- package/template/with-auth/packages/server/src/lib/trpc.ts +24 -0
- package/template/with-auth/packages/server/src/routers/index.ts +19 -0
- package/template/with-biome/biome.json +42 -0
- package/template/with-drizzle-postgres/packages/server/drizzle.config.ts +10 -0
- package/template/with-drizzle-postgres/packages/server/src/db/index.ts +5 -0
- package/template/with-drizzle-postgres/packages/server/src/db/schema/auth.ts +47 -0
- package/template/with-drizzle-postgres/packages/server/src/db/schema/todo.ts +7 -0
- package/template/with-drizzle-postgres/packages/server/src/routers/todo.ts +44 -0
- package/template/with-drizzle-postgres/packages/server/src/with-auth-lib/auth.ts +15 -0
- package/template/with-drizzle-postgres/packages/server/src/with-auth-lib/context.ts +18 -0
- package/template/with-drizzle-postgres/packages/server/src/with-auth-lib/trpc.ts +24 -0
- package/template/with-drizzle-sqlite/packages/server/drizzle.config.ts +11 -0
- package/template/with-drizzle-sqlite/packages/server/src/db/index.ts +9 -0
- package/template/with-drizzle-sqlite/packages/server/src/db/schema/auth.ts +61 -0
- package/template/with-drizzle-sqlite/packages/server/src/db/schema/todo.ts +7 -0
- package/template/with-drizzle-sqlite/packages/server/src/with-auth-lib/auth.ts +15 -0
- package/template/with-drizzle-sqlite/packages/server/src/with-auth-lib/context.ts +18 -0
- package/template/with-drizzle-sqlite/packages/server/src/with-auth-lib/trpc.ts +24 -0
- package/template/with-husky/.husky/pre-commit +1 -0
- package/template/with-prisma-postgres/packages/server/prisma/index.ts +5 -0
- package/template/with-prisma-postgres/packages/server/prisma/schema/auth.prisma +59 -0
- package/template/with-prisma-postgres/packages/server/prisma/schema/schema.prisma +9 -0
- package/template/with-prisma-postgres/packages/server/prisma/schema/todo.prisma +7 -0
- package/template/with-prisma-postgres/packages/server/src/with-auth-lib/auth.ts +17 -0
- package/template/with-prisma-postgres/packages/server/src/with-auth-lib/context.ts +18 -0
- package/template/with-prisma-postgres/packages/server/src/with-auth-lib/trpc.ts +24 -0
- package/template/with-prisma-sqlite/packages/server/prisma/index.ts +5 -0
- package/template/with-prisma-sqlite/packages/server/prisma/schema/auth.prisma +59 -0
- package/template/with-prisma-sqlite/packages/server/prisma/schema/schema.prisma +8 -0
- package/template/with-prisma-sqlite/packages/server/prisma/schema/todo.prisma +7 -0
- package/template/with-prisma-sqlite/packages/server/src/with-auth-lib/auth.ts +17 -0
- package/template/with-prisma-sqlite/packages/server/src/with-auth-lib/context.ts +18 -0
- package/template/with-prisma-sqlite/packages/server/src/with-auth-lib/trpc.ts +24 -0
- package/template/with-pwa/packages/client/public/logo.png +0 -0
- package/template/with-pwa/packages/client/pwa-assets.config.ts +12 -0
- package/template/with-pwa/packages/client/vite.config.ts +35 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
</head>
|
|
7
|
+
|
|
8
|
+
<body>
|
|
9
|
+
<div id="app"></div>
|
|
10
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "client",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite --port=3001",
|
|
8
|
+
"build": "vite build",
|
|
9
|
+
"serve": "vite preview",
|
|
10
|
+
"start": "vite",
|
|
11
|
+
"check-types": "tsc --noEmit"
|
|
12
|
+
},
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"@tanstack/router-plugin": "^1.114.27",
|
|
15
|
+
"@types/node": "^22.13.11",
|
|
16
|
+
"@types/react": "^19.0.12",
|
|
17
|
+
"@types/react-dom": "^19.0.4",
|
|
18
|
+
"@vitejs/plugin-react": "^4.3.4",
|
|
19
|
+
"postcss": "^8.5.3",
|
|
20
|
+
"tailwindcss": "^4.0.15",
|
|
21
|
+
"vite": "^6.2.2"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@hookform/resolvers": "^3.10.0",
|
|
25
|
+
"@radix-ui/react-checkbox": "^1.1.4",
|
|
26
|
+
"@radix-ui/react-dropdown-menu": "^2.1.6",
|
|
27
|
+
"@radix-ui/react-label": "^2.1.2",
|
|
28
|
+
"@radix-ui/react-slot": "^1.1.2",
|
|
29
|
+
"@tanstack/react-form": "^1.0.5",
|
|
30
|
+
"@tailwindcss/vite": "^4.0.15",
|
|
31
|
+
"@tanstack/react-query": "^5.69.0",
|
|
32
|
+
"@tanstack/react-query-devtools": "^5.69.0",
|
|
33
|
+
"@tanstack/react-router": "^1.114.25",
|
|
34
|
+
"@tanstack/router-devtools": "^1.114.25",
|
|
35
|
+
"@trpc/client": "^11.0.0",
|
|
36
|
+
"@trpc/react-query": "^11.0.0",
|
|
37
|
+
"@trpc/server": "^11.0.0",
|
|
38
|
+
"class-variance-authority": "^0.7.1",
|
|
39
|
+
"clsx": "^2.1.1",
|
|
40
|
+
"lucide-react": "^0.473.0",
|
|
41
|
+
"next-themes": "^0.4.6",
|
|
42
|
+
"react": "^19.0.0",
|
|
43
|
+
"react-dom": "^19.0.0",
|
|
44
|
+
"sonner": "^1.7.4",
|
|
45
|
+
"tailwind-merge": "^2.6.0",
|
|
46
|
+
"tailwindcss-animate": "^1.0.7",
|
|
47
|
+
"zod": "^3.24.2"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Link } from "@tanstack/react-router";
|
|
2
|
+
import { ModeToggle } from "./mode-toggle";
|
|
3
|
+
|
|
4
|
+
export default function Header() {
|
|
5
|
+
const links = [
|
|
6
|
+
{ to: "/", label: "Home" },
|
|
7
|
+
];
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<div>
|
|
11
|
+
<div className="flex flex-row items-center justify-between px-2 py-1">
|
|
12
|
+
<nav className="flex gap-4 text-lg">
|
|
13
|
+
{links.map(({ to, label }) => (
|
|
14
|
+
<Link
|
|
15
|
+
key={to}
|
|
16
|
+
to={to}
|
|
17
|
+
activeProps={{ className: "font-bold" }}
|
|
18
|
+
activeOptions={{ exact: true }}
|
|
19
|
+
>
|
|
20
|
+
{label}
|
|
21
|
+
</Link>
|
|
22
|
+
))}
|
|
23
|
+
</nav>
|
|
24
|
+
<div className="flex items-center gap-2">
|
|
25
|
+
<ModeToggle />
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
<hr />
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Moon, Sun } from "lucide-react";
|
|
2
|
+
|
|
3
|
+
import { Button } from "@/components/ui/button";
|
|
4
|
+
import {
|
|
5
|
+
DropdownMenu,
|
|
6
|
+
DropdownMenuContent,
|
|
7
|
+
DropdownMenuItem,
|
|
8
|
+
DropdownMenuTrigger,
|
|
9
|
+
} from "@/components/ui/dropdown-menu";
|
|
10
|
+
import { useTheme } from "@/components/theme-provider";
|
|
11
|
+
|
|
12
|
+
export function ModeToggle() {
|
|
13
|
+
const { setTheme, theme } = useTheme();
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<DropdownMenu>
|
|
17
|
+
<DropdownMenuTrigger asChild>
|
|
18
|
+
<Button variant="outline" size="icon">
|
|
19
|
+
<Sun className="h-[1.2rem] w-[1.2rem] scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" />
|
|
20
|
+
<Moon className="absolute h-[1.2rem] w-[1.2rem] scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" />
|
|
21
|
+
<span className="sr-only">Toggle theme</span>
|
|
22
|
+
</Button>
|
|
23
|
+
</DropdownMenuTrigger>
|
|
24
|
+
<DropdownMenuContent align="end">
|
|
25
|
+
<DropdownMenuItem onClick={() => setTheme("light")}>
|
|
26
|
+
Light
|
|
27
|
+
</DropdownMenuItem>
|
|
28
|
+
<DropdownMenuItem onClick={() => setTheme("dark")}>
|
|
29
|
+
Dark
|
|
30
|
+
</DropdownMenuItem>
|
|
31
|
+
<DropdownMenuItem onClick={() => setTheme("system")}>
|
|
32
|
+
System
|
|
33
|
+
</DropdownMenuItem>
|
|
34
|
+
</DropdownMenuContent>
|
|
35
|
+
</DropdownMenu>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { createContext, useContext, useEffect, useState } from "react";
|
|
2
|
+
|
|
3
|
+
type Theme = "dark" | "light" | "system";
|
|
4
|
+
|
|
5
|
+
type ThemeProviderProps = {
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
defaultTheme?: Theme;
|
|
8
|
+
storageKey?: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
type ThemeProviderState = {
|
|
12
|
+
theme: Theme;
|
|
13
|
+
setTheme: (theme: Theme) => void;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const initialState: ThemeProviderState = {
|
|
17
|
+
theme: "system",
|
|
18
|
+
setTheme: () => null,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const ThemeProviderContext = createContext<ThemeProviderState>(initialState);
|
|
22
|
+
|
|
23
|
+
export function ThemeProvider({
|
|
24
|
+
children,
|
|
25
|
+
defaultTheme = "system",
|
|
26
|
+
storageKey = "vite-ui-theme",
|
|
27
|
+
...props
|
|
28
|
+
}: ThemeProviderProps) {
|
|
29
|
+
const [theme, setTheme] = useState<Theme>(
|
|
30
|
+
() => (localStorage.getItem(storageKey) as Theme) || defaultTheme,
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
const root = window.document.documentElement;
|
|
35
|
+
|
|
36
|
+
root.classList.remove("light", "dark");
|
|
37
|
+
|
|
38
|
+
if (theme === "system") {
|
|
39
|
+
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)")
|
|
40
|
+
.matches
|
|
41
|
+
? "dark"
|
|
42
|
+
: "light";
|
|
43
|
+
|
|
44
|
+
root.classList.add(systemTheme);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
root.classList.add(theme);
|
|
49
|
+
}, [theme]);
|
|
50
|
+
|
|
51
|
+
const value = {
|
|
52
|
+
theme,
|
|
53
|
+
setTheme: (theme: Theme) => {
|
|
54
|
+
localStorage.setItem(storageKey, theme);
|
|
55
|
+
setTheme(theme);
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<ThemeProviderContext.Provider {...props} value={value}>
|
|
61
|
+
{children}
|
|
62
|
+
</ThemeProviderContext.Provider>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export const useTheme = () => {
|
|
67
|
+
const context = useContext(ThemeProviderContext);
|
|
68
|
+
|
|
69
|
+
if (context === undefined)
|
|
70
|
+
throw new Error("useTheme must be used within a ThemeProvider");
|
|
71
|
+
|
|
72
|
+
return context;
|
|
73
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { Slot } from "@radix-ui/react-slot"
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
6
|
+
|
|
7
|
+
const buttonVariants = cva(
|
|
8
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default:
|
|
13
|
+
"bg-primary text-primary-foreground shadow-sm hover:bg-primary/90",
|
|
14
|
+
destructive:
|
|
15
|
+
"bg-destructive text-destructive-foreground shadow-xs hover:bg-destructive/90",
|
|
16
|
+
outline:
|
|
17
|
+
"border border-input bg-background shadow-xs hover:bg-accent hover:text-accent-foreground",
|
|
18
|
+
secondary:
|
|
19
|
+
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
|
20
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
21
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
22
|
+
},
|
|
23
|
+
size: {
|
|
24
|
+
default: "h-9 px-4 py-2",
|
|
25
|
+
sm: "h-8 rounded-md px-3 text-xs",
|
|
26
|
+
lg: "h-10 rounded-md px-8",
|
|
27
|
+
icon: "h-9 w-9",
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
defaultVariants: {
|
|
31
|
+
variant: "default",
|
|
32
|
+
size: "default",
|
|
33
|
+
},
|
|
34
|
+
}
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
export interface ButtonProps
|
|
38
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
39
|
+
VariantProps<typeof buttonVariants> {
|
|
40
|
+
asChild?: boolean
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
44
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
45
|
+
const Comp = asChild ? Slot : "button"
|
|
46
|
+
return (
|
|
47
|
+
<Comp
|
|
48
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
49
|
+
ref={ref}
|
|
50
|
+
{...props}
|
|
51
|
+
/>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
)
|
|
55
|
+
Button.displayName = "Button"
|
|
56
|
+
|
|
57
|
+
export { Button, buttonVariants }
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
import { cn } from "@/lib/utils"
|
|
4
|
+
|
|
5
|
+
function Card({ className, ...props }: React.ComponentProps<"div">) {
|
|
6
|
+
return (
|
|
7
|
+
<div
|
|
8
|
+
data-slot="card"
|
|
9
|
+
className={cn(
|
|
10
|
+
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
|
|
11
|
+
className
|
|
12
|
+
)}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
19
|
+
return (
|
|
20
|
+
<div
|
|
21
|
+
data-slot="card-header"
|
|
22
|
+
className={cn(
|
|
23
|
+
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
|
|
24
|
+
className
|
|
25
|
+
)}
|
|
26
|
+
{...props}
|
|
27
|
+
/>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|
32
|
+
return (
|
|
33
|
+
<div
|
|
34
|
+
data-slot="card-title"
|
|
35
|
+
className={cn("leading-none font-semibold", className)}
|
|
36
|
+
{...props}
|
|
37
|
+
/>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
|
|
42
|
+
return (
|
|
43
|
+
<div
|
|
44
|
+
data-slot="card-description"
|
|
45
|
+
className={cn("text-muted-foreground text-sm", className)}
|
|
46
|
+
{...props}
|
|
47
|
+
/>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
|
|
52
|
+
return (
|
|
53
|
+
<div
|
|
54
|
+
data-slot="card-action"
|
|
55
|
+
className={cn(
|
|
56
|
+
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
|
|
57
|
+
className
|
|
58
|
+
)}
|
|
59
|
+
{...props}
|
|
60
|
+
/>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
65
|
+
return (
|
|
66
|
+
<div
|
|
67
|
+
data-slot="card-content"
|
|
68
|
+
className={cn("px-6", className)}
|
|
69
|
+
{...props}
|
|
70
|
+
/>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|
75
|
+
return (
|
|
76
|
+
<div
|
|
77
|
+
data-slot="card-footer"
|
|
78
|
+
className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
|
|
79
|
+
{...props}
|
|
80
|
+
/>
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export {
|
|
85
|
+
Card,
|
|
86
|
+
CardHeader,
|
|
87
|
+
CardFooter,
|
|
88
|
+
CardTitle,
|
|
89
|
+
CardAction,
|
|
90
|
+
CardDescription,
|
|
91
|
+
CardContent,
|
|
92
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
|
|
3
|
+
import { CheckIcon } from "lucide-react"
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
6
|
+
|
|
7
|
+
function Checkbox({
|
|
8
|
+
className,
|
|
9
|
+
...props
|
|
10
|
+
}: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
|
|
11
|
+
return (
|
|
12
|
+
<CheckboxPrimitive.Root
|
|
13
|
+
data-slot="checkbox"
|
|
14
|
+
className={cn(
|
|
15
|
+
"peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
|
16
|
+
className
|
|
17
|
+
)}
|
|
18
|
+
{...props}
|
|
19
|
+
>
|
|
20
|
+
<CheckboxPrimitive.Indicator
|
|
21
|
+
data-slot="checkbox-indicator"
|
|
22
|
+
className="flex items-center justify-center text-current transition-none"
|
|
23
|
+
>
|
|
24
|
+
<CheckIcon className="size-3.5" />
|
|
25
|
+
</CheckboxPrimitive.Indicator>
|
|
26
|
+
</CheckboxPrimitive.Root>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { Checkbox }
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
|
|
3
|
+
import { Check, ChevronRight, Circle } from "lucide-react"
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
6
|
+
|
|
7
|
+
const DropdownMenu = DropdownMenuPrimitive.Root
|
|
8
|
+
|
|
9
|
+
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
|
|
10
|
+
|
|
11
|
+
const DropdownMenuGroup = DropdownMenuPrimitive.Group
|
|
12
|
+
|
|
13
|
+
const DropdownMenuPortal = DropdownMenuPrimitive.Portal
|
|
14
|
+
|
|
15
|
+
const DropdownMenuSub = DropdownMenuPrimitive.Sub
|
|
16
|
+
|
|
17
|
+
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
|
|
18
|
+
|
|
19
|
+
const DropdownMenuSubTrigger = React.forwardRef<
|
|
20
|
+
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
|
|
21
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
|
|
22
|
+
inset?: boolean
|
|
23
|
+
}
|
|
24
|
+
>(({ className, inset, children, ...props }, ref) => (
|
|
25
|
+
<DropdownMenuPrimitive.SubTrigger
|
|
26
|
+
ref={ref}
|
|
27
|
+
className={cn(
|
|
28
|
+
"flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-hidden focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
29
|
+
inset && "pl-8",
|
|
30
|
+
className
|
|
31
|
+
)}
|
|
32
|
+
{...props}
|
|
33
|
+
>
|
|
34
|
+
{children}
|
|
35
|
+
<ChevronRight className="ml-auto" />
|
|
36
|
+
</DropdownMenuPrimitive.SubTrigger>
|
|
37
|
+
))
|
|
38
|
+
DropdownMenuSubTrigger.displayName =
|
|
39
|
+
DropdownMenuPrimitive.SubTrigger.displayName
|
|
40
|
+
|
|
41
|
+
const DropdownMenuSubContent = React.forwardRef<
|
|
42
|
+
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
|
|
43
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
|
|
44
|
+
>(({ className, ...props }, ref) => (
|
|
45
|
+
<DropdownMenuPrimitive.SubContent
|
|
46
|
+
ref={ref}
|
|
47
|
+
className={cn(
|
|
48
|
+
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
49
|
+
className
|
|
50
|
+
)}
|
|
51
|
+
{...props}
|
|
52
|
+
/>
|
|
53
|
+
))
|
|
54
|
+
DropdownMenuSubContent.displayName =
|
|
55
|
+
DropdownMenuPrimitive.SubContent.displayName
|
|
56
|
+
|
|
57
|
+
const DropdownMenuContent = React.forwardRef<
|
|
58
|
+
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
|
|
59
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
|
|
60
|
+
>(({ className, sideOffset = 4, ...props }, ref) => (
|
|
61
|
+
<DropdownMenuPrimitive.Portal>
|
|
62
|
+
<DropdownMenuPrimitive.Content
|
|
63
|
+
ref={ref}
|
|
64
|
+
sideOffset={sideOffset}
|
|
65
|
+
className={cn(
|
|
66
|
+
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
|
|
67
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
68
|
+
className
|
|
69
|
+
)}
|
|
70
|
+
{...props}
|
|
71
|
+
/>
|
|
72
|
+
</DropdownMenuPrimitive.Portal>
|
|
73
|
+
))
|
|
74
|
+
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
|
|
75
|
+
|
|
76
|
+
const DropdownMenuItem = React.forwardRef<
|
|
77
|
+
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
|
|
78
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
|
|
79
|
+
inset?: boolean
|
|
80
|
+
}
|
|
81
|
+
>(({ className, inset, ...props }, ref) => (
|
|
82
|
+
<DropdownMenuPrimitive.Item
|
|
83
|
+
ref={ref}
|
|
84
|
+
className={cn(
|
|
85
|
+
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0",
|
|
86
|
+
inset && "pl-8",
|
|
87
|
+
className
|
|
88
|
+
)}
|
|
89
|
+
{...props}
|
|
90
|
+
/>
|
|
91
|
+
))
|
|
92
|
+
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
|
|
93
|
+
|
|
94
|
+
const DropdownMenuCheckboxItem = React.forwardRef<
|
|
95
|
+
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
|
|
96
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
|
|
97
|
+
>(({ className, children, checked, ...props }, ref) => (
|
|
98
|
+
<DropdownMenuPrimitive.CheckboxItem
|
|
99
|
+
ref={ref}
|
|
100
|
+
className={cn(
|
|
101
|
+
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50",
|
|
102
|
+
className
|
|
103
|
+
)}
|
|
104
|
+
checked={checked}
|
|
105
|
+
{...props}
|
|
106
|
+
>
|
|
107
|
+
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
108
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
|
109
|
+
<Check className="h-4 w-4" />
|
|
110
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
|
111
|
+
</span>
|
|
112
|
+
{children}
|
|
113
|
+
</DropdownMenuPrimitive.CheckboxItem>
|
|
114
|
+
))
|
|
115
|
+
DropdownMenuCheckboxItem.displayName =
|
|
116
|
+
DropdownMenuPrimitive.CheckboxItem.displayName
|
|
117
|
+
|
|
118
|
+
const DropdownMenuRadioItem = React.forwardRef<
|
|
119
|
+
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
|
|
120
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
|
|
121
|
+
>(({ className, children, ...props }, ref) => (
|
|
122
|
+
<DropdownMenuPrimitive.RadioItem
|
|
123
|
+
ref={ref}
|
|
124
|
+
className={cn(
|
|
125
|
+
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50",
|
|
126
|
+
className
|
|
127
|
+
)}
|
|
128
|
+
{...props}
|
|
129
|
+
>
|
|
130
|
+
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
131
|
+
<DropdownMenuPrimitive.ItemIndicator>
|
|
132
|
+
<Circle className="h-2 w-2 fill-current" />
|
|
133
|
+
</DropdownMenuPrimitive.ItemIndicator>
|
|
134
|
+
</span>
|
|
135
|
+
{children}
|
|
136
|
+
</DropdownMenuPrimitive.RadioItem>
|
|
137
|
+
))
|
|
138
|
+
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
|
|
139
|
+
|
|
140
|
+
const DropdownMenuLabel = React.forwardRef<
|
|
141
|
+
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
|
|
142
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
|
|
143
|
+
inset?: boolean
|
|
144
|
+
}
|
|
145
|
+
>(({ className, inset, ...props }, ref) => (
|
|
146
|
+
<DropdownMenuPrimitive.Label
|
|
147
|
+
ref={ref}
|
|
148
|
+
className={cn(
|
|
149
|
+
"px-2 py-1.5 text-sm font-semibold",
|
|
150
|
+
inset && "pl-8",
|
|
151
|
+
className
|
|
152
|
+
)}
|
|
153
|
+
{...props}
|
|
154
|
+
/>
|
|
155
|
+
))
|
|
156
|
+
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
|
|
157
|
+
|
|
158
|
+
const DropdownMenuSeparator = React.forwardRef<
|
|
159
|
+
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
|
|
160
|
+
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
|
|
161
|
+
>(({ className, ...props }, ref) => (
|
|
162
|
+
<DropdownMenuPrimitive.Separator
|
|
163
|
+
ref={ref}
|
|
164
|
+
className={cn("-mx-1 my-1 h-px bg-muted", className)}
|
|
165
|
+
{...props}
|
|
166
|
+
/>
|
|
167
|
+
))
|
|
168
|
+
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
|
|
169
|
+
|
|
170
|
+
const DropdownMenuShortcut = ({
|
|
171
|
+
className,
|
|
172
|
+
...props
|
|
173
|
+
}: React.HTMLAttributes<HTMLSpanElement>) => {
|
|
174
|
+
return (
|
|
175
|
+
<span
|
|
176
|
+
className={cn("ml-auto text-xs tracking-widest opacity-60", className)}
|
|
177
|
+
{...props}
|
|
178
|
+
/>
|
|
179
|
+
)
|
|
180
|
+
}
|
|
181
|
+
DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
|
|
182
|
+
|
|
183
|
+
export {
|
|
184
|
+
DropdownMenu,
|
|
185
|
+
DropdownMenuTrigger,
|
|
186
|
+
DropdownMenuContent,
|
|
187
|
+
DropdownMenuItem,
|
|
188
|
+
DropdownMenuCheckboxItem,
|
|
189
|
+
DropdownMenuRadioItem,
|
|
190
|
+
DropdownMenuLabel,
|
|
191
|
+
DropdownMenuSeparator,
|
|
192
|
+
DropdownMenuShortcut,
|
|
193
|
+
DropdownMenuGroup,
|
|
194
|
+
DropdownMenuPortal,
|
|
195
|
+
DropdownMenuSub,
|
|
196
|
+
DropdownMenuSubContent,
|
|
197
|
+
DropdownMenuSubTrigger,
|
|
198
|
+
DropdownMenuRadioGroup,
|
|
199
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
import { cn } from "@/lib/utils"
|
|
4
|
+
|
|
5
|
+
const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
|
|
6
|
+
({ className, type, ...props }, ref) => {
|
|
7
|
+
return (
|
|
8
|
+
<input
|
|
9
|
+
type={type}
|
|
10
|
+
className={cn(
|
|
11
|
+
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
12
|
+
className
|
|
13
|
+
)}
|
|
14
|
+
ref={ref}
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
)
|
|
20
|
+
Input.displayName = "Input"
|
|
21
|
+
|
|
22
|
+
export { Input }
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import * as LabelPrimitive from "@radix-ui/react-label"
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
6
|
+
|
|
7
|
+
const labelVariants = cva(
|
|
8
|
+
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
const Label = React.forwardRef<
|
|
12
|
+
React.ElementRef<typeof LabelPrimitive.Root>,
|
|
13
|
+
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
|
|
14
|
+
VariantProps<typeof labelVariants>
|
|
15
|
+
>(({ className, ...props }, ref) => (
|
|
16
|
+
<LabelPrimitive.Root
|
|
17
|
+
ref={ref}
|
|
18
|
+
className={cn(labelVariants(), className)}
|
|
19
|
+
{...props}
|
|
20
|
+
/>
|
|
21
|
+
))
|
|
22
|
+
Label.displayName = LabelPrimitive.Root.displayName
|
|
23
|
+
|
|
24
|
+
export { Label }
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { cn } from "@/lib/utils"
|
|
2
|
+
|
|
3
|
+
function Skeleton({
|
|
4
|
+
className,
|
|
5
|
+
...props
|
|
6
|
+
}: React.HTMLAttributes<HTMLDivElement>) {
|
|
7
|
+
return (
|
|
8
|
+
<div
|
|
9
|
+
className={cn("animate-pulse rounded-md bg-primary/10", className)}
|
|
10
|
+
{...props}
|
|
11
|
+
/>
|
|
12
|
+
)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export { Skeleton }
|