@geogirafe/lib-geoportal
Version:
GeoGirafe is a flexible application to build online geoportals.
95 lines (94 loc) • 4.25 kB
JavaScript
// 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;
}
}