diff --git a/package-lock.json b/package-lock.json
index 1c34fa1c3..534053b61 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -25841,7 +25841,7 @@
},
"packages/map-template": {
"name": "@mapsindoors/map-template",
- "version": "1.96.18",
+ "version": "1.96.19",
"dependencies": {
"@mapsindoors/components": "*",
"@mapsindoors/css": "^3.0.0",
diff --git a/packages/map-template/CHANGELOG.md b/packages/map-template/CHANGELOG.md
index e295f7cf0..3d776cac6 100644
--- a/packages/map-template/CHANGELOG.md
+++ b/packages/map-template/CHANGELOG.md
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [1.96.20] - 2026-03-27
+
+### Fixed
+
+- An issue when expanded Floor Selector would overlap Zoom Controls and Full Screen button.
+- Added a new custom hook `useFloorSelectorToggleObserver.js`.
+- Added newly created observer to MapControls.jsx
+- `minZoom` can be now configurable via App Config object, otherwise it defaults to 10.
+- Upgraded to Web SDK 4.57.0.
+
## [1.96.19] - 2026-03-26
### Fixed
diff --git a/packages/map-template/index.html b/packages/map-template/index.html
index 3920a45de..235749219 100644
--- a/packages/map-template/index.html
+++ b/packages/map-template/index.html
@@ -6,8 +6,8 @@
+
{shouldRenderElement('zoomControls') && (
)}
diff --git a/packages/map-template/src/components/MIMap/MapControls/MapControls.scss b/packages/map-template/src/components/MIMap/MapControls/MapControls.scss
index 68b30d4a6..1c630c9bf 100644
--- a/packages/map-template/src/components/MIMap/MapControls/MapControls.scss
+++ b/packages/map-template/src/components/MIMap/MapControls/MapControls.scss
@@ -39,6 +39,12 @@
right: calc(1 * var(--spacing-medium));
}
+// Hide bottom controls when the floor selector is expanded to prevent occlusion
+.map-controls-container--floor-selector-open {
+ visibility: hidden;
+ pointer-events: none;
+}
+
// Mobile layout, two columns aligned to the top left and right
.mobile-column {
@extend %control-container;
diff --git a/packages/map-template/src/components/MapTemplate/MapTemplate.jsx b/packages/map-template/src/components/MapTemplate/MapTemplate.jsx
index fd802b95e..d5b7c1d93 100644
--- a/packages/map-template/src/components/MapTemplate/MapTemplate.jsx
+++ b/packages/map-template/src/components/MapTemplate/MapTemplate.jsx
@@ -240,8 +240,8 @@ function MapTemplate({ apiKey, gmApiKey, mapboxAccessToken, venue, locationId, p
const miSdkApiTag = document.createElement('script');
miSdkApiTag.setAttribute('type', 'text/javascript');
// Remember to update the root index.html with the same version / integrity
- miSdkApiTag.setAttribute('src', 'https://app.mapsindoors.com/mapsindoors/js/sdk/4.56.2/mapsindoors-4.56.2.js.gz');
- miSdkApiTag.setAttribute('integrity', 'sha384-AbC6/Ti9R/Fs5vC9Vd+dXtQvBOSMbw7PQ1+xyaL3W5et4ZuNiP1K0GdmoDZvve7G');
+ miSdkApiTag.setAttribute('src', 'https://app.mapsindoors.com/mapsindoors/js/sdk/4.57.0/mapsindoors-4.57.0.js.gz');
+ miSdkApiTag.setAttribute('integrity', 'sha384-Hxm9bx6t+2luBNdH7oGaUAgcZH+FMgbKfltKDlJYJsGbFnXcWH0bm6ZigXLgFmQg');
miSdkApiTag.setAttribute('crossorigin', 'anonymous');
document.body.appendChild(miSdkApiTag);
miSdkApiTag.onload = () => {
@@ -524,7 +524,7 @@ function MapTemplate({ apiKey, gmApiKey, mapboxAccessToken, venue, locationId, p
: appConfig?.appSettings?.showMapMarkers),
miTransitionLevel: miTransitionLevel,
// If ignoreViewportBounds is true, we would like to see the map at the minimum zoom level (World Map).
- minZoom: appConfig?.appSettings?.ignoreViewportBounds ? 1 : ZoomLevelValues.minZoom,
+ minZoom: appConfig?.appSettings?.minZoom ?? ZoomLevelValues.minZoom,
mapboxMapStyle: appConfig?.appSettings?.mapboxMapStyle || mapboxMapStyle,
// Boolean from the App Config comes as a string. We need to return clean boolean value based on that.
enableFullScreenButton:
diff --git a/packages/map-template/src/hooks/useFloorSelectorToggleObserver.js b/packages/map-template/src/hooks/useFloorSelectorToggleObserver.js
new file mode 100644
index 000000000..42140dd36
--- /dev/null
+++ b/packages/map-template/src/hooks/useFloorSelectorToggleObserver.js
@@ -0,0 +1,58 @@
+import { useEffect, useRef } from 'react';
+
+/**
+ * Observes the floor selector's toggle button class to detect open/close,
+ * then measures overlap after the expansion animation finishes.
+ *
+ * @param {Object} params
+ * @param {React.RefObject} params.floorSelectorRef - Ref to the floor selector element
+ * @param {Function} params.setIsFloorSelectorExpanded - State setter for floor selector expansion
+ * @param {Function} params.measureOverlap - Callback to measure overlap between floor selector and bottom controls
+ * @param {Object} params.mapsIndoorsInstance - MapsIndoors SDK instance (dependency trigger)
+ * @param {Object} params.mapInstance - Map instance (dependency trigger)
+ * @returns {React.RefObject} isFloorSelectorOpenRef - Ref tracking whether the floor selector is open
+ */
+export function useFloorSelectorToggleObserver({ floorSelectorRef, setIsFloorSelectorExpanded, measureOverlap, mapsIndoorsInstance, mapInstance }) {
+ const overlapTimerRef = useRef(null);
+ const isFloorSelectorOpenRef = useRef(false);
+
+ useEffect(() => {
+ const floorSelector = floorSelectorRef.current;
+ if (!floorSelector) return;
+
+ const onClassChange = () => {
+ if (overlapTimerRef.current) {
+ clearTimeout(overlapTimerRef.current);
+ }
+
+ const button = floorSelector.querySelector('.mi-floor-selector__button');
+ if (!button) return;
+
+ const isOpen = button.classList.contains('mi-floor-selector__button--open');
+ isFloorSelectorOpenRef.current = isOpen;
+
+ if (!isOpen) {
+ setIsFloorSelectorExpanded(false);
+ return;
+ }
+
+ overlapTimerRef.current = setTimeout(measureOverlap, 50);
+ };
+
+ const observer = new MutationObserver(onClassChange);
+ observer.observe(floorSelector, {
+ subtree: true,
+ attributes: true,
+ attributeFilter: ['class']
+ });
+
+ return () => {
+ observer.disconnect();
+ if (overlapTimerRef.current) {
+ clearTimeout(overlapTimerRef.current);
+ }
+ };
+ }, [floorSelectorRef, setIsFloorSelectorExpanded, measureOverlap, mapsIndoorsInstance, mapInstance]);
+
+ return isFloorSelectorOpenRef;
+}