build: Update to ShadCN-Svelte V5

This commit is contained in:
April Hall 2025-03-04 10:30:55 -05:00
parent 36837843c0
commit 07ce8dea90
No known key found for this signature in database
GPG Key ID: A49AC35CB186266C
72 changed files with 822 additions and 928 deletions

BIN
bun.lockb

Binary file not shown.

View File

@ -1,5 +1,5 @@
{ {
"$schema": "https://shadcn-svelte.com/schema.json", "$schema": "https://next.shadcn-svelte.com/schema.json",
"style": "new-york", "style": "new-york",
"tailwind": { "tailwind": {
"config": "tailwind.config.ts", "config": "tailwind.config.ts",
@ -8,7 +8,10 @@
}, },
"aliases": { "aliases": {
"components": "$lib/components", "components": "$lib/components",
"utils": "$lib/utils" "utils": "$lib/utils",
"ui": "$lib/components/ui",
"hooks": "$lib/hooks"
}, },
"typescript": true "typescript": true,
"registry": "https://next.shadcn-svelte.com/registry"
} }

View File

@ -35,6 +35,7 @@
"prettier-plugin-tailwindcss": "^0.6.11", "prettier-plugin-tailwindcss": "^0.6.11",
"svelte": "^5.0.0", "svelte": "^5.0.0",
"svelte-check": "^4.0.0", "svelte-check": "^4.0.0",
"tailwindcss-animate": "^1.0.7",
"typescript": "^5.0.0", "typescript": "^5.0.0",
"typescript-eslint": "^8.20.0" "typescript-eslint": "^8.20.0"
}, },
@ -49,11 +50,11 @@
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"better-auth": "^1.1.16", "better-auth": "^1.1.16",
"better-sqlite3": "^11.8.1", "better-sqlite3": "^11.8.1",
"bits-ui": "0.22.0", "bits-ui": "^1.3.5",
"cassandra-driver": "^4.7.2", "cassandra-driver": "^4.7.2",
"dotenv": "^16.4.7", "dotenv": "^16.4.7",
"express": "^4.21.2", "express": "^4.21.2",
"lucide-svelte": "^0.474.0", "lucide-svelte": "^0.477.0",
"markdown-it": "^14.1.0", "markdown-it": "^14.1.0",
"markdown-it-highlightjs": "^4.2.0", "markdown-it-highlightjs": "^4.2.0",
"markdown-it-link-attributes": "^4.0.1", "markdown-it-link-attributes": "^4.0.1",
@ -63,7 +64,7 @@
"socket.io-client": "^4.8.1", "socket.io-client": "^4.8.1",
"svelte-radix": "^2.0.1", "svelte-radix": "^2.0.1",
"sveltekit-superforms": "^2.23.1", "sveltekit-superforms": "^2.23.1",
"tailwind-merge": "^3.0.1", "tailwind-merge": "^3.0.2",
"tailwind-variants": "^0.3.1", "tailwind-variants": "^0.3.1",
"tailwindcss": "^3.4.17", "tailwindcss": "^3.4.17",
"tsm": "^2.3.0", "tsm": "^2.3.0",

View File

@ -1,5 +1,3 @@
@import '../node_modules/highlight.js/styles/github-dark.min.css';
@tailwind base; @tailwind base;
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
@ -15,45 +13,62 @@
:root { :root {
--background: 0 0% 100%; --background: 0 0% 100%;
--foreground: 20 14.3% 4.1%; --foreground: 20 14.3% 4.1%;
--card: 0 0% 100%;
--card-foreground: 20 14.3% 4.1%;
--popover: 0 0% 100%;
--popover-foreground: 20 14.3% 4.1%;
--primary: 24 9.8% 10%;
--primary-foreground: 60 9.1% 97.8%;
--secondary: 240 6% 90%;
--secondary-foreground: 24 9.8% 10%;
--muted: 60 4.8% 95.9%; --muted: 60 4.8% 95.9%;
--muted-foreground: 25 5.3% 44.7%; --muted-foreground: 25 5.3% 44.7%;
--accent: 60 4.8% 95.9%; --popover: 0 0% 100%;
--accent-foreground: 24 9.8% 10%; --popover-foreground: 20 14.3% 4.1%;
--destructive: 0 72.22% 50.59%; --card: 0 0% 100%;
--destructive-foreground: 60 9.1% 97.8%; --card-foreground: 20 14.3% 4.1%;
--border: 20 5.9% 90%; --border: 20 5.9% 90%;
--input: 20 5.9% 90%; --input: 20 5.9% 90%;
--primary: 24 9.8% 10%;
--primary-foreground: 60 9.1% 97.8%;
--secondary: 60 4.8% 95.9%;
--secondary-foreground: 24 9.8% 10%;
--accent: 60 4.8% 95.9%;
--accent-foreground: 24 9.8% 10%;
--destructive: 0 72.2% 50.6%;
--destructive-foreground: 60 9.1% 97.8%;
--ring: 20 14.3% 4.1%; --ring: 20 14.3% 4.1%;
--radius: 0.5rem; --radius: 0.5rem;
--sidebar-background: 0 0% 98%;
--sidebar-foreground: 240 5.3% 26.1%;
--sidebar-primary: 240 5.9% 10%;
--sidebar-primary-foreground: 0 0% 98%;
--sidebar-accent: 240 4.8% 95.9%;
--sidebar-accent-foreground: 240 5.9% 10%;
--sidebar-border: 220 13% 91%;
--sidebar-ring: 217.2 91.2% 59.8%;
} }
.dark { .dark {
--background: 20 14.3% 4.1%; --background: 20 14.3% 4.1%;
--foreground: 60 9.1% 97.8%; --foreground: 60 9.1% 97.8%;
--card: 20 14.3% 4.1%; --muted: 12 6.5% 15.1%;
--card-foreground: 60 9.1% 97.8%; --muted-foreground: 24 5.4% 63.9%;
--popover: 20 14.3% 4.1%; --popover: 20 14.3% 4.1%;
--popover-foreground: 60 9.1% 97.8%; --popover-foreground: 60 9.1% 97.8%;
--card: 20 14.3% 4.1%;
--card-foreground: 60 9.1% 97.8%;
--border: 12 6.5% 15.1%;
--input: 12 6.5% 15.1%;
--primary: 60 9.1% 97.8%; --primary: 60 9.1% 97.8%;
--primary-foreground: 24 9.8% 10%; --primary-foreground: 24 9.8% 10%;
--secondary: 12 6.5% 15.1%; --secondary: 12 6.5% 15.1%;
--secondary-foreground: 60 9.1% 97.8%; --secondary-foreground: 60 9.1% 97.8%;
--muted: 12 6.5% 15.1%;
--muted-foreground: 24 5.4% 63.9%;
--accent: 12 6.5% 15.1%; --accent: 12 6.5% 15.1%;
--accent-foreground: 60 9.1% 97.8%; --accent-foreground: 60 9.1% 97.8%;
--destructive: 0 62.8% 30.6%; --destructive: 0 62.8% 30.6%;
--destructive-foreground: 60 9.1% 97.8%; --destructive-foreground: 60 9.1% 97.8%;
--border: 12 6.5% 15.1%;
--input: 12 6.5% 15.1%;
--ring: 24 5.7% 82.9%; --ring: 24 5.7% 82.9%;
--sidebar-background: 240 5.9% 10%;
--sidebar-foreground: 240 4.8% 95.9%;
--sidebar-primary: 224.3 76.3% 48%;
--sidebar-primary-foreground: 0 0% 100%;
--sidebar-accent: 240 3.7% 15.9%;
--sidebar-accent-foreground: 240 4.8% 95.9%;
--sidebar-border: 240 3.7% 15.9%;
--sidebar-ring: 217.2 91.2% 59.8%;
} }
} }

View File

@ -15,7 +15,7 @@
i: number; i: number;
} }
let { message, imageSrc, user, timestamp, uid, open = $bindable(), closeDialogs, i }: TypeMessage & Props = $props(); let { message, imageSrc, user, timestamp, uid, open = $bindable(false), closeDialogs, i }: TypeMessage & Props = $props();
function copy(itemName: string, content: string | number) { function copy(itemName: string, content: string | number) {
navigator.clipboard navigator.clipboard

View File

@ -6,7 +6,8 @@
import { toggleMode } from 'mode-watcher'; import { toggleMode } from 'mode-watcher';
</script> </script>
<Tooltip.Root> <Tooltip.Provider>
<Tooltip.Root>
<Tooltip.Trigger class="ml-auto" onclick={toggleMode}> <Tooltip.Trigger class="ml-auto" onclick={toggleMode}>
<div class="size-9 {buttonVariants({ variant: 'secondary' })}" style="padding: 0;"> <div class="size-9 {buttonVariants({ variant: 'secondary' })}" style="padding: 0;">
<Sun class="size-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" /> <Sun class="size-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
@ -16,4 +17,5 @@
<Tooltip.Content> <Tooltip.Content>
<p>Toggle theme</p> <p>Toggle theme</p>
</Tooltip.Content> </Tooltip.Content>
</Tooltip.Root> </Tooltip.Root>
</Tooltip.Provider>

View File

@ -9,6 +9,6 @@
const { children, class: className = '' }: Props = $props(); const { children, class: className = '' }: Props = $props();
</script> </script>
<div class="{className} prose prose-stone prose-blue !max-w-full dark:prose-invert prose-pre:w-fit prose-pre:max-w-[95%]"> <div class="{className} prose prose-stone prose-blue dark:prose-invert prose-pre:w-fit prose-pre:max-w-[95%] !max-w-full">
{@render children()} {@render children()}
</div> </div>

View File

@ -1,13 +1,40 @@
<script lang="ts"> <script lang="ts" module>
import { type Variant, badgeVariants } from './index.js'; import { type VariantProps, tv } from 'tailwind-variants';
import { cn } from '$lib/utils.js'; export const badgeVariants = tv({
base: 'focus:ring-ring inline-flex select-none items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2',
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/80 border-transparent shadow',
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80 border-transparent',
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/80 border-transparent shadow',
outline: 'text-foreground',
},
},
defaultVariants: {
variant: 'default',
},
});
let className: string | undefined | null = undefined; export type BadgeVariant = VariantProps<typeof badgeVariants>['variant'];
export let href: string | undefined = undefined;
export let variant: Variant = 'default';
export { className as class };
</script> </script>
<svelte:element this={href ? 'a' : 'span'} {href} class={cn(badgeVariants({ variant, className }))} {...$$restProps}> <script lang="ts">
<slot /> import type { WithElementRef } from 'bits-ui';
import type { HTMLAnchorAttributes } from 'svelte/elements';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
href,
class: className,
variant = 'default',
children,
...restProps
}: WithElementRef<HTMLAnchorAttributes> & {
variant?: BadgeVariant;
} = $props();
</script>
<svelte:element this={href ? 'a' : 'span'} bind:this={ref} {href} class={cn(badgeVariants({ variant }), className)} {...restProps}>
{@render children?.()}
</svelte:element> </svelte:element>

