@teipublisher/pb-components 2.15.3 → 2.17.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,464 +1,546 @@
1
- import { LitElement, html, css } from 'lit-element';
2
- import "@lrnwebcomponents/es-global-bridge";
3
- import { pbMixin } from './pb-mixin.js';
4
- import { resolveURL } from './utils.js';
5
- import './pb-map-layer.js';
6
- import './pb-map-icon.js';
7
-
8
- /**
9
- * A wrapper component for [leaflet](https://leafletjs.com/) displaying a map.
10
- *
11
- * The map layers displayed can be configured via nested `pb-map-layer` (see docs) elements,
12
- * icons via `pb-map-icon`.
13
- *
14
- * @slot - may contain a series of `pb-map-layer` configurations
15
- * @fires pb-leaflet-marker-click - Fires event to be processed by the map upon click
16
- * @fires pb-update-map - When received, redraws the map to fit markers passed in with the event.
17
- * Event details should include an array of locations, see `pb-geolocation` event below.
18
- * @fires pb-update - When received, redraws the map to show markers for all pb-geolocation elements found in the content of the pb-view
19
- * @fires pb-geolocation - When received, focuses the map on the geocoordinates passed in with the event.
20
- * The event details should include an object:
21
- * ```
22
- * {
23
- * coordinates: {
24
- * latitude: Number,
25
- * longitude: Number
26
- * },
27
- * label: string - the label to show on mouseover,
28
- * zoom: Number - fixed zoom level to zoom to,
29
- * fitBounds: Boolean - if true, recompute current zoom level to show all markers
30
- * }
31
- * ```
32
- */
33
- export class PbLeafletMap extends pbMixin(LitElement) {
34
- static get properties() {
35
- return {
36
- ...super.properties,
37
- latitude: {
38
- type: Number
39
- },
40
- longitude: {
41
- type: Number
42
- },
43
- zoom: {
44
- type: Number
45
- },
46
- crs: {
47
- type: String
48
- },
49
- /**
50
- * If set, the map will automatically zoom so it can fit all the markers
51
- */
52
- fitMarkers: {
53
- type: Boolean,
54
- attribute: 'fit-markers'
55
- },
56
- /**
57
- * If set, combine markers into clusters if they are located too close together
58
- * to display as single markers
59
- */
60
- cluster: {
61
- type: Boolean
62
- },
63
- /**
64
- * Limits up to which zoom level markers are arranged into clusters.
65
- * Using a higher zoom level here will result in more markers to be shown.
66
- *
67
- * Requires `cluster` option to be enabled.
68
- */
69
- disableClusteringAt: {
70
- type: Number,
71
- attribute: 'disable-clustering-at'
72
- },
73
- /**
74
- * If enabled, the map will not automatically scroll to the coordinates received via `pb-geolocation`
75
- */
76
- noScroll: {
77
- type: Boolean,
78
- attribute: 'no-scroll'
79
- },
80
- accessToken: {
81
- type: String,
82
- attribute: 'access-token'
83
- },
84
- /**
85
- * If enabled, the map will remain invisible until an event is received from `pb-geolocation`.
86
- * In this case the map also offers a close button to hide it again.
87
- */
88
- toggle: {
89
- type: Boolean
90
- },
91
- imagesPath: {
92
- type: String,
93
- attribute: 'images-path'
94
- },
95
- cssPath: {
96
- type: String,
97
- attribute: 'css-path'
98
- },
99
- _map: {
100
- type: Object
101
- }
102
- };
103
- }
104
-
105
- constructor() {
106
- super();
107
- this.latitude = 51.505;
108
- this.longitude = -0.09;
109
- this.zoom = 15;
110
- this.crs = 'EPSG3857';
111
- this.accessToken = '';
112
- this.imagesPath = '../images/leaflet/';
113
- this.cssPath = '../css/leaflet';
114
- this.toggle = false;
115
- this.noScroll = false;
116
- this.disabled = true;
117
- this.cluster = false;
118
- this.fitMarkers = false;
119
- this.disableClusteringAt = null;
120
- this._icons = {};
121
- }
122
-
123
- connectedCallback() {
124
- super.connectedCallback();
125
-
126
- this._layers = this.querySelectorAll('pb-map-layer');
127
- this._markers = this.querySelectorAll('pb-map-icon');
128
-
129
- /**
130
- * Custom event which passes an array of pb-geolocation within event details
131
- * @param {{ detail: any[]; }} ev
132
- */
133
- this.subscribeTo('pb-update-map', (ev) => {
134
- this._markerLayer.clearLayers();
135
-
136
- /**
137
- * @param {{ latitude: any; longitude: any; label: any; }} loc
138
- */
139
- /**
140
- * @param {{ latitude: any; longitude: any; label: any; }} loc
141
- */
142
- ev.detail.forEach((loc) => {
143
- const marker = L.marker([loc.latitude, loc.longitude]);
144
- if (loc.label) {
145
- marker.bindTooltip(loc.label);
146
- }
147
- marker.addEventListener('click', () => {
148
- this.emitTo('pb-leaflet-marker-click', loc);
149
- });
150
- marker.bindTooltip(loc.label);
151
- this.setMarkerIcon(marker);
152
- this._markerLayer.addLayer(marker);
153
- });
154
- this._fitBounds();
155
- });
156
-
157
- /**
158
- * React to pb-update event triggered by a pb-view
159
- *
160
- * @param {{ detail: { root: { querySelectorAll: (arg0: string) => any[]; }; }; }} ev
161
- */
162
- this.subscribeTo('pb-update', (ev) => {
163
- this._markerLayer.clearLayers();
164
- const locations = ev.detail.root.querySelectorAll('pb-geolocation');
165
- /**
166
- * @param {{ latitude: any; longitude: any; }} loc
167
- */
168
- locations.forEach((loc) => {
169
- const coords = L.latLng(loc.latitude, loc.longitude);
170
- const marker = L.marker(coords).addTo(this._markerLayer);
171
- if (loc.label) {
172
- marker.bindTooltip(loc.label);
173
- }
174
- if (loc.popup) {
175
- marker.bindPopup(loc.popup);
176
- }
177
- marker.addEventListener('click', () => {
178
- this.emitTo('pb-leaflet-marker-click', loc);
179
- });
180
- this.setMarkerIcon(marker);
181
- });
182
- this._fitBounds();
183
- });
184
-
185
- /**
186
- * React to events send by pb-geolocation
187
- *
188
- * @param {{ detail: { coordinates: { latitude: number; longitude: number; }, label: string; }; }} ev
189
- */
190
- this.subscribeTo('pb-geolocation', (ev) => {
191
- if (ev.detail.coordinates) {
192
- this.latitude = ev.detail.coordinates.latitude;
193
- this.longitude = ev.detail.coordinates.longitude;
194
- if (!this._hasMarker(this.latitude, this.longitude)) {
195
- const marker = L.marker([this.latitude, this.longitude]);
196
- marker.addEventListener('click', () => {
197
- this.emitTo('pb-leaflet-marker-click', ev.detail.element);
198
- });
199
- if (ev.detail.label) {
200
- marker.bindTooltip(ev.detail.label);
201
- }
202
- if (ev.detail.popup) {
203
- marker.bindPopup(ev.detail.popup);
204
- }
205
- this.setMarkerIcon(marker);
206
- marker.addTo(this._markerLayer);
207
-
208
- if (ev.detail.fitBounds) {
209
- this._fitBounds();
210
- }
211
-
212
- console.log('<pb-leaflet-map> added marker');
213
- } else {
214
- console.log('<pb-leaflet-map> Marker already added to map');
215
- }
216
-
217
- if (this.toggle) {
218
- this.disabled = false;
219
- }
220
- const activateMarker = ev.detail.event;
221
- this._locationChanged(this.latitude, this.longitude, ev.detail.zoom, activateMarker);
222
- }
223
- });
224
- }
225
-
226
- /**
227
- * The underlying leafletjs map. Can be used for custom scripts.
228
- *
229
- * Will be null until the component is fully loaded. Listen to `pb-ready` on the component to
230
- * be sure it has initialized.
231
- */
232
- get map() {
233
- return this._map;
234
- }
235
-
236
- setMarkerIcon(layer) {
237
- if (this._icons && this._icons.default) {
238
- layer.setIcon(this._icons.default);
239
- }
240
- }
241
-
242
- firstUpdated() {
243
- if (!this.toggle) {
244
- this.disabled = false;
245
- }
246
- window.ESGlobalBridge.requestAvailability();
247
- const leafletPath = resolveURL('../lib/leaflet-src.js');
248
- const pluginPath = resolveURL('../lib/leaflet.markercluster-src.js');
249
- window.ESGlobalBridge.instance.load("leaflet", leafletPath)
250
- .then(() => window.ESGlobalBridge.instance.load("plugin", pluginPath));
251
- window.addEventListener(
252
- "es-bridge-plugin-loaded",
253
- this._initMap.bind(this),
254
- { once: true }
255
- );
256
- }
257
-
258
- render() {
259
- const cssPath = resolveURL(this.cssPath);
260
- return html`
261
- <link rel="Stylesheet" href="${cssPath}/leaflet.css">
262
- <link rel="Stylesheet" href="${cssPath}/MarkerCluster.Default.css">
263
- <div id="map" style="height: 100%; width: 100%"></div>
264
- `;
265
- }
266
-
267
- static get styles() {
268
- return css`
269
- :host {
270
- display: block;
271
- }
272
-
273
- :host([disabled]) {
274
- visibility: hidden;
275
- }
276
-
277
- .close {
278
- border-radius: 4px;
279
- background-color: #fff;
280
- color: inherit;
281
- padding: 8px;
282
- font-size: 18px;
283
- font-weight: bold;
284
- text-decoration: none;
285
- cursor: pointer;
286
- }
287
- `;
288
- }
289
-
290
- _initMap() {
291
- if (this._map) {
292
- return;
293
- }
294
-
295
- L.Icon.Default.imagePath = resolveURL(this.imagesPath);
296
-
297
- const crs = L.CRS[this.crs] || L.CRS.EPSG3857;
298
- this._map = L.map(this.shadowRoot.getElementById('map'), {
299
- zoom: this.zoom,
300
- center: L.latLng(this.latitude, this.longitude),
301
- crs
302
- });
303
- this._configureLayers();
304
- this._configureMarkers();
305
-
306
- if (this.cluster) {
307
- const options = {};
308
- if (this.disableClusteringAt) {
309
- options.disableClusteringAtZoom = this.disableClusteringAt;
310
- }
311
- this._markerLayer = L.markerClusterGroup(options);
312
- } else {
313
- this._markerLayer = L.layerGroup();
314
- }
315
- this._markerLayer.addTo(this._map);
316
-
317
- this.signalReady();
318
-
319
- L.control.scale().addTo(this._map);
320
-
321
- if (this.toggle) {
322
- let container;
323
- L.Control.CloseButton = L.Control.extend({
324
- options: {
325
- position: 'topright'
326
- },
327
- onAdd: (map) => {
328
- container = L.DomUtil.create('div');
329
- container.className = 'close';
330
- container.innerHTML = 'X';
331
- L.DomEvent.on(container, 'click', this._hide.bind(this));
332
- return container;
333
- },
334
- onRemove: (map) => {
335
- L.DomEvent.off(container, 'click', this._hide.bind(this));
336
- }
337
- });
338
- L.control.closeButton = (options) => new L.Control.CloseButton(options);
339
- L.control.closeButton({ position: 'topright' }).addTo(this._map);
340
- }
341
- }
342
-
343
- _configureMarkers() {
344
- if (this._markers.length === 0) {
345
- return;
346
- }
347
-
348
- this._icons = {};
349
- this._markers.forEach(config => {
350
- if (config.iconUrl) {
351
- this._icons[config.name] = L.icon(config.options);
352
- }
353
- })
354
- }
355
-
356
- _configureLayers() {
357
- if (this._layers.length === 0) {
358
- // configure a default layer
359
- L.tileLayer('https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/{z}/{x}/{y}?access_token={accessToken}', {
360
- attribution: '© <a href="https://www.mapbox.com/about/maps/">Mapbox</a> © <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> <strong><a href="https://www.mapbox.com/map-feedback/" target="_blank">Improve this map</a></strong>',
361
- maxZoom: 18,
362
- zoomOffset: -1,
363
- tileSize: 512,
364
- accessToken: this.accessToken
365
- }).addTo(this._map);
366
- return;
367
- }
368
- const layers = L.control.layers(null, null, { collapsed: false });
369
- this._layers.forEach(config => {
370
- let layer;
371
- switch (config.type) {
372
- case 'geojson':
373
- config.data().then((data) => {
374
- layer = L.geoJSON([data]);
375
- this._addLayer(config, layer, layers);
376
- });
377
- break;
378
- default:
379
- layer = L.tileLayer(config.url, config.options);
380
- this._addLayer(config, layer, layers);
381
- break;
382
- }
383
- });
384
- // only show layer control if there's more than one layer
385
- if (this._layers.length > 1) {
386
- layers.addTo(this._map);
387
- }
388
- this._layers = null;
389
- }
390
-
391
- _addLayer(config, layer, layers) {
392
- if (config.show) {
393
- layer.addTo(this._map);
394
- }
395
- if (config.label) {
396
- if (config.base) {
397
- layers.addBaseLayer(layer, config.label);
398
- } else {
399
- layers.addOverlay(layer, config.label);
400
- }
401
- }
402
- }
403
-
404
- _fitBounds() {
405
- if (!this.fitMarkers) {
406
- return;
407
- }
408
- const bounds = L.latLngBounds();
409
- let len = 0;
410
- this._markerLayer.eachLayer((layer) => {
411
- bounds.extend(layer.getLatLng());
412
- len += 1;
413
- });
414
- if (len === 0) {
415
- this._map.fitWorld();
416
- } else if (len === 1) {
417
- this._map.fitBounds(bounds, {maxZoom: this.zoom});
418
- } else {
419
- this._map.fitBounds(bounds);
420
- }
421
- }
422
-
423
- _locationChanged(lat, long, zoom, setActive) {
424
- if (this._map) {
425
- const coords = L.latLng([lat, long]);
426
- this._markerLayer.eachLayer((layer) => {
427
- if (layer.getLatLng().equals(coords)) {
428
- if (zoom && !this.noScroll) {
429
- layer.openTooltip();
430
- this._map.setView(coords, zoom);
431
- } else if (this.cluster) {
432
- this._markerLayer.zoomToShowLayer(layer, () =>
433
- layer.openTooltip()
434
- );
435
- } else {
436
- layer.openTooltip();
437
- this._map.setView(coords, this.zoom);
438
- }
439
- if (setActive && this._icons && this._icons.active) {
440
- layer.setIcon(this._icons.active);
441
- }
442
- } else if (this._icons && this._icons.default && layer.getIcon() !== this._icons.default) {
443
- layer.setIcon(this._icons.default);
444
- }
445
- });
446
- }
447
- }
448
-
449
- _hasMarker(lat, long) {
450
- const coords = L.latLng([lat, long]);
451
- let found = null;
452
- this._markerLayer.eachLayer((layer) => {
453
- if (layer instanceof L.Marker && layer.getLatLng().equals(coords)) {
454
- found = layer;
455
- }
456
- });
457
- return found;
458
- }
459
-
460
- _hide() {
461
- this.disabled = true;
462
- }
463
- }
1
+ import { LitElement, html, css } from 'lit-element';
2
+ import "@lrnwebcomponents/es-global-bridge";
3
+ import { pbMixin } from './pb-mixin.js';
4
+ import { resolveURL } from './utils.js';
5
+ import { get as i18n } from "./pb-i18n.js";
6
+ import './pb-map-layer.js';
7
+ import './pb-map-icon.js';
8
+
9
+ /**
10
+ * A wrapper component for [leaflet](https://leafletjs.com/) displaying a map.
11
+ *
12
+ * The map layers displayed can be configured via nested `pb-map-layer` (see docs) elements,
13
+ * icons via `pb-map-icon`.
14
+ *
15
+ * @slot - may contain a series of `pb-map-layer` configurations
16
+ * @fires pb-leaflet-marker-click - Fires event to be processed by the map upon click
17
+ * @fires pb-update-map - When received, redraws the map to fit markers passed in with the event.
18
+ * Event details should include an array of locations, see `pb-geolocation` event below.
19
+ * @fires pb-update - When received, redraws the map to show markers for all pb-geolocation elements found in the content of the pb-view
20
+ * @fires pb-geolocation - When received, focuses the map on the geocoordinates passed in with the event.
21
+ * The event details should include an object:
22
+ * ```
23
+ * {
24
+ * coordinates: {
25
+ * latitude: Number,
26
+ * longitude: Number
27
+ * },
28
+ * label: string - the label to show on mouseover,
29
+ * zoom: Number - fixed zoom level to zoom to,
30
+ * fitBounds: Boolean - if true, recompute current zoom level to show all markers
31
+ * }
32
+ * ```
33
+ * @fires pb-geocode - emitted if geocoding is enabled and the user searches or selects a location from the map
34
+ */
35
+ export class PbLeafletMap extends pbMixin(LitElement) {
36
+ static get properties() {
37
+ return {
38
+ ...super.properties,
39
+ latitude: {
40
+ type: Number
41
+ },
42
+ longitude: {
43
+ type: Number
44
+ },
45
+ zoom: {
46
+ type: Number
47
+ },
48
+ crs: {
49
+ type: String
50
+ },
51
+ /**
52
+ * If set, the map will automatically zoom so it can fit all the markers
53
+ */
54
+ fitMarkers: {
55
+ type: Boolean,
56
+ attribute: 'fit-markers'
57
+ },
58
+ /**
59
+ * If set, combine markers into clusters if they are located too close together
60
+ * to display as single markers
61
+ */
62
+ cluster: {
63
+ type: Boolean
64
+ },
65
+ /**
66
+ * Limits up to which zoom level markers are arranged into clusters.
67
+ * Using a higher zoom level here will result in more markers to be shown.
68
+ *
69
+ * Requires `cluster` option to be enabled.
70
+ */
71
+ disableClusteringAt: {
72
+ type: Number,
73
+ attribute: 'disable-clustering-at'
74
+ },
75
+ /**
76
+ * If enabled, the map will not automatically scroll to the coordinates received via `pb-geolocation`
77
+ */
78
+ noScroll: {
79
+ type: Boolean,
80
+ attribute: 'no-scroll'
81
+ },
82
+ accessToken: {
83
+ type: String,
84
+ attribute: 'access-token'
85
+ },
86
+ /**
87
+ * If enabled, the map will remain invisible until an event is received from `pb-geolocation`.
88
+ * In this case the map also offers a close button to hide it again.
89
+ */
90
+ toggle: {
91
+ type: Boolean
92
+ },
93
+ imagesPath: {
94
+ type: String,
95
+ attribute: 'images-path'
96
+ },
97
+ cssPath: {
98
+ type: String,
99
+ attribute: 'css-path'
100
+ },
101
+ /**
102
+ * Enables geocoding: an additional control will allow users to search for a place.
103
+ * Reverse geocoding is also possible: single clicking on the map will request information
104
+ * about the current location.
105
+ *
106
+ * In both cases, a `pb-geocode` event will be emitted containing additional information
107
+ * about the place in the event details (see demo).
108
+ *
109
+ * For lookups the free OSM/Nominatim service is used.
110
+ */
111
+ geoCoding: {
112
+ type: Boolean,
113
+ attribute: 'geo-coding'
114
+ },
115
+ _map: {
116
+ type: Object
117
+ }
118
+ };
119
+ }
120
+
121
+ constructor() {
122
+ super();
123
+ this.latitude = 51.505;
124
+ this.longitude = -0.09;
125
+ this.zoom = 15;
126
+ this.crs = 'EPSG3857';
127
+ this.accessToken = '';
128
+ this.imagesPath = '../images/leaflet/';
129
+ this.cssPath = '../css/leaflet';
130
+ this.toggle = false;
131
+ this.noScroll = false;
132
+ this.disabled = true;
133
+ this.cluster = false;
134
+ this.fitMarkers = false;
135
+ this.disableClusteringAt = null;
136
+ this._icons = {};
137
+ this.geoCoding = false;
138
+ }
139
+
140
+ connectedCallback() {
141
+ super.connectedCallback();
142
+
143
+ this._layers = this.querySelectorAll('pb-map-layer');
144
+ this._markers = this.querySelectorAll('pb-map-icon');
145
+
146
+ /**
147
+ * Custom event which passes an array of pb-geolocation within event details
148
+ * @param {{ detail: any[]; }} ev
149
+ */
150
+ this.subscribeTo('pb-update-map', (ev) => {
151
+ this._markerLayer.clearLayers();
152
+
153
+ /**
154
+ * @param {{ latitude: any; longitude: any; label: any; }} loc
155
+ */
156
+ /**
157
+ * @param {{ latitude: any; longitude: any; label: any; }} loc
158
+ */
159
+ ev.detail.forEach((loc) => {
160
+ const marker = L.marker([loc.latitude, loc.longitude]);
161
+ if (loc.label) {
162
+ marker.bindTooltip(loc.label);
163
+ }
164
+ marker.addEventListener('click', () => {
165
+ this.emitTo('pb-leaflet-marker-click', loc);
166
+ });
167
+ marker.bindTooltip(loc.label);
168
+ this.setMarkerIcon(marker);
169
+ this._markerLayer.addLayer(marker);
170
+ });
171
+ this._fitBounds();
172
+ });
173
+
174
+ /**
175
+ * React to pb-update event triggered by a pb-view
176
+ *
177
+ * @param {{ detail: { root: { querySelectorAll: (arg0: string) => any[]; }; }; }} ev
178
+ */
179
+ this.subscribeTo('pb-update', (ev) => {
180
+ this._markerLayer.clearLayers();
181
+ const locations = ev.detail.root.querySelectorAll('pb-geolocation');
182
+ /**
183
+ * @param {{ latitude: any; longitude: any; }} loc
184
+ */
185
+ locations.forEach((loc) => {
186
+ const coords = L.latLng(loc.latitude, loc.longitude);
187
+ const marker = L.marker(coords).addTo(this._markerLayer);
188
+ if (loc.label) {
189
+ marker.bindTooltip(loc.label);
190
+ }
191
+ if (loc.popup) {
192
+ marker.bindPopup(loc.popup);
193
+ }
194
+ marker.addEventListener('click', () => {
195
+ this.emitTo('pb-leaflet-marker-click', loc);
196
+ });
197
+ this.setMarkerIcon(marker);
198
+ });
199
+ this._fitBounds();
200
+ });
201
+
202
+ /**
203
+ * React to events send by pb-geolocation
204
+ *
205
+ * @param {{ detail: { coordinates: { latitude: number; longitude: number; }, label: string; }; }} ev
206
+ */
207
+ this.subscribeTo('pb-geolocation', (ev) => {
208
+ if (ev.detail.coordinates) {
209
+ this.latitude = ev.detail.coordinates.latitude;
210
+ this.longitude = ev.detail.coordinates.longitude;
211
+ if (ev.detail.clear) {
212
+ this._markerLayer.clearLayers();
213
+ }
214
+
215
+ if (!this._hasMarker(this.latitude, this.longitude)) {
216
+ const marker = L.marker([this.latitude, this.longitude]);
217
+ marker.addEventListener('click', () => {
218
+ this.emitTo('pb-leaflet-marker-click', ev.detail.element);
219
+ });
220
+ if (ev.detail.label) {
221
+ marker.bindTooltip(ev.detail.label);
222
+ }
223
+ if (ev.detail.popup) {
224
+ marker.bindPopup(ev.detail.popup);
225
+ }
226
+ this.setMarkerIcon(marker);
227
+ marker.addTo(this._markerLayer);
228
+
229
+ if (ev.detail.fitBounds) {
230
+ this._fitBounds();
231
+ }
232
+
233
+ console.log('<pb-leaflet-map> added marker');
234
+ } else {
235
+ console.log('<pb-leaflet-map> Marker already added to map');
236
+ }
237
+
238
+ if (this.toggle) {
239
+ this.disabled = false;
240
+ }
241
+ const activateMarker = ev.detail.event;
242
+ this._locationChanged(this.latitude, this.longitude, ev.detail.zoom, activateMarker);
243
+ }
244
+ });
245
+ }
246
+
247
+ /**
248
+ * The underlying leafletjs map. Can be used for custom scripts.
249
+ *
250
+ * Will be null until the component is fully loaded. Listen to `pb-ready` on the component to
251
+ * be sure it has initialized.
252
+ */
253
+ get map() {
254
+ return this._map;
255
+ }
256
+
257
+ setMarkerIcon(layer) {
258
+ if (this._icons && this._icons.default) {
259
+ layer.setIcon(this._icons.default);
260
+ }
261
+ }
262
+
263
+ async firstUpdated() {
264
+ if (!this.toggle) {
265
+ this.disabled = false;
266
+ }
267
+
268
+ if (window.L !== undefined) {
269
+ this._initMap();
270
+ return;
271
+ }
272
+
273
+ window.ESGlobalBridge.requestAvailability();
274
+ const leafletPath = resolveURL('../lib/leaflet-src.js');
275
+ const pluginPath = resolveURL('../lib/leaflet.markercluster-src.js');
276
+ const geoCodingPath = resolveURL('../lib/Control.Geocoder.min.js');
277
+ await window.ESGlobalBridge.instance.load("leaflet", leafletPath);
278
+ await window.ESGlobalBridge.instance.load("plugin", pluginPath);
279
+ if (this.geoCoding) {
280
+ await window.ESGlobalBridge.instance.load("geocoding", geoCodingPath);
281
+ }
282
+
283
+ window.addEventListener(
284
+ `es-bridge-${this.geocoding ? 'geoCoding' : 'plugin'}-loaded`,
285
+ this._initMap.bind(this),
286
+ { once: true }
287
+ );
288
+ }
289
+
290
+ render() {
291
+ const cssPath = resolveURL(this.cssPath);
292
+ return html`
293
+ <link rel="Stylesheet" href="${cssPath}/leaflet.css">
294
+ <link rel="Stylesheet" href="${cssPath}/MarkerCluster.Default.css">
295
+ ${this.geoCoding ? html`<link rel="Stylesheet" href="${cssPath}/Control.Geocoder.css">` : null}
296
+ <div id="map" style="height: 100%; width: 100%"></div>
297
+ `;
298
+ }
299
+
300
+ static get styles() {
301
+ return css`
302
+ :host {
303
+ display: block;
304
+ }
305
+
306
+ :host([disabled]) {
307
+ visibility: hidden;
308
+ }
309
+
310
+ .close {
311
+ border-radius: 4px;
312
+ background-color: #fff;
313
+ color: inherit;
314
+ padding: 8px;
315
+ font-size: 18px;
316
+ font-weight: bold;
317
+ text-decoration: none;
318
+ cursor: pointer;
319
+ }
320
+ `;
321
+ }
322
+
323
+ _initMap() {
324
+ if (this._map) {
325
+ return;
326
+ }
327
+
328
+ L.Icon.Default.imagePath = resolveURL(this.imagesPath);
329
+
330
+ const crs = L.CRS[this.crs] || L.CRS.EPSG3857;
331
+ this._map = L.map(this.shadowRoot.getElementById('map'), {
332
+ zoom: this.zoom,
333
+ center: L.latLng(this.latitude, this.longitude),
334
+ crs
335
+ });
336
+ this._configureLayers();
337
+ this._configureMarkers();
338
+
339
+ if (this.cluster) {
340
+ const options = {};
341
+ if (this.disableClusteringAt) {
342
+ options.disableClusteringAtZoom = this.disableClusteringAt;
343
+ }
344
+ this._markerLayer = L.markerClusterGroup(options);
345
+ } else {
346
+ this._markerLayer = L.layerGroup();
347
+ }
348
+ this._markerLayer.addTo(this._map);
349
+
350
+ this.signalReady();
351
+
352
+ L.control.scale().addTo(this._map);
353
+
354
+ if (this.toggle) {
355
+ let container;
356
+ L.Control.CloseButton = L.Control.extend({
357
+ options: {
358
+ position: 'topright'
359
+ },
360
+ onAdd: (map) => {
361
+ container = L.DomUtil.create('div');
362
+ container.className = 'close';
363
+ container.innerHTML = 'X';
364
+ L.DomEvent.on(container, 'click', this._hide.bind(this));
365
+ return container;
366
+ },
367
+ onRemove: (map) => {
368
+ L.DomEvent.off(container, 'click', this._hide.bind(this));
369
+ }
370
+ });
371
+ L.control.closeButton = (options) => new L.Control.CloseButton(options);
372
+ L.control.closeButton({ position: 'topright' }).addTo(this._map);
373
+ }
374
+
375
+ this._configureGeoCoding();
376
+ }
377
+
378
+ _configureGeoCoding() {
379
+ if (!this.geoCoding) {
380
+ return;
381
+ }
382
+ const geocoder = L.Control.Geocoder.nominatim({
383
+ geocodingQueryParams: {
384
+ 'accept-language': 'en'
385
+ }
386
+ });
387
+ const control = L.Control.geocoder({
388
+ defaultMarkGeocode: false,
389
+ geocoder,
390
+ placeholder: i18n('search.search'),
391
+ suggestMinLength: 3
392
+ });
393
+ control.on('markgeocode', (e) => {
394
+ const {geocode} = e;
395
+ const options = {
396
+ coordinates: {
397
+ longitude: geocode.center.lng,
398
+ latitude: geocode.center.lat,
399
+ },
400
+ name: geocode.name,
401
+ label: geocode.html,
402
+ properties: geocode.properties
403
+ };
404
+ this.emitTo('pb-geocode', options);
405
+ });
406
+ control.addTo(this._map);
407
+
408
+ this._map.on('click', (e) => {
409
+ geocoder.reverse(e.latlng, this._map.options.crs.scale(this._map.getZoom()), (results) => {
410
+ const geocode = results[0];
411
+ const options = {
412
+ coordinates: {
413
+ longitude: geocode.center.lng,
414
+ latitude: geocode.center.lat,
415
+ },
416
+ name: geocode.name,
417
+ label: geocode.html,
418
+ properties: geocode.properties
419
+ };
420
+ this.emitTo('pb-geocode', options);
421
+ });
422
+ });
423
+ }
424
+
425
+ _configureMarkers() {
426
+ if (this._markers.length === 0) {
427
+ return;
428
+ }
429
+
430
+ this._icons = {};
431
+ this._markers.forEach(config => {
432
+ if (config.iconUrl) {
433
+ this._icons[config.name] = L.icon(config.options);
434
+ }
435
+ })
436
+ }
437
+
438
+ _configureLayers() {
439
+ if (this._layers.length === 0) {
440
+ // configure a default layer
441
+ L.tileLayer('https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/{z}/{x}/{y}?access_token={accessToken}', {
442
+ attribution: <a href="https://www.mapbox.com/about/maps/">Mapbox</a> © <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> <strong><a href="https://www.mapbox.com/map-feedback/" target="_blank">Improve this map</a></strong>',
443
+ maxZoom: 18,
444
+ zoomOffset: -1,
445
+ tileSize: 512,
446
+ accessToken: this.accessToken
447
+ }).addTo(this._map);
448
+ return;
449
+ }
450
+ const layers = L.control.layers(null, null, { collapsed: false });
451
+ this._layers.forEach(config => {
452
+ let layer;
453
+ switch (config.type) {
454
+ case 'geojson':
455
+ config.data().then((data) => {
456
+ layer = L.geoJSON([data]);
457
+ this._addLayer(config, layer, layers);
458
+ });
459
+ break;
460
+ default:
461
+ layer = L.tileLayer(config.url, config.options);
462
+ this._addLayer(config, layer, layers);
463
+ break;
464
+ }
465
+ });
466
+ // only show layer control if there's more than one layer
467
+ if (this._layers.length > 1) {
468
+ layers.addTo(this._map);
469
+ }
470
+ this._layers = null;
471
+ }
472
+
473
+ _addLayer(config, layer, layers) {
474
+ if (config.show) {
475
+ layer.addTo(this._map);
476
+ }
477
+ if (config.label) {
478
+ if (config.base) {
479
+ layers.addBaseLayer(layer, config.label);
480
+ } else {
481
+ layers.addOverlay(layer, config.label);
482
+ }
483
+ }
484
+ }
485
+
486
+ _fitBounds() {
487
+ if (!this.fitMarkers) {
488
+ return;
489
+ }
490
+ const bounds = L.latLngBounds();
491
+ let len = 0;
492
+ this._markerLayer.eachLayer((layer) => {
493
+ bounds.extend(layer.getLatLng());
494
+ len += 1;
495
+ });
496
+ if (len === 0) {
497
+ this._map.fitWorld();
498
+ } else if (len === 1) {
499
+ this._map.fitBounds(bounds, {maxZoom: this.zoom});
500
+ } else {
501
+ this._map.fitBounds(bounds);
502
+ }
503
+ }
504
+
505
+ _locationChanged(lat, long, zoom, setActive) {
506
+ if (this._map) {
507
+ const coords = L.latLng([lat, long]);
508
+ this._markerLayer.eachLayer((layer) => {
509
+ if (layer.getLatLng().equals(coords)) {
510
+ if (zoom && !this.noScroll) {
511
+ layer.openTooltip();
512
+ this._map.setView(coords, zoom);
513
+ } else if (this.cluster) {
514
+ this._markerLayer.zoomToShowLayer(layer, () =>
515
+ layer.openTooltip()
516
+ );
517
+ } else {
518
+ layer.openTooltip();
519
+ this._map.setView(coords, this.zoom);
520
+ }
521
+ if (setActive && this._icons && this._icons.active) {
522
+ layer.setIcon(this._icons.active);
523
+ }
524
+ } else if (this._icons && this._icons.default && layer.getIcon() !== this._icons.default) {
525
+ layer.setIcon(this._icons.default);
526
+ }
527
+ });
528
+ }
529
+ }
530
+
531
+ _hasMarker(lat, long) {
532
+ const coords = L.latLng([lat, long]);
533
+ let found = null;
534
+ this._markerLayer.eachLayer((layer) => {
535
+ if (layer instanceof L.Marker && layer.getLatLng().equals(coords)) {
536
+ found = layer;
537
+ }
538
+ });
539
+ return found;
540
+ }
541
+
542
+ _hide() {
543
+ this.disabled = true;
544
+ }
545
+ }
464
546
  customElements.define('pb-leaflet-map', PbLeafletMap);