diff --git a/package-lock.json b/package-lock.json index 1b7dcf9aa..f9e9fdf46 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1040,6 +1040,26 @@ "resolved": "samples/test-example", "link": true }, + "node_modules/@js-api-samples/ui-kit-advanced-place-details": { + "resolved": "samples/ui-kit-advanced-place-details", + "link": true + }, + "node_modules/@js-api-samples/ui-kit-advanced-place-details-compact": { + "resolved": "samples/ui-kit-advanced-place-details-compact", + "link": true + }, + "node_modules/@js-api-samples/ui-kit-advanced-place-list": { + "resolved": "samples/ui-kit-advanced-place-list", + "link": true + }, + "node_modules/@js-api-samples/ui-kit-advanced-place-search-nearby-nearby": { + "resolved": "samples/ui-kit-advanced-place-search-nearby", + "link": true + }, + "node_modules/@js-api-samples/ui-kit-advanced-place-search-text": { + "resolved": "samples/ui-kit-advanced-place-search-text", + "link": true + }, "node_modules/@js-api-samples/ui-kit-place-details": { "resolved": "samples/ui-kit-place-details", "link": true @@ -6312,6 +6332,26 @@ "name": "@js-api-samples/test-example", "version": "1.0.0" }, + "samples/ui-kit-advanced-place-details": { + "name": "@js-api-samples/ui-kit-advanced-place-details", + "version": "1.0.0" + }, + "samples/ui-kit-advanced-place-details-compact": { + "name": "@js-api-samples/ui-kit-advanced-place-details-compact", + "version": "1.0.0" + }, + "samples/ui-kit-advanced-place-list": { + "name": "@js-api-samples/ui-kit-advanced-place-list", + "version": "1.0.0" + }, + "samples/ui-kit-advanced-place-search-nearby": { + "name": "@js-api-samples/ui-kit-advanced-place-search-nearby-nearby", + "version": "1.0.0" + }, + "samples/ui-kit-advanced-place-search-text": { + "name": "@js-api-samples/ui-kit-advanced-place-search-text", + "version": "1.0.0" + }, "samples/ui-kit-place-details": { "name": "@js-api-samples/ui-kit-place-details", "version": "1.0.0" @@ -6320,6 +6360,16 @@ "name": "@js-api-samples/ui-kit-place-details-compact", "version": "1.0.0" }, + "samples/ui-kit-place-details-demo": { + "name": "@js-api-samples/ui-kit-place-details-demo", + "version": "1.0.0", + "extraneous": true, + "devDependencies": { + "@types/google.maps": "^3.58.1", + "typescript": "^5.4.5", + "vite": "^5.2.11" + } + }, "samples/ui-kit-place-search-nearby": { "name": "@js-api-samples/ui-kit-place-search-nearby-nearby", "version": "1.0.0" diff --git a/samples/ui-kit-advanced-place-details-compact/README.md b/samples/ui-kit-advanced-place-details-compact/README.md new file mode 100644 index 000000000..7f54cf7e6 --- /dev/null +++ b/samples/ui-kit-advanced-place-details-compact/README.md @@ -0,0 +1,35 @@ +# Google Maps JavaScript Sample + +## ui-kit-advanced-place-details-compact + +The ui-kit-advanced-place-details-compact sample demonstrates how to use the UI Kit for Place Details in a compact layout. + +Follow these instructions to set up and run ui-kit-advanced-place-details-compact sample on your local computer. + +## Setup + +### Before starting run: + +`$npm i` + +### Run an example on a local web server + +First `cd` to the folder for the sample to run, then: + +`$npm start` + +### Build an individual example + +From `samples/`: + +`$npm run build --workspace=ui-kit-advanced-place-details-compact/` + +### Build all of the examples. + +From `samples/`: +`$npm run build-all` + +## Feedback + +For feedback related to this sample, please open a new issue on +[GitHub](https://github.com/googlemaps-samples/js-api-samples/issues). diff --git a/samples/ui-kit-advanced-place-details-compact/index.html b/samples/ui-kit-advanced-place-details-compact/index.html new file mode 100644 index 000000000..9479b2dab --- /dev/null +++ b/samples/ui-kit-advanced-place-details-compact/index.html @@ -0,0 +1,66 @@ + + + + + + Place Details Compact with Google Maps + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/samples/ui-kit-advanced-place-details-compact/index.ts b/samples/ui-kit-advanced-place-details-compact/index.ts new file mode 100644 index 000000000..4aa756526 --- /dev/null +++ b/samples/ui-kit-advanced-place-details-compact/index.ts @@ -0,0 +1,84 @@ +/* + * @license + * Copyright 2025 Google LLC. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +/* [START maps_ui_kit_advanced_place_details_compact] */ +// Use querySelector to select elements for interaction. +/* [START maps_ui_kit_advanced_place_details_compact_query_selector] */ +const map = document.querySelector('gmp-map')!; +const placeDetails = document.querySelector< + HTMLElement & { place?: google.maps.places.Place } +>('gmp-advanced-place-details-compact')!; +const placeDetailsRequest = document.querySelector( + 'gmp-place-details-place-request' +)!; +const marker = document.querySelector( + 'gmp-advanced-marker' +)!; +/* [END maps_ui_kit_advanced_place_details_compact_query_selector] */ +async function init(): Promise { + // Request needed libraries. + void Promise.all([ + google.maps.importLibrary('marker'), + google.maps.importLibrary('places'), + ]); + const { InfoWindow } = await google.maps.importLibrary('maps'); + + await window.customElements.whenDefined('gmp-map'); + // Set the inner map options. + map.innerMap.setOptions({ + mapTypeControl: false, + streetViewControl: false, + }); + + await window.customElements.whenDefined('gmp-advanced-marker'); + marker.collisionBehavior = 'REQUIRED_AND_HIDES_OPTIONAL'; + + const infoWindow = new InfoWindow(); + infoWindow.addListener('close', () => { + marker.position = null; + }); + + const showInfoWindow = () => { + if (infoWindow.isOpen) return; + infoWindow.setContent(placeDetails); + infoWindow.open({ anchor: marker }); + }; + + placeDetails.addEventListener('gmp-load', () => { + // For the initial load case, with no user click, we fall back to the place's location, and ensure the map has a center set and the InfoWindow is show. + // (The clicked POI LatLng will be a more natural marker position, when available.) + if (!map.center && placeDetails.place?.location) { + map.center = marker.position = placeDetails.place.location; + showInfoWindow(); + } + }); + + /* [START maps_ui_kit_advanced_place_details_compact_event] */ + // Add an event listener to handle clicks. + map.innerMap.addListener( + 'click', + (event: google.maps.MapMouseEvent | google.maps.IconMouseEvent) => { + event.stop(); + + if ('placeId' in event && event.placeId) { + // When the user clicks a POI. + marker.position = event.latLng; + placeDetailsRequest.setAttribute( + 'place', + `places/${event.placeId}` + ); + showInfoWindow(); + } else { + // When the user clicks the map (not on a POI). + marker.position = null; + placeDetailsRequest.removeAttribute('place'); + console.log('No place was selected.'); + } + } + ); +} +/* [END maps_ui_kit_advanced_place_details_compact_event] */ +void init(); +/* [END maps_ui_kit_advanced_place_details_compact] */ diff --git a/samples/ui-kit-advanced-place-details-compact/package.json b/samples/ui-kit-advanced-place-details-compact/package.json new file mode 100644 index 000000000..03ea2782f --- /dev/null +++ b/samples/ui-kit-advanced-place-details-compact/package.json @@ -0,0 +1,12 @@ +{ + "name": "@js-api-samples/ui-kit-advanced-place-details-compact", + "version": "1.0.0", + "scripts": { + "build": "bash ../build-single.sh", + "test": "tsc && npm run build:vite --workspace=.", + "start": "tsc && vite build --config ../../vite.config.js --base './' && vite --config ../../vite.config.js", + "build:vite": "vite build --config ../../vite.config.js --base './'", + "preview": "vite preview --config ../../vite.config.js" + }, + "author": "Google LLC" +} diff --git a/samples/ui-kit-advanced-place-details-compact/style.css b/samples/ui-kit-advanced-place-details-compact/style.css new file mode 100644 index 000000000..1dbd2d4d4 --- /dev/null +++ b/samples/ui-kit-advanced-place-details-compact/style.css @@ -0,0 +1,26 @@ +/* + * @license + * Copyright 2026 Google LLC. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +/* [START maps_ui_kit_place_details_compact] */ +/* + * Optional: Makes the sample page fill the window. + */ +html, +body { + height: 100%; + margin: 0; + padding: 0; +} + +.container { + display: flex; + height: 100vh; + width: 100%; +} + +gmp-map { + flex-grow: 1; +} +/* [END maps_ui_kit_place_details_compact] */ diff --git a/samples/ui-kit-advanced-place-details-compact/tsconfig.json b/samples/ui-kit-advanced-place-details-compact/tsconfig.json new file mode 100644 index 000000000..976bcc6ef --- /dev/null +++ b/samples/ui-kit-advanced-place-details-compact/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "." + }, + "include": ["./*.ts"] +} diff --git a/samples/ui-kit-advanced-place-details/README.md b/samples/ui-kit-advanced-place-details/README.md new file mode 100644 index 000000000..1d24e870c --- /dev/null +++ b/samples/ui-kit-advanced-place-details/README.md @@ -0,0 +1,35 @@ +# Google Maps JavaScript Sample + +## ui-kit-advanced-place-details + +The ui-kit-advanced-place-details sample demonstrates how to use the UI Kit Place Details element. + +Follow these instructions to set up and run ui-kit-advanced-place-details sample on your local computer. + +## Setup + +### Before starting run: + +`$npm i` + +### Run an example on a local web server + +First `cd` to the folder for the sample to run, then: + +`$npm start` + +### Build an individual example + +From `samples/`: + +`$npm run build --workspace=ui-kit-advanced-place-details/` + +### Build all of the examples. + +From `samples/`: +`$npm run build-all` + +## Feedback + +For feedback related to this sample, please open a new issue on +[GitHub](https://github.com/googlemaps-samples/js-api-samples/issues). diff --git a/samples/ui-kit-advanced-place-details/index.html b/samples/ui-kit-advanced-place-details/index.html new file mode 100644 index 000000000..3559d8232 --- /dev/null +++ b/samples/ui-kit-advanced-place-details/index.html @@ -0,0 +1,54 @@ + + + + + + Place Details with Google Maps + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + +
+
+ + + + diff --git a/samples/ui-kit-advanced-place-details/index.ts b/samples/ui-kit-advanced-place-details/index.ts new file mode 100644 index 000000000..c1491069c --- /dev/null +++ b/samples/ui-kit-advanced-place-details/index.ts @@ -0,0 +1,74 @@ +/* + * @license + * Copyright 2026 Google LLC. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +/* [START maps_ui_kit_advanced_place_details] */ + +// Use querySelector to select elements for interaction. +/* [START maps_ui_kit_advanced_place_details_query_selector] */ +const map = document.querySelector('gmp-map')!; +const placeDetails = document.querySelector< + HTMLElement & { place?: google.maps.places.Place } +>('gmp-advanced-place-details')!; +const placeDetailsRequest = document.querySelector( + 'gmp-place-details-place-request' +)!; +const marker = document.querySelector( + 'gmp-advanced-marker' +)!; +/* [END maps_ui_kit_advanced_place_details_query_selector] */ + +async function init(): Promise { + // Request needed libraries. + await Promise.all([ + google.maps.importLibrary('maps'), + google.maps.importLibrary('marker'), + google.maps.importLibrary('places'), + ]); + + // Hide the map type control. + map.innerMap.setOptions({ mapTypeControl: false }); + + // Function to update map and marker based on place details + const updateMapAndMarker = () => { + if (placeDetails.place?.location) { + map.innerMap.panTo(placeDetails.place.location); + map.innerMap.setZoom(16); // Set zoom after panning if needed + marker.position = placeDetails.place.location; + marker.collisionBehavior = 'REQUIRED_AND_HIDES_OPTIONAL'; + marker.style.display = 'block'; + } + }; + + // Set up map once widget is loaded. + placeDetails.addEventListener('gmp-load', () => { + updateMapAndMarker(); + }); + + /* [START maps_ui_kit_advanced_place_details_event] */ + // Add an event listener to handle clicks. + map.innerMap.addListener( + 'click', + (event: google.maps.MapMouseEvent | google.maps.IconMouseEvent) => { + marker.position = null; + event.stop(); + if ('placeId' in event && event.placeId) { + // Fire when the user clicks a POI. + placeDetailsRequest.setAttribute( + 'place', + `places/${event.placeId}` + ); + updateMapAndMarker(); + } else { + // Fire when the user clicks the map (not on a POI). + console.log('No place was selected.'); + marker.style.display = 'none'; + } + } + ); +} +/* [END maps_ui_kit_advanced_place_details_event] */ + +void init(); +/* [END maps_ui_kit_advanced_place_details] */ diff --git a/samples/ui-kit-advanced-place-details/package.json b/samples/ui-kit-advanced-place-details/package.json new file mode 100644 index 000000000..669725cc9 --- /dev/null +++ b/samples/ui-kit-advanced-place-details/package.json @@ -0,0 +1,12 @@ +{ + "name": "@js-api-samples/ui-kit-advanced-place-details", + "version": "1.0.0", + "scripts": { + "build": "bash ../build-single.sh", + "test": "tsc && npm run build:vite --workspace=.", + "start": "tsc && vite build --config ../../vite.config.js --base './' && vite --config ../../vite.config.js", + "build:vite": "vite build --config ../../vite.config.js --base './'", + "preview": "vite preview --config ../../vite.config.js" + }, + "author": "Google LLC" +} diff --git a/samples/ui-kit-advanced-place-details/style.css b/samples/ui-kit-advanced-place-details/style.css new file mode 100644 index 000000000..e25ed8162 --- /dev/null +++ b/samples/ui-kit-advanced-place-details/style.css @@ -0,0 +1,38 @@ +/* + * @license + * Copyright 2026 Google LLC. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +/* [START maps_ui_kit_place_details] */ +/* + * Optional: Makes the sample page fill the window. + */ +html, +body { + height: 100%; + margin: 0; + padding: 0; +} + +.container { + display: flex; + height: 100vh; + width: 100%; +} + +gmp-map { + flex-grow: 1; +} + +.ui-panel { + width: 400px; + margin-left: 20px; + margin-top: 10px; +} + +gmp-place-details { + width: 100%; + margin: 0; + border: none; +} +/* [END maps_ui_kit_place_details] */ diff --git a/samples/ui-kit-advanced-place-details/tsconfig.json b/samples/ui-kit-advanced-place-details/tsconfig.json new file mode 100644 index 000000000..976bcc6ef --- /dev/null +++ b/samples/ui-kit-advanced-place-details/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "." + }, + "include": ["./*.ts"] +} diff --git a/samples/ui-kit-advanced-place-list/README.md b/samples/ui-kit-advanced-place-list/README.md new file mode 100644 index 000000000..5b22c7870 --- /dev/null +++ b/samples/ui-kit-advanced-place-list/README.md @@ -0,0 +1,41 @@ +# Google Maps JavaScript Sample + +## ui-kit-advanced-place-list + +Display a list of nearby places using advanced place elements. + +## Setup + +### Before starting run: + +`npm i` + +### Run an example on a local web server + +`cd samples/ui-kit-advanced-place-list` +`npm start` + +### Build an individual example + +`cd samples/ui-kit-advanced-place-list` +`npm run build` + +From 'samples': + +`npm run build --workspace=ui-kit-advanced-place-list/` + +### Build all of the examples. + +From 'samples': + +`npm run build-all` + +### Run lint to check for problems + +`cd samples/ui-kit-advanced-place-list` +`npx eslint index.ts` + +## Feedback + +For feedback related to this sample, please open a new issue on +[GitHub](https://github.com/googlemaps-samples/js-api-samples/issues). diff --git a/samples/ui-kit-advanced-place-list/index.html b/samples/ui-kit-advanced-place-list/index.html new file mode 100644 index 000000000..a58e4ac1d --- /dev/null +++ b/samples/ui-kit-advanced-place-list/index.html @@ -0,0 +1,34 @@ + + + + + + Advanced Place List + + + + + + + + + + + + + diff --git a/samples/ui-kit-advanced-place-list/index.ts b/samples/ui-kit-advanced-place-list/index.ts new file mode 100644 index 000000000..9b5448aa1 --- /dev/null +++ b/samples/ui-kit-advanced-place-list/index.ts @@ -0,0 +1,25 @@ +/** + * @license + * Copyright 2026 Google LLC. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/* [START maps_ui_kit_advanced_place_list] */ +async function init() { + await google.maps.importLibrary('places'); + await customElements.whenDefined('gmp-advanced-place-list'); + const listElement = document.querySelector('gmp-advanced-place-list'); + + if (listElement) { + listElement.addEventListener('gmp-error', (e: Event) => { + const customEvent = e as CustomEvent<{ errors?: unknown }>; + console.error( + 'Failed to load places: ', + customEvent.detail.errors ?? customEvent + ); + }); + } +} + +void init(); +/* [END maps_ui_kit_advanced_place_list] */ diff --git a/samples/ui-kit-advanced-place-list/package.json b/samples/ui-kit-advanced-place-list/package.json new file mode 100644 index 000000000..f44f30217 --- /dev/null +++ b/samples/ui-kit-advanced-place-list/package.json @@ -0,0 +1,12 @@ +{ + "name": "@js-api-samples/ui-kit-advanced-place-list", + "version": "1.0.0", + "scripts": { + "build": "bash ../build-single.sh", + "test": "tsc && npm run build:vite --workspace=.", + "start": "tsc && vite build --config ../../vite.config.js --base './' && vite --config ../../vite.config.js", + "build:vite": "vite build --config ../../vite.config.js --base './'", + "preview": "vite preview --config ../../vite.config.js" + }, + "author": "Google LLC" +} diff --git a/samples/ui-kit-advanced-place-list/style.css b/samples/ui-kit-advanced-place-list/style.css new file mode 100644 index 000000000..4947df625 --- /dev/null +++ b/samples/ui-kit-advanced-place-list/style.css @@ -0,0 +1,14 @@ +/** + * @license + * Copyright 2026 Google LLC. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +/* [START maps_ui_kit_advanced_place_list] */ +html, +body { + height: 100%; + margin: 0; + padding: 0; + font-family: Roboto, Arial, sans-serif; +} +/* [END maps_ui_kit_advanced_place_list] */ diff --git a/samples/ui-kit-advanced-place-list/tsconfig.json b/samples/ui-kit-advanced-place-list/tsconfig.json new file mode 100644 index 000000000..976bcc6ef --- /dev/null +++ b/samples/ui-kit-advanced-place-list/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "." + }, + "include": ["./*.ts"] +} diff --git a/samples/ui-kit-advanced-place-search-nearby/README.md b/samples/ui-kit-advanced-place-search-nearby/README.md new file mode 100644 index 000000000..5d1a561f9 --- /dev/null +++ b/samples/ui-kit-advanced-place-search-nearby/README.md @@ -0,0 +1,35 @@ +# Google Maps JavaScript Sample + +## ui-kit-advanced-place-search-nearby + +The ui-kit-advanced-place-search-nearby sample demonstrates using the Places UI Kit PlaceSearchElement. + +Follow these instructions to set up and run ui-kit-advanced-place-search-nearby sample on your local computer. + +## Setup + +### Before starting run: + +`$npm i` + +### Run an example on a local web server + +First `cd` to the folder for the sample to run, then: + +`$npm start` + +### Build an individual example + +From `samples/`: + +`$npm run build --workspace=ui-kit-advanced-place-search-nearby/` + +### Build all of the examples. + +From `samples/`: +`$npm run build-all` + +## Feedback + +For feedback related to this sample, please open a new issue on +[GitHub](https://github.com/googlemaps-samples/js-api-samples/issues). diff --git a/samples/ui-kit-advanced-place-search-nearby/index.html b/samples/ui-kit-advanced-place-search-nearby/index.html new file mode 100644 index 000000000..affa30d0b --- /dev/null +++ b/samples/ui-kit-advanced-place-search-nearby/index.html @@ -0,0 +1,83 @@ + + + + + + Place Search Nearby with Google Maps + + + + + + + + +
+ + + +
+
+ +
+
+ + + + +
+
+
+ + + + + + + + + + + + + + + + + diff --git a/samples/ui-kit-advanced-place-search-nearby/index.ts b/samples/ui-kit-advanced-place-search-nearby/index.ts new file mode 100644 index 000000000..287295a08 --- /dev/null +++ b/samples/ui-kit-advanced-place-search-nearby/index.ts @@ -0,0 +1,134 @@ +/* + * @license + * Copyright 2026 Google LLC. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +/* [START maps_ui_kit_advanced_place_search_nearby] */ + +/* [START maps_ui_kit_advanced_place_search_nearby_query_selectors] */ +// Query selectors for various elements in the HTML file. +const map = document.querySelector('gmp-map')!; +const placeSearch = document.querySelector< + HTMLElement & { places?: google.maps.places.Place[] } +>('gmp-advanced-place-search')!; +const placeSearchQuery = document.querySelector< + HTMLElement & { + locationRestriction?: { + center: google.maps.LatLng | google.maps.LatLngLiteral; + radius: number; + }; + includedTypes?: string[]; + } +>('gmp-place-nearby-search-request')!; +const placeDetails = document.querySelector( + 'gmp-advanced-place-details-compact' +)!; +const placeRequest = document.querySelector< + HTMLElement & { place?: google.maps.places.Place } +>('gmp-place-details-place-request')!; +const typeSelect = document.querySelector('.type-select')!; +/* [END maps_ui_kit_advanced_place_search_nearby_query_selectors] */ + +// Global variables for the map, markers, and info window. +const markers = new Map(); +let infoWindow: google.maps.InfoWindow; + +// 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 Promise.all([ + google.maps.importLibrary('maps'), + google.maps.importLibrary('places'), + ]); + + // Create a new info window and set its content to the place details element. + placeDetails.remove(); // Hide the place details element because it is not needed until the info window opens + infoWindow = new InfoWindow({ + content: placeDetails, + ariaLabel: 'Place Details', + }); + + // Set the map options. + map.innerMap.setOptions({ + clickableIcons: false, + mapTypeControl: false, + streetViewControl: false, + }); + + /* [START maps_ui_kit_advanced_place_search_nearby_event] */ + // 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 Event & { place?: google.maps.places.Place }) + .place; + if (place?.id) { + markers.get(place.id)?.click(); + } + }); + placeSearch.addEventListener('gmp-load', () => { + void addMarkers(); + }); + + searchPlaces(); +} +/* [END maps_ui_kit_advanced_place_search_nearby_event] */ +/* [START maps_ui_kit_advanced_place_search_nearby_function] */ +// The searchPlaces function is called when the user changes the type select or when the page loads. +function searchPlaces() { + // Close the info window and clear the markers. + infoWindow.close(); + for (const marker of markers.values()) { + marker.remove(); + } + markers.clear(); + + // Set the place search query and add an event listener to the place search element. + if (typeSelect.value) { + const center = map.center!; + placeSearchQuery.locationRestriction = { + center, + radius: 50000, // 50km radius + }; + placeSearchQuery.includedTypes = [typeSelect.value]; + } +} +/* [END maps_ui_kit_advanced_place_search_nearby_function] */ + +// The addMarkers function is called when the place search element loads. +async function addMarkers() { + // Import the necessary libraries from the Google Maps API. + const [{ AdvancedMarkerElement }, { LatLngBounds }] = await Promise.all([ + google.maps.importLibrary('marker'), + google.maps.importLibrary('core'), + ]); + const bounds = new LatLngBounds(); + + if (!placeSearch.places || placeSearch.places.length === 0) { + return; + } + + for (const place of placeSearch.places) { + if (!place.location) continue; + const marker = new AdvancedMarkerElement({ + map: map.innerMap, + position: place.location, + collisionBehavior: 'REQUIRED_AND_HIDES_OPTIONAL', + }); + + markers.set(place.id, marker); + bounds.extend(place.location); + + marker.addListener('click', () => { + placeRequest.place = place; + infoWindow.open(map.innerMap, marker); + }); + } + + map.innerMap.fitBounds(bounds); +} + +void init(); +/* [END maps_ui_kit_advanced_place_search_nearby] */ diff --git a/samples/ui-kit-advanced-place-search-nearby/package.json b/samples/ui-kit-advanced-place-search-nearby/package.json new file mode 100644 index 000000000..9c0fa33dc --- /dev/null +++ b/samples/ui-kit-advanced-place-search-nearby/package.json @@ -0,0 +1,12 @@ +{ + "name": "@js-api-samples/ui-kit-advanced-place-search-nearby-nearby", + "version": "1.0.0", + "scripts": { + "build": "bash ../build-single.sh", + "test": "tsc && npm run build:vite --workspace=.", + "start": "tsc && vite build --config ../../vite.config.js --base './' && vite --config ../../vite.config.js", + "build:vite": "vite build --config ../../vite.config.js --base './'", + "preview": "vite preview --config ../../vite.config.js" + }, + "author": "Google LLC" +} diff --git a/samples/ui-kit-advanced-place-search-nearby/style.css b/samples/ui-kit-advanced-place-search-nearby/style.css new file mode 100644 index 000000000..9b97bfd8c --- /dev/null +++ b/samples/ui-kit-advanced-place-search-nearby/style.css @@ -0,0 +1,49 @@ +/* + * @license + * Copyright 2026 Google LLC. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +/* [START maps_ui_kit_place_search_nearby] */ +html, +body { + height: 100%; + margin: 0; +} + +body { + display: flex; + flex-direction: column; + font-family: Arial, Helvetica, sans-serif; +} + +.container { + display: flex; + height: 100vh; + width: 100%; +} + +gmp-map { + flex-grow: 1; +} + +.ui-panel { + width: 400px; + margin-left: 20px; + margin-top: 10px; + overflow-y: auto; + font-family: Arial, Helvetica, sans-serif; +} + +.list-container { + display: flex; + flex-direction: column; +} + +gmp-place-search { + width: 100%; + margin: 0; + border: none; + color-scheme: light; +} + +/* [END maps_ui_kit_place_search_nearby] */ diff --git a/samples/ui-kit-advanced-place-search-nearby/tsconfig.json b/samples/ui-kit-advanced-place-search-nearby/tsconfig.json new file mode 100644 index 000000000..976bcc6ef --- /dev/null +++ b/samples/ui-kit-advanced-place-search-nearby/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "." + }, + "include": ["./*.ts"] +} diff --git a/samples/ui-kit-advanced-place-search-text/README.md b/samples/ui-kit-advanced-place-search-text/README.md new file mode 100644 index 000000000..60ec6494e --- /dev/null +++ b/samples/ui-kit-advanced-place-search-text/README.md @@ -0,0 +1,35 @@ +# Google Maps JavaScript Sample + +## ui-kit-place-search-text + +The ui-kit-place-search-text-compact sample demonstrates performing a text search using the Places UI Kit Place Search element. + +Follow these instructions to set up and run ui-kit-place-search-text sample on your local computer. + +## Setup + +### Before starting run: + +`$npm i` + +### Run an example on a local web server + +First `cd` to the folder for the sample to run, then: + +`$npm start` + +### Build an individual example + +From `samples/`: + +`$npm run build --workspace=ui-kit-place-search-text/` + +### Build all of the examples. + +From `samples/`: +`$npm run build-all` + +## Feedback + +For feedback related to this sample, please open a new issue on +[GitHub](https://github.com/googlemaps-samples/js-api-samples/issues). diff --git a/samples/ui-kit-advanced-place-search-text/index.html b/samples/ui-kit-advanced-place-search-text/index.html new file mode 100644 index 000000000..65e731c1d --- /dev/null +++ b/samples/ui-kit-advanced-place-search-text/index.html @@ -0,0 +1,81 @@ + + + + + + Place Text Search with Google Maps + + + + + + + + +
+
+
+ + +
+
+ + + + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + diff --git a/samples/ui-kit-advanced-place-search-text/index.ts b/samples/ui-kit-advanced-place-search-text/index.ts new file mode 100644 index 000000000..1d16690fc --- /dev/null +++ b/samples/ui-kit-advanced-place-search-text/index.ts @@ -0,0 +1,141 @@ +/* + * @license + * Copyright 2026 Google LLC. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +/* [START maps_ui_kit_advanced_place_search_text] */ + +/* [START maps_ui_kit_advanced_place_search_text_query_selectors] */ +// Query selectors for various elements in the HTML file. +const map = document.querySelector('gmp-map')!; +const placeSearch = document.querySelector< + HTMLElement & { places?: google.maps.places.Place[] } +>('gmp-advanced-place-search')!; +const placeSearchQuery = document.querySelector< + HTMLElement & { + textQuery?: string; + locationBias?: google.maps.LatLng | google.maps.LatLngLiteral; + } +>('gmp-place-text-search-request')!; +const placeDetails = document.querySelector( + 'gmp-advanced-place-details-compact' +)!; +const placeRequest = document.querySelector< + HTMLElement & { place?: google.maps.places.Place } +>('gmp-place-details-place-request')!; +const queryInput = document.querySelector('.query-input')!; +const searchButton = document.querySelector('.search-button')!; +/* [END maps_ui_kit_advanced_place_search_text_query_selectors] */ + +// Global variables for the map, markers, and info window. +const markers = new Map(); +let infoWindow: google.maps.InfoWindow; + +// 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 Promise.all([ + google.maps.importLibrary('maps'), + google.maps.importLibrary('places'), + ]); + + // Create a new info window and set its content to the place details element. + placeDetails.remove(); // Hide the place details element because it is not needed until the info window opens + infoWindow = new InfoWindow({ + content: placeDetails, + ariaLabel: 'Place Details', + }); + + // Set the map options. + map.innerMap.setOptions({ + clickableIcons: false, + mapTypeControl: false, + streetViewControl: false, + }); + + /* [START maps_ui_kit_advanced_place_search_text_event] */ + // Add event listeners to the query input and place search elements. + searchButton.addEventListener('click', () => { + searchPlaces(); + }); + queryInput.addEventListener('keydown', (event: KeyboardEvent) => { + if (event.key === 'Enter') { + searchPlaces(); + } + }); + + placeSearch.addEventListener('gmp-select', (event: Event) => { + const place = (event as Event & { place?: google.maps.places.Place }) + .place; + if (place?.id) { + markers.get(place.id)?.click(); + } + }); + placeSearch.addEventListener('gmp-load', () => { + void addMarkers(); + }); + + searchPlaces(); +} +/* [END maps_ui_kit_advanced_place_search_text_event] */ +/* [START maps_ui_kit_advanced_place_search_text_function] */ +// The searchPlaces function is called when the user changes the query input or when the page loads. +function searchPlaces() { + // Close the info window and clear the markers. + infoWindow.close(); + for (const marker of markers.values()) { + marker.remove(); + } + markers.clear(); + + // Set the place search query and add an event listener to the place search element. + if (queryInput.value) { + const center = map.center; + if (center) { + placeSearchQuery.locationBias = center; + } + // The textQuery property is required for the search element to load. + // Any other configured properties will be ignored if textQuery is not set. + placeSearchQuery.textQuery = queryInput.value; + } +} +/* [END maps_ui_kit_advanced_place_search_text_function] */ + +// The addMarkers function is called when the place search element loads. +async function addMarkers() { + // Import the necessary libraries from the Google Maps API. + const [{ AdvancedMarkerElement }, { LatLngBounds }] = await Promise.all([ + google.maps.importLibrary('marker'), + google.maps.importLibrary('core'), + ]); + const bounds = new LatLngBounds(); + + if (!placeSearch.places || placeSearch.places.length === 0) { + return; + } + + for (const place of placeSearch.places) { + if (!place.location) { + continue; + } + + const marker = new AdvancedMarkerElement({ + map: map.innerMap, + position: place.location, + collisionBehavior: 'REQUIRED_AND_HIDES_OPTIONAL', + }); + + markers.set(place.id, marker); + bounds.extend(place.location); + + marker.addListener('click', () => { + placeRequest.place = place; + infoWindow.open(map.innerMap, marker); + }); + } + + map.innerMap.fitBounds(bounds); +} + +void init(); +/* [END maps_ui_kit_advanced_place_search_text] */ diff --git a/samples/ui-kit-advanced-place-search-text/package.json b/samples/ui-kit-advanced-place-search-text/package.json new file mode 100644 index 000000000..7ee778f2f --- /dev/null +++ b/samples/ui-kit-advanced-place-search-text/package.json @@ -0,0 +1,12 @@ +{ + "name": "@js-api-samples/ui-kit-advanced-place-search-text", + "version": "1.0.0", + "scripts": { + "build": "bash ../build-single.sh", + "test": "tsc && npm run build:vite --workspace=.", + "start": "tsc && vite build --config ../../vite.config.js --base './' && vite --config ../../vite.config.js", + "build:vite": "vite build --config ../../vite.config.js --base './'", + "preview": "vite preview --config ../../vite.config.js" + }, + "author": "Google LLC" +} diff --git a/samples/ui-kit-advanced-place-search-text/style.css b/samples/ui-kit-advanced-place-search-text/style.css new file mode 100644 index 000000000..d68006f19 --- /dev/null +++ b/samples/ui-kit-advanced-place-search-text/style.css @@ -0,0 +1,73 @@ +/* + * @license + * Copyright 2026 Google LLC. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +/* [START maps_ui_kit_place_search_text] */ +html, +body { + height: 100%; + margin: 0; +} + +body { + display: flex; + flex-direction: column; + font-family: Arial, Helvetica, sans-serif; +} + +.container { + display: flex; + height: 100vh; + width: 100%; +} + +gmp-map { + flex-grow: 1; +} + +.ui-panel { + width: 400px; + margin-left: 20px; + margin-right: 20px; + margin-top: 10px; + overflow-y: auto; + font-family: Arial, Helvetica, sans-serif; +} + +.list-container { + display: flex; + flex-direction: column; +} + +gmp-place-search { + width: 100%; + margin: 0; + border: none; + color-scheme: light; +} + +.query-input { + width: 100%; + padding: 8px; + margin-bottom: 10px; + box-sizing: border-box; +} + +.search-button { + width: 100%; + padding: 8px; + margin-bottom: 10px; + box-sizing: border-box; + background-color: #1a73e8; + color: white; + border: none; + cursor: pointer; +} + +.search-button:hover, +.search-button:focus-visible { + background-color: #1765cc; +} + +/* [END maps_ui_kit_place_search_text] */ diff --git a/samples/ui-kit-advanced-place-search-text/tsconfig.json b/samples/ui-kit-advanced-place-search-text/tsconfig.json new file mode 100644 index 000000000..976bcc6ef --- /dev/null +++ b/samples/ui-kit-advanced-place-search-text/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "." + }, + "include": ["./*.ts"] +}