View File

@ -1,19 +1,2 @@
import { type VariantProps, tv } from 'tailwind-variants';
export { default as Badge } from './badge.svelte'; export { default as Badge } from './badge.svelte';
export const badgeVariants = tv({ export { badgeVariants, type BadgeVariant } from './badge.svelte';
base: 'focus:ring-ring inline-flex select-none items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2',
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/80 border-transparent shadow',
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80 border-transparent',
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/80 border-transparent shadow',
outline: 'text-foreground',
},
},
defaultVariants: {
variant: 'default',
},
});
export type Variant = VariantProps<typeof badgeVariants>['variant'];

View File

@ -1,18 +1,63 @@
<script lang="ts"> <script lang="ts" module>
import { Button as ButtonPrimitive } from 'bits-ui'; import type { WithElementRef } from 'bits-ui';
import { type Events, type Props, buttonVariants } from './index.js'; import type { HTMLAnchorAttributes, HTMLButtonAttributes } from 'svelte/elements';
import { cn } from '$lib/utils.js'; import { type VariantProps, tv } from 'tailwind-variants';
type $$Props = Props; export const buttonVariants = tv({
type $$Events = Events; 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',
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',
},
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',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
});
let className: $$Props['class'] = undefined; export type ButtonVariant = VariantProps<typeof buttonVariants>['variant'];
export let variant: $$Props['variant'] = 'default'; export type ButtonSize = VariantProps<typeof buttonVariants>['size'];
export let size: $$Props['size'] = 'default';
export let builders: $$Props['builders'] = []; export type ButtonProps = WithElementRef<HTMLButtonAttributes> &
export { className as class }; WithElementRef<HTMLAnchorAttributes> & {
variant?: ButtonVariant;
size?: ButtonSize;
};
</script> </script>
<ButtonPrimitive.Root {builders} class={cn(buttonVariants({ variant, size, className }))} type="button" {...$$restProps} on:click on:keydown> <script lang="ts">
<slot /> import { cn } from '$lib/utils.js';
</ButtonPrimitive.Root>
let {
class: className,
variant = 'default',
size = 'default',
ref = $bindable(null),
href = undefined,
type = 'button',
children,
...restProps
}: ButtonProps = $props();
</script>
{#if href}
<a bind:this={ref} class={cn(buttonVariants({ variant, size }), className)} {href} {...restProps}>
{@render children?.()}
</a>
{:else}
<button bind:this={ref} class={cn(buttonVariants({ variant, size }), className)} {type} {...restProps}>
{@render children?.()}
</button>
{/if}

View File

@ -1,48 +1,12 @@
import type { Button as ButtonPrimitive } from 'bits-ui'; import Root, { type ButtonProps, type ButtonSize, type ButtonVariant, buttonVariants } from './button.svelte';
import { type VariantProps, tv } from 'tailwind-variants';
import Root from './button.svelte';
const buttonVariants = tv({
base: 'focus-visible:ring-ring inline-flex items-center justify-center 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',
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',
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',
},
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',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
});
type Variant = VariantProps<typeof buttonVariants>['variant'];
type Size = VariantProps<typeof buttonVariants>['size'];
type Props = ButtonPrimitive.Props & {
variant?: Variant;
size?: Size;
};
type Events = ButtonPrimitive.Events;
export { export {
Root, Root,
type Props, type ButtonProps as Props,
type Events,
// //
Root as Button, Root as Button,
type Props as ButtonProps,
type Events as ButtonEvents,
buttonVariants, buttonVariants,
type ButtonProps,
type ButtonSize,
type ButtonVariant,
}; };

View File

@ -1,13 +1,11 @@
<script lang="ts"> <script lang="ts">
import type { WithElementRef } from 'bits-ui';
import type { HTMLAttributes } from 'svelte/elements'; import type { HTMLAttributes } from 'svelte/elements';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = HTMLAttributes<HTMLDivElement>; let { ref = $bindable(null), class: className, children, ...restProps }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
let className: $$Props['class'] = undefined;
export { className as class };
</script> </script>
<div class={cn('p-6', className)} {...$$restProps}> <div bind:this={ref} class={cn('p-6', className)} {...restProps}>
<slot /> {@render children?.()}
</div> </div>

View File

@ -1,13 +1,11 @@
<script lang="ts"> <script lang="ts">
import type { WithElementRef } from 'bits-ui';
import type { HTMLAttributes } from 'svelte/elements'; import type { HTMLAttributes } from 'svelte/elements';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = HTMLAttributes<HTMLParagraphElement>; let { ref = $bindable(null), class: className, children, ...restProps }: WithElementRef<HTMLAttributes<HTMLParagraphElement>> = $props();
let className: $$Props['class'] = undefined;
export { className as class };
</script> </script>
<p class={cn('text-sm text-muted-foreground', className)} {...$$restProps}> <p bind:this={ref} class={cn('text-sm text-muted-foreground', className)} {...restProps}>
<slot /> {@render children?.()}
</p> </p>

View File

@ -1,13 +1,11 @@
<script lang="ts"> <script lang="ts">
import type { WithElementRef } from 'bits-ui';
import type { HTMLAttributes } from 'svelte/elements'; import type { HTMLAttributes } from 'svelte/elements';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = HTMLAttributes<HTMLDivElement>; let { ref = $bindable(null), class: className, children, ...restProps }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
let className: $$Props['class'] = undefined;
export { className as class };
</script> </script>
<div class={cn('flex items-center p-6 pt-0', className)} {...$$restProps}> <div bind:this={ref} class={cn('flex items-center p-6 pt-0', className)} {...restProps}>
<slot /> {@render children?.()}
</div> </div>

View File

@ -1,13 +1,11 @@
<script lang="ts"> <script lang="ts">
import type { WithElementRef } from 'bits-ui';
import type { HTMLAttributes } from 'svelte/elements'; import type { HTMLAttributes } from 'svelte/elements';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = HTMLAttributes<HTMLDivElement>; let { ref = $bindable(null), class: className, children, ...restProps }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
let className: $$Props['class'] = undefined;
export { className as class };
</script> </script>
<div class={cn('flex flex-col space-y-1.5 p-6 pb-0', className)} {...$$restProps}> <div bind:this={ref} class={cn('flex flex-col space-y-1.5 p-6 pb-0', className)} {...restProps}>
<slot /> {@render children?.()}
</div> </div>

View File

@ -1,17 +1,19 @@
<script lang="ts"> <script lang="ts">
import type { WithElementRef } from 'bits-ui';
import type { HTMLAttributes } from 'svelte/elements'; import type { HTMLAttributes } from 'svelte/elements';
import type { HeadingLevel } from './index.js';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = HTMLAttributes<HTMLHeadingElement> & { let {
tag?: HeadingLevel; ref = $bindable(null),
}; class: className,
level = 3,
let className: $$Props['class'] = undefined; children,
export let tag: $$Props['tag'] = 'h3'; ...restProps
export { className as class }; }: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
level?: 1 | 2 | 3 | 4 | 5 | 6;
} = $props();
</script> </script>
<svelte:element this={tag} class={cn('font-semibold leading-none tracking-tight', className)} {...$$restProps}> <div role="heading" aria-level={level} bind:this={ref} class={cn('font-semibold leading-none tracking-tight', className)} {...restProps}>
<slot /> {@render children?.()}
</svelte:element> </div>

View File

@ -1,22 +1,11 @@
<script lang="ts"> <script lang="ts">
import type { WithElementRef } from 'bits-ui';
import type { HTMLAttributes } from 'svelte/elements'; import type { HTMLAttributes } from 'svelte/elements';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = HTMLAttributes<HTMLDivElement>; let { ref = $bindable(null), class: className, children, ...restProps }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
let className: $$Props['class'] = undefined;
export { className as class };
</script> </script>
<!-- svelte-ignore a11y-no-static-element-interactions --> <div bind:this={ref} class={cn('rounded-xl border bg-card text-card-foreground shadow', className)} {...restProps}>
<div {@render children?.()}
class={cn('rounded-xl border bg-card text-card-foreground shadow', className)}
{...$$restProps}
on:click
on:focusin
on:focusout
on:mouseenter
on:mouseleave
>
<slot />
</div> </div>

View File

@ -20,5 +20,3 @@ export {
Header as CardHeader, Header as CardHeader,
Title as CardTitle, Title as CardTitle,
}; };
export type HeadingLevel = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';

View File

@ -1,35 +1,40 @@
<script lang="ts"> <script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from 'bits-ui'; import { ContextMenu as ContextMenuPrimitive, type WithoutChildrenOrChild } from 'bits-ui';
import Check from 'svelte-radix/Check.svelte'; import Check from 'lucide-svelte/icons/check';
import Minus from 'lucide-svelte/icons/minus';
import type { Snippet } from 'svelte';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = ContextMenuPrimitive.CheckboxItemProps; let {
type $$Events = ContextMenuPrimitive.CheckboxItemEvents; ref = $bindable(null),
class: className,
let className: $$Props['class'] = undefined; checked = $bindable(false),
export { className as class }; indeterminate = $bindable(false),
export let checked: $$Props['checked'] = undefined; children: childrenProp,
...restProps
}: WithoutChildrenOrChild<ContextMenuPrimitive.CheckboxItemProps> & {
children?: Snippet;
} = $props();
</script> </script>
<ContextMenuPrimitive.CheckboxItem <ContextMenuPrimitive.CheckboxItem
bind:ref
bind:checked bind:checked
bind:indeterminate
class={cn( class={cn(
'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', '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, className,
)} )}
{...$$restProps} {...restProps}
on:click
on:keydown
on:focusin
on:focusout
on:pointerdown
on:pointerleave
on:pointermove
> >
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> {#snippet children({ checked, indeterminate })}
<ContextMenuPrimitive.CheckboxIndicator> <span class="absolute left-2 flex size-3.5 items-center justify-center">
<Check class="h-4 w-4" /> {#if indeterminate}
</ContextMenuPrimitive.CheckboxIndicator> <Minus class="size-4" />
{:else}
<Check class={cn('size-4', !checked && 'text-transparent')} />
{/if}
</span> </span>
<slot /> {@render childrenProp?.()}
{/snippet}
</ContextMenuPrimitive.CheckboxItem> </ContextMenuPrimitive.CheckboxItem>

View File

@ -1,21 +1,21 @@
<script lang="ts"> <script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from 'bits-ui'; import { ContextMenu as ContextMenuPrimitive } from 'bits-ui';
import { cn, flyAndScale } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = ContextMenuPrimitive.ContentProps; let {
ref = $bindable(null),
let className: $$Props['class'] = undefined; class: className,
export let transition: $$Props['transition'] = flyAndScale; portalProps,
export let transitionConfig: $$Props['transitionConfig'] = undefined; ...restProps
export { className as class }; }: ContextMenuPrimitive.ContentProps & {
portalProps?: ContextMenuPrimitive.PortalProps;
} = $props();
</script> </script>
<ContextMenuPrimitive.Content <ContextMenuPrimitive.Portal {...portalProps}>
{transition} <ContextMenuPrimitive.Content
{transitionConfig}
class={cn('z-50 min-w-[8rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-md focus:outline-none', className)} class={cn('z-50 min-w-[8rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-md focus:outline-none', className)}
{...$$restProps} {...restProps}
on:keydown bind:ref
> />
<slot /> </ContextMenuPrimitive.Portal>
</ContextMenuPrimitive.Content>

View File

@ -0,0 +1,19 @@
<script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
inset,
...restProps
}: ContextMenuPrimitive.GroupHeadingProps & {
inset?: boolean;
} = $props();
</script>
<ContextMenuPrimitive.GroupHeading
bind:ref
class={cn('px-2 py-1.5 text-sm font-semibold text-foreground', inset && 'pl-8', className)}
{...restProps}
/>

View File

@ -2,14 +2,14 @@
import { ContextMenu as ContextMenuPrimitive } from 'bits-ui'; import { ContextMenu as ContextMenuPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = ContextMenuPrimitive.ItemProps & { let {
ref = $bindable(null),
class: className,
inset,
...restProps
}: ContextMenuPrimitive.ItemProps & {
inset?: boolean; inset?: boolean;
}; } = $props();
type $$Events = ContextMenuPrimitive.ItemEvents;
let className: $$Props['class'] = undefined;
export let inset: $$Props['inset'] = undefined;
export { className as class };
</script> </script>
<ContextMenuPrimitive.Item <ContextMenuPrimitive.Item
@ -18,14 +18,6 @@
inset && 'pl-8', inset && 'pl-8',
className, className,
)} )}
{...$$restProps} bind:ref
on:click {...restProps}
on:keydown />
on:focusin
on:focusout
on:pointerdown
on:pointerleave
on:pointermove
>
<slot />
</ContextMenuPrimitive.Item>

View File

