Skip to content

Commit c8c529f

Browse files
committed
[Map][Google] Introduce provider parameter "libraries"
1 parent c3e42ab commit c8c529f

File tree

8 files changed

+99
-39
lines changed

8 files changed

+99
-39
lines changed

src/Map/src/Bridge/Google/README.md

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,21 @@ UX_MAP_DSN=google://GOOGLE_MAPS_API_KEY@default
1010
# With options
1111
UX_MAP_DSN=google://GOOGLE_MAPS_API_KEY@default?version=weekly
1212
UX_MAP_DSN=google://GOOGLE_MAPS_API_KEY@default?language=fr&region=FR
13+
UX_MAP_DSN=google://GOOGLE_MAPS_API_KEY@default??libraries[]=geometry&libraries[]=places
1314
```
1415

1516
Available options:
1617

17-
| Option | Description | Default |
18-
|------------|------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------|
19-
| `id` | The id of the script tag | `__googleMapsScriptId` |
20-
| `language` | Force language, see [list of supported languages](https://developers.google.com/maps/faq#languagesupport) specified in the browser | The user's preferred language |
21-
| `region` | Unicode region subtag identifiers compatible with [ISO 3166-1](https://en.wikipedia.org/wiki/ISO_3166-1) | |
22-
| `nonce` | Use a cryptographic nonce attribute | |
23-
| `retries` | The number of script load retries | 3 |
24-
| `url` | Custom url to load the Google Maps API script | `https://maps.googleapis.com/maps/api/js` |
25-
| `version` | The release channels or version numbers | `weekly` |
18+
| Option | Description | Default |
19+
|-------------|------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------|
20+
| `id` | The id of the script tag | `__googleMapsScriptId` |
21+
| `language` | Force language, see [list of supported languages](https://developers.google.com/maps/faq#languagesupport) specified in the browser | The user's preferred language |
22+
| `region` | Unicode region subtag identifiers compatible with [ISO 3166-1](https://en.wikipedia.org/wiki/ISO_3166-1) | |
23+
| `nonce` | Use a cryptographic nonce attribute | |
24+
| `retries` | The number of script load retries | 3 |
25+
| `url` | Custom url to load the Google Maps API script | `https://maps.googleapis.com/maps/api/js` |
26+
| `version` | The release channels or version numbers | `weekly` |
27+
| `libraries` | The additional libraries to load, see [list of supported libraries](https://googlemaps.github.io/js-api-loader/types/Library.html) | `['maps', 'marker']`, those two libraries are always loaded |
2628

2729
## Map options
2830

@@ -78,6 +80,63 @@ $googleOptions = (new GoogleOptions())
7880
// Add the custom options to the map
7981
$map->options($googleOptions);
8082
```
83+
## Use cases
84+
85+
Below are some common or advanced use cases when using a map.
86+
87+
> **Note**:
88+
> As the UX Map component is new, there are not too many examples for the moment.
89+
90+
### Customize the marker
91+
92+
A common use case is to customize the marker. You can listen to the `ux:map:marker:before-create` event to customize the marker before it is created.
93+
94+
Assuming you have a map with a custom controller:
95+
```twig
96+
{{ render_map(map, {'data-controller': 'my-map' }) }}
97+
```
98+
99+
You can create a controller to customize the marker before it is created:
100+
```js
101+
// assets/controllers/my_map_controller.js
102+
import {Controller} from "@hotwired/stimulus";
103+
104+
export default class extends Controller
105+
{
106+
connect() {
107+
this.element.addEventListener('ux:map:marker:before-create', this._onMarkerBeforeCreate);
108+
}
109+
110+
disconnect() {
111+
// Always remove listeners when the controller is disconnected
112+
this.element.removeEventListener('ux:map:marker:before-create', this._onMarkerBeforeCreate);
113+
}
114+
115+
_onMarkerBeforeCreate(event) {
116+
// You can access the marker definition and the google object
117+
// Note: `definition.rawOptions` is the raw options object that will be passed to the `google.maps.Marker` constructor.
118+
const { definition, google } = event.detail;
119+
120+
// 1. To use a custom image for the marker
121+
const beachFlagImg = document.createElement("img");
122+
// Note: instead of using an hardcoded URL, you can use the `extra` parameter from `new Marker()` (PHP) and access it here with `definition.extra`.
123+
beachFlagImg.src = "https://developers.google.com/maps/documentation/javascript/examples/full/images/beachflag.png";
124+
definition.rawOptions = {
125+
content: beachFlagImg
126+
}
127+
128+
// 2. To use a custom glyph for the marker
129+
const pinElement = new google.maps.marker.PinElement({
130+
// Note: instead of using an hardcoded URL, you can use the `extra` parameter from `new Marker()` (PHP) and access it here with `definition.extra`.
131+
glyph: new URL(String('https://maps.gstatic.com/mapfiles/place_api/icons/v2/museum_pinlet.svg')),
132+
glyphColor: "white",
133+
});
134+
definition.rawOptions = {
135+
content: pinElement.element,
136+
}
137+
}
138+
}
139+
```
81140

82141
## Resources
83142

src/Map/src/Bridge/Google/assets/dist/map_controller.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export default class extends AbstractMapController<MapOptions, google.maps.Map,
77
static values: {
88
providerOptions: ObjectConstructor;
99
};
10-
providerOptionsValue: Pick<LoaderOptions, 'apiKey' | 'id' | 'language' | 'region' | 'nonce' | 'retries' | 'url' | 'version'>;
10+
providerOptionsValue: Pick<LoaderOptions, 'apiKey' | 'id' | 'language' | 'region' | 'nonce' | 'retries' | 'url' | 'version' | 'libraries'>;
1111
connect(): Promise<void>;
1212
protected doCreateMap({ center, zoom, options, }: {
1313
center: Point | null;

src/Map/src/Bridge/Google/assets/dist/map_controller.js

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,29 @@
11
import AbstractMapController from '@symfony/ux-map/abstract-map-controller';
22
import { Loader } from '@googlemaps/js-api-loader';
33

4-
let loader;
5-
let library;
4+
let _google;
65
class default_1 extends AbstractMapController {
76
async connect() {
8-
if (!loader) {
9-
loader = new Loader(this.providerOptionsValue);
7+
if (!_google) {
8+
const loader = new Loader(this.providerOptionsValue);
9+
_google = await loader.load();
1010
}
11-
const { Map: _Map, InfoWindow } = await loader.importLibrary('maps');
12-
const { AdvancedMarkerElement } = await loader.importLibrary('marker');
13-
library = { _Map, AdvancedMarkerElement, InfoWindow };
1411
super.connect();
1512
}
1613
doCreateMap({ center, zoom, options, }) {
1714
options.zoomControl = typeof options.zoomControlOptions !== 'undefined';
1815
options.mapTypeControl = typeof options.mapTypeControlOptions !== 'undefined';
1916
options.streetViewControl = typeof options.streetViewControlOptions !== 'undefined';
2017
options.fullscreenControl = typeof options.fullscreenControlOptions !== 'undefined';
21-
return new library._Map(this.element, {
18+
return new _google.maps.Map(this.element, {
2219
...options,
2320
center,
2421
zoom,
2522
});
2623
}
2724
doCreateMarker(definition) {
2825
const { position, title, infoWindow, extra, rawOptions = {}, ...otherOptions } = definition;
29-
const marker = new library.AdvancedMarkerElement({
26+
const marker = new _google.maps.marker.AdvancedMarkerElement({
3027
position,
3128
title,
3229
...otherOptions,
@@ -40,7 +37,7 @@ class default_1 extends AbstractMapController {
4037
}
4138
doCreateInfoWindow({ definition, marker, }) {
4239
const { headerContent, content, extra, rawOptions = {}, ...otherOptions } = definition;
43-
const infoWindow = new library.InfoWindow({
40+
const infoWindow = new _google.maps.InfoWindow({
4441
headerContent: this.createTextOrElement(headerContent),
4542
content: this.createTextOrElement(content),
4643
...otherOptions,

src/Map/src/Bridge/Google/assets/src/map_controller.ts

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,7 @@ type MapOptions = Pick<
2828
| 'fullscreenControlOptions'
2929
>;
3030

31-
let loader: Loader;
32-
let library: {
33-
_Map: typeof google.maps.Map;
34-
AdvancedMarkerElement: typeof google.maps.marker.AdvancedMarkerElement;
35-
InfoWindow: typeof google.maps.InfoWindow;
36-
};
31+
let _google: typeof google;
3732

3833
export default class extends AbstractMapController<
3934
MapOptions,
@@ -47,18 +42,15 @@ export default class extends AbstractMapController<
4742

4843
declare providerOptionsValue: Pick<
4944
LoaderOptions,
50-
'apiKey' | 'id' | 'language' | 'region' | 'nonce' | 'retries' | 'url' | 'version'
45+
'apiKey' | 'id' | 'language' | 'region' | 'nonce' | 'retries' | 'url' | 'version' | 'libraries'
5146
>;
5247

5348
async connect() {
54-
if (!loader) {
55-
loader = new Loader(this.providerOptionsValue);
49+
if (!_google) {
50+
const loader = new Loader(this.providerOptionsValue);
51+
_google = await loader.load();
5652
}
5753

58-
const { Map: _Map, InfoWindow } = await loader.importLibrary('maps');
59-
const { AdvancedMarkerElement } = await loader.importLibrary('marker');
60-
library = { _Map, AdvancedMarkerElement, InfoWindow };
61-
6254
super.connect();
6355
}
6456

@@ -77,7 +69,7 @@ export default class extends AbstractMapController<
7769
options.streetViewControl = typeof options.streetViewControlOptions !== 'undefined';
7870
options.fullscreenControl = typeof options.fullscreenControlOptions !== 'undefined';
7971

80-
return new library._Map(this.element, {
72+
return new _google.maps.Map(this.element, {
8173
...options,
8274
center,
8375
zoom,
@@ -89,7 +81,7 @@ export default class extends AbstractMapController<
8981
): google.maps.marker.AdvancedMarkerElement {
9082
const { position, title, infoWindow, extra, rawOptions = {}, ...otherOptions } = definition;
9183

92-
const marker = new library.AdvancedMarkerElement({
84+
const marker = new _google.maps.marker.AdvancedMarkerElement({
9385
position,
9486
title,
9587
...otherOptions,
@@ -116,7 +108,7 @@ export default class extends AbstractMapController<
116108
}): google.maps.InfoWindow {
117109
const { headerContent, content, extra, rawOptions = {}, ...otherOptions } = definition;
118110

119-
const infoWindow = new library.InfoWindow({
111+
const infoWindow = new _google.maps.InfoWindow({
120112
headerContent: this.createTextOrElement(headerContent),
121113
content: this.createTextOrElement(content),
122114
...otherOptions,

src/Map/src/Bridge/Google/assets/test/map_controller.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ describe('GoogleMapsController', () => {
4040
data-testid="map"
4141
data-controller="check google"
4242
style="height&#x3A;&#x20;700px&#x3B;&#x20;margin&#x3A;&#x20;10px"
43-
data-google-provider-options-value="&#x7B;&quot;language&quot;&#x3A;&quot;fr&quot;,&quot;region&quot;&#x3A;&quot;FR&quot;,&quot;retries&quot;&#x3A;10,&quot;version&quot;&#x3A;&quot;weekly&quot;,&quot;apiKey&quot;&#x3A;&quot;&quot;&#x7D;"
43+
data-google-provider-options-value="&#x7B;&quot;version&quot;&#x3A;&quot;weekly&quot;,&quot;libraries&quot;&#x3A;&#x5B;&quot;maps&quot;,&quot;marker&quot;&#x5D;,&quot;apiKey&quot;&#x3A;&quot;&quot;&#x7D;"
4444
data-google-view-value="&#x7B;&quot;center&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;zoom&quot;&#x3A;4,&quot;fitBoundsToMarkers&quot;&#x3A;true,&quot;options&quot;&#x3A;&#x7B;&quot;mapId&quot;&#x3A;&quot;YOUR_MAP_ID&quot;,&quot;gestureHandling&quot;&#x3A;&quot;auto&quot;,&quot;backgroundColor&quot;&#x3A;null,&quot;disableDoubleClickZoom&quot;&#x3A;false,&quot;zoomControl&quot;&#x3A;true,&quot;zoomControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;22&#x7D;,&quot;mapTypeControl&quot;&#x3A;true,&quot;mapTypeControlOptions&quot;&#x3A;&#x7B;&quot;mapTypeIds&quot;&#x3A;&#x5B;&#x5D;,&quot;position&quot;&#x3A;14,&quot;style&quot;&#x3A;0&#x7D;,&quot;streetViewControl&quot;&#x3A;true,&quot;streetViewControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;22&#x7D;,&quot;fullscreenControl&quot;&#x3A;true,&quot;fullscreenControlOptions&quot;&#x3A;&#x7B;&quot;position&quot;&#x3A;20&#x7D;&#x7D;,&quot;markers&quot;&#x3A;&#x5B;&#x7B;&quot;position&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;48.8566,&quot;lng&quot;&#x3A;2.3522&#x7D;,&quot;title&quot;&#x3A;&quot;Paris&quot;,&quot;infoWindow&quot;&#x3A;null&#x7D;,&#x7B;&quot;position&quot;&#x3A;&#x7B;&quot;lat&quot;&#x3A;45.764,&quot;lng&quot;&#x3A;4.8357&#x7D;,&quot;title&quot;&#x3A;&quot;Lyon&quot;,&quot;infoWindow&quot;&#x3A;&#x7B;&quot;headerContent&quot;&#x3A;&quot;&lt;b&gt;Lyon&lt;&#x5C;&#x2F;b&gt;&quot;,&quot;content&quot;&#x3A;&quot;The&#x20;French&#x20;town&#x20;in&#x20;the&#x20;historic&#x20;Rh&#x5C;u00f4ne-Alpes&#x20;region,&#x20;located&#x20;at&#x20;the&#x20;junction&#x20;of&#x20;the&#x20;Rh&#x5C;u00f4ne&#x20;and&#x20;Sa&#x5C;u00f4ne&#x20;rivers.&quot;,&quot;position&quot;&#x3A;null,&quot;opened&quot;&#x3A;false,&quot;autoClose&quot;&#x3A;true&#x7D;&#x7D;&#x5D;&#x7D;"
4545
></div>
4646
`);

src/Map/src/Bridge/Google/src/Renderer/GoogleRenderer.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ public function __construct(
3737
private ?int $retries = null,
3838
private ?string $url = null,
3939
private ?string $version = null,
40+
/**
41+
* @var array<'core' | 'maps' | 'places' | 'geocoding' | 'routes' | 'marker' | 'geometry' | 'elevation' | 'streetView' | 'journeySharing' | 'drawing' | 'visualization'>
42+
*/
43+
private array $libraries = [],
4044
) {
4145
parent::__construct($stimulusHelper);
4246
}
@@ -56,6 +60,7 @@ protected function getProviderOptions(): array
5660
'retries' => $this->retries,
5761
'url' => $this->url,
5862
'version' => $this->version,
63+
'libraries' => $this->libraries,
5964
]) + ['apiKey' => $this->apiKey];
6065
}
6166

