From 001c5c8438a3e74574e1d37dd5d9a27652b6d387 Mon Sep 17 00:00:00 2001 From: EJ Haselden Date: Tue, 7 Oct 2025 05:42:49 +0000 Subject: [PATCH] Update Places UI Kit Nearby Search demo --- samples/ui-kit-place-search-nearby/index.html | 68 +++-- samples/ui-kit-place-search-nearby/index.ts | 267 ++++++++++-------- samples/ui-kit-place-search-nearby/style.css | 131 +++++---- 3 files changed, 271 insertions(+), 195 deletions(-) diff --git a/samples/ui-kit-place-search-nearby/index.html b/samples/ui-kit-place-search-nearby/index.html index 13dbe4db..6491672d 100644 --- a/samples/ui-kit-place-search-nearby/index.html +++ b/samples/ui-kit-place-search-nearby/index.html @@ -7,39 +7,55 @@ - Place List Nearby Search with Google Maps - - + Place Search Nearby with Google Maps + + + - -
-
- -
-
- -
-
-
+ +
+ +
+
+ + + + + +
- - - - +
+ + + + + + + + + + + +
- diff --git a/samples/ui-kit-place-search-nearby/index.ts b/samples/ui-kit-place-search-nearby/index.ts index 2af79865..55c2c640 100644 --- a/samples/ui-kit-place-search-nearby/index.ts +++ b/samples/ui-kit-place-search-nearby/index.ts @@ -6,132 +6,165 @@ /* [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; +const map = document.querySelector('gmp-map') as google.maps.MapElement; +const placeSearch = document.querySelector('gmp-place-search') as HTMLElement; +const placeSearchQuery = document.querySelector( + 'gmp-place-nearby-search-request', +) as HTMLElement; +const placeDetails = document.querySelector( + 'gmp-place-details-compact', +) as HTMLElement; +const placeRequest = document.querySelector( + 'gmp-place-details-place-request', +) as HTMLElement; +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 }; +let gMap; +let placeDetailsPopup; +let spherical; +let AdvancedMarkerElement; +let LatLngBounds; + +async function init(): Promise { + const geometry = (await google.maps.importLibrary( + 'geometry', + )) as google.maps.GeometryLibrary; + await google.maps.importLibrary('maps'); + await google.maps.importLibrary('places'); + const marker = (await google.maps.importLibrary( + 'marker', + )) as google.maps.MarkerLibrary; + const core = (await google.maps.importLibrary( + 'core', + )) as google.maps.CoreLibrary; + + spherical = geometry.spherical; + AdvancedMarkerElement = marker.AdvancedMarkerElement; + LatLngBounds = core.LatLngBounds; + + gMap = map.innerMap; + + placeDetailsPopup = new AdvancedMarkerElement({ + map: null, + content: placeDetails, + zIndex: 100, + }); + + findCurrentLocation(); + + gMap.addListener('click', () => { + hidePlaceDetailsPopup(); + }); + /* [START maps_ui_kit_place_search_nearby_event] */ + typeSelect.addEventListener('change', (event) => { + event.preventDefault(); + searchPlaces(); + }); + + (placeSearch as any).addEventListener('gmp-select', ({place}) => { + if (markers[place.id]) { + (markers[place.id] as any).click(); } + }); +} +/* [END maps_ui_kit_place_search_nearby_event] */ +function searchPlaces() { + const bounds = gMap.getBounds(); + const cent = gMap.getCenter(); + const ne = bounds.getNorthEast(); + const sw = bounds.getSouthWest(); + const diameter = spherical.computeDistanceBetween(ne, sw); + const cappedRadius = Math.min(diameter / 2, 50000); // Radius cannot be more than 50000. + + placeDetailsPopup.map = null; + + for (const markerId in markers) { + if (Object.prototype.hasOwnProperty.call(markers, markerId)) { + markers[markerId].map = null; + } + } + markers = {}; + if (typeSelect.value) { + map.style.height = '75vh'; + placeSearch.style.visibility = 'visible'; + placeSearch.style.opacity = '1'; + (placeSearchQuery as any).maxResultCount = 10; + (placeSearchQuery as any).locationRestriction = { + center: {lat: cent.lat(), lng: cent.lng()}, + radius: cappedRadius, + }; + (placeSearchQuery as any).includedTypes = [typeSelect.value]; + placeSearch.addEventListener('gmp-load', addMarkers, {once: true}); + } +} - findCurrentLocation(); - - map.innerMap.setOptions({ - mapTypeControl: false, - clickableIcons: false, - }); - - /* [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; - } - 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(); - }); - } +async function addMarkers() { + const bounds = new LatLngBounds(); + placeSearch.style.visibility = 'visible'; + placeSearch.style.opacity = '1'; + + if ((placeSearch as any).places.length > 0) { + (placeSearch as any).places.forEach((place) => { + const marker = new AdvancedMarkerElement({ + map: gMap, + position: place.location, + collisionBehavior: + google.maps.CollisionBehavior.REQUIRED_AND_HIDES_OPTIONAL, + }); + + (marker as any).metadata = {id: place.id}; + markers[place.id] = marker; + bounds.extend(place.location); + + /* [START maps_ui_kit_place_search_nearby_click_event] */ + marker.addListener('click', () => { + (placeRequest as any).place = place; + placeDetails.style.visibility = 'visible'; + placeDetails.style.opacity = '1'; + + placeDetailsPopup.position = place.location; + placeDetailsPopup.map = gMap; + + gMap.fitBounds(place.viewport, {top: 0, left: 400}); + }); + gMap.setCenter(bounds.getCenter()); + gMap.fitBounds(bounds); + /* [END maps_ui_kit_place_search_nearby_click_event] */ }); - /* [END maps_ui_kit_place_search_nearby_event] */ + } } -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(); - - if(placeList.places.length > 0){ - placeList.places.forEach((place) => { - let marker = new AdvancedMarkerElement({ - map: map.innerMap, - position: place.location - }); - - markers[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(); - } - - placeDetailsRequest.place = place.id; - placeDetails.style.display = 'block'; - placeDetails.style.width = '350px'; - infoWindow.setOptions({ - content: placeDetails, - }); - infoWindow.open({ - anchor: marker, - map: map.innerMap - }); - }); - /* [END maps_ui_kit_place_search_nearby_click_event] */ - - map.innerMap.setCenter(bounds.getCenter()); - map.innerMap.fitBounds(bounds); - }); - } +async function findCurrentLocation() { + if (navigator.geolocation) { + navigator.geolocation.getCurrentPosition( + (position) => { + const pos = { + lat: position.coords.latitude, + lng: position.coords.longitude, + }; + gMap.panTo(pos); + gMap.setZoom(16); + }, + () => { + console.log('The Geolocation service failed.'); + gMap.setZoom(16); + }, + ); + } else { + console.log("Your browser doesn't support geolocation"); + gMap.setZoom(16); + } } -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); - } - +function hidePlaceDetailsPopup() { + if (placeDetailsPopup.map) { + placeDetailsPopup.map = null; + placeDetails.style.visibility = 'hidden'; + placeDetails.style.opacity = '0'; + } } -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..a4e49cb1 100644 --- a/samples/ui-kit-place-search-nearby/style.css +++ b/samples/ui-kit-place-search-nearby/style.css @@ -5,65 +5,92 @@ */ /* [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; - } +#map-container { + flex-grow: 1; + max-height: 600px; + box-sizing: border-box; + width: 100%; + height: 100vh; +} - .overlay { - position: relative; - top: 40px; - margin: 20px; - width: 400px; - } +.controls { + position: absolute; + top: 40px; + right: 40px; +} - .controls { - display: flex; - gap: 10px; - margin-bottom: 10px; - height: 32px; - } +.list-container { + display: flex; + position: absolute; + max-height: 500px; + top: 80px; + right: 40px; + overflow-y: hidden; +} - .search-button { - background-color: #5491f5; - color: #fff; - border: 1px solid #ccc; - border-radius: 5px; - width: 100px; - cursor: pointer; - } +.type-select { + width: 400px; + height: 32px; + border: 1px solid #000; + border-radius: 10px; + flex-grow: 1; + padding: 0 10px; +} - .type-select { - border: 1px solid #ccc; - border-radius: 5px; - flex-grow: 1; - padding: 0 10px; - } +gmp-place-search { + width: 400px; + margin: 0; + border-radius: 10px; + border: none; + visibility: hidden; + opacity: 0; + transition: opacity 0.3s ease-in-out, visibility 0.3s ease-in-out; +} - .list-container { - height: 400px; - overflow: auto; - border-radius: 10px; - } +gmp-place-details-compact { + width: 350px; + max-height: 300px; + margin-right: 20px; + border: none; + visibility: hidden; + opacity: 0; +} + +gmp-place-details-compact::after { + content: ''; + position: absolute; + bottom: -18px; + left: 50%; + transform: translateX(-50%); + width: 20px; + height: 20px; + background-color: white; + box-shadow: 2px 2px 5px 0px rgba(0, 0, 0, 0.2); + z-index: 1; + clip-path: polygon(0% 0%, 100% 0%, 50% 100%); + transform-origin: center center; +} - gmp-place-list { - background-color: #fff; - font-size: large +@media (prefers-color-scheme: dark) { + /* Style for Dark mode */ + gmp-place-details-compact::after { + background-color: #131314; } -/* [END maps_ui_kit_place_search_nearby] */ \ No newline at end of file +} +/* [END maps_ui_kit_place_search_nearby] */