added svelte frontend

pull/3/head
TZGyn 2 years ago
commit 60696fec3a
Signed by: TZGyn
GPG Key ID: 122EAF77AE81FD4A

@ -0,0 +1,3 @@
PROTOCOL_HEADER=x-forwarded-proto
HOST_HEADER=x-forwarded-host
DATABASE_URL=postgres://postgres:password@127.0.0.1:5432/link-shortener

@ -0,0 +1,10 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
vite.config.js.timestamp-*
vite.config.ts.timestamp-*

@ -0,0 +1 @@
engine-strict=true

@ -0,0 +1,13 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

@ -0,0 +1,18 @@
{
"useTabs": true,
"singleQuote": true,
"semi": false,
"trailingComma": "all",
"printWidth": 70,
"bracketSameLine": true,
"plugins": [
"prettier-plugin-svelte",
"prettier-plugin-tailwindcss"
],
"overrides": [
{
"files": "*.svelte",
"options": { "parser": "svelte" }
}
]
}

@ -0,0 +1,17 @@
FROM node:18
WORKDIR /app
COPY ./package.json ./
COPY ./package-lock.json ./
RUN npm install
COPY . .
# COPY ./.env.example ./.env
RUN npm run build
EXPOSE 3000
ENTRYPOINT [ "node", "build"]

@ -0,0 +1,64 @@
<<<<<<< HEAD
# Shortener
### Production
```bash
docker-compose up -d --build
```
### Local Development
Backend
```bash
cd elysia
docker-compose up
```
Frontend
```bash
cd react-frontend
bun run dev --host
```
=======
# create-svelte
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte).
## 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
npm create svelte@latest
# create a new project in my-app
npm create svelte@latest 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://kit.svelte.dev/docs/adapters) for your target environment.
>>>>>>> frontend/main

Binary file not shown.

@ -0,0 +1,13 @@
{
"$schema": "https://shadcn-svelte.com/schema.json",
"style": "default",
"tailwind": {
"config": "tailwind.config.js",
"css": "src/app.postcss",
"baseColor": "zinc"
},
"aliases": {
"components": "$lib/components",
"utils": "$lib/utils"
}
}

@ -0,0 +1,15 @@
// drizzle.config.ts
import type { Config } from 'drizzle-kit'
export default {
schema: './src/lib/db/schema.ts',
out: './drizzle',
driver: 'pg',
dbCredentials: {
user: 'postgres',
password: 'password',
host: '0.0.0.0',
port: 5432,
database: 'link-shortener',
},
} satisfies Config

