Skip to content

Commit a9b4ca7

Browse files
committed
[Map] Inject provider's SDK (L for Leaflet, google for Google) in dispatched event
Also added an example to custimize the marker icon.
1 parent e2421b4 commit a9b4ca7

File tree

10 files changed

+117
-42
lines changed

10 files changed

+117
-42
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ export default abstract class<MapOptions, Map, MarkerOptions, Marker, InfoWindow
3535
protected map: Map;
3636
protected markers: Array<Marker>;
3737
protected infoWindows: Array<InfoWindow>;
38-
initialize(): void;
3938
connect(): void;
4039
protected abstract doCreateMap({ center, zoom, options, }: {
4140
center: Point;
@@ -53,5 +52,6 @@ export default abstract class<MapOptions, Map, MarkerOptions, Marker, InfoWindow
5352
marker: Marker;
5453
}): InfoWindow;
5554
protected abstract doFitBoundsToMarkers(): void;
55+
protected abstract augmentEventPayload(payload: Record<string, unknown>): Record<string, unknown>;
5656
private dispatchEvent;
5757
}

src/Map/assets/dist/abstract_map_controller.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ class default_1 extends Controller {
66
this.markers = [];
77
this.infoWindows = [];
88
}
9-
initialize() { }
109
connect() {
1110
const { center, zoom, options, markers, fitBoundsToMarkers } = this.viewValue;
1211
this.dispatchEvent('pre-connect', { options });
@@ -36,7 +35,10 @@ class default_1 extends Controller {
3635
return infoWindow;
3736
}
3837
dispatchEvent(name, payload = {}) {
39-
this.dispatch(name, { prefix: 'ux:map', detail: payload });
38+
this.dispatch(name, {
39+
prefix: 'ux:map',
40+
detail: this.augmentEventPayload(payload),
41+
});
4042
}
4143
}
4244
default_1.values = {

src/Map/assets/src/abstract_map_controller.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,6 @@ export default abstract class<
6666
protected markers: Array<Marker> = [];
6767
protected infoWindows: Array<InfoWindow> = [];
6868

69-
initialize() {}
70-
7169
connect() {
7270
const { center, zoom, options, markers, fitBoundsToMarkers } = this.viewValue;
7371

@@ -136,7 +134,12 @@ export default abstract class<
136134

137135
protected abstract doFitBoundsToMarkers(): void;
138136

137+
protected abstract augmentEventPayload(payload: Record<string, unknown>): Record<string, unknown>;
138+
139139
private dispatchEvent(name: string, payload: Record<string, unknown> = {}): void {
140-
this.dispatch(name, { prefix: 'ux:map', detail: payload });
140+
this.dispatch(name, {
141+
prefix: 'ux:map',
142+
detail: this.augmentEventPayload(payload),
143+
});
141144
}
142145
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,6 @@ export default class extends AbstractMapController<MapOptions, google.maps.Map,
2222
private createTextOrElement;
2323
private closeInfoWindowsExcept;
2424
protected doFitBoundsToMarkers(): void;
25+
protected augmentEventPayload(payload: Record<string, unknown>): Record<string, unknown>;
2526
}
2627
export {};

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ class default_1 extends AbstractMapController {
9292
});
9393
this.map.fitBounds(bounds);
9494
}
95+
augmentEventPayload(payload) {
96+
payload.google = _google;
97+
return payload;
98+
}
9599
}
96100
default_1.values = {
97101
providerOptions: Object,

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,4 +176,10 @@ export default class extends AbstractMapController<
176176

177177
this.map.fitBounds(bounds);
178178
}
179+
180+
protected augmentEventPayload(payload: Record<string, unknown>): Record<string, unknown> {
181+
payload.google = _google;
182+
183+
return payload;
184+
}
179185
}

