aspen-tree-model
Version:
State container for aspen trees
184 lines • 8.35 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TreeStateManager = exports.Operation = void 0;
const aspen_core_1 = require("aspen-core");
const notificar_1 = require("notificar");
const types_1 = require("./types");
var Operation;
(function (Operation) {
Operation[Operation["SetExpanded"] = 1] = "SetExpanded";
Operation[Operation["SetCollapsed"] = 2] = "SetCollapsed";
Operation[Operation["SetActive"] = 3] = "SetActive";
})(Operation = exports.Operation || (exports.Operation = {}));
var StashKeyFrameFlag;
(function (StashKeyFrameFlag) {
StashKeyFrameFlag[StashKeyFrameFlag["Expanded"] = 1] = "Expanded";
StashKeyFrameFlag[StashKeyFrameFlag["Collapsed"] = 2] = "Collapsed";
StashKeyFrameFlag[StashKeyFrameFlag["Disabled"] = 4] = "Disabled";
})(StashKeyFrameFlag || (StashKeyFrameFlag = {}));
class TreeStateManager {
constructor(root) {
this.events = new notificar_1.Notificar();
this.expandedDirectories = new Map();
this._scrollOffset = 0;
this.stashing = false;
this.stashLockingItems = new Set();
this.handleExpansionChange = (target, isExpanded, isVisibleAtSurface) => {
if (this.stashing) {
this.stashKeyframes.set(target.id, isExpanded ? StashKeyFrameFlag.Expanded : StashKeyFrameFlag.Collapsed);
}
if (this.stashKeyframes && !this.stashing) {
// If something was "manually" (through user interaction) expanded *after* recording ended, we must remove its parents from undo queue
if (isExpanded) {
let p = target;
while (p) {
if (this.stashKeyframes.has(p.id)) {
let flags = this.stashKeyframes.get(p.id);
flags |= StashKeyFrameFlag.Disabled;
this.stashKeyframes.set(p.id, flags);
}
p = p.parent;
}
this.stashLockingItems.add(target);
}
if (this.stashLockingItems && this.stashLockingItems.has(target) && !isExpanded) {
let p = target;
while (p) {
if (this.stashKeyframes.has(p.id)) {
let flags = this.stashKeyframes.get(p.id);
flags &= ~StashKeyFrameFlag.Disabled;
this.stashKeyframes.set(p.id, flags);
}
p = p.parent;
}
this.stashLockingItems.delete(target);
}
}
let relativePath = this.expandedDirectories.get(target);
if (isExpanded && !relativePath) {
relativePath = this.root.pathfx.relative(this.root.path, target.path);
this.expandedDirectories.set(target, relativePath);
this.events.dispatch(types_1.TreeStateEvent.DidChangeDirExpansionState, relativePath, isExpanded, isVisibleAtSurface);
}
else if (!isExpanded && relativePath) {
this.expandedDirectories.delete(target);
this.events.dispatch(types_1.TreeStateEvent.DidChangeDirExpansionState, relativePath, isExpanded, isVisibleAtSurface);
}
};
this.handleDidChangePath = (target) => {
if (this.expandedDirectories.has(target)) {
const prevPath = this.expandedDirectories.get(target);
const newPath = this.root.pathfx.relative(this.root.path, target.path);
this.expandedDirectories.set(target, newPath);
this.events.dispatch(types_1.TreeStateEvent.DidChangeRelativePath, prevPath, newPath);
}
};
this.root = root;
this.root.onDidChangeDirExpansionState(this.handleExpansionChange);
this.root.onDidChangePath(this.handleDidChangePath);
}
get scrollOffset() {
return this._scrollOffset;
}
saveScrollOffset(scrollOffset) {
this._scrollOffset = scrollOffset;
this.events.dispatch(types_1.TreeStateEvent.DidChangeScrollOffset, scrollOffset);
}
onDidLoadState(callback) {
return this.events.add(types_1.TreeStateEvent.DidLoadState, callback);
}
onChangeScrollOffset(callback) {
return this.events.add(types_1.TreeStateEvent.DidChangeScrollOffset, callback);
}
onDidChangeDirExpansionState(callback) {
return this.events.add(types_1.TreeStateEvent.DidChangeDirExpansionState, callback);
}
onDidChangeRelativePath(callback) {
return this.events.add(types_1.TreeStateEvent.DidChangeRelativePath, callback);
}
/**
* Starts recording directory expands and collapses
*
* `reverseStash` can then be used to undo all those actions.
*
* Internally used by `FileTree#peekABoo`
*
* Stashing can be used for peeking file(s) temporarily, where you don't want a mess of expanded folders for user to deal with.
*
* `beginStashing` => then expand the folder(s) you need to get to the file you want => `endStahsing` => once you're done => `reverseStash` to clean up the mess
*/
beginStashing() {
this.stashing = true;
this.stashKeyframes = new Map();
}
/**
* Ends the recording session of directory expands and collapses
*
* See documentation for `beginStashing` for details
*/
endStashing() {
this.stashing = false;
this.stashLockingItems.clear();
}
/**
* Reverses all the recorded directory expands and collapses
*
* See documentation for `beginStashing` for details
*/
async reverseStash() {
if (!this.stashKeyframes) {
return;
}
this.endStashing();
const keyframes = Array.from(this.stashKeyframes);
this.stashKeyframes = null;
for (const [targetID, flags] of keyframes) {
const frameDisabled = (flags & StashKeyFrameFlag.Disabled) === StashKeyFrameFlag.Disabled;
const target = aspen_core_1.FileEntry.getFileEntryById(targetID);
// Check if target is still available (not disposed)
if (!target || frameDisabled) {
continue;
}
if ((flags & StashKeyFrameFlag.Expanded) === StashKeyFrameFlag.Expanded) {
this.root.collapseDirectory(target);
}
else if ((flags & StashKeyFrameFlag.Collapsed) === StashKeyFrameFlag.Collapsed) {
await this.root.expandDirectory(target);
}
}
}
async loadTreeState(state) {
if (state) {
for (const relDirPath of state.expandedDirectories.buried) {
try {
const dirH = await this.root.forceLoadFileEntryAtPath(relDirPath);
if (dirH && dirH.constructor === aspen_core_1.Directory) {
await this.root.expandDirectory(dirH, false);
}
}
catch (error) { }
}
for (const relDirPath of state.expandedDirectories.atSurface) {
try {
const dirH = await this.root.forceLoadFileEntryAtPath(relDirPath);
if (dirH && dirH.constructor === aspen_core_1.Directory) {
await this.root.expandDirectory(dirH, true);
}
}
catch (error) { }
}
this._scrollOffset = typeof state.scrollPosition === 'number' && state.scrollPosition > -1 ? state.scrollPosition : this._scrollOffset;
this.events.dispatch(types_1.TreeStateEvent.DidLoadState);
}
}
/**
* This will ensure directory expansions aren't altered atleast for directories leading to file to be excluded when `reverseStash` is called
*/
excludeFromStash(file) {
if (this.stashKeyframes && !this.stashing) {
this.handleExpansionChange(file.constructor === aspen_core_1.FileEntry ? file.parent : file, true, this.root.isItemVisibleAtSurface(file));
}
}
}
exports.TreeStateManager = TreeStateManager;
//# sourceMappingURL=TreeStateManager.js.map