177 lines
4.7 KiB
TypeScript
177 lines
4.7 KiB
TypeScript
"use client"
|
|
|
|
import { useCallback, useEffect, useState } from "react"
|
|
import {
|
|
Button,
|
|
Dialog,
|
|
DialogTrigger,
|
|
Modal,
|
|
Popover,
|
|
} from "react-aria-components"
|
|
import { useFormContext } from "react-hook-form"
|
|
import { useIntl } from "react-intl"
|
|
import { useMediaQuery } from "usehooks-ts"
|
|
|
|
import Body from "@/components/TempDesignSystem/Text/Body"
|
|
|
|
import PickerForm from "./Form"
|
|
|
|
import styles from "./guests-rooms-picker.module.css"
|
|
|
|
import type { GuestsRoom } from "@/types/components/bookingWidget/guestsRoomsPicker"
|
|
|
|
export default function GuestsRoomsPickerForm() {
|
|
const { watch, trigger } = useFormContext()
|
|
const rooms = watch("rooms") as GuestsRoom[]
|
|
|
|
const checkIsDesktop = useMediaQuery("(min-width: 1367px)")
|
|
const [isDesktop, setIsDesktop] = useState(true)
|
|
const [isOpen, setIsOpen] = useState(false)
|
|
const [containerHeight, setContainerHeight] = useState(0)
|
|
const childCount = rooms[0] ? rooms[0].child.length : 0 // ToDo Update for multiroom later
|
|
|
|
const htmlElement =
|
|
typeof window !== "undefined" ? document.querySelector("body") : null
|
|
//isOpen is the 'old state', so isOpen === true means "The modal is open and WILL be closed".
|
|
async function setOverflowClip(isOpen: boolean) {
|
|
if (htmlElement) {
|
|
if (isOpen) {
|
|
htmlElement.style.overflow = "visible"
|
|
} else {
|
|
// !important needed to override 'overflow: hidden' set by react-aria.
|
|
// 'overflow: hidden' does not work in combination with other sticky positioned elements, which clip does.
|
|
htmlElement.style.overflow = "clip !important"
|
|
}
|
|
}
|
|
if (!isOpen) {
|
|
const state = await trigger("rooms")
|
|
if (state) {
|
|
setIsOpen(isOpen)
|
|
}
|
|
}
|
|
}
|
|
|
|
useEffect(() => {
|
|
setIsDesktop(checkIsDesktop)
|
|
}, [checkIsDesktop])
|
|
|
|
const updateHeight = useCallback(() => {
|
|
if (typeof window !== undefined) {
|
|
// Get available space for picker to show without going beyond screen
|
|
let maxHeight =
|
|
window.innerHeight -
|
|
(document.querySelector("#booking-widget")?.getBoundingClientRect()
|
|
.bottom ?? 0) -
|
|
50
|
|
const innerContainerHeight = document
|
|
.querySelector(".guests_picker_popover")
|
|
?.getBoundingClientRect().height
|
|
if (
|
|
maxHeight != containerHeight &&
|
|
innerContainerHeight &&
|
|
maxHeight <= innerContainerHeight
|
|
) {
|
|
setContainerHeight(maxHeight)
|
|
} else if (
|
|
containerHeight &&
|
|
innerContainerHeight &&
|
|
maxHeight > innerContainerHeight
|
|
) {
|
|
setContainerHeight(0)
|
|
}
|
|
}
|
|
}, [containerHeight])
|
|
|
|
useEffect(() => {
|
|
if (typeof window !== undefined && isDesktop) {
|
|
updateHeight()
|
|
}
|
|
}, [childCount, isDesktop, updateHeight])
|
|
|
|
return isDesktop ? (
|
|
<DialogTrigger onOpenChange={setOverflowClip} isOpen={isOpen}>
|
|
<Trigger
|
|
rooms={rooms}
|
|
className={styles.triggerDesktop}
|
|
triggerFn={() => {
|
|
setIsOpen(true)
|
|
}}
|
|
/>
|
|
<Popover
|
|
className="guests_picker_popover"
|
|
placement="bottom start"
|
|
offset={36}
|
|
style={containerHeight ? { overflow: "auto" } : {}}
|
|
>
|
|
<Dialog className={styles.pickerContainerDesktop}>
|
|
{({ close }) => (
|
|
<PickerForm
|
|
rooms={rooms}
|
|
onClose={close}
|
|
isOverflowed={!!containerHeight}
|
|
/>
|
|
)}
|
|
</Dialog>
|
|
</Popover>
|
|
</DialogTrigger>
|
|
) : (
|
|
<DialogTrigger onOpenChange={setOverflowClip} isOpen={isOpen}>
|
|
<Trigger
|
|
rooms={rooms}
|
|
className={styles.triggerMobile}
|
|
triggerFn={() => {
|
|
setIsOpen(true)
|
|
}}
|
|
/>
|
|
<Modal>
|
|
<Dialog className={styles.pickerContainerMobile}>
|
|
{({ close }) => <PickerForm rooms={rooms} onClose={close} />}
|
|
</Dialog>
|
|
</Modal>
|
|
</DialogTrigger>
|
|
)
|
|
}
|
|
|
|
function Trigger({
|
|
rooms,
|
|
className,
|
|
triggerFn,
|
|
}: {
|
|
rooms: GuestsRoom[]
|
|
className: string
|
|
triggerFn?: () => void
|
|
}) {
|
|
const intl = useIntl()
|
|
|
|
return (
|
|
<Button
|
|
className={`${className} ${styles.btn}`}
|
|
type="button"
|
|
onPress={triggerFn}
|
|
>
|
|
<Body>
|
|
{rooms.map((room, i) => (
|
|
<span key={i}>
|
|
{intl.formatMessage(
|
|
{ id: "booking.rooms" },
|
|
{ totalRooms: rooms.length }
|
|
)}
|
|
{", "}
|
|
{intl.formatMessage(
|
|
{ id: "booking.adults" },
|
|
{ totalAdults: room.adults }
|
|
)}
|
|
{room.child.length > 0
|
|
? ", " +
|
|
intl.formatMessage(
|
|
{ id: "booking.children" },
|
|
{ totalChildren: room.child.length }
|
|
)
|
|
: null}
|
|
</span>
|
|
))}
|
|
</Body>
|
|
</Button>
|
|
)
|
|
}
|