import type { FC } from "react"; import { useMemo, useState } from "react"; import { endOfMonth, endOfWeek, getLocalTimeZone, startOfMonth, startOfWeek, today } from "@internationalized/date"; import { useControlledState } from "@react-stately/utils"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faCalendar } from "@fortawesome/pro-duotone-svg-icons"; const CalendarIcon: FC<{ className?: string }> = ({ className }) => ; import { useDateFormatter } from "react-aria"; import type { DateRangePickerProps as AriaDateRangePickerProps, DateValue } from "react-aria-components"; import { DateRangePicker as AriaDateRangePicker, Dialog as AriaDialog, Group as AriaGroup, Popover as AriaPopover, useLocale } from "react-aria-components"; import { Button } from "@/components/base/buttons/button"; import { cx } from "@/utils/cx"; import { DateInput } from "./date-input"; import { RangeCalendar } from "./range-calendar"; import { RangePresetButton } from "./range-preset"; const now = today(getLocalTimeZone()); const highlightedDates = [today(getLocalTimeZone())]; interface DateRangePickerProps extends AriaDateRangePickerProps { /** The function to call when the apply button is clicked. */ onApply?: () => void; /** The function to call when the cancel button is clicked. */ onCancel?: () => void; } export const DateRangePicker = ({ value: valueProp, defaultValue, onChange, onApply, onCancel, ...props }: DateRangePickerProps) => { const { locale } = useLocale(); const formatter = useDateFormatter({ month: "short", day: "numeric", year: "numeric", }); const [value, setValue] = useControlledState(valueProp, defaultValue || null, onChange); const [focusedValue, setFocusedValue] = useState(null); const formattedStartDate = value?.start ? formatter.format(value.start.toDate(getLocalTimeZone())) : "Select date"; const formattedEndDate = value?.end ? formatter.format(value.end.toDate(getLocalTimeZone())) : "Select date"; const presets = useMemo( () => ({ today: { label: "Today", value: { start: now, end: now } }, yesterday: { label: "Yesterday", value: { start: now.subtract({ days: 1 }), end: now.subtract({ days: 1 }) } }, thisWeek: { label: "This week", value: { start: startOfWeek(now, locale), end: endOfWeek(now, locale) } }, lastWeek: { label: "Last week", value: { start: startOfWeek(now, locale).subtract({ weeks: 1 }), end: endOfWeek(now, locale).subtract({ weeks: 1 }), }, }, thisMonth: { label: "This month", value: { start: startOfMonth(now), end: endOfMonth(now) } }, lastMonth: { label: "Last month", value: { start: startOfMonth(now).subtract({ months: 1 }), end: endOfMonth(now).subtract({ months: 1 }), }, }, thisYear: { label: "This year", value: { start: startOfMonth(now.set({ month: 1 })), end: endOfMonth(now.set({ month: 12 })) } }, lastYear: { label: "Last year", value: { start: startOfMonth(now.set({ month: 1 }).subtract({ years: 1 })), end: endOfMonth(now.set({ month: 12 }).subtract({ years: 1 })), }, }, allTime: { label: "All time", value: { start: now.set({ year: 2000, month: 1, day: 1 }), end: now, }, }, }), [locale], ); return ( {!value ? Select dates : `${formattedStartDate} – ${formattedEndDate}`} cx( "origin-(--trigger-anchor-point) will-change-transform", isEntering && "duration-150 ease-out animate-in fade-in placement-right:slide-in-from-left-0.5 placement-top:slide-in-from-bottom-0.5 placement-bottom:slide-in-from-top-0.5", isExiting && "duration-100 ease-in animate-out fade-out placement-right:slide-out-to-left-0.5 placement-top:slide-out-to-bottom-0.5 placement-bottom:slide-out-to-top-0.5", ) } > {({ close }) => ( <> {Object.values(presets).map((preset) => ( { setValue(preset.value); setFocusedValue(preset.value.start); }} > {preset.label} ))} – { onCancel?.(); close(); }} > Cancel { onApply?.(); close(); }} > Apply > )} ); };