"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 { Typography } from "@scandic-hotels/design-system/Typography" import ValidationError from "./ValidationError/index" import PickerForm from "./Form" import styles from "./guests-rooms-picker.module.css" import type { GuestsRoom } from ".." import type { BookingWidgetSchema } from "../Client" export default function GuestsRoomsPickerForm({ ariaLabelledBy, }: { ariaLabelledBy?: string }) { const rooms = useWatch({ name: "rooms" }) const checkIsDesktop = useMediaQuery("(min-width: 1367px)") const [isDesktop, setIsDesktop] = useState(true) const [isOpen, setIsOpen] = useState(false) const [containerConstraint, setContainerConstraint] = useState(0) const [showErrorModal, setShowErrorModal] = useState(false) const ref = useRef(null) // const childCount = rooms[0]?.childrenInRoom.length ?? 0 // ToDo Update for multiroom later const { clearErrors, formState: { errors }, } = useFormContext() const [scrollPosition, setScrollPosition] = useState(0) const roomError = errors["rooms"] useEffect(() => { if (roomError) { setShowErrorModal(true) } }, [roomError]) useEffect(() => { clearErrors("rooms") }, [clearErrors]) const timeoutRef = useRef(null) useEffect(() => { if (!roomError) return if (timeoutRef.current) return if (roomError) { timeoutRef.current = setTimeout(() => { setShowErrorModal(false) // magic number originates from animation // 5000ms delay + 120ms exectuion timeoutRef.current = null }, 5120) } return () => {} }, [clearErrors, roomError]) useEffect(() => { setIsDesktop(checkIsDesktop) }, [checkIsDesktop]) const updateHeight = useCallback((containerConstraint: number) => { // Get available space for picker to show without going beyond screen const bookingWidget = document.getElementById("booking-widget") const popoverElement = document.getElementById("guestsPopover") const maxHeight = window.innerHeight - (bookingWidget?.getBoundingClientRect().bottom ?? 0) - 50 const innerContainerHeight = popoverElement?.getBoundingClientRect().height const shouldAdjustHeight = Boolean( // height should be constrained maxHeight != containerConstraint && innerContainerHeight && maxHeight <= innerContainerHeight ) const hasExcessVerticalSpace = Boolean( // no need to constrain height containerConstraint && innerContainerHeight && Math.floor(maxHeight) > Math.floor(innerContainerHeight) ) if (shouldAdjustHeight) { // avoid clipping if there's only one room setContainerConstraint(Math.max(200, maxHeight)) } else if (hasExcessVerticalSpace) { setContainerConstraint(0) } }, []) useEffect(() => { if (isDesktop) { updateHeight(containerConstraint) } }, [ isOpen, scrollPosition, isDesktop, updateHeight, containerConstraint, rooms, ]) useEffect(() => { if (isOpen && isDesktop) { const handleScroll = () => { setScrollPosition(window.scrollY) } window.addEventListener("scroll", handleScroll) return () => { window.removeEventListener("scroll", handleScroll) } } }, [isOpen, isDesktop, rooms]) const { overlayProps, underlayProps } = useOverlay( { isOpen, onClose: () => { setIsOpen(false) }, isDismissable: true, }, ref ) return ( <>
{ setIsOpen((prev) => !prev) }} ariaLabelledBy={ariaLabelledBy} /> {isOpen && (
0 ? { maxHeight: containerConstraint } : { maxHeight: "none" } : undefined } > { setIsOpen((prev) => !prev) }} containerRef={ref} />
)}
{showErrorModal && !isOpen && errors.rooms && } ) } function Trigger({ rooms, onPress, ariaLabelledBy, }: { rooms: GuestsRoom[] onPress?: () => void ariaLabelledBy?: string }) { const intl = useIntl() const parts = [ intl.formatMessage( { id: "booking.numberOfRooms", defaultMessage: "{totalRooms, plural, one {# room} other {# rooms}}", }, { totalRooms: rooms.length } ), intl.formatMessage( { id: "booking.numberOfAdults", defaultMessage: "{adults, plural, one {# adult} other {# adults}}", }, { adults: rooms.reduce((acc, room) => acc + room.adults, 0) } ), ] if (rooms.some((room) => room.childrenInRoom.length > 0)) { parts.push( intl.formatMessage( { id: "booking.numberOfChildren", defaultMessage: "{children, plural, one {# child} other {# children}}", }, { children: rooms.reduce( (acc, room) => acc + room.childrenInRoom.length, 0 ), } ) ) } return ( {parts.join(", ")} ) }