Move map out to its own component
This commit is contained in:
139
components/Maps/InteractiveMap/index.tsx
Normal file
139
components/Maps/InteractiveMap/index.tsx
Normal file
@@ -0,0 +1,139 @@
|
||||
"use client"
|
||||
import {
|
||||
AdvancedMarker,
|
||||
AdvancedMarkerAnchorPoint,
|
||||
Map,
|
||||
type MapProps,
|
||||
useMap,
|
||||
} from "@vis.gl/react-google-maps"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import useHotelPageStore from "@/stores/hotel-page"
|
||||
|
||||
import { MinusIcon, PlusIcon } from "@/components/Icons"
|
||||
import CloseLargeIcon from "@/components/Icons/CloseLarge"
|
||||
import PoiMarker from "@/components/Maps/Markers/Poi"
|
||||
import ScandicMarker from "@/components/Maps/Markers/Scandic"
|
||||
import Button from "@/components/TempDesignSystem/Button"
|
||||
import Body from "@/components/TempDesignSystem/Text/Body"
|
||||
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||
|
||||
import styles from "./interactiveMap.module.css"
|
||||
|
||||
import type { MapContentProps } from "@/types/components/hotelPage/map/mapContent"
|
||||
|
||||
export default function InteractiveMap({
|
||||
coordinates,
|
||||
pointsOfInterest,
|
||||
activePoi,
|
||||
mapId,
|
||||
onActivePoiChange,
|
||||
}: MapContentProps) {
|
||||
const intl = useIntl()
|
||||
const { closeDynamicMap } = useHotelPageStore()
|
||||
const map = useMap()
|
||||
|
||||
const mapOptions: MapProps = {
|
||||
defaultZoom: 14,
|
||||
defaultCenter: coordinates,
|
||||
disableDefaultUI: true,
|
||||
clickableIcons: false,
|
||||
mapId,
|
||||
}
|
||||
|
||||
function zoomIn() {
|
||||
const currentZoom = map && map.getZoom()
|
||||
if (currentZoom) {
|
||||
map.setZoom(currentZoom + 1)
|
||||
}
|
||||
}
|
||||
function zoomOut() {
|
||||
const currentZoom = map && map.getZoom()
|
||||
if (currentZoom) {
|
||||
map.setZoom(currentZoom - 1)
|
||||
}
|
||||
}
|
||||
|
||||
function toggleActivePoi(poiName: string) {
|
||||
onActivePoiChange(activePoi === poiName ? null : poiName)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.mapContainer}>
|
||||
<Map {...mapOptions}>
|
||||
<AdvancedMarker position={coordinates} zIndex={1}>
|
||||
<ScandicMarker />
|
||||
</AdvancedMarker>
|
||||
|
||||
{pointsOfInterest.map((poi) => (
|
||||
<AdvancedMarker
|
||||
key={poi.name}
|
||||
className={styles.advancedMarker}
|
||||
position={poi.coordinates}
|
||||
anchorPoint={AdvancedMarkerAnchorPoint.CENTER}
|
||||
zIndex={activePoi === poi.name ? 2 : 0}
|
||||
onMouseEnter={() => onActivePoiChange(poi.name)}
|
||||
onMouseLeave={() => onActivePoiChange(null)}
|
||||
onClick={() => toggleActivePoi(poi.name)}
|
||||
>
|
||||
<span
|
||||
className={`${styles.poi} ${activePoi === poi.name ? styles.active : ""}`}
|
||||
>
|
||||
<PoiMarker
|
||||
group={poi.group}
|
||||
categoryName={poi.categoryName}
|
||||
className={styles.poiMarker}
|
||||
size={activePoi === poi.name ? 20 : 16}
|
||||
/>
|
||||
<Body className={styles.poiLabel} asChild>
|
||||
<span>
|
||||
{poi.name}
|
||||
<Caption asChild>
|
||||
<span>{poi.distance} km</span>
|
||||
</Caption>
|
||||
</span>
|
||||
</Body>
|
||||
</span>
|
||||
</AdvancedMarker>
|
||||
))}
|
||||
</Map>
|
||||
<div className={styles.ctaButtons}>
|
||||
<Button
|
||||
theme="base"
|
||||
intent="inverted"
|
||||
variant="icon"
|
||||
size="small"
|
||||
className={styles.closeButton}
|
||||
onClick={closeDynamicMap}
|
||||
>
|
||||
<CloseLargeIcon color="burgundy" />
|
||||
<span>{intl.formatMessage({ id: "Close the map" })}</span>
|
||||
</Button>
|
||||
<div className={styles.zoomButtons}>
|
||||
<Button
|
||||
theme="base"
|
||||
intent="inverted"
|
||||
variant="icon"
|
||||
size="small"
|
||||
className={styles.zoomButton}
|
||||
onClick={zoomOut}
|
||||
aria-label={intl.formatMessage({ id: "Zoom in" })}
|
||||
>
|
||||
<MinusIcon color="burgundy" width={20} height={20} />
|
||||
</Button>
|
||||
<Button
|
||||
theme="base"
|
||||
intent="inverted"
|
||||
variant="icon"
|
||||
size="small"
|
||||
className={styles.zoomButton}
|
||||
onClick={zoomIn}
|
||||
aria-label={intl.formatMessage({ id: "Zoom out" })}
|
||||
>
|
||||
<PlusIcon color="burgundy" width={20} height={20} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
108
components/Maps/InteractiveMap/interactiveMap.module.css
Normal file
108
components/Maps/InteractiveMap/interactiveMap.module.css
Normal file
@@ -0,0 +1,108 @@
|
||||
.mapContainer {
|
||||
--button-box-shadow: 0 0 8px 1px rgba(0, 0, 0, 0.1);
|
||||
width: 100%;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.mapContainer::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
background: linear-gradient(
|
||||
43deg,
|
||||
rgba(172, 172, 172, 0) 57.66%,
|
||||
rgba(0, 0, 0, 0.25) 92.45%
|
||||
);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.ctaButtons {
|
||||
position: absolute;
|
||||
top: var(--Spacing-x2);
|
||||
right: var(--Spacing-x2);
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--Spacing-x7);
|
||||
align-items: flex-end;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.zoomButtons {
|
||||
display: grid;
|
||||
gap: var(--Spacing-x1);
|
||||
}
|
||||
|
||||
.closeButton {
|
||||
pointer-events: initial;
|
||||
box-shadow: var(--button-box-shadow);
|
||||
gap: var(--Spacing-x-half);
|
||||
}
|
||||
|
||||
.zoomButton {
|
||||
width: var(--Spacing-x5);
|
||||
height: var(--Spacing-x5);
|
||||
padding: 0;
|
||||
pointer-events: initial;
|
||||
box-shadow: var(--button-box-shadow);
|
||||
}
|
||||
|
||||
.advancedMarker {
|
||||
height: var(--Spacing-x4);
|
||||
width: var(
|
||||
--Spacing-x4
|
||||
) !important; /* Overwriting the default width of the @vis.gl/react-google-maps AdvancedMarker */
|
||||
}
|
||||
|
||||
.advancedMarker.active {
|
||||
height: var(--Spacing-x5);
|
||||
width: var(
|
||||
--Spacing-x5
|
||||
) !important; /* Overwriting the default width of the @vis.gl/react-google-maps AdvancedMarker */
|
||||
}
|
||||
|
||||
.poi {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: var(--Spacing-x-half);
|
||||
border-radius: var(--Corner-radius-Rounded);
|
||||
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||
box-shadow: 0 0 4px 2px rgba(0, 0, 0, 0.1);
|
||||
gap: var(--Spacing-x1);
|
||||
}
|
||||
|
||||
.poi.active {
|
||||
padding-right: var(--Spacing-x-one-and-half);
|
||||
}
|
||||
|
||||
.poiLabel {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.poi.active .poiLabel {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--Spacing-x2);
|
||||
text-wrap: nowrap;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.ctaButtons {
|
||||
top: var(--Spacing-x4);
|
||||
right: var(--Spacing-x4);
|
||||
bottom: var(--Spacing-x4);
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.zoomButtons {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user