diff --git a/elements/layercontrol/package.json b/elements/layercontrol/package.json index ffe82a127..f58ebec62 100644 --- a/elements/layercontrol/package.json +++ b/elements/layercontrol/package.json @@ -32,6 +32,7 @@ }, "dependencies": { "dayjs": "^1.11.8", - "lit": "^3.0.2" + "lit": "^3.0.2", + "lodash.debounce": "^4.0.8" } } diff --git a/elements/layercontrol/src/components/layerList.js b/elements/layercontrol/src/components/layerList.js index 80cd4159a..01d2b1342 100644 --- a/elements/layercontrol/src/components/layerList.js +++ b/elements/layercontrol/src/components/layerList.js @@ -1,10 +1,10 @@ import { LitElement, html } from "lit"; import { when } from "lit/directives/when.js"; -import { keyed } from "lit/directives/keyed.js"; +import { repeat } from "lit/directives/repeat.js"; import { createSortable, getLayerType } from "../helpers"; import "./layer"; import "./layerGroup"; - +import _debounce from "lodash.debounce"; /** * Display of a list of layers * @@ -70,15 +70,28 @@ export class EOxLayerControlLayerList extends LitElement { this.noShadow = true; } + #handleLayersChangeLength = () => { + this.#debHandleLayersChangeLength(); + }; + #debHandleLayersChangeLength = _debounce(() => { + this.requestUpdate(); + this.dispatchEvent(new CustomEvent("changed", { bubbles: true })); + }, 50); + + firstUpdated() { + if (this.layers) { + createSortable(this.renderRoot.querySelector("ul"), this.layers, this); + } + } + updated() { if (!this.layers) { return; } - this.layers.on("change:length", () => { - this.requestUpdate(); - this.dispatchEvent(new CustomEvent("changed", { bubbles: true })); - }); - createSortable(this.renderRoot.querySelector("ul"), this.layers, this); + if (this.layers.hasListener("change:length")) { + this.layers?.un("change:length", this.#handleLayersChangeLength); + } + this.layers.on("change:length", this.#handleLayersChangeLength); } createRenderRoot() { @@ -95,57 +108,52 @@ export class EOxLayerControlLayerList extends LitElement { ${when( this.layers, () => html` - ${this.layers - .getArray() - .filter( - (l) => - !l.get("layerControlHide") && !l.get("layerControlOptional") - ) - .reverse() - .map((layer) => - keyed( - layer.get(this.idProperty), - html` -
  • - ${ - /** @type {import("ol/layer").Group} */ (layer) - .getLayers - ? html` - this.requestUpdate()} - > - - ` - : html` - { - this.requestUpdate(); - }} - > - ` - } -
  • - ` + ${repeat( + this.layers + .getArray() + .filter( + (l) => + !l.get("layerControlHide") && !l.get("layerControlOptional") ) - )} + .reverse(), + (layer) => layer, + (layer) => html` +
  • + ${ + /** @type {import("ol/layer").Group} */ (layer).getLayers + ? html` + this.requestUpdate()} + > + + ` + : html` + this.requestUpdate()} + > + ` + } +
  • + ` + )} ` )} diff --git a/elements/layercontrol/src/helpers.js b/elements/layercontrol/src/helpers.js index bfad39eaf..7707fdc98 100644 --- a/elements/layercontrol/src/helpers.js +++ b/elements/layercontrol/src/helpers.js @@ -3,10 +3,10 @@ import Sortable from "sortablejs"; /** * * @param {HTMLElement} element - * @param {import("ol").Collection} layers + * @param {import("ol").Collection} collection * @param {import("lit").LitElement} that */ -export const createSortable = (element, layers, that) => { +export const createSortable = (element, collection, that) => { /** * @type {any[]} */ @@ -41,7 +41,8 @@ export const createSortable = (element, layers, that) => { if (e.oldIndex == e.newIndex) return; // Then move the element using your own logic. // automatically dispatches "sort" event - const layer = layers.getArray().find( + const layers = collection.getArray(); + const layer = layers.find( (l) => // @ts-ignore l.ol_uid === @@ -50,8 +51,32 @@ export const createSortable = (element, layers, that) => { // @ts-ignore ).layer.ol_uid ); - layers.remove(layer); - layers.insertAt(layers.getLength() - e.newIndex, layer); + const numberOfHiddenLayers = layers.filter( + (l) => l.get("layerControlHide") || l.get("layerControlOptional") + ).length; + const target = + layers[layers.length - 1 - e.newIndex - numberOfHiddenLayers]; + let draggedIndex; + let dropIndex; + // remove dragged layer from collection + for ( + draggedIndex = layers.length - 1; + draggedIndex > -1; + draggedIndex-- + ) { + if (layers[draggedIndex] == layer) { + collection.removeAt(draggedIndex); + break; + } + } + // re-add dragged layer at position of layer that has beend dropped on + for (dropIndex = layers.length - 1; dropIndex > -1; dropIndex--) { + if (layers[dropIndex] === target) { + if (draggedIndex > dropIndex) collection.insertAt(dropIndex, layer); + else collection.insertAt(dropIndex + 1, layer); + break; + } + } that.requestUpdate(); }, }); diff --git a/elements/layercontrol/src/main.js b/elements/layercontrol/src/main.js index 5ed059245..7486f801f 100644 --- a/elements/layercontrol/src/main.js +++ b/elements/layercontrol/src/main.js @@ -99,7 +99,7 @@ export class EOxLayerControl extends LitElement { * @type Element & { map: import("ol").Map } */ const foundElement = document.querySelector(this.for); - if (foundElement) { + if (foundElement && foundElement?.map !== this.map) { this.map = foundElement.map; } } diff --git a/elements/layercontrol/test/_mockMap.js b/elements/layercontrol/test/_mockMap.js index 49c348f3d..c19964ff9 100644 --- a/elements/layercontrol/test/_mockMap.js +++ b/elements/layercontrol/test/_mockMap.js @@ -41,6 +41,15 @@ class MockLayer { this.visible = visible; } visible = true; + events = { + ["change"]: () => undefined, + }; + on = (event, fun) => (this.events = { [event]: fun }); + un = (event, fun) => { + if (this.events[event] == fun) { + delete this.events[event]; + } + }; } class MockCollection { @@ -58,6 +67,12 @@ class MockCollection { }; layers = []; on = (event, fun) => (this.events = { [event]: fun }); + un = (event, fun) => { + if (this.events[event] == fun) { + delete this.events[event]; + } + }; + hasListener = (event) => event in this.events; pop() { this.layers.pop(); this.events["change:length"](); @@ -66,6 +81,33 @@ class MockCollection { this.layers.push(new MockLayer(newLayer)); this.events["change:length"](); } + remove(layer) { + layer = new MockLayer(layer); + const i = this.layers.indexOf(layer); + if (i) { + this.layers.splice(i, 1); + return layer; + } else { + return undefined; + } + } + removeAt(index) { + this.layers = [...this.layers.slice(index)]; + } + insertAt(index, layer) { + layer = new MockLayer(layer); + this.layers = [ + ...this.layers.slice(0, index), + layer, + ...this.layers.slice(index), + ]; + } + getLength() { + return this.layers.length; + } + forEach(func) { + this.layers.forEach(func); + } } export class MockMap extends HTMLElement { diff --git a/package-lock.json b/package-lock.json index fd17c386c..3f8967060 100644 --- a/package-lock.json +++ b/package-lock.json @@ -128,7 +128,8 @@ "version": "0.11.1", "dependencies": { "dayjs": "^1.11.8", - "lit": "^3.0.2" + "lit": "^3.0.2", + "lodash.debounce": "^4.0.8" }, "devDependencies": { "@eox/eslint-config": "^1.0.0",