feat: migrate auth from supabase to better auth (#596)

* feat: add README for @cio/db package and remove existingAccounts.json

* load session, load profile and organization, add create profile hook

* chore: cache session request

The trick was passing all the cookies to the backend. Previously I was only passing the better-auth.session_token and not better-auth.session_data. Read about the difference here https://www.better-auth.com/docs/concepts/cookies#custom-cookies

* feat: login, logout, onboarding, verify email, reset password.

* feat: complete change email and email verification

* feat: standardise how we make rpc calls and create org endpoints

* fix ui errors

* fix: handle potential undefined customerId and standardize avatar URL property

* refactor: standardize custom domain handling and update telegram chat ID references

* chore: add tailwind prefix to @cio/ui

this fixed the media query issues because  @cio/ui classes were overriding this dashboard tailwind build

* chore: clean up org sidebar

* chore: persist mode state

* feat: implement order breadcrumbs

* chore: upgrade to better auth 1.4

* chore: address common auth flow bugs and setup app for others to setup locally.
This commit is contained in:
Rotimi Best
2025-11-24 01:00:50 +01:00
committed by GitHub
parent 24741644d6
commit a3924100dd
567 changed files with 22364 additions and 13437 deletions
@@ -1,22 +1,22 @@
<script lang="ts">
import { Accordion as AccordionPrimitive } from "bits-ui";
import { cn, type WithoutChild } from "../../tools";
import { Accordion as AccordionPrimitive } from 'bits-ui';
import { cn, type WithoutChild } from '../../tools';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithoutChild<AccordionPrimitive.ContentProps> = $props();
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithoutChild<AccordionPrimitive.ContentProps> = $props();
</script>
<AccordionPrimitive.Content
bind:ref
data-slot="accordion-content"
class="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm"
{...restProps}
bind:ref
data-slot="accordion-content"
class="ui:data-[state=closed]:animate-accordion-up ui:data-[state=open]:animate-accordion-down ui:overflow-hidden ui:text-sm"
{...restProps}
>
<div class={cn("pb-4 pt-0", className)}>
{@render children?.()}
</div>
<div class={cn('ui:pb-4 ui:pt-0', className)}>
{@render children?.()}
</div>
</AccordionPrimitive.Content>
@@ -1,17 +1,13 @@
<script lang="ts">
import { Accordion as AccordionPrimitive } from "bits-ui";
import { cn } from "../../tools";
import { Accordion as AccordionPrimitive } from 'bits-ui';
import { cn } from '../../tools';
let {
ref = $bindable(null),
class: className,
...restProps
}: AccordionPrimitive.ItemProps = $props();
let { ref = $bindable(null), class: className, ...restProps }: AccordionPrimitive.ItemProps = $props();
</script>
<AccordionPrimitive.Item
bind:ref
data-slot="accordion-item"
class={cn("border-b last:border-b-0", className)}
{...restProps}
bind:ref
data-slot="accordion-item"
class={cn('ui:border-b ui:last:border-b-0', className)}
{...restProps}
/>
@@ -1,32 +1,32 @@
<script lang="ts">
import { Accordion as AccordionPrimitive } from "bits-ui";
import ChevronDownIcon from "@lucide/svelte/icons/chevron-down";
import { cn, type WithoutChild } from "../../tools";
import { Accordion as AccordionPrimitive } from 'bits-ui';
import ChevronDownIcon from '@lucide/svelte/icons/chevron-down';
import { cn, type WithoutChild } from '../../tools';
let {
ref = $bindable(null),
class: className,
level = 3,
children,
...restProps
}: WithoutChild<AccordionPrimitive.TriggerProps> & {
level?: AccordionPrimitive.HeaderProps["level"];
} = $props();
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:border-ring focus-visible:ring-ring/50 flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium outline-none transition-all hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180",
className
)}
{...restProps}
>
{@render children?.()}
<ChevronDownIcon
class="text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200"
/>
</AccordionPrimitive.Trigger>
<AccordionPrimitive.Header {level} class="ui:flex">
<AccordionPrimitive.Trigger
data-slot="accordion-trigger"
bind:ref
class={cn(
'ui:focus-visible:border-ring ui:focus-visible:ring-ring/50 ui:flex ui:flex-1 ui:items-start ui:justify-between ui:gap-4 ui:rounded-md ui:py-4 ui:text-left ui:text-sm ui:font-medium ui:outline-none ui:transition-all ui:hover:underline ui:focus-visible:ring-[3px] ui:disabled:pointer-events-none ui:disabled:opacity-50 ui:[&[data-state=open]>svg]:rotate-180',
className
)}
{...restProps}
>
{@render children?.()}
<ChevronDownIcon
class="ui:text-muted-foreground ui:pointer-events-none ui:size-4 ui:shrink-0 ui:translate-y-0.5 ui:transition-transform ui:duration-200"
/>
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
@@ -1,16 +1,7 @@
<script lang="ts">
import { Accordion as AccordionPrimitive } from "bits-ui";
import { Accordion as AccordionPrimitive } from 'bits-ui';
let {
ref = $bindable(null),
value = $bindable(),
...restProps
}: AccordionPrimitive.RootProps = $props();
let { ref = $bindable(null), value = $bindable(), ...restProps }: AccordionPrimitive.RootProps = $props();
</script>
<AccordionPrimitive.Root
bind:ref
bind:value={value as never}
data-slot="accordion"
{...restProps}
/>
<AccordionPrimitive.Root bind:ref bind:value={value as never} data-slot="accordion" {...restProps} />
+13 -13
View File
@@ -1,16 +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";
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,
Root,
Content,
Item,
Trigger,
//
Root as Accordion,
Content as AccordionContent,
Item as AccordionItem,
Trigger as AccordionTrigger
};
@@ -8,6 +8,6 @@
<AvatarPrimitive.Fallback
bind:ref
data-slot="avatar-fallback"
class={cn('bg-muted flex size-full items-center justify-center rounded-full', className)}
class={cn('ui:bg-muted ui:flex ui:size-full ui:items-center ui:justify-center ui:rounded-full', className)}
{...restProps}
/>
@@ -8,6 +8,6 @@
<AvatarPrimitive.Image
bind:ref
data-slot="avatar-image"
class={cn('aspect-square size-full', className)}
class={cn('ui:size-full aspect-square', className)}
{...restProps}
/>
+1 -1
View File
@@ -14,6 +14,6 @@
bind:ref
bind:loadingStatus
data-slot="avatar"
class={cn('relative flex size-8 shrink-0 overflow-hidden rounded-full', className)}
class={cn('ui:relative ui:flex ui:size-8 ui:shrink-0 ui:overflow-hidden ui:rounded-full', className)}
{...restProps}
/>
+10 -10
View File
@@ -1,13 +1,13 @@
import Root from "./avatar.svelte";
import Image from "./avatar-image.svelte";
import Fallback from "./avatar-fallback.svelte";
import Root from './avatar.svelte';
import Image from './avatar-image.svelte';
import Fallback from './avatar-fallback.svelte';
export {
Root,
Image,
Fallback,
//
Root as Avatar,
Image as AvatarImage,
Fallback as AvatarFallback,
Root,
Image,
Fallback,
//
Root as Avatar,
Image as AvatarImage,
Fallback as AvatarFallback
};
+5 -5
View File
@@ -2,14 +2,14 @@
import { type VariantProps, tv } from 'tailwind-variants';
export const badgeVariants = tv({
base: 'focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden whitespace-nowrap rounded-full border px-2 py-0.5 text-xs font-medium transition-[color,box-shadow] focus-visible:ring-[3px] [&>svg]:pointer-events-none [&>svg]:size-3',
base: 'ui:focus-visible:border-ring ui:focus-visible:ring-ring/50 ui:aria-invalid:ring-destructive/20 ui:dark:aria-invalid:ring-destructive/40 ui:aria-invalid:border-destructive ui:inline-flex ui:w-fit ui:shrink-0 ui:items-center ui:justify-center ui:gap-1 ui:overflow-hidden ui:whitespace-nowrap ui:rounded-full ui:border ui:px-2 ui:py-0.5 ui:text-xs ui:font-medium ui:transition-[color,box-shadow] ui:focus-visible:ring-[3px] ui:[&>svg]:pointer-events-none ui:[&>svg]:size-3',
variants: {
variant: {
default: 'bg-primary text-primary-foreground [a&]:hover:bg-primary/90 border-transparent',
secondary: 'bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90 border-transparent',
default: 'ui:bg-primary ui:text-primary-foreground ui:[a&]:hover:bg-primary/90 ui:border-transparent',
secondary: 'ui:bg-secondary ui:text-secondary-foreground ui:[a&]:hover:bg-secondary/90 ui:border-transparent',
destructive:
'bg-destructive [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/70 border-transparent text-white',
outline: 'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground'
'ui:bg-destructive ui:[a&]:hover:bg-destructive/90 ui:focus-visible:ring-destructive/20 ui:dark:focus-visible:ring-destructive/40 ui:dark:bg-destructive/70 ui:border-transparent ui:text-white',
outline: 'ui:text-foreground ui:[a&]:hover:bg-accent ui:[a&]:hover:text-accent-foreground'
}
},
defaultVariants: {
+2 -2
View File
@@ -1,2 +1,2 @@
export { default as Badge } from "./badge.svelte";
export { badgeVariants, type BadgeVariant } from "./badge.svelte";
export { default as Badge } from './badge.svelte';
export { badgeVariants, type BadgeVariant } from './badge.svelte';
@@ -15,9 +15,9 @@
data-slot="breadcrumb-ellipsis"
role="presentation"
aria-hidden="true"
class={cn('flex size-9 items-center justify-center', className)}
class={cn('ui:flex ui:size-9 ui:items-center ui:justify-center', className)}
{...restProps}
>
<EllipsisIcon class="size-4" />
<EllipsisIcon class="ui:size-4" />
<span class="sr-only">More</span>
</span>
@@ -1,20 +1,15 @@
<script lang="ts">
import type { HTMLLiAttributes } from "svelte/elements";
import { cn, type WithElementRef } from '../../tools';
import type { HTMLLiAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from '../../tools';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLLiAttributes> = $props();
let { ref = $bindable(null), class: className, children, ...restProps }: WithElementRef<HTMLLiAttributes> = $props();
</script>
<li
bind:this={ref}
data-slot="breadcrumb-item"
class={cn("inline-flex items-center gap-1.5", className)}
{...restProps}
bind:this={ref}
data-slot="breadcrumb-item"
class={cn('ui:inline-flex ui:items-center ui:gap-1.5', className)}
{...restProps}
>
{@render children?.()}
{@render children?.()}
</li>
@@ -16,7 +16,7 @@
const attrs = $derived({
'data-slot': 'breadcrumb-link',
class: cn('hover:text-foreground transition-colors', className),
class: cn('ui:hover:text-foreground ui:transition-colors', className),
href,
...restProps
});
@@ -8,7 +8,10 @@
<ol
bind:this={ref}
data-slot="breadcrumb-list"
class={cn('text-muted-foreground flex flex-wrap items-center gap-1.5 break-words text-sm sm:gap-2.5', className)}
class={cn(
'ui:text-muted-foreground ui:flex ui:flex-wrap ui:items-center ui:gap-1.5 ui:break-words ui:text-sm ui:sm:gap-2.5',
className
)}
{...restProps}
>
{@render children?.()}
@@ -16,7 +16,7 @@
role="link"
aria-disabled="true"
aria-current="page"
class={cn('text-foreground font-normal', className)}
class={cn('ui:text-foreground ui:font-normal', className)}
{...restProps}
>
{@render children?.()}
@@ -11,7 +11,7 @@
data-slot="breadcrumb-separator"
role="presentation"
aria-hidden="true"
class={cn('[&>svg]:size-3.5', className)}
class={cn('ui:[&>svg]:size-3.5', className)}
{...restProps}
>
{#if children}
+22 -22
View File
@@ -1,25 +1,25 @@
import Root from "./breadcrumb.svelte";
import Ellipsis from "./breadcrumb-ellipsis.svelte";
import Item from "./breadcrumb-item.svelte";
import Separator from "./breadcrumb-separator.svelte";
import Link from "./breadcrumb-link.svelte";
import List from "./breadcrumb-list.svelte";
import Page from "./breadcrumb-page.svelte";
import Root from './breadcrumb.svelte';
import Ellipsis from './breadcrumb-ellipsis.svelte';
import Item from './breadcrumb-item.svelte';
import Separator from './breadcrumb-separator.svelte';
import Link from './breadcrumb-link.svelte';
import List from './breadcrumb-list.svelte';
import Page from './breadcrumb-page.svelte';
export {
Root,
Ellipsis,
Item,
Separator,
Link,
List,
Page,
//
Root as Breadcrumb,
Ellipsis as BreadcrumbEllipsis,
Item as BreadcrumbItem,
Separator as BreadcrumbSeparator,
Link as BreadcrumbLink,
List as BreadcrumbList,
Page as BreadcrumbPage,
Root,
Ellipsis,
Item,
Separator,
Link,
List,
Page,
//
Root as Breadcrumb,
Ellipsis as BreadcrumbEllipsis,
Item as BreadcrumbItem,
Separator as BreadcrumbSeparator,
Link as BreadcrumbLink,
List as BreadcrumbList,
Page as BreadcrumbPage
};
+13 -13
View File
@@ -5,25 +5,25 @@
import { Spinner } from '../spinner';
export const buttonVariants = tv({
base: "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium outline-none transition-all focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
base: "ui:focus-visible:border-ring ui:cursor-pointer ui:focus-visible:ring-ring/50 ui:aria-invalid:ring-destructive/20 ui:dark:aria-invalid:ring-destructive/40 ui:aria-invalid:border-destructive ui:inline-flex ui:shrink-0 ui:items-center ui:justify-center ui:gap-2 ui:whitespace-nowrap ui:rounded-md ui:text-sm ui:font-medium ui:outline-none ui:transition-all ui:focus-visible:ring-[3px] ui:disabled:pointer-events-none ui:disabled:opacity-50 ui:aria-disabled:pointer-events-none ui:aria-disabled:opacity-50 ui:[&_svg:not([class*='size-'])]:size-4 ui:[&_svg]:pointer-events-none ui:[&_svg]:shrink-0",
variants: {
variant: {
default: 'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90',
default: 'ui:bg-primary ui:text-primary-foreground ui:shadow-xs ui:hover:bg-primary/90',
destructive:
'bg-destructive shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60 text-white',
'ui:bg-destructive ui:shadow-xs ui:hover:bg-destructive/90 ui:focus-visible:ring-destructive/20 ui:dark:focus-visible:ring-destructive/40 ui:dark:bg-destructive/60 ui:text-white',
outline:
'bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 border',
secondary: 'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
link: 'text-primary underline-offset-4 hover:underline'
'ui:bg-background ui:shadow-xs ui:hover:bg-accent ui:hover:text-accent-foreground ui:dark:bg-input/30 ui:dark:border-input ui:dark:hover:bg-input/50 ui:border',
secondary: 'ui:bg-secondary ui:text-secondary-foreground ui:shadow-xs ui:hover:bg-secondary/80',
ghost: 'ui:hover:bg-accent ui:hover:text-accent-foreground ui:dark:hover:bg-accent/50',
link: 'ui:text-primary ui:underline-offset-4 ui:hover:underline'
},
size: {
default: 'h-9 px-4 py-2 has-[>svg]:px-3',
sm: 'h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5',
lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
icon: 'size-9',
'icon-sm': 'size-8',
'icon-lg': 'size-10'
default: 'ui:h-9 ui:px-4 ui:py-2 ui:has-[>svg]:px-3',
sm: 'ui:h-8 ui:gap-1.5 ui:rounded-md ui:px-3 ui:has-[>svg]:px-2.5',
lg: 'ui:h-10 ui:rounded-md ui:px-6 ui:has-[>svg]:px-4',
icon: 'ui:size-9',
'icon-sm': 'ui:size-8',
'icon-lg': 'ui:size-10'
}
},
defaultVariants: {
@@ -31,7 +31,7 @@
data-chart={chartId}
data-slot="chart"
class={cn(
'flex aspect-video justify-center overflow-visible text-xs',
'ui:flex ui:justify-center ui:overflow-visible ui:text-xs aspect-video',
// Overrides
//
// Stroke around dots/marks when hovering
+25 -25
View File
@@ -1,37 +1,37 @@
<script lang="ts">
import { THEMES, type ChartConfig } from "./chart-utils.js";
import { THEMES, type ChartConfig } from './chart-utils.js';
let { id, config }: { id: string; config: ChartConfig } = $props();
let { id, config }: { id: string; config: ChartConfig } = $props();
const colorConfig = $derived(
config ? Object.entries(config).filter(([, config]) => config.theme || config.color) : null
);
const colorConfig = $derived(
config ? Object.entries(config).filter(([, config]) => config.theme || config.color) : null
);
const themeContents = $derived.by(() => {
if (!colorConfig || !colorConfig.length) return;
const themeContents = $derived.by(() => {
if (!colorConfig || !colorConfig.length) return;
const themeContents = [];
for (let [_theme, prefix] of Object.entries(THEMES)) {
let content = `${prefix} [data-chart=${id}] {\n`;
const color = colorConfig.map(([key, itemConfig]) => {
const theme = _theme as keyof typeof itemConfig.theme;
const color = itemConfig.theme?.[theme] || itemConfig.color;
return color ? `\t--color-${key}: ${color};` : null;
});
const themeContents = [];
for (let [_theme, prefix] of Object.entries(THEMES)) {
let content = `${prefix} [data-chart=${id}] {\n`;
const color = colorConfig.map(([key, itemConfig]) => {
const theme = _theme as keyof typeof itemConfig.theme;
const color = itemConfig.theme?.[theme] || itemConfig.color;
return color ? `\t--color-${key}: ${color};` : null;
});
content += color.join("\n") + "\n}";
content += color.join('\n') + '\n}';
themeContents.push(content);
}
themeContents.push(content);
}
return themeContents.join("\n");
});
return themeContents.join('\n');
});
</script>
{#if themeContents}
{#key id}
<svelte:element this={"style"}>
{themeContents}
</svelte:element>
{/key}
{#key id}
<svelte:element this={'style'}>
{themeContents}
</svelte:element>
{/key}
{/if}
+16 -13
View File
@@ -73,7 +73,7 @@
{#snippet TooltipLabel()}
{#if formattedLabel}
<div class={cn('font-medium', labelClassName)}>
<div class={cn('ui:font-medium', labelClassName)}>
{#if typeof formattedLabel === 'function'}
{@render formattedLabel()}
{:else}
@@ -86,7 +86,7 @@
<TooltipPrimitive.Root variant="none">
<div
class={cn(
'border-border/50 bg-background grid min-w-[9rem] items-start gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs shadow-xl',
'ui:border-border/50 ui:bg-background ui:grid ui:min-w-[9rem] ui:items-start ui:gap-1.5 ui:rounded-lg ui:border ui:px-2.5 ui:py-1.5 ui:text-xs ui:shadow-xl',
className
)}
{...restProps}
@@ -94,14 +94,14 @@
{#if !nestLabel}
{@render TooltipLabel()}
{/if}
<div class="grid gap-1.5">
<div class="ui:grid ui:gap-1.5">
{#each tooltipCtx.payload as item, i (item.key + i)}
{@const key = `${nameKey || item.key || item.name || 'value'}`}
{@const itemConfig = getPayloadConfigFromPayload(chart.config, item, key)}
{@const indicatorColor = color || item.payload?.color || item.color}
<div
class={cn(
'[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:size-2.5',
'ui:[&>svg]:text-muted-foreground ui:flex ui:w-full ui:flex-wrap ui:items-stretch ui:gap-2 ui:[&>svg]:size-2.5',
indicator === 'dot' && 'items-center'
)}
>
@@ -119,27 +119,30 @@
{:else if !hideIndicator}
<div
style="--color-bg: {indicatorColor}; --color-border: {indicatorColor};"
class={cn('border-(--color-border) bg-(--color-bg) shrink-0 rounded-[2px]', {
'size-2.5': indicator === 'dot',
'h-full w-1': indicator === 'line',
'w-0 border-[1.5px] border-dashed bg-transparent': indicator === 'dashed',
'my-0.5': nestLabel && indicator === 'dashed'
class={cn('ui:border-(--color-border) ui:bg-(--color-bg) ui:shrink-0 ui:rounded-[2px]', {
'ui:size-2.5': indicator === 'dot',
'ui:h-full ui:w-1': indicator === 'line',
'ui:w-0 ui:border-[1.5px] ui:border-dashed ui:bg-transparent': indicator === 'dashed',
'ui:my-0.5': nestLabel && indicator === 'dashed'
})}
></div>
{/if}
<div
class={cn('flex flex-1 shrink-0 justify-between leading-none', nestLabel ? 'items-end' : 'items-center')}
class={cn(
'ui:flex ui:flex-1 ui:shrink-0 ui:justify-between ui:leading-none',
nestLabel ? 'ui:items-end' : 'ui:items-center'
)}
>
<div class="grid gap-1.5">
<div class="ui:grid ui:gap-1.5">
{#if nestLabel}
{@render TooltipLabel()}
{/if}
<span class="text-muted-foreground">
<span class="ui:text-muted-foreground">
{itemConfig?.label || item.name}
</span>
</div>
{#if item.value !== undefined}
<span class="text-foreground font-mono font-medium tabular-nums">
<span class="ui:text-foreground ui:font-mono ui:font-medium tabular-nums">
{item.value.toLocaleString()}
</span>
{/if}
+33 -42
View File
@@ -1,66 +1,57 @@
import type { Tooltip } from "layerchart";
import { getContext, setContext, type Component, type ComponentProps, type Snippet } from "svelte";
import type { Tooltip } from 'layerchart';
import { getContext, setContext, type Component, type ComponentProps, type Snippet } from 'svelte';
export const THEMES = { light: "", dark: ".dark" } as const;
export const THEMES = { light: '', dark: '.dark' } as const;
export type ChartConfig = {
[k in string]: {
label?: string;
icon?: Component;
} & (
| { color?: string; theme?: never }
| { color?: never; theme: Record<keyof typeof THEMES, string> }
);
[k in string]: {
label?: string;
icon?: Component;
} & ({ color?: string; theme?: never } | { color?: never; theme: Record<keyof typeof THEMES, string> });
};
export type ExtractSnippetParams<T> = T extends Snippet<[infer P]> ? P : never;
export type TooltipPayload = ExtractSnippetParams<
ComponentProps<typeof Tooltip.Root>["children"]
>["payload"][number];
export type TooltipPayload = ExtractSnippetParams<ComponentProps<typeof Tooltip.Root>['children']>['payload'][number];
// Helper to extract item config from a payload.
export function getPayloadConfigFromPayload(
config: ChartConfig,
payload: TooltipPayload,
key: string
) {
if (typeof payload !== "object" || payload === null) return undefined;
export function getPayloadConfigFromPayload(config: ChartConfig, payload: TooltipPayload, key: string) {
if (typeof payload !== 'object' || payload === null) return undefined;
const payloadPayload =
"payload" in payload && typeof payload.payload === "object" && payload.payload !== null
? payload.payload
: undefined;
const payloadPayload =
'payload' in payload && typeof payload.payload === 'object' && payload.payload !== null
? payload.payload
: undefined;
let configLabelKey: string = key;
let configLabelKey: string = key;
if (payload.key === key) {
configLabelKey = payload.key;
} else if (payload.name === key) {
configLabelKey = payload.name;
} else if (key in payload && typeof payload[key as keyof typeof payload] === "string") {
configLabelKey = payload[key as keyof typeof payload] as string;
} else if (
payloadPayload !== undefined &&
key in payloadPayload &&
typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
) {
configLabelKey = payloadPayload[key as keyof typeof payloadPayload] as string;
}
if (payload.key === key) {
configLabelKey = payload.key;
} else if (payload.name === key) {
configLabelKey = payload.name;
} else if (key in payload && typeof payload[key as keyof typeof payload] === 'string') {
configLabelKey = payload[key as keyof typeof payload] as string;
} else if (
payloadPayload !== undefined &&
key in payloadPayload &&
typeof payloadPayload[key as keyof typeof payloadPayload] === 'string'
) {
configLabelKey = payloadPayload[key as keyof typeof payloadPayload] as string;
}
return configLabelKey in config ? config[configLabelKey] : config[key as keyof typeof config];
return configLabelKey in config ? config[configLabelKey] : config[key as keyof typeof config];
}
type ChartContextValue = {
config: ChartConfig;
config: ChartConfig;
};
const chartContextKey = Symbol("chart-context");
const chartContextKey = Symbol('chart-context');
export function setChartContext(value: ChartContextValue) {
return setContext(chartContextKey, value);
return setContext(chartContextKey, value);
}
export function useChart() {
return getContext<ChartContextValue>(chartContextKey);
return getContext<ChartContextValue>(chartContextKey);
}
@@ -17,7 +17,7 @@
bind:ref
data-slot="checkbox"
class={cn(
'border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive shadow-xs peer flex size-4 shrink-0 items-center justify-center rounded-[4px] border outline-none transition-shadow focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',
'ui:border-input ui:dark:bg-input/30 ui:data-[state=checked]:bg-primary ui:data-[state=checked]:text-primary-foreground ui:dark:data-[state=checked]:bg-primary ui:data-[state=checked]:border-primary ui:focus-visible:border-ring ui:focus-visible:ring-ring/50 ui:aria-invalid:ring-destructive/20 ui:dark:aria-invalid:ring-destructive/40 ui:aria-invalid:border-destructive ui:shadow-xs ui:flex ui:size-4 ui:shrink-0 ui:items-center ui:justify-center ui:rounded-[4px] ui:border ui:outline-none ui:transition-shadow ui:focus-visible:ring-[3px] ui:disabled:cursor-not-allowed ui:disabled:opacity-50 peer',
className
)}
bind:checked
@@ -25,11 +25,11 @@
{...restProps}
>
{#snippet children({ checked, indeterminate })}
<div data-slot="checkbox-indicator" class="text-current transition-none">
<div data-slot="checkbox-indicator" class="ui:text-current ui:transition-none">
{#if checked}
<CheckIcon class="size-3.5" />
<CheckIcon class="ui:size-3.5" />
{:else if indeterminate}
<MinusIcon class="size-3.5" />
<MinusIcon class="ui:size-3.5" />
{/if}
</div>
{/snippet}
+4 -4
View File
@@ -1,6 +1,6 @@
import Root from "./checkbox.svelte";
import Root from './checkbox.svelte';
export {
Root,
//
Root as Checkbox,
Root,
//
Root as Checkbox
};
@@ -1,7 +1,7 @@
<script lang="ts">
import { Collapsible as CollapsiblePrimitive } from "bits-ui";
import { Collapsible as CollapsiblePrimitive } from 'bits-ui';
let { ref = $bindable(null), ...restProps }: CollapsiblePrimitive.ContentProps = $props();
let { ref = $bindable(null), ...restProps }: CollapsiblePrimitive.ContentProps = $props();
</script>
<CollapsiblePrimitive.Content bind:ref data-slot="collapsible-content" {...restProps} />
@@ -1,7 +1,7 @@
<script lang="ts">
import { Collapsible as CollapsiblePrimitive } from "bits-ui";
import { Collapsible as CollapsiblePrimitive } from 'bits-ui';
let { ref = $bindable(null), ...restProps }: CollapsiblePrimitive.TriggerProps = $props();
let { ref = $bindable(null), ...restProps }: CollapsiblePrimitive.TriggerProps = $props();
</script>
<CollapsiblePrimitive.Trigger bind:ref data-slot="collapsible-trigger" {...restProps} />
@@ -1,11 +1,7 @@
<script lang="ts">
import { Collapsible as CollapsiblePrimitive } from "bits-ui";
import { Collapsible as CollapsiblePrimitive } from 'bits-ui';
let {
ref = $bindable(null),
open = $bindable(false),
...restProps
}: CollapsiblePrimitive.RootProps = $props();
let { ref = $bindable(null), open = $bindable(false), ...restProps }: CollapsiblePrimitive.RootProps = $props();
</script>
<CollapsiblePrimitive.Root bind:ref bind:open data-slot="collapsible" {...restProps} />
+10 -10
View File
@@ -1,13 +1,13 @@
import Root from "./collapsible.svelte";
import Trigger from "./collapsible-trigger.svelte";
import Content from "./collapsible-content.svelte";
import Root from './collapsible.svelte';
import Trigger from './collapsible-trigger.svelte';
import Content from './collapsible-content.svelte';
export {
Root,
Content,
Trigger,
//
Root as Collapsible,
Content as CollapsibleContent,
Trigger as CollapsibleTrigger,
Root,
Content,
Trigger,
//
Root as Collapsible,
Content as CollapsibleContent,
Trigger as CollapsibleTrigger
};
@@ -28,9 +28,9 @@
<Dialog.Title>{title}</Dialog.Title>
<Dialog.Description>{description}</Dialog.Description>
</Dialog.Header>
<Dialog.Content class="overflow-hidden p-0" {portalProps}>
<Dialog.Content class="ui:overflow-hidden ui:p-0" {portalProps}>
<Command
class="**:data-[slot=command-input-wrapper]:h-12 [&_[data-command-group]:not([hidden])_~[data-command-group]]:pt-0 [&_[data-command-group]]:px-2 [&_[data-command-input-wrapper]_svg]:h-5 [&_[data-command-input-wrapper]_svg]:w-5 [&_[data-command-input]]:h-12 [&_[data-command-item]]:px-2 [&_[data-command-item]]:py-3 [&_[data-command-item]_svg]:h-5 [&_[data-command-item]_svg]:w-5"
class="**:data-[slot=command-input-wrapper]:h-12 ui:[&_[data-command-group]:not([hidden])_~[data-command-group]]:pt-0 ui:**:data-command-group:px-2 ui:[&_[data-command-input-wrapper]_svg]:h-5 ui:[&_[data-command-input-wrapper]_svg]:w-5 ui:**:data-command-input:h-12 ui:**:data-command-item:px-2 ui:**:data-command-item:py-3 ui:[&_[data-command-item]_svg]:h-5 ui:[&_[data-command-item]_svg]:w-5"
{...restProps}
bind:value
bind:ref
@@ -8,6 +8,6 @@
<CommandPrimitive.Empty
bind:ref
data-slot="command-empty"
class={cn('py-6 text-center text-sm', className)}
class={cn('ui:py-6 ui:text-center ui:text-sm', className)}
{...restProps}
/>
@@ -17,12 +17,12 @@
<CommandPrimitive.Group
bind:ref
data-slot="command-group"
class={cn('text-foreground overflow-hidden p-1', className)}
class={cn('ui:text-foreground ui:overflow-hidden ui:p-1', className)}
value={value ?? heading ?? `----${useId()}`}
{...restProps}
>
{#if heading}
<CommandPrimitive.GroupHeading class="text-muted-foreground px-2 py-1.5 text-xs font-medium">
<CommandPrimitive.GroupHeading class="ui:text-muted-foreground ui:px-2 ui:py-1.5 ui:text-xs ui:font-medium">
{heading}
</CommandPrimitive.GroupHeading>
{/if}
@@ -11,12 +11,12 @@
}: CommandPrimitive.InputProps = $props();
</script>
<div class="flex h-9 items-center gap-2 border-b pl-3 pr-8" data-slot="command-input-wrapper">
<SearchIcon class="size-4 shrink-0 opacity-50" />
<div class="ui:flex ui:h-9 ui:items-center ui:gap-2 ui:border-b ui:pl-3 ui:pr-8" data-slot="command-input-wrapper">
<SearchIcon class="ui:size-4 ui:shrink-0 ui:opacity-50" />
<CommandPrimitive.Input
data-slot="command-input"
class={cn(
'placeholder:text-muted-foreground outline-hidden flex h-10 w-full rounded-md bg-transparent py-3 text-sm disabled:cursor-not-allowed disabled:opacity-50',
'ui:placeholder:text-muted-foreground ui:outline-hidden ui:flex ui:h-10 ui:w-full ui:rounded-md ui:bg-transparent ui:py-3 ui:text-sm ui:disabled:cursor-not-allowed ui:disabled:opacity-50',
className
)}
bind:ref
@@ -9,7 +9,7 @@
bind:ref
data-slot="command-item"
class={cn(
"aria-selected:bg-accent aria-selected:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground outline-hidden relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
"ui:aria-selected:bg-accent ui:aria-selected:text-accent-foreground ui:[&_svg:not([class*='text-'])]:text-muted-foreground ui:outline-hidden ui:relative ui:flex ui:cursor-default ui:select-none ui:items-center ui:gap-2 ui:rounded-sm ui:px-2 ui:py-1.5 ui:text-sm ui:data-disabled:pointer-events-none ui:data-disabled:opacity-50 ui:[&_svg:not([class*='size-'])]:size-4 ui:[&_svg]:pointer-events-none ui:[&_svg]:shrink-0",
className
)}
{...restProps}
@@ -9,7 +9,7 @@
bind:ref
data-slot="command-item"
class={cn(
"aria-selected:bg-accent aria-selected:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground outline-hidden relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
"ui:aria-selected:bg-accent ui:aria-selected:text-accent-foreground ui:[&_svg:not([class*='text-'])]:text-muted-foreground ui:outline-hidden ui:relative ui:flex ui:cursor-default ui:select-none ui:items-center ui:gap-2 ui:rounded-sm ui:px-2 ui:py-1.5 ui:text-sm ui:data-[disabled=true]:pointer-events-none ui:data-[disabled=true]:opacity-50 ui:[&_svg:not([class*='size-'])]:size-4 ui:[&_svg]:pointer-events-none ui:[&_svg]:shrink-0",
className
)}
{...restProps}
@@ -8,6 +8,6 @@
<CommandPrimitive.List
bind:ref
data-slot="command-list"
class={cn('max-h-[300px] scroll-py-1 overflow-y-auto overflow-x-hidden', className)}
class={cn('ui:max-h-[300px] ui:scroll-py-1 ui:overflow-y-auto ui:overflow-x-hidden', className)}
{...restProps}
/>
@@ -8,6 +8,6 @@
<CommandPrimitive.Separator
bind:ref
data-slot="command-separator"
class={cn('bg-border -mx-1 h-px', className)}
class={cn('ui:bg-border ui:-mx-1 ui:h-px', className)}
{...restProps}
/>
@@ -13,7 +13,7 @@
<span
bind:this={ref}
data-slot="command-shortcut"
class={cn('text-muted-foreground ml-auto text-xs tracking-widest', className)}
class={cn('ui:text-muted-foreground ui:ml-auto ui:text-xs ui:tracking-widest', className)}
{...restProps}
>
{@render children?.()}
+4 -1
View File
@@ -20,6 +20,9 @@
bind:value
bind:ref
data-slot="command"
class={cn('bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md', className)}
class={cn(
'ui:bg-popover ui:text-popover-foreground ui:flex ui:h-full ui:w-full ui:flex-col ui:overflow-hidden ui:rounded-md',
className
)}
{...restProps}
/>
+34 -34
View File
@@ -1,40 +1,40 @@
import { Command as CommandPrimitive } from "bits-ui";
import { Command as CommandPrimitive } from 'bits-ui';
import Root from "./command.svelte";
import Dialog from "./command-dialog.svelte";
import Empty from "./command-empty.svelte";
import Group from "./command-group.svelte";
import Item from "./command-item.svelte";
import Input from "./command-input.svelte";
import List from "./command-list.svelte";
import Separator from "./command-separator.svelte";
import Shortcut from "./command-shortcut.svelte";
import LinkItem from "./command-link-item.svelte";
import Root from './command.svelte';
import Dialog from './command-dialog.svelte';
import Empty from './command-empty.svelte';
import Group from './command-group.svelte';
import Item from './command-item.svelte';
import Input from './command-input.svelte';
import List from './command-list.svelte';
import Separator from './command-separator.svelte';
import Shortcut from './command-shortcut.svelte';
import LinkItem from './command-link-item.svelte';
const Loading = CommandPrimitive.Loading;
export {
Root,
Dialog,
Empty,
Group,
Item,
LinkItem,
Input,
List,
Separator,
Shortcut,
Loading,
//
Root as Command,
Dialog as CommandDialog,
Empty as CommandEmpty,
Group as CommandGroup,
Item as CommandItem,
LinkItem as CommandLinkItem,
Input as CommandInput,
List as CommandList,
Separator as CommandSeparator,
Shortcut as CommandShortcut,
Loading as CommandLoading,
Root,
Dialog,
Empty,
Group,
Item,
LinkItem,
Input,
List,
Separator,
Shortcut,
Loading,
//
Root as Command,
Dialog as CommandDialog,
Empty as CommandEmpty,
Group as CommandGroup,
Item as CommandItem,
LinkItem as CommandLinkItem,
Input as CommandInput,
List as CommandList,
Separator as CommandSeparator,
Shortcut as CommandShortcut,
Loading as CommandLoading
};
@@ -9,8 +9,12 @@
<DropdownMenu.Root>
<DropdownMenu.Trigger class={buttonVariants({ variant: 'outline', size: 'icon' })}>
<SunIcon class="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 !transition-all dark:-rotate-90 dark:scale-0" />
<MoonIcon class="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 !transition-all dark:rotate-0 dark:scale-100" />
<SunIcon
class="ui:h-[1.2rem] ui:w-[1.2rem] ui:rotate-0 ui:scale-100 ui:dark:-rotate-90 ui:dark:scale-0 !transition-all"
/>
<MoonIcon
class="ui:absolute ui:h-[1.2rem] ui:w-[1.2rem] ui:rotate-90 ui:scale-0 ui:dark:rotate-0 ui:dark:scale-100 !transition-all"
/>
<span class="sr-only">Toggle theme</span>
</DropdownMenu.Trigger>
<DropdownMenu.Content align="end">
+1 -1
View File
@@ -1,3 +1,3 @@
export { default as ModeSwitcher } from './dark-mode.svelte';
export * from 'mode-watcher';
export { ModeWatcher } from 'mode-watcher';
@@ -1,7 +1,7 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from "bits-ui";
import { Dialog as DialogPrimitive } from 'bits-ui';
let { ref = $bindable(null), ...restProps }: DialogPrimitive.CloseProps = $props();
let { ref = $bindable(null), ...restProps }: DialogPrimitive.CloseProps = $props();
</script>
<DialogPrimitive.Close bind:ref data-slot="dialog-close" {...restProps} />
@@ -25,7 +25,7 @@
bind:ref
data-slot="dialog-content"
class={cn(
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed left-[50%] top-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg',
'ui:bg-background ui:data-[state=open]:animate-in ui:data-[state=closed]:animate-out ui:data-[state=closed]:fade-out-0 ui:data-[state=open]:fade-in-0 ui:data-[state=closed]:zoom-out-95 ui:data-[state=open]:zoom-in-95 ui:fixed ui:left-[50%] ui:top-[50%] ui:z-50 ui:grid ui:w-full ui:max-w-[calc(100%-2rem)] ui:translate-x-[-50%] ui:translate-y-[-50%] ui:gap-4 ui:rounded-lg ui:border ui:p-6 ui:shadow-lg ui:duration-200 ui:sm:max-w-lg',
className
)}
{...restProps}
@@ -33,7 +33,7 @@
{@render children?.()}
{#if showCloseButton}
<DialogPrimitive.Close
class="ring-offset-background focus:ring-ring rounded-xs focus:outline-hidden absolute end-4 top-4 opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 disabled:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0"
class="ui:ring-offset-background ui:focus:ring-ring ui:rounded-xs ui:focus:outline-hidden ui:absolute ui:top-4 ui:opacity-70 ui:transition-opacity ui:hover:opacity-100 ui:focus:ring-2 ui:focus:ring-offset-2 ui:disabled:pointer-events-none ui:[&_svg:not([class*='size-'])]:size-4 ui:[&_svg]:pointer-events-none ui:[&_svg]:shrink-0 end-4"
>
<XIcon />
<span class="sr-only">Close</span>
@@ -8,6 +8,6 @@
<DialogPrimitive.Description
bind:ref
data-slot="dialog-description"
class={cn('text-muted-foreground text-sm', className)}
class={cn('ui:text-muted-foreground ui:text-sm', className)}
{...restProps}
/>
@@ -13,7 +13,7 @@
<div
bind:this={ref}
data-slot="dialog-footer"
class={cn('flex flex-col-reverse gap-2 sm:flex-row sm:justify-end', className)}
class={cn('ui:flex ui:flex-col-reverse ui:gap-2 ui:sm:flex-row ui:sm:justify-end', className)}
{...restProps}
>
{@render children?.()}
@@ -13,7 +13,7 @@
<div
bind:this={ref}
data-slot="dialog-header"
class={cn('flex flex-col gap-2 text-center sm:text-left', className)}
class={cn('ui:flex ui:flex-col ui:gap-2 ui:text-center ui:sm:text-left', className)}
{...restProps}
>
{@render children?.()}
@@ -9,7 +9,7 @@
bind:ref
data-slot="dialog-overlay"
class={cn(
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50',
'ui:data-[state=open]:animate-in ui:data-[state=closed]:animate-out ui:data-[state=closed]:fade-out-0 ui:data-[state=open]:fade-in-0 ui:fixed ui:inset-0 ui:z-50 ui:bg-black/50',
className
)}
{...restProps}
@@ -8,6 +8,6 @@
<DialogPrimitive.Title
bind:ref
data-slot="dialog-title"
class={cn('text-lg font-semibold leading-none', className)}
class={cn('ui:text-lg ui:font-semibold ui:leading-none', className)}
{...restProps}
/>
@@ -1,7 +1,7 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from "bits-ui";
import { Dialog as DialogPrimitive } from 'bits-ui';
let { ref = $bindable(null), ...restProps }: DialogPrimitive.TriggerProps = $props();
let { ref = $bindable(null), ...restProps }: DialogPrimitive.TriggerProps = $props();
</script>
<DialogPrimitive.Trigger bind:ref data-slot="dialog-trigger" {...restProps} />
+30 -30
View File
@@ -1,37 +1,37 @@
import { Dialog as DialogPrimitive } from "bits-ui";
import { Dialog as DialogPrimitive } from 'bits-ui';
import Title from "./dialog-title.svelte";
import Footer from "./dialog-footer.svelte";
import Header from "./dialog-header.svelte";
import Overlay from "./dialog-overlay.svelte";
import Content from "./dialog-content.svelte";
import Description from "./dialog-description.svelte";
import Trigger from "./dialog-trigger.svelte";
import Close from "./dialog-close.svelte";
import Title from './dialog-title.svelte';
import Footer from './dialog-footer.svelte';
import Header from './dialog-header.svelte';
import Overlay from './dialog-overlay.svelte';
import Content from './dialog-content.svelte';
import Description from './dialog-description.svelte';
import Trigger from './dialog-trigger.svelte';
import Close from './dialog-close.svelte';
const Root = DialogPrimitive.Root;
const Portal = DialogPrimitive.Portal;
export {
Root,
Title,
Portal,
Footer,
Header,
Trigger,
Overlay,
Content,
Description,
Close,
//
Root as Dialog,
Title as DialogTitle,
Portal as DialogPortal,
Footer as DialogFooter,
Header as DialogHeader,
Trigger as DialogTrigger,
Overlay as DialogOverlay,
Content as DialogContent,
Description as DialogDescription,
Close as DialogClose,
Root,
Title,
Portal,
Footer,
Header,
Trigger,
Overlay,
Content,
Description,
Close,
//
Root as Dialog,
Title as DialogTitle,
Portal as DialogPortal,
Footer as DialogFooter,
Header as DialogHeader,
Trigger as DialogTrigger,
Overlay as DialogOverlay,
Content as DialogContent,
Description as DialogDescription,
Close as DialogClose
};
@@ -0,0 +1,7 @@
<script lang="ts">
import { Drawer as DrawerPrimitive } from "vaul-svelte";
let { ref = $bindable(null), ...restProps }: DrawerPrimitive.CloseProps = $props();
</script>
<DrawerPrimitive.Close bind:ref data-slot="drawer-close" {...restProps} />
@@ -0,0 +1,37 @@
<script lang="ts">
import { Drawer as DrawerPrimitive } from 'vaul-svelte';
import DrawerOverlay from './drawer-overlay.svelte';
import { cn } from '../../tools';
let {
ref = $bindable(null),
class: className,
portalProps,
children,
...restProps
}: DrawerPrimitive.ContentProps & {
portalProps?: DrawerPrimitive.PortalProps;
} = $props();
</script>
<DrawerPrimitive.Portal {...portalProps}>
<DrawerOverlay />
<DrawerPrimitive.Content
bind:ref
data-slot="drawer-content"
class={cn(
'ui:group/drawer-content ui:bg-background ui:fixed ui:z-50 ui:flex ui:h-auto ui:flex-col',
'ui:data-[vaul-drawer-direction=top]:inset-x-0 ui:data-[vaul-drawer-direction=top]:top-0 ui:data-[vaul-drawer-direction=top]:mb-24 ui:data-[vaul-drawer-direction=top]:max-h-[80vh] ui:data-[vaul-drawer-direction=top]:rounded-b-lg ui:data-[vaul-drawer-direction=top]:border-b',
'ui:data-[vaul-drawer-direction=bottom]:inset-x-0 ui:data-[vaul-drawer-direction=bottom]:bottom-0 ui:data-[vaul-drawer-direction=bottom]:mt-24 ui:data-[vaul-drawer-direction=bottom]:max-h-[80vh] ui:data-[vaul-drawer-direction=bottom]:rounded-t-lg ui:data-[vaul-drawer-direction=bottom]:border-t',
'ui:data-[vaul-drawer-direction=right]:inset-y-0 ui:data-[vaul-drawer-direction=right]:end-0 ui:data-[vaul-drawer-direction=right]:w-3/4 ui:data-[vaul-drawer-direction=right]:border-s ui:data-[vaul-drawer-direction=right]:sm:max-w-sm',
'ui:data-[vaul-drawer-direction=left]:inset-y-0 ui:data-[vaul-drawer-direction=left]:start-0 ui:data-[vaul-drawer-direction=left]:w-3/4 ui:data-[vaul-drawer-direction=left]:border-e ui:data-[vaul-drawer-direction=left]:sm:max-w-sm',
className
)}
{...restProps}
>
<div
class="ui:bg-muted ui:mx-auto ui:mt-4 ui:hidden ui:h-2 ui:w-[100px] ui:shrink-0 ui:rounded-full ui:group-data-[vaul-drawer-direction=bottom]/drawer-content:block"
></div>
{@render children?.()}
</DrawerPrimitive.Content>
</DrawerPrimitive.Portal>
@@ -0,0 +1,13 @@
<script lang="ts">
import { Drawer as DrawerPrimitive } from 'vaul-svelte';
import { cn } from '../../tools';
let { ref = $bindable(null), class: className, ...restProps }: DrawerPrimitive.DescriptionProps = $props();
</script>
<DrawerPrimitive.Description
bind:ref
data-slot="drawer-description"
class={cn('ui:text-muted-foreground ui:text-sm', className)}
{...restProps}
/>
@@ -0,0 +1,20 @@
<script lang="ts">
import { cn, type WithElementRef } from '../../tools';
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="drawer-footer"
class={cn('ui:mt-auto ui:flex ui:flex-col ui:gap-2 ui:p-4', className)}
{...restProps}
>
{@render children?.()}
</div>
@@ -0,0 +1,20 @@
<script lang="ts">
import type { HTMLAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from '../../tools';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="drawer-header"
class={cn('ui:flex ui:flex-col ui:gap-1.5 ui:p-4', className)}
{...restProps}
>
{@render children?.()}
</div>
@@ -0,0 +1,12 @@
<script lang="ts">
import { Drawer as DrawerPrimitive } from 'vaul-svelte';
let {
shouldScaleBackground = true,
open = $bindable(false),
activeSnapPoint = $bindable(null),
...restProps
}: DrawerPrimitive.RootProps = $props();
</script>
<DrawerPrimitive.NestedRoot {shouldScaleBackground} bind:open bind:activeSnapPoint {...restProps} />
@@ -0,0 +1,16 @@
<script lang="ts">
import { Drawer as DrawerPrimitive } from 'vaul-svelte';
import { cn } from '../../tools';
let { ref = $bindable(null), class: className, ...restProps }: DrawerPrimitive.OverlayProps = $props();
</script>
<DrawerPrimitive.Overlay
bind:ref
data-slot="drawer-overlay"
class={cn(
'ui:data-[state=open]:animate-in ui:data-[state=closed]:animate-out ui:data-[state=closed]:fade-out-0 ui:data-[state=open]:fade-in-0 ui:fixed ui:inset-0 ui:z-50 ui:bg-black/50',
className
)}
{...restProps}
/>
@@ -0,0 +1,13 @@
<script lang="ts">
import { Drawer as DrawerPrimitive } from 'vaul-svelte';
import { cn } from '../../tools';
let { ref = $bindable(null), class: className, ...restProps }: DrawerPrimitive.TitleProps = $props();
</script>
<DrawerPrimitive.Title
bind:ref
data-slot="drawer-title"
class={cn('ui:text-foreground ui:font-semibold', className)}
{...restProps}
/>
@@ -0,0 +1,7 @@
<script lang="ts">
import { Drawer as DrawerPrimitive } from "vaul-svelte";
let { ref = $bindable(null), ...restProps }: DrawerPrimitive.TriggerProps = $props();
</script>
<DrawerPrimitive.Trigger bind:ref data-slot="drawer-trigger" {...restProps} />
+12
View File
@@ -0,0 +1,12 @@
<script lang="ts">
import { Drawer as DrawerPrimitive } from "vaul-svelte";
let {
shouldScaleBackground = true,
open = $bindable(false),
activeSnapPoint = $bindable(null),
...restProps
}: DrawerPrimitive.RootProps = $props();
</script>
<DrawerPrimitive.Root {shouldScaleBackground} bind:open bind:activeSnapPoint {...restProps} />
+41
View File
@@ -0,0 +1,41 @@
import { Drawer as DrawerPrimitive } from "vaul-svelte";
import Root from "./drawer.svelte";
import Content from "./drawer-content.svelte";
import Description from "./drawer-description.svelte";
import Overlay from "./drawer-overlay.svelte";
import Footer from "./drawer-footer.svelte";
import Header from "./drawer-header.svelte";
import Title from "./drawer-title.svelte";
import NestedRoot from "./drawer-nested.svelte";
import Close from "./drawer-close.svelte";
import Trigger from "./drawer-trigger.svelte";
const Portal: typeof DrawerPrimitive.Portal = DrawerPrimitive.Portal;
export {
Root,
NestedRoot,
Content,
Description,
Overlay,
Footer,
Header,
Title,
Trigger,
Portal,
Close,
//
Root as Drawer,
NestedRoot as DrawerNestedRoot,
Content as DrawerContent,
Description as DrawerDescription,
Overlay as DrawerOverlay,
Footer as DrawerFooter,
Header as DrawerHeader,
Title as DrawerTitle,
Trigger as DrawerTrigger,
Portal as DrawerPortal,
Close as DrawerClose,
};
@@ -23,17 +23,17 @@
bind:indeterminate
data-slot="dropdown-menu-checkbox-item"
class={cn(
"focus:bg-accent focus:text-accent-foreground outline-hidden relative flex cursor-default select-none items-center gap-2 rounded-sm py-1.5 pl-8 pr-2 text-sm data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
"ui:focus:bg-accent ui:focus:text-accent-foreground ui:outline-hidden ui:relative ui:flex ui:cursor-default ui:select-none ui:items-center ui:gap-2 ui:rounded-sm ui:py-1.5 ui:pl-8 ui:pr-2 ui:text-sm ui:data-disabled:pointer-events-none ui:data-disabled:opacity-50 ui:[&_svg:not([class*='size-'])]:size-4 ui:[&_svg]:pointer-events-none ui:[&_svg]:shrink-0",
className
)}
{...restProps}
>
{#snippet children({ checked, indeterminate })}
<span class="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
<span class="ui:pointer-events-none ui:absolute ui:left-2 ui:flex ui:size-3.5 ui:items-center ui:justify-center">
{#if indeterminate}
<MinusIcon class="size-4" />
<MinusIcon class="ui:size-4" />
{:else}
<CheckIcon class={cn('size-4', !checked && 'text-transparent')} />
<CheckIcon class={cn('ui:size-4', !checked && 'text-transparent')} />
{/if}
</span>
{@render childrenProp?.()}
@@ -19,7 +19,7 @@
data-slot="dropdown-menu-content"
{sideOffset}
class={cn(
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 max-h-(--bits-dropdown-menu-content-available-height) origin-(--bits-dropdown-menu-content-transform-origin) z-50 min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border p-1 shadow-md outline-none',
'ui:bg-popover ui:text-popover-foreground ui:data-[state=open]:animate-in ui:data-[state=closed]:animate-out ui:data-[state=closed]:fade-out-0 ui:data-[state=open]:fade-in-0 ui:data-[state=closed]:zoom-out-95 ui:data-[state=open]:zoom-in-95 ui:data-[side=bottom]:slide-in-from-top-2 ui:data-[side=left]:slide-in-from-right-2 ui:data-[side=right]:slide-in-from-left-2 ui:data-[side=top]:slide-in-from-bottom-2 ui:max-h-(--bits-dropdown-menu-content-available-height) ui:origin-(--bits-dropdown-menu-content-transform-origin) ui:z-50 ui:min-w-32 ui:overflow-y-auto ui:overflow-x-hidden ui:rounded-md ui:border ui:p-1 ui:shadow-md ui:outline-none',
className
)}
{...restProps}
@@ -17,6 +17,6 @@
bind:ref
data-slot="dropdown-menu-group-heading"
data-inset={inset}
class={cn('px-2 py-1.5 text-sm font-semibold data-[inset]:pl-8', className)}
class={cn('ui:px-2 ui:py-1.5 ui:text-sm ui:font-semibold ui:data-inset:pl-8', className)}
{...restProps}
/>
@@ -1,7 +1,7 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
let { ref = $bindable(null), ...restProps }: DropdownMenuPrimitive.GroupProps = $props();
let { ref = $bindable(null), ...restProps }: DropdownMenuPrimitive.GroupProps = $props();
</script>
<DropdownMenuPrimitive.Group bind:ref data-slot="dropdown-menu-group" {...restProps} />
@@ -20,7 +20,7 @@
data-inset={inset}
data-variant={variant}
class={cn(
"data-highlighted:bg-accent data-highlighted:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:data-highlighted:bg-destructive/10 dark:data-[variant=destructive]:data-highlighted:bg-destructive/20 data-[variant=destructive]:data-highlighted:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground outline-hidden relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm data-[disabled]:pointer-events-none data-[inset]:pl-8 data-[disabled]:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
"ui:data-highlighted:bg-accent ui:data-highlighted:text-accent-foreground ui:data-[variant=destructive]:text-destructive ui:data-[variant=destructive]:data-highlighted:bg-destructive/10 ui:dark:ui:data-[variant=destructive]:data-highlighted:bg-destructive/20 ui:data-[variant=destructive]:data-highlighted:text-destructive ui:data-[variant=destructive]:*:[svg]:text-destructive! ui:[&_svg:not([class*='text-'])]:text-muted-foreground ui:outline-hidden ui:relative ui:flex ui:cursor-default ui:select-none ui:items-center ui:gap-2 ui:rounded-sm ui:px-2 ui:py-1.5 ui:text-sm ui:data-disabled:pointer-events-none ui:data-inset:pl-8 ui:data-disabled:opacity-50 ui:[&_svg:not([class*='size-'])]:size-4 ui:[&_svg]:pointer-events-none ui:[&_svg]:shrink-0",
className
)}
{...restProps}
@@ -17,7 +17,7 @@
bind:this={ref}
data-slot="dropdown-menu-label"
data-inset={inset}
class={cn('px-2 py-1.5 text-sm font-semibold data-[inset]:pl-8', className)}
class={cn('ui:px-2 ui:py-1.5 ui:text-sm ui:font-semibold ui:data-[inset]:pl-8', className)}
{...restProps}
>
{@render children?.()}
@@ -1,16 +1,7 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
let {
ref = $bindable(null),
value = $bindable(),
...restProps
}: DropdownMenuPrimitive.RadioGroupProps = $props();
let { ref = $bindable(null), value = $bindable(), ...restProps }: DropdownMenuPrimitive.RadioGroupProps = $props();
</script>
<DropdownMenuPrimitive.RadioGroup
bind:ref
bind:value
data-slot="dropdown-menu-radio-group"
{...restProps}
/>
<DropdownMenuPrimitive.RadioGroup bind:ref bind:value data-slot="dropdown-menu-radio-group" {...restProps} />
@@ -15,15 +15,15 @@
bind:ref
data-slot="dropdown-menu-radio-item"
class={cn(
"focus:bg-accent focus:text-accent-foreground outline-hidden relative flex cursor-default select-none items-center gap-2 rounded-sm py-1.5 pl-8 pr-2 text-sm data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
"ui:focus:bg-accent ui:focus:text-accent-foreground ui:outline-hidden ui:relative ui:flex ui:cursor-default ui:select-none ui:items-center ui:gap-2 ui:rounded-sm ui:py-1.5 ui:pl-8 ui:pr-2 ui:text-sm ui:data-disabled:pointer-events-none ui:data-disabled:opacity-50 ui:[&_svg:not([class*='size-'])]:size-4 ui:[&_svg]:pointer-events-none ui:[&_svg]:shrink-0",
className
)}
{...restProps}
>
{#snippet children({ checked })}
<span class="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
<span class="ui:pointer-events-none ui:absolute ui:left-2 ui:flex ui:size-3.5 ui:items-center ui:justify-center">
{#if checked}
<CircleIcon class="size-2 fill-current" />
<CircleIcon class="ui:size-2 ui:fill-current" />
{/if}
</span>
{@render childrenProp?.({ checked })}
@@ -8,6 +8,6 @@
<DropdownMenuPrimitive.Separator
bind:ref
data-slot="dropdown-menu-separator"
class={cn('bg-border -mx-1 my-1 h-px', className)}
class={cn('ui:bg-border ui:-mx-1 ui:my-1 ui:h-px', className)}
{...restProps}
/>
@@ -13,7 +13,7 @@
<span
bind:this={ref}
data-slot="dropdown-menu-shortcut"
class={cn('text-muted-foreground ml-auto text-xs tracking-widest', className)}
class={cn('ui:text-muted-foreground ui:ml-auto ui:text-xs ui:tracking-widest', className)}
{...restProps}
>
{@render children?.()}
@@ -9,7 +9,7 @@
bind:ref
data-slot="dropdown-menu-sub-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-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--bits-dropdown-menu-content-transform-origin) z-50 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-lg',
'ui:bg-popover ui:text-popover-foreground ui:data-[state=open]:animate-in ui:data-[state=closed]:animate-out ui:data-[state=closed]:fade-out-0 ui:data-[state=open]:fade-in-0 ui:data-[state=closed]:zoom-out-95 ui:data-[state=open]:zoom-in-95 ui:data-[side=bottom]:slide-in-from-top-2 ui:data-[side=left]:slide-in-from-right-2 ui:data-[side=right]:slide-in-from-left-2 ui:data-[side=top]:slide-in-from-bottom-2 ui:origin-(--bits-dropdown-menu-content-transform-origin) ui:z-50 ui:min-w-32 ui:overflow-hidden ui:rounded-md ui:border ui:p-1 ui:shadow-lg',
className
)}
{...restProps}
@@ -19,11 +19,11 @@
data-slot="dropdown-menu-sub-trigger"
data-inset={inset}
class={cn(
"data-highlighted:bg-accent data-highlighted:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground outline-hidden [&_svg:not([class*='text-'])]:text-muted-foreground flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm data-[disabled]:pointer-events-none data-[inset]:pl-8 data-[disabled]:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
"ui:data-highlighted:bg-accent ui:data-highlighted:text-accent-foreground ui:data-[state=open]:bg-accent ui:data-[state=open]:text-accent-foreground ui:outline-hidden ui:[&_svg:not([class*='text-'])]:text-muted-foreground ui:flex ui:cursor-default ui:select-none ui:items-center ui:gap-2 ui:rounded-sm ui:px-2 ui:py-1.5 ui:text-sm ui:data-disabled:pointer-events-none ui:data-inset:pl-8 ui:data-disabled:opacity-50 ui:[&_svg:not([class*='size-'])]:size-4 ui:[&_svg]:pointer-events-none ui:[&_svg]:shrink-0",
className
)}
{...restProps}
>
{@render children?.()}
<ChevronRightIcon class="ml-auto size-4" />
<ChevronRightIcon class="ui:ml-auto ui:size-4" />
</DropdownMenuPrimitive.SubTrigger>
@@ -1,7 +1,7 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
let { ref = $bindable(null), ...restProps }: DropdownMenuPrimitive.TriggerProps = $props();
let { ref = $bindable(null), ...restProps }: DropdownMenuPrimitive.TriggerProps = $props();
</script>
<DropdownMenuPrimitive.Trigger bind:ref data-slot="dropdown-menu-trigger" {...restProps} />
+44 -44
View File
@@ -1,49 +1,49 @@
import { DropdownMenu as DropdownMenuPrimitive } from "bits-ui";
import CheckboxItem from "./dropdown-menu-checkbox-item.svelte";
import Content from "./dropdown-menu-content.svelte";
import Group from "./dropdown-menu-group.svelte";
import Item from "./dropdown-menu-item.svelte";
import Label from "./dropdown-menu-label.svelte";
import RadioGroup from "./dropdown-menu-radio-group.svelte";
import RadioItem from "./dropdown-menu-radio-item.svelte";
import Separator from "./dropdown-menu-separator.svelte";
import Shortcut from "./dropdown-menu-shortcut.svelte";
import Trigger from "./dropdown-menu-trigger.svelte";
import SubContent from "./dropdown-menu-sub-content.svelte";
import SubTrigger from "./dropdown-menu-sub-trigger.svelte";
import GroupHeading from "./dropdown-menu-group-heading.svelte";
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import CheckboxItem from './dropdown-menu-checkbox-item.svelte';
import Content from './dropdown-menu-content.svelte';
import Group from './dropdown-menu-group.svelte';
import Item from './dropdown-menu-item.svelte';
import Label from './dropdown-menu-label.svelte';
import RadioGroup from './dropdown-menu-radio-group.svelte';
import RadioItem from './dropdown-menu-radio-item.svelte';
import Separator from './dropdown-menu-separator.svelte';
import Shortcut from './dropdown-menu-shortcut.svelte';
import Trigger from './dropdown-menu-trigger.svelte';
import SubContent from './dropdown-menu-sub-content.svelte';
import SubTrigger from './dropdown-menu-sub-trigger.svelte';
import GroupHeading from './dropdown-menu-group-heading.svelte';
const Sub = DropdownMenuPrimitive.Sub;
const Root = DropdownMenuPrimitive.Root;
export {
CheckboxItem,
Content,
Root as DropdownMenu,
CheckboxItem as DropdownMenuCheckboxItem,
Content as DropdownMenuContent,
Group as DropdownMenuGroup,
Item as DropdownMenuItem,
Label as DropdownMenuLabel,
RadioGroup as DropdownMenuRadioGroup,
RadioItem as DropdownMenuRadioItem,
Separator as DropdownMenuSeparator,
Shortcut as DropdownMenuShortcut,
Sub as DropdownMenuSub,
SubContent as DropdownMenuSubContent,
SubTrigger as DropdownMenuSubTrigger,
Trigger as DropdownMenuTrigger,
GroupHeading as DropdownMenuGroupHeading,
Group,
GroupHeading,
Item,
Label,
RadioGroup,
RadioItem,
Root,
Separator,
Shortcut,
Sub,
SubContent,
SubTrigger,
Trigger,
CheckboxItem,
Content,
Root as DropdownMenu,
CheckboxItem as DropdownMenuCheckboxItem,
Content as DropdownMenuContent,
Group as DropdownMenuGroup,
Item as DropdownMenuItem,
Label as DropdownMenuLabel,
RadioGroup as DropdownMenuRadioGroup,
RadioItem as DropdownMenuRadioItem,
Separator as DropdownMenuSeparator,
Shortcut as DropdownMenuShortcut,
Sub as DropdownMenuSub,
SubContent as DropdownMenuSubContent,
SubTrigger as DropdownMenuSubTrigger,
Trigger as DropdownMenuTrigger,
GroupHeading as DropdownMenuGroupHeading,
Group,
GroupHeading,
Item,
Label,
RadioGroup,
RadioItem,
Root,
Separator,
Shortcut,
Sub,
SubContent,
SubTrigger,
Trigger
};
+22
View File
@@ -0,0 +1,22 @@
import Root from "./input-group.svelte";
import Addon from "./input-group-addon.svelte";
import Button from "./input-group-button.svelte";
import Input from "./input-group-input.svelte";
import Text from "./input-group-text.svelte";
import Textarea from "./input-group-textarea.svelte";
export {
Root,
Addon,
Button,
Input,
Text,
Textarea,
//
Root as InputGroup,
Addon as InputGroupAddon,
Button as InputGroupButton,
Input as InputGroupInput,
Text as InputGroupText,
Textarea as InputGroupTextarea,
};
@@ -0,0 +1,54 @@
<script lang="ts" module>
import { tv, type VariantProps } from 'tailwind-variants';
export const inputGroupAddonVariants = tv({
base: "ui:text-muted-foreground ui:flex ui:h-auto ui:cursor-text ui:select-none ui:items-center ui:justify-center ui:gap-2 ui:py-1.5 ui:text-sm ui:font-medium ui:group-data-[disabled=true]/input-group:opacity-50 ui:[&>kbd]:rounded-[calc(var(--radius)-5px)] ui:[&>svg:not([class*='size-'])]:size-4",
variants: {
align: {
'inline-start': 'ui:order-first ui:ps-3 ui:has-[>button]:ms-[-0.45rem] ui:has-[>kbd]:ms-[-0.35rem]',
'inline-end': 'ui:order-last ui:pe-3 ui:has-[>button]:me-[-0.45rem] ui:has-[>kbd]:me-[-0.35rem]',
'block-start':
'ui:[.border-b]:pb-3 ui:order-first ui:w-full ui:justify-start ui:px-3 ui:pt-3 ui:group-has-[>input]/input-group:pt-2.5',
'block-end':
'ui:[.border-t]:pt-3 ui:order-last ui:w-full ui:justify-start ui:px-3 ui:pb-3 ui:group-has-[>input]/input-group:pb-2.5'
}
},
defaultVariants: {
align: 'inline-start'
}
});
export type InputGroupAddonAlign = VariantProps<typeof inputGroupAddonVariants>['align'];
</script>
<script lang="ts">
import { cn, type WithElementRef } from '../../tools';
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
align = 'inline-start',
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
align?: InputGroupAddonAlign;
} = $props();
</script>
<div
bind:this={ref}
role="group"
data-slot="input-group-addon"
data-align={align}
class={cn(inputGroupAddonVariants({ align }), className)}
onclick={(e) => {
if ((e.target as HTMLElement).closest('button')) {
return;
}
e.currentTarget.parentElement?.querySelector('input')?.focus();
}}
{...restProps}
>
{@render children?.()}
</div>
@@ -0,0 +1,49 @@
<script lang="ts" module>
import { tv, type VariantProps } from 'tailwind-variants';
const inputGroupButtonVariants = tv({
base: 'ui:flex ui:items-center ui:gap-2 ui:text-sm ui:shadow-none',
variants: {
size: {
xs: "ui:h-6 ui:gap-1 ui:rounded-[calc(var(--radius)-5px)] ui:px-2 ui:has-[>svg]:px-2 ui:[&>svg:not([class*='size-'])]:size-3.5",
sm: 'ui:h-8 ui:gap-1.5 ui:rounded-md ui:px-2.5 ui:has-[>svg]:px-2.5',
'icon-xs': 'ui:size-6 ui:rounded-[calc(var(--radius)-5px)] ui:p-0 ui:has-[>svg]:p-0',
'icon-sm': 'ui:size-8 ui:p-0 ui:has-[>svg]:p-0'
}
},
defaultVariants: {
size: 'xs'
}
});
export type InputGroupButtonSize = VariantProps<typeof inputGroupButtonVariants>['size'];
</script>
<script lang="ts">
import { cn } from '../../tools';
import type { ComponentProps } from 'svelte';
import { Button } from '$src/base/button/index.js';
let {
ref = $bindable(null),
class: className,
children,
type = 'button',
variant = 'ghost',
size = 'xs',
...restProps
}: Omit<ComponentProps<typeof Button>, 'href' | 'size'> & {
size?: InputGroupButtonSize;
} = $props();
</script>
<Button
bind:ref
{type}
data-size={size}
{variant}
class={cn(inputGroupButtonVariants({ size }), className)}
{...restProps}
>
{@render children?.()}
</Button>
@@ -0,0 +1,23 @@
<script lang="ts">
import { cn } from '../../tools';
import type { ComponentProps } from 'svelte';
import { Input } from '$src/base/input/index.js';
let {
ref = $bindable(null),
value = $bindable(),
class: className,
...props
}: ComponentProps<typeof Input> = $props();
</script>
<Input
bind:ref
data-slot="input-group-control"
class={cn(
'ui:flex-1 ui:rounded-none ui:border-0 ui:bg-transparent ui:shadow-none ui:focus-visible:ring-0 ui:dark:bg-transparent',
className
)}
bind:value
{...props}
/>
@@ -0,0 +1,22 @@
<script lang="ts">
import { cn, type WithElementRef } from '../../tools';
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLSpanElement>> = $props();
</script>
<span
bind:this={ref}
class={cn(
"ui:text-muted-foreground ui:flex ui:items-center ui:gap-2 ui:text-sm ui:[&_svg:not([class*='size-'])]:size-4 ui:[&_svg]:pointer-events-none",
className
)}
{...restProps}
>
{@render children?.()}
</span>
@@ -0,0 +1,23 @@
<script lang="ts">
import { cn } from '../../tools';
import { Textarea } from '$src/base/textarea/index.js';
import type { ComponentProps } from 'svelte';
let {
ref = $bindable(null),
value = $bindable(),
class: className,
...props
}: ComponentProps<typeof Textarea> = $props();
</script>
<Textarea
bind:ref
data-slot="input-group-control"
class={cn(
'ui:flex-1 ui:resize-none ui:rounded-none ui:border-0 ui:bg-transparent ui:py-3 ui:shadow-none ui:focus-visible:ring-0 ui:dark:bg-transparent',
className
)}
bind:value
{...props}
/>
@@ -0,0 +1,38 @@
<script lang="ts">
import { cn, type WithElementRef } from '../../tools';
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
...props
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="input-group"
role="group"
class={cn(
'ui:group/input-group ui:border-input ui:dark:bg-input/30 ui:shadow-xs ui:relative ui:flex ui:w-full ui:items-center ui:rounded-md ui:border ui:outline-none ui:transition-[color,box-shadow]',
'ui:h-9 ui:has-[>textarea]:h-auto',
// Variants based on alignment.
'ui:has-[>[data-align=inline-start]]:[&>input]:ps-2',
'ui:has-[>[data-align=inline-end]]:[&>input]:pe-2',
'ui:has-[>[data-align=block-start]]:h-auto ui:has-[>[data-align=block-start]]:flex-col ui:has-[>[data-align=block-start]]:[&>input]:pb-3',
'ui:has-[>[data-align=block-end]]:h-auto ui:has-[>[data-align=block-end]]:flex-col ui:has-[>[data-align=block-end]]:[&>input]:pt-3',
// Focus state.
'ui:has-[[data-slot=input-group-control]:focus-visible]:border-ring ui:has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 ui:has-[[data-slot=input-group-control]:focus-visible]:ring-[3px]',
// Error state.
'ui:has-[[data-slot][aria-invalid=true]]:ring-destructive/20 ui:has-[[data-slot][aria-invalid=true]]:border-destructive ui:dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40',
className
)}
{...props}
>
{@render children?.()}
</div>
+3 -3
View File
@@ -24,9 +24,9 @@
bind:this={ref}
data-slot={dataSlot}
class={cn(
'selection:bg-primary dark:bg-input/30 selection:text-primary-foreground border-input ring-offset-background placeholder:text-muted-foreground shadow-xs flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 pt-1.5 text-sm font-medium outline-none transition-[color,box-shadow] disabled:cursor-not-allowed disabled:opacity-50',
'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',
'ui:selection:bg-primary ui:dark:bg-input/30 ui:selection:text-primary-foreground ui:border-input ui:ring-offset-background ui:placeholder:text-muted-foreground ui:shadow-xs ui:flex ui:h-9 ui:w-full ui:min-w-0 ui:rounded-md ui:border ui:bg-transparent ui:px-3 ui:pt-1.5 ui:text-sm ui:font-medium ui:outline-none ui:transition-[color,box-shadow] ui:disabled:cursor-not-allowed ui:disabled:opacity-50',
'ui:focus-visible:border-ring ui:focus-visible:ring-ring/50 ui:focus-visible:ring-[3px]',
'ui:aria-invalid:ring-destructive/20 ui:dark:aria-invalid:ring-destructive/40 ui:aria-invalid:border-destructive',
className
)}
type="file"
+34
View File
@@ -0,0 +1,34 @@
import Root from "./item.svelte";
import Group from "./item-group.svelte";
import Separator from "./item-separator.svelte";
import Header from "./item-header.svelte";
import Footer from "./item-footer.svelte";
import Content from "./item-content.svelte";
import Title from "./item-title.svelte";
import Description from "./item-description.svelte";
import Actions from "./item-actions.svelte";
import Media from "./item-media.svelte";
export {
Root,
Group,
Separator,
Header,
Footer,
Content,
Title,
Description,
Actions,
Media,
//
Root as Item,
Group as ItemGroup,
Separator as ItemSeparator,
Header as ItemHeader,
Footer as ItemFooter,
Content as ItemContent,
Title as ItemTitle,
Description as ItemDescription,
Actions as ItemActions,
Media as ItemMedia,
};
@@ -0,0 +1,15 @@
<script lang="ts">
import { cn, type WithElementRef } from '../../tools';
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div bind:this={ref} data-slot="item-actions" class={cn('ui:flex ui:items-center ui:gap-2', className)} {...restProps}>
{@render children?.()}
</div>
@@ -0,0 +1,20 @@
<script lang="ts">
import { cn, type WithElementRef } from '../../tools';
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="item-content"
class={cn('ui:flex ui:flex-1 ui:flex-col ui:gap-1 ui:[&+[data-slot=item-content]]:flex-none', className)}
{...restProps}
>
{@render children?.()}
</div>
@@ -0,0 +1,24 @@
<script lang="ts">
import { cn, type WithElementRef } from '../../tools';
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLParagraphElement>> = $props();
</script>
<p
bind:this={ref}
data-slot="item-description"
class={cn(
'ui:text-muted-foreground ui:line-clamp-2 ui:text-balance ui:text-sm ui:font-normal ui:leading-normal',
'ui:[&>a:hover]:text-primary ui:[&>a]:underline ui:[&>a]:underline-offset-4',
className
)}
{...restProps}
>
{@render children?.()}
</p>
@@ -0,0 +1,20 @@
<script lang="ts">
import { cn, type WithElementRef } from '../../tools';
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="item-footer"
class={cn('ui:flex ui:basis-full ui:items-center ui:justify-between ui:gap-2', className)}
{...restProps}
>
{@render children?.()}
</div>
@@ -0,0 +1,21 @@
<script lang="ts">
import { cn, type WithElementRef } from '../../tools';
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
role="list"
data-slot="item-group"
class={cn('ui:group/item-group ui:flex ui:flex-col', className)}
{...restProps}
>
{@render children?.()}
</div>
@@ -0,0 +1,20 @@
<script lang="ts">
import { cn, type WithElementRef } from '../../tools';
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="item-header"
class={cn('ui:flex ui:basis-full ui:items-center ui:justify-between ui:gap-2', className)}
{...restProps}
>
{@render children?.()}
</div>
@@ -0,0 +1,42 @@
<script lang="ts" module>
import { tv, type VariantProps } from 'tailwind-variants';
export const itemMediaVariants = tv({
base: 'ui:flex ui:shrink-0 ui:items-center ui:justify-center ui:gap-2 ui:group-has-[[data-slot=item-description]]/item:translate-y-0.5 ui:group-has-[[data-slot=item-description]]/item:self-start ui:[&_svg]:pointer-events-none',
variants: {
variant: {
default: 'ui:bg-transparent',
icon: "ui:bg-muted ui:size-8 ui:rounded-sm ui:border ui:[&_svg:not([class*='size-'])]:size-4",
image: 'ui:size-10 ui:overflow-hidden ui:rounded-sm ui:[&_img]:size-full ui:[&_img]:object-cover'
}
},
defaultVariants: {
variant: 'default'
}
});
export type ItemMediaVariant = VariantProps<typeof itemMediaVariants>['variant'];
</script>
<script lang="ts">
import { cn, type WithElementRef } from '../../tools';
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
variant = 'default',
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & { variant?: ItemMediaVariant } = $props();
</script>
<div
bind:this={ref}
data-slot="item-media"
data-variant={variant}
class={cn(itemMediaVariants({ variant }), className)}
{...restProps}
>
{@render children?.()}
</div>
@@ -0,0 +1,15 @@
<script lang="ts">
import { Separator } from '../separator';
import { cn } from '../../tools';
import type { ComponentProps } from 'svelte';
let { ref = $bindable(null), class: className, ...restProps }: ComponentProps<typeof Separator> = $props();
</script>
<Separator
bind:ref
data-slot="item-separator"
orientation="horizontal"
class={cn('ui:my-0', className)}
{...restProps}
/>
@@ -0,0 +1,20 @@
<script lang="ts">
import { cn, type WithElementRef } from '../../tools';
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="item-title"
class={cn('ui:flex ui:w-fit ui:items-center ui:gap-2 ui:text-sm ui:font-medium ui:leading-snug', className)}
{...restProps}
>
{@render children?.()}
</div>
+60
View File
@@ -0,0 +1,60 @@
<script lang="ts" module>
import { tv, type VariantProps } from 'tailwind-variants';
export const itemVariants = tv({
base: 'group/item ui:[a]:hover:bg-accent/50 ui:[a]:transition-colors ui:focus-visible:border-ring ui:focus-visible:ring-ring/50 ui:flex ui:flex-wrap ui:items-center ui:rounded-md ui:border ui:border-transparent ui:text-sm ui:outline-none ui:transition-colors ui:duration-100 ui:focus-visible:ring-[3px]',
variants: {
variant: {
default: 'ui:bg-transparent',
outline: 'ui:border-border',
muted: 'ui:bg-muted/50'
},
size: {
default: 'ui:gap-4 ui:p-4',
sm: 'ui:gap-2.5 ui:px-4 ui:py-3'
}
},
defaultVariants: {
variant: 'default',
size: 'default'
}
});
export type ItemSize = VariantProps<typeof itemVariants>['size'];
export type ItemVariant = VariantProps<typeof itemVariants>['variant'];
</script>
<script lang="ts">
import { cn, type WithElementRef } from '../../tools';
import type { HTMLAttributes } from 'svelte/elements';
import type { Snippet } from 'svelte';
let {
ref = $bindable(null),
class: className,
child,
variant,
size,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
child?: Snippet<[{ props: Record<string, unknown> }]>;
variant?: ItemVariant;
size?: ItemSize;
} = $props();
const mergedProps = $derived({
class: cn(itemVariants({ variant, size }), className),
'data-slot': 'item',
'data-variant': variant,
'data-size': size,
...restProps
});
</script>
{#if child}
{@render child({ props: mergedProps })}
{:else}
<div bind:this={ref} {...mergedProps}>
{@render mergedProps.children?.()}
</div>
{/if}
+10
View File
@@ -0,0 +1,10 @@
import Root from "./kbd.svelte";
import Group from "./kbd-group.svelte";
export {
Root,
Group,
//
Root as Kbd,
Group as KbdGroup,
};
+20
View File
@@ -0,0 +1,20 @@
<script lang="ts">
import { cn, type WithElementRef } from '../../tools';
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLElement>> = $props();
</script>
<kbd
bind:this={ref}
data-slot="kbd-group"
class={cn('ui:inline-flex ui:items-center ui:gap-1', className)}
{...restProps}
>
{@render children?.()}
</kbd>
+25
View File
@@ -0,0 +1,25 @@
<script lang="ts">
import { cn, type WithElementRef } from '../../tools';
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLElement>> = $props();
</script>
<kbd
bind:this={ref}
data-slot="kbd"
class={cn(
'ui:bg-muted ui:text-muted-foreground ui:pointer-events-none ui:inline-flex ui:h-5 ui:w-fit ui:min-w-5 ui:select-none ui:items-center ui:justify-center ui:gap-1 ui:rounded-sm ui:px-1 ui:font-sans ui:text-xs ui:font-medium',
"ui:[&_svg:not([class*='size-'])]:size-3",
'ui:in-data-[slot=tooltip-content]:bg-background/20 ui:in-data-[slot=tooltip-content]:text-background ui:dark:in-data-[slot=tooltip-content]:bg-background/10',
className
)}
{...restProps}
>
{@render children?.()}
</kbd>
+4 -4
View File
@@ -1,7 +1,7 @@
import Root from "./label.svelte";
import Root from './label.svelte';
export {
Root,
//
Root as Label,
Root,
//
Root as Label
};
+1 -1
View File
@@ -9,7 +9,7 @@
bind:ref
data-slot="label"
class={cn(
'flex select-none items-center gap-2 text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-50 group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50',
'ui:flex ui:select-none ui:items-center ui:gap-2 ui:text-sm ui:font-medium ui:leading-none ui:peer-disabled:cursor-not-allowed ui:peer-disabled:opacity-50 ui:group-data-[disabled=true]:pointer-events-none ui:group-data-[disabled=true]:opacity-50',
className
)}
{...restProps}

Some files were not shown because too many files have changed in this diff Show More