From 9158b931ceda51cce13605d4ba3914492a7eee4d Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Mon, 23 Jun 2025 00:12:52 +0200 Subject: [PATCH] [Map] Deprecate property `rawOptions` from `ux:map:*:before-create` events, in favor of `bridgeOptions` --- src/Map/CHANGELOG.md | 27 ++++---- .../assets/dist/abstract_map_controller.d.ts | 22 +++--- .../assets/dist/abstract_map_controller.js | 3 + src/Map/assets/src/abstract_map_controller.ts | 67 +++++++++++++++---- src/Map/doc/index.rst | 24 ++++--- src/Map/src/Bridge/Google/CHANGELOG.md | 2 +- src/Map/src/Bridge/Google/README.md | 18 ++--- .../Google/assets/dist/map_controller.js | 32 +++++---- .../Google/assets/src/map_controller.ts | 31 +++++---- src/Map/src/Bridge/Leaflet/README.md | 4 +- .../Leaflet/assets/dist/map_controller.d.ts | 1 + .../Leaflet/assets/dist/map_controller.js | 50 ++++++++++---- .../Leaflet/assets/src/map_controller.ts | 50 ++++++++++---- 13 files changed, 225 insertions(+), 106 deletions(-) diff --git a/src/Map/CHANGELOG.md b/src/Map/CHANGELOG.md index d5be76af200..bcf69535230 100644 --- a/src/Map/CHANGELOG.md +++ b/src/Map/CHANGELOG.md @@ -1,16 +1,10 @@ # CHANGELOG -## 2.29 - -- Add support for creating `Rectangle` by passing two `Point` instances to the `Rectangle` constructor, e.g.: -```php -$map->addRectangle(new Rectangle( - southWest: new Point(48.856613, 2.352222), // Paris - northEast: new Point(48.51238 2.21080) // Gare de Lyon (Paris) -)); -``` +## 2.27 -## 2.28 +- The `fitBoundsToMarkers` option is not overridden anymore when using the `Map` LiveComponent, but now respects the value you defined. + You may encounter unwanted behavior when adding/removing elements to the map. + To use the previous behavior, you must call `$this->getMap()->fitBoundsToMarkers(false)` in your LiveComponent's live actions - Add support for creating `Circle` by passing a `Point` and a radius (in meters) to the `Circle` constructor, e.g.: ```php @@ -19,11 +13,16 @@ $map->addCircle(new Circle( radius: 5_000 // 5km )); ``` -## 2.27 -- The `fitBoundsToMarkers` option is not overridden anymore when using the `Map` LiveComponent, but now respects the value you defined. - You may encounter unwanted behavior when adding/removing elements to the map. - To use the previous behavior, you must call `$this->getMap()->fitBoundsToMarkers(false)` in your LiveComponent's live actions +- Add support for creating `Rectangle` by passing two `Point` instances to the `Rectangle` constructor, e.g.: +```php +$map->addRectangle(new Rectangle( + southWest: new Point(48.856613, 2.352222), // Paris + northEast: new Point(48.51238 2.21080) // Gare de Lyon (Paris) +)); +``` + +- Deprecate property `rawOptions` from `ux:map:*:before-create` events, in favor of `bridgeOptions` instead. ## 2.26 diff --git a/src/Map/assets/dist/abstract_map_controller.d.ts b/src/Map/assets/dist/abstract_map_controller.d.ts index cf52843d7b5..d36575bd0e8 100644 --- a/src/Map/assets/dist/abstract_map_controller.d.ts +++ b/src/Map/assets/dist/abstract_map_controller.d.ts @@ -32,36 +32,41 @@ export type MarkerDefinition = Wit infoWindow?: Omit, 'position'>; icon?: Icon; rawOptions?: BridgeMarkerOptions; + bridgeOptions?: BridgeMarkerOptions; extra: Record; }>; -export type PolygonDefinition = WithIdentifier<{ +export type PolygonDefinition = WithIdentifier<{ infoWindow?: Omit, 'position'>; points: Array | Array>; title: string | null; - rawOptions?: PolygonOptions; + rawOptions?: BridgePolygonOptions; + bridgeOptions?: BridgePolygonOptions; extra: Record; }>; -export type PolylineDefinition = WithIdentifier<{ +export type PolylineDefinition = WithIdentifier<{ infoWindow?: Omit, 'position'>; points: Array; title: string | null; - rawOptions?: PolylineOptions; + rawOptions?: BridgePolylineOptions; + bridgeOptions?: BridgePolylineOptions; extra: Record; }>; -export type CircleDefinition = WithIdentifier<{ +export type CircleDefinition = WithIdentifier<{ infoWindow?: Omit, 'position'>; center: Point; radius: number; title: string | null; - rawOptions?: CircleOptions; + rawOptions?: BridgeCircleOptions; + bridgeOptions?: BridgeCircleOptions; extra: Record; }>; -export type RectangleDefinition = WithIdentifier<{ +export type RectangleDefinition = WithIdentifier<{ infoWindow?: Omit, 'position'>; southWest: Point; northEast: Point; title: string | null; - rawOptions?: RectangleOptions; + rawOptions?: BridgeRectangleOptions; + bridgeOptions?: BridgeRectangleOptions; extra: Record; }>; export type InfoWindowDefinition = { @@ -71,6 +76,7 @@ export type InfoWindowDefinition = { opened: boolean; autoClose: boolean; rawOptions?: BridgeInfoWindowOptions; + bridgeOptions?: BridgeInfoWindowOptions; extra: Record; }; export default abstract class extends Controller { diff --git a/src/Map/assets/dist/abstract_map_controller.js b/src/Map/assets/dist/abstract_map_controller.js index 446fa7c7af9..6d114ebaf0a 100644 --- a/src/Map/assets/dist/abstract_map_controller.js +++ b/src/Map/assets/dist/abstract_map_controller.js @@ -93,6 +93,9 @@ class default_1 extends Controller { const eventAfter = `${type}:after-create`; return ({ definition }) => { this.dispatchEvent(eventBefore, { definition }); + if (typeof definition.rawOptions !== 'undefined') { + console.warn(`[Symfony UX Map] The event "${eventBefore}" added a deprecated "rawOptions" property to the definition, it will be removed in a next major version, replace it with "bridgeOptions" instead.`, definition); + } const drawing = factory({ definition }); this.dispatchEvent(eventAfter, { [type]: drawing, definition }); draws.set(definition['@id'], drawing); diff --git a/src/Map/assets/src/abstract_map_controller.ts b/src/Map/assets/src/abstract_map_controller.ts index 09b6460a75e..cc83a046122 100644 --- a/src/Map/assets/src/abstract_map_controller.ts +++ b/src/Map/assets/src/abstract_map_controller.ts @@ -43,9 +43,15 @@ export type MarkerDefinition = Wit infoWindow?: Omit, 'position'>; icon?: Icon; /** + * @deprecated Use "bridgeOptions" instead. * Raw options passed to the marker constructor, specific to the map provider (e.g.: `L.marker()` for Leaflet). */ rawOptions?: BridgeMarkerOptions; + /** + * Additional options passed to the Marker constructor. + * These options are specific to the Map Bridge, and can be defined through `ux:map:marker:before-create` event. + */ + bridgeOptions?: BridgeMarkerOptions; /** * 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: @@ -55,14 +61,20 @@ export type MarkerDefinition = Wit extra: Record; }>; -export type PolygonDefinition = WithIdentifier<{ +export type PolygonDefinition = WithIdentifier<{ infoWindow?: Omit, 'position'>; points: Array | Array>; title: string | null; /** - * Raw options passed to the marker constructor, specific to the map provider (e.g.: `L.marker()` for Leaflet). + * @deprecated Use "bridgeOptions" instead. + * Raw options passed to the polygon constructor, specific to the map provider (e.g.: `L.polygon()` for Leaflet). + */ + rawOptions?: BridgePolygonOptions; + /** + * Additional options passed to the Polygon constructor. + * These options are specific to the Map Bridge, and can be defined through `ux:map:polygon:before-create` event. */ - rawOptions?: PolygonOptions; + bridgeOptions?: BridgePolygonOptions; /** * 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: @@ -72,14 +84,20 @@ export type PolygonDefinition = WithIde extra: Record; }>; -export type PolylineDefinition = WithIdentifier<{ +export type PolylineDefinition = WithIdentifier<{ infoWindow?: Omit, 'position'>; points: Array; title: string | null; /** - * Raw options passed to the marker constructor, specific to the map provider (e.g.: `L.marker()` for Leaflet). + * @deprecated Use "bridgeOptions" instead. + * Raw options passed to the polyline constructor, specific to the map provider (e.g.: `L.polyline()` for Leaflet). + */ + rawOptions?: BridgePolylineOptions; + /** + * Additional options passed to the Polyline constructor. + * These options are specific to the Map Bridge, and can be defined through `ux:map:polyline:before-create` event. */ - rawOptions?: PolylineOptions; + bridgeOptions?: BridgePolylineOptions; /** * 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: @@ -89,15 +107,21 @@ export type PolylineDefinition = WithI extra: Record; }>; -export type CircleDefinition = WithIdentifier<{ +export type CircleDefinition = WithIdentifier<{ infoWindow?: Omit, 'position'>; center: Point; radius: number; title: string | null; /** + * @deprecated Use "bridgeOptions" instead. * Raw options passed to the circle constructor, specific to the map provider (e.g.: `L.circle()` for Leaflet). */ - rawOptions?: CircleOptions; + rawOptions?: BridgeCircleOptions; + /** + * Additional options passed to the Circle constructor. + * These options are specific to the Map Bridge, and can be defined through `ux:map:circle:before-create` event. + */ + bridgeOptions?: BridgeCircleOptions; /** * 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: @@ -107,15 +131,21 @@ export type CircleDefinition = WithIdent extra: Record; }>; -export type RectangleDefinition = WithIdentifier<{ +export type RectangleDefinition = WithIdentifier<{ infoWindow?: Omit, 'position'>; southWest: Point; northEast: Point; title: string | null; /** + * @deprecated Use "bridgeOptions" instead. * Raw options passed to the rectangle constructor, specific to the map provider (e.g.: `L.rectangle()` for Leaflet). */ - rawOptions?: RectangleOptions; + rawOptions?: BridgeRectangleOptions; + /** + * Additional options passed to the Rectangle constructor. + * These options are specific to the Map Bridge, and can be defined through `ux:map:rectangle:before-create` event. + */ + bridgeOptions?: BridgeRectangleOptions; /** * 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: @@ -132,10 +162,15 @@ export type InfoWindowDefinition = { 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). + * @deprecated Use "bridgeOptions" instead. + * Raw options passed to the info window constructor, specific to the map provider (e.g.: `google.maps.InfoWindow()` for Google Maps). */ rawOptions?: BridgeInfoWindowOptions; + /** + * Additional options passed to the InfoWindow constructor. + * These options are specific to the Map Bridge, and can be defined through `ux:map:info-window:before-create` event. + */ + bridgeOptions?: BridgeInfoWindowOptions; /** * 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: @@ -378,6 +413,14 @@ export default abstract class< // 'Factory' could be instantiated with an arbitrary type which could be unrelated to '({ definition }: { definition: WithIdentifier; }) => Draw' return ({ definition }: { definition: WithIdentifier }) => { this.dispatchEvent(eventBefore, { definition }); + + if (typeof definition.rawOptions !== 'undefined') { + console.warn( + `[Symfony UX Map] The event "${eventBefore}" added a deprecated "rawOptions" property to the definition, it will be removed in a next major version, replace it with "bridgeOptions" instead.`, + definition + ); + } + const drawing = factory({ definition }) as Draw; this.dispatchEvent(eventAfter, { [type]: drawing, definition }); diff --git a/src/Map/doc/index.rst b/src/Map/doc/index.rst index ddbd4ba9890..8ad03b783ee 100644 --- a/src/Map/doc/index.rst +++ b/src/Map/doc/index.rst @@ -501,7 +501,13 @@ maps and their elements. However, you might occasionally find this abstraction limiting and need to configure low-level options directly. Fortunately, you can customize these low-level options through the UX Map -events ``ux:map:*:before-create`` using the special ``rawOptions`` property: +events ``ux:map:*:before-create`` using the special ``bridgeOptions`` property: + +.. deprecated:: 2.27 + + The ``rawOptions`` property was deprecated in UX Map 2.27, and will be removed in 3.0. + Use ``bridgeOptions`` instead, which better reflect the purpose of these options (options that are + specific to the renderer bridge). .. code-block:: javascript @@ -519,13 +525,13 @@ events ``ux:map:*:before-create`` using the special ``rawOptions`` property: _onMarkerBeforeCreate(event) { // When using Google Maps, to configure a `google.maps.AdvancedMarkerElement` - event.detail.definition.rawOptions = { + event.detail.definition.bridgeOptions = { gmpDraggable: true, // ... }; // When using Leaflet, to configure a `L.Marker` instance - event.detail.definition.rawOptions = { + event.detail.definition.bridgeOptions = { riseOnHover: true, // ... }; @@ -533,13 +539,13 @@ events ``ux:map:*:before-create`` using the special ``rawOptions`` property: _onInfoWindowBeforeCreate(event) { // When using Google Maps, to configure a `google.maps.InfoWindow` instance - event.detail.definition.rawOptions = { + event.detail.definition.bridgeOptions = { maxWidth: 200, // ... }; // When using Leaflet, to configure a `L.Popup` instance - event.detail.definition.rawOptions = { + event.detail.definition.bridgeOptions = { direction: 'left', // ... }; @@ -547,13 +553,13 @@ events ``ux:map:*:before-create`` using the special ``rawOptions`` property: _onPolygonBeforeCreate(event) { // When using Google Maps, to configure a `google.maps.Polygon` - event.detail.definition.rawOptions = { + event.detail.definition.bridgeOptions = { strokeColor: 'red', // ... }; // When using Leaflet, to configure a `L.Polygon` - event.detail.definition.rawOptions = { + event.detail.definition.bridgeOptions = { color: 'red', // ... }; @@ -561,13 +567,13 @@ events ``ux:map:*:before-create`` using the special ``rawOptions`` property: _onPolylineBeforeCreate(event) { // When using Google Maps, to configure a `google.maps.Polyline` - event.detail.definition.rawOptions = { + event.detail.definition.bridgeOptions = { strokeColor: 'red', // ... }; // When using Leaflet, to configure a `L.Polyline` - event.detail.definition.rawOptions = { + event.detail.definition.bridgeOptions = { color: 'red', // ... }; diff --git a/src/Map/src/Bridge/Google/CHANGELOG.md b/src/Map/src/Bridge/Google/CHANGELOG.md index e6cba70ad61..fbdc858d9c9 100644 --- a/src/Map/src/Bridge/Google/CHANGELOG.md +++ b/src/Map/src/Bridge/Google/CHANGELOG.md @@ -16,7 +16,7 @@ const bounds = new google.maps.LatLngBounds(); element.getPath().forEach((latLng) => bounds.extend(latLng)); - definition.infoWindow.rawOptions.position = bounds.getCenter(); + definition.infoWindow.bridgeOptions.position = bounds.getCenter(); } }); ``` diff --git a/src/Map/src/Bridge/Google/README.md b/src/Map/src/Bridge/Google/README.md index f6985ecf918..9b6e5baa9cf 100644 --- a/src/Map/src/Bridge/Google/README.md +++ b/src/Map/src/Bridge/Google/README.md @@ -69,8 +69,8 @@ $map = (new Map()) $googleOptions = (new GoogleOptions()) // You can skip this option if you configure "ux_map.google_maps.default_map_id" // in your "config/packages/ux_map.yaml". - ->mapId('YOUR_MAP_ID') - + ->mapId('YOUR_MAP_ID') + ->gestureHandling(GestureHandling::GREEDY) ->backgroundColor('#f00') ->doubleClickZoom(true) @@ -133,24 +133,24 @@ export default class extends Controller _onMarkerBeforeCreate(event) { // You can access the marker definition and the google object - // Note: `definition.rawOptions` is the raw options object that will be passed to the `google.maps.Marker` constructor. + // Note: `definition.bridgeOptions` is the raw options object that will be passed to the `google.maps.Marker` constructor. const { definition, google } = event.detail; - // 1. To use a custom image for the marker + // 1. To use a custom image for the marker const beachFlagImg = document.createElement("img"); // Note: instead of using a hardcoded URL, you can use the `extra` parameter from `new Marker()` (PHP) and access it here with `definition.extra`. beachFlagImg.src = "https://developers.google.com/maps/documentation/javascript/examples/full/images/beachflag.png"; - definition.rawOptions = { + definition.bridgeOptions = { content: beachFlagImg } - + // 2. To use a custom glyph for the marker const pinElement = new google.maps.marker.PinElement({ - // Note: instead of using a hardcoded URL, you can use the `extra` parameter from `new Marker()` (PHP) and access it here with `definition.extra`. - glyph: new URL('https://maps.gstatic.com/mapfiles/place_api/icons/v2/museum_pinlet.svg'), + // Note: instead of using a hardcoded URL, you can use the `extra` parameter from `new Marker()` (PHP) and access it here with `definition.extra`. + glyph: new URL('https://maps.gstatic.com/mapfiles/place_api/icons/v2/museum_pinlet.svg'), glyphColor: "white", }); - definition.rawOptions = { + definition.bridgeOptions = { content: pinElement.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 73850804df7..eb43c8fac11 100644 --- a/src/Map/src/Bridge/Google/assets/dist/map_controller.js +++ b/src/Map/src/Bridge/Google/assets/dist/map_controller.js @@ -94,6 +94,9 @@ class default_1 extends Controller { const eventAfter = `${type}:after-create`; return ({ definition }) => { this.dispatchEvent(eventBefore, { definition }); + if (typeof definition.rawOptions !== 'undefined') { + console.warn(`[Symfony UX Map] The event "${eventBefore}" added a deprecated "rawOptions" property to the definition, it will be removed in a next major version, replace it with "bridgeOptions" instead.`, definition); + } const drawing = factory({ definition }); this.dispatchEvent(eventAfter, { [type]: drawing, definition }); draws.set(definition['@id'], drawing); @@ -201,13 +204,13 @@ class map_controller extends default_1 { }); } doCreateMarker({ definition, }) { - const { '@id': _id, position, title, infoWindow, icon, extra, rawOptions = {}, ...otherOptions } = definition; + const { '@id': _id, position, title, infoWindow, icon, rawOptions = {}, bridgeOptions = {} } = definition; const marker = new _google.maps.marker.AdvancedMarkerElement({ position, title, - ...otherOptions, - ...rawOptions, map: this.map, + ...rawOptions, + ...bridgeOptions, }); if (infoWindow) { this.createInfoWindow({ definition: infoWindow, element: marker }); @@ -221,11 +224,12 @@ class map_controller extends default_1 { marker.map = null; } doCreatePolygon({ definition, }) { - const { '@id': _id, points, title, infoWindow, rawOptions = {} } = definition; + const { '@id': _id, points, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; const polygon = new _google.maps.Polygon({ - ...rawOptions, paths: points, map: this.map, + ...rawOptions, + ...bridgeOptions, }); if (title) { polygon.set('title', title); @@ -239,11 +243,12 @@ class map_controller extends default_1 { polygon.setMap(null); } doCreatePolyline({ definition, }) { - const { '@id': _id, points, title, infoWindow, rawOptions = {} } = definition; + const { '@id': _id, points, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; const polyline = new _google.maps.Polyline({ - ...rawOptions, path: points, map: this.map, + ...rawOptions, + ...bridgeOptions, }); if (title) { polyline.set('title', title); @@ -257,12 +262,13 @@ class map_controller extends default_1 { polyline.setMap(null); } doCreateCircle({ definition }) { - const { '@id': _id, center, radius, title, infoWindow, rawOptions = {} } = definition; + const { '@id': _id, center, radius, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; const circle = new _google.maps.Circle({ - ...rawOptions, center, radius, map: this.map, + ...rawOptions, + ...bridgeOptions, }); if (title) { circle.set('title', title); @@ -276,11 +282,12 @@ class map_controller extends default_1 { circle.setMap(null); } doCreateRectangle({ definition, }) { - const { northEast, southWest, title, infoWindow, rawOptions = {} } = definition; + const { northEast, southWest, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; const rectangle = new _google.maps.Rectangle({ - ...rawOptions, bounds: new _google.maps.LatLngBounds(southWest, northEast), map: this.map, + ...rawOptions, + ...bridgeOptions, }); if (title) { rectangle.set('title', title); @@ -294,7 +301,7 @@ class map_controller extends default_1 { rectangle.setMap(null); } doCreateInfoWindow({ definition, element, }) { - const { headerContent, content, opened, autoClose, rawOptions = {} } = definition; + const { headerContent, content, opened, autoClose, rawOptions = {}, bridgeOptions = {} } = definition; let position = null; if (element instanceof google.maps.Circle) { position = element.getCenter(); @@ -308,6 +315,7 @@ class map_controller extends default_1 { content: this.createTextOrElement(content), position, ...rawOptions, + ...bridgeOptions, }; const infoWindow = new _google.maps.InfoWindow(infoWindowOptions); element.addListener('click', (event) => { 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 4ede9db22e5..6cf7954a0ce 100644 --- a/src/Map/src/Bridge/Google/assets/src/map_controller.ts +++ b/src/Map/src/Bridge/Google/assets/src/map_controller.ts @@ -157,14 +157,14 @@ export default class extends AbstractMapController< }: { definition: MarkerDefinition; }): google.maps.marker.AdvancedMarkerElement { - const { '@id': _id, position, title, infoWindow, icon, extra, rawOptions = {}, ...otherOptions } = definition; + const { '@id': _id, position, title, infoWindow, icon, rawOptions = {}, bridgeOptions = {} } = definition; const marker = new _google.maps.marker.AdvancedMarkerElement({ position, title, - ...otherOptions, - ...rawOptions, map: this.map, + ...rawOptions, + ...bridgeOptions, }); if (infoWindow) { @@ -187,12 +187,13 @@ export default class extends AbstractMapController< }: { definition: PolygonDefinition; }): google.maps.Polygon { - const { '@id': _id, points, title, infoWindow, rawOptions = {} } = definition; + const { '@id': _id, points, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; const polygon = new _google.maps.Polygon({ - ...rawOptions, paths: points, map: this.map, + ...rawOptions, + ...bridgeOptions, }); if (title) { @@ -215,12 +216,13 @@ export default class extends AbstractMapController< }: { definition: PolylineDefinition; }): google.maps.Polyline { - const { '@id': _id, points, title, infoWindow, rawOptions = {} } = definition; + const { '@id': _id, points, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; const polyline = new _google.maps.Polyline({ - ...rawOptions, path: points, map: this.map, + ...rawOptions, + ...bridgeOptions, }); if (title) { @@ -239,13 +241,14 @@ export default class extends AbstractMapController< } protected doCreateCircle({ definition }: { definition: CircleDefinition }): google.maps.Circle { - const { '@id': _id, center, radius, title, infoWindow, rawOptions = {} } = definition; + const { '@id': _id, center, radius, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; const circle = new _google.maps.Circle({ - ...rawOptions, center, radius, map: this.map, + ...rawOptions, + ...bridgeOptions, }); if (title) { @@ -268,12 +271,13 @@ export default class extends AbstractMapController< }: { definition: RectangleDefinition; }): google.maps.Rectangle { - const { northEast, southWest, title, infoWindow, rawOptions = {} } = definition; + const { northEast, southWest, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; const rectangle = new _google.maps.Rectangle({ - ...rawOptions, bounds: new _google.maps.LatLngBounds(southWest, northEast), map: this.map, + ...rawOptions, + ...bridgeOptions, }); if (title) { @@ -298,7 +302,7 @@ export default class extends AbstractMapController< definition: Omit, 'position'>; element: google.maps.marker.AdvancedMarkerElement | google.maps.Polygon | google.maps.Polyline | google.maps.Circle | google.maps.Rectangle; }): google.maps.InfoWindow { - const { headerContent, content, opened, autoClose, rawOptions = {} } = definition; + const { headerContent, content, opened, autoClose, rawOptions = {}, bridgeOptions = {} } = definition; let position: google.maps.LatLng | null = null; if (element instanceof google.maps.Circle) { @@ -311,7 +315,7 @@ export default class extends AbstractMapController< // ```js // const bounds = new google.maps.LatLngBounds(); // element.getPath().forEach((latLng) => bounds.extend(latLng)); - // event.definition.infoWindow.rawOptions.position = bounds.getCenter(); + // event.definition.infoWindow.bridgeOptions.position = bounds.getCenter(); // ``` } @@ -320,6 +324,7 @@ export default class extends AbstractMapController< content: this.createTextOrElement(content), position, ...rawOptions, + ...bridgeOptions, }; const infoWindow = new _google.maps.InfoWindow(infoWindowOptions); diff --git a/src/Map/src/Bridge/Leaflet/README.md b/src/Map/src/Bridge/Leaflet/README.md index e6e1732088a..f46cd4f2e61 100644 --- a/src/Map/src/Bridge/Leaflet/README.md +++ b/src/Map/src/Bridge/Leaflet/README.md @@ -94,7 +94,7 @@ export default class extends Controller _onMarkerBeforeCreate(event) { // You can access the marker definition and the Leaflet object - // Note: `definition.rawOptions` is the raw options object that will be passed to the `L.marker` constructor. + // Note: `definition.bridgeOptions` is the raw options object that will be passed to the `L.marker` constructor. const { definition, L } = event.detail; // Use a custom icon for the marker @@ -109,7 +109,7 @@ export default class extends Controller popupAnchor: [-3, -76] // point from which the popup should open relative to the iconAnchor }) - definition.rawOptions = { + definition.bridgeOptions = { icon: redIcon, } } 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 69fd9658083..fbda2420c4f 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 @@ -61,5 +61,6 @@ export default class extends AbstractMapController { this.dispatchEvent(eventBefore, { definition }); + if (typeof definition.rawOptions !== 'undefined') { + console.warn(`[Symfony UX Map] The event "${eventBefore}" added a deprecated "rawOptions" property to the definition, it will be removed in a next major version, replace it with "bridgeOptions" instead.`, definition); + } const drawing = factory({ definition }); this.dispatchEvent(eventAfter, { [type]: drawing, definition }); draws.set(definition['@id'], drawing); @@ -184,8 +187,13 @@ class map_controller extends default_1 { return map; } doCreateMarker({ definition }) { - const { '@id': _id, position, title, infoWindow, icon, extra, rawOptions = {}, ...otherOptions } = definition; - const marker = L.marker(position, { title: title || undefined, ...otherOptions, ...rawOptions }).addTo(this.map); + const { '@id': _id, position, title, infoWindow, icon, rawOptions = {}, bridgeOptions = {} } = definition; + const marker = L.marker(position, { + title: title || undefined, + ...rawOptions, + ...bridgeOptions, + riseOnHover: true, + }).addTo(this.map); if (infoWindow) { this.createInfoWindow({ definition: infoWindow, element: marker }); } @@ -198,8 +206,8 @@ class map_controller extends default_1 { marker.remove(); } doCreatePolygon({ definition }) { - const { '@id': _id, points, title, infoWindow, rawOptions = {} } = definition; - const polygon = L.polygon(points, { ...rawOptions }).addTo(this.map); + const { '@id': _id, points, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; + const polygon = L.polygon(points, { ...rawOptions, ...bridgeOptions }).addTo(this.map); if (title) { polygon.bindPopup(title); } @@ -212,8 +220,8 @@ class map_controller extends default_1 { polygon.remove(); } doCreatePolyline({ definition }) { - const { '@id': _id, points, title, infoWindow, rawOptions = {} } = definition; - const polyline = L.polyline(points, { ...rawOptions }).addTo(this.map); + const { '@id': _id, points, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; + const polyline = L.polyline(points, { ...rawOptions, ...bridgeOptions }).addTo(this.map); if (title) { polyline.bindPopup(title); } @@ -226,8 +234,8 @@ class map_controller extends default_1 { polyline.remove(); } doCreateCircle({ definition }) { - const { '@id': _id, center, radius, title, infoWindow, rawOptions = {} } = definition; - const circle = L.circle(center, { radius, ...rawOptions }).addTo(this.map); + const { '@id': _id, center, radius, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; + const circle = L.circle(center, { radius, ...rawOptions, ...bridgeOptions }).addTo(this.map); if (title) { circle.bindPopup(title); } @@ -240,11 +248,11 @@ class map_controller extends default_1 { circle.remove(); } doCreateRectangle({ definition }) { - const { '@id': _id, southWest, northEast, title, infoWindow, rawOptions = {} } = definition; + const { '@id': _id, southWest, northEast, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; const rectangle = L.rectangle([ [southWest.lat, southWest.lng], [northEast.lat, northEast.lng], - ], { ...rawOptions }).addTo(this.map); + ], { ...rawOptions, ...bridgeOptions }).addTo(this.map); if (title) { rectangle.bindPopup(title); } @@ -257,15 +265,23 @@ class map_controller extends default_1 { rectangle.remove(); } doCreateInfoWindow({ definition, element, }) { - const { headerContent, content, rawOptions = {}, ...otherOptions } = definition; - element.bindPopup([headerContent, content].filter((x) => x).join('
'), { ...otherOptions, ...rawOptions }); - if (definition.opened) { + const { headerContent, content, opened, autoClose, rawOptions = {}, bridgeOptions = {} } = definition; + element.bindPopup([headerContent, content].filter((x) => x).join('
'), { ...rawOptions, ...bridgeOptions }); + if (opened) { + if (autoClose) { + this.closePopups(); + } element.openPopup(); } const popup = element.getPopup(); if (!popup) { throw new Error('Unable to get the Popup associated with the element.'); } + popup.on('click', () => { + if (autoClose) { + this.closePopups({ except: popup }); + } + }); return popup; } doCreateIcon({ definition, element }) { @@ -308,6 +324,14 @@ class map_controller extends default_1 { }); this.map.fitBounds(bounds); } + closePopups(options = {}) { + this.infoWindows.forEach((popup) => { + if (options.except && popup === options.except) { + return; + } + popup.close(); + }); + } } 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 1f13fa384c5..672f5ae9d5c 100644 --- a/src/Map/src/Bridge/Leaflet/assets/src/map_controller.ts +++ b/src/Map/src/Bridge/Leaflet/assets/src/map_controller.ts @@ -124,9 +124,14 @@ export default class extends AbstractMapController< } protected doCreateMarker({ definition }: { definition: MarkerDefinition }): L.Marker { - const { '@id': _id, position, title, infoWindow, icon, extra, rawOptions = {}, ...otherOptions } = definition; + const { '@id': _id, position, title, infoWindow, icon, rawOptions = {}, bridgeOptions = {} } = definition; - const marker = L.marker(position, { title: title || undefined, ...otherOptions, ...rawOptions }).addTo(this.map); + const marker = L.marker(position, { + title: title || undefined, + ...rawOptions, + ...bridgeOptions, + riseOnHover: true, + }).addTo(this.map); if (infoWindow) { this.createInfoWindow({ definition: infoWindow, element: marker }); @@ -144,9 +149,9 @@ export default class extends AbstractMapController< } protected doCreatePolygon({ definition }: { definition: PolygonDefinition }): L.Polygon { - const { '@id': _id, points, title, infoWindow, rawOptions = {} } = definition; + const { '@id': _id, points, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; - const polygon = L.polygon(points, { ...rawOptions }).addTo(this.map); + const polygon = L.polygon(points, { ...rawOptions, ...bridgeOptions }).addTo(this.map); if (title) { polygon.bindPopup(title); @@ -164,9 +169,9 @@ export default class extends AbstractMapController< } protected doCreatePolyline({ definition }: { definition: PolylineDefinition }): L.Polyline { - const { '@id': _id, points, title, infoWindow, rawOptions = {} } = definition; + const { '@id': _id, points, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; - const polyline = L.polyline(points, { ...rawOptions }).addTo(this.map); + const polyline = L.polyline(points, { ...rawOptions, ...bridgeOptions }).addTo(this.map); if (title) { polyline.bindPopup(title); @@ -184,9 +189,9 @@ export default class extends AbstractMapController< } protected doCreateCircle({ definition }: { definition: CircleDefinition }): L.Circle { - const { '@id': _id, center, radius, title, infoWindow, rawOptions = {} } = definition; + const { '@id': _id, center, radius, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; - const circle = L.circle(center, { radius, ...rawOptions }).addTo(this.map); + const circle = L.circle(center, { radius, ...rawOptions, ...bridgeOptions }).addTo(this.map); if (title) { circle.bindPopup(title); @@ -204,14 +209,14 @@ export default class extends AbstractMapController< } protected doCreateRectangle({ definition }: { definition: RectangleDefinition }): L.Rectangle { - const { '@id': _id, southWest, northEast, title, infoWindow, rawOptions = {} } = definition; + const { '@id': _id, southWest, northEast, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition; const rectangle = L.rectangle( [ [southWest.lat, southWest.lng], [northEast.lat, northEast.lng], ], - { ...rawOptions } + { ...rawOptions, ...bridgeOptions } ).addTo(this.map); if (title) { @@ -236,11 +241,15 @@ export default class extends AbstractMapController< definition: Omit, 'position'>; element: L.Marker | L.Polygon | L.Polyline | L.Circle | L.Rectangle; }): L.Popup { - const { headerContent, content, rawOptions = {}, ...otherOptions } = definition; + const { headerContent, content, opened, autoClose, rawOptions = {}, bridgeOptions = {} } = definition; - element.bindPopup([headerContent, content].filter((x) => x).join('
'), { ...otherOptions, ...rawOptions }); + element.bindPopup([headerContent, content].filter((x) => x).join('
'), { ...rawOptions, ...bridgeOptions }); + + if (opened) { + if (autoClose) { + this.closePopups(); + } - if (definition.opened) { element.openPopup(); } @@ -249,6 +258,12 @@ export default class extends AbstractMapController< throw new Error('Unable to get the Popup associated with the element.'); } + popup.on('click', () => { + if (autoClose) { + this.closePopups({ except: popup }); + } + }); + return popup; } @@ -292,4 +307,13 @@ export default class extends AbstractMapController< }); this.map.fitBounds(bounds); } + + private closePopups(options: { except?: L.Popup } = {}): void { + this.infoWindows.forEach((popup) => { + if (options.except && popup === options.except) { + return; + } + popup.close(); + }); + } }