Skip to content

Commit 202a68a

Browse files
Update dist folder [skip ci] (#1648)
1 parent b3dea48 commit 202a68a

19 files changed

Lines changed: 2182 additions & 0 deletions

File tree

‎dist/index.html‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ <h1>Maps JSAPI Samples</h1>
140140
<li><a href='/samples/rectangle-event/dist'>rectangle-event</a></li>
141141
<li><a href='/samples/rgm-autocomplete/dist'>rgm-autocomplete</a></li>
142142
<li><a href='/samples/rgm-basic-map/dist'>rgm-basic-map</a></li>
143+
<li><a href='/samples/rgm-college-picker/dist'>rgm-college-picker</a></li>
143144
<li><a href='/samples/routes-compute-routes/dist'>routes-compute-routes</a></li>
144145
<li><a href='/samples/routes-get-alternatives/dist'>routes-get-alternatives</a></li>
145146
<li><a href='/samples/routes-get-directions/dist'>routes-get-directions</a></li>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"extends": [
3+
"plugin:@typescript-eslint/recommended"
4+
],
5+
"parser": "@typescript-eslint/parser",
6+
"rules": {
7+
"@typescript-eslint/ban-ts-comment": 0,
8+
"@typescript-eslint/no-this-alias": 1,
9+
"@typescript-eslint/no-empty-function": 1,
10+
"@typescript-eslint/explicit-module-boundary-types": 1,
11+
"@typescript-eslint/no-unused-vars": 1
12+
}
13+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# Google Maps JavaScript Sample
2+
3+
## rgm-college-picker
4+
5+
This sample demonstrates using React with the vis.gl wrapper to create a college
6+
picker that shows place details and allows you to get directions to the selected
7+
college.
8+
9+
## Setup
10+
11+
### Before starting run:
12+
13+
`npm i`
14+
15+
### Run an example on a local web server
16+
17+
`cd samples/rgm-college-picker` `npm start`
18+
19+
### Build an individual example
20+
21+
`cd samples/rgm-college-picker` `npm run build`
22+
23+
From 'samples':
24+
25+
`npm run build --workspace=rgm-college-picker/`
26+
27+
### Build all of the examples.
28+
29+
From 'samples':
30+
31+
`npm run build-all`
32+
33+
### Run lint to check for problems
34+
35+
`cd samples/rgm-college-picker` `npx eslint app.tsx`
36+
37+
## Feedback
38+
39+
For feedback related to this sample, please open a new issue on
40+
[GitHub](https://github.com/googlemaps-samples/js-api-samples/issues).
41+
42+
## Integration notes
43+
44+
When integrating Google Maps Web Components (Extended Component Library) and
45+
`vis.gl/react-google-maps` within a React application, you may encounter
46+
friction between React's declarative state model and native browser APIs. This
47+
section describes the specific workarounds implemented in this sample to ensure
48+
expected behavior.
49+
50+
### 1. Use slots for web component composition
51+
52+
**The Problem:** React (prior to v19) struggles to pass the standard HTML `slot`
53+
attribute directly to nested Web Components.
54+
**The Fix:** First, we wrapped
55+
slotted elements inside a standard HTML `<div>` (e.g.,
56+
`<div className="SlotDiv" slot="main">`). To avoid breaking the parent Web
57+
Component's internal Flexbox layout, we applied `display: contents;` to the
58+
wrapper `<div>` in `style.css`. This CSS rule makes the `<div>` invisible to the
59+
browser's layout engine, allowing the child component to participate in the
60+
parent's Flexbox layout natively.
61+
62+
### 2. Size the Autocomplete Drop-down
63+
64+
**The Problem:** Ideally the `<PlacePicker>` input field (and its autocomplete
65+
drop-down) should fill the sidebar. However, because of the `display: contents`
66+
wrapper, standard Flexbox rules like `flex-grow: 1` were ignored by the layout
67+
engine, leaving a gap between the edge of the drop-down and the side of the
68+
sidebar.
69+
**The Fix:** Because Web Components default to `display: inline-block`,
70+
we bypassed Flexbox entirely and applied explicit layout constraints directly to
71+
the component host in CSS:
72+
73+
```css
74+
.CollegePicker {
75+
width: calc(100% - 2rem);
76+
box-sizing: border-box;
77+
}
78+
```
79+
80+
Because the drop-down's width relies on the input field's physical width,
81+
forcing the component to stretch automatically fixed the drop-down layout
82+
without requiring global CSS overrides on the Maps API's `.pac-container`.
83+
84+
### 3. Retain Map Pan & Zoom (Controlled Camera State)
85+
86+
**The Problem:** Passing the location directly to the map, e.g.
87+
`<Map center={college.location} />` creates a strictly "Controlled Component" in
88+
`@vis.gl/react-google-maps`. Anytime the user tries to pan the map with their
89+
mouse, React immediately snaps the camera back to the locked `college.location`
90+
coordinate, effectively breaking panning and zooming.
91+
**The Fix:** We decoupled
92+
the map's camera from the selected place by doing three things:
93+
94+
1. Created a dedicated `cameraProps` React state to track the map's current
95+
center and zoom.
96+
2. Added an `onCameraChanged` event listener to the map so it could smoothly
97+
update its own state while the user dragged it.
98+
3. Created a `useEffect` hook that explicitly intercepts a new college selection
99+
and programmatically updates the `cameraProps`. This allows the map to fly to
100+
new searches while remaining fully interactive.
101+
102+
### 4. Fix Broken Reviewer Profile Photos
103+
104+
**The Problem:** Google's secure image servers (`lh3.googleusercontent.com`)
105+
routinely block requests that send unauthorized `Referer` headers (like those
106+
sent from `localhost`). This caused the `<PlaceReviews>` component to render
107+
`alt` text (e.g., "photo of John R") instead of the reviewers' actual profile
108+
avatars. Because the `<img>` tags are inside the component's Shadow DOM, we
109+
couldn't add `referrerpolicy="no-referrer"` to them.
110+
**The Fix:** We applied a
111+
global fix by injecting a meta tag into the `<head>` of `index.html`:
112+
113+
```html
114+
<meta name="referrer" content="no-referrer" />
115+
```
116+
117+
This globally instructs the browser to stop sending the `Referer` header,
118+
allowing the profile avatars to load beautifully while bypassing the Shadow DOM
119+
restriction.
120+
121+
### 5. Fix Implicit Void Returns in React Handlers
122+
123+
**The Problem:** Using arrow function shorthands for event handlers that return
124+
Promises or `void` (e.g., `onClick={() => overlayLayoutRef.current.showOverlay()}`)
125+
causes type errors in TypeScript.
126+
**The Fix:** We explicitly wrapped these handlers in curly braces `{ ... }` and
127+
utilized the `void` operator for fire-and-forget Promises. This prevents accidental
128+
return values from being passed to React's event system.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<!doctype html>
2+
<!--
3+
@license
4+
Copyright 2026 Google LLC. All Rights Reserved.
5+
SPDX-License-Identifier: Apache-2.0
6+
-->
7+
<!-- [START maps_rgm_college_picker] -->
8+
<html lang="en">
9+
<head>
10+
<meta charset="utf-8" />
11+
<!-- Prevents reviewer profile photos from breaking due to cross-origin Referer restrictions -->
12+
<meta name="referrer" content="no-referrer" />
13+
<meta
14+
name="viewport"
15+
content="width=device-width, initial-scale=1.0, user-scalable=no" />
16+
<title>React - College Picker</title>
17+
<meta name="description" content="React College Picker Example" />
18+
<link rel="stylesheet" href="./style.css" />
19+
<script type="module">
20+
import { renderToDom } from './src/app';
21+
22+
renderToDom(document.querySelector('#app'));
23+
</script>
24+
</head>
25+
<body>
26+
<div id="app"></div>
27+
</body>
28+
</html>
29+
<!-- [END maps_rgm_college_picker] -->
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"name": "@js-api-samples/rgm-college-picker",
3+
"version": "1.0.0",
4+
"type": "module",
5+
"scripts": {
6+
"build": "bash ../build-single.sh",
7+
"test": "tsc && npm run build:vite --workspace=.",
8+
"start": "tsc && vite build --config ../../vite.config.js --base './' && vite --config ../../vite.config.js",
9+
"build:vite": "vite build --config ../../vite.config.js --base './'",
10+
"preview": "vite preview --config ../../vite.config.js"
11+
},
12+
"author": "Google LLC",
13+
"buildRevision": 1
14+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* @license
3+
* Copyright 2026 Google LLC. All Rights Reserved.
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
/* [START maps_rgm_college_picker] */
8+
body {
9+
margin: 0;
10+
font-family: sans-serif;
11+
}
12+
#app {
13+
width: 100vw;
14+
height: 100vh;
15+
}
16+
17+
:root {
18+
--gmpx-fixed-panel-width-row-layout: 450px;
19+
}
20+
21+
.App {
22+
--gmpx-color-surface: #f6f5ff;
23+
--gmpx-color-on-primary: #f8e8ff;
24+
--gmpx-color-on-surface: #000;
25+
--gmpx-color-on-surface-variant: #636268;
26+
--gmpx-color-primary: #8a5cf4;
27+
--gmpx-fixed-panel-height-column-layout: 420px;
28+
background: var(--gmpx-color-surface);
29+
inset: 0;
30+
position: fixed;
31+
}
32+
33+
.MainContainer {
34+
display: flex;
35+
flex-direction: column;
36+
}
37+
38+
.SplitLayoutContainer {
39+
height: 100%;
40+
}
41+
42+
/*
43+
* Because the parent uses display: contents, flex-grow won't work.
44+
* We explicitly set the width here to stretch the search box, which
45+
* also ensures the Maps Autocomplete dropdown matches this width.
46+
*/
47+
.CollegePicker {
48+
--gmpx-color-surface: #fff;
49+
margin: 1rem;
50+
width: calc(100% - 2rem);
51+
box-sizing: border-box;
52+
}
53+
54+
.CloseButton {
55+
display: block;
56+
margin: 1rem;
57+
}
58+
59+
.SlotDiv {
60+
display: contents;
61+
}
62+
/* [END maps_rgm_college_picker] */
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"extends": "../../tsconfig.react-base.json",
3+
"compilerOptions": {
4+
"outDir": "./dist",
5+
"rootDir": "./src"
6+
},
7+
"include": ["./src/**/*.ts*"]
8+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/**
2+
* @license
3+
* Copyright 2026 Google LLC. All Rights Reserved.
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
// [START maps_rgm_college_picker]
7+
import React, { useState, useRef, useEffect } from 'react';
8+
import { createRoot } from 'react-dom/client';
9+
import { AdvancedMarker, Map, Pin, APIProvider, } from '@vis.gl/react-google-maps';
10+
import { PlaceReviews, PlaceDataProvider, PlaceDirectionsButton, IconButton, PlaceOverview, SplitLayout, OverlayLayout, PlacePicker, } from '@googlemaps/extended-component-library/react';
11+
const API_KEY = 'GOOGLE_MAPS_API_KEY';
12+
const DEFAULT_CENTER = { lat: 38, lng: -98 };
13+
const DEFAULT_ZOOM = 4;
14+
const DEFAULT_ZOOM_WITH_LOCATION = 16;
15+
/**
16+
* Sample app that helps users locate a college on the map, with place info such
17+
* as ratings, photos, and reviews displayed on the side.
18+
*/
19+
export default function App() {
20+
const overlayLayoutRef = useRef(null);
21+
const pickerRef = useRef(null);
22+
const [college, setCollege] = useState(undefined);
23+
/**
24+
* We track the map's camera state separately and use an onCameraChanged listener.
25+
* This prevents the map from becoming strictly "controlled" by college.location,
26+
* which would otherwise lock the camera and prevent the user from panning or zooming.
27+
*/
28+
const [cameraProps, setCameraProps] = useState({
29+
center: DEFAULT_CENTER,
30+
zoom: DEFAULT_ZOOM,
31+
});
32+
useEffect(() => {
33+
if (college?.location) {
34+
setCameraProps({
35+
center: {
36+
lat: college.location.lat(),
37+
lng: college.location.lng(),
38+
},
39+
zoom: DEFAULT_ZOOM_WITH_LOCATION,
40+
});
41+
}
42+
}, [college]);
43+
/**
44+
* See https://lit.dev/docs/frameworks/react/#using-slots for why
45+
* we need to wrap our custom elements in a div with a slot attribute.
46+
*/
47+
return (React.createElement("div", { className: "App" },
48+
React.createElement(APIProvider, { solutionChannel: "GMP_devsite_samples_v3_rgmcollegepicker", apiKey: API_KEY, version: "beta" },
49+
React.createElement(SplitLayout, { rowReverse: true, rowLayoutMinWidth: 700 },
50+
React.createElement("div", { className: "SlotDiv", slot: "fixed" },
51+
React.createElement(OverlayLayout, { ref: overlayLayoutRef },
52+
React.createElement("div", { className: "SlotDiv", slot: "main" },
53+
React.createElement(PlacePicker, { className: "CollegePicker", ref: pickerRef, forMap: "gmap", country: ['us', 'ca'], type: "university", placeholder: "Enter a college in the US or Canada", onPlaceChange: () => {
54+
if (!pickerRef.current?.value) {
55+
setCollege(undefined);
56+
}
57+
else {
58+
setCollege(pickerRef.current.value);
59+
}
60+
} }),
61+
React.createElement(PlaceOverview, { size: "large", place: college, googleLogoAlreadyDisplayed: true },
62+
React.createElement("div", { slot: "action", className: "SlotDiv" },
63+
React.createElement(IconButton, { slot: "action", variant: "filled", onClick: () => {
64+
if (overlayLayoutRef.current)
65+
void overlayLayoutRef.current.showOverlay();
66+
} }, "See Reviews")),
67+
React.createElement("div", { slot: "action", className: "SlotDiv" },
68+
React.createElement(PlaceDirectionsButton, { slot: "action", variant: "filled" }, "Directions")))),
69+
React.createElement("div", { slot: "overlay", className: "SlotDiv" },
70+
React.createElement(IconButton, { className: "CloseButton", onClick: () => {
71+
if (overlayLayoutRef.current)
72+
void overlayLayoutRef.current.hideOverlay();
73+
} }, "Close"),
74+
React.createElement(PlaceDataProvider, { place: college },
75+
React.createElement(PlaceReviews, null))))),
76+
React.createElement("div", { className: "SplitLayoutContainer", slot: "main" },
77+
React.createElement(Map, { id: "gmap", mapId: "8c732c82e4ec29d9", ...cameraProps, onCameraChanged: (ev) => {
78+
setCameraProps(ev.detail);
79+
} }, college?.location && (React.createElement(AdvancedMarker, { position: college.location },
80+
React.createElement(Pin, { background: '#FBBC04', glyphColor: '#000', borderColor: '#000' })))))))));
81+
}
82+
export function renderToDom(container) {
83+
const root = createRoot(container);
84+
root.render(React.createElement(React.StrictMode, null,
85+
React.createElement(App, null)));
86+
}
87+
// [END maps_rgm_college_picker]

‎dist/samples/rgm-college-picker/dist/assets/index-CXBbSg5r.js‎

Lines changed: 1363 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎dist/samples/rgm-college-picker/dist/assets/index-gkv0EidX.css‎

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)