refactor: improve code consistency and formatting across components
CI / changes (push) Successful in 7s
CI / openapi (push) Has been skipped
CI / web (push) Successful in 29s
CI / go (push) Has been skipped
CI / docker-web (push) Successful in 1m22s
CI / docker-bird (push) Has been skipped
CI / bird2 (push) Has been skipped
CI / docker-go (push) Has been skipped

- Standardized formatting in various Svelte components for better readability.
- Updated README.md and .prettierrc for consistent styling and improved documentation.
- Refactored class attributes in UI components to enhance clarity and maintainability.
- Adjusted layout files to ensure proper alignment and spacing in the application interface.
This commit is contained in:
Denozordec
2026-05-20 00:37:00 +07:00
parent 6bee5adc34
commit c263fd5c7e
173 changed files with 1608 additions and 1015 deletions
+1 -4
View File
@@ -3,10 +3,7 @@
"singleQuote": true, "singleQuote": true,
"trailingComma": "none", "trailingComma": "none",
"printWidth": 100, "printWidth": 100,
"plugins": [ "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"],
"prettier-plugin-svelte",
"prettier-plugin-tailwindcss"
],
"tailwindStylesheet": "./src/routes/layout.css", "tailwindStylesheet": "./src/routes/layout.css",
"overrides": [ "overrides": [
{ {
+1 -5
View File
@@ -1,7 +1,3 @@
{ {
"recommendations": [ "recommendations": ["svelte.svelte-vscode", "bradlc.vscode-tailwindcss", "esbenp.prettier-vscode"]
"svelte.svelte-vscode",
"bradlc.vscode-tailwindcss",
"esbenp.prettier-vscode"
]
} }
+6 -6
View File
@@ -6,12 +6,12 @@ SvelteKit-приложение панели управления EvoBGP. Зап
## UI-архитектура ## UI-архитектура
| Путь | Назначение | | Путь | Назначение |
|------|------------| | ------------------------ | ------------------------------------------------------------------- |
| `src/lib/ui/core/` | shadcn-svelte (только через `npx shadcn-svelte@latest add … -y -o`) | | `src/lib/ui/core/` | shadcn-svelte (только через `npx shadcn-svelte@latest add … -y -o`) |
| `src/lib/ui/app/` | Layout, PageHeader, toast (`notify`) | | `src/lib/ui/app/` | Layout, PageHeader, toast (`notify`) |
| `src/lib/ui/patterns/` | FormField, AppDataTable, ConfirmDialog, EmptyState | | `src/lib/ui/patterns/` | FormField, AppDataTable, ConfirmDialog, EmptyState |
| `src/lib/components/ui/` | Re-export на `ui/core` (совместимость) | | `src/lib/components/ui/` | Re-export на `ui/core` (совместимость) |
Токены темы: `src/routes/layout.css`. Справочник: `src/lib/ui/app/tokens.md`. Токены темы: `src/routes/layout.css`. Справочник: `src/lib/ui/app/tokens.md`.
@@ -1,7 +1,13 @@
<script lang="ts"> <script lang="ts">
import type { RevisionDiff, RevisionPrefix, RevisionRow } from '$lib/api/types.js'; import type { RevisionDiff, RevisionPrefix, RevisionRow } from '$lib/api/types.js';
import { Button } from '$lib/components/ui/button/index.js'; import { Button } from '$lib/components/ui/button/index.js';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '$lib/components/ui/card/index.js'; import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle
} from '$lib/components/ui/card/index.js';
import { sortRevisionDiffItems } from '$lib/sort-prefixes.js'; import { sortRevisionDiffItems } from '$lib/sort-prefixes.js';
type Props = { type Props = {
@@ -79,7 +85,7 @@
<select <select
value={diffRevA} value={diffRevA}
onchange={(e) => onDiffRevAChange((e.currentTarget as HTMLSelectElement).value)} onchange={(e) => onDiffRevAChange((e.currentTarget as HTMLSelectElement).value)}
class="border-border bg-background h-8 min-w-0 flex-1 rounded-md border px-2 text-sm" class="h-8 min-w-0 flex-1 rounded-md border border-border bg-background px-2 text-sm"
> >
<option value="">Ревизия A</option> <option value="">Ревизия A</option>
{#each revisions as rev (rev.id)} {#each revisions as rev (rev.id)}
@@ -89,51 +95,58 @@
<select <select
value={diffRevB} value={diffRevB}
onchange={(e) => onDiffRevBChange((e.currentTarget as HTMLSelectElement).value)} onchange={(e) => onDiffRevBChange((e.currentTarget as HTMLSelectElement).value)}
class="border-border bg-background h-8 min-w-0 flex-1 rounded-md border px-2 text-sm" class="h-8 min-w-0 flex-1 rounded-md border border-border bg-background px-2 text-sm"
> >
<option value="">Ревизия B</option> <option value="">Ревизия B</option>
{#each revisions as rev (rev.id)} {#each revisions as rev (rev.id)}
<option value={rev.id}>{rev.id.slice(0, 8)} ({formatDate(rev.created_at)})</option> <option value={rev.id}>{rev.id.slice(0, 8)} ({formatDate(rev.created_at)})</option>
{/each} {/each}
</select> </select>
<Button size="sm" class="shrink-0 self-start sm:self-auto" onclick={onLoadDiff} disabled={diffLoading}> <Button
size="sm"
class="shrink-0 self-start sm:self-auto"
onclick={onLoadDiff}
disabled={diffLoading}
>
{diffLoading ? 'Загрузка…' : 'Сравнить'} {diffLoading ? 'Загрузка…' : 'Сравнить'}
</Button> </Button>
</div> </div>
{#if diffData} {#if diffData}
<div <div
class="border-border grid min-h-0 grid-cols-1 overflow-hidden rounded-md border sm:grid-cols-2" class="grid min-h-0 grid-cols-1 overflow-hidden rounded-md border border-border sm:grid-cols-2"
style="scrollbar-gutter: stable;" style="scrollbar-gutter: stable;"
> >
<!-- + Добавлено --> <!-- + Добавлено -->
<div class="border-border flex min-h-0 min-w-0 flex-col border-b sm:border-r sm:border-b-0"> <div class="flex min-h-0 min-w-0 flex-col border-b border-border sm:border-r sm:border-b-0">
<div <div
class="border-border bg-muted/60 flex shrink-0 items-center border-b px-3 py-2 font-mono text-xs font-semibold text-green-600 dark:text-green-400" class="flex shrink-0 items-center border-b border-border bg-muted/60 px-3 py-2 font-mono text-xs font-semibold text-green-600 dark:text-green-400"
> >
<span class="text-muted-foreground mr-2 w-10 shrink-0 select-none text-right">+</span> <span class="mr-2 w-10 shrink-0 text-right text-muted-foreground select-none">+</span>
<span>Добавлено ({addedSorted.length})</span> <span>Добавлено ({addedSorted.length})</span>
</div> </div>
<div <div
bind:this={scrollAddedEl} bind:this={scrollAddedEl}
class="bg-background/50 max-h-[min(28rem,70vh)] min-h-0 overflow-x-auto overflow-y-scroll overscroll-contain" class="max-h-[min(28rem,70vh)] min-h-0 overflow-x-auto overflow-y-scroll overscroll-contain bg-background/50"
onscroll={syncFromAdded} onscroll={syncFromAdded}
> >
{#if addedSorted.length === 0} {#if addedSorted.length === 0}
<p class="text-muted-foreground p-3 font-mono text-xs">Нет изменений</p> <p class="p-3 font-mono text-xs text-muted-foreground">Нет изменений</p>
{:else} {:else}
<table class="w-full border-collapse font-mono text-xs"> <table class="w-full border-collapse font-mono text-xs">
<tbody> <tbody>
{#each addedSorted as line, i (`a-${i}-${line}`)} {#each addedSorted as line, i (`a-${i}-${line}`)}
<tr <tr
class="border-border/50 hover:bg-muted/30 border-b border-l-2 border-l-green-500/80 bg-green-500/[0.08] dark:bg-green-500/15" class="border-b border-l-2 border-border/50 border-l-green-500/80 bg-green-500/[0.08] hover:bg-muted/30 dark:bg-green-500/15"
> >
<td <td
class="text-muted-foreground w-10 shrink-0 border-r border-transparent py-0.5 pr-1 pl-2 text-right align-top text-[11px] tabular-nums select-none" class="w-10 shrink-0 border-r border-transparent py-0.5 pr-1 pl-2 text-right align-top text-[11px] text-muted-foreground tabular-nums select-none"
> >
{i + 1} {i + 1}
</td> </td>
<td class="text-foreground max-w-0 break-all py-0.5 pr-3 pl-1 whitespace-pre-wrap"> <td
class="max-w-0 py-0.5 pr-3 pl-1 break-all whitespace-pre-wrap text-foreground"
>
{line} {line}
</td> </td>
</tr> </tr>
@@ -147,31 +160,33 @@
<!-- Удалено --> <!-- Удалено -->
<div class="flex min-h-0 min-w-0 flex-col"> <div class="flex min-h-0 min-w-0 flex-col">
<div <div
class="border-border bg-muted/60 flex shrink-0 items-center border-b px-3 py-2 font-mono text-xs font-semibold text-red-600 dark:text-red-400" class="flex shrink-0 items-center border-b border-border bg-muted/60 px-3 py-2 font-mono text-xs font-semibold text-red-600 dark:text-red-400"
> >
<span class="text-muted-foreground mr-2 w-10 shrink-0 select-none text-right"></span> <span class="mr-2 w-10 shrink-0 text-right text-muted-foreground select-none"></span>
<span>Удалено ({removedSorted.length})</span> <span>Удалено ({removedSorted.length})</span>
</div> </div>
<div <div
bind:this={scrollRemovedEl} bind:this={scrollRemovedEl}
class="bg-background/50 max-h-[min(28rem,70vh)] min-h-0 overflow-x-auto overflow-y-scroll overscroll-contain" class="max-h-[min(28rem,70vh)] min-h-0 overflow-x-auto overflow-y-scroll overscroll-contain bg-background/50"
onscroll={syncFromRemoved} onscroll={syncFromRemoved}
> >
{#if removedSorted.length === 0} {#if removedSorted.length === 0}
<p class="text-muted-foreground p-3 font-mono text-xs">Нет изменений</p> <p class="p-3 font-mono text-xs text-muted-foreground">Нет изменений</p>
{:else} {:else}
<table class="w-full border-collapse font-mono text-xs"> <table class="w-full border-collapse font-mono text-xs">
<tbody> <tbody>
{#each removedSorted as line, i (`r-${i}-${line}`)} {#each removedSorted as line, i (`r-${i}-${line}`)}
<tr <tr
class="border-border/50 hover:bg-muted/30 border-b border-l-2 border-l-red-500/80 bg-red-500/[0.08] dark:bg-red-500/15" class="border-b border-l-2 border-border/50 border-l-red-500/80 bg-red-500/[0.08] hover:bg-muted/30 dark:bg-red-500/15"
> >
<td <td
class="text-muted-foreground w-10 shrink-0 border-r border-transparent py-0.5 pr-1 pl-2 text-right align-top text-[11px] tabular-nums select-none" class="w-10 shrink-0 border-r border-transparent py-0.5 pr-1 pl-2 text-right align-top text-[11px] text-muted-foreground tabular-nums select-none"
> >
{i + 1} {i + 1}
</td> </td>
<td class="text-foreground max-w-0 break-all py-0.5 pr-3 pl-1 whitespace-pre-wrap"> <td
class="max-w-0 py-0.5 pr-3 pl-1 break-all whitespace-pre-wrap text-foreground"
>
{line} {line}
</td> </td>
</tr> </tr>
@@ -61,7 +61,7 @@
<div class="space-y-3 rounded-lg border border-border/80 bg-muted/15 p-3"> <div class="space-y-3 rounded-lg border border-border/80 bg-muted/15 p-3">
<div class="flex flex-wrap items-center gap-2"> <div class="flex flex-wrap items-center gap-2">
<Filter class="text-muted-foreground size-4 shrink-0" aria-hidden="true" /> <Filter class="size-4 shrink-0 text-muted-foreground" aria-hidden="true" />
<span class="text-sm font-medium">Фильтры задач</span> <span class="text-sm font-medium">Фильтры задач</span>
<div class="ml-auto flex flex-wrap gap-1.5"> <div class="ml-auto flex flex-wrap gap-1.5">
<Button <Button
@@ -84,7 +84,14 @@
> >
В работе В работе
</Button> </Button>
<Button type="button" variant="ghost" size="sm" class="h-7 text-xs" {disabled} onclick={onResetFilters}> <Button
type="button"
variant="ghost"
size="sm"
class="h-7 text-xs"
{disabled}
onclick={onResetFilters}
>
<X class="size-3.5" aria-hidden="true" /> <X class="size-3.5" aria-hidden="true" />
Сброс Сброс
</Button> </Button>
@@ -93,7 +100,7 @@
<div class="relative"> <div class="relative">
<Search <Search
class="text-muted-foreground pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2" class="pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground"
aria-hidden="true" aria-hidden="true"
/> />
<Input <Input
@@ -113,7 +120,7 @@
<Label for="job-filter-status" class="text-xs">Статус</Label> <Label for="job-filter-status" class="text-xs">Статус</Label>
<select <select
id="job-filter-status" id="job-filter-status"
class="border-border bg-background h-9 w-full rounded-md border px-2 text-sm" class="h-9 w-full rounded-md border border-border bg-background px-2 text-sm"
value={filterStatus} value={filterStatus}
onchange={(e) => onFilterStatusChange((e.currentTarget as HTMLSelectElement).value)} onchange={(e) => onFilterStatusChange((e.currentTarget as HTMLSelectElement).value)}
{disabled} {disabled}
@@ -127,7 +134,7 @@
<Label for="job-filter-kind" class="text-xs">Тип задачи</Label> <Label for="job-filter-kind" class="text-xs">Тип задачи</Label>
<select <select
id="job-filter-kind" id="job-filter-kind"
class="border-border bg-background h-9 w-full rounded-md border px-2 text-sm" class="h-9 w-full rounded-md border border-border bg-background px-2 text-sm"
value={filterKind} value={filterKind}
onchange={(e) => onFilterKindChange((e.currentTarget as HTMLSelectElement).value)} onchange={(e) => onFilterKindChange((e.currentTarget as HTMLSelectElement).value)}
{disabled} {disabled}
@@ -141,7 +148,7 @@
<Label for="job-filter-module" class="text-xs">Модуль (локально)</Label> <Label for="job-filter-module" class="text-xs">Модуль (локально)</Label>
<select <select
id="job-filter-module" id="job-filter-module"
class="border-border bg-background h-9 w-full rounded-md border px-2 text-sm" class="h-9 w-full rounded-md border border-border bg-background px-2 text-sm"
value={filterModuleId} value={filterModuleId}
onchange={(e) => onFilterModuleIdChange((e.currentTarget as HTMLSelectElement).value)} onchange={(e) => onFilterModuleIdChange((e.currentTarget as HTMLSelectElement).value)}
{disabled} {disabled}
@@ -87,7 +87,7 @@
<CardDescription class="flex flex-wrap items-center gap-x-2 gap-y-1"> <CardDescription class="flex flex-wrap items-center gap-x-2 gap-y-1">
<span>Фоновые задачи (ingest, применение, обновление)</span> <span>Фоновые задачи (ingest, применение, обновление)</span>
{#if jobsFetchedTotal !== undefined} {#if jobsFetchedTotal !== undefined}
<span class="text-muted-foreground font-normal tabular-nums"> <span class="font-normal text-muted-foreground tabular-nums">
· Показано {jobs.length} из {jobsFetchedTotal} · Показано {jobs.length} из {jobsFetchedTotal}
</span> </span>
{/if} {/if}
@@ -163,20 +163,18 @@
<div <div
class="rounded-md border border-chart-1/25 bg-chart-1/5 px-2.5 py-2 dark:bg-chart-1/10" class="rounded-md border border-chart-1/25 bg-chart-1/5 px-2.5 py-2 dark:bg-chart-1/10"
> >
<p <p class="flex items-center gap-1 text-[11px] font-medium text-chart-1 uppercase">
class="text-chart-1 flex items-center gap-1 text-[11px] font-medium uppercase"
>
<CircleDot class="size-3.5 shrink-0" aria-hidden="true" /> <CircleDot class="size-3.5 shrink-0" aria-hidden="true" />
Статус Статус
</p> </p>
<Badge class="mt-1" variant={jobStatusVariant(job.status)}>{jobStatusRu(job.status)}</Badge> <Badge class="mt-1" variant={jobStatusVariant(job.status)}
>{jobStatusRu(job.status)}</Badge
>
</div> </div>
<div <div
class="rounded-md border border-chart-2/25 bg-chart-2/5 px-2.5 py-2 dark:bg-chart-2/10" class="rounded-md border border-chart-2/25 bg-chart-2/5 px-2.5 py-2 dark:bg-chart-2/10"
> >
<p <p class="flex items-center gap-1 text-[11px] font-medium text-chart-2 uppercase">
class="text-chart-2 flex items-center gap-1 text-[11px] font-medium uppercase"
>
<CalendarClock class="size-3.5 shrink-0" aria-hidden="true" /> <CalendarClock class="size-3.5 shrink-0" aria-hidden="true" />
Создана Создана
</p> </p>
@@ -185,9 +183,7 @@
<div <div
class="rounded-md border border-chart-3/25 bg-chart-3/5 px-2.5 py-2 dark:bg-chart-3/10" class="rounded-md border border-chart-3/25 bg-chart-3/5 px-2.5 py-2 dark:bg-chart-3/10"
> >
<p <p class="flex items-center gap-1 text-[11px] font-medium text-chart-3 uppercase">
class="text-chart-3 flex items-center gap-1 text-[11px] font-medium uppercase"
>
<PlayCircle class="size-3.5 shrink-0" aria-hidden="true" /> <PlayCircle class="size-3.5 shrink-0" aria-hidden="true" />
Запущена Запущена
</p> </p>
@@ -196,9 +192,7 @@
<div <div
class="rounded-md border border-chart-4/25 bg-chart-4/5 px-2.5 py-2 dark:bg-chart-4/10" class="rounded-md border border-chart-4/25 bg-chart-4/5 px-2.5 py-2 dark:bg-chart-4/10"
> >
<p <p class="flex items-center gap-1 text-[11px] font-medium text-chart-4 uppercase">
class="text-chart-4 flex items-center gap-1 text-[11px] font-medium uppercase"
>
<Flag class="size-3.5 shrink-0" aria-hidden="true" /> <Flag class="size-3.5 shrink-0" aria-hidden="true" />
Завершена Завершена
</p> </p>
@@ -343,7 +337,7 @@
class="min-w-0 overflow-hidden rounded-md border border-chart-1/30 bg-chart-1/5 p-2.5 dark:bg-chart-1/10" class="min-w-0 overflow-hidden rounded-md border border-chart-1/30 bg-chart-1/5 p-2.5 dark:bg-chart-1/10"
> >
<p <p
class="text-chart-1 flex items-center gap-1 text-[11px] font-medium uppercase" class="flex items-center gap-1 text-[11px] font-medium text-chart-1 uppercase"
> >
<Layers class="size-3.5 shrink-0" aria-hidden="true" /> <Layers class="size-3.5 shrink-0" aria-hidden="true" />
Агрегация Агрегация
@@ -354,7 +348,7 @@
class="min-w-0 overflow-hidden rounded-md border border-chart-2/30 bg-chart-2/5 p-2.5 dark:bg-chart-2/10" class="min-w-0 overflow-hidden rounded-md border border-chart-2/30 bg-chart-2/5 p-2.5 dark:bg-chart-2/10"
> >
<p <p
class="text-chart-2 flex items-center gap-1 text-[11px] font-medium uppercase" class="flex items-center gap-1 text-[11px] font-medium text-chart-2 uppercase"
> >
<Globe class="size-3.5 shrink-0" aria-hidden="true" /> <Globe class="size-3.5 shrink-0" aria-hidden="true" />
Домены Домены
@@ -365,7 +359,7 @@
class="min-w-0 overflow-hidden rounded-md border border-chart-3/30 bg-chart-3/5 p-2.5 dark:bg-chart-3/10" class="min-w-0 overflow-hidden rounded-md border border-chart-3/30 bg-chart-3/5 p-2.5 dark:bg-chart-3/10"
> >
<p <p
class="text-chart-3 flex items-center gap-1 text-[11px] font-medium uppercase" class="flex items-center gap-1 text-[11px] font-medium text-chart-3 uppercase"
> >
<Binary class="size-3.5 shrink-0" aria-hidden="true" /> <Binary class="size-3.5 shrink-0" aria-hidden="true" />
ASN ASN
@@ -376,7 +370,7 @@
class="min-w-0 overflow-hidden rounded-md border border-chart-4/30 bg-chart-4/5 p-2.5 dark:bg-chart-4/10" class="min-w-0 overflow-hidden rounded-md border border-chart-4/30 bg-chart-4/5 p-2.5 dark:bg-chart-4/10"
> >
<p <p
class="text-chart-4 flex items-center gap-1 text-[11px] font-medium uppercase" class="flex items-center gap-1 text-[11px] font-medium text-chart-4 uppercase"
> >
<Link2 class="size-3.5 shrink-0" aria-hidden="true" /> <Link2 class="size-3.5 shrink-0" aria-hidden="true" />
CDN / IP-диапазоны CDN / IP-диапазоны
@@ -427,7 +421,9 @@
/> />
</div> </div>
<div class="min-w-0 space-y-1.5 rounded-lg border p-3"> <div class="min-w-0 space-y-1.5 rounded-lg border p-3">
<p class="text-sm font-medium">IP-диапазоны: итог по статическим диапазонам</p> <p class="text-sm font-medium">
IP-диапазоны: итог по статическим диапазонам
</p>
<JobReportTableBlock <JobReportTableBlock
rows={jobReport.ipRanges as RowData[]} rows={jobReport.ipRanges as RowData[]}
columns={reportCols} columns={reportCols}
@@ -18,7 +18,9 @@
onReload: () => void; onReload: () => void;
onRefreshBirdStatus: () => void; onRefreshBirdStatus: () => void;
onOpenBirdProtocols: () => void; onOpenBirdProtocols: () => void;
birdHealthyBadgeVariant: (h: boolean | null | undefined) => 'default' | 'secondary' | 'outline' | 'destructive'; birdHealthyBadgeVariant: (
h: boolean | null | undefined
) => 'default' | 'secondary' | 'outline' | 'destructive';
birdHealthyShortLabel: (h: boolean | null | undefined) => string; birdHealthyShortLabel: (h: boolean | null | undefined) => string;
}; };
@@ -38,24 +40,28 @@
<div class="grid grid-cols-1 items-stretch gap-3 sm:grid-cols-2 xl:grid-cols-3"> <div class="grid grid-cols-1 items-stretch gap-3 sm:grid-cols-2 xl:grid-cols-3">
<Card <Card
class={cn( class={cn('min-w-0 overflow-hidden border-l-4 border-l-chart-1 bg-chart-1/5 p-4 shadow-sm')}
'min-w-0 overflow-hidden border-l-4 border-l-chart-1 bg-chart-1/5 p-4 shadow-sm'
)}
> >
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between"> <div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
<div class="flex min-w-0 flex-1 gap-3"> <div class="flex min-w-0 flex-1 gap-3">
<div <div
class="bg-chart-1/20 flex size-11 shrink-0 items-center justify-center rounded-xl" class="flex size-11 shrink-0 items-center justify-center rounded-xl bg-chart-1/20"
aria-hidden="true" aria-hidden="true"
> >
<Play class="text-chart-1 size-5" /> <Play class="size-5 text-chart-1" />
</div> </div>
<div class="min-w-0 flex-1 space-y-1"> <div class="min-w-0 flex-1 space-y-1">
<p class="font-semibold">Применить ко всем спикерам</p> <p class="font-semibold">Применить ко всем спикерам</p>
<p class="text-muted-foreground max-w-[42ch] text-sm">Применить текущую конфигурацию на всех BIRD-спикерах</p> <p class="max-w-[42ch] text-sm text-muted-foreground">
Применить текущую конфигурацию на всех BIRD-спикерах
</p>
</div> </div>
</div> </div>
<Button class="w-full shrink-0 self-start sm:w-auto sm:self-auto" onclick={onApply} disabled={applying}> <Button
class="w-full shrink-0 self-start sm:w-auto sm:self-auto"
onclick={onApply}
disabled={applying}
>
<Play class="size-4" aria-hidden="true" /> <Play class="size-4" aria-hidden="true" />
Применить Применить
</Button> </Button>
@@ -63,21 +69,21 @@
</Card> </Card>
<Card <Card
class={cn( class={cn('min-w-0 overflow-hidden border-l-4 border-l-chart-4 bg-chart-4/5 p-4 shadow-sm')}
'min-w-0 overflow-hidden border-l-4 border-l-chart-4 bg-chart-4/5 p-4 shadow-sm'
)}
> >
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between"> <div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
<div class="flex min-w-0 flex-1 gap-3"> <div class="flex min-w-0 flex-1 gap-3">
<div <div
class="bg-chart-4/20 flex size-11 shrink-0 items-center justify-center rounded-xl" class="flex size-11 shrink-0 items-center justify-center rounded-xl bg-chart-4/20"
aria-hidden="true" aria-hidden="true"
> >
<RotateCcw class="text-chart-4 size-5" /> <RotateCcw class="size-5 text-chart-4" />
</div> </div>
<div class="min-w-0 flex-1 space-y-1"> <div class="min-w-0 flex-1 space-y-1">
<p class="font-semibold">Перезагрузка BIRD</p> <p class="font-semibold">Перезагрузка BIRD</p>
<p class="text-muted-foreground max-w-[42ch] text-sm">Перезагрузить конфигурацию BIRD на всех спикерах</p> <p class="max-w-[42ch] text-sm text-muted-foreground">
Перезагрузить конфигурацию BIRD на всех спикерах
</p>
</div> </div>
</div> </div>
<Button <Button
@@ -101,26 +107,32 @@
<div class="min-w-0 flex-1 space-y-1"> <div class="min-w-0 flex-1 space-y-1">
<div class="flex flex-wrap items-center gap-2"> <div class="flex flex-wrap items-center gap-2">
<div <div
class="bg-info/15 mr-0.5 flex size-9 items-center justify-center rounded-lg" class="mr-0.5 flex size-9 items-center justify-center rounded-lg bg-info/15"
aria-hidden="true" aria-hidden="true"
> >
<Bird class="text-info size-5 shrink-0" /> <Bird class="size-5 shrink-0 text-info" />
</div> </div>
<p class="font-semibold">Состояние BIRD</p> <p class="font-semibold">Состояние BIRD</p>
{#if birdStatus} {#if birdStatus}
<Badge variant={birdHealthyBadgeVariant(birdStatus.healthy)}>{birdHealthyShortLabel(birdStatus.healthy)}</Badge> <Badge variant={birdHealthyBadgeVariant(birdStatus.healthy)}
>{birdHealthyShortLabel(birdStatus.healthy)}</Badge
>
{/if} {/if}
</div> </div>
<p class="text-foreground/80 max-w-[56ch] text-sm"> <p class="max-w-[56ch] text-sm text-foreground/80">
Локально на хосте API: <code class="bg-muted rounded px-1 text-xs">birdc show protocols</code>. Не заменяет мониторинг спикеров. Локально на хосте API: <code class="rounded bg-muted px-1 text-xs"
>birdc show protocols</code
>. Не заменяет мониторинг спикеров.
</p> </p>
{#if birdLoading} {#if birdLoading}
<p class="text-foreground/80 text-sm">Загрузка…</p> <p class="text-sm text-foreground/80">Загрузка…</p>
{:else if birdStatus} {:else if birdStatus}
{#if !birdStatus.birdc_configured} {#if !birdStatus.birdc_configured}
<p class="text-foreground/80 text-sm">{birdStatus.message ?? 'birdc не настроен на API.'}</p> <p class="text-sm text-foreground/80">
{birdStatus.message ?? 'birdc не настроен на API.'}
</p>
{:else if birdStatus.error} {:else if birdStatus.error}
<p class="text-destructive text-sm">{birdStatus.error}</p> <p class="text-sm text-destructive">{birdStatus.error}</p>
{:else} {:else}
<p class="text-sm"> <p class="text-sm">
<span class="text-muted-foreground">BGP сессий:</span> <span class="text-muted-foreground">BGP сессий:</span>
@@ -131,7 +143,7 @@
</p> </p>
{/if} {/if}
{:else} {:else}
<p class="text-foreground/80 text-sm">Статус не загружен</p> <p class="text-sm text-foreground/80">Статус не загружен</p>
{/if} {/if}
</div> </div>
@@ -1,7 +1,13 @@
<script lang="ts"> <script lang="ts">
import type { RevisionRow } from '$lib/api/types.js'; import type { RevisionRow } from '$lib/api/types.js';
import { Button } from '$lib/components/ui/button/index.js'; import { Button } from '$lib/components/ui/button/index.js';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '$lib/components/ui/card/index.js'; import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle
} from '$lib/components/ui/card/index.js';
import { import {
Table, Table,
TableBody, TableBody,
@@ -41,60 +47,69 @@
<div class="min-w-0 flex-1"> <div class="min-w-0 flex-1">
<CardTitle class="text-base">История ревизий</CardTitle> <CardTitle class="text-base">История ревизий</CardTitle>
<CardDescription class="max-w-[75ch]"> <CardDescription class="max-w-[75ch]">
Создаются задачей module_refresh (CDN / IP / AS и т.д.); в превью есть полный текст BIRD с инклюдами Создаются задачей module_refresh (CDN / IP / AS и т.д.); в превью есть полный текст BIRD с
инклюдами
</CardDescription> </CardDescription>
</div> </div>
<Button variant="outline" size="sm" class="shrink-0 self-start sm:self-auto" onclick={onReload} disabled={revLoading}> <Button
variant="outline"
size="sm"
class="shrink-0 self-start sm:self-auto"
onclick={onReload}
disabled={revLoading}
>
<RefreshCw class={revLoading ? 'animate-spin' : ''} /> <RefreshCw class={revLoading ? 'animate-spin' : ''} />
</Button> </Button>
</CardHeader> </CardHeader>
<CardContent class="min-w-0 p-0"> <CardContent class="min-w-0 p-0">
<div class="max-w-full overflow-x-auto overscroll-x-contain [scrollbar-gutter:stable]"> <div class="max-w-full overflow-x-auto overscroll-x-contain [scrollbar-gutter:stable]">
<Table class="min-w-[44rem]"> <Table class="min-w-[44rem]">
<TableHeader> <TableHeader>
<TableRow>
<TableHead>ID</TableHead>
<TableHead>Создана</TableHead>
<TableHead>Префиксов</TableHead>
<TableHead>Хэш</TableHead>
<TableHead class="w-32"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{#each revisions as rev (rev.id)}
<TableRow> <TableRow>
<TableCell class="font-mono text-xs">{rev.id.slice(0, 8)}</TableCell> <TableHead>ID</TableHead>
<TableCell class="text-sm">{formatDate(rev.created_at)}</TableCell> <TableHead>Создана</TableHead>
<TableCell>{rev.materialized_prefix_count}</TableCell> <TableHead>Префиксов</TableHead>
<TableCell class="font-mono text-xs text-muted-foreground">{rev.content_hash.slice(0, 12)}</TableCell> <TableHead>Хэш</TableHead>
<TableCell> <TableHead class="w-32"></TableHead>
<div class="flex gap-1">
<Button variant="ghost" size="icon-sm" onclick={() => onOpenPreview(rev)}>
<Eye class="size-3.5" />
</Button>
<Button variant="ghost" size="icon-sm" onclick={() => onRollbackRequest(rev)}>
<Undo class="size-3.5" />
</Button>
<Button
variant="ghost"
size="icon-sm"
title="Скачать диагностический лог"
onclick={() => onDownloadDiagnosticLog(rev)}
>
<Download class="size-3.5" />
</Button>
</div>
</TableCell>
</TableRow> </TableRow>
{:else} </TableHeader>
<TableRow> <TableBody>
<TableCell colspan={5} class="text-muted-foreground py-8 text-center"> {#each revisions as rev (rev.id)}
{revLoading ? 'Загрузка…' : 'Нет ревизий'} <TableRow>
</TableCell> <TableCell class="font-mono text-xs">{rev.id.slice(0, 8)}</TableCell>
</TableRow> <TableCell class="text-sm">{formatDate(rev.created_at)}</TableCell>
{/each} <TableCell>{rev.materialized_prefix_count}</TableCell>
</TableBody> <TableCell class="font-mono text-xs text-muted-foreground"
</Table> >{rev.content_hash.slice(0, 12)}</TableCell
>
<TableCell>
<div class="flex gap-1">
<Button variant="ghost" size="icon-sm" onclick={() => onOpenPreview(rev)}>
<Eye class="size-3.5" />
</Button>
<Button variant="ghost" size="icon-sm" onclick={() => onRollbackRequest(rev)}>
<Undo class="size-3.5" />
</Button>
<Button
variant="ghost"
size="icon-sm"
title="Скачать диагностический лог"
onclick={() => onDownloadDiagnosticLog(rev)}
>
<Download class="size-3.5" />
</Button>
</div>
</TableCell>
</TableRow>
{:else}
<TableRow>
<TableCell colspan={5} class="text-muted-foreground py-8 text-center">
{revLoading ? 'Загрузка…' : 'Нет ревизий'}
</TableCell>
</TableRow>
{/each}
</TableBody>
</Table>
</div> </div>
</CardContent> </CardContent>
</Card> </Card>
+4 -1
View File
@@ -53,7 +53,10 @@ function parseIPv6(addrPart: string): { words: number[]; pl: number } | null {
} }
const missing = 8 - left.length - right.length; const missing = 8 - left.length - right.length;
if (missing < 0) return null; if (missing < 0) return null;
return { words: [...left.map(parseGroup), ...Array(missing).fill(0), ...right.map(parseGroup)], pl: 128 }; return {
words: [...left.map(parseGroup), ...Array(missing).fill(0), ...right.map(parseGroup)],
pl: 128
};
} }
type SortKey = type SortKey =
+2 -2
View File
@@ -18,10 +18,10 @@
</script> </script>
<TooltipProvider delayDuration={0}> <TooltipProvider delayDuration={0}>
<div class="bg-background flex min-h-screen"> <div class="flex min-h-screen bg-background">
<AppSidebar bind:collapsed bind:theme /> <AppSidebar bind:collapsed bind:theme />
<div class="flex min-w-0 flex-1 flex-col"> <div class="flex min-w-0 flex-1 flex-col">
<div class="border-border flex items-center gap-2 border-b px-4 py-2 md:hidden"> <div class="flex items-center gap-2 border-b border-border px-4 py-2 md:hidden">
<AppMobileNav bind:open={mobileNavOpen} bind:theme /> <AppMobileNav bind:open={mobileNavOpen} bind:theme />
<span class="font-semibold tracking-tight">EvoBGP</span> <span class="font-semibold tracking-tight">EvoBGP</span>
</div> </div>
@@ -28,12 +28,12 @@
<Menu class="size-5" /> <Menu class="size-5" />
</Button> </Button>
</SheetTrigger> </SheetTrigger>
<SheetContent side="left" class="bg-sidebar text-sidebar-foreground flex w-72 flex-col gap-0 p-0"> <SheetContent side="left" class="flex w-72 flex-col gap-0 bg-sidebar p-0 text-sidebar-foreground">
<SheetHeader class="border-border border-b p-4 text-left"> <SheetHeader class="border-b border-border p-4 text-left">
<SheetTitle> <SheetTitle>
<a href={resolve('/')} class="font-semibold tracking-tight no-underline">EvoBGP</a> <a href={resolve('/')} class="font-semibold tracking-tight no-underline">EvoBGP</a>
</SheetTitle> </SheetTitle>
<p class="text-sidebar-foreground/50 text-xs font-normal">Панель управления</p> <p class="text-xs font-normal text-sidebar-foreground/50">Панель управления</p>
</SheetHeader> </SheetHeader>
<div class="flex items-center gap-2 px-4 py-2"> <div class="flex items-center gap-2 px-4 py-2">
<ThemeMenu bind:theme /> <ThemeMenu bind:theme />
@@ -25,8 +25,13 @@
function linkClass(active: boolean, iconOnly: boolean) { function linkClass(active: boolean, iconOnly: boolean) {
return cn( return cn(
buttonVariants({ variant: active ? 'secondary' : 'ghost', size: iconOnly ? 'icon-sm' : 'sm' }), buttonVariants({
iconOnly ? 'flex w-full items-center justify-center no-underline' : 'flex w-full items-center justify-start gap-2 no-underline', variant: active ? 'secondary' : 'ghost',
size: iconOnly ? 'icon-sm' : 'sm'
}),
iconOnly
? 'flex w-full items-center justify-center no-underline'
: 'flex w-full items-center justify-start gap-2 no-underline',
active && active &&
(iconOnly (iconOnly
? 'ring-sidebar-primary/50 bg-sidebar-accent/90 text-sidebar-accent-foreground ring-1' ? 'ring-sidebar-primary/50 bg-sidebar-accent/90 text-sidebar-accent-foreground ring-1'
+6 -5
View File
@@ -20,7 +20,7 @@
<aside <aside
class={cn( class={cn(
'border-border bg-sidebar sticky top-0 hidden h-screen shrink-0 flex-col overflow-hidden border-r transition-all duration-200 md:flex', 'sticky top-0 hidden h-screen shrink-0 flex-col overflow-hidden border-r border-border bg-sidebar transition-all duration-200 md:flex',
collapsed ? 'w-14' : 'w-56' collapsed ? 'w-14' : 'w-56'
)} )}
aria-label="Основная навигация" aria-label="Основная навигация"
@@ -28,10 +28,11 @@
<div class={cn('flex items-center gap-2 p-3', collapsed && 'justify-center')}> <div class={cn('flex items-center gap-2 p-3', collapsed && 'justify-center')}>
{#if !collapsed} {#if !collapsed}
<div class="min-w-0 flex-1"> <div class="min-w-0 flex-1">
<a href={resolve('/')} class="text-sidebar-foreground block truncate font-semibold tracking-tight" <a
>EvoBGP</a href={resolve('/')}
class="block truncate font-semibold tracking-tight text-sidebar-foreground">EvoBGP</a
> >
<p class="text-sidebar-foreground/50 truncate text-xs">Панель управления</p> <p class="truncate text-xs text-sidebar-foreground/50">Панель управления</p>
</div> </div>
{/if} {/if}
<ThemeMenu bind:theme /> <ThemeMenu bind:theme />
@@ -40,7 +41,7 @@
onclick={() => (collapsed = !collapsed)} onclick={() => (collapsed = !collapsed)}
class={cn( class={cn(
buttonVariants({ variant: 'ghost', size: 'icon-sm' }), buttonVariants({ variant: 'ghost', size: 'icon-sm' }),
'text-sidebar-foreground/60 hover:text-sidebar-foreground shrink-0' 'shrink-0 text-sidebar-foreground/60 hover:text-sidebar-foreground'
)} )}
aria-label={collapsed ? 'Развернуть меню' : 'Свернуть меню'} aria-label={collapsed ? 'Развернуть меню' : 'Свернуть меню'}
> >
+2 -2
View File
@@ -15,7 +15,7 @@
<header <header
class={cn( class={cn(
'border-border bg-background/95 supports-[backdrop-filter]:bg-background/60 sticky top-0 z-30 flex flex-col gap-3 border-b px-4 py-3 backdrop-blur md:px-6', 'sticky top-0 z-30 flex flex-col gap-3 border-b border-border bg-background/95 px-4 py-3 backdrop-blur supports-[backdrop-filter]:bg-background/60 md:px-6',
className className
)} )}
> >
@@ -28,7 +28,7 @@
<h1 class="text-lg font-semibold tracking-tight md:text-xl">{title}</h1> <h1 class="text-lg font-semibold tracking-tight md:text-xl">{title}</h1>
{/if} {/if}
{#if description} {#if description}
<p class="text-muted-foreground mt-0.5 text-sm">{description}</p> <p class="mt-0.5 text-sm text-muted-foreground">{description}</p>
{/if} {/if}
</div> </div>
{#if actions} {#if actions}
+5 -2
View File
@@ -14,14 +14,17 @@
import Moon from '@lucide/svelte/icons/moon'; import Moon from '@lucide/svelte/icons/moon';
import Monitor from '@lucide/svelte/icons/monitor'; import Monitor from '@lucide/svelte/icons/monitor';
let { theme = $bindable<ThemePreference>('system'), class: className }: { theme?: ThemePreference; class?: string } = $props(); let {
theme = $bindable<ThemePreference>('system'),
class: className
}: { theme?: ThemePreference; class?: string } = $props();
</script> </script>
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger <DropdownMenuTrigger
class={cn( class={cn(
buttonVariants({ variant: 'ghost', size: 'icon-sm' }), buttonVariants({ variant: 'ghost', size: 'icon-sm' }),
'text-sidebar-foreground/60 hover:text-sidebar-foreground shrink-0', 'shrink-0 text-sidebar-foreground/60 hover:text-sidebar-foreground',
className className
)} )}
aria-label="Тема оформления" aria-label="Тема оформления"
@@ -30,7 +30,7 @@
<div class="min-w-0"> <div class="min-w-0">
<h1 class="text-2xl font-semibold tracking-tight">{title}</h1> <h1 class="text-2xl font-semibold tracking-tight">{title}</h1>
{#if description} {#if description}
<p class="text-muted-foreground mt-1 text-sm">{description}</p> <p class="mt-1 text-sm text-muted-foreground">{description}</p>
{/if} {/if}
</div> </div>
</div> </div>
+4 -4
View File
@@ -4,10 +4,10 @@
## Layout ## Layout
| Token | Значение | Tailwind | | Token | Значение | Tailwind |
|-------|----------|----------| | ---------------- | -------- | ---------------------------------------- |
| `--page-padding` | 1.5rem | `p-page` через `p-[var(--page-padding)]` | | `--page-padding` | 1.5rem | `p-page` через `p-[var(--page-padding)]` |
| `--section-gap` | 1.5rem | `gap-section` | | `--section-gap` | 1.5rem | `gap-section` |
## Радиусы ## Радиусы
@@ -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/ui/core/button/index.js"; } from '$lib/ui/core/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/ui/core/button/index.js"; } from '$lib/ui/core/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-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-sm',
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 data-closed:animate-out data-closed:fade-out-0 data-open:animate-in data-open:fade-in-0 supports-backdrop-filter:backdrop-blur-xs',
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}
+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),
@@ -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
}; };
+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';
+32 -25
View File
@@ -1,38 +1,45 @@
<script lang="ts" module> <script lang="ts" module>
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
import type { HTMLAnchorAttributes, HTMLButtonAttributes } from "svelte/elements"; import type { HTMLAnchorAttributes, HTMLButtonAttributes } from 'svelte/elements';
import { type VariantProps, tv } from "tailwind-variants"; 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> & { WithElementRef<HTMLAnchorAttributes> & {
@@ -44,11 +51,11 @@
<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
@@ -62,7 +69,7 @@
class={cn(buttonVariants({ variant, size }), className)} class={cn(buttonVariants({ variant, size }), className)}
href={disabled ? undefined : href} href={disabled ? undefined : href}
aria-disabled={disabled} aria-disabled={disabled}
role={disabled ? "link" : undefined} role={disabled ? 'link' : undefined}
tabindex={disabled ? -1 : undefined} tabindex={disabled ? -1 : undefined}
{...restProps} {...restProps}
> >
+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
}; };
+3 -3
View File
@@ -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}
+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-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?.()}
+6 -3
View File
@@ -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?.()}
+3 -3
View File
@@ -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
}; };
+6 -6
View File
@@ -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 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 dark:data-checked:bg-primary',
className className
)} )}
bind:checked bind:checked
@@ -27,12 +27,12 @@
{#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 />
{:else if indeterminate} {:else if indeterminate}
<MinusIcon /> <MinusIcon />
{/if} {/if}
</div> </div>
{/snippet} {/snippet}
+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
}; };
@@ -4,8 +4,8 @@ import {
type TableOptionsResolved, type TableOptionsResolved,
type TableState, type TableState,
type Updater, type Updater,
createTable, createTable
} from "@tanstack/table-core"; } from '@tanstack/table-core';
/** /**
* Creates a reactive TanStack table object for Svelte. * Creates a reactive TanStack table object for Svelte.
@@ -44,7 +44,7 @@ export function createSvelteTable<TData extends RowData>(options: TableOptions<T
options: Partial<TableOptions<TData>> options: Partial<TableOptions<TData>>
) => { ) => {
return mergeObjects(defaultOptions, options); return mergeObjects(defaultOptions, options);
}, }
}, },
options options
); );
@@ -62,7 +62,7 @@ export function createSvelteTable<TData extends RowData>(options: TableOptions<T
else state = mergeObjects(state, updater); else state = mergeObjects(state, updater);
options.onStateChange?.(updater); options.onStateChange?.(updater);
}, }
}); });
}); });
} }
@@ -92,7 +92,7 @@ export function mergeObjects<Sources extends readonly MaybeThunk<any>[]>(
...sources: Sources ...sources: Sources
): Intersection<{ [K in keyof Sources]: Sources[K] }> { ): Intersection<{ [K in keyof Sources]: Sources[K] }> {
const resolve = <T extends object>(src: MaybeThunk<T>): T | undefined => const resolve = <T extends object>(src: MaybeThunk<T>): T | undefined =>
typeof src === "function" ? (src() ?? undefined) : src; typeof src === 'function' ? (src() ?? undefined) : src;
const findSourceWithKey = (key: PropertyKey) => { const findSourceWithKey = (key: PropertyKey) => {
for (let i = sources.length - 1; i >= 0; i--) { for (let i = sources.length - 1; i >= 0; i--) {
@@ -135,8 +135,8 @@ export function mergeObjects<Sources extends readonly MaybeThunk<any>[]>(
enumerable: true, enumerable: true,
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
value: (src as any)[key], value: (src as any)[key],
writable: true, writable: true
}; };
}, }
}) as Intersection<{ [K in keyof Sources]: Sources[K] }>; }) as Intersection<{ [K in keyof Sources]: Sources[K] }>;
} }
@@ -2,9 +2,9 @@
lang="ts" lang="ts"
generics="TData, TValue, TContext extends HeaderContext<TData, TValue> | CellContext<TData, TValue>" generics="TData, TValue, TContext extends HeaderContext<TData, TValue> | CellContext<TData, TValue>"
> >
import type { CellContext, ColumnDefTemplate, HeaderContext } from "@tanstack/table-core"; import type { CellContext, ColumnDefTemplate, HeaderContext } from '@tanstack/table-core';
import { RenderComponentConfig, RenderSnippetConfig } from "./render-helpers.js"; import { RenderComponentConfig, RenderSnippetConfig } from './render-helpers.js';
import type { Attachment } from "svelte/attachments"; import type { Attachment } from 'svelte/attachments';
type Props = { type Props = {
/** The cell or header field of the current cell's column definition. */ /** The cell or header field of the current cell's column definition. */
content?: TContext extends HeaderContext<TData, TValue> content?: TContext extends HeaderContext<TData, TValue>
@@ -22,7 +22,7 @@
let { content, context, attach }: Props = $props(); let { content, context, attach }: Props = $props();
</script> </script>
{#if typeof content === "string"} {#if typeof content === 'string'}
{content} {content}
{:else if content instanceof Function} {:else if content instanceof Function}
<!-- It's unlikely that a CellContext will be passed to a Header --> <!-- It's unlikely that a CellContext will be passed to a Header -->
+3 -3
View File
@@ -1,3 +1,3 @@
export { default as FlexRender } from "./flex-render.svelte"; export { default as FlexRender } from './flex-render.svelte';
export { renderComponent, renderSnippet } from "./render-helpers.js"; export { renderComponent, renderSnippet } from './render-helpers.js';
export { createSvelteTable } from "./data-table.svelte.js"; export { createSvelteTable } from './data-table.svelte.js';
@@ -1,4 +1,4 @@
import type { Component, ComponentProps, Snippet } from "svelte"; import type { Component, ComponentProps, Snippet } from 'svelte';
/** /**
* A helper class to make it easy to identify Svelte components in * A helper class to make it easy to identify Svelte components in
@@ -77,7 +77,7 @@ export class RenderSnippetConfig<TProps> {
export function renderComponent< export function renderComponent<
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
T extends Component<any>, T extends Component<any>,
Props extends ComponentProps<T>, Props extends ComponentProps<T>
>(component: T, props: Props = {} as Props) { >(component: T, props: Props = {} as Props) {
return new RenderComponentConfig(component, props); return new RenderComponentConfig(component, props);
} }
@@ -1,9 +1,9 @@
<script lang="ts"> <script lang="ts">
import { Dialog as DialogPrimitive } from "bits-ui"; import { Dialog as DialogPrimitive } from 'bits-ui';
let { let {
ref = $bindable(null), ref = $bindable(null),
type = "button", type = 'button',
...restProps ...restProps
}: DialogPrimitive.CloseProps = $props(); }: DialogPrimitive.CloseProps = $props();
</script> </script>
@@ -1,11 +1,11 @@
<script lang="ts"> <script lang="ts">
import { Dialog as DialogPrimitive } from "bits-ui"; import { Dialog as DialogPrimitive } from 'bits-ui';
import DialogPortal from "./dialog-portal.svelte"; import DialogPortal from './dialog-portal.svelte';
import type { Snippet } from "svelte"; import type { Snippet } from 'svelte';
import * as Dialog from "./index.js"; import * as Dialog from './index.js';
import { cn, type WithoutChildrenOrChild } from "$lib/utils.js"; import { cn, type WithoutChildrenOrChild } from '$lib/utils.js';
import type { ComponentProps } from "svelte"; import type { ComponentProps } from 'svelte';
import { Button } from "$lib/ui/core/button/index.js"; import { Button } from '$lib/ui/core/button/index.js';
import XIcon from '@lucide/svelte/icons/x'; import XIcon from '@lucide/svelte/icons/x';
let { let {
@@ -28,7 +28,7 @@
bind:ref bind:ref
data-slot="dialog-content" data-slot="dialog-content"
class={cn( class={cn(
"bg-popover text-popover-foreground 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 ring-foreground/10 grid max-w-[calc(100%-2rem)] gap-4 rounded-xl p-4 text-sm ring-1 duration-100 sm:max-w-sm fixed top-1/2 left-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2 outline-none", 'fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 sm:max-w-sm',
className className
)} )}
{...restProps} {...restProps}
@@ -38,7 +38,7 @@
<DialogPrimitive.Close data-slot="dialog-close"> <DialogPrimitive.Close data-slot="dialog-close">
{#snippet child({ props })} {#snippet child({ props })}
<Button variant="ghost" class="absolute top-2 right-2" size="icon-sm" {...props}> <Button variant="ghost" class="absolute top-2 right-2" size="icon-sm" {...props}>
<XIcon /> <XIcon />
<span class="sr-only">Close</span> <span class="sr-only">Close</span>
</Button> </Button>
{/snippet} {/snippet}
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { Dialog as DialogPrimitive } from "bits-ui"; import { Dialog as DialogPrimitive } 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 @@
<DialogPrimitive.Description <DialogPrimitive.Description
bind:ref bind:ref
data-slot="dialog-description" data-slot="dialog-description"
class={cn("text-muted-foreground *:[a]:hover:text-foreground text-sm *:[a]:underline *:[a]:underline-offset-3", className)} class={cn(
'text-sm text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground',
className
)}
{...restProps} {...restProps}
/> />
@@ -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 { Dialog as DialogPrimitive } from "bits-ui"; import { Dialog as DialogPrimitive } from 'bits-ui';
import { Button } from "$lib/ui/core/button/index.js"; import { Button } from '$lib/ui/core/button/index.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -18,7 +18,10 @@
<div <div
bind:this={ref} bind:this={ref}
data-slot="dialog-footer" data-slot="dialog-footer"
class={cn("bg-muted/50 -mx-4 -mb-4 rounded-b-xl border-t p-4 flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className)} class={cn(
'-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 sm:flex-row sm:justify-end',
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 @@
<div <div
bind:this={ref} bind:this={ref}
data-slot="dialog-header" data-slot="dialog-header"
class={cn("gap-2 flex flex-col", className)} class={cn('flex flex-col gap-2', className)}
{...restProps} {...restProps}
> >
{@render children?.()} {@render children?.()}
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { Dialog as DialogPrimitive } from "bits-ui"; import { Dialog as DialogPrimitive } 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 @@
<DialogPrimitive.Overlay <DialogPrimitive.Overlay
bind:ref bind:ref
data-slot="dialog-overlay" data-slot="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 isolate z-50", className)} class={cn(
'fixed inset-0 isolate z-50 bg-black/10 duration-100 data-closed:animate-out data-closed:fade-out-0 data-open:animate-in data-open:fade-in-0 supports-backdrop-filter:backdrop-blur-xs',
className
)}
{...restProps} {...restProps}
/> />
@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { Dialog as DialogPrimitive } from "bits-ui"; import { Dialog as DialogPrimitive } from 'bits-ui';
let { ...restProps }: DialogPrimitive.PortalProps = $props(); let { ...restProps }: DialogPrimitive.PortalProps = $props();
</script> </script>
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { Dialog as DialogPrimitive } from "bits-ui"; import { Dialog as DialogPrimitive } 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 @@
<DialogPrimitive.Title <DialogPrimitive.Title
bind:ref bind:ref
data-slot="dialog-title" data-slot="dialog-title"
class={cn("text-base leading-none font-medium", className)} class={cn('text-base leading-none font-medium', className)}
{...restProps} {...restProps}
/> />
@@ -1,9 +1,9 @@
<script lang="ts"> <script lang="ts">
import { Dialog as DialogPrimitive } from "bits-ui"; import { Dialog as DialogPrimitive } from 'bits-ui';
let { let {
ref = $bindable(null), ref = $bindable(null),
type = "button", type = 'button',
...restProps ...restProps
}: DialogPrimitive.TriggerProps = $props(); }: DialogPrimitive.TriggerProps = $props();
</script> </script>
+1 -1
View File
@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { Dialog as DialogPrimitive } from "bits-ui"; import { Dialog as DialogPrimitive } from 'bits-ui';
let { open = $bindable(false), ...restProps }: DialogPrimitive.RootProps = $props(); let { open = $bindable(false), ...restProps }: DialogPrimitive.RootProps = $props();
</script> </script>
+11 -11
View File
@@ -1,13 +1,13 @@
import Root from "./dialog.svelte"; import Root from './dialog.svelte';
import Portal from "./dialog-portal.svelte"; import Portal from './dialog-portal.svelte';
import Title from "./dialog-title.svelte"; import Title from './dialog-title.svelte';
import Footer from "./dialog-footer.svelte"; import Footer from './dialog-footer.svelte';
import Header from "./dialog-header.svelte"; import Header from './dialog-header.svelte';
import Overlay from "./dialog-overlay.svelte"; import Overlay from './dialog-overlay.svelte';
import Content from "./dialog-content.svelte"; import Content from './dialog-content.svelte';
import Description from "./dialog-description.svelte"; import Description from './dialog-description.svelte';
import Trigger from "./dialog-trigger.svelte"; import Trigger from './dialog-trigger.svelte';
import Close from "./dialog-close.svelte"; import Close from './dialog-close.svelte';
export { export {
Root, Root,
@@ -30,5 +30,5 @@ export {
Overlay as DialogOverlay, Overlay as DialogOverlay,
Content as DialogContent, Content as DialogContent,
Description as DialogDescription, Description as DialogDescription,
Close as DialogClose, Close as DialogClose
}; };
@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui"; import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -1,9 +1,9 @@
<script lang="ts"> <script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui"; import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import MinusIcon from '@lucide/svelte/icons/minus'; import MinusIcon from '@lucide/svelte/icons/minus';
import CheckIcon from '@lucide/svelte/icons/check'; import CheckIcon from '@lucide/svelte/icons/check';
import { cn, type WithoutChildrenOrChild } from "$lib/utils.js"; import { cn, type WithoutChildrenOrChild } from '$lib/utils.js';
import type { Snippet } from "svelte"; import type { Snippet } from 'svelte';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -23,20 +23,20 @@
bind:indeterminate bind:indeterminate
data-slot="dropdown-menu-checkbox-item" data-slot="dropdown-menu-checkbox-item"
class={cn( class={cn(
"focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm data-inset:pl-7 [&_svg:not([class*='size-'])]:size-4 relative flex cursor-default items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0", "relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className className
)} )}
{...restProps} {...restProps}
> >
{#snippet children({ checked, indeterminate })} {#snippet children({ checked, indeterminate })}
<span <span
class="absolute right-2 flex items-center justify-center pointer-events-none" class="pointer-events-none absolute right-2 flex items-center justify-center"
data-slot="dropdown-menu-checkbox-item-indicator" data-slot="dropdown-menu-checkbox-item-indicator"
> >
{#if indeterminate} {#if indeterminate}
<MinusIcon /> <MinusIcon />
{:else if checked} {:else if checked}
<CheckIcon /> <CheckIcon />
{/if} {/if}
</span> </span>
{@render childrenProp?.()} {@render childrenProp?.()}
@@ -1,13 +1,13 @@
<script lang="ts"> <script lang="ts">
import { cn, type WithoutChildrenOrChild } from "$lib/utils.js"; import { cn, type WithoutChildrenOrChild } from '$lib/utils.js';
import DropdownMenuPortal from "./dropdown-menu-portal.svelte"; import DropdownMenuPortal from './dropdown-menu-portal.svelte';
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui"; import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import type { ComponentProps } from "svelte"; import type { ComponentProps } from 'svelte';
let { let {
ref = $bindable(null), ref = $bindable(null),
sideOffset = 4, sideOffset = 4,
align = "start", align = 'start',
portalProps, portalProps,
class: className, class: className,
...restProps ...restProps
@@ -23,7 +23,7 @@
{sideOffset} {sideOffset}
{align} {align}
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 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 bg-popover text-popover-foreground min-w-32 rounded-lg p-1 shadow-md ring-1 duration-100 data-[side=inline-start]:slide-in-from-right-2 data-[side=inline-end]:slide-in-from-left-2 z-50 w-(--bits-dropdown-menu-anchor-width) overflow-x-hidden overflow-y-auto outline-none data-closed:overflow-hidden", 'z-50 w-(--bits-dropdown-menu-anchor-width) min-w-32 overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 outline-none data-closed:animate-out data-closed:overflow-hidden data-closed:fade-out-0 data-closed:zoom-out-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
className className
)} )}
{...restProps} {...restProps}
@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui"; import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import { cn } from "$lib/utils.js"; import { cn } from '$lib/utils.js';
import type { ComponentProps } from "svelte"; import type { ComponentProps } from 'svelte';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -17,6 +17,6 @@
bind:ref bind:ref
data-slot="dropdown-menu-group-heading" data-slot="dropdown-menu-group-heading"
data-inset={inset} data-inset={inset}
class={cn("px-2 py-1.5 text-sm font-semibold data-[inset]:ps-8", className)} class={cn('px-2 py-1.5 text-sm font-semibold data-[inset]:ps-8', className)}
{...restProps} {...restProps}
/> />
@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui"; import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
let { ref = $bindable(null), ...restProps }: DropdownMenuPrimitive.GroupProps = $props(); let { ref = $bindable(null), ...restProps }: DropdownMenuPrimitive.GroupProps = $props();
</script> </script>
@@ -1,16 +1,16 @@
<script lang="ts"> <script lang="ts">
import { cn } from "$lib/utils.js"; import { cn } from '$lib/utils.js';
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui"; import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
let { let {
ref = $bindable(null), ref = $bindable(null),
class: className, class: className,
inset, inset,
variant = "default", variant = 'default',
...restProps ...restProps
}: DropdownMenuPrimitive.ItemProps & { }: DropdownMenuPrimitive.ItemProps & {
inset?: boolean; inset?: boolean;
variant?: "default" | "destructive"; variant?: 'default' | 'destructive';
} = $props(); } = $props();
</script> </script>
@@ -20,7 +20,7 @@
data-inset={inset} data-inset={inset}
data-variant={variant} data-variant={variant}
class={cn( class={cn(
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:text-destructive not-data-[variant=destructive]:focus:**:text-accent-foreground gap-1.5 rounded-md px-1.5 py-1 text-sm data-inset:pl-7 [&_svg:not([class*='size-'])]:size-4 group/dropdown-menu-item relative flex cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0", "group/dropdown-menu-item relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 data-inset:pl-7 data-[inset]:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive",
className 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),
@@ -17,7 +17,10 @@
bind:this={ref} bind:this={ref}
data-slot="dropdown-menu-label" data-slot="dropdown-menu-label"
data-inset={inset} data-inset={inset}
class={cn("text-muted-foreground px-1.5 py-1 text-xs font-medium data-inset:pl-7 data-[inset]:pl-8", className)} class={cn(
'px-1.5 py-1 text-xs font-medium text-muted-foreground data-inset:pl-7 data-[inset]:pl-8',
className
)}
{...restProps} {...restProps}
> >
{@render children?.()} {@render children?.()}
@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui"; import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
let { ...restProps }: DropdownMenuPrimitive.PortalProps = $props(); let { ...restProps }: DropdownMenuPrimitive.PortalProps = $props();
</script> </script>
@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui"; import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui"; import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import CheckIcon from '@lucide/svelte/icons/check'; import CheckIcon from '@lucide/svelte/icons/check';
import { cn, type WithoutChild } from "$lib/utils.js"; import { cn, type WithoutChild } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -15,18 +15,18 @@
bind:ref bind:ref
data-slot="dropdown-menu-radio-item" data-slot="dropdown-menu-radio-item"
class={cn( class={cn(
"focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm data-inset:pl-7 [&_svg:not([class*='size-'])]:size-4 relative flex cursor-default items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0", "relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className className
)} )}
{...restProps} {...restProps}
> >
{#snippet children({ checked })} {#snippet children({ checked })}
<span <span
class="absolute right-2 flex items-center justify-center pointer-events-none" class="pointer-events-none absolute right-2 flex items-center justify-center"
data-slot="dropdown-menu-radio-item-indicator" data-slot="dropdown-menu-radio-item-indicator"
> >
{#if checked} {#if checked}
<CheckIcon /> <CheckIcon />
{/if} {/if}
</span> </span>
{@render childrenProp?.({ checked })} {@render childrenProp?.({ checked })}
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui"; import { DropdownMenu as DropdownMenuPrimitive } 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 @@
<DropdownMenuPrimitive.Separator <DropdownMenuPrimitive.Separator
bind:ref bind:ref
data-slot="dropdown-menu-separator" data-slot="dropdown-menu-separator"
class={cn("bg-border -mx-1 my-1 h-px", className)} class={cn('-mx-1 my-1 h-px bg-border', 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 @@
<span <span
bind:this={ref} bind:this={ref}
data-slot="dropdown-menu-shortcut" data-slot="dropdown-menu-shortcut"
class={cn("text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground ml-auto text-xs tracking-widest", className)} class={cn(
'ml-auto text-xs tracking-widest text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground',
className
)}
{...restProps} {...restProps}
> >
{@render children?.()} {@render children?.()}
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui"; import { DropdownMenu as DropdownMenuPrimitive } 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 @@
<DropdownMenuPrimitive.SubContent <DropdownMenuPrimitive.SubContent
bind:ref bind:ref
data-slot="dropdown-menu-sub-content" data-slot="dropdown-menu-sub-content"
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 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 bg-popover text-popover-foreground min-w-[96px] rounded-lg p-1 shadow-lg ring-1 duration-100 w-auto", className)} class={cn(
'w-auto min-w-[96px] rounded-lg bg-popover p-1 text-popover-foreground shadow-lg ring-1 ring-foreground/10 duration-100 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
className
)}
{...restProps} {...restProps}
/> />
@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui"; import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import ChevronRightIcon from '@lucide/svelte/icons/chevron-right'; import ChevronRightIcon from '@lucide/svelte/icons/chevron-right';
import { cn } from "$lib/utils.js"; import { cn } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -19,7 +19,7 @@
data-slot="dropdown-menu-sub-trigger" data-slot="dropdown-menu-sub-trigger"
data-inset={inset} data-inset={inset}
class={cn( class={cn(
"focus:bg-accent focus:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground gap-1.5 rounded-md px-1.5 py-1 text-sm data-inset:pl-7 [&_svg:not([class*='size-'])]:size-4 flex cursor-default items-center outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0", "flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-open:bg-accent data-open:text-accent-foreground data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className className
)} )}
{...restProps} {...restProps}
@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui"; import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
let { open = $bindable(false), ...restProps }: DropdownMenuPrimitive.SubProps = $props(); let { open = $bindable(false), ...restProps }: DropdownMenuPrimitive.SubProps = $props();
</script> </script>
@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui"; import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
let { ref = $bindable(null), ...restProps }: DropdownMenuPrimitive.TriggerProps = $props(); let { ref = $bindable(null), ...restProps }: DropdownMenuPrimitive.TriggerProps = $props();
</script> </script>
@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui"; import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
let { open = $bindable(false), ...restProps }: DropdownMenuPrimitive.RootProps = $props(); let { open = $bindable(false), ...restProps }: DropdownMenuPrimitive.RootProps = $props();
</script> </script>
+18 -18
View File
@@ -1,20 +1,20 @@
import Root from "./dropdown-menu.svelte"; import Root from './dropdown-menu.svelte';
import Sub from "./dropdown-menu-sub.svelte"; import Sub from './dropdown-menu-sub.svelte';
import CheckboxGroup from "./dropdown-menu-checkbox-group.svelte"; import CheckboxGroup from './dropdown-menu-checkbox-group.svelte';
import CheckboxItem from "./dropdown-menu-checkbox-item.svelte"; import CheckboxItem from './dropdown-menu-checkbox-item.svelte';
import Content from "./dropdown-menu-content.svelte"; import Content from './dropdown-menu-content.svelte';
import Group from "./dropdown-menu-group.svelte"; import Group from './dropdown-menu-group.svelte';
import Item from "./dropdown-menu-item.svelte"; import Item from './dropdown-menu-item.svelte';
import Label from "./dropdown-menu-label.svelte"; import Label from './dropdown-menu-label.svelte';
import RadioGroup from "./dropdown-menu-radio-group.svelte"; import RadioGroup from './dropdown-menu-radio-group.svelte';
import RadioItem from "./dropdown-menu-radio-item.svelte"; import RadioItem from './dropdown-menu-radio-item.svelte';
import Separator from "./dropdown-menu-separator.svelte"; import Separator from './dropdown-menu-separator.svelte';
import Shortcut from "./dropdown-menu-shortcut.svelte"; import Shortcut from './dropdown-menu-shortcut.svelte';
import Trigger from "./dropdown-menu-trigger.svelte"; import Trigger from './dropdown-menu-trigger.svelte';
import SubContent from "./dropdown-menu-sub-content.svelte"; import SubContent from './dropdown-menu-sub-content.svelte';
import SubTrigger from "./dropdown-menu-sub-trigger.svelte"; import SubTrigger from './dropdown-menu-sub-trigger.svelte';
import GroupHeading from "./dropdown-menu-group-heading.svelte"; import GroupHeading from './dropdown-menu-group-heading.svelte';
import Portal from "./dropdown-menu-portal.svelte"; import Portal from './dropdown-menu-portal.svelte';
export { export {
CheckboxGroup, CheckboxGroup,
@@ -50,5 +50,5 @@ export {
Sub, Sub,
SubContent, SubContent,
SubTrigger, SubTrigger,
Trigger, Trigger
}; };
+1 -1
View File
@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { Button, type ButtonProps } from "$lib/ui/core/button/index.js"; import { Button, type ButtonProps } from '$lib/ui/core/button/index.js';
let { ref = $bindable(null), ...restProps }: ButtonProps = $props(); let { ref = $bindable(null), ...restProps }: ButtonProps = $props();
</script> </script>
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import * as FormPrimitive from "formsnap"; import * as FormPrimitive from 'formsnap';
import { cn, type WithoutChild } from "$lib/utils.js"; import { cn, type WithoutChild } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -12,6 +12,6 @@
<FormPrimitive.Description <FormPrimitive.Description
bind:ref bind:ref
data-slot="form-description" data-slot="form-description"
class={cn("text-muted-foreground text-sm", className)} class={cn('text-sm text-muted-foreground', className)}
{...restProps} {...restProps}
/> />
@@ -1,8 +1,8 @@
<script lang="ts" generics="T extends Record<string, unknown>, U extends FormPathLeaves<T>"> <script lang="ts" generics="T extends Record<string, unknown>, U extends FormPathLeaves<T>">
import * as FormPrimitive from "formsnap"; import * as FormPrimitive from 'formsnap';
import type { FormPathLeaves } from "sveltekit-superforms"; import type { FormPathLeaves } from 'sveltekit-superforms';
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';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -17,7 +17,7 @@
<FormPrimitive.ElementField {form} {name}> <FormPrimitive.ElementField {form} {name}>
{#snippet children({ constraints, errors, tainted, value })} {#snippet children({ constraints, errors, tainted, value })}
<div bind:this={ref} class={cn("space-y-2", className)} {...restProps}> <div bind:this={ref} class={cn('space-y-2', className)} {...restProps}>
{@render childrenProp?.({ constraints, errors, tainted, value: value as T[U] })} {@render childrenProp?.({ constraints, errors, tainted, value: value as T[U] })}
</div> </div>
{/snippet} {/snippet}
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import * as FormPrimitive from "formsnap"; import * as FormPrimitive from 'formsnap';
import { cn, type WithoutChild } from "$lib/utils.js"; import { cn, type WithoutChild } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -15,7 +15,7 @@
<FormPrimitive.FieldErrors <FormPrimitive.FieldErrors
bind:ref bind:ref
class={cn("text-destructive text-sm font-medium", className)} class={cn('text-sm font-medium text-destructive', className)}
{...restProps} {...restProps}
> >
{#snippet children({ errors, errorProps })} {#snippet children({ errors, errorProps })}
+5 -10
View File
@@ -1,8 +1,8 @@
<script lang="ts" generics="T extends Record<string, unknown>, U extends FormPath<T>"> <script lang="ts" generics="T extends Record<string, unknown>, U extends FormPath<T>">
import * as FormPrimitive from "formsnap"; import * as FormPrimitive from 'formsnap';
import type { FormPath } from "sveltekit-superforms"; import type { FormPath } from 'sveltekit-superforms';
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';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -17,12 +17,7 @@
<FormPrimitive.Field {form} {name}> <FormPrimitive.Field {form} {name}>
{#snippet children({ constraints, errors, tainted, value })} {#snippet children({ constraints, errors, tainted, value })}
<div <div bind:this={ref} data-slot="form-item" class={cn('space-y-2', className)} {...restProps}>
bind:this={ref}
data-slot="form-item"
class={cn("space-y-2", className)}
{...restProps}
>
{@render childrenProp?.({ constraints, errors, tainted, value: value as T[U] })} {@render childrenProp?.({ constraints, errors, tainted, value: value as T[U] })}
</div> </div>
{/snippet} {/snippet}
@@ -1,7 +1,7 @@
<script lang="ts" generics="T extends Record<string, unknown>, U extends FormPath<T>"> <script lang="ts" generics="T extends Record<string, unknown>, U extends FormPath<T>">
import * as FormPrimitive from "formsnap"; import * as FormPrimitive from 'formsnap';
import type { FormPath } from "sveltekit-superforms"; import type { FormPath } from 'sveltekit-superforms';
import { cn, type WithoutChild } from "$lib/utils.js"; import { cn, type WithoutChild } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -12,4 +12,4 @@
}: WithoutChild<FormPrimitive.FieldsetProps<T, U>> = $props(); }: WithoutChild<FormPrimitive.FieldsetProps<T, U>> = $props();
</script> </script>
<FormPrimitive.Fieldset bind:ref {form} {name} class={cn("space-y-2", className)} {...restProps} /> <FormPrimitive.Fieldset bind:ref {form} {name} class={cn('space-y-2', className)} {...restProps} />
+4 -4
View File
@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import * as FormPrimitive from "formsnap"; import * as FormPrimitive from 'formsnap';
import { Label } from "$lib/ui/core/label/index.js"; import { Label } from '$lib/ui/core/label/index.js';
import { cn, type WithoutChild } from "$lib/utils.js"; import { cn, type WithoutChild } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -16,7 +16,7 @@
<Label <Label
{...props} {...props}
data-slot="form-label" data-slot="form-label"
class={cn("data-[fs-error]:text-destructive", className)} class={cn('data-[fs-error]:text-destructive', className)}
> >
{@render children?.()} {@render children?.()}
</Label> </Label>
+3 -3
View File
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import * as FormPrimitive from "formsnap"; import * as FormPrimitive from 'formsnap';
import { cn, type WithoutChild } from "$lib/utils.js"; import { cn, type WithoutChild } from '$lib/utils.js';
let { let {
ref = $bindable(null), ref = $bindable(null),
@@ -11,6 +11,6 @@
<FormPrimitive.Legend <FormPrimitive.Legend
bind:ref bind:ref
class={cn("data-[fs-error]:text-destructive text-sm leading-none font-medium", className)} class={cn('text-sm leading-none font-medium data-[fs-error]:text-destructive', className)}
{...restProps} {...restProps}
/> />
+10 -10
View File
@@ -1,12 +1,12 @@
import * as FormPrimitive from "formsnap"; import * as FormPrimitive from 'formsnap';
import Description from "./form-description.svelte"; import Description from './form-description.svelte';
import Label from "./form-label.svelte"; import Label from './form-label.svelte';
import FieldErrors from "./form-field-errors.svelte"; import FieldErrors from './form-field-errors.svelte';
import Field from "./form-field.svelte"; import Field from './form-field.svelte';
import Fieldset from "./form-fieldset.svelte"; import Fieldset from './form-fieldset.svelte';
import Legend from "./form-legend.svelte"; import Legend from './form-legend.svelte';
import ElementField from "./form-element-field.svelte"; import ElementField from './form-element-field.svelte';
import Button from "./form-button.svelte"; import Button from './form-button.svelte';
const Control = FormPrimitive.Control; const Control = FormPrimitive.Control;
@@ -29,5 +29,5 @@ export {
Fieldset as FormFieldset, Fieldset as FormFieldset,
Legend as FormLegend, Legend as FormLegend,
ElementField as FormElementField, ElementField as FormElementField,
Button as FormButton, Button as FormButton
}; };
+2 -2
View File
@@ -1,7 +1,7 @@
import Root from "./input.svelte"; import Root from './input.svelte';
export { export {
Root, Root,
// //
Root as Input, Root as Input
}; };
+9 -9
View File
@@ -1,12 +1,12 @@
<script lang="ts"> <script lang="ts">
import type { HTMLInputAttributes, HTMLInputTypeAttribute } from "svelte/elements"; import type { HTMLInputAttributes, HTMLInputTypeAttribute } from 'svelte/elements';
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
type InputType = Exclude<HTMLInputTypeAttribute, "file">; type InputType = Exclude<HTMLInputTypeAttribute, 'file'>;
type Props = WithElementRef< type Props = WithElementRef<
Omit<HTMLInputAttributes, "type"> & Omit<HTMLInputAttributes, 'type'> &
({ type: "file"; files?: FileList } | { type?: InputType; files?: undefined }) ({ type: 'file'; files?: FileList } | { type?: InputType; files?: undefined })
>; >;
let { let {
@@ -15,17 +15,17 @@
type, type,
files = $bindable(), files = $bindable(),
class: className, class: className,
"data-slot": dataSlot = "input", 'data-slot': dataSlot = 'input',
...restProps ...restProps
}: Props = $props(); }: Props = $props();
</script> </script>
{#if type === "file"} {#if type === 'file'}
<input <input
bind:this={ref} bind:this={ref}
data-slot={dataSlot} data-slot={dataSlot}
class={cn( class={cn(
"dark:bg-input/30 border-input 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 disabled:bg-input/50 dark:disabled:bg-input/80 h-8 rounded-lg border bg-transparent px-2.5 py-1 text-base transition-colors file:h-6 file:text-sm file:font-medium focus-visible:ring-3 aria-invalid:ring-3 md:text-sm file:text-foreground placeholder:text-muted-foreground w-full min-w-0 outline-none file:inline-flex file:border-0 file:bg-transparent disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50", 'h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40',
className className
)} )}
type="file" type="file"
@@ -38,7 +38,7 @@
bind:this={ref} bind:this={ref}
data-slot={dataSlot} data-slot={dataSlot}
class={cn( class={cn(
"dark:bg-input/30 border-input 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 disabled:bg-input/50 dark:disabled:bg-input/80 h-8 rounded-lg border bg-transparent px-2.5 py-1 text-base transition-colors file:h-6 file:text-sm file:font-medium focus-visible:ring-3 aria-invalid:ring-3 md:text-sm file:text-foreground placeholder:text-muted-foreground w-full min-w-0 outline-none file:inline-flex file:border-0 file:bg-transparent disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50", 'h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40',
className className
)} )}
{type} {type}
+2 -2
View File
@@ -1,7 +1,7 @@
import Root from "./label.svelte"; import Root from './label.svelte';
export { export {
Root, Root,
// //
Root as Label, Root as Label
}; };
+3 -3
View File
@@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { Label as LabelPrimitive } from "bits-ui"; import { Label as LabelPrimitive } 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="label" data-slot="label"
class={cn( class={cn(
"gap-2 text-sm leading-none font-medium group-data-[disabled=true]:opacity-50 peer-disabled:opacity-50 flex items-center select-none group-data-[disabled=true]:pointer-events-none peer-disabled:cursor-not-allowed", 'flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50',
className className
)} )}
{...restProps} {...restProps}
+9 -9
View File
@@ -1,11 +1,11 @@
import Root from "./popover.svelte"; import Root from './popover.svelte';
import Close from "./popover-close.svelte"; import Close from './popover-close.svelte';
import Content from "./popover-content.svelte"; import Content from './popover-content.svelte';
import Description from "./popover-description.svelte"; import Description from './popover-description.svelte';
import Header from "./popover-header.svelte"; import Header from './popover-header.svelte';
import Title from "./popover-title.svelte"; import Title from './popover-title.svelte';
import Trigger from "./popover-trigger.svelte"; import Trigger from './popover-trigger.svelte';
import Portal from "./popover-portal.svelte"; import Portal from './popover-portal.svelte';
export { export {
Root, Root,
@@ -24,5 +24,5 @@ export {
Title as PopoverTitle, Title as PopoverTitle,
Trigger as PopoverTrigger, Trigger as PopoverTrigger,
Close as PopoverClose, Close as PopoverClose,
Portal as PopoverPortal, Portal as PopoverPortal
}; };
@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { Popover as PopoverPrimitive } from "bits-ui"; import { Popover as PopoverPrimitive } from 'bits-ui';
let { ref = $bindable(null), ...restProps }: PopoverPrimitive.CloseProps = $props(); let { ref = $bindable(null), ...restProps }: PopoverPrimitive.CloseProps = $props();
</script> </script>
@@ -1,14 +1,14 @@
<script lang="ts"> <script lang="ts">
import { Popover as PopoverPrimitive } from "bits-ui"; import { Popover as PopoverPrimitive } from 'bits-ui';
import PopoverPortal from "./popover-portal.svelte"; import PopoverPortal from './popover-portal.svelte';
import { cn, type WithoutChildrenOrChild } from "$lib/utils.js"; import { cn, 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,
sideOffset = 4, sideOffset = 4,
align = "center", align = 'center',
portalProps, portalProps,
...restProps ...restProps
}: PopoverPrimitive.ContentProps & { }: PopoverPrimitive.ContentProps & {
@@ -23,7 +23,7 @@
{sideOffset} {sideOffset}
{align} {align}
class={cn( class={cn(
"bg-popover text-popover-foreground 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 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 flex flex-col gap-2.5 rounded-lg p-2.5 text-sm shadow-md ring-1 duration-100 data-[side=inline-start]:slide-in-from-right-2 data-[side=inline-end]:slide-in-from-left-2 z-50 w-72 origin-(--transform-origin) outline-hidden", 'z-50 flex w-72 origin-(--transform-origin) flex-col gap-2.5 rounded-lg bg-popover p-2.5 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-100 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
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="popover-description" data-slot="popover-description"
class={cn("text-muted-foreground", className)} class={cn('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),
@@ -13,7 +13,7 @@
<div <div
bind:this={ref} bind:this={ref}
data-slot="popover-header" data-slot="popover-header"
class={cn("flex flex-col gap-0.5 text-sm", className)} class={cn('flex flex-col gap-0.5 text-sm', className)}
{...restProps} {...restProps}
> >
{@render children?.()} {@render children?.()}

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