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>
86 lines
2.4 KiB
Svelte
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>
|