feat(SW-344): set active pin on scroll
This commit is contained in:
@@ -1,22 +1,54 @@
|
||||
"use client"
|
||||
|
||||
import { useEffect, useRef } from "react"
|
||||
import { useCallback, useEffect, useRef } from "react"
|
||||
|
||||
import HotelCardDialog from "../HotelCardDialog"
|
||||
import { getHotelPins } from "./utils"
|
||||
|
||||
import type { HotelData } from "@/types/components/hotelReservation/selectHotel/hotelCardListingProps"
|
||||
|
||||
interface HotelCardDialogListingProps {
|
||||
hotels: HotelData[]
|
||||
activeCard: string | null | undefined
|
||||
onActiveCardChange: (hotelName: string | null) => void
|
||||
}
|
||||
|
||||
export default function HotelCardDialogListing({
|
||||
hotels,
|
||||
activeCard,
|
||||
}: {
|
||||
hotels: HotelData[]
|
||||
activeCard: string | null | undefined
|
||||
}) {
|
||||
onActiveCardChange,
|
||||
}: HotelCardDialogListingProps) {
|
||||
const hotelsPinData = getHotelPins(hotels)
|
||||
const activeCardRef = useRef<HTMLDivElement | null>(null)
|
||||
|
||||
const handleIntersection = useCallback(
|
||||
(entries: IntersectionObserverEntry[]) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
const cardName = entry.target.getAttribute("data-name")
|
||||
if (cardName) {
|
||||
onActiveCardChange(cardName)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
[onActiveCardChange]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(handleIntersection, {
|
||||
root: null,
|
||||
threshold: 0.5, // Adjust threshold as needed
|
||||
})
|
||||
|
||||
const elements = document.querySelectorAll("[data-name]")
|
||||
elements.forEach((el) => observer.observe(el))
|
||||
|
||||
return () => {
|
||||
elements.forEach((el) => observer.unobserve(el))
|
||||
}
|
||||
}, [handleIntersection])
|
||||
|
||||
useEffect(() => {
|
||||
if (activeCardRef.current) {
|
||||
activeCardRef.current.scrollIntoView({
|
||||
@@ -33,7 +65,11 @@ export default function HotelCardDialogListing({
|
||||
hotelsPinData.map((data) => {
|
||||
const isActive = data.name === activeCard
|
||||
return (
|
||||
<div key={data.name} ref={isActive ? activeCardRef : null}>
|
||||
<div
|
||||
key={data.name}
|
||||
ref={isActive ? activeCardRef : null}
|
||||
data-name={data.name}
|
||||
>
|
||||
<HotelCardDialog
|
||||
data={data}
|
||||
isOpen={!!activeCard}
|
||||
|
||||
@@ -11,7 +11,7 @@ import type { HotelListingProps } from "@/types/components/hotelReservation/sele
|
||||
export default function HotelListing({
|
||||
hotels,
|
||||
activeHotelPin,
|
||||
onHotelCardHover,
|
||||
setActiveHotelPin,
|
||||
}: HotelListingProps) {
|
||||
return (
|
||||
<>
|
||||
@@ -20,11 +20,15 @@ export default function HotelListing({
|
||||
hotelData={hotels}
|
||||
type={HotelCardListingTypeEnum.MapListing}
|
||||
activeCard={activeHotelPin}
|
||||
onHotelCardHover={onHotelCardHover}
|
||||
onHotelCardHover={setActiveHotelPin}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.hotelListingMobile} data-open={!!activeHotelPin}>
|
||||
<HotelCardDialogListing hotels={hotels} activeCard={activeHotelPin} />
|
||||
<HotelCardDialogListing
|
||||
hotels={hotels}
|
||||
activeCard={activeHotelPin}
|
||||
onActiveCardChange={setActiveHotelPin}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -95,7 +95,7 @@ export default function SelectHotelMap({
|
||||
<HotelListing
|
||||
hotels={hotels}
|
||||
activeHotelPin={activeHotelPin}
|
||||
onHotelCardHover={setActiveHotelPin}
|
||||
setActiveHotelPin={setActiveHotelPin}
|
||||
/>
|
||||
{showBackToTop && (
|
||||
<Button
|
||||
|
||||
@@ -13,7 +13,7 @@ import type { Coordinates } from "@/types/components/maps/coordinates"
|
||||
export interface HotelListingProps {
|
||||
hotels: HotelData[]
|
||||
activeHotelPin?: string | null
|
||||
onHotelCardHover?: (hotelName: string | null) => void
|
||||
setActiveHotelPin: (hotelName: string | null) => void
|
||||
}
|
||||
|
||||
export interface SelectHotelMapProps {
|
||||
|
||||
Reference in New Issue
Block a user