diff --git a/src/Map/assets/dist/abstract_map_controller.d.ts b/src/Map/assets/dist/abstract_map_controller.d.ts index 683b14b1087..23677b12e3f 100644 --- a/src/Map/assets/dist/abstract_map_controller.d.ts +++ b/src/Map/assets/dist/abstract_map_controller.d.ts @@ -1,55 +1,82 @@ -import { Controller } from '@hotwired/stimulus'; -export type LatLng = { - lat: number; - lng: number; +import { Controller } from "@hotwired/stimulus"; +export type Point = { + lat: number; + lng: number; }; export type MapView = { - center: LatLng; - zoom: number; - fitBoundsToMarkers: boolean; - markers: Array>; - options: Options; + center: Point; + zoom: number; + fitBoundsToMarkers: boolean; + markers: Array>; + options: Options; }; export type MarkerDefinition = { - position: LatLng; - title: string | null; - infoWindow?: Omit, 'position'>; - rawOptions?: MarkerOptions; + position: Point; + title: string | null; + infoWindow?: Omit, "position">; + rawOptions?: MarkerOptions; }; export type InfoWindowDefinition = { - headerContent: string | null; - content: string | null; - position: LatLng; - opened: boolean; - autoClose: boolean; - rawOptions?: InfoWindowOptions; + headerContent: string | null; + content: string | null; + position: Point; + opened: boolean; + autoClose: boolean; + rawOptions?: InfoWindowOptions; }; -export default abstract class extends Controller { - static values: { - providerOptions: ObjectConstructor; - view: ObjectConstructor; - }; - viewValue: MapView; - protected map: Map; - protected markers: Array; - protected infoWindows: Array; - initialize(): void; - connect(): void; - protected abstract doCreateMap({ center, zoom, options, }: { - center: LatLng; - zoom: number; - options: MapOptions; - }): Map; - createMarker(definition: MarkerDefinition): Marker; - protected abstract doCreateMarker(definition: MarkerDefinition): Marker; - protected createInfoWindow({ definition, marker, }: { - definition: MarkerDefinition['infoWindow']; - marker: Marker; - }): InfoWindow; - protected abstract doCreateInfoWindow({ definition, marker, }: { - definition: MarkerDefinition['infoWindow']; - marker: Marker; - }): InfoWindow; - protected abstract doFitBoundsToMarkers(): void; - private dispatchEvent; +export default abstract class< + MapOptions, + Map, + MarkerOptions, + Marker, + InfoWindowOptions, + InfoWindow, +> extends Controller { + static values: { + providerOptions: ObjectConstructor; + view: ObjectConstructor; + }; + viewValue: MapView; + protected map: Map; + protected markers: Array; + protected infoWindows: Array; + initialize(): void; + connect(): void; + protected abstract doCreateMap({ + center, + zoom, + options, + }: { + center: Point; + zoom: number; + options: MapOptions; + }): Map; + createMarker( + definition: MarkerDefinition, + ): Marker; + protected abstract doCreateMarker( + definition: MarkerDefinition, + ): Marker; + protected createInfoWindow({ + definition, + marker, + }: { + definition: MarkerDefinition< + MarkerOptions, + InfoWindowOptions + >["infoWindow"]; + marker: Marker; + }): InfoWindow; + protected abstract doCreateInfoWindow({ + definition, + marker, + }: { + definition: MarkerDefinition< + MarkerOptions, + InfoWindowOptions + >["infoWindow"]; + marker: Marker; + }): InfoWindow; + protected abstract doFitBoundsToMarkers(): void; + private dispatchEvent; } diff --git a/src/Map/assets/src/abstract_map_controller.ts b/src/Map/assets/src/abstract_map_controller.ts index 0c6031ab4ad..7b64d87267c 100644 --- a/src/Map/assets/src/abstract_map_controller.ts +++ b/src/Map/assets/src/abstract_map_controller.ts @@ -1,9 +1,9 @@ import { Controller } from '@hotwired/stimulus'; -export type LatLng = { lat: number; lng: number }; +export type Point = { lat: number; lng: number }; export type MapView = { - center: LatLng; + center: Point; zoom: number; fitBoundsToMarkers: boolean; markers: Array>; @@ -11,7 +11,7 @@ export type MapView = { }; export type MarkerDefinition = { - position: LatLng; + position: Point; title: string | null; infoWindow?: Omit, 'position'>; rawOptions?: MarkerOptions; @@ -20,7 +20,7 @@ export type MarkerDefinition = { export type InfoWindowDefinition = { headerContent: string | null; content: string | null; - position: LatLng; + position: Point; opened: boolean; autoClose: boolean; rawOptions?: InfoWindowOptions; @@ -72,7 +72,7 @@ export default abstract class< zoom, options, }: { - center: LatLng; + center: Point; zoom: number; options: MapOptions; }): Map; diff --git a/src/Map/doc/index.rst b/src/Map/doc/index.rst index b60074e5ee8..43777ed2449 100644 --- a/src/Map/doc/index.rst +++ b/src/Map/doc/index.rst @@ -34,7 +34,7 @@ Configuration Configuration is done in your ``config/packages/ux_map.yaml`` file: .. code-block:: yaml - + # config/packages/ux_map.yaml ux_map: renderer: '%env(UX_MAP_DSN)%' @@ -60,17 +60,17 @@ Creating and rendering ~~~~~~~~~~~~~~~~~~~~~~ A map is created by calling ``new Map()``. You can configure the center, zoom, and add markers:: - + namespace App\Controller; - + use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; use Symfony\UX\Map\InfoWindow; - use Symfony\UX\Map\LatLng; use Symfony\UX\Map\Map; use Symfony\UX\Map\Marker; - + use Symfony\UX\Map\Point; + final class HomeController extends AbstractController { #[Route('/')] @@ -78,18 +78,18 @@ A map is created by calling ``new Map()``. You can configure the center, zoom, a { // 1. Create a new map instance $myMap = (new Map()); - ->center(new LatLng(46.903354, 1.888334)) + ->center(new Point(46.903354, 1.888334)) ->zoom(6) ; - + // 2. You can add markers, with an optional info window $myMap ->addMarker(new Marker( - position: new LatLng(48.8566, 2.3522), + position: new Point(48.8566, 2.3522), title: 'Paris' )) ->addMarker(new Marker( - position: new LatLng(45.7640, 4.8357), + position: new Point(45.7640, 4.8357), title: 'Lyon', // With an info window infoWindow: new InfoWindow( @@ -97,7 +97,7 @@ A map is created by calling ``new Map()``. You can configure the center, zoom, a content: 'The French town in the historic Rhône-Alpes region, located at the junction of the Rhône and Saône rivers.' ) )); - + // 3. And inject the map in your template to render it return $this->render('contact/index.html.twig', [ 'my_map' => $myMap, @@ -110,7 +110,7 @@ To render a map in your Twig template, use the ``render_map`` Twig function, e.g .. code-block:: twig {{ render_map(my_map) }} - + {# or with custom attributes #} {{ render_map(my_map, { style: 'height: 300px' }) }} @@ -122,9 +122,9 @@ Symfony UX Map allows you to extend its default behavior using a custom Stimulus .. code-block:: javascript // assets/controllers/mymap_controller.js - + import { Controller } from '@hotwired/stimulus'; - + export default class extends Controller { connect() { this.element.addEventListener('ux:map:pre-connect', this._onPreConnect); @@ -134,7 +134,7 @@ Symfony UX Map allows you to extend its default behavior using a custom Stimulus this.element.addEventListener('ux:map:info-window:before-create', this._onInfoWindowBeforeConnect); this.element.addEventListener('ux:map:info-window:after-create', this._onInfoWindowAfterCreate); } - + disconnect() { // You should always remove listeners when the controller is disconnected to avoid side effects this.element.removeEventListener('ux:map:pre-connect', this._onPreConnect); @@ -144,13 +144,13 @@ Symfony UX Map allows you to extend its default behavior using a custom Stimulus this.element.removeEventListener('ux:map:info-window:before-create', this._onInfoWindowBeforeConnect); this.element.removeEventListener('ux:map:info-window:after-create', this._onInfoWindowAfterCreate); } - + _onPreConnect(event) { // The map is not created yet // You can use this event to configure the map before it is created console.log(event.detail.options); } - + _onConnect(event) { // The map, markers and infoWindows are created // The instances depend on the renderer you are using @@ -158,19 +158,19 @@ Symfony UX Map allows you to extend its default behavior using a custom Stimulus console.log(event.detail.markers); console.log(event.detail.infoWindows); } - + _onMarkerBeforeConnect(event) { // The marker is not created yet // You can use this event to configure the marker before it is created console.log(event.detail.definition); } - + _onMarkerAfterCreate(event) { // The marker is created // The instance depends on the renderer you are using console.log(event.detail.marker); } - + _onInfoWindowBeforeConnect(event) { // The infoWindow is not created yet // You can use this event to configure the infoWindow before it is created @@ -178,7 +178,7 @@ Symfony UX Map allows you to extend its default behavior using a custom Stimulus // The associated marker instance is also available console.log(event.detail.marker); } - + _onInfoWindowAfterCreate(event) { // The infoWindow is created // The instance depends on the renderer you are using @@ -191,7 +191,7 @@ Symfony UX Map allows you to extend its default behavior using a custom Stimulus Then, you can use this controller in your template: .. code-block:: twig - + {{ render_map(my_map, { 'data-controller': 'mymap', style: 'height: 300px' }) }} Backward Compatibility promise diff --git a/src/Map/src/Bridge/Google/README.md b/src/Map/src/Bridge/Google/README.md index a98037d2fc4..9bec29d7053 100644 --- a/src/Map/src/Bridge/Google/README.md +++ b/src/Map/src/Bridge/Google/README.md @@ -15,7 +15,7 @@ UX_MAP_DSN=google://GOOGLE_MAPS_API_KEY@default?language=fr®ion=FR Available options: | Option | Description | Default | -|------------|------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------| +| ---------- | ---------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------- | | `id` | The id of the script tag | `__googleMapsScriptId` | | `language` | Force language, see [list of supported languages](https://developers.google.com/maps/faq#languagesupport) specified in the browser | The user's preferred language | | `region` | Unicode region subtag identifiers compatible with [ISO 3166-1](https://en.wikipedia.org/wiki/ISO_3166-1) | | @@ -37,43 +37,45 @@ use Symfony\UX\Map\Bridge\Google\Option\MapTypeControlOptions; use Symfony\UX\Map\Bridge\Google\Option\MapTypeControlStyle; use Symfony\UX\Map\Bridge\Google\Option\StreetViewControlOptions; use Symfony\UX\Map\Bridge\Google\Option\ZoomControlOptions; -use Symfony\UX\Map\LatLng; +use Symfony\UX\Map\Point; use Symfony\UX\Map\Map; -$map = (new Map()) - ->center(new LatLng(48.8566, 2.3522)) - ->zoom(6); +$map = (new Map())->center(new Point(48.8566, 2.3522))->zoom(6); // To configure controls options, and some other options: $googleOptions = (new GoogleOptions()) - ->mapId('YOUR_MAP_ID') - ->gestureHandling(GestureHandling::GREEDY) - ->backgroundColor('#f00') - ->doubleClickZoom(true) - ->zoomControlOptions(new ZoomControlOptions( - position: ControlPosition::BLOCK_START_INLINE_END, - )) - ->mapTypeControlOptions(new MapTypeControlOptions( - mapTypeIds: ['roadmap'], - position: ControlPosition::INLINE_END_BLOCK_START, - style: MapTypeControlStyle::DROPDOWN_MENU, - )) - ->streetViewControlOptions(new StreetViewControlOptions( - position: ControlPosition::BLOCK_END_INLINE_START, - )) - ->fullscreenControlOptions(new FullscreenControlOptions( - position: ControlPosition::INLINE_START_BLOCK_END, - )) -; + ->mapId("YOUR_MAP_ID") + ->gestureHandling(GestureHandling::GREEDY) + ->backgroundColor("#f00") + ->doubleClickZoom(true) + ->zoomControlOptions( + new ZoomControlOptions(position: ControlPosition::BLOCK_START_INLINE_END) + ) + ->mapTypeControlOptions( + new MapTypeControlOptions( + mapTypeIds: ["roadmap"], + position: ControlPosition::INLINE_END_BLOCK_START, + style: MapTypeControlStyle::DROPDOWN_MENU + ) + ) + ->streetViewControlOptions( + new StreetViewControlOptions( + position: ControlPosition::BLOCK_END_INLINE_START + ) + ) + ->fullscreenControlOptions( + new FullscreenControlOptions( + position: ControlPosition::INLINE_START_BLOCK_END + ) + ); // To disable controls: $googleOptions = (new GoogleOptions()) - ->mapId('YOUR_MAP_ID') - ->zoomControl(false) - ->mapTypeControl(false) - ->streetViewControl(false) - ->fullscreenControl(false) -; + ->mapId("YOUR_MAP_ID") + ->zoomControl(false) + ->mapTypeControl(false) + ->streetViewControl(false) + ->fullscreenControl(false); // Add the custom options to the map $map->options($googleOptions); 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 787fb740026..0c0487f197a 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,26 +1,73 @@ /// -import AbstractMapController from '@symfony/ux-map/abstract-map-controller'; -import type { LatLng, MarkerDefinition } from '@symfony/ux-map/abstract-map-controller'; -import type { LoaderOptions } from '@googlemaps/js-api-loader'; -type MapOptions = Pick; -export default class extends AbstractMapController { - static values: { - providerOptions: ObjectConstructor; - }; - providerOptionsValue: Pick; - connect(): Promise; - protected doCreateMap({ center, zoom, options, }: { - center: LatLng; - zoom: number; - options: MapOptions; - }): google.maps.Map; - protected doCreateMarker(definition: MarkerDefinition): google.maps.marker.AdvancedMarkerElement; - protected doCreateInfoWindow({ definition, marker, }: { - definition: MarkerDefinition['infoWindow']; - marker: google.maps.marker.AdvancedMarkerElement; - }): google.maps.InfoWindow; - private createTextOrElement; - private closeInfoWindowsExcept; - protected doFitBoundsToMarkers(): void; +import AbstractMapController from "@symfony/ux-map/abstract-map-controller"; +import type { + Point, + MarkerDefinition, +} from "@symfony/ux-map/abstract-map-controller"; +import type { LoaderOptions } from "@googlemaps/js-api-loader"; +type MapOptions = Pick< + google.maps.MapOptions, + | "mapId" + | "gestureHandling" + | "backgroundColor" + | "disableDoubleClickZoom" + | "zoomControl" + | "zoomControlOptions" + | "mapTypeControl" + | "mapTypeControlOptions" + | "streetViewControl" + | "streetViewControlOptions" + | "fullscreenControl" + | "fullscreenControlOptions" +>; +export default class extends AbstractMapController< + MapOptions, + google.maps.Map, + google.maps.marker.AdvancedMarkerElement, + google.maps.InfoWindow +> { + static values: { + providerOptions: ObjectConstructor; + }; + providerOptionsValue: Pick< + LoaderOptions, + | "apiKey" + | "id" + | "language" + | "region" + | "nonce" + | "retries" + | "url" + | "version" + >; + connect(): Promise; + protected doCreateMap({ + center, + zoom, + options, + }: { + center: Point; + zoom: number; + options: MapOptions; + }): google.maps.Map; + protected doCreateMarker( + definition: MarkerDefinition< + google.maps.marker.AdvancedMarkerElementOptions, + google.maps.InfoWindowOptions + >, + ): google.maps.marker.AdvancedMarkerElement; + protected doCreateInfoWindow({ + definition, + marker, + }: { + definition: MarkerDefinition< + google.maps.marker.AdvancedMarkerElementOptions, + google.maps.InfoWindowOptions + >["infoWindow"]; + marker: google.maps.marker.AdvancedMarkerElement; + }): google.maps.InfoWindow; + private createTextOrElement; + private closeInfoWindowsExcept; + protected doFitBoundsToMarkers(): void; } export {}; 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 e84eb65e689..a0def42d1d7 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/abstract-map-controller'; -import type { LatLng, MarkerDefinition } from '@symfony/ux-map/abstract-map-controller'; +import type { Point, MarkerDefinition } from '@symfony/ux-map/abstract-map-controller'; import type { LoaderOptions } from '@googlemaps/js-api-loader'; import { Loader } from '@googlemaps/js-api-loader'; @@ -67,7 +67,7 @@ export default class extends AbstractMapController< zoom, options, }: { - center: LatLng; + center: Point; zoom: number; options: MapOptions; }): google.maps.Map { diff --git a/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php b/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php index 97a12acb93e..a8bb8898329 100644 --- a/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php +++ b/src/Map/src/Bridge/Google/tests/GoogleRendererTest.php @@ -14,9 +14,9 @@ use Symfony\UX\Map\Bridge\Google\GoogleOptions; use Symfony\UX\Map\Bridge\Google\Renderer\GoogleRenderer; use Symfony\UX\Map\InfoWindow; -use Symfony\UX\Map\LatLng; use Symfony\UX\Map\Map; use Symfony\UX\Map\Marker; +use Symfony\UX\Map\Point; use Symfony\UX\Map\Test\RendererTestCase; use Symfony\UX\StimulusBundle\Helper\StimulusHelper; @@ -24,52 +24,80 @@ class GoogleRendererTest extends RendererTestCase { public function provideTestRenderMap(): iterable { - $map = (new Map()) - ->center(new LatLng(48.8566, 2.3522)) - ->zoom(12); + $map = (new Map())->center(new Point(48.8566, 2.3522))->zoom(12); yield 'simple map, with minimum options' => [ 'expected_render' => '
', - 'renderer' => new GoogleRenderer(new StimulusHelper(null), apiKey: 'api_key'), + 'renderer' => new GoogleRenderer( + new StimulusHelper(null), + apiKey: 'api_key' + ), 'map' => $map, ]; yield 'with every options' => [ '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'), + '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 markers and infoWindows' => [ 'expected_render' => '
', - 'renderer' => new GoogleRenderer(new StimulusHelper(null), apiKey: 'api_key'), + 'renderer' => new GoogleRenderer( + new StimulusHelper(null), + apiKey: 'api_key' + ), 'map' => (clone $map) - ->addMarker(new Marker(new LatLng(48.8566, 2.3522), 'Paris')) - ->addMarker(new Marker(new LatLng(48.8566, 2.3522), 'Lyon', infoWindow: new InfoWindow(content: 'Lyon'))), + ->addMarker(new Marker(new Point(48.8566, 2.3522), 'Paris')) + ->addMarker( + new Marker( + new Point(48.8566, 2.3522), + 'Lyon', + infoWindow: new InfoWindow(content: 'Lyon') + ) + ), ]; yield 'with controls enabled' => [ 'expected_render' => '
', - 'renderer' => new GoogleRenderer(new StimulusHelper(null), apiKey: 'api_key'), - 'map' => (clone $map) - ->options(new GoogleOptions( + 'renderer' => new GoogleRenderer( + new StimulusHelper(null), + apiKey: 'api_key' + ), + 'map' => (clone $map)->options( + new GoogleOptions( zoomControl: true, mapTypeControl: true, streetViewControl: true, - fullscreenControl: true, - )), + fullscreenControl: true + ) + ), ]; yield 'without controls enabled' => [ 'expected_render' => '
', - 'renderer' => new GoogleRenderer(new StimulusHelper(null), apiKey: 'api_key'), - 'map' => (clone $map) - ->options(new GoogleOptions( + 'renderer' => new GoogleRenderer( + new StimulusHelper(null), + apiKey: 'api_key' + ), + 'map' => (clone $map)->options( + new GoogleOptions( zoomControl: false, mapTypeControl: false, streetViewControl: false, - fullscreenControl: false, - )), + fullscreenControl: false + ) + ), ]; } } diff --git a/src/Map/src/Bridge/Leaflet/README.md b/src/Map/src/Bridge/Leaflet/README.md index 0154e2c0879..54c7b2ce19b 100644 --- a/src/Map/src/Bridge/Leaflet/README.md +++ b/src/Map/src/Bridge/Leaflet/README.md @@ -15,23 +15,21 @@ You can use the `LeafletOptions` class to configure your `Map`:: ```php use Symfony\UX\Map\Bridge\Leaflet\LeafletOptions; use Symfony\UX\Map\Bridge\Leaflet\Option\TileLayer; -use Symfony\UX\Map\LatLng; +use Symfony\UX\Map\Point; use Symfony\UX\Map\Map; -$map = (new Map()) - ->center(new LatLng(48.8566, 2.3522)) - ->zoom(6); - -$leafletOptions = (new LeafletOptions()) - ->tileLayer(new TileLayer( - url: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', - attribution: '© OpenStreetMap', - options: [ - 'minZoom' => 5, - 'maxZoom' => 10, - ] - )) -; +$map = (new Map())->center(new Point(48.8566, 2.3522))->zoom(6); + +$leafletOptions = (new LeafletOptions())->tileLayer( + new TileLayer( + url: "https://tile.openstreetmap.org/{z}/{x}/{y}.png", + attribution: '© OpenStreetMap', + options: [ + "minZoom" => 5, + "maxZoom" => 10, + ] + ) +); // Add the custom options to the map $map->options($leafletOptions); 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 6aabbc5f828..36c9ed027ae 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,5 +1,5 @@ import AbstractMapController from '@symfony/ux-map/abstract-map-controller'; -import type { LatLng, MarkerDefinition } from '@symfony/ux-map/abstract-map-controller'; +import type { Point, MarkerDefinition } from '@symfony/ux-map/abstract-map-controller'; import 'leaflet/dist/leaflet.min.css'; import { type Map as LeafletMap, Marker, type Popup } from 'leaflet'; import type { MapOptions as LeafletMapOptions, MarkerOptions, PopupOptions } from 'leaflet'; @@ -13,7 +13,7 @@ type MapOptions = Pick & { export default class extends AbstractMapController { connect(): void; protected doCreateMap({ center, zoom, options, }: { - center: LatLng; + center: Point; zoom: number; options: MapOptions; }): LeafletMap; 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 c127f2e45db..e7d2a218254 100644 --- a/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.js +++ b/src/Map/src/Bridge/Leaflet/assets/dist/map_controller.js @@ -1,59 +1,81 @@ -import AbstractMapController from '@symfony/ux-map/abstract-map-controller'; -import 'leaflet/dist/leaflet.min.css'; -import { Marker, divIcon, map, tileLayer, marker } from 'leaflet'; +import AbstractMapController from "@symfony/ux-map/abstract-map-controller"; +import "leaflet/dist/leaflet.min.css"; +import { Marker, divIcon, map, tileLayer, marker } from "leaflet"; class map_controller extends AbstractMapController { - connect() { - Marker.prototype.options.icon = divIcon({ - html: '', - iconSize: [25, 41], - iconAnchor: [12.5, 41], - popupAnchor: [0, -41], - className: '', - }); - super.connect(); + connect() { + Marker.prototype.options.icon = divIcon({ + html: '', + iconSize: [25, 41], + iconAnchor: [12.5, 41], + popupAnchor: [0, -41], + className: "", + }); + super.connect(); + } + doCreateMap({ center, zoom, options }) { + const map$1 = map(this.element, { + ...options, + center, + zoom, + }); + tileLayer(options.tileLayer.url, { + attribution: options.tileLayer.attribution, + ...options.tileLayer.options, + }).addTo(map$1); + return map$1; + } + doCreateMarker(definition) { + const { + position, + title, + infoWindow, + rawOptions = {}, + ...otherOptions + } = definition; + const marker$1 = marker(position, { + title, + ...otherOptions, + ...rawOptions, + }).addTo(this.map); + if (infoWindow) { + this.createInfoWindow({ definition: infoWindow, marker: marker$1 }); } - doCreateMap({ center, zoom, options, }) { - const map$1 = map(this.element, { - ...options, - center, - zoom, - }); - tileLayer(options.tileLayer.url, { - attribution: options.tileLayer.attribution, - ...options.tileLayer.options, - }).addTo(map$1); - return map$1; + return marker$1; + } + doCreateInfoWindow({ definition, marker }) { + const { + headerContent, + content, + rawOptions = {}, + ...otherOptions + } = definition; + marker.bindPopup(`${headerContent}
${content}`, { + ...otherOptions, + ...rawOptions, + }); + if (definition.opened) { + marker.openPopup(); } - doCreateMarker(definition) { - const { position, title, infoWindow, rawOptions = {}, ...otherOptions } = definition; - const marker$1 = marker(position, { title, ...otherOptions, ...rawOptions }).addTo(this.map); - if (infoWindow) { - this.createInfoWindow({ definition: infoWindow, marker: marker$1 }); - } - return marker$1; + const popup = marker.getPopup(); + if (!popup) { + throw new Error( + "Unable to get the Popup associated to the Marker, this should not happens.", + ); } - doCreateInfoWindow({ definition, marker, }) { - const { headerContent, content, rawOptions = {}, ...otherOptions } = definition; - marker.bindPopup(`${headerContent}
${content}`, { ...otherOptions, ...rawOptions }); - if (definition.opened) { - marker.openPopup(); - } - const popup = marker.getPopup(); - if (!popup) { - throw new Error('Unable to get the Popup associated to the Marker, this should not happens.'); - } - 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]; - })); + 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 }; 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 65b07d99aca..ced6cc050b2 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/abstract-map-controller'; -import type { LatLng, MarkerDefinition } from '@symfony/ux-map/abstract-map-controller'; +import type { Point, MarkerDefinition } from '@symfony/ux-map/abstract-map-controller'; import 'leaflet/dist/leaflet.min.css'; import { map as createMap, @@ -35,11 +35,7 @@ export default class extends AbstractMapController< super.connect(); } - protected doCreateMap({ - center, - zoom, - options, - }: { center: LatLng; zoom: number; options: MapOptions }): LeafletMap { + protected doCreateMap({ center, zoom, options }: { center: Point; zoom: number; options: MapOptions }): LeafletMap { const map = createMap(this.element, { ...options, center, diff --git a/src/Map/src/Bridge/Leaflet/tests/LeafletRendererTest.php b/src/Map/src/Bridge/Leaflet/tests/LeafletRendererTest.php index c59965d11a0..d7ba3041b76 100644 --- a/src/Map/src/Bridge/Leaflet/tests/LeafletRendererTest.php +++ b/src/Map/src/Bridge/Leaflet/tests/LeafletRendererTest.php @@ -13,9 +13,9 @@ use Symfony\UX\Map\Bridge\Leaflet\Renderer\LeafletRenderer; use Symfony\UX\Map\InfoWindow; -use Symfony\UX\Map\LatLng; use Symfony\UX\Map\Map; use Symfony\UX\Map\Marker; +use Symfony\UX\Map\Point; use Symfony\UX\Map\Test\RendererTestCase; use Symfony\UX\StimulusBundle\Helper\StimulusHelper; @@ -23,9 +23,7 @@ class LeafletRendererTest extends RendererTestCase { public function provideTestRenderMap(): iterable { - $map = (new Map()) - ->center(new LatLng(48.8566, 2.3522)) - ->zoom(12); + $map = (new Map())->center(new Point(48.8566, 2.3522))->zoom(12); yield 'simple map' => [ 'expected_render' => '
', @@ -37,8 +35,14 @@ public function provideTestRenderMap(): iterable 'expected_render' => '
', 'renderer' => new LeafletRenderer(new StimulusHelper(null)), 'map' => (clone $map) - ->addMarker(new Marker(new LatLng(48.8566, 2.3522), 'Paris')) - ->addMarker(new Marker(new LatLng(48.8566, 2.3522), 'Lyon', infoWindow: new InfoWindow(content: 'Lyon'))), + ->addMarker(new Marker(new Point(48.8566, 2.3522), 'Paris')) + ->addMarker( + new Marker( + new Point(48.8566, 2.3522), + 'Lyon', + infoWindow: new InfoWindow(content: 'Lyon') + ) + ), ]; } } diff --git a/src/Map/src/InfoWindow.php b/src/Map/src/InfoWindow.php index 1ea1b4cddd8..df432923f6d 100644 --- a/src/Map/src/InfoWindow.php +++ b/src/Map/src/InfoWindow.php @@ -21,7 +21,7 @@ public function __construct( private ?string $headerContent = null, private ?string $content = null, - private ?LatLng $position = null, + private ?Point $position = null, private bool $opened = false, private bool $autoClose = true, ) { diff --git a/src/Map/src/Map.php b/src/Map/src/Map.php index 7fa06f31686..93a6b525ee7 100644 --- a/src/Map/src/Map.php +++ b/src/Map/src/Map.php @@ -23,7 +23,7 @@ final class Map public function __construct( private readonly ?string $rendererName = null, private ?MapOptionsInterface $options = null, - private ?LatLng $center = null, + private ?Point $center = null, private ?float $zoom = null, private bool $fitBoundsToMarkers = false, /** @@ -38,7 +38,7 @@ public function getRendererName(): ?string return $this->rendererName; } - public function center(LatLng $center): self + public function center(Point $center): self { $this->center = $center; @@ -98,7 +98,10 @@ 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), + 'markers' => array_map( + static fn (Marker $marker) => $marker->toArray(), + $this->markers + ), ]; } } diff --git a/src/Map/src/Marker.php b/src/Map/src/Marker.php index 549a5f4b8cc..b33a27c9095 100644 --- a/src/Map/src/Marker.php +++ b/src/Map/src/Marker.php @@ -19,7 +19,7 @@ final readonly class Marker { public function __construct( - private LatLng $position, + private Point $position, private ?string $title = null, private ?InfoWindow $infoWindow = null, ) { diff --git a/src/Map/src/LatLng.php b/src/Map/src/Point.php similarity index 97% rename from src/Map/src/LatLng.php rename to src/Map/src/Point.php index 573b1c1b757..a6d71d88f69 100644 --- a/src/Map/src/LatLng.php +++ b/src/Map/src/Point.php @@ -18,7 +18,7 @@ * * @author Hugo Alliaume */ -final readonly class LatLng +final readonly class Point { public function __construct( public float $latitude, diff --git a/src/Map/tests/InfoWindowTest.php b/src/Map/tests/InfoWindowTest.php index 8e55df27e39..09606d77d37 100644 --- a/src/Map/tests/InfoWindowTest.php +++ b/src/Map/tests/InfoWindowTest.php @@ -13,7 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\UX\Map\InfoWindow; -use Symfony\UX\Map\LatLng; +use Symfony\UX\Map\Point; class InfoWindowTest extends TestCase { @@ -22,20 +22,23 @@ public function testToArray(): void $infoWindow = new InfoWindow( 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.', - position: new LatLng(48.8566, 2.3522), + position: new Point(48.8566, 2.3522), opened: true, - autoClose: false, + autoClose: false ); - 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.', - 'position' => [ - 'lat' => 48.8566, - 'lng' => 2.3522, + 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.', + 'position' => [ + 'lat' => 48.8566, + 'lng' => 2.3522, + ], + 'opened' => true, + 'autoClose' => false, ], - 'opened' => true, - 'autoClose' => false, - ], $infoWindow->toArray()); + $infoWindow->toArray() + ); } } diff --git a/src/Map/tests/LatLngTest.php b/src/Map/tests/LatLngTest.php deleted file mode 100644 index 878b0a45e42..00000000000 --- a/src/Map/tests/LatLngTest.php +++ /dev/null @@ -1,45 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\UX\Map\Tests; - -use PHPUnit\Framework\TestCase; -use Symfony\UX\Map\Exception\InvalidArgumentException; -use Symfony\UX\Map\LatLng; - -class LatLngTest extends TestCase -{ - public static function provideInvalidLatLng(): iterable - { - yield [91, 0, 'Latitude must be between -90 and 90 degrees, "91" given.']; - yield [-91, 0, 'Latitude must be between -90 and 90 degrees, "-91" given.']; - yield [0, 181, 'Longitude must be between -180 and 180 degrees, "181" given.']; - yield [0, -181, 'Longitude must be between -180 and 180 degrees, "-181" given.']; - } - - /** - * @dataProvider provideInvalidLatLng - */ - public function testInvalidLatLng(float $latitude, float $longitude, string $expectedExceptionMessage): void - { - self::expectException(InvalidArgumentException::class); - self::expectExceptionMessage($expectedExceptionMessage); - - new LatLng($latitude, $longitude); - } - - public function testToArray(): void - { - $latLng = new LatLng(48.8566, 2.3533); - - self::assertSame(['lat' => 48.8566, 'lng' => 2.3533], $latLng->toArray()); - } -} diff --git a/src/Map/tests/MapTest.php b/src/Map/tests/MapTest.php index 10a72be92f9..7ce579e46c9 100644 --- a/src/Map/tests/MapTest.php +++ b/src/Map/tests/MapTest.php @@ -14,10 +14,10 @@ use PHPUnit\Framework\TestCase; use Symfony\UX\Map\Exception\InvalidArgumentException; use Symfony\UX\Map\InfoWindow; -use Symfony\UX\Map\LatLng; use Symfony\UX\Map\Map; use Symfony\UX\Map\MapOptionsInterface; use Symfony\UX\Map\Marker; +use Symfony\UX\Map\Point; class MapTest extends TestCase { @@ -36,7 +36,7 @@ public function testZoomValidation(): void self::expectExceptionMessage('The zoom of the map must be set.'); $map = new Map( - center: new LatLng(48.8566, 2.3522) + center: new Point(48.8566, 2.3522) ); $map->toArray(); } @@ -45,7 +45,7 @@ public function testWithMinimumConfiguration(): void { $map = new Map(); $map - ->center(new LatLng(48.8566, 2.3522)) + ->center(new Point(48.8566, 2.3522)) ->zoom(6); $array = $map->toArray(); @@ -63,7 +63,7 @@ public function testWithMaximumConfiguration(): void { $map = new Map(); $map - ->center(new LatLng(48.8566, 2.3522)) + ->center(new Point(48.8566, 2.3522)) ->zoom(6) ->fitBoundsToMarkers() ->options(new class() implements MapOptionsInterface { @@ -75,19 +75,19 @@ public function toArray(): array } }) ->addMarker(new Marker( - position: new LatLng(48.8566, 2.3522), + position: new Point(48.8566, 2.3522), title: 'Paris', - infoWindow: new InfoWindow(headerContent: 'Paris', content: 'Paris', position: new LatLng(48.8566, 2.3522)) + infoWindow: new InfoWindow(headerContent: 'Paris', content: 'Paris', position: new Point(48.8566, 2.3522)) )) ->addMarker(new Marker( - position: new LatLng(45.764, 4.8357), + position: new Point(45.764, 4.8357), title: 'Lyon', - infoWindow: new InfoWindow(headerContent: 'Lyon', content: 'Lyon', position: new LatLng(45.764, 4.8357), opened: true) + infoWindow: new InfoWindow(headerContent: 'Lyon', content: 'Lyon', position: new Point(45.764, 4.8357), opened: true) )) ->addMarker(new Marker( - position: new LatLng(43.2965, 5.3698), + position: new Point(43.2965, 5.3698), title: 'Marseille', - infoWindow: new InfoWindow(headerContent: 'Marseille', content: 'Marseille', position: new LatLng(43.2965, 5.3698), opened: true) + infoWindow: new InfoWindow(headerContent: 'Marseille', content: 'Marseille', position: new Point(43.2965, 5.3698), opened: true) )); $array = $map->toArray(); diff --git a/src/Map/tests/MarkerTest.php b/src/Map/tests/MarkerTest.php index a58935999a1..ed6939dedd8 100644 --- a/src/Map/tests/MarkerTest.php +++ b/src/Map/tests/MarkerTest.php @@ -13,43 +13,47 @@ use PHPUnit\Framework\TestCase; use Symfony\UX\Map\InfoWindow; -use Symfony\UX\Map\LatLng; use Symfony\UX\Map\Marker; +use Symfony\UX\Map\Point; class MarkerTest extends TestCase { public function testToArray(): void { - $marker = new Marker( - position: new LatLng(48.8566, 2.3522), - ); + $marker = new Marker(position: new Point(48.8566, 2.3522)); - self::assertSame([ - 'position' => ['lat' => 48.8566, 'lng' => 2.3522], - 'title' => null, - 'infoWindow' => null, - ], $marker->toArray()); + self::assertSame( + [ + 'position' => ['lat' => 48.8566, 'lng' => 2.3522], + 'title' => null, + 'infoWindow' => null, + ], + $marker->toArray() + ); $marker = new Marker( - position: new LatLng(48.8566, 2.3522), + position: new Point(48.8566, 2.3522), title: 'Paris', infoWindow: new InfoWindow( 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.", - opened: true, - ), + opened: true + ) ); - self::assertSame([ - 'position' => ['lat' => 48.8566, 'lng' => 2.3522], - 'title' => 'Paris', - 'infoWindow' => [ - '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.", - 'position' => null, - 'opened' => true, - 'autoClose' => true, + self::assertSame( + [ + 'position' => ['lat' => 48.8566, 'lng' => 2.3522], + 'title' => 'Paris', + 'infoWindow' => [ + '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.", + 'position' => null, + 'opened' => true, + 'autoClose' => true, + ], ], - ], $marker->toArray()); + $marker->toArray() + ); } } diff --git a/src/Map/tests/PointTest.php b/src/Map/tests/PointTest.php new file mode 100644 index 00000000000..ff4ac0d1c60 --- /dev/null +++ b/src/Map/tests/PointTest.php @@ -0,0 +1,67 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\UX\Map\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\UX\Map\Exception\InvalidArgumentException; +use Symfony\UX\Map\Point; + +class PointTest extends TestCase +{ + public static function provideInvalidPoint(): iterable + { + yield [ + 91, + 0, + 'Latitude must be between -90 and 90 degrees, "91" given.', + ]; + yield [ + -91, + 0, + 'Latitude must be between -90 and 90 degrees, "-91" given.', + ]; + yield [ + 0, + 181, + 'Longitude must be between -180 and 180 degrees, "181" given.', + ]; + yield [ + 0, + -181, + 'Longitude must be between -180 and 180 degrees, "-181" given.', + ]; + } + + /** + * @dataProvider provideInvalidPoint + */ + public function testInvalidPoint( + float $latitude, + float $longitude, + string $expectedExceptionMessage, + ): void { + self::expectException(InvalidArgumentException::class); + self::expectExceptionMessage($expectedExceptionMessage); + + new Point($latitude, $longitude); + } + + public function testToArray(): void + { + $point = new Point(48.8566, 2.3533); + + self::assertSame( + ['lat' => 48.8566, 'lng' => 2.3533], + $point->toArray() + ); + } +}