maplibre-gl-layer-control 0.14.1 → 0.15.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 +2 -2
- package/dist/index.cjs +658 -107
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +658 -107
- package/dist/index.mjs.map +1 -1
- package/dist/maplibre-gl-layer-control.css +111 -51
- package/dist/types/index.d.ts +51 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -690,6 +690,7 @@ class LayerControl {
|
|
|
690
690
|
// Panel width management
|
|
691
691
|
__publicField(this, "minPanelWidth");
|
|
692
692
|
__publicField(this, "maxPanelWidth");
|
|
693
|
+
__publicField(this, "initialPanelWidth");
|
|
693
694
|
__publicField(this, "maxPanelHeight");
|
|
694
695
|
__publicField(this, "showStyleEditor");
|
|
695
696
|
__publicField(this, "showOpacitySlider");
|
|
@@ -710,6 +711,18 @@ class LayerControl {
|
|
|
710
711
|
__publicField(this, "widthDragStartX", null);
|
|
711
712
|
__publicField(this, "widthDragStartWidth", null);
|
|
712
713
|
__publicField(this, "widthFrame", null);
|
|
714
|
+
// Exact-opacity input popup
|
|
715
|
+
__publicField(this, "opacityInputEl", null);
|
|
716
|
+
// Panel edge-drag resizing (handles on both the left and right edges)
|
|
717
|
+
__publicField(this, "resizeHandleEls", []);
|
|
718
|
+
/** Which edge is anchored to the control corner (the other edge is "free") */
|
|
719
|
+
__publicField(this, "panelAnchorSide", "right");
|
|
720
|
+
__publicField(this, "isPanelResizing", false);
|
|
721
|
+
/** The physical edge being dragged in the current resize gesture */
|
|
722
|
+
__publicField(this, "panelResizeEdge", "left");
|
|
723
|
+
__publicField(this, "panelResizeStartX", null);
|
|
724
|
+
__publicField(this, "panelResizeStartWidth", null);
|
|
725
|
+
__publicField(this, "panelResizeStartOffset", 0);
|
|
713
726
|
// Context menu and drag-drop
|
|
714
727
|
__publicField(this, "contextMenuEl", null);
|
|
715
728
|
__publicField(this, "enableContextMenu");
|
|
@@ -719,12 +732,15 @@ class LayerControl {
|
|
|
719
732
|
__publicField(this, "onLayerRemove");
|
|
720
733
|
this.minPanelWidth = options.panelMinWidth || 240;
|
|
721
734
|
this.maxPanelWidth = options.panelMaxWidth || 420;
|
|
735
|
+
this.initialPanelWidth = options.panelWidth || 350;
|
|
722
736
|
this.maxPanelHeight = options.panelMaxHeight || 600;
|
|
723
737
|
this.showStyleEditor = options.showStyleEditor !== false;
|
|
724
738
|
this.showOpacitySlider = options.showOpacitySlider !== false;
|
|
725
739
|
this.showLayerSymbol = options.showLayerSymbol !== false;
|
|
726
740
|
this.excludeDrawnLayers = options.excludeDrawnLayers !== false;
|
|
727
|
-
this.excludeLayerPatterns = this.wildcardPatternsToRegex(
|
|
741
|
+
this.excludeLayerPatterns = this.wildcardPatternsToRegex(
|
|
742
|
+
options.excludeLayers || []
|
|
743
|
+
);
|
|
728
744
|
this.enableContextMenu = options.enableContextMenu !== false;
|
|
729
745
|
this.enableDragAndDrop = options.enableDragAndDrop !== false;
|
|
730
746
|
this.onLayerRename = options.onLayerRename;
|
|
@@ -804,7 +820,10 @@ class LayerControl {
|
|
|
804
820
|
}
|
|
805
821
|
this.buildLayerItems();
|
|
806
822
|
}).catch((error) => {
|
|
807
|
-
console.warn(
|
|
823
|
+
console.warn(
|
|
824
|
+
"Failed to fetch basemap style, falling back to heuristic detection:",
|
|
825
|
+
error
|
|
826
|
+
);
|
|
808
827
|
if (Object.keys(this.state.layerStates).length === 0) {
|
|
809
828
|
this.autoDetectLayers();
|
|
810
829
|
}
|
|
@@ -843,7 +862,11 @@ class LayerControl {
|
|
|
843
862
|
this.basemapLayerIds = /* @__PURE__ */ new Set();
|
|
844
863
|
}
|
|
845
864
|
} catch (error) {
|
|
846
|
-
console.warn(
|
|
865
|
+
console.warn(
|
|
866
|
+
"Failed to fetch basemap style from URL:",
|
|
867
|
+
this.basemapStyleUrl,
|
|
868
|
+
error
|
|
869
|
+
);
|
|
847
870
|
throw error;
|
|
848
871
|
}
|
|
849
872
|
}
|
|
@@ -872,6 +895,7 @@ class LayerControl {
|
|
|
872
895
|
(_a = this.contextMenuEl.parentNode) == null ? void 0 : _a.removeChild(this.contextMenuEl);
|
|
873
896
|
this.contextMenuEl = null;
|
|
874
897
|
}
|
|
898
|
+
this.hideOpacityInput();
|
|
875
899
|
this.cleanupDragState();
|
|
876
900
|
(_b = this.panel.parentNode) == null ? void 0 : _b.removeChild(this.panel);
|
|
877
901
|
(_c = this.container.parentNode) == null ? void 0 : _c.removeChild(this.container);
|
|
@@ -1030,7 +1054,9 @@ class LayerControl {
|
|
|
1030
1054
|
}
|
|
1031
1055
|
if (style.glyphs) {
|
|
1032
1056
|
try {
|
|
1033
|
-
const url = new URL(
|
|
1057
|
+
const url = new URL(
|
|
1058
|
+
style.glyphs.replace("{fontstack}", "x").replace("{range}", "x")
|
|
1059
|
+
);
|
|
1034
1060
|
basemapDomains.add(url.hostname);
|
|
1035
1061
|
} catch {
|
|
1036
1062
|
}
|
|
@@ -1102,8 +1128,12 @@ class LayerControl {
|
|
|
1102
1128
|
visible: userState.visible ?? detected.visible,
|
|
1103
1129
|
opacity: userState.opacity ?? detected.opacity,
|
|
1104
1130
|
name: userState.name ?? detected.name,
|
|
1105
|
-
...userState.isCustomLayer !== void 0 && {
|
|
1106
|
-
|
|
1131
|
+
...userState.isCustomLayer !== void 0 && {
|
|
1132
|
+
isCustomLayer: userState.isCustomLayer
|
|
1133
|
+
},
|
|
1134
|
+
...userState.customLayerType !== void 0 && {
|
|
1135
|
+
customLayerType: userState.customLayerType
|
|
1136
|
+
}
|
|
1107
1137
|
};
|
|
1108
1138
|
}
|
|
1109
1139
|
/**
|
|
@@ -1189,8 +1219,111 @@ class LayerControl {
|
|
|
1189
1219
|
panel.appendChild(header);
|
|
1190
1220
|
const actionButtons = this.createActionButtons();
|
|
1191
1221
|
panel.appendChild(actionButtons);
|
|
1222
|
+
panel.appendChild(this.createResizeHandle("left"));
|
|
1223
|
+
panel.appendChild(this.createResizeHandle("right"));
|
|
1192
1224
|
return panel;
|
|
1193
1225
|
}
|
|
1226
|
+
/**
|
|
1227
|
+
* Create a draggable edge handle for resizing the panel width. A handle is
|
|
1228
|
+
* added on each side so the panel can be resized by dragging either edge,
|
|
1229
|
+
* like a conventional resizable window.
|
|
1230
|
+
*
|
|
1231
|
+
* @param side Which physical edge of the panel the handle sits on
|
|
1232
|
+
*/
|
|
1233
|
+
createResizeHandle(side) {
|
|
1234
|
+
const handle = document.createElement("div");
|
|
1235
|
+
handle.className = `layer-control-resize-handle layer-control-resize-handle-${side}`;
|
|
1236
|
+
handle.title = "Drag to resize panel";
|
|
1237
|
+
handle.setAttribute("role", "separator");
|
|
1238
|
+
handle.setAttribute("aria-orientation", "vertical");
|
|
1239
|
+
this.resizeHandleEls.push(handle);
|
|
1240
|
+
this.setupResizeHandleEvents(handle, side);
|
|
1241
|
+
return handle;
|
|
1242
|
+
}
|
|
1243
|
+
/**
|
|
1244
|
+
* Wire pointer events for an edge resize handle.
|
|
1245
|
+
*
|
|
1246
|
+
* @param handle The handle element
|
|
1247
|
+
* @param side Which physical edge of the panel the handle sits on
|
|
1248
|
+
*/
|
|
1249
|
+
setupResizeHandleEvents(handle, side) {
|
|
1250
|
+
handle.addEventListener("pointerdown", (event) => {
|
|
1251
|
+
event.preventDefault();
|
|
1252
|
+
event.stopPropagation();
|
|
1253
|
+
this.isPanelResizing = true;
|
|
1254
|
+
this.panelResizeEdge = side;
|
|
1255
|
+
this.panelResizeStartX = event.clientX;
|
|
1256
|
+
this.panelResizeStartWidth = this.state.panelWidth;
|
|
1257
|
+
this.panelResizeStartOffset = parseFloat(this.panel.style[this.panelAnchorSide]) || 0;
|
|
1258
|
+
handle.setPointerCapture(event.pointerId);
|
|
1259
|
+
this.panel.classList.add("resizing-active");
|
|
1260
|
+
});
|
|
1261
|
+
handle.addEventListener("pointermove", (event) => {
|
|
1262
|
+
if (!this.isPanelResizing) return;
|
|
1263
|
+
this.resizePanelFromPointer(event.clientX);
|
|
1264
|
+
});
|
|
1265
|
+
const endResize = (event) => {
|
|
1266
|
+
if (!this.isPanelResizing) return;
|
|
1267
|
+
if (event.pointerId !== void 0) {
|
|
1268
|
+
try {
|
|
1269
|
+
handle.releasePointerCapture(event.pointerId);
|
|
1270
|
+
} catch {
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
this.isPanelResizing = false;
|
|
1274
|
+
this.panelResizeStartX = null;
|
|
1275
|
+
this.panelResizeStartWidth = null;
|
|
1276
|
+
this.panel.classList.remove("resizing-active");
|
|
1277
|
+
};
|
|
1278
|
+
handle.addEventListener("pointerup", endResize);
|
|
1279
|
+
handle.addEventListener("pointercancel", endResize);
|
|
1280
|
+
handle.addEventListener("lostpointercapture", endResize);
|
|
1281
|
+
handle.addEventListener("dblclick", (event) => {
|
|
1282
|
+
event.preventDefault();
|
|
1283
|
+
event.stopPropagation();
|
|
1284
|
+
this.applyPanelWidth(this.initialPanelWidth, true);
|
|
1285
|
+
});
|
|
1286
|
+
}
|
|
1287
|
+
/**
|
|
1288
|
+
* Compute and apply a new panel width from the pointer position during an
|
|
1289
|
+
* edge drag. Dragging the free edge changes the width while the anchored
|
|
1290
|
+
* edge stays put; dragging the anchored edge shifts the anchor so the free
|
|
1291
|
+
* edge stays put — both feel like resizing a normal window.
|
|
1292
|
+
*/
|
|
1293
|
+
resizePanelFromPointer(clientX) {
|
|
1294
|
+
const dx = clientX - (this.panelResizeStartX ?? clientX);
|
|
1295
|
+
const startWidth = this.panelResizeStartWidth ?? this.state.panelWidth;
|
|
1296
|
+
const desiredWidth = this.panelResizeEdge === "left" ? startWidth - dx : startWidth + dx;
|
|
1297
|
+
const clamped = Math.round(
|
|
1298
|
+
Math.min(this.maxPanelWidth, Math.max(this.minPanelWidth, desiredWidth))
|
|
1299
|
+
);
|
|
1300
|
+
if (this.panelResizeEdge === this.panelAnchorSide) {
|
|
1301
|
+
let appliedDelta = clamped - startWidth;
|
|
1302
|
+
let newOffset = this.panelResizeStartOffset - appliedDelta;
|
|
1303
|
+
if (newOffset < 0) {
|
|
1304
|
+
newOffset = 0;
|
|
1305
|
+
appliedDelta = this.panelResizeStartOffset;
|
|
1306
|
+
}
|
|
1307
|
+
const finalWidth = Math.round(
|
|
1308
|
+
Math.min(
|
|
1309
|
+
this.maxPanelWidth,
|
|
1310
|
+
Math.max(this.minPanelWidth, startWidth + appliedDelta)
|
|
1311
|
+
)
|
|
1312
|
+
);
|
|
1313
|
+
this.applyPanelWidth(finalWidth, true);
|
|
1314
|
+
this.panel.style[this.panelAnchorSide] = `${newOffset}px`;
|
|
1315
|
+
} else {
|
|
1316
|
+
this.applyPanelWidth(clamped, true);
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
/**
|
|
1320
|
+
* Record which panel edge is anchored to the control corner, based on the
|
|
1321
|
+
* corner the control occupies. Right corners anchor the right edge (panel
|
|
1322
|
+
* grows leftward); left corners anchor the left edge (panel grows rightward).
|
|
1323
|
+
*/
|
|
1324
|
+
updatePanelAnchorSide(position) {
|
|
1325
|
+
this.panelAnchorSide = position === "top-right" || position === "bottom-right" ? "right" : "left";
|
|
1326
|
+
}
|
|
1194
1327
|
/**
|
|
1195
1328
|
* Detect which corner the control is positioned in
|
|
1196
1329
|
* @returns The position: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
|
|
@@ -1198,10 +1331,14 @@ class LayerControl {
|
|
|
1198
1331
|
getControlPosition() {
|
|
1199
1332
|
const parent = this.container.parentElement;
|
|
1200
1333
|
if (!parent) return "top-right";
|
|
1201
|
-
if (parent.classList.contains("maplibregl-ctrl-top-left"))
|
|
1202
|
-
|
|
1203
|
-
if (parent.classList.contains("maplibregl-ctrl-
|
|
1204
|
-
|
|
1334
|
+
if (parent.classList.contains("maplibregl-ctrl-top-left"))
|
|
1335
|
+
return "top-left";
|
|
1336
|
+
if (parent.classList.contains("maplibregl-ctrl-top-right"))
|
|
1337
|
+
return "top-right";
|
|
1338
|
+
if (parent.classList.contains("maplibregl-ctrl-bottom-left"))
|
|
1339
|
+
return "bottom-left";
|
|
1340
|
+
if (parent.classList.contains("maplibregl-ctrl-bottom-right"))
|
|
1341
|
+
return "bottom-right";
|
|
1205
1342
|
return "top-right";
|
|
1206
1343
|
}
|
|
1207
1344
|
/**
|
|
@@ -1213,6 +1350,7 @@ class LayerControl {
|
|
|
1213
1350
|
const buttonRect = this.button.getBoundingClientRect();
|
|
1214
1351
|
const mapRect = this.mapContainer.getBoundingClientRect();
|
|
1215
1352
|
const position = this.getControlPosition();
|
|
1353
|
+
this.updatePanelAnchorSide(position);
|
|
1216
1354
|
const buttonTop = buttonRect.top - mapRect.top;
|
|
1217
1355
|
const buttonBottom = mapRect.bottom - buttonRect.bottom;
|
|
1218
1356
|
const buttonLeft = buttonRect.left - mapRect.left;
|
|
@@ -1252,13 +1390,19 @@ class LayerControl {
|
|
|
1252
1390
|
showAllBtn.className = "layer-control-action-btn";
|
|
1253
1391
|
showAllBtn.textContent = "Show All";
|
|
1254
1392
|
showAllBtn.title = "Show all layers";
|
|
1255
|
-
showAllBtn.addEventListener(
|
|
1393
|
+
showAllBtn.addEventListener(
|
|
1394
|
+
"click",
|
|
1395
|
+
() => this.setAllLayersVisibility(true)
|
|
1396
|
+
);
|
|
1256
1397
|
const hideAllBtn = document.createElement("button");
|
|
1257
1398
|
hideAllBtn.type = "button";
|
|
1258
1399
|
hideAllBtn.className = "layer-control-action-btn";
|
|
1259
1400
|
hideAllBtn.textContent = "Hide All";
|
|
1260
1401
|
hideAllBtn.title = "Hide all layers";
|
|
1261
|
-
hideAllBtn.addEventListener(
|
|
1402
|
+
hideAllBtn.addEventListener(
|
|
1403
|
+
"click",
|
|
1404
|
+
() => this.setAllLayersVisibility(false)
|
|
1405
|
+
);
|
|
1262
1406
|
container.appendChild(showAllBtn);
|
|
1263
1407
|
container.appendChild(hideAllBtn);
|
|
1264
1408
|
return container;
|
|
@@ -1271,7 +1415,9 @@ class LayerControl {
|
|
|
1271
1415
|
this.toggleLayerVisibility(layerId, visible);
|
|
1272
1416
|
const itemEl = this.panel.querySelector(`[data-layer-id="${layerId}"]`);
|
|
1273
1417
|
if (itemEl) {
|
|
1274
|
-
const checkbox = itemEl.querySelector(
|
|
1418
|
+
const checkbox = itemEl.querySelector(
|
|
1419
|
+
".layer-control-checkbox"
|
|
1420
|
+
);
|
|
1275
1421
|
if (checkbox) {
|
|
1276
1422
|
checkbox.checked = visible;
|
|
1277
1423
|
checkbox.indeterminate = false;
|
|
@@ -1422,7 +1568,9 @@ class LayerControl {
|
|
|
1422
1568
|
* Apply panel width (clamped to min/max)
|
|
1423
1569
|
*/
|
|
1424
1570
|
applyPanelWidth(width, immediate = false) {
|
|
1425
|
-
const clamped = Math.round(
|
|
1571
|
+
const clamped = Math.round(
|
|
1572
|
+
Math.min(this.maxPanelWidth, Math.max(this.minPanelWidth, width))
|
|
1573
|
+
);
|
|
1426
1574
|
const applyWidth = () => {
|
|
1427
1575
|
this.state.panelWidth = clamped;
|
|
1428
1576
|
const px = `${clamped}px`;
|
|
@@ -1449,7 +1597,10 @@ class LayerControl {
|
|
|
1449
1597
|
this.widthValueEl.textContent = `${this.state.panelWidth}px`;
|
|
1450
1598
|
}
|
|
1451
1599
|
if (this.widthSliderEl) {
|
|
1452
|
-
this.widthSliderEl.setAttribute(
|
|
1600
|
+
this.widthSliderEl.setAttribute(
|
|
1601
|
+
"aria-valuenow",
|
|
1602
|
+
String(this.state.panelWidth)
|
|
1603
|
+
);
|
|
1453
1604
|
const ratio = (this.state.panelWidth - this.minPanelWidth) / (this.maxPanelWidth - this.minPanelWidth || 1);
|
|
1454
1605
|
if (this.widthThumbEl) {
|
|
1455
1606
|
const sliderWidth = this.widthSliderEl.clientWidth;
|
|
@@ -1522,12 +1673,14 @@ class LayerControl {
|
|
|
1522
1673
|
}
|
|
1523
1674
|
});
|
|
1524
1675
|
if (this.customLayerRegistry) {
|
|
1525
|
-
this.customLayerUnsubscribe = this.customLayerRegistry.onChange(
|
|
1526
|
-
|
|
1527
|
-
|
|
1676
|
+
this.customLayerUnsubscribe = this.customLayerRegistry.onChange(
|
|
1677
|
+
(event, layerId) => {
|
|
1678
|
+
if (event === "add" && layerId) {
|
|
1679
|
+
this.removedCustomLayerIds.delete(layerId);
|
|
1680
|
+
}
|
|
1681
|
+
setTimeout(() => this.checkForNewLayers(), 100);
|
|
1528
1682
|
}
|
|
1529
|
-
|
|
1530
|
-
});
|
|
1683
|
+
);
|
|
1531
1684
|
}
|
|
1532
1685
|
}
|
|
1533
1686
|
/**
|
|
@@ -1630,6 +1783,10 @@ class LayerControl {
|
|
|
1630
1783
|
this.changeLayerOpacity(layerId, parseFloat(opacity.value));
|
|
1631
1784
|
opacity.title = `Opacity: ${Math.round(parseFloat(opacity.value) * 100)}%`;
|
|
1632
1785
|
});
|
|
1786
|
+
opacity.addEventListener("dblclick", (event) => {
|
|
1787
|
+
event.preventDefault();
|
|
1788
|
+
this.showOpacityInput(layerId, opacity);
|
|
1789
|
+
});
|
|
1633
1790
|
row.appendChild(opacity);
|
|
1634
1791
|
}
|
|
1635
1792
|
if (this.showStyleEditor) {
|
|
@@ -1727,11 +1884,103 @@ class LayerControl {
|
|
|
1727
1884
|
}, 200);
|
|
1728
1885
|
return;
|
|
1729
1886
|
}
|
|
1730
|
-
this.map.setLayoutProperty(
|
|
1887
|
+
this.map.setLayoutProperty(
|
|
1888
|
+
layerId,
|
|
1889
|
+
"visibility",
|
|
1890
|
+
visible ? "visible" : "none"
|
|
1891
|
+
);
|
|
1731
1892
|
setTimeout(() => {
|
|
1732
1893
|
this.state.isStyleOperationInProgress = false;
|
|
1733
1894
|
}, 200);
|
|
1734
1895
|
}
|
|
1896
|
+
/**
|
|
1897
|
+
* Show a small popup that lets the user type an exact opacity percentage
|
|
1898
|
+
* (0-100) for a layer. Triggered by double-clicking the opacity slider.
|
|
1899
|
+
*
|
|
1900
|
+
* @param layerId The layer ID whose opacity is being edited
|
|
1901
|
+
* @param slider The opacity range input associated with the layer row
|
|
1902
|
+
*/
|
|
1903
|
+
showOpacityInput(layerId, slider) {
|
|
1904
|
+
this.hideOpacityInput();
|
|
1905
|
+
const popup = document.createElement("div");
|
|
1906
|
+
popup.className = "layer-control-opacity-input";
|
|
1907
|
+
const label = document.createElement("span");
|
|
1908
|
+
label.className = "layer-control-opacity-input-label";
|
|
1909
|
+
label.textContent = "Opacity";
|
|
1910
|
+
const field = document.createElement("div");
|
|
1911
|
+
field.className = "layer-control-opacity-input-field";
|
|
1912
|
+
const input = document.createElement("input");
|
|
1913
|
+
input.type = "number";
|
|
1914
|
+
input.min = "0";
|
|
1915
|
+
input.max = "100";
|
|
1916
|
+
input.step = "1";
|
|
1917
|
+
input.value = String(Math.round(parseFloat(slider.value) * 100));
|
|
1918
|
+
const suffix = document.createElement("span");
|
|
1919
|
+
suffix.className = "layer-control-opacity-input-suffix";
|
|
1920
|
+
suffix.textContent = "%";
|
|
1921
|
+
field.appendChild(input);
|
|
1922
|
+
field.appendChild(suffix);
|
|
1923
|
+
popup.appendChild(label);
|
|
1924
|
+
popup.appendChild(field);
|
|
1925
|
+
popup.addEventListener("click", (event) => event.stopPropagation());
|
|
1926
|
+
popup.addEventListener("pointerdown", (event) => event.stopPropagation());
|
|
1927
|
+
let settled = false;
|
|
1928
|
+
const apply = () => {
|
|
1929
|
+
if (settled) return;
|
|
1930
|
+
settled = true;
|
|
1931
|
+
const pct = clamp(Math.round(parseFloat(input.value)), 0, 100);
|
|
1932
|
+
if (!Number.isNaN(pct)) {
|
|
1933
|
+
const opacity = pct / 100;
|
|
1934
|
+
slider.value = String(opacity);
|
|
1935
|
+
slider.title = `Opacity: ${pct}%`;
|
|
1936
|
+
this.changeLayerOpacity(layerId, opacity);
|
|
1937
|
+
}
|
|
1938
|
+
this.hideOpacityInput();
|
|
1939
|
+
};
|
|
1940
|
+
const cancel = () => {
|
|
1941
|
+
if (settled) return;
|
|
1942
|
+
settled = true;
|
|
1943
|
+
this.hideOpacityInput();
|
|
1944
|
+
};
|
|
1945
|
+
input.addEventListener("keydown", (event) => {
|
|
1946
|
+
if (event.key === "Enter") {
|
|
1947
|
+
event.preventDefault();
|
|
1948
|
+
apply();
|
|
1949
|
+
slider.focus();
|
|
1950
|
+
} else if (event.key === "Escape") {
|
|
1951
|
+
event.preventDefault();
|
|
1952
|
+
cancel();
|
|
1953
|
+
slider.focus();
|
|
1954
|
+
}
|
|
1955
|
+
});
|
|
1956
|
+
input.addEventListener("blur", apply);
|
|
1957
|
+
this.mapContainer.appendChild(popup);
|
|
1958
|
+
this.opacityInputEl = popup;
|
|
1959
|
+
const sliderRect = slider.getBoundingClientRect();
|
|
1960
|
+
const mapRect = this.mapContainer.getBoundingClientRect();
|
|
1961
|
+
const popupRect = popup.getBoundingClientRect();
|
|
1962
|
+
let left = sliderRect.left - mapRect.left;
|
|
1963
|
+
let top = sliderRect.bottom - mapRect.top + 4;
|
|
1964
|
+
if (left + popupRect.width > mapRect.width) {
|
|
1965
|
+
left = Math.max(0, mapRect.width - popupRect.width - 5);
|
|
1966
|
+
}
|
|
1967
|
+
if (top + popupRect.height > mapRect.height) {
|
|
1968
|
+
top = sliderRect.top - mapRect.top - popupRect.height - 4;
|
|
1969
|
+
}
|
|
1970
|
+
popup.style.left = `${Math.max(0, left)}px`;
|
|
1971
|
+
popup.style.top = `${Math.max(0, top)}px`;
|
|
1972
|
+
input.focus();
|
|
1973
|
+
input.select();
|
|
1974
|
+
}
|
|
1975
|
+
/**
|
|
1976
|
+
* Remove the exact-opacity input popup if it is open.
|
|
1977
|
+
*/
|
|
1978
|
+
hideOpacityInput() {
|
|
1979
|
+
if (this.opacityInputEl) {
|
|
1980
|
+
this.opacityInputEl.remove();
|
|
1981
|
+
this.opacityInputEl = null;
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1735
1984
|
/**
|
|
1736
1985
|
* Change layer opacity
|
|
1737
1986
|
*/
|
|
@@ -1794,13 +2043,21 @@ class LayerControl {
|
|
|
1794
2043
|
styleLayers.forEach((layer) => {
|
|
1795
2044
|
if (!this.isUserAddedLayer(layer.id)) {
|
|
1796
2045
|
this.state.backgroundLayerVisibility.set(layer.id, visible);
|
|
1797
|
-
this.map.setLayoutProperty(
|
|
2046
|
+
this.map.setLayoutProperty(
|
|
2047
|
+
layer.id,
|
|
2048
|
+
"visibility",
|
|
2049
|
+
visible ? "visible" : "none"
|
|
2050
|
+
);
|
|
1798
2051
|
}
|
|
1799
2052
|
});
|
|
1800
2053
|
if (this.state.backgroundLegendOpen) {
|
|
1801
|
-
const legendPanel = this.panel.querySelector(
|
|
2054
|
+
const legendPanel = this.panel.querySelector(
|
|
2055
|
+
".layer-control-background-legend"
|
|
2056
|
+
);
|
|
1802
2057
|
if (legendPanel) {
|
|
1803
|
-
const checkboxes = legendPanel.querySelectorAll(
|
|
2058
|
+
const checkboxes = legendPanel.querySelectorAll(
|
|
2059
|
+
".background-legend-checkbox"
|
|
2060
|
+
);
|
|
1804
2061
|
checkboxes.forEach((checkbox) => {
|
|
1805
2062
|
checkbox.checked = visible;
|
|
1806
2063
|
});
|
|
@@ -1833,8 +2090,14 @@ class LayerControl {
|
|
|
1833
2090
|
button.className = "layer-control-style-button layer-control-background-legend-button";
|
|
1834
2091
|
button.innerHTML = "⚙";
|
|
1835
2092
|
button.title = "Show background layer details";
|
|
1836
|
-
button.setAttribute(
|
|
1837
|
-
|
|
2093
|
+
button.setAttribute(
|
|
2094
|
+
"aria-label",
|
|
2095
|
+
"Show background layer visibility controls"
|
|
2096
|
+
);
|
|
2097
|
+
button.setAttribute(
|
|
2098
|
+
"aria-expanded",
|
|
2099
|
+
String(this.state.backgroundLegendOpen)
|
|
2100
|
+
);
|
|
1838
2101
|
button.addEventListener("click", (e) => {
|
|
1839
2102
|
e.stopPropagation();
|
|
1840
2103
|
this.toggleBackgroundLegend();
|
|
@@ -1862,7 +2125,9 @@ class LayerControl {
|
|
|
1862
2125
|
if (!itemEl) return;
|
|
1863
2126
|
let legendPanel = itemEl.querySelector(".layer-control-background-legend");
|
|
1864
2127
|
if (legendPanel) {
|
|
1865
|
-
const layerList = legendPanel.querySelector(
|
|
2128
|
+
const layerList = legendPanel.querySelector(
|
|
2129
|
+
".background-legend-layer-list"
|
|
2130
|
+
);
|
|
1866
2131
|
if (layerList) {
|
|
1867
2132
|
this.populateBackgroundLayerList(layerList);
|
|
1868
2133
|
}
|
|
@@ -1871,7 +2136,9 @@ class LayerControl {
|
|
|
1871
2136
|
itemEl.appendChild(legendPanel);
|
|
1872
2137
|
}
|
|
1873
2138
|
this.state.backgroundLegendOpen = true;
|
|
1874
|
-
const button = itemEl.querySelector(
|
|
2139
|
+
const button = itemEl.querySelector(
|
|
2140
|
+
".layer-control-background-legend-button"
|
|
2141
|
+
);
|
|
1875
2142
|
if (button) {
|
|
1876
2143
|
button.setAttribute("aria-expanded", "true");
|
|
1877
2144
|
button.classList.add("active");
|
|
@@ -1886,12 +2153,16 @@ class LayerControl {
|
|
|
1886
2153
|
closeBackgroundLegend() {
|
|
1887
2154
|
const itemEl = this.panel.querySelector('[data-layer-id="Background"]');
|
|
1888
2155
|
if (!itemEl) return;
|
|
1889
|
-
const legendPanel = itemEl.querySelector(
|
|
2156
|
+
const legendPanel = itemEl.querySelector(
|
|
2157
|
+
".layer-control-background-legend"
|
|
2158
|
+
);
|
|
1890
2159
|
if (legendPanel) {
|
|
1891
2160
|
legendPanel.remove();
|
|
1892
2161
|
}
|
|
1893
2162
|
this.state.backgroundLegendOpen = false;
|
|
1894
|
-
const button = itemEl.querySelector(
|
|
2163
|
+
const button = itemEl.querySelector(
|
|
2164
|
+
".layer-control-background-legend-button"
|
|
2165
|
+
);
|
|
1895
2166
|
if (button) {
|
|
1896
2167
|
button.setAttribute("aria-expanded", "false");
|
|
1897
2168
|
button.classList.remove("active");
|
|
@@ -1923,11 +2194,17 @@ class LayerControl {
|
|
|
1923
2194
|
const showAllBtn = document.createElement("button");
|
|
1924
2195
|
showAllBtn.className = "background-legend-action-btn";
|
|
1925
2196
|
showAllBtn.textContent = "Show All";
|
|
1926
|
-
showAllBtn.addEventListener(
|
|
2197
|
+
showAllBtn.addEventListener(
|
|
2198
|
+
"click",
|
|
2199
|
+
() => this.setAllBackgroundLayersVisibility(true)
|
|
2200
|
+
);
|
|
1927
2201
|
const hideAllBtn = document.createElement("button");
|
|
1928
2202
|
hideAllBtn.className = "background-legend-action-btn";
|
|
1929
2203
|
hideAllBtn.textContent = "Hide All";
|
|
1930
|
-
hideAllBtn.addEventListener(
|
|
2204
|
+
hideAllBtn.addEventListener(
|
|
2205
|
+
"click",
|
|
2206
|
+
() => this.setAllBackgroundLayersVisibility(false)
|
|
2207
|
+
);
|
|
1931
2208
|
actionsRow.appendChild(showAllBtn);
|
|
1932
2209
|
actionsRow.appendChild(hideAllBtn);
|
|
1933
2210
|
const filterRow = document.createElement("div");
|
|
@@ -2041,7 +2318,11 @@ class LayerControl {
|
|
|
2041
2318
|
*/
|
|
2042
2319
|
toggleIndividualBackgroundLayer(layerId, visible) {
|
|
2043
2320
|
this.state.backgroundLayerVisibility.set(layerId, visible);
|
|
2044
|
-
this.map.setLayoutProperty(
|
|
2321
|
+
this.map.setLayoutProperty(
|
|
2322
|
+
layerId,
|
|
2323
|
+
"visibility",
|
|
2324
|
+
visible ? "visible" : "none"
|
|
2325
|
+
);
|
|
2045
2326
|
this.updateBackgroundCheckboxState();
|
|
2046
2327
|
}
|
|
2047
2328
|
/**
|
|
@@ -2052,12 +2333,20 @@ class LayerControl {
|
|
|
2052
2333
|
styleLayers.forEach((layer) => {
|
|
2053
2334
|
if (!this.isUserAddedLayer(layer.id)) {
|
|
2054
2335
|
this.state.backgroundLayerVisibility.set(layer.id, visible);
|
|
2055
|
-
this.map.setLayoutProperty(
|
|
2336
|
+
this.map.setLayoutProperty(
|
|
2337
|
+
layer.id,
|
|
2338
|
+
"visibility",
|
|
2339
|
+
visible ? "visible" : "none"
|
|
2340
|
+
);
|
|
2056
2341
|
}
|
|
2057
2342
|
});
|
|
2058
|
-
const legendPanel = this.panel.querySelector(
|
|
2343
|
+
const legendPanel = this.panel.querySelector(
|
|
2344
|
+
".layer-control-background-legend"
|
|
2345
|
+
);
|
|
2059
2346
|
if (legendPanel) {
|
|
2060
|
-
const checkboxes = legendPanel.querySelectorAll(
|
|
2347
|
+
const checkboxes = legendPanel.querySelectorAll(
|
|
2348
|
+
".background-legend-checkbox"
|
|
2349
|
+
);
|
|
2061
2350
|
checkboxes.forEach((checkbox) => {
|
|
2062
2351
|
checkbox.checked = visible;
|
|
2063
2352
|
});
|
|
@@ -2078,9 +2367,13 @@ class LayerControl {
|
|
|
2078
2367
|
if (visible === false) allVisible = false;
|
|
2079
2368
|
}
|
|
2080
2369
|
});
|
|
2081
|
-
const backgroundItem = this.panel.querySelector(
|
|
2370
|
+
const backgroundItem = this.panel.querySelector(
|
|
2371
|
+
'[data-layer-id="Background"]'
|
|
2372
|
+
);
|
|
2082
2373
|
if (backgroundItem) {
|
|
2083
|
-
const checkbox = backgroundItem.querySelector(
|
|
2374
|
+
const checkbox = backgroundItem.querySelector(
|
|
2375
|
+
".layer-control-checkbox"
|
|
2376
|
+
);
|
|
2084
2377
|
if (checkbox) {
|
|
2085
2378
|
checkbox.checked = anyVisible;
|
|
2086
2379
|
checkbox.indeterminate = anyVisible && !allVisible;
|
|
@@ -2143,11 +2436,18 @@ class LayerControl {
|
|
|
2143
2436
|
if (!this.state.originalStyles.has(nativeId)) {
|
|
2144
2437
|
const nativeLayer = this.map.getLayer(nativeId);
|
|
2145
2438
|
if (nativeLayer) {
|
|
2146
|
-
cacheOriginalLayerStyle(
|
|
2439
|
+
cacheOriginalLayerStyle(
|
|
2440
|
+
this.map,
|
|
2441
|
+
nativeId,
|
|
2442
|
+
this.state.originalStyles
|
|
2443
|
+
);
|
|
2147
2444
|
}
|
|
2148
2445
|
}
|
|
2149
2446
|
}
|
|
2150
|
-
const editor3 = this.createNativeSubLayerStyleEditor(
|
|
2447
|
+
const editor3 = this.createNativeSubLayerStyleEditor(
|
|
2448
|
+
layerId,
|
|
2449
|
+
nativeLayerIds
|
|
2450
|
+
);
|
|
2151
2451
|
if (editor3) {
|
|
2152
2452
|
itemEl.appendChild(editor3);
|
|
2153
2453
|
this.styleEditors.set(layerId, editor3);
|
|
@@ -2450,14 +2750,38 @@ class LayerControl {
|
|
|
2450
2750
|
if (!fillColor) {
|
|
2451
2751
|
fillColor = this.map.getPaintProperty(layerId, "fill-color");
|
|
2452
2752
|
}
|
|
2453
|
-
this.createColorControl(
|
|
2753
|
+
this.createColorControl(
|
|
2754
|
+
container,
|
|
2755
|
+
layerId,
|
|
2756
|
+
"fill-color",
|
|
2757
|
+
"Fill Color",
|
|
2758
|
+
normalizeColor(fillColor || "#088")
|
|
2759
|
+
);
|
|
2454
2760
|
const fillOpacity = this.map.getPaintProperty(layerId, "fill-opacity");
|
|
2455
2761
|
if (fillOpacity !== void 0 && typeof fillOpacity === "number") {
|
|
2456
|
-
this.createSliderControl(
|
|
2457
|
-
|
|
2458
|
-
|
|
2762
|
+
this.createSliderControl(
|
|
2763
|
+
container,
|
|
2764
|
+
layerId,
|
|
2765
|
+
"fill-opacity",
|
|
2766
|
+
"Fill Opacity",
|
|
2767
|
+
fillOpacity,
|
|
2768
|
+
0,
|
|
2769
|
+
1,
|
|
2770
|
+
0.05
|
|
2771
|
+
);
|
|
2772
|
+
}
|
|
2773
|
+
const outlineColor = this.map.getPaintProperty(
|
|
2774
|
+
layerId,
|
|
2775
|
+
"fill-outline-color"
|
|
2776
|
+
);
|
|
2459
2777
|
if (outlineColor !== void 0) {
|
|
2460
|
-
this.createColorControl(
|
|
2778
|
+
this.createColorControl(
|
|
2779
|
+
container,
|
|
2780
|
+
layerId,
|
|
2781
|
+
"fill-outline-color",
|
|
2782
|
+
"Outline Color",
|
|
2783
|
+
normalizeColor(outlineColor)
|
|
2784
|
+
);
|
|
2461
2785
|
}
|
|
2462
2786
|
}
|
|
2463
2787
|
/**
|
|
@@ -2474,16 +2798,49 @@ class LayerControl {
|
|
|
2474
2798
|
if (!lineColor) {
|
|
2475
2799
|
lineColor = this.map.getPaintProperty(layerId, "line-color");
|
|
2476
2800
|
}
|
|
2477
|
-
this.createColorControl(
|
|
2801
|
+
this.createColorControl(
|
|
2802
|
+
container,
|
|
2803
|
+
layerId,
|
|
2804
|
+
"line-color",
|
|
2805
|
+
"Line Color",
|
|
2806
|
+
normalizeColor(lineColor || "#000")
|
|
2807
|
+
);
|
|
2478
2808
|
const lineWidth = this.map.getPaintProperty(layerId, "line-width");
|
|
2479
|
-
this.createSliderControl(
|
|
2809
|
+
this.createSliderControl(
|
|
2810
|
+
container,
|
|
2811
|
+
layerId,
|
|
2812
|
+
"line-width",
|
|
2813
|
+
"Line Width",
|
|
2814
|
+
typeof lineWidth === "number" ? lineWidth : 1,
|
|
2815
|
+
0,
|
|
2816
|
+
20,
|
|
2817
|
+
0.5
|
|
2818
|
+
);
|
|
2480
2819
|
const lineOpacity = this.map.getPaintProperty(layerId, "line-opacity");
|
|
2481
2820
|
if (lineOpacity !== void 0 && typeof lineOpacity === "number") {
|
|
2482
|
-
this.createSliderControl(
|
|
2821
|
+
this.createSliderControl(
|
|
2822
|
+
container,
|
|
2823
|
+
layerId,
|
|
2824
|
+
"line-opacity",
|
|
2825
|
+
"Line Opacity",
|
|
2826
|
+
lineOpacity,
|
|
2827
|
+
0,
|
|
2828
|
+
1,
|
|
2829
|
+
0.05
|
|
2830
|
+
);
|
|
2483
2831
|
}
|
|
2484
2832
|
const lineBlur = this.map.getPaintProperty(layerId, "line-blur");
|
|
2485
2833
|
if (lineBlur !== void 0 && typeof lineBlur === "number") {
|
|
2486
|
-
this.createSliderControl(
|
|
2834
|
+
this.createSliderControl(
|
|
2835
|
+
container,
|
|
2836
|
+
layerId,
|
|
2837
|
+
"line-blur",
|
|
2838
|
+
"Line Blur",
|
|
2839
|
+
lineBlur,
|
|
2840
|
+
0,
|
|
2841
|
+
5,
|
|
2842
|
+
0.1
|
|
2843
|
+
);
|
|
2487
2844
|
}
|
|
2488
2845
|
}
|
|
2489
2846
|
/**
|
|
@@ -2500,20 +2857,65 @@ class LayerControl {
|
|
|
2500
2857
|
if (!circleColor) {
|
|
2501
2858
|
circleColor = this.map.getPaintProperty(layerId, "circle-color");
|
|
2502
2859
|
}
|
|
2503
|
-
this.createColorControl(
|
|
2860
|
+
this.createColorControl(
|
|
2861
|
+
container,
|
|
2862
|
+
layerId,
|
|
2863
|
+
"circle-color",
|
|
2864
|
+
"Circle Color",
|
|
2865
|
+
normalizeColor(circleColor || "#000")
|
|
2866
|
+
);
|
|
2504
2867
|
const circleRadius = this.map.getPaintProperty(layerId, "circle-radius");
|
|
2505
|
-
this.createSliderControl(
|
|
2868
|
+
this.createSliderControl(
|
|
2869
|
+
container,
|
|
2870
|
+
layerId,
|
|
2871
|
+
"circle-radius",
|
|
2872
|
+
"Radius",
|
|
2873
|
+
typeof circleRadius === "number" ? circleRadius : 5,
|
|
2874
|
+
0,
|
|
2875
|
+
40,
|
|
2876
|
+
0.5
|
|
2877
|
+
);
|
|
2506
2878
|
const circleOpacity = this.map.getPaintProperty(layerId, "circle-opacity");
|
|
2507
2879
|
if (circleOpacity !== void 0 && typeof circleOpacity === "number") {
|
|
2508
|
-
this.createSliderControl(
|
|
2509
|
-
|
|
2510
|
-
|
|
2880
|
+
this.createSliderControl(
|
|
2881
|
+
container,
|
|
2882
|
+
layerId,
|
|
2883
|
+
"circle-opacity",
|
|
2884
|
+
"Opacity",
|
|
2885
|
+
circleOpacity,
|
|
2886
|
+
0,
|
|
2887
|
+
1,
|
|
2888
|
+
0.05
|
|
2889
|
+
);
|
|
2890
|
+
}
|
|
2891
|
+
const strokeColor = this.map.getPaintProperty(
|
|
2892
|
+
layerId,
|
|
2893
|
+
"circle-stroke-color"
|
|
2894
|
+
);
|
|
2511
2895
|
if (strokeColor !== void 0) {
|
|
2512
|
-
this.createColorControl(
|
|
2513
|
-
|
|
2514
|
-
|
|
2896
|
+
this.createColorControl(
|
|
2897
|
+
container,
|
|
2898
|
+
layerId,
|
|
2899
|
+
"circle-stroke-color",
|
|
2900
|
+
"Stroke Color",
|
|
2901
|
+
normalizeColor(strokeColor)
|
|
2902
|
+
);
|
|
2903
|
+
}
|
|
2904
|
+
const strokeWidth = this.map.getPaintProperty(
|
|
2905
|
+
layerId,
|
|
2906
|
+
"circle-stroke-width"
|
|
2907
|
+
);
|
|
2515
2908
|
if (strokeWidth !== void 0 && typeof strokeWidth === "number") {
|
|
2516
|
-
this.createSliderControl(
|
|
2909
|
+
this.createSliderControl(
|
|
2910
|
+
container,
|
|
2911
|
+
layerId,
|
|
2912
|
+
"circle-stroke-width",
|
|
2913
|
+
"Stroke Width",
|
|
2914
|
+
strokeWidth,
|
|
2915
|
+
0,
|
|
2916
|
+
10,
|
|
2917
|
+
0.1
|
|
2918
|
+
);
|
|
2517
2919
|
}
|
|
2518
2920
|
}
|
|
2519
2921
|
/**
|
|
@@ -2521,17 +2923,77 @@ class LayerControl {
|
|
|
2521
2923
|
*/
|
|
2522
2924
|
addRasterControls(container, layerId) {
|
|
2523
2925
|
const rasterOpacity = this.map.getPaintProperty(layerId, "raster-opacity");
|
|
2524
|
-
this.createSliderControl(
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2926
|
+
this.createSliderControl(
|
|
2927
|
+
container,
|
|
2928
|
+
layerId,
|
|
2929
|
+
"raster-opacity",
|
|
2930
|
+
"Opacity",
|
|
2931
|
+
typeof rasterOpacity === "number" ? rasterOpacity : 1,
|
|
2932
|
+
0,
|
|
2933
|
+
1,
|
|
2934
|
+
0.05
|
|
2935
|
+
);
|
|
2936
|
+
const brightnessMin = this.map.getPaintProperty(
|
|
2937
|
+
layerId,
|
|
2938
|
+
"raster-brightness-min"
|
|
2939
|
+
);
|
|
2940
|
+
this.createSliderControl(
|
|
2941
|
+
container,
|
|
2942
|
+
layerId,
|
|
2943
|
+
"raster-brightness-min",
|
|
2944
|
+
"Brightness Min",
|
|
2945
|
+
typeof brightnessMin === "number" ? brightnessMin : 0,
|
|
2946
|
+
-1,
|
|
2947
|
+
1,
|
|
2948
|
+
0.05
|
|
2949
|
+
);
|
|
2950
|
+
const brightnessMax = this.map.getPaintProperty(
|
|
2951
|
+
layerId,
|
|
2952
|
+
"raster-brightness-max"
|
|
2953
|
+
);
|
|
2954
|
+
this.createSliderControl(
|
|
2955
|
+
container,
|
|
2956
|
+
layerId,
|
|
2957
|
+
"raster-brightness-max",
|
|
2958
|
+
"Brightness Max",
|
|
2959
|
+
typeof brightnessMax === "number" ? brightnessMax : 1,
|
|
2960
|
+
-1,
|
|
2961
|
+
1,
|
|
2962
|
+
0.05
|
|
2963
|
+
);
|
|
2529
2964
|
const saturation = this.map.getPaintProperty(layerId, "raster-saturation");
|
|
2530
|
-
this.createSliderControl(
|
|
2965
|
+
this.createSliderControl(
|
|
2966
|
+
container,
|
|
2967
|
+
layerId,
|
|
2968
|
+
"raster-saturation",
|
|
2969
|
+
"Saturation",
|
|
2970
|
+
typeof saturation === "number" ? saturation : 0,
|
|
2971
|
+
-1,
|
|
2972
|
+
1,
|
|
2973
|
+
0.05
|
|
2974
|
+
);
|
|
2531
2975
|
const contrast = this.map.getPaintProperty(layerId, "raster-contrast");
|
|
2532
|
-
this.createSliderControl(
|
|
2976
|
+
this.createSliderControl(
|
|
2977
|
+
container,
|
|
2978
|
+
layerId,
|
|
2979
|
+
"raster-contrast",
|
|
2980
|
+
"Contrast",
|
|
2981
|
+
typeof contrast === "number" ? contrast : 0,
|
|
2982
|
+
-1,
|
|
2983
|
+
1,
|
|
2984
|
+
0.05
|
|
2985
|
+
);
|
|
2533
2986
|
const hueRotate = this.map.getPaintProperty(layerId, "raster-hue-rotate");
|
|
2534
|
-
this.createSliderControl(
|
|
2987
|
+
this.createSliderControl(
|
|
2988
|
+
container,
|
|
2989
|
+
layerId,
|
|
2990
|
+
"raster-hue-rotate",
|
|
2991
|
+
"Hue Rotate",
|
|
2992
|
+
typeof hueRotate === "number" ? hueRotate : 0,
|
|
2993
|
+
0,
|
|
2994
|
+
350,
|
|
2995
|
+
5
|
|
2996
|
+
);
|
|
2535
2997
|
}
|
|
2536
2998
|
/**
|
|
2537
2999
|
* Add controls for symbol layers
|
|
@@ -2539,15 +3001,39 @@ class LayerControl {
|
|
|
2539
3001
|
addSymbolControls(container, layerId) {
|
|
2540
3002
|
const textColor = this.map.getPaintProperty(layerId, "text-color");
|
|
2541
3003
|
if (textColor !== void 0) {
|
|
2542
|
-
this.createColorControl(
|
|
3004
|
+
this.createColorControl(
|
|
3005
|
+
container,
|
|
3006
|
+
layerId,
|
|
3007
|
+
"text-color",
|
|
3008
|
+
"Text Color",
|
|
3009
|
+
normalizeColor(textColor)
|
|
3010
|
+
);
|
|
2543
3011
|
}
|
|
2544
3012
|
const textOpacity = this.map.getPaintProperty(layerId, "text-opacity");
|
|
2545
3013
|
if (textOpacity !== void 0 && typeof textOpacity === "number") {
|
|
2546
|
-
this.createSliderControl(
|
|
3014
|
+
this.createSliderControl(
|
|
3015
|
+
container,
|
|
3016
|
+
layerId,
|
|
3017
|
+
"text-opacity",
|
|
3018
|
+
"Text Opacity",
|
|
3019
|
+
textOpacity,
|
|
3020
|
+
0,
|
|
3021
|
+
1,
|
|
3022
|
+
0.05
|
|
3023
|
+
);
|
|
2547
3024
|
}
|
|
2548
3025
|
const iconOpacity = this.map.getPaintProperty(layerId, "icon-opacity");
|
|
2549
3026
|
if (iconOpacity !== void 0 && typeof iconOpacity === "number") {
|
|
2550
|
-
this.createSliderControl(
|
|
3027
|
+
this.createSliderControl(
|
|
3028
|
+
container,
|
|
3029
|
+
layerId,
|
|
3030
|
+
"icon-opacity",
|
|
3031
|
+
"Icon Opacity",
|
|
3032
|
+
iconOpacity,
|
|
3033
|
+
0,
|
|
3034
|
+
1,
|
|
3035
|
+
0.05
|
|
3036
|
+
);
|
|
2551
3037
|
}
|
|
2552
3038
|
}
|
|
2553
3039
|
/**
|
|
@@ -2630,7 +3116,9 @@ class LayerControl {
|
|
|
2630
3116
|
restoreOriginalStyle(this.map, layerId, this.state.originalStyles);
|
|
2631
3117
|
const editor = this.styleEditors.get(layerId);
|
|
2632
3118
|
if (editor) {
|
|
2633
|
-
const sliders = editor.querySelectorAll(
|
|
3119
|
+
const sliders = editor.querySelectorAll(
|
|
3120
|
+
".style-control-slider"
|
|
3121
|
+
);
|
|
2634
3122
|
sliders.forEach((slider) => {
|
|
2635
3123
|
var _a;
|
|
2636
3124
|
const property = slider.dataset.property;
|
|
@@ -2638,7 +3126,9 @@ class LayerControl {
|
|
|
2638
3126
|
const value = this.map.getPaintProperty(layerId, property);
|
|
2639
3127
|
if (value !== void 0 && typeof value === "number") {
|
|
2640
3128
|
slider.value = String(value);
|
|
2641
|
-
const valueDisplay = (_a = slider.parentElement) == null ? void 0 : _a.querySelector(
|
|
3129
|
+
const valueDisplay = (_a = slider.parentElement) == null ? void 0 : _a.querySelector(
|
|
3130
|
+
".style-control-value"
|
|
3131
|
+
);
|
|
2642
3132
|
if (valueDisplay) {
|
|
2643
3133
|
const step = parseFloat(slider.step);
|
|
2644
3134
|
valueDisplay.textContent = formatNumericValue(value, step);
|
|
@@ -2646,7 +3136,9 @@ class LayerControl {
|
|
|
2646
3136
|
}
|
|
2647
3137
|
}
|
|
2648
3138
|
});
|
|
2649
|
-
const colorPickers = editor.querySelectorAll(
|
|
3139
|
+
const colorPickers = editor.querySelectorAll(
|
|
3140
|
+
".style-control-color-picker"
|
|
3141
|
+
);
|
|
2650
3142
|
colorPickers.forEach((picker) => {
|
|
2651
3143
|
var _a;
|
|
2652
3144
|
const property = picker.dataset.property;
|
|
@@ -2655,7 +3147,9 @@ class LayerControl {
|
|
|
2655
3147
|
if (value !== void 0) {
|
|
2656
3148
|
const hexColor = normalizeColor(value);
|
|
2657
3149
|
picker.value = hexColor;
|
|
2658
|
-
const hexDisplay = (_a = picker.parentElement) == null ? void 0 : _a.querySelector(
|
|
3150
|
+
const hexDisplay = (_a = picker.parentElement) == null ? void 0 : _a.querySelector(
|
|
3151
|
+
".style-control-color-value"
|
|
3152
|
+
);
|
|
2659
3153
|
if (hexDisplay) {
|
|
2660
3154
|
hexDisplay.textContent = hexColor;
|
|
2661
3155
|
}
|
|
@@ -2703,8 +3197,12 @@ class LayerControl {
|
|
|
2703
3197
|
const layerItems = this.panel.querySelectorAll(".layer-control-item");
|
|
2704
3198
|
layerItems.forEach((item) => {
|
|
2705
3199
|
if (item.dataset.layerId === layerId) {
|
|
2706
|
-
const checkbox = item.querySelector(
|
|
2707
|
-
|
|
3200
|
+
const checkbox = item.querySelector(
|
|
3201
|
+
".layer-control-checkbox"
|
|
3202
|
+
);
|
|
3203
|
+
const opacitySlider = item.querySelector(
|
|
3204
|
+
".layer-control-opacity"
|
|
3205
|
+
);
|
|
2708
3206
|
if (checkbox) {
|
|
2709
3207
|
checkbox.checked = visible;
|
|
2710
3208
|
}
|
|
@@ -2728,7 +3226,9 @@ class LayerControl {
|
|
|
2728
3226
|
return;
|
|
2729
3227
|
}
|
|
2730
3228
|
const currentMapLayerIds = new Set(style.layers.map((layer) => layer.id));
|
|
2731
|
-
const isAutoDetectMode = this.targetLayers.length === 0 || this.targetLayers.length === 1 && this.targetLayers[0] === "Background" || this.targetLayers.every(
|
|
3229
|
+
const isAutoDetectMode = this.targetLayers.length === 0 || this.targetLayers.length === 1 && this.targetLayers[0] === "Background" || this.targetLayers.every(
|
|
3230
|
+
(id) => id === "Background" || this.state.layerStates[id]
|
|
3231
|
+
);
|
|
2732
3232
|
const newLayers = [];
|
|
2733
3233
|
const useBasemapStyleDetection = this.basemapLayerIds !== null && this.basemapLayerIds.size > 0;
|
|
2734
3234
|
const useInitialLayerDetection = !useBasemapStyleDetection && this.initialLayerIds !== null && this.initialLayerIds.size > 0;
|
|
@@ -2783,7 +3283,9 @@ class LayerControl {
|
|
|
2783
3283
|
if (removedLayers.length > 0) {
|
|
2784
3284
|
removedLayers.forEach((layerId) => {
|
|
2785
3285
|
delete this.state.layerStates[layerId];
|
|
2786
|
-
const itemEl = this.panel.querySelector(
|
|
3286
|
+
const itemEl = this.panel.querySelector(
|
|
3287
|
+
`[data-layer-id="${layerId}"]`
|
|
3288
|
+
);
|
|
2787
3289
|
if (itemEl) {
|
|
2788
3290
|
itemEl.remove();
|
|
2789
3291
|
}
|
|
@@ -2830,7 +3332,9 @@ class LayerControl {
|
|
|
2830
3332
|
const state = this.state.layerStates[layerId];
|
|
2831
3333
|
if (state.isCustomLayer && !customLayerIds.includes(layerId)) {
|
|
2832
3334
|
delete this.state.layerStates[layerId];
|
|
2833
|
-
const itemEl = this.panel.querySelector(
|
|
3335
|
+
const itemEl = this.panel.querySelector(
|
|
3336
|
+
`[data-layer-id="${layerId}"]`
|
|
3337
|
+
);
|
|
2834
3338
|
if (itemEl) {
|
|
2835
3339
|
itemEl.remove();
|
|
2836
3340
|
}
|
|
@@ -2853,12 +3357,14 @@ class LayerControl {
|
|
|
2853
3357
|
registerCustomAdapter(adapter) {
|
|
2854
3358
|
if (!this.customLayerRegistry) {
|
|
2855
3359
|
this.customLayerRegistry = new CustomLayerRegistry();
|
|
2856
|
-
this.customLayerUnsubscribe = this.customLayerRegistry.onChange(
|
|
2857
|
-
|
|
2858
|
-
|
|
3360
|
+
this.customLayerUnsubscribe = this.customLayerRegistry.onChange(
|
|
3361
|
+
(event, layerId) => {
|
|
3362
|
+
if (event === "add" && layerId) {
|
|
3363
|
+
this.removedCustomLayerIds.delete(layerId);
|
|
3364
|
+
}
|
|
3365
|
+
this.checkForNewLayers();
|
|
2859
3366
|
}
|
|
2860
|
-
|
|
2861
|
-
});
|
|
3367
|
+
);
|
|
2862
3368
|
}
|
|
2863
3369
|
this.customLayerRegistry.register(adapter);
|
|
2864
3370
|
if (this.panel) {
|
|
@@ -2905,20 +3411,29 @@ class LayerControl {
|
|
|
2905
3411
|
}
|
|
2906
3412
|
this.hideContextMenu();
|
|
2907
3413
|
});
|
|
2908
|
-
const moveBottomItem = this.createContextMenuItem(
|
|
2909
|
-
|
|
2910
|
-
|
|
3414
|
+
const moveBottomItem = this.createContextMenuItem(
|
|
3415
|
+
"Move to Bottom",
|
|
3416
|
+
"⤓",
|
|
3417
|
+
() => {
|
|
3418
|
+
if (this.state.contextMenu.targetLayerId) {
|
|
3419
|
+
this.moveLayerToBottom(this.state.contextMenu.targetLayerId);
|
|
3420
|
+
}
|
|
3421
|
+
this.hideContextMenu();
|
|
2911
3422
|
}
|
|
2912
|
-
|
|
2913
|
-
});
|
|
3423
|
+
);
|
|
2914
3424
|
const sep2 = document.createElement("div");
|
|
2915
3425
|
sep2.className = "context-menu-separator";
|
|
2916
|
-
const removeItem = this.createContextMenuItem(
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
3426
|
+
const removeItem = this.createContextMenuItem(
|
|
3427
|
+
"Remove Layer",
|
|
3428
|
+
"🗑️",
|
|
3429
|
+
() => {
|
|
3430
|
+
if (this.state.contextMenu.targetLayerId) {
|
|
3431
|
+
this.removeLayer(this.state.contextMenu.targetLayerId);
|
|
3432
|
+
}
|
|
3433
|
+
this.hideContextMenu();
|
|
3434
|
+
},
|
|
3435
|
+
true
|
|
3436
|
+
);
|
|
2922
3437
|
menu.appendChild(renameItem);
|
|
2923
3438
|
menu.appendChild(zoomItem);
|
|
2924
3439
|
menu.appendChild(sep1);
|
|
@@ -3048,7 +3563,9 @@ class LayerControl {
|
|
|
3048
3563
|
if ((layerState == null ? void 0 : layerState.isCustomLayer) && this.customLayerRegistry) {
|
|
3049
3564
|
const bounds = this.customLayerRegistry.getBounds(layerId);
|
|
3050
3565
|
if (bounds) {
|
|
3051
|
-
this.map.fitBounds(bounds, {
|
|
3566
|
+
this.map.fitBounds(bounds, {
|
|
3567
|
+
padding: 50
|
|
3568
|
+
});
|
|
3052
3569
|
return;
|
|
3053
3570
|
}
|
|
3054
3571
|
}
|
|
@@ -3080,7 +3597,9 @@ class LayerControl {
|
|
|
3080
3597
|
} else if (feature.geometry.type === "LineString" || feature.geometry.type === "MultiPoint") {
|
|
3081
3598
|
feature.geometry.coordinates.forEach(processCoords);
|
|
3082
3599
|
} else if (feature.geometry.type === "Polygon" || feature.geometry.type === "MultiLineString") {
|
|
3083
|
-
feature.geometry.coordinates.forEach(
|
|
3600
|
+
feature.geometry.coordinates.forEach(
|
|
3601
|
+
(ring) => ring.forEach(processCoords)
|
|
3602
|
+
);
|
|
3084
3603
|
} else if (feature.geometry.type === "MultiPolygon") {
|
|
3085
3604
|
feature.geometry.coordinates.forEach(
|
|
3086
3605
|
(polygon) => polygon.forEach((ring) => ring.forEach(processCoords))
|
|
@@ -3088,7 +3607,13 @@ class LayerControl {
|
|
|
3088
3607
|
}
|
|
3089
3608
|
});
|
|
3090
3609
|
if (minLng !== Infinity && minLat !== Infinity && maxLng !== -Infinity && maxLat !== -Infinity) {
|
|
3091
|
-
this.map.fitBounds(
|
|
3610
|
+
this.map.fitBounds(
|
|
3611
|
+
[
|
|
3612
|
+
[minLng, minLat],
|
|
3613
|
+
[maxLng, maxLat]
|
|
3614
|
+
],
|
|
3615
|
+
{ padding: 50 }
|
|
3616
|
+
);
|
|
3092
3617
|
}
|
|
3093
3618
|
} catch (error) {
|
|
3094
3619
|
console.warn(`Failed to zoom to layer ${layerId}:`, error);
|
|
@@ -3102,15 +3627,21 @@ class LayerControl {
|
|
|
3102
3627
|
const style = this.map.getStyle();
|
|
3103
3628
|
if (!(style == null ? void 0 : style.layers)) return [];
|
|
3104
3629
|
const mapLayerIds = style.layers.map((l) => l.id);
|
|
3105
|
-
const userLayerIds = Object.keys(this.state.layerStates).filter(
|
|
3106
|
-
|
|
3630
|
+
const userLayerIds = Object.keys(this.state.layerStates).filter(
|
|
3631
|
+
(id) => id !== "Background"
|
|
3632
|
+
);
|
|
3633
|
+
const mapLibreLayers = userLayerIds.filter(
|
|
3634
|
+
(id) => mapLayerIds.includes(id)
|
|
3635
|
+
);
|
|
3107
3636
|
const customLayers = userLayerIds.filter(
|
|
3108
3637
|
(id) => {
|
|
3109
3638
|
var _a;
|
|
3110
3639
|
return ((_a = this.state.layerStates[id]) == null ? void 0 : _a.isCustomLayer) && !mapLayerIds.includes(id);
|
|
3111
3640
|
}
|
|
3112
3641
|
);
|
|
3113
|
-
const sortedMapLibreLayers = mapLibreLayers.sort(
|
|
3642
|
+
const sortedMapLibreLayers = mapLibreLayers.sort(
|
|
3643
|
+
(a, b) => mapLayerIds.indexOf(b) - mapLayerIds.indexOf(a)
|
|
3644
|
+
);
|
|
3114
3645
|
return [...customLayers, ...sortedMapLibreLayers];
|
|
3115
3646
|
}
|
|
3116
3647
|
/**
|
|
@@ -3152,7 +3683,10 @@ class LayerControl {
|
|
|
3152
3683
|
}
|
|
3153
3684
|
const newLayerStates = {};
|
|
3154
3685
|
const orderedIds = [...layerIds];
|
|
3155
|
-
[orderedIds[index], orderedIds[index - 1]] = [
|
|
3686
|
+
[orderedIds[index], orderedIds[index - 1]] = [
|
|
3687
|
+
orderedIds[index - 1],
|
|
3688
|
+
orderedIds[index]
|
|
3689
|
+
];
|
|
3156
3690
|
if (this.state.layerStates["Background"]) {
|
|
3157
3691
|
newLayerStates["Background"] = this.state.layerStates["Background"];
|
|
3158
3692
|
}
|
|
@@ -3216,7 +3750,10 @@ class LayerControl {
|
|
|
3216
3750
|
}
|
|
3217
3751
|
const newLayerStates = {};
|
|
3218
3752
|
const orderedIds = [...layerIds];
|
|
3219
|
-
[orderedIds[index], orderedIds[index + 1]] = [
|
|
3753
|
+
[orderedIds[index], orderedIds[index + 1]] = [
|
|
3754
|
+
orderedIds[index + 1],
|
|
3755
|
+
orderedIds[index]
|
|
3756
|
+
];
|
|
3220
3757
|
if (this.state.layerStates["Background"]) {
|
|
3221
3758
|
newLayerStates["Background"] = this.state.layerStates["Background"];
|
|
3222
3759
|
}
|
|
@@ -3240,7 +3777,11 @@ class LayerControl {
|
|
|
3240
3777
|
if (index < 0 || index === layerIds.length - 1) return;
|
|
3241
3778
|
const isCustom = (_a = this.state.layerStates[layerId]) == null ? void 0 : _a.isCustomLayer;
|
|
3242
3779
|
if (!isCustom && this.isMapLibreLayer(layerId)) {
|
|
3243
|
-
const bottomLayerId = this.findNextMapLibreLayer(
|
|
3780
|
+
const bottomLayerId = this.findNextMapLibreLayer(
|
|
3781
|
+
layerIds,
|
|
3782
|
+
layerIds.length - 1,
|
|
3783
|
+
-1
|
|
3784
|
+
);
|
|
3244
3785
|
if (bottomLayerId && bottomLayerId !== layerId) {
|
|
3245
3786
|
try {
|
|
3246
3787
|
this.map.moveLayer(layerId, bottomLayerId);
|
|
@@ -3328,7 +3869,9 @@ class LayerControl {
|
|
|
3328
3869
|
this.map.removeLayer(layerId);
|
|
3329
3870
|
if (sourceId) {
|
|
3330
3871
|
const style = this.map.getStyle();
|
|
3331
|
-
const sourceStillUsed = (_a = style == null ? void 0 : style.layers) == null ? void 0 : _a.some(
|
|
3872
|
+
const sourceStillUsed = (_a = style == null ? void 0 : style.layers) == null ? void 0 : _a.some(
|
|
3873
|
+
(l) => l.source === sourceId
|
|
3874
|
+
);
|
|
3332
3875
|
if (!sourceStillUsed) {
|
|
3333
3876
|
try {
|
|
3334
3877
|
this.map.removeSource(sourceId);
|
|
@@ -3395,7 +3938,9 @@ class LayerControl {
|
|
|
3395
3938
|
*/
|
|
3396
3939
|
startDrag(layerId, e) {
|
|
3397
3940
|
var _a;
|
|
3398
|
-
const itemEl = this.panel.querySelector(
|
|
3941
|
+
const itemEl = this.panel.querySelector(
|
|
3942
|
+
`[data-layer-id="${layerId}"]`
|
|
3943
|
+
);
|
|
3399
3944
|
if (!itemEl) return;
|
|
3400
3945
|
const rect = itemEl.getBoundingClientRect();
|
|
3401
3946
|
this.state.drag = {
|
|
@@ -3446,7 +3991,11 @@ class LayerControl {
|
|
|
3446
3991
|
clone.style.top = `${currentTop + deltaY}px`;
|
|
3447
3992
|
this.state.drag.startY = e.clientY;
|
|
3448
3993
|
this.state.drag.currentY = e.clientY;
|
|
3449
|
-
const items = Array.from(
|
|
3994
|
+
const items = Array.from(
|
|
3995
|
+
this.panel.querySelectorAll(".layer-control-item:not(.dragging)")
|
|
3996
|
+
).filter(
|
|
3997
|
+
(item) => item.dataset.layerId !== "Background"
|
|
3998
|
+
);
|
|
3450
3999
|
for (const item of items) {
|
|
3451
4000
|
const itemRect = item.getBoundingClientRect();
|
|
3452
4001
|
if (e.clientY >= itemRect.top && e.clientY <= itemRect.bottom) {
|
|
@@ -3472,7 +4021,9 @@ class LayerControl {
|
|
|
3472
4021
|
if (!this.state.drag.active) return;
|
|
3473
4022
|
const layerId = this.state.drag.layerId;
|
|
3474
4023
|
const placeholder = this.state.drag.placeholder;
|
|
3475
|
-
const itemEl = this.panel.querySelector(
|
|
4024
|
+
const itemEl = this.panel.querySelector(
|
|
4025
|
+
`[data-layer-id="${layerId}"]`
|
|
4026
|
+
);
|
|
3476
4027
|
if (itemEl && placeholder) {
|
|
3477
4028
|
(_a = placeholder.parentNode) == null ? void 0 : _a.insertBefore(itemEl, placeholder);
|
|
3478
4029
|
itemEl.classList.remove("dragging");
|