@ -0,0 +1,30 @@
CREATE TABLE IF NOT EXISTS "session" (
"token" varchar(255) NOT NULL,
"user_id" integer NOT NULL,
"expires" timestamp NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "shortener" (
"id" serial PRIMARY KEY NOT NULL,
"link" varchar(255) NOT NULL,
"code" varchar(255) NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "user" (
"id" serial PRIMARY KEY NOT NULL,
"uuid" uuid DEFAULT gen_random_uuid(),
"email" varchar(255) NOT NULL,
"username" varchar(255),
"password" varchar(255) NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL,
CONSTRAINT "user_email_unique" UNIQUE("email")
);
--> statement-breakpoint
CREATE TABLE IF NOT EXISTS "visitor" (
"id" serial PRIMARY KEY NOT NULL,
"shortener_id" integer NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL,
"country_code" varchar(255) NOT NULL,
"country" varchar(255) NOT NULL
);

@ -0,0 +1 @@
ALTER TABLE "shortener" ADD COLUMN "user_id" integer NOT NULL;

@ -0,0 +1,173 @@
{
"id": "9d44b46b-3e93-4782-8750-dbf39513bb0c",
"prevId": "00000000-0000-0000-0000-000000000000",
"version": "5",
"dialect": "pg",
"tables": {
"session": {
"name": "session",
"schema": "",
"columns": {
"token": {
"name": "token",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
},
"user_id": {
"name": "user_id",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"expires": {
"name": "expires",
"type": "timestamp",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"shortener": {
"name": "shortener",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"link": {
"name": "link",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
},
"code": {
"name": "code",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"user": {
"name": "user",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"uuid": {
"name": "uuid",
"type": "uuid",
"primaryKey": false,
"notNull": false,
"default": "gen_random_uuid()"
},
"email": {
"name": "email",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
},
"username": {
"name": "username",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"password": {
"name": "password",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"user_email_unique": {
"name": "user_email_unique",
"nullsNotDistinct": false,
"columns": ["email"]
}
}
},
"visitor": {
"name": "visitor",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"shortener_id": {
"name": "shortener_id",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"country_code": {
"name": "country_code",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
},
"country": {
"name": "country",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {},
"schemas": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
}
}

@ -0,0 +1,179 @@
{
"id": "548a0489-b083-464f-800c-4dee6f1ff161",
"prevId": "9d44b46b-3e93-4782-8750-dbf39513bb0c",
"version": "5",
"dialect": "pg",
"tables": {
"session": {
"name": "session",
"schema": "",
"columns": {
"token": {
"name": "token",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
},
"user_id": {
"name": "user_id",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"expires": {
"name": "expires",
"type": "timestamp",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"shortener": {
"name": "shortener",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"link": {
"name": "link",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
},
"code": {
"name": "code",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"user_id": {
"name": "user_id",
"type": "integer",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"user": {
"name": "user",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"uuid": {
"name": "uuid",
"type": "uuid",
"primaryKey": false,
"notNull": false,
"default": "gen_random_uuid()"
},
"email": {
"name": "email",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
},
"username": {
"name": "username",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"password": {
"name": "password",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"user_email_unique": {
"name": "user_email_unique",
"nullsNotDistinct": false,
"columns": ["email"]
}
}
},
"visitor": {
"name": "visitor",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"shortener_id": {
"name": "shortener_id",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"country_code": {
"name": "country_code",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
},
"country": {
"name": "country",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {},
"schemas": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
}
}

@ -0,0 +1,20 @@
{
"version": "5",
"dialect": "pg",
"entries": [
{
"idx": 0,
"version": "5",
"when": 1699851315914,
"tag": "0000_nebulous_energizer",
"breakpoints": true
},
{
"idx": 1,
"version": "5",
"when": 1700134783172,
"tag": "0001_regular_microchip",
"breakpoints": true
}
]
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,49 @@
{
"name": "link-shortener-svelte",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --plugin-search-dir . --check .",
"format": "prettier --write ."
},
"devDependencies": {
"@sveltejs/adapter-node": "^1.3.1",
"@sveltejs/kit": "^1.20.4",
"autoprefixer": "^10.4.14",
"bun-types": "^1.0.11",
"drizzle-kit": "^0.20.1",
"postcss": "^8.4.24",
"postcss-load-config": "^4.0.1",
"prettier": "^3.1.0",
"prettier-plugin-svelte": "^3.1.0",
"prettier-plugin-tailwindcss": "^0.5.7",
"svelte": "^4.0.5",
"svelte-adapter-bun": "^0.5.1",
"svelte-check": "^3.4.3",
"tailwindcss": "^3.3.2",
"tslib": "^2.4.1",
"typescript": "^5.0.0",
"vite": "^4.4.2"
},
"type": "module",
"dependencies": {
"argon2": "^0.31.2",
"bits-ui": "^0.9.8",
"clsx": "^2.0.0",
"drizzle-orm": "^0.29.0",
"formsnap": "^0.4.1",
"lucide-svelte": "^0.292.0",
"mode-watcher": "^0.0.7",
"nanoid": "^5.0.3",
"postgres": "^3.4.3",
"sveltekit-superforms": "^1.10.1",
"tailwind-merge": "^2.0.0",
"tailwind-variants": "^0.1.18",
"zod": "^3.22.4"
}
}

@ -0,0 +1,13 @@
const tailwindcss = require('tailwindcss')
const autoprefixer = require('autoprefixer')
const config = {
plugins: [
//Some plugins, like tailwindcss/nesting, need to run before Tailwind,
tailwindcss(),
//But others, like autoprefixer, need to run after,
autoprefixer,
],
}
module.exports = config

@ -0,0 +1,14 @@
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
interface Locals {
user: number | null
}
// interface PageData {}
// interface Platform {}
}
}
export {}

@ -0,0 +1,14 @@
<!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>

@ -0,0 +1,78 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 240 10% 3.9%;
--muted: 240 4.8% 95.9%;
--muted-foreground: 240 3.8% 46.1%;
--popover: 0 0% 100%;
--popover-foreground: 240 10% 3.9%;
--card: 0 0% 100%;
--card-foreground: 240 10% 3.9%;
--border: 240 5.9% 90%;
--input: 240 5.9% 90%;
--primary: 240 5.9% 10%;
--primary-foreground: 0 0% 98%;
--secondary: 240 4.8% 95.9%;
--secondary-foreground: 240 5.9% 10%;
--accent: 240 4.8% 95.9%;
--accent-foreground: 240 5.9% 10%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--ring: 240 10% 3.9%;
--radius: 0.5rem;
}
.dark {
--background: 240 10% 3.9%;
--foreground: 0 0% 98%;
--muted: 240 3.7% 15.9%;
--muted-foreground: 240 5% 64.9%;
--popover: 240 10% 3.9%;
--popover-foreground: 0 0% 98%;
--card: 240 10% 3.9%;
--card-foreground: 0 0% 98%;
--border: 240 3.7% 15.9%;
--input: 240 3.7% 15.9%;
--primary: 0 0% 98%;
--primary-foreground: 240 5.9% 10%;
--secondary: 240 3.7% 15.9%;
--secondary-foreground: 0 0% 98%;
--accent: 240 3.7% 15.9%;
--accent-foreground: 0 0% 98%;
--destructive: #c20e4d;
--destructive-foreground: 0 0% 98%;
--ring: 240 4.9% 83.9%;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}

@ -0,0 +1,31 @@
import { authenticateUser } from '$lib/server/auth'
import { redirect, type Handle } from '@sveltejs/kit'
export const handle: Handle = async ({ event, resolve }) => {
event.locals.user = await authenticateUser(event)
const pathname = event.url.pathname
const allowedPath = [
'/login',
'/signup',
'/api/login',
'/api/signup',
]
if (pathname === '/login' || pathname === 'signup') {
if (event.locals.user) {
throw redirect(303, '/')
}
}
if (!allowedPath.includes(pathname)) {
if (!event.locals.user) {
throw redirect(303, '/login')
}
}
const response = await resolve(event)
return response
}

@ -0,0 +1,75 @@
<script lang="ts">
import { Button } from '$lib/components/ui/button'
import * as Avatar from '$lib/components/ui/avatar'
import * as DropdownMenu from '$lib/components/ui/dropdown-menu'
import * as AlertDialog from '$lib/components/ui/alert-dialog'
import { Loader2, User } from 'lucide-svelte'
import { goto } from '$app/navigation'
export let email: string = ''
export let onClick: () => void = () => {}
let dialogOpen = false
let isLoading = false
const logout = async () => {
isLoading = true
await fetch('/api/logout', { method: 'post' })
isLoading = false
dialogOpen = false
goto('/login')
}
</script>
<DropdownMenu.Root>
<DropdownMenu.Trigger>
<Avatar.Root>
<Avatar.Image src="" alt="@shadcn" />
<Avatar.Fallback><User /></Avatar.Fallback>
</Avatar.Root>
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Group>
<DropdownMenu.Label>{email}</DropdownMenu.Label>
<DropdownMenu.Separator />
<DropdownMenu.Item
on:click={() => {
goto('/profile')
onClick()
}}>
Profile
</DropdownMenu.Item>
<DropdownMenu.Separator />
<DropdownMenu.Item
on:click={() => (dialogOpen = true)}
class="text-destructive data-[highlighted]:bg-destructive">
Log Out
</DropdownMenu.Item>
</DropdownMenu.Group>
</DropdownMenu.Content>
</DropdownMenu.Root>
<AlertDialog.Root bind:open={dialogOpen}>
<AlertDialog.Content>
<AlertDialog.Header>
<AlertDialog.Title>Are you absolutely sure?</AlertDialog.Title>
<AlertDialog.Description>
You are about to log out of this account.
</AlertDialog.Description>
</AlertDialog.Header>
<AlertDialog.Footer>
<AlertDialog.Cancel disabled={isLoading}
>Cancel</AlertDialog.Cancel>
<Button
on:click={logout}
class="flex gap-2"
disabled={isLoading}>
{#if isLoading}
<Loader2 class="animate-spin" />
{/if}
Log Out
</Button>
</AlertDialog.Footer>
</AlertDialog.Content>
</AlertDialog.Root>

@ -0,0 +1,45 @@
<script lang="ts">
import UserIcon from './UserIcon.svelte'
import { Separator } from '$lib/components/ui/separator'
import ThemeToggle from './theme-toggle.svelte'
import { Button } from '$lib/components/ui/button'
import { cn } from '$lib/utils'
export let email: string = ''
let className: string | undefined = undefined
export { className as class }
</script>
<div
class={cn(
'flex h-full min-w-[350px] flex-col justify-between border-r p-4',
className,
)}>
<div>
<div class="flex items-center justify-between pb-16">
<div class="text-xl font-bold">
<a href="/">Shortener</a>
</div>
<ThemeToggle />
</div>
<div class="flex flex-col gap-4">
<Button
variant="ghost"
href="/links"
class="justify-start text-base">Links</Button>
<Button
variant="ghost"
href="/projects"
class="justify-start text-base ">Projects</Button>
</div>
</div>
<div class="flex flex-col gap-4">
<Separator />
<div class="flex items-center justify-between">
<UserIcon {email} />
</div>
</div>
</div>

@ -0,0 +1,27 @@
<script lang="ts">
import { Button } from '$lib/components/ui/button'
import * as DropdownMenu from '$lib/components/ui/dropdown-menu'
import { Sun, Moon } from 'lucide-svelte'
import { setMode, resetMode } from 'mode-watcher'
</script>
<DropdownMenu.Root positioning={{ placement: 'bottom-end' }}>
<DropdownMenu.Trigger asChild let:builder>
<Button builders={[builder]} variant="outline" size="icon">
<Sun
class="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<Moon
class="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
<span class="sr-only">Toggle theme</span>
</Button>
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.Item on:click={() => setMode('light')}
>Light</DropdownMenu.Item>
<DropdownMenu.Item on:click={() => setMode('dark')}
>Dark</DropdownMenu.Item>
<DropdownMenu.Item on:click={() => resetMode()}
>System</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>

@ -0,0 +1,20 @@
<script lang="ts">
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui'
import { buttonVariants } from '$lib/components/ui/button'
import { cn } from '$lib/utils'
type $$Props = AlertDialogPrimitive.ActionProps
type $$Events = AlertDialogPrimitive.ActionEvents
let className: $$Props['class'] = undefined
export { className as class }
</script>
<AlertDialogPrimitive.Action
class={cn(buttonVariants(), className)}
{...$$restProps}
on:click
on:keydown
let:builder>
<slot {builder} />
</AlertDialogPrimitive.Action>

@ -0,0 +1,24 @@
<script lang="ts">
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui'
import { buttonVariants } from '$lib/components/ui/button'
import { cn } from '$lib/utils'
type $$Props = AlertDialogPrimitive.CancelProps
type $$Events = AlertDialogPrimitive.CancelEvents
let className: $$Props['class'] = undefined
export { className as class }
</script>
<AlertDialogPrimitive.Cancel
class={cn(
buttonVariants({ variant: 'outline' }),
'mt-2 sm:mt-0',
className,
)}
{...$$restProps}
on:click
on:keydown
let:builder>
<slot {builder} />
</AlertDialogPrimitive.Cancel>

@ -0,0 +1,27 @@
<script lang="ts">
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui'
import * as AlertDialog from '.'
import { cn, flyAndScale } from '$lib/utils'
type $$Props = AlertDialogPrimitive.ContentProps
export let transition: $$Props['transition'] = flyAndScale
export let transitionConfig: $$Props['transitionConfig'] = undefined
let className: $$Props['class'] = undefined
export { className as class }
</script>
<AlertDialog.Portal>
<AlertDialog.Overlay />
<AlertDialogPrimitive.Content
{transition}
{transitionConfig}
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 sm:rounded-lg md:w-full',
className,
)}
{...$$restProps}>
<slot />
</AlertDialogPrimitive.Content>
</AlertDialog.Portal>

@ -0,0 +1,15 @@
<script lang="ts">
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui'
import { cn } from '$lib/utils'
type $$Props = AlertDialogPrimitive.DescriptionProps
let className: $$Props['class'] = undefined
export { className as class }
</script>
<AlertDialogPrimitive.Description
class={cn('text-sm text-muted-foreground', className)}
{...$$restProps}>
<slot />
</AlertDialogPrimitive.Description>

@ -0,0 +1,18 @@
<script lang="ts">
import { cn } from '$lib/utils'
import type { HTMLAttributes } from 'svelte/elements'
type $$Props = HTMLAttributes<HTMLDivElement>
let className: $$Props['class'] = undefined
export { className as class }
</script>
<div
class={cn(
'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',
className,
)}
{...$$restProps}>
<slot />
</div>

@ -0,0 +1,18 @@
<script lang="ts">
import { cn } from '$lib/utils'
import type { HTMLAttributes } from 'svelte/elements'
type $$Props = HTMLAttributes<HTMLDivElement>
let className: $$Props['class'] = undefined
export { className as class }
</script>
<div
class={cn(
'flex flex-col space-y-2 text-center sm:text-left',
className,
)}
{...$$restProps}>
<slot />
</div>

@ -0,0 +1,23 @@
<script lang="ts">
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui'
import { cn } from '$lib/utils'
import { fade } from 'svelte/transition'
type $$Props = AlertDialogPrimitive.OverlayProps
let className: $$Props['class'] = undefined
export let transition: $$Props['transition'] = fade
export let transitionConfig: $$Props['transitionConfig'] = {
duration: 150,
}
export { className as class }
</script>
<AlertDialogPrimitive.Overlay
{transition}
{transitionConfig}
class={cn(
'fixed inset-0 z-50 bg-background/80 backdrop-blur-sm ',
className,
)}
{...$$restProps} />

@ -0,0 +1,9 @@
<script lang="ts">
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui'
type $$Props = AlertDialogPrimitive.PortalProps
</script>
<AlertDialogPrimitive.Portal {...$$restProps}>
<slot />
</AlertDialogPrimitive.Portal>

@ -0,0 +1,17 @@
<script lang="ts">
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui'
import { cn } from '$lib/utils'
type $$Props = AlertDialogPrimitive.TitleProps
let className: $$Props['class'] = undefined
export let level: $$Props['level'] = 'h3'
export { className as class }
</script>
<AlertDialogPrimitive.Title
class={cn('text-lg font-semibold', className)}
{level}
{...$$restProps}>
<slot />
</AlertDialogPrimitive.Title>

@ -0,0 +1,40 @@
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui'
const Root = AlertDialogPrimitive.Root
const Trigger = AlertDialogPrimitive.Trigger
import Title from './alert-dialog-title.svelte'
import Action from './alert-dialog-action.svelte'
import Cancel from './alert-dialog-cancel.svelte'
import Portal from './alert-dialog-portal.svelte'
import Footer from './alert-dialog-footer.svelte'
import Header from './alert-dialog-header.svelte'
import Overlay from './alert-dialog-overlay.svelte'
import Content from './alert-dialog-content.svelte'
import Description from './alert-dialog-description.svelte'
export {
Root,
Title,
Action,
Cancel,
Portal,
Footer,
Header,
Trigger,
Overlay,
Content,
Description,
//
Root as AlertDialog,
Title as AlertDialogTitle,
Action as AlertDialogAction,
Cancel as AlertDialogCancel,
Portal as AlertDialogPortal,
Footer as AlertDialogFooter,
Header as AlertDialogHeader,
Trigger as AlertDialogTrigger,
Overlay as AlertDialogOverlay,
Content as AlertDialogContent,
Description as AlertDialogDescription,
}

@ -0,0 +1,18 @@
<script lang="ts">
import { Avatar as AvatarPrimitive } from 'bits-ui'
import { cn } from '$lib/utils'
type $$Props = AvatarPrimitive.FallbackProps
let className: $$Props['class'] = undefined
export { className as class }
</script>
<AvatarPrimitive.Fallback
class={cn(
'flex h-full w-full items-center justify-center rounded-full bg-muted',
className,
)}
{...$$restProps}>
<slot />
</AvatarPrimitive.Fallback>

@ -0,0 +1,17 @@
<script lang="ts">
import { Avatar as AvatarPrimitive } from 'bits-ui'
import { cn } from '$lib/utils'
type $$Props = AvatarPrimitive.ImageProps
let className: $$Props['class'] = undefined
export let src: $$Props['src'] = undefined
export let alt: $$Props['alt'] = undefined
export { className as class }
</script>
<AvatarPrimitive.Image
{src}
{alt}
class={cn('aspect-square h-full w-full', className)}
{...$$restProps} />

@ -0,0 +1,20 @@
<script lang="ts">
import { Avatar as AvatarPrimitive } from 'bits-ui'
import { cn } from '$lib/utils'
type $$Props = AvatarPrimitive.Props
let className: $$Props['class'] = undefined
export let delayMs: $$Props['delayMs'] = undefined
export { className as class }
</script>
<AvatarPrimitive.Root
{delayMs}
class={cn(
'relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full',
className,
)}
{...$$restProps}>
<slot />
</AvatarPrimitive.Root>

@ -0,0 +1,13 @@
import Root from './avatar.svelte'
import Image from './avatar-image.svelte'
import Fallback from './avatar-fallback.svelte'
export {
Root,
Image,
Fallback,
//
Root as Avatar,
Image as AvatarImage,
Fallback as AvatarFallback,
}

@ -0,0 +1,24 @@
<script lang="ts">
import { Button as ButtonPrimitive } from 'bits-ui'
import { cn } from '$lib/utils'
import { buttonVariants, type Props, type Events } from '.'
type $$Props = Props
type $$Events = Events
let className: $$Props['class'] = undefined
export let variant: $$Props['variant'] = 'default'
export let size: $$Props['size'] = 'default'
export let builders: $$Props['builders'] = []
export { className as class }
</script>
<ButtonPrimitive.Root
{builders}
class={cn(buttonVariants({ variant, size, className }))}
type="button"
{...$$restProps}
on:click
on:keydown>
<slot />
</ButtonPrimitive.Root>

@ -0,0 +1,52 @@
import Root from './button.svelte'
import { tv, type VariantProps } from 'tailwind-variants'
import type { Button as ButtonPrimitive } from 'bits-ui'
const buttonVariants = tv({
base: 'inline-flex items-center justify-center rounded-md text-sm font-medium whitespace-nowrap ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
variants: {
variant: {
default:
'bg-primary-foreground text-primary hover:bg-primary-foreground/70',
destructive:
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
outline:
'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
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',
},
})
type Variant = VariantProps<typeof buttonVariants>['variant']
type Size = VariantProps<typeof buttonVariants>['size']
type Props = ButtonPrimitive.Props & {
variant?: Variant
size?: Size
}
type Events = ButtonPrimitive.Events
export {
Root,
type Props,
type Events,
//
Root as Button,
type Props as ButtonProps,
type Events as ButtonEvents,
buttonVariants,
}

@ -0,0 +1,13 @@
<script lang="ts">
import { cn } from '$lib/utils'
import type { HTMLAttributes } from 'svelte/elements'
type $$Props = HTMLAttributes<HTMLDivElement>
let className: $$Props['class'] = undefined
export { className as class }
</script>
<div class={cn('p-6 pt-0', className)} {...$$restProps}>
<slot />
</div>

@ -0,0 +1,15 @@
<script lang="ts">
import type { HTMLAttributes } from 'svelte/elements'
import { cn } from '$lib/utils'
type $$Props = HTMLAttributes<HTMLParagraphElement>
let className: $$Props['class'] = undefined
export { className as class }
</script>
<p
class={cn('text-sm text-muted-foreground', className)}
{...$$restProps}>
<slot />
</p>

@ -0,0 +1,15 @@
<script lang="ts">
import type { HTMLAttributes } from 'svelte/elements'
import { cn } from '$lib/utils'
type $$Props = HTMLAttributes<HTMLDivElement>
let className: $$Props['class'] = undefined
export { className as class }
</script>
<div
class={cn('flex items-center p-6 pt-0', className)}
{...$$restProps}>
<slot />
</div>

@ -0,0 +1,15 @@
<script lang="ts">
import type { HTMLAttributes } from 'svelte/elements'
import { cn } from '$lib/utils'
type $$Props = HTMLAttributes<HTMLDivElement>
let className: $$Props['class'] = undefined
export { className as class }
</script>
<div
class={cn('flex flex-col space-y-1.5 p-6', className)}
{...$$restProps}>
<slot />
</div>

@ -0,0 +1,23 @@
<script lang="ts">
import type { HTMLAttributes } from 'svelte/elements'
import { cn } from '$lib/utils'
import type { HeadingLevel } from '.'
type $$Props = HTMLAttributes<HTMLHeadingElement> & {
tag?: HeadingLevel
}
let className: $$Props['class'] = undefined
export let tag: $$Props['tag'] = 'h3'
export { className as class }
</script>
<svelte:element
this={tag}
class={cn(
'text-lg font-semibold leading-none tracking-tight',
className,
)}
{...$$restProps}>
<slot />
</svelte:element>

@ -0,0 +1,18 @@
<script lang="ts">
import type { HTMLAttributes } from 'svelte/elements'
import { cn } from '$lib/utils'
type $$Props = HTMLAttributes<HTMLDivElement>
let className: $$Props['class'] = undefined
export { className as class }
</script>
<div
class={cn(
'rounded-lg border bg-card text-card-foreground shadow-sm',
className,
)}
{...$$restProps}>
<slot />
</div>

@ -0,0 +1,24 @@
import Root from './card.svelte'
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'
export {
Root,
Content,
Description,
Footer,
Header,
Title,
//
Root as Card,
Content as CardContent,
Description as CardDescription,
Footer as CardFooter,
Header as CardHeader,
Title as CardTitle,
}
export type HeadingLevel = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'

@ -0,0 +1,34 @@
<script lang="ts">
import { Checkbox as CheckboxPrimitive } from 'bits-ui'
import { Check, Minus } from 'lucide-svelte'
import { cn } from '$lib/utils'
type $$Props = CheckboxPrimitive.Props
type $$Events = CheckboxPrimitive.Events
let className: $$Props['class'] = undefined
export let checked: $$Props['checked'] = false
export { className as class }
</script>
<CheckboxPrimitive.Root
class={cn(
'peer box-content h-4 w-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}
on:click>
<CheckboxPrimitive.Indicator
class={cn(
'flex h-4 w-4 items-center justify-center text-current',
)}
let:isChecked
let:isIndeterminate>
{#if isChecked}
<Check class="h-3.5 w-3.5" />
{:else if isIndeterminate}
<Minus class="h-3.5 w-3.5" />
{/if}
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>

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

@ -0,0 +1,34 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from 'bits-ui'
import * as Dialog from '.'
import { cn, flyAndScale } from '$lib/utils'
import { X } from 'lucide-svelte'
type $$Props = DialogPrimitive.ContentProps
let className: $$Props['class'] = undefined
export let transition: $$Props['transition'] = flyAndScale
export let transitionConfig: $$Props['transitionConfig'] = {
duration: 200,
}
export { className as class }
</script>
<Dialog.Portal>
<Dialog.Overlay />
<DialogPrimitive.Content
{transition}
{transitionConfig}
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 sm:rounded-lg md:w-full',
className,
)}
{...$$restProps}>
<slot />
<DialogPrimitive.Close
class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
<X class="h-4 w-4" />
<span class="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</Dialog.Portal>

@ -0,0 +1,15 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from 'bits-ui'
import { cn } from '$lib/utils'
type $$Props = DialogPrimitive.DescriptionProps
let className: $$Props['class'] = undefined
export { className as class }
</script>
<DialogPrimitive.Description
class={cn('text-sm text-muted-foreground', className)}
{...$$restProps}>
<slot />
</DialogPrimitive.Description>

@ -0,0 +1,18 @@
<script lang="ts">
import { cn } from '$lib/utils'
import type { HTMLAttributes } from 'svelte/elements'
type $$Props = HTMLAttributes<HTMLDivElement>
let className: $$Props['class'] = undefined
export { className as class }
</script>
<div
class={cn(
'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',
className,
)}
{...$$restProps}>
<slot />
</div>

@ -0,0 +1,18 @@
<script lang="ts">
import { cn } from '$lib/utils'
import type { HTMLAttributes } from 'svelte/elements'
type $$Props = HTMLAttributes<HTMLDivElement>
let className: $$Props['class'] = undefined
export { className as class }
</script>
<div
class={cn(
'flex flex-col space-y-1.5 text-center sm:text-left',
className,
)}
{...$$restProps}>
<slot />
</div>

@ -0,0 +1,23 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from 'bits-ui'
import { cn } from '$lib/utils'
import { fade } from 'svelte/transition'
type $$Props = DialogPrimitive.OverlayProps
let className: $$Props['class'] = undefined
export let transition: $$Props['transition'] = fade
export let transitionConfig: $$Props['transitionConfig'] = {
duration: 150,
}
export { className as class }
</script>
<DialogPrimitive.Overlay
{transition}
{transitionConfig}
class={cn(
'fixed inset-0 z-50 bg-background/80 backdrop-blur-sm',
className,
)}
{...$$restProps} />

@ -0,0 +1,8 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from 'bits-ui'
type $$Props = DialogPrimitive.PortalProps
</script>
<DialogPrimitive.Portal {...$$restProps}>
<slot />
</DialogPrimitive.Portal>

@ -0,0 +1,18 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from 'bits-ui'
import { cn } from '$lib/utils'
type $$Props = DialogPrimitive.TitleProps
let className: $$Props['class'] = undefined
export { className as class }
</script>
<DialogPrimitive.Title
class={cn(
'text-lg font-semibold leading-none tracking-tight',
className,
)}
{...$$restProps}>
<slot />
</DialogPrimitive.Title>

@ -0,0 +1,34 @@
import { Dialog as DialogPrimitive } from 'bits-ui'
const Root = DialogPrimitive.Root
const Trigger = DialogPrimitive.Trigger
import Title from './dialog-title.svelte'
import Portal from './dialog-portal.svelte'
import Footer from './dialog-footer.svelte'
import Header from './dialog-header.svelte'
import Overlay from './dialog-overlay.svelte'
import Content from './dialog-content.svelte'
import Description from './dialog-description.svelte'
export {
Root,
Title,
Portal,
Footer,
Header,
Trigger,
Overlay,
Content,
Description,
//
Root as Dialog,
Title as DialogTitle,
Portal as DialogPortal,
Footer as DialogFooter,
Header as DialogHeader,
Trigger as DialogTrigger,
Overlay as DialogOverlay,
Content as DialogContent,
Description as DialogDescription,
}

@ -0,0 +1,35 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'
import { cn } from '$lib/utils'
import { Check } from 'lucide-svelte'
type $$Props = DropdownMenuPrimitive.CheckboxItemProps
type $$Events = DropdownMenuPrimitive.CheckboxItemEvents
let className: $$Props['class'] = undefined
export let checked: $$Props['checked'] = undefined
export { className as class }
</script>
<DropdownMenuPrimitive.CheckboxItem
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}
on:click
on:keydown
on:focusin
on:focusout
on:pointerdown
on:pointerleave
on:pointermove>
<span
class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.CheckboxIndicator>
<Check class="h-4 w-4" />
</DropdownMenuPrimitive.CheckboxIndicator>
</span>
<slot />
</DropdownMenuPrimitive.CheckboxItem>

@ -0,0 +1,24 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'
import { cn, flyAndScale } from '$lib/utils'
type $$Props = DropdownMenuPrimitive.ContentProps
type $$Events = DropdownMenuPrimitive.ContentEvents
let className: $$Props['class'] = undefined
export let transition: $$Props['transition'] = flyAndScale
export let transitionConfig: $$Props['transitionConfig'] = undefined
export { className as class }
</script>
<DropdownMenuPrimitive.Content
{transition}
{transitionConfig}
class={cn(
'z-50 min-w-[8rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-md focus:outline-none',
className,
)}
{...$$restProps}
on:keydown>
<slot />
</DropdownMenuPrimitive.Content>

@ -0,0 +1,30 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'
import { cn } from '$lib/utils'
type $$Props = DropdownMenuPrimitive.ItemProps & {
inset?: boolean
}
type $$Events = DropdownMenuPrimitive.ItemEvents
let className: $$Props['class'] = undefined
export let inset: $$Props['inset'] = undefined
export { className as class }
</script>
<DropdownMenuPrimitive.Item
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}
on:click
on:keydown
on:focusin
on:focusout
on:pointerdown
on:pointerleave
on:pointermove>
<slot />
</DropdownMenuPrimitive.Item>

@ -0,0 +1,22 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'
import { cn } from '$lib/utils'
type $$Props = DropdownMenuPrimitive.LabelProps & {
inset?: boolean
}
let className: $$Props['class'] = undefined
export let inset: $$Props['inset'] = undefined
export { className as class }
</script>
<DropdownMenuPrimitive.Label
class={cn(
'px-2 py-1.5 text-sm font-semibold',
inset && 'pl-8',
className,
)}
{...$$restProps}>
<slot />
</DropdownMenuPrimitive.Label>

@ -0,0 +1,11 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'
type $$Props = DropdownMenuPrimitive.RadioGroupProps
export let value: $$Props['value'] = undefined
</script>
<DropdownMenuPrimitive.RadioGroup {...$$restProps} bind:value>
<slot />
</DropdownMenuPrimitive.RadioGroup>

@ -0,0 +1,35 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'
import { cn } from '$lib/utils'
import { Circle } from 'lucide-svelte'
type $$Props = DropdownMenuPrimitive.RadioItemProps
type $$Events = DropdownMenuPrimitive.RadioItemEvents
let className: $$Props['class'] = undefined
export let value: $$Props['value']
export { className as class }
</script>
<DropdownMenuPrimitive.RadioItem
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,
)}
{value}
{...$$restProps}
on:click
on:keydown
on:focusin
on:focusout
on:pointerdown
on:pointerleave
on:pointermove>
<span
class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<DropdownMenuPrimitive.RadioIndicator>
<Circle class="h-2 w-2 fill-current" />
</DropdownMenuPrimitive.RadioIndicator>
</span>
<slot />
</DropdownMenuPrimitive.RadioItem>

@ -0,0 +1,13 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'
import { cn } from '$lib/utils'
type $$Props = DropdownMenuPrimitive.SeparatorProps
let className: $$Props['class'] = undefined
export { className as class }
</script>
<DropdownMenuPrimitive.Separator
class={cn('-mx-1 my-1 h-px bg-muted', className)}
{...$$restProps} />

@ -0,0 +1,15 @@
<script lang="ts">
import { cn } from '$lib/utils'
import type { HTMLAttributes } from 'svelte/elements'
type $$Props = HTMLAttributes<HTMLSpanElement>
let className: $$Props['class'] = undefined
export { className as class }
</script>
<span
class={cn('ml-auto text-xs tracking-widest opacity-60', className)}
{...$$restProps}>
<slot />
</span>

@ -0,0 +1,29 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'
import { cn, flyAndScale } from '$lib/utils'
type $$Props = DropdownMenuPrimitive.SubContentProps
type $$Events = DropdownMenuPrimitive.SubContentEvents
let className: $$Props['class'] = undefined
export let transition: $$Props['transition'] = flyAndScale
export let transitionConfig: $$Props['transitionConfig'] = {
x: -10,
y: 0,
}
export { className as class }
</script>
<DropdownMenuPrimitive.SubContent
{transition}
{transitionConfig}
class={cn(
'z-50 min-w-[8rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-lg focus:outline-none',
className,
)}
{...$$restProps}
on:keydown
on:focusout
on:pointermove>
<slot />
</DropdownMenuPrimitive.SubContent>

@ -0,0 +1,31 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'
import { cn } from '$lib/utils'
import { ChevronRight } from 'lucide-svelte'
type $$Props = DropdownMenuPrimitive.SubTriggerProps & {
inset?: boolean
}
type $$Events = DropdownMenuPrimitive.SubTriggerEvents
let className: $$Props['class'] = undefined
export let inset: $$Props['inset'] = undefined
export { className as class }
</script>
<DropdownMenuPrimitive.SubTrigger
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',
inset && 'pl-8',
className,
)}
{...$$restProps}
on:click
on:keydown
on:focusin
on:focusout
on:pointerleave
on:pointermove>
<slot />
<ChevronRight class="ml-auto h-4 w-4" />
</DropdownMenuPrimitive.SubTrigger>

@ -0,0 +1,48 @@
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'
import Item from './dropdown-menu-item.svelte'
import Label from './dropdown-menu-label.svelte'
import Content from './dropdown-menu-content.svelte'
import Shortcut from './dropdown-menu-shortcut.svelte'
import RadioItem from './dropdown-menu-radio-item.svelte'
import Separator from './dropdown-menu-separator.svelte'
import RadioGroup from './dropdown-menu-radio-group.svelte'
import SubContent from './dropdown-menu-sub-content.svelte'
import SubTrigger from './dropdown-menu-sub-trigger.svelte'
import CheckboxItem from './dropdown-menu-checkbox-item.svelte'
const Sub = DropdownMenuPrimitive.Sub
const Root = DropdownMenuPrimitive.Root
const Trigger = DropdownMenuPrimitive.Trigger
const Group = DropdownMenuPrimitive.Group
export {
Sub,
Root,
Item,
Label,
Group,
Trigger,
Content,
Shortcut,
Separator,
RadioItem,
SubContent,
SubTrigger,
RadioGroup,
CheckboxItem,
//
Root as DropdownMenu,
Sub as DropdownMenuSub,
Item as DropdownMenuItem,
Label as DropdownMenuLabel,
Group as DropdownMenuGroup,
Content as DropdownMenuContent,
Trigger as DropdownMenuTrigger,
Shortcut as DropdownMenuShortcut,
RadioItem as DropdownMenuRadioItem,
Separator as DropdownMenuSeparator,
RadioGroup as DropdownMenuRadioGroup,
SubContent as DropdownMenuSubContent,
SubTrigger as DropdownMenuSubTrigger,
CheckboxItem as DropdownMenuCheckboxItem,
}

@ -0,0 +1,9 @@
<script lang="ts">
import * as Button from '$lib/components/ui/button'
type $$Props = Button.Props
type $$Events = Button.Events
</script>
<Button.Root type="submit" {...$$restProps} on:click on:keydown>
<slot />
</Button.Root>

@ -0,0 +1,25 @@
<script lang="ts">
import { getFormField } from 'formsnap'
import type { Checkbox as CheckboxPrimitive } from 'bits-ui'
import { Checkbox } from '$lib/components/ui/checkbox'
type $$Props = CheckboxPrimitive.Props
type $$Events = CheckboxPrimitive.Events
export let onCheckedChange: $$Props['onCheckedChange'] = undefined
const { name, setValue, attrStore, value } = getFormField()
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { name: nameAttr, value: valueAttr, ...rest } = $attrStore
</script>
<Checkbox
{...rest}
checked={typeof $value === 'boolean' ? $value : false}
onCheckedChange={(v) => {
onCheckedChange?.(v)
setValue(v)
}}
{...$$restProps}
on:click
on:keydown />
<input hidden {name} value={$value} />

@ -0,0 +1,15 @@
<script lang="ts">
import { Form as FormPrimitive } from 'formsnap'
import { cn } from '$lib/utils'
import type { HTMLAttributes } from 'svelte/elements'
type $$Props = HTMLAttributes<HTMLSpanElement>
let className: string | undefined | null = undefined
export { className as class }
</script>
<FormPrimitive.Description
class={cn('text-sm text-muted-foreground', className)}
{...$$restProps}>
<slot />
</FormPrimitive.Description>

@ -0,0 +1,27 @@
<script lang="ts">
import { getFormField } from 'formsnap'
import type { HTMLInputAttributes } from 'svelte/elements'
import { Input, type InputEvents } from '$lib/components/ui/input'
type $$Props = HTMLInputAttributes
type $$Events = InputEvents
const { attrStore, value } = getFormField()
</script>
<Input
{...$attrStore}
bind:value={$value}
{...$$restProps}
on:blur
on:change
on:click
on:focus
on:keydown
on:keypress
on:keyup
on:mouseover
on:mouseenter
on:mouseleave
on:paste
on:input />

@ -0,0 +1,12 @@
<script lang="ts">
import { cn } from '$lib/utils'
import type { HTMLAttributes } from 'svelte/elements'
type $$Props = HTMLAttributes<HTMLDivElement>
let className: string | undefined | null = undefined
export { className as class }
</script>
<div class={cn('space-y-2', className)} {...$$restProps}>
<slot />
</div>

@ -0,0 +1,20 @@
<script lang="ts">
import type { Label as LabelPrimitive } from 'bits-ui'
import { getFormField } from 'formsnap'
import { cn } from '$lib/utils'
import { Label } from '$lib/components/ui/label'
type $$Props = LabelPrimitive.Props
let className: $$Props['class'] = undefined
export { className as class }
const { errors, ids } = getFormField()
</script>
<Label
for={$ids.input}
class={cn($errors && 'text-destructive', className)}
{...$$restProps}>
<slot />
</Label>

@ -0,0 +1,23 @@
<script lang="ts">
import { Form as FormPrimitive } from 'formsnap'
import { buttonVariants } from '$lib/components/ui/button'
import { cn } from '$lib/utils'
import { ChevronDown } from 'lucide-svelte'
import type { HTMLSelectAttributes } from 'svelte/elements'
type $$Props = HTMLSelectAttributes
let className: string | undefined | null = undefined
export { className as class }
</script>
<FormPrimitive.Select
class={cn(
buttonVariants({ variant: 'outline' }),
'appearance-none bg-transparent font-normal',
className,
)}
{...$$restProps}>
<slot />
</FormPrimitive.Select>
<ChevronDown class="absolute right-3 top-2.5 h-4 w-4 opacity-50" />

@ -0,0 +1,21 @@
<script lang="ts">
import { getFormField } from 'formsnap'
import type { RadioGroup as RadioGroupPrimitive } from 'bits-ui'
import * as RadioGroup from '$lib/components/ui/radio-group'
type $$Props = RadioGroupPrimitive.Props
const { attrStore, setValue, name, value } = getFormField()
export let onValueChange: $$Props['onValueChange'] = undefined
</script>
<RadioGroup.Root
{...$attrStore}
onValueChange={(v) => {
onValueChange?.(v)
setValue(v)
}}
{...$$restProps}>
<slot />
<input hidden {name} value={$value} />
</RadioGroup.Root>

@ -0,0 +1,17 @@
<script lang="ts">
import * as Select from '$lib/components/ui/select'
import type { Select as SelectPrimitive } from 'bits-ui'
import { getFormField } from 'formsnap'
type $$Props = SelectPrimitive.TriggerProps & {
placeholder?: string
}
type $$Events = SelectPrimitive.TriggerEvents
const { attrStore } = getFormField()
export let placeholder = ''
</script>
<Select.Trigger {...$$restProps} {...$attrStore} on:click on:keydown>
<Select.Value {placeholder} />
<slot />
</Select.Trigger>

@ -0,0 +1,19 @@
<script lang="ts">
import * as Select from '$lib/components/ui/select'
import { getFormField } from 'formsnap'
import type { Select as SelectPrimitive } from 'bits-ui'
type $$Props = SelectPrimitive.Props
const { setValue, name, value } = getFormField()
export let onSelectedChange: $$Props['onSelectedChange'] = undefined
</script>
<Select.Root
onSelectedChange={(v) => {
onSelectedChange?.(v)
setValue(v ? v.value : undefined)
}}
{...$$restProps}>
<slot />
<input hidden {name} value={$value} />
</Select.Root>

@ -0,0 +1,23 @@
<script lang="ts">
import { getFormField } from 'formsnap'
import type { Switch as SwitchPrimitive } from 'bits-ui'
import { Switch } from '$lib/components/ui/switch'
type $$Props = SwitchPrimitive.Props
type $$Events = SwitchPrimitive.Events
export let onCheckedChange: $$Props['onCheckedChange'] = undefined
const { name, setValue, attrStore, value } = getFormField()
</script>
<Switch
{...$attrStore}
checked={typeof $value === 'boolean' ? $value : false}
onCheckedChange={(v) => {
onCheckedChange?.(v)
setValue(v)
}}
{...$$restProps}
on:click
on:keydown />
<input hidden {name} value={$value} />

@ -0,0 +1,31 @@
<script lang="ts">
import { getFormField } from 'formsnap'
import type { HTMLTextareaAttributes } from 'svelte/elements'
import type { TextareaGetFormField } from '.'
import {
Textarea,
type TextareaEvents,
} from '$lib/components/ui/textarea'
type $$Props = HTMLTextareaAttributes
type $$Events = TextareaEvents
const { attrStore, value } = getFormField() as TextareaGetFormField
</script>
<Textarea
{...$attrStore}
bind:value={$value}
{...$$restProps}
on:blur
on:change
on:click
on:focus
on:keydown
on:keypress
on:keyup
on:mouseover
on:mouseenter
on:mouseleave
on:paste
on:input />

@ -0,0 +1,13 @@
<script lang="ts">
import { Form as FormPrimitive } from 'formsnap'
import { cn } from '$lib/utils'
import type { HTMLAttributes } from 'svelte/elements'
type $$Props = HTMLAttributes<HTMLParagraphElement>
let className: string | undefined | null = undefined
export { className as class }
</script>
<FormPrimitive.Validation
class={cn('text-sm font-medium text-destructive', className)}
{...$$restProps} />

@ -0,0 +1,85 @@
import { Form as FormPrimitive, getFormField } from 'formsnap'
import * as RadioGroupComp from '$lib/components/ui/radio-group'
import * as SelectComp from '$lib/components/ui/select'
import type { Writable } from 'svelte/store'
import Item from './form-item.svelte'
import Input from './form-input.svelte'
import Textarea from './form-textarea.svelte'
import Description from './form-description.svelte'
import Label from './form-label.svelte'
import Validation from './form-validation.svelte'
import Checkbox from './form-checkbox.svelte'
import Switch from './form-switch.svelte'
import NativeSelect from './form-native-select.svelte'
import RadioGroup from './form-radio-group.svelte'
import Select from './form-select.svelte'
import SelectTrigger from './form-select-trigger.svelte'
import Button from './form-button.svelte'
const Root = FormPrimitive.Root
const Field = FormPrimitive.Field
const Control = FormPrimitive.Control
const RadioItem = RadioGroupComp.Item
const NativeRadio = FormPrimitive.Radio
const SelectContent = SelectComp.Content
const SelectLabel = SelectComp.Label
const SelectGroup = SelectComp.Group
const SelectItem = SelectComp.Item
const SelectSeparator = SelectComp.Separator
export type TextareaGetFormField = Omit<
ReturnType<typeof getFormField>,
'value'
> & {
value: Writable<string>
}
export {
Root,
Field,
Control,
Item,
Input,
Label,
Button,
Switch,
Select,
Checkbox,
Textarea,
Validation,
RadioGroup,
RadioItem,
Description,
SelectContent,
SelectLabel,
SelectGroup,
SelectItem,
SelectSeparator,
SelectTrigger,
NativeSelect,
NativeRadio,
//
Root as Form,
Field as FormField,
Control as FormControl,
Item as FormItem,
Input as FormInput,
Textarea as FormTextarea,
Description as FormDescription,
Label as FormLabel,
Validation as FormValidation,
NativeSelect as FormNativeSelect,
NativeRadio as FormNativeRadio,
Checkbox as FormCheckbox,
Switch as FormSwitch,
RadioGroup as FormRadioGroup,
RadioItem as FormRadioItem,
Select as FormSelect,
SelectContent as FormSelectContent,
SelectLabel as FormSelectLabel,
SelectGroup as FormSelectGroup,
SelectItem as FormSelectItem,
SelectSeparator as FormSelectSeparator,
SelectTrigger as FormSelectTrigger,
Button as FormButton,
}

@ -0,0 +1,25 @@
import Root from './input.svelte'
type FormInputEvent<T extends Event = Event> = T & {
currentTarget: EventTarget & HTMLInputElement
}
export type InputEvents = {
blur: FormInputEvent<FocusEvent>
change: FormInputEvent<Event>
click: FormInputEvent<MouseEvent>
focus: FormInputEvent<FocusEvent>
keydown: FormInputEvent<KeyboardEvent>
keypress: FormInputEvent<KeyboardEvent>
keyup: FormInputEvent<KeyboardEvent>
mouseover: FormInputEvent<MouseEvent>
mouseenter: FormInputEvent<MouseEvent>
mouseleave: FormInputEvent<MouseEvent>
paste: FormInputEvent<ClipboardEvent>
input: FormInputEvent<InputEvent>
}
export {
Root,
//
Root as Input,
}

@ -0,0 +1,32 @@
<script lang="ts">
import type { HTMLInputAttributes } from 'svelte/elements'
import { cn } from '$lib/utils'
import type { InputEvents } from '.'
type $$Props = HTMLInputAttributes
type $$Events = InputEvents
let className: $$Props['class'] = undefined
export let value: $$Props['value'] = undefined
export { className as class }
</script>
<input
class={cn(
'flex h-10 w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
className,
)}
bind:value
on:blur
on:change
on:click
on:focus
on:keydown
on:keypress
on:keyup
on:mouseover
on:mouseenter
on:mouseleave
on:paste
on:input
{...$$restProps} />

@ -0,0 +1,7 @@
import Root from './label.svelte'
export {
Root,
//
Root as Label,
}

@ -0,0 +1,20 @@
<script lang="ts">
import { Label as LabelPrimitive } from 'bits-ui'
import { cn } from '$lib/utils'
type $$Props = LabelPrimitive.Props
type $$Events = LabelPrimitive.Events
let className: $$Props['class'] = undefined
export { className as class }
</script>
<LabelPrimitive.Root
class={cn(
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
className,
)}
{...$$restProps}
on:mousedown>
<slot />
</LabelPrimitive.Root>

@ -0,0 +1,15 @@
import { RadioGroup as RadioGroupPrimitive } from 'bits-ui'
import Root from './radio-group.svelte'
import Item from './radio-group-item.svelte'
const Input = RadioGroupPrimitive.Input
export {
Root,
Input,
Item,
//
Root as RadioGroup,
Input as RadioGroupInput,
Item as RadioGroupItem,
}

@ -0,0 +1,27 @@
<script lang="ts">
import { RadioGroup as RadioGroupPrimitive } from 'bits-ui'
import { Circle } from 'lucide-svelte'
import { cn } from '$lib/utils'
type $$Props = RadioGroupPrimitive.ItemProps
type $$Events = RadioGroupPrimitive.ItemEvents
let className: $$Props['class'] = undefined
export let value: $$Props['value']
export { className as class }
</script>
<RadioGroupPrimitive.Item
{value}
class={cn(
'aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
className,
)}
{...$$restProps}
on:click>
<div class="flex items-center justify-center">
<RadioGroupPrimitive.ItemIndicator>
<Circle class="h-2.5 w-2.5 fill-current text-current" />
</RadioGroupPrimitive.ItemIndicator>
</div>
</RadioGroupPrimitive.Item>

@ -0,0 +1,17 @@
<script lang="ts">
import { RadioGroup as RadioGroupPrimitive } from 'bits-ui'
import { cn } from '$lib/utils'
type $$Props = RadioGroupPrimitive.Props
let className: $$Props['class'] = undefined
export let value: $$Props['value'] = undefined
export { className as class }
</script>
<RadioGroupPrimitive.Root
bind:value
class={cn('grid gap-2', className)}
{...$$restProps}>
<slot />
</RadioGroupPrimitive.Root>

@ -0,0 +1,33 @@
import { Select as SelectPrimitive } from 'bits-ui'
import Root from './select.svelte'
import Label from './select-label.svelte'
import Item from './select-item.svelte'
import Content from './select-content.svelte'
import Trigger from './select-trigger.svelte'
import Separator from './select-separator.svelte'
const Group = SelectPrimitive.Group
const Input = SelectPrimitive.Input
const Value = SelectPrimitive.Value
export {
Root,
Group,
Input,
Label,
Item,
Value,
Content,
Trigger,
Separator,
//
Root as Select,
Group as SelectGroup,
Input as SelectInput,
Label as SelectLabel,
Item as SelectItem,
Value as SelectValue,
Content as SelectContent,
Trigger as SelectTrigger,
Separator as SelectSeparator,
}

@ -0,0 +1,36 @@
<script lang="ts">
import { Select as SelectPrimitive } from 'bits-ui'
import { cn, flyAndScale } from '$lib/utils'
import { scale } from 'svelte/transition'
type $$Props = SelectPrimitive.ContentProps
type $$Events = SelectPrimitive.ContentEvents
export let inTransition: $$Props['inTransition'] = flyAndScale
export let inTransitionConfig: $$Props['inTransitionConfig'] =
undefined
export let outTransition: $$Props['outTransition'] = scale
export let outTransitionConfig: $$Props['outTransitionConfig'] = {
start: 0.95,
opacity: 0,
duration: 50,
}
let className: $$Props['class'] = undefined
export { className as class }
</script>
<SelectPrimitive.Content
{inTransition}
{inTransitionConfig}
{outTransition}
{outTransitionConfig}
class={cn(
'relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md outline-none',
className,
)}
{...$$restProps}
on:keydown>
<div class="w-full p-1">
<slot />
</div>
</SelectPrimitive.Content>

@ -0,0 +1,38 @@
<script lang="ts">
import { cn } from '$lib/utils'
import { Select as SelectPrimitive } from 'bits-ui'
import { Check } from 'lucide-svelte'
type $$Props = SelectPrimitive.ItemProps
type $$Events = SelectPrimitive.ItemEvents
let className: $$Props['class'] = undefined
export let value: $$Props['value']
export let label: $$Props['label'] = undefined
export let disabled: $$Props['disabled'] = undefined
export { className as class }
</script>
<SelectPrimitive.Item
{value}
{disabled}
{label}
class={cn(
'relative flex w-full 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}
on:click
on:keydown
on:focusin
on:focusout
on:pointerleave
on:pointermove>
<span
class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<Check class="h-4 w-4" />
</SelectPrimitive.ItemIndicator>
</span>
<slot />
</SelectPrimitive.Item>

@ -0,0 +1,15 @@
<script lang="ts">
import { Select as SelectPrimitive } from 'bits-ui'
import { cn } from '$lib/utils'
type $$Props = SelectPrimitive.LabelProps
let className: $$Props['class'] = undefined
export { className as class }
</script>
<SelectPrimitive.Label
class={cn('py-1.5 pl-8 pr-2 text-sm font-semibold', className)}
{...$$restProps}>
<slot />
</SelectPrimitive.Label>

@ -0,0 +1,13 @@
<script lang="ts">
import { Select as SelectPrimitive } from 'bits-ui'
import { cn } from '$lib/utils'
type $$Props = SelectPrimitive.SeparatorProps
let className: $$Props['class'] = undefined
export { className as class }
</script>
<SelectPrimitive.Separator
class={cn('-mx-1 my-1 h-px bg-muted', className)}
{...$$restProps} />

@ -0,0 +1,26 @@
<script lang="ts">
import { Select as SelectPrimitive } from 'bits-ui'
import { ChevronDown } from 'lucide-svelte'
import { cn } from '$lib/utils'
type $$Props = SelectPrimitive.TriggerProps
type $$Events = SelectPrimitive.TriggerEvents
let className: $$Props['class'] = undefined
export { className as class }
</script>
<SelectPrimitive.Trigger
class={cn(
'flex h-10 w-full items-center justify-between rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
className,
)}
{...$$restProps}
let:builder
on:click
on:keydown>
<slot {builder} />
<div>
<ChevronDown class="h-4 w-4 opacity-50" />
</div>
</SelectPrimitive.Trigger>

@ -0,0 +1,12 @@
<script lang="ts">
import { Select as SelectPrimitive } from 'bits-ui'
type $$Props = SelectPrimitive.Props
export let selected: $$Props['selected'] = undefined
export let open: $$Props['open'] = undefined
</script>
<SelectPrimitive.Root bind:selected bind:open {...$$restProps}>
<slot />
</SelectPrimitive.Root>

@ -0,0 +1,7 @@
import Root from './separator.svelte'
export {
Root,
//
Root as Separator,
}

@ -0,0 +1,23 @@
<script lang="ts">
import { Separator as SeparatorPrimitive } from 'bits-ui'
import { cn } from '$lib/utils'
type $$Props = SeparatorPrimitive.Props
let className: $$Props['class'] = undefined
export let orientation: $$Props['orientation'] = 'horizontal'
export let decorative: $$Props['decorative'] = undefined
export { className as class }
</script>
<SeparatorPrimitive.Root
class={cn(
'shrink-0 bg-border',
orientation === 'horizontal'
? 'h-[1px] w-full'
: 'h-full w-[1px]',
className,
)}
{orientation}
{decorative}
{...$$restProps} />

@ -0,0 +1,106 @@
import { Dialog as SheetPrimitive } from "bits-ui";
import { tv, type VariantProps } from "tailwind-variants";
import Portal from "./sheet-portal.svelte";
import Overlay from "./sheet-overlay.svelte";
import Content from "./sheet-content.svelte";
import Header from "./sheet-header.svelte";
import Footer from "./sheet-footer.svelte";
import Title from "./sheet-title.svelte";
import Description from "./sheet-description.svelte";
const Root = SheetPrimitive.Root;
const Close = SheetPrimitive.Close;
const Trigger = SheetPrimitive.Trigger;
export {
Root,
Close,
Trigger,
Portal,
Overlay,
Content,
Header,
Footer,
Title,
Description,
//
Root as Sheet,
Close as SheetClose,
Trigger as SheetTrigger,
Portal as SheetPortal,
Overlay as SheetOverlay,
Content as SheetContent,
Header as SheetHeader,
Footer as SheetFooter,
Title as SheetTitle,
Description as SheetDescription
};
export const sheetVariants = tv({
base: "fixed z-50 gap-4 bg-background p-6 shadow-lg",
variants: {
side: {
top: "inset-x-0 top-0 border-b",
bottom: "inset-x-0 bottom-0 border-t",
left: "inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
right: "inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm"
}
},
defaultVariants: {
side: "right"
}
});
export const sheetTransitions = {
top: {
in: {
y: "-100%",
duration: 500,
opacity: 1
},
out: {
y: "-100%",
duration: 300,
opacity: 1
}
},
bottom: {
in: {
y: "100%",
duration: 500,
opacity: 1
},
out: {
y: "100%",
duration: 300,
opacity: 1
}
},
left: {
in: {
x: "-100%",
duration: 500,
opacity: 1
},
out: {
x: "-100%",
duration: 300,
opacity: 1
}
},
right: {
in: {
x: "100%",
duration: 500,
opacity: 1
},
out: {
x: "100%",
duration: 300,
opacity: 1
}
}
};
export type Side = VariantProps<typeof sheetVariants>["side"];

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save