From 1d26ef7b4e3862dbe5ae8a477592db75b5df8c29 Mon Sep 17 00:00:00 2001 From: EJ Haselden Date: Tue, 28 Oct 2025 23:34:58 +0000 Subject: [PATCH] Update Places UI Kit Nearby Search --- samples/ui-kit-place-search-nearby/index.html | 70 +++-- samples/ui-kit-place-search-nearby/index.ts | 246 ++++++++++-------- samples/ui-kit-place-search-nearby/style.css | 109 ++++---- 3 files changed, 246 insertions(+), 179 deletions(-) diff --git a/samples/ui-kit-place-search-nearby/index.html b/samples/ui-kit-place-search-nearby/index.html index 13dbe4db..8db15075 100644 --- a/samples/ui-kit-place-search-nearby/index.html +++ b/samples/ui-kit-place-search-nearby/index.html @@ -7,39 +7,67 @@ - Place List Nearby Search with Google Maps - - - + Place Search Nearby with Google Maps + + + + + - -
+
+ +
- + - +
- + + + + +
- +
- - - - +
+ + + + + + + + + + + +
- - + \ No newline at end of file diff --git a/samples/ui-kit-place-search-nearby/index.ts b/samples/ui-kit-place-search-nearby/index.ts index 2af79865..0a573398 100644 --- a/samples/ui-kit-place-search-nearby/index.ts +++ b/samples/ui-kit-place-search-nearby/index.ts @@ -6,132 +6,166 @@ /* [START maps_ui_kit_place_search_nearby] */ /* [START maps_ui_kit_place_search_nearby_query_selectors] */ -const map = document.querySelector("gmp-map") as any; -const placeList = document.querySelector("gmp-place-list") as any; -const typeSelect = document.querySelector(".type-select") as any; -const placeDetails = document.querySelector("gmp-place-details") as any; -const placeDetailsRequest = document.querySelector('gmp-place-details-place-request') as any; +// Query selectors for various elements in the HTML file. +const map = document.querySelector('gmp-map') as google.maps.MapElement +const placeSearch = document.querySelector('gmp-place-search') as any +const placeSearchQuery = document.querySelector( + 'gmp-place-nearby-search-request' +) as any +const placeDetails = document.querySelector('gmp-place-details-compact') as any +const placeRequest = document.querySelector( + 'gmp-place-details-place-request' +) as any +const typeSelect = document.querySelector('.type-select') as HTMLSelectElement /* [END maps_ui_kit_place_search_nearby_query_selectors] */ -let markers = {}; -let infoWindow; - -async function initMap(): Promise { - await google.maps.importLibrary('places'); - const { LatLngBounds } = await google.maps.importLibrary('core') as google.maps.CoreLibrary; - const { InfoWindow } = await google.maps.importLibrary('maps') as google.maps.MapsLibrary; - const { spherical } = await google.maps.importLibrary('geometry') as google.maps.GeometryLibrary; - - infoWindow = new InfoWindow; - let marker; - - function getContainingCircle(bounds) { - const diameter = spherical.computeDistanceBetween( - bounds.getNorthEast(), - bounds.getSouthWest() - ); - const calculatedRadius = diameter / 2; - const cappedRadius = Math.min(calculatedRadius, 50000); // Radius cannot be more than 50000. - return { center: bounds.getCenter(), radius: cappedRadius }; - } - - findCurrentLocation(); +// Global variables for the map, markers, and info window. +const markers: Map = new Map() +let infoWindow + +let AdvancedMarkerElement: typeof google.maps.marker.AdvancedMarkerElement +let LatLngBounds: typeof google.maps.LatLngBounds + +// The init function is called when the page loads. +async function init(): Promise { + // Import the necessary libraries from the Google Maps API. + const { InfoWindow } = (await google.maps.importLibrary( + 'maps' + )) as google.maps.MapsLibrary + await google.maps.importLibrary('places') + const markerLib = (await google.maps.importLibrary( + 'marker' + )) as google.maps.MarkerLibrary + const coreLib = (await google.maps.importLibrary( + 'core' + )) as google.maps.CoreLibrary + + AdvancedMarkerElement = markerLib.AdvancedMarkerElement + LatLngBounds = coreLib.LatLngBounds + + // Create a new info window and set its content to the place details element. + infoWindow = new InfoWindow({ + content: placeDetails, + ariaLabel: 'Place Details', + headerDisabled: true, + pixelOffset: { width: 0, height: -40 } as google.maps.Size, + }) + + // Set the map options. map.innerMap.setOptions({ - mapTypeControl: false, clickableIcons: false, - }); + mapTypeControl: false, + streetViewControl: false, + }) + // Add a click listener to the map to hide the info window when the map is clicked. + map.innerMap.addListener('click', () => { + hideInfoWindow() + }) /* [START maps_ui_kit_place_search_nearby_event] */ - placeDetails.addEventListener('gmp-load', (event) => { - // Center the info window on the map. - map.innerMap.fitBounds(placeDetails.place.viewport, { top: 500, left: 400 }); - }); - - typeSelect.addEventListener('change', (event) => { - // First remove all existing markers. - for(marker in markers){ - markers[marker].map = null; + // Add event listeners to the type select and place search elements. + typeSelect.addEventListener('change', searchPlaces) + + placeSearch.addEventListener('gmp-select', (event: Event) => { + const { place } = event as any + if (markers.has(place.id)) { + markers.get(place.id)!.click() } - markers = {}; - - if (typeSelect.value) { - placeList.style.display = 'block'; - placeList.configureFromSearchNearbyRequest({ - locationRestriction: getContainingCircle( - map.innerMap.getBounds() - ), - includedPrimaryTypes: [typeSelect.value], - }).then(addMarkers); - // Handle user selection in Place Details. - placeList.addEventListener('gmp-placeselect', ({ place }) => { - markers[place.id].click(); - }); + }) + placeSearch.addEventListener( + 'gmp-load', + () => { + searchPlaces() + }, + { once: true } + ) +} +/* [END maps_ui_kit_place_search_nearby_event] */ +// The searchPlaces function is called when the user changes the type select or when the page loads. +function searchPlaces() { + // Get the map bounds and center. + const bounds = map.innerMap.getBounds()! + const cent = map.innerMap.getCenter()! + const ne = bounds.getNorthEast() + const sw = bounds.getSouthWest() + // Calculate the diameter of the map bounds and cap the radius at 50000. + const diameter = google.maps.geometry.spherical.computeDistanceBetween( + ne, + sw + ) + const cappedRadius = Math.min(diameter / 2, 50000) // Radius cannot be more than 50000. + + // Close the info window and clear the markers. + infoWindow.close() + + for (const marker of markers.values()) { + marker.map = null + } + markers.clear() + + // Set the place search query and add an event listener to the place search element. + if (typeSelect.value) { + // map.style.height = '75vh'; + placeSearch.style.visibility = 'visible' + placeSearchQuery.maxResultCount = 10 + placeSearchQuery.locationRestriction = { + center: { lat: cent.lat(), lng: cent.lng() }, + radius: cappedRadius, } - }); - /* [END maps_ui_kit_place_search_nearby_event] */ + placeSearchQuery.includedTypes = [typeSelect.value] + placeSearch.addEventListener('gmp-load', () => addMarkers(), { + once: true, + }) + } } -async function addMarkers(){ - const { AdvancedMarkerElement } = await google.maps.importLibrary('marker') as google.maps.MarkerLibrary; - const { LatLngBounds } = await google.maps.importLibrary('core') as google.maps.CoreLibrary; - - const bounds = new LatLngBounds(); +// The addMarkers function is called when the place search element loads. +async function addMarkers() { + // Create a new LatLngBounds object and make the place search element visible. + const bounds = new LatLngBounds() + placeSearch.style.visibility = 'visible' - if(placeList.places.length > 0){ - placeList.places.forEach((place) => { - let marker = new AdvancedMarkerElement({ + // Iterate over the places and create a marker for each place. + if (placeSearch.places.length > 0) { + placeSearch.places.forEach((place) => { + const marker = new AdvancedMarkerElement({ map: map.innerMap, - position: place.location - }); + position: place.location, + collisionBehavior: + google.maps.CollisionBehavior.REQUIRED_AND_HIDES_OPTIONAL, + }) - markers[place.id] = marker; - bounds.extend(place.location); + // Add the marker to the markers map and extend the bounds. + markers.set(place.id, marker) + bounds.extend(place.location) /* [START maps_ui_kit_place_search_nearby_click_event] */ - marker.addListener('gmp-click', (event) => { - if(infoWindow.isOpen){ - infoWindow.close(); + // Add a click listener to the marker to show the info window. + marker.addListener('click', () => { + if (place.viewport) { + map.innerMap.fitBounds(place.viewport) + } else { + map.innerMap.panTo(place.location) + map.innerMap.setZoom(18) } - - placeDetailsRequest.place = place.id; - placeDetails.style.display = 'block'; - placeDetails.style.width = '350px'; - infoWindow.setOptions({ - content: placeDetails, - }); - infoWindow.open({ - anchor: marker, - map: map.innerMap - }); - }); + placeRequest.place = place + infoWindow.setPosition(place.location) + placeDetails.style.visibility = 'visible' // Ensure place details are visible + infoWindow.open(map.innerMap) + }) /* [END maps_ui_kit_place_search_nearby_click_event] */ - - map.innerMap.setCenter(bounds.getCenter()); - map.innerMap.fitBounds(bounds); - }); + }) + // Set the map center and fit the bounds. + map.innerMap.setCenter(bounds.getCenter()) + map.innerMap.fitBounds(bounds) } } -async function findCurrentLocation(){ - const { LatLng } = await google.maps.importLibrary('core') as google.maps.CoreLibrary; - if (navigator.geolocation) { - navigator.geolocation.getCurrentPosition( - (position) => { - const pos = new LatLng(position.coords.latitude,position.coords.longitude); - map.innerMap.panTo(pos); - map.innerMap.setZoom(14); - }, - () => { - console.log('The Geolocation service failed.'); - map.innerMap.setZoom(14); - }, - ); - } else { - console.log('Your browser doesn\'t support geolocation'); - map.innerMap.setZoom(14); - } - +// The hideInfoWindow function is called when the user clicks on the map. +function hideInfoWindow() { + infoWindow.close() + placeDetails.style.visibility = 'hidden' } -initMap(); +init() /* [END maps_ui_kit_place_search_nearby] */ diff --git a/samples/ui-kit-place-search-nearby/style.css b/samples/ui-kit-place-search-nearby/style.css index e5a3c5d6..f05c81e2 100644 --- a/samples/ui-kit-place-search-nearby/style.css +++ b/samples/ui-kit-place-search-nearby/style.css @@ -5,65 +5,70 @@ */ /* [START maps_ui_kit_place_search_nearby] */ html, - body { - height: 100%; - margin: 0; - } +body { + height: 100%; + margin: 0; +} - body { - display: flex; - flex-direction: column; - font-family: Arial, Helvetica, sans-serif; - } +body { + display: flex; + flex-direction: column; + font-family: Arial, Helvetica, sans-serif; +} - h1 { - font-size: large; - text-align: center; - } +h1 { + font-size: large; + text-align: center; +} - gmp-map { - box-sizing: border-box; - height: 600px; - } +.container { + display: flex; + height: 100vh; + width: 100%; +} - .overlay { - position: relative; - top: 40px; - margin: 20px; - width: 400px; - } +gmp-map { + flex-grow: 1; +} - .controls { - display: flex; - gap: 10px; - margin-bottom: 10px; - height: 32px; - } +.ui-panel { + width: 400px; + margin-left: 20px; + overflow-y: auto; +} - .search-button { - background-color: #5491f5; - color: #fff; - border: 1px solid #ccc; - border-radius: 5px; - width: 100px; - cursor: pointer; - } +.controls { + margin-bottom: 20px; +} - .type-select { - border: 1px solid #ccc; - border-radius: 5px; - flex-grow: 1; - padding: 0 10px; - } +.list-container { + display: flex; + flex-direction: column; +} - .list-container { - height: 400px; - overflow: auto; - border-radius: 10px; - } +.type-select { + width: 100%; + height: 32px; + border: 1px solid #000; + border-radius: 10px; + flex-grow: 1; + padding: 0 10px; +} + +gmp-place-search { + width: 100%; + margin: 0; + border-radius: 10px; + border: none; + visibility: hidden; + color-scheme: light; +} + +gmp-place-details-compact { + max-height: 300px; + border: none; + visibility: hidden; + color-scheme: light; +} - gmp-place-list { - background-color: #fff; - font-size: large - } /* [END maps_ui_kit_place_search_nearby] */ \ No newline at end of file