maplibre-gl-layer-control 0.1.0 → 0.2.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.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 !== "Background") {
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
  */