Files
sexy/packages/frontend/src/lib/components/ui/date-picker/date-picker.svelte
Sebastian Krüger 609f116b5d feat: replace native date inputs with shadcn date picker
Add calendar + popover components and a custom DateTimePicker wrapper.
Video forms use date-only; article forms include a time picker.
Also add video player preview to the video edit form.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 17:03:35 +01:00

86 lines
2.4 KiB
Svelte

<script lang="ts">
import { parseDate, type DateValue } from "@internationalized/date";
import { Calendar } from "$lib/components/ui/calendar";
import * as Popover from "$lib/components/ui/popover";
import { Button } from "$lib/components/ui/button";
let {
value = $bindable(""),
placeholder = "Pick a date",
showTime = true,
}: {
value?: string;
placeholder?: string;
showTime?: boolean;
} = $props();
function toCalendarDate(v: string): DateValue | undefined {
if (!v) return undefined;
try {
return parseDate(v.slice(0, 10));
} catch {
return undefined;
}
}
function pad(n: number) {
return String(n).padStart(2, "0");
}
let calendarDate = $state<DateValue | undefined>(toCalendarDate(value));
let timeStr = $state(value.length >= 16 ? value.slice(11, 16) : "00:00");
let open = $state(false);
$effect(() => {
if (calendarDate) {
const d = calendarDate;
const dateStr = `${d.year}-${pad(d.month)}-${pad(d.day)}`;
value = showTime ? `${dateStr}T${timeStr}` : dateStr;
} else {
value = "";
}
});
let displayLabel = $derived.by(() => {
if (!calendarDate) return placeholder;
const d = calendarDate;
const date = new Date(d.year, d.month - 1, d.day);
const dateLabel = date.toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
});
return showTime ? `${dateLabel} ${timeStr}` : dateLabel;
});
</script>
<Popover.Root bind:open>
<Popover.Trigger>
{#snippet child({ props })}
<Button
variant="outline"
{...props}
class="w-full justify-start font-normal {!calendarDate ? 'text-muted-foreground' : ''}"
>
<span class="icon-[ri--calendar-line] h-4 w-4 mr-2 shrink-0"></span>
{displayLabel}
</Button>
{/snippet}
</Popover.Trigger>
<Popover.Content class="w-auto p-0" align="start">
<Calendar bind:value={calendarDate} />
{#if showTime}
<div class="border-t border-border/40 p-3">
<input
type="time"
value={timeStr}
oninput={(e) => {
timeStr = (e.target as HTMLInputElement).value;
}}
class="w-full rounded-md border border-input bg-background px-3 py-1.5 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
/>
</div>
{/if}
</Popover.Content>
</Popover.Root>