@teipublisher/pb-components 1.30.3 → 1.32.1
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.
- package/CHANGELOG.md +40 -0
- package/css/leaflet/MarkerCluster.Default.css +60 -0
- package/css/leaflet/MarkerCluster.css +14 -0
- package/dist/demo/demos.json +3 -0
- package/dist/demo/pb-custom-form.html +61 -0
- package/dist/demo/pb-leaflet-map.html +9 -7
- package/dist/demo/pb-leaflet-map2.html +2 -2
- package/dist/demo/pb-leaflet-map3.html +15 -22
- package/dist/es-global-bridge-6abe3a88.js +5 -0
- package/dist/pb-components-bundle.js +126 -92
- package/dist/pb-elements.json +204 -2
- package/dist/pb-leaflet-map.js +4 -8
- package/lib/leaflet-src.js +14062 -0
- package/lib/leaflet.markercluster-src.js +2718 -0
- package/package.json +2 -1
- package/pb-elements.json +204 -2
- package/src/pb-components.js +1 -0
- package/src/pb-custom-form.js +49 -7
- package/src/pb-geolocation.js +48 -11
- package/src/pb-leaflet-map.js +79 -38
- package/src/pb-split-list.js +188 -0
package/src/pb-leaflet-map.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { LitElement, html, css } from 'lit-element';
|
|
2
|
-
import
|
|
2
|
+
import "@lrnwebcomponents/es-global-bridge";
|
|
3
3
|
import { pbMixin } from './pb-mixin.js';
|
|
4
4
|
import { resolveURL } from './utils.js';
|
|
5
5
|
import './pb-map-layer.js';
|
|
@@ -12,7 +12,6 @@ import './pb-map-layer.js';
|
|
|
12
12
|
* @fires pb-update-map - When received, redraws the map to fit markers passed in with the event
|
|
13
13
|
* @fires pb-update - When received, redraws the map to show markers for all pb-geolocation elements
|
|
14
14
|
* @fires pb-geolocation - When received, focuses the map on the geocoordinates passed in with the event
|
|
15
|
-
|
|
16
15
|
*/
|
|
17
16
|
export class PbLeafletMap extends pbMixin(LitElement) {
|
|
18
17
|
static get properties() {
|
|
@@ -30,6 +29,20 @@ export class PbLeafletMap extends pbMixin(LitElement) {
|
|
|
30
29
|
crs: {
|
|
31
30
|
type: String
|
|
32
31
|
},
|
|
32
|
+
/**
|
|
33
|
+
* If set, the map will automatically zoom so it can fit all the markers
|
|
34
|
+
*/
|
|
35
|
+
fitMarkers: {
|
|
36
|
+
type: Boolean,
|
|
37
|
+
attribute: 'fit-markers'
|
|
38
|
+
},
|
|
39
|
+
/**
|
|
40
|
+
* If set, combine markers into clusters if they are located too close together
|
|
41
|
+
* to display as single markers
|
|
42
|
+
*/
|
|
43
|
+
cluster: {
|
|
44
|
+
type: Boolean
|
|
45
|
+
},
|
|
33
46
|
/**
|
|
34
47
|
* If enabled, the map will not automatically scroll to the coordinates received via `pb-geolocation`
|
|
35
48
|
*/
|
|
@@ -74,6 +87,8 @@ export class PbLeafletMap extends pbMixin(LitElement) {
|
|
|
74
87
|
this.toggle = false;
|
|
75
88
|
this.noScroll = false;
|
|
76
89
|
this.disabled = true;
|
|
90
|
+
this.cluster = false;
|
|
91
|
+
this.fitMarkers = false;
|
|
77
92
|
}
|
|
78
93
|
|
|
79
94
|
connectedCallback() {
|
|
@@ -86,7 +101,8 @@ export class PbLeafletMap extends pbMixin(LitElement) {
|
|
|
86
101
|
* @param {{ detail: any[]; }} ev
|
|
87
102
|
*/
|
|
88
103
|
this.subscribeTo('pb-update-map', (ev) => {
|
|
89
|
-
|
|
104
|
+
this._markerLayer.clearLayers();
|
|
105
|
+
|
|
90
106
|
/**
|
|
91
107
|
* @param {{ latitude: any; longitude: any; label: any; }} loc
|
|
92
108
|
*/
|
|
@@ -102,14 +118,9 @@ export class PbLeafletMap extends pbMixin(LitElement) {
|
|
|
102
118
|
this.emitTo('pb-leaflet-marker-click', loc);
|
|
103
119
|
});
|
|
104
120
|
marker.bindTooltip(loc.label);
|
|
105
|
-
|
|
106
|
-
bounds.extend([loc.latitude, loc.longitude]);
|
|
121
|
+
this._markerLayer.addLayer(marker);
|
|
107
122
|
});
|
|
108
|
-
|
|
109
|
-
this._map.fitBounds(bounds);
|
|
110
|
-
} else {
|
|
111
|
-
this._map.setZoom(this.zoom);
|
|
112
|
-
}
|
|
123
|
+
this._fitBounds();
|
|
113
124
|
});
|
|
114
125
|
|
|
115
126
|
/**
|
|
@@ -118,20 +129,14 @@ export class PbLeafletMap extends pbMixin(LitElement) {
|
|
|
118
129
|
* @param {{ detail: { root: { querySelectorAll: (arg0: string) => any[]; }; }; }} ev
|
|
119
130
|
*/
|
|
120
131
|
this.subscribeTo('pb-update', (ev) => {
|
|
121
|
-
this.
|
|
122
|
-
if (layer instanceof L.Marker) {
|
|
123
|
-
layer.remove();
|
|
124
|
-
}
|
|
125
|
-
});
|
|
126
|
-
const bounds = L.latLngBounds();
|
|
132
|
+
this._markerLayer.clearLayers();
|
|
127
133
|
const locations = ev.detail.root.querySelectorAll('pb-geolocation');
|
|
128
134
|
/**
|
|
129
135
|
* @param {{ latitude: any; longitude: any; }} loc
|
|
130
136
|
*/
|
|
131
137
|
locations.forEach((loc) => {
|
|
132
138
|
const coords = L.latLng(loc.latitude, loc.longitude);
|
|
133
|
-
|
|
134
|
-
const marker = L.marker(coords).addTo(this._map);
|
|
139
|
+
const marker = L.marker(coords).addTo(this._markerLayer);
|
|
135
140
|
if (loc.label) {
|
|
136
141
|
marker.bindTooltip(loc.label);
|
|
137
142
|
}
|
|
@@ -142,12 +147,7 @@ export class PbLeafletMap extends pbMixin(LitElement) {
|
|
|
142
147
|
this.emitTo('pb-leaflet-marker-click', loc);
|
|
143
148
|
});
|
|
144
149
|
});
|
|
145
|
-
|
|
146
|
-
if (locations.length > 1) {
|
|
147
|
-
this._map.fitBounds(bounds);
|
|
148
|
-
} else {
|
|
149
|
-
this._map.fitWorld();
|
|
150
|
-
}
|
|
150
|
+
this._fitBounds();
|
|
151
151
|
});
|
|
152
152
|
|
|
153
153
|
/**
|
|
@@ -159,6 +159,9 @@ export class PbLeafletMap extends pbMixin(LitElement) {
|
|
|
159
159
|
if (ev.detail.coordinates) {
|
|
160
160
|
this.latitude = ev.detail.coordinates.latitude;
|
|
161
161
|
this.longitude = ev.detail.coordinates.longitude;
|
|
162
|
+
if (ev.detail.zoom) {
|
|
163
|
+
this.zoom = ev.detail.zoom;
|
|
164
|
+
}
|
|
162
165
|
if (!this._hasMarker(this.latitude, this.longitude)) {
|
|
163
166
|
const marker = L.marker([this.latitude, this.longitude]);
|
|
164
167
|
marker.addEventListener('click', () => {
|
|
@@ -170,7 +173,12 @@ export class PbLeafletMap extends pbMixin(LitElement) {
|
|
|
170
173
|
if (ev.detail.popup) {
|
|
171
174
|
marker.bindPopup(ev.detail.popup);
|
|
172
175
|
}
|
|
173
|
-
marker.addTo(this.
|
|
176
|
+
marker.addTo(this._markerLayer);
|
|
177
|
+
|
|
178
|
+
if (ev.detail.fitBounds) {
|
|
179
|
+
this._fitBounds();
|
|
180
|
+
}
|
|
181
|
+
|
|
174
182
|
console.log('<pb-leaflet-map> added marker');
|
|
175
183
|
} else {
|
|
176
184
|
console.log('<pb-leaflet-map> Marker already added to map');
|
|
@@ -178,7 +186,7 @@ export class PbLeafletMap extends pbMixin(LitElement) {
|
|
|
178
186
|
if (this.toggle) {
|
|
179
187
|
this.disabled = false;
|
|
180
188
|
}
|
|
181
|
-
this._locationChanged();
|
|
189
|
+
this._locationChanged(this.latitude, this.longitude, this.zoom);
|
|
182
190
|
}
|
|
183
191
|
});
|
|
184
192
|
}
|
|
@@ -187,13 +195,23 @@ export class PbLeafletMap extends pbMixin(LitElement) {
|
|
|
187
195
|
if (!this.toggle) {
|
|
188
196
|
this.disabled = false;
|
|
189
197
|
}
|
|
190
|
-
|
|
198
|
+
window.ESGlobalBridge.requestAvailability();
|
|
199
|
+
const leafletPath = resolveURL('../lib/leaflet-src.js');
|
|
200
|
+
const pluginPath = resolveURL('../lib/leaflet.markercluster-src.js');
|
|
201
|
+
window.ESGlobalBridge.instance.load("leaflet", leafletPath)
|
|
202
|
+
.then(() => window.ESGlobalBridge.instance.load("plugin", pluginPath));
|
|
203
|
+
window.addEventListener(
|
|
204
|
+
"es-bridge-plugin-loaded",
|
|
205
|
+
this._initMap.bind(this),
|
|
206
|
+
{ once: true }
|
|
207
|
+
);
|
|
191
208
|
}
|
|
192
209
|
|
|
193
210
|
render() {
|
|
194
211
|
const cssPath = resolveURL(this.cssPath);
|
|
195
212
|
return html`
|
|
196
213
|
<link rel="Stylesheet" href="${cssPath}/leaflet.css">
|
|
214
|
+
<link rel="Stylesheet" href="${cssPath}/MarkerCluster.Default.css">
|
|
197
215
|
<div id="map" style="height: 100%; width: 100%"></div>
|
|
198
216
|
`;
|
|
199
217
|
}
|
|
@@ -235,6 +253,14 @@ export class PbLeafletMap extends pbMixin(LitElement) {
|
|
|
235
253
|
crs
|
|
236
254
|
});
|
|
237
255
|
this._configureLayers();
|
|
256
|
+
|
|
257
|
+
if (this.cluster) {
|
|
258
|
+
this._markerLayer = L.markerClusterGroup();
|
|
259
|
+
} else {
|
|
260
|
+
this._markerLayer = L.layerGroup();
|
|
261
|
+
}
|
|
262
|
+
this._markerLayer.addTo(this._map);
|
|
263
|
+
|
|
238
264
|
this.signalReady();
|
|
239
265
|
|
|
240
266
|
L.control.scale().addTo(this._map);
|
|
@@ -309,27 +335,42 @@ export class PbLeafletMap extends pbMixin(LitElement) {
|
|
|
309
335
|
}
|
|
310
336
|
}
|
|
311
337
|
|
|
312
|
-
|
|
338
|
+
_fitBounds() {
|
|
339
|
+
if (!this.fitMarkers) {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
const bounds = L.latLngBounds();
|
|
343
|
+
let len = 0;
|
|
344
|
+
this._markerLayer.eachLayer((layer) => {
|
|
345
|
+
bounds.extend(layer.getLatLng());
|
|
346
|
+
len += 1;
|
|
347
|
+
});
|
|
348
|
+
if (len === 0) {
|
|
349
|
+
this._map.fitWorld();
|
|
350
|
+
} else if (len === 1) {
|
|
351
|
+
this._map.fitBounds(bounds, {maxZoom: this.zoom});
|
|
352
|
+
} else {
|
|
353
|
+
this._map.fitBounds(bounds);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
_locationChanged(lat, long, zoom) {
|
|
313
358
|
if (this._map) {
|
|
314
|
-
const coords = L.latLng([
|
|
315
|
-
this.
|
|
316
|
-
if (layer
|
|
317
|
-
|
|
318
|
-
layer.openTooltip();
|
|
319
|
-
} else {
|
|
320
|
-
layer.closeTooltip();
|
|
321
|
-
}
|
|
359
|
+
const coords = L.latLng([lat, long]);
|
|
360
|
+
this._markerLayer.eachLayer((layer) => {
|
|
361
|
+
if (layer.getLatLng().equals(coords)) {
|
|
362
|
+
layer.openTooltip();
|
|
322
363
|
}
|
|
323
364
|
});
|
|
324
365
|
if (!this.noScroll)
|
|
325
|
-
this._map.setView(coords,
|
|
366
|
+
this._map.setView(coords, zoom);
|
|
326
367
|
}
|
|
327
368
|
}
|
|
328
369
|
|
|
329
370
|
_hasMarker(lat, long) {
|
|
330
371
|
const coords = L.latLng([lat, long]);
|
|
331
372
|
let found = false;
|
|
332
|
-
this.
|
|
373
|
+
this._markerLayer.eachLayer((layer) => {
|
|
333
374
|
if (layer instanceof L.Marker && layer.getLatLng().equals(coords)) {
|
|
334
375
|
found = true;
|
|
335
376
|
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { LitElement, html, css } from 'lit-element';
|
|
2
|
+
import { unsafeHTML } from 'lit-html/directives/unsafe-html.js';
|
|
3
|
+
import { pbMixin } from './pb-mixin.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Implements a list which is split into different categories
|
|
7
|
+
* (e.g. letters of the alphabet, countries ...).
|
|
8
|
+
* Only one category is shown at a time unless the server reports
|
|
9
|
+
* no categories (e.g. if the number of items to display goes below
|
|
10
|
+
* a defined threshold).
|
|
11
|
+
*
|
|
12
|
+
* The server-side API endpoint should return a JSON object with two
|
|
13
|
+
* properties:
|
|
14
|
+
*
|
|
15
|
+
* + `categories`: an array of category descriptions: each item should
|
|
16
|
+
* be an object with two properties: `category` - containing the name of the category
|
|
17
|
+
* and `count` - containing a count of items available under this category.
|
|
18
|
+
* + `items`: an array with the items to be shown for the currently selected
|
|
19
|
+
* category. Those may contain HTML markup.
|
|
20
|
+
*
|
|
21
|
+
* @cssprop --pb-categorized-list-columns - the number of columns to display (default: 2)
|
|
22
|
+
* @fires pb-submit - when received, submit a request to the server and refresh
|
|
23
|
+
* @fires pb-start-update - sent before the element sends the request to the server
|
|
24
|
+
* @fires pb-end-update - sent after new content has been received
|
|
25
|
+
*/
|
|
26
|
+
export class PbSplitList extends pbMixin(LitElement) {
|
|
27
|
+
static get properties() {
|
|
28
|
+
return {
|
|
29
|
+
/**
|
|
30
|
+
* Server-side API endpoint to retrieve items from
|
|
31
|
+
*/
|
|
32
|
+
url: {
|
|
33
|
+
type: String
|
|
34
|
+
},
|
|
35
|
+
/**
|
|
36
|
+
* The initially selected category
|
|
37
|
+
*/
|
|
38
|
+
selected: {
|
|
39
|
+
type: String
|
|
40
|
+
},
|
|
41
|
+
/**
|
|
42
|
+
* A CSS selector pointing to one or more `pb-custom-form`
|
|
43
|
+
* instances. The element will collect additional parameters
|
|
44
|
+
* from those forms and includes them in the request to the server
|
|
45
|
+
*/
|
|
46
|
+
subforms: {
|
|
47
|
+
type: String
|
|
48
|
+
},
|
|
49
|
+
_categories: {
|
|
50
|
+
type: Array
|
|
51
|
+
},
|
|
52
|
+
...super.properties
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
constructor() {
|
|
57
|
+
super();
|
|
58
|
+
this._categories = [];
|
|
59
|
+
this._params = {};
|
|
60
|
+
this.selected = null;
|
|
61
|
+
this.subforms = null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
connectedCallback() {
|
|
65
|
+
super.connectedCallback();
|
|
66
|
+
|
|
67
|
+
this.selected = this.getParameter('category', this.selected);
|
|
68
|
+
|
|
69
|
+
window.addEventListener('popstate', (ev) => {
|
|
70
|
+
console.log('<pb-split-list> popstate: %o', ev);
|
|
71
|
+
this.selected = ev.state.category;
|
|
72
|
+
this.submit();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
this.subscribeTo('pb-submit', this.load.bind(this));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
firstUpdated() {
|
|
79
|
+
super.firstUpdated();
|
|
80
|
+
|
|
81
|
+
PbSplitList.waitOnce('pb-page-ready', () => {
|
|
82
|
+
this.load();
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
submit() {
|
|
87
|
+
this.load();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
load() {
|
|
91
|
+
const formParams = this._paramsFromSubforms({ category: this.selected });
|
|
92
|
+
this.setParameters(formParams);
|
|
93
|
+
this.pushHistory('pb-split-list', formParams);
|
|
94
|
+
|
|
95
|
+
const params = new URLSearchParams(formParams);
|
|
96
|
+
|
|
97
|
+
const url = `${this.toAbsoluteURL(this.url)}?${params.toString()}`;
|
|
98
|
+
console.log(`<pb-split-list> Fetching from URL: ${url}`);
|
|
99
|
+
|
|
100
|
+
this.emitTo('pb-start-update');
|
|
101
|
+
|
|
102
|
+
fetch(url)
|
|
103
|
+
.then((response) => {
|
|
104
|
+
if (response.ok) {
|
|
105
|
+
return response.json();
|
|
106
|
+
}
|
|
107
|
+
return Promise.reject(response.status);
|
|
108
|
+
})
|
|
109
|
+
.then((json) => {
|
|
110
|
+
this._categories = json.categories;
|
|
111
|
+
this.innerHTML = json.items.join('');
|
|
112
|
+
this.emitTo('pb-end-update');
|
|
113
|
+
})
|
|
114
|
+
.catch((error) => {
|
|
115
|
+
console.error(`<pb-split-list> Error caught: ${error}`);
|
|
116
|
+
this.emitTo('pb-end-update');
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
_selectCategory(ev, category) {
|
|
121
|
+
ev.preventDefault();
|
|
122
|
+
this.selected = category;
|
|
123
|
+
this.load();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
_paramsFromSubforms(params) {
|
|
127
|
+
if (this.subforms) {
|
|
128
|
+
document.querySelectorAll(this.subforms).forEach((form) => {
|
|
129
|
+
if (form.serializeForm) {
|
|
130
|
+
Object.assign(params, form.serializeForm());
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
return params;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
render() {
|
|
138
|
+
return html`
|
|
139
|
+
<header>
|
|
140
|
+
${
|
|
141
|
+
this._categories.map((cat) =>
|
|
142
|
+
html`
|
|
143
|
+
<a part="${this.selected === cat.category ? 'active-category' : 'category'}" href="#${cat.category}" title="${cat.count}" class="${this.selected === cat.category ? 'active' : ''}"
|
|
144
|
+
@click="${(ev) => this._selectCategory(ev, cat.category)}">
|
|
145
|
+
${cat.category}
|
|
146
|
+
</a>
|
|
147
|
+
`
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
</header>
|
|
151
|
+
<div id="items" part="items"><slot></slot></div>
|
|
152
|
+
`;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
static get styles() {
|
|
156
|
+
return css`
|
|
157
|
+
:host {
|
|
158
|
+
display: block;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
header {
|
|
162
|
+
display: flex;
|
|
163
|
+
flex-wrap: wrap;
|
|
164
|
+
column-gap: 10px;
|
|
165
|
+
width: 100%;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
#items {
|
|
169
|
+
display: grid;
|
|
170
|
+
grid-template-columns: repeat(var(--pb-categorized-list-columns, 2), auto);
|
|
171
|
+
grid-auto-rows: 1fr;
|
|
172
|
+
column-gap: 10px;
|
|
173
|
+
width: 100%;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
[part=category], #items a {
|
|
177
|
+
text-decoration: none;
|
|
178
|
+
color: var(--pb-link-color);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
[part=active-category] {
|
|
182
|
+
text-decoration: none;
|
|
183
|
+
color: var(--pb-highlight-color);
|
|
184
|
+
}
|
|
185
|
+
`;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
customElements.define('pb-split-list', PbSplitList);
|