"use client" import { useCallback, useEffect, useRef, useState } from "react" import { FocusScope, useOverlay } from "react-aria" import { Button as ButtonRAC } from "react-aria-components" import { useFormContext, useWatch } from "react-hook-form" import { useIntl } from "react-intl" import { useMediaQuery } from "usehooks-ts" import { longDateFormat } from "@scandic-hotels/common/constants/dateFormats" import { dt } from "@scandic-hotels/common/dt" import { Typography } from "@scandic-hotels/design-system/Typography" import useLang from "../../../hooks/useLang" import DatePickerRangeDesktop from "./Range/Desktop" import DatePickerRangeMobile from "./Range/Mobile" import styles from "./date-picker.module.css" import type { DateRange } from "react-day-picker" type DatePickerFormProps = { ariaLabelledBy?: string name?: string } export default function DatePickerForm({ ariaLabelledBy, name = "date", }: DatePickerFormProps) { const lang = useLang() const checkIsDesktop = useMediaQuery("(min-width: 1367px)") const [isDesktop, setIsDesktop] = useState(true) const [isOpen, setIsOpen] = useState(false) const selectedDate = useWatch({ name }) const { setValue } = useFormContext() const ref = useRef(null) const close = useCallback(() => { if (!selectedDate.toDate) { setValue( name, { fromDate: selectedDate.fromDate, toDate: dt(selectedDate.fromDate).add(1, "day").format("YYYY-MM-DD"), }, { shouldDirty: true } ) } setIsOpen(false) }, [name, setValue, selectedDate]) const { overlayProps, underlayProps } = useOverlay( { isOpen, onClose: () => { close() }, isDismissable: true, }, ref ) function handleSelectDate( _nextRange: DateRange | undefined, selectedDay: Date ) { const now = dt() const dateClicked = dt(selectedDay) const dateClickedFormatted = dateClicked.format("YYYY-MM-DD") /* check if selected date is not before todays date, which happens when "Enter" key is pressed in any other input field of the form */ if (!dateClicked.isBefore(now, "day")) { // Handle form value updates based on the requirements if (selectedDate.fromDate && selectedDate.toDate) { // Both dates were previously selected, starting fresh with new date setValue( name, { fromDate: dateClickedFormatted, toDate: undefined, }, { shouldDirty: true } ) } else if (selectedDate.fromDate && !selectedDate.toDate) { // If the selected day is the same as the first date, we don't need to update the form value if (dateClicked.isSame(selectedDate.fromDate)) { return } // We're selecting the second date if (dateClicked.isBefore(selectedDate.fromDate)) { // If second selected date is before first date, swap them setValue( name, { fromDate: dateClickedFormatted, toDate: selectedDate.fromDate, }, { shouldDirty: true } ) } else { // If second selected date is after first date, keep order setValue( name, { fromDate: selectedDate.fromDate, toDate: dateClickedFormatted, }, { shouldDirty: true } ) } } } } useEffect(() => { setIsDesktop(checkIsDesktop) }, [checkIsDesktop]) const selectedFromDate = dt(selectedDate.fromDate) .locale(lang) .format(longDateFormat[lang]) const selectedToDate = !!selectedDate.toDate ? dt(selectedDate.toDate).locale(lang).format(longDateFormat[lang]) : "" return isDesktop ? (
{ setIsOpen((prev) => !prev) }} selectedFromDate={selectedFromDate} selectedToDate={selectedToDate} /> {isOpen && (
)}
) : (
{ setIsOpen((prev) => !prev) }} selectedFromDate={selectedFromDate} selectedToDate={selectedToDate} /> {isOpen && (
)}
) } function Trigger({ onPress, selectedFromDate, selectedToDate, ariaLabelledBy, }: { onPress?: () => void selectedFromDate: string selectedToDate: string ariaLabelledBy?: string }) { const intl = useIntl() const { register } = useFormContext() const triggerText = intl.formatMessage( { id: "booking.selectedDateRange", defaultMessage: "{selectedFromDate} – {selectedToDate}", }, { selectedFromDate, selectedToDate, } ) return ( <> {triggerText} ) }