Files
web/apps/scandic-web/components/Maps/InteractiveMap/index.tsx
Matilda Landström 5397437628 Merged in feat/SW-2041-map-zoom-buttons (pull request #2550)
Feat(SW-661): Hotel page map zoom restrictions

* fix(SW-2041): update tokens

* chore(SW-2041): restrict zooming

* fix(SW-2041): remove ref

* fix(SW-2041): create map zoom hook


Approved-by: Erik Tiekstra
Approved-by: Hrishikesh Vaipurkar
2025-08-13 07:50:39 +00:00

115 lines
3.2 KiB
TypeScript

"use client"
import { Map, type MapProps, useMap } from "@vis.gl/react-google-maps"
import { useEffect, useState } from "react"
import { useIntl } from "react-intl"
import { IconButton } from "@scandic-hotels/design-system/IconButton"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import {
DEFAULT_ZOOM,
MAP_RESTRICTIONS,
MAX_ZOOM,
MIN_ZOOM,
} from "@/constants/map"
import { useZoomControls } from "@/hooks/maps/useZoomControls"
import HotelListingMapContent from "./HotelListingMapContent"
import PoiMapMarkers from "./PoiMapMarkers"
import styles from "./interactiveMap.module.css"
import type { InteractiveMapProps } from "@/types/components/hotelPage/map/interactiveMap"
export default function InteractiveMap({
coordinates,
pointsOfInterest,
activePoi,
hotelPins,
mapId,
closeButton,
markerInfo,
fitBounds = true,
onTilesLoaded,
onActivePoiChange,
}: InteractiveMapProps) {
const intl = useIntl()
const map = useMap()
const [hasInitializedBounds, setHasInitializedBounds] = useState(false)
const { zoomIn, zoomOut, isMaxZoom, isMinZoom } = useZoomControls()
const mapOptions: MapProps = {
defaultZoom: DEFAULT_ZOOM,
minZoom: MIN_ZOOM,
maxZoom: MAX_ZOOM,
defaultCenter: coordinates,
disableDefaultUI: true,
clickableIcons: false,
mapId,
gestureHandling: "greedy",
restriction: MAP_RESTRICTIONS,
}
useEffect(() => {
if (map && hotelPins?.length && !hasInitializedBounds) {
if (fitBounds) {
const bounds = new google.maps.LatLngBounds()
hotelPins.forEach((marker) => {
bounds.extend(marker.coordinates)
})
map.fitBounds(bounds, 100)
}
setHasInitializedBounds(true)
}
}, [map, fitBounds, hotelPins, hasInitializedBounds])
return (
<div className={styles.mapContainer}>
<Map {...mapOptions} onTilesLoaded={onTilesLoaded}>
{hotelPins && <HotelListingMapContent hotelPins={hotelPins} />}
{pointsOfInterest && markerInfo && (
<PoiMapMarkers
coordinates={coordinates}
pointsOfInterest={pointsOfInterest}
onActivePoiChange={onActivePoiChange}
activePoi={activePoi}
markerInfo={markerInfo}
/>
)}
</Map>
<div className={styles.ctaButtons}>
{closeButton}
<div className={styles.zoomButtons}>
<IconButton
theme="Inverted"
style="Elevated"
className={styles.zoomButton}
onClick={zoomOut}
aria-label={intl.formatMessage({
defaultMessage: "Zoom out",
})}
isDisabled={isMinZoom}
>
<MaterialIcon icon="remove" color="CurrentColor" />
</IconButton>
<IconButton
theme="Inverted"
style="Elevated"
className={styles.zoomButton}
onClick={zoomIn}
aria-label={intl.formatMessage({
defaultMessage: "Zoom in",
})}
isDisabled={isMaxZoom}
>
<MaterialIcon icon="add" color="CurrentColor" />
</IconButton>
</div>
</div>
</div>
)
}