diff --git a/packages/map-template/CHANGELOG.md b/packages/map-template/CHANGELOG.md index 857dc7bc2..c530d716d 100644 --- a/packages/map-template/CHANGELOG.md +++ b/packages/map-template/CHANGELOG.md @@ -5,6 +5,12 @@ 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.66.0] - 2025-01-16 + +## Added + +- The map now fits to matching Locations when the user clicks/taps on a Category. + ## [1.65.3] - 2025-01-15 ## Fixed diff --git a/packages/map-template/src/components/Search/Search.jsx b/packages/map-template/src/components/Search/Search.jsx index 7439d44b1..44ffcdb8b 100644 --- a/packages/map-template/src/components/Search/Search.jsx +++ b/packages/map-template/src/components/Search/Search.jsx @@ -119,7 +119,7 @@ function Search({ onSetSize, isOpen }) { window.mapsindoors.services.LocationsService.getLocations({ categories: category, venue: searchAllVenues ? undefined : venuesInSolution.find(venue => venue.name.toLowerCase() === currentVenueName.toLowerCase())?.name, - }).then(onResults); + }).then(results => onResults(results, true)); } /** @@ -136,15 +136,20 @@ function Search({ onSetSize, isOpen }) { /** * Handle search results from the search field. * - * @param {array} locations + * @param {array} locations - An array of MapsIndoors Location objects. + * @param {boolean} fitMapBounds - If the map bounds should be adjusted to fit the locations. */ - function onResults(locations) { + function onResults(locations, fitMapBounds = false) { const displayResults = locations.slice(0, MAX_RESULTS); setSearchResults(displayResults); setFilteredLocations(displayResults); setShowNotFoundMessage(displayResults.length === 0); + if (locations && fitMapBounds) { + fitMapBoundsToLocations(locations); + } + // Handles updates to scroll buttons when the category changes. // When a category changes, the scroll buttons need to have their enabled/disabled states updated. // Since some categories might load before the DOM element is fully rendered, we listen for the 'transitionend' event. @@ -157,6 +162,46 @@ function Search({ onSetSize, isOpen }) { } } + + /** + * Adjusts the map view to fit the bounds of the provided locations. + * It will filter out Locations that are not on the current floor or not part of the current venue. + * + * @param {Array} locations - An array of Location objects to fit within the map bounds. + */ + function fitMapBoundsToLocations(locations) { + if (!mapsIndoorsInstance.goTo) return; // Early exit to prevent crashes if using an older version of the MapsIndoors JS SDK. The goTo method was introduced in version 4.38.0. + + const currentFloorIndex = mapsIndoorsInstance.getFloor(); + + // Create a GeoJSON FeatureCollection from the locations that can be used as input to the goTo method. + const featureCollection = { + type: 'FeatureCollection', + features: locations + // Filter out locations that are not on the current floor. If those were included, it could result in a wrong fit since they are not visible on the map anyway. + .filter(location => parseInt(location.properties.floor, 10) === parseInt(currentFloorIndex, 10)) + + // Filter out locations that are not part of the current venue. Including those when fitting to bounds could cause the map to zoom out too much. + .filter(location => location.properties.venueId.toLowerCase() === currentVenueName.toLowerCase()) + + // Map the locations to GeoJSON features. + .map(location => ({ + type: 'Feature', + geometry: location.geometry, + properties: location.properties + })) + }; + + if (featureCollection.features.length > 0) { + Promise.all([getBottomPadding(), getLeftPadding()]).then(([bottomPadding, leftPadding]) => { + mapsIndoorsInstance.goTo(featureCollection, { + maxZoom: 22, + padding: { bottom: bottomPadding, left: leftPadding, top: 0, right: 0 } + }); + }); + } + } + /** * Clear results list when search field is cleared. */ diff --git a/packages/map-template/src/hooks/useMediaQuery.jsx b/packages/map-template/src/hooks/useMediaQuery.jsx index 4c6c5af51..411e4799e 100644 --- a/packages/map-template/src/hooks/useMediaQuery.jsx +++ b/packages/map-template/src/hooks/useMediaQuery.jsx @@ -6,7 +6,7 @@ import { useState, useEffect } from "react"; * @param {string} query */ const useMediaQuery = (query) => { - const [matches, setMatches] = useState(false); + const [matches, setMatches] = useState(window.matchMedia(query).matches); useEffect(() => { const media = window.matchMedia(query);