134 lines
3.0 KiB
Svelte
134 lines
3.0 KiB
Svelte
<script lang="ts">
|
|
import CalendarIcon from "@lucide/svelte/icons/calendar";
|
|
import {
|
|
DateFormatter,
|
|
getLocalTimeZone,
|
|
toCalendarDate,
|
|
today,
|
|
type DateValue,
|
|
} from "@internationalized/date";
|
|
import { cn } from "$lib/utils.js";
|
|
import { buttonVariants } from "$lib/components/ui/button/index.js";
|
|
import * as Popover from "$lib/components/ui/popover/index.js";
|
|
|
|
import { RangeCalendar } from "$lib/components/ui/range-calendar/index.js";
|
|
import Icon from "$lib/components/atoms/icon.svelte";
|
|
import { ticketSearchStore } from "../data/store";
|
|
import { onMount } from "svelte";
|
|
import {
|
|
makeDateStringISO,
|
|
parseCalDateToDateString,
|
|
} from "$lib/core/date.utils";
|
|
import Button from "$lib/components/ui/button/button.svelte";
|
|
|
|
const df = new DateFormatter("en-US", {
|
|
month: "short",
|
|
day: "numeric",
|
|
year: "numeric",
|
|
});
|
|
|
|
let contentRef = $state<HTMLElement | null>(null);
|
|
|
|
let open = $state(false);
|
|
|
|
const todayDate = today(getLocalTimeZone());
|
|
|
|
const start = today(getLocalTimeZone());
|
|
const end = start.add({ days: 7 });
|
|
|
|
let value = $state({ start, end });
|
|
|
|
let startFmt = $derived(value?.start?.toDate(getLocalTimeZone()));
|
|
let endFmt = $derived(value?.end?.toDate(getLocalTimeZone()));
|
|
|
|
let defaultPlaceholder = "Pick a date";
|
|
let placeholder = $derived(
|
|
value?.start && value?.end && startFmt && endFmt
|
|
? `${df.formatRange(startFmt, endFmt)}`
|
|
: value?.start && startFmt
|
|
? `${df.format(startFmt)} - Select end date`
|
|
: defaultPlaceholder,
|
|
);
|
|
|
|
function isDateDisabled(date: DateValue) {
|
|
return date.compare(todayDate) < 0;
|
|
}
|
|
|
|
function handleStartChange(v: DateValue | undefined) {
|
|
if (!v) {
|
|
value.start = today(getLocalTimeZone());
|
|
return;
|
|
}
|
|
|
|
value.start = toCalendarDate(v);
|
|
}
|
|
|
|
function handleEndChange(v: DateValue | undefined) {
|
|
if (!v) {
|
|
value.end = today(getLocalTimeZone());
|
|
return;
|
|
}
|
|
|
|
value.end = toCalendarDate(v);
|
|
}
|
|
|
|
function updateValsInStore() {
|
|
const sVal = parseCalDateToDateString(value.start);
|
|
const eVal = parseCalDateToDateString(value.end);
|
|
ticketSearchStore.update((prev) => {
|
|
return {
|
|
...prev,
|
|
departureDate: makeDateStringISO(sVal),
|
|
returnDate: makeDateStringISO(eVal),
|
|
};
|
|
});
|
|
}
|
|
|
|
$effect(() => {
|
|
updateValsInStore();
|
|
});
|
|
|
|
onMount(() => {
|
|
updateValsInStore();
|
|
});
|
|
</script>
|
|
|
|
<Popover.Root
|
|
{open}
|
|
onOpenChange={(o) => {
|
|
open = o;
|
|
}}
|
|
>
|
|
<Popover.Trigger
|
|
class={cn(
|
|
buttonVariants({
|
|
variant: "white",
|
|
class: "w-full justify-start text-left font-normal",
|
|
}),
|
|
!value && "text-muted-foreground",
|
|
)}
|
|
>
|
|
<Icon icon={CalendarIcon} cls="w-auto h-4" />
|
|
<span class="text-sm md:text-base">{placeholder}</span>
|
|
</Popover.Trigger>
|
|
<Popover.Content bind:ref={contentRef} class="w-auto p-0">
|
|
<RangeCalendar
|
|
{value}
|
|
{isDateDisabled}
|
|
onStartValueChange={handleStartChange}
|
|
onEndValueChange={handleEndChange}
|
|
class="rounded-md border border-white"
|
|
/>
|
|
<div class="w-full p-2">
|
|
<Button
|
|
class="w-full"
|
|
onclick={() => {
|
|
open = false;
|
|
}}
|
|
>
|
|
Confirm
|
|
</Button>
|
|
</div>
|
|
</Popover.Content>
|
|
</Popover.Root>
|