Skip to content

Commit

Permalink
[Map] Listen for zoom, center, markers and polygons changes
Browse files Browse the repository at this point in the history
  • Loading branch information
Kocal committed Nov 17, 2024
1 parent 1fe568b commit 81087ca
Show file tree
Hide file tree
Showing 17 changed files with 463 additions and 83 deletions.
1 change: 1 addition & 0 deletions src/Map/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

- Add method `Symfony\UX\Map\Renderer\AbstractRenderer::tapOptions()`, to allow Renderer to modify options before rendering a Map.
- Add `ux_map.google_maps.default_map_id` configuration to set the Google ``Map ID``
- 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.

## 2.20

Expand Down
20 changes: 16 additions & 4 deletions src/Map/assets/dist/abstract_map_controller.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ export type Point = {
lng: number;
};
export type MarkerDefinition<MarkerOptions, InfoWindowOptions> = {
'@id': string;
position: Point;
title: string | null;
infoWindow?: Omit<InfoWindowDefinition<InfoWindowOptions>, 'position'>;
rawOptions?: MarkerOptions;
extra: Record<string, unknown>;
};
export type PolygonDefinition<PolygonOptions, InfoWindowOptions> = {
'@id': string;
infoWindow?: Omit<InfoWindowDefinition<InfoWindowOptions>, 'position'>;
points: Array<Point>;
title: string | null;
Expand All @@ -29,7 +31,12 @@ export type InfoWindowDefinition<InfoWindowOptions> = {
export default abstract class<MapOptions, Map, MarkerOptions, Marker, InfoWindowOptions, InfoWindow, PolygonOptions, Polygon> extends Controller<HTMLElement> {
static values: {
providerOptions: ObjectConstructor;
view: ObjectConstructor;
center: ObjectConstructor;
zoom: NumberConstructor;
fitBoundsToMarkers: BooleanConstructor;
markers: ArrayConstructor;
polygons: ArrayConstructor;
options: ObjectConstructor;
};
centerValue: Point | null;
zoomValue: number | null;
Expand All @@ -38,18 +45,19 @@ export default abstract class<MapOptions, Map, MarkerOptions, Marker, InfoWindow
polygonsValue: Array<PolygonDefinition<PolygonOptions, InfoWindowOptions>>;
optionsValue: MapOptions;
protected map: Map;
protected markers: Array<Marker>;
protected markers: globalThis.Map<any, any>;
protected infoWindows: Array<InfoWindow>;
protected polygons: Array<Polygon>;
protected polygons: globalThis.Map<any, any>;
connect(): void;
protected abstract doCreateMap({ center, zoom, options, }: {
center: Point | null;
zoom: number | null;
options: MapOptions;
}): Map;
createMarker(definition: MarkerDefinition<MarkerOptions, InfoWindowOptions>): Marker;
createPolygon(definition: PolygonDefinition<PolygonOptions, InfoWindowOptions>): Polygon;
protected abstract removeMarker(marker: Marker): void;
protected abstract doCreateMarker(definition: MarkerDefinition<MarkerOptions, InfoWindowOptions>): Marker;
createPolygon(definition: PolygonDefinition<PolygonOptions, InfoWindowOptions>): Polygon;
protected abstract doCreatePolygon(definition: PolygonDefinition<PolygonOptions, InfoWindowOptions>): Polygon;
protected createInfoWindow({ definition, element, }: {
definition: MarkerDefinition<MarkerOptions, InfoWindowOptions>['infoWindow'] | PolygonDefinition<PolygonOptions, InfoWindowOptions>['infoWindow'];
Expand All @@ -64,4 +72,8 @@ export default abstract class<MapOptions, Map, MarkerOptions, Marker, InfoWindow
}): InfoWindow;
protected abstract doFitBoundsToMarkers(): void;
protected abstract dispatchEvent(name: string, payload: Record<string, unknown>): void;
abstract centerValueChanged(): void;
abstract zoomValueChanged(): void;
markersValueChanged(): void;
polygonsValueChanged(): void;
}
54 changes: 47 additions & 7 deletions src/Map/assets/dist/abstract_map_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { Controller } from '@hotwired/stimulus';
class default_1 extends Controller {
constructor() {
super(...arguments);
this.markers = [];
this.markers = new Map();
this.infoWindows = [];
this.polygons = [];
this.polygons = new Map();
}
connect() {
const options = this.optionsValue;
Expand All @@ -18,23 +18,25 @@ class default_1 extends Controller {
}
this.dispatchEvent('connect', {
map: this.map,
markers: this.markers,
polygons: this.polygons,
markers: [...this.markers.values()],
polygons: [...this.polygons.values()],
infoWindows: this.infoWindows,
});
}
createMarker(definition) {
this.dispatchEvent('marker:before-create', { definition });
const marker = this.doCreateMarker(definition);
this.dispatchEvent('marker:after-create', { marker });
this.markers.push(marker);
marker['@id'] = definition['@id'];
this.markers.set(definition['@id'], marker);
return marker;
}
createPolygon(definition) {
this.dispatchEvent('polygon:before-create', { definition });
const polygon = this.doCreatePolygon(definition);
this.dispatchEvent('polygon:after-create', { polygon });
this.polygons.push(polygon);
polygon['@id'] = definition['@id'];
this.polygons.set(definition['@id'], polygon);
return polygon;
}
createInfoWindow({ definition, element, }) {
Expand All @@ -44,10 +46,48 @@ class default_1 extends Controller {
this.infoWindows.push(infoWindow);
return infoWindow;
}
markersValueChanged() {
if (this.map) {
this.markers.forEach((marker) => {
if (!this.markersValue.find((m) => m['@id'] === marker['@id'])) {
this.removeMarker(marker);
this.markers.delete(marker['@id']);
}
});
this.markersValue.forEach((marker) => {
if (!this.markers.has(marker['@id'])) {
this.createMarker(marker);
}
});
if (this.fitBoundsToMarkersValue) {
this.doFitBoundsToMarkers();
}
}
}
polygonsValueChanged() {
if (this.map) {
this.polygons.forEach((polygon) => {
if (!this.polygonsValue.find((p) => p['@id'] === polygon['@id'])) {
polygon.remove();
this.polygons.delete(polygon['@id']);
}
});
this.polygonsValue.forEach((polygon) => {
if (!this.polygons.has(polygon['@id'])) {
this.createPolygon(polygon);
}
});
}
}
}
default_1.values = {
providerOptions: Object,
view: Object,
center: Object,
zoom: Number,
fitBoundsToMarkers: Boolean,
markers: Array,
polygons: Array,
options: Object,
};

export { default_1 as default };
80 changes: 71 additions & 9 deletions src/Map/assets/src/abstract_map_controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Controller } from '@hotwired/stimulus';
export type Point = { lat: number; lng: number };

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

export type PolygonDefinition<PolygonOptions, InfoWindowOptions> = {
'@id': string;
infoWindow?: Omit<InfoWindowDefinition<InfoWindowOptions>, 'position'>;
points: Array<Point>;
title: string | null;
Expand Down Expand Up @@ -59,7 +61,12 @@ export default abstract class<
> extends Controller<HTMLElement> {
static values = {
providerOptions: Object,
view: Object,
center: Object,
zoom: Number,
fitBoundsToMarkers: Boolean,
markers: Array,
polygons: Array,
options: Object,
};

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

protected map: Map;
protected markers: Array<Marker> = [];
protected markers = new Map<Marker>();
protected infoWindows: Array<InfoWindow> = [];
protected polygons: Array<Polygon> = [];
protected polygons = new Map<Polygon>();

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

this.dispatchEvent('connect', {
map: this.map,
markers: this.markers,
polygons: this.polygons,
markers: [...this.markers.values()],
polygons: [...this.polygons.values()],
infoWindows: this.infoWindows,
});
}
Expand All @@ -112,20 +119,29 @@ export default abstract class<
const marker = this.doCreateMarker(definition);
this.dispatchEvent('marker:after-create', { marker });

this.markers.push(marker);
marker['@id'] = definition['@id'];

this.markers.set(definition['@id'], marker);

return marker;
}

createPolygon(definition: PolygonDefinition<PolygonOptions, InfoWindowOptions>): Polygon {
protected abstract removeMarker(marker: Marker): void;

protected abstract doCreateMarker(definition: MarkerDefinition<MarkerOptions, InfoWindowOptions>): Marker;

public createPolygon(definition: PolygonDefinition<PolygonOptions, InfoWindowOptions>): Polygon {
this.dispatchEvent('polygon:before-create', { definition });
const polygon = this.doCreatePolygon(definition);
this.dispatchEvent('polygon:after-create', { polygon });
this.polygons.push(polygon);

polygon['@id'] = definition['@id'];

this.polygons.set(definition['@id'], polygon);

return polygon;
}

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

protected createInfoWindow({
Expand Down Expand Up @@ -162,4 +178,50 @@ export default abstract class<
protected abstract doFitBoundsToMarkers(): void;

protected abstract dispatchEvent(name: string, payload: Record<string, unknown>): void;

public abstract centerValueChanged(): void;

public abstract zoomValueChanged(): void;

public markersValueChanged(): void {
if (this.map) {
// Remove markers that are not in the new list
this.markers.forEach((marker) => {
if (!this.markersValue.find((m) => m['@id'] === marker['@id'])) {
this.removeMarker(marker);
this.markers.delete(marker['@id']);
}
});

// Add new markers
this.markersValue.forEach((marker) => {
if (!this.markers.has(marker['@id'])) {
this.createMarker(marker);
}
});

if (this.fitBoundsToMarkersValue) {
this.doFitBoundsToMarkers();
}
}
}

public polygonsValueChanged(): void {
if (this.map) {
// Remove polygons that are not in the new list
this.polygons.forEach((polygon) => {
if (!this.polygonsValue.find((p) => p['@id'] === polygon['@id'])) {
polygon.remove();
this.polygons.delete(polygon['@id']);
}
});

// Add new polygons
this.polygonsValue.forEach((polygon) => {
if (!this.polygons.has(polygon['@id'])) {
this.createPolygon(polygon);
}
});
}
}
}
Loading

0 comments on commit 81087ca

Please sign in to comment.