first commit
This commit is contained in:
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>
|
||||
@@ -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
Reference in new issue
Block a user