@itwin/unified-selection
Version:
Package for managing unified selection in iTwin.js applications.
146 lines • 5.2 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import { BeEvent } from "@itwin/core-bentley";
import { Selectables } from "./Selectable.js";
/**
* Creates a selection storage which stores and allows managing application-level selection.
*
* **Note:** `clearSelection` should be called upon iModel close to free-up memory:
*
* ```ts
* import { IModelConnection } from "@itwin/core-frontend";
* import { createIModelKey } from "@itwin/presentation-core-interop";
*
* IModelConnection.onClose.addListener((imodel) => {
* storage.clearStorage(createIModelKey(imodel));
* });
* ```
*
* @public
*/
export function createStorage() {
return new SelectionStorageImpl();
}
/** @internal */
export const IMODEL_CLOSE_SELECTION_CLEAR_SOURCE = "Unified selection storage: clear";
class SelectionStorageImpl {
_storage = new Map();
selectionChangeEvent;
constructor() {
this.selectionChangeEvent = new BeEvent();
}
getSelectionLevels(props) {
return this.getContainer(getIModelKey(props)).getSelectionLevels();
}
getSelection(props) {
return this.getContainer(getIModelKey(props)).getSelection(props.level ?? 0);
}
addToSelection(props) {
this.handleChange({ ...props, changeType: "add" });
}
removeFromSelection(props) {
this.handleChange({ ...props, changeType: "remove" });
}
replaceSelection(props) {
this.handleChange({ ...props, changeType: "replace" });
}
clearSelection(props) {
this.handleChange({ ...props, changeType: "clear", selectables: [] });
}
clearStorage(props) {
const imodelKey = getIModelKey(props);
this.clearSelection({ source: IMODEL_CLOSE_SELECTION_CLEAR_SOURCE, imodelKey });
this._storage.delete(imodelKey);
}
getContainer(imodelKey) {
let selectionContainer = this._storage.get(imodelKey);
if (!selectionContainer) {
selectionContainer = new MultiLevelSelectablesContainer();
this._storage.set(imodelKey, selectionContainer);
}
return selectionContainer;
}
handleChange(props) {
const { source, level: inLevel, changeType, selectables: change } = props;
const imodelKey = getIModelKey(props);
const container = this.getContainer(imodelKey);
const level = inLevel ?? 0;
const selectables = container.getSelection(level);
const selected = Selectables.create(change);
switch (changeType) {
case "add":
if (!Selectables.add(selectables, change)) {
return;
}
break;
case "remove":
if (!Selectables.remove(selectables, change)) {
return;
}
break;
case "replace":
if (Selectables.size(selectables) === Selectables.size(selected) && Selectables.hasAll(selectables, change)) {
return;
}
Selectables.clear(selectables);
Selectables.add(selectables, change);
break;
case "clear":
if (!Selectables.clear(selectables)) {
return;
}
break;
}
container.clear(level + 1);
const event = {
source,
level,
imodelKey,
iModelKey: imodelKey,
changeType,
selectables: selected,
timestamp: new Date(),
storage: this,
};
this.selectionChangeEvent.raiseEvent(event, this);
}
}
function getIModelKey(props) {
// eslint-disable-next-line @typescript-eslint/no-deprecated
return "imodelKey" in props ? props.imodelKey : props.iModelKey;
}
class MultiLevelSelectablesContainer {
_selectablesContainers;
constructor() {
this._selectablesContainers = new Map();
}
getSelection(level) {
let selectables = this._selectablesContainers.get(level);
if (!selectables) {
selectables = Selectables.create([]);
this._selectablesContainers.set(level, selectables);
}
return selectables;
}
getSelectionLevels() {
const levels = new Array();
for (const entry of this._selectablesContainers.entries()) {
if (!Selectables.isEmpty(entry[1])) {
levels.push(entry[0]);
}
}
return levels.sort();
}
clear(level) {
const storedLevels = this._selectablesContainers.keys();
for (const storedLevel of storedLevels) {
if (storedLevel >= level) {
const selectables = this._selectablesContainers.get(storedLevel);
Selectables.clear(selectables);
}
}
}
}
//# sourceMappingURL=SelectionStorage.js.map