Skip to content

Commit 00fc2d4

Browse files
committed
[Map] Listen for zoom, center, markers and polygons changes, and update JS tests
1 parent 1fe568b commit 00fc2d4

20 files changed

+520
-162
lines changed

src/Map/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
- Add method `Symfony\UX\Map\Renderer\AbstractRenderer::tapOptions()`, to allow Renderer to modify options before rendering a Map.
66
- Add `ux_map.google_maps.default_map_id` configuration to set the Google ``Map ID``
7+
- Add compatibility with [Live Components](https://symfony.com/bundles/ux-live-component/current/index.html), for the moment only zoom, center, markers and polygons are supported.
78

89
## 2.20
910

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

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ export type Point = {
44
lng: number;
55
};
66
export type MarkerDefinition<MarkerOptions, InfoWindowOptions> = {
7+
'@id': string;
78
position: Point;
89
title: string | null;
910
infoWindow?: Omit<InfoWindowDefinition<InfoWindowOptions>, 'position'>;
1011
rawOptions?: MarkerOptions;
1112
extra: Record<string, unknown>;
1213
};
1314
export type PolygonDefinition<PolygonOptions, InfoWindowOptions> = {
15+
'@id': string;
1416
infoWindow?: Omit<InfoWindowDefinition<InfoWindowOptions>, 'position'>;
1517
points: Array<Point>;
1618
title: string | null;
@@ -29,7 +31,12 @@ export type InfoWindowDefinition<InfoWindowOptions> = {
2931
export default abstract class<MapOptions, Map, MarkerOptions, Marker, InfoWindowOptions, InfoWindow, PolygonOptions, Polygon> extends Controller<HTMLElement> {
3032
static values: {
3133
providerOptions: ObjectConstructor;
32-
view: ObjectConstructor;
34+
center: ObjectConstructor;
35+
zoom: NumberConstructor;
36+
fitBoundsToMarkers: BooleanConstructor;
37+
markers: ArrayConstructor;
38+
polygons: ArrayConstructor;
39+
options: ObjectConstructor;
3340
};
3441
centerValue: Point | null;
3542
zoomValue: number | null;
@@ -38,18 +45,19 @@ export default abstract class<MapOptions, Map, MarkerOptions, Marker, InfoWindow
3845
polygonsValue: Array<PolygonDefinition<PolygonOptions, InfoWindowOptions>>;
3946
optionsValue: MapOptions;
4047
protected map: Map;
41-
protected markers: Array<Marker>;
48+
protected markers: globalThis.Map<any, any>;
4249
protected infoWindows: Array<InfoWindow>;
43-
protected polygons: Array<Polygon>;
50+
protected polygons: globalThis.Map<any, any>;
4451
connect(): void;
4552
protected abstract doCreateMap({ center, zoom, options, }: {
4653
center: Point | null;
4754
zoom: number | null;
4855
options: MapOptions;
4956
}): Map;
5057
createMarker(definition: MarkerDefinition<MarkerOptions, InfoWindowOptions>): Marker;
51-
createPolygon(definition: PolygonDefinition<PolygonOptions, InfoWindowOptions>): Polygon;
58+
protected abstract removeMarker(marker: Marker): void;
5259
protected abstract doCreateMarker(definition: MarkerDefinition<MarkerOptions, InfoWindowOptions>): Marker;
60+
createPolygon(definition: PolygonDefinition<PolygonOptions, InfoWindowOptions>): Polygon;
5361
protected abstract doCreatePolygon(definition: PolygonDefinition<PolygonOptions, InfoWindowOptions>): Polygon;
5462
protected createInfoWindow({ definition, element, }: {
5563
definition: MarkerDefinition<MarkerOptions, InfoWindowOptions>['infoWindow'] | PolygonDefinition<PolygonOptions, InfoWindowOptions>['infoWindow'];
@@ -64,4 +72,8 @@ export default abstract class<MapOptions, Map, MarkerOptions, Marker, InfoWindow
6472
}): InfoWindow;
6573
protected abstract doFitBoundsToMarkers(): void;
6674
protected abstract dispatchEvent(name: string, payload: Record<string, unknown>): void;
75+
abstract centerValueChanged(): void;
76+
abstract zoomValueChanged(): void;
77+
markersValueChanged(): void;
78+
polygonsValueChanged(): void;
6779
}

src/Map/assets/dist/abstract_map_controller.js

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import { Controller } from '@hotwired/stimulus';
33
class default_1 extends Controller {
44
constructor() {
55
super(...arguments);
6-
this.markers = [];
6+
this.markers = new Map();
77
this.infoWindows = [];
8-
this.polygons = [];
8+
this.polygons = new Map();
99
}
1010
connect() {
1111
const options = this.optionsValue;
@@ -18,23 +18,25 @@ class default_1 extends Controller {
1818
}
1919
this.dispatchEvent('connect', {
2020
map: this.map,
21-
markers: this.markers,
22-
polygons: this.polygons,
21+
markers: [...this.markers.values()],
22+
polygons: [...this.polygons.values()],
2323
infoWindows: this.infoWindows,
2424
});
2525
}
2626
createMarker(definition) {
2727
this.dispatchEvent('marker:before-create', { definition });
2828
const marker = this.doCreateMarker(definition);
2929
this.dispatchEvent('marker:after-create', { marker });
30-
this.markers.push(marker);
30+
marker['@id'] = definition['@id'];
31+
this.markers.set(definition['@id'], marker);
3132
return marker;
3233
}
3334
createPolygon(definition) {
3435
this.dispatchEvent('polygon:before-create', { definition });
3536
const polygon = this.doCreatePolygon(definition);
3637
this.dispatchEvent('polygon:after-create', { polygon });
37-
this.polygons.push(polygon);
38+
polygon['@id'] = definition['@id'];
39+
this.polygons.set(definition['@id'], polygon);
3840
return polygon;
3941
}
4042
createInfoWindow({ definition, element, }) {
@@ -44,10 +46,48 @@ class default_1 extends Controller {
4446
this.infoWindows.push(infoWindow);
4547
return infoWindow;
4648
}
49+
markersValueChanged() {
50+
if (this.map) {
51+
this.markers.forEach((marker) => {
52+
if (!this.markersValue.find((m) => m['@id'] === marker['@id'])) {
53+
this.removeMarker(marker);
54+
this.markers.delete(marker['@id']);
55+
}
56+
});
57+
this.markersValue.forEach((marker) => {
58+
if (!this.markers.has(marker['@id'])) {
59+
this.createMarker(marker);
60+
}
61+
});
62+
if (this.fitBoundsToMarkersValue) {
63+
this.doFitBoundsToMarkers();
64+
}
65+
}
66+
}
67+
polygonsValueChanged() {
68+
if (this.map) {
69+
this.polygons.forEach((polygon) => {
70+
if (!this.polygonsValue.find((p) => p['@id'] === polygon['@id'])) {
71+
polygon.remove();
72+
this.polygons.delete(polygon['@id']);
73+
}
74+
});
75+
this.polygonsValue.forEach((polygon) => {
76+
if (!this.polygons.has(polygon['@id'])) {
77+
this.createPolygon(polygon);
78+
}
79+
});
80+
}
81+
}
4782
}
4883
default_1.values = {
4984
providerOptions: Object,
50-
view: Object,
85+
center: Object,
86+
zoom: Number,
87+
fitBoundsToMarkers: Boolean,
88+
markers: Array,
89+
polygons: Array,
90+
options: Object,
5191
};
5292

5393
export { default_1 as default };

src/Map/assets/src/abstract_map_controller.ts

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Controller } from '@hotwired/stimulus';
33
export type Point = { lat: number; lng: number };
44

55
export type MarkerDefinition<MarkerOptions, InfoWindowOptions> = {
6+
'@id': string;
67
position: Point;
78
title: string | null;
89
infoWindow?: Omit<InfoWindowDefinition<InfoWindowOptions>, 'position'>;
@@ -20,6 +21,7 @@ export type MarkerDefinition<MarkerOptions, InfoWindowOptions> = {
2021
};
2122

2223
export type PolygonDefinition<PolygonOptions, InfoWindowOptions> = {
24+
'@id': string;
2325
infoWindow?: Omit<InfoWindowDefinition<InfoWindowOptions>, 'position'>;
2426
points: Array<Point>;
2527
title: string | null;
@@ -59,7 +61,12 @@ export default abstract class<
5961
> extends Controller<HTMLElement> {
6062
static values = {
6163
providerOptions: Object,
62-
view: Object,
64+
center: Object,
65+
zoom: Number,
66+
fitBoundsToMarkers: Boolean,
67+
markers: Array,
68+
polygons: Array,
69+
options: Object,
6370
};
6471

6572
declare centerValue: Point | null;
@@ -70,9 +77,9 @@ export default abstract class<
7077
declare optionsValue: MapOptions;
7178

7279
protected map: Map;
73-
protected markers: Array<Marker> = [];
80+
protected markers = new Map<Marker>();
7481
protected infoWindows: Array<InfoWindow> = [];
75-
protected polygons: Array<Polygon> = [];
82+
protected polygons = new Map<Polygon>();
7683

7784
connect() {
7885
const options = this.optionsValue;
@@ -91,8 +98,8 @@ export default abstract class<
9198

9299
this.dispatchEvent('connect', {
93100
map: this.map,
94-
markers: this.markers,
95-
polygons: this.polygons,
101+
markers: [...this.markers.values()],
102+
polygons: [...this.polygons.values()],
96103
infoWindows: this.infoWindows,
97104
});
98105
}
@@ -112,20 +119,29 @@ export default abstract class<
112119
const marker = this.doCreateMarker(definition);
113120
this.dispatchEvent('marker:after-create', { marker });
114121

115-
this.markers.push(marker);
122+
marker['@id'] = definition['@id'];
123+
124+
this.markers.set(definition['@id'], marker);
116125

117126
return marker;
118127
}
119128

120-
createPolygon(definition: PolygonDefinition<PolygonOptions, InfoWindowOptions>): Polygon {
129+
protected abstract removeMarker(marker: Marker): void;
130+
131+
protected abstract doCreateMarker(definition: MarkerDefinition<MarkerOptions, InfoWindowOptions>): Marker;
132+
133+
public createPolygon(definition: PolygonDefinition<PolygonOptions, InfoWindowOptions>): Polygon {
121134
this.dispatchEvent('polygon:before-create', { definition });
122135
const polygon = this.doCreatePolygon(definition);
123136
this.dispatchEvent('polygon:after-create', { polygon });
124-
this.polygons.push(polygon);
137+
138+
polygon['@id'] = definition['@id'];
139+
140+
this.polygons.set(definition['@id'], polygon);
141+
125142
return polygon;
126143
}
127144

128-
protected abstract doCreateMarker(definition: MarkerDefinition<MarkerOptions, InfoWindowOptions>): Marker;
129145
protected abstract doCreatePolygon(definition: PolygonDefinition<PolygonOptions, InfoWindowOptions>): Polygon;
130146

131147
protected createInfoWindow({
@@ -162,4 +178,50 @@ export default abstract class<
162178
protected abstract doFitBoundsToMarkers(): void;
163179

164180
protected abstract dispatchEvent(name: string, payload: Record<string, unknown>): void;
181+
182+
public abstract centerValueChanged(): void;
183+
184+
public abstract zoomValueChanged(): void;
185+
186+
public markersValueChanged(): void {
187+
if (this.map) {
188+
// Remove markers that are not in the new list
189+
this.markers.forEach((marker) => {
190+
if (!this.markersValue.find((m) => m['@id'] === marker['@id'])) {
191+
this.removeMarker(marker);
192+
this.markers.delete(marker['@id']);
193+
}
194+
});
195+
196+
// Add new markers
197+
this.markersValue.forEach((marker) => {
198+
if (!this.markers.has(marker['@id'])) {
199+
this.createMarker(marker);
200+
}
201+
});
202+
203+
if (this.fitBoundsToMarkersValue) {
204+
this.doFitBoundsToMarkers();
205+
}
206+
}
207+
}
208+
209+
public polygonsValueChanged(): void {
210+
if (this.map) {
211+
// Remove polygons that are not in the new list
212+
this.polygons.forEach((polygon) => {
213+
if (!this.polygonsValue.find((p) => p['@id'] === polygon['@id'])) {
214+
polygon.remove();
215+
this.polygons.delete(polygon['@id']);
216+
}
217+
});
218+
219+
// Add new polygons
220+
this.polygonsValue.forEach((polygon) => {
221+
if (!this.polygons.has(polygon['@id'])) {
222+
this.createPolygon(polygon);
223+
}
224+
});
225+
}
226+
}
165227
}

0 commit comments

Comments
 (0)