Skip to content

Commit 0fc93a5

Browse files
Merge pull request #669 from MapsPeople/feature/matb/fix_floor_selector_expansion_on_small_screens
Feature/matb/fix floor selector expansion on small screens
2 parents 0f698ad + d366ee0 commit 0fc93a5

File tree

7 files changed

+131
-8
lines changed

7 files changed

+131
-8
lines changed

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/map-template/CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.96.20] - 2026-03-27
9+
10+
### Fixed
11+
12+
- An issue when expanded Floor Selector would overlap Zoom Controls and Full Screen button.
13+
- Added a new custom hook `useFloorSelectorToggleObserver.js`.
14+
- Added newly created observer to MapControls.jsx
15+
- `minZoom` can be now configurable via App Config object, otherwise it defaults to 10.
16+
- Upgraded to Web SDK 4.57.0.
17+
818
## [1.96.19] - 2026-03-26
919

1020
### Fixed

packages/map-template/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
<meta name="viewport" content="width=device-width, initial-scale=1" />
77
<meta name="theme-color" content="#005655" />
88
<title>MapsIndoors Web</title>
9-
<script defer src="https://app.mapsindoors.com/mapsindoors/js/sdk/4.56.2/mapsindoors-4.56.2.js.gz"
10-
integrity="sha384-AbC6/Ti9R/Fs5vC9Vd+dXtQvBOSMbw7PQ1+xyaL3W5et4ZuNiP1K0GdmoDZvve7G"
9+
<script defer src="https://app.mapsindoors.com/mapsindoors/js/sdk/4.57.0/mapsindoors-4.57.0.js.gz"
10+
integrity="sha384-Hxm9bx6t+2luBNdH7oGaUAgcZH+FMgbKfltKDlJYJsGbFnXcWH0bm6ZigXLgFmQg"
1111
crossorigin="anonymous"></script>
1212
</head>
1313

