diff --git a/src/Map/assets/dist/abstract_map_controller.d.ts b/src/Map/assets/dist/abstract_map_controller.d.ts index 7e78dc6b36e..e7a257737be 100644 --- a/src/Map/assets/dist/abstract_map_controller.d.ts +++ b/src/Map/assets/dist/abstract_map_controller.d.ts @@ -15,6 +15,7 @@ export type MarkerDefinition = { title: string | null; infoWindow?: Omit, 'position'>; rawOptions?: MarkerOptions; + extra: Record; }; export type InfoWindowDefinition = { headerContent: string | null; @@ -23,6 +24,7 @@ export type InfoWindowDefinition = { opened: boolean; autoClose: boolean; rawOptions?: InfoWindowOptions; + extra: Record; }; export default abstract class extends Controller { static values: { diff --git a/src/Map/assets/src/abstract_map_controller.ts b/src/Map/assets/src/abstract_map_controller.ts index 7b64d87267c..633ce7e0e00 100644 --- a/src/Map/assets/src/abstract_map_controller.ts +++ b/src/Map/assets/src/abstract_map_controller.ts @@ -14,7 +14,17 @@ export type MarkerDefinition = { position: Point; title: string | null; infoWindow?: Omit, 'position'>; + /** + * Raw options passed to the marker constructor, specific to the map provider (e.g.: `L.marker()` for Leaflet). + */ rawOptions?: MarkerOptions; + /** + * Extra data defined by the developer. + * They are not directly used by the Stimulus controller, but they can be used by the developer with event listeners: + * - `ux:map:marker:before-create` + * - `ux:map:marker:after-create` + */ + extra: Record; }; export type InfoWindowDefinition = { @@ -23,7 +33,18 @@ export type InfoWindowDefinition = { position: Point; opened: boolean; autoClose: boolean; + /** + * Raw options passed to the info window constructor, + * specific to the map provider (e.g.: `google.maps.InfoWindow()` for Google Maps). + */ rawOptions?: InfoWindowOptions; + /** + * Extra data defined by the developer. + * They are not directly used by the Stimulus controller, but they can be used by the developer with event listeners: + * - `ux:map:info-window:before-create` + * - `ux:map:info-window:after-create` + */ + extra: Record; }; export default abstract class< diff --git a/src/Map/doc/index.rst b/src/Map/doc/index.rst index 4414822ed53..78f63d1c16f 100644 --- a/src/Map/doc/index.rst +++ b/src/Map/doc/index.rst @@ -37,7 +37,7 @@ Configuration is done in your ``config/packages/ux_map.yaml`` file: # config/packages/ux_map.yaml ux_map: - renderer: '%env(UX_MAP_DSN)%' + renderer: '%env(resolve:default::UX_MAP_DSN)%' The ``UX_MAP_DSN`` environment variable configure which renderer to use. @@ -82,21 +82,33 @@ A map is created by calling ``new Map()``. You can configure the center, zoom, a ->zoom(6) ; - // 2. You can add markers, with an optional info window + // 2. You can add markers $myMap ->addMarker(new Marker( position: new Point(48.8566, 2.3522), title: 'Paris' )) + + // With an info window associated to the marker: ->addMarker(new Marker( position: new Point(45.7640, 4.8357), title: 'Lyon', - // With an info window infoWindow: new InfoWindow( headerContent: 'Lyon', content: 'The French town in the historic Rhône-Alpes region, located at the junction of the Rhône and Saône rivers.' ) - )); + )) + + // You can also pass extra data, that you can later use in your custom Stimulus controller + // when listening to "ux:map:marker:before-create" event: + ->addMarker(new Marker( + position: new Point(46.5074666, 6.633729), + title: 'Olympic Parc', + extra: [ + 'icon_mask_url' => 'https://maps.gstatic.com/mapfiles/place_api/icons/v2/tree_pinlet.svg', + ] + ) + ; // 3. And inject the map in your template to render it return $this->render('contact/index.html.twig', [ 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 d74b1268469..03f2ed92142 100644 --- a/src/Map/src/Bridge/Google/assets/dist/map_controller.js +++ b/src/Map/src/Bridge/Google/assets/dist/map_controller.js @@ -25,7 +25,7 @@ class default_1 extends AbstractMapController { }); } doCreateMarker(definition) { - const { position, title, infoWindow, rawOptions = {}, ...otherOptions } = definition; + const { position, title, infoWindow, extra, rawOptions = {}, ...otherOptions } = definition; const marker = new library.AdvancedMarkerElement({ position, title, @@ -39,7 +39,7 @@ class default_1 extends AbstractMapController { return marker; } doCreateInfoWindow({ definition, marker, }) { - const { headerContent, content, rawOptions = {}, ...otherOptions } = definition; + const { headerContent, content, extra, rawOptions = {}, ...otherOptions } = definition; const infoWindow = new library.InfoWindow({ headerContent: this.createTextOrElement(headerContent), content: this.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 a0def42d1d7..2712903d36e 100644 --- a/src/Map/src/Bridge/Google/assets/src/map_controller.ts +++ b/src/Map/src/Bridge/Google/assets/src/map_controller.ts @@ -87,7 +87,7 @@ export default class extends AbstractMapController< protected doCreateMarker( definition: MarkerDefinition ): google.maps.marker.AdvancedMarkerElement { - const { position, title, infoWindow, rawOptions = {}, ...otherOptions } = definition; + const { position, title, infoWindow, extra, rawOptions = {}, ...otherOptions } = definition; const marker = new library.AdvancedMarkerElement({ position, @@ -114,7 +114,7 @@ export default class extends AbstractMapController< >['infoWindow']; marker: google.maps.marker.AdvancedMarkerElement; }): google.maps.InfoWindow { - const { headerContent, content, rawOptions = {}, ...otherOptions } = definition; + const { headerContent, content, extra, rawOptions = {}, ...otherOptions } = definition; const infoWindow = new library.InfoWindow({ headerContent: this.createTextOrElement(headerContent), diff --git a/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php b/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php index 48ed1cbf52a..db011e30998 100644 --- a/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php +++ b/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php @@ -48,7 +48,7 @@ public function provideTestRenderMap(): iterable ]; 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')) 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 dde48536a42..3e8d3b80de9 100644 --- a/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.js +++ b/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.js @@ -26,7 +26,7 @@ class map_controller extends AbstractMapController { return map$1; } doCreateMarker(definition) { - const { position, title, infoWindow, rawOptions = {}, ...otherOptions } = definition; + const { position, title, infoWindow, extra, rawOptions = {}, ...otherOptions } = definition; const marker$1 = marker(position, { title, ...otherOptions, ...rawOptions }).addTo(this.map); if (infoWindow) { this.createInfoWindow({ definition: infoWindow, marker: marker$1 }); @@ -34,7 +34,7 @@ class map_controller extends AbstractMapController { return marker$1; } doCreateInfoWindow({ definition, marker, }) { - const { headerContent, content, rawOptions = {}, ...otherOptions } = definition; + const { headerContent, content, extra, rawOptions = {}, ...otherOptions } = definition; marker.bindPopup([headerContent, content].filter((x) => x).join('
'), { ...otherOptions, ...rawOptions }); if (definition.opened) { marker.openPopup(); 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 2988d0fbd6a..71230b141eb 100644 --- a/src/Map/src/Bridge/Leaflet/assets/src/map_controller.ts +++ b/src/Map/src/Bridge/Leaflet/assets/src/map_controller.ts @@ -51,7 +51,7 @@ export default class extends AbstractMapController< } protected doCreateMarker(definition: MarkerDefinition): Marker { - const { position, title, infoWindow, rawOptions = {}, ...otherOptions } = definition; + const { position, title, infoWindow, extra, rawOptions = {}, ...otherOptions } = definition; const marker = createMarker(position, { title, ...otherOptions, ...rawOptions }).addTo(this.map); @@ -69,7 +69,7 @@ export default class extends AbstractMapController< definition: MarkerDefinition['infoWindow']; marker: Marker; }): Popup { - const { headerContent, content, rawOptions = {}, ...otherOptions } = definition; + const { headerContent, content, extra, rawOptions = {}, ...otherOptions } = definition; marker.bindPopup([headerContent, content].filter((x) => x).join('
'), { ...otherOptions, ...rawOptions }); if (definition.opened) { diff --git a/src/Map/src/Bridge/Leaflet/tests/LeafletRendererTest.php b/src/Map/src/Bridge/Leaflet/tests/LeafletRendererTest.php index bfd596289c9..6931f53abf6 100644 --- a/src/Map/src/Bridge/Leaflet/tests/LeafletRendererTest.php +++ b/src/Map/src/Bridge/Leaflet/tests/LeafletRendererTest.php @@ -41,7 +41,7 @@ public function provideTestRenderMap(): iterable ]; 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/InfoWindow.php b/src/Map/src/InfoWindow.php index df432923f6d..4897b3c9cd2 100644 --- a/src/Map/src/InfoWindow.php +++ b/src/Map/src/InfoWindow.php @@ -18,12 +18,16 @@ */ final readonly class InfoWindow { + /** + * @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 ?string $headerContent = null, private ?string $content = null, private ?Point $position = null, private bool $opened = false, private bool $autoClose = true, + private array $extra = [], ) { } @@ -35,6 +39,7 @@ public function toArray(): array 'position' => $this->position?->toArray(), 'opened' => $this->opened, 'autoClose' => $this->autoClose, + 'extra' => (object) $this->extra, ]; } } diff --git a/src/Map/src/Marker.php b/src/Map/src/Marker.php index b33a27c9095..5c822698b74 100644 --- a/src/Map/src/Marker.php +++ b/src/Map/src/Marker.php @@ -18,10 +18,14 @@ */ final readonly class Marker { + /** + * @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 Point $position, private ?string $title = null, private ?InfoWindow $infoWindow = null, + private array $extra = [], ) { } @@ -31,6 +35,7 @@ public function toArray(): array 'position' => $this->position->toArray(), 'title' => $this->title, 'infoWindow' => $this->infoWindow?->toArray(), + 'extra' => (object) $this->extra, ]; } } diff --git a/src/Map/tests/InfoWindowTest.php b/src/Map/tests/InfoWindowTest.php index ca6325b3543..12c11f9b7af 100644 --- a/src/Map/tests/InfoWindowTest.php +++ b/src/Map/tests/InfoWindowTest.php @@ -27,6 +27,8 @@ public function testToArray(): void autoClose: false, ); + $array = $infoWindow->toArray(); + self::assertSame([ 'headerContent' => 'Paris', 'content' => 'Capitale de la France, est une grande ville européenne et un centre mondial de l\'art, de la mode, de la gastronomie et de la culture.', @@ -36,6 +38,7 @@ public function testToArray(): void ], 'opened' => true, 'autoClose' => false, - ], $infoWindow->toArray()); + 'extra' => $array['extra'], + ], $array); } } diff --git a/src/Map/tests/MapTest.php b/src/Map/tests/MapTest.php index 7ce579e46c9..293aa973806 100644 --- a/src/Map/tests/MapTest.php +++ b/src/Map/tests/MapTest.php @@ -101,17 +101,41 @@ public function toArray(): array [ 'position' => ['lat' => 48.8566, 'lng' => 2.3522], 'title' => 'Paris', - 'infoWindow' => ['headerContent' => 'Paris', 'content' => 'Paris', 'position' => ['lat' => 48.8566, 'lng' => 2.3522], 'opened' => false, 'autoClose' => true], + 'infoWindow' => [ + 'headerContent' => 'Paris', + 'content' => 'Paris', + 'position' => ['lat' => 48.8566, 'lng' => 2.3522], + 'opened' => false, + 'autoClose' => true, + 'extra' => $array['markers'][0]['infoWindow']['extra'], + ], + 'extra' => $array['markers'][0]['extra'], ], [ 'position' => ['lat' => 45.764, 'lng' => 4.8357], 'title' => 'Lyon', - 'infoWindow' => ['headerContent' => 'Lyon', 'content' => 'Lyon', 'position' => ['lat' => 45.764, 'lng' => 4.8357], 'opened' => true, 'autoClose' => true], + 'infoWindow' => [ + 'headerContent' => 'Lyon', + 'content' => 'Lyon', + 'position' => ['lat' => 45.764, 'lng' => 4.8357], + 'opened' => true, + 'autoClose' => true, + 'extra' => $array['markers'][1]['infoWindow']['extra'], + ], + 'extra' => $array['markers'][1]['extra'], ], [ 'position' => ['lat' => 43.2965, 'lng' => 5.3698], 'title' => 'Marseille', - 'infoWindow' => ['headerContent' => 'Marseille', 'content' => 'Marseille', 'position' => ['lat' => 43.2965, 'lng' => 5.3698], 'opened' => true, 'autoClose' => true], + 'infoWindow' => [ + 'headerContent' => 'Marseille', + 'content' => 'Marseille', + 'position' => ['lat' => 43.2965, 'lng' => 5.3698], + 'opened' => true, + 'autoClose' => true, + 'extra' => $array['markers'][2]['infoWindow']['extra'], + ], + 'extra' => $array['markers'][2]['extra'], ], ], ], $array); diff --git a/src/Map/tests/MarkerTest.php b/src/Map/tests/MarkerTest.php index 00468e8478f..e55dca0e29d 100644 --- a/src/Map/tests/MarkerTest.php +++ b/src/Map/tests/MarkerTest.php @@ -24,11 +24,14 @@ public function testToArray(): void position: new Point(48.8566, 2.3522), ); + $array = $marker->toArray(); + self::assertSame([ 'position' => ['lat' => 48.8566, 'lng' => 2.3522], 'title' => null, 'infoWindow' => null, - ], $marker->toArray()); + 'extra' => $array['extra'], + ], $array); $marker = new Marker( position: new Point(48.8566, 2.3522), @@ -40,6 +43,8 @@ public function testToArray(): void ), ); + $array = $marker->toArray(); + self::assertSame([ 'position' => ['lat' => 48.8566, 'lng' => 2.3522], 'title' => 'Paris', @@ -49,7 +54,9 @@ public function testToArray(): void 'position' => null, 'opened' => true, 'autoClose' => true, + 'extra' => $array['infoWindow']['extra'], ], - ], $marker->toArray()); + 'extra' => $array['extra'], + ], $array); } }