src/Map/src/Bridge/Leaflet/README.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,62 @@ $leafletOptions = (new LeafletOptions())
3737
$map->options($leafletOptions);
3838
```
3939

40+
## Use cases
41+
42+
Below are some common or advanced use cases when using a map.
43+
44+
> **Note**:
45+
> As the UX Map component is new, there are not too many examples for the moment.
46+
47+
### Customize the marker
48+
49+
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.
50+
51+
Assuming you have a map with a custom controller:
52+
```twig
53+
{{ render_map(map, {'data-controller': 'my-map' }) }}
54+
```
55+
56+
You can create a controller to customize the marker before it is created:
57+
```js
58+
// assets/controllers/my_map_controller.js
59+
import {Controller} from "@hotwired/stimulus";
60+
61+
export default class extends Controller
62+
{
63+
connect() {
64+
this.element.addEventListener('ux:map:marker:before-create', this._onMarkerBeforeCreate);
65+
}
66+
67+
disconnect() {
68+
// Always remove listeners when the controller is disconnected
69+
this.element.removeEventListener('ux:map:marker:before-create', this._onMarkerBeforeCreate);
70+
}
71+
72+
_onMarkerBeforeCreate(event) {
73+
// You can access the marker definition and the Leaflet object
74+
// Note: `definition.rawOptions` is the raw options object that will be passed to the `L.marker` constructor.
75+
const { definition, L } = event.detail;
76+
77+
// Use a custom icon for the marker
78+
const redIcon = L.icon({
79+
// Note: instead of using an hardcoded URL, you can use the `extra` parameter from `new Marker()` (PHP) and access it here with `definition.extra`.
80+
iconUrl: 'https://leafletjs.com/examples/custom-icons/leaf-red.png',
81+
shadowUrl: 'https://leafletjs.com/examples/custom-icons/leaf-shadow.png',
82+
iconSize: [38, 95], // size of the icon
83+
shadowSize: [50, 64], // size of the shadow
84+
iconAnchor: [22, 94], // point of the icon which will correspond to marker's location
85+
shadowAnchor: [4, 62], // the same for the shadow
86+
popupAnchor: [-3, -76] // point from which the popup should open relative to the iconAnchor
87+
})
88+
89+
definition.rawOptions = {
90+
icon: redIcon,
91+
}
92+
}
93+
}
94+
```
95+
4096
## Resources
4197

4298
- [Documentation](https://symfony.com/bundles/ux-map/current/index.html)
Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import AbstractMapController from '@symfony/ux-map/abstract-map-controller';
22
import type { Point, MarkerDefinition } from '@symfony/ux-map/abstract-map-controller';
33
import 'leaflet/dist/leaflet.min.css';
4-
import { type Map as LeafletMap, Marker, type Popup } from 'leaflet';
4+
import * as L from 'leaflet';
55
import type { MapOptions as LeafletMapOptions, MarkerOptions, PopupOptions } from 'leaflet';
66
type MapOptions = Pick<LeafletMapOptions, 'center' | 'zoom'> & {
77
tileLayer: {
@@ -10,18 +10,19 @@ type MapOptions = Pick<LeafletMapOptions, 'center' | 'zoom'> & {
1010
options: Record<string, unknown>;
1111
};
1212
};
13-
export default class extends AbstractMapController<MapOptions, typeof LeafletMap, MarkerOptions, Marker, Popup, PopupOptions> {
13+
export default class extends AbstractMapController<MapOptions, typeof L.Map, MarkerOptions, typeof L.Marker, typeof L.Popup, PopupOptions> {
1414
connect(): void;
1515
protected doCreateMap({ center, zoom, options }: {
1616
center: Point;
1717
zoom: number;
1818
options: MapOptions;
19-
}): LeafletMap;
20-
protected doCreateMarker(definition: MarkerDefinition): Marker;
19+
}): L.Map;
20+
protected doCreateMarker(definition: MarkerDefinition): L.Marker;
2121
protected doCreateInfoWindow({ definition, marker, }: {
2222
definition: MarkerDefinition['infoWindow'];
23-
marker: Marker;
24-
}): Popup;
23+
marker: L.Marker;
24+
}): L.Popup;
2525
protected doFitBoundsToMarkers(): void;
26+
protected augmentEventPayload(payload: Record<string, unknown>): Record<string, unknown>;
2627
}
2728
export {};

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

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import AbstractMapController from '@symfony/ux-map/abstract-map-controller';
22
import 'leaflet/dist/leaflet.min.css';
3-
import { Marker, divIcon, map, tileLayer, marker } from 'leaflet';
3+
import * as L from 'leaflet';
44

55
class map_controller extends AbstractMapController {
66
connect() {
7-
Marker.prototype.options.icon = divIcon({
7+
L.Marker.prototype.options.icon = L.divIcon({
88
html: '<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" fill-rule="evenodd" stroke-linecap="round" clip-rule="evenodd" viewBox="0 0 500 820"><defs><linearGradient id="a" x1="0" x2="1" y1="0" y2="0" gradientTransform="matrix(0 -37.57 37.57 0 416.45 541)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#126FC6"/><stop offset="1" stop-color="#4C9CD1"/></linearGradient><linearGradient id="b" x1="0" x2="1" y1="0" y2="0" gradientTransform="matrix(0 -19.05 19.05 0 414.48 522.49)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#2E6C97"/><stop offset="1" stop-color="#3883B7"/></linearGradient></defs><circle cx="252.31" cy="266.24" r="83.99" fill="#fff"/><path fill="url(#a)" stroke="url(#b)" stroke-width="1.1" d="M416.54 503.61c-6.57 0-12.04 5.7-12.04 11.87 0 2.78 1.56 6.3 2.7 8.74l9.3 17.88 9.26-17.88c1.13-2.43 2.74-5.79 2.74-8.74 0-6.18-5.38-11.87-11.96-11.87Zm0 7.16a4.69 4.69 0 1 1-.02 9.4 4.69 4.69 0 0 1 .02-9.4Z" transform="translate(-7889.1 -9807.44) scale(19.54)"/></svg>',
99
iconSize: [25, 41],
1010
iconAnchor: [12.5, 41],
@@ -14,24 +14,24 @@ class map_controller extends AbstractMapController {
1414
super.connect();
1515
}
1616
doCreateMap({ center, zoom, options }) {
17-
const map$1 = map(this.element, {
17+
const map = L.map(this.element, {
1818
...options,
1919
center,
2020
zoom,
2121
});
22-
tileLayer(options.tileLayer.url, {
22+
L.tileLayer(options.tileLayer.url, {
2323
attribution: options.tileLayer.attribution,
2424
...options.tileLayer.options,
25-
}).addTo(map$1);
26-
return map$1;
25+
}).addTo(map);
26+
return map;
2727
}
2828
doCreateMarker(definition) {
2929
const { position, title, infoWindow, extra, rawOptions = {}, ...otherOptions } = definition;
30-
const marker$1 = marker(position, { title, ...otherOptions, ...rawOptions }).addTo(this.map);
30+
const marker = L.marker(position, { title, ...otherOptions, ...rawOptions }).addTo(this.map);
3131
if (infoWindow) {
32-
this.createInfoWindow({ definition: infoWindow, marker: marker$1 });
32+
this.createInfoWindow({ definition: infoWindow, marker });
3333
}
34-
return marker$1;
34+
return marker;
3535
}
3636
doCreateInfoWindow({ definition, marker, }) {
3737
const { headerContent, content, extra, rawOptions = {}, ...otherOptions } = definition;
@@ -54,6 +54,10 @@ class map_controller extends AbstractMapController {
5454
return [position.lat, position.lng];
5555
}));
5656
}
57+
augmentEventPayload(payload) {
58+
payload.L = L;
59+
return payload;
60+
}
5761
}
5862

5963
export { map_controller as default };

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

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,7 @@
11
import AbstractMapController from '@symfony/ux-map/abstract-map-controller';
22
import type { Point, MarkerDefinition } from '@symfony/ux-map/abstract-map-controller';
33
import 'leaflet/dist/leaflet.min.css';
4-
import {
5-
map as createMap,
6-
tileLayer as createTileLayer,
7-
marker as createMarker,
8-
divIcon,
9-
type Map as LeafletMap,
10-
Marker,
11-
type Popup,
12-
} from 'leaflet';
4+
import * as L from 'leaflet';
135
import type { MapOptions as LeafletMapOptions, MarkerOptions, PopupOptions } from 'leaflet';
146

157
type MapOptions = Pick<LeafletMapOptions, 'center' | 'zoom'> & {
@@ -18,14 +10,14 @@ type MapOptions = Pick<LeafletMapOptions, 'center' | 'zoom'> & {
1810

1911
export default class extends AbstractMapController<
2012
MapOptions,
21-
typeof LeafletMap,
13+
typeof L.Map,
2214
MarkerOptions,
23-
Marker,
24-
Popup,
15+
typeof L.Marker,
16+
typeof L.Popup,
2517
PopupOptions
2618
> {
2719
connect(): void {
28-
Marker.prototype.options.icon = divIcon({
20+
L.Marker.prototype.options.icon = L.divIcon({
2921
html: '<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" fill-rule="evenodd" stroke-linecap="round" clip-rule="evenodd" viewBox="0 0 500 820"><defs><linearGradient id="a" x1="0" x2="1" y1="0" y2="0" gradientTransform="matrix(0 -37.57 37.57 0 416.45 541)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#126FC6"/><stop offset="1" stop-color="#4C9CD1"/></linearGradient><linearGradient id="b" x1="0" x2="1" y1="0" y2="0" gradientTransform="matrix(0 -19.05 19.05 0 414.48 522.49)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#2E6C97"/><stop offset="1" stop-color="#3883B7"/></linearGradient></defs><circle cx="252.31" cy="266.24" r="83.99" fill="#fff"/><path fill="url(#a)" stroke="url(#b)" stroke-width="1.1" d="M416.54 503.61c-6.57 0-12.04 5.7-12.04 11.87 0 2.78 1.56 6.3 2.7 8.74l9.3 17.88 9.26-17.88c1.13-2.43 2.74-5.79 2.74-8.74 0-6.18-5.38-11.87-11.96-11.87Zm0 7.16a4.69 4.69 0 1 1-.02 9.4 4.69 4.69 0 0 1 .02-9.4Z" transform="translate(-7889.1 -9807.44) scale(19.54)"/></svg>',
3022
iconSize: [25, 41],
3123
iconAnchor: [12.5, 41],
@@ -35,25 +27,25 @@ export default class extends AbstractMapController<
3527
super.connect();
3628
}
3729

38-
protected doCreateMap({ center, zoom, options }: { center: Point; zoom: number; options: MapOptions }): LeafletMap {
39-
const map = createMap(this.element, {
30+
protected doCreateMap({ center, zoom, options }: { center: Point; zoom: number; options: MapOptions }): L.Map {
31+
const map = L.map(this.element, {
4032
...options,
4133
center,
4234
zoom,
4335
});
4436

45-
createTileLayer(options.tileLayer.url, {
37+
L.tileLayer(options.tileLayer.url, {
4638
attribution: options.tileLayer.attribution,
4739
...options.tileLayer.options,
4840
}).addTo(map);
4941

5042
return map;
5143
}
5244

53-
protected doCreateMarker(definition: MarkerDefinition): Marker {
45+
protected doCreateMarker(definition: MarkerDefinition): L.Marker {
5446
const { position, title, infoWindow, extra, rawOptions = {}, ...otherOptions } = definition;
5547

56-
const marker = createMarker(position, { title, ...otherOptions, ...rawOptions }).addTo(this.map);
48+
const marker = L.marker(position, { title, ...otherOptions, ...rawOptions }).addTo(this.map);
5749

5850
if (infoWindow) {
5951
this.createInfoWindow({ definition: infoWindow, marker });
@@ -67,8 +59,8 @@ export default class extends AbstractMapController<
6759
marker,
6860
}: {
6961
definition: MarkerDefinition['infoWindow'];
70-
marker: Marker;
71-
}): Popup {
62+
marker: L.Marker;
63+
}): L.Popup {
7264
const { headerContent, content, extra, rawOptions = {}, ...otherOptions } = definition;
7365

7466
marker.bindPopup([headerContent, content].filter((x) => x).join('<br>'), { ...otherOptions, ...rawOptions });
@@ -89,11 +81,17 @@ export default class extends AbstractMapController<
8981
}
9082

9183
this.map.fitBounds(
92-
this.markers.map((marker: Marker) => {
84+
this.markers.map((marker: L.Marker) => {
9385
const position = marker.getLatLng();
9486

9587
return [position.lat, position.lng];
9688
})
9789
);
9890
}
91+
92+
protected augmentEventPayload(payload: Record<string, unknown>): Record<string, unknown> {
93+
payload.L = L;
94+
95+
return payload;
96+
}
9997
}

0 commit comments

Comments
 (0)