maplibre-gl-layer-control 0.8.2 → 0.9.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/dist/index.cjs CHANGED
@@ -120,6 +120,31 @@ class CustomLayerRegistry {
120
120
  }
121
121
  return null;
122
122
  }
123
+ /**
124
+ * Get the bounds of a custom layer (for zoom-to-layer).
125
+ * @param layerId The layer ID
126
+ * @returns The bounds [west, south, east, north] or null if not available
127
+ */
128
+ getBounds(layerId) {
129
+ const adapter = this.getAdapterForLayer(layerId);
130
+ if (adapter && adapter.getBounds) {
131
+ return adapter.getBounds(layerId);
132
+ }
133
+ return null;
134
+ }
135
+ /**
136
+ * Remove a custom layer through its adapter.
137
+ * @param layerId The layer ID to remove
138
+ * @returns true if the operation was handled by an adapter
139
+ */
140
+ removeLayer(layerId) {
141
+ const adapter = this.getAdapterForLayer(layerId);
142
+ if (adapter && adapter.removeLayer) {
143
+ adapter.removeLayer(layerId);
144
+ return true;
145
+ }
146
+ return false;
147
+ }
123
148
  /**
124
149
  * Subscribe to layer changes across all adapters.
125
150
  * @param callback Function called when layers are added or removed
@@ -670,6 +695,13 @@ class LayerControl {
670
695
  __publicField(this, "widthDragStartX", null);
671
696
  __publicField(this, "widthDragStartWidth", null);
672
697
  __publicField(this, "widthFrame", null);
698
+ // Context menu and drag-drop
699
+ __publicField(this, "contextMenuEl", null);
700
+ __publicField(this, "enableContextMenu");
701
+ __publicField(this, "enableDragAndDrop");
702
+ __publicField(this, "onLayerRename");
703
+ __publicField(this, "onLayerReorder");
704
+ __publicField(this, "onLayerRemove");
673
705
  this.minPanelWidth = options.panelMinWidth || 240;
674
706
  this.maxPanelWidth = options.panelMaxWidth || 420;
675
707
  this.maxPanelHeight = options.panelMaxHeight || 600;
@@ -678,16 +710,37 @@ class LayerControl {
678
710
  this.showLayerSymbol = options.showLayerSymbol !== false;
679
711
  this.excludeDrawnLayers = options.excludeDrawnLayers !== false;
680
712
  this.excludeLayerPatterns = this.wildcardPatternsToRegex(options.excludeLayers || []);
713
+ this.enableContextMenu = options.enableContextMenu !== false;
714
+ this.enableDragAndDrop = options.enableDragAndDrop !== false;
715
+ this.onLayerRename = options.onLayerRename;
716
+ this.onLayerReorder = options.onLayerReorder;
717
+ this.onLayerRemove = options.onLayerRemove;
681
718
  this.state = {
682
719
  collapsed: options.collapsed !== false,
683
- panelWidth: options.panelWidth || 360,
720
+ panelWidth: options.panelWidth || 350,
684
721
  activeStyleEditor: null,
685
722
  layerStates: options.layerStates || {},
686
723
  originalStyles: /* @__PURE__ */ new Map(),
687
724
  userInteractingWithSlider: false,
688
725
  backgroundLegendOpen: false,
689
726
  backgroundLayerVisibility: /* @__PURE__ */ new Map(),
690
- onlyRenderedFilter: false
727
+ onlyRenderedFilter: false,
728
+ contextMenu: {
729
+ visible: false,
730
+ targetLayerId: null,
731
+ x: 0,
732
+ y: 0
733
+ },
734
+ renamingLayerId: null,
735
+ customLayerNames: /* @__PURE__ */ new Map(),
736
+ drag: {
737
+ active: false,
738
+ layerId: null,
739
+ startY: 0,
740
+ currentY: 0,
741
+ placeholder: null,
742
+ draggedElement: null
743
+ }
691
744
  };
692
745
  this.targetLayers = options.layers || Object.keys(this.state.layerStates);
693
746
  this.styleEditors = /* @__PURE__ */ new Map();
@@ -721,6 +774,10 @@ class LayerControl {
721
774
  this.panel = this.createPanel();
722
775
  this.container.appendChild(this.button);
723
776
  this.mapContainer.appendChild(this.panel);
777
+ if (this.enableContextMenu) {
778
+ this.contextMenuEl = this.createContextMenu();
779
+ this.mapContainer.appendChild(this.contextMenuEl);
780
+ }
724
781
  this.updateWidthDisplay();
725
782
  this.setupEventListeners();
726
783
  if (this.basemapStyleUrl && !this.basemapLayerIds) {
@@ -777,7 +834,7 @@ class LayerControl {
777
834
  * Called when the control is removed from the map
778
835
  */
779
836
  onRemove() {
780
- var _a, _b;
837
+ var _a, _b, _c;
781
838
  if (this.customLayerUnsubscribe) {
782
839
  this.customLayerUnsubscribe();
783
840
  this.customLayerUnsubscribe = null;
@@ -794,8 +851,13 @@ class LayerControl {
794
851
  this.map.off("resize", this.mapResizeHandler);
795
852
  this.mapResizeHandler = null;
796
853
  }
797
- (_a = this.panel.parentNode) == null ? void 0 : _a.removeChild(this.panel);
798
- (_b = this.container.parentNode) == null ? void 0 : _b.removeChild(this.container);
854
+ if (this.contextMenuEl) {
855
+ (_a = this.contextMenuEl.parentNode) == null ? void 0 : _a.removeChild(this.contextMenuEl);
856
+ this.contextMenuEl = null;
857
+ }
858
+ this.cleanupDragState();
859
+ (_b = this.panel.parentNode) == null ? void 0 : _b.removeChild(this.panel);
860
+ (_c = this.container.parentNode) == null ? void 0 : _c.removeChild(this.container);
799
861
  }
800
862
  /**
801
863
  * Auto-detect layers from the map and populate layerStates
@@ -1371,6 +1433,11 @@ class LayerControl {
1371
1433
  this.button.addEventListener("click", () => this.toggle());
1372
1434
  document.addEventListener("click", (e) => {
1373
1435
  const target = e.target;
1436
+ if (this.contextMenuEl && this.state.contextMenu.visible) {
1437
+ if (!this.contextMenuEl.contains(target)) {
1438
+ this.hideContextMenu();
1439
+ }
1440
+ }
1374
1441
  if (!this.container.contains(target) && !this.panel.contains(target)) {
1375
1442
  this.collapse();
1376
1443
  }
@@ -1467,6 +1534,15 @@ class LayerControl {
1467
1534
  item.setAttribute("data-layer-id", layerId);
1468
1535
  const row = document.createElement("div");
1469
1536
  row.className = "layer-control-row";
1537
+ if (this.enableDragAndDrop) {
1538
+ if (layerId === "Background") {
1539
+ const disabledHandle = this.createDisabledDragHandle();
1540
+ row.appendChild(disabledHandle);
1541
+ } else {
1542
+ const dragHandle = this.createDragHandle(layerId);
1543
+ row.appendChild(dragHandle);
1544
+ }
1545
+ }
1470
1546
  const checkbox = document.createElement("input");
1471
1547
  checkbox.type = "checkbox";
1472
1548
  checkbox.className = "layer-control-checkbox";
@@ -1474,10 +1550,11 @@ class LayerControl {
1474
1550
  checkbox.addEventListener("change", () => {
1475
1551
  this.toggleLayerVisibility(layerId, checkbox.checked);
1476
1552
  });
1553
+ const displayName = this.state.customLayerNames.get(layerId) || state.name || layerId;
1477
1554
  const name = document.createElement("span");
1478
1555
  name.className = "layer-control-name";
1479
- name.textContent = state.name || layerId;
1480
- name.title = state.name || layerId;
1556
+ name.textContent = displayName;
1557
+ name.title = displayName;
1481
1558
  row.appendChild(checkbox);
1482
1559
  if (this.showLayerSymbol) {
1483
1560
  if (layerId === "Background") {
@@ -1524,6 +1601,13 @@ class LayerControl {
1524
1601
  }
1525
1602
  }
1526
1603
  item.appendChild(row);
1604
+ if (this.enableContextMenu && layerId !== "Background") {
1605
+ row.addEventListener("contextmenu", (e) => {
1606
+ e.preventDefault();
1607
+ e.stopPropagation();
1608
+ this.showContextMenu(layerId, e.clientX, e.clientY);
1609
+ });
1610
+ }
1527
1611
  this.panel.appendChild(item);
1528
1612
  }
1529
1613
  /**
@@ -2243,7 +2327,7 @@ class LayerControl {
2243
2327
  const contrast = this.map.getPaintProperty(layerId, "raster-contrast");
2244
2328
  this.createSliderControl(container, layerId, "raster-contrast", "Contrast", typeof contrast === "number" ? contrast : 0, -1, 1, 0.05);
2245
2329
  const hueRotate = this.map.getPaintProperty(layerId, "raster-hue-rotate");
2246
- this.createSliderControl(container, layerId, "raster-hue-rotate", "Hue Rotate", typeof hueRotate === "number" ? hueRotate : 0, 0, 360, 5);
2330
+ this.createSliderControl(container, layerId, "raster-hue-rotate", "Hue Rotate", typeof hueRotate === "number" ? hueRotate : 0, 0, 350, 5);
2247
2331
  }
2248
2332
  /**
2249
2333
  * Add controls for symbol layers
@@ -2439,6 +2523,9 @@ class LayerControl {
2439
2523
  if (layerId !== "Background" && !this.state.layerStates[layerId]) {
2440
2524
  const layer = this.map.getLayer(layerId);
2441
2525
  if (layer) {
2526
+ if (this.initialLayerIds !== null && this.initialLayerIds.has(layerId)) {
2527
+ return;
2528
+ }
2442
2529
  if (isAutoDetectMode) {
2443
2530
  if (this.excludeDrawnLayers && this.isDrawnLayer(layerId)) {
2444
2531
  return;
@@ -2446,6 +2533,9 @@ class LayerControl {
2446
2533
  if (this.isExcludedByPattern(layerId)) {
2447
2534
  return;
2448
2535
  }
2536
+ if (!this.isUserAddedLayer(layerId)) {
2537
+ return;
2538
+ }
2449
2539
  if (useBasemapStyleDetection) {
2450
2540
  if (this.basemapLayerIds.has(layerId)) {
2451
2541
  return;
@@ -2559,6 +2649,543 @@ class LayerControl {
2559
2649
  this.checkForNewLayers();
2560
2650
  }
2561
2651
  }
2652
+ // ===== Context Menu Methods =====
2653
+ /**
2654
+ * Create context menu element
2655
+ */
2656
+ createContextMenu() {
2657
+ const menu = document.createElement("div");
2658
+ menu.className = "layer-control-context-menu";
2659
+ menu.style.display = "none";
2660
+ const renameItem = this.createContextMenuItem("Rename", "✏️", () => {
2661
+ if (this.state.contextMenu.targetLayerId) {
2662
+ this.startRenaming(this.state.contextMenu.targetLayerId);
2663
+ }
2664
+ this.hideContextMenu();
2665
+ });
2666
+ const zoomItem = this.createContextMenuItem("Zoom to Layer", "🔍", () => {
2667
+ if (this.state.contextMenu.targetLayerId) {
2668
+ this.zoomToLayer(this.state.contextMenu.targetLayerId);
2669
+ }
2670
+ this.hideContextMenu();
2671
+ });
2672
+ const sep1 = document.createElement("div");
2673
+ sep1.className = "context-menu-separator";
2674
+ const moveUpItem = this.createContextMenuItem("Move Up", "↑", () => {
2675
+ if (this.state.contextMenu.targetLayerId) {
2676
+ this.moveLayerUp(this.state.contextMenu.targetLayerId);
2677
+ }
2678
+ this.hideContextMenu();
2679
+ });
2680
+ const moveTopItem = this.createContextMenuItem("Move to Top", "⤒", () => {
2681
+ if (this.state.contextMenu.targetLayerId) {
2682
+ this.moveLayerToTop(this.state.contextMenu.targetLayerId);
2683
+ }
2684
+ this.hideContextMenu();
2685
+ });
2686
+ const moveDownItem = this.createContextMenuItem("Move Down", "↓", () => {
2687
+ if (this.state.contextMenu.targetLayerId) {
2688
+ this.moveLayerDown(this.state.contextMenu.targetLayerId);
2689
+ }
2690
+ this.hideContextMenu();
2691
+ });
2692
+ const moveBottomItem = this.createContextMenuItem("Move to Bottom", "⤓", () => {
2693
+ if (this.state.contextMenu.targetLayerId) {
2694
+ this.moveLayerToBottom(this.state.contextMenu.targetLayerId);
2695
+ }
2696
+ this.hideContextMenu();
2697
+ });
2698
+ const sep2 = document.createElement("div");
2699
+ sep2.className = "context-menu-separator";
2700
+ const removeItem = this.createContextMenuItem("Remove Layer", "🗑️", () => {
2701
+ if (this.state.contextMenu.targetLayerId) {
2702
+ this.removeLayer(this.state.contextMenu.targetLayerId);
2703
+ }
2704
+ this.hideContextMenu();
2705
+ }, true);
2706
+ menu.appendChild(renameItem);
2707
+ menu.appendChild(zoomItem);
2708
+ menu.appendChild(sep1);
2709
+ menu.appendChild(moveUpItem);
2710
+ menu.appendChild(moveTopItem);
2711
+ menu.appendChild(moveDownItem);
2712
+ menu.appendChild(moveBottomItem);
2713
+ menu.appendChild(sep2);
2714
+ menu.appendChild(removeItem);
2715
+ return menu;
2716
+ }
2717
+ /**
2718
+ * Create a context menu item
2719
+ */
2720
+ createContextMenuItem(label, icon, onClick, isDanger = false) {
2721
+ const item = document.createElement("div");
2722
+ item.className = "context-menu-item" + (isDanger ? " context-menu-item-danger" : "");
2723
+ const iconEl = document.createElement("span");
2724
+ iconEl.className = "context-menu-item-icon";
2725
+ iconEl.textContent = icon;
2726
+ const labelEl = document.createElement("span");
2727
+ labelEl.className = "context-menu-item-label";
2728
+ labelEl.textContent = label;
2729
+ item.appendChild(iconEl);
2730
+ item.appendChild(labelEl);
2731
+ item.addEventListener("click", (e) => {
2732
+ e.stopPropagation();
2733
+ onClick();
2734
+ });
2735
+ return item;
2736
+ }
2737
+ /**
2738
+ * Show context menu at position
2739
+ */
2740
+ showContextMenu(layerId, x, y) {
2741
+ if (!this.contextMenuEl) return;
2742
+ this.state.contextMenu = {
2743
+ visible: true,
2744
+ targetLayerId: layerId,
2745
+ x,
2746
+ y
2747
+ };
2748
+ const mapRect = this.mapContainer.getBoundingClientRect();
2749
+ let menuX = x - mapRect.left;
2750
+ let menuY = y - mapRect.top;
2751
+ this.contextMenuEl.style.display = "block";
2752
+ const menuRect = this.contextMenuEl.getBoundingClientRect();
2753
+ if (menuX + menuRect.width > mapRect.width) {
2754
+ menuX = mapRect.width - menuRect.width - 5;
2755
+ }
2756
+ if (menuY + menuRect.height > mapRect.height) {
2757
+ menuY = mapRect.height - menuRect.height - 5;
2758
+ }
2759
+ this.contextMenuEl.style.left = `${menuX}px`;
2760
+ this.contextMenuEl.style.top = `${menuY}px`;
2761
+ }
2762
+ /**
2763
+ * Hide context menu
2764
+ */
2765
+ hideContextMenu() {
2766
+ if (!this.contextMenuEl) return;
2767
+ this.state.contextMenu = {
2768
+ visible: false,
2769
+ targetLayerId: null,
2770
+ x: 0,
2771
+ y: 0
2772
+ };
2773
+ this.contextMenuEl.style.display = "none";
2774
+ }
2775
+ /**
2776
+ * Start renaming a layer
2777
+ */
2778
+ startRenaming(layerId) {
2779
+ var _a;
2780
+ const itemEl = this.panel.querySelector(`[data-layer-id="${layerId}"]`);
2781
+ if (!itemEl) return;
2782
+ const nameEl = itemEl.querySelector(".layer-control-name");
2783
+ if (!nameEl) return;
2784
+ this.state.renamingLayerId = layerId;
2785
+ const currentName = this.state.customLayerNames.get(layerId) || ((_a = this.state.layerStates[layerId]) == null ? void 0 : _a.name) || layerId;
2786
+ const input = document.createElement("input");
2787
+ input.type = "text";
2788
+ input.className = "layer-control-name-input";
2789
+ input.value = currentName;
2790
+ const finishRename = () => {
2791
+ var _a2;
2792
+ const newName = input.value.trim() || currentName;
2793
+ const oldName = currentName;
2794
+ if (newName !== oldName) {
2795
+ this.state.customLayerNames.set(layerId, newName);
2796
+ if (this.state.layerStates[layerId]) {
2797
+ this.state.layerStates[layerId].name = newName;
2798
+ }
2799
+ (_a2 = this.onLayerRename) == null ? void 0 : _a2.call(this, layerId, oldName, newName);
2800
+ }
2801
+ const newNameEl = document.createElement("span");
2802
+ newNameEl.className = "layer-control-name";
2803
+ newNameEl.textContent = newName;
2804
+ newNameEl.title = newName;
2805
+ input.replaceWith(newNameEl);
2806
+ this.state.renamingLayerId = null;
2807
+ };
2808
+ input.addEventListener("blur", finishRename);
2809
+ input.addEventListener("keydown", (e) => {
2810
+ if (e.key === "Enter") {
2811
+ e.preventDefault();
2812
+ finishRename();
2813
+ } else if (e.key === "Escape") {
2814
+ e.preventDefault();
2815
+ const newNameEl = document.createElement("span");
2816
+ newNameEl.className = "layer-control-name";
2817
+ newNameEl.textContent = currentName;
2818
+ newNameEl.title = currentName;
2819
+ input.replaceWith(newNameEl);
2820
+ this.state.renamingLayerId = null;
2821
+ }
2822
+ });
2823
+ nameEl.replaceWith(input);
2824
+ input.focus();
2825
+ input.select();
2826
+ }
2827
+ /**
2828
+ * Zoom to a layer's bounds
2829
+ */
2830
+ zoomToLayer(layerId) {
2831
+ const layerState = this.state.layerStates[layerId];
2832
+ if ((layerState == null ? void 0 : layerState.isCustomLayer) && this.customLayerRegistry) {
2833
+ const bounds = this.customLayerRegistry.getBounds(layerId);
2834
+ if (bounds) {
2835
+ this.map.fitBounds(bounds, { padding: 50 });
2836
+ return;
2837
+ }
2838
+ }
2839
+ const layer = this.map.getLayer(layerId);
2840
+ if (!layer) return;
2841
+ try {
2842
+ const sourceId = layer.source;
2843
+ let features = sourceId ? this.map.querySourceFeatures(sourceId) : [];
2844
+ if (features.length === 0) {
2845
+ features = this.map.queryRenderedFeatures({ layers: [layerId] });
2846
+ }
2847
+ if (features.length === 0) return;
2848
+ let minLng = Infinity, minLat = Infinity, maxLng = -Infinity, maxLat = -Infinity;
2849
+ features.forEach((feature) => {
2850
+ if (!feature.geometry) return;
2851
+ const processCoords = (coords) => {
2852
+ if (typeof coords[0] === "number") {
2853
+ const [lng, lat] = coords;
2854
+ minLng = Math.min(minLng, lng);
2855
+ minLat = Math.min(minLat, lat);
2856
+ maxLng = Math.max(maxLng, lng);
2857
+ maxLat = Math.max(maxLat, lat);
2858
+ } else {
2859
+ coords.forEach(processCoords);
2860
+ }
2861
+ };
2862
+ if (feature.geometry.type === "Point") {
2863
+ processCoords(feature.geometry.coordinates);
2864
+ } else if (feature.geometry.type === "LineString" || feature.geometry.type === "MultiPoint") {
2865
+ feature.geometry.coordinates.forEach(processCoords);
2866
+ } else if (feature.geometry.type === "Polygon" || feature.geometry.type === "MultiLineString") {
2867
+ feature.geometry.coordinates.forEach((ring) => ring.forEach(processCoords));
2868
+ } else if (feature.geometry.type === "MultiPolygon") {
2869
+ feature.geometry.coordinates.forEach(
2870
+ (polygon) => polygon.forEach((ring) => ring.forEach(processCoords))
2871
+ );
2872
+ }
2873
+ });
2874
+ if (minLng !== Infinity && minLat !== Infinity && maxLng !== -Infinity && maxLat !== -Infinity) {
2875
+ this.map.fitBounds([[minLng, minLat], [maxLng, maxLat]], { padding: 50 });
2876
+ }
2877
+ } catch (error) {
2878
+ console.warn(`Failed to zoom to layer ${layerId}:`, error);
2879
+ }
2880
+ }
2881
+ /**
2882
+ * Get user layer IDs in current map order (top to bottom in UI = high z-index to low)
2883
+ */
2884
+ getUserLayerIdsInMapOrder() {
2885
+ const style = this.map.getStyle();
2886
+ if (!(style == null ? void 0 : style.layers)) return [];
2887
+ const mapLayerIds = style.layers.map((l) => l.id);
2888
+ const userLayerIds = Object.keys(this.state.layerStates).filter((id) => id !== "Background");
2889
+ return userLayerIds.filter((id) => mapLayerIds.includes(id)).sort((a, b) => mapLayerIds.indexOf(b) - mapLayerIds.indexOf(a));
2890
+ }
2891
+ /**
2892
+ * Move a layer up in UI (higher rendering order = move to higher z-index)
2893
+ */
2894
+ moveLayerUp(layerId) {
2895
+ var _a;
2896
+ const layerIds = this.getUserLayerIdsInMapOrder();
2897
+ const index = layerIds.indexOf(layerId);
2898
+ if (index <= 0) return;
2899
+ try {
2900
+ if (index === 1) {
2901
+ this.map.moveLayer(layerId);
2902
+ } else {
2903
+ const targetBeforeId = layerIds[index - 2];
2904
+ this.map.moveLayer(layerId, targetBeforeId);
2905
+ }
2906
+ } catch (e) {
2907
+ }
2908
+ this.rebuildLayerItems();
2909
+ (_a = this.onLayerReorder) == null ? void 0 : _a.call(this, this.getUserLayerIdsInMapOrder());
2910
+ }
2911
+ /**
2912
+ * Move a layer to the top (highest rendering order)
2913
+ */
2914
+ moveLayerToTop(layerId) {
2915
+ var _a;
2916
+ try {
2917
+ this.map.moveLayer(layerId);
2918
+ } catch (e) {
2919
+ }
2920
+ this.rebuildLayerItems();
2921
+ (_a = this.onLayerReorder) == null ? void 0 : _a.call(this, this.getUserLayerIdsInMapOrder());
2922
+ }
2923
+ /**
2924
+ * Move a layer down in UI (lower rendering order = move to lower z-index)
2925
+ */
2926
+ moveLayerDown(layerId) {
2927
+ var _a;
2928
+ const layerIds = this.getUserLayerIdsInMapOrder();
2929
+ const index = layerIds.indexOf(layerId);
2930
+ if (index < 0 || index >= layerIds.length - 1) return;
2931
+ const belowLayerId = layerIds[index + 1];
2932
+ try {
2933
+ this.map.moveLayer(layerId, belowLayerId);
2934
+ } catch (e) {
2935
+ }
2936
+ this.rebuildLayerItems();
2937
+ (_a = this.onLayerReorder) == null ? void 0 : _a.call(this, this.getUserLayerIdsInMapOrder());
2938
+ }
2939
+ /**
2940
+ * Move a layer to the bottom (lowest rendering order among user layers)
2941
+ */
2942
+ moveLayerToBottom(layerId) {
2943
+ var _a;
2944
+ const layerIds = this.getUserLayerIdsInMapOrder();
2945
+ if (layerIds.length <= 1) return;
2946
+ const index = layerIds.indexOf(layerId);
2947
+ if (index < 0 || index === layerIds.length - 1) return;
2948
+ const bottomLayerId = layerIds[layerIds.length - 1];
2949
+ try {
2950
+ this.map.moveLayer(layerId, bottomLayerId);
2951
+ } catch (e) {
2952
+ }
2953
+ this.rebuildLayerItems();
2954
+ (_a = this.onLayerReorder) == null ? void 0 : _a.call(this, this.getUserLayerIdsInMapOrder());
2955
+ }
2956
+ /**
2957
+ * Remove a layer from the map
2958
+ */
2959
+ removeLayer(layerId) {
2960
+ var _a, _b;
2961
+ const layerState = this.state.layerStates[layerId];
2962
+ if ((layerState == null ? void 0 : layerState.isCustomLayer) && this.customLayerRegistry) {
2963
+ this.customLayerRegistry.removeLayer(layerId);
2964
+ } else {
2965
+ try {
2966
+ const layer = this.map.getLayer(layerId);
2967
+ if (layer) {
2968
+ const sourceId = layer.source;
2969
+ this.map.removeLayer(layerId);
2970
+ if (sourceId) {
2971
+ const style = this.map.getStyle();
2972
+ const sourceStillUsed = (_a = style == null ? void 0 : style.layers) == null ? void 0 : _a.some((l) => l.source === sourceId);
2973
+ if (!sourceStillUsed) {
2974
+ try {
2975
+ this.map.removeSource(sourceId);
2976
+ } catch (e) {
2977
+ }
2978
+ }
2979
+ }
2980
+ }
2981
+ } catch (error) {
2982
+ console.warn(`Failed to remove layer ${layerId}:`, error);
2983
+ }
2984
+ }
2985
+ delete this.state.layerStates[layerId];
2986
+ this.state.customLayerNames.delete(layerId);
2987
+ const itemEl = this.panel.querySelector(`[data-layer-id="${layerId}"]`);
2988
+ if (itemEl) {
2989
+ itemEl.remove();
2990
+ }
2991
+ (_b = this.onLayerRemove) == null ? void 0 : _b.call(this, layerId);
2992
+ }
2993
+ /**
2994
+ * Rebuild layer items to reflect current order
2995
+ */
2996
+ rebuildLayerItems() {
2997
+ const userLayerIds = this.getUserLayerIdsInMapOrder();
2998
+ const newLayerStates = {};
2999
+ if (this.state.layerStates["Background"]) {
3000
+ newLayerStates["Background"] = this.state.layerStates["Background"];
3001
+ }
3002
+ userLayerIds.forEach((id) => {
3003
+ if (this.state.layerStates[id]) {
3004
+ newLayerStates[id] = this.state.layerStates[id];
3005
+ }
3006
+ });
3007
+ this.state.layerStates = newLayerStates;
3008
+ this.buildLayerItems();
3009
+ }
3010
+ // ===== Drag and Drop Methods =====
3011
+ /**
3012
+ * Create drag handle element
3013
+ */
3014
+ createDragHandle(layerId) {
3015
+ const handle = document.createElement("div");
3016
+ handle.className = "layer-control-drag-handle";
3017
+ handle.innerHTML = `<svg viewBox="0 0 16 16" fill="currentColor">
3018
+ <circle cx="5" cy="3" r="1.5"/>
3019
+ <circle cx="11" cy="3" r="1.5"/>
3020
+ <circle cx="5" cy="8" r="1.5"/>
3021
+ <circle cx="11" cy="8" r="1.5"/>
3022
+ <circle cx="5" cy="13" r="1.5"/>
3023
+ <circle cx="11" cy="13" r="1.5"/>
3024
+ </svg>`;
3025
+ handle.title = "Drag to reorder";
3026
+ handle.addEventListener("pointerdown", (e) => {
3027
+ e.preventDefault();
3028
+ e.stopPropagation();
3029
+ this.startDrag(layerId, e);
3030
+ });
3031
+ return handle;
3032
+ }
3033
+ /**
3034
+ * Create a disabled drag handle for alignment (used for Background layer)
3035
+ */
3036
+ createDisabledDragHandle() {
3037
+ const handle = document.createElement("div");
3038
+ handle.className = "layer-control-drag-handle layer-control-drag-handle-disabled";
3039
+ handle.innerHTML = `<svg viewBox="0 0 16 16" fill="currentColor">
3040
+ <circle cx="5" cy="3" r="1.5"/>
3041
+ <circle cx="11" cy="3" r="1.5"/>
3042
+ <circle cx="5" cy="8" r="1.5"/>
3043
+ <circle cx="11" cy="8" r="1.5"/>
3044
+ <circle cx="5" cy="13" r="1.5"/>
3045
+ <circle cx="11" cy="13" r="1.5"/>
3046
+ </svg>`;
3047
+ return handle;
3048
+ }
3049
+ /**
3050
+ * Start dragging a layer
3051
+ */
3052
+ startDrag(layerId, e) {
3053
+ var _a;
3054
+ const itemEl = this.panel.querySelector(`[data-layer-id="${layerId}"]`);
3055
+ if (!itemEl) return;
3056
+ const rect = itemEl.getBoundingClientRect();
3057
+ this.state.drag = {
3058
+ active: true,
3059
+ layerId,
3060
+ startY: e.clientY,
3061
+ currentY: e.clientY,
3062
+ placeholder: null,
3063
+ draggedElement: null
3064
+ };
3065
+ this.panel.classList.add("dragging-active");
3066
+ itemEl.classList.add("dragging");
3067
+ const placeholder = document.createElement("div");
3068
+ placeholder.className = "layer-control-drop-placeholder";
3069
+ placeholder.style.height = `${rect.height}px`;
3070
+ (_a = itemEl.parentNode) == null ? void 0 : _a.insertBefore(placeholder, itemEl);
3071
+ this.state.drag.placeholder = placeholder;
3072
+ const clone = itemEl.cloneNode(true);
3073
+ clone.classList.remove("dragging");
3074
+ clone.className = "layer-control-item layer-control-item-dragging";
3075
+ clone.style.width = `${rect.width}px`;
3076
+ clone.style.left = `${rect.left}px`;
3077
+ clone.style.top = `${rect.top}px`;
3078
+ document.body.appendChild(clone);
3079
+ this.state.drag.draggedElement = clone;
3080
+ const onMove = (moveE) => this.onDragMove(moveE);
3081
+ const onEnd = (endE) => {
3082
+ document.removeEventListener("pointermove", onMove);
3083
+ document.removeEventListener("pointerup", onEnd);
3084
+ document.removeEventListener("pointercancel", onEnd);
3085
+ this.endDrag(endE);
3086
+ };
3087
+ document.addEventListener("pointermove", onMove);
3088
+ document.addEventListener("pointerup", onEnd);
3089
+ document.addEventListener("pointercancel", onEnd);
3090
+ }
3091
+ /**
3092
+ * Handle drag move
3093
+ */
3094
+ onDragMove(e) {
3095
+ var _a, _b;
3096
+ if (!this.state.drag.active || !this.state.drag.draggedElement) return;
3097
+ const placeholder = this.state.drag.placeholder;
3098
+ if (!placeholder) return;
3099
+ const deltaY = e.clientY - this.state.drag.startY;
3100
+ const clone = this.state.drag.draggedElement;
3101
+ const currentTop = parseFloat(clone.style.top) || 0;
3102
+ clone.style.top = `${currentTop + deltaY}px`;
3103
+ this.state.drag.startY = e.clientY;
3104
+ this.state.drag.currentY = e.clientY;
3105
+ const items = Array.from(this.panel.querySelectorAll(".layer-control-item:not(.dragging)")).filter((item) => item.dataset.layerId !== "Background");
3106
+ for (const item of items) {
3107
+ const itemRect = item.getBoundingClientRect();
3108
+ if (e.clientY >= itemRect.top && e.clientY <= itemRect.bottom) {
3109
+ const itemMiddle = itemRect.top + itemRect.height / 2;
3110
+ if (e.clientY < itemMiddle) {
3111
+ if (placeholder.nextSibling !== item) {
3112
+ (_a = item.parentNode) == null ? void 0 : _a.insertBefore(placeholder, item);
3113
+ }
3114
+ } else {
3115
+ if (placeholder.previousSibling !== item) {
3116
+ (_b = item.parentNode) == null ? void 0 : _b.insertBefore(placeholder, item.nextSibling);
3117
+ }
3118
+ }
3119
+ break;
3120
+ }
3121
+ }
3122
+ }
3123
+ /**
3124
+ * End dragging
3125
+ */
3126
+ endDrag(_e) {
3127
+ var _a;
3128
+ if (!this.state.drag.active) return;
3129
+ const layerId = this.state.drag.layerId;
3130
+ const placeholder = this.state.drag.placeholder;
3131
+ const itemEl = this.panel.querySelector(`[data-layer-id="${layerId}"]`);
3132
+ if (itemEl && placeholder) {
3133
+ (_a = placeholder.parentNode) == null ? void 0 : _a.insertBefore(itemEl, placeholder);
3134
+ itemEl.classList.remove("dragging");
3135
+ }
3136
+ this.cleanupDragState();
3137
+ this.applyUIOrderToMap();
3138
+ }
3139
+ /**
3140
+ * Clean up drag state
3141
+ */
3142
+ cleanupDragState() {
3143
+ if (this.state.drag.draggedElement) {
3144
+ this.state.drag.draggedElement.remove();
3145
+ }
3146
+ if (this.state.drag.placeholder) {
3147
+ this.state.drag.placeholder.remove();
3148
+ }
3149
+ this.panel.classList.remove("dragging-active");
3150
+ this.panel.querySelectorAll(".layer-control-item.dragging").forEach((el) => {
3151
+ el.classList.remove("dragging");
3152
+ });
3153
+ this.state.drag = {
3154
+ active: false,
3155
+ layerId: null,
3156
+ startY: 0,
3157
+ currentY: 0,
3158
+ placeholder: null,
3159
+ draggedElement: null
3160
+ };
3161
+ }
3162
+ /**
3163
+ * Apply UI order to map layers
3164
+ */
3165
+ applyUIOrderToMap() {
3166
+ var _a;
3167
+ const items = this.panel.querySelectorAll(".layer-control-item");
3168
+ const uiLayerIds = [];
3169
+ items.forEach((item) => {
3170
+ const layerId = item.dataset.layerId;
3171
+ if (layerId && layerId !== "Background") {
3172
+ uiLayerIds.push(layerId);
3173
+ }
3174
+ });
3175
+ const reversedIds = [...uiLayerIds].reverse();
3176
+ for (let i = 0; i < reversedIds.length; i++) {
3177
+ const layerId = reversedIds[i];
3178
+ const beforeId = i > 0 ? reversedIds[i - 1] : void 0;
3179
+ try {
3180
+ const layer = this.map.getLayer(layerId);
3181
+ if (layer) {
3182
+ this.map.moveLayer(layerId, beforeId);
3183
+ }
3184
+ } catch (e) {
3185
+ }
3186
+ }
3187
+ (_a = this.onLayerReorder) == null ? void 0 : _a.call(this, uiLayerIds);
3188
+ }
2562
3189
  }
2563
3190
  exports.CustomLayerRegistry = CustomLayerRegistry;
2564
3191
  exports.LayerControl = LayerControl;