This commit is contained in:
David Senoner 2025-05-18 13:18:46 +02:00
commit 0738070ce1
287 changed files with 10116 additions and 0 deletions

38
README.md Normal file
View 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
View 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
View 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
View 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",
},
});

View 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 $$;

View 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": {}
}
}

View 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
View file

@ -0,0 +1,3 @@
{
"extends": "./.svelte-kit/tsconfig.json"
}

66
package.json Normal file
View 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

File diff suppressed because it is too large Load diff

6
postcss.config.js Normal file
View file

@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

75
src/app.css Normal file
View 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
View 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
View 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;

View 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>

View 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} />

View 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>

View 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,
};

View file

@ -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} />

View file

@ -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}
/>

View file

@ -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>

View file

@ -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}
/>

View 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("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)}
{...restProps}
>
{@render children?.()}
</div>

View 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("flex flex-col space-y-2 text-center sm:text-left", className)}
{...restProps}
>
{@render children?.()}
</div>

View file

@ -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}
/>

View 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}
/>

View 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,
};

View 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>

View 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>

View 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>

View 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,
};

View file

@ -0,0 +1,5 @@
import { AspectRatio as AspectRatioPrimitive } from "bits-ui";
const Root = AspectRatioPrimitive.Root;
export { Root as AspectRatio, Root };

View 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}
/>

View 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}
/>

View 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}
/>

View 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,
};

View 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>

View file

@ -0,0 +1 @@
export { default as Badge, badgeVariants } from "./badge.svelte";

View 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>

View 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>

View 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}

View 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>

View 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>

View 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>

View 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>

View 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,
};

View 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}

View file

@ -0,0 +1,8 @@
import Root, { buttonVariants } from "./button.svelte";
export {
//
Root as Button,
buttonVariants,
Root,
};

View 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}
/>

View 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}
/>

View 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} />

View 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} />

View 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} />

View 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}
/>

View 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}
/>

View 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}
/>

View 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} />

View 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>

View 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}
/>

View 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}
/>

View 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>

View 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,
};

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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,
};

View 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>

View 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>

View 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>

View 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>

View 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>

View 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);
}

View 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,
};

View 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>

View file

@ -0,0 +1,6 @@
import Root from "./checkbox.svelte";
export {
//
Root as Checkbox,
Root,
};

View 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,
};

View 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>

View 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} />

View 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>

View 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>

View 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}
/>

View 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}
/>

View 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
/>

View 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} />

View 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>

View 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}
/>

View 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,
};

View file

@ -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>

View file

@ -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}
/>

View file

@ -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}
/>

View 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}
/>

View file

@ -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>

View file

@ -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}
/>

View 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>

View file

@ -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}
/>

View file

@ -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>

View 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