Files
web/components/GuestsRoomsPicker/index.tsx
Pontus Dreij b4060d720b Merged in feat/SW-716-multiroom-guest-picker (pull request #1084)
feat(SW-716) Added UI to add more rooms in guest/room picker

* feat(SW-716) Added UI to add more rooms in guest/room picker

* feat(SW-716) Renamed GuestRoom Type and updated html structure

* Feat(SW-716): Created a BookingFlowIteration1 folder

* feat(SW-716) Moved forms/bookingwidget to new flow

* feat(SW-716) Added new ENABLE_BOOKING_FLOW_ITERATION_1 and ENABLE_BOOKING_FLOW_ITERATION_2

* feat(SW-716) Re added booking widget into interaction1 of how it looks now in prod

* Revert "feat(SW-716) Re added booking widget into interaction1 of how it looks now in prod"

This reverts commit 9a5514e8e71b1487e610bf64986ca77a538c0023.

* Revert "feat(SW-716) Added new ENABLE_BOOKING_FLOW_ITERATION_1 and ENABLE_BOOKING_FLOW_ITERATION_2"

This reverts commit b00bfc08cb7878d91483220ba3e8322671c145e4.

* Revert "feat(SW-716) Moved forms/bookingwidget to new flow"

This reverts commit 6c81635fe929a71fb3a42d8f174706787d8578ed.

* Revert "Feat(SW-716): Created a BookingFlowIteration1 folder"

This reverts commit db41f1c7fcd8e3adf15713d5d36f0da11e03e3a4.

* feat(SW-716): Added NEXT_PUBLIC_HIDE_FOR_NEXT_RELEASE

* feat(SW-716) Readded Tooltip if NEXT_PUBLIC_HIDE_FOR_NEXT_RELEASE is true

* feat(SW-716) remove log


Approved-by: Niclas Edenvin
Approved-by: Christian Andolf
2025-01-08 15:09:29 +00:00

174 lines
4.8 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 { TGuestsRoom } from "@/types/components/bookingWidget/guestsRoomsPicker"
export default function GuestsRoomsPickerForm() {
const { watch, trigger } = useFormContext()
const rooms = watch("rooms") as TGuestsRoom[]
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 && rooms.length > 0) {
updateHeight()
}
}, [childCount, isDesktop, updateHeight, rooms])
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} />}
</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: TGuestsRoom[]
className: string
triggerFn?: () => void
}) {
const intl = useIntl()
return (
<Button
className={`${className} ${styles.btn}`}
type="button"
onPress={triggerFn}
>
<Body color="uiTextHighContrast">
<span>
{intl.formatMessage(
{ id: "booking.rooms" },
{ totalRooms: rooms.length }
)}
{", "}
{intl.formatMessage(
{ id: "booking.adults" },
{ totalAdults: rooms.reduce((acc, room) => acc + room.adults, 0) }
)}
{rooms.some((room) => room.child.length > 0)
? ", " +
intl.formatMessage(
{ id: "booking.children" },
{
totalChildren: rooms.reduce(
(acc, room) => acc + room.child.length,
0
),
}
)
: null}
</span>
</Body>
</Button>
)
}