mirror of https://github.com/TZGyn/shortener
added svelte frontend
commit
60696fec3a
@ -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…
Reference in New Issue