115 lines
3.7 KiB
TypeScript
115 lines
3.7 KiB
TypeScript
import {
|
|
AdvancedMarker,
|
|
AdvancedMarkerAnchorPoint,
|
|
} from "@vis.gl/react-google-maps"
|
|
import { useCallback, useState } from "react"
|
|
import { useIntl } from "react-intl"
|
|
|
|
import { useHotelsMapStore } from "@/stores/hotels-map"
|
|
|
|
import HotelCardDialog from "@/components/HotelReservation/HotelCardDialog"
|
|
import Body from "@/components/TempDesignSystem/Text/Body"
|
|
import { formatPrice } from "@/utils/numberFormatting"
|
|
|
|
import HotelMarker from "../../Markers/HotelMarker"
|
|
|
|
import styles from "./hotelListingMapContent.module.css"
|
|
|
|
import type { HotelListingMapContentProps } from "@/types/components/hotelReservation/selectHotel/map"
|
|
|
|
function HotelListingMapContent({ hotelPins }: HotelListingMapContentProps) {
|
|
const intl = useIntl()
|
|
const [hoveredHotelPin, setHoveredHotelPin] = useState<string | null>(null)
|
|
const { activeHotelPin, setActiveHotelPin, setActiveHotelCard } =
|
|
useHotelsMapStore()
|
|
|
|
const toggleActiveHotelPin = useCallback(
|
|
(pinName: string | null) => {
|
|
if (setActiveHotelPin) {
|
|
const newActivePin = activeHotelPin === pinName ? null : pinName
|
|
setActiveHotelPin(newActivePin)
|
|
setActiveHotelCard(newActivePin)
|
|
if (newActivePin === null) {
|
|
setHoveredHotelPin(null)
|
|
setActiveHotelCard(null)
|
|
}
|
|
}
|
|
},
|
|
[activeHotelPin, setActiveHotelPin, setActiveHotelCard]
|
|
)
|
|
|
|
const handleHover = useCallback(
|
|
(pinName: string | null) => {
|
|
if (pinName !== null && activeHotelPin !== pinName) {
|
|
setHoveredHotelPin(pinName)
|
|
if (activeHotelPin && setActiveHotelPin) {
|
|
setActiveHotelPin(null)
|
|
setActiveHotelCard(null)
|
|
}
|
|
} else if (pinName === null) {
|
|
setHoveredHotelPin(null)
|
|
}
|
|
},
|
|
[activeHotelPin, setActiveHotelPin, setActiveHotelCard]
|
|
)
|
|
|
|
return (
|
|
<div>
|
|
{hotelPins.map((pin) => {
|
|
const isActiveOrHovered =
|
|
activeHotelPin === pin.name || hoveredHotelPin === pin.name
|
|
const hotelPrice = pin.memberPrice ?? pin.publicPrice
|
|
return (
|
|
<AdvancedMarker
|
|
key={pin.name}
|
|
className={styles.advancedMarker}
|
|
position={pin.coordinates}
|
|
anchorPoint={AdvancedMarkerAnchorPoint.CENTER}
|
|
zIndex={isActiveOrHovered ? 2 : 0}
|
|
onMouseEnter={() => handleHover(pin.name)}
|
|
onMouseLeave={() => handleHover(null)}
|
|
onClick={() => toggleActiveHotelPin(pin.name)}
|
|
>
|
|
<div className={styles.dialogContainer}>
|
|
<HotelCardDialog
|
|
isOpen={isActiveOrHovered}
|
|
handleClose={(event: { stopPropagation: () => void }) => {
|
|
event.stopPropagation()
|
|
if (activeHotelPin === pin.name) {
|
|
toggleActiveHotelPin(null)
|
|
}
|
|
}}
|
|
data={pin}
|
|
/>
|
|
</div>
|
|
<span
|
|
className={`${styles.pin} ${isActiveOrHovered ? styles.active : ""}`}
|
|
>
|
|
<span className={styles.pinIcon}>
|
|
<HotelMarker
|
|
width={16}
|
|
color={isActiveOrHovered ? "burgundy" : "white"}
|
|
/>
|
|
</span>
|
|
|
|
<Body
|
|
asChild
|
|
color={isActiveOrHovered ? "white" : "baseTextHighContrast"}
|
|
>
|
|
<span>
|
|
{/* TODO: Handle when no price is available */}
|
|
{hotelPrice
|
|
? formatPrice(intl, hotelPrice, pin.currency)
|
|
: intl.formatMessage({ id: "N/A" })}
|
|
</span>
|
|
</Body>
|
|
</span>
|
|
</AdvancedMarker>
|
|
)
|
|
})}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default HotelListingMapContent
|