UNPKG

@geogirafe/lib-geoportal

Version:

GeoGirafe is a flexible application to build online geoportals.

95 lines (94 loc) • 4.25 kB
// SPDX-License-Identifier: Apache-2.0 import GirafeSingleton from '../../base/GirafeSingleton.js'; import GroupLayer from '../../models/layers/grouplayer.js'; import ThemeLayer from '../../models/layers/themelayer.js'; export const LayerTreeStartOrder = 1000; export default class OrderingManager extends GirafeSingleton { timeoutId; isReordering = false; get state() { return this.context.stateManager.state; } async initializeSingleton() { this.registerEvents(); } registerEvents() { this.context.stateManager.subscribe('layers.layersList', (oldLayers, newLayers) => this.onLayerListChanged(oldLayers, newLayers)); this.context.stateManager.subscribe(/layers\.layersList\..*\.children/, (oldLayers, newLayers) => this.onLayerListChanged(oldLayers, newLayers)); this.context.stateManager.subscribe(/layers\.layersList\..*\.order/, () => { if (!this.isReordering) { this.reorderLayers(); } }); } onLayerListChanged(oldLayers, newLayers) { // Need to reorder only if new layers were added. const oldTreeItemIds = new Set(oldLayers.map((oldChild) => oldChild.treeItemId)); const hasAddedLayers = newLayers.some((newChild) => !oldTreeItemIds.has(newChild.treeItemId)); if (hasAddedLayers) { this.reorderLayers(); } } /** * This method do a reorder of all layers present in the treeview * in order to keep a order attribut corresponding to what the use wants. * This order will be used by the map component to calculate the list of layers in the right order */ reorderLayers() { // Use a debouncing to prevent multiple execution of this method // If multiple order attributes are modified at the same time. if (this.timeoutId) { clearTimeout(this.timeoutId); } this.timeoutId = setTimeout(() => { this.timeoutId = undefined; this.isReordering = true; const wasRenderEnabled = this.state.treeview.renderEnabled; if (wasRenderEnabled) { this.state.treeview.renderEnabled = false; } try { const orderedLayers = this.getSortedLayers(this.state.layers.layersList); const counter = { index: LayerTreeStartOrder }; this.context.stateManager.batchChanges(() => this.reorderLayersRecursively(orderedLayers, counter)); } finally { if (wasRenderEnabled) { this.state.treeview.renderEnabled = true; } this.isReordering = false; } }); } reorderLayersRecursively(orderedLayers, counter) { // Traverse the layertree in depth first to renumber all the elements for (const layer of orderedLayers) { // NOTE REG : Setting a variable to the very same value is managed by brain in theory, // and no event will be sent by brain is the value is identical. // But even if the call to a proxy has a small cost, here during a reorder, it can be called many times // Because we reorder the whole treeview, which can be very big. // Therefore, it is more performant here to add this test before setting the variable to the same value. const nextOrder = counter.index++; if (layer.order !== nextOrder) { layer.order = nextOrder; } if (layer instanceof GroupLayer || layer instanceof ThemeLayer) { const orderedChilds = this.getSortedLayers(layer.children); this.reorderLayersRecursively(orderedChilds, counter); } } } getSortedLayers(layers) { const orderedLayers = layers.slice().sort((l1, l2) => { // Make sure pinned layers are always on top if (l1.isPinned && !l2.isPinned) return -1; if (!l1.isPinned && l2.isPinned) return 1; if (l1.isPinned && l2.isPinned) return 0; return l1.order - l2.order; }); return orderedLayers; } }