Skip to content

Commit b0463b7

Browse files
committed
[Map] Allows Map options customization in ux:map:pre-connect event (e.g.: zoom, options, bridgeOptions...)
1 parent 523c591 commit b0463b7

File tree

12 files changed

+121
-81
lines changed

12 files changed

+121
-81
lines changed

src/Map/CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,22 @@ $map->addRectangle(new Rectangle(
2323
```
2424

2525
- Deprecate property `rawOptions` from `ux:map:*:before-create` events, in favor of `bridgeOptions` instead.
26+
- Map options can now be configured and overridden through the `ux:map:pre-connect` event:
27+
```js
28+
this.element.addEventListener('ux:map:pre-connect', (event) => {
29+
// Override the map center and zoom
30+
event.detail.zoom = 10;
31+
event.detail.center = { lat: 48.856613, lng: 2.352222 };
32+
33+
// Override the normalized `*Options` PHP classes (e.g. `GoogleMapOptions` or `LeafletMapOptions`)
34+
console.log(event.detail.options);
35+
36+
// Override the options specific to the renderer bridge (e.g. `google.maps.MapOptions` or `L.MapOptions`)
37+
event.detail.bridgeOptions = {
38+
// ...
39+
};
40+
});
41+
```
2642

2743
## 2.26
2844

src/Map/assets/dist/abstract_map_controller.d.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ export type Icon = {
2626
type: typeof IconTypes.Svg;
2727
html: string;
2828
});
29+
export type MapDefinition<MapOptions, BridgeMapOptions> = {
30+
center: Point | null;
31+
zoom: number | null;
32+
options: MapOptions;
33+
bridgeOptions?: BridgeMapOptions;
34+
};
2935
export type MarkerDefinition<BridgeMarkerOptions, BridgeInfoWindowOptions> = WithIdentifier<{
3036
position: Point;
3137
title: string | null;
@@ -79,7 +85,7 @@ export type InfoWindowDefinition<BridgeInfoWindowOptions> = {
7985
bridgeOptions?: BridgeInfoWindowOptions;
8086
extra: Record<string, unknown>;
8187
};
82-
export default abstract class<MapOptions, BridgeMap, BridgeMarkerOptions, BridgeMarker, BridgeInfoWindowOptions, BridgeInfoWindow, BridgePolygonOptions, BridgePolygon, BridgePolylineOptions, BridgePolyline, BridgeCircleOptions, BridgeCircle, BridgeRectangleOptions, BridgeRectangle> extends Controller<HTMLElement> {
88+
export default abstract class<MapOptions, BridgeMapOptions, BridgeMap, BridgeMarkerOptions, BridgeMarker, BridgeInfoWindowOptions, BridgeInfoWindow, BridgePolygonOptions, BridgePolygon, BridgePolylineOptions, BridgePolyline, BridgeCircleOptions, BridgeCircle, BridgeRectangleOptions, BridgeRectangle> extends Controller<HTMLElement> {
8389
static values: {
8490
providerOptions: ObjectConstructor;
8591
center: ObjectConstructor;
@@ -136,10 +142,8 @@ export default abstract class<MapOptions, BridgeMap, BridgeMarkerOptions, Bridge
136142
polylinesValueChanged(): void;
137143
circlesValueChanged(): void;
138144
rectanglesValueChanged(): void;
139-
protected abstract doCreateMap({ center, zoom, options }: {
140-
center: Point | null;
141-
zoom: number | null;
142-
options: MapOptions;
145+
protected abstract doCreateMap({ definition }: {
146+
definition: MapDefinition<MapOptions, BridgeMapOptions>;
143147
}): BridgeMap;
144148
protected abstract doFitBoundsToMarkers(): void;
145149
protected abstract doCreateMarker({ definition }: {

src/Map/assets/dist/abstract_map_controller.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,18 @@ class default_1 extends Controller {
1717
this.isConnected = false;
1818
}
1919
connect() {
20-
const options = this.optionsValue;
21-
this.dispatchEvent('pre-connect', { options });
20+
const mapDefinition = {
21+
center: this.hasCenterValue ? this.centerValue : null,
22+
zoom: this.hasZoomValue ? this.zoomValue : null,
23+
options: this.optionsValue,
24+
};
25+
this.dispatchEvent('pre-connect', mapDefinition);
2226
this.createMarker = this.createDrawingFactory('marker', this.markers, this.doCreateMarker.bind(this));
2327
this.createPolygon = this.createDrawingFactory('polygon', this.polygons, this.doCreatePolygon.bind(this));
2428
this.createPolyline = this.createDrawingFactory('polyline', this.polylines, this.doCreatePolyline.bind(this));
2529
this.createCircle = this.createDrawingFactory('circle', this.circles, this.doCreateCircle.bind(this));
2630
this.createRectangle = this.createDrawingFactory('rectangle', this.rectangles, this.doCreateRectangle.bind(this));
27-
this.map = this.doCreateMap({
28-
center: this.hasCenterValue ? this.centerValue : null,
29-
zoom: this.hasZoomValue ? this.zoomValue : null,
30-
options,
31-
});
31+
this.map = this.doCreateMap({ definition: mapDefinition });
3232
this.markersValue.forEach((definition) => this.createMarker({ definition }));
3333
this.polygonsValue.forEach((definition) => this.createPolygon({ definition }));
3434
this.polylinesValue.forEach((definition) => this.createPolyline({ definition }));

src/Map/assets/src/abstract_map_controller.ts

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,17 @@ export type Icon = {
3737
}
3838
);
3939

40+
export type MapDefinition<MapOptions, BridgeMapOptions> = {
41+
center: Point | null;
42+
zoom: number | null;
43+
options: MapOptions;
44+
/**
45+
* Additional options passed to the Map constructor.
46+
* These options are specific to the Map Bridge, and can be defined through `ux:map:pre-connect` event.
47+
*/
48+
bridgeOptions?: BridgeMapOptions;
49+
};
50+
4051
export type MarkerDefinition<BridgeMarkerOptions, BridgeInfoWindowOptions> = WithIdentifier<{
4152
position: Point;
4253
title: string | null;
@@ -182,6 +193,7 @@ export type InfoWindowDefinition<BridgeInfoWindowOptions> = {
182193

183194
export default abstract class<
184195
MapOptions, // Normalized `*Options` PHP class from Bridge (to not be confused with the JS Map class options)
196+
BridgeMapOptions, // The options for the JavaScript Map class from Bridge
185197
BridgeMap, // The JavaScript Map class from Bridge (e.g.: `L.Map` for Leaflet, `google.maps.Map` for Google Maps)
186198
BridgeMarkerOptions, // The options for the JavaScript Marker class from Bridge
187199
BridgeMarker, // The JavaScript Marker class from Bridge
@@ -247,21 +259,20 @@ export default abstract class<
247259
protected abstract dispatchEvent(name: string, payload: Record<string, unknown>): void;
248260

249261
connect() {
250-
const options = this.optionsValue;
251-
252-
this.dispatchEvent('pre-connect', { options });
262+
const mapDefinition: MapDefinition<MapOptions, BridgeMapOptions> = {
263+
center: this.hasCenterValue ? this.centerValue : null,
264+
zoom: this.hasZoomValue ? this.zoomValue : null,
265+
options: this.optionsValue,
266+
};
267+
this.dispatchEvent('pre-connect', mapDefinition);
253268

254269
this.createMarker = this.createDrawingFactory('marker', this.markers, this.doCreateMarker.bind(this));
255270
this.createPolygon = this.createDrawingFactory('polygon', this.polygons, this.doCreatePolygon.bind(this));
256271
this.createPolyline = this.createDrawingFactory('polyline', this.polylines, this.doCreatePolyline.bind(this));
257272
this.createCircle = this.createDrawingFactory('circle', this.circles, this.doCreateCircle.bind(this));
258273
this.createRectangle = this.createDrawingFactory('rectangle', this.rectangles, this.doCreateRectangle.bind(this));
259274

260-
this.map = this.doCreateMap({
261-
center: this.hasCenterValue ? this.centerValue : null,
262-
zoom: this.hasZoomValue ? this.zoomValue : null,
263-
options,
264-
});
275+
this.map = this.doCreateMap({ definition: mapDefinition });
265276
this.markersValue.forEach((definition) => this.createMarker({ definition }));
266277
this.polygonsValue.forEach((definition) => this.createPolygon({ definition }));
267278
this.polylinesValue.forEach((definition) => this.createPolyline({ definition }));
@@ -356,7 +367,7 @@ export default abstract class<
356367
//endregion
357368

358369
//region Abstract factory methods to be implemented by the concrete classes, they are specific to the map provider
359-
protected abstract doCreateMap({ center, zoom, options }: { center: Point | null; zoom: number | null; options: MapOptions }): BridgeMap;
370+
protected abstract doCreateMap({ definition }: { definition: MapDefinition<MapOptions, BridgeMapOptions> }): BridgeMap;
360371

361372
protected abstract doFitBoundsToMarkers(): void;
362373

src/Map/assets/test/abstract_map_controller.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ class MyMapController extends AbstractMapController {
1212
});
1313
}
1414

15-
doCreateMap({ center, zoom, options }) {
16-
return { map: 'map', center, zoom, options };
15+
doCreateMap({ definition }) {
16+
return { map: 'map', center: definition.center, zoom: definition.zoom, options: definition.options };
1717
}
1818

1919
doCreateMarker({ definition }) {

src/Map/doc/index.rst

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ Configuration is done in your ``config/packages/ux_map.yaml`` file:
3333
# without to manually configure it in each map instance (through "new GoogleOptions(mapId: 'your_map_id')").
3434
default_map_id: null
3535
36-
The ``UX_MAP_DSN`` environment variable configure which renderer to use.
36+
The ``UX_MAP_DSN`` environment variable configure which renderer (Bridge) to use.
3737

3838
Map renderers
3939
~~~~~~~~~~~~~
@@ -383,7 +383,19 @@ Symfony UX Map allows you to extend its default behavior using a custom Stimulus
383383
* You can use this event to configure the map before it is created
384384
*/
385385
_onPreConnect(event) {
386+
// You can read or write the zoom level
387+
console.log(event.detail.zoom);
388+
389+
console.log(event.detail.center);
390+
391+
// You can read or write map options, specific to the Bridge, it represents the normalized `*Options` PHP class (e.g. `GoogleOptions`, `LeafletOptions`)
386392
console.log(event.detail.options);
393+
394+
// Finally, you can also set Bridge-specific options that will be used when creating the map.
395+
event.detail.bridgeOptions = {
396+
preferCanvas: true, // e.g. for Leaflet (https://leafletjs.com/reference.html#map-prefercanvas)
397+
backgroundColor: '#f0f0f0', // e.g. for Google Maps (https://developers.google.com/maps/documentation/javascript/reference/map#MapOptions.backgroundColor)
398+
}
387399
}
388400
389401
/**

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

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
11
import type { LoaderOptions } from '@googlemaps/js-api-loader';
2-
import type { CircleDefinition, Icon, InfoWindowDefinition, MarkerDefinition, Point, PolygonDefinition, PolylineDefinition, RectangleDefinition } from '@symfony/ux-map';
2+
import type { CircleDefinition, Icon, InfoWindowDefinition, MapDefinition, MarkerDefinition, PolygonDefinition, PolylineDefinition, RectangleDefinition } from '@symfony/ux-map';
33
import AbstractMapController from '@symfony/ux-map';
44
type MapOptions = Pick<google.maps.MapOptions, 'mapId' | 'gestureHandling' | 'backgroundColor' | 'disableDoubleClickZoom' | 'zoomControl' | 'zoomControlOptions' | 'mapTypeControl' | 'mapTypeControlOptions' | 'streetViewControl' | 'streetViewControlOptions' | 'fullscreenControl' | 'fullscreenControlOptions'>;
5-
export default class extends AbstractMapController<MapOptions, google.maps.Map, google.maps.marker.AdvancedMarkerElementOptions, google.maps.marker.AdvancedMarkerElement, google.maps.InfoWindowOptions, google.maps.InfoWindow, google.maps.PolygonOptions, google.maps.Polygon, google.maps.PolylineOptions, google.maps.Polyline, google.maps.CircleOptions, google.maps.Circle, google.maps.RectangleOptions, google.maps.Rectangle> {
5+
export default class extends AbstractMapController<MapOptions, google.maps.MapOptions, google.maps.Map, google.maps.marker.AdvancedMarkerElementOptions, google.maps.marker.AdvancedMarkerElement, google.maps.InfoWindowOptions, google.maps.InfoWindow, google.maps.PolygonOptions, google.maps.Polygon, google.maps.PolylineOptions, google.maps.Polyline, google.maps.CircleOptions, google.maps.Circle, google.maps.RectangleOptions, google.maps.Rectangle> {
66
providerOptionsValue: Pick<LoaderOptions, 'apiKey' | 'id' | 'language' | 'region' | 'nonce' | 'retries' | 'url' | 'version' | 'libraries'>;
77
map: google.maps.Map;
8-
parser: DOMParser;
98
connect(): Promise<void>;
109
centerValueChanged(): void;
1110
zoomValueChanged(): void;
1211
protected dispatchEvent(name: string, payload?: Record<string, unknown>): void;
13-
protected doCreateMap({ center, zoom, options }: {
14-
center: Point | null;
15-
zoom: number | null;
16-
options: MapOptions;
12+
protected doCreateMap({ definition }: {
13+
definition: MapDefinition<MapOptions, google.maps.MapOptions>;
1714
}): google.maps.Map;
1815
protected doCreateMarker({ definition, }: {
1916
definition: MarkerDefinition<google.maps.marker.AdvancedMarkerElementOptions, google.maps.InfoWindowOptions>;

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

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,18 @@ class default_1 extends Controller {
1818
this.isConnected = false;
1919
}
2020
connect() {
21-
const options = this.optionsValue;
22-
this.dispatchEvent('pre-connect', { options });
21+
const mapDefinition = {
22+
center: this.hasCenterValue ? this.centerValue : null,
23+
zoom: this.hasZoomValue ? this.zoomValue : null,
24+
options: this.optionsValue,
25+
};
26+
this.dispatchEvent('pre-connect', mapDefinition);
2327
this.createMarker = this.createDrawingFactory('marker', this.markers, this.doCreateMarker.bind(this));
2428
this.createPolygon = this.createDrawingFactory('polygon', this.polygons, this.doCreatePolygon.bind(this));
2529
this.createPolyline = this.createDrawingFactory('polyline', this.polylines, this.doCreatePolyline.bind(this));
2630
this.createCircle = this.createDrawingFactory('circle', this.circles, this.doCreateCircle.bind(this));
2731
this.createRectangle = this.createDrawingFactory('rectangle', this.rectangles, this.doCreateRectangle.bind(this));
28-
this.map = this.doCreateMap({
29-
center: this.hasCenterValue ? this.centerValue : null,
30-
zoom: this.hasZoomValue ? this.zoomValue : null,
31-
options,
32-
});
32+
this.map = this.doCreateMap({ definition: mapDefinition });
3333
this.markersValue.forEach((definition) => this.createMarker({ definition }));
3434
this.polygonsValue.forEach((definition) => this.createPolygon({ definition }));
3535
this.polylinesValue.forEach((definition) => this.createPolyline({ definition }));
@@ -184,23 +184,23 @@ class map_controller extends default_1 {
184184
}
185185
}
186186
dispatchEvent(name, payload = {}) {
187+
payload.google = _google;
187188
this.dispatch(name, {
188189
prefix: 'ux:map',
189-
detail: {
190-
...payload,
191-
google: _google,
192-
},
190+
detail: payload,
193191
});
194192
}
195-
doCreateMap({ center, zoom, options }) {
193+
doCreateMap({ definition }) {
194+
const { center, zoom, options, bridgeOptions = {} } = definition;
196195
options.zoomControl = typeof options.zoomControlOptions !== 'undefined';
197196
options.mapTypeControl = typeof options.mapTypeControlOptions !== 'undefined';
198197
options.streetViewControl = typeof options.streetViewControlOptions !== 'undefined';
199198
options.fullscreenControl = typeof options.fullscreenControlOptions !== 'undefined';
200199
return new _google.maps.Map(this.element, {
201-
...options,
202200
center,
203201
zoom,
202+
...options,
203+
...bridgeOptions,
204204
});
205205
}
206206
doCreateMarker({ definition, }) {

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

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import type {
1313
CircleDefinition,
1414
Icon,
1515
InfoWindowDefinition,
16+
MapDefinition,
1617
MarkerDefinition,
17-
Point,
1818
PolygonDefinition,
1919
PolylineDefinition,
2020
RectangleDefinition,
@@ -48,6 +48,7 @@ const parser = new DOMParser();
4848

4949
export default class extends AbstractMapController<
5050
MapOptions,
51+
google.maps.MapOptions,
5152
google.maps.Map,
5253
google.maps.marker.AdvancedMarkerElementOptions,
5354
google.maps.marker.AdvancedMarkerElement,
@@ -66,8 +67,6 @@ export default class extends AbstractMapController<
6667

6768
declare map: google.maps.Map;
6869

69-
public parser: DOMParser;
70-
7170
async connect() {
7271
const onLoaded = () => super.connect();
7372

@@ -129,26 +128,27 @@ export default class extends AbstractMapController<
129128
}
130129

131130
protected dispatchEvent(name: string, payload: Record<string, unknown> = {}): void {
131+
payload.google = _google;
132132
this.dispatch(name, {
133133
prefix: 'ux:map',
134-
detail: {
135-
...payload,
136-
google: _google,
137-
},
134+
detail: payload,
138135
});
139136
}
140137

141-
protected doCreateMap({ center, zoom, options }: { center: Point | null; zoom: number | null; options: MapOptions }): google.maps.Map {
138+
protected doCreateMap({ definition }: { definition: MapDefinition<MapOptions, google.maps.MapOptions> }): google.maps.Map {
139+
const { center, zoom, options, bridgeOptions = {} } = definition;
140+
142141
// We assume the following control options are enabled if their options are set
143142
options.zoomControl = typeof options.zoomControlOptions !== 'undefined';
144143
options.mapTypeControl = typeof options.mapTypeControlOptions !== 'undefined';
145144
options.streetViewControl = typeof options.streetViewControlOptions !== 'undefined';
146145
options.fullscreenControl = typeof options.fullscreenControlOptions !== 'undefined';
147146

148147
return new _google.maps.Map(this.element, {
149-
...options,
150148
center,
151149
zoom,
150+
...options,
151+
...bridgeOptions,
152152
});
153153
}
154154

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

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import type { CircleDefinition, Icon, InfoWindowDefinition, MarkerDefinition, Point, PolygonDefinition, PolylineDefinition, RectangleDefinition } from '@symfony/ux-map';
1+
import type { CircleDefinition, Icon, InfoWindowDefinition, MapDefinition, MarkerDefinition, PolygonDefinition, PolylineDefinition, RectangleDefinition } from '@symfony/ux-map';
22
import AbstractMapController from '@symfony/ux-map';
33
import 'leaflet/dist/leaflet.min.css';
44
import type { CircleOptions, ControlPosition, MapOptions as LeafletMapOptions, MarkerOptions, PolylineOptions as PolygonOptions, PolylineOptions, PopupOptions, PolylineOptions as RectangleOptions } from 'leaflet';
55
import * as L from 'leaflet';
6-
type MapOptions = Pick<LeafletMapOptions, 'center' | 'zoom' | 'attributionControl' | 'zoomControl'> & {
6+
type MapOptions = Pick<LeafletMapOptions, 'attributionControl' | 'zoomControl'> & {
77
attributionControlOptions?: {
88
position: ControlPosition;
99
prefix: string | false;
@@ -21,16 +21,14 @@ type MapOptions = Pick<LeafletMapOptions, 'center' | 'zoom' | 'attributionContro
2121
options: Record<string, unknown>;
2222
} | false;
2323
};
24-
export default class extends AbstractMapController<MapOptions, L.Map, MarkerOptions, L.Marker, PopupOptions, L.Popup, PolygonOptions, L.Polygon, PolylineOptions, L.Polyline, CircleOptions, L.Circle, RectangleOptions, L.Rectangle> {
24+
export default class extends AbstractMapController<MapOptions, LeafletMapOptions, L.Map, MarkerOptions, L.Marker, PopupOptions, L.Popup, PolygonOptions, L.Polygon, PolylineOptions, L.Polyline, CircleOptions, L.Circle, RectangleOptions, L.Rectangle> {
2525
map: L.Map;
2626
connect(): void;
2727
centerValueChanged(): void;
2828
zoomValueChanged(): void;
2929
protected dispatchEvent(name: string, payload?: Record<string, unknown>): void;
30-
protected doCreateMap({ center, zoom, options }: {
31-
center: Point | null;
32-
zoom: number | null;
33-
options: MapOptions;
30+
protected doCreateMap({ definition }: {
31+
definition: MapDefinition<MapOptions, LeafletMapOptions>;
3432
}): L.Map;
3533
protected doCreateMarker({ definition }: {
3634
definition: MarkerDefinition<MarkerOptions, PopupOptions>;

0 commit comments

Comments
 (0)