85 lines
2.4 KiB
TypeScript
85 lines
2.4 KiB
TypeScript
"use client"
|
|
|
|
import { useRouter } from "next/navigation"
|
|
import { useCallback, useEffect, useRef, useState } from "react"
|
|
import { Dialog, Modal } from "react-aria-components"
|
|
|
|
import { debounce } from "@/utils/debounce"
|
|
|
|
import styles from "./mapModal.module.css"
|
|
|
|
export function MapModal({ children }: { children: React.ReactNode }) {
|
|
const router = useRouter()
|
|
const [mapHeight, setMapHeight] = useState("0px")
|
|
const [mapTop, setMapTop] = useState("0px")
|
|
const [isOpen, setIsOpen] = useState(true)
|
|
const [scrollHeightWhenOpened, setScrollHeightWhenOpened] = useState(0)
|
|
|
|
const rootDiv = useRef<HTMLDivElement | null>(null)
|
|
|
|
const handleOnOpenChange = (open: boolean) => {
|
|
setIsOpen(open)
|
|
if (!open) {
|
|
router.back()
|
|
}
|
|
}
|
|
|
|
// Calculate the height of the map based on the viewport height from the start-point (below the header and booking widget)
|
|
const handleMapHeight = useCallback(() => {
|
|
const topPosition = rootDiv.current?.getBoundingClientRect().top ?? 0
|
|
const scrollY = window.scrollY
|
|
setMapHeight(`calc(100dvh - ${topPosition + scrollY}px)`)
|
|
setMapTop(`${topPosition + scrollY}px`)
|
|
}, [])
|
|
|
|
// Making sure the map is always opened at the top of the page,
|
|
// just below the header and booking widget as these should stay visible.
|
|
// When closing, the page should scroll back to the position it was before opening the map.
|
|
useEffect(() => {
|
|
// Skip the first render
|
|
if (!rootDiv.current) {
|
|
return
|
|
}
|
|
|
|
if (scrollHeightWhenOpened === 0) {
|
|
const scrollY = window.scrollY
|
|
setScrollHeightWhenOpened(scrollY)
|
|
window.scrollTo({ top: 0, behavior: "instant" })
|
|
}
|
|
}, [scrollHeightWhenOpened, rootDiv])
|
|
|
|
useEffect(() => {
|
|
const debouncedResizeHandler = debounce(function () {
|
|
handleMapHeight()
|
|
})
|
|
|
|
const observer = new ResizeObserver(debouncedResizeHandler)
|
|
|
|
observer.observe(document.documentElement)
|
|
|
|
return () => {
|
|
if (observer) {
|
|
observer.unobserve(document.documentElement)
|
|
}
|
|
}
|
|
}, [rootDiv, handleMapHeight])
|
|
|
|
return (
|
|
<div className={styles.wrapper} ref={rootDiv}>
|
|
<Modal isOpen={isOpen} onOpenChange={handleOnOpenChange}>
|
|
<Dialog
|
|
style={
|
|
{
|
|
"--hotel-map-height": mapHeight,
|
|
"--hotel-map-top": mapTop,
|
|
} as React.CSSProperties
|
|
}
|
|
className={styles.dynamicMap}
|
|
>
|
|
{children}
|
|
</Dialog>
|
|
</Modal>
|
|
</div>
|
|
)
|
|
}
|