refactor: migrate shadcn-components to Svelte 5 and TW4 (#551)

Co-authored-by: Elias Schneider <login@eliasschneider.com>
This commit is contained in:
Kyle Mendell
2025-05-21 12:15:27 -05:00
committed by Elias Schneider
parent 05b443d984
commit 28c85990ba
197 changed files with 8142 additions and 7471 deletions

View File

@@ -2,20 +2,18 @@
import { Calendar as CalendarPrimitive } from 'bits-ui';
import { cn } from '$lib/utils/style.js';
type $$Props = CalendarPrimitive.CellProps;
export let date: $$Props['date'];
let className: $$Props['class'] = undefined;
export { className as class };
let {
ref = $bindable(null),
class: className,
...restProps
}: CalendarPrimitive.CellProps = $props();
</script>
<CalendarPrimitive.Cell
{date}
bind:ref
class={cn(
'[&:has([data-selected])]:bg-accent [&:has([data-selected][data-outside-month])]:bg-accent/50 relative h-9 w-9 p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([data-selected])]:rounded-md',
'[&:has([data-selected])]:bg-accent [&:has([data-selected][data-outside-month])]:bg-accent/50 relative size-8 p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([data-selected])]:rounded-md',
className
)}
{...$$restProps}
>
<slot />
</CalendarPrimitive.Cell>
{...restProps}
/>

View File

@@ -1,42 +1,30 @@
<script lang="ts">
import { Calendar as CalendarPrimitive } from 'bits-ui';
import { buttonVariants } from '$lib/components/ui/button/index.js';
import { cn } from '$lib/utils/style.js';
import { Calendar as CalendarPrimitive } from 'bits-ui';
type $$Props = CalendarPrimitive.DayProps;
type $$Events = CalendarPrimitive.DayEvents;
export let date: $$Props['date'];
export let month: $$Props['month'];
let className: $$Props['class'] = undefined;
export { className as class };
let {
ref = $bindable(null),
class: className,
...restProps
}: CalendarPrimitive.DayProps = $props();
</script>
<CalendarPrimitive.Day
on:click
{date}
{month}
bind:ref
class={cn(
buttonVariants({ variant: 'ghost' }),
'h-9 w-9 p-0 font-normal ',
'size-8 p-0 font-normal select-none',
'[&[data-today]:not([data-selected])]:bg-accent [&[data-today]:not([data-selected])]:text-accent-foreground',
// Selected
'data-[selected]:bg-primary data-[selected]:text-primary-foreground data-[selected]:hover:bg-primary data-[selected]:hover:text-primary-foreground data-[selected]:focus:bg-primary data-[selected]:focus:text-primary-foreground data-[selected]:opacity-100',
'data-selected:bg-primary data-selected:text-primary-foreground data-selected:hover:bg-primary data-selected:hover:text-primary-foreground data-selected:focus:bg-primary data-selected:focus:text-primary-foreground dark:data-selected:hover:bg-primary dark:data-selected:focus:bg-primary data-selected:opacity-100',
// Disabled
'data-[disabled]:text-muted-foreground data-[disabled]:opacity-50',
'data-disabled:text-muted-foreground data-disabled:opacity-50',
// Unavailable
'data-[unavailable]:text-destructive-foreground data-[unavailable]:line-through',
'data-unavailable:text-destructive-foreground data-unavailable:line-through',
// Outside months
'data-[outside-month]:text-muted-foreground [&[data-outside-month][data-selected]]:bg-accent/50 [&[data-outside-month][data-selected]]:text-muted-foreground data-[outside-month]:pointer-events-none data-[outside-month]:opacity-50 [&[data-outside-month][data-selected]]:opacity-30',
className
)}
{...$$restProps}
let:selected
let:disabled
let:unavailable
let:builder
>
<slot {selected} {disabled} {unavailable} {builder}>
{date.day}
</slot>
</CalendarPrimitive.Day>
{...restProps}
/>

View File

@@ -2,12 +2,11 @@
import { Calendar as CalendarPrimitive } from 'bits-ui';
import { cn } from '$lib/utils/style.js';
type $$Props = CalendarPrimitive.GridBodyProps;
let className: $$Props['class'] = undefined;
export { className as class };
let {
ref = $bindable(null),
class: className,
...restProps
}: CalendarPrimitive.GridBodyProps = $props();
</script>
<CalendarPrimitive.GridBody class={cn(className)} {...$$restProps}>
<slot />
</CalendarPrimitive.GridBody>
<CalendarPrimitive.GridBody bind:ref class={cn(className)} {...restProps} />

View File

@@ -2,12 +2,11 @@
import { Calendar as CalendarPrimitive } from 'bits-ui';
import { cn } from '$lib/utils/style.js';
type $$Props = CalendarPrimitive.GridHeadProps;
let className: $$Props['class'] = undefined;
export { className as class };
let {
ref = $bindable(null),
class: className,
...restProps
}: CalendarPrimitive.GridHeadProps = $props();
</script>
<CalendarPrimitive.GridHead class={cn(className)} {...$$restProps}>
<slot />
</CalendarPrimitive.GridHead>
<CalendarPrimitive.GridHead bind:ref class={cn(className)} {...restProps} />

View File

@@ -2,12 +2,11 @@
import { Calendar as CalendarPrimitive } from 'bits-ui';
import { cn } from '$lib/utils/style.js';
type $$Props = CalendarPrimitive.GridRowProps;
let className: $$Props['class'] = undefined;
export { className as class };
let {
ref = $bindable(null),
class: className,
...restProps
}: CalendarPrimitive.GridRowProps = $props();
</script>
<CalendarPrimitive.GridRow class={cn('flex', className)} {...$$restProps}>
<slot />
</CalendarPrimitive.GridRow>
<CalendarPrimitive.GridRow bind:ref class={cn('flex', className)} {...restProps} />

View File

@@ -2,12 +2,15 @@
import { Calendar as CalendarPrimitive } from 'bits-ui';
import { cn } from '$lib/utils/style.js';
type $$Props = CalendarPrimitive.GridProps;
let className: $$Props['class'] = undefined;
export { className as class };
let {
ref = $bindable(null),
class: className,
...restProps
}: CalendarPrimitive.GridProps = $props();
</script>
<CalendarPrimitive.Grid class={cn('w-full border-collapse space-y-1', className)} {...$$restProps}>
<slot />
</CalendarPrimitive.Grid>
<CalendarPrimitive.Grid
bind:ref
class={cn('w-full border-collapse space-y-1', className)}
{...restProps}
/>

View File

@@ -2,15 +2,15 @@
import { Calendar as CalendarPrimitive } from 'bits-ui';
import { cn } from '$lib/utils/style.js';
type $$Props = CalendarPrimitive.HeadCellProps;
let className: $$Props['class'] = undefined;
export { className as class };
let {
ref = $bindable(null),
class: className,
...restProps
}: CalendarPrimitive.HeadCellProps = $props();
</script>
<CalendarPrimitive.HeadCell
class={cn('text-muted-foreground w-9 rounded-md text-[0.8rem] font-normal', className)}
{...$$restProps}
>
<slot />
</CalendarPrimitive.HeadCell>
bind:ref
class={cn('text-muted-foreground w-8 rounded-md text-[0.8rem] font-normal', className)}
{...restProps}
/>

View File

@@ -2,15 +2,15 @@
import { Calendar as CalendarPrimitive } from 'bits-ui';
import { cn } from '$lib/utils/style.js';
type $$Props = CalendarPrimitive.HeaderProps;
let className: $$Props['class'] = undefined;
export { className as class };
let {
ref = $bindable(null),
class: className,
...restProps
}: CalendarPrimitive.HeaderProps = $props();
</script>
<CalendarPrimitive.Header
bind:ref
class={cn('relative flex w-full items-center justify-between pt-1', className)}
{...$$restProps}
>
<slot />
</CalendarPrimitive.Header>
{...restProps}
/>

View File

@@ -2,18 +2,11 @@
import { Calendar as CalendarPrimitive } from 'bits-ui';
import { cn } from '$lib/utils/style.js';
type $$Props = CalendarPrimitive.HeadingProps;
let className: $$Props['class'] = undefined;
export { className as class };
let {
ref = $bindable(null),
class: className,
...restProps
}: CalendarPrimitive.HeadingProps = $props();
</script>
<CalendarPrimitive.Heading
let:headingValue
class={cn('text-sm font-medium', className)}
{...$$restProps}
>
<slot {headingValue}>
{headingValue}
</slot>
</CalendarPrimitive.Heading>
<CalendarPrimitive.Heading bind:ref class={cn('text-sm font-medium', className)} {...restProps} />

View File

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

View File

@@ -1,27 +1,28 @@
<script lang="ts">
import { Calendar as CalendarPrimitive } from 'bits-ui';
import ChevronRight from 'lucide-svelte/icons/chevron-right';
import ChevronRightIcon from '@lucide/svelte/icons/chevron-right';
import { buttonVariants } from '$lib/components/ui/button/index.js';
import { cn } from '$lib/utils/style.js';
type $$Props = CalendarPrimitive.NextButtonProps;
type $$Events = CalendarPrimitive.NextButtonEvents;
let className: $$Props['class'] = undefined;
export { className as class };
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: CalendarPrimitive.PrevButtonProps = $props();
</script>
{#snippet Fallback()}
<ChevronRightIcon class="size-4" />
{/snippet}
<CalendarPrimitive.NextButton
on:click
bind:ref
class={cn(
buttonVariants({ variant: 'outline' }),
'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100',
'size-7 bg-transparent p-0 opacity-50 hover:opacity-100',
className
)}
{...$$restProps}
let:builder
>
<slot {builder}>
<ChevronRight class="h-4 w-4" />
</slot>
</CalendarPrimitive.NextButton>
children={children || Fallback}
{...restProps}
/>

View File

@@ -1,27 +1,28 @@
<script lang="ts">
import { Calendar as CalendarPrimitive } from 'bits-ui';
import ChevronLeft from 'lucide-svelte/icons/chevron-left';
import ChevronLeftIcon from '@lucide/svelte/icons/chevron-left';
import { buttonVariants } from '$lib/components/ui/button/index.js';
import { cn } from '$lib/utils/style.js';
type $$Props = CalendarPrimitive.PrevButtonProps;
type $$Events = CalendarPrimitive.PrevButtonEvents;
let className: $$Props['class'] = undefined;
export { className as class };
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: CalendarPrimitive.PrevButtonProps = $props();
</script>
{#snippet Fallback()}
<ChevronLeftIcon class="size-4" />
{/snippet}
<CalendarPrimitive.PrevButton
on:click
bind:ref
class={cn(
buttonVariants({ variant: 'outline' }),
'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100',
'size-7 bg-transparent p-0 opacity-50 hover:opacity-100',
className
)}
{...$$restProps}
let:builder
>
<slot {builder}>
<ChevronLeft class="h-4 w-4" />
</slot>
</CalendarPrimitive.PrevButton>
children={children || Fallback}
{...restProps}
/>

View File

@@ -1,137 +1,61 @@
<script lang="ts">
import * as Calendar from '$lib/components/ui/calendar/index.js';
import * as Select from '$lib/components/ui/select/index.js';
import { cn } from '$lib/utils/style';
import { DateFormatter, getLocalTimeZone, today } from '@internationalized/date';
import { Calendar as CalendarPrimitive } from 'bits-ui';
import * as Calendar from './index.js';
import { cn, type WithoutChildrenOrChild } from '$lib/utils/style.js';
type $$Props = CalendarPrimitive.Props;
type $$Events = CalendarPrimitive.Events;
export let value: $$Props['value'] = undefined;
export let placeholder: $$Props['placeholder'] = today(getLocalTimeZone());
export let weekdayFormat: $$Props['weekdayFormat'] = 'short';
const monthOptions = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December'
].map((month, i) => ({ value: i + 1, label: month }));
const monthFmt = new DateFormatter('en-US', {
month: 'long'
});
const yearOptions = Array.from({ length: 100 }, (_, i) => ({
label: String(new Date().getFullYear() + i),
value: new Date().getFullYear() + i
}));
$: defaultYear = placeholder
? {
value: placeholder.year,
label: String(placeholder.year)
}
: undefined;
$: defaultMonth = placeholder
? {
value: placeholder.month,
label: monthFmt.format(placeholder.toDate(getLocalTimeZone()))
}
: undefined;
let className: $$Props['class'] = undefined;
export { className as class };
let {
ref = $bindable(null),
value = $bindable(),
placeholder = $bindable(),
class: className,
weekdayFormat = 'short',
...restProps
}: WithoutChildrenOrChild<CalendarPrimitive.RootProps> = $props();
</script>
<!--
Discriminated Unions + Destructing (required for bindable) do not
get along, so we shut typescript up by casting `value` to `never`.
-->
<CalendarPrimitive.Root
{weekdayFormat}
class={cn('rounded-md border p-3', className)}
{...$$restProps}
on:keydown
let:months
let:weekdays
bind:value
bind:value={value as never}
bind:ref
bind:placeholder
{weekdayFormat}
class={cn('p-3', className)}
{...restProps}
>
<Calendar.Header>
<Calendar.Heading class="flex w-full items-center justify-between gap-2">
<Select.Root
selected={defaultMonth}
items={monthOptions}
onSelectedChange={(v) => {
if (!v || !placeholder) return;
if (v.value === placeholder?.month) return;
placeholder = placeholder.set({ month: v.value });
}}
>
<Select.Trigger aria-label="Select month" class="w-[60%]">
<Select.Value placeholder="Select month" />
</Select.Trigger>
<Select.Content class="max-h-[200px] overflow-y-auto">
{#each monthOptions as { value, label }}
<Select.Item {value} {label}>
{label}
</Select.Item>
{/each}
</Select.Content>
</Select.Root>
<Select.Root
selected={defaultYear}
items={yearOptions}
onSelectedChange={(v) => {
if (!v || !placeholder) return;
if (v.value === placeholder?.year) return;
placeholder = placeholder.set({ year: v.value });
}}
>
<Select.Trigger aria-label="Select year" class="w-[40%]">
<Select.Value placeholder="Select year" />
</Select.Trigger>
<Select.Content class="max-h-[200px] overflow-y-auto">
{#each yearOptions as { value, label }}
<Select.Item {value} {label}>
{label}
</Select.Item>
{/each}
</Select.Content>
</Select.Root>
</Calendar.Heading>
</Calendar.Header>
<Calendar.Months>
{#each months as month}
<Calendar.Grid>
<Calendar.GridHead>
<Calendar.GridRow class="flex">
{#each weekdays as weekday}
<Calendar.HeadCell>
{weekday.slice(0, 2)}
</Calendar.HeadCell>
{/each}
</Calendar.GridRow>
</Calendar.GridHead>
<Calendar.GridBody>
{#each month.weeks as weekDates}
<Calendar.GridRow class="mt-2 w-full">
{#each weekDates as date}
<Calendar.Cell {date}>
<Calendar.Day {date} month={month.value} />
</Calendar.Cell>
{#snippet children({ months, weekdays })}
<Calendar.Header>
<Calendar.PrevButton />
<Calendar.Heading />
<Calendar.NextButton />
</Calendar.Header>
<Calendar.Months>
{#each months as month (month)}
<Calendar.Grid>
<Calendar.GridHead>
<Calendar.GridRow class="flex">
{#each weekdays as weekday (weekday)}
<Calendar.HeadCell>
{weekday.slice(0, 2)}
</Calendar.HeadCell>
{/each}
</Calendar.GridRow>
{/each}
</Calendar.GridBody>
</Calendar.Grid>
{/each}
</Calendar.Months>
</Calendar.GridHead>
<Calendar.GridBody>
{#each month.weeks as weekDates (weekDates)}
<Calendar.GridRow class="mt-2 w-full">
{#each weekDates as date (date)}
<Calendar.Cell {date} month={month.value}>
<Calendar.Day />
</Calendar.Cell>
{/each}
</Calendar.GridRow>
{/each}
</Calendar.GridBody>
</Calendar.Grid>
{/each}
</Calendar.Months>
{/snippet}
</CalendarPrimitive.Root>