maplair
Version:
JavaScript library for mobile-friendly interactive maps
220 lines (219 loc) • 8.02 kB
JavaScript
import { stamp } from "../core/Util.js";
import { disableClickPropagation, disableScrollPropagation, off, on, preventDefault } from "../dom/DomEvent.js";
import { create } from "../dom/DomUtil.js";
import { Control } from "./Control.js";
import { I18n } from "../core/I18n.js";
//#region src/control/LayersControl.js
var LayersControl = class LayersControl extends Control {
static {
this.setDefaultOptions({
collapsed: true,
collapseDelay: 0,
position: "topright",
autoZIndex: true,
hideSingleBase: false,
sortLayers: false,
sortFunction(layerA, layerB, nameA, nameB) {
return nameA < nameB ? -1 : nameB < nameA ? 1 : 0;
}
});
}
constructor(baseLayers, overlays, options) {
super(options);
this._layerControlInputs = [];
this._layers = [];
this._lastZIndex = 0;
this._handlingClick = false;
this._preventClick = false;
for (const [name, layer] of Object.entries(baseLayers ?? {})) this._addLayer(layer, name);
for (const [name, layer] of Object.entries(overlays ?? {})) this._addLayer(layer, name, true);
this.callInitHooks(LayersControl);
}
onAdd(map) {
this._initLayout();
this._update();
this._map = map;
map.on("zoomend", this._checkDisabledLayers, this);
for (const layer of this._layers) layer.layer.on("add remove", this._onLayerChange, this);
if (!this.options.collapsed) map.on("resize", this._expandIfNotCollapsed, this);
return this._container;
}
addTo(map) {
super.addTo(map);
return this._expandIfNotCollapsed();
}
onRemove() {
this._map.off("zoomend", this._checkDisabledLayers, this);
for (const layer of this._layers) layer.layer.off("add remove", this._onLayerChange, this);
this._map.off("resize", this._expandIfNotCollapsed, this);
}
addBaseLayer(layer, name) {
this._addLayer(layer, name);
return this._map ? this._update() : this;
}
addOverlay(layer, name) {
this._addLayer(layer, name, true);
return this._map ? this._update() : this;
}
removeLayer(layer) {
layer.off("add remove", this._onLayerChange, this);
const obj = this._getLayer(stamp(layer));
if (obj) this._layers.splice(this._layers.indexOf(obj), 1);
return this._map ? this._update() : this;
}
expand() {
clearTimeout(this._collapseDelayTimeout);
this._container.classList.add("maplair-control-layers-expanded");
this._section.style.height = null;
const acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50);
if (acceptableHeight < this._section.clientHeight) {
this._section.classList.add("maplair-control-layers-scrollbar");
this._section.style.height = `${acceptableHeight}px`;
} else this._section.classList.remove("maplair-control-layers-scrollbar");
this._checkDisabledLayers();
return this;
}
collapse(ev) {
if (!ev || !((ev.type === "pointerleave" || ev.type === "pointerout") && ev.pointerType === "touch")) {
if (this.options.collapseDelay > 0) {
this._collapseDelayTimeout = setTimeout(() => {
this._container.classList.remove("maplair-control-layers-expanded");
}, this.options.collapseDelay);
return this;
}
this._container.classList.remove("maplair-control-layers-expanded");
}
return this;
}
_initLayout() {
const className = "maplair-control-layers", container = this._container = create("div", className), collapsed = this.options.collapsed;
disableClickPropagation(container);
disableScrollPropagation(container);
const section = this._section = create("fieldset", `${className}-list`);
if (collapsed) {
this._map.on("click", this.collapse, this);
on(container, {
pointerenter: this._expandSafely,
pointerleave: this.collapse
}, this);
}
const link = this._layersLink = create("a", `${className}-toggle`, container);
link.href = "#";
link.title = I18n.translate("Layers");
link.setAttribute("role", "button");
on(link, {
keydown(e) {
if (e.code === "Enter") this._expandSafely();
},
click(e) {
e.preventDefault();
this._expandSafely();
}
}, this);
if (!collapsed) this.expand();
this._baseLayersList = create("div", `${className}-base`, section);
this._separator = create("div", `${className}-separator`, section);
this._overlaysList = create("div", `${className}-overlays`, section);
container.appendChild(section);
}
_getLayer(id) {
for (const layer of this._layers) if (layer && stamp(layer.layer) === id) return layer;
}
_addLayer(layer, name, overlay) {
if (this._map) layer.on("add remove", this._onLayerChange, this);
this._layers.push({
layer,
name,
overlay
});
if (this.options.sortLayers) this._layers.sort(((a, b) => this.options.sortFunction(a.layer, b.layer, a.name, b.name)));
if (this.options.autoZIndex && layer.setZIndex) {
this._lastZIndex++;
layer.setZIndex(this._lastZIndex);
}
this._expandIfNotCollapsed();
}
_update() {
if (!this._container) return this;
this._baseLayersList.replaceChildren();
this._overlaysList.replaceChildren();
this._layerControlInputs = [];
let baseLayersPresent, overlaysPresent, baseLayersCount = 0;
for (const obj of this._layers) {
this._addItem(obj);
overlaysPresent ||= obj.overlay;
baseLayersPresent ||= !obj.overlay;
baseLayersCount += !obj.overlay ? 1 : 0;
}
if (this.options.hideSingleBase) {
baseLayersPresent = baseLayersPresent && baseLayersCount > 1;
this._baseLayersList.style.display = baseLayersPresent ? "" : "none";
}
this._separator.style.display = overlaysPresent && baseLayersPresent ? "" : "none";
return this;
}
_onLayerChange(e) {
if (!this._handlingClick) this._update();
const obj = this._getLayer(stamp(e.target));
const type = obj.overlay ? e.type === "add" ? "overlayadd" : "overlayremove" : e.type === "add" ? "baselayerchange" : null;
if (type) this._map.fire(type, obj);
}
_addItem(obj) {
const label = document.createElement("label"), checked = this._map.hasLayer(obj.layer);
const input = document.createElement("input");
input.type = obj.overlay ? "checkbox" : "radio";
input.className = "maplair-control-layers-selector";
input.defaultChecked = checked;
if (!obj.overlay) input.name = `maplair-base-layers_${stamp(this)}`;
this._layerControlInputs.push(input);
input.layerId = stamp(obj.layer);
on(input, "click", this._onInputClick, this);
const name = document.createElement("span");
name.innerHTML = ` ${obj.name}`;
const holder = document.createElement("span");
label.appendChild(holder);
holder.appendChild(input);
holder.appendChild(name);
(obj.overlay ? this._overlaysList : this._baseLayersList).appendChild(label);
this._checkDisabledLayers();
return label;
}
_onInputClick(e) {
if (this._preventClick) return;
const inputs = this._layerControlInputs, addedLayers = [], removedLayers = [];
this._handlingClick = true;
for (const input of inputs) {
const layer = this._getLayer(input.layerId).layer;
if (input.checked) addedLayers.push(layer);
else if (!input.checked) removedLayers.push(layer);
}
for (const layer of removedLayers) if (this._map.hasLayer(layer)) this._map.removeLayer(layer);
for (const layer of addedLayers) if (!this._map.hasLayer(layer)) this._map.addLayer(layer);
this._handlingClick = false;
this._refocusOnMap(e);
}
_checkDisabledLayers() {
const inputs = this._layerControlInputs, zoom = this._map.getZoom();
for (const input of inputs) {
const layer = this._getLayer(input.layerId).layer;
input.disabled = layer.options.minZoom !== void 0 && zoom < layer.options.minZoom || layer.options.maxZoom !== void 0 && zoom > layer.options.maxZoom;
}
}
_expandIfNotCollapsed() {
if (this._map && !this.options.collapsed) this.expand();
return this;
}
_expandSafely() {
const section = this._section;
this._preventClick = true;
on(section, "click", preventDefault);
this.expand();
setTimeout(() => {
off(section, "click", preventDefault);
this._preventClick = false;
});
}
};
//#endregion
export { LayersControl };
//# sourceMappingURL=LayersControl.js.map