106 lines
3.0 KiB
TypeScript
106 lines
3.0 KiB
TypeScript
"use client"
|
|
|
|
import { useCallback, useEffect, useRef } from "react"
|
|
import { useMediaQuery } from "usehooks-ts"
|
|
|
|
import { useHotelsMapStore } from "@/stores/hotels-map"
|
|
|
|
import useClickOutside from "@/hooks/useClickOutside"
|
|
|
|
import HotelCardDialog from "../HotelCardDialog"
|
|
import { getHotelPins } from "./utils"
|
|
|
|
import styles from "./hotelCardDialogListing.module.css"
|
|
|
|
import type { HotelCardDialogListingProps } from "@/types/components/hotelReservation/selectHotel/map"
|
|
|
|
export default function HotelCardDialogListing({
|
|
hotels,
|
|
}: HotelCardDialogListingProps) {
|
|
const hotelsPinData = hotels ? getHotelPins(hotels) : []
|
|
const activeCardRef = useRef<HTMLDivElement | null>(null)
|
|
const observerRef = useRef<IntersectionObserver | null>(null)
|
|
const dialogRef = useRef<HTMLDivElement>(null)
|
|
const isMobile = useMediaQuery("(max-width: 768px)")
|
|
const { activeHotelCard, setActiveHotelCard, setActiveHotelPin } =
|
|
useHotelsMapStore()
|
|
|
|
function handleClose() {
|
|
setActiveHotelCard(null)
|
|
setActiveHotelPin(null)
|
|
}
|
|
|
|
useClickOutside(dialogRef, !!activeHotelCard && isMobile, handleClose)
|
|
|
|
const handleIntersection = useCallback(
|
|
(entries: IntersectionObserverEntry[]) => {
|
|
entries.forEach((entry) => {
|
|
if (entry.isIntersecting) {
|
|
const cardName = entry.target.getAttribute("data-name")
|
|
if (cardName) {
|
|
setActiveHotelCard(cardName)
|
|
}
|
|
}
|
|
})
|
|
},
|
|
[setActiveHotelCard]
|
|
)
|
|
|
|
useEffect(() => {
|
|
observerRef.current = new IntersectionObserver(handleIntersection, {
|
|
root: null,
|
|
threshold: 0.5,
|
|
})
|
|
|
|
const elements = document.querySelectorAll("[data-name]")
|
|
elements.forEach((el) => observerRef.current?.observe(el))
|
|
|
|
return () => {
|
|
elements.forEach((el) => observerRef.current?.unobserve(el))
|
|
observerRef.current?.disconnect()
|
|
}
|
|
}, [handleIntersection])
|
|
|
|
useEffect(() => {
|
|
if (activeCardRef.current) {
|
|
// Temporarily disconnect the observer
|
|
observerRef.current?.disconnect()
|
|
|
|
activeCardRef.current.scrollIntoView({
|
|
behavior: "smooth",
|
|
block: "nearest",
|
|
inline: "center",
|
|
})
|
|
|
|
// Reconnect the observer after scrolling
|
|
const elements = document.querySelectorAll("[data-name]")
|
|
setTimeout(() => {
|
|
elements.forEach((el) => observerRef.current?.observe(el))
|
|
}, 1000)
|
|
}
|
|
}, [activeHotelCard])
|
|
|
|
return (
|
|
<div className={styles.hotelCardDialogListing} ref={dialogRef}>
|
|
{!!hotelsPinData?.length &&
|
|
hotelsPinData.map((data) => {
|
|
const isActive = data.name === activeHotelCard
|
|
return (
|
|
<div
|
|
key={data.name}
|
|
ref={isActive ? activeCardRef : null}
|
|
data-name={data.name}
|
|
>
|
|
<HotelCardDialog
|
|
data={data}
|
|
isOpen={!!activeHotelCard}
|
|
handleClose={handleClose}
|
|
type="listing"
|
|
/>
|
|
</div>
|
|
)
|
|
})}
|
|
</div>
|
|
)
|
|
}
|