"use client" import { useCallback, useEffect, useRef } from "react" import { useIntl } from "react-intl" import { useHotelsMapStore } from "@/stores/hotels-map" 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 intl = useIntl() const isRedemption = hotels?.find( (hotel) => hotel.availability.productType?.redemptions?.length ) const currencyValue = isRedemption ? intl.formatMessage({ defaultMessage: "Points", }) : undefined const hotelsPinData = getHotelPins(hotels, currencyValue) const activeCardRef = useRef(null) const observerRef = useRef(null) const dialogRef = useRef(null) const isScrollingRef = useRef(false) const debounceTimerRef = useRef | null>(null) const { activeHotel, activate, deactivate } = useHotelsMapStore() const handleIntersection = useCallback( (entries: IntersectionObserverEntry[]) => { // skip intersection handling during scrolling if (isScrollingRef.current) return if (debounceTimerRef.current) { clearTimeout(debounceTimerRef.current) } debounceTimerRef.current = setTimeout(() => { entries.forEach((entry) => { if (entry.isIntersecting) { const cardName = entry.target.getAttribute("data-name") if (cardName && cardName !== activeHotel) { activate(cardName) } } }) }, 100) }, [activate, activeHotel] ) useEffect(() => { observerRef.current = new IntersectionObserver(handleIntersection, { root: null, threshold: [0.3, 0.5, 0.7], }) const elements = document.querySelectorAll("[data-name]") elements.forEach((el) => observerRef.current?.observe(el)) return () => { if (debounceTimerRef.current) { clearTimeout(debounceTimerRef.current) } elements.forEach((el) => observerRef.current?.unobserve(el)) observerRef.current?.disconnect() observerRef.current = null } }, [handleIntersection]) useEffect(() => { if (activeCardRef.current) { isScrollingRef.current = true requestAnimationFrame(() => { activeCardRef.current?.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "center", }) setTimeout(() => { isScrollingRef.current = false }, 800) }) } }, [activeHotel]) useEffect(() => { const handleMapClick = (e: MouseEvent) => { // ignore clicks within the dialog if (dialogRef.current?.contains(e.target as Node)) { return } // ignore clicks on hotel pins const target = e.target as HTMLElement if (target.closest("[data-hotelpin]")) { return } deactivate() } if (activeHotel) { document.addEventListener("click", handleMapClick) } return () => { document.removeEventListener("click", handleMapClick) } }, [dialogRef, activeHotel, deactivate]) return (
{hotelsPinData?.map((data) => { const isActive = data.name === activeHotel return (
) })}
) }