@ -1,35 +1,25 @@
<script lang="ts"> <script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from 'bits-ui'; import { ContextMenu as ContextMenuPrimitive, type WithoutChild } from 'bits-ui';
import DotFilled from 'svelte-radix/DotFilled.svelte'; import Circle from 'lucide-svelte/icons/circle';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = ContextMenuPrimitive.RadioItemProps; let { ref = $bindable(null), class: className, children: childrenProp, ...restProps }: WithoutChild<ContextMenuPrimitive.RadioItemProps> = $props();
type $$Events = ContextMenuPrimitive.RadioItemEvents;
let className: $$Props['class'] = undefined;
export let value: $$Props['value'];
export { className as class };
</script> </script>
<ContextMenuPrimitive.RadioItem <ContextMenuPrimitive.RadioItem
bind:ref
class={cn( class={cn(
'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', '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, className,
)} )}
{value} {...restProps}
{...$$restProps}
on:click
on:keydown
on:focusin
on:focusout
on:pointerdown
on:pointerleave
on:pointermove
> >
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> {#snippet children({ checked })}
<ContextMenuPrimitive.RadioIndicator> <span class="absolute left-2 flex size-3.5 items-center justify-center">
<DotFilled class="h-4 w-4 fill-current" /> {#if checked}
</ContextMenuPrimitive.RadioIndicator> <Circle class="size-2 fill-current" />
{/if}
</span> </span>
<slot /> {@render childrenProp?.({ checked })}
{/snippet}
</ContextMenuPrimitive.RadioItem> </ContextMenuPrimitive.RadioItem>

View File

@ -2,10 +2,7 @@
import { ContextMenu as ContextMenuPrimitive } from 'bits-ui'; import { ContextMenu as ContextMenuPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = ContextMenuPrimitive.SeparatorProps; let { ref = $bindable(null), class: className, ...restProps }: ContextMenuPrimitive.SeparatorProps = $props();
let className: $$Props['class'] = undefined;
export { className as class };
</script> </script>
<ContextMenuPrimitive.Separator class={cn('-mx-1 my-1 h-px bg-border', className)} {...$$restProps} /> <ContextMenuPrimitive.Separator bind:ref class={cn('-mx-1 my-1 h-px bg-border', className)} {...restProps} />

View File

@ -1,13 +1,11 @@
<script lang="ts"> <script lang="ts">
import type { WithElementRef } from 'bits-ui';
import type { HTMLAttributes } from 'svelte/elements'; import type { HTMLAttributes } from 'svelte/elements';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = HTMLAttributes<HTMLSpanElement>; let { ref = $bindable(null), class: className, children, ...restProps }: WithElementRef<HTMLAttributes<HTMLSpanElement>> = $props();
let className: $$Props['class'] = undefined;
export { className as class };
</script> </script>
<span class={cn('ml-auto text-xs tracking-widest text-muted-foreground', className)} {...$$restProps}> <span bind:this={ref} class={cn('ml-auto text-xs tracking-widest text-muted-foreground', className)} {...restProps}>
<slot /> {@render children?.()}
</span> </span>

View File

@ -1,23 +1,12 @@
<script lang="ts"> <script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from 'bits-ui'; import { ContextMenu as ContextMenuPrimitive } from 'bits-ui';
import { cn, flyAndScale } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = ContextMenuPrimitive.SubContentProps; let { ref = $bindable(null), class: className, ...restProps }: ContextMenuPrimitive.SubContentProps = $props();
let className: $$Props['class'] = undefined;
export let transition: $$Props['transition'] = flyAndScale;
export let transitionConfig: $$Props['transitionConfig'] = undefined;
export { className as class };
</script> </script>
<ContextMenuPrimitive.SubContent <ContextMenuPrimitive.SubContent
{transition} bind:ref
{transitionConfig}
class={cn('z-50 min-w-[8rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-lg focus:outline-none', className)} class={cn('z-50 min-w-[8rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-lg focus:outline-none', className)}
{...$$restProps} {...restProps}
on:keydown />
on:focusout
on:pointermove
>
<slot />
</ContextMenuPrimitive.SubContent>

View File

@ -1,32 +1,28 @@
<script lang="ts"> <script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from 'bits-ui'; import { ContextMenu as ContextMenuPrimitive, type WithoutChild } from 'bits-ui';
import ChevronRight from 'svelte-radix/ChevronRight.svelte'; import ChevronRight from 'lucide-svelte/icons/chevron-right';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = ContextMenuPrimitive.SubTriggerProps & { let {
ref = $bindable(null),
class: className,
children,
inset,
...restProps
}: WithoutChild<ContextMenuPrimitive.SubTriggerProps> & {
inset?: boolean; inset?: boolean;
}; } = $props();
type $$Events = ContextMenuPrimitive.SubTriggerEvents;
let className: $$Props['class'] = undefined;
export let inset: $$Props['inset'] = undefined;
export { className as class };
</script> </script>
<ContextMenuPrimitive.SubTrigger <ContextMenuPrimitive.SubTrigger
bind:ref
class={cn( class={cn(
'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[highlighted]:bg-accent data-[state=open]:bg-accent data-[highlighted]:text-accent-foreground data-[state=open]:text-accent-foreground', 'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[highlighted]:bg-accent data-[state=open]:bg-accent data-[highlighted]:text-accent-foreground data-[state=open]:text-accent-foreground',
inset && 'pl-8', inset && 'pl-8',
className, className,
)} )}
{...$$restProps} {...restProps}
on:click
on:keydown
on:focusin
on:focusout
on:pointerleave
on:pointermove
> >
<slot /> {@render children?.()}
<ChevronRight class="ml-auto h-4 w-4" /> <ChevronRight class="ml-auto size-4" />
</ContextMenuPrimitive.SubTrigger> </ContextMenuPrimitive.SubTrigger>

View File

@ -1,32 +1,32 @@
import { ContextMenu as ContextMenuPrimitive } from 'bits-ui'; import { ContextMenu as ContextMenuPrimitive } from 'bits-ui';
import Item from './context-menu-item.svelte'; import Item from './context-menu-item.svelte';
import Label from './context-menu-label.svelte'; import GroupHeading from './context-menu-group-heading.svelte';
import Content from './context-menu-content.svelte'; import Content from './context-menu-content.svelte';
import Shortcut from './context-menu-shortcut.svelte'; import Shortcut from './context-menu-shortcut.svelte';
import RadioItem from './context-menu-radio-item.svelte'; import RadioItem from './context-menu-radio-item.svelte';
import Separator from './context-menu-separator.svelte'; import Separator from './context-menu-separator.svelte';
import RadioGroup from './context-menu-radio-group.svelte';
import SubContent from './context-menu-sub-content.svelte'; import SubContent from './context-menu-sub-content.svelte';
import SubTrigger from './context-menu-sub-trigger.svelte'; import SubTrigger from './context-menu-sub-trigger.svelte';
import CheckboxItem from './context-menu-checkbox-item.svelte'; import CheckboxItem from './context-menu-checkbox-item.svelte';
const Sub = ContextMenuPrimitive.Sub; const Sub: typeof ContextMenuPrimitive.Sub = ContextMenuPrimitive.Sub;
const Root = ContextMenuPrimitive.Root; const Root: typeof ContextMenuPrimitive.Root = ContextMenuPrimitive.Root;
const Trigger = ContextMenuPrimitive.Trigger; const Trigger: typeof ContextMenuPrimitive.Trigger = ContextMenuPrimitive.Trigger;
const Group = ContextMenuPrimitive.Group; const Group: typeof ContextMenuPrimitive.Group = ContextMenuPrimitive.Group;
const RadioGroup: typeof ContextMenuPrimitive.RadioGroup = ContextMenuPrimitive.RadioGroup;
export { export {
Sub, Sub,
Root, Root,
Item, Item,
Label,
Group, Group,
Trigger, Trigger,
Content, Content,
Shortcut, Shortcut,
Separator, Separator,
RadioItem, RadioItem,
GroupHeading,
SubContent, SubContent,
SubTrigger, SubTrigger,
RadioGroup, RadioGroup,
@ -35,13 +35,13 @@ export {
Root as ContextMenu, Root as ContextMenu,
Sub as ContextMenuSub, Sub as ContextMenuSub,
Item as ContextMenuItem, Item as ContextMenuItem,
Label as ContextMenuLabel,
Group as ContextMenuGroup, Group as ContextMenuGroup,
Content as ContextMenuContent, Content as ContextMenuContent,
Trigger as ContextMenuTrigger, Trigger as ContextMenuTrigger,
Shortcut as ContextMenuShortcut, Shortcut as ContextMenuShortcut,
RadioItem as ContextMenuRadioItem, RadioItem as ContextMenuRadioItem,
Separator as ContextMenuSeparator, Separator as ContextMenuSeparator,
GroupHeading as ContextMenuGroupHeading,
RadioGroup as ContextMenuRadioGroup, RadioGroup as ContextMenuRadioGroup,
SubContent as ContextMenuSubContent, SubContent as ContextMenuSubContent,
SubTrigger as ContextMenuSubTrigger, SubTrigger as ContextMenuSubTrigger,

View File

@ -1,35 +1,37 @@
<script lang="ts"> <script lang="ts">
import { Dialog as DialogPrimitive } from 'bits-ui'; import { Dialog as DialogPrimitive, type WithoutChildrenOrChild } from 'bits-ui';
import Cross2 from 'svelte-radix/Cross2.svelte'; import X from 'lucide-svelte/icons/x';
import type { Snippet } from 'svelte';
import * as Dialog from './index.js'; import * as Dialog from './index.js';
import { cn, flyAndScale } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = DialogPrimitive.ContentProps; let {
ref = $bindable(null),
let className: $$Props['class'] = undefined; class: className,
export let transition: $$Props['transition'] = flyAndScale; portalProps,
export let transitionConfig: $$Props['transitionConfig'] = { children,
duration: 200, ...restProps
}; }: WithoutChildrenOrChild<DialogPrimitive.ContentProps> & {
export { className as class }; portalProps?: DialogPrimitive.PortalProps;
children: Snippet;
} = $props();
</script> </script>
<Dialog.Portal> <Dialog.Portal {...portalProps}>
<Dialog.Overlay /> <Dialog.Overlay />
<DialogPrimitive.Content <DialogPrimitive.Content
{transition} bind:ref
{transitionConfig}
class={cn( class={cn(
'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg sm:rounded-lg md:w-full', 'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
className, className,
)} )}
{...$$restProps} {...restProps}
> >
<slot /> {@render children?.()}
<DialogPrimitive.Close <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" 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"
> >
<Cross2 class="h-4 w-4" /> <X class="size-4" />
<span class="sr-only">Close</span> <span class="sr-only">Close</span>
</DialogPrimitive.Close> </DialogPrimitive.Close>
</DialogPrimitive.Content> </DialogPrimitive.Content>

View File

@ -2,12 +2,7 @@
import { Dialog as DialogPrimitive } from 'bits-ui'; import { Dialog as DialogPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = DialogPrimitive.DescriptionProps; let { ref = $bindable(null), class: className, ...restProps }: DialogPrimitive.DescriptionProps = $props();
let className: $$Props['class'] = undefined;
export { className as class };
</script> </script>
<DialogPrimitive.Description class={cn('text-sm text-muted-foreground', className)} {...$$restProps}> <DialogPrimitive.Description bind:ref class={cn('text-sm text-muted-foreground', className)} {...restProps} />
<slot />
</DialogPrimitive.Description>

View File

@ -1,13 +1,11 @@
<script lang="ts"> <script lang="ts">
import type { WithElementRef } from 'bits-ui';
import type { HTMLAttributes } from 'svelte/elements'; import type { HTMLAttributes } from 'svelte/elements';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = HTMLAttributes<HTMLDivElement>; let { ref = $bindable(null), class: className, children, ...restProps }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
let className: $$Props['class'] = undefined;
export { className as class };
</script> </script>
<div class={cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', className)} {...$$restProps}> <div bind:this={ref} class={cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', className)} {...restProps}>
<slot /> {@render children?.()}
</div> </div>

View File

@ -1,13 +1,11 @@
<script lang="ts"> <script lang="ts">
import type { HTMLAttributes } from 'svelte/elements'; import type { HTMLAttributes } from 'svelte/elements';
import type { WithElementRef } from 'bits-ui';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = HTMLAttributes<HTMLDivElement>; let { ref = $bindable(null), class: className, children, ...restProps }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
let className: $$Props['class'] = undefined;
export { className as class };
</script> </script>
<div class={cn('flex flex-col space-y-1.5 text-center sm:text-left', className)} {...$$restProps}> <div bind:this={ref} class={cn('flex flex-col space-y-1.5 text-center sm:text-left', className)} {...restProps}>
<slot /> {@render children?.()}
</div> </div>

View File

@ -1,21 +1,15 @@
<script lang="ts"> <script lang="ts">
import { Dialog as DialogPrimitive } from 'bits-ui'; import { Dialog as DialogPrimitive } from 'bits-ui';
import { fade } from 'svelte/transition';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = DialogPrimitive.OverlayProps; let { ref = $bindable(null), class: className, ...restProps }: DialogPrimitive.OverlayProps = $props();
let className: $$Props['class'] = undefined;
export let transition: $$Props['transition'] = fade;
export let transitionConfig: $$Props['transitionConfig'] = {
duration: 150,
};
export { className as class };
</script> </script>
<DialogPrimitive.Overlay <DialogPrimitive.Overlay
{transition} bind:ref
{transitionConfig} class={cn(
class={cn('fixed inset-0 z-50 bg-background/80 backdrop-blur-sm ', className)} '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',
{...$$restProps} className,
)}
{...restProps}
/> />

View File

@ -2,12 +2,7 @@
import { Dialog as DialogPrimitive } from 'bits-ui'; import { Dialog as DialogPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = DialogPrimitive.TitleProps; let { ref = $bindable(null), class: className, ...restProps }: DialogPrimitive.TitleProps = $props();
let className: $$Props['class'] = undefined;
export { className as class };
</script> </script>
<DialogPrimitive.Title class={cn('text-lg font-semibold leading-none tracking-tight', className)} {...$$restProps}> <DialogPrimitive.Title bind:ref class={cn('text-lg font-semibold leading-none tracking-tight', className)} {...restProps} />
<slot />
</DialogPrimitive.Title>

View File

@ -1,16 +1,16 @@
import { Dialog as DialogPrimitive } from 'bits-ui'; import { Dialog as DialogPrimitive } from 'bits-ui';
import Title from './dialog-title.svelte'; import Title from './dialog-title.svelte';
import Portal from './dialog-portal.svelte';
import Footer from './dialog-footer.svelte'; import Footer from './dialog-footer.svelte';
import Header from './dialog-header.svelte'; import Header from './dialog-header.svelte';
import Overlay from './dialog-overlay.svelte'; import Overlay from './dialog-overlay.svelte';
import Content from './dialog-content.svelte'; import Content from './dialog-content.svelte';
import Description from './dialog-description.svelte'; import Description from './dialog-description.svelte';
const Root = DialogPrimitive.Root; const Root: typeof DialogPrimitive.Root = DialogPrimitive.Root;
const Trigger = DialogPrimitive.Trigger; const Trigger: typeof DialogPrimitive.Trigger = DialogPrimitive.Trigger;
const Close = DialogPrimitive.Close; const Close: typeof DialogPrimitive.Close = DialogPrimitive.Close;
const Portal: typeof DialogPrimitive.Portal = DialogPrimitive.Portal;
export { export {
Root, Root,

View File

@ -1,35 +1,40 @@
<script lang="ts"> <script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'; import { DropdownMenu as DropdownMenuPrimitive, type WithoutChildrenOrChild } from 'bits-ui';
import Check from 'svelte-radix/Check.svelte'; import Check from 'lucide-svelte/icons/check';
import Minus from 'lucide-svelte/icons/minus';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
import type { Snippet } from 'svelte';
type $$Props = DropdownMenuPrimitive.CheckboxItemProps; let {
type $$Events = DropdownMenuPrimitive.CheckboxItemEvents; ref = $bindable(null),
class: className,
let className: $$Props['class'] = undefined; children: childrenProp,
export let checked: $$Props['checked'] = undefined; checked = $bindable(false),
export { className as class }; indeterminate = $bindable(false),
...restProps
}: WithoutChildrenOrChild<DropdownMenuPrimitive.CheckboxItemProps> & {
children?: Snippet;
} = $props();
</script> </script>
<DropdownMenuPrimitive.CheckboxItem <DropdownMenuPrimitive.CheckboxItem
bind:ref
bind:checked bind:checked
bind:indeterminate
class={cn( class={cn(
'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', '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, className,
)} )}
{...$$restProps} {...restProps}
on:click
on:keydown
on:focusin
on:focusout
on:pointerdown
on:pointerleave
on:pointermove
> >
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> {#snippet children({ checked, indeterminate })}
<DropdownMenuPrimitive.CheckboxIndicator> <span class="absolute left-2 flex size-3.5 items-center justify-center">
<Check class="h-4 w-4" /> {#if indeterminate}
</DropdownMenuPrimitive.CheckboxIndicator> <Minus class="size-4" />
{:else}
<Check class={cn('size-4', !checked && 'text-transparent')} />
{/if}
</span> </span>
<slot /> {@render childrenProp?.()}
{/snippet}
</DropdownMenuPrimitive.CheckboxItem> </DropdownMenuPrimitive.CheckboxItem>

View File

@ -1,23 +1,27 @@
<script lang="ts"> <script lang="ts">
import { cn } from '$lib/utils.js';
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'; import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import { cn, flyAndScale } from '$lib/utils.js';
type $$Props = DropdownMenuPrimitive.ContentProps; let {
ref = $bindable(null),
let className: $$Props['class'] = undefined; class: className,
export let sideOffset: $$Props['sideOffset'] = 4; sideOffset = 4,
export let transition: $$Props['transition'] = flyAndScale; portalProps,
export let transitionConfig: $$Props['transitionConfig'] = undefined; ...restProps
export { className as class }; }: DropdownMenuPrimitive.ContentProps & {
portalProps?: DropdownMenuPrimitive.PortalProps;
} = $props();
</script> </script>
<DropdownMenuPrimitive.Content <DropdownMenuPrimitive.Portal {...portalProps}>
{transition} <DropdownMenuPrimitive.Content
{transitionConfig} bind:ref
{sideOffset} {sideOffset}
class={cn('z-50 min-w-[8rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-md focus:outline-none', className)} class={cn(
{...$$restProps} 'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md',
on:keydown '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,
<slot /> )}
</DropdownMenuPrimitive.Content> {...restProps}
/>
</DropdownMenuPrimitive.Portal>

View File

@ -0,0 +1,15 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
inset,
...restProps
}: DropdownMenuPrimitive.GroupHeadingProps & {
inset?: boolean;
} = $props();
</script>
<DropdownMenuPrimitive.GroupHeading bind:ref class={cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', className)} {...restProps} />

View File

@ -1,31 +1,23 @@
<script lang="ts"> <script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
type $$Props = DropdownMenuPrimitive.ItemProps & { let {
ref = $bindable(null),
class: className,
inset,
...restProps
}: DropdownMenuPrimitive.ItemProps & {
inset?: boolean; inset?: boolean;
}; } = $props();
type $$Events = DropdownMenuPrimitive.ItemEvents;
let className: $$Props['class'] = undefined;
export let inset: $$Props['inset'] = undefined;
export { className as class };
</script> </script>
<DropdownMenuPrimitive.Item <DropdownMenuPrimitive.Item
bind:ref
class={cn( class={cn(
'relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:opacity-50', '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', inset && 'pl-8',
className, className,
)} )}
on:click {...restProps}
on:keydown />
on:focusin
on:focusout
on:pointerdown
on:pointerleave
on:pointermove
{...$$restProps}
>
<slot />
</DropdownMenuPrimitive.Item>

View File

@ -1,16 +1,19 @@
<script lang="ts"> <script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
import { type WithElementRef } from 'bits-ui';
import type { HTMLAttributes } from 'svelte/elements';
type $$Props = DropdownMenuPrimitive.LabelProps & { let {
ref = $bindable(null),
class: className,
inset,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
inset?: boolean; inset?: boolean;
}; } = $props();
let className: $$Props['class'] = undefined;
export let inset: $$Props['inset'] = undefined;
export { className as class };
</script> </script>
<DropdownMenuPrimitive.Label class={cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', className)} {...$$restProps}> <div bind:this={ref} class={cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', className)} {...restProps}>
<slot /> {@render children?.()}
</DropdownMenuPrimitive.Label> </div>

View File

@ -1,35 +1,30 @@
<script lang="ts"> <script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'; import { DropdownMenu as DropdownMenuPrimitive, type WithoutChild } from 'bits-ui';
import DotFilled from 'svelte-radix/DotFilled.svelte'; import Circle from 'lucide-svelte/icons/circle';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = DropdownMenuPrimitive.RadioItemProps; let {
type $$Events = DropdownMenuPrimitive.RadioItemEvents; ref = $bindable(null),
class: className,
let className: $$Props['class'] = undefined; children: childrenProp,
export let value: DropdownMenuPrimitive.RadioItemProps['value']; ...restProps
export { className as class }; }: WithoutChild<DropdownMenuPrimitive.RadioItemProps> = $props();
</script> </script>
<DropdownMenuPrimitive.RadioItem <DropdownMenuPrimitive.RadioItem
bind:ref
class={cn( class={cn(
'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', '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, className,
)} )}
{value} {...restProps}
{...$$restProps}
on:click
on:keydown
on:focusin
on:focusout
on:pointerdown
on:pointerleave
on:pointermove
> >
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"> {#snippet children({ checked })}
<DropdownMenuPrimitive.RadioIndicator> <span class="absolute left-2 flex size-3.5 items-center justify-center">
<DotFilled class="h-4 w-4 fill-current" /> {#if checked}
</DropdownMenuPrimitive.RadioIndicator> <Circle class="size-2 fill-current" />
{/if}
</span> </span>
<slot /> {@render childrenProp?.({ checked })}
{/snippet}
</DropdownMenuPrimitive.RadioItem> </DropdownMenuPrimitive.RadioItem>

View File

@ -2,10 +2,7 @@
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'; import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = DropdownMenuPrimitive.SeparatorProps; let { ref = $bindable(null), class: className, ...restProps }: DropdownMenuPrimitive.SeparatorProps = $props();
let className: $$Props['class'] = undefined;
export { className as class };
</script> </script>
<DropdownMenuPrimitive.Separator class={cn('-mx-1 my-1 h-px bg-muted', className)} {...$$restProps} /> <DropdownMenuPrimitive.Separator bind:ref class={cn('-mx-1 my-1 h-px bg-muted', className)} {...restProps} />

View File

@ -1,13 +1,11 @@
<script lang="ts"> <script lang="ts">
import type { HTMLAttributes } from 'svelte/elements'; import type { HTMLAttributes } from 'svelte/elements';
import { type WithElementRef } from 'bits-ui';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = HTMLAttributes<HTMLSpanElement>; let { ref = $bindable(null), class: className, children, ...restProps }: WithElementRef<HTMLAttributes<HTMLSpanElement>> = $props();
let className: $$Props['class'] = undefined;
export { className as class };
</script> </script>
<span class={cn('ml-auto text-xs tracking-widest opacity-60', className)} {...$$restProps}> <span bind:this={ref} class={cn('ml-auto text-xs tracking-widest opacity-60', className)} {...restProps}>
<slot /> {@render children?.()}
</span> </span>

View File

@ -1,26 +1,12 @@
<script lang="ts"> <script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'; import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import { cn, flyAndScale } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = DropdownMenuPrimitive.SubContentProps; let { ref = $bindable(null), class: className, ...restProps }: DropdownMenuPrimitive.SubContentProps = $props();
let className: $$Props['class'] = undefined;
export let transition: $$Props['transition'] = flyAndScale;
export let transitionConfig: $$Props['transitionConfig'] = {
x: -10,
y: 0,
};
export { className as class };
</script> </script>
<DropdownMenuPrimitive.SubContent <DropdownMenuPrimitive.SubContent
{transition} bind:ref
{transitionConfig}
class={cn('z-50 min-w-[8rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-lg focus:outline-none', className)} class={cn('z-50 min-w-[8rem] rounded-md border bg-popover p-1 text-popover-foreground shadow-lg focus:outline-none', className)}
{...$$restProps} {...restProps}
on:keydown />
on:focusout
on:pointermove
>
<slot />
</DropdownMenuPrimitive.SubContent>

View File

@ -1,32 +1,28 @@
<script lang="ts"> <script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'; import { DropdownMenu as DropdownMenuPrimitive, type WithoutChild } from 'bits-ui';
import ChevronRight from 'svelte-radix/ChevronRight.svelte'; import ChevronRight from 'lucide-svelte/icons/chevron-right';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = DropdownMenuPrimitive.SubTriggerProps & { let {
ref = $bindable(null),
class: className,
inset,
children,
...restProps
}: WithoutChild<DropdownMenuPrimitive.SubTriggerProps> & {
inset?: boolean; inset?: boolean;
}; } = $props();
type $$Events = DropdownMenuPrimitive.SubTriggerEvents;
let className: $$Props['class'] = undefined;
export let inset: $$Props['inset'] = undefined;
export { className as class };
</script> </script>
<DropdownMenuPrimitive.SubTrigger <DropdownMenuPrimitive.SubTrigger
bind:ref
class={cn( class={cn(
'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[highlighted]:bg-accent data-[state=open]:bg-accent data-[highlighted]:text-accent-foreground data-[state=open]:text-accent-foreground', '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', inset && 'pl-8',
className, className,
)} )}
{...$$restProps} {...restProps}
on:click
on:keydown
on:focusin
on:focusout
on:pointerleave
on:pointermove
> >
<slot /> {@render children?.()}
<ChevronRight class="ml-auto h-4 w-4" /> <ChevronRight class="ml-auto" />
</DropdownMenuPrimitive.SubTrigger> </DropdownMenuPrimitive.SubTrigger>

View File

@ -1,48 +1,50 @@
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui'; 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 Item from './dropdown-menu-item.svelte';
import Label from './dropdown-menu-label.svelte'; import Label from './dropdown-menu-label.svelte';
import Content from './dropdown-menu-content.svelte';
import Shortcut from './dropdown-menu-shortcut.svelte';
import RadioItem from './dropdown-menu-radio-item.svelte'; import RadioItem from './dropdown-menu-radio-item.svelte';
import Separator from './dropdown-menu-separator.svelte'; import Separator from './dropdown-menu-separator.svelte';
import RadioGroup from './dropdown-menu-radio-group.svelte'; import Shortcut from './dropdown-menu-shortcut.svelte';
import SubContent from './dropdown-menu-sub-content.svelte'; import SubContent from './dropdown-menu-sub-content.svelte';
import SubTrigger from './dropdown-menu-sub-trigger.svelte'; import SubTrigger from './dropdown-menu-sub-trigger.svelte';
import CheckboxItem from './dropdown-menu-checkbox-item.svelte';
const Sub = DropdownMenuPrimitive.Sub; const Sub = DropdownMenuPrimitive.Sub;
const Root = DropdownMenuPrimitive.Root; const Root = DropdownMenuPrimitive.Root;
const Trigger = DropdownMenuPrimitive.Trigger; const Trigger = DropdownMenuPrimitive.Trigger;
const Group = DropdownMenuPrimitive.Group; const Group = DropdownMenuPrimitive.Group;
const RadioGroup = DropdownMenuPrimitive.RadioGroup;
export { export {
Sub,
Root,
Item,
Label,
Group,
Trigger,
Content,
Shortcut,
Separator,
RadioItem,
SubContent,
SubTrigger,
RadioGroup,
CheckboxItem, CheckboxItem,
// Content,
Root as DropdownMenu, Root as DropdownMenu,
Sub as DropdownMenuSub, CheckboxItem as DropdownMenuCheckboxItem,
Content as DropdownMenuContent,
Group as DropdownMenuGroup,
GroupHeading as DropdownMenuGroupHeading,
Item as DropdownMenuItem, Item as DropdownMenuItem,
Label as DropdownMenuLabel, Label as DropdownMenuLabel,
Group as DropdownMenuGroup, RadioGroup as DropdownMenuRadioGroup,
Content as DropdownMenuContent,
Trigger as DropdownMenuTrigger,
Shortcut as DropdownMenuShortcut,
RadioItem as DropdownMenuRadioItem, RadioItem as DropdownMenuRadioItem,
Separator as DropdownMenuSeparator, Separator as DropdownMenuSeparator,
RadioGroup as DropdownMenuRadioGroup, Shortcut as DropdownMenuShortcut,
Sub as DropdownMenuSub,
SubContent as DropdownMenuSubContent, SubContent as DropdownMenuSubContent,
SubTrigger as DropdownMenuSubTrigger, SubTrigger as DropdownMenuSubTrigger,
CheckboxItem as DropdownMenuCheckboxItem, Trigger as DropdownMenuTrigger,
Group,
GroupHeading,
Item,
Label,
RadioGroup,
RadioItem,
Root,
Separator,
Shortcut,
Sub,
SubContent,
SubTrigger,
Trigger,
}; };

View File

@ -1,27 +1,5 @@
import Root from './input.svelte'; import Root from './input.svelte';
export type FormInputEvent<T extends Event = Event> = T & {
currentTarget: EventTarget & HTMLInputElement;
};
export type InputEvents = {
blur: FormInputEvent<FocusEvent>;
change: FormInputEvent<Event>;
click: FormInputEvent<MouseEvent>;
focus: FormInputEvent<FocusEvent>;
focusin: FormInputEvent<FocusEvent>;
focusout: FormInputEvent<FocusEvent>;
keydown: FormInputEvent<KeyboardEvent>;
keypress: FormInputEvent<KeyboardEvent>;
keyup: FormInputEvent<KeyboardEvent>;
mouseover: FormInputEvent<MouseEvent>;
mouseenter: FormInputEvent<MouseEvent>;
mouseleave: FormInputEvent<MouseEvent>;
mousemove: FormInputEvent<MouseEvent>;
paste: FormInputEvent<ClipboardEvent>;
input: FormInputEvent<InputEvent>;
wheel: FormInputEvent<WheelEvent>;
};
export { export {
Root, Root,
// //

View File

@ -1,42 +1,36 @@
<script lang="ts"> <script lang="ts">
import type { HTMLInputAttributes } from 'svelte/elements'; import type { HTMLInputAttributes, HTMLInputTypeAttribute } from 'svelte/elements';
import type { InputEvents } from './index.js'; import type { WithElementRef } from 'bits-ui';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = HTMLInputAttributes; type InputType = Exclude<HTMLInputTypeAttribute, 'file'>;
type $$Events = InputEvents;
let className: $$Props['class'] = undefined; type Props = WithElementRef<Omit<HTMLInputAttributes, 'type'> & ({ type: 'file'; files?: FileList } | { type?: InputType; files?: undefined })>;
export let value: $$Props['value'] = undefined;
export { className as class };
// Workaround for https://github.com/sveltejs/svelte/issues/9305 let { ref = $bindable(null), value = $bindable(), type, files = $bindable(), class: className, ...restProps }: Props = $props();
// Fixed in Svelte 5, but not backported to 4.x.
export let readonly: $$Props['readonly'] = undefined;
</script> </script>
<input {#if type === 'file'}
<input
bind:this={ref}
class={cn( class={cn(
'flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm 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', '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, className,
)} )}
type="file"
bind:files
bind:value bind:value
{readonly} {...restProps}
on:blur />
on:change {:else}
on:click <input
on:focus bind:this={ref}
on:focusin class={cn(
on:focusout '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',
on:keydown className,
on:keypress )}
on:keyup {type}
on:mouseover bind:value
on:mouseenter {...restProps}
on:mouseleave />
on:mousemove {/if}
on:paste
on:input
on:wheel|passive
{...$$restProps}
/>

View File

@ -2,15 +2,11 @@
import { Label as LabelPrimitive } from 'bits-ui'; import { Label as LabelPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = LabelPrimitive.Props; let { ref = $bindable(null), class: className, ...restProps }: LabelPrimitive.RootProps = $props();
let className: $$Props['class'] = undefined;
export { className as class };
</script> </script>
<LabelPrimitive.Root <LabelPrimitive.Root
bind:ref
class={cn('text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70', className)} class={cn('text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70', className)}
{...$$restProps} {...restProps}
> />
<slot />
</LabelPrimitive.Root>

View File

@ -1,34 +1,34 @@
import { Select as SelectPrimitive } from 'bits-ui'; import { Select as SelectPrimitive } from 'bits-ui';
import Label from './select-label.svelte'; import GroupHeading from './select-group-heading.svelte';
import Item from './select-item.svelte'; import Item from './select-item.svelte';
import Content from './select-content.svelte'; import Content from './select-content.svelte';
import Trigger from './select-trigger.svelte'; import Trigger from './select-trigger.svelte';
import Separator from './select-separator.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 Root = SelectPrimitive.Root;
const Group = SelectPrimitive.Group; const Group = SelectPrimitive.Group;
const Input = SelectPrimitive.Input;
const Value = SelectPrimitive.Value;
export { export {
Root, Root,
Item, Item,
Group, Group,
Input, GroupHeading,
Label,
Value,
Content, Content,
Trigger, Trigger,
Separator, Separator,
ScrollDownButton,
ScrollUpButton,
// //
Root as Select, Root as Select,
Item as SelectItem, Item as SelectItem,
Group as SelectGroup, Group as SelectGroup,
Input as SelectInput, GroupHeading as SelectGroupHeading,
Label as SelectLabel,
Value as SelectValue,
Content as SelectContent, Content as SelectContent,
Trigger as SelectTrigger, Trigger as SelectTrigger,
Separator as SelectSeparator, Separator as SelectSeparator,
ScrollDownButton as SelectScrollDownButton,
ScrollUpButton as SelectScrollUpButton,
}; };

View File

@ -1,36 +1,34 @@
<script lang="ts"> <script lang="ts">
import { Select as SelectPrimitive } from 'bits-ui'; import { Select as SelectPrimitive, type WithoutChild } from 'bits-ui';
import { scale } from 'svelte/transition'; import * as Select from './index.js';
import { cn, flyAndScale } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = SelectPrimitive.ContentProps; let {
ref = $bindable(null),
let className: $$Props['class'] = undefined; class: className,
export let sideOffset: $$Props['sideOffset'] = 4; sideOffset = 4,
export let inTransition: $$Props['inTransition'] = flyAndScale; portalProps,
export let inTransitionConfig: $$Props['inTransitionConfig'] = undefined; children,
export let outTransition: $$Props['outTransition'] = scale; ...restProps
export let outTransitionConfig: $$Props['outTransitionConfig'] = { }: WithoutChild<SelectPrimitive.ContentProps> & {
start: 0.95, portalProps?: SelectPrimitive.PortalProps;
opacity: 0, } = $props();
duration: 50,
};
export { className as class };
</script> </script>
<SelectPrimitive.Content <SelectPrimitive.Portal {...portalProps}>
{inTransition} <SelectPrimitive.Content
{inTransitionConfig} bind:ref
{outTransition}
{outTransitionConfig}
{sideOffset} {sideOffset}
class={cn( class={cn(
'relative z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md focus:outline-none', '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, className,
)} )}
{...$$restProps} {...restProps}
> >
<div class="w-full p-1"> <Select.ScrollUpButton />
<slot /> <SelectPrimitive.Viewport class={cn('h-[var(--bits-select-anchor-height)] w-full min-w-[var(--bits-select-anchor-width)] p-1')}>
</div> {@render children?.()}
</SelectPrimitive.Content> </SelectPrimitive.Viewport>
<Select.ScrollDownButton />
</SelectPrimitive.Content>
</SelectPrimitive.Portal>

View File

@ -0,0 +1,8 @@
<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} />

View File

@ -1,37 +1,37 @@
<script lang="ts"> <script lang="ts">
import { Select as SelectPrimitive } from 'bits-ui'; import { Select as SelectPrimitive, type WithoutChild } from 'bits-ui';
import Check from 'svelte-radix/Check.svelte'; import Check from 'lucide-svelte/icons/check';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = SelectPrimitive.ItemProps; let {
type $$Events = Required<SelectPrimitive.ItemEvents>; ref = $bindable(null),
class: className,
let className: $$Props['class'] = undefined; value,
export let value: $$Props['value']; label,
export let label: $$Props['label'] = undefined; children: childrenProp,
export let disabled: $$Props['disabled'] = undefined; ...restProps
export { className as class }; }: WithoutChild<SelectPrimitive.ItemProps> = $props();
</script> </script>
<SelectPrimitive.Item <SelectPrimitive.Item
bind:ref
{value} {value}
{disabled}
{label}
class={cn( 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', '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, className,
)} )}
{...$$restProps} {...restProps}
on:click
on:pointermove
on:focusin
> >
<span class="absolute right-2 flex h-3.5 w-3.5 items-center justify-center"> {#snippet children({ selected, highlighted })}
<SelectPrimitive.ItemIndicator> <span class="absolute right-2 flex size-3.5 items-center justify-center">
<Check class="h-4 w-4" /> {#if selected}
</SelectPrimitive.ItemIndicator> <Check class="size-4" />
{/if}
</span> </span>
<slot> {#if childrenProp}
{@render childrenProp({ selected, highlighted })}
{:else}
{label || value} {label || value}
</slot> {/if}
{/snippet}
</SelectPrimitive.Item> </SelectPrimitive.Item>

View File

@ -0,0 +1,11 @@
<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>

View File

@ -0,0 +1,11 @@
<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>

View File

@ -1,11 +1,9 @@
<script lang="ts"> <script lang="ts">
import { Select as SelectPrimitive } from 'bits-ui'; import type { Separator as SeparatorPrimitive } from 'bits-ui';
import { Separator } from '$lib/components/ui/separator/index.js';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = SelectPrimitive.SeparatorProps; let { ref = $bindable(null), class: className, ...restProps }: SeparatorPrimitive.RootProps = $props();
let className: $$Props['class'] = undefined;
export { className as class };
</script> </script>
<SelectPrimitive.Separator class={cn('-mx-1 my-1 h-px bg-muted', className)} {...$$restProps} /> <Separator bind:ref class={cn('-mx-1 my-1 h-px bg-muted', className)} {...restProps} />

View File

@ -1,24 +1,19 @@
<script lang="ts"> <script lang="ts">
import { Select as SelectPrimitive } from 'bits-ui'; import { Select as SelectPrimitive, type WithoutChild } from 'bits-ui';
import CaretSort from 'svelte-radix/CaretSort.svelte'; import ChevronDown from 'lucide-svelte/icons/chevron-down';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = SelectPrimitive.TriggerProps; let { ref = $bindable(null), class: className, children, ...restProps }: WithoutChild<SelectPrimitive.TriggerProps> = $props();
type $$Events = SelectPrimitive.TriggerEvents;
let className: $$Props['class'] = undefined;
export { className as class };
</script> </script>
<SelectPrimitive.Trigger <SelectPrimitive.Trigger
bind:ref
class={cn( 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 placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 aria-[invalid]:border-destructive [&>span]:line-clamp-1 data-[placeholder]:[&>span]:text-muted-foreground', '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, className,
)} )}
{...$$restProps} {...restProps}
> >
<slot /> {@render children?.()}
<div> <ChevronDown class="size-4 opacity-50" />
<CaretSort class="h-4 w-4 opacity-50" />
</div>
</SelectPrimitive.Trigger> </SelectPrimitive.Trigger>

View File

@ -0,0 +1,7 @@
import Root from './separator.svelte';
export {
Root,
//
Root as Separator,
};

View File

@ -0,0 +1,13 @@
<script lang="ts">
import { Separator as SeparatorPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js';
let { ref = $bindable(null), class: className, orientation = 'horizontal', ...restProps }: SeparatorPrimitive.RootProps = $props();
</script>
<SeparatorPrimitive.Root
bind:ref
class={cn('shrink-0 bg-border', orientation === 'horizontal' ? 'h-[1px] w-full' : 'min-h-full w-[1px]', className)}
{orientation}
{...restProps}
/>

View File

@ -1,7 +1,5 @@
import { Dialog as SheetPrimitive } from 'bits-ui'; import { Dialog as SheetPrimitive } from 'bits-ui';
import { type VariantProps, tv } from 'tailwind-variants';
import Portal from './sheet-portal.svelte';
import Overlay from './sheet-overlay.svelte'; import Overlay from './sheet-overlay.svelte';
import Content from './sheet-content.svelte'; import Content from './sheet-content.svelte';
import Header from './sheet-header.svelte'; import Header from './sheet-header.svelte';
@ -12,6 +10,7 @@ import Description from './sheet-description.svelte';
const Root = SheetPrimitive.Root; const Root = SheetPrimitive.Root;
const Close = SheetPrimitive.Close; const Close = SheetPrimitive.Close;
const Trigger = SheetPrimitive.Trigger; const Trigger = SheetPrimitive.Trigger;
const Portal = SheetPrimitive.Portal;
export { export {
Root, Root,
@ -36,71 +35,3 @@ export {
Title as SheetTitle, Title as SheetTitle,
Description as SheetDescription, Description as SheetDescription,
}; };
export const sheetVariants = tv({
base: 'bg-background fixed z-50 gap-4 p-6 shadow-lg',
variants: {
side: {
top: 'inset-x-0 top-0 border-b ',
bottom: 'inset-x-0 bottom-0 border-t',
left: 'inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm',
right: 'inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm',
},
},
defaultVariants: {
side: 'right',
},
});
export const sheetTransitions = {
top: {
in: {
y: '-100%',
duration: 500,
opacity: 1,
},
out: {
y: '-100%',
duration: 300,
opacity: 1,
},
},
bottom: {
in: {
y: '100%',
duration: 500,
opacity: 1,
},
out: {
y: '100%',
duration: 300,
opacity: 1,
},
},
left: {
in: {
x: '-100%',
duration: 500,
opacity: 1,
},
out: {
x: '-100%',
duration: 300,
opacity: 1,
},
},
right: {
in: {
x: '100%',
duration: 500,
opacity: 1,
},
out: {
x: '100%',
duration: 300,
opacity: 1,
},
},
};
export type Side = VariantProps<typeof sheetVariants>['side'];

View File

@ -1,39 +1,54 @@
<script lang="ts"> <script lang="ts" module>
import { Dialog as SheetPrimitive } from 'bits-ui'; import { tv, type VariantProps } from 'tailwind-variants';
import Cross2 from 'svelte-radix/Cross2.svelte';
import { fly } from 'svelte/transition';
import { SheetOverlay, SheetPortal, type Side, sheetTransitions, sheetVariants } from './index.js';
import { cn } from '$lib/utils.js';
type $$Props = SheetPrimitive.ContentProps & { export const sheetVariants = tv({
side?: Side; 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',
},
},
defaultVariants: {
side: 'right',
},
});
let className: $$Props['class'] = undefined; export type Side = VariantProps<typeof sheetVariants>['side'];
export let side: $$Props['side'] = 'right';
export { className as class };
export let inTransition: $$Props['inTransition'] = fly;
export let inTransitionConfig: $$Props['inTransitionConfig'] = sheetTransitions[side ?? 'right'].in;
export let outTransition: $$Props['outTransition'] = fly;
export let outTransitionConfig: $$Props['outTransitionConfig'] = sheetTransitions[side ?? 'right'].out;
</script> </script>
<SheetPortal> <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';
let {
ref = $bindable(null),
class: className,
portalProps,
side = 'right',
children,
...restProps
}: WithoutChildrenOrChild<SheetPrimitive.ContentProps> & {
portalProps?: SheetPrimitive.PortalProps;
side?: Side;
children: Snippet;
} = $props();
</script>
<SheetPrimitive.Portal {...portalProps}>
<SheetOverlay /> <SheetOverlay />
<SheetPrimitive.Content <SheetPrimitive.Content bind:ref class={cn(sheetVariants({ side }), className)} {...restProps}>
{inTransition} {@render children?.()}
{inTransitionConfig}
{outTransition}
{outTransitionConfig}
class={cn(sheetVariants({ side }), className)}
{...$$restProps}
>
<slot />
<SheetPrimitive.Close <SheetPrimitive.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-secondary" 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"
> >
<Cross2 class="h-4 w-4" /> <X class="size-4" />
<span class="sr-only">Close</span> <span class="sr-only">Close</span>
</SheetPrimitive.Close> </SheetPrimitive.Close>
</SheetPrimitive.Content> </SheetPrimitive.Content>
</SheetPortal> </SheetPrimitive.Portal>

View File

@ -2,12 +2,7 @@
import { Dialog as SheetPrimitive } from 'bits-ui'; import { Dialog as SheetPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = SheetPrimitive.DescriptionProps; let { ref = $bindable(null), class: className, ...restProps }: SheetPrimitive.DescriptionProps = $props();
let className: $$Props['class'] = undefined;
export { className as class };
</script> </script>
<SheetPrimitive.Description class={cn('text-sm text-muted-foreground', className)} {...$$restProps}> <SheetPrimitive.Description bind:ref class={cn('text-sm text-muted-foreground', className)} {...restProps} />
<slot />
</SheetPrimitive.Description>

View File

@ -1,13 +1,11 @@
<script lang="ts"> <script lang="ts">
import type { WithElementRef } from 'bits-ui';
import type { HTMLAttributes } from 'svelte/elements'; import type { HTMLAttributes } from 'svelte/elements';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = HTMLAttributes<HTMLDivElement>; let { ref = $bindable(null), class: className, children, ...restProps }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
let className: $$Props['class'] = undefined;
export { className as class };
</script> </script>
<div class={cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', className)} {...$$restProps}> <div bind:this={ref} class={cn('flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2', className)} {...restProps}>
<slot /> {@render children?.()}
</div> </div>

View File

@ -1,13 +1,11 @@
<script lang="ts"> <script lang="ts">
import type { HTMLAttributes } from 'svelte/elements'; import type { HTMLAttributes } from 'svelte/elements';
import type { WithElementRef } from 'bits-ui';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = HTMLAttributes<HTMLDivElement>; let { ref = $bindable(null), class: className, children, ...restProps }: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
let className: $$Props['class'] = undefined;
export { className as class };
</script> </script>
<div class={cn('flex flex-col space-y-2 text-center sm:text-left', className)} {...$$restProps}> <div bind:this={ref} class={cn('flex flex-col space-y-2 text-center sm:text-left', className)} {...restProps}>
<slot /> {@render children?.()}
</div> </div>

View File

@ -1,21 +1,15 @@
<script lang="ts"> <script lang="ts">
import { Dialog as SheetPrimitive } from 'bits-ui'; import { Dialog as SheetPrimitive } from 'bits-ui';
import { fade } from 'svelte/transition';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = SheetPrimitive.OverlayProps; let { ref = $bindable(null), class: className, ...restProps }: SheetPrimitive.OverlayProps = $props();
let className: $$Props['class'] = undefined;
export { className as class };
export let transition: $$Props['transition'] = fade;
export let transitionConfig: $$Props['transitionConfig'] = {
duration: 150,
};
</script> </script>
<SheetPrimitive.Overlay <SheetPrimitive.Overlay
{transition} bind:ref
{transitionConfig} class={cn(
class={cn('fixed inset-0 z-50 bg-background/80 backdrop-blur-sm', className)} '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',
{...$$restProps} className,
)}
{...restProps}
/> />

View File

@ -2,12 +2,7 @@
import { Dialog as SheetPrimitive } from 'bits-ui'; import { Dialog as SheetPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = SheetPrimitive.TitleProps; let { ref = $bindable(null), class: className, ...restProps }: SheetPrimitive.TitleProps = $props();
let className: $$Props['class'] = undefined;
export { className as class };
</script> </script>
<SheetPrimitive.Title class={cn('text-lg font-semibold text-foreground', className)} {...$$restProps}> <SheetPrimitive.Title bind:ref class={cn('text-lg font-semibold text-foreground', className)} {...restProps} />
<slot />
</SheetPrimitive.Title>

View File

@ -3,13 +3,16 @@ import Content from './tooltip-content.svelte';
const Root = TooltipPrimitive.Root; const Root = TooltipPrimitive.Root;
const Trigger = TooltipPrimitive.Trigger; const Trigger = TooltipPrimitive.Trigger;
const Provider = TooltipPrimitive.Provider;
export { export {
Root, Root,
Trigger, Trigger,
Content, Content,
Provider,
// //
Root as Tooltip, Root as Tooltip,
Content as TooltipContent, Content as TooltipContent,
Trigger as TooltipTrigger, Trigger as TooltipTrigger,
Provider as TooltipProvider,
}; };

View File

@ -1,25 +1,16 @@
<script lang="ts"> <script lang="ts">
import { Tooltip as TooltipPrimitive } from 'bits-ui'; import { Tooltip as TooltipPrimitive } from 'bits-ui';
import { cn, flyAndScale } from '$lib/utils.js'; import { cn } from '$lib/utils.js';
type $$Props = TooltipPrimitive.ContentProps; let { ref = $bindable(null), class: className, sideOffset = 4, ...restProps }: TooltipPrimitive.ContentProps = $props();
let className: $$Props['class'] = undefined;
export let sideOffset: $$Props['sideOffset'] = 4;
export let transition: $$Props['transition'] = flyAndScale;
export let transitionConfig: $$Props['transitionConfig'] = {
y: 8,
duration: 150,
};
export { className as class };
</script> </script>
<TooltipPrimitive.Content <TooltipPrimitive.Content
{transition} bind:ref
{transitionConfig}
{sideOffset} {sideOffset}
class={cn('z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground', className)} class={cn(
{...$$restProps} 'z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-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,
<slot /> )}
</TooltipPrimitive.Content> {...restProps}
/>

View File

@ -18,6 +18,7 @@
<div class="flex w-full items-center align-middle"> <div class="flex w-full items-center align-middle">
<p class="font-bold" id="currentuser-username">{data.user.username}</p> <p class="font-bold" id="currentuser-username">{data.user.username}</p>
</div> </div>
<Tooltip.Provider>
<Tooltip.Root> <Tooltip.Root>
<Tooltip.Trigger> <Tooltip.Trigger>
<Button size="icon" href="/account"><Cog /></Button> <Button size="icon" href="/account"><Cog /></Button>
@ -26,5 +27,6 @@
<p>Account Settings</p> <p>Account Settings</p>
</Tooltip.Content> </Tooltip.Content>
</Tooltip.Root> </Tooltip.Root>
</Tooltip.Provider>
</div> </div>
{/if} {/if}

View File

@ -1,53 +1,6 @@
import { type ClassValue, clsx } from 'clsx'; import { type ClassValue, clsx } from 'clsx';
import { cubicOut } from 'svelte/easing';
import type { TransitionConfig } from 'svelte/transition';
import { twMerge } from 'tailwind-merge'; import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) { export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs)); return twMerge(clsx(inputs));
} }
type FlyAndScaleParams = {
y?: number;
x?: number;
start?: number;
duration?: number;
};
export const flyAndScale = (node: Element, params: FlyAndScaleParams = { y: -8, x: 0, start: 0.95, duration: 150 }): TransitionConfig => {
const style = getComputedStyle(node);
const transform = style.transform === 'none' ? '' : style.transform;
const scaleConversion = (valueA: number, scaleA: [number, number], scaleB: [number, number]) => {
const [minA, maxA] = scaleA;
const [minB, maxB] = scaleB;
const percentage = (valueA - minA) / (maxA - minA);
const valueB = percentage * (maxB - minB) + minB;
return valueB;
};
const styleToString = (style: Record<string, number | string | undefined>): string => {
return Object.keys(style).reduce((str, key) => {
if (style[key] === undefined) return str;
return str + `${key}:${style[key]};`;
}, '');
};
return {
duration: params.duration ?? 200,
delay: 0,
css: (t) => {
const y = scaleConversion(t, [0, 1], [params.y ?? 5, 0]);
const x = scaleConversion(t, [0, 1], [params.x ?? 0, 0]);
const scale = scaleConversion(t, [0, 1], [params.start ?? 0.95, 1]);
return styleToString({
transform: `${transform} translate3d(${x}px, ${y}px, 0) scale(${scale})`,
opacity: t,
});
},
easing: cubicOut,
};
};

View File

@ -20,7 +20,14 @@
const channel: string = $derived(page.params.channel); const channel: string = $derived(page.params.channel);
let textareaRef: HTMLTextAreaElement | undefined = $state(); let textareaRef: HTMLTextAreaElement | undefined = $state();
let formref: HTMLFormElement | undefined = $state(); let formref: HTMLFormElement | undefined = $state();
let contextMenus: boolean[] = $state([]); let contextMenus: boolean[] = $state(Array(data.messages.length).fill(false));
$effect(() => {
const totalMessages = (socket?.messages?.length || 0) + data.messages.length;
if (contextMenus.length !== totalMessages) {
contextMenus = Array(totalMessages).fill(false);
}
});
function askNotificationPermission() { function askNotificationPermission() {
// Check if the browser supports notifications // Check if the browser supports notifications
@ -84,6 +91,7 @@
<div class="relative flex size-full h-full w-full flex-auto flex-grow flex-col-reverse overflow-x-hidden overflow-y-scroll rounded-lg border"> <div class="relative flex size-full h-full w-full flex-auto flex-grow flex-col-reverse overflow-x-hidden overflow-y-scroll rounded-lg border">
{#if data.messages.length != 0 || socket?.messages.length != 0} {#if data.messages.length != 0 || socket?.messages.length != 0}
<div class="flex flex-col-reverse"> <div class="flex flex-col-reverse">
{#if contextMenus.length === (socket?.messages?.length || 0) + data.messages.length}
<!-- Concatenate the two arrays together --> <!-- Concatenate the two arrays together -->
{#each [...(socket?.messages ?? []), ...data.messages] as message, i} {#each [...(socket?.messages ?? []), ...data.messages] as message, i}
<Message <Message
@ -97,6 +105,7 @@
{i} {i}
/> />
{/each} {/each}
{/if}
</div> </div>
{:else} {:else}
<EmptyChannel /> <EmptyChannel />
@ -112,6 +121,7 @@
shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1
focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50" focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
></textarea> ></textarea>
<Tooltip.Provider>
<Tooltip.Root> <Tooltip.Root>
<Tooltip.Trigger class="h-full min-h-10 w-14 {buttonVariants({ variant: 'default' })}" type="submit"> <Tooltip.Trigger class="h-full min-h-10 w-14 {buttonVariants({ variant: 'default' })}" type="submit">
<Send class="size-full" /> <Send class="size-full" />
@ -120,5 +130,6 @@
<p>Send</p> <p>Send</p>
</Tooltip.Content> </Tooltip.Content>
</Tooltip.Root> </Tooltip.Root>
</Tooltip.Provider>
</form> </form>
</div> </div>

View File

@ -1,6 +1,6 @@
import { fontFamily } from 'tailwindcss/defaultTheme'; import { fontFamily } from 'tailwindcss/defaultTheme';
import type { Config } from 'tailwindcss'; import type { Config } from 'tailwindcss';
import typography from '@tailwindcss/typography'; import tailwindcssAnimate from 'tailwindcss-animate';
const config: Config = { const config: Config = {
darkMode: ['class'], darkMode: ['class'],
@ -49,31 +49,48 @@ const config: Config = {
DEFAULT: 'hsl(var(--card) / <alpha-value>)', DEFAULT: 'hsl(var(--card) / <alpha-value>)',
foreground: 'hsl(var(--card-foreground) / <alpha-value>)', foreground: 'hsl(var(--card-foreground) / <alpha-value>)',
}, },
sidebar: {
DEFAULT: 'hsl(var(--sidebar-background))',
foreground: 'hsl(var(--sidebar-foreground))',
primary: 'hsl(var(--sidebar-primary))',
'primary-foreground': 'hsl(var(--sidebar-primary-foreground))',
accent: 'hsl(var(--sidebar-accent))',
'accent-foreground': 'hsl(var(--sidebar-accent-foreground))',
border: 'hsl(var(--sidebar-border))',
ring: 'hsl(var(--sidebar-ring))',
},
}, },
borderRadius: { borderRadius: {
xl: 'calc(var(--radius) + 4px)',
lg: 'var(--radius)', lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)', md: 'calc(var(--radius) - 2px)',
'background-color': '#22272E',
sm: 'calc(var(--radius) - 4px)', sm: 'calc(var(--radius) - 4px)',
}, },
fontFamily: { fontFamily: {
sans: [...fontFamily.sans], sans: [...fontFamily.sans],
}, },
typography: () => ({ keyframes: {
DEFAULT: { 'accordion-down': {
css: { from: { height: '0' },
pre: { to: { height: 'var(--bits-accordion-content-height)' },
'background-color': '#22272E', },
code: { 'accordion-up': {
'background-color': '#22272E', from: { height: 'var(--bits-accordion-content-height)' },
to: { height: '0' },
},
'caret-blink': {
'0%,70%,100%': { opacity: '1' },
'20%,50%': { opacity: '0' },
},
},
animation: {
'accordion-down': 'accordion-down 0.2s ease-out',
'accordion-up': 'accordion-up 0.2s ease-out',
'caret-blink': 'caret-blink 1.25s ease-out infinite',
}, },
}, },
}, },
}, plugins: [tailwindcssAnimate],
}),
},
},
plugins: [typography],
}; };
export default config; export default config;