From 9e0ef18369b676bc0e56e370a4fc862445dd5ad9 Mon Sep 17 00:00:00 2001 From: Sylvain Blondeau Date: Mon, 4 Nov 2024 22:10:32 +0100 Subject: [PATCH 01/12] add polyline support --- .../assets/dist/abstract_map_controller.d.ts | 24 +++++-- .../assets/dist/abstract_map_controller.js | 12 +++- src/Map/assets/src/abstract_map_controller.ts | 53 ++++++++++---- .../test/abstract_map_controller.test.ts | 49 ++++++++++++- src/Map/doc/index.rst | 18 ++++- .../Google/assets/dist/map_controller.d.ts | 7 +- .../Google/assets/dist/map_controller.js | 46 +++++++++++- .../Google/assets/src/map_controller.ts | 48 ++++++++++++- .../Google/assets/test/map_controller.test.ts | 2 +- .../Google/tests/GoogleRendererTest.php | 12 ++-- .../Leaflet/assets/dist/map_controller.d.ts | 13 ++-- .../Leaflet/assets/dist/map_controller.js | 23 +++++- .../Leaflet/assets/src/map_controller.ts | 28 ++++++-- .../assets/test/map_controller.test.ts | 2 +- .../Leaflet/tests/LeafletRendererTest.php | 6 +- src/Map/src/Map.php | 26 +++++-- src/Map/src/Polyline.php | 70 +++++++++++++++++++ src/Map/src/Twig/MapRuntime.php | 8 ++- src/Map/src/Twig/UXMapComponent.php | 7 +- src/Map/tests/MapFactoryTest.php | 54 ++++++++++++++ src/Map/tests/MapTest.php | 56 +++++++++++++++ 21 files changed, 505 insertions(+), 59 deletions(-) create mode 100644 src/Map/src/Polyline.php diff --git a/src/Map/assets/dist/abstract_map_controller.d.ts b/src/Map/assets/dist/abstract_map_controller.d.ts index f7f9ffd8096..4e3200d89d7 100644 --- a/src/Map/assets/dist/abstract_map_controller.d.ts +++ b/src/Map/assets/dist/abstract_map_controller.d.ts @@ -3,12 +3,13 @@ export type Point = { lat: number; lng: number; }; -export type MapView = { +export type MapView = { center: Point | null; zoom: number | null; fitBoundsToMarkers: boolean; markers: Array>; polygons: Array>; + polylines: Array>; options: Options; }; export type MarkerDefinition = { @@ -25,6 +26,13 @@ export type PolygonDefinition = { rawOptions?: PolygonOptions; extra: Record; }; +export type PolylineDefinition = { + infoWindow?: Omit, 'position'>; + points: Array; + title: string | null; + rawOptions?: PolylineOptions; + extra: Record; +}; export type InfoWindowDefinition = { headerContent: string | null; content: string | null; @@ -34,16 +42,17 @@ export type InfoWindowDefinition = { rawOptions?: InfoWindowOptions; extra: Record; }; -export default abstract class extends Controller { +export default abstract class extends Controller { static values: { providerOptions: ObjectConstructor; view: ObjectConstructor; }; - viewValue: MapView; + viewValue: MapView; protected map: Map; protected markers: Array; protected infoWindows: Array; protected polygons: Array; + protected polylines: Array; connect(): void; protected abstract doCreateMap({ center, zoom, options, }: { center: Point | null; @@ -52,11 +61,13 @@ export default abstract class): Marker; createPolygon(definition: PolygonDefinition): Polygon; + createPolyline(definition: PolylineDefinition): Polyline; protected abstract doCreateMarker(definition: MarkerDefinition): Marker; protected abstract doCreatePolygon(definition: PolygonDefinition): Polygon; + protected abstract doCreatePolyline(definition: PolylineDefinition): Polyline; protected createInfoWindow({ definition, element, }: { - definition: MarkerDefinition['infoWindow'] | PolygonDefinition['infoWindow']; - element: Marker | Polygon; + definition: MarkerDefinition['infoWindow'] | PolygonDefinition['infoWindow'] | PolylineDefinition['infoWindow']; + element: Marker | Polygon| Polyline; }): InfoWindow; protected abstract doCreateInfoWindow({ definition, element, }: { definition: MarkerDefinition['infoWindow']; @@ -64,6 +75,9 @@ export default abstract class['infoWindow']; element: Polygon; + }| { + definition: PolylineDefinition['infoWindow']; + element: Polyline; }): InfoWindow; protected abstract doFitBoundsToMarkers(): void; protected abstract dispatchEvent(name: string, payload: Record): void; diff --git a/src/Map/assets/dist/abstract_map_controller.js b/src/Map/assets/dist/abstract_map_controller.js index 83cc772e76a..c7c6651af17 100644 --- a/src/Map/assets/dist/abstract_map_controller.js +++ b/src/Map/assets/dist/abstract_map_controller.js @@ -6,13 +6,15 @@ class default_1 extends Controller { this.markers = []; this.infoWindows = []; this.polygons = []; + this.polylines = []; } connect() { - const { center, zoom, options, markers, polygons, fitBoundsToMarkers } = this.viewValue; + const { center, zoom, options, markers, polygons, polylines, fitBoundsToMarkers } = this.viewValue; this.dispatchEvent('pre-connect', { options }); this.map = this.doCreateMap({ center, zoom, options }); markers.forEach((marker) => this.createMarker(marker)); polygons.forEach((polygon) => this.createPolygon(polygon)); + polylines.forEach((polyline) => this.createPolyline(polyline)); if (fitBoundsToMarkers) { this.doFitBoundsToMarkers(); } @@ -20,6 +22,7 @@ class default_1 extends Controller { map: this.map, markers: this.markers, polygons: this.polygons, + polylines: this.polylines, infoWindows: this.infoWindows, }); } @@ -37,6 +40,13 @@ class default_1 extends Controller { this.polygons.push(polygon); return polygon; } + createPolyline(definition) { + this.dispatchEvent('polyline:before-create', { definition }); + const polyline = this.doCreatePolyline(definition); + this.dispatchEvent('polyline:after-create', { polyline }); + this.polylines.push(polyline); + return polyline; + } createInfoWindow({ definition, element, }) { this.dispatchEvent('info-window:before-create', { definition, element }); const infoWindow = this.doCreateInfoWindow({ definition, element }); diff --git a/src/Map/assets/src/abstract_map_controller.ts b/src/Map/assets/src/abstract_map_controller.ts index bae763cc529..84b41938555 100644 --- a/src/Map/assets/src/abstract_map_controller.ts +++ b/src/Map/assets/src/abstract_map_controller.ts @@ -2,12 +2,13 @@ import { Controller } from '@hotwired/stimulus'; export type Point = { lat: number; lng: number }; -export type MapView = { +export type MapView = { center: Point | null; zoom: number | null; fitBoundsToMarkers: boolean; markers: Array>; polygons: Array>; + polylines: Array>; options: Options; }; @@ -36,6 +37,14 @@ export type PolygonDefinition = { extra: Record; }; +export type PolylineDefinition = { + infoWindow?: Omit, 'position'>; + points: Array; + title: string | null; + rawOptions?: PolylineOptions; + extra: Record; +}; + export type InfoWindowDefinition = { headerContent: string | null; content: string | null; @@ -56,7 +65,7 @@ export type InfoWindowDefinition = { extra: Record; }; -export default abstract class< +export default abstract class < MapOptions, Map, MarkerOptions, @@ -65,18 +74,21 @@ export default abstract class< InfoWindow, PolygonOptions, Polygon, + PolylineOptions, + Polyline, > extends Controller { static values = { providerOptions: Object, view: Object, }; - declare viewValue: MapView; + declare viewValue: MapView; protected map: Map; protected markers: Array = []; protected infoWindows: Array = []; protected polygons: Array = []; + protected polylines: Array = []; connect() { const { center, zoom, options, markers, polygons, fitBoundsToMarkers } = this.viewValue; @@ -89,6 +101,8 @@ export default abstract class< polygons.forEach((polygon) => this.createPolygon(polygon)); + polylines.forEach((polyline) => this.createPolyline(polyline)); + if (fitBoundsToMarkers) { this.doFitBoundsToMarkers(); } @@ -97,6 +111,7 @@ export default abstract class< map: this.map, markers: this.markers, polygons: this.polygons, + polylines: this.polylines, infoWindows: this.infoWindows, }); } @@ -129,17 +144,27 @@ export default abstract class< return polygon; } + createPolyline(definition: PolylineDefinition): Polyline { + this.dispatchEvent('polyline:before-create', { definition }); + const polyline = this.doCreatePolyline(definition); + this.dispatchEvent('polyline:after-create', { polyline }); + this.polylines.push(polyline); + return polyline; + } + protected abstract doCreateMarker(definition: MarkerDefinition): Marker; protected abstract doCreatePolygon(definition: PolygonDefinition): Polygon; + protected abstract doCreatePolyline(definition: PolylineDefinition): Polyline; protected createInfoWindow({ definition, element, }: { definition: - | MarkerDefinition['infoWindow'] - | PolygonDefinition['infoWindow']; - element: Marker | Polygon; + | MarkerDefinition['infoWindow'] + | PolygonDefinition['infoWindow'] + | PolylineDefinition['infoWindow']; + element: Marker | Polygon | Polyline; }): InfoWindow { this.dispatchEvent('info-window:before-create', { definition, element }); const infoWindow = this.doCreateInfoWindow({ definition, element }); @@ -155,13 +180,17 @@ export default abstract class< element, }: | { - definition: MarkerDefinition['infoWindow']; - element: Marker; - } + definition: MarkerDefinition['infoWindow']; + element: Marker; + } + | { + definition: PolygonDefinition['infoWindow']; + element: Polygon; + } | { - definition: PolygonDefinition['infoWindow']; - element: Polygon; - }): InfoWindow; + definition: PolylineDefinition['infoWindow']; + element: Polyline; + }): InfoWindow; protected abstract doFitBoundsToMarkers(): void; diff --git a/src/Map/assets/test/abstract_map_controller.test.ts b/src/Map/assets/test/abstract_map_controller.test.ts index c9e0e38aeba..b1a20a713b7 100644 --- a/src/Map/assets/test/abstract_map_controller.test.ts +++ b/src/Map/assets/test/abstract_map_controller.test.ts @@ -35,6 +35,15 @@ class MyMapController extends AbstractMapController { return polygon; } + doCreatePolyline(definition) { + const polyline = { polyline: 'polyline', title: definition.title }; + + if (definition.infoWindow) { + this.createInfoWindow({ definition: definition.infoWindow, element: polyline }); + } + return polyline; + } + doCreateInfoWindow({ definition, element }) { if (element.marker) { return { infoWindow: 'infoWindow', headerContent: definition.headerContent, marker: element.title }; @@ -42,6 +51,9 @@ class MyMapController extends AbstractMapController { if (element.polygon) { return { infoWindow: 'infoWindow', headerContent: definition.headerContent, polygon: element.title }; } + if (element.polyline) { + return { infoWindow: 'infoWindow', headerContent: definition.headerContent, polyline: element.title }; + } } doFitBoundsToMarkers() { @@ -113,6 +125,32 @@ describe('AbstractMapController', () => { "autoClose": true } } + ], + "polylines": [ + { + "coordinates": [ + { "lat": 48.858844, "lng": 2.294351 }, + { "lat": 48.853, "lng": 2.3499 }, + { "lat": 48.8566, "lng": 2.3522 } + ], + "title": "Polyline 1", + "infoWindow": null + }, + { + "coordinates": [ + { "lat": 45.764043, "lng": 4.835659 }, + { "lat": 45.750000, "lng": 4.850000 }, + { "lat": 45.770000, "lng": 4.820000 } + ], + "title": "Polyline 2", + "infoWindow": { + "headerContent": "Polyline 2", + "content": "A polyline around Lyon with some additional info.", + "position": null, + "opened": false, + "autoClose": true + } + } ] }'> @@ -123,7 +161,7 @@ describe('AbstractMapController', () => { clearDOM(); }); - it('connect and create map, marker, polygon and info window', async () => { + it('connect and create map, marker, polygon, polyline and info window', async () => { const div = getByTestId(container, 'map'); expect(div).not.toHaveClass('connected'); @@ -140,6 +178,10 @@ describe('AbstractMapController', () => { { polygon: 'polygon', title: 'Polygon 1' }, { polygon: 'polygon', title: 'Polygon 2' }, ]); + expect(controller.polylines).toEqual([ + { polyline: 'polyline', title: 'Polyline 1' }, + { polyline: 'polyline', title: 'Polyline 2' }, + ]); expect(controller.infoWindows).toEqual([ { headerContent: 'Lyon', @@ -151,6 +193,11 @@ describe('AbstractMapController', () => { infoWindow: 'infoWindow', polygon: 'Polygon 2', }, + { + headerContent: 'Polyline 2', + infoWindow: 'infoWindow', + polyline: 'Polyline 2', + }, ]); }); }); diff --git a/src/Map/doc/index.rst b/src/Map/doc/index.rst index d950ebfe983..dd6fa3c31e6 100644 --- a/src/Map/doc/index.rst +++ b/src/Map/doc/index.rst @@ -114,8 +114,20 @@ A map is created by calling ``new Map()``. You can configure the center, zoom, a ), ) ; + // 3. You can also add Polylines, which represents a path made by a series of `Point` instances + $myMap->addPolyline(new Polyline( + points: [ + new Point(48.8566, 2.3522), + new Point(45.7640, 4.8357), + new Point(43.2965, 5.3698), + new Point(44.8378, -0.5792), + ], + infoWindow: new InfoWindow( + content: 'A line passing through Paris, Lyon, Marseille, Bordeaux', + ), + )); - // 3. You can also add Polygons, which represents an area enclosed by a series of `Point` instances + // 4. You can also add Polygons, which represents an area enclosed by a series of `Point` instances $myMap->addPolygon(new Polygon( points: [ new Point(48.8566, 2.3522), @@ -124,11 +136,11 @@ A map is created by calling ``new Map()``. You can configure the center, zoom, a new Point(44.8378, -0.5792), ], infoWindow: new InfoWindow( - content: 'Paris, Lyon, Marseille, Bordeaux', + content: 'A polygon enclosed by Paris, Lyon, Marseille, Bordeaux', ), )); - // 4. And inject the map in your template to render it + // 5. And inject the map in your template to render it return $this->render('contact/index.html.twig', [ 'my_map' => $myMap, ]); diff --git a/src/Map/src/Bridge/Google/assets/dist/map_controller.d.ts b/src/Map/src/Bridge/Google/assets/dist/map_controller.d.ts index 5095762fc07..28f4d408442 100644 --- a/src/Map/src/Bridge/Google/assets/dist/map_controller.d.ts +++ b/src/Map/src/Bridge/Google/assets/dist/map_controller.d.ts @@ -1,5 +1,5 @@ import AbstractMapController from '@symfony/ux-map'; -import type { Point, MarkerDefinition, PolygonDefinition } from '@symfony/ux-map'; +import type { Point, MarkerDefinition, PolygonDefinition, PolylineDefinition } from '@symfony/ux-map'; import type { LoaderOptions } from '@googlemaps/js-api-loader'; type MapOptions = Pick; export default class extends AbstractMapController { @@ -16,9 +16,10 @@ export default class extends AbstractMapController): google.maps.marker.AdvancedMarkerElement; protected doCreatePolygon(definition: PolygonDefinition): google.maps.Polygon; + protected doCreatePolyline(definition: PolylineDefinition): google.maps.Polyline; protected doCreateInfoWindow({ definition, element, }: { - definition: MarkerDefinition['infoWindow'] | PolygonDefinition['infoWindow']; - element: google.maps.marker.AdvancedMarkerElement | google.maps.Polygon; + definition: MarkerDefinition['infoWindow'] | PolygonDefinition['infoWindow'] | PolylineDefinition['infoWindow']; + element: google.maps.marker.AdvancedMarkerElement | google.maps.Polygon | google.maps.Polyline; }): google.maps.InfoWindow; private createTextOrElement; private closeInfoWindowsExcept; diff --git a/src/Map/src/Bridge/Google/assets/dist/map_controller.js b/src/Map/src/Bridge/Google/assets/dist/map_controller.js index 30fbe283118..b150afe4737 100644 --- a/src/Map/src/Bridge/Google/assets/dist/map_controller.js +++ b/src/Map/src/Bridge/Google/assets/dist/map_controller.js @@ -7,13 +7,15 @@ let default_1$1 = class default_1 extends Controller { this.markers = []; this.infoWindows = []; this.polygons = []; + this.polylines = []; } connect() { - const { center, zoom, options, markers, polygons, fitBoundsToMarkers } = this.viewValue; + const { center, zoom, options, markers, polygons, polylines, fitBoundsToMarkers } = this.viewValue; this.dispatchEvent('pre-connect', { options }); this.map = this.doCreateMap({ center, zoom, options }); markers.forEach((marker) => this.createMarker(marker)); polygons.forEach((polygon) => this.createPolygon(polygon)); + polylines.forEach((polyline) => this.createPolyline(polyline)); if (fitBoundsToMarkers) { this.doFitBoundsToMarkers(); } @@ -21,6 +23,7 @@ let default_1$1 = class default_1 extends Controller { map: this.map, markers: this.markers, polygons: this.polygons, + polylines: this.polylines, infoWindows: this.infoWindows, }); } @@ -38,6 +41,13 @@ let default_1$1 = class default_1 extends Controller { this.polygons.push(polygon); return polygon; } + createPolyline(definition) { + this.dispatchEvent('polyline:before-create', { definition }); + const polyline = this.doCreatePolyline(definition); + this.dispatchEvent('polyline:after-create', { polyline }); + this.polylines.push(polyline); + return polyline; + } createInfoWindow({ definition, element, }) { this.dispatchEvent('info-window:before-create', { definition, element }); const infoWindow = this.doCreateInfoWindow({ definition, element }); @@ -110,7 +120,7 @@ class default_1 extends default_1$1 { const { points, title, infoWindow, rawOptions = {} } = definition; const polygon = new _google.maps.Polygon({ ...rawOptions, - paths: points, + path: points, map: this.map, }); if (title) { @@ -121,6 +131,21 @@ class default_1 extends default_1$1 { } return polygon; } + doCreatePolyline(definition) { + const { points, title, infoWindow, rawOptions = {} } = definition; + const polyline = new _google.maps.Polyline({ + ...rawOptions, + path: points, + map: this.map, + }); + if (title) { + polyline.set('title', title); + } + if (infoWindow) { + this.createInfoWindow({ definition: infoWindow, element: polyline }); + } + return polyline; + } doCreateInfoWindow({ definition, element, }) { const { headerContent, content, extra, rawOptions = {}, ...otherOptions } = definition; const infoWindow = new _google.maps.InfoWindow({ @@ -157,6 +182,23 @@ class default_1 extends default_1$1 { infoWindow.open({ map: this.map, anchor: element }); } } + else if (element instanceof google.maps.Polyline) { + element.addListener('click', (event) => { + if (definition.autoClose) { + this.closeInfoWindowsExcept(infoWindow); + } + infoWindow.setPosition(event.latLng); + infoWindow.open(this.map); + }); + if (definition.opened) { + const bounds = new google.maps.LatLngBounds(); + element.getPath().forEach((point) => { + bounds.extend(point); + }); + infoWindow.setPosition(bounds.getCenter()); + infoWindow.open({ map: this.map, anchor: element }); + } + } return infoWindow; } createTextOrElement(content) { diff --git a/src/Map/src/Bridge/Google/assets/src/map_controller.ts b/src/Map/src/Bridge/Google/assets/src/map_controller.ts index 05116d80253..ca81a7acfd9 100644 --- a/src/Map/src/Bridge/Google/assets/src/map_controller.ts +++ b/src/Map/src/Bridge/Google/assets/src/map_controller.ts @@ -8,7 +8,7 @@ */ import AbstractMapController from '@symfony/ux-map'; -import type { Point, MarkerDefinition, PolygonDefinition } from '@symfony/ux-map'; +import type { Point, MarkerDefinition, PolygonDefinition, PolylineDefinition } from '@symfony/ux-map'; import type { LoaderOptions } from '@googlemaps/js-api-loader'; import { Loader } from '@googlemaps/js-api-loader'; @@ -39,6 +39,8 @@ export default class extends AbstractMapController< google.maps.InfoWindow, google.maps.PolygonOptions, google.maps.Polygon + google.maps.PolylineOptions, + google.maps.Polyline > { static values = { providerOptions: Object, @@ -153,6 +155,28 @@ export default class extends AbstractMapController< return polygon; } + protected doCreatePolyline( + definition: PolylineDefinition + ): google.maps.Polyline { + const { points, title, infoWindow, rawOptions = {} } = definition; + + const polyline = new _google.maps.Polyline({ + ...rawOptions, + path: points, + map: this.map, + }); + + if (title) { + polyline.set('title', title); + } + + if (infoWindow) { + this.createInfoWindow({ definition: infoWindow, element: polyline }); + } + + return polyline; + } + protected doCreateInfoWindow({ definition, element, @@ -162,8 +186,9 @@ export default class extends AbstractMapController< google.maps.marker.AdvancedMarkerElementOptions, google.maps.InfoWindowOptions >['infoWindow'] - | PolygonDefinition['infoWindow']; - element: google.maps.marker.AdvancedMarkerElement | google.maps.Polygon; + | PolygonDefinition['infoWindow'] + | PolylineDefinition['infoWindow']; + element: google.maps.marker.AdvancedMarkerElement | google.maps.Polygon | google.maps.Polyline; }): google.maps.InfoWindow { const { headerContent, content, extra, rawOptions = {}, ...otherOptions } = definition; @@ -194,6 +219,23 @@ export default class extends AbstractMapController< infoWindow.open(this.map); }); + if (definition.opened) { + const bounds = new google.maps.LatLngBounds(); + element.getPath().forEach((point: google.maps.LatLng) => { + bounds.extend(point); + }); + infoWindow.setPosition(bounds.getCenter()); + infoWindow.open({ map: this.map, anchor: element }); + } + } else if (element instanceof google.maps.Polyline) { + element.addListener('click', (event: any) => { + if (definition.autoClose) { + this.closeInfoWindowsExcept(infoWindow); + } + infoWindow.setPosition(event.latLng); + infoWindow.open(this.map); + }); + if (definition.opened) { const bounds = new google.maps.LatLngBounds(); element.getPath().forEach((point: google.maps.LatLng) => { diff --git a/src/Map/src/Bridge/Google/assets/test/map_controller.test.ts b/src/Map/src/Bridge/Google/assets/test/map_controller.test.ts index f1b08abba5c..ec688f28c4a 100644 --- a/src/Map/src/Bridge/Google/assets/test/map_controller.test.ts +++ b/src/Map/src/Bridge/Google/assets/test/map_controller.test.ts @@ -41,7 +41,7 @@ describe('GoogleMapsController', () => { data-controller="check google" style="height: 700px; margin: 10px" data-google-provider-options-value="{"version":"weekly","libraries":["maps","marker"],"apiKey":""}" - data-google-view-value="{"center":{"lat":48.8566,"lng":2.3522},"zoom":4,"fitBoundsToMarkers":true,"options":{"mapId":"YOUR_MAP_ID","gestureHandling":"auto","backgroundColor":null,"disableDoubleClickZoom":false,"zoomControl":true,"zoomControlOptions":{"position":22},"mapTypeControl":true,"mapTypeControlOptions":{"mapTypeIds":[],"position":14,"style":0},"streetViewControl":true,"streetViewControlOptions":{"position":22},"fullscreenControl":true,"fullscreenControlOptions":{"position":20}},"markers":[{"position":{"lat":48.8566,"lng":2.3522},"title":"Paris","infoWindow":null},{"position":{"lat":45.764,"lng":4.8357},"title":"Lyon","infoWindow":{"headerContent":"<b>Lyon<\/b>","content":"The French town in the historic Rh\u00f4ne-Alpes region, located at the junction of the Rh\u00f4ne and Sa\u00f4ne rivers.","position":null,"opened":false,"autoClose":true}}],"polygons":[]}" + data-google-view-value="{"center":{"lat":48.8566,"lng":2.3522},"zoom":4,"fitBoundsToMarkers":true,"options":{"mapId":"YOUR_MAP_ID","gestureHandling":"auto","backgroundColor":null,"disableDoubleClickZoom":false,"zoomControl":true,"zoomControlOptions":{"position":22},"mapTypeControl":true,"mapTypeControlOptions":{"mapTypeIds":[],"position":14,"style":0},"streetViewControl":true,"streetViewControlOptions":{"position":22},"fullscreenControl":true,"fullscreenControlOptions":{"position":20}},"markers":[{"position":{"lat":48.8566,"lng":2.3522},"title":"Paris","infoWindow":null},{"position":{"lat":45.764,"lng":4.8357},"title":"Lyon","infoWindow":{"headerContent":"<b>Lyon<\/b>","content":"The French town in the historic Rh\u00f4ne-Alpes region, located at the junction of the Rh\u00f4ne and Sa\u00f4ne rivers.","position":null,"opened":false,"autoClose":true}}],"polygons":[]}polylines":[]}" > `); }); diff --git a/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php b/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php index e688d0c89f3..0dee4010410 100644 --- a/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php +++ b/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php @@ -29,26 +29,26 @@ public function provideTestRenderMap(): iterable ->zoom(12); yield 'simple map, with minimum options' => [ - 'expected_render' => '
', + 'expected_render' => '
', 'renderer' => new GoogleRenderer(new StimulusHelper(null), apiKey: 'api_key'), 'map' => $map, ]; yield 'with every options' => [ - 'expected_render' => '
', + 'expected_render' => '
', 'renderer' => new GoogleRenderer(new StimulusHelper(null), apiKey: 'api_key', id: 'gmap', language: 'fr', region: 'FR', nonce: 'abcd', retries: 10, url: 'https://maps.googleapis.com/maps/api/js', version: 'quarterly'), 'map' => $map, ]; yield 'with custom attributes' => [ - 'expected_render' => '
', + 'expected_render' => '
', 'renderer' => new GoogleRenderer(new StimulusHelper(null), apiKey: 'api_key'), 'map' => $map, 'attributes' => ['data-controller' => 'my-custom-controller', 'class' => 'map'], ]; yield 'with markers and infoWindows' => [ - 'expected_render' => '
', + 'expected_render' => '
', 'renderer' => new GoogleRenderer(new StimulusHelper(null), apiKey: 'api_key'), 'map' => (clone $map) ->addMarker(new Marker(new Point(48.8566, 2.3522), 'Paris')) @@ -56,7 +56,7 @@ public function provideTestRenderMap(): iterable ]; yield 'with controls enabled' => [ - 'expected_render' => '
', + 'expected_render' => '
', 'renderer' => new GoogleRenderer(new StimulusHelper(null), apiKey: 'api_key'), 'map' => (clone $map) ->options(new GoogleOptions( @@ -68,7 +68,7 @@ public function provideTestRenderMap(): iterable ]; yield 'without controls enabled' => [ - 'expected_render' => '
', + 'expected_render' => '
', 'renderer' => new GoogleRenderer(new StimulusHelper(null), apiKey: 'api_key'), 'map' => (clone $map) ->options(new GoogleOptions( diff --git a/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.d.ts b/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.d.ts index 6b32a8df45b..c5e35a5a0a7 100644 --- a/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.d.ts +++ b/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.d.ts @@ -1,8 +1,8 @@ import AbstractMapController from '@symfony/ux-map'; -import type { Point, MarkerDefinition, PolygonDefinition } from '@symfony/ux-map'; +import type { Point, MarkerDefinition, PolygonDefinition, PolylineDefinition } from '@symfony/ux-map'; import 'leaflet/dist/leaflet.min.css'; import * as L from 'leaflet'; -import type { MapOptions as LeafletMapOptions, MarkerOptions, PopupOptions, PolygonOptions } from 'leaflet'; +import type { MapOptions as LeafletMapOptions, MarkerOptions, PopupOptions, PolygonOptions, PolylineOptions } from 'leaflet'; type MapOptions = Pick & { tileLayer: { url: string; @@ -10,7 +10,7 @@ type MapOptions = Pick & { options: Record; }; }; -export default class extends AbstractMapController { +export default class extends AbstractMapController { connect(): void; protected dispatchEvent(name: string, payload?: Record): void; protected doCreateMap({ center, zoom, options, }: { @@ -20,10 +20,11 @@ export default class extends AbstractMapController this.createMarker(marker)); polygons.forEach((polygon) => this.createPolygon(polygon)); + polylines.forEach((polyline) => this.createPolyline(polyline)); if (fitBoundsToMarkers) { this.doFitBoundsToMarkers(); } @@ -22,6 +24,7 @@ class default_1 extends Controller { map: this.map, markers: this.markers, polygons: this.polygons, + polylines: this.polylines, infoWindows: this.infoWindows, }); } @@ -39,6 +42,13 @@ class default_1 extends Controller { this.polygons.push(polygon); return polygon; } + createPolyline(definition) { + this.dispatchEvent('polyline:before-create', { definition }); + const polyline = this.doCreatePolyline(definition); + this.dispatchEvent('polyline:after-create', { polyline }); + this.polylines.push(polyline); + return polyline; + } createInfoWindow({ definition, element, }) { this.dispatchEvent('info-window:before-create', { definition, element }); const infoWindow = this.doCreateInfoWindow({ definition, element }); @@ -103,6 +113,17 @@ class map_controller extends default_1 { } return polygon; } + doCreatePolyline(definition) { + const { points, title, infoWindow, rawOptions = {} } = definition; + const polyline = L.polyline(points, { ...rawOptions }).addTo(this.map); + if (title) { + polyline.bindPopup(title); + } + if (infoWindow) { + this.createInfoWindow({ definition: infoWindow, element: polyline }); + } + return polyline; + } doCreateInfoWindow({ definition, element, }) { const { headerContent, content, rawOptions = {}, ...otherOptions } = definition; element.bindPopup([headerContent, content].filter((x) => x).join('
'), { ...otherOptions, ...rawOptions }); diff --git a/src/Map/src/Bridge/Leaflet/assets/src/map_controller.ts b/src/Map/src/Bridge/Leaflet/assets/src/map_controller.ts index 12ed1f2922f..8dd6b790eac 100644 --- a/src/Map/src/Bridge/Leaflet/assets/src/map_controller.ts +++ b/src/Map/src/Bridge/Leaflet/assets/src/map_controller.ts @@ -1,8 +1,8 @@ import AbstractMapController from '@symfony/ux-map'; -import type { Point, MarkerDefinition, PolygonDefinition } from '@symfony/ux-map'; +import type { Point, MarkerDefinition, PolygonDefinition PolylineDefinition } from '@symfony/ux-map'; import 'leaflet/dist/leaflet.min.css'; import * as L from 'leaflet'; -import type { MapOptions as LeafletMapOptions, MarkerOptions, PopupOptions, PolygonOptions } from 'leaflet'; +import type { MapOptions as LeafletMapOptions, MarkerOptions, PopupOptions, PolygonOptions , PolylineOptions } from 'leaflet'; type MapOptions = Pick & { tileLayer: { url: string; attribution: string; options: Record }; @@ -16,7 +16,9 @@ export default class extends AbstractMapController< PopupOptions, typeof L.Popup, PolygonOptions, - typeof L.Polygon + typeof L.Polygon, + PolylineOptions, + typeof L.Polyline > { connect(): void { L.Marker.prototype.options.icon = L.divIcon({ @@ -87,12 +89,28 @@ export default class extends AbstractMapController< return polygon; } + protected doCreatePolyline(definition: PolylineDefinition): L.Polyline { + const { points, title, infoWindow, rawOptions = {} } = definition; + + const polyline = L.polyline(points, { ...rawOptions }).addTo(this.map); + + if (title) { + polyline.bindPopup(title); + } + + if (infoWindow) { + this.createInfoWindow({ definition: infoWindow, element: polyline }); + } + + return polyline; + } + protected doCreateInfoWindow({ definition, element, }: { - definition: MarkerDefinition['infoWindow'] | PolygonDefinition['infoWindow']; - element: L.Marker | L.Polygon; + definition: MarkerDefinition['infoWindow'] | PolygonDefinition['infoWindow'] | PolylineDefinition['infoWindow']; + element: L.Marker | L.Polygon | L.Polyline; }): L.Popup { const { headerContent, content, rawOptions = {}, ...otherOptions } = definition; diff --git a/src/Map/src/Bridge/Leaflet/assets/test/map_controller.test.ts b/src/Map/src/Bridge/Leaflet/assets/test/map_controller.test.ts index 5a51bf5f8a0..2fb368467e8 100644 --- a/src/Map/src/Bridge/Leaflet/assets/test/map_controller.test.ts +++ b/src/Map/src/Bridge/Leaflet/assets/test/map_controller.test.ts @@ -41,7 +41,7 @@ describe('LeafletController', () => { data-controller="check leaflet" style="height: 700px; margin: 10px" data-leaflet-provider-options-value="{}" - data-leaflet-view-value="{"center":{"lat":48.8566,"lng":2.3522},"zoom":4,"fitBoundsToMarkers":true,"options":{"tileLayer":{"url":"https:\/\/tile.openstreetmap.org\/{z}\/{x}\/{y}.png","attribution":"\u00a9 <a href=\"https:\/\/www.openstreetmap.org\/copyright\">OpenStreetMap<\/a>","options":{}}},"markers":[{"position":{"lat":48.8566,"lng":2.3522},"title":"Paris","infoWindow":null},{"position":{"lat":45.764,"lng":4.8357},"title":"Lyon","infoWindow":{"headerContent":"<b>Lyon<\/b>","content":"The French town in the historic Rh\u00f4ne-Alpes region, located at the junction of the Rh\u00f4ne and Sa\u00f4ne rivers.","position":null,"opened":false,"autoClose":true}}],"polygons":[]}" + data-leaflet-view-value="{"center":{"lat":48.8566,"lng":2.3522},"zoom":4,"fitBoundsToMarkers":true,"options":{"tileLayer":{"url":"https:\/\/tile.openstreetmap.org\/{z}\/{x}\/{y}.png","attribution":"\u00a9 <a href=\"https:\/\/www.openstreetmap.org\/copyright\">OpenStreetMap<\/a>","options":{}}},"markers":[{"position":{"lat":48.8566,"lng":2.3522},"title":"Paris","infoWindow":null},{"position":{"lat":45.764,"lng":4.8357},"title":"Lyon","infoWindow":{"headerContent":"<b>Lyon<\/b>","content":"The French town in the historic Rh\u00f4ne-Alpes region, located at the junction of the Rh\u00f4ne and Sa\u00f4ne rivers.","position":null,"opened":false,"autoClose":true}}],"polygons":[]}polylines":[]}" > `); }); diff --git a/src/Map/src/Bridge/Leaflet/tests/LeafletRendererTest.php b/src/Map/src/Bridge/Leaflet/tests/LeafletRendererTest.php index a23f103ec53..2624877c43b 100644 --- a/src/Map/src/Bridge/Leaflet/tests/LeafletRendererTest.php +++ b/src/Map/src/Bridge/Leaflet/tests/LeafletRendererTest.php @@ -28,20 +28,20 @@ public function provideTestRenderMap(): iterable ->zoom(12); yield 'simple map' => [ - 'expected_render' => '
', + 'expected_render' => '
', 'renderer' => new LeafletRenderer(new StimulusHelper(null)), 'map' => $map, ]; yield 'with custom attributes' => [ - 'expected_render' => '
', + 'expected_render' => '
', 'renderer' => new LeafletRenderer(new StimulusHelper(null)), 'map' => $map, 'attributes' => ['data-controller' => 'my-custom-controller', 'class' => 'map'], ]; yield 'with markers and infoWindows' => [ - 'expected_render' => '
', + 'expected_render' => '
', 'renderer' => new LeafletRenderer(new StimulusHelper(null)), 'map' => (clone $map) ->addMarker(new Marker(new Point(48.8566, 2.3522), 'Paris')) diff --git a/src/Map/src/Map.php b/src/Map/src/Map.php index 3ab240ae1e2..49e360776f6 100644 --- a/src/Map/src/Map.php +++ b/src/Map/src/Map.php @@ -35,8 +35,12 @@ public function __construct( * @var array */ private array $polygons = [], - ) { - } + + /** + * @var array + */ + private array $polylines = [], + ) {} public function getRendererName(): ?string { @@ -94,6 +98,12 @@ public function addPolygon(Polygon $polygon): self return $this; } + public function addPolyline(Polyline $polyline): self + { + $this->polylines[] = $polyline; + + return $this; + } public function toArray(): array { @@ -112,8 +122,9 @@ public function toArray(): array 'zoom' => $this->zoom, 'fitBoundsToMarkers' => $this->fitBoundsToMarkers, 'options' => (object) ($this->options?->toArray() ?? []), - 'markers' => array_map(static fn (Marker $marker) => $marker->toArray(), $this->markers), - 'polygons' => array_map(static fn (Polygon $polygon) => $polygon->toArray(), $this->polygons), + 'markers' => array_map(static fn(Marker $marker) => $marker->toArray(), $this->markers), + 'polygons' => array_map(static fn(Polygon $polygon) => $polygon->toArray(), $this->polygons), + 'polylines' => array_map(static fn(Polyline $polyline) => $polyline->toArray(), $this->polylines), ]; } @@ -123,6 +134,7 @@ public function toArray(): array * zoom?: float, * markers?: list, * polygons?: list, + * polylines?: list, * fitBoundsToMarkers?: bool, * options?: object, * } $map @@ -153,6 +165,12 @@ public static function fromArray(array $map): self } $map['polygons'] = array_map(Polygon::fromArray(...), $map['polygons']); + $map['polylines'] ??= []; + if (!\is_array($map['polylines'])) { + throw new InvalidArgumentException('The "polylines" parameter must be an array.'); + } + $map['polylines'] = array_map(Polygon::fromArray(...), $map['polylines']); + return new self(...$map); } } diff --git a/src/Map/src/Polyline.php b/src/Map/src/Polyline.php new file mode 100644 index 00000000000..44f9f11dc8b --- /dev/null +++ b/src/Map/src/Polyline.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\UX\Map; + +use Symfony\UX\Map\Exception\InvalidArgumentException; + +/** + * Represents a polyline on a map. + * + * @author [Pierre Svgnt] + */ +final readonly class Polyline +{ + /** + * @param array $extra Extra data, can be used by the developer to store additional information and use them later JavaScript side + */ + public function __construct( + private array $points, + private ?string $title = null, + private ?InfoWindow $infoWindow = null, + private array $extra = [], + ) { + } + + /** + * Convert the polyline to an array representation. + */ + public function toArray(): array + { + return [ + 'points' => array_map(fn (Point $point) => $point->toArray(), $this->points), + 'title' => $this->title, + 'infoWindow' => $this->infoWindow?->toArray(), + 'extra' => (object) $this->extra, + ]; + } + + /** + * @param array{ + * points: array, + * title: string|null, + * infoWindow: array|null, + * extra: object, + * } $polyline + * + * @internal + */ + public static function fromArray(array $polyline): self + { + if (!isset($polyline['points'])) { + throw new InvalidArgumentException('The "points" parameter is required.'); + } + $polyline['points'] = array_map(Point::fromArray(...), $polyline['points']); + + if (isset($polyline['infoWindow'])) { + $polyline['infoWindow'] = InfoWindow::fromArray($polyline['infoWindow']); + } + + return new self(...$polyline); + } +} diff --git a/src/Map/src/Twig/MapRuntime.php b/src/Map/src/Twig/MapRuntime.php index 8b9a1ca2036..d81462f8a07 100644 --- a/src/Map/src/Twig/MapRuntime.php +++ b/src/Map/src/Twig/MapRuntime.php @@ -15,6 +15,7 @@ use Symfony\UX\Map\Marker; use Symfony\UX\Map\Point; use Symfony\UX\Map\Polygon; +use Symfony\UX\Map\Polyline; use Symfony\UX\Map\Renderer\RendererInterface; use Twig\Extension\RuntimeExtensionInterface; @@ -34,12 +35,14 @@ public function __construct( * @param array $attributes * @param array $markers * @param array $polygons + * @param array $polylines */ public function renderMap( ?Map $map = null, array $attributes = [], ?array $markers = null, ?array $polygons = null, + ?array $polylines = null, ?array $center = null, ?float $zoom = null, ): string { @@ -58,6 +61,9 @@ public function renderMap( foreach ($polygons ?? [] as $polygons) { $map->addPolygon(Polygon::fromArray($polygons)); } + foreach ($polylines ?? [] as $polylines) { + $map->addPolyline(Polyline::fromArray($polylines)); + } if (null !== $center) { $map->center(Point::fromArray($center)); } @@ -70,7 +76,7 @@ public function renderMap( public function render(array $args = []): string { - $map = array_intersect_key($args, ['map' => 0, 'markers' => 0, 'polygons' => 0, 'center' => 1, 'zoom' => 2]); + $map = array_intersect_key($args, ['map' => 0, 'markers' => 0, 'polygons' => 0, 'polylines' => 0, 'center' => 1, 'zoom' => 2]); $attributes = array_diff_key($args, $map); return $this->renderMap(...$map, attributes: $attributes); diff --git a/src/Map/src/Twig/UXMapComponent.php b/src/Map/src/Twig/UXMapComponent.php index 39e362b34b9..ae764f41e49 100644 --- a/src/Map/src/Twig/UXMapComponent.php +++ b/src/Map/src/Twig/UXMapComponent.php @@ -11,9 +11,10 @@ namespace Symfony\UX\Map\Twig; -use Symfony\UX\Map\Marker; use Symfony\UX\Map\Point; +use Symfony\UX\Map\Marker; use Symfony\UX\Map\Polygon; +use Symfony\UX\Map\Polyline; /** * @author Simon André @@ -35,4 +36,8 @@ final class UXMapComponent * @var Polygon[] */ public array $polygons; + /** + * @var Polyline[] + */ + public array $polylines; } diff --git a/src/Map/tests/MapFactoryTest.php b/src/Map/tests/MapFactoryTest.php index fcff3b0539c..907c0fb72db 100644 --- a/src/Map/tests/MapFactoryTest.php +++ b/src/Map/tests/MapFactoryTest.php @@ -39,6 +39,13 @@ public function testFromArray(): void $this->assertSame($array['polygons'][0]['title'], $polygons[0]['title']); $this->assertSame($array['polygons'][0]['infoWindow']['headerContent'], $polygons[0]['infoWindow']['headerContent']); $this->assertSame($array['polygons'][0]['infoWindow']['content'], $polygons[0]['infoWindow']['content']); + + $this->assertCount(1, $polylines = $map->toArray()['polylines']); + $this->assertEquals($array['polylines'][0]['points'], $polylines[0]['points']); + $this->assertEquals($array['polylines'][0]['points'], $polylines[0]['points']); + $this->assertSame($array['polylines'][0]['title'], $polylines[0]['title']); + $this->assertSame($array['polylines'][0]['infoWindow']['headerContent'], $polylines[0]['infoWindow']['headerContent']); + $this->assertSame($array['polylines'][0]['infoWindow']['content'], $polylines[0]['infoWindow']['content']); } public function testFromArrayWithInvalidCenter(): void @@ -107,6 +114,30 @@ public function testFromArrayWithInvalidPolygon(): void Map::fromArray($array); } + public function testFromArrayWithInvalidPolylines(): void + { + $array = self::createMapArray(); + $array['polylines'] = 'invalid'; + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('The "polylines" parameter must be an array.'); + Map::fromArray($array); + } + + public function testFromArrayWithInvalidPolyline(): void + { + $array = self::createMapArray(); + $array['polylines'] = [ + [ + 'invalid', + ], + ]; + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('The "points" parameter is required.'); + Map::fromArray($array); + } + private static function createMapArray(): array { return [ @@ -151,6 +182,29 @@ private static function createMapArray(): array ], ], ], + 'polylines' => [ + [ + 'points' => [ + [ + 'lat' => 48.858844, + 'lng' => 2.294351, + ], + [ + 'lat' => 48.853, + 'lng' => 2.3499, + ], + [ + 'lat' => 48.8566, + 'lng' => 2.3522, + ], + ], + 'title' => 'Polyline 1', + 'infoWindow' => [ + 'headerContent' => 'Polyline 1', + 'content' => 'Polyline 1', + ], + ], + ], ]; } } diff --git a/src/Map/tests/MapTest.php b/src/Map/tests/MapTest.php index 95703724466..afb400737dd 100644 --- a/src/Map/tests/MapTest.php +++ b/src/Map/tests/MapTest.php @@ -19,6 +19,7 @@ use Symfony\UX\Map\Marker; use Symfony\UX\Map\Point; use Symfony\UX\Map\Polygon; +use Symfony\UX\Map\Polyline; class MapTest extends TestCase { @@ -57,6 +58,7 @@ public function testZoomAndCenterCanBeOmittedIfFitBoundsToMarkers(): void 'options' => $array['options'], 'markers' => [], 'polygons' => [], + 'polylines' => [], ], $array); } @@ -76,6 +78,7 @@ public function testWithMinimumConfiguration(): void 'options' => $array['options'], 'markers' => [], 'polygons' => [], + 'polylines' => [], ], $array); } @@ -133,6 +136,30 @@ public function toArray(): array autoClose: true, ), )) + ->addPolyline(new Polyline( + points: [ + new Point(48.858844, 2.294351), + new Point(48.853, 2.3499), + new Point(48.8566, 2.3522), + ], + title: 'Polyline 1', + infoWindow: null, + )) + ->addPolyline(new Polyline( + points: [ + new Point(45.764043, 4.835659), + new Point(45.75, 4.85), + new Point(45.77, 4.82), + ], + title: 'Polyline 2', + infoWindow: new InfoWindow( + headerContent: 'Polyline 2', + content: 'A polyline around Lyon with some additional info.', + position: new Point(45.764, 4.8357), + opened: true, + autoClose: true, + ), + )) ; $array = $map->toArray(); @@ -212,6 +239,35 @@ public function toArray(): array 'extra' => $array['polygons'][1]['extra'], ], ], + 'polylines' => [ + [ + 'points' => [ + ['lat' => 48.858844, 'lng' => 2.294351], + ['lat' => 48.853, 'lng' => 2.3499], + ['lat' => 48.8566, 'lng' => 2.3522], + ], + 'title' => 'Polyline 1', + 'infoWindow' => null, + 'extra' => $array['polylines'][0]['extra'], + ], + [ + 'points' => [ + ['lat' => 45.764043, 'lng' => 4.835659], + ['lat' => 45.75, 'lng' => 4.85], + ['lat' => 45.77, 'lng' => 4.82], + ], + 'title' => 'Polyline 2', + 'infoWindow' => [ + 'headerContent' => 'Polyline 2', + 'content' => 'A polyline around Lyon with some additional info.', + 'position' => ['lat' => 45.764, 'lng' => 4.8357], + 'opened' => true, + 'autoClose' => true, + 'extra' => $array['polylines'][1]['infoWindow']['extra'], + ], + 'extra' => $array['polylines'][1]['extra'], + ], + ], ], $array); self::assertSame('roadmap', $array['options']->mapTypeId); From d9b2fc7f7178a5b2f73619baafa97ccaba8eb672 Mon Sep 17 00:00:00 2001 From: Sylvain Blondeau Date: Mon, 4 Nov 2024 22:14:19 +0100 Subject: [PATCH 02/12] update changelog --- src/Map/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Map/CHANGELOG.md b/src/Map/CHANGELOG.md index 3883fe057d4..47fc02ae74c 100644 --- a/src/Map/CHANGELOG.md +++ b/src/Map/CHANGELOG.md @@ -10,6 +10,7 @@ - The importmap entry `@symfony/ux-map/abstract-map-controller` can be removed from your importmap, it is no longer needed. - Add `Polygon` support +- Add `Polyline` support ## 2.19 From d872427a4980525ad2bc675518e36b96f284952e Mon Sep 17 00:00:00 2001 From: Sylvain Blondeau Date: Mon, 4 Nov 2024 22:40:49 +0100 Subject: [PATCH 03/12] fix linter --- src/Map/doc/index.rst | 3 ++- src/Map/src/Bridge/Google/assets/src/map_controller.ts | 2 +- src/Map/src/Bridge/Leaflet/assets/src/map_controller.ts | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Map/doc/index.rst b/src/Map/doc/index.rst index d7897cdc621..c0fd61a1237 100644 --- a/src/Map/doc/index.rst +++ b/src/Map/doc/index.rst @@ -131,7 +131,8 @@ You can add markers to a map using the ``addMarker()`` method:: ; Add Polylines -~~~~~~~~~~~~ +~~~~~~~~~~~~~ + You can add Polylines, which represents a path made by a series of `Point` instances $myMap->addPolyline(new Polyline( points: [ diff --git a/src/Map/src/Bridge/Google/assets/src/map_controller.ts b/src/Map/src/Bridge/Google/assets/src/map_controller.ts index ca81a7acfd9..fe2fcea8a0b 100644 --- a/src/Map/src/Bridge/Google/assets/src/map_controller.ts +++ b/src/Map/src/Bridge/Google/assets/src/map_controller.ts @@ -38,7 +38,7 @@ export default class extends AbstractMapController< google.maps.InfoWindowOptions, google.maps.InfoWindow, google.maps.PolygonOptions, - google.maps.Polygon + google.maps.Polygon, google.maps.PolylineOptions, google.maps.Polyline > { diff --git a/src/Map/src/Bridge/Leaflet/assets/src/map_controller.ts b/src/Map/src/Bridge/Leaflet/assets/src/map_controller.ts index 8dd6b790eac..16cd9074e58 100644 --- a/src/Map/src/Bridge/Leaflet/assets/src/map_controller.ts +++ b/src/Map/src/Bridge/Leaflet/assets/src/map_controller.ts @@ -1,5 +1,5 @@ import AbstractMapController from '@symfony/ux-map'; -import type { Point, MarkerDefinition, PolygonDefinition PolylineDefinition } from '@symfony/ux-map'; +import type { Point, MarkerDefinition, PolygonDefinition, PolylineDefinition } from '@symfony/ux-map'; import 'leaflet/dist/leaflet.min.css'; import * as L from 'leaflet'; import type { MapOptions as LeafletMapOptions, MarkerOptions, PopupOptions, PolygonOptions , PolylineOptions } from 'leaflet'; From fde4dee8f2ae530e7c9d1c5978ec6675c8eafd04 Mon Sep 17 00:00:00 2001 From: Sylvain Blondeau Date: Tue, 5 Nov 2024 08:49:16 +0100 Subject: [PATCH 04/12] fix linter js --- src/Map/assets/src/abstract_map_controller.ts | 26 +++++++++---------- .../Google/assets/src/map_controller.ts | 2 +- .../Leaflet/assets/src/map_controller.ts | 8 +++++- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/Map/assets/src/abstract_map_controller.ts b/src/Map/assets/src/abstract_map_controller.ts index 84b41938555..262aa2b327b 100644 --- a/src/Map/assets/src/abstract_map_controller.ts +++ b/src/Map/assets/src/abstract_map_controller.ts @@ -65,7 +65,7 @@ export type InfoWindowDefinition = { extra: Record; }; -export default abstract class < +export default abstract class< MapOptions, Map, MarkerOptions, @@ -161,9 +161,9 @@ export default abstract class < element, }: { definition: - | MarkerDefinition['infoWindow'] - | PolygonDefinition['infoWindow'] - | PolylineDefinition['infoWindow']; + | MarkerDefinition['infoWindow'] + | PolygonDefinition['infoWindow'] + | PolylineDefinition['infoWindow']; element: Marker | Polygon | Polyline; }): InfoWindow { this.dispatchEvent('info-window:before-create', { definition, element }); @@ -180,17 +180,17 @@ export default abstract class < element, }: | { - definition: MarkerDefinition['infoWindow']; - element: Marker; - } + definition: MarkerDefinition['infoWindow']; + element: Marker; + } | { - definition: PolygonDefinition['infoWindow']; - element: Polygon; - } + definition: PolygonDefinition['infoWindow']; + element: Polygon; + } | { - definition: PolylineDefinition['infoWindow']; - element: Polyline; - }): InfoWindow; + definition: PolylineDefinition['infoWindow']; + element: Polyline; + }): InfoWindow; protected abstract doFitBoundsToMarkers(): void; diff --git a/src/Map/src/Bridge/Google/assets/src/map_controller.ts b/src/Map/src/Bridge/Google/assets/src/map_controller.ts index fe2fcea8a0b..18396a0c85a 100644 --- a/src/Map/src/Bridge/Google/assets/src/map_controller.ts +++ b/src/Map/src/Bridge/Google/assets/src/map_controller.ts @@ -8,7 +8,7 @@ */ import AbstractMapController from '@symfony/ux-map'; -import type { Point, MarkerDefinition, PolygonDefinition, PolylineDefinition } from '@symfony/ux-map'; +import type { Point, MarkerDefinition, PolygonDefinition, PolylineDefinition } from '@symfony/ux-map'; import type { LoaderOptions } from '@googlemaps/js-api-loader'; import { Loader } from '@googlemaps/js-api-loader'; diff --git a/src/Map/src/Bridge/Leaflet/assets/src/map_controller.ts b/src/Map/src/Bridge/Leaflet/assets/src/map_controller.ts index 16cd9074e58..5a18bbb1491 100644 --- a/src/Map/src/Bridge/Leaflet/assets/src/map_controller.ts +++ b/src/Map/src/Bridge/Leaflet/assets/src/map_controller.ts @@ -2,7 +2,13 @@ import AbstractMapController from '@symfony/ux-map'; import type { Point, MarkerDefinition, PolygonDefinition, PolylineDefinition } from '@symfony/ux-map'; import 'leaflet/dist/leaflet.min.css'; import * as L from 'leaflet'; -import type { MapOptions as LeafletMapOptions, MarkerOptions, PopupOptions, PolygonOptions , PolylineOptions } from 'leaflet'; +import type { + MapOptions as LeafletMapOptions, + MarkerOptions, + PopupOptions, + PolygonOptions, + PolylineOptions, +} from 'leaflet'; type MapOptions = Pick & { tileLayer: { url: string; attribution: string; options: Record }; From 67befe6fcfd1d9b404359d844b0392fd3c09472e Mon Sep 17 00:00:00 2001 From: Sylvain Blondeau Date: Tue, 5 Nov 2024 08:54:50 +0100 Subject: [PATCH 05/12] yarn build --- src/Map/assets/dist/abstract_map_controller.d.ts | 4 ++-- src/Map/assets/dist/abstract_map_controller.js | 2 +- src/Map/src/Bridge/Google/assets/dist/map_controller.d.ts | 2 +- src/Map/src/Bridge/Google/assets/dist/map_controller.js | 4 ++-- src/Map/src/Bridge/Leaflet/assets/dist/map_controller.d.ts | 2 +- src/Map/src/Bridge/Leaflet/assets/dist/map_controller.js | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Map/assets/dist/abstract_map_controller.d.ts b/src/Map/assets/dist/abstract_map_controller.d.ts index 4e3200d89d7..9cb0676457a 100644 --- a/src/Map/assets/dist/abstract_map_controller.d.ts +++ b/src/Map/assets/dist/abstract_map_controller.d.ts @@ -67,7 +67,7 @@ export default abstract class): Polyline; protected createInfoWindow({ definition, element, }: { definition: MarkerDefinition['infoWindow'] | PolygonDefinition['infoWindow'] | PolylineDefinition['infoWindow']; - element: Marker | Polygon| Polyline; + element: Marker | Polygon | Polyline; }): InfoWindow; protected abstract doCreateInfoWindow({ definition, element, }: { definition: MarkerDefinition['infoWindow']; @@ -75,7 +75,7 @@ export default abstract class['infoWindow']; element: Polygon; - }| { + } | { definition: PolylineDefinition['infoWindow']; element: Polyline; }): InfoWindow; diff --git a/src/Map/assets/dist/abstract_map_controller.js b/src/Map/assets/dist/abstract_map_controller.js index c7c6651af17..120ef1c8921 100644 --- a/src/Map/assets/dist/abstract_map_controller.js +++ b/src/Map/assets/dist/abstract_map_controller.js @@ -9,7 +9,7 @@ class default_1 extends Controller { this.polylines = []; } connect() { - const { center, zoom, options, markers, polygons, polylines, fitBoundsToMarkers } = this.viewValue; + const { center, zoom, options, markers, polygons, fitBoundsToMarkers } = this.viewValue; this.dispatchEvent('pre-connect', { options }); this.map = this.doCreateMap({ center, zoom, options }); markers.forEach((marker) => this.createMarker(marker)); diff --git a/src/Map/src/Bridge/Google/assets/dist/map_controller.d.ts b/src/Map/src/Bridge/Google/assets/dist/map_controller.d.ts index 28f4d408442..727c4e216f9 100644 --- a/src/Map/src/Bridge/Google/assets/dist/map_controller.d.ts +++ b/src/Map/src/Bridge/Google/assets/dist/map_controller.d.ts @@ -2,7 +2,7 @@ import AbstractMapController from '@symfony/ux-map'; import type { Point, MarkerDefinition, PolygonDefinition, PolylineDefinition } from '@symfony/ux-map'; import type { LoaderOptions } from '@googlemaps/js-api-loader'; type MapOptions = Pick; -export default class extends AbstractMapController { +export default class extends AbstractMapController { static values: { providerOptions: ObjectConstructor; }; diff --git a/src/Map/src/Bridge/Google/assets/dist/map_controller.js b/src/Map/src/Bridge/Google/assets/dist/map_controller.js index b150afe4737..14fa4f22642 100644 --- a/src/Map/src/Bridge/Google/assets/dist/map_controller.js +++ b/src/Map/src/Bridge/Google/assets/dist/map_controller.js @@ -10,7 +10,7 @@ let default_1$1 = class default_1 extends Controller { this.polylines = []; } connect() { - const { center, zoom, options, markers, polygons, polylines, fitBoundsToMarkers } = this.viewValue; + const { center, zoom, options, markers, polygons, fitBoundsToMarkers } = this.viewValue; this.dispatchEvent('pre-connect', { options }); this.map = this.doCreateMap({ center, zoom, options }); markers.forEach((marker) => this.createMarker(marker)); @@ -120,7 +120,7 @@ class default_1 extends default_1$1 { const { points, title, infoWindow, rawOptions = {} } = definition; const polygon = new _google.maps.Polygon({ ...rawOptions, - path: points, + paths: points, map: this.map, }); if (title) { diff --git a/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.d.ts b/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.d.ts index c5e35a5a0a7..f5dd9682125 100644 --- a/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.d.ts +++ b/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.d.ts @@ -27,4 +27,4 @@ export default class extends AbstractMapController this.createMarker(marker)); @@ -48,7 +48,7 @@ class default_1 extends Controller { this.dispatchEvent('polyline:after-create', { polyline }); this.polylines.push(polyline); return polyline; - } + } createInfoWindow({ definition, element, }) { this.dispatchEvent('info-window:before-create', { definition, element }); const infoWindow = this.doCreateInfoWindow({ definition, element }); From 23e592873c075f8484d9235f6611d5e8d1025093 Mon Sep 17 00:00:00 2001 From: Sylvain Blondeau Date: Wed, 6 Nov 2024 21:00:26 +0100 Subject: [PATCH 06/12] fix lint and tests --- src/Map/CHANGELOG.md | 5 +- .../assets/dist/abstract_map_controller.d.ts | 14 ++-- .../assets/dist/abstract_map_controller.js | 5 +- src/Map/assets/src/abstract_map_controller.ts | 59 +++++++++-------- src/Map/doc/index.rst | 25 ++++---- .../Google/assets/dist/map_controller.js | 24 ++----- .../Google/assets/src/map_controller.ts | 19 +----- .../Google/tests/GoogleRendererTest.php | 12 ++-- .../Leaflet/assets/dist/map_controller.js | 64 +------------------ .../Leaflet/tests/LeafletRendererTest.php | 6 +- src/Map/src/Polygon.php | 6 ++ src/Map/src/Polyline.php | 8 ++- src/Map/src/Twig/MapRuntime.php | 2 +- src/Map/src/Twig/UXMapComponent.php | 2 +- 14 files changed, 94 insertions(+), 157 deletions(-) diff --git a/src/Map/CHANGELOG.md b/src/Map/CHANGELOG.md index 47fc02ae74c..693f7147a46 100644 --- a/src/Map/CHANGELOG.md +++ b/src/Map/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +## 2.22 + +- Add `Polyline` support + ## 2.20 - Deprecate `render_map` Twig function (will be removed in 2.21). Use @@ -10,7 +14,6 @@ - The importmap entry `@symfony/ux-map/abstract-map-controller` can be removed from your importmap, it is no longer needed. - Add `Polygon` support -- Add `Polyline` support ## 2.19 diff --git a/src/Map/assets/dist/abstract_map_controller.d.ts b/src/Map/assets/dist/abstract_map_controller.d.ts index 9cb0676457a..12a297d844a 100644 --- a/src/Map/assets/dist/abstract_map_controller.d.ts +++ b/src/Map/assets/dist/abstract_map_controller.d.ts @@ -65,11 +65,17 @@ export default abstract class): Marker; protected abstract doCreatePolygon(definition: PolygonDefinition): Polygon; protected abstract doCreatePolyline(definition: PolylineDefinition): Polyline; - protected createInfoWindow({ definition, element, }: { - definition: MarkerDefinition['infoWindow'] | PolygonDefinition['infoWindow'] | PolylineDefinition['infoWindow']; - element: Marker | Polygon | Polyline; + protected abstract createInfoWindow(args: { + definition: MarkerDefinition['infoWindow']; + element: Marker; + } | { + definition: PolygonDefinition['infoWindow']; + element: Polygon; + } | { + definition: PolylineDefinition['infoWindow']; + element: Polyline; }): InfoWindow; - protected abstract doCreateInfoWindow({ definition, element, }: { + protected abstract doCreateInfoWindow(args: { definition: MarkerDefinition['infoWindow']; element: Marker; } | { diff --git a/src/Map/assets/dist/abstract_map_controller.js b/src/Map/assets/dist/abstract_map_controller.js index 120ef1c8921..d93c21da3dc 100644 --- a/src/Map/assets/dist/abstract_map_controller.js +++ b/src/Map/assets/dist/abstract_map_controller.js @@ -9,7 +9,7 @@ class default_1 extends Controller { this.polylines = []; } connect() { - const { center, zoom, options, markers, polygons, fitBoundsToMarkers } = this.viewValue; + const { center, zoom, options, markers, polygons, polylines, fitBoundsToMarkers } = this.viewValue; this.dispatchEvent('pre-connect', { options }); this.map = this.doCreateMap({ center, zoom, options }); markers.forEach((marker) => this.createMarker(marker)); @@ -47,7 +47,8 @@ class default_1 extends Controller { this.polylines.push(polyline); return polyline; } - createInfoWindow({ definition, element, }) { + createInfoWindow(args) { + const { definition, element } = args; this.dispatchEvent('info-window:before-create', { definition, element }); const infoWindow = this.doCreateInfoWindow({ definition, element }); this.dispatchEvent('info-window:after-create', { infoWindow, element }); diff --git a/src/Map/assets/src/abstract_map_controller.ts b/src/Map/assets/src/abstract_map_controller.ts index 262aa2b327b..e08fd87486b 100644 --- a/src/Map/assets/src/abstract_map_controller.ts +++ b/src/Map/assets/src/abstract_map_controller.ts @@ -91,7 +91,7 @@ export default abstract class< protected polylines: Array = []; connect() { - const { center, zoom, options, markers, polygons, fitBoundsToMarkers } = this.viewValue; + const { center, zoom, options, markers, polygons, polylines, fitBoundsToMarkers } = this.viewValue; this.dispatchEvent('pre-connect', { options }); @@ -156,16 +156,22 @@ export default abstract class< protected abstract doCreatePolygon(definition: PolygonDefinition): Polygon; protected abstract doCreatePolyline(definition: PolylineDefinition): Polyline; - protected createInfoWindow({ - definition, - element, - }: { - definition: - | MarkerDefinition['infoWindow'] - | PolygonDefinition['infoWindow'] - | PolylineDefinition['infoWindow']; - element: Marker | Polygon | Polyline; - }): InfoWindow { + protected abstract createInfoWindow( + args: + | { + definition: MarkerDefinition['infoWindow']; + element: Marker; + } + | { + definition: PolygonDefinition['infoWindow']; + element: Polygon; + } + | { + definition: PolylineDefinition['infoWindow']; + element: Polyline; + } + ): InfoWindow { + const { definition, element } = args; this.dispatchEvent('info-window:before-create', { definition, element }); const infoWindow = this.doCreateInfoWindow({ definition, element }); this.dispatchEvent('info-window:after-create', { infoWindow, element }); @@ -175,22 +181,21 @@ export default abstract class< return infoWindow; } - protected abstract doCreateInfoWindow({ - definition, - element, - }: - | { - definition: MarkerDefinition['infoWindow']; - element: Marker; - } - | { - definition: PolygonDefinition['infoWindow']; - element: Polygon; - } - | { - definition: PolylineDefinition['infoWindow']; - element: Polyline; - }): InfoWindow; + protected abstract doCreateInfoWindow( + args: + | { + definition: MarkerDefinition['infoWindow']; + element: Marker; + } + | { + definition: PolygonDefinition['infoWindow']; + element: Polygon; + } + | { + definition: PolylineDefinition['infoWindow']; + element: Polyline; + } + ): InfoWindow; protected abstract doFitBoundsToMarkers(): void; diff --git a/src/Map/doc/index.rst b/src/Map/doc/index.rst index c0fd61a1237..3ecbf47914c 100644 --- a/src/Map/doc/index.rst +++ b/src/Map/doc/index.rst @@ -130,11 +130,13 @@ You can add markers to a map using the ``addMarker()`` method:: ) ; -Add Polylines -~~~~~~~~~~~~~ -You can add Polylines, which represents a path made by a series of `Point` instances - $myMap->addPolyline(new Polyline( +Add Polygons +~~~~~~~~~~~~ + +You can also add Polygons, which represents an area enclosed by a series of ``Point`` instances:: + + $myMap->addPolygon(new Polygon( points: [ new Point(48.8566, 2.3522), new Point(45.7640, 4.8357), @@ -142,16 +144,15 @@ You can add Polylines, which represents a path made by a series of `Point` insta new Point(44.8378, -0.5792), ], infoWindow: new InfoWindow( - content: 'A line passing through Paris, Lyon, Marseille, Bordeaux', + content: 'Paris, Lyon, Marseille, Bordeaux', ), )); -Add Polygons -~~~~~~~~~~~~ - -You can also add Polygons, which represents an area enclosed by a series of ``Point`` instances:: +Add Polylines +~~~~~~~~~~~~~ - $myMap->addPolygon(new Polygon( +You can add Polylines, which represents a path made by a series of `Point` instances + $myMap->addPolyline(new Polyline( points: [ new Point(48.8566, 2.3522), new Point(45.7640, 4.8357), @@ -159,11 +160,13 @@ You can also add Polygons, which represents an area enclosed by a series of ``Po new Point(44.8378, -0.5792), ], infoWindow: new InfoWindow( - content: 'Paris, Lyon, Marseille, Bordeaux', + content: 'A line passing through Paris, Lyon, Marseille, Bordeaux', ), )); + Render a map +------------ To render a map in your Twig template, use the ``ux_map`` Twig function, e.g.: diff --git a/src/Map/src/Bridge/Google/assets/dist/map_controller.js b/src/Map/src/Bridge/Google/assets/dist/map_controller.js index 14fa4f22642..a2bee153b3b 100644 --- a/src/Map/src/Bridge/Google/assets/dist/map_controller.js +++ b/src/Map/src/Bridge/Google/assets/dist/map_controller.js @@ -10,7 +10,7 @@ let default_1$1 = class default_1 extends Controller { this.polylines = []; } connect() { - const { center, zoom, options, markers, polygons, fitBoundsToMarkers } = this.viewValue; + const { center, zoom, options, markers, polygons, polylines, fitBoundsToMarkers } = this.viewValue; this.dispatchEvent('pre-connect', { options }); this.map = this.doCreateMap({ center, zoom, options }); markers.forEach((marker) => this.createMarker(marker)); @@ -48,7 +48,8 @@ let default_1$1 = class default_1 extends Controller { this.polylines.push(polyline); return polyline; } - createInfoWindow({ definition, element, }) { + createInfoWindow(args) { + const { definition, element } = args; this.dispatchEvent('info-window:before-create', { definition, element }); const infoWindow = this.doCreateInfoWindow({ definition, element }); this.dispatchEvent('info-window:after-create', { infoWindow, element }); @@ -165,24 +166,7 @@ class default_1 extends default_1$1 { infoWindow.open({ map: this.map, anchor: element }); } } - else if (element instanceof google.maps.Polygon) { - element.addListener('click', (event) => { - if (definition.autoClose) { - this.closeInfoWindowsExcept(infoWindow); - } - infoWindow.setPosition(event.latLng); - infoWindow.open(this.map); - }); - if (definition.opened) { - const bounds = new google.maps.LatLngBounds(); - element.getPath().forEach((point) => { - bounds.extend(point); - }); - infoWindow.setPosition(bounds.getCenter()); - infoWindow.open({ map: this.map, anchor: element }); - } - } - else if (element instanceof google.maps.Polyline) { + else if (element instanceof google.maps.Polygon || element instanceof google.maps.Polyline) { element.addListener('click', (event) => { if (definition.autoClose) { this.closeInfoWindowsExcept(infoWindow); diff --git a/src/Map/src/Bridge/Google/assets/src/map_controller.ts b/src/Map/src/Bridge/Google/assets/src/map_controller.ts index 18396a0c85a..9e5f8762d6f 100644 --- a/src/Map/src/Bridge/Google/assets/src/map_controller.ts +++ b/src/Map/src/Bridge/Google/assets/src/map_controller.ts @@ -210,24 +210,7 @@ export default class extends AbstractMapController< if (definition.opened) { infoWindow.open({ map: this.map, anchor: element }); } - } else if (element instanceof google.maps.Polygon) { - element.addListener('click', (event: any) => { - if (definition.autoClose) { - this.closeInfoWindowsExcept(infoWindow); - } - infoWindow.setPosition(event.latLng); - infoWindow.open(this.map); - }); - - if (definition.opened) { - const bounds = new google.maps.LatLngBounds(); - element.getPath().forEach((point: google.maps.LatLng) => { - bounds.extend(point); - }); - infoWindow.setPosition(bounds.getCenter()); - infoWindow.open({ map: this.map, anchor: element }); - } - } else if (element instanceof google.maps.Polyline) { + } else if (element instanceof google.maps.Polygon || element instanceof google.maps.Polyline) { element.addListener('click', (event: any) => { if (definition.autoClose) { this.closeInfoWindowsExcept(infoWindow); diff --git a/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php b/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php index 0dee4010410..5a3b167b267 100644 --- a/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php +++ b/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php @@ -29,26 +29,26 @@ public function provideTestRenderMap(): iterable ->zoom(12); yield 'simple map, with minimum options' => [ - 'expected_render' => '
', + 'expected_render' => '
', 'renderer' => new GoogleRenderer(new StimulusHelper(null), apiKey: 'api_key'), 'map' => $map, ]; yield 'with every options' => [ - 'expected_render' => '
', + 'expected_render' => '
', 'renderer' => new GoogleRenderer(new StimulusHelper(null), apiKey: 'api_key', id: 'gmap', language: 'fr', region: 'FR', nonce: 'abcd', retries: 10, url: 'https://maps.googleapis.com/maps/api/js', version: 'quarterly'), 'map' => $map, ]; yield 'with custom attributes' => [ - 'expected_render' => '
', + 'expected_render' => '
', 'renderer' => new GoogleRenderer(new StimulusHelper(null), apiKey: 'api_key'), 'map' => $map, 'attributes' => ['data-controller' => 'my-custom-controller', 'class' => 'map'], ]; yield 'with markers and infoWindows' => [ - 'expected_render' => '
', + 'expected_render' => '
', 'renderer' => new GoogleRenderer(new StimulusHelper(null), apiKey: 'api_key'), 'map' => (clone $map) ->addMarker(new Marker(new Point(48.8566, 2.3522), 'Paris')) @@ -56,7 +56,7 @@ public function provideTestRenderMap(): iterable ]; yield 'with controls enabled' => [ - 'expected_render' => '
', + 'expected_render' => '
', 'renderer' => new GoogleRenderer(new StimulusHelper(null), apiKey: 'api_key'), 'map' => (clone $map) ->options(new GoogleOptions( @@ -68,7 +68,7 @@ public function provideTestRenderMap(): iterable ]; yield 'without controls enabled' => [ - 'expected_render' => '
', + 'expected_render' => '
', 'renderer' => new GoogleRenderer(new StimulusHelper(null), apiKey: 'api_key'), 'map' => (clone $map) ->options(new GoogleOptions( diff --git a/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.js b/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.js index f98c299b31a..0087754f43d 100644 --- a/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.js +++ b/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.js @@ -1,68 +1,8 @@ -import { Controller } from '@hotwired/stimulus'; +import AbstractMapController from '@symfony/ux-map'; import 'leaflet/dist/leaflet.min.css'; import * as L from 'leaflet'; -class default_1 extends Controller { - constructor() { - super(...arguments); - this.markers = []; - this.infoWindows = []; - this.polygons = []; - this.polylines = []; - } - connect() { - const { center, zoom, options, markers, polygons, fitBoundsToMarkers } = this.viewValue; - this.dispatchEvent('pre-connect', { options }); - this.map = this.doCreateMap({ center, zoom, options }); - markers.forEach((marker) => this.createMarker(marker)); - polygons.forEach((polygon) => this.createPolygon(polygon)); - polylines.forEach((polyline) => this.createPolyline(polyline)); - if (fitBoundsToMarkers) { - this.doFitBoundsToMarkers(); - } - this.dispatchEvent('connect', { - map: this.map, - markers: this.markers, - polygons: this.polygons, - polylines: this.polylines, - 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); - 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); - return polygon; - } - createPolyline(definition) { - this.dispatchEvent('polyline:before-create', { definition }); - const polyline = this.doCreatePolyline(definition); - this.dispatchEvent('polyline:after-create', { polyline }); - this.polylines.push(polyline); - return polyline; - } - createInfoWindow({ definition, element, }) { - this.dispatchEvent('info-window:before-create', { definition, element }); - const infoWindow = this.doCreateInfoWindow({ definition, element }); - this.dispatchEvent('info-window:after-create', { infoWindow, element }); - this.infoWindows.push(infoWindow); - return infoWindow; - } -} -default_1.values = { - providerOptions: Object, - view: Object, -}; - -class map_controller extends default_1 { +class map_controller extends AbstractMapController { connect() { L.Marker.prototype.options.icon = L.divIcon({ html: '', diff --git a/src/Map/src/Bridge/Leaflet/tests/LeafletRendererTest.php b/src/Map/src/Bridge/Leaflet/tests/LeafletRendererTest.php index 2624877c43b..e59d27838b5 100644 --- a/src/Map/src/Bridge/Leaflet/tests/LeafletRendererTest.php +++ b/src/Map/src/Bridge/Leaflet/tests/LeafletRendererTest.php @@ -28,20 +28,20 @@ public function provideTestRenderMap(): iterable ->zoom(12); yield 'simple map' => [ - 'expected_render' => '
', + 'expected_render' => '
', 'renderer' => new LeafletRenderer(new StimulusHelper(null)), 'map' => $map, ]; yield 'with custom attributes' => [ - 'expected_render' => '
', + 'expected_render' => '
', 'renderer' => new LeafletRenderer(new StimulusHelper(null)), 'map' => $map, 'attributes' => ['data-controller' => 'my-custom-controller', 'class' => 'map'], ]; yield 'with markers and infoWindows' => [ - 'expected_render' => '
', + 'expected_render' => '
', 'renderer' => new LeafletRenderer(new StimulusHelper(null)), 'map' => (clone $map) ->addMarker(new Marker(new Point(48.8566, 2.3522), 'Paris')) diff --git a/src/Map/src/Polygon.php b/src/Map/src/Polygon.php index 5d474346e7d..2339aff4ebb 100644 --- a/src/Map/src/Polygon.php +++ b/src/Map/src/Polygon.php @@ -33,6 +33,12 @@ public function __construct( /** * Convert the polygon to an array representation. + * @return array{ + * points: array, + * title: string|null, + * infoWindow: array|null, + * extra: object, + * } */ public function toArray(): array { diff --git a/src/Map/src/Polyline.php b/src/Map/src/Polyline.php index 44f9f11dc8b..345441bc387 100644 --- a/src/Map/src/Polyline.php +++ b/src/Map/src/Polyline.php @@ -16,7 +16,7 @@ /** * Represents a polyline on a map. * - * @author [Pierre Svgnt] + * @author [Sylvain Blondeau] */ final readonly class Polyline { @@ -33,6 +33,12 @@ public function __construct( /** * Convert the polyline to an array representation. + * @return array{ + * points: array, + * title: string|null, + * infoWindow: array|null, + * extra: object, + * } */ public function toArray(): array { diff --git a/src/Map/src/Twig/MapRuntime.php b/src/Map/src/Twig/MapRuntime.php index d81462f8a07..6dca2c53b4e 100644 --- a/src/Map/src/Twig/MapRuntime.php +++ b/src/Map/src/Twig/MapRuntime.php @@ -76,7 +76,7 @@ public function renderMap( public function render(array $args = []): string { - $map = array_intersect_key($args, ['map' => 0, 'markers' => 0, 'polygons' => 0, 'polylines' => 0, 'center' => 1, 'zoom' => 2]); + $map = array_intersect_key($args, ['map' => 0, 'markers' => 1, 'polygons' => 2, 'polylines' => 3, 'center' => 4, 'zoom' => 5]); $attributes = array_diff_key($args, $map); return $this->renderMap(...$map, attributes: $attributes); diff --git a/src/Map/src/Twig/UXMapComponent.php b/src/Map/src/Twig/UXMapComponent.php index ae764f41e49..a8801ad780d 100644 --- a/src/Map/src/Twig/UXMapComponent.php +++ b/src/Map/src/Twig/UXMapComponent.php @@ -11,8 +11,8 @@ namespace Symfony\UX\Map\Twig; -use Symfony\UX\Map\Point; use Symfony\UX\Map\Marker; +use Symfony\UX\Map\Point; use Symfony\UX\Map\Polygon; use Symfony\UX\Map\Polyline; From c13c914ad78c54383b617088d8acea079b0411a8 Mon Sep 17 00:00:00 2001 From: Sylvain Blondeau Date: Wed, 6 Nov 2024 23:27:27 +0100 Subject: [PATCH 07/12] corect lint --- .../assets/dist/abstract_map_controller.d.ts | 14 ++-- .../assets/dist/abstract_map_controller.js | 3 +- src/Map/assets/src/abstract_map_controller.ts | 58 ++++++++--------- .../Google/assets/dist/map_controller.js | 65 +------------------ src/Map/src/Twig/UXMapComponent.php | 1 + 5 files changed, 35 insertions(+), 106 deletions(-) diff --git a/src/Map/assets/dist/abstract_map_controller.d.ts b/src/Map/assets/dist/abstract_map_controller.d.ts index 12a297d844a..9cb0676457a 100644 --- a/src/Map/assets/dist/abstract_map_controller.d.ts +++ b/src/Map/assets/dist/abstract_map_controller.d.ts @@ -65,17 +65,11 @@ export default abstract class): Marker; protected abstract doCreatePolygon(definition: PolygonDefinition): Polygon; protected abstract doCreatePolyline(definition: PolylineDefinition): Polyline; - protected abstract createInfoWindow(args: { - definition: MarkerDefinition['infoWindow']; - element: Marker; - } | { - definition: PolygonDefinition['infoWindow']; - element: Polygon; - } | { - definition: PolylineDefinition['infoWindow']; - element: Polyline; + protected createInfoWindow({ definition, element, }: { + definition: MarkerDefinition['infoWindow'] | PolygonDefinition['infoWindow'] | PolylineDefinition['infoWindow']; + element: Marker | Polygon | Polyline; }): InfoWindow; - protected abstract doCreateInfoWindow(args: { + protected abstract doCreateInfoWindow({ definition, element, }: { definition: MarkerDefinition['infoWindow']; element: Marker; } | { diff --git a/src/Map/assets/dist/abstract_map_controller.js b/src/Map/assets/dist/abstract_map_controller.js index d93c21da3dc..c7c6651af17 100644 --- a/src/Map/assets/dist/abstract_map_controller.js +++ b/src/Map/assets/dist/abstract_map_controller.js @@ -47,8 +47,7 @@ class default_1 extends Controller { this.polylines.push(polyline); return polyline; } - createInfoWindow(args) { - const { definition, element } = args; + createInfoWindow({ definition, element, }) { this.dispatchEvent('info-window:before-create', { definition, element }); const infoWindow = this.doCreateInfoWindow({ definition, element }); this.dispatchEvent('info-window:after-create', { infoWindow, element }); diff --git a/src/Map/assets/src/abstract_map_controller.ts b/src/Map/assets/src/abstract_map_controller.ts index e08fd87486b..43ce620320c 100644 --- a/src/Map/assets/src/abstract_map_controller.ts +++ b/src/Map/assets/src/abstract_map_controller.ts @@ -156,22 +156,16 @@ export default abstract class< protected abstract doCreatePolygon(definition: PolygonDefinition): Polygon; protected abstract doCreatePolyline(definition: PolylineDefinition): Polyline; - protected abstract createInfoWindow( - args: - | { - definition: MarkerDefinition['infoWindow']; - element: Marker; - } - | { - definition: PolygonDefinition['infoWindow']; - element: Polygon; - } - | { - definition: PolylineDefinition['infoWindow']; - element: Polyline; - } - ): InfoWindow { - const { definition, element } = args; + protected createInfoWindow({ + definition, + element, + }: { + definition: + | MarkerDefinition['infoWindow'] + | PolygonDefinition['infoWindow'] + | PolylineDefinition['infoWindow']; + element: Marker | Polygon | Polyline; + }): InfoWindow { this.dispatchEvent('info-window:before-create', { definition, element }); const infoWindow = this.doCreateInfoWindow({ definition, element }); this.dispatchEvent('info-window:after-create', { infoWindow, element }); @@ -181,21 +175,23 @@ export default abstract class< return infoWindow; } - protected abstract doCreateInfoWindow( - args: - | { - definition: MarkerDefinition['infoWindow']; - element: Marker; - } - | { - definition: PolygonDefinition['infoWindow']; - element: Polygon; - } - | { - definition: PolylineDefinition['infoWindow']; - element: Polyline; - } - ): InfoWindow; + + protected abstract doCreateInfoWindow({ + definition, + element, + }: + | { + definition: MarkerDefinition['infoWindow']; + element: Marker; + } + | { + definition: PolygonDefinition['infoWindow']; + element: Polygon; + } + | { + definition: PolylineDefinition['infoWindow']; + element: Polyline; + }): InfoWindow; protected abstract doFitBoundsToMarkers(): void; diff --git a/src/Map/src/Bridge/Google/assets/dist/map_controller.js b/src/Map/src/Bridge/Google/assets/dist/map_controller.js index a2bee153b3b..5e18af2261b 100644 --- a/src/Map/src/Bridge/Google/assets/dist/map_controller.js +++ b/src/Map/src/Bridge/Google/assets/dist/map_controller.js @@ -1,69 +1,8 @@ -import { Controller } from '@hotwired/stimulus'; +import AbstractMapController from '@symfony/ux-map'; import { Loader } from '@googlemaps/js-api-loader'; -let default_1$1 = class default_1 extends Controller { - constructor() { - super(...arguments); - this.markers = []; - this.infoWindows = []; - this.polygons = []; - this.polylines = []; - } - connect() { - const { center, zoom, options, markers, polygons, polylines, fitBoundsToMarkers } = this.viewValue; - this.dispatchEvent('pre-connect', { options }); - this.map = this.doCreateMap({ center, zoom, options }); - markers.forEach((marker) => this.createMarker(marker)); - polygons.forEach((polygon) => this.createPolygon(polygon)); - polylines.forEach((polyline) => this.createPolyline(polyline)); - if (fitBoundsToMarkers) { - this.doFitBoundsToMarkers(); - } - this.dispatchEvent('connect', { - map: this.map, - markers: this.markers, - polygons: this.polygons, - polylines: this.polylines, - 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); - 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); - return polygon; - } - createPolyline(definition) { - this.dispatchEvent('polyline:before-create', { definition }); - const polyline = this.doCreatePolyline(definition); - this.dispatchEvent('polyline:after-create', { polyline }); - this.polylines.push(polyline); - return polyline; - } - createInfoWindow(args) { - const { definition, element } = args; - this.dispatchEvent('info-window:before-create', { definition, element }); - const infoWindow = this.doCreateInfoWindow({ definition, element }); - this.dispatchEvent('info-window:after-create', { infoWindow, element }); - this.infoWindows.push(infoWindow); - return infoWindow; - } -}; -default_1$1.values = { - providerOptions: Object, - view: Object, -}; - let _google; -class default_1 extends default_1$1 { +class default_1 extends AbstractMapController { async connect() { if (!_google) { _google = { maps: {} }; diff --git a/src/Map/src/Twig/UXMapComponent.php b/src/Map/src/Twig/UXMapComponent.php index a8801ad780d..bfe4b12a322 100644 --- a/src/Map/src/Twig/UXMapComponent.php +++ b/src/Map/src/Twig/UXMapComponent.php @@ -36,6 +36,7 @@ final class UXMapComponent * @var Polygon[] */ public array $polygons; + /** * @var Polyline[] */ From 43026839feb4e93e9ca819e447d2fc7646e155a9 Mon Sep 17 00:00:00 2001 From: Sylvain Blondeau Date: Sun, 10 Nov 2024 00:00:58 +0100 Subject: [PATCH 08/12] fix php tests --- .../src/Bridge/Google/tests/GoogleRendererTest.php | 12 ++++++------ .../src/Bridge/Leaflet/tests/LeafletRendererTest.php | 8 +++++--- src/Map/src/Map.php | 1 + 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php b/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php index 5a3b167b267..aa332f661e0 100644 --- a/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php +++ b/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php @@ -29,26 +29,26 @@ public function provideTestRenderMap(): iterable ->zoom(12); yield 'simple map, with minimum options' => [ - 'expected_render' => '
', + 'expected_render' => '
', 'renderer' => new GoogleRenderer(new StimulusHelper(null), apiKey: 'api_key'), 'map' => $map, ]; yield 'with every options' => [ - 'expected_render' => '
', + 'expected_render' => '
', 'renderer' => new GoogleRenderer(new StimulusHelper(null), apiKey: 'api_key', id: 'gmap', language: 'fr', region: 'FR', nonce: 'abcd', retries: 10, url: 'https://maps.googleapis.com/maps/api/js', version: 'quarterly'), 'map' => $map, ]; yield 'with custom attributes' => [ - 'expected_render' => '
', + 'expected_render' => '
', 'renderer' => new GoogleRenderer(new StimulusHelper(null), apiKey: 'api_key'), 'map' => $map, 'attributes' => ['data-controller' => 'my-custom-controller', 'class' => 'map'], ]; yield 'with markers and infoWindows' => [ - 'expected_render' => '
', + 'expected_render' => '
', 'renderer' => new GoogleRenderer(new StimulusHelper(null), apiKey: 'api_key'), 'map' => (clone $map) ->addMarker(new Marker(new Point(48.8566, 2.3522), 'Paris')) @@ -56,7 +56,7 @@ public function provideTestRenderMap(): iterable ]; yield 'with controls enabled' => [ - 'expected_render' => '
', + 'expected_render' => '
', 'renderer' => new GoogleRenderer(new StimulusHelper(null), apiKey: 'api_key'), 'map' => (clone $map) ->options(new GoogleOptions( @@ -68,7 +68,7 @@ public function provideTestRenderMap(): iterable ]; yield 'without controls enabled' => [ - 'expected_render' => '
', + 'expected_render' => '
', 'renderer' => new GoogleRenderer(new StimulusHelper(null), apiKey: 'api_key'), 'map' => (clone $map) ->options(new GoogleOptions( diff --git a/src/Map/src/Bridge/Leaflet/tests/LeafletRendererTest.php b/src/Map/src/Bridge/Leaflet/tests/LeafletRendererTest.php index e59d27838b5..0e061a7cfc2 100644 --- a/src/Map/src/Bridge/Leaflet/tests/LeafletRendererTest.php +++ b/src/Map/src/Bridge/Leaflet/tests/LeafletRendererTest.php @@ -28,20 +28,20 @@ public function provideTestRenderMap(): iterable ->zoom(12); yield 'simple map' => [ - 'expected_render' => '
', + 'expected_render' => '
', 'renderer' => new LeafletRenderer(new StimulusHelper(null)), 'map' => $map, ]; yield 'with custom attributes' => [ - 'expected_render' => '
', + 'expected_render' => '
', 'renderer' => new LeafletRenderer(new StimulusHelper(null)), 'map' => $map, 'attributes' => ['data-controller' => 'my-custom-controller', 'class' => 'map'], ]; yield 'with markers and infoWindows' => [ - 'expected_render' => '
', + 'expected_render' => '
', 'renderer' => new LeafletRenderer(new StimulusHelper(null)), 'map' => (clone $map) ->addMarker(new Marker(new Point(48.8566, 2.3522), 'Paris')) @@ -49,3 +49,5 @@ public function provideTestRenderMap(): iterable ]; } } + + diff --git a/src/Map/src/Map.php b/src/Map/src/Map.php index 49e360776f6..739eac71c59 100644 --- a/src/Map/src/Map.php +++ b/src/Map/src/Map.php @@ -98,6 +98,7 @@ public function addPolygon(Polygon $polygon): self return $this; } + public function addPolyline(Polyline $polyline): self { $this->polylines[] = $polyline; From 4190620a8bf577a00ce1254a5e17e55e698793a3 Mon Sep 17 00:00:00 2001 From: Sylvain Blondeau Date: Sun, 10 Nov 2024 00:21:35 +0100 Subject: [PATCH 09/12] fix some linter and coding style --- src/Autocomplete/assets/dist/controller.js | 2 +- src/Map/assets/src/abstract_map_controller.ts | 4 ++-- .../src/Bridge/Leaflet/tests/LeafletRendererTest.php | 2 -- src/Map/src/Map.php | 11 ++++++----- src/Map/src/Polygon.php | 1 + src/Map/src/Polyline.php | 1 + src/Map/src/Twig/UXMapComponent.php | 2 +- 7 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/Autocomplete/assets/dist/controller.js b/src/Autocomplete/assets/dist/controller.js index 7d9d148dd26..ee18096c3a7 100644 --- a/src/Autocomplete/assets/dist/controller.js +++ b/src/Autocomplete/assets/dist/controller.js @@ -15,7 +15,7 @@ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ -/* global Reflect, Promise, SuppressedError, Symbol */ +/* global Reflect, Promise, SuppressedError, Symbol, Iterator */ function __classPrivateFieldGet(receiver, state, kind, f) { diff --git a/src/Map/assets/src/abstract_map_controller.ts b/src/Map/assets/src/abstract_map_controller.ts index 43ce620320c..f19bb66cdb0 100644 --- a/src/Map/assets/src/abstract_map_controller.ts +++ b/src/Map/assets/src/abstract_map_controller.ts @@ -189,8 +189,8 @@ export default abstract class< element: Polygon; } | { - definition: PolylineDefinition['infoWindow']; - element: Polyline; + definition: PolylineDefinition['infoWindow']; + element: Polyline; }): InfoWindow; protected abstract doFitBoundsToMarkers(): void; diff --git a/src/Map/src/Bridge/Leaflet/tests/LeafletRendererTest.php b/src/Map/src/Bridge/Leaflet/tests/LeafletRendererTest.php index 0e061a7cfc2..d48ebeef5ed 100644 --- a/src/Map/src/Bridge/Leaflet/tests/LeafletRendererTest.php +++ b/src/Map/src/Bridge/Leaflet/tests/LeafletRendererTest.php @@ -49,5 +49,3 @@ public function provideTestRenderMap(): iterable ]; } } - - diff --git a/src/Map/src/Map.php b/src/Map/src/Map.php index 739eac71c59..3053d524df2 100644 --- a/src/Map/src/Map.php +++ b/src/Map/src/Map.php @@ -40,7 +40,8 @@ public function __construct( * @var array */ private array $polylines = [], - ) {} + ) { + } public function getRendererName(): ?string { @@ -98,7 +99,7 @@ public function addPolygon(Polygon $polygon): self return $this; } - + public function addPolyline(Polyline $polyline): self { $this->polylines[] = $polyline; @@ -123,9 +124,9 @@ public function toArray(): array 'zoom' => $this->zoom, 'fitBoundsToMarkers' => $this->fitBoundsToMarkers, 'options' => (object) ($this->options?->toArray() ?? []), - 'markers' => array_map(static fn(Marker $marker) => $marker->toArray(), $this->markers), - 'polygons' => array_map(static fn(Polygon $polygon) => $polygon->toArray(), $this->polygons), - 'polylines' => array_map(static fn(Polyline $polyline) => $polyline->toArray(), $this->polylines), + 'markers' => array_map(static fn (Marker $marker) => $marker->toArray(), $this->markers), + 'polygons' => array_map(static fn (Polygon $polygon) => $polygon->toArray(), $this->polygons), + 'polylines' => array_map(static fn (Polyline $polyline) => $polyline->toArray(), $this->polylines), ]; } diff --git a/src/Map/src/Polygon.php b/src/Map/src/Polygon.php index 2339aff4ebb..444828b39ea 100644 --- a/src/Map/src/Polygon.php +++ b/src/Map/src/Polygon.php @@ -33,6 +33,7 @@ public function __construct( /** * Convert the polygon to an array representation. + * * @return array{ * points: array, * title: string|null, diff --git a/src/Map/src/Polyline.php b/src/Map/src/Polyline.php index 345441bc387..83afae9bb36 100644 --- a/src/Map/src/Polyline.php +++ b/src/Map/src/Polyline.php @@ -33,6 +33,7 @@ public function __construct( /** * Convert the polyline to an array representation. + * * @return array{ * points: array, * title: string|null, diff --git a/src/Map/src/Twig/UXMapComponent.php b/src/Map/src/Twig/UXMapComponent.php index bfe4b12a322..4c167420d52 100644 --- a/src/Map/src/Twig/UXMapComponent.php +++ b/src/Map/src/Twig/UXMapComponent.php @@ -36,7 +36,7 @@ final class UXMapComponent * @var Polygon[] */ public array $polygons; - + /** * @var Polyline[] */ From 03b6284af87dc07dc56b88105c9d045951af0e04 Mon Sep 17 00:00:00 2001 From: Sylvain Blondeau Date: Sun, 10 Nov 2024 00:28:14 +0100 Subject: [PATCH 10/12] fix lint --- src/Map/assets/src/abstract_map_controller.ts | 1 - .../Google/assets/dist/map_controller.js | 64 ++++++++++++++++++- .../Leaflet/assets/dist/map_controller.js | 64 ++++++++++++++++++- 3 files changed, 124 insertions(+), 5 deletions(-) diff --git a/src/Map/assets/src/abstract_map_controller.ts b/src/Map/assets/src/abstract_map_controller.ts index f19bb66cdb0..b5c67476481 100644 --- a/src/Map/assets/src/abstract_map_controller.ts +++ b/src/Map/assets/src/abstract_map_controller.ts @@ -175,7 +175,6 @@ export default abstract class< return infoWindow; } - protected abstract doCreateInfoWindow({ definition, element, diff --git a/src/Map/src/Bridge/Google/assets/dist/map_controller.js b/src/Map/src/Bridge/Google/assets/dist/map_controller.js index 5e18af2261b..469052d6fff 100644 --- a/src/Map/src/Bridge/Google/assets/dist/map_controller.js +++ b/src/Map/src/Bridge/Google/assets/dist/map_controller.js @@ -1,8 +1,68 @@ -import AbstractMapController from '@symfony/ux-map'; +import { Controller } from '@hotwired/stimulus'; import { Loader } from '@googlemaps/js-api-loader'; +let default_1$1 = class default_1 extends Controller { + constructor() { + super(...arguments); + this.markers = []; + this.infoWindows = []; + this.polygons = []; + this.polylines = []; + } + connect() { + const { center, zoom, options, markers, polygons, polylines, fitBoundsToMarkers } = this.viewValue; + this.dispatchEvent('pre-connect', { options }); + this.map = this.doCreateMap({ center, zoom, options }); + markers.forEach((marker) => this.createMarker(marker)); + polygons.forEach((polygon) => this.createPolygon(polygon)); + polylines.forEach((polyline) => this.createPolyline(polyline)); + if (fitBoundsToMarkers) { + this.doFitBoundsToMarkers(); + } + this.dispatchEvent('connect', { + map: this.map, + markers: this.markers, + polygons: this.polygons, + polylines: this.polylines, + 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); + 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); + return polygon; + } + createPolyline(definition) { + this.dispatchEvent('polyline:before-create', { definition }); + const polyline = this.doCreatePolyline(definition); + this.dispatchEvent('polyline:after-create', { polyline }); + this.polylines.push(polyline); + return polyline; + } + createInfoWindow({ definition, element, }) { + this.dispatchEvent('info-window:before-create', { definition, element }); + const infoWindow = this.doCreateInfoWindow({ definition, element }); + this.dispatchEvent('info-window:after-create', { infoWindow, element }); + this.infoWindows.push(infoWindow); + return infoWindow; + } +}; +default_1$1.values = { + providerOptions: Object, + view: Object, +}; + let _google; -class default_1 extends AbstractMapController { +class default_1 extends default_1$1 { async connect() { if (!_google) { _google = { maps: {} }; diff --git a/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.js b/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.js index 0087754f43d..f391cd06b36 100644 --- a/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.js +++ b/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.js @@ -1,8 +1,68 @@ -import AbstractMapController from '@symfony/ux-map'; +import { Controller } from '@hotwired/stimulus'; import 'leaflet/dist/leaflet.min.css'; import * as L from 'leaflet'; -class map_controller extends AbstractMapController { +class default_1 extends Controller { + constructor() { + super(...arguments); + this.markers = []; + this.infoWindows = []; + this.polygons = []; + this.polylines = []; + } + connect() { + const { center, zoom, options, markers, polygons, polylines, fitBoundsToMarkers } = this.viewValue; + this.dispatchEvent('pre-connect', { options }); + this.map = this.doCreateMap({ center, zoom, options }); + markers.forEach((marker) => this.createMarker(marker)); + polygons.forEach((polygon) => this.createPolygon(polygon)); + polylines.forEach((polyline) => this.createPolyline(polyline)); + if (fitBoundsToMarkers) { + this.doFitBoundsToMarkers(); + } + this.dispatchEvent('connect', { + map: this.map, + markers: this.markers, + polygons: this.polygons, + polylines: this.polylines, + 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); + 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); + return polygon; + } + createPolyline(definition) { + this.dispatchEvent('polyline:before-create', { definition }); + const polyline = this.doCreatePolyline(definition); + this.dispatchEvent('polyline:after-create', { polyline }); + this.polylines.push(polyline); + return polyline; + } + createInfoWindow({ definition, element, }) { + this.dispatchEvent('info-window:before-create', { definition, element }); + const infoWindow = this.doCreateInfoWindow({ definition, element }); + this.dispatchEvent('info-window:after-create', { infoWindow, element }); + this.infoWindows.push(infoWindow); + return infoWindow; + } +} +default_1.values = { + providerOptions: Object, + view: Object, +}; + +class map_controller extends default_1 { connect() { L.Marker.prototype.options.icon = L.divIcon({ html: '', From 1517fe1c303cabfe913236b8fe470a37062b0223 Mon Sep 17 00:00:00 2001 From: Sylvain Blondeau Date: Sun, 10 Nov 2024 08:52:28 +0100 Subject: [PATCH 11/12] remove autocomplete file --- src/Autocomplete/assets/dist/controller.js | 52 +++--- .../Google/assets/dist/map_controller.js | 64 +------- .../Leaflet/assets/dist/map_controller.d.ts | 30 ---- .../Leaflet/assets/dist/map_controller.js | 150 ------------------ 4 files changed, 28 insertions(+), 268 deletions(-) delete mode 100644 src/Map/src/Bridge/Leaflet/assets/dist/map_controller.d.ts delete mode 100644 src/Map/src/Bridge/Leaflet/assets/dist/map_controller.js diff --git a/src/Autocomplete/assets/dist/controller.js b/src/Autocomplete/assets/dist/controller.js index ee18096c3a7..f78a7e06159 100644 --- a/src/Autocomplete/assets/dist/controller.js +++ b/src/Autocomplete/assets/dist/controller.js @@ -1,32 +1,32 @@ import { Controller } from '@hotwired/stimulus'; import TomSelect from 'tom-select'; -/****************************************************************************** -Copyright (c) Microsoft Corporation. - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THIS SOFTWARE. -***************************************************************************** */ -/* global Reflect, Promise, SuppressedError, Symbol, Iterator */ - - -function __classPrivateFieldGet(receiver, state, kind, f) { - if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); - if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); - return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); -} - -typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { - var e = new Error(message); - return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; +/****************************************************************************** +Copyright (c) Microsoft Corporation. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. +***************************************************************************** */ +/* global Reflect, Promise, SuppressedError, Symbol */ + + +function __classPrivateFieldGet(receiver, state, kind, f) { + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); + return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); +} + +typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { + var e = new Error(message); + return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; var _default_1_instances, _default_1_getCommonConfig, _default_1_createAutocomplete, _default_1_createAutocompleteWithHtmlContents, _default_1_createAutocompleteWithRemoteData, _default_1_stripTags, _default_1_mergeObjects, _default_1_createTomSelect; diff --git a/src/Map/src/Bridge/Google/assets/dist/map_controller.js b/src/Map/src/Bridge/Google/assets/dist/map_controller.js index 469052d6fff..5e18af2261b 100644 --- a/src/Map/src/Bridge/Google/assets/dist/map_controller.js +++ b/src/Map/src/Bridge/Google/assets/dist/map_controller.js @@ -1,68 +1,8 @@ -import { Controller } from '@hotwired/stimulus'; +import AbstractMapController from '@symfony/ux-map'; import { Loader } from '@googlemaps/js-api-loader'; -let default_1$1 = class default_1 extends Controller { - constructor() { - super(...arguments); - this.markers = []; - this.infoWindows = []; - this.polygons = []; - this.polylines = []; - } - connect() { - const { center, zoom, options, markers, polygons, polylines, fitBoundsToMarkers } = this.viewValue; - this.dispatchEvent('pre-connect', { options }); - this.map = this.doCreateMap({ center, zoom, options }); - markers.forEach((marker) => this.createMarker(marker)); - polygons.forEach((polygon) => this.createPolygon(polygon)); - polylines.forEach((polyline) => this.createPolyline(polyline)); - if (fitBoundsToMarkers) { - this.doFitBoundsToMarkers(); - } - this.dispatchEvent('connect', { - map: this.map, - markers: this.markers, - polygons: this.polygons, - polylines: this.polylines, - 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); - 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); - return polygon; - } - createPolyline(definition) { - this.dispatchEvent('polyline:before-create', { definition }); - const polyline = this.doCreatePolyline(definition); - this.dispatchEvent('polyline:after-create', { polyline }); - this.polylines.push(polyline); - return polyline; - } - createInfoWindow({ definition, element, }) { - this.dispatchEvent('info-window:before-create', { definition, element }); - const infoWindow = this.doCreateInfoWindow({ definition, element }); - this.dispatchEvent('info-window:after-create', { infoWindow, element }); - this.infoWindows.push(infoWindow); - return infoWindow; - } -}; -default_1$1.values = { - providerOptions: Object, - view: Object, -}; - let _google; -class default_1 extends default_1$1 { +class default_1 extends AbstractMapController { async connect() { if (!_google) { _google = { maps: {} }; diff --git a/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.d.ts b/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.d.ts deleted file mode 100644 index f5dd9682125..00000000000 --- a/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.d.ts +++ /dev/null @@ -1,30 +0,0 @@ -import AbstractMapController from '@symfony/ux-map'; -import type { Point, MarkerDefinition, PolygonDefinition, PolylineDefinition } from '@symfony/ux-map'; -import 'leaflet/dist/leaflet.min.css'; -import * as L from 'leaflet'; -import type { MapOptions as LeafletMapOptions, MarkerOptions, PopupOptions, PolygonOptions, PolylineOptions } from 'leaflet'; -type MapOptions = Pick & { - tileLayer: { - url: string; - attribution: string; - options: Record; - }; -}; -export default class extends AbstractMapController { - connect(): void; - protected dispatchEvent(name: string, payload?: Record): void; - protected doCreateMap({ center, zoom, options, }: { - center: Point | null; - zoom: number | null; - options: MapOptions; - }): L.Map; - protected doCreateMarker(definition: MarkerDefinition): L.Marker; - protected doCreatePolygon(definition: PolygonDefinition): L.Polygon; - protected doCreatePolyline(definition: PolylineDefinition): L.Polyline; - protected doCreateInfoWindow({ definition, element, }: { - definition: MarkerDefinition['infoWindow'] | PolygonDefinition['infoWindow'] | PolylineDefinition['infoWindow']; - element: L.Marker | L.Polygon | L.Polyline; - }): L.Popup; - protected doFitBoundsToMarkers(): void; -} -export {}; diff --git a/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.js b/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.js deleted file mode 100644 index f391cd06b36..00000000000 --- a/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.js +++ /dev/null @@ -1,150 +0,0 @@ -import { Controller } from '@hotwired/stimulus'; -import 'leaflet/dist/leaflet.min.css'; -import * as L from 'leaflet'; - -class default_1 extends Controller { - constructor() { - super(...arguments); - this.markers = []; - this.infoWindows = []; - this.polygons = []; - this.polylines = []; - } - connect() { - const { center, zoom, options, markers, polygons, polylines, fitBoundsToMarkers } = this.viewValue; - this.dispatchEvent('pre-connect', { options }); - this.map = this.doCreateMap({ center, zoom, options }); - markers.forEach((marker) => this.createMarker(marker)); - polygons.forEach((polygon) => this.createPolygon(polygon)); - polylines.forEach((polyline) => this.createPolyline(polyline)); - if (fitBoundsToMarkers) { - this.doFitBoundsToMarkers(); - } - this.dispatchEvent('connect', { - map: this.map, - markers: this.markers, - polygons: this.polygons, - polylines: this.polylines, - 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); - 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); - return polygon; - } - createPolyline(definition) { - this.dispatchEvent('polyline:before-create', { definition }); - const polyline = this.doCreatePolyline(definition); - this.dispatchEvent('polyline:after-create', { polyline }); - this.polylines.push(polyline); - return polyline; - } - createInfoWindow({ definition, element, }) { - this.dispatchEvent('info-window:before-create', { definition, element }); - const infoWindow = this.doCreateInfoWindow({ definition, element }); - this.dispatchEvent('info-window:after-create', { infoWindow, element }); - this.infoWindows.push(infoWindow); - return infoWindow; - } -} -default_1.values = { - providerOptions: Object, - view: Object, -}; - -class map_controller extends default_1 { - connect() { - L.Marker.prototype.options.icon = L.divIcon({ - html: '', - iconSize: [25, 41], - iconAnchor: [12.5, 41], - popupAnchor: [0, -41], - className: '', - }); - super.connect(); - } - dispatchEvent(name, payload = {}) { - this.dispatch(name, { - prefix: 'ux:map', - detail: { - ...payload, - L, - }, - }); - } - doCreateMap({ center, zoom, options, }) { - const map = L.map(this.element, { - ...options, - center: center === null ? undefined : center, - zoom: zoom === null ? undefined : zoom, - }); - L.tileLayer(options.tileLayer.url, { - attribution: options.tileLayer.attribution, - ...options.tileLayer.options, - }).addTo(map); - return map; - } - doCreateMarker(definition) { - const { position, title, infoWindow, extra, rawOptions = {}, ...otherOptions } = definition; - const marker = L.marker(position, { title, ...otherOptions, ...rawOptions }).addTo(this.map); - if (infoWindow) { - this.createInfoWindow({ definition: infoWindow, element: marker }); - } - return marker; - } - doCreatePolygon(definition) { - const { points, title, infoWindow, rawOptions = {} } = definition; - const polygon = L.polygon(points, { ...rawOptions }).addTo(this.map); - if (title) { - polygon.bindPopup(title); - } - if (infoWindow) { - this.createInfoWindow({ definition: infoWindow, element: polygon }); - } - return polygon; - } - doCreatePolyline(definition) { - const { points, title, infoWindow, rawOptions = {} } = definition; - const polyline = L.polyline(points, { ...rawOptions }).addTo(this.map); - if (title) { - polyline.bindPopup(title); - } - if (infoWindow) { - this.createInfoWindow({ definition: infoWindow, element: polyline }); - } - return polyline; - } - doCreateInfoWindow({ definition, element, }) { - const { headerContent, content, rawOptions = {}, ...otherOptions } = definition; - element.bindPopup([headerContent, content].filter((x) => x).join('
'), { ...otherOptions, ...rawOptions }); - if (definition.opened) { - element.openPopup(); - } - const popup = element.getPopup(); - if (!popup) { - throw new Error('Unable to get the Popup associated with the element.'); - } - return popup; - } - doFitBoundsToMarkers() { - if (this.markers.length === 0) { - return; - } - this.map.fitBounds(this.markers.map((marker) => { - const position = marker.getLatLng(); - return [position.lat, position.lng]; - })); - } -} - -export { map_controller as default }; From fd17d188afa66d249be2ea57870e2c31ef273755 Mon Sep 17 00:00:00 2001 From: Sylvain Blondeau Date: Mon, 11 Nov 2024 09:07:43 +0100 Subject: [PATCH 12/12] tests: fix in Map.php fromArray() --- src/Map/src/Map.php | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/Map/src/Map.php b/src/Map/src/Map.php index 3053d524df2..530e9a5c6cb 100644 --- a/src/Map/src/Map.php +++ b/src/Map/src/Map.php @@ -103,22 +103,21 @@ public function addPolygon(Polygon $polygon): self public function addPolyline(Polyline $polyline): self { $this->polylines[] = $polyline; - return $this; } - + public function toArray(): array { if (!$this->fitBoundsToMarkers) { if (null === $this->center) { throw new InvalidArgumentException('The map "center" must be explicitly set when not enabling "fitBoundsToMarkers" feature.'); } - + if (null === $this->zoom) { throw new InvalidArgumentException('The map "zoom" must be explicitly set when not enabling "fitBoundsToMarkers" feature.'); } } - + return [ 'center' => $this->center?->toArray(), 'zoom' => $this->zoom, @@ -146,32 +145,31 @@ public function toArray(): array public static function fromArray(array $map): self { $map['fitBoundsToMarkers'] = true; - if (isset($map['center'])) { $map['center'] = Point::fromArray($map['center']); } - + if (isset($map['zoom']) || isset($map['center'])) { $map['fitBoundsToMarkers'] = false; } - + $map['markers'] ??= []; if (!\is_array($map['markers'])) { throw new InvalidArgumentException('The "markers" parameter must be an array.'); } $map['markers'] = array_map(Marker::fromArray(...), $map['markers']); - + $map['polygons'] ??= []; if (!\is_array($map['polygons'])) { throw new InvalidArgumentException('The "polygons" parameter must be an array.'); } $map['polygons'] = array_map(Polygon::fromArray(...), $map['polygons']); - + $map['polylines'] ??= []; if (!\is_array($map['polylines'])) { throw new InvalidArgumentException('The "polylines" parameter must be an array.'); } - $map['polylines'] = array_map(Polygon::fromArray(...), $map['polylines']); + $map['polylines'] = array_map(Polyline::fromArray(...), $map['polylines']); return new self(...$map); }