first commit
linter / quality (push) Canceled after 0s
tests / ci (8.3) (push) Canceled after 0s
tests / ci (8.4) (push) Canceled after 0s
tests / ci (8.5) (push) Canceled after 0s

This commit is contained in:
salam committed 2026-10-06 18:53:03 +02:00
commit e687b4437b
418 files changed
+33577

No files matched your search

@@ -0,0 +1,27 @@
<script lang="ts">
import { Accordion as AccordionPrimitive } from "bits-ui";
import { cn, type WithoutChild } from "@/lib/utils.js";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithoutChild<AccordionPrimitive.ContentProps> = $props();
</script>
<AccordionPrimitive.Content
bind:ref
data-slot="accordion-content"
class="data-open:animate-accordion-down data-closed:animate-accordion-up text-sm overflow-hidden"
{...restProps}
>
<div
class={cn(
"pt-0 pb-2.5 [&_a]:hover:text-foreground [&_a]:underline [&_a]:underline-offset-3 [&_p:not(:last-child)]:mb-4",
className
)}
>
{@render children?.()}
</div>
</AccordionPrimitive.Content>
@@ -0,0 +1,17 @@
<script lang="ts">
import { Accordion as AccordionPrimitive } from "bits-ui";
import { cn } from "@/lib/utils.js";
let {
ref = $bindable(null),
class: className,
...restProps
}: AccordionPrimitive.ItemProps = $props();
</script>
<AccordionPrimitive.Item
bind:ref
data-slot="accordion-item"
class={cn("not-last:border-b", className)}
{...restProps}
/>
@@ -0,0 +1,32 @@
<script lang="ts">
import { Accordion as AccordionPrimitive } from "bits-ui";
import { cn, type WithoutChild } from "@/lib/utils.js";
import ChevronDownIcon from '@lucide/svelte/icons/chevron-down';
import ChevronUpIcon from '@lucide/svelte/icons/chevron-up';
let {
ref = $bindable(null),
class: className,
level = 3,
children,
...restProps
}: WithoutChild<AccordionPrimitive.TriggerProps> & {
level?: AccordionPrimitive.HeaderProps["level"];
} = $props();
</script>
<AccordionPrimitive.Header {level} class="flex">
<AccordionPrimitive.Trigger
data-slot="accordion-trigger"
bind:ref
class={cn(
"focus-visible:ring-ring/50 focus-visible:border-ring focus-visible:after:border-ring **:data-[slot=accordion-trigger-icon]:text-muted-foreground rounded-lg py-2.5 text-left text-sm font-medium hover:underline focus-visible:ring-3 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 group/accordion-trigger relative flex flex-1 items-start justify-between border border-transparent transition-all outline-none disabled:pointer-events-none disabled:opacity-50",
className
)}
{...restProps}
>
{@render children?.()}
<ChevronDownIcon data-slot="accordion-trigger-icon" class="cn-accordion-trigger-icon pointer-events-none shrink-0 group-aria-expanded/accordion-trigger:hidden" />
<ChevronUpIcon data-slot="accordion-trigger-icon" class="cn-accordion-trigger-icon pointer-events-none hidden shrink-0 group-aria-expanded/accordion-trigger:inline" />
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
@@ -0,0 +1,19 @@
<script lang="ts">
import { Accordion as AccordionPrimitive } from "bits-ui";
import { cn } from "@/lib/utils.js";
let {
ref = $bindable(null),
value = $bindable(),
class: className,
...restProps
}: AccordionPrimitive.RootProps = $props();
</script>
<AccordionPrimitive.Root
bind:ref
bind:value={value as never}
data-slot="accordion"
class={cn("cn-accordion flex w-full flex-col", className)}
{...restProps}
/>
@@ -0,0 +1,16 @@
import Root from "./accordion.svelte";
import Content from "./accordion-content.svelte";
import Item from "./accordion-item.svelte";
import Trigger from "./accordion-trigger.svelte";
export {
Root,
Content,
Item,
Trigger,
//
Root as Accordion,
Content as AccordionContent,
Item as AccordionItem,
Trigger as AccordionTrigger,
};
@@ -0,0 +1,29 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { cn } from '@/lib/utils';
type Variant = 'default' | 'destructive';
const base =
'relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current';
const variantClasses: Record<Variant, string> = {
default: 'bg-card text-card-foreground',
destructive:
'text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90',
};
let {
variant = 'default',
class: className = '',
children,
}: {
variant?: Variant;
class?: string;
children?: Snippet;
} = $props();
</script>
<div data-slot="alert" class={cn(base, variantClasses[variant], className)} role="alert">
{@render children?.()}
</div>
@@ -0,0 +1,19 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { cn } from '@/lib/utils';
let {
class: className = '',
children,
}: {
class?: string;
children?: Snippet;
} = $props();
</script>
<div
data-slot="alert-description"
class={cn('text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed', className)}
>
{@render children?.()}
</div>
@@ -0,0 +1,16 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { cn } from '@/lib/utils';
let {
class: className = '',
children,
}: {
class?: string;
children?: Snippet;
} = $props();
</script>
<div data-slot="alert-title" class={cn('col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight', className)}>
{@render children?.()}
</div>
@@ -0,0 +1,3 @@
export { default as Alert } from './Alert.svelte';
export { default as AlertDescription } from './AlertDescription.svelte';
export { default as AlertTitle } from './AlertTitle.svelte';
@@ -0,0 +1,13 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { cn } from '@/lib/utils';
let { class: className = '', children, ...rest }: { class?: string; children?: Snippet } = $props();
</script>
<div
class={cn('relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full', className)}
{...rest}
>
{@render children?.()}
</div>
@@ -0,0 +1,16 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { cn } from '@/lib/utils';
let { class: className = '', children, ...rest }: { class?: string; children?: Snippet } = $props();
</script>
<div
class={cn(
'flex h-full w-full items-center justify-center rounded-full bg-muted text-sm font-medium text-muted-foreground',
className,
)}
{...rest}
>
{@render children?.()}
</div>
@@ -0,0 +1,7 @@
<script lang="ts">
import { cn } from '@/lib/utils';
let { class: className = '', ...rest } = $props();
</script>
<img class={cn('aspect-square h-full w-full', className)} {...rest} />
@@ -0,0 +1,3 @@
export { default as Avatar } from './Avatar.svelte';
export { default as AvatarFallback } from './AvatarFallback.svelte';
export { default as AvatarImage } from './AvatarImage.svelte';
@@ -0,0 +1,33 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { cn } from '@/lib/utils';
type Variant = 'default' | 'secondary' | 'destructive' | 'outline';
const base =
'inline-flex items-center justify-center rounded-full border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden';
const variantClasses: Record<Variant, string> = {
default: 'border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90',
secondary: 'border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90',
destructive:
'border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
outline: 'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground',
};
let {
variant = 'default',
class: className = '',
children,
...rest
}: {
variant?: Variant;
class?: string;
children?: Snippet;
[key: string]: unknown;
} = $props();
</script>
<div data-slot="badge" class={cn(base, variantClasses[variant], className)} {...rest}>
{@render children?.()}
</div>
@@ -0,0 +1 @@
export { default as Badge } from './Badge.svelte';
@@ -0,0 +1,3 @@
<nav aria-label="breadcrumb" class="text-sm">
<slot />
</nav>
@@ -0,0 +1,3 @@
<li class="inline-flex items-center gap-1.5">
<slot />
</li>
@@ -0,0 +1,26 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { cn } from '@/lib/utils';
let {
children,
asChild = false,
class: className = '',
...rest
}: {
children?: Snippet<[Record<string, unknown>]>;
asChild?: boolean;
class?: string;
[key: string]: unknown;
} = $props();
const classes = () => cn('transition-colors hover:text-foreground', className);
</script>
{#if asChild}
{@render children?.({ class: classes(), ...rest })}
{:else}
<a class={classes()} {...rest}>
{@render children?.({})}
</a>
{/if}
@@ -0,0 +1,3 @@
<ol class="flex flex-wrap items-center gap-1.5">
<slot />
</ol>
@@ -0,0 +1,3 @@
<span class="font-medium text-foreground">
<slot />
</span>
@@ -0,0 +1 @@
<span class="text-muted-foreground">/</span>
@@ -0,0 +1,6 @@
export { default as Breadcrumb } from './Breadcrumb.svelte';
export { default as BreadcrumbItem } from './BreadcrumbItem.svelte';
export { default as BreadcrumbLink } from './BreadcrumbLink.svelte';
export { default as BreadcrumbList } from './BreadcrumbList.svelte';
export { default as BreadcrumbPage } from './BreadcrumbPage.svelte';
export { default as BreadcrumbSeparator } from './BreadcrumbSeparator.svelte';
@@ -0,0 +1,67 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { cn } from '@/lib/utils';
type Variant =
| 'default'
| 'secondary'
| 'ghost'
| 'destructive'
| 'outline'
| 'link';
type Size = 'default' | 'sm' | 'lg' | 'icon';
type AsChildProps = {
class?: string;
onClick?: (event: MouseEvent) => void;
[key: string]: any;
};
const base =
'inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50';
const variants: Record<Variant, string> = {
default: 'bg-primary text-primary-foreground shadow hover:bg-primary/90',
secondary:
'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
destructive:
'bg-destructive text-destructive-foreground shadow hover:bg-destructive/90',
outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline',
};
const sizes: Record<Size, string> = {
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',
};
let {
children,
asChild = false,
variant = 'default',
size = 'default',
class: className = '',
type = 'button',
...rest
}: {
children?: Snippet<[AsChildProps]>;
asChild?: boolean;
variant?: Variant;
size?: Size;
class?: string;
type?: 'button' | 'submit' | 'reset';
[key: string]: unknown;
} = $props();
const classes = () => cn(base, variants[variant], sizes[size], className);
</script>
{#if asChild}
{@render children?.({ class: classes(), ...rest })}
{:else}
<button class={classes()} type={type} {...rest}>
{@render children?.({})}
</button>
{/if}
@@ -0,0 +1 @@
export { default as Button } from './Button.svelte';
@@ -0,0 +1,16 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { cn } from '@/lib/utils';
let {
class: className = '',
children,
}: {
class?: string;
children?: Snippet;
} = $props();
</script>
<div data-slot="card" class={cn('bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm', className)}>
{@render children?.()}
</div>
@@ -0,0 +1,16 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { cn } from '@/lib/utils';
let {
class: className = '',
children,
}: {
class?: string;
children?: Snippet;
} = $props();
</script>
<div data-slot="card-action" class={cn('col-start-2 row-span-2 row-start-1 self-start justify-self-end', className)}>
{@render children?.()}
</div>
@@ -0,0 +1,16 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { cn } from '@/lib/utils';
let {
class: className = '',
children,
}: {
class?: string;
children?: Snippet;
} = $props();
</script>
<div data-slot="card-content" class={cn('px-6', className)}>
{@render children?.()}
</div>
@@ -0,0 +1,16 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { cn } from '@/lib/utils';
let {
class: className = '',
children,
}: {
class?: string;
children?: Snippet;
} = $props();
</script>
<p data-slot="card-description" class={cn('text-muted-foreground text-sm', className)}>
{@render children?.()}
</p>
@@ -0,0 +1,16 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { cn } from '@/lib/utils';
let {
class: className = '',
children,
}: {
class?: string;
children?: Snippet;
} = $props();
</script>
<div data-slot="card-footer" class={cn('flex items-center px-6 [.border-t]:pt-6', className)}>
{@render children?.()}
</div>
@@ -0,0 +1,22 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { cn } from '@/lib/utils';
let {
class: className = '',
children,
}: {
class?: string;
children?: Snippet;
} = $props();
</script>
<div
data-slot="card-header"
class={cn(
'@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6',
className,
)}
>
{@render children?.()}
</div>
@@ -0,0 +1,16 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { cn } from '@/lib/utils';
let {
class: className = '',
children,
}: {
class?: string;
children?: Snippet;
} = $props();
</script>
<h3 data-slot="card-title" class={cn('leading-none font-semibold', className)}>
{@render children?.()}
</h3>
+7
View File
@@ -0,0 +1,7 @@
export { default as Card } from './Card.svelte';
export { default as CardAction } from './CardAction.svelte';
export { default as CardContent } from './CardContent.svelte';
export { default as CardDescription } from './CardDescription.svelte';
export { default as CardFooter } from './CardFooter.svelte';
export { default as CardHeader } from './CardHeader.svelte';
export { default as CardTitle } from './CardTitle.svelte';
@@ -0,0 +1,47 @@
<script lang="ts">
import { cn } from '@/lib/utils';
import Check from 'lucide-svelte/icons/check';
let {
checked = $bindable(false),
disabled = false,
class: className = '',
id,
name,
value,
...rest
}: {
checked?: boolean;
disabled?: boolean;
class?: string;
id?: string;
name?: string;
value?: string;
[key: string]: unknown;
} = $props();
</script>
<button
type="button"
role="checkbox"
aria-checked={checked}
data-state={checked ? 'checked' : 'unchecked'}
data-slot="checkbox"
{disabled}
{id}
class={cn(
'peer border-input data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-lg border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',
className,
)}
onclick={() => { if (!disabled) checked = !checked; }}
{...rest}
>
{#if checked}
<div data-slot="checkbox-indicator" class="grid place-content-center text-current transition-none">
<Check class="size-3.5" />
</div>
{/if}
</button>
{#if name}
<input type="hidden" {name} {value} />
{/if}
@@ -0,0 +1 @@
export { default as Checkbox } from './Checkbox.svelte';
@@ -0,0 +1,17 @@
<script lang="ts">
import type { Snippet } from 'svelte';
let {
open = $bindable(false),
disabled = false,
children,
}: {
open?: boolean;
disabled?: boolean;
children?: Snippet;
} = $props();
</script>
<div data-slot="collapsible" data-state={open ? 'open' : 'closed'} data-disabled={disabled || undefined}>
{@render children?.()}
</div>
@@ -0,0 +1,15 @@
<script lang="ts">
import type { Snippet } from 'svelte';
let {
children,
...rest
}: {
children?: Snippet;
[key: string]: unknown;
} = $props();
</script>
<div data-slot="collapsible-content" {...rest}>
{@render children?.()}
</div>
@@ -0,0 +1,15 @@
<script lang="ts">
import type { Snippet } from 'svelte';
let {
children,
...rest
}: {
children?: Snippet;
[key: string]: unknown;
} = $props();
</script>
<button type="button" data-slot="collapsible-trigger" {...rest}>
{@render children?.()}
</button>
@@ -0,0 +1,3 @@
export { default as Collapsible } from './Collapsible.svelte';
export { default as CollapsibleContent } from './CollapsibleContent.svelte';
export { default as CollapsibleTrigger } from './CollapsibleTrigger.svelte';
@@ -0,0 +1,23 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { setContext } from 'svelte';
import { DIALOG_CONTEXT, type DialogContext } from './context';
let {
open = $bindable(false),
onOpenChange,
children,
}: { open?: boolean; onOpenChange?: (value: boolean) => void; children?: Snippet } = $props();
const context: DialogContext = {
open: () => open,
setOpen: (value: boolean) => {
open = value;
onOpenChange?.(value);
},
};
setContext(DIALOG_CONTEXT, context);
</script>
{@render children?.()}
@@ -0,0 +1,19 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { getContext } from 'svelte';
import { DIALOG_CONTEXT, type DialogContext } from './context';
let { asChild = false, children }: { asChild?: boolean; children?: Snippet<[Record<string, unknown>]> } = $props();
const { setOpen } = getContext<DialogContext>(DIALOG_CONTEXT);
const handleClick = () => setOpen(false);
</script>
{#if asChild}
{@render children?.({ onClick: handleClick })}
{:else}
<button type="button" onclick={handleClick}>
{@render children?.({})}
</button>
{/if}
@@ -0,0 +1,33 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { getContext } from 'svelte';
import { cn } from '@/lib/utils';
import { DIALOG_CONTEXT, type DialogContext } from './context';
let { class: className = '', children }: { class?: string; children?: Snippet } = $props();
const { open, setOpen } = getContext<DialogContext>(DIALOG_CONTEXT);
const close = () => setOpen(false);
</script>
{#if open()}
<div class="fixed inset-0 z-50 flex items-center justify-center">
<button
type="button"
class="fixed inset-0 bg-black/50"
aria-label="Close"
onclick={close}
></button>
<div
class={cn(
'relative z-10 w-full max-w-lg rounded-lg border bg-background p-6 shadow-lg',
className,
)}
role="dialog"
aria-modal="true"
>
{@render children?.()}
</div>
</div>
{/if}
@@ -0,0 +1,3 @@
<p class="text-sm text-muted-foreground">
<slot />
</p>
@@ -0,0 +1,3 @@
<div class="flex flex-col-reverse gap-2 sm:flex-row sm:justify-end">
<slot />
</div>
@@ -0,0 +1,3 @@
<h2 class="text-lg font-semibold leading-none tracking-tight">
<slot />
</h2>
@@ -0,0 +1,19 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { getContext } from 'svelte';
import { DIALOG_CONTEXT, type DialogContext } from './context';
let { asChild = false, children }: { asChild?: boolean; children?: Snippet<[Record<string, unknown>]> } = $props();
const { setOpen, open } = getContext<DialogContext>(DIALOG_CONTEXT);
const handleClick = () => setOpen(true);
</script>
{#if asChild}
{@render children?.({ onClick: handleClick, 'aria-expanded': open() })}
{:else}
<button type="button" onclick={handleClick} aria-expanded={open()}>
{@render children?.({})}
</button>
{/if}
@@ -0,0 +1,6 @@
export type DialogContext = {
open: () => boolean;
setOpen: (value: boolean) => void;
};
export const DIALOG_CONTEXT = Symbol('dialog');
@@ -0,0 +1,7 @@
export { default as Dialog } from './Dialog.svelte';
export { default as DialogClose } from './DialogClose.svelte';
export { default as DialogContent } from './DialogContent.svelte';
export { default as DialogDescription } from './DialogDescription.svelte';
export { default as DialogFooter } from './DialogFooter.svelte';
export { default as DialogTitle } from './DialogTitle.svelte';
export { default as DialogTrigger } from './DialogTrigger.svelte';
@@ -0,0 +1,47 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { onMount, setContext } from 'svelte';
import { cn } from '@/lib/utils';
import { DROPDOWN_MENU_CONTEXT, type DropdownMenuContext } from './context';
let {
open = $bindable(false),
class: className = '',
children,
}: {
open?: boolean;
class?: string;
children?: Snippet;
} = $props();
let menuElement: HTMLDivElement | null = null;
const context: DropdownMenuContext = {
open: () => open,
setOpen: (value: boolean) => {
open = value;
},
};
setContext(DROPDOWN_MENU_CONTEXT, context);
onMount(() => {
const handlePointerDown = (event: PointerEvent) => {
if (!open || !menuElement) return;
const target = event.target as Node | null;
if (target && !menuElement.contains(target)) {
open = false;
}
};
document.addEventListener('pointerdown', handlePointerDown);
return () => {
document.removeEventListener('pointerdown', handlePointerDown);
};
});
</script>
<div class={cn('relative', className)} bind:this={menuElement}>
{@render children?.()}
</div>
@@ -0,0 +1,67 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { getContext } from 'svelte';
import { cn } from '@/lib/utils';
import { DROPDOWN_MENU_CONTEXT, type DropdownMenuContext } from './context';
let {
align = 'start',
side = 'bottom',
sideOffset = 0,
class: className = '',
children,
}: {
align?: 'start' | 'center' | 'end';
side?: 'top' | 'right' | 'bottom' | 'left';
sideOffset?: number;
class?: string;
children?: Snippet;
} = $props();
const { open, setOpen } = getContext<DropdownMenuContext>(DROPDOWN_MENU_CONTEXT);
const alignClasses: Record<string, string> = {
start: 'left-0',
center: 'left-1/2 -translate-x-1/2',
end: 'right-0',
};
const sideClasses: Record<string, string> = {
bottom: 'top-full',
top: 'bottom-full',
left: 'right-full',
right: 'left-full',
};
const close = () => setOpen(false);
const offsetStyle = () => {
switch (side) {
case 'top':
return `margin-bottom: ${sideOffset}px;`;
case 'left':
return `margin-right: ${sideOffset}px;`;
case 'right':
return `margin-left: ${sideOffset}px;`;
default:
return `margin-top: ${sideOffset}px;`;
}
};
</script>
{#if open()}
<div
class={cn(
'absolute z-50 min-w-48 rounded-md border bg-popover p-2 text-popover-foreground shadow-md',
alignClasses[align] ?? alignClasses.start,
sideClasses[side] ?? sideClasses.bottom,
className,
)}
style={offsetStyle()}
role="menu"
tabindex="-1"
onkeydown={(event) => event.key === 'Escape' && close()}
>
{@render children?.()}
</div>
{/if}
@@ -0,0 +1,3 @@
<div class="flex flex-col">
<slot />
</div>
@@ -0,0 +1,40 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { getContext } from 'svelte';
import { cn } from '@/lib/utils';
import { DROPDOWN_MENU_CONTEXT, type DropdownMenuContext } from './context';
type AsChildProps = {
class?: string;
onClick?: () => void;
[key: string]: any;
};
let {
asChild = false,
class: className = '',
children,
}: {
asChild?: boolean;
class?: string;
children?: Snippet<[AsChildProps]>;
} = $props();
const { setOpen } = getContext<DropdownMenuContext>(DROPDOWN_MENU_CONTEXT);
const handleClick = () => setOpen(false);
const classes = () =>
cn(
'flex w-full cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent hover:text-accent-foreground',
className,
);
</script>
{#if asChild}
{@render children?.({ class: classes(), onClick: handleClick })}
{:else}
<button type="button" class={classes()} onclick={handleClick}>
{@render children?.({})}
</button>
{/if}
@@ -0,0 +1,9 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { cn } from '@/lib/utils';
let { class: className = '', children }: { class?: string; children?: Snippet } = $props();
</script>
<div class={cn('px-2 py-1.5 text-sm font-semibold', className)}>
{@render children?.()}
</div>
@@ -0,0 +1 @@
<div class="my-1 h-px bg-border"></div>
@@ -0,0 +1,29 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { getContext } from 'svelte';
import { DROPDOWN_MENU_CONTEXT, type DropdownMenuContext } from './context';
type TriggerProps = {
onclick?: (event: MouseEvent) => void;
'aria-expanded'?: boolean;
'data-state'?: 'open' | 'closed';
[key: string]: any;
};
let {
asChild = false,
children,
}: { asChild?: boolean; children?: Snippet<[TriggerProps]> } = $props();
const { open, setOpen } = getContext<DropdownMenuContext>(DROPDOWN_MENU_CONTEXT);
const handleClick = () => setOpen(!open());
</script>
{#if asChild}
{@render children?.({ onclick: handleClick, 'aria-expanded': open(), 'data-state': open() ? 'open' : 'closed' })}
{:else}
<button type="button" onclick={handleClick} aria-expanded={open()}>
{@render children?.({})}
</button>
{/if}
@@ -0,0 +1,6 @@
export type DropdownMenuContext = {
open: () => boolean;
setOpen: (value: boolean) => void;
};
export const DROPDOWN_MENU_CONTEXT = Symbol('dropdown-menu');
@@ -0,0 +1,7 @@
export { default as DropdownMenu } from './DropdownMenu.svelte';
export { default as DropdownMenuContent } from './DropdownMenuContent.svelte';
export { default as DropdownMenuGroup } from './DropdownMenuGroup.svelte';
export { default as DropdownMenuItem } from './DropdownMenuItem.svelte';
export { default as DropdownMenuLabel } from './DropdownMenuLabel.svelte';
export { default as DropdownMenuSeparator } from './DropdownMenuSeparator.svelte';
export { default as DropdownMenuTrigger } from './DropdownMenuTrigger.svelte';
@@ -0,0 +1,71 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { cn } from '@/lib/utils';
import { setInputOTPContext } from './context';
let {
value = $bindable(''),
maxlength = 6,
disabled = false,
id,
class: className = '',
children,
...rest
}: {
value?: string;
maxlength?: number;
disabled?: boolean;
id?: string;
class?: string;
children?: Snippet;
[key: string]: unknown;
} = $props();
let inputRef: HTMLInputElement;
let isFocused = $state(false);
const activeIndex = $derived(Math.min(value.length, maxlength - 1));
setInputOTPContext({
value: () => value,
activeIndex: () => (isFocused ? activeIndex : -1),
isFocused: () => isFocused,
focus: () => inputRef?.focus(),
});
function handleInput(e: Event) {
const target = e.target as HTMLInputElement;
value = target.value.replace(/[^0-9]/g, '').slice(0, maxlength);
}
function handleKeyDown(e: KeyboardEvent) {
if (e.key === 'Backspace' && value.length > 0) {
e.preventDefault();
value = value.slice(0, -1);
}
}
</script>
<div
data-slot="input-otp"
class={cn('relative flex items-center gap-2 has-disabled:opacity-50', className)}
role="group"
{...rest}
>
<input
bind:this={inputRef}
{id}
{disabled}
type="text"
inputmode="numeric"
autocomplete="one-time-code"
{value}
oninput={handleInput}
onkeydown={handleKeyDown}
onfocus={() => (isFocused = true)}
onblur={() => (isFocused = false)}
class="absolute inset-0 z-10 h-full w-full cursor-text opacity-0 disabled:cursor-not-allowed"
/>
<div class="pointer-events-none">
{@render children?.()}
</div>
</div>
@@ -0,0 +1,16 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { cn } from '@/lib/utils';
let {
class: className = '',
children,
}: {
class?: string;
children?: Snippet;
} = $props();
</script>
<div data-slot="input-otp-group" class={cn('flex items-center', className)}>
{@render children?.()}
</div>
@@ -0,0 +1,7 @@
<script lang="ts">
import Minus from 'lucide-svelte/icons/minus';
</script>
<div data-slot="input-otp-separator" role="separator">
<Minus />
</div>
@@ -0,0 +1,34 @@
<script lang="ts">
import { cn } from '@/lib/utils';
import { getInputOTPContext } from './context';
let {
index,
class: className = '',
}: {
index: number;
class?: string;
} = $props();
const ctx = getInputOTPContext();
const char = $derived(ctx.value()[index]);
const isActive = $derived(ctx.isFocused() && ctx.activeIndex() === index);
const hasFakeCaret = $derived(isActive && !char);
</script>
<div
data-slot="input-otp-slot"
data-active={isActive}
class={cn(
'data-[active=true]:border-ring data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive dark:bg-input/30 border-input relative flex h-9 w-9 items-center justify-center border-y border-r text-sm shadow-xs transition-all outline-none first:rounded-l-md first:border-l last:rounded-r-md data-[active=true]:z-10 data-[active=true]:ring-[3px]',
className,
)}
>
{char ?? ''}
{#if hasFakeCaret}
<div class="pointer-events-none absolute inset-0 flex items-center justify-center">
<div class="animate-caret-blink bg-foreground h-4 w-px duration-1000"></div>
</div>
{/if}
</div>
@@ -0,0 +1,18 @@
import { getContext, setContext } from 'svelte';
export const INPUT_OTP_CONTEXT = Symbol('input-otp');
export type InputOTPContext = {
value: () => string;
activeIndex: () => number;
isFocused: () => boolean;
focus: () => void;
};
export function setInputOTPContext(ctx: InputOTPContext): void {
setContext(INPUT_OTP_CONTEXT, ctx);
}
export function getInputOTPContext(): InputOTPContext {
return getContext<InputOTPContext>(INPUT_OTP_CONTEXT);
}
@@ -0,0 +1,4 @@
export { default as InputOTP } from './InputOTP.svelte';
export { default as InputOTPGroup } from './InputOTPGroup.svelte';
export { default as InputOTPSeparator } from './InputOTPSeparator.svelte';
export { default as InputOTPSlot } from './InputOTPSlot.svelte';
@@ -0,0 +1,14 @@
<script lang="ts">
import { cn } from '@/lib/utils';
let { class: className = '', type = 'text', ...rest } = $props();
</script>
<input
type={type}
class={cn(
'flex h-9 w-full rounded-md border border-input bg-background 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-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
className,
)}
{...rest}
/>
@@ -0,0 +1 @@
export { default as Input } from './Input.svelte';
@@ -0,0 +1,19 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import type { HTMLLabelAttributes } from 'svelte/elements';
import { cn } from '@/lib/utils';
type Props = HTMLLabelAttributes & { children?: Snippet };
let { class: className = '', children, ...rest }: Props = $props();
</script>
<label
class={cn(
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
className,
)}
{...rest}
>
{@render children?.()}
</label>
@@ -0,0 +1 @@
export { default as Label } from './Label.svelte';
@@ -0,0 +1,16 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { cn } from '@/lib/utils';
let {
class: className = '',
children,
}: {
class?: string;
children?: Snippet;
} = $props();
</script>
<nav class={cn('group/navigation-menu relative flex max-w-max flex-1 items-center justify-center', className)}>
{@render children?.()}
</nav>
@@ -0,0 +1,16 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { cn } from '@/lib/utils';
let {
class: className = '',
children,
}: {
class?: string;
children?: Snippet;
} = $props();
</script>
<li class={cn('list-none', className)}>
{@render children?.()}
</li>
@@ -0,0 +1,19 @@
<script lang="ts">
import type { Snippet } from 'svelte';
let {
href = '#',
class: className = 'inline-flex items-center',
children,
...rest
}: {
href?: string;
class?: string;
children?: Snippet;
[key: string]: unknown;
} = $props();
</script>
<a {href} class={className} {...rest}>
{@render children?.()}
</a>
@@ -0,0 +1,16 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { cn } from '@/lib/utils';
let {
class: className = '',
children,
}: {
class?: string;
children?: Snippet;
} = $props();
</script>
<ul class={cn('group flex flex-1 list-none items-center justify-center gap-1', className)}>
{@render children?.()}
</ul>
@@ -0,0 +1,28 @@
import Root from "./navigation-menu.svelte";
import Content from "./navigation-menu-content.svelte";
import Indicator from "./navigation-menu-indicator.svelte";
import Item from "./navigation-menu-item.svelte";
import Link from "./navigation-menu-link.svelte";
import List from "./navigation-menu-list.svelte";
import Trigger from "./navigation-menu-trigger.svelte";
import Viewport from "./navigation-menu-viewport.svelte";
export {
Root,
Content,
Indicator,
Item,
Link,
List,
Trigger,
Viewport,
//
Root as NavigationMenuRoot,
Content as NavigationMenuContent,
Indicator as NavigationMenuIndicator,
Item as NavigationMenuItem,
Link as NavigationMenuLink,
List as NavigationMenuList,
Trigger as NavigationMenuTrigger,
Viewport as NavigationMenuViewport,
};
@@ -0,0 +1,20 @@
<script lang="ts">
import { NavigationMenu as NavigationMenuPrimitive } from "bits-ui";
import { cn } from "@/lib/utils.js";
let {
ref = $bindable(null),
class: className,
...restProps
}: NavigationMenuPrimitive.ContentProps = $props();
</script>
<NavigationMenuPrimitive.Content
bind:ref
data-slot="navigation-menu-content"
class={cn(
"data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:data-open:animate-in group-data-[viewport=false]/navigation-menu:data-closed:animate-out group-data-[viewport=false]/navigation-menu:data-closed:zoom-out-95 group-data-[viewport=false]/navigation-menu:data-open:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-open:fade-in-0 group-data-[viewport=false]/navigation-menu:data-closed:fade-out-0 group-data-[viewport=false]/navigation-menu:ring-foreground/10 p-1 ease-[cubic-bezier(0.22,1,0.36,1)] group-data-[viewport=false]/navigation-menu:rounded-lg group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:ring-1 group-data-[viewport=false]/navigation-menu:duration-300 top-0 left-0 w-full group-data-[viewport=false]/navigation-menu:top-full group-data-[viewport=false]/navigation-menu:mt-1.5 group-data-[viewport=false]/navigation-menu:overflow-hidden **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none md:absolute md:w-auto",
className
)}
{...restProps}
/>
@@ -0,0 +1,22 @@
<script lang="ts">
import { NavigationMenu as NavigationMenuPrimitive } from "bits-ui";
import { cn } from "@/lib/utils.js";
let {
ref = $bindable(null),
class: className,
...restProps
}: NavigationMenuPrimitive.IndicatorProps = $props();
</script>
<NavigationMenuPrimitive.Indicator
bind:ref
data-slot="navigation-menu-indicator"
class={cn(
"data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden",
className
)}
{...restProps}
>
<div class="bg-border rounded-tl-sm shadow-md relative top-[60%] h-2 w-2 rotate-45"></div>
</NavigationMenuPrimitive.Indicator>
@@ -0,0 +1,17 @@
<script lang="ts">
import { NavigationMenu as NavigationMenuPrimitive } from "bits-ui";
import { cn } from "@/lib/utils.js";
let {
ref = $bindable(null),
class: className,
...restProps
}: NavigationMenuPrimitive.ItemProps = $props();
</script>
<NavigationMenuPrimitive.Item
bind:ref
data-slot="navigation-menu-item"
class={cn("cn-navigation-menu-item relative", className)}
{...restProps}
/>
@@ -0,0 +1,17 @@
<script lang="ts">
import { NavigationMenu as NavigationMenuPrimitive } from "bits-ui";
import { cn } from "@/lib/utils.js";
let {
ref = $bindable(null),
class: className,
...restProps
}: NavigationMenuPrimitive.LinkProps = $props();
</script>
<NavigationMenuPrimitive.Link
bind:ref
data-slot="navigation-menu-link"
class={cn("data-active:focus:bg-muted data-active:hover:bg-muted data-active:bg-muted/50 focus-visible:ring-ring/50 hover:bg-muted focus:bg-muted flex items-center gap-2 rounded-lg p-2 text-sm transition-all outline-none focus-visible:ring-3 focus-visible:outline-1 in-data-[slot=navigation-menu-content]:rounded-md [&_svg:not([class*='size-'])]:size-4", className)}
{...restProps}
/>
@@ -0,0 +1,20 @@
<script lang="ts">
import { NavigationMenu as NavigationMenuPrimitive } from "bits-ui";
import { cn } from "@/lib/utils.js";
let {
ref = $bindable(null),
class: className,
...restProps
}: NavigationMenuPrimitive.ListProps = $props();
</script>
<NavigationMenuPrimitive.List
bind:ref
data-slot="navigation-menu-list"
class={cn(
"gap-0 group flex flex-1 list-none items-center justify-center",
className
)}
{...restProps}
/>
@@ -0,0 +1,29 @@
<script lang="ts" module>
import { cn } from "@/lib/utils.js";
import { tv } from "tailwind-variants";
export const navigationMenuTriggerStyle = tv({
base: "hover:bg-muted focus:bg-muted data-open:hover:bg-muted data-open:focus:bg-muted data-open:bg-muted/50 focus-visible:ring-ring/50 data-popup-open:bg-muted/50 data-popup-open:hover:bg-muted rounded-lg px-2.5 py-1.5 text-sm font-medium transition-all focus-visible:ring-3 focus-visible:outline-1 disabled:opacity-50 group/navigation-menu-trigger inline-flex h-9 w-max items-center justify-center outline-none disabled:pointer-events-none",
});
</script>
<script lang="ts">
import { NavigationMenu as NavigationMenuPrimitive } from "bits-ui";
import ChevronDownIcon from '@lucide/svelte/icons/chevron-down';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: NavigationMenuPrimitive.TriggerProps = $props();
</script>
<NavigationMenuPrimitive.Trigger
bind:ref
data-slot="navigation-menu-trigger"
class={cn(navigationMenuTriggerStyle(), "group", className)}
{...restProps}
>
{@render children?.()}
<ChevronDownIcon class="relative top-px ml-1 size-3 transition duration-300 group-data-open/navigation-menu-trigger:rotate-180 group-data-popup-open/navigation-menu-trigger:rotate-180" aria-hidden="true" />
</NavigationMenuPrimitive.Trigger>
@@ -0,0 +1,22 @@
<script lang="ts">
import { NavigationMenu as NavigationMenuPrimitive } from "bits-ui";
import { cn } from "@/lib/utils.js";
let {
ref = $bindable(null),
class: className,
...restProps
}: NavigationMenuPrimitive.ViewportProps = $props();
</script>
<div class={cn("absolute start-0 top-full isolate z-50 flex justify-center")}>
<NavigationMenuPrimitive.Viewport
bind:ref
data-slot="navigation-menu-viewport"
class={cn(
"bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:zoom-out-90 data-open:zoom-in-90 ring-foreground/10 rounded-lg shadow ring-1 duration-100 origin-top-center relative mt-1.5 h-[calc(var(--bits-navigation-menu-viewport-height)+1rem)] w-full overflow-hidden md:w-[calc(var(--bits-navigation-menu-viewport-width)+1rem)]",
className
)}
{...restProps}
/>
</div>
@@ -0,0 +1,31 @@
<script lang="ts">
import { NavigationMenu as NavigationMenuPrimitive } from "bits-ui";
import { cn } from "@/lib/utils.js";
import NavigationMenuViewport from "./navigation-menu-viewport.svelte";
let {
ref = $bindable(null),
class: className,
viewport = true,
children,
...restProps
}: NavigationMenuPrimitive.RootProps & {
viewport?: boolean;
} = $props();
</script>
<NavigationMenuPrimitive.Root
bind:ref
data-slot="navigation-menu"
data-viewport={viewport}
class={cn(
"max-w-max group/navigation-menu relative flex max-w-max flex-1 items-center justify-center",
className
)}
{...restProps}
>
{@render children?.()}
{#if viewport}
<NavigationMenuViewport />
{/if}
</NavigationMenuPrimitive.Root>
@@ -0,0 +1,31 @@
import Root from "./pagination.svelte";
import Content from "./pagination-content.svelte";
import Item from "./pagination-item.svelte";
import Link from "./pagination-link.svelte";
import PrevButton from "./pagination-prev-button.svelte";
import NextButton from "./pagination-next-button.svelte";
import Ellipsis from "./pagination-ellipsis.svelte";
import Previous from "./pagination-previous.svelte";
import Next from "./pagination-next.svelte";
export {
Root,
Content,
Item,
Link,
PrevButton, // old
NextButton, // old
Ellipsis,
Previous,
Next,
//
Root as Pagination,
Content as PaginationContent,
Item as PaginationItem,
Link as PaginationLink,
PrevButton as PaginationPrevButton, // old
NextButton as PaginationNextButton, // old
Ellipsis as PaginationEllipsis,
Previous as PaginationPrevious,
Next as PaginationNext,
};
@@ -0,0 +1,20 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import { cn, type WithElementRef } from "@/lib/utils.js";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLUListElement>> = $props();
</script>
<ul
bind:this={ref}
data-slot="pagination-content"
class={cn("gap-0.5 flex items-center", className)}
{...restProps}
>
{@render children?.()}
</ul>
@@ -0,0 +1,22 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";
import { cn, type WithElementRef, type WithoutChildren } from "@/lib/utils.js";
import MoreHorizontalIcon from '@lucide/svelte/icons/more-horizontal';
let {
ref = $bindable(null),
class: className,
...restProps
}: WithoutChildren<WithElementRef<HTMLAttributes<HTMLSpanElement>>> = $props();
</script>
<span
bind:this={ref}
aria-hidden="true"
data-slot="pagination-ellipsis"
class={cn("size-8 items-center justify-center [&_svg:not([class*='size-'])]:size-4 flex items-center justify-center", className)}
{...restProps}
>
<MoreHorizontalIcon />
<span class="sr-only">More pages</span>
</span>
@@ -0,0 +1,14 @@
<script lang="ts">
import type { HTMLLiAttributes } from "svelte/elements";
import type { WithElementRef } from "@/lib/utils.js";
let {
ref = $bindable(null),
children,
...restProps
}: WithElementRef<HTMLLiAttributes> = $props();
</script>
<li bind:this={ref} data-slot="pagination-item" {...restProps}>
{@render children?.()}
</li>
@@ -0,0 +1,42 @@
<script lang="ts">
import { Pagination as PaginationPrimitive } from "bits-ui";
import { cn } from "@/lib/utils.js";
import { buttonVariants, type ButtonSize } from "@/components/ui/button/index.js";
let {
ref = $bindable(null),
class: className,
size = "icon",
isActive,
page,
children,
...restProps
}: PaginationPrimitive.PageProps & {
size?: ButtonSize;
isActive: boolean;
} = $props();
</script>
{#snippet Fallback()}
{page.value}
{/snippet}
<PaginationPrimitive.Page
bind:ref
{page}
aria-current={isActive ? "page" : undefined}
data-slot="pagination-link"
data-active={isActive}
data-size={size}
class={cn(
buttonVariants({ size, variant: isActive ? "outline" : "ghost" }),
"cn-pagination-link",
className
)}
{...restProps}
>
{#if children}
{@render children?.()}
{:else}
{@render Fallback()}
{/if}
</PaginationPrimitive.Page>
@@ -0,0 +1,31 @@
<script lang="ts">
import { Pagination as PaginationPrimitive } from "bits-ui";
import ChevronRightIcon from '@lucide/svelte/icons/chevron-right';
import { cn } from "@/lib/utils.js";
import { buttonVariants } from "../button/index.js";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: PaginationPrimitive.NextButtonProps = $props();
</script>
{#snippet Fallback()}
<span>Next</span>
<ChevronRightIcon class={cn("size-4", className)} />
{/snippet}
<PaginationPrimitive.NextButton
bind:ref
aria-label="Go to next page"
class={cn(buttonVariants({ variant: "ghost" }), "pr-1.5!", className)}
{...restProps}
>
{#if children}
{@render children?.()}
{:else}
{@render Fallback()}
{/if}
</PaginationPrimitive.NextButton>
@@ -0,0 +1,20 @@
<script lang="ts">
import type { ComponentProps } from "svelte";
import { cn } from "@/lib/utils.js";
import { PaginationLink } from "./index.js";
import ChevronRightIcon from '@lucide/svelte/icons/chevron-right';
type PaginationNextProps = ComponentProps<typeof PaginationLink>;
let { class: className, ...restProps }: PaginationNextProps = $props();
</script>
<PaginationLink
aria-label="Go to next page"
size="default"
class={cn("pr-1.5!", className)}
{...restProps}
>
<span class="cn-pagination-next-text hidden sm:block">Next</span>
<ChevronRightIcon data-icon="inline-end" />
</PaginationLink>
@@ -0,0 +1,31 @@
<script lang="ts">
import { Pagination as PaginationPrimitive } from "bits-ui";
import ChevronLeftIcon from '@lucide/svelte/icons/chevron-left';
import { cn } from "@/lib/utils.js";
import { buttonVariants } from "../button/index.js";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: PaginationPrimitive.PrevButtonProps = $props();
</script>
{#snippet Fallback()}
<ChevronLeftIcon class={cn("size-4", className)} />
<span>Previous</span>
{/snippet}
<PaginationPrimitive.PrevButton
bind:ref
aria-label="Go to previous page"
class={cn(buttonVariants({ variant: "ghost" }), "pl-1.5!", className)}
{...restProps}
>
{#if children}
{@render children?.()}
{:else}
{@render Fallback()}
{/if}
</PaginationPrimitive.PrevButton>
@@ -0,0 +1,20 @@
<script lang="ts">
import type { ComponentProps } from "svelte";
import { cn } from "@/lib/utils.js";
import { PaginationLink } from "./index.js";
import ChevronLeftIcon from '@lucide/svelte/icons/chevron-left';
type PaginationPreviousProps = ComponentProps<typeof PaginationLink>;
let { class: className, ...restProps }: PaginationPreviousProps = $props();
</script>
<PaginationLink
aria-label="Go to previous page"
size="default"
class={cn("pl-1.5!", className)}
{...restProps}
>
<ChevronLeftIcon data-icon="inline-start" />
<span class="cn-pagination-previous-text hidden sm:block">Previous</span>
</PaginationLink>
@@ -0,0 +1,28 @@
<script lang="ts">
import { Pagination as PaginationPrimitive } from "bits-ui";
import { cn } from "@/lib/utils.js";
let {
ref = $bindable(null),
class: className,
count = 0,
perPage = 10,
page = $bindable(1),
siblingCount = 1,
...restProps
}: PaginationPrimitive.RootProps = $props();
</script>
<PaginationPrimitive.Root
bind:ref
bind:page
role="navigation"
aria-label="pagination"
data-slot="pagination"
{count}
{perPage}
{siblingCount}
class={cn("cn-pagination mx-auto flex w-full justify-center", className)}
{...restProps}
/>
@@ -0,0 +1,18 @@
<script lang="ts">
import { Select as SelectPrimitive } from 'bits-ui';
type SelectProps = Omit<SelectPrimitive.RootProps, 'type' | 'value'> & {
type?: 'single';
value?: string;
onValueChange?: (value: string) => void;
};
let {
open = $bindable(false),
value = $bindable(''),
type = 'single',
...restProps
}: SelectProps = $props();
</script>
<SelectPrimitive.Root {type} bind:open bind:value={value as never} {...restProps} />
@@ -0,0 +1,46 @@
<script lang="ts">
import { Select as SelectPrimitive } from 'bits-ui';
import SelectPortal from './SelectPortal.svelte';
import SelectScrollUpButton from './SelectScrollUpButton.svelte';
import SelectScrollDownButton from './SelectScrollDownButton.svelte';
import { cn } from '@/lib/utils';
import type { ComponentProps } from 'svelte';
type ContentProps = ComponentProps<typeof SelectPrimitive.Content> & {
portalProps?: ComponentProps<typeof SelectPortal>;
};
let {
ref = $bindable(null),
class: className,
sideOffset = 4,
portalProps,
children,
preventScroll = true,
...restProps
}: ContentProps = $props();
</script>
<SelectPortal {...portalProps}>
<SelectPrimitive.Content
bind:ref
{sideOffset}
{preventScroll}
data-slot="select-content"
class={cn(
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-end-2 data-[side=right]:slide-in-from-start-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--bits-select-content-available-height) min-w-32 origin-(--bits-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border 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",
className
)}
{...restProps}
>
<SelectScrollUpButton />
<SelectPrimitive.Viewport
class={cn(
"h-(--bits-select-anchor-height) w-full min-w-(--bits-select-anchor-width) scroll-my-1 p-1"
)}
>
{@render children?.()}
</SelectPrimitive.Viewport>
<SelectScrollDownButton />
</SelectPrimitive.Content>
</SelectPortal>
@@ -0,0 +1,7 @@
<script lang="ts">
import { Select as SelectPrimitive } from "bits-ui";
let { ref = $bindable(null), ...restProps }: SelectPrimitive.GroupProps = $props();
</script>
<SelectPrimitive.Group bind:ref data-slot="select-group" {...restProps} />
@@ -0,0 +1,21 @@
<script lang="ts">
import { Select as SelectPrimitive } from "bits-ui";
import { cn } from '@/lib/utils';
import type { ComponentProps } from "svelte";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: ComponentProps<typeof SelectPrimitive.GroupHeading> = $props();
</script>
<SelectPrimitive.GroupHeading
bind:ref
data-slot="select-group-heading"
class={cn("text-muted-foreground px-2 py-1.5 text-xs", className)}
{...restProps}
>
{@render children?.()}
</SelectPrimitive.GroupHeading>
@@ -0,0 +1,39 @@
<script lang="ts">
import CheckIcon from 'lucide-svelte/icons/check';
import { Select as SelectPrimitive } from 'bits-ui';
import { cn } from '@/lib/utils';
import type { ComponentProps } from 'svelte';
let {
ref = $bindable(null),
class: className,
value,
label,
children: childrenProp,
...restProps
}: ComponentProps<typeof SelectPrimitive.Item> = $props();
</script>
<SelectPrimitive.Item
bind:ref
{value}
data-slot="select-item"
class={cn(
"data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 ps-2 pe-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
className
)}
{...restProps}
>
{#snippet children({ selected, highlighted })}
<span class="absolute end-2 flex size-3.5 items-center justify-center">
{#if selected}
<CheckIcon class="size-4" />
{/if}
</span>
{#if childrenProp}
{@render childrenProp({ selected, highlighted })}
{:else}
{label || value}
{/if}
{/snippet}
</SelectPrimitive.Item>
@@ -0,0 +1,23 @@
<script lang="ts">
import { cn } from '@/lib/utils';
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: HTMLAttributes<HTMLDivElement> & {
ref?: HTMLDivElement | null;
children?: () => unknown;
} = $props();
</script>
<div
bind:this={ref}
data-slot="select-label"
class={cn("text-muted-foreground px-2 py-1.5 text-xs", className)}
{...restProps}
>
{@render children?.()}
</div>
@@ -0,0 +1,7 @@
<script lang="ts">
import { Select as SelectPrimitive } from "bits-ui";
let { ...restProps }: SelectPrimitive.PortalProps = $props();
</script>
<SelectPrimitive.Portal {...restProps} />
@@ -0,0 +1,21 @@
<script lang="ts">
import ChevronDownIcon from 'lucide-svelte/icons/chevron-down';
import { Select as SelectPrimitive } from 'bits-ui';
import { cn } from '@/lib/utils';
import type { ComponentProps } from 'svelte';
let {
ref = $bindable(null),
class: className,
...restProps
}: ComponentProps<typeof SelectPrimitive.ScrollDownButton> = $props();
</script>
<SelectPrimitive.ScrollDownButton
bind:ref
data-slot="select-scroll-down-button"
class={cn("flex cursor-default items-center justify-center py-1", className)}
{...restProps}
>
<ChevronDownIcon class="size-4" />
</SelectPrimitive.ScrollDownButton>
@@ -0,0 +1,21 @@
<script lang="ts">
import ChevronUpIcon from 'lucide-svelte/icons/chevron-up';
import { Select as SelectPrimitive } from 'bits-ui';
import { cn } from '@/lib/utils';
import type { ComponentProps } from 'svelte';
let {
ref = $bindable(null),
class: className,
...restProps
}: ComponentProps<typeof SelectPrimitive.ScrollUpButton> = $props();
</script>
<SelectPrimitive.ScrollUpButton
bind:ref
data-slot="select-scroll-up-button"
class={cn("flex cursor-default items-center justify-center py-1", className)}
{...restProps}
>
<ChevronUpIcon class="size-4" />
</SelectPrimitive.ScrollUpButton>
@@ -0,0 +1,16 @@
<script lang="ts">
import { Separator } from '@/components/ui/separator';
import { cn } from '@/lib/utils';
import type { ComponentProps } from 'svelte';
let {
class: className,
...restProps
}: ComponentProps<typeof Separator> = $props();
</script>
<Separator
data-slot="select-separator"
class={cn("bg-border pointer-events-none -mx-1 my-1 h-px", className)}
{...restProps}
/>
@@ -0,0 +1,32 @@
<script lang="ts">
import { Select as SelectPrimitive } from 'bits-ui';
import ChevronDownIcon from 'lucide-svelte/icons/chevron-down';
import { cn } from '@/lib/utils';
import type { ComponentProps } from 'svelte';
type TriggerProps = ComponentProps<typeof SelectPrimitive.Trigger> & {
size?: 'sm' | 'default';
};
let {
ref = $bindable(null),
class: className,
children,
size = 'default',
...restProps
}: TriggerProps = $props();
</script>
<SelectPrimitive.Trigger
bind:ref
data-slot="select-trigger"
data-size={size}
class={cn(
"border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none select-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...restProps}
>
{@render children?.()}
<ChevronDownIcon class="size-4 opacity-50" />
</SelectPrimitive.Trigger>
Loaded 100 of 418 files, more files were not shown because too many files have changed in this diff. Show more