first
This commit is contained in:
commit
0738070ce1
287 changed files with 10116 additions and 0 deletions
38
README.md
Normal file
38
README.md
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
# sv
|
||||||
|
|
||||||
|
Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli).
|
||||||
|
|
||||||
|
## Creating a project
|
||||||
|
|
||||||
|
If you're seeing this, you've probably already done this step. Congrats!
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# create a new project in the current directory
|
||||||
|
npx sv create
|
||||||
|
|
||||||
|
# create a new project in my-app
|
||||||
|
npx sv create my-app
|
||||||
|
```
|
||||||
|
|
||||||
|
## Developing
|
||||||
|
|
||||||
|
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# or start the server and open the app in a new browser tab
|
||||||
|
npm run dev -- --open
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
To create a production version of your app:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
You can preview the production build with `npm run preview`.
|
||||||
|
|
||||||
|
> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.
|
17
components.json
Normal file
17
components.json
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://next.shadcn-svelte.com/schema.json",
|
||||||
|
"style": "default",
|
||||||
|
"tailwind": {
|
||||||
|
"config": "tailwind.config.js",
|
||||||
|
"css": "src/app.css",
|
||||||
|
"baseColor": "slate"
|
||||||
|
},
|
||||||
|
"aliases": {
|
||||||
|
"components": "$lib/components",
|
||||||
|
"utils": "$lib/utils",
|
||||||
|
"ui": "$lib/components/ui",
|
||||||
|
"hooks": "$lib/hooks"
|
||||||
|
},
|
||||||
|
"typescript": false,
|
||||||
|
"registry": "https://next.shadcn-svelte.com/registry"
|
||||||
|
}
|
10
docker-compose.yml
Normal file
10
docker-compose.yml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
services:
|
||||||
|
db:
|
||||||
|
image: postgis/postgis:17-3.5-alpine
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- 5432:5432
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: root
|
||||||
|
POSTGRES_PASSWORD: mysecretpassword
|
||||||
|
POSTGRES_DB: local
|
18
drizzle.config.js
Normal file
18
drizzle.config.js
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { defineConfig } from "drizzle-kit";
|
||||||
|
if (!process.env.DATABASE_URL) throw new Error("DATABASE_URL is not set");
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
schema: "./src/lib/server/db/schema.js",
|
||||||
|
|
||||||
|
dbCredentials: {
|
||||||
|
url: process.env.DATABASE_URL,
|
||||||
|
},
|
||||||
|
|
||||||
|
verbose: true,
|
||||||
|
strict: true,
|
||||||
|
dialect: "postgresql",
|
||||||
|
casing: "snake_case",
|
||||||
|
migrations: {
|
||||||
|
prefix: "timestamp",
|
||||||
|
},
|
||||||
|
});
|
19
drizzle/20241104190811_lucia.sql
Normal file
19
drizzle/20241104190811_lucia.sql
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
CREATE TABLE IF NOT EXISTS "sessions" (
|
||||||
|
"id" text PRIMARY KEY NOT NULL,
|
||||||
|
"user_id" text NOT NULL,
|
||||||
|
"expires_at" timestamp with time zone NOT NULL
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
CREATE TABLE IF NOT EXISTS "users" (
|
||||||
|
"id" text PRIMARY KEY NOT NULL,
|
||||||
|
"age" integer,
|
||||||
|
"username" text NOT NULL,
|
||||||
|
"password_hash" text NOT NULL,
|
||||||
|
CONSTRAINT "users_username_unique" UNIQUE("username")
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
DO $$ BEGIN
|
||||||
|
ALTER TABLE "sessions" ADD CONSTRAINT "sessions_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;
|
||||||
|
EXCEPTION
|
||||||
|
WHEN duplicate_object THEN null;
|
||||||
|
END $$;
|
103
drizzle/meta/20241104190811_snapshot.json
Normal file
103
drizzle/meta/20241104190811_snapshot.json
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
{
|
||||||
|
"id": "4b3474a3-7de5-4b99-878e-f839b53c52f7",
|
||||||
|
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||||
|
"version": "7",
|
||||||
|
"dialect": "postgresql",
|
||||||
|
"tables": {
|
||||||
|
"public.sessions": {
|
||||||
|
"name": "sessions",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"user_id": {
|
||||||
|
"name": "user_id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"expires_at": {
|
||||||
|
"name": "expires_at",
|
||||||
|
"type": "timestamp with time zone",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"sessions_user_id_users_id_fk": {
|
||||||
|
"name": "sessions_user_id_users_id_fk",
|
||||||
|
"tableFrom": "sessions",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": ["user_id"],
|
||||||
|
"columnsTo": ["id"],
|
||||||
|
"onDelete": "no action",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.users": {
|
||||||
|
"name": "users",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"age": {
|
||||||
|
"name": "age",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"name": "username",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"password_hash": {
|
||||||
|
"name": "password_hash",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {
|
||||||
|
"users_username_unique": {
|
||||||
|
"name": "users_username_unique",
|
||||||
|
"nullsNotDistinct": false,
|
||||||
|
"columns": ["username"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"enums": {},
|
||||||
|
"schemas": {},
|
||||||
|
"sequences": {},
|
||||||
|
"roles": {},
|
||||||
|
"policies": {},
|
||||||
|
"views": {},
|
||||||
|
"_meta": {
|
||||||
|
"columns": {},
|
||||||
|
"schemas": {},
|
||||||
|
"tables": {}
|
||||||
|
}
|
||||||
|
}
|
13
drizzle/meta/_journal.json
Normal file
13
drizzle/meta/_journal.json
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"version": "7",
|
||||||
|
"dialect": "postgresql",
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"idx": 0,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1730747291671,
|
||||||
|
"tag": "20241104190811_lucia",
|
||||||
|
"breakpoints": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
3
jsconfig.json
Normal file
3
jsconfig.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"extends": "./.svelte-kit/tsconfig.json"
|
||||||
|
}
|
66
package.json
Normal file
66
package.json
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
{
|
||||||
|
"name": "hackathon-template",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite dev",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"check": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json",
|
||||||
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch",
|
||||||
|
"format": "prettier --write .",
|
||||||
|
"lint": "prettier --check .",
|
||||||
|
"db:start": "docker compose up",
|
||||||
|
"db:push": "drizzle-kit push",
|
||||||
|
"db:migrate": "drizzle-kit migrate",
|
||||||
|
"db:studio": "drizzle-kit studio",
|
||||||
|
"db:generate": "drizzle-kit generate --name",
|
||||||
|
"db:drop": "drizzle-kit drop",
|
||||||
|
"prepare": "husky"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@internationalized/date": "^3.8.0",
|
||||||
|
"@lucide/svelte": "^0.511.0",
|
||||||
|
"@sveltejs/adapter-auto": "^3.3.1",
|
||||||
|
"@sveltejs/kit": "^2.21.0",
|
||||||
|
"@sveltejs/vite-plugin-svelte": "^4.0.4",
|
||||||
|
"autoprefixer": "^10.4.21",
|
||||||
|
"bits-ui": "1.0.0-next.40",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"drizzle-kit": "^0.27.2",
|
||||||
|
"embla-carousel-svelte": "^8.6.0",
|
||||||
|
"formsnap": "2.0.0-next.1",
|
||||||
|
"husky": "^9.1.7",
|
||||||
|
"lint-staged": "^15.5.2",
|
||||||
|
"lucide-svelte": "^0.454.0",
|
||||||
|
"mode-watcher": "^0.4.1",
|
||||||
|
"paneforge": "1.0.0-next.1",
|
||||||
|
"prettier": "^3.5.3",
|
||||||
|
"prettier-plugin-organize-imports": "^4.1.0",
|
||||||
|
"prettier-plugin-svelte": "^3.4.0",
|
||||||
|
"prettier-plugin-tailwindcss": "^0.6.11",
|
||||||
|
"svelte": "^5.30.2",
|
||||||
|
"svelte-check": "^4.2.1",
|
||||||
|
"svelte-sonner": "^0.3.28",
|
||||||
|
"sveltekit-superforms": "^2.25.0",
|
||||||
|
"tailwind-merge": "^2.6.0",
|
||||||
|
"tailwind-variants": "^0.2.1",
|
||||||
|
"tailwindcss": "^3.4.17",
|
||||||
|
"tailwindcss-animate": "^1.0.7",
|
||||||
|
"typescript": "^5.8.3",
|
||||||
|
"vaul-svelte": "1.0.0-next.1",
|
||||||
|
"vite": "^5.4.19",
|
||||||
|
"zod": "^3.24.4"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@node-rs/argon2": "^2.0.2",
|
||||||
|
"@oslojs/crypto": "^1.0.1",
|
||||||
|
"@oslojs/encoding": "^1.1.0",
|
||||||
|
"drizzle-orm": "^0.36.4",
|
||||||
|
"postgres": "^3.4.5"
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*.{js,css,md,svelte,json}": "prettier --write"
|
||||||
|
},
|
||||||
|
"packageManager": "pnpm@9.12.3+sha512.cce0f9de9c5a7c95bef944169cc5dfe8741abfb145078c0d508b868056848a87c81e626246cb60967cbd7fd29a6c062ef73ff840d96b3c86c40ac92cf4a813ee"
|
||||||
|
}
|
3953
pnpm-lock.yaml
generated
Normal file
3953
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load diff
6
postcss.config.js
Normal file
6
postcss.config.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
export default {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
};
|
75
src/app.css
Normal file
75
src/app.css
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
:root {
|
||||||
|
--background: 0 0% 100%;
|
||||||
|
--foreground: 222.2 84% 4.9%;
|
||||||
|
--muted: 210 40% 96.1%;
|
||||||
|
--muted-foreground: 215.4 16.3% 46.9%;
|
||||||
|
--popover: 0 0% 100%;
|
||||||
|
--popover-foreground: 222.2 84% 4.9%;
|
||||||
|
--card: 0 0% 100%;
|
||||||
|
--card-foreground: 222.2 84% 4.9%;
|
||||||
|
--border: 214.3 31.8% 91.4%;
|
||||||
|
--input: 214.3 31.8% 91.4%;
|
||||||
|
--primary: 222.2 47.4% 11.2%;
|
||||||
|
--primary-foreground: 210 40% 98%;
|
||||||
|
--secondary: 210 40% 96.1%;
|
||||||
|
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||||
|
--accent: 210 40% 96.1%;
|
||||||
|
--accent-foreground: 222.2 47.4% 11.2%;
|
||||||
|
--destructive: 0 72.2% 50.6%;
|
||||||
|
--destructive-foreground: 210 40% 98%;
|
||||||
|
--ring: 222.2 84% 4.9%;
|
||||||
|
--radius: 0.5rem;
|
||||||
|
--sidebar-background: 0 0% 98%;
|
||||||
|
--sidebar-foreground: 240 5.3% 26.1%;
|
||||||
|
--sidebar-primary: 240 5.9% 10%;
|
||||||
|
--sidebar-primary-foreground: 0 0% 98%;
|
||||||
|
--sidebar-accent: 240 4.8% 95.9%;
|
||||||
|
--sidebar-accent-foreground: 240 5.9% 10%;
|
||||||
|
--sidebar-border: 220 13% 91%;
|
||||||
|
--sidebar-ring: 217.2 91.2% 59.8%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--background: 222.2 84% 4.9%;
|
||||||
|
--foreground: 210 40% 98%;
|
||||||
|
--muted: 217.2 32.6% 17.5%;
|
||||||
|
--muted-foreground: 215 20.2% 65.1%;
|
||||||
|
--popover: 222.2 84% 4.9%;
|
||||||
|
--popover-foreground: 210 40% 98%;
|
||||||
|
--card: 222.2 84% 4.9%;
|
||||||
|
--card-foreground: 210 40% 98%;
|
||||||
|
--border: 217.2 32.6% 17.5%;
|
||||||
|
--input: 217.2 32.6% 17.5%;
|
||||||
|
--primary: 210 40% 98%;
|
||||||
|
--primary-foreground: 222.2 47.4% 11.2%;
|
||||||
|
--secondary: 217.2 32.6% 17.5%;
|
||||||
|
--secondary-foreground: 210 40% 98%;
|
||||||
|
--accent: 217.2 32.6% 17.5%;
|
||||||
|
--accent-foreground: 210 40% 98%;
|
||||||
|
--destructive: 0 62.8% 30.6%;
|
||||||
|
--destructive-foreground: 210 40% 98%;
|
||||||
|
--ring: 212.7 26.8% 83.9%;
|
||||||
|
--sidebar-background: 240 5.9% 10%;
|
||||||
|
--sidebar-foreground: 240 4.8% 95.9%;
|
||||||
|
--sidebar-primary: 224.3 76.3% 48%;
|
||||||
|
--sidebar-primary-foreground: 0 0% 100%;
|
||||||
|
--sidebar-accent: 240 3.7% 15.9%;
|
||||||
|
--sidebar-accent-foreground: 240 4.8% 95.9%;
|
||||||
|
--sidebar-border: 240 3.7% 15.9%;
|
||||||
|
--sidebar-ring: 217.2 91.2% 59.8%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
* {
|
||||||
|
@apply border-border;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
@apply bg-background text-foreground;
|
||||||
|
}
|
||||||
|
}
|
12
src/app.html
Normal file
12
src/app.html
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
%sveltekit.head%
|
||||||
|
</head>
|
||||||
|
<body data-sveltekit-preload-data="hover">
|
||||||
|
<div style="display: contents">%sveltekit.body%</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
24
src/hooks.server.js
Normal file
24
src/hooks.server.js
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import * as auth from "$lib/server/auth.js";
|
||||||
|
|
||||||
|
const handleAuth = async ({ event, resolve }) => {
|
||||||
|
const sessionToken = event.cookies.get(auth.sessionCookieName);
|
||||||
|
if (!sessionToken) {
|
||||||
|
event.locals.user = null;
|
||||||
|
event.locals.session = null;
|
||||||
|
return resolve(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { session, user } = await auth.validateSessionToken(sessionToken);
|
||||||
|
if (session) {
|
||||||
|
auth.setSessionTokenCookie(event, sessionToken, session.expiresAt);
|
||||||
|
} else {
|
||||||
|
auth.deleteSessionTokenCookie(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
event.locals.user = user;
|
||||||
|
event.locals.session = session;
|
||||||
|
|
||||||
|
return resolve(event);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const handle = handleAuth;
|
19
src/lib/components/ui/accordion/accordion-content.svelte
Normal file
19
src/lib/components/ui/accordion/accordion-content.svelte
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<script>
|
||||||
|
import { Accordion as AccordionPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<AccordionPrimitive.Content
|
||||||
|
bind:ref
|
||||||
|
class={cn(
|
||||||
|
"overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
<div class="pb-4 pt-0">
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
||||||
|
</AccordionPrimitive.Content>
|
8
src/lib/components/ui/accordion/accordion-item.svelte
Normal file
8
src/lib/components/ui/accordion/accordion-item.svelte
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<script>
|
||||||
|
import { Accordion as AccordionPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<AccordionPrimitive.Item bind:ref class={cn("border-b", className)} {...restProps} />
|
21
src/lib/components/ui/accordion/accordion-trigger.svelte
Normal file
21
src/lib/components/ui/accordion/accordion-trigger.svelte
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<script>
|
||||||
|
import { Accordion as AccordionPrimitive } from "bits-ui";
|
||||||
|
import ChevronDown from "lucide-svelte/icons/chevron-down";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, level = 3, children, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<AccordionPrimitive.Header {level} class="flex">
|
||||||
|
<AccordionPrimitive.Trigger
|
||||||
|
bind:ref
|
||||||
|
class={cn(
|
||||||
|
"flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
<ChevronDown class="size-4 shrink-0 transition-transform duration-200" />
|
||||||
|
</AccordionPrimitive.Trigger>
|
||||||
|
</AccordionPrimitive.Header>
|
17
src/lib/components/ui/accordion/index.js
Normal file
17
src/lib/components/ui/accordion/index.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { Accordion as AccordionPrimitive } from "bits-ui";
|
||||||
|
import Content from "./accordion-content.svelte";
|
||||||
|
import Item from "./accordion-item.svelte";
|
||||||
|
import Trigger from "./accordion-trigger.svelte";
|
||||||
|
const Root = AccordionPrimitive.Root;
|
||||||
|
|
||||||
|
export {
|
||||||
|
//
|
||||||
|
Root as Accordion,
|
||||||
|
Content as AccordionContent,
|
||||||
|
Item as AccordionItem,
|
||||||
|
Trigger as AccordionTrigger,
|
||||||
|
Content,
|
||||||
|
Item,
|
||||||
|
Root,
|
||||||
|
Trigger,
|
||||||
|
};
|
|
@ -0,0 +1,9 @@
|
||||||
|
<script>
|
||||||
|
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
||||||
|
import { buttonVariants } from "$lib/components/ui/button/index.js";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<AlertDialogPrimitive.Action bind:ref class={cn(buttonVariants(), className)} {...restProps} />
|
|
@ -0,0 +1,13 @@
|
||||||
|
<script>
|
||||||
|
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
||||||
|
import { buttonVariants } from "$lib/components/ui/button/index.js";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<AlertDialogPrimitive.Cancel
|
||||||
|
bind:ref
|
||||||
|
class={cn(buttonVariants({ variant: "outline" }), "mt-2 sm:mt-0", className)}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
|
@ -0,0 +1,19 @@
|
||||||
|
<script>
|
||||||
|
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
||||||
|
import AlertDialogOverlay from "./alert-dialog-overlay.svelte";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<AlertDialogPrimitive.Portal>
|
||||||
|
<AlertDialogOverlay />
|
||||||
|
<AlertDialogPrimitive.Content
|
||||||
|
bind:ref
|
||||||
|
class={cn(
|
||||||
|
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 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-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
||||||
|
</AlertDialogPrimitive.Portal>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<script>
|
||||||
|
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<AlertDialogPrimitive.Description
|
||||||
|
bind:ref
|
||||||
|
class={cn("text-sm text-muted-foreground", className)}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
|
@ -0,0 +1,13 @@
|
||||||
|
<script>
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
bind:this={ref}
|
||||||
|
class={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
|
@ -0,0 +1,13 @@
|
||||||
|
<script>
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
bind:this={ref}
|
||||||
|
class={cn("flex flex-col space-y-2 text-center sm:text-left", className)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
|
@ -0,0 +1,15 @@
|
||||||
|
<script>
|
||||||
|
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<AlertDialogPrimitive.Overlay
|
||||||
|
bind:ref
|
||||||
|
class={cn(
|
||||||
|
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
13
src/lib/components/ui/alert-dialog/alert-dialog-title.svelte
Normal file
13
src/lib/components/ui/alert-dialog/alert-dialog-title.svelte
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<script>
|
||||||
|
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, level = 3, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<AlertDialogPrimitive.Title
|
||||||
|
bind:ref
|
||||||
|
class={cn("text-lg font-semibold", className)}
|
||||||
|
{level}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
39
src/lib/components/ui/alert-dialog/index.js
Normal file
39
src/lib/components/ui/alert-dialog/index.js
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
||||||
|
import Action from "./alert-dialog-action.svelte";
|
||||||
|
import Cancel from "./alert-dialog-cancel.svelte";
|
||||||
|
import Content from "./alert-dialog-content.svelte";
|
||||||
|
import Description from "./alert-dialog-description.svelte";
|
||||||
|
import Footer from "./alert-dialog-footer.svelte";
|
||||||
|
import Header from "./alert-dialog-header.svelte";
|
||||||
|
import Overlay from "./alert-dialog-overlay.svelte";
|
||||||
|
import Title from "./alert-dialog-title.svelte";
|
||||||
|
|
||||||
|
const Root = AlertDialogPrimitive.Root;
|
||||||
|
const Trigger = AlertDialogPrimitive.Trigger;
|
||||||
|
const Portal = AlertDialogPrimitive.Portal;
|
||||||
|
|
||||||
|
export {
|
||||||
|
Action,
|
||||||
|
//
|
||||||
|
Root as AlertDialog,
|
||||||
|
Action as AlertDialogAction,
|
||||||
|
Cancel as AlertDialogCancel,
|
||||||
|
Content as AlertDialogContent,
|
||||||
|
Description as AlertDialogDescription,
|
||||||
|
Footer as AlertDialogFooter,
|
||||||
|
Header as AlertDialogHeader,
|
||||||
|
Overlay as AlertDialogOverlay,
|
||||||
|
Portal as AlertDialogPortal,
|
||||||
|
Title as AlertDialogTitle,
|
||||||
|
Trigger as AlertDialogTrigger,
|
||||||
|
Cancel,
|
||||||
|
Content,
|
||||||
|
Description,
|
||||||
|
Footer,
|
||||||
|
Header,
|
||||||
|
Overlay,
|
||||||
|
Portal,
|
||||||
|
Root,
|
||||||
|
Title,
|
||||||
|
Trigger,
|
||||||
|
};
|
9
src/lib/components/ui/alert/alert-description.svelte
Normal file
9
src/lib/components/ui/alert/alert-description.svelte
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<script>
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={ref} class={cn("text-sm [&_p]:leading-relaxed", className)} {...restProps}>
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
15
src/lib/components/ui/alert/alert-title.svelte
Normal file
15
src/lib/components/ui/alert/alert-title.svelte
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<script>
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, level = 5, children, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
role="heading"
|
||||||
|
aria-level={level}
|
||||||
|
bind:this={ref}
|
||||||
|
class={cn("mb-1 font-medium leading-none tracking-tight", className)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
33
src/lib/components/ui/alert/alert.svelte
Normal file
33
src/lib/components/ui/alert/alert.svelte
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
<script module>
|
||||||
|
import { tv } from "tailwind-variants";
|
||||||
|
|
||||||
|
export const alertVariants = tv({
|
||||||
|
base: "[&>svg]:text-foreground relative w-full rounded-lg border p-4 [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg~*]:pl-7",
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default: "bg-background text-foreground",
|
||||||
|
destructive:
|
||||||
|
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "default",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
variant = "default",
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
} = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={ref} class={cn(alertVariants({ variant }), className)} {...restProps} role="alert">
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
14
src/lib/components/ui/alert/index.js
Normal file
14
src/lib/components/ui/alert/index.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import Description from "./alert-description.svelte";
|
||||||
|
import Title from "./alert-title.svelte";
|
||||||
|
import Root from "./alert.svelte";
|
||||||
|
export { alertVariants } from "./alert.svelte";
|
||||||
|
|
||||||
|
export {
|
||||||
|
//
|
||||||
|
Root as Alert,
|
||||||
|
Description as AlertDescription,
|
||||||
|
Title as AlertTitle,
|
||||||
|
Description,
|
||||||
|
Root,
|
||||||
|
Title,
|
||||||
|
};
|
5
src/lib/components/ui/aspect-ratio/index.js
Normal file
5
src/lib/components/ui/aspect-ratio/index.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import { AspectRatio as AspectRatioPrimitive } from "bits-ui";
|
||||||
|
|
||||||
|
const Root = AspectRatioPrimitive.Root;
|
||||||
|
|
||||||
|
export { Root as AspectRatio, Root };
|
12
src/lib/components/ui/avatar/avatar-fallback.svelte
Normal file
12
src/lib/components/ui/avatar/avatar-fallback.svelte
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<script>
|
||||||
|
import { Avatar as AvatarPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<AvatarPrimitive.Fallback
|
||||||
|
bind:ref
|
||||||
|
class={cn("flex h-full w-full items-center justify-center rounded-full bg-muted", className)}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
12
src/lib/components/ui/avatar/avatar-image.svelte
Normal file
12
src/lib/components/ui/avatar/avatar-image.svelte
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<script>
|
||||||
|
import { Avatar as AvatarPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<AvatarPrimitive.Image
|
||||||
|
bind:ref
|
||||||
|
class={cn("aspect-square h-full w-full", className)}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
12
src/lib/components/ui/avatar/avatar.svelte
Normal file
12
src/lib/components/ui/avatar/avatar.svelte
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<script>
|
||||||
|
import { Avatar as AvatarPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<AvatarPrimitive.Root
|
||||||
|
bind:ref
|
||||||
|
class={cn("relative flex size-10 shrink-0 overflow-hidden rounded-full", className)}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
13
src/lib/components/ui/avatar/index.js
Normal file
13
src/lib/components/ui/avatar/index.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import Fallback from "./avatar-fallback.svelte";
|
||||||
|
import Image from "./avatar-image.svelte";
|
||||||
|
import Root from "./avatar.svelte";
|
||||||
|
|
||||||
|
export {
|
||||||
|
//
|
||||||
|
Root as Avatar,
|
||||||
|
Fallback as AvatarFallback,
|
||||||
|
Image as AvatarImage,
|
||||||
|
Fallback,
|
||||||
|
Image,
|
||||||
|
Root,
|
||||||
|
};
|
43
src/lib/components/ui/badge/badge.svelte
Normal file
43
src/lib/components/ui/badge/badge.svelte
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
<script module>
|
||||||
|
import { tv } from "tailwind-variants";
|
||||||
|
|
||||||
|
export const badgeVariants = tv({
|
||||||
|
base: "focus:ring-ring inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2",
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default: "bg-primary text-primary-foreground hover:bg-primary/80 border-transparent",
|
||||||
|
secondary:
|
||||||
|
"bg-secondary text-secondary-foreground hover:bg-secondary/80 border-transparent",
|
||||||
|
destructive:
|
||||||
|
"bg-destructive text-destructive-foreground hover:bg-destructive/80 border-transparent",
|
||||||
|
outline: "text-foreground",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "default",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
href,
|
||||||
|
class: className,
|
||||||
|
variant = "default",
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
} = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:element
|
||||||
|
this={href ? "a" : "span"}
|
||||||
|
bind:this={ref}
|
||||||
|
{href}
|
||||||
|
class={cn(badgeVariants({ variant, className }))}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
</svelte:element>
|
1
src/lib/components/ui/badge/index.js
Normal file
1
src/lib/components/ui/badge/index.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export { default as Badge, badgeVariants } from "./badge.svelte";
|
17
src/lib/components/ui/breadcrumb/breadcrumb-ellipsis.svelte
Normal file
17
src/lib/components/ui/breadcrumb/breadcrumb-ellipsis.svelte
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<script>
|
||||||
|
import Ellipsis from "lucide-svelte/icons/ellipsis";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<span
|
||||||
|
bind:this={ref}
|
||||||
|
role="presentation"
|
||||||
|
aria-hidden="true"
|
||||||
|
class={cn("flex size-9 items-center justify-center", className)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
<Ellipsis class="size-4" />
|
||||||
|
<span class="sr-only">More</span>
|
||||||
|
</span>
|
9
src/lib/components/ui/breadcrumb/breadcrumb-item.svelte
Normal file
9
src/lib/components/ui/breadcrumb/breadcrumb-item.svelte
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<script>
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<li bind:this={ref} class={cn("inline-flex items-center gap-1.5", className)} {...restProps}>
|
||||||
|
{@render children?.()}
|
||||||
|
</li>
|
26
src/lib/components/ui/breadcrumb/breadcrumb-link.svelte
Normal file
26
src/lib/components/ui/breadcrumb/breadcrumb-link.svelte
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<script>
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
href = undefined,
|
||||||
|
child,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
} = $props();
|
||||||
|
|
||||||
|
const attrs = $derived({
|
||||||
|
class: cn("hover:text-foreground transition-colors", className),
|
||||||
|
href,
|
||||||
|
...restProps,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if child}
|
||||||
|
{@render child({ props: attrs })}
|
||||||
|
{:else}
|
||||||
|
<a bind:this={ref} {...attrs}>
|
||||||
|
{@render children?.()}
|
||||||
|
</a>
|
||||||
|
{/if}
|
16
src/lib/components/ui/breadcrumb/breadcrumb-list.svelte
Normal file
16
src/lib/components/ui/breadcrumb/breadcrumb-list.svelte
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<script>
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ol
|
||||||
|
bind:this={ref}
|
||||||
|
class={cn(
|
||||||
|
"flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
</ol>
|
16
src/lib/components/ui/breadcrumb/breadcrumb-page.svelte
Normal file
16
src/lib/components/ui/breadcrumb/breadcrumb-page.svelte
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<script>
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<span
|
||||||
|
bind:this={ref}
|
||||||
|
role="link"
|
||||||
|
aria-disabled="true"
|
||||||
|
aria-current="page"
|
||||||
|
class={cn("font-normal text-foreground", className)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
</span>
|
20
src/lib/components/ui/breadcrumb/breadcrumb-separator.svelte
Normal file
20
src/lib/components/ui/breadcrumb/breadcrumb-separator.svelte
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<script>
|
||||||
|
import ChevronRight from "lucide-svelte/icons/chevron-right";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<li
|
||||||
|
role="presentation"
|
||||||
|
aria-hidden="true"
|
||||||
|
class={cn("[&>svg]:size-3.5", className)}
|
||||||
|
bind:this={ref}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{#if children}
|
||||||
|
{@render children?.()}
|
||||||
|
{:else}
|
||||||
|
<ChevronRight />
|
||||||
|
{/if}
|
||||||
|
</li>
|
7
src/lib/components/ui/breadcrumb/breadcrumb.svelte
Normal file
7
src/lib/components/ui/breadcrumb/breadcrumb.svelte
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<script>
|
||||||
|
let { ref = $bindable(), class: className, children, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<nav bind:this={ref} class={className} aria-label="breadcrumb" {...restProps}>
|
||||||
|
{@render children?.()}
|
||||||
|
</nav>
|
25
src/lib/components/ui/breadcrumb/index.js
Normal file
25
src/lib/components/ui/breadcrumb/index.js
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import Ellipsis from "./breadcrumb-ellipsis.svelte";
|
||||||
|
import Item from "./breadcrumb-item.svelte";
|
||||||
|
import Link from "./breadcrumb-link.svelte";
|
||||||
|
import List from "./breadcrumb-list.svelte";
|
||||||
|
import Page from "./breadcrumb-page.svelte";
|
||||||
|
import Separator from "./breadcrumb-separator.svelte";
|
||||||
|
import Root from "./breadcrumb.svelte";
|
||||||
|
|
||||||
|
export {
|
||||||
|
//
|
||||||
|
Root as Breadcrumb,
|
||||||
|
Ellipsis as BreadcrumbEllipsis,
|
||||||
|
Item as BreadcrumbItem,
|
||||||
|
Link as BreadcrumbLink,
|
||||||
|
List as BreadcrumbList,
|
||||||
|
Page as BreadcrumbPage,
|
||||||
|
Separator as BreadcrumbSeparator,
|
||||||
|
Ellipsis,
|
||||||
|
Item,
|
||||||
|
Link,
|
||||||
|
List,
|
||||||
|
Page,
|
||||||
|
Root,
|
||||||
|
Separator,
|
||||||
|
};
|
57
src/lib/components/ui/button/button.svelte
Normal file
57
src/lib/components/ui/button/button.svelte
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
<script module>
|
||||||
|
import { tv } from "tailwind-variants";
|
||||||
|
|
||||||
|
export const buttonVariants = tv({
|
||||||
|
base: "ring-offset-background focus-visible:ring-ring inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
||||||
|
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
||||||
|
outline: "border-input bg-background hover:bg-accent hover:text-accent-foreground border",
|
||||||
|
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||||
|
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||||
|
link: "text-primary underline-offset-4 hover:underline",
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
default: "h-10 px-4 py-2",
|
||||||
|
sm: "h-9 rounded-md px-3",
|
||||||
|
lg: "h-11 rounded-md px-8",
|
||||||
|
icon: "h-10 w-10",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "default",
|
||||||
|
size: "default",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
class: className,
|
||||||
|
variant = "default",
|
||||||
|
size = "default",
|
||||||
|
ref = $bindable(null),
|
||||||
|
href = undefined,
|
||||||
|
type = "button",
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
} = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if href}
|
||||||
|
<a bind:this={ref} class={cn(buttonVariants({ variant, size }), className)} {href} {...restProps}>
|
||||||
|
{@render children?.()}
|
||||||
|
</a>
|
||||||
|
{:else}
|
||||||
|
<button
|
||||||
|
bind:this={ref}
|
||||||
|
class={cn(buttonVariants({ variant, size }), className)}
|
||||||
|
{type}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
</button>
|
||||||
|
{/if}
|
8
src/lib/components/ui/button/index.js
Normal file
8
src/lib/components/ui/button/index.js
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import Root, { buttonVariants } from "./button.svelte";
|
||||||
|
|
||||||
|
export {
|
||||||
|
//
|
||||||
|
Root as Button,
|
||||||
|
buttonVariants,
|
||||||
|
Root,
|
||||||
|
};
|
15
src/lib/components/ui/calendar/calendar-cell.svelte
Normal file
15
src/lib/components/ui/calendar/calendar-cell.svelte
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<script>
|
||||||
|
import { Calendar as CalendarPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CalendarPrimitive.Cell
|
||||||
|
bind:ref
|
||||||
|
class={cn(
|
||||||
|
"relative size-9 p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([data-selected])]:rounded-md [&:has([data-selected])]:bg-accent [&:has([data-selected][data-outside-month])]:bg-accent/50",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
26
src/lib/components/ui/calendar/calendar-day.svelte
Normal file
26
src/lib/components/ui/calendar/calendar-day.svelte
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<script>
|
||||||
|
import { buttonVariants } from "$lib/components/ui/button/index.js";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
import { Calendar as CalendarPrimitive } from "bits-ui";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CalendarPrimitive.Day
|
||||||
|
bind:ref
|
||||||
|
class={cn(
|
||||||
|
buttonVariants({ variant: "ghost" }),
|
||||||
|
"size-9 p-0 font-normal",
|
||||||
|
"[&[data-today]:not([data-selected])]:bg-accent [&[data-today]:not([data-selected])]:text-accent-foreground",
|
||||||
|
// Selected
|
||||||
|
"data-[selected]:bg-primary data-[selected]:text-primary-foreground data-[selected]:opacity-100 data-[selected]:hover:bg-primary data-[selected]:hover:text-primary-foreground data-[selected]:focus:bg-primary data-[selected]:focus:text-primary-foreground",
|
||||||
|
// Disabled
|
||||||
|
"data-[disabled]:text-muted-foreground data-[disabled]:opacity-50",
|
||||||
|
// Unavailable
|
||||||
|
"data-[unavailable]:text-destructive-foreground data-[unavailable]:line-through",
|
||||||
|
// Outside months
|
||||||
|
"data-[outside-month]:pointer-events-none data-[outside-month]:text-muted-foreground data-[outside-month]:opacity-50 [&[data-outside-month][data-selected]]:bg-accent/50 [&[data-outside-month][data-selected]]:text-muted-foreground [&[data-outside-month][data-selected]]:opacity-30",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
8
src/lib/components/ui/calendar/calendar-grid-body.svelte
Normal file
8
src/lib/components/ui/calendar/calendar-grid-body.svelte
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<script>
|
||||||
|
import { Calendar as CalendarPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CalendarPrimitive.GridBody bind:ref class={cn(className)} {...restProps} />
|
8
src/lib/components/ui/calendar/calendar-grid-head.svelte
Normal file
8
src/lib/components/ui/calendar/calendar-grid-head.svelte
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<script>
|
||||||
|
import { Calendar as CalendarPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CalendarPrimitive.GridHead bind:ref class={cn(className)} {...restProps} />
|
8
src/lib/components/ui/calendar/calendar-grid-row.svelte
Normal file
8
src/lib/components/ui/calendar/calendar-grid-row.svelte
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<script>
|
||||||
|
import { Calendar as CalendarPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CalendarPrimitive.GridRow bind:ref class={cn("flex", className)} {...restProps} />
|
12
src/lib/components/ui/calendar/calendar-grid.svelte
Normal file
12
src/lib/components/ui/calendar/calendar-grid.svelte
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<script>
|
||||||
|
import { Calendar as CalendarPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CalendarPrimitive.Grid
|
||||||
|
bind:ref
|
||||||
|
class={cn("w-full border-collapse space-y-1", className)}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
12
src/lib/components/ui/calendar/calendar-head-cell.svelte
Normal file
12
src/lib/components/ui/calendar/calendar-head-cell.svelte
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<script>
|
||||||
|
import { Calendar as CalendarPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CalendarPrimitive.HeadCell
|
||||||
|
bind:ref
|
||||||
|
class={cn("w-9 rounded-md text-[0.8rem] font-normal text-muted-foreground", className)}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
12
src/lib/components/ui/calendar/calendar-header.svelte
Normal file
12
src/lib/components/ui/calendar/calendar-header.svelte
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<script>
|
||||||
|
import { Calendar as CalendarPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CalendarPrimitive.Header
|
||||||
|
bind:ref
|
||||||
|
class={cn("relative flex w-full items-center justify-between pt-1", className)}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
8
src/lib/components/ui/calendar/calendar-heading.svelte
Normal file
8
src/lib/components/ui/calendar/calendar-heading.svelte
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<script>
|
||||||
|
import { Calendar as CalendarPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CalendarPrimitive.Heading bind:ref class={cn("text-sm font-medium", className)} {...restProps} />
|
13
src/lib/components/ui/calendar/calendar-months.svelte
Normal file
13
src/lib/components/ui/calendar/calendar-months.svelte
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<script>
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
bind:this={ref}
|
||||||
|
class={cn("mt-4 flex flex-col space-y-4 sm:flex-row sm:space-x-4 sm:space-y-0", className)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
23
src/lib/components/ui/calendar/calendar-next-button.svelte
Normal file
23
src/lib/components/ui/calendar/calendar-next-button.svelte
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<script>
|
||||||
|
import { Calendar as CalendarPrimitive } from "bits-ui";
|
||||||
|
import ChevronRight from "lucide-svelte/icons/chevron-right";
|
||||||
|
import { buttonVariants } from "$lib/components/ui/button/index.js";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#snippet Fallback()}
|
||||||
|
<ChevronRight class="size-4" />
|
||||||
|
{/snippet}
|
||||||
|
|
||||||
|
<CalendarPrimitive.NextButton
|
||||||
|
bind:ref
|
||||||
|
class={cn(
|
||||||
|
buttonVariants({ variant: "outline" }),
|
||||||
|
"size-7 bg-transparent p-0 opacity-50 hover:opacity-100",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
children={children || Fallback}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
23
src/lib/components/ui/calendar/calendar-prev-button.svelte
Normal file
23
src/lib/components/ui/calendar/calendar-prev-button.svelte
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<script>
|
||||||
|
import { Calendar as CalendarPrimitive } from "bits-ui";
|
||||||
|
import ChevronLeft from "lucide-svelte/icons/chevron-left";
|
||||||
|
import { buttonVariants } from "$lib/components/ui/button/index.js";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#snippet Fallback()}
|
||||||
|
<ChevronLeft class="size-4" />
|
||||||
|
{/snippet}
|
||||||
|
|
||||||
|
<CalendarPrimitive.PrevButton
|
||||||
|
bind:ref
|
||||||
|
class={cn(
|
||||||
|
buttonVariants({ variant: "outline" }),
|
||||||
|
"size-7 bg-transparent p-0 opacity-50 hover:opacity-100",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
children={children || Fallback}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
61
src/lib/components/ui/calendar/calendar.svelte
Normal file
61
src/lib/components/ui/calendar/calendar.svelte
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
<script>
|
||||||
|
import { Calendar as CalendarPrimitive } from "bits-ui";
|
||||||
|
import * as Calendar from "./index.js";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
value = $bindable(),
|
||||||
|
placeholder = $bindable(),
|
||||||
|
class: className,
|
||||||
|
weekdayFormat = "short",
|
||||||
|
...restProps
|
||||||
|
} = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Discriminated Unions + Destructing (required for bindable) do not
|
||||||
|
get along, so we shut typescript up by casting `value` to `never`.
|
||||||
|
-->
|
||||||
|
<CalendarPrimitive.Root
|
||||||
|
bind:value
|
||||||
|
bind:ref
|
||||||
|
bind:placeholder
|
||||||
|
{weekdayFormat}
|
||||||
|
class={cn("p-3", className)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{#snippet children({ months, weekdays })}
|
||||||
|
<Calendar.Header>
|
||||||
|
<Calendar.PrevButton />
|
||||||
|
<Calendar.Heading />
|
||||||
|
<Calendar.NextButton />
|
||||||
|
</Calendar.Header>
|
||||||
|
<Calendar.Months>
|
||||||
|
{#each months as month}
|
||||||
|
<Calendar.Grid>
|
||||||
|
<Calendar.GridHead>
|
||||||
|
<Calendar.GridRow class="flex">
|
||||||
|
{#each weekdays as weekday}
|
||||||
|
<Calendar.HeadCell>
|
||||||
|
{weekday.slice(0, 2)}
|
||||||
|
</Calendar.HeadCell>
|
||||||
|
{/each}
|
||||||
|
</Calendar.GridRow>
|
||||||
|
</Calendar.GridHead>
|
||||||
|
<Calendar.GridBody>
|
||||||
|
{#each month.weeks as weekDates}
|
||||||
|
<Calendar.GridRow class="mt-2 w-full">
|
||||||
|
{#each weekDates as date}
|
||||||
|
<Calendar.Cell {date} month={month.value}>
|
||||||
|
<Calendar.Day />
|
||||||
|
</Calendar.Cell>
|
||||||
|
{/each}
|
||||||
|
</Calendar.GridRow>
|
||||||
|
{/each}
|
||||||
|
</Calendar.GridBody>
|
||||||
|
</Calendar.Grid>
|
||||||
|
{/each}
|
||||||
|
</Calendar.Months>
|
||||||
|
{/snippet}
|
||||||
|
</CalendarPrimitive.Root>
|
30
src/lib/components/ui/calendar/index.js
Normal file
30
src/lib/components/ui/calendar/index.js
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import Cell from "./calendar-cell.svelte";
|
||||||
|
import Day from "./calendar-day.svelte";
|
||||||
|
import GridBody from "./calendar-grid-body.svelte";
|
||||||
|
import GridHead from "./calendar-grid-head.svelte";
|
||||||
|
import GridRow from "./calendar-grid-row.svelte";
|
||||||
|
import Grid from "./calendar-grid.svelte";
|
||||||
|
import HeadCell from "./calendar-head-cell.svelte";
|
||||||
|
import Header from "./calendar-header.svelte";
|
||||||
|
import Heading from "./calendar-heading.svelte";
|
||||||
|
import Months from "./calendar-months.svelte";
|
||||||
|
import NextButton from "./calendar-next-button.svelte";
|
||||||
|
import PrevButton from "./calendar-prev-button.svelte";
|
||||||
|
import Root from "./calendar.svelte";
|
||||||
|
|
||||||
|
export {
|
||||||
|
//
|
||||||
|
Root as Calendar,
|
||||||
|
Cell,
|
||||||
|
Day,
|
||||||
|
Grid,
|
||||||
|
GridBody,
|
||||||
|
GridHead,
|
||||||
|
GridRow,
|
||||||
|
HeadCell,
|
||||||
|
Header,
|
||||||
|
Heading,
|
||||||
|
Months,
|
||||||
|
NextButton,
|
||||||
|
PrevButton,
|
||||||
|
};
|
9
src/lib/components/ui/card/card-content.svelte
Normal file
9
src/lib/components/ui/card/card-content.svelte
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<script>
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={ref} class={cn("p-6", className)} {...restProps}>
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
9
src/lib/components/ui/card/card-description.svelte
Normal file
9
src/lib/components/ui/card/card-description.svelte
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<script>
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<p bind:this={ref} class={cn("text-sm text-muted-foreground", className)} {...restProps}>
|
||||||
|
{@render children?.()}
|
||||||
|
</p>
|
9
src/lib/components/ui/card/card-footer.svelte
Normal file
9
src/lib/components/ui/card/card-footer.svelte
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<script>
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={ref} class={cn("flex items-center p-6 pt-0", className)} {...restProps}>
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
9
src/lib/components/ui/card/card-header.svelte
Normal file
9
src/lib/components/ui/card/card-header.svelte
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<script>
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={ref} class={cn("flex flex-col space-y-1.5 p-6 pb-0", className)} {...restProps}>
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
15
src/lib/components/ui/card/card-title.svelte
Normal file
15
src/lib/components/ui/card/card-title.svelte
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<script>
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, level = 3, children, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
role="heading"
|
||||||
|
aria-level={level}
|
||||||
|
bind:this={ref}
|
||||||
|
class={cn("text-lg font-semibold leading-none tracking-tight", className)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
13
src/lib/components/ui/card/card.svelte
Normal file
13
src/lib/components/ui/card/card.svelte
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<script>
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
bind:this={ref}
|
||||||
|
class={cn("rounded-lg border bg-card text-card-foreground shadow-sm", className)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
22
src/lib/components/ui/card/index.js
Normal file
22
src/lib/components/ui/card/index.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import Content from "./card-content.svelte";
|
||||||
|
import Description from "./card-description.svelte";
|
||||||
|
import Footer from "./card-footer.svelte";
|
||||||
|
import Header from "./card-header.svelte";
|
||||||
|
import Title from "./card-title.svelte";
|
||||||
|
import Root from "./card.svelte";
|
||||||
|
|
||||||
|
export {
|
||||||
|
//
|
||||||
|
Root as Card,
|
||||||
|
Content as CardContent,
|
||||||
|
Description as CardDescription,
|
||||||
|
Footer as CardFooter,
|
||||||
|
Header as CardHeader,
|
||||||
|
Title as CardTitle,
|
||||||
|
Content,
|
||||||
|
Description,
|
||||||
|
Footer,
|
||||||
|
Header,
|
||||||
|
Root,
|
||||||
|
Title,
|
||||||
|
};
|
37
src/lib/components/ui/carousel/carousel-content.svelte
Normal file
37
src/lib/components/ui/carousel/carousel-content.svelte
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<script>
|
||||||
|
import emblaCarouselSvelte from "embla-carousel-svelte";
|
||||||
|
import { getEmblaContext } from "./context.js";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
||||||
|
|
||||||
|
const emblaCtx = getEmblaContext("<Carousel.Content/>");
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore event_directive_deprecated -->
|
||||||
|
<div
|
||||||
|
class="overflow-hidden"
|
||||||
|
use:emblaCarouselSvelte={{
|
||||||
|
options: {
|
||||||
|
container: "[data-embla-container]",
|
||||||
|
slides: "[data-embla-slide]",
|
||||||
|
...emblaCtx.options,
|
||||||
|
axis: emblaCtx.orientation === "horizontal" ? "x" : "y",
|
||||||
|
},
|
||||||
|
plugins: emblaCtx.plugins,
|
||||||
|
}}
|
||||||
|
on:emblaInit={emblaCtx.onInit}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
bind:this={ref}
|
||||||
|
class={cn(
|
||||||
|
"flex",
|
||||||
|
emblaCtx.orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
data-embla-container=""
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
||||||
|
</div>
|
23
src/lib/components/ui/carousel/carousel-item.svelte
Normal file
23
src/lib/components/ui/carousel/carousel-item.svelte
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<script>
|
||||||
|
import { getEmblaContext } from "./context.js";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
||||||
|
|
||||||
|
const emblaCtx = getEmblaContext("<Carousel.Item/>");
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
bind:this={ref}
|
||||||
|
role="group"
|
||||||
|
aria-roledescription="slide"
|
||||||
|
class={cn(
|
||||||
|
"min-w-0 shrink-0 grow-0 basis-full",
|
||||||
|
emblaCtx.orientation === "horizontal" ? "pl-4" : "pt-4",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
data-embla-slide=""
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
36
src/lib/components/ui/carousel/carousel-next.svelte
Normal file
36
src/lib/components/ui/carousel/carousel-next.svelte
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<script>
|
||||||
|
import ArrowRight from "lucide-svelte/icons/arrow-right";
|
||||||
|
import { getEmblaContext } from "./context.js";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
import { Button } from "$lib/components/ui/button/index.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
variant = "outline",
|
||||||
|
size = "icon",
|
||||||
|
...restProps
|
||||||
|
} = $props();
|
||||||
|
|
||||||
|
const emblaCtx = getEmblaContext("<Carousel.Next/>");
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
{variant}
|
||||||
|
{size}
|
||||||
|
class={cn(
|
||||||
|
"absolute size-8 touch-manipulation rounded-full",
|
||||||
|
emblaCtx.orientation === "horizontal"
|
||||||
|
? "-right-12 top-1/2 -translate-y-1/2"
|
||||||
|
: "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
disabled={!emblaCtx.canScrollNext}
|
||||||
|
onclick={emblaCtx.scrollNext}
|
||||||
|
onkeydown={emblaCtx.handleKeyDown}
|
||||||
|
bind:ref
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
<ArrowRight class="size-4" />
|
||||||
|
<span class="sr-only">Next slide</span>
|
||||||
|
</Button>
|
36
src/lib/components/ui/carousel/carousel-previous.svelte
Normal file
36
src/lib/components/ui/carousel/carousel-previous.svelte
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<script>
|
||||||
|
import ArrowLeft from "lucide-svelte/icons/arrow-left";
|
||||||
|
import { getEmblaContext } from "./context.js";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
import { Button } from "$lib/components/ui/button/index.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
variant = "outline",
|
||||||
|
size = "icon",
|
||||||
|
...restProps
|
||||||
|
} = $props();
|
||||||
|
|
||||||
|
const emblaCtx = getEmblaContext("<Carousel.Previous/>");
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
{variant}
|
||||||
|
{size}
|
||||||
|
class={cn(
|
||||||
|
"absolute size-8 touch-manipulation rounded-full",
|
||||||
|
emblaCtx.orientation === "horizontal"
|
||||||
|
? "-left-12 top-1/2 -translate-y-1/2"
|
||||||
|
: "-top-12 left-1/2 -translate-x-1/2 rotate-90",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
disabled={!emblaCtx.canScrollPrev}
|
||||||
|
onclick={emblaCtx.scrollPrev}
|
||||||
|
onkeydown={emblaCtx.handleKeyDown}
|
||||||
|
{...restProps}
|
||||||
|
bind:ref
|
||||||
|
>
|
||||||
|
<ArrowLeft class="size-4" />
|
||||||
|
<span class="sr-only">Previous slide</span>
|
||||||
|
</Button>
|
87
src/lib/components/ui/carousel/carousel.svelte
Normal file
87
src/lib/components/ui/carousel/carousel.svelte
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
<script>
|
||||||
|
import { setEmblaContext } from "./context.js";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
opts = {},
|
||||||
|
plugins = [],
|
||||||
|
setApi = () => {},
|
||||||
|
orientation = "horizontal",
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
} = $props();
|
||||||
|
|
||||||
|
let carouselState = $state({
|
||||||
|
api: undefined,
|
||||||
|
scrollPrev,
|
||||||
|
scrollNext,
|
||||||
|
orientation,
|
||||||
|
canScrollNext: false,
|
||||||
|
canScrollPrev: false,
|
||||||
|
handleKeyDown,
|
||||||
|
options: opts,
|
||||||
|
plugins,
|
||||||
|
onInit,
|
||||||
|
scrollSnaps: [],
|
||||||
|
selectedIndex: 0,
|
||||||
|
scrollTo,
|
||||||
|
});
|
||||||
|
|
||||||
|
setEmblaContext(carouselState);
|
||||||
|
|
||||||
|
function scrollPrev() {
|
||||||
|
carouselState.api?.scrollPrev();
|
||||||
|
}
|
||||||
|
function scrollNext() {
|
||||||
|
carouselState.api?.scrollNext();
|
||||||
|
}
|
||||||
|
function scrollTo(index, jump) {
|
||||||
|
carouselState.api?.scrollTo(index, jump);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSelect(api) {
|
||||||
|
if (!api) return;
|
||||||
|
carouselState.canScrollPrev = api.canScrollPrev();
|
||||||
|
carouselState.canScrollNext = api.canScrollNext();
|
||||||
|
carouselState.selectedIndex = api.selectedScrollSnap();
|
||||||
|
}
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
if (carouselState.api) {
|
||||||
|
onSelect(carouselState.api);
|
||||||
|
carouselState.api.on("select", onSelect);
|
||||||
|
carouselState.api.on("reInit", onSelect);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleKeyDown(e) {
|
||||||
|
if (e.key === "ArrowLeft") {
|
||||||
|
e.preventDefault();
|
||||||
|
scrollPrev();
|
||||||
|
} else if (e.key === "ArrowRight") {
|
||||||
|
e.preventDefault();
|
||||||
|
scrollNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
setApi(carouselState.api);
|
||||||
|
});
|
||||||
|
|
||||||
|
function onInit(event) {
|
||||||
|
carouselState.api = event.detail;
|
||||||
|
|
||||||
|
carouselState.scrollSnaps = carouselState.api.scrollSnapList();
|
||||||
|
}
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
return () => {
|
||||||
|
carouselState.api?.off("select", onSelect);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class={cn("relative", className)} role="region" aria-roledescription="carousel" {...restProps}>
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
16
src/lib/components/ui/carousel/context.js
Normal file
16
src/lib/components/ui/carousel/context.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { getContext, hasContext, setContext } from "svelte";
|
||||||
|
////
|
||||||
|
|
||||||
|
const EMBLA_CAROUSEL_CONTEXT = Symbol("EMBLA_CAROUSEL_CONTEXT");
|
||||||
|
|
||||||
|
export function setEmblaContext(config) {
|
||||||
|
setContext(EMBLA_CAROUSEL_CONTEXT, config);
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getEmblaContext(name = "This component") {
|
||||||
|
if (!hasContext(EMBLA_CAROUSEL_CONTEXT)) {
|
||||||
|
throw new Error(`${name} must be used within a <Carousel.Root> component`);
|
||||||
|
}
|
||||||
|
return getContext(EMBLA_CAROUSEL_CONTEXT);
|
||||||
|
}
|
19
src/lib/components/ui/carousel/index.js
Normal file
19
src/lib/components/ui/carousel/index.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import Content from "./carousel-content.svelte";
|
||||||
|
import Item from "./carousel-item.svelte";
|
||||||
|
import Next from "./carousel-next.svelte";
|
||||||
|
import Previous from "./carousel-previous.svelte";
|
||||||
|
import Root from "./carousel.svelte";
|
||||||
|
|
||||||
|
export {
|
||||||
|
//
|
||||||
|
Root as Carousel,
|
||||||
|
Content as CarouselContent,
|
||||||
|
Item as CarouselItem,
|
||||||
|
Next as CarouselNext,
|
||||||
|
Previous as CarouselPrevious,
|
||||||
|
Content,
|
||||||
|
Item,
|
||||||
|
Next,
|
||||||
|
Previous,
|
||||||
|
Root,
|
||||||
|
};
|
33
src/lib/components/ui/checkbox/checkbox.svelte
Normal file
33
src/lib/components/ui/checkbox/checkbox.svelte
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
<script>
|
||||||
|
import { Checkbox as CheckboxPrimitive } from "bits-ui";
|
||||||
|
import Check from "lucide-svelte/icons/check";
|
||||||
|
import Minus from "lucide-svelte/icons/minus";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
checked = $bindable(false),
|
||||||
|
class: className,
|
||||||
|
...restProps
|
||||||
|
} = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CheckboxPrimitive.Root
|
||||||
|
bind:ref
|
||||||
|
class={cn(
|
||||||
|
"peer box-content size-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[disabled=true]:cursor-not-allowed data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[disabled=true]:opacity-50",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
bind:checked
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{#snippet children({ checked })}
|
||||||
|
<div class="flex size-4 items-center justify-center text-current">
|
||||||
|
{#if checked === "indeterminate"}
|
||||||
|
<Minus class="size-3.5" />
|
||||||
|
{:else}
|
||||||
|
<Check class={cn("size-3.5", !checked && "text-transparent")} />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/snippet}
|
||||||
|
</CheckboxPrimitive.Root>
|
6
src/lib/components/ui/checkbox/index.js
Normal file
6
src/lib/components/ui/checkbox/index.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import Root from "./checkbox.svelte";
|
||||||
|
export {
|
||||||
|
//
|
||||||
|
Root as Checkbox,
|
||||||
|
Root,
|
||||||
|
};
|
15
src/lib/components/ui/collapsible/index.js
Normal file
15
src/lib/components/ui/collapsible/index.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { Collapsible as CollapsiblePrimitive } from "bits-ui";
|
||||||
|
|
||||||
|
const Root = CollapsiblePrimitive.Root;
|
||||||
|
const Trigger = CollapsiblePrimitive.Trigger;
|
||||||
|
const Content = CollapsiblePrimitive.Content;
|
||||||
|
|
||||||
|
export {
|
||||||
|
//
|
||||||
|
Root as Collapsible,
|
||||||
|
Content as CollapsibleContent,
|
||||||
|
Trigger as CollapsibleTrigger,
|
||||||
|
Content,
|
||||||
|
Root,
|
||||||
|
Trigger,
|
||||||
|
};
|
24
src/lib/components/ui/command/command-dialog.svelte
Normal file
24
src/lib/components/ui/command/command-dialog.svelte
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<script>
|
||||||
|
import Command from "./command.svelte";
|
||||||
|
import * as Dialog from "$lib/components/ui/dialog/index.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
open = $bindable(false),
|
||||||
|
ref = $bindable(null),
|
||||||
|
value = $bindable(""),
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
} = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Dialog.Root bind:open {...restProps}>
|
||||||
|
<Dialog.Content class="overflow-hidden p-0 shadow-lg">
|
||||||
|
<Command
|
||||||
|
class="[&_[data-command-group]:not([hidden])_~[data-command-group]]:pt-0 [&_[data-command-group]]:px-2 [&_[data-command-input-wrapper]_svg]:h-5 [&_[data-command-input-wrapper]_svg]:w-5 [&_[data-command-input]]:h-12 [&_[data-command-item]]:px-2 [&_[data-command-item]]:py-3 [&_[data-command-item]_svg]:h-5 [&_[data-command-item]_svg]:w-5"
|
||||||
|
{...restProps}
|
||||||
|
bind:value
|
||||||
|
bind:ref
|
||||||
|
{children}
|
||||||
|
/>
|
||||||
|
</Dialog.Content>
|
||||||
|
</Dialog.Root>
|
8
src/lib/components/ui/command/command-empty.svelte
Normal file
8
src/lib/components/ui/command/command-empty.svelte
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<script>
|
||||||
|
import { Command as CommandPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CommandPrimitive.Empty class={cn("py-6 text-center text-sm", className)} {...restProps} />
|
19
src/lib/components/ui/command/command-group.svelte
Normal file
19
src/lib/components/ui/command/command-group.svelte
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<script>
|
||||||
|
import { Command as CommandPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, children, heading, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CommandPrimitive.Group
|
||||||
|
class={cn("overflow-hidden p-1 text-foreground", className)}
|
||||||
|
bind:ref
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{#if heading}
|
||||||
|
<CommandPrimitive.GroupHeading class="px-2 py-1.5 text-xs font-medium text-muted-foreground">
|
||||||
|
{heading}
|
||||||
|
</CommandPrimitive.GroupHeading>
|
||||||
|
{/if}
|
||||||
|
<CommandPrimitive.GroupItems {children} />
|
||||||
|
</CommandPrimitive.Group>
|
20
src/lib/components/ui/command/command-input.svelte
Normal file
20
src/lib/components/ui/command/command-input.svelte
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<script>
|
||||||
|
import { Command as CommandPrimitive } from "bits-ui";
|
||||||
|
import Search from "lucide-svelte/icons/search";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, value = $bindable(""), ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex items-center border-b px-2" data-command-input-wrapper="">
|
||||||
|
<Search class="mr-2 size-4 shrink-0 opacity-50" />
|
||||||
|
<CommandPrimitive.Input
|
||||||
|
class={cn(
|
||||||
|
"flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
bind:ref
|
||||||
|
{...restProps}
|
||||||
|
bind:value
|
||||||
|
/>
|
||||||
|
</div>
|
15
src/lib/components/ui/command/command-item.svelte
Normal file
15
src/lib/components/ui/command/command-item.svelte
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<script>
|
||||||
|
import { Command as CommandPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CommandPrimitive.Item
|
||||||
|
class={cn(
|
||||||
|
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
bind:ref
|
||||||
|
{...restProps}
|
||||||
|
/>
|
15
src/lib/components/ui/command/command-link-item.svelte
Normal file
15
src/lib/components/ui/command/command-link-item.svelte
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<script>
|
||||||
|
import { Command as CommandPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CommandPrimitive.LinkItem
|
||||||
|
class={cn(
|
||||||
|
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
bind:ref
|
||||||
|
{...restProps}
|
||||||
|
/>
|
12
src/lib/components/ui/command/command-list.svelte
Normal file
12
src/lib/components/ui/command/command-list.svelte
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<script>
|
||||||
|
import { Command as CommandPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CommandPrimitive.List
|
||||||
|
class={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className)}
|
||||||
|
{...restProps}
|
||||||
|
bind:ref
|
||||||
|
/>
|
8
src/lib/components/ui/command/command-separator.svelte
Normal file
8
src/lib/components/ui/command/command-separator.svelte
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<script>
|
||||||
|
import { Command as CommandPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CommandPrimitive.Separator class={cn("-mx-1 h-px bg-border", className)} bind:ref {...restProps} />
|
13
src/lib/components/ui/command/command-shortcut.svelte
Normal file
13
src/lib/components/ui/command/command-shortcut.svelte
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<script>
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<span
|
||||||
|
bind:this={ref}
|
||||||
|
class={cn("ml-auto text-xs tracking-widest text-muted-foreground", className)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
</span>
|
16
src/lib/components/ui/command/command.svelte
Normal file
16
src/lib/components/ui/command/command.svelte
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<script>
|
||||||
|
import { Command as CommandPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), value = $bindable(""), class: className, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CommandPrimitive.Root
|
||||||
|
class={cn(
|
||||||
|
"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
bind:value
|
||||||
|
bind:ref
|
||||||
|
{...restProps}
|
||||||
|
/>
|
40
src/lib/components/ui/command/index.js
Normal file
40
src/lib/components/ui/command/index.js
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import { Command as CommandPrimitive } from "bits-ui";
|
||||||
|
|
||||||
|
import Dialog from "./command-dialog.svelte";
|
||||||
|
import Empty from "./command-empty.svelte";
|
||||||
|
import Group from "./command-group.svelte";
|
||||||
|
import Input from "./command-input.svelte";
|
||||||
|
import Item from "./command-item.svelte";
|
||||||
|
import LinkItem from "./command-link-item.svelte";
|
||||||
|
import List from "./command-list.svelte";
|
||||||
|
import Separator from "./command-separator.svelte";
|
||||||
|
import Shortcut from "./command-shortcut.svelte";
|
||||||
|
import Root from "./command.svelte";
|
||||||
|
|
||||||
|
const Loading = CommandPrimitive.Loading;
|
||||||
|
|
||||||
|
export {
|
||||||
|
//
|
||||||
|
Root as Command,
|
||||||
|
Dialog as CommandDialog,
|
||||||
|
Empty as CommandEmpty,
|
||||||
|
Group as CommandGroup,
|
||||||
|
Input as CommandInput,
|
||||||
|
Item as CommandItem,
|
||||||
|
LinkItem as CommandLinkItem,
|
||||||
|
List as CommandList,
|
||||||
|
Loading as CommandLoading,
|
||||||
|
Separator as CommandSeparator,
|
||||||
|
Shortcut as CommandShortcut,
|
||||||
|
Dialog,
|
||||||
|
Empty,
|
||||||
|
Group,
|
||||||
|
Input,
|
||||||
|
Item,
|
||||||
|
LinkItem,
|
||||||
|
List,
|
||||||
|
Loading,
|
||||||
|
Root,
|
||||||
|
Separator,
|
||||||
|
Shortcut,
|
||||||
|
};
|
|
@ -0,0 +1,35 @@
|
||||||
|
<script>
|
||||||
|
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
|
||||||
|
import Check from "lucide-svelte/icons/check";
|
||||||
|
import Minus from "lucide-svelte/icons/minus";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
checked = $bindable(false),
|
||||||
|
class: className,
|
||||||
|
children: childrenProp,
|
||||||
|
...restProps
|
||||||
|
} = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ContextMenuPrimitive.CheckboxItem
|
||||||
|
bind:ref
|
||||||
|
bind:checked
|
||||||
|
class={cn(
|
||||||
|
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:opacity-50",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{#snippet children({ checked })}
|
||||||
|
<span class="absolute left-2 flex size-3.5 items-center justify-center">
|
||||||
|
{#if checked === "indeterminate"}
|
||||||
|
<Minus class="size-3.5" />
|
||||||
|
{:else}
|
||||||
|
<Check class={cn("size-3.5", !checked && "text-transparent")} />
|
||||||
|
{/if}
|
||||||
|
</span>
|
||||||
|
{@render childrenProp?.({ checked })}
|
||||||
|
{/snippet}
|
||||||
|
</ContextMenuPrimitive.CheckboxItem>
|
|
@ -0,0 +1,15 @@
|
||||||
|
<script>
|
||||||
|
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ContextMenuPrimitive.Content
|
||||||
|
bind:ref
|
||||||
|
class={cn(
|
||||||
|
"z-50 min-w-[8rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-md focus:outline-none",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<script>
|
||||||
|
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, inset, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ContextMenuPrimitive.GroupHeading
|
||||||
|
bind:ref
|
||||||
|
class={cn("px-2 py-1.5 text-sm font-semibold text-foreground", inset && "pl-8", className)}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
16
src/lib/components/ui/context-menu/context-menu-item.svelte
Normal file
16
src/lib/components/ui/context-menu/context-menu-item.svelte
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<script>
|
||||||
|
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, inset, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ContextMenuPrimitive.Item
|
||||||
|
bind:ref
|
||||||
|
class={cn(
|
||||||
|
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:opacity-50",
|
||||||
|
inset && "pl-8",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
|
@ -0,0 +1,25 @@
|
||||||
|
<script>
|
||||||
|
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
|
||||||
|
import Circle from "lucide-svelte/icons/circle";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, children: childrenProp, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ContextMenuPrimitive.RadioItem
|
||||||
|
bind:ref
|
||||||
|
class={cn(
|
||||||
|
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:opacity-50",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{#snippet children({ checked })}
|
||||||
|
<span class="absolute left-2 flex size-3.5 items-center justify-center">
|
||||||
|
{#if checked}
|
||||||
|
<Circle class="size-2 fill-current" />
|
||||||
|
{/if}
|
||||||
|
</span>
|
||||||
|
{@render childrenProp?.({ checked })}
|
||||||
|
{/snippet}
|
||||||
|
</ContextMenuPrimitive.RadioItem>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<script>
|
||||||
|
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ContextMenuPrimitive.Separator
|
||||||
|
bind:ref
|
||||||
|
class={cn("-mx-1 my-1 h-px bg-border", className)}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
|
@ -0,0 +1,13 @@
|
||||||
|
<script>
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<span
|
||||||
|
bind:this={ref}
|
||||||
|
class={cn("ml-auto text-xs tracking-widest text-muted-foreground", className)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
</span>
|
|
@ -0,0 +1,15 @@
|
||||||
|
<script>
|
||||||
|
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ContextMenuPrimitive.SubContent
|
||||||
|
bind:ref
|
||||||
|
class={cn(
|
||||||
|
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md focus:outline-none",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
|
@ -0,0 +1,20 @@
|
||||||
|
<script>
|
||||||
|
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
|
||||||
|
import ChevronRight from "lucide-svelte/icons/chevron-right";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), class: className, inset, children, ...restProps } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ContextMenuPrimitive.SubTrigger
|
||||||
|
bind:ref
|
||||||
|
class={cn(
|
||||||
|
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[highlighted]:bg-accent data-[state=open]:bg-accent data-[highlighted]:text-accent-foreground data-[state=open]:text-accent-foreground",
|
||||||
|
inset && "pl-8",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
<ChevronRight class="ml-auto size-4" />
|
||||||
|
</ContextMenuPrimitive.SubTrigger>
|
49
src/lib/components/ui/context-menu/index.js
Normal file
49
src/lib/components/ui/context-menu/index.js
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import { ContextMenu as ContextMenuPrimitive } from "bits-ui";
|
||||||
|
|
||||||
|
import CheckboxItem from "./context-menu-checkbox-item.svelte";
|
||||||
|
import Content from "./context-menu-content.svelte";
|
||||||
|
import GroupHeading from "./context-menu-group-heading.svelte";
|
||||||
|
import Item from "./context-menu-item.svelte";
|
||||||
|
import RadioItem from "./context-menu-radio-item.svelte";
|
||||||
|
import Separator from "./context-menu-separator.svelte";
|
||||||
|
import Shortcut from "./context-menu-shortcut.svelte";
|
||||||
|
import SubContent from "./context-menu-sub-content.svelte";
|
||||||
|
import SubTrigger from "./context-menu-sub-trigger.svelte";
|
||||||
|
|
||||||
|
const Sub = ContextMenuPrimitive.Sub;
|
||||||
|
const Root = ContextMenuPrimitive.Root;
|
||||||
|
const Trigger = ContextMenuPrimitive.Trigger;
|
||||||
|
const Group = ContextMenuPrimitive.Group;
|
||||||
|
const RadioGroup = ContextMenuPrimitive.RadioGroup;
|
||||||
|
|
||||||
|
export {
|
||||||
|
CheckboxItem,
|
||||||
|
Content,
|
||||||
|
//
|
||||||
|
Root as ContextMenu,
|
||||||
|
CheckboxItem as ContextMenuCheckboxItem,
|
||||||
|
Content as ContextMenuContent,
|
||||||
|
Group as ContextMenuGroup,
|
||||||
|
GroupHeading as ContextMenuGroupHeading,
|
||||||
|
Item as ContextMenuItem,
|
||||||
|
RadioGroup as ContextMenuRadioGroup,
|
||||||
|
RadioItem as ContextMenuRadioItem,
|
||||||
|
Separator as ContextMenuSeparator,
|
||||||
|
Shortcut as ContextMenuShortcut,
|
||||||
|
Sub as ContextMenuSub,
|
||||||
|
SubContent as ContextMenuSubContent,
|
||||||
|
SubTrigger as ContextMenuSubTrigger,
|
||||||
|
Trigger as ContextMenuTrigger,
|
||||||
|
Group,
|
||||||
|
GroupHeading,
|
||||||
|
Item,
|
||||||
|
RadioGroup,
|
||||||
|
RadioItem,
|
||||||
|
Root,
|
||||||
|
Separator,
|
||||||
|
Shortcut,
|
||||||
|
Sub,
|
||||||
|
SubContent,
|
||||||
|
SubTrigger,
|
||||||
|
Trigger,
|
||||||
|
};
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue