maplibre-gl-layer-control 0.15.0 → 0.16.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.
- package/README.md +26 -0
- package/dist/index.cjs +252 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +252 -0
- package/dist/index.mjs.map +1 -1
- package/dist/maplibre-gl-layer-control.css +75 -0
- package/dist/types/index.d.ts +94 -0
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -728,6 +728,10 @@ class LayerControl {
|
|
|
728
728
|
__publicField(this, "onLayerRename");
|
|
729
729
|
__publicField(this, "onLayerReorder");
|
|
730
730
|
__publicField(this, "onLayerRemove");
|
|
731
|
+
// Background-layer visibility presets
|
|
732
|
+
__publicField(this, "enableBackgroundPresets");
|
|
733
|
+
__publicField(this, "backgroundPresetStorageKey");
|
|
734
|
+
__publicField(this, "onBackgroundPresetsChange");
|
|
731
735
|
this.minPanelWidth = options.panelMinWidth || 240;
|
|
732
736
|
this.maxPanelWidth = options.panelMaxWidth || 420;
|
|
733
737
|
this.initialPanelWidth = options.panelWidth || 350;
|
|
@@ -744,6 +748,9 @@ class LayerControl {
|
|
|
744
748
|
this.onLayerRename = options.onLayerRename;
|
|
745
749
|
this.onLayerReorder = options.onLayerReorder;
|
|
746
750
|
this.onLayerRemove = options.onLayerRemove;
|
|
751
|
+
this.enableBackgroundPresets = options.enableBackgroundPresets !== false;
|
|
752
|
+
this.backgroundPresetStorageKey = options.backgroundPresetStorageKey || "maplibre-layer-control:background-presets";
|
|
753
|
+
this.onBackgroundPresetsChange = options.onBackgroundPresetsChange;
|
|
747
754
|
this.initialLayerStates = options.layerStates || {};
|
|
748
755
|
this.state = {
|
|
749
756
|
collapsed: options.collapsed !== false,
|
|
@@ -2232,8 +2239,112 @@ class LayerControl {
|
|
|
2232
2239
|
panel.appendChild(actionsRow);
|
|
2233
2240
|
panel.appendChild(filterRow);
|
|
2234
2241
|
panel.appendChild(layerList);
|
|
2242
|
+
if (this.enableBackgroundPresets) {
|
|
2243
|
+
panel.appendChild(this.createBackgroundPresetsRow());
|
|
2244
|
+
}
|
|
2235
2245
|
return panel;
|
|
2236
2246
|
}
|
|
2247
|
+
/**
|
|
2248
|
+
* Build the "Saved configurations" controls: a dropdown of saved presets with
|
|
2249
|
+
* Apply/Delete actions, plus a name field and Save button to capture the
|
|
2250
|
+
* current basemap element visibility as a reusable, persisted preset.
|
|
2251
|
+
*/
|
|
2252
|
+
createBackgroundPresetsRow() {
|
|
2253
|
+
const section = document.createElement("div");
|
|
2254
|
+
section.className = "background-legend-presets";
|
|
2255
|
+
const label = document.createElement("div");
|
|
2256
|
+
label.className = "background-legend-presets-label";
|
|
2257
|
+
label.textContent = "Saved configurations";
|
|
2258
|
+
section.appendChild(label);
|
|
2259
|
+
const selectRow = document.createElement("div");
|
|
2260
|
+
selectRow.className = "background-legend-presets-row";
|
|
2261
|
+
const select = document.createElement("select");
|
|
2262
|
+
select.className = "background-legend-presets-select";
|
|
2263
|
+
select.title = "Saved configurations";
|
|
2264
|
+
const applyBtn = document.createElement("button");
|
|
2265
|
+
applyBtn.className = "background-legend-action-btn";
|
|
2266
|
+
applyBtn.textContent = "Apply";
|
|
2267
|
+
applyBtn.title = "Apply the selected configuration";
|
|
2268
|
+
const deleteBtn = document.createElement("button");
|
|
2269
|
+
deleteBtn.className = "background-legend-action-btn";
|
|
2270
|
+
deleteBtn.textContent = "Delete";
|
|
2271
|
+
deleteBtn.title = "Delete the selected configuration";
|
|
2272
|
+
const refreshSelect = (selected) => {
|
|
2273
|
+
const presets = this.getBackgroundPresets();
|
|
2274
|
+
const names = Object.keys(presets).sort(
|
|
2275
|
+
(a, b) => a.localeCompare(b, void 0, { sensitivity: "base" })
|
|
2276
|
+
);
|
|
2277
|
+
select.innerHTML = "";
|
|
2278
|
+
const placeholder = document.createElement("option");
|
|
2279
|
+
placeholder.value = "";
|
|
2280
|
+
placeholder.textContent = names.length ? "Select a configuration…" : "No saved configurations";
|
|
2281
|
+
placeholder.disabled = names.length > 0;
|
|
2282
|
+
select.appendChild(placeholder);
|
|
2283
|
+
names.forEach((name) => {
|
|
2284
|
+
const option = document.createElement("option");
|
|
2285
|
+
option.value = name;
|
|
2286
|
+
option.textContent = name;
|
|
2287
|
+
select.appendChild(option);
|
|
2288
|
+
});
|
|
2289
|
+
select.value = selected && names.includes(selected) ? selected : "";
|
|
2290
|
+
const hasSelection = select.value !== "";
|
|
2291
|
+
applyBtn.disabled = !hasSelection;
|
|
2292
|
+
deleteBtn.disabled = !hasSelection;
|
|
2293
|
+
};
|
|
2294
|
+
select.addEventListener("change", () => {
|
|
2295
|
+
const hasSelection = select.value !== "";
|
|
2296
|
+
applyBtn.disabled = !hasSelection;
|
|
2297
|
+
deleteBtn.disabled = !hasSelection;
|
|
2298
|
+
});
|
|
2299
|
+
applyBtn.addEventListener("click", (e) => {
|
|
2300
|
+
e.stopPropagation();
|
|
2301
|
+
if (select.value) this.applyBackgroundPreset(select.value);
|
|
2302
|
+
});
|
|
2303
|
+
deleteBtn.addEventListener("click", (e) => {
|
|
2304
|
+
e.stopPropagation();
|
|
2305
|
+
if (select.value) {
|
|
2306
|
+
this.deleteBackgroundPreset(select.value);
|
|
2307
|
+
refreshSelect();
|
|
2308
|
+
}
|
|
2309
|
+
});
|
|
2310
|
+
selectRow.appendChild(select);
|
|
2311
|
+
selectRow.appendChild(applyBtn);
|
|
2312
|
+
selectRow.appendChild(deleteBtn);
|
|
2313
|
+
const saveRow = document.createElement("div");
|
|
2314
|
+
saveRow.className = "background-legend-presets-row";
|
|
2315
|
+
const nameInput = document.createElement("input");
|
|
2316
|
+
nameInput.type = "text";
|
|
2317
|
+
nameInput.className = "background-legend-presets-input";
|
|
2318
|
+
nameInput.placeholder = "Configuration name…";
|
|
2319
|
+
nameInput.maxLength = 60;
|
|
2320
|
+
const saveBtn = document.createElement("button");
|
|
2321
|
+
saveBtn.className = "background-legend-action-btn";
|
|
2322
|
+
saveBtn.textContent = "Save";
|
|
2323
|
+
saveBtn.title = "Save the current visibility as a configuration";
|
|
2324
|
+
const commitSave = () => {
|
|
2325
|
+
const name = nameInput.value.trim();
|
|
2326
|
+
if (!name) return;
|
|
2327
|
+
this.saveBackgroundPreset(name);
|
|
2328
|
+
nameInput.value = "";
|
|
2329
|
+
refreshSelect(name);
|
|
2330
|
+
};
|
|
2331
|
+
saveBtn.addEventListener("click", (e) => {
|
|
2332
|
+
e.stopPropagation();
|
|
2333
|
+
commitSave();
|
|
2334
|
+
});
|
|
2335
|
+
nameInput.addEventListener("keydown", (e) => {
|
|
2336
|
+
if (e.key === "Enter") {
|
|
2337
|
+
e.preventDefault();
|
|
2338
|
+
commitSave();
|
|
2339
|
+
}
|
|
2340
|
+
});
|
|
2341
|
+
saveRow.appendChild(nameInput);
|
|
2342
|
+
saveRow.appendChild(saveBtn);
|
|
2343
|
+
section.appendChild(selectRow);
|
|
2344
|
+
section.appendChild(saveRow);
|
|
2345
|
+
refreshSelect();
|
|
2346
|
+
return section;
|
|
2347
|
+
}
|
|
2237
2348
|
/**
|
|
2238
2349
|
* Check if a layer is currently rendered in the map viewport
|
|
2239
2350
|
*/
|
|
@@ -2381,6 +2492,147 @@ class LayerControl {
|
|
|
2381
2492
|
this.state.layerStates["Background"].visible = anyVisible;
|
|
2382
2493
|
}
|
|
2383
2494
|
}
|
|
2495
|
+
/**
|
|
2496
|
+
* Return the IDs of the background (basemap) style layers the control can
|
|
2497
|
+
* toggle, honoring the drawn-layer and user-defined exclusion filters but not
|
|
2498
|
+
* the transient "only rendered" view filter.
|
|
2499
|
+
*/
|
|
2500
|
+
getControllableBackgroundLayerIds() {
|
|
2501
|
+
const styleLayers = this.map.getStyle().layers || [];
|
|
2502
|
+
return styleLayers.filter((layer) => {
|
|
2503
|
+
if (this.isUserAddedLayer(layer.id)) return false;
|
|
2504
|
+
if (this.excludeDrawnLayers && this.isDrawnLayer(layer.id)) return false;
|
|
2505
|
+
if (this.isExcludedByPattern(layer.id)) return false;
|
|
2506
|
+
return true;
|
|
2507
|
+
}).map((layer) => layer.id);
|
|
2508
|
+
}
|
|
2509
|
+
/**
|
|
2510
|
+
* Get the current visibility of every controllable background (basemap) layer.
|
|
2511
|
+
*
|
|
2512
|
+
* @returns A map of style-layer ID to whether it is currently visible.
|
|
2513
|
+
*/
|
|
2514
|
+
getBackgroundLayerVisibility() {
|
|
2515
|
+
const visibility = {};
|
|
2516
|
+
for (const layerId of this.getControllableBackgroundLayerIds()) {
|
|
2517
|
+
const value = this.map.getLayoutProperty(layerId, "visibility");
|
|
2518
|
+
visibility[layerId] = value !== "none";
|
|
2519
|
+
}
|
|
2520
|
+
return visibility;
|
|
2521
|
+
}
|
|
2522
|
+
/**
|
|
2523
|
+
* Apply a saved background-layer visibility configuration to the map. Only
|
|
2524
|
+
* layers that currently exist in the style are affected, so a configuration
|
|
2525
|
+
* captured on one basemap degrades gracefully when applied to another.
|
|
2526
|
+
*
|
|
2527
|
+
* @param visibility - Map of style-layer ID to desired visibility.
|
|
2528
|
+
*/
|
|
2529
|
+
applyBackgroundLayerVisibility(visibility) {
|
|
2530
|
+
for (const [layerId, visible] of Object.entries(visibility)) {
|
|
2531
|
+
if (this.isUserAddedLayer(layerId)) continue;
|
|
2532
|
+
if (!this.map.getLayer(layerId)) continue;
|
|
2533
|
+
this.state.backgroundLayerVisibility.set(layerId, visible);
|
|
2534
|
+
this.map.setLayoutProperty(
|
|
2535
|
+
layerId,
|
|
2536
|
+
"visibility",
|
|
2537
|
+
visible ? "visible" : "none"
|
|
2538
|
+
);
|
|
2539
|
+
}
|
|
2540
|
+
const legendPanel = this.panel.querySelector(
|
|
2541
|
+
".background-legend-layer-list"
|
|
2542
|
+
);
|
|
2543
|
+
if (legendPanel) {
|
|
2544
|
+
this.populateBackgroundLayerList(legendPanel);
|
|
2545
|
+
}
|
|
2546
|
+
this.updateBackgroundCheckboxState();
|
|
2547
|
+
}
|
|
2548
|
+
/**
|
|
2549
|
+
* Read all saved background-layer presets from localStorage. Returns an empty
|
|
2550
|
+
* object when storage is unavailable or the stored value is malformed.
|
|
2551
|
+
*/
|
|
2552
|
+
getBackgroundPresets() {
|
|
2553
|
+
try {
|
|
2554
|
+
const raw = typeof localStorage !== "undefined" ? localStorage.getItem(this.backgroundPresetStorageKey) : null;
|
|
2555
|
+
if (!raw) return {};
|
|
2556
|
+
const parsed = JSON.parse(raw);
|
|
2557
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
2558
|
+
return {};
|
|
2559
|
+
}
|
|
2560
|
+
const presets = {};
|
|
2561
|
+
for (const [name, value] of Object.entries(parsed)) {
|
|
2562
|
+
if (this.isUnsafePresetKey(name)) continue;
|
|
2563
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
2564
|
+
presets[name] = value;
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
return presets;
|
|
2568
|
+
} catch {
|
|
2569
|
+
return {};
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
/**
|
|
2573
|
+
* Reject preset names that, used as object keys, could pollute the prototype
|
|
2574
|
+
* chain or otherwise alias built-in object members.
|
|
2575
|
+
*/
|
|
2576
|
+
isUnsafePresetKey(name) {
|
|
2577
|
+
return name === "__proto__" || name === "constructor" || name === "prototype";
|
|
2578
|
+
}
|
|
2579
|
+
/**
|
|
2580
|
+
* Persist the full preset map to localStorage and notify listeners.
|
|
2581
|
+
*/
|
|
2582
|
+
writeBackgroundPresets(presets) {
|
|
2583
|
+
var _a;
|
|
2584
|
+
try {
|
|
2585
|
+
if (typeof localStorage !== "undefined") {
|
|
2586
|
+
localStorage.setItem(
|
|
2587
|
+
this.backgroundPresetStorageKey,
|
|
2588
|
+
JSON.stringify(presets)
|
|
2589
|
+
);
|
|
2590
|
+
}
|
|
2591
|
+
} catch {
|
|
2592
|
+
}
|
|
2593
|
+
(_a = this.onBackgroundPresetsChange) == null ? void 0 : _a.call(this, presets);
|
|
2594
|
+
}
|
|
2595
|
+
/**
|
|
2596
|
+
* Save the current background-layer visibility as a named preset. Reusing an
|
|
2597
|
+
* existing name overwrites that preset.
|
|
2598
|
+
*
|
|
2599
|
+
* @param name - Preset name; whitespace is trimmed. Empty names are ignored.
|
|
2600
|
+
* @returns The updated preset map.
|
|
2601
|
+
*/
|
|
2602
|
+
saveBackgroundPreset(name) {
|
|
2603
|
+
const trimmed = name.trim();
|
|
2604
|
+
const presets = this.getBackgroundPresets();
|
|
2605
|
+
if (!trimmed || this.isUnsafePresetKey(trimmed)) return presets;
|
|
2606
|
+
presets[trimmed] = this.getBackgroundLayerVisibility();
|
|
2607
|
+
this.writeBackgroundPresets(presets);
|
|
2608
|
+
return presets;
|
|
2609
|
+
}
|
|
2610
|
+
/**
|
|
2611
|
+
* Apply a previously saved preset to the map.
|
|
2612
|
+
*
|
|
2613
|
+
* @param name - The preset name to apply.
|
|
2614
|
+
* @returns `true` if a preset with that name existed and was applied.
|
|
2615
|
+
*/
|
|
2616
|
+
applyBackgroundPreset(name) {
|
|
2617
|
+
const presets = this.getBackgroundPresets();
|
|
2618
|
+
if (!Object.prototype.hasOwnProperty.call(presets, name)) return false;
|
|
2619
|
+
this.applyBackgroundLayerVisibility(presets[name]);
|
|
2620
|
+
return true;
|
|
2621
|
+
}
|
|
2622
|
+
/**
|
|
2623
|
+
* Delete a saved preset.
|
|
2624
|
+
*
|
|
2625
|
+
* @param name - The preset name to remove.
|
|
2626
|
+
* @returns The updated preset map.
|
|
2627
|
+
*/
|
|
2628
|
+
deleteBackgroundPreset(name) {
|
|
2629
|
+
const presets = this.getBackgroundPresets();
|
|
2630
|
+
if (Object.prototype.hasOwnProperty.call(presets, name)) {
|
|
2631
|
+
delete presets[name];
|
|
2632
|
+
this.writeBackgroundPresets(presets);
|
|
2633
|
+
}
|
|
2634
|
+
return presets;
|
|
2635
|
+
}
|
|
2384
2636
|
/**
|
|
2385
2637
|
* Create style button for a layer
|
|
2386
2638
|
*/
|