packages/map-template/src/components/MIMap/MapControls/MapControls.jsx

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { useEffect, useRef, useCallback, useMemo, memo } from 'react';
1+
import { useEffect, useRef, useState, useCallback, useMemo, memo } from 'react';
22
import PropTypes from 'prop-types';
33
import './MapControls.scss';
44
import { useIsDesktop } from '../../../hooks/useIsDesktop';
5+
import { useFloorSelectorToggleObserver } from '../../../hooks/useFloorSelectorToggleObserver';
56
import CustomPositionProvider from '../../../utils/CustomPositionProvider';
67
import MapZoomControl from '../MapZoomControl/MapZoomControl';
78
import TextSizeButton from '../TextSizeButton/TextSizeButton';
@@ -60,6 +61,26 @@ function MapControls({ mapType, mapsIndoorsInstance, mapInstance, onPositionCont
6061
const isDesktop = useIsDesktop();
6162
const floorSelectorRef = useRef(null);
6263
const positionButtonRef = useRef(null);
64+
const bottomControlsRef = useRef(null);
65+
const [isFloorSelectorExpanded, setIsFloorSelectorExpanded] = useState(false);
66+
67+
// Measures whether the expanded floor selector overlaps the bottom controls.
68+
// Uses only refs and stable setters so the dependency array can stay empty.
69+
const measureOverlap = useCallback(() => {
70+
const bottomControls = bottomControlsRef.current;
71+
const floorSelector = floorSelectorRef.current;
72+
73+
if (!bottomControls || !floorSelector) {
74+
setIsFloorSelectorExpanded(false);
75+
return;
76+
}
77+
78+
const selectorEl = floorSelector.querySelector('.mi-floor-selector') || floorSelector;
79+
const selectorRect = selectorEl.getBoundingClientRect();
80+
const controlsRect = bottomControls.getBoundingClientRect();
81+
82+
setIsFloorSelectorExpanded(selectorRect.bottom > controlsRect.top);
83+
}, []);
6384

6485
// Helper function to check if an element should be rendered
6586
const shouldRenderElement = useCallback((elementName) => {
@@ -210,6 +231,34 @@ function MapControls({ mapType, mapsIndoorsInstance, mapInstance, onPositionCont
210231
});
211232
}, [excludedElements, shouldRenderElement, isDesktop]);
212233

234+
const isFloorSelectorOpenRef = useFloorSelectorToggleObserver({
235+
floorSelectorRef,
236+
setIsFloorSelectorExpanded,
237+
measureOverlap,
238+
mapsIndoorsInstance,
239+
mapInstance
240+
});
241+
242+
// Recompute overlap on viewport resize, orientation change, and layout transitions.
243+
useEffect(() => {
244+
const onViewportChange = () => {
245+
if (isFloorSelectorOpenRef.current) {
246+
measureOverlap();
247+
}
248+
};
249+
250+
window.addEventListener('resize', onViewportChange);
251+
window.addEventListener('orientationchange', onViewportChange);
252+
253+
// Re-check immediately after a layout switch while the selector is already open
254+
onViewportChange();
255+
256+
return () => {
257+
window.removeEventListener('resize', onViewportChange);
258+
window.removeEventListener('orientationchange', onViewportChange);
259+
};
260+
}, [isDesktop, isKiosk, measureOverlap]);
261+
213262
if (isKiosk) {
214263
if (enableAccessibilityKioskControls) {
215264
return (
@@ -274,7 +323,7 @@ function MapControls({ mapType, mapsIndoorsInstance, mapInstance, onPositionCont
274323
</div>
275324

276325
{/* Bottom right desktop controls */}
277-
<div className="map-controls-container desktop bottom-right">
326+
<div ref={bottomControlsRef} className={`map-controls-container desktop bottom-right ${isFloorSelectorExpanded ? 'map-controls-container--floor-selector-open' : ''}`}>
278327
{shouldRenderElement('zoomControls') && (
279328
<MapZoomControl mapType={mapType} mapInstance={mapInstance} />
280329
)}

packages/map-template/src/components/MIMap/MapControls/MapControls.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@
3939
right: calc(1 * var(--spacing-medium));
4040
}
4141

42+
// Hide bottom controls when the floor selector is expanded to prevent occlusion
43+
.map-controls-container--floor-selector-open {
44+
visibility: hidden;
45+
pointer-events: none;
46+
}
47+
4248
// Mobile layout, two columns aligned to the top left and right
4349
.mobile-column {
4450
@extend %control-container;

packages/map-template/src/components/MapTemplate/MapTemplate.jsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -240,8 +240,8 @@ function MapTemplate({ apiKey, gmApiKey, mapboxAccessToken, venue, locationId, p
240240
const miSdkApiTag = document.createElement('script');
241241
miSdkApiTag.setAttribute('type', 'text/javascript');
242242
// Remember to update the root index.html with the same version / integrity
243-
miSdkApiTag.setAttribute('src', 'https://app.mapsindoors.com/mapsindoors/js/sdk/4.56.2/mapsindoors-4.56.2.js.gz');
244-
miSdkApiTag.setAttribute('integrity', 'sha384-AbC6/Ti9R/Fs5vC9Vd+dXtQvBOSMbw7PQ1+xyaL3W5et4ZuNiP1K0GdmoDZvve7G');
243+
miSdkApiTag.setAttribute('src', 'https://app.mapsindoors.com/mapsindoors/js/sdk/4.57.0/mapsindoors-4.57.0.js.gz');
244+
miSdkApiTag.setAttribute('integrity', 'sha384-Hxm9bx6t+2luBNdH7oGaUAgcZH+FMgbKfltKDlJYJsGbFnXcWH0bm6ZigXLgFmQg');
245245
miSdkApiTag.setAttribute('crossorigin', 'anonymous');
246246
document.body.appendChild(miSdkApiTag);
247247
miSdkApiTag.onload = () => {
@@ -524,7 +524,7 @@ function MapTemplate({ apiKey, gmApiKey, mapboxAccessToken, venue, locationId, p
524524
: appConfig?.appSettings?.showMapMarkers),
525525
miTransitionLevel: miTransitionLevel,
526526
// If ignoreViewportBounds is true, we would like to see the map at the minimum zoom level (World Map).
527-
minZoom: appConfig?.appSettings?.ignoreViewportBounds ? 1 : ZoomLevelValues.minZoom,
527+
minZoom: appConfig?.appSettings?.minZoom ?? ZoomLevelValues.minZoom,
528528
mapboxMapStyle: appConfig?.appSettings?.mapboxMapStyle || mapboxMapStyle,
529529
// Boolean from the App Config comes as a string. We need to return clean boolean value based on that.
530530
enableFullScreenButton:
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { useEffect, useRef } from 'react';
2+
3+
/**
4+
* Observes the floor selector's toggle button class to detect open/close,
5+
* then measures overlap after the expansion animation finishes.
6+
*
7+
* @param {Object} params
8+
* @param {React.RefObject} params.floorSelectorRef - Ref to the floor selector element
9+
* @param {Function} params.setIsFloorSelectorExpanded - State setter for floor selector expansion
10+
* @param {Function} params.measureOverlap - Callback to measure overlap between floor selector and bottom controls
11+
* @param {Object} params.mapsIndoorsInstance - MapsIndoors SDK instance (dependency trigger)
12+
* @param {Object} params.mapInstance - Map instance (dependency trigger)
13+
* @returns {React.RefObject<boolean>} isFloorSelectorOpenRef - Ref tracking whether the floor selector is open
14+
*/
15+
export function useFloorSelectorToggleObserver({ floorSelectorRef, setIsFloorSelectorExpanded, measureOverlap, mapsIndoorsInstance, mapInstance }) {
16+
const overlapTimerRef = useRef(null);
17+
const isFloorSelectorOpenRef = useRef(false);
18+
19+
useEffect(() => {
20+
const floorSelector = floorSelectorRef.current;
21+
if (!floorSelector) return;
22+
23+
const onClassChange = () => {
24+
if (overlapTimerRef.current) {
25+
clearTimeout(overlapTimerRef.current);
26+
}
27+
28+
const button = floorSelector.querySelector('.mi-floor-selector__button');
29+
if (!button) return;
30+
31+
const isOpen = button.classList.contains('mi-floor-selector__button--open');
32+
isFloorSelectorOpenRef.current = isOpen;
33+
34+
if (!isOpen) {
35+
setIsFloorSelectorExpanded(false);
36+
return;
37+
}
38+
39+
overlapTimerRef.current = setTimeout(measureOverlap, 50);
40+
};
41+
42+
const observer = new MutationObserver(onClassChange);
43+
observer.observe(floorSelector, {
44+
subtree: true,
45+
attributes: true,
46+
attributeFilter: ['class']
47+
});
48+
49+
return () => {
50+
observer.disconnect();
51+
if (overlapTimerRef.current) {
52+
clearTimeout(overlapTimerRef.current);
53+
}
54+
};
55+
}, [floorSelectorRef, setIsFloorSelectorExpanded, measureOverlap, mapsIndoorsInstance, mapInstance]);
56+
57+
return isFloorSelectorOpenRef;
58+
}

0 commit comments

Comments
 (0)