@symfony/ux-leaflet-map 2.26.0 → 2.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,46 +1,64 @@
1
+ import type { CircleDefinition, Icon, InfoWindowDefinition, MapDefinition, MarkerDefinition, PolygonDefinition, PolylineDefinition, RectangleDefinition } from '@symfony/ux-map';
1
2
  import AbstractMapController from '@symfony/ux-map';
2
- import type { Icon, InfoWindowWithoutPositionDefinition, MarkerDefinition, Point, PolygonDefinition, PolylineDefinition } from '@symfony/ux-map';
3
3
  import 'leaflet/dist/leaflet.min.css';
4
+ import type { CircleOptions, ControlPosition, MapOptions as LeafletMapOptions, MarkerOptions, PolylineOptions as PolygonOptions, PolylineOptions, PopupOptions, PolylineOptions as RectangleOptions } from 'leaflet';
4
5
  import * as L from 'leaflet';
5
- import type { MapOptions as LeafletMapOptions, MarkerOptions, PolylineOptions as PolygonOptions, PolylineOptions, PopupOptions } from 'leaflet';
6
- type MapOptions = Pick<LeafletMapOptions, 'center' | 'zoom'> & {
6
+ type MapOptions = Pick<LeafletMapOptions, 'attributionControl' | 'zoomControl'> & {
7
+ attributionControlOptions?: {
8
+ position: ControlPosition;
9
+ prefix: string | false;
10
+ };
11
+ zoomControlOptions?: {
12
+ position: ControlPosition;
13
+ zoomInText: string;
14
+ zoomInTitle: string;
15
+ zoomOutText: string;
16
+ zoomOutTitle: string;
17
+ };
7
18
  tileLayer: {
8
19
  url: string;
9
20
  attribution: string;
10
21
  options: Record<string, unknown>;
11
22
  } | false;
12
23
  };
13
- export default class extends AbstractMapController<MapOptions, L.Map, MarkerOptions, L.Marker, PopupOptions, L.Popup, PolygonOptions, L.Polygon, PolylineOptions, L.Polyline> {
24
+ export default class extends AbstractMapController<MapOptions, LeafletMapOptions, L.Map, MarkerOptions, L.Marker, PopupOptions, L.Popup, PolygonOptions, L.Polygon, PolylineOptions, L.Polyline, CircleOptions, L.Circle, RectangleOptions, L.Rectangle> {
14
25
  map: L.Map;
15
26
  connect(): void;
16
27
  centerValueChanged(): void;
17
28
  zoomValueChanged(): void;
18
29
  protected dispatchEvent(name: string, payload?: Record<string, unknown>): void;
19
- protected doCreateMap({ center, zoom, options, }: {
20
- center: Point | null;
21
- zoom: number | null;
22
- options: MapOptions;
30
+ protected doCreateMap({ definition }: {
31
+ definition: MapDefinition<MapOptions, LeafletMapOptions>;
23
32
  }): L.Map;
24
33
  protected doCreateMarker({ definition }: {
25
34
  definition: MarkerDefinition<MarkerOptions, PopupOptions>;
26
35
  }): L.Marker;
27
36
  protected doRemoveMarker(marker: L.Marker): void;
28
- protected doCreatePolygon({ definition, }: {
37
+ protected doCreatePolygon({ definition }: {
29
38
  definition: PolygonDefinition<PolygonOptions, PopupOptions>;
30
39
  }): L.Polygon;
31
40
  protected doRemovePolygon(polygon: L.Polygon): void;
32
- protected doCreatePolyline({ definition, }: {
41
+ protected doCreatePolyline({ definition }: {
33
42
  definition: PolylineDefinition<PolylineOptions, PopupOptions>;
34
43
  }): L.Polyline;
35
44
  protected doRemovePolyline(polyline: L.Polyline): void;
45
+ protected doCreateCircle({ definition }: {
46
+ definition: CircleDefinition<CircleOptions, PopupOptions>;
47
+ }): L.Circle;
48
+ protected doRemoveCircle(circle: L.Circle): void;
49
+ protected doCreateRectangle({ definition }: {
50
+ definition: RectangleDefinition<RectangleOptions, PopupOptions>;
51
+ }): L.Rectangle;
52
+ protected doRemoveRectangle(rectangle: L.Rectangle): void;
36
53
  protected doCreateInfoWindow({ definition, element, }: {
37
- definition: InfoWindowWithoutPositionDefinition<PopupOptions>;
38
- element: L.Marker | L.Polygon | L.Polyline;
54
+ definition: Omit<InfoWindowDefinition<PopupOptions>, 'position'>;
55
+ element: L.Marker | L.Polygon | L.Polyline | L.Circle | L.Rectangle;
39
56
  }): L.Popup;
40
- protected doCreateIcon({ definition, element, }: {
57
+ protected doCreateIcon({ definition, element }: {
41
58
  definition: Icon;
42
59
  element: L.Marker;
43
60
  }): void;
44
61
  protected doFitBoundsToMarkers(): void;
62
+ private closePopups;
45
63
  }
46
64
  export {};
@@ -13,23 +13,31 @@ class default_1 extends Controller {
13
13
  this.markers = new Map();
14
14
  this.polygons = new Map();
15
15
  this.polylines = new Map();
16
+ this.circles = new Map();
17
+ this.rectangles = new Map();
16
18
  this.infoWindows = [];
17
19
  this.isConnected = false;
18
20
  }
19
21
  connect() {
20
- const options = this.optionsValue;
21
- this.dispatchEvent('pre-connect', { options });
22
+ const extra = this.hasExtraValue ? this.extraValue : {};
23
+ const mapDefinition = {
24
+ center: this.hasCenterValue ? this.centerValue : null,
25
+ zoom: this.hasZoomValue ? this.zoomValue : null,
26
+ options: this.optionsValue,
27
+ extra,
28
+ };
29
+ this.dispatchEvent('pre-connect', mapDefinition);
22
30
  this.createMarker = this.createDrawingFactory('marker', this.markers, this.doCreateMarker.bind(this));
23
31
  this.createPolygon = this.createDrawingFactory('polygon', this.polygons, this.doCreatePolygon.bind(this));
24
32
  this.createPolyline = this.createDrawingFactory('polyline', this.polylines, this.doCreatePolyline.bind(this));
25
- this.map = this.doCreateMap({
26
- center: this.hasCenterValue ? this.centerValue : null,
27
- zoom: this.hasZoomValue ? this.zoomValue : null,
28
- options,
29
- });
33
+ this.createCircle = this.createDrawingFactory('circle', this.circles, this.doCreateCircle.bind(this));
34
+ this.createRectangle = this.createDrawingFactory('rectangle', this.rectangles, this.doCreateRectangle.bind(this));
35
+ this.map = this.doCreateMap({ definition: mapDefinition });
30
36
  this.markersValue.forEach((definition) => this.createMarker({ definition }));
31
37
  this.polygonsValue.forEach((definition) => this.createPolygon({ definition }));
32
38
  this.polylinesValue.forEach((definition) => this.createPolyline({ definition }));
39
+ this.circlesValue.forEach((definition) => this.createCircle({ definition }));
40
+ this.rectanglesValue.forEach((definition) => this.createRectangle({ definition }));
33
41
  if (this.fitBoundsToMarkersValue) {
34
42
  this.doFitBoundsToMarkers();
35
43
  }
@@ -38,7 +46,10 @@ class default_1 extends Controller {
38
46
  markers: [...this.markers.values()],
39
47
  polygons: [...this.polygons.values()],
40
48
  polylines: [...this.polylines.values()],
49
+ circles: [...this.circles.values()],
50
+ rectangles: [...this.rectangles.values()],
41
51
  infoWindows: this.infoWindows,
52
+ extra,
42
53
  });
43
54
  this.isConnected = true;
44
55
  }
@@ -70,11 +81,26 @@ class default_1 extends Controller {
70
81
  }
71
82
  this.onDrawChanged(this.polylines, this.polylinesValue, this.createPolyline, this.doRemovePolyline);
72
83
  }
84
+ circlesValueChanged() {
85
+ if (!this.isConnected) {
86
+ return;
87
+ }
88
+ this.onDrawChanged(this.circles, this.circlesValue, this.createCircle, this.doRemoveCircle);
89
+ }
90
+ rectanglesValueChanged() {
91
+ if (!this.isConnected) {
92
+ return;
93
+ }
94
+ this.onDrawChanged(this.rectangles, this.rectanglesValue, this.createRectangle, this.doRemoveRectangle);
95
+ }
73
96
  createDrawingFactory(type, draws, factory) {
74
97
  const eventBefore = `${type}:before-create`;
75
98
  const eventAfter = `${type}:after-create`;
76
99
  return ({ definition }) => {
77
100
  this.dispatchEvent(eventBefore, { definition });
101
+ if (typeof definition.rawOptions !== 'undefined') {
102
+ 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);
103
+ }
78
104
  const drawing = factory({ definition });
79
105
  this.dispatchEvent(eventAfter, { [type]: drawing, definition });
80
106
  draws.set(definition['@id'], drawing);
@@ -106,7 +132,10 @@ default_1.values = {
106
132
  markers: Array,
107
133
  polygons: Array,
108
134
  polylines: Array,
135
+ circles: Array,
136
+ rectangles: Array,
109
137
  options: Object,
138
+ extra: Object,
110
139
  };
111
140
 
112
141
  class map_controller extends default_1 {
@@ -131,19 +160,21 @@ class map_controller extends default_1 {
131
160
  }
132
161
  }
133
162
  dispatchEvent(name, payload = {}) {
163
+ payload.L = L;
134
164
  this.dispatch(name, {
135
165
  prefix: 'ux:map',
136
- detail: {
137
- ...payload,
138
- L,
139
- },
166
+ detail: payload,
140
167
  });
141
168
  }
142
- doCreateMap({ center, zoom, options, }) {
169
+ doCreateMap({ definition }) {
170
+ const { center, zoom, options, bridgeOptions = {} } = definition;
143
171
  const map = L.map(this.element, {
144
- ...options,
145
172
  center: center === null ? undefined : center,
146
173
  zoom: zoom === null ? undefined : zoom,
174
+ attributionControl: false,
175
+ zoomControl: false,
176
+ ...options,
177
+ ...bridgeOptions,
147
178
  });
148
179
  if (options.tileLayer) {
149
180
  L.tileLayer(options.tileLayer.url, {
@@ -151,11 +182,22 @@ class map_controller extends default_1 {
151
182
  ...options.tileLayer.options,
152
183
  }).addTo(map);
153
184
  }
185
+ if (typeof options.attributionControlOptions !== 'undefined') {
186
+ L.control.attribution({ ...options.attributionControlOptions }).addTo(map);
187
+ }
188
+ if (typeof options.zoomControlOptions !== 'undefined') {
189
+ L.control.zoom({ ...options.zoomControlOptions }).addTo(map);
190
+ }
154
191
  return map;
155
192
  }
156
193
  doCreateMarker({ definition }) {
157
- const { '@id': _id, position, title, infoWindow, icon, extra, rawOptions = {}, ...otherOptions } = definition;
158
- const marker = L.marker(position, { title: title || undefined, ...otherOptions, ...rawOptions }).addTo(this.map);
194
+ const { '@id': _id, position, title, infoWindow, icon, rawOptions = {}, bridgeOptions = {} } = definition;
195
+ const marker = L.marker(position, {
196
+ title: title || undefined,
197
+ ...rawOptions,
198
+ ...bridgeOptions,
199
+ riseOnHover: true,
200
+ }).addTo(this.map);
159
201
  if (infoWindow) {
160
202
  this.createInfoWindow({ definition: infoWindow, element: marker });
161
203
  }
@@ -167,9 +209,9 @@ class map_controller extends default_1 {
167
209
  doRemoveMarker(marker) {
168
210
  marker.remove();
169
211
  }
170
- doCreatePolygon({ definition, }) {
171
- const { '@id': _id, points, title, infoWindow, rawOptions = {} } = definition;
172
- const polygon = L.polygon(points, { ...rawOptions }).addTo(this.map);
212
+ doCreatePolygon({ definition }) {
213
+ const { '@id': _id, points, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition;
214
+ const polygon = L.polygon(points, { ...rawOptions, ...bridgeOptions }).addTo(this.map);
173
215
  if (title) {
174
216
  polygon.bindPopup(title);
175
217
  }
@@ -181,9 +223,9 @@ class map_controller extends default_1 {
181
223
  doRemovePolygon(polygon) {
182
224
  polygon.remove();
183
225
  }
184
- doCreatePolyline({ definition, }) {
185
- const { '@id': _id, points, title, infoWindow, rawOptions = {} } = definition;
186
- const polyline = L.polyline(points, { ...rawOptions }).addTo(this.map);
226
+ doCreatePolyline({ definition }) {
227
+ const { '@id': _id, points, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition;
228
+ const polyline = L.polyline(points, { ...rawOptions, ...bridgeOptions }).addTo(this.map);
187
229
  if (title) {
188
230
  polyline.bindPopup(title);
189
231
  }
@@ -195,19 +237,58 @@ class map_controller extends default_1 {
195
237
  doRemovePolyline(polyline) {
196
238
  polyline.remove();
197
239
  }
240
+ doCreateCircle({ definition }) {
241
+ const { '@id': _id, center, radius, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition;
242
+ const circle = L.circle(center, { radius, ...rawOptions, ...bridgeOptions }).addTo(this.map);
243
+ if (title) {
244
+ circle.bindPopup(title);
245
+ }
246
+ if (infoWindow) {
247
+ this.createInfoWindow({ definition: infoWindow, element: circle });
248
+ }
249
+ return circle;
250
+ }
251
+ doRemoveCircle(circle) {
252
+ circle.remove();
253
+ }
254
+ doCreateRectangle({ definition }) {
255
+ const { '@id': _id, southWest, northEast, title, infoWindow, rawOptions = {}, bridgeOptions = {} } = definition;
256
+ const rectangle = L.rectangle([
257
+ [southWest.lat, southWest.lng],
258
+ [northEast.lat, northEast.lng],
259
+ ], { ...rawOptions, ...bridgeOptions }).addTo(this.map);
260
+ if (title) {
261
+ rectangle.bindPopup(title);
262
+ }
263
+ if (infoWindow) {
264
+ this.createInfoWindow({ definition: infoWindow, element: rectangle });
265
+ }
266
+ return rectangle;
267
+ }
268
+ doRemoveRectangle(rectangle) {
269
+ rectangle.remove();
270
+ }
198
271
  doCreateInfoWindow({ definition, element, }) {
199
- const { headerContent, content, rawOptions = {}, ...otherOptions } = definition;
200
- element.bindPopup([headerContent, content].filter((x) => x).join('<br>'), { ...otherOptions, ...rawOptions });
201
- if (definition.opened) {
272
+ const { headerContent, content, opened, autoClose, rawOptions = {}, bridgeOptions = {} } = definition;
273
+ element.bindPopup([headerContent, content].filter((x) => x).join('<br>'), { ...rawOptions, ...bridgeOptions });
274
+ if (opened) {
275
+ if (autoClose) {
276
+ this.closePopups();
277
+ }
202
278
  element.openPopup();
203
279
  }
204
280
  const popup = element.getPopup();
205
281
  if (!popup) {
206
282
  throw new Error('Unable to get the Popup associated with the element.');
207
283
  }
284
+ popup.on('click', () => {
285
+ if (autoClose) {
286
+ this.closePopups({ except: popup });
287
+ }
288
+ });
208
289
  return popup;
209
290
  }
210
- doCreateIcon({ definition, element, }) {
291
+ doCreateIcon({ definition, element }) {
211
292
  const { type, width, height } = definition;
212
293
  let icon;
213
294
  if (type === IconTypes.Svg) {
@@ -247,6 +328,14 @@ class map_controller extends default_1 {
247
328
  });
248
329
  this.map.fitBounds(bounds);
249
330
  }
331
+ closePopups(options = {}) {
332
+ this.infoWindows.forEach((popup) => {
333
+ if (options.except && popup === options.except) {
334
+ return;
335
+ }
336
+ popup.close();
337
+ });
338
+ }
250
339
  }
251
340
 
252
341
  export { map_controller as default };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@symfony/ux-leaflet-map",
3
3
  "description": "Leaflet bridge for Symfony UX Map, integrate interactive maps in your Symfony applications",
4
4
  "license": "MIT",
5
- "version": "2.26.0",
5
+ "version": "2.27.0",
6
6
  "keywords": [
7
7
  "symfony-ux",
8
8
  "leaflet",
@@ -49,7 +49,7 @@
49
49
  },
50
50
  "devDependencies": {
51
51
  "@hotwired/stimulus": "^3.0.0",
52
- "@symfony/ux-map": "2.26.0",
52
+ "@symfony/ux-map": "2.27.0",
53
53
  "@types/leaflet": "^1.9.12",
54
54
  "leaflet": "^1.9.4"
55
55
  }