diff --git a/components/ContentType/HotelPage/Map/DynamicMap/Content.tsx b/components/ContentType/HotelPage/Map/DynamicMap/Content.tsx
deleted file mode 100644
index 451bb07e6..000000000
--- a/components/ContentType/HotelPage/Map/DynamicMap/Content.tsx
+++ /dev/null
@@ -1,147 +0,0 @@
-"use client"
-import {
- AdvancedMarker,
- Map,
- type MapProps,
- useMap,
-} from "@vis.gl/react-google-maps"
-import { useState } from "react"
-import { useIntl } from "react-intl"
-
-import useHotelPageStore from "@/stores/hotel-page"
-
-import { CloseIcon, MinusIcon, PlusIcon } from "@/components/Icons"
-import Button from "@/components/TempDesignSystem/Button"
-import Divider from "@/components/TempDesignSystem/Divider"
-import Title from "@/components/TempDesignSystem/Text/Title"
-import { useHandleKeyUp } from "@/hooks/useHandleKeyUp"
-import useLang from "@/hooks/useLang"
-
-import ScandicMarker from "../Markers/Scandic"
-
-import styles from "./dynamicMap.module.css"
-
-import type { DynamicMapContentProps } from "@/types/components/hotelPage/map/dynamicMapContent"
-
-export default function DynamicMapContent({
- hotelName,
- coordinates,
-}: DynamicMapContentProps) {
- const intl = useIntl()
- const lang = useLang()
- const { isDynamicMapOpen, closeDynamicMap } = useHotelPageStore()
- const [isFullScreenSidebar, setIsFullScreenSidebar] = useState(false)
- const map = useMap()
-
- const mapOptions: MapProps = {
- defaultZoom: 15,
- defaultCenter: coordinates,
- disableDefaultUI: true,
- clickableIcons: false,
- mapId: `${hotelName}-${lang}-map`,
- // As reference for future styles when adding POIs
- // styles: [
- // {
- // featureType: "poi",
- // elementType: "all",
- // stylers: [{ visibility: "off" }],
- // },
- // ],
- }
-
- useHandleKeyUp((event: KeyboardEvent) => {
- if (event.key === "Escape" && isDynamicMapOpen) {
- closeDynamicMap()
- }
- })
-
- 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 toggleFullScreenSidebar() {
- setIsFullScreenSidebar((prev) => !prev)
- }
-
- return (
- <>
-
-
-
-
-
-
- >
- )
-}
diff --git a/components/ContentType/HotelPage/Map/DynamicMap/Map/index.tsx b/components/ContentType/HotelPage/Map/DynamicMap/Map/index.tsx
new file mode 100644
index 000000000..196baff83
--- /dev/null
+++ b/components/ContentType/HotelPage/Map/DynamicMap/Map/index.tsx
@@ -0,0 +1,137 @@
+"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 "./map.module.css"
+
+import type { MapContentProps } from "@/types/components/hotelPage/map/mapContent"
+
+export default function MapContent({
+ coordinates,
+ pointsOfInterest,
+ activePoi,
+ onActivePoiChange,
+}: MapContentProps) {
+ const intl = useIntl()
+ const { closeDynamicMap } = useHotelPageStore()
+ const map = useMap()
+
+ const mapOptions: MapProps = {
+ defaultZoom: 14,
+ defaultCenter: coordinates,
+ disableDefaultUI: true,
+ clickableIcons: false,
+ mapId: "6b48ef228325ae84",
+ }
+
+ 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 (
+
+
+
+
+
+
+
+ )
+}
diff --git a/components/ContentType/HotelPage/Map/DynamicMap/Map/map.module.css b/components/ContentType/HotelPage/Map/DynamicMap/Map/map.module.css
new file mode 100644
index 000000000..557730bfe
--- /dev/null
+++ b/components/ContentType/HotelPage/Map/DynamicMap/Map/map.module.css
@@ -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;
+ }
+}
diff --git a/components/ContentType/HotelPage/Map/DynamicMap/Sidebar/index.tsx b/components/ContentType/HotelPage/Map/DynamicMap/Sidebar/index.tsx
new file mode 100644
index 000000000..217fa51bd
--- /dev/null
+++ b/components/ContentType/HotelPage/Map/DynamicMap/Sidebar/index.tsx
@@ -0,0 +1,102 @@
+"use client"
+
+import { useState } from "react"
+import { useIntl } from "react-intl"
+
+import PoiMarker from "@/components/Maps/Markers/Poi"
+import Button from "@/components/TempDesignSystem/Button"
+import Body from "@/components/TempDesignSystem/Text/Body"
+import Title from "@/components/TempDesignSystem/Text/Title"
+
+import styles from "./sidebar.module.css"
+
+import type { SidebarProps } from "@/types/components/hotelPage/map/sidebar"
+
+export default function Sidebar({
+ activePoi,
+ hotelName,
+ pointsOfInterest,
+ onActivePoiChange,
+}: SidebarProps) {
+ const intl = useIntl()
+ const [isFullScreenSidebar, setIsFullScreenSidebar] = useState(false)
+ const poiCategories = new Set(
+ pointsOfInterest.map(({ category }) => category)
+ )
+ const poisInCategories = Array.from(poiCategories).map((category) => ({
+ category,
+ pois: pointsOfInterest.filter((poi) => poi.category === category),
+ }))
+
+ function toggleFullScreenSidebar() {
+ setIsFullScreenSidebar((prev) => !prev)
+ }
+
+ return (
+
+ )
+}
diff --git a/components/ContentType/HotelPage/Map/DynamicMap/Sidebar/sidebar.module.css b/components/ContentType/HotelPage/Map/DynamicMap/Sidebar/sidebar.module.css
new file mode 100644
index 000000000..11f26b822
--- /dev/null
+++ b/components/ContentType/HotelPage/Map/DynamicMap/Sidebar/sidebar.module.css
@@ -0,0 +1,111 @@
+.sidebar {
+ --sidebar-max-width: 26.25rem;
+ --sidebar-mobile-toggle-height: 91px;
+ --sidebar-mobile-fullscreen-height: calc(
+ 100vh - var(--main-menu-mobile-height) - var(--sidebar-mobile-toggle-height)
+ );
+
+ position: absolute;
+ top: var(--sidebar-mobile-fullscreen-height);
+ height: 100%;
+ right: 0;
+ left: 0;
+ background-color: var(--Base-Surface-Primary-light-Normal);
+ z-index: 1;
+ transition: top 0.3s;
+}
+
+.sidebar:not(.fullscreen) {
+ border-radius: var(--Corner-radius-Large) var(--Corner-radius-Large) 0 0;
+}
+
+.sidebar.fullscreen {
+ top: 0;
+}
+
+.sidebarToggle {
+ position: relative;
+ margin: var(--Spacing-x4) 0 var(--Spacing-x2);
+ width: 100%;
+}
+
+.sidebarToggle::before {
+ content: "";
+ position: absolute;
+ display: block;
+ top: -0.5rem;
+ width: 100px;
+ height: 3px;
+ background-color: var(--UI-Text-High-contrast);
+}
+
+.sidebarContent {
+ display: grid;
+ gap: var(--Spacing-x5);
+ align-content: start;
+ padding: var(--Spacing-x3) var(--Spacing-x2);
+ height: var(--sidebar-mobile-fullscreen-height);
+ overflow-y: auto;
+}
+
+.poiGroup {
+ display: grid;
+ gap: var(--Spacing-x2);
+}
+
+.poiHeading {
+ display: flex;
+ align-items: center;
+ gap: var(--Spacing-x1);
+}
+
+.poiList {
+ list-style: none;
+}
+
+.poiItem {
+ padding: var(--Spacing-x1) 0;
+ border-bottom: 1px solid var(--Base-Border-Subtle);
+}
+
+.poiButton {
+ background-color: var(--Base-Surface-Primary-light-Normal);
+ border-width: 0;
+ font-family: var(--typography-Body-Regular-fontFamily);
+ font-size: var(--typography-Body-Regular-fontSize);
+ font-weight: var(--typography-Body-Regular-fontWeight);
+ color: var(--UI-Text-High-contrast);
+ width: 100%;
+ display: grid;
+ grid-template-columns: 1fr max-content;
+ gap: var(--Spacing-x2);
+ align-items: center;
+ padding: var(--Spacing-x-half) var(--Spacing-x1);
+ border-radius: var(--Corner-radius-Medium);
+ cursor: pointer;
+ text-align: left;
+ transition: background-color 0.3s;
+}
+.poiButton.active {
+ background-color: var(--Base-Surface-Primary-light-Hover);
+}
+
+@media screen and (min-width: 768px) {
+ .sidebar {
+ position: static;
+ width: 40vw;
+ min-width: 10rem;
+ max-width: var(--sidebar-max-width);
+ background-color: var(--Base-Surface-Primary-light-Normal);
+ }
+
+ .sidebarToggle {
+ display: none;
+ }
+
+ .sidebarContent {
+ padding: var(--Spacing-x4) var(--Spacing-x5);
+ height: 100%;
+ position: relative;
+ }
+}
diff --git a/components/ContentType/HotelPage/Map/DynamicMap/dynamicMap.module.css b/components/ContentType/HotelPage/Map/DynamicMap/dynamicMap.module.css
index 303ad4c1f..2a3e4ff78 100644
--- a/components/ContentType/HotelPage/Map/DynamicMap/dynamicMap.module.css
+++ b/components/ContentType/HotelPage/Map/DynamicMap/dynamicMap.module.css
@@ -1,5 +1,4 @@
.dynamicMap {
- --sidebar-height: 88px;
position: fixed;
top: var(--main-menu-mobile-height);
right: 0;
@@ -10,143 +9,8 @@
background-color: var(--Base-Surface-Primary-light-Normal);
}
-.sidebar {
- position: absolute;
- top: calc(100vh - var(--main-menu-mobile-height) - var(--sidebar-height));
- height: 100%;
- right: 0;
- left: 0;
- background-color: var(--Base-Surface-Primary-light-Normal);
- z-index: 1;
- transition: top 0.3s;
-}
-
-.sidebar:not(.fullscreen) {
- border-radius: var(--Corner-radius-Large) var(--Corner-radius-Large) 0 0;
-}
-
-.sidebar.fullscreen {
- top: 0;
-}
-
-.sidebarToggle {
- display: grid;
- gap: var(--Spacing-x1);
- padding: var(--Spacing-x2);
- height: var(--sidebar-height);
-}
-
-.toggleButton {
- position: relative;
- display: flex;
- justify-content: center;
- background-color: transparent;
- border-width: 0;
- padding: var(--Spacing-x-one-and-half) var(--Spacing-x2);
- font-family: var(--typography-Body-Bold-fontFamily);
- font-size: var(--typography-Body-Bold-fontSize);
- font-weight: var(--typography-Body-Bold-fontWeight);
- color: var(--UI-Text-Medium-contrast);
- width: 100%;
-}
-
-.toggleButton::before {
- content: "";
- position: absolute;
- display: block;
- top: -0.5rem;
- width: 100px;
- height: 3px;
- background-color: var(--UI-Text-High-contrast);
-}
-
-.sidebarContent {
- display: grid;
- gap: var(--Spacing-x3);
- align-content: start;
- padding: var(--Spacing-x3) var(--Spacing-x2);
- height: 100%;
- overflow-y: auto;
-}
-
-.mapContainer {
- flex: 1;
- position: relative;
-}
-
-.mapContainer::after {
- content: "";
- display: block;
- 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: 0 0 8px 1px rgba(0, 0, 0, 0.1);
-}
-
-.zoomButton {
- width: var(--Spacing-x5);
- height: var(--Spacing-x5);
- padding: 0;
- pointer-events: initial;
- box-shadow: 0 0 8px 1px rgba(0, 0, 0, 0.1);
-}
-
@media screen and (min-width: 768px) {
.dynamicMap {
top: var(--main-menu-desktop-height);
}
- .sidebar {
- position: static;
- width: 40vw;
- min-width: 10rem;
- max-width: 26.25rem;
- background-color: var(--Base-Surface-Primary-light-Normal);
- }
- .sidebarToggle {
- display: none;
- }
-
- .sidebarContent {
- padding: var(--Spacing-x4) var(--Spacing-x5);
- }
-
- .ctaButtons {
- top: var(--Spacing-x4);
- right: var(--Spacing-x4);
- bottom: var(--Spacing-x4);
- justify-content: space-between;
- }
-
- .zoomButtons {
- display: flex;
- }
}
diff --git a/components/ContentType/HotelPage/Map/DynamicMap/index.tsx b/components/ContentType/HotelPage/Map/DynamicMap/index.tsx
index 2eee0b6ae..28cf8cc9b 100644
--- a/components/ContentType/HotelPage/Map/DynamicMap/index.tsx
+++ b/components/ContentType/HotelPage/Map/DynamicMap/index.tsx
@@ -8,7 +8,8 @@ import useHotelPageStore from "@/stores/hotel-page"
import { useHandleKeyUp } from "@/hooks/useHandleKeyUp"
-import DynamicMapContent from "./Content"
+import MapContent from "./Map"
+import Sidebar from "./Sidebar"
import styles from "./dynamicMap.module.css"
@@ -18,11 +19,13 @@ export default function DynamicMap({
apiKey,
hotelName,
coordinates,
+ pointsOfInterest,
}: DynamicMapProps) {
const intl = useIntl()
const { isDynamicMapOpen, closeDynamicMap } = useHotelPageStore()
const [scrollHeightWhenOpened, setScrollHeightWhenOpened] = useState(0)
const hasMounted = useRef(false)
+ const [activePoi, setActivePoi] = useState(null)
useHandleKeyUp((event: KeyboardEvent) => {
if (event.key === "Escape" && isDynamicMapOpen) {
@@ -58,7 +61,18 @@ export default function DynamicMap({
{ hotelName }
)}
>
-
+
+
diff --git a/components/ContentType/HotelPage/Map/MapCard/index.tsx b/components/ContentType/HotelPage/Map/MapCard/index.tsx
index c3caca9ed..8764870bc 100644
--- a/components/ContentType/HotelPage/Map/MapCard/index.tsx
+++ b/components/ContentType/HotelPage/Map/MapCard/index.tsx
@@ -4,7 +4,9 @@ import { useIntl } from "react-intl"
import useHotelPageStore from "@/stores/hotel-page"
+import PoiMarker from "@/components/Maps/Markers/Poi"
import Button from "@/components/TempDesignSystem/Button"
+import Body from "@/components/TempDesignSystem/Text/Body"
import Caption from "@/components/TempDesignSystem/Text/Caption"
import Title from "@/components/TempDesignSystem/Text/Title"
@@ -12,7 +14,7 @@ import styles from "./mapCard.module.css"
import type { MapCardProps } from "@/types/components/hotelPage/map/mapCard"
-export default function MapCard({ hotelName }: MapCardProps) {
+export default function MapCard({ hotelName, pois }: MapCardProps) {
const intl = useIntl()
const { openDynamicMap } = useHotelPageStore()
@@ -34,6 +36,15 @@ export default function MapCard({ hotelName }: MapCardProps) {
>
{hotelName}
+
+ {pois.map((poi) => (
+ -
+
+ {poi.name}
+ {poi.distance} km
+
+ ))}
+