update to svelte 5 and tw 4 etc
This commit is contained in:
parent
2ea45109d1
commit
e3b9a83e73
327 changed files with 5275 additions and 3363 deletions
21
.gitignore
vendored
Normal file
21
.gitignore
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# Output
|
||||||
|
.output
|
||||||
|
.vercel
|
||||||
|
/.svelte-kit
|
||||||
|
/build
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Env
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
!.env.test
|
||||||
|
|
||||||
|
# Vite
|
||||||
|
vite.config.js.timestamp-*
|
||||||
|
vite.config.ts.timestamp-*
|
1
.husky/pre-commit
Normal file
1
.husky/pre-commit
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pnpm lint-staged
|
1
.npmrc
Normal file
1
.npmrc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
engine-strict=true
|
4
.prettierignore
Normal file
4
.prettierignore
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# Package Managers
|
||||||
|
package-lock.json
|
||||||
|
pnpm-lock.yaml
|
||||||
|
yarn.lock
|
22
.prettierrc
Normal file
22
.prettierrc
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"useTabs": false,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"singleQuote": false,
|
||||||
|
"trailingComma": "all",
|
||||||
|
"printWidth": 100,
|
||||||
|
"arrowParens": "always",
|
||||||
|
"semi": true,
|
||||||
|
"plugins": [
|
||||||
|
"prettier-plugin-svelte",
|
||||||
|
"prettier-plugin-tailwindcss",
|
||||||
|
"prettier-plugin-organize-imports"
|
||||||
|
],
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": "*.svelte",
|
||||||
|
"options": {
|
||||||
|
"parser": "svelte"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -1,8 +1,6 @@
|
||||||
{
|
{
|
||||||
"$schema": "https://next.shadcn-svelte.com/schema.json",
|
"$schema": "https://next.shadcn-svelte.com/schema.json",
|
||||||
"style": "default",
|
|
||||||
"tailwind": {
|
"tailwind": {
|
||||||
"config": "tailwind.config.js",
|
|
||||||
"css": "src/app.css",
|
"css": "src/app.css",
|
||||||
"baseColor": "slate"
|
"baseColor": "slate"
|
||||||
},
|
},
|
||||||
|
@ -10,8 +8,9 @@
|
||||||
"components": "$lib/components",
|
"components": "$lib/components",
|
||||||
"utils": "$lib/utils",
|
"utils": "$lib/utils",
|
||||||
"ui": "$lib/components/ui",
|
"ui": "$lib/components/ui",
|
||||||
"hooks": "$lib/hooks"
|
"hooks": "$lib/hooks",
|
||||||
|
"lib": "$lib"
|
||||||
},
|
},
|
||||||
"typescript": false,
|
"typescript": true,
|
||||||
"registry": "https://next.shadcn-svelte.com/registry"
|
"registry": "https://next.shadcn-svelte.com/registry"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"extends": "./.svelte-kit/tsconfig.json"
|
|
||||||
}
|
|
50
package.json
50
package.json
|
@ -6,8 +6,8 @@
|
||||||
"dev": "vite dev",
|
"dev": "vite dev",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"check": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json",
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch",
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||||
"format": "prettier --write .",
|
"format": "prettier --write .",
|
||||||
"lint": "prettier --check .",
|
"lint": "prettier --check .",
|
||||||
"db:start": "docker compose up",
|
"db:start": "docker compose up",
|
||||||
|
@ -19,44 +19,56 @@
|
||||||
"prepare": "husky"
|
"prepare": "husky"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@exodus/schemasafe": "^1.3.0",
|
||||||
"@internationalized/date": "^3.8.0",
|
"@internationalized/date": "^3.8.0",
|
||||||
"@lucide/svelte": "^0.511.0",
|
"@lucide/svelte": "^0.482.0",
|
||||||
"@sveltejs/adapter-auto": "^3.3.1",
|
"@sinclair/typebox": ">=0.32.30 <1",
|
||||||
"@sveltejs/kit": "^2.21.0",
|
"@sveltejs/adapter-auto": "^6.0.1",
|
||||||
"@sveltejs/vite-plugin-svelte": "^4.0.4",
|
"@sveltejs/kit": "^2.21.1",
|
||||||
|
"@sveltejs/vite-plugin-svelte": "^5.0.3",
|
||||||
|
"@tailwindcss/vite": "^4.1.7",
|
||||||
|
"@tanstack/table-core": "^8.20.5",
|
||||||
|
"@typeschema/class-validator": "^0.2.0",
|
||||||
|
"@vinejs/vine": "^1.8.0",
|
||||||
|
"arktype": ">=2.0.0-rc.8",
|
||||||
"autoprefixer": "^10.4.21",
|
"autoprefixer": "^10.4.21",
|
||||||
"bits-ui": "1.0.0-next.40",
|
"bits-ui": "1.5.2",
|
||||||
|
"class-validator": "^0.14.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"drizzle-kit": "^0.27.2",
|
"drizzle-kit": "^0.31.1",
|
||||||
"embla-carousel-svelte": "^8.6.0",
|
"embla-carousel-svelte": "^8.6.0",
|
||||||
"formsnap": "2.0.0-next.1",
|
"formsnap": "2.0.1",
|
||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
|
"joi": "^17.13.1",
|
||||||
|
"layerchart": "2.0.0-next.6",
|
||||||
"lint-staged": "^15.5.2",
|
"lint-staged": "^15.5.2",
|
||||||
"lucide-svelte": "^0.454.0",
|
"mode-watcher": "^1.0.7",
|
||||||
"mode-watcher": "^0.4.1",
|
"paneforge": "1.0.0-next.5",
|
||||||
"paneforge": "1.0.0-next.1",
|
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"prettier-plugin-organize-imports": "^4.1.0",
|
"prettier-plugin-organize-imports": "^4.1.0",
|
||||||
"prettier-plugin-svelte": "^3.4.0",
|
"prettier-plugin-svelte": "^3.4.0",
|
||||||
"prettier-plugin-tailwindcss": "^0.6.11",
|
"prettier-plugin-tailwindcss": "^0.6.11",
|
||||||
|
"superstruct": "^2.0.2",
|
||||||
"svelte": "^5.30.2",
|
"svelte": "^5.30.2",
|
||||||
"svelte-check": "^4.2.1",
|
"svelte-check": "^4.2.1",
|
||||||
"svelte-sonner": "^0.3.28",
|
"svelte-sonner": "^1.0.1",
|
||||||
"sveltekit-superforms": "^2.25.0",
|
"sveltekit-superforms": "^2.25.0",
|
||||||
"tailwind-merge": "^2.6.0",
|
"tailwind-merge": "^3.3.0",
|
||||||
"tailwind-variants": "^0.2.1",
|
"tailwind-variants": "^0.2.1",
|
||||||
"tailwindcss": "^3.4.17",
|
"tailwindcss": "^4.1.7",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tw-animate-css": "^1.3.0",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"vaul-svelte": "1.0.0-next.1",
|
"valibot": ">=0.33.0 <1",
|
||||||
"vite": "^5.4.19",
|
"vaul-svelte": "1.0.0-next.7",
|
||||||
|
"vite": "^6.3.5",
|
||||||
|
"yup": "^1.4.0",
|
||||||
"zod": "^3.24.4"
|
"zod": "^3.24.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@node-rs/argon2": "^2.0.2",
|
"@node-rs/argon2": "^2.0.2",
|
||||||
"@oslojs/crypto": "^1.0.1",
|
"@oslojs/crypto": "^1.0.1",
|
||||||
"@oslojs/encoding": "^1.1.0",
|
"@oslojs/encoding": "^1.1.0",
|
||||||
"drizzle-orm": "^0.36.4",
|
"drizzle-orm": "^0.43.1",
|
||||||
"postgres": "^3.4.5"
|
"postgres": "^3.4.5"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
|
|
2315
pnpm-lock.yaml
generated
2315
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
@ -1,6 +0,0 @@
|
||||||
export default {
|
|
||||||
plugins: {
|
|
||||||
tailwindcss: {},
|
|
||||||
autoprefixer: {},
|
|
||||||
},
|
|
||||||
};
|
|
164
src/app.css
164
src/app.css
|
@ -1,73 +1,109 @@
|
||||||
@tailwind base;
|
@import "tailwindcss";
|
||||||
@tailwind components;
|
|
||||||
@tailwind utilities;
|
|
||||||
|
|
||||||
@layer base {
|
@import "tw-animate-css";
|
||||||
:root {
|
|
||||||
--background: 0 0% 100%;
|
|
||||||
--foreground: 222.2 84% 4.9%;
|
|
||||||
--muted: 210 40% 96.1%;
|
|
||||||
--muted-foreground: 215.4 16.3% 46.9%;
|
|
||||||
--popover: 0 0% 100%;
|
|
||||||
--popover-foreground: 222.2 84% 4.9%;
|
|
||||||
--card: 0 0% 100%;
|
|
||||||
--card-foreground: 222.2 84% 4.9%;
|
|
||||||
--border: 214.3 31.8% 91.4%;
|
|
||||||
--input: 214.3 31.8% 91.4%;
|
|
||||||
--primary: 222.2 47.4% 11.2%;
|
|
||||||
--primary-foreground: 210 40% 98%;
|
|
||||||
--secondary: 210 40% 96.1%;
|
|
||||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
|
||||||
--accent: 210 40% 96.1%;
|
|
||||||
--accent-foreground: 222.2 47.4% 11.2%;
|
|
||||||
--destructive: 0 72.2% 50.6%;
|
|
||||||
--destructive-foreground: 210 40% 98%;
|
|
||||||
--ring: 222.2 84% 4.9%;
|
|
||||||
--radius: 0.5rem;
|
|
||||||
--sidebar-background: 0 0% 98%;
|
|
||||||
--sidebar-foreground: 240 5.3% 26.1%;
|
|
||||||
--sidebar-primary: 240 5.9% 10%;
|
|
||||||
--sidebar-primary-foreground: 0 0% 98%;
|
|
||||||
--sidebar-accent: 240 4.8% 95.9%;
|
|
||||||
--sidebar-accent-foreground: 240 5.9% 10%;
|
|
||||||
--sidebar-border: 220 13% 91%;
|
|
||||||
--sidebar-ring: 217.2 91.2% 59.8%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark {
|
@custom-variant dark (&:is(.dark *));
|
||||||
--background: 222.2 84% 4.9%;
|
|
||||||
--foreground: 210 40% 98%;
|
:root {
|
||||||
--muted: 217.2 32.6% 17.5%;
|
--radius: 0.625rem;
|
||||||
--muted-foreground: 215 20.2% 65.1%;
|
--background: oklch(1 0 0);
|
||||||
--popover: 222.2 84% 4.9%;
|
--foreground: oklch(0.129 0.042 264.695);
|
||||||
--popover-foreground: 210 40% 98%;
|
--card: oklch(1 0 0);
|
||||||
--card: 222.2 84% 4.9%;
|
--card-foreground: oklch(0.129 0.042 264.695);
|
||||||
--card-foreground: 210 40% 98%;
|
--popover: oklch(1 0 0);
|
||||||
--border: 217.2 32.6% 17.5%;
|
--popover-foreground: oklch(0.129 0.042 264.695);
|
||||||
--input: 217.2 32.6% 17.5%;
|
--primary: oklch(0.208 0.042 265.755);
|
||||||
--primary: 210 40% 98%;
|
--primary-foreground: oklch(0.984 0.003 247.858);
|
||||||
--primary-foreground: 222.2 47.4% 11.2%;
|
--secondary: oklch(0.968 0.007 247.896);
|
||||||
--secondary: 217.2 32.6% 17.5%;
|
--secondary-foreground: oklch(0.208 0.042 265.755);
|
||||||
--secondary-foreground: 210 40% 98%;
|
--muted: oklch(0.968 0.007 247.896);
|
||||||
--accent: 217.2 32.6% 17.5%;
|
--muted-foreground: oklch(0.554 0.046 257.417);
|
||||||
--accent-foreground: 210 40% 98%;
|
--accent: oklch(0.968 0.007 247.896);
|
||||||
--destructive: 0 62.8% 30.6%;
|
--accent-foreground: oklch(0.208 0.042 265.755);
|
||||||
--destructive-foreground: 210 40% 98%;
|
--destructive: oklch(0.577 0.245 27.325);
|
||||||
--ring: 212.7 26.8% 83.9%;
|
--border: oklch(0.929 0.013 255.508);
|
||||||
--sidebar-background: 240 5.9% 10%;
|
--input: oklch(0.929 0.013 255.508);
|
||||||
--sidebar-foreground: 240 4.8% 95.9%;
|
--ring: oklch(0.704 0.04 256.788);
|
||||||
--sidebar-primary: 224.3 76.3% 48%;
|
--sidebar: oklch(0.984 0.003 247.858);
|
||||||
--sidebar-primary-foreground: 0 0% 100%;
|
--sidebar-foreground: oklch(0.129 0.042 264.695);
|
||||||
--sidebar-accent: 240 3.7% 15.9%;
|
--sidebar-primary: oklch(0.208 0.042 265.755);
|
||||||
--sidebar-accent-foreground: 240 4.8% 95.9%;
|
--sidebar-primary-foreground: oklch(0.984 0.003 247.858);
|
||||||
--sidebar-border: 240 3.7% 15.9%;
|
--sidebar-accent: oklch(0.968 0.007 247.896);
|
||||||
--sidebar-ring: 217.2 91.2% 59.8%;
|
--sidebar-accent-foreground: oklch(0.208 0.042 265.755);
|
||||||
}
|
--sidebar-border: oklch(0.929 0.013 255.508);
|
||||||
|
--sidebar-ring: oklch(0.704 0.04 256.788);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--background: oklch(0.129 0.042 264.695);
|
||||||
|
--foreground: oklch(0.984 0.003 247.858);
|
||||||
|
--card: oklch(0.208 0.042 265.755);
|
||||||
|
--card-foreground: oklch(0.984 0.003 247.858);
|
||||||
|
--popover: oklch(0.208 0.042 265.755);
|
||||||
|
--popover-foreground: oklch(0.984 0.003 247.858);
|
||||||
|
--primary: oklch(0.929 0.013 255.508);
|
||||||
|
--primary-foreground: oklch(0.208 0.042 265.755);
|
||||||
|
--secondary: oklch(0.279 0.041 260.031);
|
||||||
|
--secondary-foreground: oklch(0.984 0.003 247.858);
|
||||||
|
--muted: oklch(0.279 0.041 260.031);
|
||||||
|
--muted-foreground: oklch(0.704 0.04 256.788);
|
||||||
|
--accent: oklch(0.279 0.041 260.031);
|
||||||
|
--accent-foreground: oklch(0.984 0.003 247.858);
|
||||||
|
--destructive: oklch(0.704 0.191 22.216);
|
||||||
|
--border: oklch(1 0 0 / 10%);
|
||||||
|
--input: oklch(1 0 0 / 15%);
|
||||||
|
--ring: oklch(0.551 0.027 264.364);
|
||||||
|
--sidebar: oklch(0.208 0.042 265.755);
|
||||||
|
--sidebar-foreground: oklch(0.984 0.003 247.858);
|
||||||
|
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||||
|
--sidebar-primary-foreground: oklch(0.984 0.003 247.858);
|
||||||
|
--sidebar-accent: oklch(0.279 0.041 260.031);
|
||||||
|
--sidebar-accent-foreground: oklch(0.984 0.003 247.858);
|
||||||
|
--sidebar-border: oklch(1 0 0 / 10%);
|
||||||
|
--sidebar-ring: oklch(0.551 0.027 264.364);
|
||||||
|
}
|
||||||
|
|
||||||
|
@theme inline {
|
||||||
|
--radius-sm: calc(var(--radius) - 4px);
|
||||||
|
--radius-md: calc(var(--radius) - 2px);
|
||||||
|
--radius-lg: var(--radius);
|
||||||
|
--radius-xl: calc(var(--radius) + 4px);
|
||||||
|
--color-background: var(--background);
|
||||||
|
--color-foreground: var(--foreground);
|
||||||
|
--color-card: var(--card);
|
||||||
|
--color-card-foreground: var(--card-foreground);
|
||||||
|
--color-popover: var(--popover);
|
||||||
|
--color-popover-foreground: var(--popover-foreground);
|
||||||
|
--color-primary: var(--primary);
|
||||||
|
--color-primary-foreground: var(--primary-foreground);
|
||||||
|
--color-secondary: var(--secondary);
|
||||||
|
--color-secondary-foreground: var(--secondary-foreground);
|
||||||
|
--color-muted: var(--muted);
|
||||||
|
--color-muted-foreground: var(--muted-foreground);
|
||||||
|
--color-accent: var(--accent);
|
||||||
|
--color-accent-foreground: var(--accent-foreground);
|
||||||
|
--color-destructive: var(--destructive);
|
||||||
|
--color-border: var(--border);
|
||||||
|
--color-input: var(--input);
|
||||||
|
--color-ring: var(--ring);
|
||||||
|
--color-chart-1: var(--chart-1);
|
||||||
|
--color-chart-2: var(--chart-2);
|
||||||
|
--color-chart-3: var(--chart-3);
|
||||||
|
--color-chart-4: var(--chart-4);
|
||||||
|
--color-chart-5: var(--chart-5);
|
||||||
|
--color-sidebar: var(--sidebar);
|
||||||
|
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||||
|
--color-sidebar-primary: var(--sidebar-primary);
|
||||||
|
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||||
|
--color-sidebar-accent: var(--sidebar-accent);
|
||||||
|
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||||
|
--color-sidebar-border: var(--sidebar-border);
|
||||||
|
--color-sidebar-ring: var(--sidebar-ring);
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
* {
|
* {
|
||||||
@apply border-border;
|
@apply border-border outline-ring/50;
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
@apply bg-background text-foreground;
|
@apply bg-background text-foreground;
|
||||||
|
|
|
@ -1,19 +1,25 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { Accordion as AccordionPrimitive } from "bits-ui";
|
import { Accordion as AccordionPrimitive } from "bits-ui";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn, type WithoutChild } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithoutChild<AccordionPrimitive.ContentProps> = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<AccordionPrimitive.Content
|
<AccordionPrimitive.Content
|
||||||
bind:ref
|
bind:ref
|
||||||
|
data-slot="accordion-content"
|
||||||
class={cn(
|
class={cn(
|
||||||
"overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down",
|
"data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
<div class="pb-4 pt-0">
|
<div class="pt-0 pb-4">
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
</div>
|
</div>
|
||||||
</AccordionPrimitive.Content>
|
</AccordionPrimitive.Content>
|
||||||
|
|
|
@ -1,8 +1,17 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { Accordion as AccordionPrimitive } from "bits-ui";
|
import { Accordion as AccordionPrimitive } from "bits-ui";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
...restProps
|
||||||
|
}: AccordionPrimitive.ItemProps = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<AccordionPrimitive.Item bind:ref class={cn("border-b", className)} {...restProps} />
|
<AccordionPrimitive.Item
|
||||||
|
bind:ref
|
||||||
|
data-slot="accordion-item"
|
||||||
|
class={cn("border-b last:border-b-0", className)}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
||||||
|
|
16
src/lib/components/ui/accordion/accordion-root.svelte
Normal file
16
src/lib/components/ui/accordion/accordion-root.svelte
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Accordion as AccordionPrimitive } from "bits-ui";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
value = $bindable(),
|
||||||
|
...restProps
|
||||||
|
}: AccordionPrimitive.RootProps = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<AccordionPrimitive.Root
|
||||||
|
bind:ref
|
||||||
|
bind:value={value as never}
|
||||||
|
data-slot="accordion"
|
||||||
|
{...restProps}
|
||||||
|
/>
|
|
@ -1,21 +1,32 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { Accordion as AccordionPrimitive } from "bits-ui";
|
import { Accordion as AccordionPrimitive } from "bits-ui";
|
||||||
import ChevronDown from "@lucide/svelte/icons/chevron-down";
|
import ChevronDownIcon from "@lucide/svelte/icons/chevron-down";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn, type WithoutChild } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, level = 3, children, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
level = 3,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithoutChild<AccordionPrimitive.TriggerProps> & {
|
||||||
|
level?: AccordionPrimitive.HeaderProps["level"];
|
||||||
|
} = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<AccordionPrimitive.Header {level} class="flex">
|
<AccordionPrimitive.Header {level} class="flex">
|
||||||
<AccordionPrimitive.Trigger
|
<AccordionPrimitive.Trigger
|
||||||
|
data-slot="accordion-trigger"
|
||||||
bind:ref
|
bind:ref
|
||||||
class={cn(
|
class={cn(
|
||||||
"flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
|
"focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
<ChevronDown class="size-4 shrink-0 transition-transform duration-200" />
|
<ChevronDownIcon
|
||||||
|
class="text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200"
|
||||||
|
/>
|
||||||
</AccordionPrimitive.Trigger>
|
</AccordionPrimitive.Trigger>
|
||||||
</AccordionPrimitive.Header>
|
</AccordionPrimitive.Header>
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
import { Accordion as AccordionPrimitive } from "bits-ui";
|
|
||||||
import Content from "./accordion-content.svelte";
|
|
||||||
import Item from "./accordion-item.svelte";
|
|
||||||
import Trigger from "./accordion-trigger.svelte";
|
|
||||||
const Root = AccordionPrimitive.Root;
|
|
||||||
|
|
||||||
export {
|
|
||||||
//
|
|
||||||
Root as Accordion,
|
|
||||||
Content as AccordionContent,
|
|
||||||
Item as AccordionItem,
|
|
||||||
Trigger as AccordionTrigger,
|
|
||||||
Content,
|
|
||||||
Item,
|
|
||||||
Root,
|
|
||||||
Trigger,
|
|
||||||
};
|
|
16
src/lib/components/ui/accordion/index.ts
Normal file
16
src/lib/components/ui/accordion/index.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import Root from "./accordion-root.svelte";
|
||||||
|
import Content from "./accordion-content.svelte";
|
||||||
|
import Item from "./accordion-item.svelte";
|
||||||
|
import Trigger from "./accordion-trigger.svelte";
|
||||||
|
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
Content,
|
||||||
|
Item,
|
||||||
|
Trigger,
|
||||||
|
//
|
||||||
|
Root as Accordion,
|
||||||
|
Content as AccordionContent,
|
||||||
|
Item as AccordionItem,
|
||||||
|
Trigger as AccordionTrigger,
|
||||||
|
};
|
|
@ -1,9 +1,18 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
||||||
import { buttonVariants } from "$lib/components/ui/button/index.js";
|
import { buttonVariants } from "$lib/components/ui/button/index.js";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
...restProps
|
||||||
|
}: AlertDialogPrimitive.ActionProps = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<AlertDialogPrimitive.Action bind:ref class={cn(buttonVariants(), className)} {...restProps} />
|
<AlertDialogPrimitive.Action
|
||||||
|
bind:ref
|
||||||
|
data-slot="alert-dialog-action"
|
||||||
|
class={cn(buttonVariants(), className)}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
||||||
|
|
|
@ -1,13 +1,18 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
||||||
import { buttonVariants } from "$lib/components/ui/button/index.js";
|
import { buttonVariants } from "$lib/components/ui/button/index.js";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
...restProps
|
||||||
|
}: AlertDialogPrimitive.CancelProps = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<AlertDialogPrimitive.Cancel
|
<AlertDialogPrimitive.Cancel
|
||||||
bind:ref
|
bind:ref
|
||||||
class={cn(buttonVariants({ variant: "outline" }), "mt-2 sm:mt-0", className)}
|
data-slot="alert-dialog-cancel"
|
||||||
|
class={cn(buttonVariants({ variant: "outline" }), className)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,17 +1,25 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
||||||
import AlertDialogOverlay from "./alert-dialog-overlay.svelte";
|
import AlertDialogOverlay from "./alert-dialog-overlay.svelte";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn, type WithoutChild, type WithoutChildrenOrChild } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, portalProps, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
portalProps,
|
||||||
|
...restProps
|
||||||
|
}: WithoutChild<AlertDialogPrimitive.ContentProps> & {
|
||||||
|
portalProps?: WithoutChildrenOrChild<AlertDialogPrimitive.PortalProps>;
|
||||||
|
} = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<AlertDialogPrimitive.Portal {...portalProps}>
|
<AlertDialogPrimitive.Portal {...portalProps}>
|
||||||
<AlertDialogOverlay />
|
<AlertDialogOverlay />
|
||||||
<AlertDialogPrimitive.Content
|
<AlertDialogPrimitive.Content
|
||||||
bind:ref
|
bind:ref
|
||||||
|
data-slot="alert-dialog-content"
|
||||||
class={cn(
|
class={cn(
|
||||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
...restProps
|
||||||
|
}: AlertDialogPrimitive.DescriptionProps = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<AlertDialogPrimitive.Description
|
<AlertDialogPrimitive.Description
|
||||||
bind:ref
|
bind:ref
|
||||||
class={cn("text-sm text-muted-foreground", className)}
|
data-slot="alert-dialog-description"
|
||||||
|
class={cn("text-muted-foreground text-sm", className)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,12 +1,19 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
bind:this={ref}
|
bind:this={ref}
|
||||||
class={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)}
|
data-slot="alert-dialog-footer"
|
||||||
|
class={cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
|
|
|
@ -1,12 +1,19 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { cn } from "$lib/utils.js";
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
bind:this={ref}
|
bind:this={ref}
|
||||||
class={cn("flex flex-col space-y-2 text-center sm:text-left", className)}
|
data-slot="alert-dialog-header"
|
||||||
|
class={cn("flex flex-col gap-2 text-center sm:text-left", className)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
|
|
|
@ -1,14 +1,19 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
...restProps
|
||||||
|
}: AlertDialogPrimitive.OverlayProps = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<AlertDialogPrimitive.Overlay
|
<AlertDialogPrimitive.Overlay
|
||||||
bind:ref
|
bind:ref
|
||||||
|
data-slot="alert-dialog-overlay"
|
||||||
class={cn(
|
class={cn(
|
||||||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, level = 3, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
...restProps
|
||||||
|
}: AlertDialogPrimitive.TitleProps = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<AlertDialogPrimitive.Title
|
<AlertDialogPrimitive.Title
|
||||||
bind:ref
|
bind:ref
|
||||||
|
data-slot="alert-dialog-title"
|
||||||
class={cn("text-lg font-semibold", className)}
|
class={cn("text-lg font-semibold", className)}
|
||||||
{level}
|
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), ...restProps }: AlertDialogPrimitive.TriggerProps = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<AlertDialogPrimitive.Trigger bind:ref data-slot="alert-dialog-trigger" {...restProps} />
|
|
@ -1,39 +1,39 @@
|
||||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
||||||
|
import Trigger from "./alert-dialog-trigger.svelte";
|
||||||
|
import Title from "./alert-dialog-title.svelte";
|
||||||
import Action from "./alert-dialog-action.svelte";
|
import Action from "./alert-dialog-action.svelte";
|
||||||
import Cancel from "./alert-dialog-cancel.svelte";
|
import Cancel from "./alert-dialog-cancel.svelte";
|
||||||
import Content from "./alert-dialog-content.svelte";
|
|
||||||
import Description from "./alert-dialog-description.svelte";
|
|
||||||
import Footer from "./alert-dialog-footer.svelte";
|
import Footer from "./alert-dialog-footer.svelte";
|
||||||
import Header from "./alert-dialog-header.svelte";
|
import Header from "./alert-dialog-header.svelte";
|
||||||
import Overlay from "./alert-dialog-overlay.svelte";
|
import Overlay from "./alert-dialog-overlay.svelte";
|
||||||
import Title from "./alert-dialog-title.svelte";
|
import Content from "./alert-dialog-content.svelte";
|
||||||
|
import Description from "./alert-dialog-description.svelte";
|
||||||
|
|
||||||
const Root = AlertDialogPrimitive.Root;
|
const Root = AlertDialogPrimitive.Root;
|
||||||
const Trigger = AlertDialogPrimitive.Trigger;
|
|
||||||
const Portal = AlertDialogPrimitive.Portal;
|
const Portal = AlertDialogPrimitive.Portal;
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Action,
|
Root,
|
||||||
//
|
Title,
|
||||||
Root as AlertDialog,
|
Action,
|
||||||
Action as AlertDialogAction,
|
Cancel,
|
||||||
Cancel as AlertDialogCancel,
|
Portal,
|
||||||
Content as AlertDialogContent,
|
Footer,
|
||||||
Description as AlertDialogDescription,
|
Header,
|
||||||
Footer as AlertDialogFooter,
|
Trigger,
|
||||||
Header as AlertDialogHeader,
|
Overlay,
|
||||||
Overlay as AlertDialogOverlay,
|
Content,
|
||||||
Portal as AlertDialogPortal,
|
Description,
|
||||||
Title as AlertDialogTitle,
|
//
|
||||||
Trigger as AlertDialogTrigger,
|
Root as AlertDialog,
|
||||||
Cancel,
|
Title as AlertDialogTitle,
|
||||||
Content,
|
Action as AlertDialogAction,
|
||||||
Description,
|
Cancel as AlertDialogCancel,
|
||||||
Footer,
|
Portal as AlertDialogPortal,
|
||||||
Header,
|
Footer as AlertDialogFooter,
|
||||||
Overlay,
|
Header as AlertDialogHeader,
|
||||||
Portal,
|
Trigger as AlertDialogTrigger,
|
||||||
Root,
|
Overlay as AlertDialogOverlay,
|
||||||
Title,
|
Content as AlertDialogContent,
|
||||||
Trigger,
|
Description as AlertDialogDescription,
|
||||||
};
|
};
|
|
@ -1,9 +1,23 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { cn } from "$lib/utils.js";
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div bind:this={ref} class={cn("text-sm [&_p]:leading-relaxed", className)} {...restProps}>
|
<div
|
||||||
|
bind:this={ref}
|
||||||
|
data-slot="alert-description"
|
||||||
|
class={cn(
|
||||||
|
"text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,14 +1,19 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { cn } from "$lib/utils.js";
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, level = 5, children, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
role="heading"
|
|
||||||
aria-level={level}
|
|
||||||
bind:this={ref}
|
bind:this={ref}
|
||||||
class={cn("mb-1 font-medium leading-none tracking-tight", className)}
|
data-slot="alert-title"
|
||||||
|
class={cn("col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight", className)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
|
|
|
@ -1,23 +1,26 @@
|
||||||
<script module>
|
<script lang="ts" module>
|
||||||
import { tv } from "tailwind-variants";
|
import { type VariantProps, tv } from "tailwind-variants";
|
||||||
|
|
||||||
export const alertVariants = tv({
|
export const alertVariants = tv({
|
||||||
base: "[&>svg]:text-foreground relative w-full rounded-lg border p-4 [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg~*]:pl-7",
|
base: "relative grid w-full grid-cols-[0_1fr] items-start gap-y-0.5 rounded-lg border px-4 py-3 text-sm has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] has-[>svg]:gap-x-3 [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
default: "bg-background text-foreground",
|
default: "bg-card text-card-foreground",
|
||||||
destructive:
|
destructive:
|
||||||
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
|
"text-destructive bg-card *:data-[slot=alert-description]:text-destructive/90 [&>svg]:text-current",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
variant: "default",
|
variant: "default",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export type AlertVariant = VariantProps<typeof alertVariants>["variant"];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { cn } from "$lib/utils.js";
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||||
|
|
||||||
let {
|
let {
|
||||||
ref = $bindable(null),
|
ref = $bindable(null),
|
||||||
|
@ -25,9 +28,17 @@
|
||||||
variant = "default",
|
variant = "default",
|
||||||
children,
|
children,
|
||||||
...restProps
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
|
||||||
|
variant?: AlertVariant;
|
||||||
} = $props();
|
} = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div bind:this={ref} class={cn(alertVariants({ variant }), className)} {...restProps} role="alert">
|
<div
|
||||||
|
bind:this={ref}
|
||||||
|
data-slot="alert"
|
||||||
|
class={cn(alertVariants({ variant }), className)}
|
||||||
|
{...restProps}
|
||||||
|
role="alert"
|
||||||
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
import Description from "./alert-description.svelte";
|
|
||||||
import Title from "./alert-title.svelte";
|
|
||||||
import Root from "./alert.svelte";
|
|
||||||
export { alertVariants } from "./alert.svelte";
|
|
||||||
|
|
||||||
export {
|
|
||||||
//
|
|
||||||
Root as Alert,
|
|
||||||
Description as AlertDescription,
|
|
||||||
Title as AlertTitle,
|
|
||||||
Description,
|
|
||||||
Root,
|
|
||||||
Title,
|
|
||||||
};
|
|
14
src/lib/components/ui/alert/index.ts
Normal file
14
src/lib/components/ui/alert/index.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import Root from "./alert.svelte";
|
||||||
|
import Description from "./alert-description.svelte";
|
||||||
|
import Title from "./alert-title.svelte";
|
||||||
|
export { alertVariants, type AlertVariant } from "./alert.svelte";
|
||||||
|
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
Description,
|
||||||
|
Title,
|
||||||
|
//
|
||||||
|
Root as Alert,
|
||||||
|
Description as AlertDescription,
|
||||||
|
Title as AlertTitle,
|
||||||
|
};
|
7
src/lib/components/ui/aspect-ratio/aspect-ratio.svelte
Normal file
7
src/lib/components/ui/aspect-ratio/aspect-ratio.svelte
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { AspectRatio as AspectRatioPrimitive } from "bits-ui";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), ...restProps }: AspectRatioPrimitive.RootProps = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<AspectRatioPrimitive.Root bind:ref data-slot="aspect-ratio" {...restProps} />
|
|
@ -1,5 +0,0 @@
|
||||||
import { AspectRatio as AspectRatioPrimitive } from "bits-ui";
|
|
||||||
|
|
||||||
const Root = AspectRatioPrimitive.Root;
|
|
||||||
|
|
||||||
export { Root as AspectRatio, Root };
|
|
3
src/lib/components/ui/aspect-ratio/index.ts
Normal file
3
src/lib/components/ui/aspect-ratio/index.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import Root from "./aspect-ratio.svelte";
|
||||||
|
|
||||||
|
export { Root, Root as AspectRatio };
|
|
@ -1,12 +1,17 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { Avatar as AvatarPrimitive } from "bits-ui";
|
import { Avatar as AvatarPrimitive } from "bits-ui";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
...restProps
|
||||||
|
}: AvatarPrimitive.FallbackProps = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<AvatarPrimitive.Fallback
|
<AvatarPrimitive.Fallback
|
||||||
bind:ref
|
bind:ref
|
||||||
class={cn("flex size-full items-center justify-center bg-muted", className)}
|
data-slot="avatar-fallback"
|
||||||
|
class={cn("bg-muted flex size-full items-center justify-center rounded-full", className)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,8 +1,17 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { Avatar as AvatarPrimitive } from "bits-ui";
|
import { Avatar as AvatarPrimitive } from "bits-ui";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
...restProps
|
||||||
|
}: AvatarPrimitive.ImageProps = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<AvatarPrimitive.Image bind:ref class={cn("aspect-square size-full", className)} {...restProps} />
|
<AvatarPrimitive.Image
|
||||||
|
bind:ref
|
||||||
|
data-slot="avatar-image"
|
||||||
|
class={cn("aspect-square size-full", className)}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { Avatar as AvatarPrimitive } from "bits-ui";
|
import { Avatar as AvatarPrimitive } from "bits-ui";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
...restProps
|
||||||
|
}: AvatarPrimitive.RootProps = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<AvatarPrimitive.Root
|
<AvatarPrimitive.Root
|
||||||
bind:ref
|
bind:ref
|
||||||
class={cn("relative flex size-10 shrink-0 overflow-hidden rounded-full", className)}
|
data-slot="avatar"
|
||||||
|
class={cn("relative flex size-8 shrink-0 overflow-hidden rounded-full", className)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import Fallback from "./avatar-fallback.svelte";
|
|
||||||
import Image from "./avatar-image.svelte";
|
|
||||||
import Root from "./avatar.svelte";
|
import Root from "./avatar.svelte";
|
||||||
|
import Image from "./avatar-image.svelte";
|
||||||
|
import Fallback from "./avatar-fallback.svelte";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
//
|
Root,
|
||||||
Root as Avatar,
|
Image,
|
||||||
Fallback as AvatarFallback,
|
Fallback,
|
||||||
Image as AvatarImage,
|
//
|
||||||
Fallback,
|
Root as Avatar,
|
||||||
Image,
|
Image as AvatarImage,
|
||||||
Root,
|
Fallback as AvatarFallback,
|
||||||
};
|
};
|
|
@ -1,26 +1,29 @@
|
||||||
<script module>
|
<script lang="ts" module>
|
||||||
import { tv } from "tailwind-variants";
|
import { type VariantProps, tv } from "tailwind-variants";
|
||||||
|
|
||||||
export const badgeVariants = tv({
|
export const badgeVariants = tv({
|
||||||
base: "focus:ring-ring inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2",
|
base: "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden whitespace-nowrap rounded-md border px-2 py-0.5 text-xs font-medium transition-[color,box-shadow] focus-visible:ring-[3px] [&>svg]:pointer-events-none [&>svg]:size-3",
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
default: "bg-primary text-primary-foreground hover:bg-primary/80 border-transparent",
|
default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90 border-transparent",
|
||||||
secondary:
|
secondary:
|
||||||
"bg-secondary text-secondary-foreground hover:bg-secondary/80 border-transparent",
|
"bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90 border-transparent",
|
||||||
destructive:
|
destructive:
|
||||||
"bg-destructive text-destructive-foreground hover:bg-destructive/80 border-transparent",
|
"bg-destructive [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/70 border-transparent text-white",
|
||||||
outline: "text-foreground",
|
outline: "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
variant: "default",
|
variant: "default",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export type BadgeVariant = VariantProps<typeof badgeVariants>["variant"];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { cn } from "$lib/utils.js";
|
import type { HTMLAnchorAttributes } from "svelte/elements";
|
||||||
|
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||||
|
|
||||||
let {
|
let {
|
||||||
ref = $bindable(null),
|
ref = $bindable(null),
|
||||||
|
@ -29,12 +32,15 @@
|
||||||
variant = "default",
|
variant = "default",
|
||||||
children,
|
children,
|
||||||
...restProps
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAnchorAttributes> & {
|
||||||
|
variant?: BadgeVariant;
|
||||||
} = $props();
|
} = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:element
|
<svelte:element
|
||||||
this={href ? "a" : "span"}
|
this={href ? "a" : "span"}
|
||||||
bind:this={ref}
|
bind:this={ref}
|
||||||
|
data-slot="badge"
|
||||||
{href}
|
{href}
|
||||||
class={cn(badgeVariants({ variant }), className)}
|
class={cn(badgeVariants({ variant }), className)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
export { default as Badge, badgeVariants } from "./badge.svelte";
|
|
2
src/lib/components/ui/badge/index.ts
Normal file
2
src/lib/components/ui/badge/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export { default as Badge } from "./badge.svelte";
|
||||||
|
export { badgeVariants, type BadgeVariant } from "./badge.svelte";
|
|
@ -1,17 +1,23 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import Ellipsis from "@lucide/svelte/icons/ellipsis";
|
import EllipsisIcon from "@lucide/svelte/icons/ellipsis";
|
||||||
import { cn } from "$lib/utils.js";
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn, type WithElementRef, type WithoutChildren } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
...restProps
|
||||||
|
}: WithoutChildren<WithElementRef<HTMLAttributes<HTMLSpanElement>>> = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
bind:this={ref}
|
bind:this={ref}
|
||||||
|
data-slot="breadcrumb-ellipsis"
|
||||||
role="presentation"
|
role="presentation"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
class={cn("flex size-9 items-center justify-center", className)}
|
class={cn("flex size-9 items-center justify-center", className)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
<Ellipsis class="size-4" />
|
<EllipsisIcon class="size-4" />
|
||||||
<span class="sr-only">More</span>
|
<span class="sr-only">More</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -1,9 +1,20 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { cn } from "$lib/utils.js";
|
import type { HTMLLiAttributes } from "svelte/elements";
|
||||||
|
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLLiAttributes> = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<li bind:this={ref} class={cn("inline-flex items-center gap-1.5", className)} {...restProps}>
|
<li
|
||||||
|
bind:this={ref}
|
||||||
|
data-slot="breadcrumb-item"
|
||||||
|
class={cn("inline-flex items-center gap-1.5", className)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { cn } from "$lib/utils.js";
|
import type { HTMLAnchorAttributes } from "svelte/elements";
|
||||||
|
import type { Snippet } from "svelte";
|
||||||
|
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||||
|
|
||||||
let {
|
let {
|
||||||
ref = $bindable(null),
|
ref = $bindable(null),
|
||||||
|
@ -8,9 +10,12 @@
|
||||||
child,
|
child,
|
||||||
children,
|
children,
|
||||||
...restProps
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAnchorAttributes> & {
|
||||||
|
child?: Snippet<[{ props: HTMLAnchorAttributes }]>;
|
||||||
} = $props();
|
} = $props();
|
||||||
|
|
||||||
const attrs = $derived({
|
const attrs = $derived({
|
||||||
|
"data-slot": "breadcrumb-link",
|
||||||
class: cn("hover:text-foreground transition-colors", className),
|
class: cn("hover:text-foreground transition-colors", className),
|
||||||
href,
|
href,
|
||||||
...restProps,
|
...restProps,
|
||||||
|
|
|
@ -1,13 +1,20 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { cn } from "$lib/utils.js";
|
import type { HTMLOlAttributes } from "svelte/elements";
|
||||||
|
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLOlAttributes> = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ol
|
<ol
|
||||||
bind:this={ref}
|
bind:this={ref}
|
||||||
|
data-slot="breadcrumb-list"
|
||||||
class={cn(
|
class={cn(
|
||||||
"flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5",
|
"text-muted-foreground flex flex-wrap items-center gap-1.5 text-sm break-words sm:gap-2.5",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
|
|
|
@ -1,15 +1,22 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { cn } from "$lib/utils.js";
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLSpanElement>> = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<span
|
<span
|
||||||
bind:this={ref}
|
bind:this={ref}
|
||||||
|
data-slot="breadcrumb-page"
|
||||||
role="link"
|
role="link"
|
||||||
aria-disabled="true"
|
aria-disabled="true"
|
||||||
aria-current="page"
|
aria-current="page"
|
||||||
class={cn("font-normal text-foreground", className)}
|
class={cn("text-foreground font-normal", className)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
|
|
|
@ -1,20 +1,27 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import ChevronRight from "@lucide/svelte/icons/chevron-right";
|
import ChevronRightIcon from "@lucide/svelte/icons/chevron-right";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||||
|
import type { HTMLLiAttributes } from "svelte/elements";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLLiAttributes> = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<li
|
<li
|
||||||
|
bind:this={ref}
|
||||||
|
data-slot="breadcrumb-separator"
|
||||||
role="presentation"
|
role="presentation"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
class={cn("[&>svg]:size-3.5", className)}
|
class={cn("[&>svg]:size-3.5", className)}
|
||||||
bind:this={ref}
|
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
{#if children}
|
{#if children}
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
{:else}
|
{:else}
|
||||||
<ChevronRight />
|
<ChevronRightIcon />
|
||||||
{/if}
|
{/if}
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -1,7 +1,21 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
let { ref = $bindable(), class: className, children, ...restProps } = $props();
|
import type { WithElementRef } from "$lib/utils.js";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLElement>> = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav bind:this={ref} class={className} aria-label="breadcrumb" {...restProps}>
|
<nav
|
||||||
|
bind:this={ref}
|
||||||
|
data-slot="breadcrumb"
|
||||||
|
class={className}
|
||||||
|
aria-label="breadcrumb"
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
</nav>
|
</nav>
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
|
import Root from "./breadcrumb.svelte";
|
||||||
import Ellipsis from "./breadcrumb-ellipsis.svelte";
|
import Ellipsis from "./breadcrumb-ellipsis.svelte";
|
||||||
import Item from "./breadcrumb-item.svelte";
|
import Item from "./breadcrumb-item.svelte";
|
||||||
|
import Separator from "./breadcrumb-separator.svelte";
|
||||||
import Link from "./breadcrumb-link.svelte";
|
import Link from "./breadcrumb-link.svelte";
|
||||||
import List from "./breadcrumb-list.svelte";
|
import List from "./breadcrumb-list.svelte";
|
||||||
import Page from "./breadcrumb-page.svelte";
|
import Page from "./breadcrumb-page.svelte";
|
||||||
import Separator from "./breadcrumb-separator.svelte";
|
|
||||||
import Root from "./breadcrumb.svelte";
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
//
|
Root,
|
||||||
Root as Breadcrumb,
|
Ellipsis,
|
||||||
Ellipsis as BreadcrumbEllipsis,
|
Item,
|
||||||
Item as BreadcrumbItem,
|
Separator,
|
||||||
Link as BreadcrumbLink,
|
Link,
|
||||||
List as BreadcrumbList,
|
List,
|
||||||
Page as BreadcrumbPage,
|
Page,
|
||||||
Separator as BreadcrumbSeparator,
|
//
|
||||||
Ellipsis,
|
Root as Breadcrumb,
|
||||||
Item,
|
Ellipsis as BreadcrumbEllipsis,
|
||||||
Link,
|
Item as BreadcrumbItem,
|
||||||
List,
|
Separator as BreadcrumbSeparator,
|
||||||
Page,
|
Link as BreadcrumbLink,
|
||||||
Root,
|
List as BreadcrumbList,
|
||||||
Separator,
|
Page as BreadcrumbPage,
|
||||||
};
|
};
|
|
@ -1,22 +1,26 @@
|
||||||
<script module>
|
<script lang="ts" module>
|
||||||
import { tv } from "tailwind-variants";
|
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||||
|
import type { HTMLAnchorAttributes, HTMLButtonAttributes } from "svelte/elements";
|
||||||
|
import { type VariantProps, tv } from "tailwind-variants";
|
||||||
|
|
||||||
export const buttonVariants = tv({
|
export const buttonVariants = tv({
|
||||||
base: "ring-offset-background focus-visible:ring-ring inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
base: "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium outline-none transition-all focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
||||||
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
destructive:
|
||||||
outline: "border-input bg-background hover:bg-accent hover:text-accent-foreground border",
|
"bg-destructive shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60 text-white",
|
||||||
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
outline:
|
||||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
"bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 border",
|
||||||
|
secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
||||||
|
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
||||||
link: "text-primary underline-offset-4 hover:underline",
|
link: "text-primary underline-offset-4 hover:underline",
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
default: "h-10 px-4 py-2",
|
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
||||||
sm: "h-9 rounded-md px-3",
|
sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
|
||||||
lg: "h-11 rounded-md px-8",
|
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
||||||
icon: "h-10 w-10",
|
icon: "size-9",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
|
@ -24,11 +28,18 @@
|
||||||
size: "default",
|
size: "default",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export type ButtonVariant = VariantProps<typeof buttonVariants>["variant"];
|
||||||
|
export type ButtonSize = VariantProps<typeof buttonVariants>["size"];
|
||||||
|
|
||||||
|
export type ButtonProps = WithElementRef<HTMLButtonAttributes> &
|
||||||
|
WithElementRef<HTMLAnchorAttributes> & {
|
||||||
|
variant?: ButtonVariant;
|
||||||
|
size?: ButtonSize;
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
let {
|
||||||
class: className,
|
class: className,
|
||||||
variant = "default",
|
variant = "default",
|
||||||
|
@ -36,20 +47,32 @@
|
||||||
ref = $bindable(null),
|
ref = $bindable(null),
|
||||||
href = undefined,
|
href = undefined,
|
||||||
type = "button",
|
type = "button",
|
||||||
|
disabled,
|
||||||
children,
|
children,
|
||||||
...restProps
|
...restProps
|
||||||
} = $props();
|
}: ButtonProps = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if href}
|
{#if href}
|
||||||
<a bind:this={ref} class={cn(buttonVariants({ variant, size }), className)} {href} {...restProps}>
|
<a
|
||||||
|
bind:this={ref}
|
||||||
|
data-slot="button"
|
||||||
|
class={cn(buttonVariants({ variant, size }), className)}
|
||||||
|
href={disabled ? undefined : href}
|
||||||
|
aria-disabled={disabled}
|
||||||
|
role={disabled ? "link" : undefined}
|
||||||
|
tabindex={disabled ? -1 : undefined}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
</a>
|
</a>
|
||||||
{:else}
|
{:else}
|
||||||
<button
|
<button
|
||||||
bind:this={ref}
|
bind:this={ref}
|
||||||
|
data-slot="button"
|
||||||
class={cn(buttonVariants({ variant, size }), className)}
|
class={cn(buttonVariants({ variant, size }), className)}
|
||||||
{type}
|
{type}
|
||||||
|
{disabled}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
import Root, { buttonVariants } from "./button.svelte";
|
|
||||||
|
|
||||||
export {
|
|
||||||
//
|
|
||||||
Root as Button,
|
|
||||||
buttonVariants,
|
|
||||||
Root,
|
|
||||||
};
|
|
17
src/lib/components/ui/button/index.ts
Normal file
17
src/lib/components/ui/button/index.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import Root, {
|
||||||
|
type ButtonProps,
|
||||||
|
type ButtonSize,
|
||||||
|
type ButtonVariant,
|
||||||
|
buttonVariants,
|
||||||
|
} from "./button.svelte";
|
||||||
|
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
type ButtonProps as Props,
|
||||||
|
//
|
||||||
|
Root as Button,
|
||||||
|
buttonVariants,
|
||||||
|
type ButtonProps,
|
||||||
|
type ButtonSize,
|
||||||
|
type ButtonVariant,
|
||||||
|
};
|
|
@ -1,14 +1,18 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { Calendar as CalendarPrimitive } from "bits-ui";
|
import { Calendar as CalendarPrimitive } from "bits-ui";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
...restProps
|
||||||
|
}: CalendarPrimitive.CellProps = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<CalendarPrimitive.Cell
|
<CalendarPrimitive.Cell
|
||||||
bind:ref
|
bind:ref
|
||||||
class={cn(
|
class={cn(
|
||||||
"relative size-9 p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([data-selected])]:rounded-md [&:has([data-selected])]:bg-accent [&:has([data-selected][data-outside-month])]:bg-accent/50",
|
"[&:has([data-selected])]:bg-accent [&:has([data-selected][data-outside-month])]:bg-accent/50 relative size-8 p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([data-selected])]:rounded-md",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
|
|
|
@ -1,25 +1,29 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { buttonVariants } from "$lib/components/ui/button/index.js";
|
import { buttonVariants } from "$lib/components/ui/button/index.js";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn } from "$lib/utils.js";
|
||||||
import { Calendar as CalendarPrimitive } from "bits-ui";
|
import { Calendar as CalendarPrimitive } from "bits-ui";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
...restProps
|
||||||
|
}: CalendarPrimitive.DayProps = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<CalendarPrimitive.Day
|
<CalendarPrimitive.Day
|
||||||
bind:ref
|
bind:ref
|
||||||
class={cn(
|
class={cn(
|
||||||
buttonVariants({ variant: "ghost" }),
|
buttonVariants({ variant: "ghost" }),
|
||||||
"size-9 p-0 font-normal",
|
"size-8 p-0 font-normal select-none",
|
||||||
"[&[data-today]:not([data-selected])]:bg-accent [&[data-today]:not([data-selected])]:text-accent-foreground",
|
"[&[data-today]:not([data-selected])]:bg-accent [&[data-today]:not([data-selected])]:text-accent-foreground",
|
||||||
// Selected
|
// Selected
|
||||||
"data-[selected]:bg-primary data-[selected]:text-primary-foreground data-[selected]:opacity-100 data-[selected]:hover:bg-primary data-[selected]:hover:text-primary-foreground data-[selected]:focus:bg-primary data-[selected]:focus:text-primary-foreground",
|
"data-selected:bg-primary data-selected:text-primary-foreground data-selected:hover:bg-primary data-selected:hover:text-primary-foreground data-selected:focus:bg-primary data-selected:focus:text-primary-foreground dark:data-selected:hover:bg-primary dark:data-selected:focus:bg-primary data-selected:opacity-100",
|
||||||
// Disabled
|
// Disabled
|
||||||
"data-[disabled]:text-muted-foreground data-[disabled]:opacity-50",
|
"data-disabled:text-muted-foreground data-disabled:opacity-50",
|
||||||
// Unavailable
|
// Unavailable
|
||||||
"data-[unavailable]:text-destructive-foreground data-[unavailable]:line-through",
|
"data-unavailable:text-destructive-foreground data-unavailable:line-through",
|
||||||
// Outside months
|
// Outside months
|
||||||
"data-[outside-month]:pointer-events-none data-[outside-month]:text-muted-foreground data-[outside-month]:opacity-50 [&[data-outside-month][data-selected]]:bg-accent/50 [&[data-outside-month][data-selected]]:text-muted-foreground [&[data-outside-month][data-selected]]:opacity-30",
|
"data-[outside-month]:text-muted-foreground [&[data-outside-month][data-selected]]:bg-accent/50 [&[data-outside-month][data-selected]]:text-muted-foreground data-[outside-month]:pointer-events-none data-[outside-month]:opacity-50 [&[data-outside-month][data-selected]]:opacity-30",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { Calendar as CalendarPrimitive } from "bits-ui";
|
import { Calendar as CalendarPrimitive } from "bits-ui";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
...restProps
|
||||||
|
}: CalendarPrimitive.GridBodyProps = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<CalendarPrimitive.GridBody bind:ref class={cn(className)} {...restProps} />
|
<CalendarPrimitive.GridBody bind:ref class={cn(className)} {...restProps} />
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { Calendar as CalendarPrimitive } from "bits-ui";
|
import { Calendar as CalendarPrimitive } from "bits-ui";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
...restProps
|
||||||
|
}: CalendarPrimitive.GridHeadProps = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<CalendarPrimitive.GridHead bind:ref class={cn(className)} {...restProps} />
|
<CalendarPrimitive.GridHead bind:ref class={cn(className)} {...restProps} />
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { Calendar as CalendarPrimitive } from "bits-ui";
|
import { Calendar as CalendarPrimitive } from "bits-ui";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
...restProps
|
||||||
|
}: CalendarPrimitive.GridRowProps = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<CalendarPrimitive.GridRow bind:ref class={cn("flex", className)} {...restProps} />
|
<CalendarPrimitive.GridRow bind:ref class={cn("flex", className)} {...restProps} />
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { Calendar as CalendarPrimitive } from "bits-ui";
|
import { Calendar as CalendarPrimitive } from "bits-ui";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
...restProps
|
||||||
|
}: CalendarPrimitive.GridProps = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<CalendarPrimitive.Grid
|
<CalendarPrimitive.Grid
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { Calendar as CalendarPrimitive } from "bits-ui";
|
import { Calendar as CalendarPrimitive } from "bits-ui";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
...restProps
|
||||||
|
}: CalendarPrimitive.HeadCellProps = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<CalendarPrimitive.HeadCell
|
<CalendarPrimitive.HeadCell
|
||||||
bind:ref
|
bind:ref
|
||||||
class={cn("w-9 rounded-md text-[0.8rem] font-normal text-muted-foreground", className)}
|
class={cn("text-muted-foreground w-8 rounded-md text-[0.8rem] font-normal", className)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { Calendar as CalendarPrimitive } from "bits-ui";
|
import { Calendar as CalendarPrimitive } from "bits-ui";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
...restProps
|
||||||
|
}: CalendarPrimitive.HeaderProps = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<CalendarPrimitive.Header
|
<CalendarPrimitive.Header
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { Calendar as CalendarPrimitive } from "bits-ui";
|
import { Calendar as CalendarPrimitive } from "bits-ui";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
...restProps
|
||||||
|
}: CalendarPrimitive.HeadingProps = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<CalendarPrimitive.Heading bind:ref class={cn("text-sm font-medium", className)} {...restProps} />
|
<CalendarPrimitive.Heading bind:ref class={cn("text-sm font-medium", className)} {...restProps} />
|
||||||
|
|
|
@ -1,12 +1,18 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { cn } from "$lib/utils.js";
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
bind:this={ref}
|
bind:this={ref}
|
||||||
class={cn("mt-4 flex flex-col space-y-4 sm:flex-row sm:space-x-4 sm:space-y-0", className)}
|
class={cn("mt-4 flex flex-col space-y-4 sm:flex-row sm:space-y-0 sm:space-x-4", className)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
|
|
|
@ -1,14 +1,19 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { Calendar as CalendarPrimitive } from "bits-ui";
|
import { Calendar as CalendarPrimitive } from "bits-ui";
|
||||||
import ChevronRight from "@lucide/svelte/icons/chevron-right";
|
import ChevronRightIcon from "@lucide/svelte/icons/chevron-right";
|
||||||
import { buttonVariants } from "$lib/components/ui/button/index.js";
|
import { buttonVariants } from "$lib/components/ui/button/index.js";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: CalendarPrimitive.PrevButtonProps = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#snippet Fallback()}
|
{#snippet Fallback()}
|
||||||
<ChevronRight class="size-4" />
|
<ChevronRightIcon class="size-4" />
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|
||||||
<CalendarPrimitive.NextButton
|
<CalendarPrimitive.NextButton
|
||||||
|
|
|
@ -1,14 +1,19 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { Calendar as CalendarPrimitive } from "bits-ui";
|
import { Calendar as CalendarPrimitive } from "bits-ui";
|
||||||
import ChevronLeft from "@lucide/svelte/icons/chevron-left";
|
import ChevronLeftIcon from "@lucide/svelte/icons/chevron-left";
|
||||||
import { buttonVariants } from "$lib/components/ui/button/index.js";
|
import { buttonVariants } from "$lib/components/ui/button/index.js";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: CalendarPrimitive.PrevButtonProps = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#snippet Fallback()}
|
{#snippet Fallback()}
|
||||||
<ChevronLeft class="size-4" />
|
<ChevronLeftIcon class="size-4" />
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|
||||||
<CalendarPrimitive.PrevButton
|
<CalendarPrimitive.PrevButton
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { Calendar as CalendarPrimitive } from "bits-ui";
|
import { Calendar as CalendarPrimitive } from "bits-ui";
|
||||||
import * as Calendar from "./index.js";
|
import * as Calendar from "./index.js";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn, type WithoutChildrenOrChild } from "$lib/utils.js";
|
||||||
|
|
||||||
let {
|
let {
|
||||||
ref = $bindable(null),
|
ref = $bindable(null),
|
||||||
|
@ -10,7 +10,7 @@
|
||||||
class: className,
|
class: className,
|
||||||
weekdayFormat = "short",
|
weekdayFormat = "short",
|
||||||
...restProps
|
...restProps
|
||||||
} = $props();
|
}: WithoutChildrenOrChild<CalendarPrimitive.RootProps> = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
|
@ -18,7 +18,7 @@ Discriminated Unions + Destructing (required for bindable) do not
|
||||||
get along, so we shut typescript up by casting `value` to `never`.
|
get along, so we shut typescript up by casting `value` to `never`.
|
||||||
-->
|
-->
|
||||||
<CalendarPrimitive.Root
|
<CalendarPrimitive.Root
|
||||||
bind:value
|
bind:value={value as never}
|
||||||
bind:ref
|
bind:ref
|
||||||
bind:placeholder
|
bind:placeholder
|
||||||
{weekdayFormat}
|
{weekdayFormat}
|
||||||
|
|
|
@ -1,30 +1,30 @@
|
||||||
|
import Root from "./calendar.svelte";
|
||||||
import Cell from "./calendar-cell.svelte";
|
import Cell from "./calendar-cell.svelte";
|
||||||
import Day from "./calendar-day.svelte";
|
import Day from "./calendar-day.svelte";
|
||||||
|
import Grid from "./calendar-grid.svelte";
|
||||||
|
import Header from "./calendar-header.svelte";
|
||||||
|
import Months from "./calendar-months.svelte";
|
||||||
|
import GridRow from "./calendar-grid-row.svelte";
|
||||||
|
import Heading from "./calendar-heading.svelte";
|
||||||
import GridBody from "./calendar-grid-body.svelte";
|
import GridBody from "./calendar-grid-body.svelte";
|
||||||
import GridHead from "./calendar-grid-head.svelte";
|
import GridHead from "./calendar-grid-head.svelte";
|
||||||
import GridRow from "./calendar-grid-row.svelte";
|
|
||||||
import Grid from "./calendar-grid.svelte";
|
|
||||||
import HeadCell from "./calendar-head-cell.svelte";
|
import HeadCell from "./calendar-head-cell.svelte";
|
||||||
import Header from "./calendar-header.svelte";
|
|
||||||
import Heading from "./calendar-heading.svelte";
|
|
||||||
import Months from "./calendar-months.svelte";
|
|
||||||
import NextButton from "./calendar-next-button.svelte";
|
import NextButton from "./calendar-next-button.svelte";
|
||||||
import PrevButton from "./calendar-prev-button.svelte";
|
import PrevButton from "./calendar-prev-button.svelte";
|
||||||
import Root from "./calendar.svelte";
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
//
|
Day,
|
||||||
Root as Calendar,
|
Cell,
|
||||||
Cell,
|
Grid,
|
||||||
Day,
|
Header,
|
||||||
Grid,
|
Months,
|
||||||
GridBody,
|
GridRow,
|
||||||
GridHead,
|
Heading,
|
||||||
GridRow,
|
GridBody,
|
||||||
HeadCell,
|
GridHead,
|
||||||
Header,
|
HeadCell,
|
||||||
Heading,
|
NextButton,
|
||||||
Months,
|
PrevButton,
|
||||||
NextButton,
|
//
|
||||||
PrevButton,
|
Root as Calendar,
|
||||||
};
|
};
|
20
src/lib/components/ui/card/card-action.svelte
Normal file
20
src/lib/components/ui/card/card-action.svelte
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
bind:this={ref}
|
||||||
|
data-slot="card-action"
|
||||||
|
class={cn("col-start-2 row-span-2 row-start-1 self-start justify-self-end", className)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
|
@ -1,9 +1,15 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { cn } from "$lib/utils.js";
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div bind:this={ref} class={cn("p-6", className)} {...restProps}>
|
<div bind:this={ref} data-slot="card-content" class={cn("px-6", className)} {...restProps}>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,9 +1,20 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { cn } from "$lib/utils.js";
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLParagraphElement>> = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<p bind:this={ref} class={cn("text-sm text-muted-foreground", className)} {...restProps}>
|
<p
|
||||||
|
bind:this={ref}
|
||||||
|
data-slot="card-description"
|
||||||
|
class={cn("text-muted-foreground text-sm", className)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -1,9 +1,20 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div bind:this={ref} class={cn("flex items-center p-6 pt-0", className)} {...restProps}>
|
<div
|
||||||
|
bind:this={ref}
|
||||||
|
data-slot="card-footer"
|
||||||
|
class={cn("flex items-center px-6 [.border-t]:pt-6", className)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,9 +1,23 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div bind:this={ref} class={cn("flex flex-col space-y-1.5 p-6 pb-0", className)} {...restProps}>
|
<div
|
||||||
|
bind:this={ref}
|
||||||
|
data-slot="card-header"
|
||||||
|
class={cn(
|
||||||
|
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,14 +1,19 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { cn } from "$lib/utils.js";
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, level = 3, children, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
role="heading"
|
|
||||||
aria-level={level}
|
|
||||||
bind:this={ref}
|
bind:this={ref}
|
||||||
class={cn("text-2xl font-semibold leading-none tracking-tight", className)}
|
data-slot="card-title"
|
||||||
|
class={cn("leading-none font-semibold", className)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
|
|
|
@ -1,12 +1,22 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { cn } from "$lib/utils.js";
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
bind:this={ref}
|
bind:this={ref}
|
||||||
class={cn("rounded-lg border bg-card text-card-foreground shadow-sm", className)}
|
data-slot="card"
|
||||||
|
class={cn(
|
||||||
|
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
import Content from "./card-content.svelte";
|
|
||||||
import Description from "./card-description.svelte";
|
|
||||||
import Footer from "./card-footer.svelte";
|
|
||||||
import Header from "./card-header.svelte";
|
|
||||||
import Title from "./card-title.svelte";
|
|
||||||
import Root from "./card.svelte";
|
|
||||||
|
|
||||||
export {
|
|
||||||
//
|
|
||||||
Root as Card,
|
|
||||||
Content as CardContent,
|
|
||||||
Description as CardDescription,
|
|
||||||
Footer as CardFooter,
|
|
||||||
Header as CardHeader,
|
|
||||||
Title as CardTitle,
|
|
||||||
Content,
|
|
||||||
Description,
|
|
||||||
Footer,
|
|
||||||
Header,
|
|
||||||
Root,
|
|
||||||
Title,
|
|
||||||
};
|
|
25
src/lib/components/ui/card/index.ts
Normal file
25
src/lib/components/ui/card/index.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
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";
|
||||||
|
import Action from "./card-action.svelte";
|
||||||
|
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
Content,
|
||||||
|
Description,
|
||||||
|
Footer,
|
||||||
|
Header,
|
||||||
|
Title,
|
||||||
|
Action,
|
||||||
|
//
|
||||||
|
Root as Card,
|
||||||
|
Content as CardContent,
|
||||||
|
Description as CardDescription,
|
||||||
|
Footer as CardFooter,
|
||||||
|
Header as CardHeader,
|
||||||
|
Title as CardTitle,
|
||||||
|
Action as CardAction,
|
||||||
|
};
|
|
@ -1,15 +1,21 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import emblaCarouselSvelte from "embla-carousel-svelte";
|
import emblaCarouselSvelte from "embla-carousel-svelte";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
import { getEmblaContext } from "./context.js";
|
import { getEmblaContext } from "./context.js";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||||
|
|
||||||
const emblaCtx = getEmblaContext("<Carousel.Content/>");
|
const emblaCtx = getEmblaContext("<Carousel.Content/>");
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- svelte-ignore event_directive_deprecated -->
|
|
||||||
<div
|
<div
|
||||||
|
data-slot="carousel-content"
|
||||||
class="overflow-hidden"
|
class="overflow-hidden"
|
||||||
use:emblaCarouselSvelte={{
|
use:emblaCarouselSvelte={{
|
||||||
options: {
|
options: {
|
||||||
|
@ -20,7 +26,7 @@
|
||||||
},
|
},
|
||||||
plugins: emblaCtx.plugins,
|
plugins: emblaCtx.plugins,
|
||||||
}}
|
}}
|
||||||
on:emblaInit={emblaCtx.onInit}
|
onemblaInit={emblaCtx.onInit}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
bind:this={ref}
|
bind:this={ref}
|
||||||
|
|
|
@ -1,14 +1,21 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
import { getEmblaContext } from "./context.js";
|
import { getEmblaContext } from "./context.js";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||||
|
|
||||||
const emblaCtx = getEmblaContext("<Carousel.Item/>");
|
const emblaCtx = getEmblaContext("<Carousel.Item/>");
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
bind:this={ref}
|
bind:this={ref}
|
||||||
|
data-slot="carousel-item"
|
||||||
role="group"
|
role="group"
|
||||||
aria-roledescription="slide"
|
aria-roledescription="slide"
|
||||||
class={cn(
|
class={cn(
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import ArrowRight from "@lucide/svelte/icons/arrow-right";
|
import ArrowRightIcon from "@lucide/svelte/icons/arrow-right";
|
||||||
|
import type { WithoutChildren } from "bits-ui";
|
||||||
import { getEmblaContext } from "./context.js";
|
import { getEmblaContext } from "./context.js";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn } from "$lib/utils.js";
|
||||||
import { Button } from "$lib/components/ui/button/index.js";
|
import { Button, type Props } from "$lib/components/ui/button/index.js";
|
||||||
|
|
||||||
let {
|
let {
|
||||||
ref = $bindable(null),
|
ref = $bindable(null),
|
||||||
|
@ -10,18 +11,19 @@
|
||||||
variant = "outline",
|
variant = "outline",
|
||||||
size = "icon",
|
size = "icon",
|
||||||
...restProps
|
...restProps
|
||||||
} = $props();
|
}: WithoutChildren<Props> = $props();
|
||||||
|
|
||||||
const emblaCtx = getEmblaContext("<Carousel.Next/>");
|
const emblaCtx = getEmblaContext("<Carousel.Next/>");
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
data-slot="carousel-next"
|
||||||
{variant}
|
{variant}
|
||||||
{size}
|
{size}
|
||||||
class={cn(
|
class={cn(
|
||||||
"absolute size-8 touch-manipulation rounded-full",
|
"absolute size-8 rounded-full",
|
||||||
emblaCtx.orientation === "horizontal"
|
emblaCtx.orientation === "horizontal"
|
||||||
? "-right-12 top-1/2 -translate-y-1/2"
|
? "top-1/2 -right-12 -translate-y-1/2"
|
||||||
: "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
|
: "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
|
@ -31,6 +33,6 @@
|
||||||
bind:ref
|
bind:ref
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
<ArrowRight class="size-4" />
|
<ArrowRightIcon class="size-4" />
|
||||||
<span class="sr-only">Next slide</span>
|
<span class="sr-only">Next slide</span>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import ArrowLeft from "@lucide/svelte/icons/arrow-left";
|
import ArrowLeftIcon from "@lucide/svelte/icons/arrow-left";
|
||||||
|
import type { WithoutChildren } from "bits-ui";
|
||||||
import { getEmblaContext } from "./context.js";
|
import { getEmblaContext } from "./context.js";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn } from "$lib/utils.js";
|
||||||
import { Button } from "$lib/components/ui/button/index.js";
|
import { Button, type Props } from "$lib/components/ui/button/index.js";
|
||||||
|
|
||||||
let {
|
let {
|
||||||
ref = $bindable(null),
|
ref = $bindable(null),
|
||||||
|
@ -10,18 +11,19 @@
|
||||||
variant = "outline",
|
variant = "outline",
|
||||||
size = "icon",
|
size = "icon",
|
||||||
...restProps
|
...restProps
|
||||||
} = $props();
|
}: WithoutChildren<Props> = $props();
|
||||||
|
|
||||||
const emblaCtx = getEmblaContext("<Carousel.Previous/>");
|
const emblaCtx = getEmblaContext("<Carousel.Previous/>");
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
data-slot="carousel-previous"
|
||||||
{variant}
|
{variant}
|
||||||
{size}
|
{size}
|
||||||
class={cn(
|
class={cn(
|
||||||
"absolute size-8 touch-manipulation rounded-full",
|
"absolute size-8 rounded-full",
|
||||||
emblaCtx.orientation === "horizontal"
|
emblaCtx.orientation === "horizontal"
|
||||||
? "-left-12 top-1/2 -translate-y-1/2"
|
? "top-1/2 -left-12 -translate-y-1/2"
|
||||||
: "-top-12 left-1/2 -translate-x-1/2 rotate-90",
|
: "-top-12 left-1/2 -translate-x-1/2 rotate-90",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
|
@ -31,6 +33,6 @@
|
||||||
{...restProps}
|
{...restProps}
|
||||||
bind:ref
|
bind:ref
|
||||||
>
|
>
|
||||||
<ArrowLeft class="size-4" />
|
<ArrowLeftIcon class="size-4" />
|
||||||
<span class="sr-only">Previous slide</span>
|
<span class="sr-only">Previous slide</span>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { setEmblaContext } from "./context.js";
|
import {
|
||||||
import { cn } from "$lib/utils.js";
|
type CarouselAPI,
|
||||||
|
type CarouselProps,
|
||||||
|
type EmblaContext,
|
||||||
|
setEmblaContext,
|
||||||
|
} from "./context.js";
|
||||||
|
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||||
|
|
||||||
let {
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
opts = {},
|
opts = {},
|
||||||
plugins = [],
|
plugins = [],
|
||||||
setApi = () => {},
|
setApi = () => {},
|
||||||
|
@ -10,9 +16,9 @@
|
||||||
class: className,
|
class: className,
|
||||||
children,
|
children,
|
||||||
...restProps
|
...restProps
|
||||||
} = $props();
|
}: WithElementRef<CarouselProps> = $props();
|
||||||
|
|
||||||
let carouselState = $state({
|
let carouselState = $state<EmblaContext>({
|
||||||
api: undefined,
|
api: undefined,
|
||||||
scrollPrev,
|
scrollPrev,
|
||||||
scrollNext,
|
scrollNext,
|
||||||
|
@ -36,11 +42,11 @@
|
||||||
function scrollNext() {
|
function scrollNext() {
|
||||||
carouselState.api?.scrollNext();
|
carouselState.api?.scrollNext();
|
||||||
}
|
}
|
||||||
function scrollTo(index, jump?) {
|
function scrollTo(index: number, jump?: boolean) {
|
||||||
carouselState.api?.scrollTo(index, jump);
|
carouselState.api?.scrollTo(index, jump);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSelect(api) {
|
function onSelect(api: CarouselAPI) {
|
||||||
if (!api) return;
|
if (!api) return;
|
||||||
carouselState.canScrollPrev = api.canScrollPrev();
|
carouselState.canScrollPrev = api.canScrollPrev();
|
||||||
carouselState.canScrollNext = api.canScrollNext();
|
carouselState.canScrollNext = api.canScrollNext();
|
||||||
|
@ -55,7 +61,7 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function handleKeyDown(e) {
|
function handleKeyDown(e: KeyboardEvent) {
|
||||||
if (e.key === "ArrowLeft") {
|
if (e.key === "ArrowLeft") {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
scrollPrev();
|
scrollPrev();
|
||||||
|
@ -69,7 +75,7 @@
|
||||||
setApi(carouselState.api);
|
setApi(carouselState.api);
|
||||||
});
|
});
|
||||||
|
|
||||||
function onInit(event) {
|
function onInit(event: CustomEvent<CarouselAPI>) {
|
||||||
carouselState.api = event.detail;
|
carouselState.api = event.detail;
|
||||||
|
|
||||||
carouselState.scrollSnaps = carouselState.api.scrollSnapList();
|
carouselState.scrollSnaps = carouselState.api.scrollSnapList();
|
||||||
|
@ -82,6 +88,13 @@
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class={cn("relative", className)} role="region" aria-roledescription="carousel" {...restProps}>
|
<div
|
||||||
|
bind:this={ref}
|
||||||
|
data-slot="carousel"
|
||||||
|
class={cn("relative", className)}
|
||||||
|
role="region"
|
||||||
|
aria-roledescription="carousel"
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
{@render children?.()}
|
{@render children?.()}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
import { getContext, hasContext, setContext } from "svelte";
|
|
||||||
|
|
||||||
const EMBLA_CAROUSEL_CONTEXT = Symbol("EMBLA_CAROUSEL_CONTEXT");
|
|
||||||
|
|
||||||
export function setEmblaContext(config) {
|
|
||||||
setContext(EMBLA_CAROUSEL_CONTEXT, config);
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getEmblaContext(name = "This component") {
|
|
||||||
if (!hasContext(EMBLA_CAROUSEL_CONTEXT)) {
|
|
||||||
throw new Error(`${name} must be used within a <Carousel.Root> component`);
|
|
||||||
}
|
|
||||||
return getContext(EMBLA_CAROUSEL_CONTEXT);
|
|
||||||
}
|
|
56
src/lib/components/ui/carousel/context.ts
Normal file
56
src/lib/components/ui/carousel/context.ts
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
import type { WithElementRef } from "$lib/utils.js";
|
||||||
|
import type { EmblaCarouselSvelteType } from "embla-carousel-svelte";
|
||||||
|
import type emblaCarouselSvelte from "embla-carousel-svelte";
|
||||||
|
import { getContext, hasContext, setContext } from "svelte";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
|
||||||
|
export type CarouselAPI =
|
||||||
|
NonNullable<NonNullable<EmblaCarouselSvelteType["$$_attributes"]>["on:emblaInit"]> extends (
|
||||||
|
evt: CustomEvent<infer CarouselAPI>
|
||||||
|
) => void
|
||||||
|
? CarouselAPI
|
||||||
|
: never;
|
||||||
|
|
||||||
|
type EmblaCarouselConfig = NonNullable<Parameters<typeof emblaCarouselSvelte>[1]>;
|
||||||
|
|
||||||
|
export type CarouselOptions = EmblaCarouselConfig["options"];
|
||||||
|
export type CarouselPlugins = EmblaCarouselConfig["plugins"];
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
export type CarouselProps = {
|
||||||
|
opts?: CarouselOptions;
|
||||||
|
plugins?: CarouselPlugins;
|
||||||
|
setApi?: (api: CarouselAPI | undefined) => void;
|
||||||
|
orientation?: "horizontal" | "vertical";
|
||||||
|
} & WithElementRef<HTMLAttributes<HTMLDivElement>>;
|
||||||
|
|
||||||
|
const EMBLA_CAROUSEL_CONTEXT = Symbol("EMBLA_CAROUSEL_CONTEXT");
|
||||||
|
|
||||||
|
export type EmblaContext = {
|
||||||
|
api: CarouselAPI | undefined;
|
||||||
|
orientation: "horizontal" | "vertical";
|
||||||
|
scrollNext: () => void;
|
||||||
|
scrollPrev: () => void;
|
||||||
|
canScrollNext: boolean;
|
||||||
|
canScrollPrev: boolean;
|
||||||
|
handleKeyDown: (e: KeyboardEvent) => void;
|
||||||
|
options: CarouselOptions;
|
||||||
|
plugins: CarouselPlugins;
|
||||||
|
onInit: (e: CustomEvent<CarouselAPI>) => void;
|
||||||
|
scrollTo: (index: number, jump?: boolean) => void;
|
||||||
|
scrollSnaps: number[];
|
||||||
|
selectedIndex: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function setEmblaContext(config: EmblaContext): EmblaContext {
|
||||||
|
setContext(EMBLA_CAROUSEL_CONTEXT, config);
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getEmblaContext(name = "This component") {
|
||||||
|
if (!hasContext(EMBLA_CAROUSEL_CONTEXT)) {
|
||||||
|
throw new Error(`${name} must be used within a <Carousel.Root> component`);
|
||||||
|
}
|
||||||
|
return getContext<ReturnType<typeof setEmblaContext>>(EMBLA_CAROUSEL_CONTEXT);
|
||||||
|
}
|
|
@ -1,19 +1,19 @@
|
||||||
|
import Root from "./carousel.svelte";
|
||||||
import Content from "./carousel-content.svelte";
|
import Content from "./carousel-content.svelte";
|
||||||
import Item from "./carousel-item.svelte";
|
import Item from "./carousel-item.svelte";
|
||||||
import Next from "./carousel-next.svelte";
|
|
||||||
import Previous from "./carousel-previous.svelte";
|
import Previous from "./carousel-previous.svelte";
|
||||||
import Root from "./carousel.svelte";
|
import Next from "./carousel-next.svelte";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
//
|
Root,
|
||||||
Root as Carousel,
|
Content,
|
||||||
Content as CarouselContent,
|
Item,
|
||||||
Item as CarouselItem,
|
Previous,
|
||||||
Next as CarouselNext,
|
Next,
|
||||||
Previous as CarouselPrevious,
|
//
|
||||||
Content,
|
Root as Carousel,
|
||||||
Item,
|
Content as CarouselContent,
|
||||||
Next,
|
Item as CarouselItem,
|
||||||
Previous,
|
Previous as CarouselPrevious,
|
||||||
Root,
|
Next as CarouselNext,
|
||||||
};
|
};
|
81
src/lib/components/ui/chart/chart-container.svelte
Normal file
81
src/lib/components/ui/chart/chart-container.svelte
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { cn, type WithElementRef } from "$lib/utils.js";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import ChartStyle from "./chart-style.svelte";
|
||||||
|
import { setChartContext, type ChartConfig } from "./chart-utils.js";
|
||||||
|
|
||||||
|
const uid = $props.id();
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
id = uid,
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
config,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLElement>> & {
|
||||||
|
config: ChartConfig;
|
||||||
|
} = $props();
|
||||||
|
|
||||||
|
const chartId = `chart-${id || uid.replace(/:/g, "")}`;
|
||||||
|
|
||||||
|
setChartContext({
|
||||||
|
get config() {
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
bind:this={ref}
|
||||||
|
data-chart={chartId}
|
||||||
|
data-slot="chart"
|
||||||
|
class={cn(
|
||||||
|
// "flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-none [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none",
|
||||||
|
"flex aspect-video justify-center overflow-visible text-xs",
|
||||||
|
// Overrides
|
||||||
|
//
|
||||||
|
// Stroke around dots/marks when hovering
|
||||||
|
"[&_.stroke-white]:stroke-transparent",
|
||||||
|
// override the default stroke color of lines
|
||||||
|
"[&_.lc-line]:stroke-border/50",
|
||||||
|
|
||||||
|
// by default, layerchart shows a line intersecting the point when hovering, this hides that
|
||||||
|
"[&_.lc-highlight-line]:stroke-0",
|
||||||
|
|
||||||
|
// by default, when you hover a point on a stacked series chart, it will drop the opacity
|
||||||
|
// of the other series, this overrides that
|
||||||
|
"[&_.lc-area-path]:opacity-100 [&_.lc-highlight-line]:opacity-100 [&_.lc-highlight-point]:opacity-100 [&_.lc-spline-path]:opacity-100 [&_.lc-text]:text-xs",
|
||||||
|
|
||||||
|
// We don't want the little tick lines between the axis labels and the chart, so we remove
|
||||||
|
// the stroke. The alternative is to manually disable `tickMarks` on the x/y axis of every
|
||||||
|
// chart.
|
||||||
|
"[&_.lc-axis-tick]:stroke-0",
|
||||||
|
|
||||||
|
// We don't want to display the rule on the x/y axis, as there is already going to be
|
||||||
|
// a grid line there and rule ends up overlapping the marks because it is rendered after
|
||||||
|
// the marks
|
||||||
|
"[&_.lc-rule-x-line:not(.lc-grid-x-rule)]:stroke-0 [&_.lc-rule-y-line:not(.lc-grid-y-rule)]:stroke-0",
|
||||||
|
"[&_.lc-grid-x-radial-line]:stroke-border [&_.lc-grid-x-radial-circle]:stroke-border",
|
||||||
|
"[&_.lc-grid-y-radial-line]:stroke-border [&_.lc-grid-y-radial-circle]:stroke-border",
|
||||||
|
|
||||||
|
// Legend adjustments
|
||||||
|
"[&_.lc-legend-swatch-button]:items-center [&_.lc-legend-swatch-button]:gap-1.5",
|
||||||
|
"[&_.lc-legend-swatch-group]:items-center [&_.lc-legend-swatch-group]:gap-4",
|
||||||
|
"[&_.lc-legend-swatch]:size-2.5 [&_.lc-legend-swatch]:rounded-[2px]",
|
||||||
|
|
||||||
|
// Labels
|
||||||
|
"[&_.lc-labels-text:not([fill])]:fill-foreground [&_text]:stroke-transparent",
|
||||||
|
|
||||||
|
// Tick labels on th x/y axes
|
||||||
|
"[&_.lc-axis-tick-label]:fill-muted-foreground [&_.lc-axis-tick-label]:font-normal",
|
||||||
|
"[&_.lc-tooltip-rects-g]:fill-transparent",
|
||||||
|
"[&_.lc-layout-svg-g]:fill-transparent",
|
||||||
|
"[&_.lc-root-container]:w-full",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
<ChartStyle id={chartId} {config} />
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
36
src/lib/components/ui/chart/chart-style.svelte
Normal file
36
src/lib/components/ui/chart/chart-style.svelte
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { THEMES, type ChartConfig } from "./chart-utils.js";
|
||||||
|
|
||||||
|
let { id, config }: { id: string; config: ChartConfig } = $props();
|
||||||
|
|
||||||
|
const colorConfig = $derived(
|
||||||
|
config ? Object.entries(config).filter(([, config]) => config.theme || config.color) : null,
|
||||||
|
);
|
||||||
|
|
||||||
|
const styleOpen = ">elyts<".split("").reverse().join("");
|
||||||
|
const styleClose = ">elyts/<".split("").reverse().join("");
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if colorConfig && colorConfig.length}
|
||||||
|
{@const themeContents = Object.entries(THEMES)
|
||||||
|
.map(
|
||||||
|
([theme, prefix]) => `
|
||||||
|
${prefix} [data-chart=${id}] {
|
||||||
|
${colorConfig
|
||||||
|
.map(([key, itemConfig]) => {
|
||||||
|
const color = itemConfig.theme?.[theme as keyof typeof itemConfig.theme] || itemConfig.color;
|
||||||
|
return color ? ` --color-${key}: ${color};` : null;
|
||||||
|
})
|
||||||
|
.join("\n")}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
)
|
||||||
|
.join("\n")}
|
||||||
|
|
||||||
|
{#key id}
|
||||||
|
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
||||||
|
{@html `${styleOpen}
|
||||||
|
${themeContents}
|
||||||
|
${styleClose}`}
|
||||||
|
{/key}
|
||||||
|
{/if}
|
155
src/lib/components/ui/chart/chart-tooltip.svelte
Normal file
155
src/lib/components/ui/chart/chart-tooltip.svelte
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { cn, type WithElementRef, type WithoutChildren } from "$lib/utils.js";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { getPayloadConfigFromPayload, useChart, type TooltipPayload } from "./chart-utils.js";
|
||||||
|
import { getTooltipContext, Tooltip as TooltipPrimitive } from "layerchart";
|
||||||
|
import type { Snippet } from "svelte";
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
function defaultFormatter(value: any, _payload: TooltipPayload[]) {
|
||||||
|
return `${value}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
hideLabel = false,
|
||||||
|
indicator = "dot",
|
||||||
|
hideIndicator = false,
|
||||||
|
labelKey,
|
||||||
|
label,
|
||||||
|
labelFormatter = defaultFormatter,
|
||||||
|
labelClassName,
|
||||||
|
formatter,
|
||||||
|
nameKey,
|
||||||
|
color,
|
||||||
|
...restProps
|
||||||
|
}: WithoutChildren<WithElementRef<HTMLAttributes<HTMLDivElement>>> & {
|
||||||
|
hideLabel?: boolean;
|
||||||
|
label?: string;
|
||||||
|
indicator?: "line" | "dot" | "dashed";
|
||||||
|
nameKey?: string;
|
||||||
|
labelKey?: string;
|
||||||
|
hideIndicator?: boolean;
|
||||||
|
labelClassName?: string;
|
||||||
|
labelFormatter?: // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
((value: any, payload: TooltipPayload[]) => string | number | Snippet) | null;
|
||||||
|
formatter?: Snippet<
|
||||||
|
[
|
||||||
|
{
|
||||||
|
value: unknown;
|
||||||
|
name: string;
|
||||||
|
item: TooltipPayload;
|
||||||
|
index: number;
|
||||||
|
payload: TooltipPayload[];
|
||||||
|
},
|
||||||
|
]
|
||||||
|
>;
|
||||||
|
} = $props();
|
||||||
|
|
||||||
|
const chart = useChart();
|
||||||
|
const tooltipCtx = getTooltipContext();
|
||||||
|
|
||||||
|
const formattedLabel = $derived.by(() => {
|
||||||
|
if (hideLabel || !tooltipCtx.payload?.length) return null;
|
||||||
|
|
||||||
|
const [item] = tooltipCtx.payload;
|
||||||
|
const key = labelKey || item?.label || item?.name || "value";
|
||||||
|
|
||||||
|
const itemConfig = getPayloadConfigFromPayload(chart.config, item, key);
|
||||||
|
|
||||||
|
const value =
|
||||||
|
!labelKey && typeof label === "string"
|
||||||
|
? chart.config[label as keyof typeof chart.config]?.label || label
|
||||||
|
: (itemConfig?.label ?? item.label);
|
||||||
|
|
||||||
|
if (!value) return null;
|
||||||
|
if (!labelFormatter) return value;
|
||||||
|
return labelFormatter(value, tooltipCtx.payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
const nestLabel = $derived(tooltipCtx.payload.length === 1 && indicator !== "dot");
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#snippet TooltipLabel()}
|
||||||
|
{#if formattedLabel}
|
||||||
|
<div class={cn("font-medium", labelClassName)}>
|
||||||
|
{#if typeof formattedLabel === "function"}
|
||||||
|
{@render formattedLabel()}
|
||||||
|
{:else}
|
||||||
|
{formattedLabel}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/snippet}
|
||||||
|
|
||||||
|
<TooltipPrimitive.Root variant="none">
|
||||||
|
<div
|
||||||
|
class={cn(
|
||||||
|
"border-border/50 bg-background grid min-w-[9rem] items-start gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs shadow-xl",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{#if !nestLabel}
|
||||||
|
{@render TooltipLabel()}
|
||||||
|
{/if}
|
||||||
|
<div class="grid gap-1.5">
|
||||||
|
{#each tooltipCtx.payload as item, i (item.key + i)}
|
||||||
|
{@const key = `${nameKey || item.key || item.name || "value"}`}
|
||||||
|
{@const itemConfig = getPayloadConfigFromPayload(chart.config, item, key)}
|
||||||
|
{@const indicatorColor = color || item.payload?.color || item.color}
|
||||||
|
<div
|
||||||
|
class={cn(
|
||||||
|
"[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:size-2.5",
|
||||||
|
indicator === "dot" && "items-center",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{#if formatter && item.value !== undefined && item.name}
|
||||||
|
{@render formatter({
|
||||||
|
value: item.value,
|
||||||
|
name: item.name,
|
||||||
|
item,
|
||||||
|
index: i,
|
||||||
|
payload: tooltipCtx.payload,
|
||||||
|
})}
|
||||||
|
{:else}
|
||||||
|
{#if itemConfig?.icon}
|
||||||
|
<itemConfig.icon />
|
||||||
|
{:else if !hideIndicator}
|
||||||
|
<div
|
||||||
|
style="--color-bg: {indicatorColor}; --color-border: {indicatorColor};"
|
||||||
|
class={cn("shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)", {
|
||||||
|
"size-2.5": indicator === "dot",
|
||||||
|
"h-full w-1": indicator === "line",
|
||||||
|
"w-0 border-[1.5px] border-dashed bg-transparent": indicator === "dashed",
|
||||||
|
"my-0.5": nestLabel && indicator === "dashed",
|
||||||
|
})}
|
||||||
|
></div>
|
||||||
|
{/if}
|
||||||
|
<div
|
||||||
|
class={cn(
|
||||||
|
"flex flex-1 shrink-0 justify-between leading-none",
|
||||||
|
nestLabel ? "items-end" : "items-center",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div class="grid gap-1.5">
|
||||||
|
{#if nestLabel}
|
||||||
|
{@render TooltipLabel()}
|
||||||
|
{/if}
|
||||||
|
<span class="text-muted-foreground">
|
||||||
|
{itemConfig?.label || item.name}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{#if item.value}
|
||||||
|
<span class="text-foreground font-mono font-medium tabular-nums">
|
||||||
|
{item.value.toLocaleString()}
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</TooltipPrimitive.Root>
|
66
src/lib/components/ui/chart/chart-utils.ts
Normal file
66
src/lib/components/ui/chart/chart-utils.ts
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
import type { Tooltip } from "layerchart";
|
||||||
|
import { getContext, setContext, type Component, type ComponentProps, type Snippet } from "svelte";
|
||||||
|
|
||||||
|
export const THEMES = { light: "", dark: ".dark" } as const;
|
||||||
|
|
||||||
|
export type ChartConfig = {
|
||||||
|
[k in string]: {
|
||||||
|
label?: string;
|
||||||
|
icon?: Component;
|
||||||
|
} & (
|
||||||
|
| { color?: string; theme?: never }
|
||||||
|
| { color?: never; theme: Record<keyof typeof THEMES, string> }
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ExtractSnippetParams<T> = T extends Snippet<[infer P]> ? P : never;
|
||||||
|
|
||||||
|
export type TooltipPayload = ExtractSnippetParams<
|
||||||
|
ComponentProps<typeof Tooltip.Root>["children"]
|
||||||
|
>["payload"][number];
|
||||||
|
|
||||||
|
// Helper to extract item config from a payload.
|
||||||
|
export function getPayloadConfigFromPayload(
|
||||||
|
config: ChartConfig,
|
||||||
|
payload: TooltipPayload,
|
||||||
|
key: string
|
||||||
|
) {
|
||||||
|
if (typeof payload !== "object" || payload === null) return undefined;
|
||||||
|
|
||||||
|
const payloadPayload =
|
||||||
|
"payload" in payload && typeof payload.payload === "object" && payload.payload !== null
|
||||||
|
? payload.payload
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
let configLabelKey: string = key;
|
||||||
|
|
||||||
|
if (payload.key === key) {
|
||||||
|
configLabelKey = payload.key;
|
||||||
|
} else if (payload.name === key) {
|
||||||
|
configLabelKey = payload.name;
|
||||||
|
} else if (key in payload && typeof payload[key as keyof typeof payload] === "string") {
|
||||||
|
configLabelKey = payload[key as keyof typeof payload] as string;
|
||||||
|
} else if (
|
||||||
|
payloadPayload &&
|
||||||
|
key in payloadPayload &&
|
||||||
|
typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
|
||||||
|
) {
|
||||||
|
configLabelKey = payloadPayload[key as keyof typeof payloadPayload] as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
return configLabelKey in config ? config[configLabelKey] : config[key as keyof typeof config];
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChartContextValue = {
|
||||||
|
config: ChartConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
const chartContextKey = Symbol("chart-context");
|
||||||
|
|
||||||
|
export function setChartContext(value: ChartContextValue) {
|
||||||
|
return setContext(chartContextKey, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useChart() {
|
||||||
|
return getContext<ChartContextValue>(chartContextKey);
|
||||||
|
}
|
6
src/lib/components/ui/chart/index.ts
Normal file
6
src/lib/components/ui/chart/index.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import ChartContainer from "./chart-container.svelte";
|
||||||
|
import ChartTooltip from "./chart-tooltip.svelte";
|
||||||
|
|
||||||
|
export { getPayloadConfigFromPayload, type ChartConfig } from "./chart-utils.js";
|
||||||
|
|
||||||
|
export { ChartContainer, ChartTooltip, ChartContainer as Container, ChartTooltip as Tooltip };
|
|
@ -1,8 +1,8 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { Checkbox as CheckboxPrimitive } from "bits-ui";
|
import { Checkbox as CheckboxPrimitive } from "bits-ui";
|
||||||
import Check from "@lucide/svelte/icons/check";
|
import CheckIcon from "@lucide/svelte/icons/check";
|
||||||
import Minus from "@lucide/svelte/icons/minus";
|
import MinusIcon from "@lucide/svelte/icons/minus";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn, type WithoutChildrenOrChild } from "$lib/utils.js";
|
||||||
|
|
||||||
let {
|
let {
|
||||||
ref = $bindable(null),
|
ref = $bindable(null),
|
||||||
|
@ -10,13 +10,14 @@
|
||||||
indeterminate = $bindable(false),
|
indeterminate = $bindable(false),
|
||||||
class: className,
|
class: className,
|
||||||
...restProps
|
...restProps
|
||||||
} = $props();
|
}: WithoutChildrenOrChild<CheckboxPrimitive.RootProps> = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<CheckboxPrimitive.Root
|
<CheckboxPrimitive.Root
|
||||||
bind:ref
|
bind:ref
|
||||||
|
data-slot="checkbox"
|
||||||
class={cn(
|
class={cn(
|
||||||
"peer box-content size-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[disabled=true]:cursor-not-allowed data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[disabled=true]:opacity-50",
|
"border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive peer size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
bind:checked
|
bind:checked
|
||||||
|
@ -24,11 +25,14 @@
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
{#snippet children({ checked, indeterminate })}
|
{#snippet children({ checked, indeterminate })}
|
||||||
<div class="flex size-4 items-center justify-center text-current">
|
<div
|
||||||
{#if indeterminate}
|
data-slot="checkbox-indicator"
|
||||||
<Minus class="size-3.5" />
|
class="flex items-center justify-center text-current transition-none"
|
||||||
{:else}
|
>
|
||||||
<Check class={cn("size-3.5", !checked && "text-transparent")} />
|
{#if checked}
|
||||||
|
<CheckIcon class="size-3.5" />
|
||||||
|
{:else if indeterminate}
|
||||||
|
<MinusIcon class="size-3.5" />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import Root from "./checkbox.svelte";
|
import Root from "./checkbox.svelte";
|
||||||
export {
|
export {
|
||||||
//
|
Root,
|
||||||
Root as Checkbox,
|
//
|
||||||
Root,
|
Root as Checkbox,
|
||||||
};
|
};
|
|
@ -0,0 +1,7 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Collapsible as CollapsiblePrimitive } from "bits-ui";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), ...restProps }: CollapsiblePrimitive.ContentProps = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CollapsiblePrimitive.Content bind:ref data-slot="collapsible-content" {...restProps} />
|
|
@ -0,0 +1,7 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Collapsible as CollapsiblePrimitive } from "bits-ui";
|
||||||
|
|
||||||
|
let { ref = $bindable(null), ...restProps }: CollapsiblePrimitive.TriggerProps = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CollapsiblePrimitive.Trigger bind:ref data-slot="collapsible-trigger" {...restProps} />
|
11
src/lib/components/ui/collapsible/collapsible.svelte
Normal file
11
src/lib/components/ui/collapsible/collapsible.svelte
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Collapsible as CollapsiblePrimitive } from "bits-ui";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
open = $bindable(false),
|
||||||
|
...restProps
|
||||||
|
}: CollapsiblePrimitive.RootProps = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<CollapsiblePrimitive.Root bind:ref data-slot="collapsible" {...restProps} />
|
|
@ -1,15 +0,0 @@
|
||||||
import { Collapsible as CollapsiblePrimitive } from "bits-ui";
|
|
||||||
|
|
||||||
const Root = CollapsiblePrimitive.Root;
|
|
||||||
const Trigger = CollapsiblePrimitive.Trigger;
|
|
||||||
const Content = CollapsiblePrimitive.Content;
|
|
||||||
|
|
||||||
export {
|
|
||||||
//
|
|
||||||
Root as Collapsible,
|
|
||||||
Content as CollapsibleContent,
|
|
||||||
Trigger as CollapsibleTrigger,
|
|
||||||
Content,
|
|
||||||
Root,
|
|
||||||
Trigger,
|
|
||||||
};
|
|
|
@ -5,11 +5,11 @@ const Trigger = CollapsiblePrimitive.Trigger;
|
||||||
const Content = CollapsiblePrimitive.Content;
|
const Content = CollapsiblePrimitive.Content;
|
||||||
|
|
||||||
export {
|
export {
|
||||||
//
|
Root,
|
||||||
Root as Collapsible,
|
Content,
|
||||||
Content as CollapsibleContent,
|
Trigger,
|
||||||
Trigger as CollapsibleTrigger,
|
//
|
||||||
Content,
|
Root as Collapsible,
|
||||||
Root,
|
Content as CollapsibleContent,
|
||||||
Trigger,
|
Trigger as CollapsibleTrigger,
|
||||||
};
|
};
|
|
@ -1,21 +1,36 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
|
import type { Command as CommandPrimitive, Dialog as DialogPrimitive } from "bits-ui";
|
||||||
|
import type { Snippet } from "svelte";
|
||||||
import Command from "./command.svelte";
|
import Command from "./command.svelte";
|
||||||
import * as Dialog from "$lib/components/ui/dialog/index.js";
|
import * as Dialog from "$lib/components/ui/dialog/index.js";
|
||||||
|
import type { WithoutChildrenOrChild } from "$lib/utils.js";
|
||||||
|
|
||||||
let {
|
let {
|
||||||
open = $bindable(false),
|
open = $bindable(false),
|
||||||
ref = $bindable(null),
|
ref = $bindable(null),
|
||||||
value = $bindable(""),
|
value = $bindable(""),
|
||||||
|
title = "Command Palette",
|
||||||
|
description = "Search for a command to run",
|
||||||
portalProps,
|
portalProps,
|
||||||
children,
|
children,
|
||||||
...restProps
|
...restProps
|
||||||
} = $props();
|
}: WithoutChildrenOrChild<DialogPrimitive.RootProps> &
|
||||||
|
WithoutChildrenOrChild<CommandPrimitive.RootProps> & {
|
||||||
|
portalProps?: DialogPrimitive.PortalProps;
|
||||||
|
children: Snippet;
|
||||||
|
title?: string;
|
||||||
|
description?: string;
|
||||||
|
} = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Dialog.Root bind:open {...restProps}>
|
<Dialog.Root bind:open {...restProps}>
|
||||||
<Dialog.Content class="overflow-hidden p-0 shadow-lg" {portalProps}>
|
<Dialog.Header class="sr-only">
|
||||||
|
<Dialog.Title>{title}</Dialog.Title>
|
||||||
|
<Dialog.Description>{description}</Dialog.Description>
|
||||||
|
</Dialog.Header>
|
||||||
|
<Dialog.Content class="overflow-hidden p-0" {portalProps}>
|
||||||
<Command
|
<Command
|
||||||
class="[&_[data-command-group]:not([hidden])_~[data-command-group]]:pt-0 [&_[data-command-group]]:px-2 [&_[data-command-input-wrapper]_svg]:h-5 [&_[data-command-input-wrapper]_svg]:w-5 [&_[data-command-input]]:h-12 [&_[data-command-item]]:px-2 [&_[data-command-item]]:py-3 [&_[data-command-item]_svg]:h-5 [&_[data-command-item]_svg]:w-5"
|
class="**:data-[slot=command-input-wrapper]:h-12 [&_[data-command-group]]:px-2 [&_[data-command-group]:not([hidden])_~[data-command-group]]:pt-0 [&_[data-command-input-wrapper]_svg]:h-5 [&_[data-command-input-wrapper]_svg]:w-5 [&_[data-command-input]]:h-12 [&_[data-command-item]]:px-2 [&_[data-command-item]]:py-3 [&_[data-command-item]_svg]:h-5 [&_[data-command-item]_svg]:w-5"
|
||||||
{...restProps}
|
{...restProps}
|
||||||
bind:value
|
bind:value
|
||||||
bind:ref
|
bind:ref
|
||||||
|
|
|
@ -1,8 +1,17 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { Command as CommandPrimitive } from "bits-ui";
|
import { Command as CommandPrimitive } from "bits-ui";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
...restProps
|
||||||
|
}: CommandPrimitive.EmptyProps = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<CommandPrimitive.Empty class={cn("py-6 text-center text-sm", className)} {...restProps} />
|
<CommandPrimitive.Empty
|
||||||
|
bind:ref
|
||||||
|
data-slot="command-empty"
|
||||||
|
class={cn("py-6 text-center text-sm", className)}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { Command as CommandPrimitive, useId } from "bits-ui";
|
import { Command as CommandPrimitive, useId } from "bits-ui";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
@ -9,17 +9,20 @@
|
||||||
heading,
|
heading,
|
||||||
value,
|
value,
|
||||||
...restProps
|
...restProps
|
||||||
|
}: CommandPrimitive.GroupProps & {
|
||||||
|
heading?: string;
|
||||||
} = $props();
|
} = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<CommandPrimitive.Group
|
<CommandPrimitive.Group
|
||||||
class={cn("overflow-hidden p-1 text-foreground", className)}
|
|
||||||
bind:ref
|
bind:ref
|
||||||
|
data-slot="command-group"
|
||||||
|
class={cn("text-foreground overflow-hidden p-1", className)}
|
||||||
value={value ?? heading ?? `----${useId()}`}
|
value={value ?? heading ?? `----${useId()}`}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
>
|
>
|
||||||
{#if heading}
|
{#if heading}
|
||||||
<CommandPrimitive.GroupHeading class="px-2 py-1.5 text-xs font-medium text-muted-foreground">
|
<CommandPrimitive.GroupHeading class="text-muted-foreground px-2 py-1.5 text-xs font-medium">
|
||||||
{heading}
|
{heading}
|
||||||
</CommandPrimitive.GroupHeading>
|
</CommandPrimitive.GroupHeading>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -1,16 +1,22 @@
|
||||||
<script>
|
<script lang="ts">
|
||||||
import { Command as CommandPrimitive } from "bits-ui";
|
import { Command as CommandPrimitive } from "bits-ui";
|
||||||
import Search from "@lucide/svelte/icons/search";
|
import SearchIcon from "@lucide/svelte/icons/search";
|
||||||
import { cn } from "$lib/utils.js";
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
let { ref = $bindable(null), class: className, value = $bindable(""), ...restProps } = $props();
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
value = $bindable(""),
|
||||||
|
...restProps
|
||||||
|
}: CommandPrimitive.InputProps = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex items-center border-b px-2" data-command-input-wrapper="">
|
<div class="flex h-9 items-center gap-2 border-b px-3" data-slot="command-input-wrapper">
|
||||||
<Search class="mr-2 size-4 shrink-0 opacity-50" />
|
<SearchIcon class="size-4 shrink-0 opacity-50" />
|
||||||
<CommandPrimitive.Input
|
<CommandPrimitive.Input
|
||||||
|
data-slot="command-input"
|
||||||
class={cn(
|
class={cn(
|
||||||
"flex h-11 w-full rounded-md bg-transparent py-3 text-base outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
"placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
bind:ref
|
bind:ref
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue