maplibre-gl-layer-control 0.1.0 → 0.3.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 +18 -0
- package/dist/index.cjs +272 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +272 -2
- package/dist/index.mjs.map +1 -1
- package/dist/maplibre-gl-layer-control.css +183 -0
- package/dist/types/index.d.ts +375 -0
- package/package.json +6 -1
package/dist/index.mjs
CHANGED
|
@@ -195,7 +195,10 @@ class LayerControl {
|
|
|
195
195
|
activeStyleEditor: null,
|
|
196
196
|
layerStates: options.layerStates || {},
|
|
197
197
|
originalStyles: /* @__PURE__ */ new Map(),
|
|
198
|
-
userInteractingWithSlider: false
|
|
198
|
+
userInteractingWithSlider: false,
|
|
199
|
+
backgroundLegendOpen: false,
|
|
200
|
+
backgroundLayerVisibility: /* @__PURE__ */ new Map(),
|
|
201
|
+
onlyRenderedFilter: false
|
|
199
202
|
};
|
|
200
203
|
this.targetLayers = options.layers || Object.keys(this.state.layerStates);
|
|
201
204
|
this.styleEditors = /* @__PURE__ */ new Map();
|
|
@@ -627,7 +630,10 @@ class LayerControl {
|
|
|
627
630
|
row.appendChild(checkbox);
|
|
628
631
|
row.appendChild(name);
|
|
629
632
|
row.appendChild(opacity);
|
|
630
|
-
if (layerId
|
|
633
|
+
if (layerId === "Background") {
|
|
634
|
+
const legendButton = this.createBackgroundLegendButton();
|
|
635
|
+
row.appendChild(legendButton);
|
|
636
|
+
} else {
|
|
631
637
|
const styleButton = this.createStyleButton(layerId);
|
|
632
638
|
if (styleButton) {
|
|
633
639
|
row.appendChild(styleButton);
|
|
@@ -681,9 +687,19 @@ class LayerControl {
|
|
|
681
687
|
const styleLayers = this.map.getStyle().layers || [];
|
|
682
688
|
styleLayers.forEach((layer) => {
|
|
683
689
|
if (!this.isUserAddedLayer(layer.id)) {
|
|
690
|
+
this.state.backgroundLayerVisibility.set(layer.id, visible);
|
|
684
691
|
this.map.setLayoutProperty(layer.id, "visibility", visible ? "visible" : "none");
|
|
685
692
|
}
|
|
686
693
|
});
|
|
694
|
+
if (this.state.backgroundLegendOpen) {
|
|
695
|
+
const legendPanel = this.panel.querySelector(".layer-control-background-legend");
|
|
696
|
+
if (legendPanel) {
|
|
697
|
+
const checkboxes = legendPanel.querySelectorAll(".background-legend-checkbox");
|
|
698
|
+
checkboxes.forEach((checkbox) => {
|
|
699
|
+
checkbox.checked = visible;
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
}
|
|
687
703
|
}
|
|
688
704
|
/**
|
|
689
705
|
* Change opacity for all background layers (basemap layers)
|
|
@@ -702,6 +718,260 @@ class LayerControl {
|
|
|
702
718
|
}
|
|
703
719
|
});
|
|
704
720
|
}
|
|
721
|
+
// ===== Background Legend Methods =====
|
|
722
|
+
/**
|
|
723
|
+
* Create legend button for Background layer
|
|
724
|
+
*/
|
|
725
|
+
createBackgroundLegendButton() {
|
|
726
|
+
const button = document.createElement("button");
|
|
727
|
+
button.className = "layer-control-style-button layer-control-background-legend-button";
|
|
728
|
+
button.innerHTML = "⚙";
|
|
729
|
+
button.title = "Show background layer details";
|
|
730
|
+
button.setAttribute("aria-label", "Show background layer visibility controls");
|
|
731
|
+
button.setAttribute("aria-expanded", String(this.state.backgroundLegendOpen));
|
|
732
|
+
button.addEventListener("click", (e) => {
|
|
733
|
+
e.stopPropagation();
|
|
734
|
+
this.toggleBackgroundLegend();
|
|
735
|
+
});
|
|
736
|
+
return button;
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* Toggle background legend panel visibility
|
|
740
|
+
*/
|
|
741
|
+
toggleBackgroundLegend() {
|
|
742
|
+
if (this.state.backgroundLegendOpen) {
|
|
743
|
+
this.closeBackgroundLegend();
|
|
744
|
+
} else {
|
|
745
|
+
this.openBackgroundLegend();
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* Open background legend panel
|
|
750
|
+
*/
|
|
751
|
+
openBackgroundLegend() {
|
|
752
|
+
if (this.state.activeStyleEditor) {
|
|
753
|
+
this.closeStyleEditor(this.state.activeStyleEditor);
|
|
754
|
+
}
|
|
755
|
+
const itemEl = this.panel.querySelector('[data-layer-id="Background"]');
|
|
756
|
+
if (!itemEl) return;
|
|
757
|
+
let legendPanel = itemEl.querySelector(".layer-control-background-legend");
|
|
758
|
+
if (legendPanel) {
|
|
759
|
+
const layerList = legendPanel.querySelector(".background-legend-layer-list");
|
|
760
|
+
if (layerList) {
|
|
761
|
+
this.populateBackgroundLayerList(layerList);
|
|
762
|
+
}
|
|
763
|
+
} else {
|
|
764
|
+
legendPanel = this.createBackgroundLegendPanel();
|
|
765
|
+
itemEl.appendChild(legendPanel);
|
|
766
|
+
}
|
|
767
|
+
this.state.backgroundLegendOpen = true;
|
|
768
|
+
const button = itemEl.querySelector(".layer-control-background-legend-button");
|
|
769
|
+
if (button) {
|
|
770
|
+
button.setAttribute("aria-expanded", "true");
|
|
771
|
+
button.classList.add("active");
|
|
772
|
+
}
|
|
773
|
+
setTimeout(() => {
|
|
774
|
+
legendPanel == null ? void 0 : legendPanel.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
775
|
+
}, 50);
|
|
776
|
+
}
|
|
777
|
+
/**
|
|
778
|
+
* Close background legend panel
|
|
779
|
+
*/
|
|
780
|
+
closeBackgroundLegend() {
|
|
781
|
+
const itemEl = this.panel.querySelector('[data-layer-id="Background"]');
|
|
782
|
+
if (!itemEl) return;
|
|
783
|
+
const legendPanel = itemEl.querySelector(".layer-control-background-legend");
|
|
784
|
+
if (legendPanel) {
|
|
785
|
+
legendPanel.remove();
|
|
786
|
+
}
|
|
787
|
+
this.state.backgroundLegendOpen = false;
|
|
788
|
+
const button = itemEl.querySelector(".layer-control-background-legend-button");
|
|
789
|
+
if (button) {
|
|
790
|
+
button.setAttribute("aria-expanded", "false");
|
|
791
|
+
button.classList.remove("active");
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* Create the background legend panel with individual layer controls
|
|
796
|
+
*/
|
|
797
|
+
createBackgroundLegendPanel() {
|
|
798
|
+
const panel = document.createElement("div");
|
|
799
|
+
panel.className = "layer-control-background-legend";
|
|
800
|
+
const header = document.createElement("div");
|
|
801
|
+
header.className = "background-legend-header";
|
|
802
|
+
const title = document.createElement("span");
|
|
803
|
+
title.className = "background-legend-title";
|
|
804
|
+
title.textContent = "Background Layers";
|
|
805
|
+
const closeBtn = document.createElement("button");
|
|
806
|
+
closeBtn.className = "background-legend-close";
|
|
807
|
+
closeBtn.innerHTML = "×";
|
|
808
|
+
closeBtn.title = "Close";
|
|
809
|
+
closeBtn.addEventListener("click", (e) => {
|
|
810
|
+
e.stopPropagation();
|
|
811
|
+
this.closeBackgroundLegend();
|
|
812
|
+
});
|
|
813
|
+
header.appendChild(title);
|
|
814
|
+
header.appendChild(closeBtn);
|
|
815
|
+
const actionsRow = document.createElement("div");
|
|
816
|
+
actionsRow.className = "background-legend-actions";
|
|
817
|
+
const showAllBtn = document.createElement("button");
|
|
818
|
+
showAllBtn.className = "background-legend-action-btn";
|
|
819
|
+
showAllBtn.textContent = "Show All";
|
|
820
|
+
showAllBtn.addEventListener("click", () => this.setAllBackgroundLayersVisibility(true));
|
|
821
|
+
const hideAllBtn = document.createElement("button");
|
|
822
|
+
hideAllBtn.className = "background-legend-action-btn";
|
|
823
|
+
hideAllBtn.textContent = "Hide All";
|
|
824
|
+
hideAllBtn.addEventListener("click", () => this.setAllBackgroundLayersVisibility(false));
|
|
825
|
+
actionsRow.appendChild(showAllBtn);
|
|
826
|
+
actionsRow.appendChild(hideAllBtn);
|
|
827
|
+
const filterRow = document.createElement("div");
|
|
828
|
+
filterRow.className = "background-legend-filter";
|
|
829
|
+
const filterCheckbox = document.createElement("input");
|
|
830
|
+
filterCheckbox.type = "checkbox";
|
|
831
|
+
filterCheckbox.className = "background-legend-filter-checkbox";
|
|
832
|
+
filterCheckbox.id = "background-legend-only-rendered";
|
|
833
|
+
filterCheckbox.checked = this.state.onlyRenderedFilter;
|
|
834
|
+
filterCheckbox.addEventListener("change", () => {
|
|
835
|
+
this.state.onlyRenderedFilter = filterCheckbox.checked;
|
|
836
|
+
const layerList2 = panel.querySelector(".background-legend-layer-list");
|
|
837
|
+
if (layerList2) {
|
|
838
|
+
this.populateBackgroundLayerList(layerList2);
|
|
839
|
+
}
|
|
840
|
+
});
|
|
841
|
+
const filterLabel = document.createElement("label");
|
|
842
|
+
filterLabel.className = "background-legend-filter-label";
|
|
843
|
+
filterLabel.htmlFor = "background-legend-only-rendered";
|
|
844
|
+
filterLabel.textContent = "Only rendered";
|
|
845
|
+
filterRow.appendChild(filterCheckbox);
|
|
846
|
+
filterRow.appendChild(filterLabel);
|
|
847
|
+
const layerList = document.createElement("div");
|
|
848
|
+
layerList.className = "background-legend-layer-list";
|
|
849
|
+
this.populateBackgroundLayerList(layerList);
|
|
850
|
+
panel.appendChild(header);
|
|
851
|
+
panel.appendChild(actionsRow);
|
|
852
|
+
panel.appendChild(filterRow);
|
|
853
|
+
panel.appendChild(layerList);
|
|
854
|
+
return panel;
|
|
855
|
+
}
|
|
856
|
+
/**
|
|
857
|
+
* Check if a layer is currently rendered in the map viewport
|
|
858
|
+
*/
|
|
859
|
+
isLayerRendered(layerId) {
|
|
860
|
+
try {
|
|
861
|
+
const layer = this.map.getLayer(layerId);
|
|
862
|
+
if (!layer) return false;
|
|
863
|
+
const visibility = this.map.getLayoutProperty(layerId, "visibility");
|
|
864
|
+
if (visibility === "none") return false;
|
|
865
|
+
if (layer.type === "raster" || layer.type === "hillshade") {
|
|
866
|
+
return true;
|
|
867
|
+
}
|
|
868
|
+
if (layer.type === "background") {
|
|
869
|
+
return true;
|
|
870
|
+
}
|
|
871
|
+
const features = this.map.queryRenderedFeatures({ layers: [layerId] });
|
|
872
|
+
return features.length > 0;
|
|
873
|
+
} catch (error) {
|
|
874
|
+
return true;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
/**
|
|
878
|
+
* Populate the background layer list with individual layers
|
|
879
|
+
*/
|
|
880
|
+
populateBackgroundLayerList(container) {
|
|
881
|
+
container.innerHTML = "";
|
|
882
|
+
const styleLayers = this.map.getStyle().layers || [];
|
|
883
|
+
styleLayers.forEach((layer) => {
|
|
884
|
+
if (!this.isUserAddedLayer(layer.id)) {
|
|
885
|
+
if (this.state.onlyRenderedFilter && !this.isLayerRendered(layer.id)) {
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
const layerRow = document.createElement("div");
|
|
889
|
+
layerRow.className = "background-legend-layer-row";
|
|
890
|
+
layerRow.setAttribute("data-background-layer-id", layer.id);
|
|
891
|
+
const checkbox = document.createElement("input");
|
|
892
|
+
checkbox.type = "checkbox";
|
|
893
|
+
checkbox.className = "background-legend-checkbox";
|
|
894
|
+
const visibility = this.map.getLayoutProperty(layer.id, "visibility");
|
|
895
|
+
const isVisible = visibility !== "none";
|
|
896
|
+
checkbox.checked = isVisible;
|
|
897
|
+
this.state.backgroundLayerVisibility.set(layer.id, isVisible);
|
|
898
|
+
checkbox.addEventListener("change", () => {
|
|
899
|
+
this.toggleIndividualBackgroundLayer(layer.id, checkbox.checked);
|
|
900
|
+
});
|
|
901
|
+
const name = document.createElement("span");
|
|
902
|
+
name.className = "background-legend-layer-name";
|
|
903
|
+
name.textContent = this.generateFriendlyName(layer.id);
|
|
904
|
+
name.title = layer.id;
|
|
905
|
+
const typeIndicator = document.createElement("span");
|
|
906
|
+
typeIndicator.className = "background-legend-layer-type";
|
|
907
|
+
typeIndicator.textContent = layer.type;
|
|
908
|
+
layerRow.appendChild(checkbox);
|
|
909
|
+
layerRow.appendChild(name);
|
|
910
|
+
layerRow.appendChild(typeIndicator);
|
|
911
|
+
container.appendChild(layerRow);
|
|
912
|
+
}
|
|
913
|
+
});
|
|
914
|
+
if (container.children.length === 0) {
|
|
915
|
+
const emptyMsg = document.createElement("p");
|
|
916
|
+
emptyMsg.className = "background-legend-empty";
|
|
917
|
+
emptyMsg.textContent = this.state.onlyRenderedFilter ? "No rendered layers in current view." : "No background layers found.";
|
|
918
|
+
container.appendChild(emptyMsg);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
922
|
+
* Toggle visibility of an individual background layer
|
|
923
|
+
*/
|
|
924
|
+
toggleIndividualBackgroundLayer(layerId, visible) {
|
|
925
|
+
this.state.backgroundLayerVisibility.set(layerId, visible);
|
|
926
|
+
this.map.setLayoutProperty(layerId, "visibility", visible ? "visible" : "none");
|
|
927
|
+
this.updateBackgroundCheckboxState();
|
|
928
|
+
}
|
|
929
|
+
/**
|
|
930
|
+
* Set visibility for all background layers
|
|
931
|
+
*/
|
|
932
|
+
setAllBackgroundLayersVisibility(visible) {
|
|
933
|
+
const styleLayers = this.map.getStyle().layers || [];
|
|
934
|
+
styleLayers.forEach((layer) => {
|
|
935
|
+
if (!this.isUserAddedLayer(layer.id)) {
|
|
936
|
+
this.state.backgroundLayerVisibility.set(layer.id, visible);
|
|
937
|
+
this.map.setLayoutProperty(layer.id, "visibility", visible ? "visible" : "none");
|
|
938
|
+
}
|
|
939
|
+
});
|
|
940
|
+
const legendPanel = this.panel.querySelector(".layer-control-background-legend");
|
|
941
|
+
if (legendPanel) {
|
|
942
|
+
const checkboxes = legendPanel.querySelectorAll(".background-legend-checkbox");
|
|
943
|
+
checkboxes.forEach((checkbox) => {
|
|
944
|
+
checkbox.checked = visible;
|
|
945
|
+
});
|
|
946
|
+
}
|
|
947
|
+
this.updateBackgroundCheckboxState();
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* Update the main Background checkbox based on individual layer states
|
|
951
|
+
*/
|
|
952
|
+
updateBackgroundCheckboxState() {
|
|
953
|
+
const styleLayers = this.map.getStyle().layers || [];
|
|
954
|
+
let anyVisible = false;
|
|
955
|
+
let allVisible = true;
|
|
956
|
+
styleLayers.forEach((layer) => {
|
|
957
|
+
if (!this.isUserAddedLayer(layer.id)) {
|
|
958
|
+
const visible = this.state.backgroundLayerVisibility.get(layer.id);
|
|
959
|
+
if (visible === true) anyVisible = true;
|
|
960
|
+
if (visible === false) allVisible = false;
|
|
961
|
+
}
|
|
962
|
+
});
|
|
963
|
+
const backgroundItem = this.panel.querySelector('[data-layer-id="Background"]');
|
|
964
|
+
if (backgroundItem) {
|
|
965
|
+
const checkbox = backgroundItem.querySelector(".layer-control-checkbox");
|
|
966
|
+
if (checkbox) {
|
|
967
|
+
checkbox.checked = anyVisible;
|
|
968
|
+
checkbox.indeterminate = anyVisible && !allVisible;
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
if (this.state.layerStates["Background"]) {
|
|
972
|
+
this.state.layerStates["Background"].visible = anyVisible;
|
|
973
|
+
}
|
|
974
|
+
}
|
|
705
975
|
/**
|
|
706
976
|
* Create style button for a layer
|
|
707
977
|
*/
|