mirror of
https://github.com/pocket-id/pocket-id.git
synced 2025-12-06 09:13:19 +03:00
refactor: migrate shadcn-components to Svelte 5 and TW4 (#551)
Co-authored-by: Elias Schneider <login@eliasschneider.com>
This commit is contained in:
committed by
Elias Schneider
parent
05b443d984
commit
28c85990ba
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -1,3 +1,4 @@
|
||||
{
|
||||
"go.buildTags": "e2etest"
|
||||
"go.buildTags": "e2etest",
|
||||
"prettier.documentSelectors": ["**/*.svelte"],
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"$schema": "https://shadcn-svelte.com/schema.json",
|
||||
"$schema": "https://next.shadcn-svelte.com/schema.json",
|
||||
"style": "default",
|
||||
"tailwind": {
|
||||
"config": "tailwind.config.ts",
|
||||
@@ -8,7 +8,11 @@
|
||||
},
|
||||
"aliases": {
|
||||
"components": "$lib/components",
|
||||
"utils": "$lib/utils/style"
|
||||
"utils": "$lib/utils/style",
|
||||
"ui": "$lib/components/ui",
|
||||
"hooks": "$lib/hooks",
|
||||
"lib": "$lib"
|
||||
},
|
||||
"typescript": true
|
||||
"typescript": true,
|
||||
"registry": "https://next.shadcn-svelte.com/registry"
|
||||
}
|
||||
|
||||
10830
frontend/package-lock.json
generated
10830
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,61 +1,61 @@
|
||||
{
|
||||
"name": "pocket-id-frontend",
|
||||
"version": "0.53.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite dev --port 3000",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview --port 3000",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"lint": "prettier --check . && eslint .",
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@simplewebauthn/browser": "^13.1.0",
|
||||
"@tailwindcss/vite": "^4.0.0",
|
||||
"axios": "^1.8.2",
|
||||
"clsx": "^2.1.1",
|
||||
"crypto": "^1.0.1",
|
||||
"formsnap": "^1.0.1",
|
||||
"jose": "^5.9.6",
|
||||
"lucide-svelte": "^0.487.0",
|
||||
"mode-watcher": "^0.5.1",
|
||||
"qrcode": "^1.5.4",
|
||||
"svelte-sonner": "^0.3.28",
|
||||
"sveltekit-superforms": "^2.23.1",
|
||||
"tailwind-merge": "^2.6.0",
|
||||
"tailwind-variants": "^0.3.1",
|
||||
"zod": "^3.24.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@inlang/paraglide-js": "^2.0.0",
|
||||
"@inlang/plugin-m-function-matcher": "^2.0.7",
|
||||
"@inlang/plugin-message-format": "^4.0.0",
|
||||
"@internationalized/date": "^3.7.0",
|
||||
"@playwright/test": "^1.50.0",
|
||||
"@sveltejs/adapter-static": "^3.0.8",
|
||||
"@sveltejs/kit": "^2.20.7",
|
||||
"@sveltejs/vite-plugin-svelte": "^5.0.3",
|
||||
"@types/eslint": "^9.6.1",
|
||||
"@types/node": "^22.10.10",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"bits-ui": "^0.22.0",
|
||||
"cmdk-sv": "^0.0.19",
|
||||
"eslint": "^9.19.0",
|
||||
"eslint-config-prettier": "^10.0.1",
|
||||
"eslint-plugin-svelte": "^2.46.1",
|
||||
"globals": "^15.14.0",
|
||||
"prettier": "^3.4.2",
|
||||
"prettier-plugin-svelte": "^3.3.3",
|
||||
"prettier-plugin-tailwindcss": "^0.6.11",
|
||||
"svelte": "^5.19.3",
|
||||
"svelte-check": "^4.1.4",
|
||||
"tailwindcss": "^4.0.0",
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.7.3",
|
||||
"typescript-eslint": "^8.21.0",
|
||||
"vite": "^6.3.4"
|
||||
}
|
||||
"name": "pocket-id-frontend",
|
||||
"version": "0.53.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite dev --port 3000",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview --port 3000",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"lint": "prettier --check . && eslint .",
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@lucide/svelte": "^0.511.0",
|
||||
"@simplewebauthn/browser": "^13.1.0",
|
||||
"@tailwindcss/vite": "^4.1.7",
|
||||
"axios": "^1.8.2",
|
||||
"clsx": "^2.1.1",
|
||||
"crypto": "^1.0.1",
|
||||
"jose": "^5.9.6",
|
||||
"qrcode": "^1.5.4",
|
||||
"sveltekit-superforms": "^2.23.1",
|
||||
"tailwind-merge": "^3.3.0",
|
||||
"zod": "^3.24.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@inlang/paraglide-js": "^2.0.0",
|
||||
"@inlang/plugin-m-function-matcher": "^2.0.7",
|
||||
"@inlang/plugin-message-format": "^4.0.0",
|
||||
"@internationalized/date": "^3.7.0",
|
||||
"@playwright/test": "^1.50.0",
|
||||
"@sveltejs/adapter-static": "^3.0.8",
|
||||
"@sveltejs/kit": "^2.20.7",
|
||||
"@sveltejs/vite-plugin-svelte": "^5.0.3",
|
||||
"@types/eslint": "^9.6.1",
|
||||
"@types/node": "^22.10.10",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"bits-ui": "^1.5.3",
|
||||
"eslint": "^9.19.0",
|
||||
"eslint-config-prettier": "^10.0.1",
|
||||
"eslint-plugin-svelte": "^2.46.1",
|
||||
"formsnap": "^2.0.1",
|
||||
"globals": "^15.14.0",
|
||||
"mode-watcher": "^1.0.7",
|
||||
"prettier": "^3.4.2",
|
||||
"prettier-plugin-svelte": "^3.3.3",
|
||||
"prettier-plugin-tailwindcss": "^0.6.11",
|
||||
"svelte": "^5.31.1",
|
||||
"svelte-check": "^4.1.4",
|
||||
"svelte-sonner": "^1.0.1",
|
||||
"tailwind-variants": "^1.0.0",
|
||||
"tailwindcss": "^4.1.7",
|
||||
"tslib": "^2.8.1",
|
||||
"tw-animate-css": "^1.3.0",
|
||||
"typescript": "^5.7.3",
|
||||
"typescript-eslint": "^8.21.0",
|
||||
"vite": "^6.3.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,70 +1,199 @@
|
||||
@import 'tailwindcss';
|
||||
@import 'tw-animate-css';
|
||||
|
||||
@config '../tailwind.config.ts';
|
||||
/*
|
||||
The default border color has changed to `currentcolor` in Tailwind CSS v4,
|
||||
so we've added these compatibility styles to make sure everything still
|
||||
looks the same as it did with Tailwind CSS v3.
|
||||
|
||||
If we ever want to remove these styles, we need to add an explicit border
|
||||
color utility to any element that depends on these defaults.
|
||||
*/
|
||||
@layer base {
|
||||
*,
|
||||
::after,
|
||||
::before,
|
||||
::backdrop,
|
||||
::file-selector-button {
|
||||
border-color: var(--color-gray-200, currentcolor);
|
||||
}
|
||||
}
|
||||
|
||||
:root {
|
||||
--background: hsl(0 0% 100%);
|
||||
--foreground: hsl(240 10% 3.9%);
|
||||
|
||||
--muted: hsl(240 4.8% 95.9%);
|
||||
--muted-foreground: hsl(240 3.8% 46.1%);
|
||||
|
||||
--popover: hsl(0 0% 100%);
|
||||
--popover-foreground: hsl(240 10% 3.9%);
|
||||
|
||||
--card: hsl(0 0% 100%);
|
||||
--card-foreground: hsl(240 10% 3.9%);
|
||||
|
||||
--border: hsl(240 5.9% 90%);
|
||||
--input: hsl(240 5.9% 90%);
|
||||
|
||||
--primary: hsl(240 5.9% 10%);
|
||||
--primary-foreground: hsl(0 0% 98%);
|
||||
|
||||
--secondary: hsl(240 4.8% 95.9%);
|
||||
--secondary-foreground: hsl(240 5.9% 10%);
|
||||
|
||||
--accent: hsl(240 4.8% 95.9%);
|
||||
--accent-foreground: hsl(240 5.9% 10%);
|
||||
|
||||
--destructive: hsl(0 72.2% 50.6%);
|
||||
--destructive-foreground: hsl(0 0% 98%);
|
||||
|
||||
--ring: hsl(240 10% 3.9%);
|
||||
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: hsl(240 10% 3.9%);
|
||||
--foreground: hsl(0 0% 98%);
|
||||
|
||||
--muted: hsl(240 3.7% 15.9%);
|
||||
--muted-foreground: hsl(240 5% 64.9%);
|
||||
|
||||
--popover: hsl(240 10% 3.9%);
|
||||
--popover-foreground: hsl(0 0% 98%);
|
||||
|
||||
--card: hsl(240 10% 3.9%);
|
||||
--card-foreground: hsl(0 0% 98%);
|
||||
|
||||
--border: hsl(240 3.7% 15.9%);
|
||||
--input: hsl(240 3.7% 15.9%);
|
||||
|
||||
--primary: hsl(0 0% 98%);
|
||||
--primary-foreground: hsl(240 5.9% 10%);
|
||||
|
||||
--secondary: hsl(240 3.7% 15.9%);
|
||||
--secondary-foreground: hsl(0 0% 98%);
|
||||
|
||||
--accent: hsl(240 3.7% 15.9%);
|
||||
--accent-foreground: hsl(0 0% 98%);
|
||||
|
||||
--destructive: hsl(0 62.8% 30.6%);
|
||||
--destructive-foreground: hsl(0 0% 98%);
|
||||
|
||||
--ring: hsl(240 4.9% 83.9%);
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
/* Radius (for rounded-*) */
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) + 4px);
|
||||
|
||||
/* Colors */
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--color-muted: var(--muted);
|
||||
--color-muted-foreground: var(--muted-foreground);
|
||||
--color-popover: var(--popover);
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
--color-card: var(--card);
|
||||
--color-card-foreground: var(--card-foreground);
|
||||
--color-border: var(--border);
|
||||
--color-input: var(--input);
|
||||
--color-primary: var(--primary);
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
--color-secondary: var(--secondary);
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
--color-accent: var(--accent);
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
--color-destructive: var(--destructive);
|
||||
--color-destructive-foreground: var(--destructive-foreground);
|
||||
--color-ring: var(--ring);
|
||||
--color-radius: var(--radius);
|
||||
--color-sidebar-background: var(--sidebar-background);
|
||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||
--color-sidebar-primary: var(--sidebar-primary);
|
||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||
--color-sidebar-accent: var(--sidebar-accent);
|
||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
|
||||
/* Animations */
|
||||
--animate-accordion-up: accordion-up 0.2s ease-out;
|
||||
--animate-accordion-down: accordion-down 0.2s ease-out;
|
||||
--animate-caret-blink: caret-blink 1.25s ease-out infinite;
|
||||
|
||||
/* Font */
|
||||
--font-playfair: 'Playfair Display', serif;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 240 10% 3.9%;
|
||||
|
||||
--muted: 240 4.8% 95.9%;
|
||||
--muted-foreground: 240 3.8% 46.1%;
|
||||
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 240 10% 3.9%;
|
||||
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 240 10% 3.9%;
|
||||
|
||||
--border: 240 5.9% 90%;
|
||||
--input: 240 5.9% 90%;
|
||||
|
||||
--primary: 240 5.9% 10%;
|
||||
--primary-foreground: 0 0% 98%;
|
||||
|
||||
--secondary: 240 4.8% 95.9%;
|
||||
--secondary-foreground: 240 5.9% 10%;
|
||||
|
||||
--accent: 240 4.8% 95.9%;
|
||||
--accent-foreground: 240 5.9% 10%;
|
||||
|
||||
--destructive: 0 72.2% 50.6%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
|
||||
--ring: 240 10% 3.9%;
|
||||
|
||||
--radius: 0.5rem;
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 240 10% 3.9%;
|
||||
--foreground: 0 0% 98%;
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
|
||||
--muted: 240 3.7% 15.9%;
|
||||
--muted-foreground: 240 5% 64.9%;
|
||||
button {
|
||||
@apply cursor-pointer;
|
||||
}
|
||||
|
||||
--popover: 240 10% 3.9%;
|
||||
--popover-foreground: 0 0% 98%;
|
||||
@font-face {
|
||||
font-family: 'Playfair Display';
|
||||
font-weight: 400;
|
||||
src: url('/fonts/PlayfairDisplay-Regular.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Playfair Display';
|
||||
font-weight: 500;
|
||||
src: url('/fonts/PlayfairDisplay-Medium.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Playfair Display';
|
||||
font-weight: 600;
|
||||
src: url('/fonts/PlayfairDisplay-SemiBold.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Playfair Display';
|
||||
font-weight: 700;
|
||||
src: url('/fonts/PlayfairDisplay-Bold.woff') format('woff');
|
||||
}
|
||||
}
|
||||
|
||||
--card: 240 10% 3.9%;
|
||||
--card-foreground: 0 0% 98%;
|
||||
@keyframes accordion-down {
|
||||
from {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
--border: 240 3.7% 15.9%;
|
||||
--input: 240 3.7% 15.9%;
|
||||
to {
|
||||
height: var(--bits-accordion-content-height);
|
||||
}
|
||||
}
|
||||
|
||||
--primary: 0 0% 98%;
|
||||
--primary-foreground: 240 5.9% 10%;
|
||||
@keyframes accordion-up {
|
||||
from {
|
||||
height: var(--bits-accordion-content-height);
|
||||
}
|
||||
|
||||
--secondary: 240 3.7% 15.9%;
|
||||
--secondary-foreground: 0 0% 98%;
|
||||
to {
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
--accent: 240 3.7% 15.9%;
|
||||
--accent-foreground: 0 0% 98%;
|
||||
@keyframes caret-blink {
|
||||
0%,
|
||||
70%,
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
|
||||
--ring: 240 4.9% 83.9%;
|
||||
20%,
|
||||
50% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +231,6 @@
|
||||
animation: slide-bg-container 1.2s cubic-bezier(0.33, 1, 0.68, 1) forwards;
|
||||
}
|
||||
|
||||
/* Fade in for content after the slide is mostly complete */
|
||||
@keyframes delayed-fade {
|
||||
0%,
|
||||
40% {
|
||||
@@ -116,38 +244,3 @@
|
||||
.animate-delayed-fade {
|
||||
animation: delayed-fade 1.5s ease-out forwards;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
|
||||
button {
|
||||
@apply cursor-pointer;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Playfair Display';
|
||||
font-weight: 400;
|
||||
src: url('/fonts/PlayfairDisplay-Regular.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Playfair Display';
|
||||
font-weight: 500;
|
||||
src: url('/fonts/PlayfairDisplay-Medium.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Playfair Display';
|
||||
font-weight: 600;
|
||||
src: url('/fonts/PlayfairDisplay-SemiBold.woff') format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Playfair Display';
|
||||
font-weight: 700;
|
||||
src: url('/fonts/PlayfairDisplay-Bold.woff') format('woff');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import type { Paginated, SearchPaginationSortRequest } from '$lib/types/pagination.type';
|
||||
import { debounced } from '$lib/utils/debounce-util';
|
||||
import { cn } from '$lib/utils/style';
|
||||
import { ChevronDown } from 'lucide-svelte';
|
||||
import { ChevronDown } from '@lucide/svelte';
|
||||
import type { Snippet } from 'svelte';
|
||||
import Button from './ui/button/button.svelte';
|
||||
import { m } from '$lib/paraglide/messages';
|
||||
@@ -96,7 +96,7 @@
|
||||
)}
|
||||
placeholder={m.search()}
|
||||
type="text"
|
||||
oninput={(e) => onSearch((e.target as HTMLInputElement).value)}
|
||||
oninput={(e: Event) => onSearch((e.currentTarget as HTMLInputElement).value)}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
@@ -114,7 +114,7 @@
|
||||
<Checkbox
|
||||
disabled={selectionDisabled}
|
||||
checked={allChecked}
|
||||
onCheckedChange={(c) => onAllCheck(c as boolean)}
|
||||
onCheckedChange={(c: boolean) => onAllCheck(c as boolean)}
|
||||
/>
|
||||
</Table.Head>
|
||||
{/if}
|
||||
@@ -124,7 +124,7 @@
|
||||
<Button
|
||||
variant="ghost"
|
||||
class="flex items-center"
|
||||
on:click={() =>
|
||||
onclick={() =>
|
||||
onSort(
|
||||
column.sortColumn,
|
||||
requestOptions.sort?.direction === 'desc' ? 'asc' : 'desc'
|
||||
@@ -134,7 +134,7 @@
|
||||
{#if requestOptions.sort?.column === column.sortColumn}
|
||||
<ChevronDown
|
||||
class={cn(
|
||||
'ml-2 h-4 w-4',
|
||||
'ml-2 size-4',
|
||||
requestOptions.sort?.direction === 'asc' ? 'rotate-180' : ''
|
||||
)}
|
||||
/>
|
||||
@@ -155,7 +155,7 @@
|
||||
<Checkbox
|
||||
disabled={selectionDisabled}
|
||||
checked={selectedIds.includes(item.id)}
|
||||
onCheckedChange={(c) => onCheck(c as boolean, item.id)}
|
||||
onCheckedChange={(c: boolean) => onCheck(c, item.id)}
|
||||
/>
|
||||
</Table.Cell>
|
||||
{/if}
|
||||
@@ -169,18 +169,16 @@
|
||||
<div class="flex items-center space-x-2">
|
||||
<p class="text-sm font-medium">{m.items_per_page()}</p>
|
||||
<Select.Root
|
||||
selected={{
|
||||
label: items.pagination.itemsPerPage.toString(),
|
||||
value: items.pagination.itemsPerPage
|
||||
}}
|
||||
onSelectedChange={(v) => onPageSizeChange(v?.value as number)}
|
||||
type="single"
|
||||
value={items.pagination.itemsPerPage.toString()}
|
||||
onValueChange={(v) => onPageSizeChange(Number(v))}
|
||||
>
|
||||
<Select.Trigger class="h-9 w-[80px]">
|
||||
<Select.Value>{items.pagination.itemsPerPage}</Select.Value>
|
||||
{items.pagination.itemsPerPage}
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each availablePageSizes as size}
|
||||
<Select.Item value={size}>{size}</Select.Item>
|
||||
<Select.Item value={size.toString()}>{size}</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
@@ -191,25 +189,26 @@
|
||||
perPage={items.pagination.itemsPerPage}
|
||||
{onPageChange}
|
||||
page={items.pagination.currentPage}
|
||||
let:pages
|
||||
>
|
||||
<Pagination.Content class="flex justify-end">
|
||||
<Pagination.Item>
|
||||
<Pagination.PrevButton />
|
||||
</Pagination.Item>
|
||||
{#each pages as page (page.key)}
|
||||
{#if page.type !== 'ellipsis' && page.value != 0}
|
||||
<Pagination.Item>
|
||||
<Pagination.Link {page} isActive={items.pagination.currentPage === page.value}>
|
||||
{page.value}
|
||||
</Pagination.Link>
|
||||
</Pagination.Item>
|
||||
{/if}
|
||||
{/each}
|
||||
<Pagination.Item>
|
||||
<Pagination.NextButton />
|
||||
</Pagination.Item>
|
||||
</Pagination.Content>
|
||||
{#snippet children({ pages })}
|
||||
<Pagination.Content class="flex justify-end">
|
||||
<Pagination.Item>
|
||||
<Pagination.PrevButton />
|
||||
</Pagination.Item>
|
||||
{#each pages as page (page.key)}
|
||||
{#if page.type !== 'ellipsis' && page.value != 0}
|
||||
<Pagination.Item>
|
||||
<Pagination.Link {page} isActive={items.pagination.currentPage === page.value}>
|
||||
{page.value}
|
||||
</Pagination.Link>
|
||||
</Pagination.Item>
|
||||
{/if}
|
||||
{/each}
|
||||
<Pagination.Item>
|
||||
<Pagination.NextButton />
|
||||
</Pagination.Item>
|
||||
</Pagination.Content>
|
||||
{/snippet}
|
||||
</Pagination.Root>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
</Table.Cell>
|
||||
{/if}
|
||||
<Table.Cell>
|
||||
<Badge variant="outline">{toFriendlyEventString(item.event)}</Badge>
|
||||
<Badge class="rounded-full" variant="outline">{toFriendlyEventString(item.event)}</Badge>
|
||||
</Table.Cell>
|
||||
<Table.Cell
|
||||
>{item.city && item.country ? `${item.city}, ${item.country}` : m.unknown()}</Table.Cell
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { m } from '$lib/paraglide/messages';
|
||||
import { cn } from '$lib/utils/style';
|
||||
import { LucideChevronDown, type Icon as IconType } from 'lucide-svelte';
|
||||
import { LucideChevronDown, type Icon as IconType } from '@lucide/svelte';
|
||||
import { onMount, type Snippet } from 'svelte';
|
||||
import { slide } from 'svelte/transition';
|
||||
import { Button } from './ui/button';
|
||||
@@ -50,12 +50,12 @@
|
||||
</script>
|
||||
|
||||
<Card.Root>
|
||||
<Card.Header class="cursor-pointer" onclick={toggleExpanded}>
|
||||
<Card.Header class="bg-card cursor-pointer" onclick={toggleExpanded}>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<Card.Title class="flex items-center gap-2 text-xl font-semibold">
|
||||
{#if icon}{@const Icon = icon}
|
||||
<Icon class="text-primary/80 h-5 w-5" />
|
||||
<Icon class="text-primary/80 size-5" />
|
||||
{/if}
|
||||
{title}
|
||||
</Card.Title>
|
||||
@@ -65,17 +65,14 @@
|
||||
</div>
|
||||
<Button class="ml-10 h-8 p-3" variant="ghost" aria-label={m.expand_card()}>
|
||||
<LucideChevronDown
|
||||
class={cn(
|
||||
'h-5 w-5 transition-transform duration-200',
|
||||
expanded && 'rotate-180 transform'
|
||||
)}
|
||||
class={cn('size-5 transition-transform duration-200', expanded && 'rotate-180 transform')}
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
</Card.Header>
|
||||
{#if expanded}
|
||||
<div transition:slide={{ duration: 200 }}>
|
||||
<Card.Content class="bg-muted/20 pt-5">
|
||||
<Card.Content class="pt-5">
|
||||
{@render children()}
|
||||
</Card.Content>
|
||||
</div>
|
||||
|
||||
@@ -14,16 +14,18 @@
|
||||
</AlertDialog.Header>
|
||||
<AlertDialog.Footer>
|
||||
<AlertDialog.Cancel>Cancel</AlertDialog.Cancel>
|
||||
<AlertDialog.Action asChild>
|
||||
<Button
|
||||
variant={$confirmDialogStore.confirm.destructive ? 'destructive' : 'default'}
|
||||
on:click={() => {
|
||||
$confirmDialogStore.confirm.action();
|
||||
$confirmDialogStore.open = false;
|
||||
}}
|
||||
>
|
||||
{$confirmDialogStore.confirm.label}
|
||||
</Button>
|
||||
<AlertDialog.Action>
|
||||
{#snippet child()}
|
||||
<Button
|
||||
variant={$confirmDialogStore.confirm.destructive ? 'destructive' : 'default'}
|
||||
onclick={() => {
|
||||
$confirmDialogStore.confirm.action();
|
||||
$confirmDialogStore.open = false;
|
||||
}}
|
||||
>
|
||||
{$confirmDialogStore.confirm.label}
|
||||
</Button>
|
||||
{/snippet}
|
||||
</AlertDialog.Action>
|
||||
</AlertDialog.Footer>
|
||||
</AlertDialog.Content>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import * as Tooltip from '$lib/components/ui/tooltip';
|
||||
import { m } from '$lib/paraglide/messages';
|
||||
import { LucideCheck } from 'lucide-svelte';
|
||||
import { LucideCheck } from '@lucide/svelte';
|
||||
import type { Snippet } from 'svelte';
|
||||
|
||||
let { value, children }: { value: string; children: Snippet } = $props();
|
||||
@@ -17,7 +17,7 @@
|
||||
function onOpenChange(state: boolean) {
|
||||
open = state;
|
||||
if (!state) {
|
||||
copied = false;
|
||||
setTimeout(() => (copied = false), 500);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,15 +28,15 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Tooltip.Root closeOnPointerDown={false} {onOpenChange} {open}>
|
||||
<Tooltip.Trigger class="text-start" tabindex={-1} onclick={onClick}
|
||||
>{@render children()}</Tooltip.Trigger
|
||||
>
|
||||
<Tooltip.Content onclick={copyToClipboard}>
|
||||
{#if copied}
|
||||
<span class="flex items-center"><LucideCheck class="mr-1 h-4 w-4" /> {m.copied()}</span>
|
||||
{:else}
|
||||
<span>{m.click_to_copy()}</span>
|
||||
{/if}
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
<Tooltip.Provider>
|
||||
<Tooltip.Root {onOpenChange} {open} disableCloseOnTriggerClick>
|
||||
<Tooltip.Trigger class="text-start" onclick={onClick}>{@render children()}</Tooltip.Trigger>
|
||||
<Tooltip.Content onclick={copyToClipboard}>
|
||||
{#if copied}
|
||||
<span class="flex items-center"><LucideCheck class="mr-1 size-4" /> {m.copied()}</span>
|
||||
{:else}
|
||||
<span>{m.click_to_copy()}</span>
|
||||
{/if}
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
</Tooltip.Provider>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<script lang="ts">
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { m } from '$lib/paraglide/messages';
|
||||
import { LucideXCircle } from 'lucide-svelte';
|
||||
import { LucideXCircle } from '@lucide/svelte';
|
||||
|
||||
let { message, showButton = true }: { message: string; showButton?: boolean } = $props();
|
||||
</script>
|
||||
|
||||
<div class="mt-[20%] flex flex-col items-center">
|
||||
<LucideXCircle class="text-muted-foreground h-12 w-12" />
|
||||
<LucideXCircle class="text-muted-foreground size-12" />
|
||||
<h1 class="mt-3 text-2xl font-semibold">{m.something_went_wrong()}</h1>
|
||||
<p class="text-muted-foreground">{message}</p>
|
||||
{#if showButton}
|
||||
|
||||
@@ -75,15 +75,16 @@
|
||||
onfocus={() => (isInputFocused = true)}
|
||||
onblur={() => (isInputFocused = false)}
|
||||
/>
|
||||
<Popover.Root
|
||||
open={isOpen}
|
||||
disableFocusTrap
|
||||
openFocus={() => {}}
|
||||
closeOnOutsideClick={false}
|
||||
closeOnEscape={false}
|
||||
>
|
||||
<Popover.Root open={isOpen}>
|
||||
<Popover.Trigger tabindex={-1} class="h-0 w-full" aria-hidden />
|
||||
<Popover.Content class="p-0" sideOffset={5} sameWidth>
|
||||
<Popover.Content
|
||||
sameWidth
|
||||
class="p-0"
|
||||
sideOffset={5}
|
||||
trapFocus={false}
|
||||
interactOutsideBehavior="ignore"
|
||||
onCloseAutoFocus={(e) => e.preventDefault()}
|
||||
>
|
||||
{#each filteredSuggestions as suggestion, index}
|
||||
<div
|
||||
role="button"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import CustomClaimService from '$lib/services/custom-claim-service';
|
||||
import type { CustomClaim } from '$lib/types/custom-claim.type';
|
||||
import { LucideMinus, LucidePlus } from 'lucide-svelte';
|
||||
import { LucideMinus, LucidePlus } from '@lucide/svelte';
|
||||
import { onMount, type Snippet } from 'svelte';
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import AutoCompleteInput from './auto-complete-input.svelte';
|
||||
@@ -51,25 +51,25 @@
|
||||
variant="outline"
|
||||
size="sm"
|
||||
aria-label={m.remove_custom_claim()}
|
||||
on:click={() => (customClaims = customClaims.filter((_, index) => index !== i))}
|
||||
onclick={() => (customClaims = customClaims.filter((_, index) => index !== i))}
|
||||
>
|
||||
<LucideMinus class="h-4 w-4" />
|
||||
<LucideMinus class="size-4" />
|
||||
</Button>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</FormInput>
|
||||
{#if error}
|
||||
<p class="mt-1 text-sm text-red-500">{error}</p>
|
||||
<p class="text-destructive mt-1 text-xs">{error}</p>
|
||||
{/if}
|
||||
{#if customClaims.length < limit}
|
||||
<Button
|
||||
class="mt-2"
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
on:click={() => (customClaims = [...customClaims, { key: '', value: '' }])}
|
||||
onclick={() => (customClaims = [...customClaims, { key: '', value: '' }])}
|
||||
>
|
||||
<LucidePlus class="mr-1 h-4 w-4" />
|
||||
<LucidePlus class="mr-1 size-4" />
|
||||
{customClaims.length === 0 ? m.add_custom_claim() : m.add_another()}
|
||||
</Button>
|
||||
{/if}
|
||||
|
||||
@@ -11,24 +11,53 @@
|
||||
getLocalTimeZone,
|
||||
type DateValue
|
||||
} from '@internationalized/date';
|
||||
import CalendarIcon from 'lucide-svelte/icons/calendar';
|
||||
import CalendarIcon from '@lucide/svelte/icons/calendar';
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
|
||||
let { value = $bindable(), ...restProps }: HTMLAttributes<HTMLButtonElement> & { value: Date } =
|
||||
$props();
|
||||
type Props = {
|
||||
value?: Date;
|
||||
id?: string;
|
||||
} & HTMLAttributes<HTMLDivElement>;
|
||||
|
||||
let { value = $bindable(undefined), id, ...restProps }: Props = $props();
|
||||
|
||||
let calendarDisplayDate: CalendarDate | undefined = $state(
|
||||
value ? dateToCalendarDate(value) : undefined
|
||||
);
|
||||
|
||||
let date: CalendarDate = $state(dateToCalendarDate(value));
|
||||
let open = $state(false);
|
||||
|
||||
function dateToCalendarDate(date: Date) {
|
||||
return new CalendarDate(date.getFullYear(), date.getMonth() + 1, date.getDate());
|
||||
function dateToCalendarDate(d: Date): CalendarDate {
|
||||
return new CalendarDate(d.getFullYear(), d.getMonth() + 1, d.getDate());
|
||||
}
|
||||
|
||||
function onValueChange(newDate?: DateValue) {
|
||||
if (!newDate) return;
|
||||
$effect(() => {
|
||||
if (calendarDisplayDate) {
|
||||
const newExternalDate = calendarDisplayDate.toDate(getLocalTimeZone());
|
||||
if (!value || value.getTime() !== newExternalDate.getTime()) {
|
||||
value = newExternalDate;
|
||||
}
|
||||
} else {
|
||||
if (value !== undefined) {
|
||||
value = undefined;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
value = newDate.toDate(getLocalTimeZone());
|
||||
date = newDate as CalendarDate;
|
||||
$effect(() => {
|
||||
if (value) {
|
||||
const newInternalCalendarDate = dateToCalendarDate(value);
|
||||
if (!calendarDisplayDate || calendarDisplayDate.compare(newInternalCalendarDate) !== 0) {
|
||||
calendarDisplayDate = newInternalCalendarDate;
|
||||
}
|
||||
} else {
|
||||
if (calendarDisplayDate !== undefined) {
|
||||
calendarDisplayDate = undefined;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function handleCalendarInteraction(newDateValue?: DateValue) {
|
||||
open = false;
|
||||
}
|
||||
|
||||
@@ -37,19 +66,28 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<Popover.Root openFocus {open} onOpenChange={(o) => (open = o)}>
|
||||
<Popover.Trigger asChild let:builder>
|
||||
<Button
|
||||
{...restProps}
|
||||
variant="outline"
|
||||
class={cn('w-full justify-start text-left font-normal', !value && 'text-muted-foreground')}
|
||||
builders={[builder]}
|
||||
>
|
||||
<CalendarIcon class="mr-2 h-4 w-4" />
|
||||
{date ? df.format(date.toDate(getLocalTimeZone())) : m.select_a_date()}
|
||||
</Button>
|
||||
</Popover.Trigger>
|
||||
<Popover.Content class="w-auto p-0" align="start">
|
||||
<Calendar bind:value={date} initialFocus {onValueChange} />
|
||||
</Popover.Content>
|
||||
</Popover.Root>
|
||||
<div class="w-full" {...restProps}>
|
||||
<Popover.Root bind:open>
|
||||
<Popover.Trigger class="w-full">
|
||||
<Button
|
||||
{id}
|
||||
variant="outline"
|
||||
class={cn('w-full justify-start text-left font-normal', !value && 'text-muted-foreground')}
|
||||
aria-label={m.select_a_date()}
|
||||
>
|
||||
<CalendarIcon class="mr-2 size-4" />
|
||||
{calendarDisplayDate
|
||||
? df.format(calendarDisplayDate.toDate(getLocalTimeZone()))
|
||||
: m.select_a_date()}
|
||||
</Button>
|
||||
</Popover.Trigger>
|
||||
<Popover.Content class="w-auto p-0" align="start">
|
||||
<Calendar
|
||||
type="single"
|
||||
bind:value={calendarDisplayDate}
|
||||
onValueChange={handleCalendarInteraction}
|
||||
initialFocus
|
||||
/>
|
||||
</Popover.Content>
|
||||
</Popover.Root>
|
||||
</div>
|
||||
|
||||
@@ -45,17 +45,18 @@
|
||||
<DatePicker {id} bind:value={input.value as Date} />
|
||||
{:else}
|
||||
<Input
|
||||
aria-invalid={!!input.error}
|
||||
{id}
|
||||
{placeholder}
|
||||
{type}
|
||||
bind:value={input.value}
|
||||
{disabled}
|
||||
on:input={(e) => onInput?.(e)}
|
||||
oninput={(e) => onInput?.(e)}
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
{#if input?.error}
|
||||
<p class="mt-1 text-sm text-red-500">{input.error}</p>
|
||||
<p class="text-destructive mt-1 text-xs">{input.error}</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import Button from '$lib/components/ui/button/button.svelte';
|
||||
import { m } from '$lib/paraglide/messages';
|
||||
import { getProfilePictureUrl } from '$lib/utils/profile-picture-util';
|
||||
import { LucideLoader, LucideRefreshCw, LucideUpload } from 'lucide-svelte';
|
||||
import { LucideLoader, LucideRefreshCw, LucideUpload } from '@lucide/svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { openConfirmDialog } from '../confirm-dialog';
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
<div class="flex flex-col items-center gap-6 sm:flex-row">
|
||||
<div class="shrink-0">
|
||||
{#if isLdapUser}
|
||||
<Avatar.Root class="h-24 w-24">
|
||||
<Avatar.Root class="size-24">
|
||||
<Avatar.Image class="object-cover" src={imageDataURL} />
|
||||
</Avatar.Root>
|
||||
{:else}
|
||||
@@ -75,7 +75,7 @@
|
||||
accept="image/png, image/jpeg"
|
||||
onchange={onImageChange}
|
||||
>
|
||||
<div class="group relative h-24 w-24 rounded-full">
|
||||
<div class="group relative size-24 rounded-full">
|
||||
<Avatar.Root class="h-full w-full transition-opacity duration-200">
|
||||
<Avatar.Image
|
||||
class="object-cover group-hover:opacity-30 {isLoading ? 'opacity-30' : ''}"
|
||||
@@ -84,9 +84,9 @@
|
||||
</Avatar.Root>
|
||||
<div class="absolute inset-0 flex items-center justify-center">
|
||||
{#if isLoading}
|
||||
<LucideLoader class="h-5 w-5 animate-spin" />
|
||||
<LucideLoader class="size-5 animate-spin" />
|
||||
{:else}
|
||||
<LucideUpload class="h-5 w-5 opacity-0 transition-opacity group-hover:opacity-100" />
|
||||
<LucideUpload class="size-5 opacity-0 transition-opacity group-hover:opacity-100" />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
@@ -105,8 +105,8 @@
|
||||
{m.click_profile_picture_to_upload_custom()}
|
||||
</p>
|
||||
<p class="text-muted-foreground mb-2 text-sm">{m.image_should_be_in_format()}</p>
|
||||
<Button variant="outline" size="sm" on:click={onReset} disabled={isLoading || isLdapUser}>
|
||||
<LucideRefreshCw class="mr-2 h-4 w-4" />
|
||||
<Button variant="outline" size="sm" onclick={onReset} disabled={isLoading || isLdapUser}>
|
||||
<LucideRefreshCw class="mr-2 size-4" />
|
||||
{m.reset_to_default()}
|
||||
</Button>
|
||||
{/if}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import * as Command from '$lib/components/ui/command';
|
||||
import * as Popover from '$lib/components/ui/popover';
|
||||
import { cn } from '$lib/utils/style';
|
||||
import { LucideCheck, LucideChevronDown } from 'lucide-svelte';
|
||||
import { LucideCheck, LucideChevronDown } from '@lucide/svelte';
|
||||
import { tick } from 'svelte';
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
|
||||
@@ -52,21 +52,19 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<Popover.Root bind:open let:ids>
|
||||
<Popover.Trigger asChild let:builder>
|
||||
<Popover.Root bind:open {...restProps}>
|
||||
<Popover.Trigger class="w-full">
|
||||
<Button
|
||||
{...restProps}
|
||||
builders={[builder]}
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
aria-expanded={open}
|
||||
class={cn('justify-between', restProps.class)}
|
||||
>
|
||||
{items.find((item) => item.value === value)?.label || 'Select an option'}
|
||||
<LucideChevronDown class="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
<LucideChevronDown class="ml-2 size-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</Popover.Trigger>
|
||||
<Popover.Content class="p-0" sameWidth>
|
||||
<Popover.Content class="p-0">
|
||||
<Command.Root shouldFilter={false}>
|
||||
<Command.Input placeholder="Search..." oninput={(e: any) => filterItems(e.target.value)} />
|
||||
<Command.Empty>No results found.</Command.Empty>
|
||||
@@ -77,10 +75,11 @@
|
||||
onSelect={() => {
|
||||
value = item.value;
|
||||
onSelect?.(item.value);
|
||||
closeAndFocusTrigger(ids.trigger);
|
||||
// If you need to focus the trigger, you may need to refactor to get the trigger id another way
|
||||
closeAndFocusTrigger('popover-trigger');
|
||||
}}
|
||||
>
|
||||
<LucideCheck class={cn('mr-2 h-4 w-4', value !== item.value && 'text-transparent')} />
|
||||
<LucideCheck class={cn('mr-2 size-4', value !== item.value && 'text-transparent')} />
|
||||
{item.label}
|
||||
</Command.Item>
|
||||
{/each}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '$lib/components/ui/tooltip';
|
||||
import * as Tooltip from '$lib/components/ui/tooltip/index.js';
|
||||
import { m } from '$lib/paraglide/messages';
|
||||
import { LucideCalendar, LucidePencil, LucideTrash, type Icon as IconType } from 'lucide-svelte';
|
||||
import { LucideCalendar, LucidePencil, LucideTrash, type Icon as IconType } from '@lucide/svelte';
|
||||
|
||||
let {
|
||||
icon,
|
||||
@@ -24,7 +24,7 @@
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="bg-primary/10 text-primary mt-1 rounded-lg p-2">
|
||||
{#if icon}{@const Icon = icon}
|
||||
<Icon class="h-5 w-5" />
|
||||
<Icon class="size-5" />
|
||||
{/if}
|
||||
</div>
|
||||
<div>
|
||||
@@ -33,7 +33,7 @@
|
||||
</div>
|
||||
{#if description}
|
||||
<div class="text-muted-foreground mt-1 flex items-center text-xs">
|
||||
<LucideCalendar class="mr-1 h-3 w-3" />
|
||||
<LucideCalendar class="mr-1 size-3" />
|
||||
{description}
|
||||
</div>
|
||||
{/if}
|
||||
@@ -41,35 +41,39 @@
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2 opacity-0 transition-opacity group-hover:opacity-100">
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
on:click={onRename}
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
class="h-8 w-8"
|
||||
aria-label={m.rename()}
|
||||
>
|
||||
<LucidePencil class="h-4 w-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{m.rename()}</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip.Provider>
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger>
|
||||
<Button
|
||||
onclick={onRename}
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
class="size-8"
|
||||
aria-label={m.rename()}
|
||||
>
|
||||
<LucidePencil class="size-4" />
|
||||
</Button>
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Content>{m.rename()}</Tooltip.Content>
|
||||
</Tooltip.Root></Tooltip.Provider
|
||||
>
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
on:click={onDelete}
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
class="hover:bg-destructive/10 hover:text-destructive h-8 w-8"
|
||||
aria-label={m.delete()}
|
||||
>
|
||||
<LucideTrash class="h-4 w-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{m.delete()}</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip.Provider>
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger>
|
||||
<Button
|
||||
onclick={onDelete}
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
class="hover:bg-destructive/10 hover:text-destructive size-8"
|
||||
aria-label={m.delete()}
|
||||
>
|
||||
<LucideTrash class="size-4" />
|
||||
</Button>
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Content>{m.delete()}</Tooltip.Content>
|
||||
</Tooltip.Root></Tooltip.Provider
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import * as Avatar from '$lib/components/ui/avatar';
|
||||
import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
|
||||
import { m } from '$lib/paraglide/messages';
|
||||
import WebAuthnService from '$lib/services/webauthn-service';
|
||||
import userStore from '$lib/stores/user-store';
|
||||
import { getProfilePictureUrl } from '$lib/utils/profile-picture-util';
|
||||
import { LucideLogOut, LucideUser } from 'lucide-svelte';
|
||||
import { LucideLogOut, LucideUser } from '@lucide/svelte';
|
||||
|
||||
const webauthnService = new WebAuthnService();
|
||||
|
||||
@@ -17,11 +18,11 @@
|
||||
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger
|
||||
><Avatar.Root class="h-9 w-9">
|
||||
><Avatar.Root class="size-9">
|
||||
<Avatar.Image src={getProfilePictureUrl($userStore?.id)} />
|
||||
</Avatar.Root></DropdownMenu.Trigger
|
||||
>
|
||||
<DropdownMenu.Content class="min-w-40" align="start">
|
||||
<DropdownMenu.Content class="min-w-40" align="end">
|
||||
<DropdownMenu.Label class="font-normal">
|
||||
<div class="flex flex-col space-y-1">
|
||||
<p class="text-sm leading-none font-medium">
|
||||
@@ -33,11 +34,11 @@
|
||||
</DropdownMenu.Label>
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.Group>
|
||||
<DropdownMenu.Item href="/settings/account"
|
||||
><LucideUser class="mr-2 h-4 w-4" /> {m.my_account()}</DropdownMenu.Item
|
||||
<DropdownMenu.Item onclick={() => goto('/settings/account')}
|
||||
><LucideUser class="mr-2 size-4" /> {m.my_account()}</DropdownMenu.Item
|
||||
>
|
||||
<DropdownMenu.Item on:click={logout}
|
||||
><LucideLogOut class="mr-2 h-4 w-4" /> {m.logout()}</DropdownMenu.Item
|
||||
<DropdownMenu.Item onclick={logout}
|
||||
><LucideLogOut class="mr-2 size-4" /> {m.logout()}</DropdownMenu.Item
|
||||
>
|
||||
</DropdownMenu.Group>
|
||||
</DropdownMenu.Content>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
href="/settings/account"
|
||||
class="flex items-center gap-3 transition-opacity hover:opacity-80"
|
||||
>
|
||||
<Logo class="h-8 w-8" />
|
||||
<Logo class="size-8" />
|
||||
<h1 class="text-lg font-semibold tracking-tight" data-testid="application-name">
|
||||
{$appConfigStore.appName}
|
||||
</h1>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
let { ...props }: HTMLAttributes<HTMLImageElement> = $props();
|
||||
|
||||
const isDarkMode = $derived($mode === 'dark');
|
||||
const isDarkMode = $derived(mode.current === 'dark');
|
||||
</script>
|
||||
|
||||
<img {...props} src="/api/application-configuration/logo?light={!isDarkMode}" alt={m.logo()} />
|
||||
|
||||
@@ -77,15 +77,12 @@
|
||||
<div>
|
||||
<Label for="expiration">{m.expiration()}</Label>
|
||||
<Select.Root
|
||||
selected={{
|
||||
label: Object.keys(availableExpirations)[0],
|
||||
value: Object.keys(availableExpirations)[0]
|
||||
}}
|
||||
onSelectedChange={(v) =>
|
||||
(selectedExpiration = v!.value as keyof typeof availableExpirations)}
|
||||
type="single"
|
||||
value={Object.keys(availableExpirations)[0]}
|
||||
onValueChange={(v) => (selectedExpiration = v! as keyof typeof availableExpirations)}
|
||||
>
|
||||
<Select.Trigger class="h-9 w-full">
|
||||
<Select.Value>{selectedExpiration}</Select.Value>
|
||||
{selectedExpiration}
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each Object.keys(availableExpirations) as key}
|
||||
@@ -124,8 +121,8 @@
|
||||
class="mb-2"
|
||||
value={oneTimeLink}
|
||||
size={180}
|
||||
color={$mode === 'dark' ? '#FFFFFF' : '#000000'}
|
||||
backgroundColor={$mode === 'dark' ? '#000000' : '#FFFFFF'}
|
||||
color={mode.current === 'dark' ? '#FFFFFF' : '#000000'}
|
||||
backgroundColor={mode.current === 'dark' ? '#000000' : '#FFFFFF'}
|
||||
/>
|
||||
<CopyToClipboard value={oneTimeLink!}>
|
||||
<p data-testId="login-code-link">{oneTimeLink!}</p>
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
<script lang="ts">
|
||||
export let icon: ConstructorOfATypedSvelteComponent;
|
||||
export let name: string;
|
||||
export let description: string;
|
||||
import type { Icon as IconType } from '@lucide/svelte';
|
||||
interface Props {
|
||||
icon: typeof IconType;
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
let { icon, name, description }: Props = $props();
|
||||
|
||||
const SvelteComponent = $derived(icon);
|
||||
</script>
|
||||
|
||||
<div class="flex items-center">
|
||||
<div class="bg-muted mr-5 rounded-lg p-2"><svelte:component this={icon} /></div>
|
||||
<div class="bg-muted mr-5 rounded-lg p-2"><SvelteComponent /></div>
|
||||
<div class="text-start">
|
||||
<h3 class="font-semibold">{name}</h3>
|
||||
<p class="text-muted-foreground text-sm">{description}</p>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { m } from '$lib/paraglide/messages';
|
||||
import { LucideMail, LucideUser, LucideUsers } from 'lucide-svelte';
|
||||
import { LucideMail, LucideUser, LucideUsers } from '@lucide/svelte';
|
||||
import ScopeItem from './scope-item.svelte';
|
||||
|
||||
let { scope }: { scope: string } = $props();
|
||||
|
||||
@@ -3,19 +3,16 @@
|
||||
import { buttonVariants } from '$lib/components/ui/button/index.js';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = AlertDialogPrimitive.ActionProps;
|
||||
type $$Events = AlertDialogPrimitive.ActionEvents;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: AlertDialogPrimitive.ActionProps = $props();
|
||||
</script>
|
||||
|
||||
<AlertDialogPrimitive.Action
|
||||
bind:ref
|
||||
data-slot="alert-dialog-action"
|
||||
class={cn(buttonVariants(), className)}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:keydown
|
||||
let:builder
|
||||
>
|
||||
<slot {builder} />
|
||||
</AlertDialogPrimitive.Action>
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -3,19 +3,16 @@
|
||||
import { buttonVariants } from '$lib/components/ui/button/index.js';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = AlertDialogPrimitive.CancelProps;
|
||||
type $$Events = AlertDialogPrimitive.CancelEvents;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: AlertDialogPrimitive.CancelProps = $props();
|
||||
</script>
|
||||
|
||||
<AlertDialogPrimitive.Cancel
|
||||
class={cn(buttonVariants({ variant: 'outline' }), 'mt-2 sm:mt-0', className)}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:keydown
|
||||
let:builder
|
||||
>
|
||||
<slot {builder} />
|
||||
</AlertDialogPrimitive.Cancel>
|
||||
bind:ref
|
||||
data-slot="alert-dialog-cancel"
|
||||
class={cn(buttonVariants({ variant: 'outline' }), className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -1,28 +1,27 @@
|
||||
<script lang="ts">
|
||||
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
|
||||
import * as AlertDialog from './index.js';
|
||||
import { cn, flyAndScale } from '$lib/utils/style.js';
|
||||
import AlertDialogOverlay from './alert-dialog-overlay.svelte';
|
||||
import { cn, type WithoutChild, type WithoutChildrenOrChild } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = AlertDialogPrimitive.ContentProps;
|
||||
|
||||
export let transition: $$Props['transition'] = flyAndScale;
|
||||
export let transitionConfig: $$Props['transitionConfig'] = undefined;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
portalProps,
|
||||
...restProps
|
||||
}: WithoutChild<AlertDialogPrimitive.ContentProps> & {
|
||||
portalProps?: WithoutChildrenOrChild<AlertDialogPrimitive.PortalProps>;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<AlertDialog.Portal>
|
||||
<AlertDialog.Overlay />
|
||||
<AlertDialogPrimitive.Portal {...portalProps}>
|
||||
<AlertDialogOverlay />
|
||||
<AlertDialogPrimitive.Content
|
||||
{transition}
|
||||
{transitionConfig}
|
||||
bind:ref
|
||||
data-slot="alert-dialog-content"
|
||||
class={cn(
|
||||
'bg-background fixed top-[50%] left-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border p-6 shadow-lg sm:rounded-lg md:w-full',
|
||||
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg',
|
||||
className
|
||||
)}
|
||||
{...$$restProps}
|
||||
>
|
||||
<slot />
|
||||
</AlertDialogPrimitive.Content>
|
||||
</AlertDialog.Portal>
|
||||
{...restProps}
|
||||
/>
|
||||
</AlertDialogPrimitive.Portal>
|
||||
|
||||
@@ -2,15 +2,16 @@
|
||||
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = AlertDialogPrimitive.DescriptionProps;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: AlertDialogPrimitive.DescriptionProps = $props();
|
||||
</script>
|
||||
|
||||
<AlertDialogPrimitive.Description
|
||||
bind:ref
|
||||
data-slot="alert-dialog-description"
|
||||
class={cn('text-muted-foreground text-sm', className)}
|
||||
{...$$restProps}
|
||||
>
|
||||
<slot />
|
||||
</AlertDialogPrimitive.Description>
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
<script lang="ts">
|
||||
import { cn, type WithElementRef } from '$lib/utils/style.js';
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
class={cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', className)}
|
||||
{...$$restProps}
|
||||
bind:this={ref}
|
||||
data-slot="alert-dialog-footer"
|
||||
class={cn('flex flex-col-reverse gap-2 sm:flex-row sm:justify-end', className)}
|
||||
{...restProps}
|
||||
>
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
<script lang="ts">
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
import { cn, type WithElementRef } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<div class={cn('flex flex-col space-y-2 text-center sm:text-left', className)} {...$$restProps}>
|
||||
<slot />
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="alert-dialog-header"
|
||||
class={cn('flex flex-col gap-2 text-center sm:text-left', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
<script lang="ts">
|
||||
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
|
||||
import { fade } from 'svelte/transition';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = AlertDialogPrimitive.OverlayProps;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export let transition: $$Props['transition'] = fade;
|
||||
export let transitionConfig: $$Props['transitionConfig'] = {
|
||||
duration: 150
|
||||
};
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: AlertDialogPrimitive.OverlayProps = $props();
|
||||
</script>
|
||||
|
||||
<AlertDialogPrimitive.Overlay
|
||||
{transition}
|
||||
{transitionConfig}
|
||||
class={cn('bg-background/80 fixed inset-0 z-50 backdrop-blur-sm ', className)}
|
||||
{...$$restProps}
|
||||
bind:ref
|
||||
data-slot="alert-dialog-overlay"
|
||||
class={cn(
|
||||
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
|
||||
|
||||
type $$Props = AlertDialogPrimitive.PortalProps;
|
||||
</script>
|
||||
|
||||
<AlertDialogPrimitive.Portal {...$$restProps}>
|
||||
<slot />
|
||||
</AlertDialogPrimitive.Portal>
|
||||
@@ -2,13 +2,16 @@
|
||||
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = AlertDialogPrimitive.TitleProps;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export let level: $$Props['level'] = 'h3';
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: AlertDialogPrimitive.TitleProps = $props();
|
||||
</script>
|
||||
|
||||
<AlertDialogPrimitive.Title class={cn('text-lg font-semibold', className)} {level} {...$$restProps}>
|
||||
<slot />
|
||||
</AlertDialogPrimitive.Title>
|
||||
<AlertDialogPrimitive.Title
|
||||
bind:ref
|
||||
data-slot="alert-dialog-title"
|
||||
class={cn('text-lg font-semibold', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
|
||||
|
||||
let { ref = $bindable(null), ...restProps }: AlertDialogPrimitive.TriggerProps = $props();
|
||||
</script>
|
||||
|
||||
<AlertDialogPrimitive.Trigger bind:ref data-slot="alert-dialog-trigger" {...restProps} />
|
||||
@@ -1,9 +1,8 @@
|
||||
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
|
||||
|
||||
import Trigger from './alert-dialog-trigger.svelte';
|
||||
import Title from './alert-dialog-title.svelte';
|
||||
import Action from './alert-dialog-action.svelte';
|
||||
import Cancel from './alert-dialog-cancel.svelte';
|
||||
import Portal from './alert-dialog-portal.svelte';
|
||||
import Footer from './alert-dialog-footer.svelte';
|
||||
import Header from './alert-dialog-header.svelte';
|
||||
import Overlay from './alert-dialog-overlay.svelte';
|
||||
@@ -11,14 +10,12 @@ import Content from './alert-dialog-content.svelte';
|
||||
import Description from './alert-dialog-description.svelte';
|
||||
|
||||
const Root = AlertDialogPrimitive.Root;
|
||||
const Trigger = AlertDialogPrimitive.Trigger;
|
||||
|
||||
export {
|
||||
Root,
|
||||
Title,
|
||||
Action,
|
||||
Cancel,
|
||||
Portal,
|
||||
Footer,
|
||||
Header,
|
||||
Trigger,
|
||||
@@ -30,7 +27,6 @@ export {
|
||||
Title as AlertDialogTitle,
|
||||
Action as AlertDialogAction,
|
||||
Cancel as AlertDialogCancel,
|
||||
Portal as AlertDialogPortal,
|
||||
Footer as AlertDialogFooter,
|
||||
Header as AlertDialogHeader,
|
||||
Trigger as AlertDialogTrigger,
|
||||
|
||||
@@ -1,13 +1,23 @@
|
||||
<script lang="ts">
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
import { cn, type WithElementRef } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<div class={cn('text-sm [&_p]:leading-relaxed', className)} {...$$restProps}>
|
||||
<slot />
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="alert-description"
|
||||
class={cn(
|
||||
'text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
<script lang="ts">
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import type { HeadingLevel } from './index.js';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
import { cn, type WithElementRef } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = HTMLAttributes<HTMLHeadingElement> & {
|
||||
level?: HeadingLevel;
|
||||
};
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export let level: $$Props['level'] = 'h5';
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<svelte:element
|
||||
this={level}
|
||||
class={cn('mb-1 leading-none font-medium tracking-tight', className)}
|
||||
{...$$restProps}
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="alert-title"
|
||||
class={cn('col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight', className)}
|
||||
{...restProps}
|
||||
>
|
||||
<slot />
|
||||
</svelte:element>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -1,21 +1,44 @@
|
||||
<script lang="ts" module>
|
||||
import { type VariantProps, tv } from 'tailwind-variants';
|
||||
|
||||
export const alertVariants = tv({
|
||||
base: 'relative grid w-full grid-cols-[0_1fr] items-start gap-y-0.5 rounded-lg border px-4 py-3 text-sm has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] has-[>svg]:gap-x-3 [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current',
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'bg-card text-card-foreground',
|
||||
destructive:
|
||||
'text-destructive bg-card *:data-[slot=alert-description]:text-destructive/90 [&>svg]:text-current',
|
||||
warning:
|
||||
'bg-amber-100 text-amber-900 dark:bg-amber-900 dark:text-amber-100 [&>svg]:text-amber-900 dark:[&>svg]:text-amber-100'
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default'
|
||||
}
|
||||
});
|
||||
|
||||
export type AlertVariant = VariantProps<typeof alertVariants>['variant'];
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
import { LucideX } from 'lucide-svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import { type Variant, alertVariants } from './index.js';
|
||||
import { cn, type WithElementRef } from '$lib/utils/style.js';
|
||||
import { onMount } from 'svelte';
|
||||
import { LucideX } from '@lucide/svelte';
|
||||
|
||||
type $$Props = HTMLAttributes<HTMLDivElement> & {
|
||||
variant?: Variant;
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
variant = 'default',
|
||||
children,
|
||||
dismissibleId = undefined,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
|
||||
variant?: AlertVariant;
|
||||
dismissibleId?: string;
|
||||
};
|
||||
} = $props();
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export let variant: $$Props['variant'] = 'default';
|
||||
export let dismissibleId: $$Props['dismissibleId'] = undefined;
|
||||
export { className as class };
|
||||
|
||||
let isVisible = !dismissibleId;
|
||||
let isVisible = $state(!dismissibleId);
|
||||
|
||||
onMount(() => {
|
||||
if (dismissibleId) {
|
||||
@@ -34,11 +57,17 @@
|
||||
</script>
|
||||
|
||||
{#if isVisible}
|
||||
<div class={cn(alertVariants({ variant }), className)} {...$$restProps} role="alert">
|
||||
<slot />
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="alert"
|
||||
class={cn(alertVariants({ variant }), className)}
|
||||
{...restProps}
|
||||
role="alert"
|
||||
>
|
||||
{@render children?.()}
|
||||
{#if dismissibleId}
|
||||
<button on:click={dismiss} class="absolute top-0 right-0 m-3 text-black dark:text-white"
|
||||
><LucideX class="w-4" /></button
|
||||
<button onclick={dismiss} class="absolute top-0 right-0 m-3 text-black dark:text-white"
|
||||
><LucideX class="size-4" /></button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -1,35 +1,14 @@
|
||||
import { type VariantProps, tv } from 'tailwind-variants';
|
||||
|
||||
import Root from './alert.svelte';
|
||||
import Description from './alert-description.svelte';
|
||||
import Title from './alert-title.svelte';
|
||||
import Root from './alert.svelte';
|
||||
|
||||
export const alertVariants = tv({
|
||||
base: '[&>svg]:text-foreground relative w-full rounded-lg border p-4 [&:has(svg)]:pl-11 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4',
|
||||
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'bg-background text-foreground',
|
||||
destructive:
|
||||
'border-destructive/50 text-destructive text-destructive dark:border-destructive [&>svg]:text-destructive',
|
||||
warning:
|
||||
'bg-amber-100 text-amber-900 dark:bg-amber-900 dark:text-amber-100 [&>svg]:text-amber-900 dark:[&>svg]:text-amber-100'
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default'
|
||||
}
|
||||
});
|
||||
|
||||
export type Variant = VariantProps<typeof alertVariants>['variant'];
|
||||
export type HeadingLevel = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
|
||||
export { alertVariants, type AlertVariant } from './alert.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
Description,
|
||||
Title,
|
||||
//
|
||||
Root as Alert,
|
||||
Description as AlertDescription,
|
||||
Title as AlertTitle,
|
||||
Description,
|
||||
Root,
|
||||
Title
|
||||
Title as AlertTitle
|
||||
};
|
||||
|
||||
@@ -2,15 +2,16 @@
|
||||
import { Avatar as AvatarPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = AvatarPrimitive.FallbackProps;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: AvatarPrimitive.FallbackProps = $props();
|
||||
</script>
|
||||
|
||||
<AvatarPrimitive.Fallback
|
||||
class={cn('bg-muted flex h-full w-full items-center justify-center rounded-full', className)}
|
||||
{...$$restProps}
|
||||
>
|
||||
<slot />
|
||||
</AvatarPrimitive.Fallback>
|
||||
bind:ref
|
||||
data-slot="avatar-fallback"
|
||||
class={cn('bg-muted flex size-full items-center justify-center rounded-full', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -2,17 +2,16 @@
|
||||
import { Avatar as AvatarPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = AvatarPrimitive.ImageProps;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export let src: $$Props['src'] = undefined;
|
||||
export let alt: $$Props['alt'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: AvatarPrimitive.ImageProps = $props();
|
||||
</script>
|
||||
|
||||
<AvatarPrimitive.Image
|
||||
{src}
|
||||
{alt}
|
||||
class={cn('aspect-square h-full w-full', className)}
|
||||
{...$$restProps}
|
||||
bind:ref
|
||||
data-slot="avatar-image"
|
||||
class={cn('aspect-square size-full', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -2,17 +2,16 @@
|
||||
import { Avatar as AvatarPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = AvatarPrimitive.Props;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export let delayMs: $$Props['delayMs'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: AvatarPrimitive.RootProps = $props();
|
||||
</script>
|
||||
|
||||
<AvatarPrimitive.Root
|
||||
{delayMs}
|
||||
class={cn('relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full border', className)}
|
||||
{...$$restProps}
|
||||
>
|
||||
<slot />
|
||||
</AvatarPrimitive.Root>
|
||||
bind:ref
|
||||
data-slot="avatar"
|
||||
class={cn('relative flex size-10 shrink-0 overflow-hidden rounded-full border', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -1,18 +1,49 @@
|
||||
<script lang="ts">
|
||||
import { type Variant, badgeVariants } from './index.js';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
<script lang="ts" module>
|
||||
import { type VariantProps, tv } from 'tailwind-variants';
|
||||
|
||||
let className: string | undefined | null = undefined;
|
||||
export let href: string | undefined = undefined;
|
||||
export let variant: Variant = 'default';
|
||||
export { className as class };
|
||||
export const badgeVariants = 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 inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden whitespace-nowrap rounded-md border px-2 py-0.5 text-xs font-medium transition-[color,box-shadow] focus-visible:ring-[3px] [&>svg]:pointer-events-none [&>svg]:size-3',
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'bg-primary text-primary-foreground [a&]:hover:bg-primary/90 border-transparent',
|
||||
secondary:
|
||||
'bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90 border-transparent',
|
||||
destructive:
|
||||
'bg-destructive [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/70 border-transparent text-white',
|
||||
outline: 'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground'
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default'
|
||||
}
|
||||
});
|
||||
|
||||
export type BadgeVariant = VariantProps<typeof badgeVariants>['variant'];
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import type { HTMLAnchorAttributes } from 'svelte/elements';
|
||||
import { cn, type WithElementRef } from '$lib/utils/style.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
href,
|
||||
class: className,
|
||||
variant = 'default',
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAnchorAttributes> & {
|
||||
variant?: BadgeVariant;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<svelte:element
|
||||
this={href ? 'a' : 'span'}
|
||||
bind:this={ref}
|
||||
data-slot="badge"
|
||||
{href}
|
||||
class={cn(badgeVariants({ variant, className }))}
|
||||
{...$$restProps}
|
||||
class={cn(badgeVariants({ variant }), className)}
|
||||
{...restProps}
|
||||
>
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</svelte:element>
|
||||
|
||||
@@ -1,20 +1,2 @@
|
||||
import { type VariantProps, tv } from 'tailwind-variants';
|
||||
export { default as Badge } from './badge.svelte';
|
||||
|
||||
export const badgeVariants = tv({
|
||||
base: 'inline-flex select-none items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 break-keep whitespace-nowrap',
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'border-transparent bg-primary text-primary-foreground hover:bg-primary/80',
|
||||
secondary: 'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
||||
destructive:
|
||||
'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80',
|
||||
outline: 'text-foreground'
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default'
|
||||
}
|
||||
});
|
||||
|
||||
export type Variant = VariantProps<typeof badgeVariants>['variant'];
|
||||
export { badgeVariants, type BadgeVariant } from './badge.svelte';
|
||||
|
||||
@@ -1,33 +1,90 @@
|
||||
<script lang="ts">
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
import { Button as ButtonPrimitive } from 'bits-ui';
|
||||
import LoaderCircle from 'lucide-svelte/icons/loader-circle';
|
||||
import type { ClassNameValue } from 'tailwind-merge';
|
||||
import { type Events, type Props, buttonVariants } from './index.js';
|
||||
<script lang="ts" module>
|
||||
import { cn, type WithElementRef } from '$lib/utils/style.js';
|
||||
import type { HTMLAnchorAttributes, HTMLButtonAttributes } from 'svelte/elements';
|
||||
import { type VariantProps, tv } from 'tailwind-variants';
|
||||
|
||||
type $$Props = Props;
|
||||
type $$Events = Events;
|
||||
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 inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium outline-none transition-all focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90',
|
||||
destructive:
|
||||
'bg-destructive shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60 text-white',
|
||||
outline:
|
||||
'bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 border',
|
||||
secondary: 'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80',
|
||||
ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
|
||||
link: 'text-primary underline-offset-4 hover:underline'
|
||||
},
|
||||
size: {
|
||||
default: 'h-9 px-4 py-2 has-[>svg]:px-3',
|
||||
sm: 'h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5',
|
||||
lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
|
||||
icon: 'size-9'
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default',
|
||||
size: 'default'
|
||||
}
|
||||
});
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export let variant: $$Props['variant'] = 'default';
|
||||
export let size: $$Props['size'] = 'default';
|
||||
export let disabled: boolean | undefined | null = false;
|
||||
export let isLoading: $$Props['isLoading'] = false;
|
||||
export let builders: $$Props['builders'] = [];
|
||||
export { className as class };
|
||||
export type ButtonVariant = VariantProps<typeof buttonVariants>['variant'];
|
||||
export type ButtonSize = VariantProps<typeof buttonVariants>['size'];
|
||||
|
||||
export type ButtonProps = WithElementRef<HTMLButtonAttributes> &
|
||||
WithElementRef<HTMLAnchorAttributes> & {
|
||||
variant?: ButtonVariant;
|
||||
size?: ButtonSize;
|
||||
isLoading?: boolean;
|
||||
};
|
||||
</script>
|
||||
|
||||
<ButtonPrimitive.Root
|
||||
{builders}
|
||||
disabled={isLoading || disabled}
|
||||
class={cn(buttonVariants({ variant, size, className: className as ClassNameValue }))}
|
||||
type="button"
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:keydown
|
||||
>
|
||||
{#if isLoading}
|
||||
<LoaderCircle class="mr-2 h-4 w-4 animate-spin" />
|
||||
{/if}
|
||||
<slot />
|
||||
</ButtonPrimitive.Root>
|
||||
<script lang="ts">
|
||||
import LoaderCircle from '@lucide/svelte/icons/loader-circle';
|
||||
|
||||
let {
|
||||
class: className,
|
||||
variant = 'default',
|
||||
size = 'default',
|
||||
ref = $bindable(null),
|
||||
href = undefined,
|
||||
type = 'button',
|
||||
disabled,
|
||||
isLoading = false,
|
||||
children,
|
||||
...restProps
|
||||
}: ButtonProps = $props();
|
||||
</script>
|
||||
|
||||
{#if href}
|
||||
<a
|
||||
bind:this={ref}
|
||||
data-slot="button"
|
||||
class={cn(buttonVariants({ variant, size }), className)}
|
||||
href={disabled ? undefined : href}
|
||||
aria-disabled={disabled}
|
||||
role={disabled ? 'link' : undefined}
|
||||
tabindex={disabled ? -1 : undefined}
|
||||
{...restProps}
|
||||
>
|
||||
{#if isLoading}
|
||||
<LoaderCircle class="mr-2 size-4 animate-spin" />
|
||||
{/if}
|
||||
{@render children?.()}
|
||||
</a>
|
||||
{:else}
|
||||
<button
|
||||
bind:this={ref}
|
||||
data-slot="button"
|
||||
class={cn(buttonVariants({ variant, size }), className)}
|
||||
{type}
|
||||
{disabled}
|
||||
{...restProps}
|
||||
>
|
||||
{#if isLoading}
|
||||
<LoaderCircle class="mr-2 size-4 animate-spin" />
|
||||
{/if}
|
||||
{@render children?.()}
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
@@ -1,49 +1,17 @@
|
||||
import { type VariantProps, tv } from 'tailwind-variants';
|
||||
import type { Button as ButtonPrimitive } from 'bits-ui';
|
||||
import Root from './button.svelte';
|
||||
|
||||
const buttonVariants = tv({
|
||||
base: 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
|
||||
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
|
||||
outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
|
||||
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
||||
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
||||
link: 'text-primary underline-offset-4 hover:underline'
|
||||
},
|
||||
size: {
|
||||
default: 'h-10 px-4 py-2',
|
||||
sm: 'h-9 rounded-md px-3',
|
||||
lg: 'h-11 rounded-md px-8',
|
||||
icon: 'h-10 w-10'
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: 'default',
|
||||
size: 'default'
|
||||
}
|
||||
});
|
||||
|
||||
type Variant = VariantProps<typeof buttonVariants>['variant'];
|
||||
type Size = VariantProps<typeof buttonVariants>['size'];
|
||||
|
||||
type Props = ButtonPrimitive.Props & {
|
||||
variant?: Variant;
|
||||
size?: Size;
|
||||
isLoading?: boolean;
|
||||
};
|
||||
|
||||
type Events = ButtonPrimitive.Events;
|
||||
import Root, {
|
||||
type ButtonProps,
|
||||
type ButtonSize,
|
||||
type ButtonVariant,
|
||||
buttonVariants
|
||||
} from './button.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
type Props,
|
||||
type Events,
|
||||
type ButtonProps as Props,
|
||||
//
|
||||
Root as Button,
|
||||
type Props as ButtonProps,
|
||||
type Events as ButtonEvents,
|
||||
buttonVariants
|
||||
buttonVariants,
|
||||
type ButtonProps,
|
||||
type ButtonSize,
|
||||
type ButtonVariant
|
||||
};
|
||||
|
||||
@@ -2,20 +2,18 @@
|
||||
import { Calendar as CalendarPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = CalendarPrimitive.CellProps;
|
||||
|
||||
export let date: $$Props['date'];
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: CalendarPrimitive.CellProps = $props();
|
||||
</script>
|
||||
|
||||
<CalendarPrimitive.Cell
|
||||
{date}
|
||||
bind:ref
|
||||
class={cn(
|
||||
'[&:has([data-selected])]:bg-accent [&:has([data-selected][data-outside-month])]:bg-accent/50 relative h-9 w-9 p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([data-selected])]:rounded-md',
|
||||
'[&:has([data-selected])]:bg-accent [&:has([data-selected][data-outside-month])]:bg-accent/50 relative size-8 p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([data-selected])]:rounded-md',
|
||||
className
|
||||
)}
|
||||
{...$$restProps}
|
||||
>
|
||||
<slot />
|
||||
</CalendarPrimitive.Cell>
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -1,42 +1,30 @@
|
||||
<script lang="ts">
|
||||
import { Calendar as CalendarPrimitive } from 'bits-ui';
|
||||
import { buttonVariants } from '$lib/components/ui/button/index.js';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
import { Calendar as CalendarPrimitive } from 'bits-ui';
|
||||
|
||||
type $$Props = CalendarPrimitive.DayProps;
|
||||
type $$Events = CalendarPrimitive.DayEvents;
|
||||
|
||||
export let date: $$Props['date'];
|
||||
export let month: $$Props['month'];
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: CalendarPrimitive.DayProps = $props();
|
||||
</script>
|
||||
|
||||
<CalendarPrimitive.Day
|
||||
on:click
|
||||
{date}
|
||||
{month}
|
||||
bind:ref
|
||||
class={cn(
|
||||
buttonVariants({ variant: 'ghost' }),
|
||||
'h-9 w-9 p-0 font-normal ',
|
||||
'size-8 p-0 font-normal select-none',
|
||||
'[&[data-today]:not([data-selected])]:bg-accent [&[data-today]:not([data-selected])]:text-accent-foreground',
|
||||
// Selected
|
||||
'data-[selected]:bg-primary data-[selected]:text-primary-foreground data-[selected]:hover:bg-primary data-[selected]:hover:text-primary-foreground data-[selected]:focus:bg-primary data-[selected]:focus:text-primary-foreground data-[selected]:opacity-100',
|
||||
'data-selected:bg-primary data-selected:text-primary-foreground data-selected:hover:bg-primary data-selected:hover:text-primary-foreground data-selected:focus:bg-primary data-selected:focus:text-primary-foreground dark:data-selected:hover:bg-primary dark:data-selected:focus:bg-primary data-selected:opacity-100',
|
||||
// Disabled
|
||||
'data-[disabled]:text-muted-foreground data-[disabled]:opacity-50',
|
||||
'data-disabled:text-muted-foreground data-disabled:opacity-50',
|
||||
// Unavailable
|
||||
'data-[unavailable]:text-destructive-foreground data-[unavailable]:line-through',
|
||||
'data-unavailable:text-destructive-foreground data-unavailable:line-through',
|
||||
// Outside months
|
||||
'data-[outside-month]:text-muted-foreground [&[data-outside-month][data-selected]]:bg-accent/50 [&[data-outside-month][data-selected]]:text-muted-foreground data-[outside-month]:pointer-events-none data-[outside-month]:opacity-50 [&[data-outside-month][data-selected]]:opacity-30',
|
||||
className
|
||||
)}
|
||||
{...$$restProps}
|
||||
let:selected
|
||||
let:disabled
|
||||
let:unavailable
|
||||
let:builder
|
||||
>
|
||||
<slot {selected} {disabled} {unavailable} {builder}>
|
||||
{date.day}
|
||||
</slot>
|
||||
</CalendarPrimitive.Day>
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -2,12 +2,11 @@
|
||||
import { Calendar as CalendarPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = CalendarPrimitive.GridBodyProps;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: CalendarPrimitive.GridBodyProps = $props();
|
||||
</script>
|
||||
|
||||
<CalendarPrimitive.GridBody class={cn(className)} {...$$restProps}>
|
||||
<slot />
|
||||
</CalendarPrimitive.GridBody>
|
||||
<CalendarPrimitive.GridBody bind:ref class={cn(className)} {...restProps} />
|
||||
|
||||
@@ -2,12 +2,11 @@
|
||||
import { Calendar as CalendarPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = CalendarPrimitive.GridHeadProps;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: CalendarPrimitive.GridHeadProps = $props();
|
||||
</script>
|
||||
|
||||
<CalendarPrimitive.GridHead class={cn(className)} {...$$restProps}>
|
||||
<slot />
|
||||
</CalendarPrimitive.GridHead>
|
||||
<CalendarPrimitive.GridHead bind:ref class={cn(className)} {...restProps} />
|
||||
|
||||
@@ -2,12 +2,11 @@
|
||||
import { Calendar as CalendarPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = CalendarPrimitive.GridRowProps;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: CalendarPrimitive.GridRowProps = $props();
|
||||
</script>
|
||||
|
||||
<CalendarPrimitive.GridRow class={cn('flex', className)} {...$$restProps}>
|
||||
<slot />
|
||||
</CalendarPrimitive.GridRow>
|
||||
<CalendarPrimitive.GridRow bind:ref class={cn('flex', className)} {...restProps} />
|
||||
|
||||
@@ -2,12 +2,15 @@
|
||||
import { Calendar as CalendarPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = CalendarPrimitive.GridProps;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: CalendarPrimitive.GridProps = $props();
|
||||
</script>
|
||||
|
||||
<CalendarPrimitive.Grid class={cn('w-full border-collapse space-y-1', className)} {...$$restProps}>
|
||||
<slot />
|
||||
</CalendarPrimitive.Grid>
|
||||
<CalendarPrimitive.Grid
|
||||
bind:ref
|
||||
class={cn('w-full border-collapse space-y-1', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
import { Calendar as CalendarPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = CalendarPrimitive.HeadCellProps;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: CalendarPrimitive.HeadCellProps = $props();
|
||||
</script>
|
||||
|
||||
<CalendarPrimitive.HeadCell
|
||||
class={cn('text-muted-foreground w-9 rounded-md text-[0.8rem] font-normal', className)}
|
||||
{...$$restProps}
|
||||
>
|
||||
<slot />
|
||||
</CalendarPrimitive.HeadCell>
|
||||
bind:ref
|
||||
class={cn('text-muted-foreground w-8 rounded-md text-[0.8rem] font-normal', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
import { Calendar as CalendarPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = CalendarPrimitive.HeaderProps;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: CalendarPrimitive.HeaderProps = $props();
|
||||
</script>
|
||||
|
||||
<CalendarPrimitive.Header
|
||||
bind:ref
|
||||
class={cn('relative flex w-full items-center justify-between pt-1', className)}
|
||||
{...$$restProps}
|
||||
>
|
||||
<slot />
|
||||
</CalendarPrimitive.Header>
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -2,18 +2,11 @@
|
||||
import { Calendar as CalendarPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = CalendarPrimitive.HeadingProps;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: CalendarPrimitive.HeadingProps = $props();
|
||||
</script>
|
||||
|
||||
<CalendarPrimitive.Heading
|
||||
let:headingValue
|
||||
class={cn('text-sm font-medium', className)}
|
||||
{...$$restProps}
|
||||
>
|
||||
<slot {headingValue}>
|
||||
{headingValue}
|
||||
</slot>
|
||||
</CalendarPrimitive.Heading>
|
||||
<CalendarPrimitive.Heading bind:ref class={cn('text-sm font-medium', className)} {...restProps} />
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
<script lang="ts">
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
import { cn, type WithElementRef } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
bind:this={ref}
|
||||
class={cn('mt-4 flex flex-col space-y-4 sm:flex-row sm:space-y-0 sm:space-x-4', className)}
|
||||
{...$$restProps}
|
||||
{...restProps}
|
||||
>
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
<script lang="ts">
|
||||
import { Calendar as CalendarPrimitive } from 'bits-ui';
|
||||
import ChevronRight from 'lucide-svelte/icons/chevron-right';
|
||||
import ChevronRightIcon from '@lucide/svelte/icons/chevron-right';
|
||||
import { buttonVariants } from '$lib/components/ui/button/index.js';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = CalendarPrimitive.NextButtonProps;
|
||||
type $$Events = CalendarPrimitive.NextButtonEvents;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: CalendarPrimitive.PrevButtonProps = $props();
|
||||
</script>
|
||||
|
||||
{#snippet Fallback()}
|
||||
<ChevronRightIcon class="size-4" />
|
||||
{/snippet}
|
||||
|
||||
<CalendarPrimitive.NextButton
|
||||
on:click
|
||||
bind:ref
|
||||
class={cn(
|
||||
buttonVariants({ variant: 'outline' }),
|
||||
'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100',
|
||||
'size-7 bg-transparent p-0 opacity-50 hover:opacity-100',
|
||||
className
|
||||
)}
|
||||
{...$$restProps}
|
||||
let:builder
|
||||
>
|
||||
<slot {builder}>
|
||||
<ChevronRight class="h-4 w-4" />
|
||||
</slot>
|
||||
</CalendarPrimitive.NextButton>
|
||||
children={children || Fallback}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
<script lang="ts">
|
||||
import { Calendar as CalendarPrimitive } from 'bits-ui';
|
||||
import ChevronLeft from 'lucide-svelte/icons/chevron-left';
|
||||
import ChevronLeftIcon from '@lucide/svelte/icons/chevron-left';
|
||||
import { buttonVariants } from '$lib/components/ui/button/index.js';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = CalendarPrimitive.PrevButtonProps;
|
||||
type $$Events = CalendarPrimitive.PrevButtonEvents;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: CalendarPrimitive.PrevButtonProps = $props();
|
||||
</script>
|
||||
|
||||
{#snippet Fallback()}
|
||||
<ChevronLeftIcon class="size-4" />
|
||||
{/snippet}
|
||||
|
||||
<CalendarPrimitive.PrevButton
|
||||
on:click
|
||||
bind:ref
|
||||
class={cn(
|
||||
buttonVariants({ variant: 'outline' }),
|
||||
'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100',
|
||||
'size-7 bg-transparent p-0 opacity-50 hover:opacity-100',
|
||||
className
|
||||
)}
|
||||
{...$$restProps}
|
||||
let:builder
|
||||
>
|
||||
<slot {builder}>
|
||||
<ChevronLeft class="h-4 w-4" />
|
||||
</slot>
|
||||
</CalendarPrimitive.PrevButton>
|
||||
children={children || Fallback}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -1,137 +1,61 @@
|
||||
<script lang="ts">
|
||||
import * as Calendar from '$lib/components/ui/calendar/index.js';
|
||||
import * as Select from '$lib/components/ui/select/index.js';
|
||||
import { cn } from '$lib/utils/style';
|
||||
import { DateFormatter, getLocalTimeZone, today } from '@internationalized/date';
|
||||
import { Calendar as CalendarPrimitive } from 'bits-ui';
|
||||
import * as Calendar from './index.js';
|
||||
import { cn, type WithoutChildrenOrChild } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = CalendarPrimitive.Props;
|
||||
type $$Events = CalendarPrimitive.Events;
|
||||
|
||||
export let value: $$Props['value'] = undefined;
|
||||
export let placeholder: $$Props['placeholder'] = today(getLocalTimeZone());
|
||||
export let weekdayFormat: $$Props['weekdayFormat'] = 'short';
|
||||
|
||||
const monthOptions = [
|
||||
'January',
|
||||
'February',
|
||||
'March',
|
||||
'April',
|
||||
'May',
|
||||
'June',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'October',
|
||||
'November',
|
||||
'December'
|
||||
].map((month, i) => ({ value: i + 1, label: month }));
|
||||
|
||||
const monthFmt = new DateFormatter('en-US', {
|
||||
month: 'long'
|
||||
});
|
||||
|
||||
const yearOptions = Array.from({ length: 100 }, (_, i) => ({
|
||||
label: String(new Date().getFullYear() + i),
|
||||
value: new Date().getFullYear() + i
|
||||
}));
|
||||
|
||||
$: defaultYear = placeholder
|
||||
? {
|
||||
value: placeholder.year,
|
||||
label: String(placeholder.year)
|
||||
}
|
||||
: undefined;
|
||||
|
||||
$: defaultMonth = placeholder
|
||||
? {
|
||||
value: placeholder.month,
|
||||
label: monthFmt.format(placeholder.toDate(getLocalTimeZone()))
|
||||
}
|
||||
: undefined;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
value = $bindable(),
|
||||
placeholder = $bindable(),
|
||||
class: className,
|
||||
weekdayFormat = 'short',
|
||||
...restProps
|
||||
}: WithoutChildrenOrChild<CalendarPrimitive.RootProps> = $props();
|
||||
</script>
|
||||
|
||||
<!--
|
||||
Discriminated Unions + Destructing (required for bindable) do not
|
||||
get along, so we shut typescript up by casting `value` to `never`.
|
||||
-->
|
||||
<CalendarPrimitive.Root
|
||||
{weekdayFormat}
|
||||
class={cn('rounded-md border p-3', className)}
|
||||
{...$$restProps}
|
||||
on:keydown
|
||||
let:months
|
||||
let:weekdays
|
||||
bind:value
|
||||
bind:value={value as never}
|
||||
bind:ref
|
||||
bind:placeholder
|
||||
{weekdayFormat}
|
||||
class={cn('p-3', className)}
|
||||
{...restProps}
|
||||
>
|
||||
<Calendar.Header>
|
||||
<Calendar.Heading class="flex w-full items-center justify-between gap-2">
|
||||
<Select.Root
|
||||
selected={defaultMonth}
|
||||
items={monthOptions}
|
||||
onSelectedChange={(v) => {
|
||||
if (!v || !placeholder) return;
|
||||
if (v.value === placeholder?.month) return;
|
||||
placeholder = placeholder.set({ month: v.value });
|
||||
}}
|
||||
>
|
||||
<Select.Trigger aria-label="Select month" class="w-[60%]">
|
||||
<Select.Value placeholder="Select month" />
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-[200px] overflow-y-auto">
|
||||
{#each monthOptions as { value, label }}
|
||||
<Select.Item {value} {label}>
|
||||
{label}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
<Select.Root
|
||||
selected={defaultYear}
|
||||
items={yearOptions}
|
||||
onSelectedChange={(v) => {
|
||||
if (!v || !placeholder) return;
|
||||
if (v.value === placeholder?.year) return;
|
||||
placeholder = placeholder.set({ year: v.value });
|
||||
}}
|
||||
>
|
||||
<Select.Trigger aria-label="Select year" class="w-[40%]">
|
||||
<Select.Value placeholder="Select year" />
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-[200px] overflow-y-auto">
|
||||
{#each yearOptions as { value, label }}
|
||||
<Select.Item {value} {label}>
|
||||
{label}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</Calendar.Heading>
|
||||
</Calendar.Header>
|
||||
<Calendar.Months>
|
||||
{#each months as month}
|
||||
<Calendar.Grid>
|
||||
<Calendar.GridHead>
|
||||
<Calendar.GridRow class="flex">
|
||||
{#each weekdays as weekday}
|
||||
<Calendar.HeadCell>
|
||||
{weekday.slice(0, 2)}
|
||||
</Calendar.HeadCell>
|
||||
{/each}
|
||||
</Calendar.GridRow>
|
||||
</Calendar.GridHead>
|
||||
<Calendar.GridBody>
|
||||
{#each month.weeks as weekDates}
|
||||
<Calendar.GridRow class="mt-2 w-full">
|
||||
{#each weekDates as date}
|
||||
<Calendar.Cell {date}>
|
||||
<Calendar.Day {date} month={month.value} />
|
||||
</Calendar.Cell>
|
||||
{#snippet children({ months, weekdays })}
|
||||
<Calendar.Header>
|
||||
<Calendar.PrevButton />
|
||||
<Calendar.Heading />
|
||||
<Calendar.NextButton />
|
||||
</Calendar.Header>
|
||||
<Calendar.Months>
|
||||
{#each months as month (month)}
|
||||
<Calendar.Grid>
|
||||
<Calendar.GridHead>
|
||||
<Calendar.GridRow class="flex">
|
||||
{#each weekdays as weekday (weekday)}
|
||||
<Calendar.HeadCell>
|
||||
{weekday.slice(0, 2)}
|
||||
</Calendar.HeadCell>
|
||||
{/each}
|
||||
</Calendar.GridRow>
|
||||
{/each}
|
||||
</Calendar.GridBody>
|
||||
</Calendar.Grid>
|
||||
{/each}
|
||||
</Calendar.Months>
|
||||
</Calendar.GridHead>
|
||||
<Calendar.GridBody>
|
||||
{#each month.weeks as weekDates (weekDates)}
|
||||
<Calendar.GridRow class="mt-2 w-full">
|
||||
{#each weekDates as date (date)}
|
||||
<Calendar.Cell {date} month={month.value}>
|
||||
<Calendar.Day />
|
||||
</Calendar.Cell>
|
||||
{/each}
|
||||
</Calendar.GridRow>
|
||||
{/each}
|
||||
</Calendar.GridBody>
|
||||
</Calendar.Grid>
|
||||
{/each}
|
||||
</Calendar.Months>
|
||||
{/snippet}
|
||||
</CalendarPrimitive.Root>
|
||||
|
||||
20
frontend/src/lib/components/ui/card/card-action.svelte
Normal file
20
frontend/src/lib/components/ui/card/card-action.svelte
Normal file
@@ -0,0 +1,20 @@
|
||||
<script lang="ts">
|
||||
import { cn, type WithElementRef } from '$lib/utils/style.js';
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="card-action"
|
||||
class={cn('col-start-2 row-span-2 row-start-1 self-start justify-self-end', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
@@ -1,13 +1,15 @@
|
||||
<script lang="ts">
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import { cn, type WithElementRef } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<div class={cn('bg-muted/20 p-6 pt-5 peer-[.card-header]:border-t', className)} {...$$restProps}>
|
||||
<slot />
|
||||
<div bind:this={ref} data-slot="card-content" class={cn('px-6', className)} {...restProps}>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
<script lang="ts">
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
import { cn, type WithElementRef } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = HTMLAttributes<HTMLParagraphElement>;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLParagraphElement>> = $props();
|
||||
</script>
|
||||
|
||||
<p class={cn('text-muted-foreground mt-1 text-sm', className)} {...$$restProps}>
|
||||
<slot />
|
||||
<p
|
||||
bind:this={ref}
|
||||
data-slot="card-description"
|
||||
class={cn('text-muted-foreground text-sm', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</p>
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
<script lang="ts">
|
||||
import { cn, type WithElementRef } from '$lib/utils/style.js';
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<div class={cn('flex items-center p-6 pt-0', className)} {...$$restProps}>
|
||||
<slot />
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="card-footer"
|
||||
class={cn('flex items-center px-6 [.border-t]:pt-6', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -1,13 +1,23 @@
|
||||
<script lang="ts">
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
import { cn, type WithElementRef } from '$lib/utils/style.js';
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
|
||||
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<div class={cn('card-header peer flex flex-col space-y-1.5 p-6', className)} {...$$restProps}>
|
||||
<slot />
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="card-header"
|
||||
class={cn(
|
||||
'@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
<script lang="ts">
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import type { HeadingLevel } from './index.js';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
import { cn, type WithElementRef } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = HTMLAttributes<HTMLHeadingElement> & {
|
||||
tag?: HeadingLevel;
|
||||
};
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export let tag: $$Props['tag'] = 'h3';
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<svelte:element
|
||||
this={tag}
|
||||
class={cn('flex items-center gap-2 text-xl leading-none font-semibold tracking-tight', className)}
|
||||
{...$$restProps}
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="card-title"
|
||||
class={cn('flex flex-row items-center gap-2 text-xl leading-none font-semibold', className)}
|
||||
{...restProps}
|
||||
>
|
||||
<slot />
|
||||
</svelte:element>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
<script lang="ts">
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
import { cn, type WithElementRef } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
class={cn('bg-card text-card-foreground overflow-hidden rounded-lg border shadow-sm', className)}
|
||||
{...$$restProps}
|
||||
bind:this={ref}
|
||||
data-slot="card"
|
||||
class={cn(
|
||||
'bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -4,6 +4,7 @@ import Description from './card-description.svelte';
|
||||
import Footer from './card-footer.svelte';
|
||||
import Header from './card-header.svelte';
|
||||
import Title from './card-title.svelte';
|
||||
import Action from './card-action.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
@@ -12,13 +13,13 @@ export {
|
||||
Footer,
|
||||
Header,
|
||||
Title,
|
||||
Action,
|
||||
//
|
||||
Root as Card,
|
||||
Content as CardContent,
|
||||
Description as CardDescription,
|
||||
Footer as CardFooter,
|
||||
Header as CardHeader,
|
||||
Title as CardTitle
|
||||
Title as CardTitle,
|
||||
Action as CardAction
|
||||
};
|
||||
|
||||
export type HeadingLevel = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
|
||||
|
||||
@@ -1,35 +1,39 @@
|
||||
<script lang="ts">
|
||||
import { Checkbox as CheckboxPrimitive } from 'bits-ui';
|
||||
import Check from 'lucide-svelte/icons/check';
|
||||
import Minus from 'lucide-svelte/icons/minus';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
import CheckIcon from '@lucide/svelte/icons/check';
|
||||
import MinusIcon from '@lucide/svelte/icons/minus';
|
||||
import { cn, type WithoutChildrenOrChild } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = CheckboxPrimitive.Props;
|
||||
type $$Events = CheckboxPrimitive.Events;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export let checked: $$Props['checked'] = false;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
checked = $bindable(false),
|
||||
indeterminate = $bindable(false),
|
||||
class: className,
|
||||
...restProps
|
||||
}: WithoutChildrenOrChild<CheckboxPrimitive.RootProps> = $props();
|
||||
</script>
|
||||
|
||||
<CheckboxPrimitive.Root
|
||||
bind:ref
|
||||
data-slot="checkbox"
|
||||
class={cn(
|
||||
'peer border-primary ring-offset-background focus-visible:ring-ring data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground box-content h-4 w-4 shrink-0 rounded-sm border focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-50',
|
||||
'border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive peer size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',
|
||||
className
|
||||
)}
|
||||
bind:checked
|
||||
{...$$restProps}
|
||||
on:click
|
||||
bind:indeterminate
|
||||
{...restProps}
|
||||
>
|
||||
<CheckboxPrimitive.Indicator
|
||||
class={cn('flex h-4 w-4 items-center justify-center text-current')}
|
||||
let:isChecked
|
||||
let:isIndeterminate
|
||||
>
|
||||
{#if isChecked}
|
||||
<Check class="h-3.5 w-3.5" />
|
||||
{:else if isIndeterminate}
|
||||
<Minus class="h-3.5 w-3.5" />
|
||||
{/if}
|
||||
</CheckboxPrimitive.Indicator>
|
||||
{#snippet children({ checked, indeterminate })}
|
||||
<div
|
||||
data-slot="checkbox-indicator"
|
||||
class="flex items-center justify-center text-current transition-none"
|
||||
>
|
||||
{#if checked}
|
||||
<CheckIcon class="size-3.5" />
|
||||
{:else if indeterminate}
|
||||
<MinusIcon class="size-3.5" />
|
||||
{/if}
|
||||
</div>
|
||||
{/snippet}
|
||||
</CheckboxPrimitive.Root>
|
||||
|
||||
@@ -1,23 +1,40 @@
|
||||
<script lang="ts">
|
||||
import type { Dialog as DialogPrimitive } from 'bits-ui';
|
||||
import type { Command as CommandPrimitive } from 'cmdk-sv';
|
||||
import type { Command as CommandPrimitive, Dialog as DialogPrimitive } from 'bits-ui';
|
||||
import type { Snippet } from 'svelte';
|
||||
import Command from './command.svelte';
|
||||
import * as Dialog from '$lib/components/ui/dialog/index.js';
|
||||
import type { WithoutChildrenOrChild } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = DialogPrimitive.Props & CommandPrimitive.CommandProps;
|
||||
|
||||
export let open: $$Props['open'] = false;
|
||||
export let value: $$Props['value'] = undefined;
|
||||
let {
|
||||
open = $bindable(false),
|
||||
ref = $bindable(null),
|
||||
value = $bindable(''),
|
||||
title = 'Command Palette',
|
||||
description = 'Search for a command to run',
|
||||
portalProps,
|
||||
children,
|
||||
...restProps
|
||||
}: WithoutChildrenOrChild<DialogPrimitive.RootProps> &
|
||||
WithoutChildrenOrChild<CommandPrimitive.RootProps> & {
|
||||
portalProps?: DialogPrimitive.PortalProps;
|
||||
children: Snippet;
|
||||
title?: string;
|
||||
description?: string;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<Dialog.Root bind:open {...$$restProps}>
|
||||
<Dialog.Content class="overflow-hidden p-0 shadow-lg">
|
||||
<Dialog.Root bind:open {...restProps}>
|
||||
<Dialog.Header class="sr-only">
|
||||
<Dialog.Title>{title}</Dialog.Title>
|
||||
<Dialog.Description>{description}</Dialog.Description>
|
||||
</Dialog.Header>
|
||||
<Dialog.Content class="overflow-hidden p-0" {portalProps}>
|
||||
<Command
|
||||
class="[&_[data-cmdk-group-heading]]:text-muted-foreground [&_[data-cmdk-group-heading]]:px-2 [&_[data-cmdk-group-heading]]:font-medium [&_[data-cmdk-group]]:px-2 [&_[data-cmdk-group]:not([hidden])_~[data-cmdk-group]]:pt-0 [&_[data-cmdk-input-wrapper]_svg]:h-5 [&_[data-cmdk-input-wrapper]_svg]:w-5 [&_[data-cmdk-input]]:h-12 [&_[data-cmdk-item]]:px-2 [&_[data-cmdk-item]]:py-3 [&_[data-cmdk-item]_svg]:h-5 [&_[data-cmdk-item]_svg]:w-5"
|
||||
{...$$restProps}
|
||||
class="**:data-[slot=command-input-wrapper]:h-12 [&_[data-command-group]]:px-2 [&_[data-command-group]:not([hidden])_~[data-command-group]]:pt-0 [&_[data-command-input-wrapper]_svg]:h-5 [&_[data-command-input-wrapper]_svg]:w-5 [&_[data-command-input]]:h-12 [&_[data-command-item]]:px-2 [&_[data-command-item]]:py-3 [&_[data-command-item]_svg]:h-5 [&_[data-command-item]_svg]:w-5"
|
||||
{...restProps}
|
||||
bind:value
|
||||
>
|
||||
<slot />
|
||||
</Command>
|
||||
bind:ref
|
||||
{children}
|
||||
/>
|
||||
</Dialog.Content>
|
||||
</Dialog.Root>
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
<script lang="ts">
|
||||
import { Command as CommandPrimitive } from 'cmdk-sv';
|
||||
import { Command as CommandPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
import type { ClassValue } from 'svelte/elements';
|
||||
|
||||
type $$Props = CommandPrimitive.EmptyProps;
|
||||
let className: ClassValue | undefined | null = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: CommandPrimitive.EmptyProps = $props();
|
||||
</script>
|
||||
|
||||
<CommandPrimitive.Empty class={cn('py-6 text-center text-sm', className)} {...$$restProps}>
|
||||
<slot />
|
||||
</CommandPrimitive.Empty>
|
||||
<CommandPrimitive.Empty
|
||||
bind:ref
|
||||
data-slot="command-empty"
|
||||
class={cn('py-6 text-center text-sm', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -1,19 +1,30 @@
|
||||
<script lang="ts">
|
||||
import { Command as CommandPrimitive } from 'cmdk-sv';
|
||||
import { Command as CommandPrimitive, useId } from 'bits-ui';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
import type { ClassValue } from 'svelte/elements';
|
||||
type $$Props = CommandPrimitive.GroupProps;
|
||||
|
||||
let className: ClassValue | undefined | null = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
heading,
|
||||
value,
|
||||
...restProps
|
||||
}: CommandPrimitive.GroupProps & {
|
||||
heading?: string;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<CommandPrimitive.Group
|
||||
class={cn(
|
||||
'text-foreground [&_[data-cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 [&_[data-cmdk-group-heading]]:px-2 [&_[data-cmdk-group-heading]]:py-1.5 [&_[data-cmdk-group-heading]]:text-xs [&_[data-cmdk-group-heading]]:font-medium',
|
||||
className
|
||||
)}
|
||||
{...$$restProps}
|
||||
bind:ref
|
||||
data-slot="command-group"
|
||||
class={cn('text-foreground overflow-hidden p-1', className)}
|
||||
value={value ?? heading ?? `----${useId()}`}
|
||||
{...restProps}
|
||||
>
|
||||
<slot />
|
||||
{#if heading}
|
||||
<CommandPrimitive.GroupHeading class="text-muted-foreground px-2 py-1.5 text-xs font-medium">
|
||||
{heading}
|
||||
</CommandPrimitive.GroupHeading>
|
||||
{/if}
|
||||
<CommandPrimitive.GroupItems {children} />
|
||||
</CommandPrimitive.Group>
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
<script lang="ts">
|
||||
import { Command as CommandPrimitive } from 'bits-ui';
|
||||
import SearchIcon from '@lucide/svelte/icons/search';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
import { Command as CommandPrimitive } from 'cmdk-sv';
|
||||
import Search from 'lucide-svelte/icons/search';
|
||||
import type { ClassValue } from 'svelte/elements';
|
||||
|
||||
type $$Props = CommandPrimitive.InputProps;
|
||||
|
||||
let className: ClassValue | undefined | null = undefined;
|
||||
export { className as class };
|
||||
export let value: string = '';
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
value = $bindable(''),
|
||||
...restProps
|
||||
}: CommandPrimitive.InputProps = $props();
|
||||
</script>
|
||||
|
||||
<div class="flex items-center border-b px-2" data-cmdk-input-wrapper="">
|
||||
<Search class="mr-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
<div class="flex h-9 items-center gap-2 border-b px-3" data-slot="command-input-wrapper">
|
||||
<SearchIcon class="size-4 shrink-0 opacity-50" />
|
||||
<CommandPrimitive.Input
|
||||
data-slot="command-input"
|
||||
class={cn(
|
||||
'placeholder:text-muted-foreground flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none disabled:cursor-not-allowed disabled:opacity-50',
|
||||
'placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50',
|
||||
className
|
||||
)}
|
||||
{...$$restProps}
|
||||
bind:ref
|
||||
{...restProps}
|
||||
bind:value
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,25 +1,20 @@
|
||||
<script lang="ts">
|
||||
import { Command as CommandPrimitive } from 'cmdk-sv';
|
||||
import { Command as CommandPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
import type { ClassValue } from 'svelte/elements';
|
||||
|
||||
type $$Props = CommandPrimitive.ItemProps;
|
||||
|
||||
export let asChild = false;
|
||||
|
||||
let className: ClassValue | undefined | null = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: CommandPrimitive.ItemProps = $props();
|
||||
</script>
|
||||
|
||||
<CommandPrimitive.Item
|
||||
{asChild}
|
||||
bind:ref
|
||||
data-slot="command-item"
|
||||
class={cn(
|
||||
'aria-selected:bg-accent aria-selected:text-accent-foreground relative flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
"aria-selected:bg-accent aria-selected:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...$$restProps}
|
||||
let:action
|
||||
let:attrs
|
||||
>
|
||||
<slot {action} {attrs} />
|
||||
</CommandPrimitive.Item>
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<script lang="ts">
|
||||
import { Command as CommandPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: CommandPrimitive.LinkItemProps = $props();
|
||||
</script>
|
||||
|
||||
<CommandPrimitive.LinkItem
|
||||
bind:ref
|
||||
data-slot="command-item"
|
||||
class={cn(
|
||||
"aria-selected:bg-accent aria-selected:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
/>
|
||||
@@ -1,16 +1,17 @@
|
||||
<script lang="ts">
|
||||
import { Command as CommandPrimitive } from 'cmdk-sv';
|
||||
import { Command as CommandPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
import type { ClassValue } from 'svelte/elements';
|
||||
|
||||
type $$Props = CommandPrimitive.ListProps;
|
||||
let className: ClassValue | undefined | null = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: CommandPrimitive.ListProps = $props();
|
||||
</script>
|
||||
|
||||
<CommandPrimitive.List
|
||||
class={cn('max-h-[300px] overflow-x-hidden overflow-y-auto', className)}
|
||||
{...$$restProps}
|
||||
>
|
||||
<slot />
|
||||
</CommandPrimitive.List>
|
||||
bind:ref
|
||||
data-slot="command-list"
|
||||
class={cn('max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
<script lang="ts">
|
||||
import { Command as CommandPrimitive } from 'cmdk-sv';
|
||||
import { Command as CommandPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
import type { ClassValue } from 'svelte/elements';
|
||||
|
||||
type $$Props = CommandPrimitive.SeparatorProps;
|
||||
let className: ClassValue | undefined | null = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: CommandPrimitive.SeparatorProps = $props();
|
||||
</script>
|
||||
|
||||
<CommandPrimitive.Separator class={cn('bg-border -mx-1 h-px', className)} {...$$restProps} />
|
||||
<CommandPrimitive.Separator
|
||||
bind:ref
|
||||
data-slot="command-separator"
|
||||
class={cn('bg-border -mx-1 h-px', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
<script lang="ts">
|
||||
import type { ClassValue, HTMLAttributes } from 'svelte/elements';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
import { cn, type WithElementRef } from '$lib/utils/style.js';
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
|
||||
type $$Props = HTMLAttributes<HTMLSpanElement>;
|
||||
|
||||
let className: ClassValue | undefined | null = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLSpanElement>> = $props();
|
||||
</script>
|
||||
|
||||
<span
|
||||
bind:this={ref}
|
||||
data-slot="command-shortcut"
|
||||
class={cn('text-muted-foreground ml-auto text-xs tracking-widest', className)}
|
||||
{...$$restProps}
|
||||
{...restProps}
|
||||
>
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</span>
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
<script lang="ts">
|
||||
import { Command as CommandPrimitive } from 'cmdk-sv';
|
||||
import { Command as CommandPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
import type { ClassValue } from 'svelte/elements';
|
||||
|
||||
type $$Props = CommandPrimitive.CommandProps;
|
||||
|
||||
export let value: $$Props['value'] = undefined;
|
||||
|
||||
let className: ClassValue | undefined | null = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
value = $bindable(''),
|
||||
class: className,
|
||||
...restProps
|
||||
}: CommandPrimitive.RootProps = $props();
|
||||
</script>
|
||||
|
||||
<CommandPrimitive.Root
|
||||
bind:value
|
||||
bind:ref
|
||||
data-slot="command"
|
||||
class={cn(
|
||||
'bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md',
|
||||
className
|
||||
)}
|
||||
bind:value
|
||||
{...$$restProps}
|
||||
>
|
||||
<slot />
|
||||
</CommandPrimitive.Root>
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Command as CommandPrimitive } from 'cmdk-sv';
|
||||
import { Command as CommandPrimitive } from 'bits-ui';
|
||||
|
||||
import Root from './command.svelte';
|
||||
import Dialog from './command-dialog.svelte';
|
||||
@@ -9,6 +9,7 @@ import Input from './command-input.svelte';
|
||||
import List from './command-list.svelte';
|
||||
import Separator from './command-separator.svelte';
|
||||
import Shortcut from './command-shortcut.svelte';
|
||||
import LinkItem from './command-link-item.svelte';
|
||||
|
||||
const Loading = CommandPrimitive.Loading;
|
||||
|
||||
@@ -18,6 +19,7 @@ export {
|
||||
Empty,
|
||||
Group,
|
||||
Item,
|
||||
LinkItem,
|
||||
Input,
|
||||
List,
|
||||
Separator,
|
||||
@@ -29,6 +31,7 @@ export {
|
||||
Empty as CommandEmpty,
|
||||
Group as CommandGroup,
|
||||
Item as CommandItem,
|
||||
LinkItem as CommandLinkItem,
|
||||
Input as CommandInput,
|
||||
List as CommandList,
|
||||
Separator as CommandSeparator,
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { Dialog as DialogPrimitive } from 'bits-ui';
|
||||
|
||||
let { ref = $bindable(null), ...restProps }: DialogPrimitive.CloseProps = $props();
|
||||
</script>
|
||||
|
||||
<DialogPrimitive.Close bind:ref data-slot="dialog-close" {...restProps} />
|
||||
@@ -1,41 +1,39 @@
|
||||
<script lang="ts">
|
||||
import { Dialog as DialogPrimitive } from 'bits-ui';
|
||||
import X from 'lucide-svelte/icons/x';
|
||||
import XIcon from '@lucide/svelte/icons/x';
|
||||
import type { Snippet } from 'svelte';
|
||||
import * as Dialog from './index.js';
|
||||
import { cn, flyAndScale } from '$lib/utils/style.js';
|
||||
import { cn, type WithoutChildrenOrChild } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = DialogPrimitive.ContentProps & {
|
||||
closeButton?: boolean;
|
||||
};
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export let transition: $$Props['transition'] = flyAndScale;
|
||||
export let closeButton: $$Props['closeButton'] = true;
|
||||
export let transitionConfig: $$Props['transitionConfig'] = {
|
||||
duration: 200
|
||||
};
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
portalProps,
|
||||
children,
|
||||
...restProps
|
||||
}: WithoutChildrenOrChild<DialogPrimitive.ContentProps> & {
|
||||
portalProps?: DialogPrimitive.PortalProps;
|
||||
children: Snippet;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<Dialog.Portal>
|
||||
<Dialog.Portal {...portalProps}>
|
||||
<Dialog.Overlay />
|
||||
<DialogPrimitive.Content
|
||||
{transition}
|
||||
{transitionConfig}
|
||||
bind:ref
|
||||
data-slot="dialog-content"
|
||||
class={cn(
|
||||
'bg-background fixed top-[50%] left-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border p-6 shadow-lg sm:rounded-lg md:w-full',
|
||||
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg',
|
||||
className
|
||||
)}
|
||||
{...$$restProps}
|
||||
{...restProps}
|
||||
>
|
||||
<slot />
|
||||
{#if closeButton}
|
||||
<DialogPrimitive.Close
|
||||
class="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-none disabled:pointer-events-none"
|
||||
>
|
||||
<X class="h-4 w-4" />
|
||||
<span class="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
{/if}
|
||||
{@render children?.()}
|
||||
<DialogPrimitive.Close
|
||||
class="ring-offset-background focus:ring-ring absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
|
||||
>
|
||||
<XIcon />
|
||||
<span class="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
</DialogPrimitive.Content>
|
||||
</Dialog.Portal>
|
||||
|
||||
@@ -2,15 +2,16 @@
|
||||
import { Dialog as DialogPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = DialogPrimitive.DescriptionProps;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: DialogPrimitive.DescriptionProps = $props();
|
||||
</script>
|
||||
|
||||
<DialogPrimitive.Description
|
||||
bind:ref
|
||||
data-slot="dialog-description"
|
||||
class={cn('text-muted-foreground text-sm', className)}
|
||||
{...$$restProps}
|
||||
>
|
||||
<slot />
|
||||
</DialogPrimitive.Description>
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
<script lang="ts">
|
||||
import { cn, type WithElementRef } from '$lib/utils/style.js';
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
class={cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', className)}
|
||||
{...$$restProps}
|
||||
bind:this={ref}
|
||||
data-slot="dialog-footer"
|
||||
class={cn('flex flex-col-reverse gap-2 sm:flex-row sm:justify-end', className)}
|
||||
{...restProps}
|
||||
>
|
||||
<slot />
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
<script lang="ts">
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
import { cn, type WithElementRef } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = HTMLAttributes<HTMLDivElement>;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<div class={cn('flex flex-col space-y-1.5 text-center sm:text-left', className)} {...$$restProps}>
|
||||
<slot />
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="dialog-header"
|
||||
class={cn('flex flex-col gap-2 text-center sm:text-left', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
<script lang="ts">
|
||||
import { Dialog as DialogPrimitive } from 'bits-ui';
|
||||
import { fade } from 'svelte/transition';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = DialogPrimitive.OverlayProps;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export let transition: $$Props['transition'] = fade;
|
||||
export let transitionConfig: $$Props['transitionConfig'] = {
|
||||
duration: 150
|
||||
};
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: DialogPrimitive.OverlayProps = $props();
|
||||
</script>
|
||||
|
||||
<DialogPrimitive.Overlay
|
||||
{transition}
|
||||
{transitionConfig}
|
||||
class={cn('bg-background/80 fixed inset-0 z-50 backdrop-blur-sm', className)}
|
||||
{...$$restProps}
|
||||
bind:ref
|
||||
data-slot="dialog-overlay"
|
||||
class={cn(
|
||||
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
<script lang="ts">
|
||||
import { Dialog as DialogPrimitive } from 'bits-ui';
|
||||
interface Props {
|
||||
children?: import('svelte').Snippet;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
let { children, ...rest }: Props = $props();
|
||||
type $$Props = DialogPrimitive.PortalProps;
|
||||
</script>
|
||||
|
||||
<DialogPrimitive.Portal {...$$restProps}>
|
||||
<slot />
|
||||
<DialogPrimitive.Portal {...rest}>
|
||||
{@render children?.()}
|
||||
</DialogPrimitive.Portal>
|
||||
|
||||
@@ -2,15 +2,16 @@
|
||||
import { Dialog as DialogPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = DialogPrimitive.TitleProps;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: DialogPrimitive.TitleProps = $props();
|
||||
</script>
|
||||
|
||||
<DialogPrimitive.Title
|
||||
class={cn('text-lg leading-none font-semibold tracking-tight', className)}
|
||||
{...$$restProps}
|
||||
>
|
||||
<slot />
|
||||
</DialogPrimitive.Title>
|
||||
bind:ref
|
||||
data-slot="dialog-title"
|
||||
class={cn('text-lg leading-none font-semibold', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { Dialog as DialogPrimitive } from 'bits-ui';
|
||||
|
||||
let { ref = $bindable(null), ...restProps }: DialogPrimitive.TriggerProps = $props();
|
||||
</script>
|
||||
|
||||
<DialogPrimitive.Trigger bind:ref data-slot="dialog-trigger" {...restProps} />
|
||||
@@ -1,16 +1,16 @@
|
||||
import { Dialog as DialogPrimitive } from 'bits-ui';
|
||||
|
||||
import Title from './dialog-title.svelte';
|
||||
import Portal from './dialog-portal.svelte';
|
||||
import Footer from './dialog-footer.svelte';
|
||||
import Header from './dialog-header.svelte';
|
||||
import Overlay from './dialog-overlay.svelte';
|
||||
import Content from './dialog-content.svelte';
|
||||
import Description from './dialog-description.svelte';
|
||||
import Trigger from './dialog-trigger.svelte';
|
||||
import Close from './dialog-close.svelte';
|
||||
|
||||
const Root = DialogPrimitive.Root;
|
||||
const Trigger = DialogPrimitive.Trigger;
|
||||
const Close = DialogPrimitive.Close;
|
||||
const Portal = DialogPrimitive.Portal;
|
||||
|
||||
export {
|
||||
Root,
|
||||
|
||||
@@ -1,35 +1,41 @@
|
||||
<script lang="ts">
|
||||
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
|
||||
import Check from 'lucide-svelte/icons/check';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
import CheckIcon from '@lucide/svelte/icons/check';
|
||||
import MinusIcon from '@lucide/svelte/icons/minus';
|
||||
import { cn, type WithoutChildrenOrChild } from '$lib/utils/style.js';
|
||||
import type { Snippet } from 'svelte';
|
||||
|
||||
type $$Props = DropdownMenuPrimitive.CheckboxItemProps;
|
||||
type $$Events = DropdownMenuPrimitive.CheckboxItemEvents;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export let checked: $$Props['checked'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
checked = $bindable(false),
|
||||
indeterminate = $bindable(false),
|
||||
class: className,
|
||||
children: childrenProp,
|
||||
...restProps
|
||||
}: WithoutChildrenOrChild<DropdownMenuPrimitive.CheckboxItemProps> & {
|
||||
children?: Snippet;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<DropdownMenuPrimitive.CheckboxItem
|
||||
bind:ref
|
||||
bind:checked
|
||||
bind:indeterminate
|
||||
data-slot="dropdown-menu-checkbox-item"
|
||||
class={cn(
|
||||
'data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:keydown
|
||||
on:focusin
|
||||
on:focusout
|
||||
on:pointerdown
|
||||
on:pointerleave
|
||||
on:pointermove
|
||||
{...restProps}
|
||||
>
|
||||
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.CheckboxIndicator>
|
||||
<Check class="h-4 w-4" />
|
||||
</DropdownMenuPrimitive.CheckboxIndicator>
|
||||
</span>
|
||||
<slot />
|
||||
{#snippet children({ checked, indeterminate })}
|
||||
<span class="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
||||
{#if indeterminate}
|
||||
<MinusIcon class="size-4" />
|
||||
{:else}
|
||||
<CheckIcon class={cn('size-4', !checked && 'text-transparent')} />
|
||||
{/if}
|
||||
</span>
|
||||
{@render childrenProp?.()}
|
||||
{/snippet}
|
||||
</DropdownMenuPrimitive.CheckboxItem>
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
<script lang="ts">
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
|
||||
import { cn, flyAndScale } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = DropdownMenuPrimitive.ContentProps;
|
||||
type $$Events = DropdownMenuPrimitive.ContentEvents;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export let sideOffset: $$Props['sideOffset'] = 4;
|
||||
export let transition: $$Props['transition'] = flyAndScale;
|
||||
export let transitionConfig: $$Props['transitionConfig'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
sideOffset = 4,
|
||||
portalProps,
|
||||
class: className,
|
||||
...restProps
|
||||
}: DropdownMenuPrimitive.ContentProps & {
|
||||
portalProps?: DropdownMenuPrimitive.PortalProps;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<DropdownMenuPrimitive.Content
|
||||
{transition}
|
||||
{transitionConfig}
|
||||
{sideOffset}
|
||||
class={cn(
|
||||
'bg-popover text-popover-foreground z-50 min-w-[8rem] rounded-md border p-1 shadow-md focus:outline-none',
|
||||
className
|
||||
)}
|
||||
{...$$restProps}
|
||||
on:keydown
|
||||
>
|
||||
<slot />
|
||||
</DropdownMenuPrimitive.Content>
|
||||
<DropdownMenuPrimitive.Portal {...portalProps}>
|
||||
<DropdownMenuPrimitive.Content
|
||||
bind:ref
|
||||
data-slot="dropdown-menu-content"
|
||||
{sideOffset}
|
||||
class={cn(
|
||||
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[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 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
/>
|
||||
</DropdownMenuPrimitive.Portal>
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<script lang="ts">
|
||||
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
import type { ComponentProps } from 'svelte';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
inset,
|
||||
...restProps
|
||||
}: ComponentProps<typeof DropdownMenuPrimitive.GroupHeading> & {
|
||||
inset?: boolean;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<DropdownMenuPrimitive.GroupHeading
|
||||
bind:ref
|
||||
data-slot="dropdown-menu-group-heading"
|
||||
data-inset={inset}
|
||||
class={cn('px-2 py-1.5 text-sm font-semibold data-[inset]:pl-8', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
@@ -0,0 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
|
||||
|
||||
let { ref = $bindable(null), ...restProps }: DropdownMenuPrimitive.GroupProps = $props();
|
||||
</script>
|
||||
|
||||
<DropdownMenuPrimitive.Group bind:ref data-slot="dropdown-menu-group" {...restProps} />
|
||||
@@ -1,31 +1,27 @@
|
||||
<script lang="ts">
|
||||
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
|
||||
|
||||
type $$Props = DropdownMenuPrimitive.ItemProps & {
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
inset,
|
||||
variant = 'default',
|
||||
...restProps
|
||||
}: DropdownMenuPrimitive.ItemProps & {
|
||||
inset?: boolean;
|
||||
};
|
||||
type $$Events = DropdownMenuPrimitive.ItemEvents;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export let inset: $$Props['inset'] = undefined;
|
||||
export { className as class };
|
||||
variant?: 'default' | 'destructive';
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<DropdownMenuPrimitive.Item
|
||||
bind:ref
|
||||
data-slot="dropdown-menu-item"
|
||||
data-inset={inset}
|
||||
data-variant={variant}
|
||||
class={cn(
|
||||
'data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex items-center rounded-sm px-2 py-1.5 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
inset && 'pl-8',
|
||||
"data-highlighted:bg-accent data-highlighted:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:data-highlighted:bg-destructive/10 dark:data-[variant=destructive]:data-highlighted:bg-destructive/20 data-[variant=destructive]:data-highlighted:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:keydown
|
||||
on:focusin
|
||||
on:focusout
|
||||
on:pointerdown
|
||||
on:pointerleave
|
||||
on:pointermove
|
||||
>
|
||||
<slot />
|
||||
</DropdownMenuPrimitive.Item>
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
<script lang="ts">
|
||||
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
import { cn, type WithElementRef } from '$lib/utils/style.js';
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
|
||||
type $$Props = DropdownMenuPrimitive.LabelProps & {
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
inset,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
|
||||
inset?: boolean;
|
||||
};
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export let inset: $$Props['inset'] = undefined;
|
||||
export { className as class };
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<DropdownMenuPrimitive.Label
|
||||
class={cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', className)}
|
||||
{...$$restProps}
|
||||
<div
|
||||
bind:this={ref}
|
||||
data-slot="dropdown-menu-label"
|
||||
data-inset={inset}
|
||||
class={cn('px-2 py-1.5 text-sm font-semibold data-[inset]:pl-8', className)}
|
||||
{...restProps}
|
||||
>
|
||||
<slot />
|
||||
</DropdownMenuPrimitive.Label>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
<script lang="ts">
|
||||
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
|
||||
|
||||
type $$Props = DropdownMenuPrimitive.RadioGroupProps;
|
||||
|
||||
export let value: $$Props['value'] = undefined;
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
value = $bindable(),
|
||||
...restProps
|
||||
}: DropdownMenuPrimitive.RadioGroupProps = $props();
|
||||
</script>
|
||||
|
||||
<DropdownMenuPrimitive.RadioGroup {...$$restProps} bind:value>
|
||||
<slot />
|
||||
</DropdownMenuPrimitive.RadioGroup>
|
||||
<DropdownMenuPrimitive.RadioGroup
|
||||
bind:ref
|
||||
bind:value
|
||||
data-slot="dropdown-menu-radio-group"
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -1,35 +1,31 @@
|
||||
<script lang="ts">
|
||||
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
|
||||
import Circle from 'lucide-svelte/icons/circle';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
import CircleIcon from '@lucide/svelte/icons/circle';
|
||||
import { cn, type WithoutChild } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = DropdownMenuPrimitive.RadioItemProps;
|
||||
type $$Events = DropdownMenuPrimitive.RadioItemEvents;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export let value: $$Props['value'];
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children: childrenProp,
|
||||
...restProps
|
||||
}: WithoutChild<DropdownMenuPrimitive.RadioItemProps> = $props();
|
||||
</script>
|
||||
|
||||
<DropdownMenuPrimitive.RadioItem
|
||||
bind:ref
|
||||
data-slot="dropdown-menu-radio-item"
|
||||
class={cn(
|
||||
'data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{value}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:keydown
|
||||
on:focusin
|
||||
on:focusout
|
||||
on:pointerdown
|
||||
on:pointerleave
|
||||
on:pointermove
|
||||
{...restProps}
|
||||
>
|
||||
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.RadioIndicator>
|
||||
<Circle class="h-2 w-2 fill-current" />
|
||||
</DropdownMenuPrimitive.RadioIndicator>
|
||||
</span>
|
||||
<slot />
|
||||
{#snippet children({ checked })}
|
||||
<span class="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
||||
{#if checked}
|
||||
<CircleIcon class="size-2 fill-current" />
|
||||
{/if}
|
||||
</span>
|
||||
{@render childrenProp?.({ checked })}
|
||||
{/snippet}
|
||||
</DropdownMenuPrimitive.RadioItem>
|
||||
|
||||
@@ -2,13 +2,16 @@
|
||||
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = DropdownMenuPrimitive.SeparatorProps;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: DropdownMenuPrimitive.SeparatorProps = $props();
|
||||
</script>
|
||||
|
||||
<DropdownMenuPrimitive.Separator
|
||||
class={cn('bg-muted -mx-1 my-1 h-px', className)}
|
||||
{...$$restProps}
|
||||
bind:ref
|
||||
data-slot="dropdown-menu-separator"
|
||||
class={cn('bg-border -mx-1 my-1 h-px', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
<script lang="ts">
|
||||
import type { HTMLAttributes } from 'svelte/elements';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
import { cn, type WithElementRef } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = HTMLAttributes<HTMLSpanElement>;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLSpanElement>> = $props();
|
||||
</script>
|
||||
|
||||
<span class={cn('ml-auto text-xs tracking-widest opacity-60', className)} {...$$restProps}>
|
||||
<slot />
|
||||
<span
|
||||
bind:this={ref}
|
||||
data-slot="dropdown-menu-shortcut"
|
||||
class={cn('text-muted-foreground ml-auto text-xs tracking-widest', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</span>
|
||||
|
||||
@@ -1,30 +1,20 @@
|
||||
<script lang="ts">
|
||||
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
|
||||
import { cn, flyAndScale } from '$lib/utils/style.js';
|
||||
import { cn } from '$lib/utils/style.js';
|
||||
|
||||
type $$Props = DropdownMenuPrimitive.SubContentProps;
|
||||
type $$Events = DropdownMenuPrimitive.SubContentEvents;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export let transition: $$Props['transition'] = flyAndScale;
|
||||
export let transitionConfig: $$Props['transitionConfig'] = {
|
||||
x: -10,
|
||||
y: 0
|
||||
};
|
||||
export { className as class };
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: DropdownMenuPrimitive.SubContentProps = $props();
|
||||
</script>
|
||||
|
||||
<DropdownMenuPrimitive.SubContent
|
||||
{transition}
|
||||
{transitionConfig}
|
||||
bind:ref
|
||||
data-slot="dropdown-menu-sub-content"
|
||||
class={cn(
|
||||
'bg-popover text-popover-foreground z-50 min-w-[8rem] rounded-md border p-1 shadow-lg focus:outline-none',
|
||||
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[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 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg',
|
||||
className
|
||||
)}
|
||||
{...$$restProps}
|
||||
on:keydown
|
||||
on:focusout
|
||||
on:pointermove
|
||||
>
|
||||
<slot />
|
||||
</DropdownMenuPrimitive.SubContent>
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user