mirror of
https://github.com/maxdorninger/MediaManager.git
synced 2026-04-21 08:15:36 +02:00
working on the frontend, adding torrent page, working on show directory, working on the api
This commit is contained in:
@@ -1,122 +1,107 @@
|
||||
<script lang="ts" module>
|
||||
import BookOpen from "@lucide/svelte/icons/book-open";
|
||||
import Bot from "@lucide/svelte/icons/bot";
|
||||
import Settings2 from "@lucide/svelte/icons/settings-2";
|
||||
import SquareTerminal from "@lucide/svelte/icons/square-terminal";
|
||||
import LifeBuoy from '@lucide/svelte/icons/life-buoy';
|
||||
import Send from '@lucide/svelte/icons/send';
|
||||
import TvIcon from '@lucide/svelte/icons/tv';
|
||||
import LayoutPanelLeft from '@lucide/svelte/icons/layout-panel-left';
|
||||
import DownloadIcon from '@lucide/svelte/icons/download';
|
||||
|
||||
const data = {
|
||||
navMain: [
|
||||
{
|
||||
title: "Playground",
|
||||
url: "#",
|
||||
icon: SquareTerminal,
|
||||
isActive: true,
|
||||
items: [
|
||||
{
|
||||
title: "History",
|
||||
url: "#",
|
||||
},
|
||||
{
|
||||
title: "Starred",
|
||||
url: "#",
|
||||
},
|
||||
{
|
||||
title: "Settings",
|
||||
url: "#",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Models",
|
||||
url: "#",
|
||||
icon: Bot,
|
||||
items: [
|
||||
{
|
||||
title: "Genesis",
|
||||
url: "#",
|
||||
},
|
||||
{
|
||||
title: "Explorer",
|
||||
url: "#",
|
||||
},
|
||||
{
|
||||
title: "Quantum",
|
||||
url: "#",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Documentation",
|
||||
url: "#",
|
||||
icon: BookOpen,
|
||||
items: [
|
||||
{
|
||||
title: "Introduction",
|
||||
url: "#",
|
||||
},
|
||||
{
|
||||
title: "Get Started",
|
||||
url: "#",
|
||||
},
|
||||
{
|
||||
title: "Tutorials",
|
||||
url: "#",
|
||||
},
|
||||
{
|
||||
title: "Changelog",
|
||||
url: "#",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Settings",
|
||||
url: "#",
|
||||
icon: Settings2,
|
||||
items: [
|
||||
{
|
||||
title: "General",
|
||||
url: "#",
|
||||
},
|
||||
{
|
||||
title: "Team",
|
||||
url: "#",
|
||||
},
|
||||
{
|
||||
title: "Billing",
|
||||
url: "#",
|
||||
},
|
||||
{
|
||||
title: "Limits",
|
||||
url: "#",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
const data = {
|
||||
navMain: [
|
||||
{
|
||||
title: 'TV',
|
||||
url: '#',
|
||||
icon: TvIcon,
|
||||
isActive: true,
|
||||
items: [
|
||||
{
|
||||
title: 'Shows',
|
||||
url: '/dashboard/tv'
|
||||
},
|
||||
{
|
||||
title: 'Add Show',
|
||||
url: '/dashboard/tv/add-show'
|
||||
},
|
||||
{
|
||||
title: 'Settings',
|
||||
url: '#'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Torrents',
|
||||
url: '#',
|
||||
icon: DownloadIcon,
|
||||
isActive: true,
|
||||
items: [
|
||||
{
|
||||
title: 'Show Torrents',
|
||||
url: '/dashboard/torrents'
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
navSecondary: [
|
||||
{
|
||||
title: 'Support',
|
||||
url: '#',
|
||||
icon: LifeBuoy
|
||||
},
|
||||
{
|
||||
title: 'Feedback',
|
||||
url: '#',
|
||||
icon: Send
|
||||
}
|
||||
],
|
||||
projects: [
|
||||
{
|
||||
name: 'Dashboard',
|
||||
url: '/dashboard',
|
||||
icon: LayoutPanelLeft
|
||||
}
|
||||
]
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import NavMain from "$lib/components/nav-main.svelte";
|
||||
import NavUser from "$lib/components/nav-user.svelte";
|
||||
import * as Sidebar from "$lib/components/ui/sidebar/index.js";
|
||||
import type {ComponentProps} from "svelte";
|
||||
import NavMain from '$lib/components/nav-main.svelte';
|
||||
import NavProjects from '$lib/components/nav-projects.svelte';
|
||||
import NavSecondary from '$lib/components/nav-secondary.svelte';
|
||||
import NavUser from '$lib/components/nav-user.svelte';
|
||||
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
|
||||
import Command from '@lucide/svelte/icons/command';
|
||||
import type {ComponentProps} from 'svelte';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
collapsible = "icon",
|
||||
...restProps
|
||||
}: ComponentProps<typeof Sidebar.Root> = $props();
|
||||
let {ref = $bindable(null), ...restProps}: ComponentProps<typeof Sidebar.Root> = $props();
|
||||
</script>
|
||||
|
||||
<Sidebar.Root {...restProps} bind:ref {collapsible}>
|
||||
<Sidebar.Header>
|
||||
<h1 class="font-bold">MediaManager</h1>
|
||||
</Sidebar.Header>
|
||||
<Sidebar.Content>
|
||||
<NavMain items={data.navMain}/>
|
||||
</Sidebar.Content>
|
||||
<Sidebar.Footer>
|
||||
<NavUser user={data.user}/>
|
||||
</Sidebar.Footer>
|
||||
<Sidebar.Rail/>
|
||||
<Sidebar.Root bind:ref variant="inset" {...restProps}>
|
||||
<Sidebar.Header>
|
||||
<Sidebar.Menu>
|
||||
<Sidebar.MenuItem>
|
||||
<Sidebar.MenuButton size="lg">
|
||||
{#snippet child({props})}
|
||||
<a href="##" {...props}>
|
||||
<div
|
||||
class="flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground"
|
||||
>
|
||||
<Command class="size-4"/>
|
||||
</div>
|
||||
<div class="grid flex-1 text-left text-sm leading-tight">
|
||||
<span class="truncate font-semibold">Acme Inc</span>
|
||||
<span class="truncate text-xs">Enterprise</span>
|
||||
</div>
|
||||
</a>
|
||||
{/snippet}
|
||||
</Sidebar.MenuButton>
|
||||
</Sidebar.MenuItem>
|
||||
</Sidebar.Menu>
|
||||
</Sidebar.Header>
|
||||
<Sidebar.Content>
|
||||
<NavMain items={data.navMain}/>
|
||||
<NavProjects projects={data.projects}/>
|
||||
<NavSecondary items={data.navSecondary} class="mt-auto"/>
|
||||
</Sidebar.Content>
|
||||
<Sidebar.Footer>
|
||||
<NavUser/>
|
||||
</Sidebar.Footer>
|
||||
</Sidebar.Root>
|
||||
|
||||
67
web/src/lib/components/chip.svelte
Normal file
67
web/src/lib/components/chip.svelte
Normal file
@@ -0,0 +1,67 @@
|
||||
<script lang="ts">
|
||||
import {cn} from '$lib/utils'; // Assuming you have the cn utility from shadcn-svelte
|
||||
|
||||
let {
|
||||
label,
|
||||
variant = 'default',
|
||||
size = 'default',
|
||||
onClose = undefined,
|
||||
class: className = ''
|
||||
} = $props<{
|
||||
label: string;
|
||||
variant?: 'default' | 'secondary' | 'outline' | 'destructive';
|
||||
size?: 'default' | 'sm' | 'lg';
|
||||
onClose?: () => void;
|
||||
class?: string;
|
||||
}>();
|
||||
|
||||
// Base styles for the chip
|
||||
const baseStyles =
|
||||
'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50';
|
||||
|
||||
// Variant styles
|
||||
const variantStyles = {
|
||||
default:
|
||||
'border bg-background text-foreground shadow-sm hover:bg-accent hover:text-accent-foreground',
|
||||
secondary: 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
|
||||
outline:
|
||||
'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
|
||||
destructive: 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90'
|
||||
};
|
||||
|
||||
// Size styles
|
||||
const sizeStyles = {
|
||||
default: 'h-9 px-3 py-0.5', // Adjusted height for New York style
|
||||
sm: 'h-7 px-2 py-0.5 text-xs', // Adjusted height for New York style
|
||||
lg: 'h-10 px-4 py-0.5' // Adjusted height for New York style
|
||||
};
|
||||
|
||||
// Styles for the close button
|
||||
const closeButtonStyles =
|
||||
'ml-1 inline-flex h-4 w-4 shrink-0 items-center justify-center rounded-full';
|
||||
</script>
|
||||
|
||||
<div class={cn(baseStyles, variantStyles[variant], sizeStyles[size], className)}>
|
||||
{label}
|
||||
{#if onClose}
|
||||
<button
|
||||
class={cn(closeButtonStyles, 'hover:bg-accent-foreground/20')}
|
||||
onclick={onClose}
|
||||
aria-label="remove tag"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="h-3 w-3"
|
||||
>
|
||||
<path d="M18 6L6 18"/>
|
||||
<path d="M6 6L18 18"/>
|
||||
</svg>
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -1,102 +1,99 @@
|
||||
<script lang="ts">
|
||||
import {Button} from "$lib/components/ui/button/index.js";
|
||||
import {Input} from "$lib/components/ui/input/index.js";
|
||||
import {Label} from "$lib/components/ui/label/index.js";
|
||||
import {goto} from "$app/navigation";
|
||||
import {env} from "$env/dynamic/public"
|
||||
import {Button} from '$lib/components/ui/button/index.js';
|
||||
import * as Card from '$lib/components/ui/card/index.js';
|
||||
import {Input} from '$lib/components/ui/input/index.js';
|
||||
import {Label} from '$lib/components/ui/label/index.js';
|
||||
import {goto} from '$app/navigation';
|
||||
import {env} from '$env/dynamic/public';
|
||||
|
||||
let apiUrl = env.PUBLIC_API_URL
|
||||
let apiUrl = env.PUBLIC_API_URL;
|
||||
|
||||
let email = '';
|
||||
let password = '';
|
||||
let errorMessage = '';
|
||||
let isLoading = false;
|
||||
let email = '';
|
||||
let password = '';
|
||||
let errorMessage = '';
|
||||
let isLoading = false;
|
||||
|
||||
async function handleLogin(event: Event) {
|
||||
event.preventDefault();
|
||||
async function handleLogin(event: Event) {
|
||||
event.preventDefault();
|
||||
|
||||
isLoading = true;
|
||||
errorMessage = '';
|
||||
isLoading = true;
|
||||
errorMessage = '';
|
||||
|
||||
const formData = new URLSearchParams();
|
||||
formData.append('username', email);
|
||||
formData.append('password', password);
|
||||
console.log(apiUrl + '/auth/cookie/login')
|
||||
try {
|
||||
const response = await fetch(apiUrl + '/auth/cookie/login', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: formData.toString(),
|
||||
credentials: 'include',
|
||||
});
|
||||
const formData = new URLSearchParams();
|
||||
formData.append('username', email);
|
||||
formData.append('password', password);
|
||||
console.log(apiUrl + '/auth/cookie/login');
|
||||
try {
|
||||
const response = await fetch(apiUrl + '/auth/cookie/login', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
body: formData.toString(),
|
||||
credentials: 'include'
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
console.log('Login successful!');
|
||||
console.log("Received User Data: ", response);
|
||||
await goto('/dashboard');
|
||||
errorMessage = 'Login successful! Redirecting...';
|
||||
|
||||
} else {
|
||||
let errorText = await response.text();
|
||||
try {
|
||||
const errorData = JSON.parse(errorText);
|
||||
errorMessage = errorData.message || 'Login failed. Please check your credentials.';
|
||||
} catch {
|
||||
errorMessage = errorText || 'Login failed. Please check your credentials.';
|
||||
}
|
||||
console.error('Login failed:', response.status, errorText);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Login request failed:', error);
|
||||
errorMessage = 'An error occurred during the login request.';
|
||||
} finally {
|
||||
isLoading = false;
|
||||
}
|
||||
}
|
||||
if (response.ok) {
|
||||
console.log('Login successful!');
|
||||
console.log('Received User Data: ', response);
|
||||
await goto('/dashboard');
|
||||
errorMessage = 'Login successful! Redirecting...';
|
||||
} else {
|
||||
let errorText = await response.text();
|
||||
try {
|
||||
const errorData = JSON.parse(errorText);
|
||||
errorMessage = errorData.message || 'Login failed. Please check your credentials.';
|
||||
} catch {
|
||||
errorMessage = errorText || 'Login failed. Please check your credentials.';
|
||||
}
|
||||
console.error('Login failed:', response.status, errorText);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Login request failed:', error);
|
||||
errorMessage = 'An error occurred during the login request.';
|
||||
} finally {
|
||||
isLoading = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card.Root class="mx-auto max-w-sm">
|
||||
<Card.Header>
|
||||
<Card.Title class="text-2xl">Login</Card.Title>
|
||||
<Card.Description>Enter your email below to login to your account</Card.Description>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
<form class="grid gap-4" on:submit={handleLogin}>
|
||||
<div class="grid gap-2">
|
||||
<Label for="email">Email</Label>
|
||||
<Input bind:value={email} id="email" placeholder="m@example.com" required type="email"/>
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<div class="flex items-center">
|
||||
<Label for="password">Password</Label>
|
||||
<a class="ml-auto inline-block text-sm underline" href="##">
|
||||
Forgot your password?
|
||||
</a>
|
||||
</div>
|
||||
<Input bind:value={password} id="password" required type="password"/>
|
||||
</div>
|
||||
<Card.Header>
|
||||
<Card.Title class="text-2xl">Login</Card.Title>
|
||||
<Card.Description>Enter your email below to login to your account</Card.Description>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
<form class="grid gap-4" on:submit={handleLogin}>
|
||||
<div class="grid gap-2">
|
||||
<Label for="email">Email</Label>
|
||||
<Input bind:value={email} id="email" placeholder="m@example.com" required type="email"/>
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<div class="flex items-center">
|
||||
<Label for="password">Password</Label>
|
||||
<a class="ml-auto inline-block text-sm underline" href="##"> Forgot your password? </a>
|
||||
</div>
|
||||
<Input bind:value={password} id="password" required type="password"/>
|
||||
</div>
|
||||
|
||||
{#if errorMessage}
|
||||
<p class="text-sm text-red-500">{errorMessage}</p>
|
||||
{/if}
|
||||
{#if errorMessage}
|
||||
<p class="text-sm text-red-500">{errorMessage}</p>
|
||||
{/if}
|
||||
|
||||
<Button class="w-full" disabled={isLoading} type="submit">
|
||||
{#if isLoading}
|
||||
Logging in...
|
||||
{:else}
|
||||
Login
|
||||
{/if}
|
||||
</Button>
|
||||
</form>
|
||||
<Button class="w-full" disabled={isLoading} type="submit">
|
||||
{#if isLoading}
|
||||
Logging in...
|
||||
{:else}
|
||||
Login
|
||||
{/if}
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
<Button class="w-full mt-2" variant="outline">Login with Google</Button>
|
||||
<Button class="mt-2 w-full" variant="outline">Login with Google</Button>
|
||||
|
||||
|
||||
<div class="mt-4 text-center text-sm">
|
||||
Don't have an account?
|
||||
<a class="underline" href="##"> Sign up </a>
|
||||
</div>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
<div class="mt-4 text-center text-sm">
|
||||
Don't have an account?
|
||||
<a class="underline" href="##"> Sign up </a>
|
||||
</div>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
|
||||
@@ -1,66 +1,68 @@
|
||||
<script lang="ts">
|
||||
import * as Collapsible from '$lib/components/ui/collapsible/index.js';
|
||||
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
|
||||
import ChevronRight from '@lucide/svelte/icons/chevron-right';
|
||||
|
||||
let {
|
||||
items,
|
||||
}: {
|
||||
items: {
|
||||
title: string;
|
||||
url: string;
|
||||
// this should be `Component` after @lucide/svelte updates types
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
icon?: any;
|
||||
isActive?: boolean;
|
||||
items?: {
|
||||
title: string;
|
||||
url: string;
|
||||
}[];
|
||||
}[];
|
||||
} = $props();
|
||||
let {
|
||||
items
|
||||
}: {
|
||||
items: {
|
||||
title: string;
|
||||
url: string;
|
||||
// This should be `Component` after @lucide/svelte updates types
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
icon: any;
|
||||
isActive?: boolean;
|
||||
items?: {
|
||||
title: string;
|
||||
url: string;
|
||||
}[];
|
||||
}[];
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<Sidebar.Group>
|
||||
<Sidebar.GroupLabel>Platform</Sidebar.GroupLabel>
|
||||
<Sidebar.Menu>
|
||||
{#each items as mainItem (mainItem.title)}
|
||||
<Collapsible.Root open={mainItem.isActive} class="group/collapsible">
|
||||
{#snippet child({props})}
|
||||
<Sidebar.MenuItem {...props}>
|
||||
<Collapsible.Trigger>
|
||||
{#snippet child({props})}
|
||||
<Sidebar.MenuButton {...props}>
|
||||
{#snippet tooltipContent()}
|
||||
{mainItem.title}
|
||||
{/snippet}
|
||||
{#if mainItem.icon}
|
||||
<mainItem.icon/>
|
||||
{/if}
|
||||
<span>{mainItem.title}</span>
|
||||
<ChevronRight
|
||||
class="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90"
|
||||
/>
|
||||
</Sidebar.MenuButton>
|
||||
{/snippet}
|
||||
</Collapsible.Trigger>
|
||||
<Collapsible.Content>
|
||||
{#if mainItem.items}
|
||||
<Sidebar.MenuSub>
|
||||
{#each mainItem.items as subItem (subItem.title)}
|
||||
<Sidebar.MenuSubItem>
|
||||
<Sidebar.MenuSubButton>
|
||||
{#snippet child({props})}
|
||||
<a href={subItem.url} {...props}>
|
||||
<span>{subItem.title}</span>
|
||||
</a>
|
||||
{/snippet}
|
||||
</Sidebar.MenuSubButton>
|
||||
</Sidebar.MenuSubItem>
|
||||
{/each}
|
||||
</Sidebar.MenuSub>
|
||||
{/if}
|
||||
</Collapsible.Content>
|
||||
</Sidebar.MenuItem>
|
||||
{/snippet}
|
||||
</Collapsible.Root>
|
||||
{/each}
|
||||
</Sidebar.Menu>
|
||||
<Sidebar.GroupLabel>Platform</Sidebar.GroupLabel>
|
||||
<Sidebar.Menu>
|
||||
{#each items as mainItem (mainItem.title)}
|
||||
<Collapsible.Root open={mainItem.isActive}>
|
||||
{#snippet child({props})}
|
||||
<Sidebar.MenuItem {...props}>
|
||||
<Sidebar.MenuButton>
|
||||
{#snippet tooltipContent()}
|
||||
{mainItem.title}
|
||||
{/snippet}
|
||||
{#snippet child({props})}
|
||||
<a href={mainItem.url} {...props}>
|
||||
<mainItem.icon/>
|
||||
<span>{mainItem.title}</span>
|
||||
</a>
|
||||
{/snippet}
|
||||
</Sidebar.MenuButton>
|
||||
{#if mainItem.items?.length}
|
||||
<Collapsible.Trigger>
|
||||
{#snippet child({props})}
|
||||
<Sidebar.MenuAction {...props} class="data-[state=open]:rotate-90">
|
||||
<ChevronRight/>
|
||||
<span class="sr-only">Toggle</span>
|
||||
</Sidebar.MenuAction>
|
||||
{/snippet}
|
||||
</Collapsible.Trigger>
|
||||
<Collapsible.Content>
|
||||
<Sidebar.MenuSub>
|
||||
{#each mainItem.items as subItem (subItem.title)}
|
||||
<Sidebar.MenuSubItem>
|
||||
<Sidebar.MenuSubButton href={subItem.url}>
|
||||
<span>{subItem.title}</span>
|
||||
</Sidebar.MenuSubButton>
|
||||
</Sidebar.MenuSubItem>
|
||||
{/each}
|
||||
</Sidebar.MenuSub>
|
||||
</Collapsible.Content>
|
||||
{/if}
|
||||
</Sidebar.MenuItem>
|
||||
{/snippet}
|
||||
</Collapsible.Root>
|
||||
{/each}
|
||||
</Sidebar.Menu>
|
||||
</Sidebar.Group>
|
||||
|
||||
@@ -1,70 +1,76 @@
|
||||
<script lang="ts">
|
||||
import {useSidebar} from "$lib/components/ui/sidebar/context.svelte.js";
|
||||
import * as DropdownMenu from '$lib/components/ui/dropdown-menu/index.js';
|
||||
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
|
||||
import {useSidebar} from '$lib/components/ui/sidebar/index.js';
|
||||
import Ellipsis from '@lucide/svelte/icons/ellipsis';
|
||||
import Folder from '@lucide/svelte/icons/folder';
|
||||
import Share from '@lucide/svelte/icons/share';
|
||||
import Trash2 from '@lucide/svelte/icons/trash-2';
|
||||
|
||||
let {
|
||||
projects,
|
||||
}: {
|
||||
projects: {
|
||||
name: string;
|
||||
url: string;
|
||||
// This should be `Component` after @lucide/svelte updates types
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
icon: any;
|
||||
}[];
|
||||
} = $props();
|
||||
let {
|
||||
projects
|
||||
}: {
|
||||
projects: {
|
||||
name: string;
|
||||
url: string;
|
||||
// This should be `Component` after @lucide/svelte updates types
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
icon: any;
|
||||
}[];
|
||||
} = $props();
|
||||
|
||||
const sidebar = useSidebar();
|
||||
const sidebar = useSidebar();
|
||||
</script>
|
||||
|
||||
<Sidebar.Group class="group-data-[collapsible=icon]:hidden">
|
||||
<Sidebar.GroupLabel>Projects</Sidebar.GroupLabel>
|
||||
<Sidebar.Menu>
|
||||
{#each projects as item (item.name)}
|
||||
<Sidebar.MenuItem>
|
||||
<Sidebar.MenuButton>
|
||||
{#snippet child({props})}
|
||||
<a href={item.url} {...props}>
|
||||
<item.icon/>
|
||||
<span>{item.name}</span>
|
||||
</a>
|
||||
{/snippet}
|
||||
</Sidebar.MenuButton>
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
{#snippet child({props})}
|
||||
<Sidebar.MenuAction showOnHover {...props}>
|
||||
<Ellipsis/>
|
||||
<span class="sr-only">More</span>
|
||||
</Sidebar.MenuAction>
|
||||
{/snippet}
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content
|
||||
class="w-48 rounded-lg"
|
||||
side={sidebar.isMobile ? "bottom" : "right"}
|
||||
align={sidebar.isMobile ? "end" : "start"}
|
||||
>
|
||||
<DropdownMenu.Item>
|
||||
<Folder class="text-muted-foreground"/>
|
||||
<span>View Project</span>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item>
|
||||
<Forward class="text-muted-foreground"/>
|
||||
<span>Share Project</span>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Separator/>
|
||||
<DropdownMenu.Item>
|
||||
<Trash2 class="text-muted-foreground"/>
|
||||
<span>Delete Project</span>
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
</Sidebar.MenuItem>
|
||||
{/each}
|
||||
<Sidebar.MenuItem>
|
||||
<Sidebar.MenuButton class="text-sidebar-foreground/70">
|
||||
<Ellipsis class="text-sidebar-foreground/70"/>
|
||||
<span>More</span>
|
||||
</Sidebar.MenuButton>
|
||||
</Sidebar.MenuItem>
|
||||
</Sidebar.Menu>
|
||||
<Sidebar.GroupLabel>Projects</Sidebar.GroupLabel>
|
||||
<Sidebar.Menu>
|
||||
{#each projects as item (item.name)}
|
||||
<Sidebar.MenuItem>
|
||||
<Sidebar.MenuButton>
|
||||
{#snippet child({props})}
|
||||
<a href={item.url} {...props}>
|
||||
<item.icon/>
|
||||
<span>{item.name}</span>
|
||||
</a>
|
||||
{/snippet}
|
||||
</Sidebar.MenuButton>
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
{#snippet child({props})}
|
||||
<Sidebar.MenuAction showOnHover {...props}>
|
||||
<Ellipsis/>
|
||||
<span class="sr-only">More</span>
|
||||
</Sidebar.MenuAction>
|
||||
{/snippet}
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content
|
||||
class="w-48"
|
||||
side={sidebar.isMobile ? 'bottom' : 'right'}
|
||||
align={sidebar.isMobile ? 'end' : 'start'}
|
||||
>
|
||||
<DropdownMenu.Item>
|
||||
<Folder class="text-muted-foreground"/>
|
||||
<span>View Project</span>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item>
|
||||
<Share class="text-muted-foreground"/>
|
||||
<span>Share Project</span>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Separator/>
|
||||
<DropdownMenu.Item>
|
||||
<Trash2 class="text-muted-foreground"/>
|
||||
<span>Delete Project</span>
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
</Sidebar.MenuItem>
|
||||
{/each}
|
||||
<Sidebar.MenuItem>
|
||||
<Sidebar.MenuButton>
|
||||
<Ellipsis/>
|
||||
<span>More</span>
|
||||
</Sidebar.MenuButton>
|
||||
</Sidebar.MenuItem>
|
||||
</Sidebar.Menu>
|
||||
</Sidebar.Group>
|
||||
|
||||
37
web/src/lib/components/nav-secondary.svelte
Normal file
37
web/src/lib/components/nav-secondary.svelte
Normal file
@@ -0,0 +1,37 @@
|
||||
<script lang="ts">
|
||||
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
|
||||
import type {ComponentProps} from 'svelte';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
items,
|
||||
...restProps
|
||||
}: {
|
||||
items: {
|
||||
title: string;
|
||||
url: string;
|
||||
// This should be `Component` after @lucide/svelte updates types
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
icon: any;
|
||||
}[];
|
||||
} & ComponentProps<typeof Sidebar.Group> = $props();
|
||||
</script>
|
||||
|
||||
<Sidebar.Group bind:ref {...restProps}>
|
||||
<Sidebar.GroupContent>
|
||||
<Sidebar.Menu>
|
||||
{#each items as item (item.title)}
|
||||
<Sidebar.MenuItem>
|
||||
<Sidebar.MenuButton size="sm">
|
||||
{#snippet child({props})}
|
||||
<a href={item.url} {...props}>
|
||||
<item.icon/>
|
||||
<span>{item.title}</span>
|
||||
</a>
|
||||
{/snippet}
|
||||
</Sidebar.MenuButton>
|
||||
</Sidebar.MenuItem>
|
||||
{/each}
|
||||
</Sidebar.Menu>
|
||||
</Sidebar.GroupContent>
|
||||
</Sidebar.Group>
|
||||
@@ -1,69 +1,89 @@
|
||||
<script lang="ts">
|
||||
import {useSidebar} from "$lib/components/ui/sidebar/index.js";
|
||||
import UserDetails from "$lib/components/user-details.svelte"
|
||||
import BadgeCheck from '@lucide/svelte/icons/badge-check';
|
||||
import Bell from '@lucide/svelte/icons/bell';
|
||||
import ChevronsUpDown from '@lucide/svelte/icons/chevrons-up-down';
|
||||
import CreditCard from '@lucide/svelte/icons/credit-card';
|
||||
import LogOut from '@lucide/svelte/icons/log-out';
|
||||
import Sparkles from '@lucide/svelte/icons/sparkles';
|
||||
|
||||
//let { user }: { user: { name: string; email: string; avatar: string } } = $props();
|
||||
const sidebar = useSidebar();
|
||||
import * as Avatar from '$lib/components/ui/avatar/index.js';
|
||||
import * as DropdownMenu from '$lib/components/ui/dropdown-menu/index.js';
|
||||
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
|
||||
import {useSidebar} from '$lib/components/ui/sidebar/index.js';
|
||||
import {getContext} from 'svelte';
|
||||
import UserDetails from './user-details.svelte';
|
||||
import type {User} from '$lib/types';
|
||||
|
||||
const user: () => User = getContext('user');
|
||||
const sidebar = useSidebar();
|
||||
</script>
|
||||
|
||||
<Sidebar.Menu>
|
||||
<Sidebar.MenuItem>
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
{#snippet child({props})}
|
||||
<Sidebar.MenuButton
|
||||
size="lg"
|
||||
class="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
|
||||
{...props}
|
||||
>
|
||||
<div class="grid flex-1 text-left text-sm leading-tight">
|
||||
<UserDetails/>
|
||||
</div>
|
||||
<ChevronsUpDown class="ml-auto size-4"/>
|
||||
</Sidebar.MenuButton>
|
||||
{/snippet}
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content
|
||||
align="end"
|
||||
class="w-[var(--bits-dropdown-menu-anchor-width)] min-w-56 rounded-lg"
|
||||
side={sidebar.isMobile ? "bottom" : "right"}
|
||||
sideOffset={4}
|
||||
>
|
||||
<DropdownMenu.Label class="p-0 font-normal">
|
||||
<div class="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
|
||||
<div class="grid flex-1 text-left text-sm leading-tight">
|
||||
<UserDetails/>
|
||||
</div>
|
||||
</div>
|
||||
</DropdownMenu.Label>
|
||||
<DropdownMenu.Separator/>
|
||||
<DropdownMenu.Group>
|
||||
<DropdownMenu.Item>
|
||||
<Sparkles/>
|
||||
Upgrade to Pro
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Group>
|
||||
<DropdownMenu.Separator/>
|
||||
<DropdownMenu.Group>
|
||||
<DropdownMenu.Item>
|
||||
<BadgeCheck/>
|
||||
Account
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item>
|
||||
<CreditCard/>
|
||||
Billing
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item>
|
||||
<Bell/>
|
||||
Notifications
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Group>
|
||||
<DropdownMenu.Separator/>
|
||||
<DropdownMenu.Item>
|
||||
<LogOut/>
|
||||
Log out
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
</Sidebar.MenuItem>
|
||||
<Sidebar.MenuItem>
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
{#snippet child({props})}
|
||||
<Sidebar.MenuButton
|
||||
{...props}
|
||||
size="lg"
|
||||
class="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
|
||||
>
|
||||
<Avatar.Root class="h-8 w-8 rounded-lg">
|
||||
<!--<Avatar.Image src={user.avatar} alt={user.name} />-->
|
||||
<Avatar.Fallback class="rounded-lg">CN</Avatar.Fallback>
|
||||
</Avatar.Root>
|
||||
<div class="grid flex-1 text-left text-sm leading-tight">
|
||||
<UserDetails/>
|
||||
</div>
|
||||
<ChevronsUpDown class="ml-auto size-4"/>
|
||||
</Sidebar.MenuButton>
|
||||
{/snippet}
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content
|
||||
class="w-[var(--bits-dropdown-menu-anchor-width)] min-w-56 rounded-lg"
|
||||
side={sidebar.isMobile ? 'bottom' : 'right'}
|
||||
align="end"
|
||||
sideOffset={4}
|
||||
>
|
||||
<DropdownMenu.Label class="p-0 font-normal">
|
||||
<div class="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
|
||||
<Avatar.Root class="h-8 w-8 rounded-lg">
|
||||
<!--<Avatar.Image src={user.avatar} alt={user.name} />-->
|
||||
<Avatar.Fallback class="rounded-lg">CN</Avatar.Fallback>
|
||||
</Avatar.Root>
|
||||
<div class="grid flex-1 text-left text-sm leading-tight">
|
||||
<UserDetails/>
|
||||
</div>
|
||||
</div>
|
||||
</DropdownMenu.Label>
|
||||
<DropdownMenu.Separator/>
|
||||
<DropdownMenu.Group>
|
||||
<DropdownMenu.Item>
|
||||
<Sparkles/>
|
||||
Upgrade to Pro
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Group>
|
||||
<DropdownMenu.Separator/>
|
||||
<DropdownMenu.Group>
|
||||
<DropdownMenu.Item>
|
||||
<BadgeCheck/>
|
||||
Account
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item>
|
||||
<CreditCard/>
|
||||
Billing
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item>
|
||||
<Bell/>
|
||||
Notifications
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Group>
|
||||
<DropdownMenu.Separator/>
|
||||
<DropdownMenu.Item>
|
||||
<LogOut/>
|
||||
Log out
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
</Sidebar.MenuItem>
|
||||
</Sidebar.Menu>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import {Avatar as AvatarPrimitive} from "bits-ui";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import {Avatar as AvatarPrimitive} from 'bits-ui';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
class: className,
|
||||
@@ -10,7 +10,7 @@
|
||||
</script>
|
||||
|
||||
<AvatarPrimitive.Fallback
|
||||
{...restProps}
|
||||
bind:ref
|
||||
class={cn("bg-muted flex size-full items-center justify-center", className)}
|
||||
class={cn('flex size-full items-center justify-center bg-muted', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import {Avatar as AvatarPrimitive} from "bits-ui";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import {Avatar as AvatarPrimitive} from 'bits-ui';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
class: className,
|
||||
@@ -12,9 +12,9 @@
|
||||
</script>
|
||||
|
||||
<AvatarPrimitive.Image
|
||||
{...restProps}
|
||||
{alt}
|
||||
bind:ref
|
||||
class={cn("aspect-square size-full", className)}
|
||||
{src}
|
||||
{alt}
|
||||
class={cn('aspect-square size-full', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
<script lang="ts">
|
||||
import {Avatar as AvatarPrimitive} from "bits-ui";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import {Avatar as AvatarPrimitive} from 'bits-ui';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
class: className,
|
||||
ref = $bindable(null),
|
||||
loadingStatus = $bindable("loading"),
|
||||
loadingStatus = $bindable('loading'),
|
||||
...restProps
|
||||
}: AvatarPrimitive.RootProps = $props();
|
||||
</script>
|
||||
|
||||
<AvatarPrimitive.Root
|
||||
{...restProps}
|
||||
bind:loadingStatus
|
||||
bind:ref
|
||||
class={cn("relative flex size-10 shrink-0 overflow-hidden rounded-full", className)}
|
||||
class={cn('relative flex size-10 shrink-0 overflow-hidden rounded-full', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Root from "./avatar.svelte";
|
||||
import Image from "./avatar-image.svelte";
|
||||
import Fallback from "./avatar-fallback.svelte";
|
||||
import Root from './avatar.svelte';
|
||||
import Image from './avatar-image.svelte';
|
||||
import Fallback from './avatar-fallback.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
@@ -9,5 +9,5 @@ export {
|
||||
//
|
||||
Root as Avatar,
|
||||
Image as AvatarImage,
|
||||
Fallback as AvatarFallback,
|
||||
Fallback as AvatarFallback
|
||||
};
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<script lang="ts">
|
||||
import type {WithElementRef, WithoutChildren} from "bits-ui";
|
||||
import type {HTMLAttributes} from "svelte/elements";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import Ellipsis from '@lucide/svelte/icons/ellipsis';
|
||||
import type {WithElementRef, WithoutChildren} from 'bits-ui';
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -11,11 +12,11 @@
|
||||
</script>
|
||||
|
||||
<span
|
||||
{...restProps}
|
||||
aria-hidden="true"
|
||||
bind:this={ref}
|
||||
class={cn("flex size-9 items-center justify-center", className)}
|
||||
role="presentation"
|
||||
aria-hidden="true"
|
||||
class={cn('flex size-9 items-center justify-center', className)}
|
||||
{...restProps}
|
||||
>
|
||||
<Ellipsis class="size-4"/>
|
||||
<span class="sr-only">More</span>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import type {HTMLLiAttributes} from "svelte/elements";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import type {HTMLLiAttributes} from 'svelte/elements';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -11,6 +11,6 @@
|
||||
}: WithElementRef<HTMLLiAttributes> = $props();
|
||||
</script>
|
||||
|
||||
<li {...restProps} bind:this={ref} class={cn("inline-flex items-center gap-1.5", className)}>
|
||||
<li bind:this={ref} class={cn('inline-flex items-center gap-1.5', className)} {...restProps}>
|
||||
{@render children?.()}
|
||||
</li>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import type {HTMLAnchorAttributes} from "svelte/elements";
|
||||
import type {Snippet} from "svelte";
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {HTMLAnchorAttributes} from 'svelte/elements';
|
||||
import type {Snippet} from 'svelte';
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -16,9 +16,9 @@
|
||||
} = $props();
|
||||
|
||||
const attrs = $derived({
|
||||
class: cn("hover:text-foreground transition-colors", className),
|
||||
class: cn('hover:text-foreground transition-colors', className),
|
||||
href,
|
||||
...restProps,
|
||||
...restProps
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import type {HTMLOlAttributes} from "svelte/elements";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import type {HTMLOlAttributes} from 'svelte/elements';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -12,12 +12,12 @@
|
||||
</script>
|
||||
|
||||
<ol
|
||||
{...restProps}
|
||||
bind:this={ref}
|
||||
class={cn(
|
||||
"text-muted-foreground flex flex-wrap items-center gap-1.5 break-words text-sm sm:gap-2.5",
|
||||
'flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</ol>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import type {HTMLAttributes} from "svelte/elements";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -12,12 +12,12 @@
|
||||
</script>
|
||||
|
||||
<span
|
||||
{...restProps}
|
||||
aria-current="page"
|
||||
aria-disabled="true"
|
||||
bind:this={ref}
|
||||
class={cn("text-foreground font-normal", className)}
|
||||
role="link"
|
||||
aria-disabled="true"
|
||||
aria-current="page"
|
||||
class={cn('font-normal text-foreground', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</span>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<script lang="ts">
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import type {HTMLLiAttributes} from "svelte/elements";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import ChevronRight from '@lucide/svelte/icons/chevron-right';
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import type {HTMLLiAttributes} from 'svelte/elements';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -12,11 +13,11 @@
|
||||
</script>
|
||||
|
||||
<li
|
||||
{...restProps}
|
||||
aria-hidden="true"
|
||||
bind:this={ref}
|
||||
class={cn("[&>svg]:size-3.5", className)}
|
||||
role="presentation"
|
||||
aria-hidden="true"
|
||||
class={cn('[&>svg]:size-3.5', className)}
|
||||
bind:this={ref}
|
||||
{...restProps}
|
||||
>
|
||||
{#if children}
|
||||
{@render children?.()}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import type {HTMLAttributes} from "svelte/elements";
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
|
||||
let {
|
||||
ref = $bindable(),
|
||||
@@ -10,6 +10,6 @@
|
||||
}: WithElementRef<HTMLAttributes<HTMLElement>> = $props();
|
||||
</script>
|
||||
|
||||
<nav {...restProps} aria-label="breadcrumb" bind:this={ref} class={className}>
|
||||
<nav class={className} bind:this={ref} aria-label="breadcrumb" {...restProps}>
|
||||
{@render children?.()}
|
||||
</nav>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import Root from "./breadcrumb.svelte";
|
||||
import Ellipsis from "./breadcrumb-ellipsis.svelte";
|
||||
import Item from "./breadcrumb-item.svelte";
|
||||
import Separator from "./breadcrumb-separator.svelte";
|
||||
import Link from "./breadcrumb-link.svelte";
|
||||
import List from "./breadcrumb-list.svelte";
|
||||
import Page from "./breadcrumb-page.svelte";
|
||||
import Root from './breadcrumb.svelte';
|
||||
import Ellipsis from './breadcrumb-ellipsis.svelte';
|
||||
import Item from './breadcrumb-item.svelte';
|
||||
import Separator from './breadcrumb-separator.svelte';
|
||||
import Link from './breadcrumb-link.svelte';
|
||||
import List from './breadcrumb-list.svelte';
|
||||
import Page from './breadcrumb-page.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
@@ -21,5 +21,5 @@ export {
|
||||
Separator as BreadcrumbSeparator,
|
||||
Link as BreadcrumbLink,
|
||||
List as BreadcrumbList,
|
||||
Page as BreadcrumbPage,
|
||||
Page as BreadcrumbPage
|
||||
};
|
||||
|
||||
@@ -1,36 +1,35 @@
|
||||
<script lang="ts" module>
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import type {HTMLAnchorAttributes, HTMLButtonAttributes} from "svelte/elements";
|
||||
import {tv, type VariantProps} from "tailwind-variants";
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import type {HTMLAnchorAttributes, HTMLButtonAttributes} from 'svelte/elements';
|
||||
import {type VariantProps, tv} from 'tailwind-variants';
|
||||
|
||||
export const buttonVariants = tv({
|
||||
base: "focus-visible:ring-ring inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
||||
base: 'focus-visible:ring-ring inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-primary text-primary-foreground hover:bg-primary/90 shadow",
|
||||
destructive:
|
||||
"bg-destructive text-destructive-foreground hover:bg-destructive/90 shadow-sm",
|
||||
default: 'bg-primary text-primary-foreground hover:bg-primary/90 shadow',
|
||||
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90 shadow-sm',
|
||||
outline:
|
||||
"border-input bg-background hover:bg-accent hover:text-accent-foreground border shadow-sm",
|
||||
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 shadow-sm",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
'border-input bg-background hover:bg-accent hover:text-accent-foreground border shadow-sm',
|
||||
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80 shadow-sm',
|
||||
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
||||
link: 'text-primary underline-offset-4 hover:underline'
|
||||
},
|
||||
size: {
|
||||
default: "h-9 px-4 py-2",
|
||||
sm: "h-8 rounded-md px-3 text-xs",
|
||||
lg: "h-10 rounded-md px-8",
|
||||
icon: "h-9 w-9",
|
||||
},
|
||||
default: 'h-9 px-4 py-2',
|
||||
sm: 'h-8 rounded-md px-3 text-xs',
|
||||
lg: 'h-10 rounded-md px-8',
|
||||
icon: 'h-9 w-9'
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
variant: 'default',
|
||||
size: 'default'
|
||||
}
|
||||
});
|
||||
|
||||
export type ButtonVariant = VariantProps<typeof buttonVariants>["variant"];
|
||||
export type ButtonSize = VariantProps<typeof buttonVariants>["size"];
|
||||
export type ButtonVariant = VariantProps<typeof buttonVariants>['variant'];
|
||||
export type ButtonSize = VariantProps<typeof buttonVariants>['size'];
|
||||
|
||||
export type ButtonProps = WithElementRef<HTMLButtonAttributes> &
|
||||
WithElementRef<HTMLAnchorAttributes> & {
|
||||
@@ -40,27 +39,22 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import {cn} from "$lib/utils.js";
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
class: className,
|
||||
variant = "default",
|
||||
size = "default",
|
||||
variant = 'default',
|
||||
size = 'default',
|
||||
ref = $bindable(null),
|
||||
href = undefined,
|
||||
type = "button",
|
||||
type = 'button',
|
||||
children,
|
||||
...restProps
|
||||
}: ButtonProps = $props();
|
||||
</script>
|
||||
|
||||
{#if href}
|
||||
<a
|
||||
bind:this={ref}
|
||||
class={cn(buttonVariants({ variant, size }), className)}
|
||||
{href}
|
||||
{...restProps}
|
||||
>
|
||||
<a bind:this={ref} class={cn(buttonVariants({ variant, size }), className)} {href} {...restProps}>
|
||||
{@render children?.()}
|
||||
</a>
|
||||
{:else}
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import Root, {type ButtonProps, type ButtonSize, type ButtonVariant, buttonVariants,} from "./button.svelte";
|
||||
import Root, {
|
||||
type ButtonProps,
|
||||
type ButtonSize,
|
||||
type ButtonVariant,
|
||||
buttonVariants
|
||||
} from './button.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
@@ -8,5 +13,5 @@ export {
|
||||
buttonVariants,
|
||||
type ButtonProps,
|
||||
type ButtonSize,
|
||||
type ButtonVariant,
|
||||
type ButtonVariant
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import type {HTMLAttributes} from "svelte/elements";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -11,6 +11,6 @@
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<div {...restProps} bind:this={ref} class={cn("p-6", className)}>
|
||||
<div {...restProps} bind:this={ref} class={cn('p-6', className)}>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import type {HTMLAttributes} from "svelte/elements";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -11,6 +11,6 @@
|
||||
}: WithElementRef<HTMLAttributes<HTMLParagraphElement>> = $props();
|
||||
</script>
|
||||
|
||||
<p {...restProps} bind:this={ref} class={cn("text-muted-foreground text-sm", className)}>
|
||||
<p {...restProps} bind:this={ref} class={cn('text-sm text-muted-foreground', className)}>
|
||||
{@render children?.()}
|
||||
</p>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import type {HTMLAttributes} from "svelte/elements";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -11,6 +11,6 @@
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<div {...restProps} bind:this={ref} class={cn("flex items-center p-6 pt-0", className)}>
|
||||
<div {...restProps} bind:this={ref} class={cn('flex items-center p-6 pt-0', className)}>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import type {HTMLAttributes} from "svelte/elements";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -11,6 +11,6 @@
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<div {...restProps} bind:this={ref} class={cn("flex flex-col space-y-1.5 p-6 pb-0", className)}>
|
||||
<div {...restProps} bind:this={ref} class={cn('flex flex-col space-y-1.5 p-6 pb-0', className)}>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import type {HTMLAttributes} from "svelte/elements";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -18,7 +18,7 @@
|
||||
{...restProps}
|
||||
aria-level={level}
|
||||
bind:this={ref}
|
||||
class={cn("font-semibold leading-none tracking-tight", className)}
|
||||
class={cn('font-semibold leading-none tracking-tight', className)}
|
||||
role="heading"
|
||||
>
|
||||
{@render children?.()}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import type {HTMLAttributes} from "svelte/elements";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -14,7 +14,7 @@
|
||||
<div
|
||||
{...restProps}
|
||||
bind:this={ref}
|
||||
class={cn("bg-card text-card-foreground rounded-xl border shadow", className)}
|
||||
class={cn('rounded-xl border bg-card text-card-foreground shadow', className)}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import Root from "./card.svelte";
|
||||
import Content from "./card-content.svelte";
|
||||
import Description from "./card-description.svelte";
|
||||
import Footer from "./card-footer.svelte";
|
||||
import Header from "./card-header.svelte";
|
||||
import Title from "./card-title.svelte";
|
||||
import Root from './card.svelte';
|
||||
import Content from './card-content.svelte';
|
||||
import Description from './card-description.svelte';
|
||||
import Footer from './card-footer.svelte';
|
||||
import Header from './card-header.svelte';
|
||||
import Title from './card-title.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
@@ -18,5 +18,5 @@ export {
|
||||
Description as CardDescription,
|
||||
Footer as CardFooter,
|
||||
Header as CardHeader,
|
||||
Title as CardTitle,
|
||||
Title as CardTitle
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {Collapsible as CollapsiblePrimitive} from "bits-ui";
|
||||
import {Collapsible as CollapsiblePrimitive} from 'bits-ui';
|
||||
|
||||
const Root: typeof CollapsiblePrimitive.Root = CollapsiblePrimitive.Root;
|
||||
const Trigger: typeof CollapsiblePrimitive.Trigger = CollapsiblePrimitive.Trigger;
|
||||
@@ -11,5 +11,5 @@ export {
|
||||
//
|
||||
Root as Collapsible,
|
||||
Content as CollapsibleContent,
|
||||
Trigger as CollapsibleTrigger,
|
||||
Trigger as CollapsibleTrigger
|
||||
};
|
||||
|
||||
38
web/src/lib/components/ui/dialog/dialog-content.svelte
Normal file
38
web/src/lib/components/ui/dialog/dialog-content.svelte
Normal file
@@ -0,0 +1,38 @@
|
||||
<script lang="ts">
|
||||
import {Dialog as DialogPrimitive, type WithoutChildrenOrChild} from 'bits-ui';
|
||||
import X from '@lucide/svelte/icons/x';
|
||||
import type {Snippet} from 'svelte';
|
||||
import * as Dialog from './index.js';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
portalProps,
|
||||
children,
|
||||
...restProps
|
||||
}: WithoutChildrenOrChild<DialogPrimitive.ContentProps> & {
|
||||
portalProps?: DialogPrimitive.PortalProps;
|
||||
children: Snippet;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<Dialog.Portal {...portalProps}>
|
||||
<Dialog.Overlay/>
|
||||
<DialogPrimitive.Content
|
||||
bind:ref
|
||||
class={cn(
|
||||
'fixed left-[50%] top-[50%] z-50 grid translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
<DialogPrimitive.Close
|
||||
class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground"
|
||||
>
|
||||
<X class="size-4"/>
|
||||
<span class="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
</DialogPrimitive.Content>
|
||||
</Dialog.Portal>
|
||||
16
web/src/lib/components/ui/dialog/dialog-description.svelte
Normal file
16
web/src/lib/components/ui/dialog/dialog-description.svelte
Normal file
@@ -0,0 +1,16 @@
|
||||
<script lang="ts">
|
||||
import {Dialog as DialogPrimitive} from 'bits-ui';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: DialogPrimitive.DescriptionProps = $props();
|
||||
</script>
|
||||
|
||||
<DialogPrimitive.Description
|
||||
bind:ref
|
||||
class={cn('text-sm text-muted-foreground', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
20
web/src/lib/components/ui/dialog/dialog-footer.svelte
Normal file
20
web/src/lib/components/ui/dialog/dialog-footer.svelte
Normal file
@@ -0,0 +1,20 @@
|
||||
<script lang="ts">
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
bind:this={ref}
|
||||
class={cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
20
web/src/lib/components/ui/dialog/dialog-header.svelte
Normal file
20
web/src/lib/components/ui/dialog/dialog-header.svelte
Normal file
@@ -0,0 +1,20 @@
|
||||
<script lang="ts">
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
bind:this={ref}
|
||||
class={cn('flex flex-col space-y-1.5 text-center sm:text-left', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
19
web/src/lib/components/ui/dialog/dialog-overlay.svelte
Normal file
19
web/src/lib/components/ui/dialog/dialog-overlay.svelte
Normal file
@@ -0,0 +1,19 @@
|
||||
<script lang="ts">
|
||||
import {Dialog as DialogPrimitive} from 'bits-ui';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: DialogPrimitive.OverlayProps = $props();
|
||||
</script>
|
||||
|
||||
<DialogPrimitive.Overlay
|
||||
bind:ref
|
||||
class={cn(
|
||||
'fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
/>
|
||||
16
web/src/lib/components/ui/dialog/dialog-title.svelte
Normal file
16
web/src/lib/components/ui/dialog/dialog-title.svelte
Normal file
@@ -0,0 +1,16 @@
|
||||
<script lang="ts">
|
||||
import {Dialog as DialogPrimitive} from 'bits-ui';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: DialogPrimitive.TitleProps = $props();
|
||||
</script>
|
||||
|
||||
<DialogPrimitive.Title
|
||||
bind:ref
|
||||
class={cn('text-lg font-semibold leading-none tracking-tight', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
37
web/src/lib/components/ui/dialog/index.ts
Normal file
37
web/src/lib/components/ui/dialog/index.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import {Dialog as DialogPrimitive} from 'bits-ui';
|
||||
|
||||
import Title from './dialog-title.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';
|
||||
|
||||
const Root: typeof DialogPrimitive.Root = DialogPrimitive.Root;
|
||||
const Trigger: typeof DialogPrimitive.Trigger = DialogPrimitive.Trigger;
|
||||
const Close: typeof DialogPrimitive.Close = DialogPrimitive.Close;
|
||||
const Portal: typeof DialogPrimitive.Portal = DialogPrimitive.Portal;
|
||||
|
||||
export {
|
||||
Root,
|
||||
Title,
|
||||
Portal,
|
||||
Footer,
|
||||
Header,
|
||||
Trigger,
|
||||
Overlay,
|
||||
Content,
|
||||
Description,
|
||||
Close,
|
||||
//
|
||||
Root as Dialog,
|
||||
Title as DialogTitle,
|
||||
Portal as DialogPortal,
|
||||
Footer as DialogFooter,
|
||||
Header as DialogHeader,
|
||||
Trigger as DialogTrigger,
|
||||
Overlay as DialogOverlay,
|
||||
Content as DialogContent,
|
||||
Description as DialogDescription,
|
||||
Close as DialogClose
|
||||
};
|
||||
@@ -1,7 +1,9 @@
|
||||
<script lang="ts">
|
||||
import {DropdownMenu as DropdownMenuPrimitive, type WithoutChildrenOrChild} from "bits-ui";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {Snippet} from "svelte";
|
||||
import {DropdownMenu as DropdownMenuPrimitive, type WithoutChildrenOrChild} from 'bits-ui';
|
||||
import Check from '@lucide/svelte/icons/check';
|
||||
import Minus from '@lucide/svelte/icons/minus';
|
||||
import {cn} from '$lib/utils.js';
|
||||
import type {Snippet} from 'svelte';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -16,21 +18,21 @@
|
||||
</script>
|
||||
|
||||
<DropdownMenuPrimitive.CheckboxItem
|
||||
{...restProps}
|
||||
bind:ref
|
||||
bind:checked
|
||||
bind:indeterminate
|
||||
bind:ref
|
||||
class={cn(
|
||||
"data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:opacity-50',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
{#snippet children({checked, indeterminate})}
|
||||
<span class="absolute left-2 flex size-3.5 items-center justify-center">
|
||||
{#if indeterminate}
|
||||
<Minus class="size-4"/>
|
||||
{:else}
|
||||
<Check class={cn("size-4", !checked && "text-transparent")}/>
|
||||
<Check class={cn('size-4', !checked && 'text-transparent')}/>
|
||||
{/if}
|
||||
</span>
|
||||
{@render childrenProp?.()}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import {cn} from "$lib/utils.js";
|
||||
import {DropdownMenu as DropdownMenuPrimitive} from "bits-ui";
|
||||
import {cn} from '$lib/utils.js';
|
||||
import {DropdownMenu as DropdownMenuPrimitive} from 'bits-ui';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -15,13 +15,13 @@
|
||||
|
||||
<DropdownMenuPrimitive.Portal {...portalProps}>
|
||||
<DropdownMenuPrimitive.Content
|
||||
{...restProps}
|
||||
bind:ref
|
||||
{sideOffset}
|
||||
class={cn(
|
||||
"bg-popover text-popover-foreground z-50 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-md",
|
||||
"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 outline-none",
|
||||
'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md',
|
||||
'outline-none 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',
|
||||
className
|
||||
)}
|
||||
{sideOffset}
|
||||
{...restProps}
|
||||
/>
|
||||
</DropdownMenuPrimitive.Portal>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import {DropdownMenu as DropdownMenuPrimitive} from "bits-ui";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import {DropdownMenu as DropdownMenuPrimitive} from 'bits-ui';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -13,7 +13,7 @@
|
||||
</script>
|
||||
|
||||
<DropdownMenuPrimitive.GroupHeading
|
||||
{...restProps}
|
||||
bind:ref
|
||||
class={cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className)}
|
||||
class={cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import {cn} from "$lib/utils.js";
|
||||
import {DropdownMenu as DropdownMenuPrimitive} from "bits-ui";
|
||||
import {cn} from '$lib/utils.js';
|
||||
import {DropdownMenu as DropdownMenuPrimitive} from 'bits-ui';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -13,11 +13,11 @@
|
||||
</script>
|
||||
|
||||
<DropdownMenuPrimitive.Item
|
||||
{...restProps}
|
||||
bind:ref
|
||||
class={cn(
|
||||
"data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0",
|
||||
inset && "pl-8",
|
||||
'relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0',
|
||||
inset && 'pl-8',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import {cn} from "$lib/utils.js";
|
||||
import {type WithElementRef} from "bits-ui";
|
||||
import type {HTMLAttributes} from "svelte/elements";
|
||||
import {cn} from '$lib/utils.js';
|
||||
import {type WithElementRef} from 'bits-ui';
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -15,9 +15,9 @@
|
||||
</script>
|
||||
|
||||
<div
|
||||
{...restProps}
|
||||
bind:this={ref}
|
||||
class={cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className)}
|
||||
class={cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import {DropdownMenu as DropdownMenuPrimitive, type WithoutChild} from "bits-ui";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import {DropdownMenu as DropdownMenuPrimitive, type WithoutChild} from 'bits-ui';
|
||||
import Circle from '@lucide/svelte/icons/circle';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -11,12 +12,12 @@
|
||||
</script>
|
||||
|
||||
<DropdownMenuPrimitive.RadioItem
|
||||
{...restProps}
|
||||
bind:ref
|
||||
class={cn(
|
||||
"data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:opacity-50',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
{#snippet children({checked})}
|
||||
<span class="absolute left-2 flex size-3.5 items-center justify-center">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import {DropdownMenu as DropdownMenuPrimitive} from "bits-ui";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import {DropdownMenu as DropdownMenuPrimitive} from 'bits-ui';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -10,7 +10,7 @@
|
||||
</script>
|
||||
|
||||
<DropdownMenuPrimitive.Separator
|
||||
{...restProps}
|
||||
bind:ref
|
||||
class={cn("bg-muted -mx-1 my-1 h-px", className)}
|
||||
class={cn('-mx-1 my-1 h-px bg-muted', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type {HTMLAttributes} from "svelte/elements";
|
||||
import {type WithElementRef} from "bits-ui";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
import {type WithElementRef} from 'bits-ui';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -12,9 +12,9 @@
|
||||
</script>
|
||||
|
||||
<span
|
||||
{...restProps}
|
||||
bind:this={ref}
|
||||
class={cn("ml-auto text-xs tracking-widest opacity-60", className)}
|
||||
class={cn('ml-auto text-xs tracking-widest opacity-60', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</span>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import {DropdownMenu as DropdownMenuPrimitive} from "bits-ui";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import {DropdownMenu as DropdownMenuPrimitive} from 'bits-ui';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -10,10 +10,10 @@
|
||||
</script>
|
||||
|
||||
<DropdownMenuPrimitive.SubContent
|
||||
{...restProps}
|
||||
bind:ref
|
||||
class={cn(
|
||||
"bg-popover text-popover-foreground z-50 min-w-[8rem] rounded-md border p-1 shadow-lg focus:outline-none",
|
||||
'z-50 min-w-[8rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-lg focus:outline-none',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import {DropdownMenu as DropdownMenuPrimitive, type WithoutChild} from "bits-ui";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import {DropdownMenu as DropdownMenuPrimitive, type WithoutChild} from 'bits-ui';
|
||||
import ChevronRight from '@lucide/svelte/icons/chevron-right';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -14,13 +15,13 @@
|
||||
</script>
|
||||
|
||||
<DropdownMenuPrimitive.SubTrigger
|
||||
{...restProps}
|
||||
bind:ref
|
||||
class={cn(
|
||||
"data-[highlighted]:bg-accent data-[state=open]:bg-accent flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
||||
inset && "pl-8",
|
||||
'flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none data-[highlighted]:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
||||
inset && 'pl-8',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
<ChevronRight class="ml-auto"/>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import {DropdownMenu as DropdownMenuPrimitive} from "bits-ui";
|
||||
import CheckboxItem from "./dropdown-menu-checkbox-item.svelte";
|
||||
import Content from "./dropdown-menu-content.svelte";
|
||||
import GroupHeading from "./dropdown-menu-group-heading.svelte";
|
||||
import Item from "./dropdown-menu-item.svelte";
|
||||
import Label from "./dropdown-menu-label.svelte";
|
||||
import RadioItem from "./dropdown-menu-radio-item.svelte";
|
||||
import Separator from "./dropdown-menu-separator.svelte";
|
||||
import Shortcut from "./dropdown-menu-shortcut.svelte";
|
||||
import SubContent from "./dropdown-menu-sub-content.svelte";
|
||||
import SubTrigger from "./dropdown-menu-sub-trigger.svelte";
|
||||
import {DropdownMenu as DropdownMenuPrimitive} from 'bits-ui';
|
||||
import CheckboxItem from './dropdown-menu-checkbox-item.svelte';
|
||||
import Content from './dropdown-menu-content.svelte';
|
||||
import GroupHeading from './dropdown-menu-group-heading.svelte';
|
||||
import Item from './dropdown-menu-item.svelte';
|
||||
import Label from './dropdown-menu-label.svelte';
|
||||
import RadioItem from './dropdown-menu-radio-item.svelte';
|
||||
import Separator from './dropdown-menu-separator.svelte';
|
||||
import Shortcut from './dropdown-menu-shortcut.svelte';
|
||||
import SubContent from './dropdown-menu-sub-content.svelte';
|
||||
import SubTrigger from './dropdown-menu-sub-trigger.svelte';
|
||||
|
||||
const Sub = DropdownMenuPrimitive.Sub;
|
||||
const Root = DropdownMenuPrimitive.Root;
|
||||
@@ -46,5 +46,5 @@ export {
|
||||
Sub,
|
||||
SubContent,
|
||||
SubTrigger,
|
||||
Trigger,
|
||||
Trigger
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Root from "./input.svelte";
|
||||
import Root from './input.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
//
|
||||
Root as Input,
|
||||
Root as Input
|
||||
};
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<script lang="ts">
|
||||
import type {HTMLInputAttributes, HTMLInputTypeAttribute} from "svelte/elements";
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {HTMLInputAttributes, HTMLInputTypeAttribute} from 'svelte/elements';
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
type InputType = Exclude<HTMLInputTypeAttribute, "file">;
|
||||
type InputType = Exclude<HTMLInputTypeAttribute, 'file'>;
|
||||
|
||||
type Props = WithElementRef<
|
||||
Omit<HTMLInputAttributes, "type"> &
|
||||
({ type: "file"; files?: FileList } | { type?: InputType; files?: undefined })
|
||||
Omit<HTMLInputAttributes, 'type'> &
|
||||
({ type: 'file'; files?: FileList } | { type?: InputType; files?: undefined })
|
||||
>;
|
||||
|
||||
let {
|
||||
@@ -20,11 +20,11 @@
|
||||
}: Props = $props();
|
||||
</script>
|
||||
|
||||
{#if type === "file"}
|
||||
{#if type === 'file'}
|
||||
<input
|
||||
bind:this={ref}
|
||||
class={cn(
|
||||
"border-input placeholder:text-muted-foreground focus-visible:ring-ring flex h-9 w-full rounded-md border bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:outline-none focus-visible:ring-1 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
'flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
|
||||
className
|
||||
)}
|
||||
type="file"
|
||||
@@ -36,7 +36,7 @@
|
||||
<input
|
||||
bind:this={ref}
|
||||
class={cn(
|
||||
"border-input placeholder:text-muted-foreground focus-visible:ring-ring flex h-9 w-full rounded-md border bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:outline-none focus-visible:ring-1 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
'flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
|
||||
className
|
||||
)}
|
||||
{type}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Root from "./label.svelte";
|
||||
import Root from './label.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
//
|
||||
Root as Label,
|
||||
Root as Label
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import {Label as LabelPrimitive} from "bits-ui";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import {Label as LabelPrimitive} from 'bits-ui';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -13,7 +13,7 @@
|
||||
{...restProps}
|
||||
bind:ref
|
||||
class={cn(
|
||||
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
|
||||
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
|
||||
className
|
||||
)}
|
||||
/>
|
||||
|
||||
10
web/src/lib/components/ui/radio-group/index.ts
Normal file
10
web/src/lib/components/ui/radio-group/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import Root from './radio-group.svelte';
|
||||
import Item from './radio-group-item.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
Item,
|
||||
//
|
||||
Root as RadioGroup,
|
||||
Item as RadioGroupItem
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
<script lang="ts">
|
||||
import {RadioGroup as RadioGroupPrimitive, type WithoutChildrenOrChild} from 'bits-ui';
|
||||
import Circle from '@lucide/svelte/icons/circle';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: WithoutChildrenOrChild<RadioGroupPrimitive.ItemProps> & {
|
||||
value: string;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<RadioGroupPrimitive.Item
|
||||
bind:ref
|
||||
class={cn(
|
||||
'aspect-square size-4 rounded-full border border-primary text-primary shadow focus:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
{#snippet children({checked})}
|
||||
<div class="flex items-center justify-center">
|
||||
{#if checked}
|
||||
<Circle class="size-3.5 fill-primary"/>
|
||||
{/if}
|
||||
</div>
|
||||
{/snippet}
|
||||
</RadioGroupPrimitive.Item>
|
||||
13
web/src/lib/components/ui/radio-group/radio-group.svelte
Normal file
13
web/src/lib/components/ui/radio-group/radio-group.svelte
Normal file
@@ -0,0 +1,13 @@
|
||||
<script lang="ts">
|
||||
import {RadioGroup as RadioGroupPrimitive} from 'bits-ui';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
value = $bindable(),
|
||||
...restProps
|
||||
}: RadioGroupPrimitive.RootProps = $props();
|
||||
</script>
|
||||
|
||||
<RadioGroupPrimitive.Root bind:value class={cn('grid gap-2', className)} {...restProps} bind:ref/>
|
||||
34
web/src/lib/components/ui/select/index.ts
Normal file
34
web/src/lib/components/ui/select/index.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import {Select as SelectPrimitive} from 'bits-ui';
|
||||
|
||||
import GroupHeading from './select-group-heading.svelte';
|
||||
import Item from './select-item.svelte';
|
||||
import Content from './select-content.svelte';
|
||||
import Trigger from './select-trigger.svelte';
|
||||
import Separator from './select-separator.svelte';
|
||||
import ScrollDownButton from './select-scroll-down-button.svelte';
|
||||
import ScrollUpButton from './select-scroll-up-button.svelte';
|
||||
|
||||
const Root = SelectPrimitive.Root;
|
||||
const Group = SelectPrimitive.Group;
|
||||
|
||||
export {
|
||||
Root,
|
||||
Item,
|
||||
Group,
|
||||
GroupHeading,
|
||||
Content,
|
||||
Trigger,
|
||||
Separator,
|
||||
ScrollDownButton,
|
||||
ScrollUpButton,
|
||||
//
|
||||
Root as Select,
|
||||
Item as SelectItem,
|
||||
Group as SelectGroup,
|
||||
GroupHeading as SelectGroupHeading,
|
||||
Content as SelectContent,
|
||||
Trigger as SelectTrigger,
|
||||
Separator as SelectSeparator,
|
||||
ScrollDownButton as SelectScrollDownButton,
|
||||
ScrollUpButton as SelectScrollUpButton
|
||||
};
|
||||
38
web/src/lib/components/ui/select/select-content.svelte
Normal file
38
web/src/lib/components/ui/select/select-content.svelte
Normal file
@@ -0,0 +1,38 @@
|
||||
<script lang="ts">
|
||||
import {Select as SelectPrimitive, type WithoutChild} from 'bits-ui';
|
||||
import * as Select from './index.js';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
sideOffset = 4,
|
||||
portalProps,
|
||||
children,
|
||||
...restProps
|
||||
}: WithoutChild<SelectPrimitive.ContentProps> & {
|
||||
portalProps?: SelectPrimitive.PortalProps;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<SelectPrimitive.Portal {...portalProps}>
|
||||
<SelectPrimitive.Content
|
||||
bind:ref
|
||||
{sideOffset}
|
||||
class={cn(
|
||||
'relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1 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',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
<Select.ScrollUpButton/>
|
||||
<SelectPrimitive.Viewport
|
||||
class={cn(
|
||||
'h-[var(--bits-select-anchor-height)] w-full min-w-[var(--bits-select-anchor-width)] p-1'
|
||||
)}
|
||||
>
|
||||
{@render children?.()}
|
||||
</SelectPrimitive.Viewport>
|
||||
<Select.ScrollDownButton/>
|
||||
</SelectPrimitive.Content>
|
||||
</SelectPrimitive.Portal>
|
||||
16
web/src/lib/components/ui/select/select-group-heading.svelte
Normal file
16
web/src/lib/components/ui/select/select-group-heading.svelte
Normal file
@@ -0,0 +1,16 @@
|
||||
<script lang="ts">
|
||||
import {Select as SelectPrimitive} from 'bits-ui';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: SelectPrimitive.GroupHeadingProps = $props();
|
||||
</script>
|
||||
|
||||
<SelectPrimitive.GroupHeading
|
||||
bind:ref
|
||||
class={cn('px-2 py-1.5 text-sm font-semibold', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
37
web/src/lib/components/ui/select/select-item.svelte
Normal file
37
web/src/lib/components/ui/select/select-item.svelte
Normal file
@@ -0,0 +1,37 @@
|
||||
<script lang="ts">
|
||||
import {Select as SelectPrimitive, type WithoutChild} from 'bits-ui';
|
||||
import Check from '@lucide/svelte/icons/check';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
value,
|
||||
label,
|
||||
children: childrenProp,
|
||||
...restProps
|
||||
}: WithoutChild<SelectPrimitive.ItemProps> = $props();
|
||||
</script>
|
||||
|
||||
<SelectPrimitive.Item
|
||||
bind:ref
|
||||
{value}
|
||||
class={cn(
|
||||
'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:opacity-50',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
{#snippet children({selected, highlighted})}
|
||||
<span class="absolute right-2 flex size-3.5 items-center justify-center">
|
||||
{#if selected}
|
||||
<Check class="size-4"/>
|
||||
{/if}
|
||||
</span>
|
||||
{#if childrenProp}
|
||||
{@render childrenProp({selected, highlighted})}
|
||||
{:else}
|
||||
{label || value}
|
||||
{/if}
|
||||
{/snippet}
|
||||
</SelectPrimitive.Item>
|
||||
@@ -0,0 +1,19 @@
|
||||
<script lang="ts">
|
||||
import ChevronDown from '@lucide/svelte/icons/chevron-down';
|
||||
import {Select as SelectPrimitive, type WithoutChildrenOrChild} from 'bits-ui';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: WithoutChildrenOrChild<SelectPrimitive.ScrollDownButtonProps> = $props();
|
||||
</script>
|
||||
|
||||
<SelectPrimitive.ScrollDownButton
|
||||
bind:ref
|
||||
class={cn('flex cursor-default items-center justify-center py-1', className)}
|
||||
{...restProps}
|
||||
>
|
||||
<ChevronDown class="size-4"/>
|
||||
</SelectPrimitive.ScrollDownButton>
|
||||
@@ -0,0 +1,19 @@
|
||||
<script lang="ts">
|
||||
import ChevronUp from '@lucide/svelte/icons/chevron-up';
|
||||
import {Select as SelectPrimitive, type WithoutChildrenOrChild} from 'bits-ui';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: WithoutChildrenOrChild<SelectPrimitive.ScrollDownButtonProps> = $props();
|
||||
</script>
|
||||
|
||||
<SelectPrimitive.ScrollUpButton
|
||||
bind:ref
|
||||
class={cn('flex cursor-default items-center justify-center py-1', className)}
|
||||
{...restProps}
|
||||
>
|
||||
<ChevronUp class="size-4"/>
|
||||
</SelectPrimitive.ScrollUpButton>
|
||||
13
web/src/lib/components/ui/select/select-separator.svelte
Normal file
13
web/src/lib/components/ui/select/select-separator.svelte
Normal file
@@ -0,0 +1,13 @@
|
||||
<script lang="ts">
|
||||
import type {Separator as SeparatorPrimitive} from 'bits-ui';
|
||||
import {Separator} from '$lib/components/ui/separator/index.js';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: SeparatorPrimitive.RootProps = $props();
|
||||
</script>
|
||||
|
||||
<Separator bind:ref class={cn('-mx-1 my-1 h-px bg-muted', className)} {...restProps}/>
|
||||
24
web/src/lib/components/ui/select/select-trigger.svelte
Normal file
24
web/src/lib/components/ui/select/select-trigger.svelte
Normal file
@@ -0,0 +1,24 @@
|
||||
<script lang="ts">
|
||||
import {Select as SelectPrimitive, type WithoutChild} from 'bits-ui';
|
||||
import ChevronDown from '@lucide/svelte/icons/chevron-down';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithoutChild<SelectPrimitive.TriggerProps> = $props();
|
||||
</script>
|
||||
|
||||
<SelectPrimitive.Trigger
|
||||
bind:ref
|
||||
class={cn(
|
||||
'flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[placeholder]:text-muted-foreground [&>span]:line-clamp-1',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
<ChevronDown class="size-4 opacity-50"/>
|
||||
</SelectPrimitive.Trigger>
|
||||
@@ -1,7 +1,7 @@
|
||||
import Root from "./separator.svelte";
|
||||
import Root from './separator.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
//
|
||||
Root as Separator,
|
||||
Root as Separator
|
||||
};
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
<script lang="ts">
|
||||
import {Separator as SeparatorPrimitive} from "bits-ui";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import {Separator as SeparatorPrimitive} from 'bits-ui';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
orientation = "horizontal",
|
||||
orientation = 'horizontal',
|
||||
...restProps
|
||||
}: SeparatorPrimitive.RootProps = $props();
|
||||
</script>
|
||||
|
||||
<SeparatorPrimitive.Root
|
||||
{...restProps}
|
||||
bind:ref
|
||||
class={cn(
|
||||
"bg-border shrink-0",
|
||||
orientation === "horizontal" ? "h-[1px] w-full" : "min-h-full w-[1px]",
|
||||
'shrink-0 bg-border',
|
||||
orientation === 'horizontal' ? 'h-[1px] w-full' : 'min-h-full w-[1px]',
|
||||
className
|
||||
)}
|
||||
{orientation}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import {Dialog as SheetPrimitive} from "bits-ui";
|
||||
import {Dialog as SheetPrimitive} from 'bits-ui';
|
||||
|
||||
import Overlay from "./sheet-overlay.svelte";
|
||||
import Content from "./sheet-content.svelte";
|
||||
import Header from "./sheet-header.svelte";
|
||||
import Footer from "./sheet-footer.svelte";
|
||||
import Title from "./sheet-title.svelte";
|
||||
import Description from "./sheet-description.svelte";
|
||||
import Overlay from './sheet-overlay.svelte';
|
||||
import Content from './sheet-content.svelte';
|
||||
import Header from './sheet-header.svelte';
|
||||
import Footer from './sheet-footer.svelte';
|
||||
import Title from './sheet-title.svelte';
|
||||
import Description from './sheet-description.svelte';
|
||||
|
||||
const Root = SheetPrimitive.Root;
|
||||
const Close = SheetPrimitive.Close;
|
||||
@@ -33,5 +33,5 @@ export {
|
||||
Header as SheetHeader,
|
||||
Footer as SheetFooter,
|
||||
Title as SheetTitle,
|
||||
Description as SheetDescription,
|
||||
Description as SheetDescription
|
||||
};
|
||||
|
||||
@@ -1,36 +1,38 @@
|
||||
<script lang="ts" module>
|
||||
import {tv, type VariantProps} from "tailwind-variants";
|
||||
import {type VariantProps, tv} from 'tailwind-variants';
|
||||
|
||||
export const sheetVariants = tv({
|
||||
base: "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 gap-4 p-6 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
|
||||
base: 'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 gap-4 p-6 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500',
|
||||
variants: {
|
||||
side: {
|
||||
top: "data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 border-b",
|
||||
bottom: "data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 border-t",
|
||||
left: "data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
|
||||
right: "data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
|
||||
},
|
||||
top: 'data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 border-b',
|
||||
bottom:
|
||||
'data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 border-t',
|
||||
left: 'data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm',
|
||||
right:
|
||||
'data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm'
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
side: "right",
|
||||
},
|
||||
side: 'right'
|
||||
}
|
||||
});
|
||||
|
||||
export type Side = VariantProps<typeof sheetVariants>["side"];
|
||||
export type Side = VariantProps<typeof sheetVariants>['side'];
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import {Dialog as SheetPrimitive, type WithoutChildrenOrChild} from "bits-ui";
|
||||
import X from "@lucide/svelte/icons/x";
|
||||
import type {Snippet} from "svelte";
|
||||
import SheetOverlay from "./sheet-overlay.svelte";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import {Dialog as SheetPrimitive, type WithoutChildrenOrChild} from 'bits-ui';
|
||||
import X from '@lucide/svelte/icons/x';
|
||||
import type {Snippet} from 'svelte';
|
||||
import SheetOverlay from './sheet-overlay.svelte';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
portalProps,
|
||||
side = "right",
|
||||
side = 'right',
|
||||
children,
|
||||
...restProps
|
||||
}: WithoutChildrenOrChild<SheetPrimitive.ContentProps> & {
|
||||
@@ -42,10 +44,10 @@
|
||||
|
||||
<SheetPrimitive.Portal {...portalProps}>
|
||||
<SheetOverlay/>
|
||||
<SheetPrimitive.Content {...restProps} bind:ref class={cn(sheetVariants({ side }), className)}>
|
||||
<SheetPrimitive.Content bind:ref class={cn(sheetVariants({ side }), className)} {...restProps}>
|
||||
{@render children?.()}
|
||||
<SheetPrimitive.Close
|
||||
class="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:pointer-events-none"
|
||||
class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary"
|
||||
>
|
||||
<X class="size-4"/>
|
||||
<span class="sr-only">Close</span>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import {Dialog as SheetPrimitive} from "bits-ui";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import {Dialog as SheetPrimitive} from 'bits-ui';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -10,7 +10,7 @@
|
||||
</script>
|
||||
|
||||
<SheetPrimitive.Description
|
||||
{...restProps}
|
||||
bind:ref
|
||||
class={cn("text-muted-foreground text-sm", className)}
|
||||
class={cn('text-sm text-muted-foreground', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import type {HTMLAttributes} from "svelte/elements";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -12,9 +12,9 @@
|
||||
</script>
|
||||
|
||||
<div
|
||||
{...restProps}
|
||||
bind:this={ref}
|
||||
class={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)}
|
||||
class={cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type {HTMLAttributes} from "svelte/elements";
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -12,9 +12,9 @@
|
||||
</script>
|
||||
|
||||
<div
|
||||
{...restProps}
|
||||
bind:this={ref}
|
||||
class={cn("flex flex-col space-y-2 text-center sm:text-left", className)}
|
||||
class={cn('flex flex-col space-y-2 text-center sm:text-left', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import {Dialog as SheetPrimitive} from "bits-ui";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import {Dialog as SheetPrimitive} from 'bits-ui';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -10,10 +10,10 @@
|
||||
</script>
|
||||
|
||||
<SheetPrimitive.Overlay
|
||||
{...restProps}
|
||||
bind:ref
|
||||
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/80",
|
||||
'fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import {Dialog as SheetPrimitive} from "bits-ui";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import {Dialog as SheetPrimitive} from 'bits-ui';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -10,7 +10,7 @@
|
||||
</script>
|
||||
|
||||
<SheetPrimitive.Title
|
||||
{...restProps}
|
||||
bind:ref
|
||||
class={cn("text-foreground text-lg font-semibold", className)}
|
||||
class={cn('text-lg font-semibold text-foreground', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export const SIDEBAR_COOKIE_NAME = "sidebar:state";
|
||||
export const SIDEBAR_COOKIE_NAME = 'sidebar:state';
|
||||
export const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
|
||||
export const SIDEBAR_WIDTH = "16rem";
|
||||
export const SIDEBAR_WIDTH_MOBILE = "18rem";
|
||||
export const SIDEBAR_WIDTH_ICON = "3rem";
|
||||
export const SIDEBAR_KEYBOARD_SHORTCUT = "b";
|
||||
export const SIDEBAR_WIDTH = '16rem';
|
||||
export const SIDEBAR_WIDTH_MOBILE = '18rem';
|
||||
export const SIDEBAR_WIDTH_ICON = '3rem';
|
||||
export const SIDEBAR_KEYBOARD_SHORTCUT = 'b';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {IsMobile} from "$lib/hooks/is-mobile.svelte.js";
|
||||
import {getContext, setContext} from "svelte";
|
||||
import {SIDEBAR_KEYBOARD_SHORTCUT} from "./constants.js";
|
||||
import {IsMobile} from '$lib/hooks/is-mobile.svelte.js';
|
||||
import {getContext, setContext} from 'svelte';
|
||||
import {SIDEBAR_KEYBOARD_SHORTCUT} from './constants.js';
|
||||
|
||||
type Getter<T> = () => T;
|
||||
|
||||
@@ -24,9 +24,9 @@ class SidebarState {
|
||||
readonly props: SidebarStateProps;
|
||||
open = $derived.by(() => this.props.open());
|
||||
openMobile = $state(false);
|
||||
setOpen: SidebarStateProps["setOpen"];
|
||||
state = $derived.by(() => (this.open ? "expanded" : "collapsed"));
|
||||
setOpen: SidebarStateProps['setOpen'];
|
||||
#isMobile: IsMobile;
|
||||
state = $derived.by(() => (this.open ? 'expanded' : 'collapsed'));
|
||||
|
||||
constructor(props: SidebarStateProps) {
|
||||
this.setOpen = props.setOpen;
|
||||
@@ -53,13 +53,11 @@ class SidebarState {
|
||||
};
|
||||
|
||||
toggle = () => {
|
||||
return this.#isMobile.current
|
||||
? (this.openMobile = !this.openMobile)
|
||||
: this.setOpen(!this.open);
|
||||
return this.#isMobile.current ? (this.openMobile = !this.openMobile) : this.setOpen(!this.open);
|
||||
};
|
||||
}
|
||||
|
||||
const SYMBOL_KEY = "scn-sidebar";
|
||||
const SYMBOL_KEY = 'scn-sidebar';
|
||||
|
||||
/**
|
||||
* Instantiates a new `SidebarState` instance and sets it in the context.
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
import {useSidebar} from "./context.svelte.js";
|
||||
import Content from "./sidebar-content.svelte";
|
||||
import Footer from "./sidebar-footer.svelte";
|
||||
import GroupAction from "./sidebar-group-action.svelte";
|
||||
import GroupContent from "./sidebar-group-content.svelte";
|
||||
import GroupLabel from "./sidebar-group-label.svelte";
|
||||
import Group from "./sidebar-group.svelte";
|
||||
import Header from "./sidebar-header.svelte";
|
||||
import Input from "./sidebar-input.svelte";
|
||||
import Inset from "./sidebar-inset.svelte";
|
||||
import MenuAction from "./sidebar-menu-action.svelte";
|
||||
import MenuBadge from "./sidebar-menu-badge.svelte";
|
||||
import MenuButton from "./sidebar-menu-button.svelte";
|
||||
import MenuItem from "./sidebar-menu-item.svelte";
|
||||
import MenuSkeleton from "./sidebar-menu-skeleton.svelte";
|
||||
import MenuSubButton from "./sidebar-menu-sub-button.svelte";
|
||||
import MenuSubItem from "./sidebar-menu-sub-item.svelte";
|
||||
import MenuSub from "./sidebar-menu-sub.svelte";
|
||||
import Menu from "./sidebar-menu.svelte";
|
||||
import Provider from "./sidebar-provider.svelte";
|
||||
import Rail from "./sidebar-rail.svelte";
|
||||
import Separator from "./sidebar-separator.svelte";
|
||||
import Trigger from "./sidebar-trigger.svelte";
|
||||
import Root from "./sidebar.svelte";
|
||||
import {useSidebar} from './context.svelte.js';
|
||||
import Content from './sidebar-content.svelte';
|
||||
import Footer from './sidebar-footer.svelte';
|
||||
import GroupAction from './sidebar-group-action.svelte';
|
||||
import GroupContent from './sidebar-group-content.svelte';
|
||||
import GroupLabel from './sidebar-group-label.svelte';
|
||||
import Group from './sidebar-group.svelte';
|
||||
import Header from './sidebar-header.svelte';
|
||||
import Input from './sidebar-input.svelte';
|
||||
import Inset from './sidebar-inset.svelte';
|
||||
import MenuAction from './sidebar-menu-action.svelte';
|
||||
import MenuBadge from './sidebar-menu-badge.svelte';
|
||||
import MenuButton from './sidebar-menu-button.svelte';
|
||||
import MenuItem from './sidebar-menu-item.svelte';
|
||||
import MenuSkeleton from './sidebar-menu-skeleton.svelte';
|
||||
import MenuSubButton from './sidebar-menu-sub-button.svelte';
|
||||
import MenuSubItem from './sidebar-menu-sub-item.svelte';
|
||||
import MenuSub from './sidebar-menu-sub.svelte';
|
||||
import Menu from './sidebar-menu.svelte';
|
||||
import Provider from './sidebar-provider.svelte';
|
||||
import Rail from './sidebar-rail.svelte';
|
||||
import Separator from './sidebar-separator.svelte';
|
||||
import Trigger from './sidebar-trigger.svelte';
|
||||
import Root from './sidebar.svelte';
|
||||
|
||||
export {
|
||||
Content,
|
||||
@@ -71,5 +71,5 @@ export {
|
||||
Separator as SidebarSeparator,
|
||||
Trigger as SidebarTrigger,
|
||||
Trigger,
|
||||
useSidebar,
|
||||
useSidebar
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type {HTMLAttributes} from "svelte/elements";
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -12,13 +12,13 @@
|
||||
</script>
|
||||
|
||||
<div
|
||||
{...restProps}
|
||||
bind:this={ref}
|
||||
data-sidebar="content"
|
||||
class={cn(
|
||||
"flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden",
|
||||
'flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden',
|
||||
className
|
||||
)}
|
||||
data-sidebar="content"
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type {HTMLAttributes} from "svelte/elements";
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -12,10 +12,10 @@
|
||||
</script>
|
||||
|
||||
<div
|
||||
{...restProps}
|
||||
bind:this={ref}
|
||||
class={cn("flex flex-col gap-2 p-2", className)}
|
||||
data-sidebar="footer"
|
||||
class={cn('flex flex-col gap-2 p-2', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import type {Snippet} from "svelte";
|
||||
import type {HTMLButtonAttributes} from "svelte/elements";
|
||||
import {cn} from '$lib/utils.js';
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import type {Snippet} from 'svelte';
|
||||
import type {HTMLButtonAttributes} from 'svelte/elements';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -16,14 +16,14 @@
|
||||
|
||||
const propObj = $derived({
|
||||
class: cn(
|
||||
"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute right-3 top-3.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-none transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
|
||||
'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute right-3 top-3.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-none transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
|
||||
// Increases the hit area of the button on mobile.
|
||||
"after:absolute after:-inset-2 after:md:hidden",
|
||||
"group-data-[collapsible=icon]:hidden",
|
||||
'after:absolute after:-inset-2 after:md:hidden',
|
||||
'group-data-[collapsible=icon]:hidden',
|
||||
className
|
||||
),
|
||||
"data-sidebar": "group-action",
|
||||
...restProps,
|
||||
'data-sidebar': 'group-action',
|
||||
...restProps
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import type {HTMLAttributes} from "svelte/elements";
|
||||
import {cn} from '$lib/utils.js';
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -12,10 +12,10 @@
|
||||
</script>
|
||||
|
||||
<div
|
||||
{...restProps}
|
||||
bind:this={ref}
|
||||
class={cn("w-full text-sm", className)}
|
||||
data-sidebar="group-content"
|
||||
class={cn('w-full text-sm', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import type {Snippet} from "svelte";
|
||||
import type {HTMLAttributes} from "svelte/elements";
|
||||
import {cn} from '$lib/utils.js';
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import type {Snippet} from 'svelte';
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -16,12 +16,12 @@
|
||||
|
||||
const mergedProps = $derived({
|
||||
class: cn(
|
||||
"text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-none transition-[margin,opa] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
|
||||
"group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0",
|
||||
'text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-none transition-[margin,opa] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
|
||||
'group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0',
|
||||
className
|
||||
),
|
||||
"data-sidebar": "group-label",
|
||||
...restProps,
|
||||
'data-sidebar': 'group-label',
|
||||
...restProps
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type {HTMLAttributes} from "svelte/elements";
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -12,10 +12,10 @@
|
||||
</script>
|
||||
|
||||
<div
|
||||
{...restProps}
|
||||
bind:this={ref}
|
||||
class={cn("relative flex w-full min-w-0 flex-col p-2", className)}
|
||||
data-sidebar="group"
|
||||
class={cn('relative flex w-full min-w-0 flex-col p-2', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type {HTMLAttributes} from "svelte/elements";
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -12,10 +12,10 @@
|
||||
</script>
|
||||
|
||||
<div
|
||||
{...restProps}
|
||||
bind:this={ref}
|
||||
class={cn("flex flex-col gap-2 p-2", className)}
|
||||
data-sidebar="header"
|
||||
class={cn('flex flex-col gap-2 p-2', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
<script lang="ts">
|
||||
import {Input} from "$lib/components/ui/input/index.js";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {ComponentProps} from "svelte";
|
||||
import {Input} from '$lib/components/ui/input/index.js';
|
||||
import {cn} from '$lib/utils.js';
|
||||
import type {ComponentProps} from 'svelte';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
value = $bindable(""),
|
||||
value = $bindable(''),
|
||||
class: className,
|
||||
...restProps
|
||||
}: ComponentProps<typeof Input> = $props();
|
||||
</script>
|
||||
|
||||
<Input
|
||||
{...restProps}
|
||||
bind:ref
|
||||
bind:value
|
||||
data-sidebar="input"
|
||||
class={cn(
|
||||
"bg-background focus-visible:ring-sidebar-ring h-8 w-full shadow-none focus-visible:ring-2",
|
||||
'h-8 w-full bg-background shadow-none focus-visible:ring-2 focus-visible:ring-sidebar-ring',
|
||||
className
|
||||
)}
|
||||
data-sidebar="input"
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import type {HTMLAttributes} from "svelte/elements";
|
||||
import {cn} from '$lib/utils.js';
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -12,13 +12,13 @@
|
||||
</script>
|
||||
|
||||
<main
|
||||
{...restProps}
|
||||
bind:this={ref}
|
||||
class={cn(
|
||||
"bg-background relative flex min-h-svh flex-1 flex-col",
|
||||
"peer-data-[variant=inset]:min-h-[calc(100svh-theme(spacing.4))] md:peer-data-[variant=inset]:m-2 md:peer-data-[state=collapsed]:peer-data-[variant=inset]:ml-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow",
|
||||
'relative flex min-h-svh flex-1 flex-col bg-background',
|
||||
'peer-data-[variant=inset]:min-h-[calc(100svh-theme(spacing.4))] md:peer-data-[variant=inset]:m-2 md:peer-data-[state=collapsed]:peer-data-[variant=inset]:ml-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</main>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import type {Snippet} from "svelte";
|
||||
import type {HTMLButtonAttributes} from "svelte/elements";
|
||||
import {cn} from '$lib/utils.js';
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import type {Snippet} from 'svelte';
|
||||
import type {HTMLButtonAttributes} from 'svelte/elements';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -18,19 +18,19 @@
|
||||
|
||||
const mergedProps = $derived({
|
||||
class: cn(
|
||||
"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground absolute right-1 top-1.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-none transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
|
||||
'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground absolute right-1 top-1.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-none transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',
|
||||
// Increases the hit area of the button on mobile.
|
||||
"after:absolute after:-inset-2 after:md:hidden",
|
||||
"peer-data-[size=sm]/menu-button:top-1",
|
||||
"peer-data-[size=default]/menu-button:top-1.5",
|
||||
"peer-data-[size=lg]/menu-button:top-2.5",
|
||||
"group-data-[collapsible=icon]:hidden",
|
||||
'after:absolute after:-inset-2 after:md:hidden',
|
||||
'peer-data-[size=sm]/menu-button:top-1',
|
||||
'peer-data-[size=default]/menu-button:top-1.5',
|
||||
'peer-data-[size=lg]/menu-button:top-2.5',
|
||||
'group-data-[collapsible=icon]:hidden',
|
||||
showOnHover &&
|
||||
"peer-data-[active=true]/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 md:opacity-0",
|
||||
'peer-data-[active=true]/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 md:opacity-0',
|
||||
className
|
||||
),
|
||||
"data-sidebar": "menu-action",
|
||||
...restProps,
|
||||
'data-sidebar': 'menu-action',
|
||||
...restProps
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import type {HTMLAttributes} from "svelte/elements";
|
||||
import {cn} from '$lib/utils.js';
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -12,18 +12,18 @@
|
||||
</script>
|
||||
|
||||
<div
|
||||
{...restProps}
|
||||
bind:this={ref}
|
||||
data-sidebar="menu-badge"
|
||||
class={cn(
|
||||
"text-sidebar-foreground pointer-events-none absolute right-1 flex h-5 min-w-5 select-none items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums",
|
||||
"peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground",
|
||||
"peer-data-[size=sm]/menu-button:top-1",
|
||||
"peer-data-[size=default]/menu-button:top-1.5",
|
||||
"peer-data-[size=lg]/menu-button:top-2.5",
|
||||
"group-data-[collapsible=icon]:hidden",
|
||||
'pointer-events-none absolute right-1 flex h-5 min-w-5 select-none items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums text-sidebar-foreground',
|
||||
'peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground',
|
||||
'peer-data-[size=sm]/menu-button:top-1',
|
||||
'peer-data-[size=default]/menu-button:top-1.5',
|
||||
'peer-data-[size=lg]/menu-button:top-2.5',
|
||||
'group-data-[collapsible=icon]:hidden',
|
||||
className
|
||||
)}
|
||||
data-sidebar="menu-badge"
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -1,47 +1,45 @@
|
||||
<script lang="ts" module>
|
||||
import {tv, type VariantProps} from "tailwind-variants";
|
||||
import {type VariantProps, tv} from 'tailwind-variants';
|
||||
|
||||
export const sidebarMenuButtonVariants = tv({
|
||||
base: "peer/menu-button ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none transition-[width,height,padding] focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:font-medium group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
|
||||
base: 'peer/menu-button ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none transition-[width,height,padding] focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:font-medium group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0',
|
||||
variants: {
|
||||
variant: {
|
||||
default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
||||
default: 'hover:bg-sidebar-accent hover:text-sidebar-accent-foreground',
|
||||
outline:
|
||||
"bg-background hover:bg-sidebar-accent hover:text-sidebar-accent-foreground shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]",
|
||||
'bg-background hover:bg-sidebar-accent hover:text-sidebar-accent-foreground shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]'
|
||||
},
|
||||
size: {
|
||||
default: "h-8 text-sm",
|
||||
sm: "h-7 text-xs",
|
||||
lg: "h-12 text-sm group-data-[collapsible=icon]:!p-0",
|
||||
},
|
||||
default: 'h-8 text-sm',
|
||||
sm: 'h-7 text-xs',
|
||||
lg: 'h-12 text-sm group-data-[collapsible=icon]:!p-0'
|
||||
}
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
variant: 'default',
|
||||
size: 'default'
|
||||
}
|
||||
});
|
||||
|
||||
export type SidebarMenuButtonVariant = VariantProps<
|
||||
typeof sidebarMenuButtonVariants
|
||||
>["variant"];
|
||||
export type SidebarMenuButtonSize = VariantProps<typeof sidebarMenuButtonVariants>["size"];
|
||||
export type SidebarMenuButtonVariant = VariantProps<typeof sidebarMenuButtonVariants>['variant'];
|
||||
export type SidebarMenuButtonSize = VariantProps<typeof sidebarMenuButtonVariants>['size'];
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import * as Tooltip from "$lib/components/ui/tooltip/index.js";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import {mergeProps, type WithElementRef, type WithoutChildrenOrChild} from "bits-ui";
|
||||
import type {ComponentProps, Snippet} from "svelte";
|
||||
import type {HTMLAttributes} from "svelte/elements";
|
||||
import {useSidebar} from "./context.svelte.js";
|
||||
import * as Tooltip from '$lib/components/ui/tooltip/index.js';
|
||||
import {cn} from '$lib/utils.js';
|
||||
import {type WithElementRef, type WithoutChildrenOrChild, mergeProps} from 'bits-ui';
|
||||
import type {ComponentProps, Snippet} from 'svelte';
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
import {useSidebar} from './context.svelte.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
children,
|
||||
child,
|
||||
variant = "default",
|
||||
size = "default",
|
||||
variant = 'default',
|
||||
size = 'default',
|
||||
isActive = false,
|
||||
tooltipContent,
|
||||
tooltipContentProps,
|
||||
@@ -59,10 +57,10 @@
|
||||
|
||||
const buttonProps = $derived({
|
||||
class: cn(sidebarMenuButtonVariants({variant, size}), className),
|
||||
"data-sidebar": "menu-button",
|
||||
"data-size": size,
|
||||
"data-active": isActive,
|
||||
...restProps,
|
||||
'data-sidebar': 'menu-button',
|
||||
'data-size': size,
|
||||
'data-active': isActive,
|
||||
...restProps
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -89,7 +87,7 @@
|
||||
<Tooltip.Content
|
||||
side="right"
|
||||
align="center"
|
||||
hidden={sidebar.state !== "collapsed" || sidebar.isMobile}
|
||||
hidden={sidebar.state !== 'collapsed' || sidebar.isMobile}
|
||||
children={tooltipContent}
|
||||
{...tooltipContentProps}
|
||||
/>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import type {HTMLAttributes} from "svelte/elements";
|
||||
import {cn} from '$lib/utils.js';
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -12,10 +12,10 @@
|
||||
</script>
|
||||
|
||||
<li
|
||||
{...restProps}
|
||||
bind:this={ref}
|
||||
class={cn("group/menu-item relative", className)}
|
||||
data-sidebar="menu-item"
|
||||
class={cn('group/menu-item relative', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</li>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import {Skeleton} from "$lib/components/ui/skeleton/index.js";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import type {HTMLAttributes} from "svelte/elements";
|
||||
import {Skeleton} from '$lib/components/ui/skeleton/index.js';
|
||||
import {cn} from '$lib/utils.js';
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -19,10 +19,10 @@
|
||||
</script>
|
||||
|
||||
<div
|
||||
{...restProps}
|
||||
bind:this={ref}
|
||||
class={cn("flex h-8 items-center gap-2 rounded-md px-2", className)}
|
||||
data-sidebar="menu-skeleton"
|
||||
class={cn('flex h-8 items-center gap-2 rounded-md px-2', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{#if showIcon}
|
||||
<Skeleton class="size-4 rounded-md" data-sidebar="menu-skeleton-icon"/>
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
<script lang="ts">
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import type {Snippet} from "svelte";
|
||||
import type {HTMLAnchorAttributes} from "svelte/elements";
|
||||
import {cn} from '$lib/utils.js';
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import type {Snippet} from 'svelte';
|
||||
import type {HTMLAnchorAttributes} from 'svelte/elements';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
children,
|
||||
child,
|
||||
class: className,
|
||||
size = "md",
|
||||
size = 'md',
|
||||
isActive,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAnchorAttributes> & {
|
||||
child?: Snippet<[{ props: Record<string, unknown> }]>;
|
||||
size?: "sm" | "md";
|
||||
size?: 'sm' | 'md';
|
||||
isActive?: boolean;
|
||||
} = $props();
|
||||
|
||||
const mergedProps = $derived({
|
||||
class: cn(
|
||||
"text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground [&>svg]:text-sidebar-accent-foreground flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 outline-none focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
|
||||
"data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground",
|
||||
size === "sm" && "text-xs",
|
||||
size === "md" && "text-sm",
|
||||
"group-data-[collapsible=icon]:hidden",
|
||||
'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground [&>svg]:text-sidebar-accent-foreground flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 outline-none focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0',
|
||||
'data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground',
|
||||
size === 'sm' && 'text-xs',
|
||||
size === 'md' && 'text-sm',
|
||||
'group-data-[collapsible=icon]:hidden',
|
||||
className
|
||||
),
|
||||
"data-sidebar": "menu-sub-button",
|
||||
"data-size": size,
|
||||
"data-active": isActive,
|
||||
...restProps,
|
||||
'data-sidebar': 'menu-sub-button',
|
||||
'data-size': size,
|
||||
'data-active': isActive,
|
||||
...restProps
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import type {HTMLAttributes} from "svelte/elements";
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -9,6 +9,6 @@
|
||||
}: WithElementRef<HTMLAttributes<HTMLLIElement>> = $props();
|
||||
</script>
|
||||
|
||||
<li {...restProps} bind:this={ref} data-sidebar="menu-sub-item">
|
||||
<li bind:this={ref} data-sidebar="menu-sub-item" {...restProps}>
|
||||
{@render children?.()}
|
||||
</li>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import type {HTMLAttributes} from "svelte/elements";
|
||||
import {cn} from '$lib/utils.js';
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -12,14 +12,14 @@
|
||||
</script>
|
||||
|
||||
<ul
|
||||
{...restProps}
|
||||
bind:this={ref}
|
||||
data-sidebar="menu-sub"
|
||||
class={cn(
|
||||
"border-sidebar-border mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l px-2.5 py-0.5",
|
||||
"group-data-[collapsible=icon]:hidden",
|
||||
'mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l border-sidebar-border px-2.5 py-0.5',
|
||||
'group-data-[collapsible=icon]:hidden',
|
||||
className
|
||||
)}
|
||||
data-sidebar="menu-sub"
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</ul>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import type {HTMLAttributes} from "svelte/elements";
|
||||
import {cn} from '$lib/utils.js';
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -12,10 +12,10 @@
|
||||
</script>
|
||||
|
||||
<ul
|
||||
{...restProps}
|
||||
bind:this={ref}
|
||||
class={cn("flex w-full min-w-0 flex-col gap-1", className)}
|
||||
data-sidebar="menu"
|
||||
class={cn('flex w-full min-w-0 flex-col gap-1', className)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</ul>
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
<script lang="ts">
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import type {HTMLAttributes} from "svelte/elements";
|
||||
import {SIDEBAR_COOKIE_MAX_AGE, SIDEBAR_COOKIE_NAME, SIDEBAR_WIDTH, SIDEBAR_WIDTH_ICON,} from "./constants.js";
|
||||
import {setSidebar} from "./context.svelte.js";
|
||||
import * as Tooltip from '$lib/components/ui/tooltip/index.js';
|
||||
import {cn} from '$lib/utils.js';
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
import {
|
||||
SIDEBAR_COOKIE_MAX_AGE,
|
||||
SIDEBAR_COOKIE_NAME,
|
||||
SIDEBAR_WIDTH,
|
||||
SIDEBAR_WIDTH_ICON
|
||||
} from './constants.js';
|
||||
import {setSidebar} from './context.svelte.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -27,7 +33,7 @@
|
||||
|
||||
// This sets the cookie to keep the sidebar state.
|
||||
document.cookie = `${SIDEBAR_COOKIE_NAME}=${open}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -35,13 +41,13 @@
|
||||
|
||||
<Tooltip.Provider delayDuration={0}>
|
||||
<div
|
||||
{...restProps}
|
||||
bind:this={ref}
|
||||
style="--sidebar-width: {SIDEBAR_WIDTH}; --sidebar-width-icon: {SIDEBAR_WIDTH_ICON}; {style}"
|
||||
class={cn(
|
||||
"group/sidebar-wrapper has-[[data-variant=inset]]:bg-sidebar flex min-h-svh w-full",
|
||||
'group/sidebar-wrapper flex min-h-svh w-full has-[[data-variant=inset]]:bg-sidebar',
|
||||
className
|
||||
)}
|
||||
style="--sidebar-width: {SIDEBAR_WIDTH}; --sidebar-width-icon: {SIDEBAR_WIDTH_ICON}; {style}"
|
||||
bind:this={ref}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import type {HTMLAttributes} from "svelte/elements";
|
||||
import {useSidebar} from "./context.svelte.js";
|
||||
import {cn} from '$lib/utils.js';
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
import {useSidebar} from './context.svelte.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -15,22 +15,22 @@
|
||||
</script>
|
||||
|
||||
<button
|
||||
{...restProps}
|
||||
aria-label="Toggle Sidebar"
|
||||
bind:this={ref}
|
||||
data-sidebar="rail"
|
||||
aria-label="Toggle Sidebar"
|
||||
tabIndex={-1}
|
||||
onclick={() => sidebar.toggle()}
|
||||
title="Toggle Sidebar"
|
||||
class={cn(
|
||||
"hover:after:bg-sidebar-border absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] group-data-[side=left]:-right-4 group-data-[side=right]:left-0 sm:flex",
|
||||
"[[data-side=left]_&]:cursor-w-resize [[data-side=right]_&]:cursor-e-resize",
|
||||
"[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize",
|
||||
"group-data-[collapsible=offcanvas]:hover:bg-sidebar group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full",
|
||||
"[[data-side=left][data-collapsible=offcanvas]_&]:-right-2",
|
||||
"[[data-side=right][data-collapsible=offcanvas]_&]:-left-2",
|
||||
'absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] hover:after:bg-sidebar-border group-data-[side=left]:-right-4 group-data-[side=right]:left-0 sm:flex',
|
||||
'[[data-side=left]_&]:cursor-w-resize [[data-side=right]_&]:cursor-e-resize',
|
||||
'[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize',
|
||||
'group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full group-data-[collapsible=offcanvas]:hover:bg-sidebar',
|
||||
'[[data-side=left][data-collapsible=offcanvas]_&]:-right-2',
|
||||
'[[data-side=right][data-collapsible=offcanvas]_&]:-left-2',
|
||||
className
|
||||
)}
|
||||
data-sidebar="rail"
|
||||
onclick={() => sidebar.toggle()}
|
||||
tabIndex={-1}
|
||||
title="Toggle Sidebar"
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
</button>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import {Separator} from "$lib/components/ui/separator/index.js";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {ComponentProps} from "svelte";
|
||||
import {Separator} from '$lib/components/ui/separator/index.js';
|
||||
import {cn} from '$lib/utils.js';
|
||||
import type {ComponentProps} from 'svelte';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -11,8 +11,8 @@
|
||||
</script>
|
||||
|
||||
<Separator
|
||||
{...restProps}
|
||||
bind:ref
|
||||
class={cn("bg-sidebar-border mx-2 w-auto", className)}
|
||||
data-sidebar="separator"
|
||||
class={cn('mx-2 w-auto bg-sidebar-border', className)}
|
||||
{...restProps}
|
||||
/>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<script lang="ts">
|
||||
import {Button} from "$lib/components/ui/button/index.js";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {ComponentProps} from "svelte";
|
||||
import {useSidebar} from "./context.svelte.js";
|
||||
import {Button} from '$lib/components/ui/button/index.js';
|
||||
import {cn} from '$lib/utils.js';
|
||||
import PanelLeft from '@lucide/svelte/icons/panel-left';
|
||||
import type {ComponentProps} from 'svelte';
|
||||
import {useSidebar} from './context.svelte.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -17,16 +18,16 @@
|
||||
</script>
|
||||
|
||||
<Button
|
||||
{...restProps}
|
||||
class={cn("h-7 w-7", className)}
|
||||
data-sidebar="trigger"
|
||||
type="button"
|
||||
onclick={(e) => {
|
||||
onclick?.(e);
|
||||
sidebar.toggle();
|
||||
}}
|
||||
size="icon"
|
||||
type="button"
|
||||
data-sidebar="trigger"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
class={cn('h-7 w-7', className)}
|
||||
{...restProps}
|
||||
>
|
||||
<PanelLeft/>
|
||||
<span class="sr-only">Toggle Sidebar</span>
|
||||
|
||||
@@ -1,31 +1,32 @@
|
||||
<script lang="ts">
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {WithElementRef} from "bits-ui";
|
||||
import type {HTMLAttributes} from "svelte/elements";
|
||||
import {SIDEBAR_WIDTH_MOBILE} from "./constants.js";
|
||||
import {useSidebar} from "./context.svelte.js";
|
||||
import * as Sheet from '$lib/components/ui/sheet/index.js';
|
||||
import {cn} from '$lib/utils.js';
|
||||
import type {WithElementRef} from 'bits-ui';
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
import {SIDEBAR_WIDTH_MOBILE} from './constants.js';
|
||||
import {useSidebar} from './context.svelte.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
side = "left",
|
||||
variant = "sidebar",
|
||||
collapsible = "offcanvas",
|
||||
side = 'left',
|
||||
variant = 'sidebar',
|
||||
collapsible = 'offcanvas',
|
||||
class: className,
|
||||
children,
|
||||
...restProps
|
||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
|
||||
side?: "left" | "right";
|
||||
variant?: "sidebar" | "floating" | "inset";
|
||||
collapsible?: "offcanvas" | "icon" | "none";
|
||||
side?: 'left' | 'right';
|
||||
variant?: 'sidebar' | 'floating' | 'inset';
|
||||
collapsible?: 'offcanvas' | 'icon' | 'none';
|
||||
} = $props();
|
||||
|
||||
const sidebar = useSidebar();
|
||||
</script>
|
||||
|
||||
{#if collapsible === "none"}
|
||||
{#if collapsible === 'none'}
|
||||
<div
|
||||
class={cn(
|
||||
"bg-sidebar text-sidebar-foreground flex h-full w-[var(--sidebar-width)] flex-col",
|
||||
'flex h-full w-[var(--sidebar-width)] flex-col bg-sidebar text-sidebar-foreground',
|
||||
className
|
||||
)}
|
||||
bind:this={ref}
|
||||
@@ -34,14 +35,11 @@
|
||||
{@render children?.()}
|
||||
</div>
|
||||
{:else if sidebar.isMobile}
|
||||
<Sheet.Root
|
||||
bind:open={() => sidebar.openMobile, (v) => sidebar.setOpenMobile(v)}
|
||||
{...restProps}
|
||||
>
|
||||
<Sheet.Root bind:open={() => sidebar.openMobile, (v) => sidebar.setOpenMobile(v)} {...restProps}>
|
||||
<Sheet.Content
|
||||
data-sidebar="sidebar"
|
||||
data-mobile="true"
|
||||
class="bg-sidebar text-sidebar-foreground w-[var(--sidebar-width)] p-0 [&>button]:hidden"
|
||||
class="w-[var(--sidebar-width)] bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden"
|
||||
style="--sidebar-width: {SIDEBAR_WIDTH_MOBILE};"
|
||||
{side}
|
||||
>
|
||||
@@ -53,40 +51,40 @@
|
||||
{:else}
|
||||
<div
|
||||
bind:this={ref}
|
||||
class="text-sidebar-foreground group peer hidden md:block"
|
||||
class="group peer hidden text-sidebar-foreground md:block"
|
||||
data-state={sidebar.state}
|
||||
data-collapsible={sidebar.state === "collapsed" ? collapsible : ""}
|
||||
data-collapsible={sidebar.state === 'collapsed' ? collapsible : ''}
|
||||
data-variant={variant}
|
||||
data-side={side}
|
||||
>
|
||||
<!-- This is what handles the sidebar gap on desktop -->
|
||||
<div
|
||||
class={cn(
|
||||
"relative h-svh w-[var(--sidebar-width)] bg-transparent transition-[width] duration-200 ease-linear",
|
||||
"group-data-[collapsible=offcanvas]:w-0",
|
||||
"group-data-[side=right]:rotate-180",
|
||||
variant === "floating" || variant === "inset"
|
||||
? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]"
|
||||
: "group-data-[collapsible=icon]:w-[var(--sidebar-width-icon)]"
|
||||
'relative h-svh w-[var(--sidebar-width)] bg-transparent transition-[width] duration-200 ease-linear',
|
||||
'group-data-[collapsible=offcanvas]:w-0',
|
||||
'group-data-[side=right]:rotate-180',
|
||||
variant === 'floating' || variant === 'inset'
|
||||
? 'group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]'
|
||||
: 'group-data-[collapsible=icon]:w-[var(--sidebar-width-icon)]'
|
||||
)}
|
||||
></div>
|
||||
<div
|
||||
class={cn(
|
||||
"fixed inset-y-0 z-10 hidden h-svh w-[var(--sidebar-width)] transition-[left,right,width] duration-200 ease-linear md:flex",
|
||||
side === "left"
|
||||
? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]"
|
||||
: "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",
|
||||
'fixed inset-y-0 z-10 hidden h-svh w-[var(--sidebar-width)] transition-[left,right,width] duration-200 ease-linear md:flex',
|
||||
side === 'left'
|
||||
? 'left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]'
|
||||
: 'right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]',
|
||||
// Adjust the padding for floating and inset variants.
|
||||
variant === "floating" || variant === "inset"
|
||||
? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]"
|
||||
: "group-data-[collapsible=icon]:w-[var(--sidebar-width-icon)] group-data-[side=left]:border-r group-data-[side=right]:border-l",
|
||||
variant === 'floating' || variant === 'inset'
|
||||
? 'p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]'
|
||||
: 'group-data-[collapsible=icon]:w-[var(--sidebar-width-icon)] group-data-[side=left]:border-r group-data-[side=right]:border-l',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
<div
|
||||
data-sidebar="sidebar"
|
||||
class="bg-sidebar group-data-[variant=floating]:border-sidebar-border flex h-full w-full flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow"
|
||||
class="flex h-full w-full flex-col bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:border-sidebar-border group-data-[variant=floating]:shadow"
|
||||
>
|
||||
{@render children?.()}
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Root from "./skeleton.svelte";
|
||||
import Root from './skeleton.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
//
|
||||
Root as Skeleton,
|
||||
Root as Skeleton
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type {WithElementRef, WithoutChildren} from "bits-ui";
|
||||
import type {HTMLAttributes} from "svelte/elements";
|
||||
import {cn} from "$lib/utils.js";
|
||||
import type {WithElementRef, WithoutChildren} from 'bits-ui';
|
||||
import type {HTMLAttributes} from 'svelte/elements';
|
||||
import {cn} from '$lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
@@ -11,7 +11,7 @@
|
||||
</script>
|
||||
|
||||
<div
|
||||
{...restProps}
|
||||
bind:this={ref}
|
||||
class={cn("bg-primary/10 animate-pulse rounded-md", className)}
|
||||
class={cn('animate-pulse rounded-md bg-primary/10', className)}
|
||||
{...restProps}
|
||||
></div>
|
||||
|
||||
28
web/src/lib/components/ui/table/index.ts
Normal file
28
web/src/lib/components/ui/table/index.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import Root from './table.svelte';
|
||||
import Body from './table-body.svelte';
|
||||
import Caption from './table-caption.svelte';
|
||||
import Cell from './table-cell.svelte';
|
||||
import Footer from './table-footer.svelte';
|
||||
import Head from './table-head.svelte';
|
||||
import Header from './table-header.svelte';
|
||||
import Row from './table-row.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
Body,
|
||||
Caption,
|
||||
Cell,
|
||||
Footer,
|
||||
Head,
|
||||
Header,
|
||||
Row,
|
||||
//
|
||||
Root as Table,
|
||||
Body as TableBody,
|
||||
Caption as TableCaption,
|
||||
Cell as TableCell,
|
||||
Footer as TableFooter,
|
||||
Head as TableHead,
|
||||
Header as TableHeader,
|
||||
Row as TableRow
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user