@@ -77,6 +82,7 @@ public function __toString(): string
7782
'retries' => $this->retries,
7883
'url' => $this->url,
7984
'version' => $this->version,
85+
'libraries' => $this->libraries,
8086
]))
8187
);
8288
}

src/Map/src/Bridge/Google/src/Renderer/GoogleRendererFactory.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public function create(Dsn $dsn): RendererInterface
4141
retries: $dsn->getOption('retries'),
4242
url: $dsn->getOption('url'),
4343
version: $dsn->getOption('version', 'weekly'),
44+
libraries: ['maps', 'marker', ...$dsn->getOption('libraries', [])],
4445
);
4546
}
4647

src/Map/src/Bridge/Google/tests/GoogleRendererFactoryTest.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,19 @@ public static function supportsRenderer(): iterable
3232
public static function createRenderer(): iterable
3333
{
3434
yield [
35-
'google://*******************@default/?version=weekly',
35+
'google://*******************@default/?version=weekly&libraries%5B0%5D=maps&libraries%5B1%5D=marker',
3636
'google://GOOGLE_MAPS_API_KEY@default',
3737
];
3838

3939
yield [
40-
'google://*******************@default/?version=quartly',
40+
'google://*******************@default/?version=quartly&libraries%5B0%5D=maps&libraries%5B1%5D=marker',
4141
'google://GOOGLE_MAPS_API_KEY@default?version=quartly',
4242
];
43+
44+
yield [
45+
'google://*******************@default/?version=quartly&libraries%5B0%5D=maps&libraries%5B1%5D=marker&libraries%5B2%5D=geometry',
46+
'google://GOOGLE_MAPS_API_KEY@default?version=quartly&libraries[]=geometry',
47+
];
4348
}
4449

4550
public static function unsupportedSchemeRenderer(): iterable

0 commit comments

Comments
 (0)