diff --git a/samples/control-replacement/README.md b/samples/control-replacement/README.md new file mode 100644 index 00000000..683e91e3 --- /dev/null +++ b/samples/control-replacement/README.md @@ -0,0 +1,41 @@ +# Google Maps JavaScript Sample + +## control-replacement + +This sample demonstrates replacing default map controls with custom controls. + +## Setup + +### Before starting run: + +`npm i` + +### Run an example on a local web server + +`cd samples/control-replacement` +`npm start` + +### Build an individual example + +`cd samples/control-replacement` +`npm run build` + +From 'samples': + +`npm run build --workspace=control-replacement/` + +### Build all of the examples. + +From 'samples': + +`npm run build-all` + +### Run lint to check for problems + +`cd samples/control-replacement` +`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/control-replacement/index.html b/samples/control-replacement/index.html new file mode 100644 index 00000000..c79c4cb6 --- /dev/null +++ b/samples/control-replacement/index.html @@ -0,0 +1,51 @@ + + + + + + Replacing Default Controls + + + + + + + + + +
+ + +
+
+ + +
+
+ +
+
+ + + diff --git a/samples/control-replacement/index.ts b/samples/control-replacement/index.ts new file mode 100644 index 00000000..3eb6178c --- /dev/null +++ b/samples/control-replacement/index.ts @@ -0,0 +1,98 @@ +/** + * @license + * Copyright 2026 Google LLC. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +// [START maps_control_replacement] +const mapElement = document.querySelector('gmp-map') as google.maps.MapElement; +let innerMap: google.maps.Map; + +async function initMap() { + // Load the needed libraries. + await google.maps.importLibrary('maps'); + + innerMap = mapElement.innerMap; + + // Disable the default controls. + innerMap.setOptions({ + disableDefaultUI: true, + }); + + initZoomControl(innerMap); + initMapTypeControl(innerMap); + initFullscreenControl(innerMap); +} + +function initZoomControl(map: google.maps.Map) { + const zoomInButton = document.querySelector( + '.zoom-control-in' + ) as HTMLButtonElement; + const zoomOutButton = document.querySelector( + '.zoom-control-out' + ) as HTMLButtonElement; + + zoomInButton?.addEventListener('click', () => { + map.setZoom((map.getZoom() || 0) + 1); + }); + + zoomOutButton?.addEventListener('click', () => { + map.setZoom((map.getZoom() || 0) - 1); + }); +} + +async function initMapTypeControl(innerMap: google.maps.Map) { + const mapTypeControlDiv = document.querySelector( + '.maptype-control' + ) as HTMLElement; + const btnMap = document.querySelector( + '.maptype-control-map' + ) as HTMLButtonElement; + const btnSatellite = document.querySelector( + '.maptype-control-satellite' + ) as HTMLButtonElement; + + btnMap?.addEventListener('click', () => { + mapTypeControlDiv.classList.add('maptype-control-is-map'); + mapTypeControlDiv.classList.remove('maptype-control-is-satellite'); + innerMap.setMapTypeId('roadmap'); + }); + + btnSatellite?.addEventListener('click', () => { + mapTypeControlDiv.classList.add('maptype-control-is-satellite'); + mapTypeControlDiv.classList.remove('maptype-control-is-map'); + innerMap.setMapTypeId('hybrid'); + }); +} + +async function initFullscreenControl(innerMap: google.maps.Map) { + // Get the UI elements for the fullscreen control. + const btnFullscreen = document.querySelector( + '#fullscreen-button' + ) as HTMLButtonElement; + + btnFullscreen.addEventListener('click', () => { + toggleFullScreen(mapElement); + }); +} + +async function toggleFullScreen(element: google.maps.MapElement) { + const fullScreenIcon = document.querySelector( + '#fullscreen-button .material-icons' + ) as HTMLElement; + + try { + if (!document.fullscreenElement) { + element.requestFullscreen(); + fullScreenIcon.innerText = 'fullscreen_exit'; + } else { + document.exitFullscreen(); + fullScreenIcon.innerText = 'fullscreen'; + } + } catch (error) { + console.error('Error toggling fullscreen:', error); + } +} + +initMap(); +// [END maps_control_replacement] diff --git a/samples/control-replacement/package.json b/samples/control-replacement/package.json new file mode 100644 index 00000000..9bf3e807 --- /dev/null +++ b/samples/control-replacement/package.json @@ -0,0 +1,14 @@ +{ + "name": "@js-api-samples/control-replacement", + "version": "1.0.0", + "scripts": { + "build": "tsc && bash ../jsfiddle.sh control-replacement && bash ../app.sh control-replacement && bash ../docs.sh control-replacement && npm run build:vite --workspace=. && bash ../dist.sh control-replacement", + "test": "tsc && npm run build:vite --workspace=.", + "start": "tsc && vite build --base './' && vite", + "build:vite": "vite build --base './'", + "preview": "vite preview" + }, + "dependencies": { + + } +} diff --git a/samples/control-replacement/style.css b/samples/control-replacement/style.css new file mode 100644 index 00000000..249dce5c --- /dev/null +++ b/samples/control-replacement/style.css @@ -0,0 +1,102 @@ +/** + * @license + * Copyright 2026 Google LLC. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +/* [START maps_control_replacement] */ + +/* Optional: Makes the sample page fill the window. */ +html, +body { + height: 100%; + margin: 0; + padding: 0; +} + +.controls { + background-color: white; + box-shadow: rgba(0, 0, 0, 0.3) 0px 1px 4px -1px; + box-sizing: border-box; + border-radius: 2px; + display: flex; + user-select: none; + background-clip: padding-box; + overflow: hidden; /* Keeps button backgrounds inside the rounded corners */ +} + +.controls button { + border: 0; + background-color: white; + color: rgba(0, 0, 0, 0.6); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + padding: 0; + transition: color 0.2s ease; +} + +.controls button:hover { + color: rgba(0, 0, 0, 0.9); +} + +.controls.zoom-control { + flex-direction: column; + width: 40px; +} + +.controls.zoom-control button { + height: 40px; + width: 40px; + font-size: 24px; +} + +.controls.maptype-control { + flex-direction: row; + height: 40px; +} + +.controls.maptype-control button { + padding: 0 12px; + font-size: 14px; + font-family: Roboto, Arial, sans-serif; + text-transform: uppercase; + height: 100%; +} + +.controls.maptype-control.maptype-control-is-map .maptype-control-map { + font-weight: 700; +} + +.controls.maptype-control.maptype-control-is-satellite + .maptype-control-satellite { + font-weight: 700; +} + +.controls.fullscreen-control { + width: 40px; + height: 40px; +} + +.controls.fullscreen-control button { + background: none; + border: none; + padding: 0; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + width: 100%; + height: 100%; +} + +.controls.fullscreen-control { + width: 40px; + height: 40px; +} + +#fullscreen-button .material-icons { + font-size: 28px; +} + +/* [END maps_control_replacement] */ diff --git a/samples/control-replacement/tsconfig.json b/samples/control-replacement/tsconfig.json new file mode 100644 index 00000000..139208da --- /dev/null +++ b/samples/control-replacement/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ES2022", + "lib": ["ES2022", "DOM"], + "strict": true, + "moduleResolution": "node", + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "types": ["google.maps"] + }, + "include": ["./*.ts", "./types/*.d.ts"] +} \ No newline at end of file