Compare commits

..

2 Commits

Author SHA1 Message Date
kdaniel2410 8450712afa feat: manage invoice lifecycle 2026-06-25 11:40:13 +01:00
kdaniel2410 db7fee0432 feat: add invoice lifecycle schema 2026-06-25 11:40:05 +01:00
457 changed files with 2551 additions and 3570 deletions
+3
View File
@@ -1,3 +1,6 @@
# Drizzle
DATABASE_URL=file:local.db
ORIGIN="" ORIGIN=""
# Better Auth # Better Auth
+22 -16
View File
@@ -2,7 +2,7 @@
Clearity is a workspace, client, and contract management platform for managed office operations. It tracks clients, contacts, addresses, rooms, services, bookings, contracts, invoices, and workspace admins from a SvelteKit dashboard. Clearity is a workspace, client, and contract management platform for managed office operations. It tracks clients, contacts, addresses, rooms, services, bookings, contracts, invoices, and workspace admins from a SvelteKit dashboard.
The app is built with SvelteKit, Svelte 5, shadcn-svelte, Superforms, Better Auth, Drizzle ORM, Cloudflare D1, and Cloudflare Workers. The app is built with SvelteKit, Svelte 5, shadcn-svelte, Superforms, Better Auth, Drizzle ORM, SQLite/libSQL, and Cloudflare Workers.
## Development ## Development
@@ -15,6 +15,7 @@ pnpm install
Create a local `.env` file: Create a local `.env` file:
```sh ```sh
DATABASE_URL=file:local.db
ORIGIN=http://localhost:5173 ORIGIN=http://localhost:5173
BETTER_AUTH_SECRET=replace-with-a-long-random-secret BETTER_AUTH_SECRET=replace-with-a-long-random-secret
``` ```
@@ -28,7 +29,7 @@ openssl rand -base64 32
Apply database migrations: Apply database migrations:
```sh ```sh
pnpm db:migrate:local DATABASE_URL=file:local.db pnpm db:migrate
``` ```
Start the development server: Start the development server:
@@ -46,36 +47,34 @@ pnpm check # Wrangler types, SvelteKit sync, and svelte-check
pnpm lint # Prettier check and ESLint pnpm lint # Prettier check and ESLint
pnpm format # Format the codebase pnpm format # Format the codebase
pnpm db:generate # Generate Drizzle migrations after schema changes pnpm db:generate # Generate Drizzle migrations after schema changes
pnpm db:migrate:local # Apply migrations to the local D1 database pnpm db:migrate # Apply Drizzle migrations to DATABASE_URL
pnpm db:migrate:remote # Apply migrations to the remote D1 database pnpm db:studio # Open Drizzle Studio
pnpm gen # Regenerate Cloudflare Worker types pnpm gen # Regenerate Cloudflare Worker types
``` ```
## Environment Variables And Bindings ## Environment Variables
Required: Required:
- `DATABASE_URL`: SQLite/libSQL connection URL. Use `file:local.db` for local development.
- `ORIGIN`: Public app origin, for example `http://localhost:5173` locally or `https://clearity.example.com` in production. - `ORIGIN`: Public app origin, for example `http://localhost:5173` locally or `https://clearity.example.com` in production.
- `BETTER_AUTH_SECRET`: Secret used by Better Auth. - `BETTER_AUTH_SECRET`: Secret used by Better Auth.
- Cloudflare D1 binding `DB`, configured in `wrangler.jsonc`.
Local development uses the same `DB` binding shape as production. SvelteKit's Cloudflare adapter populates `platform.env.DB` from `wrangler.jsonc` during dev and preview, and Wrangler stores local D1 data in its local state directory.
## Database ## Database
The application schema lives in `src/lib/server/db`. Drizzle migration files live in `drizzle`. The application schema lives in `src/lib/server/db`. Drizzle migration files live in `drizzle`.
Clearity always uses Drizzle's Cloudflare D1 driver through the `DB` binding. Apply migrations to the local D1 database before starting the app: Local development currently uses the libSQL client through `DATABASE_URL`, so the fastest local database is a SQLite file:
```sh ```sh
pnpm db:migrate:local DATABASE_URL=file:local.db pnpm db:migrate
``` ```
For schema changes: For schema changes:
```sh ```sh
pnpm db:generate pnpm db:generate
pnpm db:migrate:local DATABASE_URL=file:local.db pnpm db:migrate
``` ```
## Production Deployment ## Production Deployment
@@ -92,10 +91,10 @@ pnpm wrangler whoami
Create a production D1 database: Create a production D1 database:
```sh ```sh
pnpm wrangler d1 create clearity-production --binding DB pnpm wrangler d1 create clearity-production
``` ```
Wrangler prints a `database_id`. Replace the placeholder in `wrangler.jsonc`: Wrangler prints a `database_id`. Add it to `wrangler.jsonc`:
```jsonc ```jsonc
{ {
@@ -103,8 +102,7 @@ Wrangler prints a `database_id`. Replace the placeholder in `wrangler.jsonc`:
{ {
"binding": "DB", "binding": "DB",
"database_name": "clearity-production", "database_name": "clearity-production",
"database_id": "<database_id>", "database_id": "<database_id>"
"migrations_dir": "drizzle"
} }
] ]
} }
@@ -119,7 +117,9 @@ pnpm gen
Apply SQL migrations to the remote D1 database: Apply SQL migrations to the remote D1 database:
```sh ```sh
pnpm wrangler d1 migrations apply clearity-production --remote for file in drizzle/*.sql; do
pnpm wrangler d1 execute clearity-production --remote --file "$file"
done
``` ```
Set production secrets: Set production secrets:
@@ -129,6 +129,12 @@ pnpm wrangler secret put ORIGIN
pnpm wrangler secret put BETTER_AUTH_SECRET pnpm wrangler secret put BETTER_AUTH_SECRET
``` ```
This codebase currently reads the database through `DATABASE_URL`. If production is deployed against Cloudflare D1, wire the runtime database client to the `DB` D1 binding before deploying. If production is deployed against a hosted libSQL database instead, set `DATABASE_URL` as a Worker secret:
```sh
pnpm wrangler secret put DATABASE_URL
```
Build and deploy: Build and deploy:
```sh ```sh
+3
View File
@@ -1,8 +1,11 @@
import { defineConfig } from 'drizzle-kit'; import { defineConfig } from 'drizzle-kit';
if (!process.env.DATABASE_URL) throw new Error('DATABASE_URL is not set');
export default defineConfig({ export default defineConfig({
schema: './src/lib/server/db/schema.ts', schema: './src/lib/server/db/schema.ts',
dialect: 'sqlite', dialect: 'sqlite',
dbCredentials: { url: process.env.DATABASE_URL },
verbose: true, verbose: true,
strict: true strict: true
}); });
+4 -6
View File
@@ -13,9 +13,9 @@
"lint": "prettier --check . && eslint .", "lint": "prettier --check . && eslint .",
"format": "prettier --write .", "format": "prettier --write .",
"gen": "wrangler types", "gen": "wrangler types",
"db:push": "drizzle-kit push",
"db:generate": "drizzle-kit generate", "db:generate": "drizzle-kit generate",
"db:migrate:local": "wrangler d1 migrations apply clearity-production --local", "db:migrate": "drizzle-kit migrate",
"db:migrate:remote": "wrangler d1 migrations apply clearity-production --remote",
"auth:schema": "better-auth generate --config src/lib/server/auth.ts --output src/lib/server/db/auth.schema.ts --yes" "auth:schema": "better-auth generate --config src/lib/server/auth.ts --output src/lib/server/db/auth.schema.ts --yes"
}, },
"devDependencies": { "devDependencies": {
@@ -24,7 +24,8 @@
"@eslint/js": "latest", "@eslint/js": "latest",
"@fontsource-variable/geist": "^5.2.9", "@fontsource-variable/geist": "^5.2.9",
"@internationalized/date": "^3.12.1", "@internationalized/date": "^3.12.1",
"@lucide/svelte": "^1.21.0", "@libsql/client": "^0.17.2",
"@lucide/svelte": "^1.16.0",
"@sveltejs/adapter-cloudflare": "^7.2.8", "@sveltejs/adapter-cloudflare": "^7.2.8",
"@sveltejs/kit": "^2.57.0", "@sveltejs/kit": "^2.57.0",
"@sveltejs/vite-plugin-svelte": "^7.0.0", "@sveltejs/vite-plugin-svelte": "^7.0.0",
@@ -64,8 +65,5 @@
"vite": "^8.0.7", "vite": "^8.0.7",
"wrangler": "^4.81.0", "wrangler": "^4.81.0",
"zod": "^4.4.3" "zod": "^4.4.3"
},
"dependencies": {
"date-fns": "^4.4.0"
} }
} }
+13 -30
View File
@@ -7,10 +7,6 @@ settings:
importers: importers:
.: .:
dependencies:
date-fns:
specifier: ^4.4.0
version: 4.4.0
devDependencies: devDependencies:
'@better-auth/cli': '@better-auth/cli':
specifier: ~1.4.21 specifier: ~1.4.21
@@ -27,9 +23,12 @@ importers:
'@internationalized/date': '@internationalized/date':
specifier: ^3.12.1 specifier: ^3.12.1
version: 3.12.1 version: 3.12.1
'@libsql/client':
specifier: ^0.17.2
version: 0.17.3
'@lucide/svelte': '@lucide/svelte':
specifier: ^1.21.0 specifier: ^1.16.0
version: 1.21.0(svelte@5.55.9(@typescript-eslint/types@8.59.4)) version: 1.16.0(svelte@5.55.9(@typescript-eslint/types@8.59.4))
'@sveltejs/adapter-cloudflare': '@sveltejs/adapter-cloudflare':
specifier: ^7.2.8 specifier: ^7.2.8
version: 7.2.8(@sveltejs/kit@2.61.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.55.9(@typescript-eslint/types@8.59.4))(vite@8.0.14(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.3)))(svelte@5.55.9(@typescript-eslint/types@8.59.4))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.3)))(wrangler@4.94.0(@cloudflare/workers-types@4.20260523.1)) version: 7.2.8(@sveltejs/kit@2.61.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.55.9(@typescript-eslint/types@8.59.4))(vite@8.0.14(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.3)))(svelte@5.55.9(@typescript-eslint/types@8.59.4))(typescript@6.0.3)(vite@8.0.14(@types/node@24.12.4)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.3)))(wrangler@4.94.0(@cloudflare/workers-types@4.20260523.1))
@@ -1386,8 +1385,8 @@ packages:
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
'@lucide/svelte@1.21.0': '@lucide/svelte@1.16.0':
resolution: {integrity: sha512-MEv//A7Jv3kHukZowv/DWp1MAtUzJKYwtJsmnQ7X98lCgtac3z3NbaToDl3Q6jO3gS9sougFpcD+t+YuxOkRMw==} resolution: {integrity: sha512-AvvPJnaWxeiNkAljI5MsSEc84yHPLMaWQIAJOcbX7k9au/f9ITS7cxTTQiautDiOFKVOXiYdZ+d6mtl88J+Kbg==}
peerDependencies: peerDependencies:
svelte: ^5 svelte: ^5
@@ -2214,9 +2213,6 @@ packages:
resolution: {integrity: sha512-G7gHKj89n2owmkGb6WX6ixcnQ0Kf/0wpa9VIh9DGdbHu8wdrlaHU4ir3/bFNERl8N8nn4G7e7qbtBG8N9caihQ==} resolution: {integrity: sha512-G7gHKj89n2owmkGb6WX6ixcnQ0Kf/0wpa9VIh9DGdbHu8wdrlaHU4ir3/bFNERl8N8nn4G7e7qbtBG8N9caihQ==}
engines: {node: '>=12'} engines: {node: '>=12'}
date-fns@4.4.0:
resolution: {integrity: sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==}
dayjs@1.11.20: dayjs@1.11.20:
resolution: {integrity: sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==} resolution: {integrity: sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==}
@@ -4763,12 +4759,10 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- bufferutil - bufferutil
- utf-8-validate - utf-8-validate
optional: true
'@libsql/core@0.17.3': '@libsql/core@0.17.3':
dependencies: dependencies:
js-base64: 3.7.8 js-base64: 3.7.8
optional: true
'@libsql/darwin-arm64@0.5.29': '@libsql/darwin-arm64@0.5.29':
optional: true optional: true
@@ -4783,7 +4777,6 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- bufferutil - bufferutil
- utf-8-validate - utf-8-validate
optional: true
'@libsql/isomorphic-ws@0.1.5': '@libsql/isomorphic-ws@0.1.5':
dependencies: dependencies:
@@ -4792,7 +4785,6 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- bufferutil - bufferutil
- utf-8-validate - utf-8-validate
optional: true
'@libsql/linux-arm-gnueabihf@0.5.29': '@libsql/linux-arm-gnueabihf@0.5.29':
optional: true optional: true
@@ -4815,7 +4807,7 @@ snapshots:
'@libsql/win32-x64-msvc@0.5.29': '@libsql/win32-x64-msvc@0.5.29':
optional: true optional: true
'@lucide/svelte@1.21.0(svelte@5.55.9(@typescript-eslint/types@8.59.4))': '@lucide/svelte@1.16.0(svelte@5.55.9(@typescript-eslint/types@8.59.4))':
dependencies: dependencies:
svelte: 5.55.9(@typescript-eslint/types@8.59.4) svelte: 5.55.9(@typescript-eslint/types@8.59.4)
@@ -4831,8 +4823,7 @@ snapshots:
'@tybys/wasm-util': 0.10.2 '@tybys/wasm-util': 0.10.2
optional: true optional: true
'@neon-rs/load@0.0.4': '@neon-rs/load@0.0.4': {}
optional: true
'@noble/ciphers@2.2.0': {} '@noble/ciphers@2.2.0': {}
@@ -5081,7 +5072,6 @@ snapshots:
'@types/ws@8.18.1': '@types/ws@8.18.1':
dependencies: dependencies:
'@types/node': 24.12.4 '@types/node': 24.12.4
optional: true
'@typeschema/class-validator@0.3.0(@types/json-schema@7.0.15)(class-validator@0.14.4)': '@typeschema/class-validator@0.3.0(@types/json-schema@7.0.15)(class-validator@0.14.4)':
dependencies: dependencies:
@@ -5531,8 +5521,6 @@ snapshots:
d3-delaunay: 6.0.4 d3-delaunay: 6.0.4
d3-scale: 4.0.2 d3-scale: 4.0.2
date-fns@4.4.0: {}
dayjs@1.11.20: dayjs@1.11.20:
optional: true optional: true
@@ -5569,8 +5557,7 @@ snapshots:
destr@2.0.5: {} destr@2.0.5: {}
detect-libc@2.0.2: detect-libc@2.0.2: {}
optional: true
detect-libc@2.1.2: {} detect-libc@2.1.2: {}
@@ -5989,8 +5976,7 @@ snapshots:
jose@6.2.3: {} jose@6.2.3: {}
js-base64@3.7.8: js-base64@3.7.8: {}
optional: true
js-tokens@4.0.0: {} js-tokens@4.0.0: {}
@@ -6080,7 +6066,6 @@ snapshots:
'@libsql/linux-x64-gnu': 0.5.29 '@libsql/linux-x64-gnu': 0.5.29
'@libsql/linux-x64-musl': 0.5.29 '@libsql/linux-x64-musl': 0.5.29
'@libsql/win32-x64-msvc': 0.5.29 '@libsql/win32-x64-msvc': 0.5.29
optional: true
lightningcss-android-arm64@1.32.0: lightningcss-android-arm64@1.32.0:
optional: true optional: true
@@ -6371,8 +6356,7 @@ snapshots:
prettier@3.8.3: {} prettier@3.8.3: {}
promise-limit@2.7.0: promise-limit@2.7.0: {}
optional: true
prompts@2.4.2: prompts@2.4.2:
dependencies: dependencies:
@@ -6894,8 +6878,7 @@ snapshots:
ws@8.20.1: {} ws@8.20.1: {}
ws@8.21.0: ws@8.21.0: {}
optional: true
wsl-utils@0.1.0: wsl-utils@0.1.0:
dependencies: dependencies:
+2 -2
View File
@@ -11,8 +11,8 @@
import type { HTMLAttributes } from 'svelte/elements'; import type { HTMLAttributes } from 'svelte/elements';
import { Button } from '$lib/components/ui/button/index.js'; import { Button } from '$lib/components/ui/button/index.js';
import { Input } from '$lib/components/ui/input/index.js'; import { Input } from '$lib/components/ui/input/index.js';
import { cn, type WithElementRef } from '$lib/utils/format.js'; import { cn, type WithElementRef } from '$lib/utils.js';
import { firstFormError } from '$lib/utils/feedback'; import { firstFormError } from '$lib/form-feedback';
import { firstAdminSchema, type FirstAdminSchema } from '$lib/schemas/auth'; import { firstAdminSchema, type FirstAdminSchema } from '$lib/schemas/auth';
let { let {
+1 -1
View File
@@ -5,7 +5,7 @@
import { Button } from '$lib/components/ui/button/index.js'; import { Button } from '$lib/components/ui/button/index.js';
import * as Command from '$lib/components/ui/command/index.js'; import * as Command from '$lib/components/ui/command/index.js';
import * as Popover from '$lib/components/ui/popover/index.js'; import * as Popover from '$lib/components/ui/popover/index.js';
import { cn } from '$lib/utils/format.js'; import { cn } from '$lib/utils.js';
type Option = { type Option = {
value: string; value: string;
+1 -1
View File
@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import type { ComponentProps } from 'svelte'; import type { ComponentProps } from 'svelte';
import * as Select from '$lib/components/ui/select/index.js'; import * as Select from '$lib/components/ui/select/index.js';
import { cn } from '$lib/utils/format.js'; import { cn } from '$lib/utils.js';
type Option = { type Option = {
value: string; value: string;
+1 -1
View File
@@ -8,7 +8,7 @@
import type { HTMLAttributes } from 'svelte/elements'; import type { HTMLAttributes } from 'svelte/elements';
import { Input } from '$lib/components/ui/input/index.js'; import { Input } from '$lib/components/ui/input/index.js';
import { Button } from '$lib/components/ui/button/index.js'; import { Button } from '$lib/components/ui/button/index.js';
import { cn, type WithElementRef } from '$lib/utils/format.js'; import { cn, type WithElementRef } from '$lib/utils.js';
import { loginSchema, type LoginSchema } from '$lib/schemas/auth'; import { loginSchema, type LoginSchema } from '$lib/schemas/auth';
let { let {
@@ -31,6 +31,7 @@
<div class="grid gap-5"> <div class="grid gap-5">
<div class="grid gap-3"> <div class="grid gap-3">
<div class="grid gap-2 sm:grid-cols-2">
<Field.Field> <Field.Field>
<Field.Label for={`${prefix}-name`}>Name</Field.Label> <Field.Label for={`${prefix}-name`}>Name</Field.Label>
<Input <Input
@@ -51,6 +52,8 @@
required required
/> />
</Field.Field> </Field.Field>
</div>
<Field.Field> <Field.Field>
<Field.Label for={`${prefix}-address-line-2`}>Address line 2</Field.Label> <Field.Label for={`${prefix}-address-line-2`}>Address line 2</Field.Label>
<Input <Input
+1 -1
View File
@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import { Label } from '$lib/components/ui/label/index.js'; import { Label } from '$lib/components/ui/label/index.js';
import * as Sidebar from '$lib/components/ui/sidebar/index.js'; import * as Sidebar from '$lib/components/ui/sidebar/index.js';
import type { WithElementRef } from '$lib/utils/format.js'; import type { WithElementRef } from '$lib/utils.js';
import SearchIcon from '@lucide/svelte/icons/search'; import SearchIcon from '@lucide/svelte/icons/search';
import type { HTMLFormAttributes } from 'svelte/elements'; import type { HTMLFormAttributes } from 'svelte/elements';
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { Accordion as AccordionPrimitive } from "bits-ui"; import { Accordion as AccordionPrimitive } from 'bits-ui';
import { cn, type WithoutChild } from "$lib/utils.js"; import { cn, type WithoutChild } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -13,12 +13,12 @@
<AccordionPrimitive.Content <AccordionPrimitive.Content
bind:ref bind:ref
data-slot="accordion-content" data-slot="accordion-content"
class="data-open:animate-accordion-down data-closed:animate-accordion-up text-sm overflow-hidden" class="overflow-hidden text-sm data-open:animate-accordion-down data-closed:animate-accordion-up"
{...restProps} {...restProps}
> >
<div <div
class={cn( class={cn(
"pt-0 pb-2.5 [&_a]:hover:text-foreground [&_a]:underline [&_a]:underline-offset-3 [&_p:not(:last-child)]:mb-4", 'pt-0 pb-2.5 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4',
className className
)} )}
> >
@@ -1,6 +1,6 @@
<script lang="ts"> <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 { let {
ref = $bindable(null), ref = $bindable(null),
@@ -12,6 +12,6 @@
<AccordionPrimitive.Item <AccordionPrimitive.Item
bind:ref bind:ref
data-slot="accordion-item" data-slot="accordion-item"
class={cn("not-last:border-b", className)} class={cn('not-last:border-b', className)}
{...restProps} {...restProps}
/> />
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { Accordion as AccordionPrimitive } from "bits-ui"; import { Accordion as AccordionPrimitive } from 'bits-ui';
import { cn, type WithoutChild } from "$lib/utils.js"; import { cn, type WithoutChild } from '$lib/utils.js';
import ChevronDownIcon from '@lucide/svelte/icons/chevron-down'; import ChevronDownIcon from '@lucide/svelte/icons/chevron-down';
import ChevronUpIcon from '@lucide/svelte/icons/chevron-up'; import ChevronUpIcon from '@lucide/svelte/icons/chevron-up';
@@ -11,7 +11,7 @@
children, children,
...restProps ...restProps
}: WithoutChild<AccordionPrimitive.TriggerProps> & { }: WithoutChild<AccordionPrimitive.TriggerProps> & {
level?: AccordionPrimitive.HeaderProps["level"]; level?: AccordionPrimitive.HeaderProps['level'];
} = $props(); } = $props();
</script> </script>
@@ -20,13 +20,19 @@
data-slot="accordion-trigger" data-slot="accordion-trigger"
bind:ref bind:ref
class={cn( class={cn(
"focus-visible:ring-ring/50 focus-visible:border-ring focus-visible:after:border-ring **:data-[slot=accordion-trigger-icon]:text-muted-foreground rounded-lg py-2.5 text-left text-sm font-medium hover:underline focus-visible:ring-3 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 group/accordion-trigger relative flex flex-1 items-start justify-between border border-transparent transition-all outline-none disabled:pointer-events-none disabled:opacity-50", 'group/accordion-trigger relative flex flex-1 items-start justify-between rounded-lg border border-transparent py-2.5 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:after:border-ring disabled:pointer-events-none disabled:opacity-50 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 **:data-[slot=accordion-trigger-icon]:text-muted-foreground',
className className
)} )}
{...restProps} {...restProps}
> >
{@render children?.()} {@render children?.()}
<ChevronDownIcon data-slot="accordion-trigger-icon" class="cn-accordion-trigger-icon pointer-events-none shrink-0 group-aria-expanded/accordion-trigger:hidden" /> <ChevronDownIcon
<ChevronUpIcon data-slot="accordion-trigger-icon" class="cn-accordion-trigger-icon pointer-events-none hidden shrink-0 group-aria-expanded/accordion-trigger:inline" /> data-slot="accordion-trigger-icon"
class="cn-accordion-trigger-icon pointer-events-none shrink-0 group-aria-expanded/accordion-trigger:hidden"
/>
<ChevronUpIcon
data-slot="accordion-trigger-icon"
class="cn-accordion-trigger-icon pointer-events-none hidden shrink-0 group-aria-expanded/accordion-trigger:inline"
/>
</AccordionPrimitive.Trigger> </AccordionPrimitive.Trigger>
</AccordionPrimitive.Header> </AccordionPrimitive.Header>
@@ -1,6 +1,6 @@
<script lang="ts"> <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 { let {
ref = $bindable(null), ref = $bindable(null),
@@ -14,6 +14,6 @@
bind:ref bind:ref
bind:value={value as never} bind:value={value as never}
data-slot="accordion" data-slot="accordion"
class={cn("cn-accordion flex w-full flex-col", className)} class={cn('cn-accordion flex w-full flex-col', className)}
{...restProps} {...restProps}
/> />
+5 -5
View File
@@ -1,7 +1,7 @@
import Root from "./accordion.svelte"; import Root from './accordion.svelte';
import Content from "./accordion-content.svelte"; import Content from './accordion-content.svelte';
import Item from "./accordion-item.svelte"; import Item from './accordion-item.svelte';
import Trigger from "./accordion-trigger.svelte"; import Trigger from './accordion-trigger.svelte';
export { export {
Root, Root,
@@ -12,5 +12,5 @@ export {
Root as Accordion, Root as Accordion,
Content as AccordionContent, Content as AccordionContent,
Item as AccordionItem, Item as AccordionItem,
Trigger as AccordionTrigger, Trigger as AccordionTrigger
}; };
@@ -1,17 +1,17 @@
<script lang="ts"> <script lang="ts">
import { AlertDialog as AlertDialogPrimitive } from "bits-ui"; import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
import { import {
buttonVariants, buttonVariants,
type ButtonVariant, type ButtonVariant,
type ButtonSize, type ButtonSize
} from "$lib/components/ui/button/index.js"; } from '$lib/components/ui/button/index.js';
import { cn } from "$lib/utils.js"; import { cn } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
class: className, class: className,
variant = "default", variant = 'default',
size = "default", size = 'default',
...restProps ...restProps
}: AlertDialogPrimitive.ActionProps & { }: AlertDialogPrimitive.ActionProps & {
variant?: ButtonVariant; variant?: ButtonVariant;
@@ -22,6 +22,6 @@
<AlertDialogPrimitive.Action <AlertDialogPrimitive.Action
bind:ref bind:ref
data-slot="alert-dialog-action" data-slot="alert-dialog-action"
class={cn(buttonVariants({ variant, size }), "cn-alert-dialog-action", className)} class={cn(buttonVariants({ variant, size }), 'cn-alert-dialog-action', className)}
{...restProps} {...restProps}
/> />
@@ -1,17 +1,17 @@
<script lang="ts"> <script lang="ts">
import { AlertDialog as AlertDialogPrimitive } from "bits-ui"; import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
import { import {
buttonVariants, buttonVariants,
type ButtonVariant, type ButtonVariant,
type ButtonSize, type ButtonSize
} from "$lib/components/ui/button/index.js"; } from '$lib/components/ui/button/index.js';
import { cn } from "$lib/utils.js"; import { cn } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
class: className, class: className,
variant = "outline", variant = 'outline',
size = "default", size = 'default',
...restProps ...restProps
}: AlertDialogPrimitive.CancelProps & { }: AlertDialogPrimitive.CancelProps & {
variant?: ButtonVariant; variant?: ButtonVariant;
@@ -22,6 +22,6 @@
<AlertDialogPrimitive.Cancel <AlertDialogPrimitive.Cancel
bind:ref bind:ref
data-slot="alert-dialog-cancel" data-slot="alert-dialog-cancel"
class={cn(buttonVariants({ variant, size }), "cn-alert-dialog-cancel", className)} class={cn(buttonVariants({ variant, size }), 'cn-alert-dialog-cancel', className)}
{...restProps} {...restProps}
/> />
@@ -1,18 +1,18 @@
<script lang="ts"> <script lang="ts">
import { AlertDialog as AlertDialogPrimitive } from "bits-ui"; import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
import AlertDialogPortal from "./alert-dialog-portal.svelte"; import AlertDialogPortal from './alert-dialog-portal.svelte';
import AlertDialogOverlay from "./alert-dialog-overlay.svelte"; import AlertDialogOverlay from './alert-dialog-overlay.svelte';
import { cn, type WithoutChild, type WithoutChildrenOrChild } from "$lib/utils.js"; import { cn, type WithoutChild, type WithoutChildrenOrChild } from '$lib/utils.js';
import type { ComponentProps } from "svelte"; import type { ComponentProps } from 'svelte';
let { let {
ref = $bindable(null), ref = $bindable(null),
class: className, class: className,
size = "default", size = 'default',
portalProps, portalProps,
...restProps ...restProps
}: WithoutChild<AlertDialogPrimitive.ContentProps> & { }: WithoutChild<AlertDialogPrimitive.ContentProps> & {
size?: "default" | "sm"; size?: 'default' | 'sm';
portalProps?: WithoutChildrenOrChild<ComponentProps<typeof AlertDialogPortal>>; portalProps?: WithoutChildrenOrChild<ComponentProps<typeof AlertDialogPortal>>;
} = $props(); } = $props();
</script> </script>
@@ -24,7 +24,7 @@
data-slot="alert-dialog-content" data-slot="alert-dialog-content"
data-size={size} data-size={size}
class={cn( class={cn(
"data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 bg-popover text-popover-foreground ring-foreground/10 gap-4 rounded-xl p-4 ring-1 duration-100 data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-sm group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 outline-none", 'group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95',
className className
)} )}
{...restProps} {...restProps}
@@ -1,6 +1,6 @@
<script lang="ts"> <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 { let {
ref = $bindable(null), ref = $bindable(null),
@@ -12,6 +12,9 @@
<AlertDialogPrimitive.Description <AlertDialogPrimitive.Description
bind:ref bind:ref
data-slot="alert-dialog-description" data-slot="alert-dialog-description"
class={cn("text-muted-foreground *:[a]:hover:text-foreground text-sm text-balance md:text-pretty *:[a]:underline *:[a]:underline-offset-3", className)} class={cn(
'text-sm text-balance text-muted-foreground md:text-pretty *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground',
className
)}
{...restProps} {...restProps}
/> />
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -14,7 +14,7 @@
bind:this={ref} bind:this={ref}
data-slot="alert-dialog-footer" data-slot="alert-dialog-footer"
class={cn( class={cn(
"bg-muted/50 -mx-4 -mb-4 rounded-b-xl border-t p-4 flex flex-col-reverse gap-2 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end", '-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end',
className className
)} )}
{...restProps} {...restProps}
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -13,7 +13,10 @@
<div <div
bind:this={ref} bind:this={ref}
data-slot="alert-dialog-header" data-slot="alert-dialog-header"
class={cn("grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]", className)} class={cn(
'grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]',
className
)}
{...restProps} {...restProps}
> >
{@render children?.()} {@render children?.()}
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -13,7 +13,10 @@
<div <div
bind:this={ref} bind:this={ref}
data-slot="alert-dialog-media" data-slot="alert-dialog-media"
class={cn("bg-muted mb-2 inline-flex size-10 items-center justify-center rounded-md sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-6", className)} class={cn(
"mb-2 inline-flex size-10 items-center justify-center rounded-md bg-muted sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-6",
className
)}
{...restProps} {...restProps}
> >
{@render children?.()} {@render children?.()}
@@ -1,6 +1,6 @@
<script lang="ts"> <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 { let {
ref = $bindable(null), ref = $bindable(null),
@@ -12,6 +12,9 @@
<AlertDialogPrimitive.Overlay <AlertDialogPrimitive.Overlay
bind:ref bind:ref
data-slot="alert-dialog-overlay" data-slot="alert-dialog-overlay"
class={cn("data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 z-50", className)} class={cn(
'fixed inset-0 z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0',
className
)}
{...restProps} {...restProps}
/> />
@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { AlertDialog as AlertDialogPrimitive } from "bits-ui"; import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
let { ...restProps }: AlertDialogPrimitive.PortalProps = $props(); let { ...restProps }: AlertDialogPrimitive.PortalProps = $props();
</script> </script>
@@ -1,6 +1,6 @@
<script lang="ts"> <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 { let {
ref = $bindable(null), ref = $bindable(null),
@@ -12,6 +12,9 @@
<AlertDialogPrimitive.Title <AlertDialogPrimitive.Title
bind:ref bind:ref
data-slot="alert-dialog-title" data-slot="alert-dialog-title"
class={cn("text-base font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2", className)} class={cn(
'text-base font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2',
className
)}
{...restProps} {...restProps}
/> />
@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { AlertDialog as AlertDialogPrimitive } from "bits-ui"; import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
let { ref = $bindable(null), ...restProps }: AlertDialogPrimitive.TriggerProps = $props(); let { ref = $bindable(null), ...restProps }: AlertDialogPrimitive.TriggerProps = $props();
</script> </script>
@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { AlertDialog as AlertDialogPrimitive } from "bits-ui"; import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
let { open = $bindable(false), ...restProps }: AlertDialogPrimitive.RootProps = $props(); let { open = $bindable(false), ...restProps }: AlertDialogPrimitive.RootProps = $props();
</script> </script>
+13 -13
View File
@@ -1,15 +1,15 @@
import Root from "./alert-dialog.svelte"; import Root from './alert-dialog.svelte';
import Portal from "./alert-dialog-portal.svelte"; import Portal from './alert-dialog-portal.svelte';
import Trigger from "./alert-dialog-trigger.svelte"; import Trigger from './alert-dialog-trigger.svelte';
import Title from "./alert-dialog-title.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 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 Content from "./alert-dialog-content.svelte"; import Content from './alert-dialog-content.svelte';
import Description from "./alert-dialog-description.svelte"; import Description from './alert-dialog-description.svelte';
import Media from "./alert-dialog-media.svelte"; import Media from './alert-dialog-media.svelte';
export { export {
Root, Root,
@@ -36,5 +36,5 @@ export {
Overlay as AlertDialogOverlay, Overlay as AlertDialogOverlay,
Content as AlertDialogContent, Content as AlertDialogContent,
Description as AlertDialogDescription, Description as AlertDialogDescription,
Media as AlertDialogMedia, Media as AlertDialogMedia
}; };
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -13,7 +13,7 @@
<div <div
bind:this={ref} bind:this={ref}
data-slot="alert-action" data-slot="alert-action"
class={cn("absolute top-2 right-2", className)} class={cn('absolute top-2 right-2', className)}
{...restProps} {...restProps}
> >
{@render children?.()} {@render children?.()}
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -14,7 +14,7 @@
bind:this={ref} bind:this={ref}
data-slot="alert-description" data-slot="alert-description"
class={cn( class={cn(
"text-muted-foreground text-sm text-balance md:text-pretty [&_p:not(:last-child)]:mb-4 [&_a]:hover:text-foreground [&_a]:underline [&_a]:underline-offset-3", 'text-sm text-balance text-muted-foreground md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4',
className className
)} )}
{...restProps} {...restProps}
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -14,7 +14,7 @@
bind:this={ref} bind:this={ref}
data-slot="alert-title" data-slot="alert-title"
class={cn( class={cn(
"font-medium group-has-[>svg]/alert:col-start-2 [&_a]:hover:text-foreground [&_a]:underline [&_a]:underline-offset-3", 'font-medium group-has-[>svg]/alert:col-start-2 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground',
className className
)} )}
{...restProps} {...restProps}
+11 -10
View File
@@ -1,30 +1,31 @@
<script lang="ts" module> <script lang="ts" module>
import { type VariantProps, tv } from "tailwind-variants"; import { type VariantProps, tv } from 'tailwind-variants';
export const alertVariants = tv({ export const alertVariants = tv({
base: "grid gap-0.5 rounded-lg border px-2.5 py-2 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4 group/alert relative w-full", base: "grid gap-0.5 rounded-lg border px-2.5 py-2 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4 group/alert relative w-full",
variants: { variants: {
variant: { variant: {
default: "bg-card text-card-foreground", default: 'bg-card text-card-foreground',
destructive: "text-destructive bg-card *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current", 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"]; export type AlertVariant = VariantProps<typeof alertVariants>['variant'];
</script> </script>
<script lang="ts"> <script lang="ts">
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
class: className, class: className,
variant = "default", variant = 'default',
children, children,
...restProps ...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & { }: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
+6 -6
View File
@@ -1,8 +1,8 @@
import Root from "./alert.svelte"; import Root from './alert.svelte';
import Description from "./alert-description.svelte"; import Description from './alert-description.svelte';
import Title from "./alert-title.svelte"; import Title from './alert-title.svelte';
import Action from "./alert-action.svelte"; import Action from './alert-action.svelte';
export { alertVariants, type AlertVariant } from "./alert.svelte"; export { alertVariants, type AlertVariant } from './alert.svelte';
export { export {
Root, Root,
@@ -13,5 +13,5 @@ export {
Root as Alert, Root as Alert,
Description as AlertDescription, Description as AlertDescription,
Title as AlertTitle, Title as AlertTitle,
Action as AlertAction, Action as AlertAction
}; };
@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { AspectRatio as AspectRatioPrimitive } from "bits-ui"; import { AspectRatio as AspectRatioPrimitive } from 'bits-ui';
let { ref = $bindable(null), ...restProps }: AspectRatioPrimitive.RootProps = $props(); let { ref = $bindable(null), ...restProps }: AspectRatioPrimitive.RootProps = $props();
</script> </script>
+1 -1
View File
@@ -1,3 +1,3 @@
import Root from "./aspect-ratio.svelte"; import Root from './aspect-ratio.svelte';
export { Root, Root as AspectRatio }; export { Root, Root as AspectRatio };
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -14,10 +14,10 @@
bind:this={ref} bind:this={ref}
data-slot="avatar-badge" data-slot="avatar-badge"
class={cn( class={cn(
"bg-primary text-primary-foreground ring-background absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-blend-color ring-2 select-none", 'absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground bg-blend-color ring-2 ring-background select-none',
"group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden", 'group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden',
"group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2", 'group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2',
"group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2", 'group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2',
className className
)} )}
{...restProps} {...restProps}
@@ -1,6 +1,6 @@
<script lang="ts"> <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 { let {
ref = $bindable(null), ref = $bindable(null),
@@ -13,7 +13,7 @@
bind:ref bind:ref
data-slot="avatar-fallback" data-slot="avatar-fallback"
class={cn( class={cn(
"bg-muted text-muted-foreground rounded-full flex size-full items-center justify-center text-sm group-data-[size=sm]/avatar:text-xs", 'flex size-full items-center justify-center rounded-full bg-muted text-sm text-muted-foreground group-data-[size=sm]/avatar:text-xs',
className className
)} )}
{...restProps} {...restProps}
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -14,7 +14,7 @@
bind:this={ref} bind:this={ref}
data-slot="avatar-group-count" data-slot="avatar-group-count"
class={cn( class={cn(
"bg-muted text-muted-foreground size-8 rounded-full text-sm group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3 ring-background relative flex shrink-0 items-center justify-center ring-2", 'relative flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-sm text-muted-foreground ring-2 ring-background group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3',
className className
)} )}
{...restProps} {...restProps}
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -14,7 +14,7 @@
bind:this={ref} bind:this={ref}
data-slot="avatar-group" data-slot="avatar-group"
class={cn( class={cn(
"cn-avatar-group *:data-[slot=avatar]:ring-background group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2", 'cn-avatar-group group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background',
className className
)} )}
{...restProps} {...restProps}
@@ -1,6 +1,6 @@
<script lang="ts"> <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 { let {
ref = $bindable(null), ref = $bindable(null),
@@ -12,6 +12,6 @@
<AvatarPrimitive.Image <AvatarPrimitive.Image
bind:ref bind:ref
data-slot="avatar-image" data-slot="avatar-image"
class={cn("rounded-full aspect-square size-full object-cover", className)} class={cn('aspect-square size-full rounded-full object-cover', className)}
{...restProps} {...restProps}
/> />
+6 -6
View File
@@ -1,15 +1,15 @@
<script lang="ts"> <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 { let {
ref = $bindable(null), ref = $bindable(null),
loadingStatus = $bindable("loading"), loadingStatus = $bindable('loading'),
size = "default", size = 'default',
class: className, class: className,
...restProps ...restProps
}: AvatarPrimitive.RootProps & { }: AvatarPrimitive.RootProps & {
size?: "default" | "sm" | "lg"; size?: 'default' | 'sm' | 'lg';
} = $props(); } = $props();
</script> </script>
@@ -19,7 +19,7 @@
data-slot="avatar" data-slot="avatar"
data-size={size} data-size={size}
class={cn( class={cn(
"size-8 rounded-full after:rounded-full data-[size=lg]:size-10 data-[size=sm]:size-6 after:border-border group/avatar relative flex shrink-0 select-none after:absolute after:inset-0 after:border after:mix-blend-darken dark:after:mix-blend-lighten", 'group/avatar relative flex size-8 shrink-0 rounded-full select-none after:absolute after:inset-0 after:rounded-full after:border after:border-border after:mix-blend-darken data-[size=lg]:size-10 data-[size=sm]:size-6 dark:after:mix-blend-lighten',
className className
)} )}
{...restProps} {...restProps}
+7 -7
View File
@@ -1,9 +1,9 @@
import Root from "./avatar.svelte"; import Root from './avatar.svelte';
import Image from "./avatar-image.svelte"; import Image from './avatar-image.svelte';
import Fallback from "./avatar-fallback.svelte"; import Fallback from './avatar-fallback.svelte';
import Badge from "./avatar-badge.svelte"; import Badge from './avatar-badge.svelte';
import Group from "./avatar-group.svelte"; import Group from './avatar-group.svelte';
import GroupCount from "./avatar-group-count.svelte"; import GroupCount from './avatar-group-count.svelte';
export { export {
Root, Root,
@@ -18,5 +18,5 @@ export {
Fallback as AvatarFallback, Fallback as AvatarFallback,
Badge as AvatarBadge, Badge as AvatarBadge,
Group as AvatarGroup, Group as AvatarGroup,
GroupCount as AvatarGroupCount, GroupCount as AvatarGroupCount
}; };
+17 -16
View File
@@ -1,35 +1,36 @@
<script lang="ts" module> <script lang="ts" module>
import { type VariantProps, tv } from "tailwind-variants"; import { type VariantProps, tv } from 'tailwind-variants';
export const badgeVariants = tv({ export const badgeVariants = tv({
base: "h-5 gap-1 rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium transition-all has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&>svg]:size-3! focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive group/badge inline-flex w-fit shrink-0 items-center justify-center overflow-hidden whitespace-nowrap transition-colors focus-visible:ring-[3px] [&>svg]:pointer-events-none", base: 'h-5 gap-1 rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium transition-all has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&>svg]:size-3! focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive group/badge inline-flex w-fit shrink-0 items-center justify-center overflow-hidden whitespace-nowrap transition-colors focus-visible:ring-[3px] [&>svg]:pointer-events-none',
variants: { variants: {
variant: { variant: {
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80", default: 'bg-primary text-primary-foreground [a]:hover:bg-primary/80',
secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80", secondary: 'bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80',
destructive: "bg-destructive/10 [a]:hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 text-destructive dark:bg-destructive/20", destructive:
outline: "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground", 'bg-destructive/10 [a]:hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 text-destructive dark:bg-destructive/20',
ghost: "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50", outline: 'border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground',
link: "text-primary underline-offset-4 hover:underline", ghost: 'hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50',
}, link: 'text-primary underline-offset-4 hover:underline'
}
}, },
defaultVariants: { defaultVariants: {
variant: "default", variant: 'default'
}, }
}); });
export type BadgeVariant = VariantProps<typeof badgeVariants>["variant"]; export type BadgeVariant = VariantProps<typeof badgeVariants>['variant'];
</script> </script>
<script lang="ts"> <script lang="ts">
import type { HTMLAnchorAttributes } from "svelte/elements"; import type { HTMLAnchorAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
href, href,
class: className, class: className,
variant = "default", variant = 'default',
children, children,
...restProps ...restProps
}: WithElementRef<HTMLAnchorAttributes> & { }: WithElementRef<HTMLAnchorAttributes> & {
@@ -38,7 +39,7 @@
</script> </script>
<svelte:element <svelte:element
this={href ? "a" : "span"} this={href ? 'a' : 'span'}
bind:this={ref} bind:this={ref}
data-slot="badge" data-slot="badge"
{href} {href}
+2 -2
View File
@@ -1,2 +1,2 @@
export { default as Badge } from "./badge.svelte"; export { default as Badge } from './badge.svelte';
export { badgeVariants, type BadgeVariant } from "./badge.svelte"; export { badgeVariants, type BadgeVariant } from './badge.svelte';
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
import { cn, type WithElementRef, type WithoutChildren } from "$lib/utils.js"; import { cn, type WithElementRef, type WithoutChildren } from '$lib/utils.js';
import MoreHorizontalIcon from '@lucide/svelte/icons/more-horizontal'; import MoreHorizontalIcon from '@lucide/svelte/icons/more-horizontal';
let { let {
@@ -15,7 +15,7 @@
data-slot="breadcrumb-ellipsis" data-slot="breadcrumb-ellipsis"
role="presentation" role="presentation"
aria-hidden="true" aria-hidden="true"
class={cn("size-5 [&>svg]:size-4 flex items-center justify-center", className)} class={cn('flex size-5 items-center justify-center [&>svg]:size-4', className)}
{...restProps} {...restProps}
> >
<MoreHorizontalIcon /> <MoreHorizontalIcon />
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { HTMLLiAttributes } from "svelte/elements"; import type { HTMLLiAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -13,7 +13,7 @@
<li <li
bind:this={ref} bind:this={ref}
data-slot="breadcrumb-item" data-slot="breadcrumb-item"
class={cn("gap-1 inline-flex items-center", className)} class={cn('inline-flex items-center gap-1', className)}
{...restProps} {...restProps}
> >
{@render children?.()} {@render children?.()}
@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import type { HTMLAnchorAttributes } from "svelte/elements"; import type { HTMLAnchorAttributes } from 'svelte/elements';
import type { Snippet } from "svelte"; import type { Snippet } from 'svelte';
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -15,10 +15,10 @@
} = $props(); } = $props();
const attrs = $derived({ const attrs = $derived({
"data-slot": "breadcrumb-link", 'data-slot': 'breadcrumb-link',
class: cn("hover:text-foreground transition-colors", className), class: cn('hover:text-foreground transition-colors', className),
href, href,
...restProps, ...restProps
}); });
</script> </script>
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { HTMLOlAttributes } from "svelte/elements"; import type { HTMLOlAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -13,7 +13,10 @@
<ol <ol
bind:this={ref} bind:this={ref}
data-slot="breadcrumb-list" data-slot="breadcrumb-list"
class={cn("text-muted-foreground gap-1.5 text-sm flex flex-wrap items-center wrap-break-word", className)} class={cn(
'flex flex-wrap items-center gap-1.5 text-sm wrap-break-word text-muted-foreground',
className
)}
{...restProps} {...restProps}
> >
{@render children?.()} {@render children?.()}
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -16,7 +16,7 @@
role="link" role="link"
aria-disabled="true" aria-disabled="true"
aria-current="page" aria-current="page"
class={cn("text-foreground font-normal", className)} class={cn('font-normal text-foreground', className)}
{...restProps} {...restProps}
> >
{@render children?.()} {@render children?.()}
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
import type { HTMLLiAttributes } from "svelte/elements"; import type { HTMLLiAttributes } from 'svelte/elements';
import ChevronRightIcon from '@lucide/svelte/icons/chevron-right'; import ChevronRightIcon from '@lucide/svelte/icons/chevron-right';
let { let {
@@ -16,7 +16,7 @@
data-slot="breadcrumb-separator" 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)}
{...restProps} {...restProps}
> >
{#if children} {#if children}
@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import type { WithElementRef } from "$lib/utils.js"; import type { WithElementRef } from '$lib/utils.js';
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
import { cn } from "$lib/utils.js"; import { cn } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -15,7 +15,7 @@
bind:this={ref} bind:this={ref}
data-slot="breadcrumb" data-slot="breadcrumb"
aria-label="breadcrumb" aria-label="breadcrumb"
class={cn("cn-breadcrumb", className)} class={cn('cn-breadcrumb', className)}
{...restProps} {...restProps}
> >
{@render children?.()} {@render children?.()}
+8 -8
View File
@@ -1,10 +1,10 @@
import Root from "./breadcrumb.svelte"; 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 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';
export { export {
Root, Root,
@@ -21,5 +21,5 @@ export {
Separator as BreadcrumbSeparator, Separator as BreadcrumbSeparator,
Link as BreadcrumbLink, Link as BreadcrumbLink,
List as BreadcrumbList, List as BreadcrumbList,
Page as BreadcrumbPage, Page as BreadcrumbPage
}; };
@@ -1,12 +1,12 @@
<script lang="ts"> <script lang="ts">
import { cn } from "$lib/utils.js"; import { cn } from '$lib/utils.js';
import type { ComponentProps } from "svelte"; import type { ComponentProps } from 'svelte';
import { Separator } from "$lib/components/ui/separator/index.js"; import { Separator } from '$lib/components/ui/separator/index.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
class: className, class: className,
orientation = "vertical", orientation = 'vertical',
...restProps ...restProps
}: ComponentProps<typeof Separator> = $props(); }: ComponentProps<typeof Separator> = $props();
</script> </script>
@@ -16,7 +16,7 @@
data-slot="button-group-separator" data-slot="button-group-separator"
{orientation} {orientation}
class={cn( class={cn(
"bg-input relative self-stretch data-[orientation=horizontal]:mx-px data-[orientation=horizontal]:w-auto data-[orientation=vertical]:my-px data-[orientation=vertical]:h-auto", 'relative self-stretch bg-input data-[orientation=horizontal]:mx-px data-[orientation=horizontal]:w-auto data-[orientation=vertical]:my-px data-[orientation=vertical]:h-auto',
className className
)} )}
{...restProps} {...restProps}
@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
import type { Snippet } from "svelte"; import type { Snippet } from 'svelte';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -14,8 +14,11 @@
const mergedProps = $derived({ const mergedProps = $derived({
...restProps, ...restProps,
class: cn("bg-muted gap-2 rounded-lg border px-2.5 text-sm font-medium [&_svg:not([class*='size-'])]:size-4 flex items-center [&_svg]:pointer-events-none", className), class: cn(
"data-slot": "button-group-text", "bg-muted gap-2 rounded-lg border px-2.5 text-sm font-medium [&_svg:not([class*='size-'])]:size-4 flex items-center [&_svg]:pointer-events-none",
className
),
'data-slot': 'button-group-text'
}); });
</script> </script>
@@ -1,33 +1,33 @@
<script lang="ts" module> <script lang="ts" module>
import { tv, type VariantProps } from "tailwind-variants"; import { tv, type VariantProps } from 'tailwind-variants';
export const buttonGroupVariants = tv({ export const buttonGroupVariants = tv({
base: "has-[>[data-slot=button-group]]:gap-2 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-lg flex w-fit items-stretch [&>*]:focus-visible:relative [&>*]:focus-visible:z-10 [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1", base: "has-[>[data-slot=button-group]]:gap-2 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-lg flex w-fit items-stretch [&>*]:focus-visible:relative [&>*]:focus-visible:z-10 [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1",
variants: { variants: {
orientation: { orientation: {
horizontal: horizontal:
"[&>[data-slot]:not(:has(~[data-slot]))]:rounded-r-lg! [&>[data-slot]]:rounded-r-none [&>[data-slot]~[data-slot]]:rounded-l-none [&>[data-slot]~[data-slot]]:border-l-0", '[&>[data-slot]:not(:has(~[data-slot]))]:rounded-r-lg! [&>[data-slot]]:rounded-r-none [&>[data-slot]~[data-slot]]:rounded-l-none [&>[data-slot]~[data-slot]]:border-l-0',
vertical: vertical:
"[&>[data-slot]:not(:has(~[data-slot]))]:rounded-b-lg! flex-col [&>[data-slot]]:rounded-b-none [&>[data-slot]~[data-slot]]:rounded-t-none [&>[data-slot]~[data-slot]]:border-t-0", '[&>[data-slot]:not(:has(~[data-slot]))]:rounded-b-lg! flex-col [&>[data-slot]]:rounded-b-none [&>[data-slot]~[data-slot]]:rounded-t-none [&>[data-slot]~[data-slot]]:border-t-0'
}, }
}, },
defaultVariants: { defaultVariants: {
orientation: "horizontal", orientation: 'horizontal'
}, }
}); });
export type ButtonGroupOrientation = VariantProps<typeof buttonGroupVariants>["orientation"]; export type ButtonGroupOrientation = VariantProps<typeof buttonGroupVariants>['orientation'];
</script> </script>
<script lang="ts"> <script lang="ts">
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
let { let {
ref = $bindable(null), ref = $bindable(null),
class: className, class: className,
children, children,
orientation = "horizontal", orientation = 'horizontal',
...restProps ...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & { }: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
orientation?: ButtonGroupOrientation; orientation?: ButtonGroupOrientation;
+4 -4
View File
@@ -1,6 +1,6 @@
import Root, { buttonGroupVariants, type ButtonGroupOrientation } from "./button-group.svelte"; import Root, { buttonGroupVariants, type ButtonGroupOrientation } from './button-group.svelte';
import Text from "./button-group-text.svelte"; import Text from './button-group-text.svelte';
import Separator from "./button-group-separator.svelte"; import Separator from './button-group-separator.svelte';
export { export {
Root, Root,
@@ -11,5 +11,5 @@ export {
// //
Root as ButtonGroup, Root as ButtonGroup,
Text as ButtonGroupText, Text as ButtonGroupText,
Separator as ButtonGroupSeparator, Separator as ButtonGroupSeparator
}; };
+49 -30
View File
@@ -1,41 +1,51 @@
<script lang="ts" module> <script lang="ts" module>
import { cn, type WithElementRef } from "$lib/utils.js"; import { resolve } from '$app/paths';
import type { HTMLAnchorAttributes, HTMLButtonAttributes } from "svelte/elements"; import type { PathnameWithSearchOrHash } from '$app/types';
import { type VariantProps, 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: "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-lg border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-3 active:not-aria-[haspopup]:translate-y-px aria-invalid:ring-3 [&_svg:not([class*='size-'])]:size-4 group/button inline-flex shrink-0 items-center justify-center whitespace-nowrap transition-all outline-none select-none disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_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 dark:aria-invalid:border-destructive/50 rounded-lg border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-3 active:not-aria-[haspopup]:translate-y-px aria-invalid:ring-3 [&_svg:not([class*='size-'])]:size-4 group/button inline-flex shrink-0 items-center justify-center whitespace-nowrap transition-all outline-none select-none disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
variants: { variants: {
variant: { variant: {
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80", default: 'bg-primary text-primary-foreground [a]:hover:bg-primary/80',
outline: "border-border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground", outline:
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground", 'border-border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground',
ghost: "hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground", secondary:
destructive: "bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40 dark:hover:bg-destructive/30", 'bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground',
link: "text-primary underline-offset-4 hover:underline", ghost:
'hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground',
destructive:
'bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40 dark:hover:bg-destructive/30',
link: 'text-primary underline-offset-4 hover:underline'
}, },
size: { size: {
default: "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", default:
'h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2',
xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3", xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5", sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", lg: 'h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2',
icon: "size-8", icon: 'size-8',
"icon-xs": "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3", 'icon-xs':
"icon-sm": "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg", "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
"icon-lg": "size-9", 'icon-sm':
}, 'size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg',
'icon-lg': 'size-9'
}
}, },
defaultVariants: { defaultVariants: {
variant: "default", variant: 'default',
size: "default", size: 'default'
}, }
}); });
export type ButtonVariant = VariantProps<typeof buttonVariants>["variant"]; export type ButtonVariant = VariantProps<typeof buttonVariants>['variant'];
export type ButtonSize = VariantProps<typeof buttonVariants>["size"]; export type ButtonSize = VariantProps<typeof buttonVariants>['size'];
export type ButtonProps = WithElementRef<HTMLButtonAttributes> & export type ButtonProps = WithElementRef<HTMLButtonAttributes> &
WithElementRef<HTMLAnchorAttributes> & { Omit<WithElementRef<HTMLAnchorAttributes>, 'href'> & {
href?: PathnameWithSearchOrHash;
variant?: ButtonVariant; variant?: ButtonVariant;
size?: ButtonSize; size?: ButtonSize;
}; };
@@ -44,26 +54,35 @@
<script lang="ts"> <script lang="ts">
let { let {
class: className, class: className,
variant = "default", variant = 'default',
size = "default", size = 'default',
ref = $bindable(null), ref = $bindable(null),
href = undefined, href = undefined,
type = "button", type = 'button',
disabled, disabled,
children, children,
...restProps ...restProps
}: ButtonProps = $props(); }: ButtonProps = $props();
</script> </script>
{#if href} {#if href && disabled}
<a <a
bind:this={ref} bind:this={ref}
data-slot="button" data-slot="button"
class={cn(buttonVariants({ variant, size }), className)} class={cn(buttonVariants({ variant, size }), className)}
href={disabled ? undefined : href} aria-disabled="true"
aria-disabled={disabled} role="link"
role={disabled ? "link" : undefined} tabindex={-1}
tabindex={disabled ? -1 : undefined} {...restProps}
>
{@render children?.()}
</a>
{:else if href}
<a
bind:this={ref}
data-slot="button"
class={cn(buttonVariants({ variant, size }), className)}
href={resolve(href as '/')}
{...restProps} {...restProps}
> >
{@render children?.()} {@render children?.()}
+3 -3
View File
@@ -2,8 +2,8 @@ import Root, {
type ButtonProps, type ButtonProps,
type ButtonSize, type ButtonSize,
type ButtonVariant, type ButtonVariant,
buttonVariants, buttonVariants
} from "./button.svelte"; } from './button.svelte';
export { export {
Root, Root,
@@ -13,5 +13,5 @@ export {
buttonVariants, buttonVariants,
type ButtonProps, type ButtonProps,
type ButtonSize, type ButtonSize,
type ButtonVariant, type ButtonVariant
}; };
@@ -1,9 +1,9 @@
<script lang="ts"> <script lang="ts">
import type { ComponentProps } from "svelte"; import type { ComponentProps } from 'svelte';
import type Calendar from "./calendar.svelte"; import type Calendar from './calendar.svelte';
import CalendarMonthSelect from "./calendar-month-select.svelte"; import CalendarMonthSelect from './calendar-month-select.svelte';
import CalendarYearSelect from "./calendar-year-select.svelte"; import CalendarYearSelect from './calendar-year-select.svelte';
import { DateFormatter, getLocalTimeZone, type DateValue } from "@internationalized/date"; import { DateFormatter, getLocalTimeZone, type DateValue } from '@internationalized/date';
let { let {
captionLayout, captionLayout,
@@ -14,13 +14,13 @@
month, month,
locale, locale,
placeholder = $bindable(), placeholder = $bindable(),
monthIndex = 0, monthIndex = 0
}: { }: {
captionLayout: ComponentProps<typeof Calendar>["captionLayout"]; captionLayout: ComponentProps<typeof Calendar>['captionLayout'];
months: ComponentProps<typeof CalendarMonthSelect>["months"]; months: ComponentProps<typeof CalendarMonthSelect>['months'];
monthFormat: ComponentProps<typeof CalendarMonthSelect>["monthFormat"]; monthFormat: ComponentProps<typeof CalendarMonthSelect>['monthFormat'];
years: ComponentProps<typeof CalendarYearSelect>["years"]; years: ComponentProps<typeof CalendarYearSelect>['years'];
yearFormat: ComponentProps<typeof CalendarYearSelect>["yearFormat"]; yearFormat: ComponentProps<typeof CalendarYearSelect>['yearFormat'];
month: DateValue; month: DateValue;
placeholder: DateValue | undefined; placeholder: DateValue | undefined;
locale: string; locale: string;
@@ -29,13 +29,13 @@
function formatYear(date: DateValue) { function formatYear(date: DateValue) {
const dateObj = date.toDate(getLocalTimeZone()); const dateObj = date.toDate(getLocalTimeZone());
if (typeof yearFormat === "function") return yearFormat(dateObj.getFullYear()); if (typeof yearFormat === 'function') return yearFormat(dateObj.getFullYear());
return new DateFormatter(locale, { year: yearFormat }).format(dateObj); return new DateFormatter(locale, { year: yearFormat }).format(dateObj);
} }
function formatMonth(date: DateValue) { function formatMonth(date: DateValue) {
const dateObj = date.toDate(getLocalTimeZone()); const dateObj = date.toDate(getLocalTimeZone());
if (typeof monthFormat === "function") return monthFormat(dateObj.getMonth() + 1); if (typeof monthFormat === 'function') return monthFormat(dateObj.getMonth() + 1);
return new DateFormatter(locale, { month: monthFormat }).format(dateObj); return new DateFormatter(locale, { month: monthFormat }).format(dateObj);
} }
</script> </script>
@@ -58,15 +58,15 @@
<CalendarYearSelect {years} {yearFormat} value={month.year} /> <CalendarYearSelect {years} {yearFormat} value={month.year} />
{/snippet} {/snippet}
{#if captionLayout === "dropdown"} {#if captionLayout === 'dropdown'}
{@render MonthSelect()} {@render MonthSelect()}
{@render YearSelect()} {@render YearSelect()}
{:else if captionLayout === "dropdown-months"} {:else if captionLayout === 'dropdown-months'}
{@render MonthSelect()} {@render MonthSelect()}
{#if placeholder} {#if placeholder}
{formatYear(placeholder)} {formatYear(placeholder)}
{/if} {/if}
{:else if captionLayout === "dropdown-years"} {:else if captionLayout === 'dropdown-years'}
{#if placeholder} {#if placeholder}
{formatMonth(placeholder)} {formatMonth(placeholder)}
{/if} {/if}
@@ -1,6 +1,6 @@
<script lang="ts"> <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 { let {
ref = $bindable(null), ref = $bindable(null),
@@ -12,7 +12,7 @@
<CalendarPrimitive.Cell <CalendarPrimitive.Cell
bind:ref bind:ref
class={cn( class={cn(
"relative size-(--cell-size) p-0 text-center text-sm focus-within:z-20 [&:first-child[data-selected]_[data-bits-day]]:rounded-s-(--cell-radius) [&:last-child[data-selected]_[data-bits-day]]:rounded-e-(--cell-radius)", 'relative size-(--cell-size) p-0 text-center text-sm focus-within:z-20 [&:first-child[data-selected]_[data-bits-day]]:rounded-s-(--cell-radius) [&:last-child[data-selected]_[data-bits-day]]:rounded-e-(--cell-radius)',
className className
)} )}
{...restProps} {...restProps}
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
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 { let {
ref = $bindable(null), ref = $bindable(null),
@@ -12,21 +12,21 @@
<CalendarPrimitive.Day <CalendarPrimitive.Day
bind:ref bind:ref
class={cn( class={cn(
"flex size-(--cell-size) flex-col items-center justify-center gap-1 rounded-(--cell-radius) p-0 leading-none font-normal whitespace-nowrap select-none", 'flex size-(--cell-size) flex-col items-center justify-center gap-1 rounded-(--cell-radius) p-0 leading-none font-normal whitespace-nowrap select-none',
"[&:last-child[data-selected=true]_button]:rounded-r-(--cell-radius)", '[&:last-child[data-selected=true]_button]:rounded-r-(--cell-radius)',
"not-data-selected:hover:bg-accent/50 not-data-selected:hover:text-accent-foreground", 'not-data-selected:hover:bg-accent/50 not-data-selected:hover:text-accent-foreground',
"[&[data-today]:not([data-selected])]:bg-accent [&[data-today]:not([data-selected])]:text-accent-foreground [&[data-today][data-disabled]]:text-muted-foreground", '[&[data-today]:not([data-selected])]:bg-accent [&[data-today]:not([data-selected])]:text-accent-foreground [&[data-today][data-disabled]]:text-muted-foreground',
"data-[selected]:bg-primary data-[selected]:text-primary-foreground data-[selected]:hover:text-foreground", 'data-[selected]:bg-primary data-[selected]:text-primary-foreground data-[selected]:hover:text-foreground',
// Outside months // Outside months
"[&[data-outside-month]:not([data-selected])]:text-muted-foreground [&[data-outside-month]:not([data-selected])]:hover:text-accent-foreground", '[&[data-outside-month]:not([data-selected])]:text-muted-foreground [&[data-outside-month]:not([data-selected])]:hover:text-accent-foreground',
// Disabled // Disabled
"data-[disabled]:text-muted-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", 'data-[disabled]:pointer-events-none data-[disabled]:text-muted-foreground data-[disabled]:opacity-50',
// Unavailable // Unavailable
"data-[unavailable]:text-muted-foreground data-[unavailable]:line-through", 'data-[unavailable]:text-muted-foreground data-[unavailable]:line-through',
// focus // focus
"focus:border-ring focus:ring-ring/50 focus:relative", 'focus:relative focus:border-ring focus:ring-ring/50',
// inner spans // inner spans
"[&>span]:text-xs [&>span]:opacity-70", '[&>span]:text-xs [&>span]:opacity-70',
className className
)} )}
{...restProps} {...restProps}
@@ -1,6 +1,6 @@
<script lang="ts"> <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 { let {
ref = $bindable(null), ref = $bindable(null),
@@ -1,6 +1,6 @@
<script lang="ts"> <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 { let {
ref = $bindable(null), ref = $bindable(null),
@@ -1,6 +1,6 @@
<script lang="ts"> <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 { let {
ref = $bindable(null), ref = $bindable(null),
@@ -9,4 +9,4 @@
}: CalendarPrimitive.GridRowProps = $props(); }: CalendarPrimitive.GridRowProps = $props();
</script> </script>
<CalendarPrimitive.GridRow bind:ref class={cn("flex", className)} {...restProps} /> <CalendarPrimitive.GridRow bind:ref class={cn('flex', className)} {...restProps} />
@@ -1,6 +1,6 @@
<script lang="ts"> <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 { let {
ref = $bindable(null), ref = $bindable(null),
@@ -11,6 +11,6 @@
<CalendarPrimitive.Grid <CalendarPrimitive.Grid
bind:ref bind:ref
class={cn("flex w-full border-collapse flex-col", className)} class={cn('flex w-full border-collapse flex-col', className)}
{...restProps} {...restProps}
/> />
@@ -1,6 +1,6 @@
<script lang="ts"> <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 { let {
ref = $bindable(null), ref = $bindable(null),
@@ -12,7 +12,7 @@
<CalendarPrimitive.HeadCell <CalendarPrimitive.HeadCell
bind:ref bind:ref
class={cn( class={cn(
"text-muted-foreground w-(--cell-size) rounded-md text-[0.8rem] font-normal", 'w-(--cell-size) rounded-md text-[0.8rem] font-normal text-muted-foreground',
className className
)} )}
{...restProps} {...restProps}
@@ -1,6 +1,6 @@
<script lang="ts"> <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 { let {
ref = $bindable(null), ref = $bindable(null),
@@ -12,7 +12,7 @@
<CalendarPrimitive.Header <CalendarPrimitive.Header
bind:ref bind:ref
class={cn( class={cn(
"flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium", 'flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium',
className className
)} )}
{...restProps} {...restProps}
@@ -1,6 +1,6 @@
<script lang="ts"> <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 { let {
ref = $bindable(null), ref = $bindable(null),
@@ -11,6 +11,6 @@
<CalendarPrimitive.Heading <CalendarPrimitive.Heading
bind:ref bind:ref
class={cn("px-(--cell-size) text-sm font-medium", className)} class={cn('px-(--cell-size) text-sm font-medium', className)}
{...restProps} {...restProps}
/> />
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { Calendar as CalendarPrimitive } from "bits-ui"; import { Calendar as CalendarPrimitive } from 'bits-ui';
import { cn, type WithoutChildrenOrChild } from "$lib/utils.js"; import { cn, type WithoutChildrenOrChild } from '$lib/utils.js';
import ChevronDownIcon from '@lucide/svelte/icons/chevron-down'; import ChevronDownIcon from '@lucide/svelte/icons/chevron-down';
let { let {
@@ -14,13 +14,13 @@
<span <span
class={cn( class={cn(
"has-focus:border-ring border-input has-focus:ring-ring/50 relative flex rounded-md border shadow-xs has-focus:ring-[3px]", 'relative flex rounded-md border border-input shadow-xs has-focus:border-ring has-focus:ring-[3px] has-focus:ring-ring/50',
className className
)} )}
> >
<CalendarPrimitive.MonthSelect <CalendarPrimitive.MonthSelect
bind:ref bind:ref
class="bg-background dark:bg-popover dark:text-popover-foreground absolute inset-0 opacity-0" class="absolute inset-0 bg-background opacity-0 dark:bg-popover dark:text-popover-foreground"
{...restProps} {...restProps}
> >
{#snippet child({ props, monthItems, selectedMonthItem })} {#snippet child({ props, monthItems, selectedMonthItem })}
@@ -37,11 +37,11 @@
{/each} {/each}
</select> </select>
<span <span
class="[&>svg]:text-muted-foreground flex h-(--cell-size) items-center gap-1 rounded-md ps-2 pe-1 text-sm font-medium select-none [&>svg]:size-3.5" class="flex h-(--cell-size) items-center gap-1 rounded-md ps-2 pe-1 text-sm font-medium select-none [&>svg]:size-3.5 [&>svg]:text-muted-foreground"
aria-hidden="true" aria-hidden="true"
> >
{monthItems.find((item) => item.value === value)?.label || selectedMonthItem.label} {monthItems.find((item) => item.value === value)?.label || selectedMonthItem.label}
<ChevronDownIcon class={cn("size-4", className)} /> <ChevronDownIcon class={cn('size-4', className)} />
</span> </span>
{/snippet} {/snippet}
</CalendarPrimitive.MonthSelect> </CalendarPrimitive.MonthSelect>
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { type WithElementRef, cn } from "$lib/utils.js"; import { type WithElementRef, cn } from '$lib/utils.js';
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -10,6 +10,6 @@
}: WithElementRef<HTMLAttributes<HTMLElement>> = $props(); }: WithElementRef<HTMLAttributes<HTMLElement>> = $props();
</script> </script>
<div {...restProps} bind:this={ref} class={cn("flex w-full flex-col gap-4", className)}> <div {...restProps} bind:this={ref} class={cn('flex w-full flex-col gap-4', className)}>
{@render children?.()} {@render children?.()}
</div> </div>
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -12,7 +12,7 @@
<div <div
bind:this={ref} bind:this={ref}
class={cn("relative flex flex-col gap-4 md:flex-row", className)} class={cn('relative flex flex-col gap-4 md:flex-row', className)}
{...restProps} {...restProps}
> >
{@render children?.()} {@render children?.()}
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -13,7 +13,7 @@
<nav <nav
{...restProps} {...restProps}
bind:this={ref} bind:this={ref}
class={cn("absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1", className)} class={cn('absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1', className)}
> >
{@render children?.()} {@render children?.()}
</nav> </nav>
@@ -1,14 +1,14 @@
<script lang="ts"> <script lang="ts">
import { Calendar as CalendarPrimitive } from "bits-ui"; import { Calendar as CalendarPrimitive } from 'bits-ui';
import ChevronRightIcon from '@lucide/svelte/icons/chevron-right'; import ChevronRightIcon from '@lucide/svelte/icons/chevron-right';
import { buttonVariants, type ButtonVariant } from "$lib/components/ui/button/index.js"; import { buttonVariants, type ButtonVariant } from '$lib/components/ui/button/index.js';
import { cn } from "$lib/utils.js"; import { cn } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
class: className, class: className,
children, children,
variant = "ghost", variant = 'ghost',
...restProps ...restProps
}: CalendarPrimitive.NextButtonProps & { }: CalendarPrimitive.NextButtonProps & {
variant?: ButtonVariant; variant?: ButtonVariant;
@@ -16,14 +16,14 @@
</script> </script>
{#snippet Fallback()} {#snippet Fallback()}
<ChevronRightIcon class={cn("size-4", className)} /> <ChevronRightIcon class={cn('size-4', className)} />
{/snippet} {/snippet}
<CalendarPrimitive.NextButton <CalendarPrimitive.NextButton
bind:ref bind:ref
class={cn( class={cn(
buttonVariants({ variant }), buttonVariants({ variant }),
"size-(--cell-size) bg-transparent p-0 select-none disabled:opacity-50 rtl:rotate-180", 'size-(--cell-size) bg-transparent p-0 select-none disabled:opacity-50 rtl:rotate-180',
className className
)} )}
{...restProps} {...restProps}
@@ -1,14 +1,14 @@
<script lang="ts"> <script lang="ts">
import { Calendar as CalendarPrimitive } from "bits-ui"; import { Calendar as CalendarPrimitive } from 'bits-ui';
import ChevronLeftIcon from '@lucide/svelte/icons/chevron-left'; import ChevronLeftIcon from '@lucide/svelte/icons/chevron-left';
import { buttonVariants, type ButtonVariant } from "$lib/components/ui/button/index.js"; import { buttonVariants, type ButtonVariant } from '$lib/components/ui/button/index.js';
import { cn } from "$lib/utils.js"; import { cn } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
class: className, class: className,
children, children,
variant = "ghost", variant = 'ghost',
...restProps ...restProps
}: CalendarPrimitive.PrevButtonProps & { }: CalendarPrimitive.PrevButtonProps & {
variant?: ButtonVariant; variant?: ButtonVariant;
@@ -16,14 +16,14 @@
</script> </script>
{#snippet Fallback()} {#snippet Fallback()}
<ChevronLeftIcon class={cn("size-4", className)} /> <ChevronLeftIcon class={cn('size-4', className)} />
{/snippet} {/snippet}
<CalendarPrimitive.PrevButton <CalendarPrimitive.PrevButton
bind:ref bind:ref
class={cn( class={cn(
buttonVariants({ variant }), buttonVariants({ variant }),
"size-(--cell-size) bg-transparent p-0 select-none disabled:opacity-50 rtl:rotate-180", 'size-(--cell-size) bg-transparent p-0 select-none disabled:opacity-50 rtl:rotate-180',
className className
)} )}
{...restProps} {...restProps}
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { Calendar as CalendarPrimitive } from "bits-ui"; import { Calendar as CalendarPrimitive } from 'bits-ui';
import { cn, type WithoutChildrenOrChild } from "$lib/utils.js"; import { cn, type WithoutChildrenOrChild } from '$lib/utils.js';
import ChevronDownIcon from '@lucide/svelte/icons/chevron-down'; import ChevronDownIcon from '@lucide/svelte/icons/chevron-down';
let { let {
@@ -13,13 +13,13 @@
<span <span
class={cn( class={cn(
"has-focus:border-ring border-input has-focus:ring-ring/50 relative flex rounded-md border shadow-xs has-focus:ring-[3px]", 'relative flex rounded-md border border-input shadow-xs has-focus:border-ring has-focus:ring-[3px] has-focus:ring-ring/50',
className className
)} )}
> >
<CalendarPrimitive.YearSelect <CalendarPrimitive.YearSelect
bind:ref bind:ref
class="dark:bg-popover dark:text-popover-foreground absolute inset-0 opacity-0" class="absolute inset-0 opacity-0 dark:bg-popover dark:text-popover-foreground"
{...restProps} {...restProps}
> >
{#snippet child({ props, yearItems, selectedYearItem })} {#snippet child({ props, yearItems, selectedYearItem })}
@@ -36,11 +36,11 @@
{/each} {/each}
</select> </select>
<span <span
class="[&>svg]:text-muted-foreground flex h-(--cell-size) items-center gap-1 rounded-md ps-2 pe-1 text-sm font-medium select-none [&>svg]:size-3.5" class="flex h-(--cell-size) items-center gap-1 rounded-md ps-2 pe-1 text-sm font-medium select-none [&>svg]:size-3.5 [&>svg]:text-muted-foreground"
aria-hidden="true" aria-hidden="true"
> >
{yearItems.find((item) => item.value === value)?.label || selectedYearItem.label} {yearItems.find((item) => item.value === value)?.label || selectedYearItem.label}
<ChevronDownIcon class={cn("size-4", className)} /> <ChevronDownIcon class={cn('size-4', className)} />
</span> </span>
{/snippet} {/snippet}
</CalendarPrimitive.YearSelect> </CalendarPrimitive.YearSelect>
+20 -20
View File
@@ -1,41 +1,41 @@
<script lang="ts"> <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, type WithoutChildrenOrChild } from "$lib/utils.js"; import { cn, type WithoutChildrenOrChild } from '$lib/utils.js';
import type { ButtonVariant } from "../button/button.svelte"; import type { ButtonVariant } from '../button/button.svelte';
import { isEqualMonth, type DateValue } from "@internationalized/date"; import { isEqualMonth, type DateValue } from '@internationalized/date';
import type { Snippet } from "svelte"; import type { Snippet } from 'svelte';
let { let {
ref = $bindable(null), ref = $bindable(null),
value = $bindable(), value = $bindable(),
placeholder = $bindable(), placeholder = $bindable(),
class: className, class: className,
weekdayFormat = "short", weekdayFormat = 'short',
buttonVariant = "ghost", buttonVariant = 'ghost',
captionLayout = "label", captionLayout = 'label',
locale = "en-US", locale = 'en-US',
months: monthsProp, months: monthsProp,
years, years,
monthFormat: monthFormatProp, monthFormat: monthFormatProp,
yearFormat = "numeric", yearFormat = 'numeric',
day, day,
disableDaysOutsideMonth = false, disableDaysOutsideMonth = false,
...restProps ...restProps
}: WithoutChildrenOrChild<CalendarPrimitive.RootProps> & { }: WithoutChildrenOrChild<CalendarPrimitive.RootProps> & {
buttonVariant?: ButtonVariant; buttonVariant?: ButtonVariant;
captionLayout?: "dropdown" | "dropdown-months" | "dropdown-years" | "label"; captionLayout?: 'dropdown' | 'dropdown-months' | 'dropdown-years' | 'label';
months?: CalendarPrimitive.MonthSelectProps["months"]; months?: CalendarPrimitive.MonthSelectProps['months'];
years?: CalendarPrimitive.YearSelectProps["years"]; years?: CalendarPrimitive.YearSelectProps['years'];
monthFormat?: CalendarPrimitive.MonthSelectProps["monthFormat"]; monthFormat?: CalendarPrimitive.MonthSelectProps['monthFormat'];
yearFormat?: CalendarPrimitive.YearSelectProps["yearFormat"]; yearFormat?: CalendarPrimitive.YearSelectProps['yearFormat'];
day?: Snippet<[{ day: DateValue; outsideMonth: boolean }]>; day?: Snippet<[{ day: DateValue; outsideMonth: boolean }]>;
} = $props(); } = $props();
const monthFormat = $derived.by(() => { const monthFormat = $derived.by(() => {
if (monthFormatProp) return monthFormatProp; if (monthFormatProp) return monthFormatProp;
if (captionLayout.startsWith("dropdown")) return "short"; if (captionLayout.startsWith('dropdown')) return 'short';
return "long"; return 'long';
}); });
</script> </script>
@@ -50,7 +50,7 @@ get along, so we shut typescript up by casting `value` to `never`.
{weekdayFormat} {weekdayFormat}
{disableDaysOutsideMonth} {disableDaysOutsideMonth}
class={cn( class={cn(
"p-2 [--cell-radius:var(--radius-md)] [--cell-size:--spacing(7)] bg-background group/calendar in-data-[slot=card-content]:bg-transparent in-data-[slot=popover-content]:bg-transparent", 'group/calendar bg-background p-2 [--cell-radius:var(--radius-md)] [--cell-size:--spacing(7)] in-data-[slot=card-content]:bg-transparent in-data-[slot=popover-content]:bg-transparent',
className className
)} )}
{locale} {locale}
@@ -97,7 +97,7 @@ get along, so we shut typescript up by casting `value` to `never`.
{#if day} {#if day}
{@render day({ {@render day({
day: date, day: date,
outsideMonth: !isEqualMonth(date, month.value), outsideMonth: !isEqualMonth(date, month.value)
})} })}
{:else} {:else}
<Calendar.Day /> <Calendar.Day />
+19 -19
View File
@@ -1,21 +1,21 @@
import Root from "./calendar.svelte"; 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 Grid from './calendar-grid.svelte';
import Header from "./calendar-header.svelte"; import Header from './calendar-header.svelte';
import Months from "./calendar-months.svelte"; import Months from './calendar-months.svelte';
import GridRow from "./calendar-grid-row.svelte"; import GridRow from './calendar-grid-row.svelte';
import Heading from "./calendar-heading.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 HeadCell from "./calendar-head-cell.svelte"; import HeadCell from './calendar-head-cell.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 MonthSelect from "./calendar-month-select.svelte"; import MonthSelect from './calendar-month-select.svelte';
import YearSelect from "./calendar-year-select.svelte"; import YearSelect from './calendar-year-select.svelte';
import Month from "./calendar-month.svelte"; import Month from './calendar-month.svelte';
import Nav from "./calendar-nav.svelte"; import Nav from './calendar-nav.svelte';
import Caption from "./calendar-caption.svelte"; import Caption from './calendar-caption.svelte';
export { export {
Day, Day,
@@ -36,5 +36,5 @@ export {
MonthSelect, MonthSelect,
Caption, Caption,
// //
Root as Calendar, Root as Calendar
}; };
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -14,7 +14,7 @@
bind:this={ref} bind:this={ref}
data-slot="card-action" data-slot="card-action"
class={cn( class={cn(
"cn-card-action col-start-2 row-span-2 row-start-1 self-start justify-self-end", 'cn-card-action col-start-2 row-span-2 row-start-1 self-start justify-self-end',
className className
)} )}
{...restProps} {...restProps}
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -13,7 +13,7 @@
<div <div
bind:this={ref} bind:this={ref}
data-slot="card-content" data-slot="card-content"
class={cn("px-4 group-data-[size=sm]/card:px-3", className)} class={cn('px-4 group-data-[size=sm]/card:px-3', className)}
{...restProps} {...restProps}
> >
{@render children?.()} {@render children?.()}
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -13,7 +13,7 @@
<p <p
bind:this={ref} bind:this={ref}
data-slot="card-description" data-slot="card-description"
class={cn("text-muted-foreground text-sm", className)} class={cn('text-sm text-muted-foreground', className)}
{...restProps} {...restProps}
> >
{@render children?.()} {@render children?.()}
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -13,7 +13,10 @@
<div <div
bind:this={ref} bind:this={ref}
data-slot="card-footer" data-slot="card-footer"
class={cn("bg-muted/50 rounded-b-xl border-t p-4 group-data-[size=sm]/card:p-3 flex items-center", className)} class={cn(
'flex items-center rounded-b-xl border-t bg-muted/50 p-4 group-data-[size=sm]/card:p-3',
className
)}
{...restProps} {...restProps}
> >
{@render children?.()} {@render children?.()}
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -14,7 +14,7 @@
bind:this={ref} bind:this={ref}
data-slot="card-header" data-slot="card-header"
class={cn( class={cn(
"gap-1 rounded-t-xl px-4 group-data-[size=sm]/card:px-3 [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3 group/card-header @container/card-header grid auto-rows-min items-start has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto]", 'group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-4 group-data-[size=sm]/card:px-3 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3',
className className
)} )}
{...restProps} {...restProps}
+3 -3
View File
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -13,7 +13,7 @@
<div <div
bind:this={ref} bind:this={ref}
data-slot="card-title" data-slot="card-title"
class={cn("text-base leading-snug font-medium group-data-[size=sm]/card:text-sm", className)} class={cn('text-base leading-snug font-medium group-data-[size=sm]/card:text-sm', className)}
{...restProps} {...restProps}
> >
{@render children?.()} {@render children?.()}
+8 -5
View File
@@ -1,21 +1,24 @@
<script lang="ts"> <script lang="ts">
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
class: className, class: className,
children, children,
size = "default", size = 'default',
...restProps ...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & { size?: "default" | "sm" } = $props(); }: WithElementRef<HTMLAttributes<HTMLDivElement>> & { size?: 'default' | 'sm' } = $props();
</script> </script>
<div <div
bind:this={ref} bind:this={ref}
data-slot="card" data-slot="card"
data-size={size} data-size={size}
class={cn("ring-foreground/10 bg-card text-card-foreground gap-4 overflow-hidden rounded-xl py-4 text-sm ring-1 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl group/card flex flex-col", className)} class={cn(
'group/card flex flex-col gap-4 overflow-hidden rounded-xl bg-card py-4 text-sm text-card-foreground ring-1 ring-foreground/10 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl',
className
)}
{...restProps} {...restProps}
> >
{@render children?.()} {@render children?.()}
+8 -8
View File
@@ -1,10 +1,10 @@
import Root from "./card.svelte"; import Root from './card.svelte';
import Content from "./card-content.svelte"; import Content from './card-content.svelte';
import Description from "./card-description.svelte"; import Description from './card-description.svelte';
import Footer from "./card-footer.svelte"; import Footer from './card-footer.svelte';
import Header from "./card-header.svelte"; import Header from './card-header.svelte';
import Title from "./card-title.svelte"; import Title from './card-title.svelte';
import Action from "./card-action.svelte"; import Action from './card-action.svelte';
export { export {
Root, Root,
@@ -21,5 +21,5 @@ export {
Footer as CardFooter, Footer as CardFooter,
Header as CardHeader, Header as CardHeader,
Title as CardTitle, Title as CardTitle,
Action as CardAction, Action as CardAction
}; };
@@ -1,8 +1,8 @@
<script lang="ts"> <script lang="ts">
import emblaCarouselSvelte from "embla-carousel-svelte"; import emblaCarouselSvelte from 'embla-carousel-svelte';
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
import { getEmblaContext } from "./context.js"; import { getEmblaContext } from './context.js';
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -11,7 +11,7 @@
...restProps ...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props(); }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
const emblaCtx = getEmblaContext("<Carousel.Content/>"); const emblaCtx = getEmblaContext('<Carousel.Content/>');
</script> </script>
<div <div
@@ -19,20 +19,20 @@
class="overflow-hidden" class="overflow-hidden"
use:emblaCarouselSvelte={{ use:emblaCarouselSvelte={{
options: { options: {
container: "[data-embla-container]", container: '[data-embla-container]',
slides: "[data-embla-slide]", slides: '[data-embla-slide]',
...emblaCtx.options, ...emblaCtx.options,
axis: emblaCtx.orientation === "horizontal" ? "x" : "y", axis: emblaCtx.orientation === 'horizontal' ? 'x' : 'y'
}, },
plugins: emblaCtx.plugins, plugins: emblaCtx.plugins
}} }}
onemblaInit={emblaCtx.onInit} onemblaInit={emblaCtx.onInit}
> >
<div <div
bind:this={ref} bind:this={ref}
class={cn( class={cn(
"flex", 'flex',
emblaCtx.orientation === "horizontal" ? "-ms-4" : "-mt-4 flex-col", emblaCtx.orientation === 'horizontal' ? '-ms-4' : '-mt-4 flex-col',
className className
)} )}
data-embla-container="" data-embla-container=""
@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
import { getEmblaContext } from "./context.js"; import { getEmblaContext } from './context.js';
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -10,7 +10,7 @@
...restProps ...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props(); }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
const emblaCtx = getEmblaContext("<Carousel.Item/>"); const emblaCtx = getEmblaContext('<Carousel.Item/>');
</script> </script>
<div <div
@@ -19,8 +19,8 @@
role="group" role="group"
aria-roledescription="slide" aria-roledescription="slide"
class={cn( class={cn(
"min-w-0 shrink-0 grow-0 basis-full", 'min-w-0 shrink-0 grow-0 basis-full',
emblaCtx.orientation === "horizontal" ? "ps-4" : "pt-4", emblaCtx.orientation === 'horizontal' ? 'ps-4' : 'pt-4',
className className
)} )}
data-embla-slide="" data-embla-slide=""
@@ -1,19 +1,19 @@
<script lang="ts"> <script lang="ts">
import type { WithoutChildren } from "bits-ui"; 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, type Props } from "$lib/components/ui/button/index.js"; import { Button, type Props } from '$lib/components/ui/button/index.js';
import ChevronRightIcon from '@lucide/svelte/icons/chevron-right'; import ChevronRightIcon from '@lucide/svelte/icons/chevron-right';
let { let {
ref = $bindable(null), ref = $bindable(null),
class: className, class: className,
variant = "outline", variant = 'outline',
size = "icon-sm", size = 'icon-sm',
...restProps ...restProps
}: WithoutChildren<Props> = $props(); }: WithoutChildren<Props> = $props();
const emblaCtx = getEmblaContext("<Carousel.Next/>"); const emblaCtx = getEmblaContext('<Carousel.Next/>');
</script> </script>
<Button <Button
@@ -23,10 +23,10 @@
aria-disabled={!emblaCtx.canScrollNext} aria-disabled={!emblaCtx.canScrollNext}
disabled={!emblaCtx.canScrollNext} disabled={!emblaCtx.canScrollNext}
class={cn( class={cn(
"rounded-full absolute touch-manipulation", 'absolute touch-manipulation rounded-full',
emblaCtx.orientation === "horizontal" emblaCtx.orientation === 'horizontal'
? "-end-12 top-1/2 -translate-y-1/2" ? '-end-12 top-1/2 -translate-y-1/2'
: "start-1/2 -bottom-12 -translate-x-1/2 rotate-90", : 'start-1/2 -bottom-12 -translate-x-1/2 rotate-90',
className className
)} )}
onclick={emblaCtx.scrollNext} onclick={emblaCtx.scrollNext}
@@ -1,19 +1,19 @@
<script lang="ts"> <script lang="ts">
import type { WithoutChildren } from "bits-ui"; 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, type Props } from "$lib/components/ui/button/index.js"; import { Button, type Props } from '$lib/components/ui/button/index.js';
import ChevronLeftIcon from '@lucide/svelte/icons/chevron-left'; import ChevronLeftIcon from '@lucide/svelte/icons/chevron-left';
let { let {
ref = $bindable(null), ref = $bindable(null),
class: className, class: className,
variant = "outline", variant = 'outline',
size = "icon-sm", size = 'icon-sm',
...restProps ...restProps
}: WithoutChildren<Props> = $props(); }: WithoutChildren<Props> = $props();
const emblaCtx = getEmblaContext("<Carousel.Previous/>"); const emblaCtx = getEmblaContext('<Carousel.Previous/>');
</script> </script>
<Button <Button
@@ -23,10 +23,10 @@
aria-disabled={!emblaCtx.canScrollPrev} aria-disabled={!emblaCtx.canScrollPrev}
disabled={!emblaCtx.canScrollPrev} disabled={!emblaCtx.canScrollPrev}
class={cn( class={cn(
"rounded-full absolute touch-manipulation", 'absolute touch-manipulation rounded-full',
emblaCtx.orientation === "horizontal" emblaCtx.orientation === 'horizontal'
? "-start-12 top-1/2 -translate-y-1/2" ? '-start-12 top-1/2 -translate-y-1/2'
: "start-1/2 -top-12 -translate-x-1/2 rotate-90", : 'start-1/2 -top-12 -translate-x-1/2 rotate-90',
className className
)} )}
onclick={emblaCtx.scrollPrev} onclick={emblaCtx.scrollPrev}
+10 -10
View File
@@ -3,16 +3,16 @@
type CarouselAPI, type CarouselAPI,
type CarouselProps, type CarouselProps,
type EmblaContext, type EmblaContext,
setEmblaContext, setEmblaContext
} from "./context.js"; } from './context.js';
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
opts = {}, opts = {},
plugins = [], plugins = [],
setApi = () => {}, setApi = () => {},
orientation = "horizontal", orientation = 'horizontal',
class: className, class: className,
children, children,
...restProps ...restProps
@@ -32,7 +32,7 @@
onInit, onInit,
scrollSnaps: [], scrollSnaps: [],
selectedIndex: 0, selectedIndex: 0,
scrollTo, scrollTo
}); });
setEmblaContext(carouselState); setEmblaContext(carouselState);
@@ -57,10 +57,10 @@
} }
function handleKeyDown(e: KeyboardEvent) { function handleKeyDown(e: KeyboardEvent) {
if (e.key === "ArrowLeft") { if (e.key === 'ArrowLeft') {
e.preventDefault(); e.preventDefault();
scrollPrev(); scrollPrev();
} else if (e.key === "ArrowRight") { } else if (e.key === 'ArrowRight') {
e.preventDefault(); e.preventDefault();
scrollNext(); scrollNext();
} }
@@ -71,13 +71,13 @@
setApi(carouselState.api); setApi(carouselState.api);
carouselState.scrollSnaps = carouselState.api.scrollSnapList(); carouselState.scrollSnaps = carouselState.api.scrollSnapList();
carouselState.api.on("select", onSelect); carouselState.api.on('select', onSelect);
onSelect(); onSelect();
} }
$effect(() => { $effect(() => {
return () => { return () => {
carouselState.api?.off("select", onSelect); carouselState.api?.off('select', onSelect);
}; };
}); });
</script> </script>
@@ -85,7 +85,7 @@
<div <div
bind:this={ref} bind:this={ref}
data-slot="carousel" data-slot="carousel"
class={cn("relative", className)} class={cn('relative', className)}
role="region" role="region"
aria-roledescription="carousel" aria-roledescription="carousel"
{...restProps} {...restProps}
+12 -12
View File
@@ -1,13 +1,13 @@
import type { WithElementRef } from "$lib/utils.js"; import type { WithElementRef } from '$lib/utils.js';
import type { import type {
EmblaCarouselSvelteType, EmblaCarouselSvelteType,
default as emblaCarouselSvelte, default as emblaCarouselSvelte
} from "embla-carousel-svelte"; } from 'embla-carousel-svelte';
import { getContext, hasContext, setContext } from "svelte"; import { getContext, hasContext, setContext } from 'svelte';
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
export type CarouselAPI = export type CarouselAPI =
NonNullable<NonNullable<EmblaCarouselSvelteType["$$_attributes"]>["on:emblaInit"]> extends ( NonNullable<NonNullable<EmblaCarouselSvelteType['$$_attributes']>['on:emblaInit']> extends (
evt: CustomEvent<infer CarouselAPI> evt: CustomEvent<infer CarouselAPI>
) => void ) => void
? CarouselAPI ? CarouselAPI
@@ -15,8 +15,8 @@ export type CarouselAPI =
type EmblaCarouselConfig = NonNullable<Parameters<typeof emblaCarouselSvelte>[1]>; type EmblaCarouselConfig = NonNullable<Parameters<typeof emblaCarouselSvelte>[1]>;
export type CarouselOptions = EmblaCarouselConfig["options"]; export type CarouselOptions = EmblaCarouselConfig['options'];
export type CarouselPlugins = EmblaCarouselConfig["plugins"]; export type CarouselPlugins = EmblaCarouselConfig['plugins'];
//// ////
@@ -24,14 +24,14 @@ export type CarouselProps = {
opts?: CarouselOptions; opts?: CarouselOptions;
plugins?: CarouselPlugins; plugins?: CarouselPlugins;
setApi?: (api: CarouselAPI | undefined) => void; setApi?: (api: CarouselAPI | undefined) => void;
orientation?: "horizontal" | "vertical"; orientation?: 'horizontal' | 'vertical';
} & WithElementRef<HTMLAttributes<HTMLDivElement>>; } & WithElementRef<HTMLAttributes<HTMLDivElement>>;
const EMBLA_CAROUSEL_CONTEXT = Symbol("EMBLA_CAROUSEL_CONTEXT"); const EMBLA_CAROUSEL_CONTEXT = Symbol('EMBLA_CAROUSEL_CONTEXT');
export type EmblaContext = { export type EmblaContext = {
api: CarouselAPI | undefined; api: CarouselAPI | undefined;
orientation: "horizontal" | "vertical"; orientation: 'horizontal' | 'vertical';
scrollNext: () => void; scrollNext: () => void;
scrollPrev: () => void; scrollPrev: () => void;
canScrollNext: boolean; canScrollNext: boolean;
@@ -50,7 +50,7 @@ export function setEmblaContext(config: EmblaContext): EmblaContext {
return config; return config;
} }
export function getEmblaContext(name = "This component") { export function getEmblaContext(name = 'This component') {
if (!hasContext(EMBLA_CAROUSEL_CONTEXT)) { if (!hasContext(EMBLA_CAROUSEL_CONTEXT)) {
throw new Error(`${name} must be used within a <Carousel.Root> component`); throw new Error(`${name} must be used within a <Carousel.Root> component`);
} }
+6 -6
View File
@@ -1,8 +1,8 @@
import Root from "./carousel.svelte"; 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 Previous from "./carousel-previous.svelte"; import Previous from './carousel-previous.svelte';
import Next from "./carousel-next.svelte"; import Next from './carousel-next.svelte';
export { export {
Root, Root,
@@ -15,5 +15,5 @@ export {
Content as CarouselContent, Content as CarouselContent,
Item as CarouselItem, Item as CarouselItem,
Previous as CarouselPrevious, Previous as CarouselPrevious,
Next as CarouselNext, Next as CarouselNext
}; };
@@ -1,8 +1,8 @@
<script lang="ts"> <script lang="ts">
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
import ChartStyle from "./chart-style.svelte"; import ChartStyle from './chart-style.svelte';
import { setChartContext, type ChartConfig } from "./chart-utils.js"; import { setChartContext, type ChartConfig } from './chart-utils.js';
const uid = $props.id(); const uid = $props.id();
@@ -17,12 +17,12 @@
config: ChartConfig; config: ChartConfig;
} = $props(); } = $props();
const chartId = $derived(`chart-${id || uid.replace(/:/g, "")}`); const chartId = $derived(`chart-${id || uid.replace(/:/g, '')}`);
setChartContext({ setChartContext({
get config() { get config() {
return config; return config;
}, }
}); });
</script> </script>
@@ -31,46 +31,46 @@
data-chart={chartId} data-chart={chartId}
data-slot="chart" data-slot="chart"
class={cn( class={cn(
"flex aspect-video justify-center overflow-visible text-xs", 'flex aspect-video justify-center overflow-visible text-xs',
// Overrides // Overrides
// //
// Stroke around dots/marks when hovering // Stroke around dots/marks when hovering
"[&_.lc-highlight-point]:stroke-transparent", '[&_.lc-highlight-point]:stroke-transparent',
// override the default stroke color of lines // override the default stroke color of lines
"[&_.lc-line]:stroke-border/50", '[&_.lc-line]:stroke-border/50',
// by default, layerchart shows a line intersecting the point when hovering, this hides that // by default, layerchart shows a line intersecting the point when hovering, this hides that
"[&_.lc-highlight-line]:stroke-0", '[&_.lc-highlight-line]:stroke-0',
// by default, when you hover a point on a stacked series chart, it will drop the opacity // by default, when you hover a point on a stacked series chart, it will drop the opacity
// of the other series, this overrides that // 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 [&_.lc-text-svg]:overflow-visible", '[&_.lc-area-path]:opacity-100 [&_.lc-highlight-line]:opacity-100 [&_.lc-highlight-point]:opacity-100 [&_.lc-spline-path]:opacity-100 [&_.lc-text]:text-xs [&_.lc-text-svg]:overflow-visible',
// We don't want the little tick lines between the axis labels and the chart, so we remove // 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 // the stroke. The alternative is to manually disable `tickMarks` on the x/y axis of every
// chart. // chart.
"[&_.lc-axis-tick]:stroke-0", '[&_.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 // 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 // a grid line there and rule ends up overlapping the marks because it is rendered after
// the marks // 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-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-x-radial-circle]:stroke-border [&_.lc-grid-x-radial-line]:stroke-border',
"[&_.lc-grid-y-radial-line]:stroke-border [&_.lc-grid-y-radial-circle]:stroke-border", '[&_.lc-grid-y-radial-circle]:stroke-border [&_.lc-grid-y-radial-line]:stroke-border',
// Legend adjustments // Legend adjustments
"[&_.lc-legend-swatch-button]:items-center [&_.lc-legend-swatch-button]:gap-1.5", '[&_.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-group]:items-center [&_.lc-legend-swatch-group]:gap-4',
"[&_.lc-legend-swatch]:size-2.5 [&_.lc-legend-swatch]:rounded-[2px]", '[&_.lc-legend-swatch]:size-2.5 [&_.lc-legend-swatch]:rounded-[2px]',
// Labels // Labels
"[&_.lc-labels-text:not([fill])]:fill-foreground [&_text]:stroke-transparent", '[&_.lc-labels-text:not([fill])]:fill-foreground [&_text]:stroke-transparent',
// Tick labels on th x/y axes // Tick labels on th x/y axes
"[&_.lc-axis-tick-label]:fill-muted-foreground [&_.lc-axis-tick-label]:font-normal", '[&_.lc-axis-tick-label]:fill-muted-foreground [&_.lc-axis-tick-label]:font-normal',
"[&_.lc-tooltip-rects-g]:fill-transparent", '[&_.lc-tooltip-rects-g]:fill-transparent',
"[&_.lc-layout-svg-g]:fill-transparent", '[&_.lc-layout-svg-g]:fill-transparent',
"[&_.lc-root-container]:w-full", '[&_.lc-root-container]:w-full',
className className
)} )}
{...restProps} {...restProps}
@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { THEMES, type ChartConfig } from "./chart-utils.js"; import { THEMES, type ChartConfig } from './chart-utils.js';
let { id, config }: { id: string; config: ChartConfig } = $props(); let { id, config }: { id: string; config: ChartConfig } = $props();
@@ -19,18 +19,18 @@
return color ? `\t--color-${key}: ${color};` : null; return color ? `\t--color-${key}: ${color};` : null;
}); });
content += color.join("\n") + "\n}"; content += color.join('\n') + '\n}';
themeContents.push(content); themeContents.push(content);
} }
return themeContents.join("\n"); return themeContents.join('\n');
}); });
</script> </script>
{#if themeContents} {#if themeContents}
{#key id} {#key id}
<svelte:element this={"style"}> <svelte:element this={'style'}>
{themeContents} {themeContents}
</svelte:element> </svelte:element>
{/key} {/key}
@@ -1,12 +1,11 @@
<script lang="ts"> <script lang="ts">
import { cn, type WithElementRef, type WithoutChildren } from "$lib/utils.js"; import { cn, type WithElementRef, type WithoutChildren } from '$lib/utils.js';
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
import { getPayloadConfigFromPayload, useChart, type TooltipPayload } from "./chart-utils.js"; import { getPayloadConfigFromPayload, useChart, type TooltipPayload } from './chart-utils.js';
import { getChartContext, Tooltip as TooltipPrimitive } from "layerchart"; import { getChartContext, Tooltip as TooltipPrimitive } from 'layerchart';
import type { Snippet } from "svelte"; import type { Snippet } from 'svelte';
// eslint-disable-next-line @typescript-eslint/no-explicit-any function defaultFormatter(value: unknown) {
function defaultFormatter(value: any, _payload: TooltipPayload[]) {
return `${value}`; return `${value}`;
} }
@@ -14,7 +13,7 @@
ref = $bindable(null), ref = $bindable(null),
class: className, class: className,
hideLabel = false, hideLabel = false,
indicator = "dot", indicator = 'dot',
hideIndicator = false, hideIndicator = false,
labelKey, labelKey,
label, label,
@@ -27,13 +26,14 @@
}: WithoutChildren<WithElementRef<HTMLAttributes<HTMLDivElement>>> & { }: WithoutChildren<WithElementRef<HTMLAttributes<HTMLDivElement>>> & {
hideLabel?: boolean; hideLabel?: boolean;
label?: string; label?: string;
indicator?: "line" | "dot" | "dashed"; indicator?: 'line' | 'dot' | 'dashed';
nameKey?: string; nameKey?: string;
labelKey?: string; labelKey?: string;
hideIndicator?: boolean; hideIndicator?: boolean;
labelClassName?: string; labelClassName?: string;
labelFormatter?: // eslint-disable-next-line @typescript-eslint/no-explicit-any labelFormatter?:
((value: any, payload: TooltipPayload[]) => string | number | Snippet) | null; | ((value: unknown, payload: TooltipPayload[]) => string | number | Snippet)
| null;
formatter?: Snippet< formatter?: Snippet<
[ [
{ {
@@ -42,7 +42,7 @@
item: TooltipPayload; item: TooltipPayload;
index: number; index: number;
payload: TooltipPayload[]; payload: TooltipPayload[];
}, }
] ]
>; >;
} = $props(); } = $props();
@@ -65,7 +65,7 @@
// Get the x-axis label value from the raw tooltip data (e.g. a Date or month string) // Get the x-axis label value from the raw tooltip data (e.g. a Date or month string)
const dataLabel = tooltipData != null ? chartCtx.x(tooltipData) : undefined; const dataLabel = tooltipData != null ? chartCtx.x(tooltipData) : undefined;
const key = labelKey ?? item?.label ?? item?.key ?? "value"; const key = labelKey ?? item?.label ?? item?.key ?? 'value';
const itemConfig = getPayloadConfigFromPayload( const itemConfig = getPayloadConfigFromPayload(
chart.config, chart.config,
item, item,
@@ -74,7 +74,7 @@
); );
let value: unknown; let value: unknown;
if (!labelKey && typeof label === "string") { if (!labelKey && typeof label === 'string') {
value = chart.config[label as keyof typeof chart.config]?.label ?? label; value = chart.config[label as keyof typeof chart.config]?.label ?? label;
} else if (labelKey) { } else if (labelKey) {
value = itemConfig?.label ?? dataLabel; value = itemConfig?.label ?? dataLabel;
@@ -87,13 +87,13 @@
return labelFormatter(value, visibleSeries); return labelFormatter(value, visibleSeries);
}); });
const nestLabel = $derived(visibleSeries.length === 1 && indicator !== "dot"); const nestLabel = $derived(visibleSeries.length === 1 && indicator !== 'dot');
</script> </script>
{#snippet TooltipLabel()} {#snippet TooltipLabel()}
{#if formattedLabel} {#if formattedLabel}
<div class={cn("font-medium", labelClassName)}> <div class={cn('font-medium', labelClassName)}>
{#if typeof formattedLabel === "function"} {#if typeof formattedLabel === 'function'}
{@render formattedLabel()} {@render formattedLabel()}
{:else} {:else}
{formattedLabel} {formattedLabel}
@@ -106,7 +106,7 @@
<div <div
bind:this={ref} bind:this={ref}
class={cn( 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", 'grid min-w-[9rem] items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl',
className className
)} )}
{...restProps} {...restProps}
@@ -116,7 +116,7 @@
{/if} {/if}
<div class="grid gap-1.5"> <div class="grid gap-1.5">
{#each visibleSeries as item, i (item.key + i)} {#each visibleSeries as item, i (item.key + i)}
{@const key = `${nameKey || item.key || item.label || "value"}`} {@const key = `${nameKey || item.key || item.label || 'value'}`}
{@const itemConfig = getPayloadConfigFromPayload( {@const itemConfig = getPayloadConfigFromPayload(
chart.config, chart.config,
item, item,
@@ -126,8 +126,8 @@
{@const indicatorColor = color || item.config?.color || item.color} {@const indicatorColor = color || item.config?.color || item.color}
<div <div
class={cn( class={cn(
"[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:size-2.5", 'flex w-full flex-wrap items-stretch gap-2 [&>svg]:size-2.5 [&>svg]:text-muted-foreground',
indicator === "dot" && "items-center" indicator === 'dot' && 'items-center'
)} )}
> >
{#if formatter && item.value !== undefined && item.label} {#if formatter && item.value !== undefined && item.label}
@@ -136,7 +136,7 @@
name: item.label, name: item.label,
item, item,
index: i, index: i,
payload: visibleSeries, payload: visibleSeries
})} })}
{:else} {:else}
{#if itemConfig?.icon} {#if itemConfig?.icon}
@@ -144,22 +144,18 @@
{:else if !hideIndicator} {:else if !hideIndicator}
<div <div
style="--color-bg: {indicatorColor}; --color-border: {indicatorColor};" style="--color-bg: {indicatorColor}; --color-border: {indicatorColor};"
class={cn( class={cn('shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)', {
"shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)", 'size-2.5': indicator === 'dot',
{ 'h-full w-1': indicator === 'line',
"size-2.5": indicator === "dot", 'w-0 border-[1.5px] border-dashed bg-transparent': indicator === 'dashed',
"h-full w-1": indicator === "line", 'my-0.5': nestLabel && indicator === 'dashed'
"w-0 border-[1.5px] border-dashed bg-transparent": })}
indicator === "dashed",
"my-0.5": nestLabel && indicator === "dashed",
}
)}
></div> ></div>
{/if} {/if}
<div <div
class={cn( class={cn(
"flex flex-1 shrink-0 justify-between leading-none", 'flex flex-1 shrink-0 justify-between leading-none',
nestLabel ? "items-end" : "items-center" nestLabel ? 'items-end' : 'items-center'
)} )}
> >
<div class="grid gap-1.5"> <div class="grid gap-1.5">
@@ -171,7 +167,7 @@
</span> </span>
</div> </div>
{#if item.value !== undefined} {#if item.value !== undefined}
<span class="text-foreground font-mono font-medium tabular-nums"> <span class="font-mono font-medium text-foreground tabular-nums">
{item.value.toLocaleString()} {item.value.toLocaleString()}
</span> </span>
{/if} {/if}
+9 -9
View File
@@ -1,7 +1,7 @@
import type { Tooltip } from "layerchart"; import type { Tooltip } from 'layerchart';
import { getContext, setContext, type Component, type Snippet } from "svelte"; import { getContext, setContext, type Component, type Snippet } from 'svelte';
export const THEMES = { light: "", dark: ".dark" } as const; export const THEMES = { light: '', dark: '.dark' } as const;
export type ChartConfig = { export type ChartConfig = {
[k in string]: { [k in string]: {
@@ -25,10 +25,10 @@ export function getPayloadConfigFromPayload(
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
data?: Record<string, any> | null data?: Record<string, any> | null
) { ) {
if (typeof payload !== "object" || payload === null) return undefined; if (typeof payload !== 'object' || payload === null) return undefined;
const payloadConfig = const payloadConfig =
"config" in payload && typeof payload.config === "object" && payload.config !== null 'config' in payload && typeof payload.config === 'object' && payload.config !== null
? payload.config ? payload.config
: undefined; : undefined;
@@ -38,15 +38,15 @@ export function getPayloadConfigFromPayload(
configLabelKey = payload.key; configLabelKey = payload.key;
} else if (payload.label === key) { } else if (payload.label === key) {
configLabelKey = payload.label; configLabelKey = payload.label;
} else if (key in payload && typeof payload[key as keyof typeof payload] === "string") { } else if (key in payload && typeof payload[key as keyof typeof payload] === 'string') {
configLabelKey = payload[key as keyof typeof payload] as string; configLabelKey = payload[key as keyof typeof payload] as string;
} else if ( } else if (
payloadConfig !== undefined && payloadConfig !== undefined &&
key in payloadConfig && key in payloadConfig &&
typeof payloadConfig[key as keyof typeof payloadConfig] === "string" typeof payloadConfig[key as keyof typeof payloadConfig] === 'string'
) { ) {
configLabelKey = payloadConfig[key as keyof typeof payloadConfig] as string; configLabelKey = payloadConfig[key as keyof typeof payloadConfig] as string;
} else if (data != null && key in data && typeof data[key] === "string") { } else if (data != null && key in data && typeof data[key] === 'string') {
configLabelKey = data[key] as string; configLabelKey = data[key] as string;
} }
@@ -57,7 +57,7 @@ type ChartContextValue = {
config: ChartConfig; config: ChartConfig;
}; };
const chartContextKey = Symbol("chart-context"); const chartContextKey = Symbol('chart-context');
export function setChartContext(value: ChartContextValue) { export function setChartContext(value: ChartContextValue) {
return setContext(chartContextKey, value); return setContext(chartContextKey, value);
+3 -3
View File
@@ -1,6 +1,6 @@
import ChartContainer from "./chart-container.svelte"; import ChartContainer from './chart-container.svelte';
import ChartTooltip from "./chart-tooltip.svelte"; import ChartTooltip from './chart-tooltip.svelte';
export { getPayloadConfigFromPayload, type ChartConfig } from "./chart-utils.js"; export { getPayloadConfigFromPayload, type ChartConfig } from './chart-utils.js';
export { ChartContainer, ChartTooltip, ChartContainer as Container, ChartTooltip as Tooltip }; export { ChartContainer, ChartTooltip, ChartContainer as Container, ChartTooltip as Tooltip };
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { Checkbox as CheckboxPrimitive } from "bits-ui"; import { Checkbox as CheckboxPrimitive } from 'bits-ui';
import { cn, type WithoutChildrenOrChild } from "$lib/utils.js"; import { cn, type WithoutChildrenOrChild } from '$lib/utils.js';
import CheckIcon from '@lucide/svelte/icons/check'; import CheckIcon from '@lucide/svelte/icons/check';
import MinusIcon from '@lucide/svelte/icons/minus'; import MinusIcon from '@lucide/svelte/icons/minus';
@@ -17,7 +17,7 @@
bind:ref bind:ref
data-slot="checkbox" data-slot="checkbox"
class={cn( class={cn(
"border-input dark:bg-input/30 data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary data-checked:border-primary aria-invalid:aria-checked:border-primary aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 flex size-4 items-center justify-center rounded-[4px] border transition-colors group-has-disabled/field:opacity-50 focus-visible:ring-3 aria-invalid:ring-3 peer relative shrink-0 outline-none after:absolute after:-inset-x-3 after:-inset-y-2 disabled:cursor-not-allowed disabled:opacity-50", 'peer relative flex size-4 shrink-0 items-center justify-center rounded-[4px] border border-input transition-colors outline-none group-has-disabled/field:opacity-50 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary',
className className
)} )}
bind:checked bind:checked
@@ -27,7 +27,7 @@
{#snippet children({ checked, indeterminate })} {#snippet children({ checked, indeterminate })}
<div <div
data-slot="checkbox-indicator" data-slot="checkbox-indicator"
class="[&>svg]:size-3.5 grid place-content-center text-current transition-none" class="grid place-content-center text-current transition-none [&>svg]:size-3.5"
> >
{#if checked} {#if checked}
<CheckIcon /> <CheckIcon />
+2 -2
View File
@@ -1,6 +1,6 @@
import Root from "./checkbox.svelte"; import Root from './checkbox.svelte';
export { export {
Root, Root,
// //
Root as Checkbox, Root as Checkbox
}; };

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