ol-layer-control 0.1.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.
@@ -0,0 +1,451 @@
1
+ /**
2
+ * All user-visible strings. Pass a (partial) object with the same keys as
3
+ * the `i18n` option to translate the UI.
4
+ *
5
+ * @typedef {Object} LayerControlI18n
6
+ * @property {string} buttonTitle Tooltip / accessible name of the map button.
7
+ * @property {string} panelTitle Heading of the panel.
8
+ * @property {string} closeTitle Tooltip of the close button.
9
+ * @property {string} resizeTitle Tooltip of the resize handle.
10
+ * @property {string} collapseTitle Tooltip of the fold button when a group is expanded.
11
+ * @property {string} expandTitle Tooltip of the fold button when a group is collapsed.
12
+ * @property {string} exclusiveHint Tooltip of a group title when it can be right-clicked
13
+ * to switch between exclusive and non-exclusive.
14
+ * @property {string} outOfRangeHint Tooltip of a layer that is not shown at the
15
+ * current zoom level (outside its min/max resolution or zoom).
16
+ * @property {string} groupOffHint Tooltip of a layer inside a switched-off group.
17
+ * `{group}` is replaced by the title of that group.
18
+ * @property {string} opacityTitle Tooltip of the opacity button and label of the slider.
19
+ * @property {string} searchToggleTitle Tooltip of the panel title (click shows/hides the search box).
20
+ * @property {string} searchPlaceholder Placeholder text of the search box.
21
+ * @property {string} searchClearTitle Tooltip of the button that clears the search box.
22
+ * @property {string} searchNoResults Message shown when no layer matches the search.
23
+ */
24
+ /** @type {LayerControlI18n} */
25
+ export const DEFAULT_I18N: LayerControlI18n;
26
+ /**
27
+ * Where the map button sits on the map. Each position leaves room for the
28
+ * OpenLayers default control that normally occupies that corner:
29
+ *
30
+ * 'top-left' below the Zoom buttons
31
+ * 'bottom-left' in the bottom-left corner
32
+ * 'top-right' in the top-right corner (shared with the Rotate button,
33
+ * which only shows when the map is rotated)
34
+ * 'right' top-right, below the Rotate button
35
+ * 'bottom-right' above the Attribution button
36
+ *
37
+ * @typedef {'top-left'|'bottom-left'|'top-right'|'right'|'bottom-right'} ButtonPosition
38
+ */
39
+ /** @type {ReadonlyArray<ButtonPosition>} */
40
+ export const BUTTON_POSITIONS: ReadonlyArray<ButtonPosition>;
41
+ /**
42
+ * Layer control: a map button plus a docked, resizable side panel.
43
+ *
44
+ * Observable properties (use `control.on('change:open', ...)` etc.):
45
+ * - `open` boolean, whether the panel is shown
46
+ * - `panelWidth` number, current panel width in pixels
47
+ *
48
+ * @extends {Control}
49
+ */
50
+ export default class LayerControl extends Control {
51
+ /**
52
+ * @param {LayerControlOptions} [options]
53
+ */
54
+ constructor(options?: LayerControlOptions);
55
+ /** @private @type {LayerControlI18n} */
56
+ private i18n_;
57
+ /** @private @type {'left'|'right'} */
58
+ private side_;
59
+ /** @private @type {ButtonPosition} */
60
+ private defaultButtonPosition_;
61
+ /** @private */
62
+ private defaultPanelWidth_;
63
+ /** @private */
64
+ private minPanelWidth_;
65
+ /** @private */
66
+ private maxPanelWidth_;
67
+ /** @private */
68
+ private resizable_;
69
+ /** @private */
70
+ private reverse_;
71
+ /** @private */
72
+ private exclusiveToggle_;
73
+ /** @private */
74
+ private opacitySlider_;
75
+ /** @private @type {HTMLElement|null} */
76
+ private panelTarget_;
77
+ /** @private @type {HTMLElement|null} The map's target element we shrink. */
78
+ private hostElement_;
79
+ /** @private @type {{width: string, marginLeft: string}|null} */
80
+ private hostSavedStyle_;
81
+ /** @private @type {Array<import('ol/events.js').EventsKey>} */
82
+ private mapListenerKeys_;
83
+ /** @private @type {Array<import('ol/events.js').EventsKey>} */
84
+ private layerListenerKeys_;
85
+ /** @private */
86
+ private rebuildScheduled_;
87
+ /** @private */
88
+ private layoutScheduled_;
89
+ /** @private @type {Array<HTMLButtonElement>} */
90
+ private foldButtons_;
91
+ /** @private @type {Array<{layer: import('ol/layer/Base.js').default, parent: LayerGroup|null, item: HTMLElement, label: HTMLElement}>} */
92
+ private entries_;
93
+ /** @private @type {Map<import('ol/layer/Base.js').default, LayerGroup|null>} */
94
+ private parents_;
95
+ /** @private @type {Array<import('ol/events.js').EventsKey>} */
96
+ private viewListenerKeys_;
97
+ /** @private @type {HTMLElement|null} */
98
+ private resizer_;
99
+ /** @private @type {HTMLButtonElement} */
100
+ private button_;
101
+ /** @private @type {HTMLElement} */
102
+ private panel_;
103
+ /** @private @type {HTMLButtonElement} */
104
+ private titleElement_;
105
+ /** @private @type {HTMLButtonElement} */
106
+ private closeButton_;
107
+ /** @private @type {HTMLElement} */
108
+ private searchRow_;
109
+ /** @private @type {HTMLInputElement} */
110
+ private searchInput_;
111
+ /** @private @type {HTMLButtonElement} */
112
+ private searchClear_;
113
+ /** @private @type {HTMLElement} */
114
+ private rootList_;
115
+ /** @private @type {HTMLElement} */
116
+ private noResults_;
117
+ /** Show the panel. */
118
+ open(): void;
119
+ /** Hide the panel. */
120
+ close(): void;
121
+ /** Show the panel if hidden, hide it if shown. */
122
+ toggle(): void;
123
+ /** @return {boolean} Whether the panel is shown. */
124
+ isOpen(): boolean;
125
+ /**
126
+ * @param {number} width Panel width in pixels (clamped to min/max).
127
+ */
128
+ setPanelWidth(width: number): void;
129
+ /** @return {number} Current panel width in pixels. */
130
+ getPanelWidth(): number;
131
+ /** @return {HTMLElement} The panel element. */
132
+ getPanelElement(): HTMLElement;
133
+ /**
134
+ * Move the map button. An unknown value falls back to the default for the
135
+ * panel's side.
136
+ * @param {ButtonPosition} position
137
+ */
138
+ setButtonPosition(position: ButtonPosition): void;
139
+ /** @return {ButtonPosition} Where the map button sits. */
140
+ getButtonPosition(): ButtonPosition;
141
+ /**
142
+ * Show or hide the search box. Hiding it clears the filter.
143
+ * @param {boolean} [visible=true]
144
+ */
145
+ showSearch(visible?: boolean): void;
146
+ /** Show the search box if hidden, hide it if shown. */
147
+ toggleSearch(): void;
148
+ /** @return {boolean} Whether the search box is shown. */
149
+ isSearchVisible(): boolean;
150
+ /**
151
+ * Filter the layer list by (part of) a title, case-insensitive. Groups
152
+ * stay listed when their title or any of their layers match.
153
+ * @param {string} query Empty string shows everything.
154
+ */
155
+ setFilter(query: string): void;
156
+ /** @return {string} The current filter text. */
157
+ getFilter(): string;
158
+ /**
159
+ * Replace (part of) the UI strings.
160
+ * @param {Partial<LayerControlI18n>} i18n
161
+ */
162
+ setI18n(i18n: Partial<LayerControlI18n>): void;
163
+ /** @return {LayerControlI18n} The strings in use. */
164
+ getI18n(): LayerControlI18n;
165
+ /**
166
+ * Rebuild the layer list from the map. Normally not needed: the control
167
+ * follows layer additions, removals and visibility changes by itself.
168
+ */
169
+ refresh(): void;
170
+ /**
171
+ * Make a layer group exclusive (children as radio buttons, at most one
172
+ * visible) or non-exclusive (checkboxes). When switching to exclusive and
173
+ * several children are visible, only the top-most one stays visible.
174
+ *
175
+ * Basemap groups (children with `type: 'base'`) are always exclusive.
176
+ *
177
+ * @param {LayerGroup} group
178
+ * @param {boolean} exclusive
179
+ */
180
+ setExclusive(group: LayerGroup, exclusive: boolean): void;
181
+ /** @private Follow the (possibly new) view's resolution. */
182
+ private observeView_;
183
+ /**
184
+ * Dim entries that are outside their resolution/zoom range and explain,
185
+ * in a tooltip, why an entry is not shown on the map (out of range and/or
186
+ * inside a switched-off group).
187
+ * @private
188
+ */
189
+ private updateStates_;
190
+ /** @private */
191
+ private attachPanel_;
192
+ /** @private */
193
+ private restoreHost_;
194
+ /** @private */
195
+ private detach_;
196
+ /** @private */
197
+ private scheduleLayout_;
198
+ /** @private */
199
+ private applyLayout_;
200
+ /**
201
+ * @private
202
+ * @param {number} width
203
+ * @return {number}
204
+ */
205
+ private clampWidth_;
206
+ /**
207
+ * @private
208
+ * @param {HTMLElement} handle
209
+ */
210
+ private initResizer_;
211
+ /** @private */
212
+ private applyTexts_;
213
+ /**
214
+ * @private
215
+ * @param {string|undefined} position
216
+ * @return {ButtonPosition}
217
+ */
218
+ private normalizeButtonPosition_;
219
+ /** @private */
220
+ private applyButtonPosition_;
221
+ /** @private */
222
+ private applySearchVisibility_;
223
+ /** @private Show only entries that match the search text. */
224
+ private applyFilter_;
225
+ /** @private */
226
+ private clearLayerListeners_;
227
+ /** @private */
228
+ private scheduleRebuild_;
229
+ /** @private */
230
+ private rebuild_;
231
+ /**
232
+ * @private
233
+ * @param {import('ol/layer/Base.js').default} layer
234
+ * @param {LayerGroup|null} parent
235
+ * @param {HTMLElement} item
236
+ * @param {HTMLElement} label
237
+ */
238
+ private registerEntry_;
239
+ /**
240
+ * @private
241
+ * @param {import('ol/Collection.js').default<import('ol/layer/Base.js').default>} collection
242
+ * @param {HTMLElement} list The <ul> to render into.
243
+ * @param {string|null} radioName Non-null when the parent group is exclusive.
244
+ * @param {LayerGroup|null} parent The group that owns the collection.
245
+ */
246
+ private renderCollection_;
247
+ /**
248
+ * The layers of a collection that appear in the panel, in panel order.
249
+ * @private
250
+ * @param {import('ol/Collection.js').default<import('ol/layer/Base.js').default>} collection
251
+ * @return {Array<import('ol/layer/Base.js').default>}
252
+ */
253
+ private listedLayers_;
254
+ /**
255
+ * @private
256
+ * @param {LayerGroup} group
257
+ * @return {Array<import('ol/layer/Base.js').default>}
258
+ */
259
+ private listedChildren_;
260
+ /**
261
+ * @private
262
+ * @param {import('ol/layer/Base.js').default} layer
263
+ * @param {Array<import('ol/layer/Base.js').default>} siblings Rendered siblings, incl. the layer itself.
264
+ * @param {HTMLElement} list
265
+ * @param {string|null} radioName
266
+ * @param {LayerGroup|null} parent
267
+ */
268
+ private renderLayer_;
269
+ /**
270
+ * Adds an opacity button to the row and a (collapsed) slider row to the item.
271
+ * @private
272
+ * @param {import('ol/layer/Base.js').default} layer
273
+ * @param {HTMLElement} item The <li>.
274
+ * @param {HTMLElement} row The row holding the input and label.
275
+ */
276
+ private addOpacitySlider_;
277
+ /**
278
+ * @private
279
+ * @param {LayerGroup} group
280
+ * @param {Array<import('ol/layer/Base.js').default>} siblings
281
+ * @param {HTMLElement} list
282
+ * @param {string|null} radioName
283
+ * @param {LayerGroup|null} parent
284
+ */
285
+ private renderGroup_;
286
+ /**
287
+ * @private
288
+ * @param {import('ol/layer/Base.js').default} layer
289
+ * @param {Array<import('ol/layer/Base.js').default>} siblings
290
+ * @param {string|null} radioName
291
+ * @return {HTMLInputElement}
292
+ */
293
+ private createInput_;
294
+ /**
295
+ * @private
296
+ * @param {import('ol/layer/Base.js').default} layer
297
+ * @param {HTMLInputElement|null} input
298
+ * @return {HTMLElement}
299
+ */
300
+ private createLabel_;
301
+ /**
302
+ * @private
303
+ * @param {HTMLButtonElement} button
304
+ */
305
+ private updateFoldButton_;
306
+ }
307
+ /**
308
+ * All user-visible strings. Pass a (partial) object with the same keys as
309
+ * the `i18n` option to translate the UI.
310
+ */
311
+ export type LayerControlI18n = {
312
+ /**
313
+ * Tooltip / accessible name of the map button.
314
+ */
315
+ buttonTitle: string;
316
+ /**
317
+ * Heading of the panel.
318
+ */
319
+ panelTitle: string;
320
+ /**
321
+ * Tooltip of the close button.
322
+ */
323
+ closeTitle: string;
324
+ /**
325
+ * Tooltip of the resize handle.
326
+ */
327
+ resizeTitle: string;
328
+ /**
329
+ * Tooltip of the fold button when a group is expanded.
330
+ */
331
+ collapseTitle: string;
332
+ /**
333
+ * Tooltip of the fold button when a group is collapsed.
334
+ */
335
+ expandTitle: string;
336
+ /**
337
+ * Tooltip of a group title when it can be right-clicked
338
+ * to switch between exclusive and non-exclusive.
339
+ */
340
+ exclusiveHint: string;
341
+ /**
342
+ * Tooltip of a layer that is not shown at the
343
+ * current zoom level (outside its min/max resolution or zoom).
344
+ */
345
+ outOfRangeHint: string;
346
+ /**
347
+ * Tooltip of a layer inside a switched-off group.
348
+ * `{group}` is replaced by the title of that group.
349
+ */
350
+ groupOffHint: string;
351
+ /**
352
+ * Tooltip of the opacity button and label of the slider.
353
+ */
354
+ opacityTitle: string;
355
+ /**
356
+ * Tooltip of the panel title (click shows/hides the search box).
357
+ */
358
+ searchToggleTitle: string;
359
+ /**
360
+ * Placeholder text of the search box.
361
+ */
362
+ searchPlaceholder: string;
363
+ /**
364
+ * Tooltip of the button that clears the search box.
365
+ */
366
+ searchClearTitle: string;
367
+ /**
368
+ * Message shown when no layer matches the search.
369
+ */
370
+ searchNoResults: string;
371
+ };
372
+ export type LayerControlOptions = {
373
+ /**
374
+ * Where to render the map button
375
+ * (standard OpenLayers control option). Default: the map's control container.
376
+ */
377
+ target?: HTMLElement | string;
378
+ /**
379
+ * Element (or element id) to
380
+ * render the panel into. When set, the control leaves the map element
381
+ * alone and the host page is responsible for the layout.
382
+ */
383
+ panelTarget?: HTMLElement | string;
384
+ /**
385
+ * Side of the map the panel docks to.
386
+ */
387
+ side?: "left" | "right";
388
+ /**
389
+ * Where the map button sits. Defaults to
390
+ * `'top-left'` when `side` is `'left'` and `'right'` otherwise.
391
+ */
392
+ buttonPosition?: ButtonPosition;
393
+ /**
394
+ * Initial panel width in pixels.
395
+ */
396
+ panelWidth?: number;
397
+ /**
398
+ * Smallest width the user can resize to.
399
+ */
400
+ minPanelWidth?: number;
401
+ /**
402
+ * Largest width the user can resize to.
403
+ */
404
+ maxPanelWidth?: number;
405
+ /**
406
+ * Show a drag handle to resize the panel.
407
+ */
408
+ resizable?: boolean;
409
+ /**
410
+ * Start with the panel open.
411
+ */
412
+ open?: boolean;
413
+ /**
414
+ * List layers top-most first (rendering order reversed).
415
+ */
416
+ reverse?: boolean;
417
+ /**
418
+ * Let the user switch a group between
419
+ * exclusive (radio buttons) and non-exclusive (checkboxes) by right-clicking
420
+ * its title. Basemap groups are always exclusive.
421
+ */
422
+ exclusiveToggle?: boolean;
423
+ /**
424
+ * Give every layer a button that reveals
425
+ * an opacity slider.
426
+ */
427
+ opacitySlider?: boolean;
428
+ /**
429
+ * Show the search box from the start. The user
430
+ * can always show or hide it by clicking the panel title.
431
+ */
432
+ search?: boolean;
433
+ /**
434
+ * Translations, see {@link DEFAULT_I18N}.
435
+ */
436
+ i18n?: Partial<LayerControlI18n>;
437
+ };
438
+ /**
439
+ * Where the map button sits on the map. Each position leaves room for the
440
+ * OpenLayers default control that normally occupies that corner:
441
+ *
442
+ * 'top-left' below the Zoom buttons
443
+ * 'bottom-left' in the bottom-left corner
444
+ * 'top-right' in the top-right corner (shared with the Rotate button,
445
+ * which only shows when the map is rotated)
446
+ * 'right' top-right, below the Rotate button
447
+ * 'bottom-right' above the Attribution button
448
+ */
449
+ export type ButtonPosition = "top-left" | "bottom-left" | "top-right" | "right" | "bottom-right";
450
+ import Control from 'ol/control/Control.js';
451
+ import LayerGroup from 'ol/layer/Group.js';