Restructure code
This commit is contained in:
123
mixins/tree-reveal.js
Normal file
123
mixins/tree-reveal.js
Normal file
@@ -0,0 +1,123 @@
|
||||
export const TreeRevealMixin = (superClass) => class TreeRevealMixin extends superClass {
|
||||
async scrollToKey(key) {
|
||||
await this.updateComplete;
|
||||
const idx = this._findFlatIndexByKey(key);
|
||||
if (idx < 0) return false;
|
||||
const scroller = this.shadowRoot?.querySelector('.tree');
|
||||
if (scroller?.scrollToIndex) {
|
||||
scroller.scrollToIndex(idx, { block: 'center' });
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand+load ancestors so `path` becomes visible, then optionally select/scroll it.
|
||||
* `path` is a key path (slug path), like `root/dir/file`.
|
||||
*/
|
||||
async revealPath(path, options = {}) {
|
||||
const segments = this._normalizeKeyPath(path);
|
||||
if (!segments.length) return { found: false, key: '', node: undefined };
|
||||
|
||||
const select = options.select !== false;
|
||||
const scroll = options.scroll !== false;
|
||||
const open = options.open === true;
|
||||
const source = options.source ?? 'revealPath';
|
||||
const timeoutMs = Number.isFinite(options.timeoutMs) ? options.timeoutMs : 15000;
|
||||
|
||||
if (this._revealController) {
|
||||
try { this._revealController.abort(); } catch (e) { }
|
||||
}
|
||||
const controller = new AbortController();
|
||||
this._revealController = controller;
|
||||
|
||||
const externalSignal = options.signal;
|
||||
if (externalSignal?.aborted) return { found: false, key: segments.join('/') };
|
||||
const abortIfExternal = () => {
|
||||
try { controller.abort(); } catch (e) { }
|
||||
};
|
||||
externalSignal?.addEventListener?.('abort', abortIfExternal, { once: true });
|
||||
|
||||
const start = performance.now();
|
||||
const ensureNotTimedOut = () => {
|
||||
if (timeoutMs <= 0) return;
|
||||
if (performance.now() - start > timeoutMs) {
|
||||
const err = new Error('revealPath timeout');
|
||||
err.name = 'TimeoutError';
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
const ensureExpanded = (key) => {
|
||||
if (!this.manageState) return;
|
||||
const set = new Set(this._expandedPathSet);
|
||||
if (!set.has(key)) {
|
||||
set.add(key);
|
||||
this._expandedPathSet = set;
|
||||
this.expandedPaths = Array.from(set);
|
||||
this._rebuildManagedTree();
|
||||
}
|
||||
};
|
||||
|
||||
const ensureSelected = (key) => {
|
||||
if (!this.manageState) return;
|
||||
const set = new Set();
|
||||
set.add(key);
|
||||
this._selectedPathSet = set;
|
||||
this.selectedPaths = Array.from(set);
|
||||
this._rebuildManagedTree();
|
||||
};
|
||||
|
||||
let currentKey = '';
|
||||
for (let i = 0; i < segments.length; i++) {
|
||||
ensureNotTimedOut();
|
||||
if (controller.signal.aborted) break;
|
||||
|
||||
currentKey = currentKey ? `${currentKey}/${segments[i]}` : segments[i];
|
||||
const node = this._findNodeByKey(currentKey);
|
||||
|
||||
if (!node) {
|
||||
externalSignal?.removeEventListener?.('abort', abortIfExternal);
|
||||
return { found: false, key: currentKey, node: undefined };
|
||||
}
|
||||
|
||||
const isLeaf = i === segments.length - 1;
|
||||
ensureExpanded(currentKey);
|
||||
|
||||
if (!isLeaf && typeof this.loadChildren === 'function') {
|
||||
await this._loadChildrenForExpanded(currentKey, source, null, node, currentKey.split('/'));
|
||||
if (controller.signal.aborted) break;
|
||||
const nextKey = `${currentKey}/${segments[i + 1]}`;
|
||||
if (!this._findNodeByKey(nextKey)) {
|
||||
externalSignal?.removeEventListener?.('abort', abortIfExternal);
|
||||
return { found: false, key: nextKey, node: undefined };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (controller.signal.aborted) {
|
||||
externalSignal?.removeEventListener?.('abort', abortIfExternal);
|
||||
return { found: false, key: currentKey, node: this._findNodeByKey(currentKey) };
|
||||
}
|
||||
|
||||
if (open && typeof this.loadChildren === 'function') {
|
||||
const leafKey = currentKey;
|
||||
const leafNode = this._findNodeByKey(leafKey);
|
||||
if (leafNode && this._isExpandableNode(leafNode)) {
|
||||
ensureExpanded(leafKey);
|
||||
await this._loadChildrenForExpanded(leafKey, source, null, leafNode, leafKey.split('/'));
|
||||
}
|
||||
}
|
||||
|
||||
const finalNode = this._findNodeByKey(currentKey);
|
||||
if (finalNode && select) {
|
||||
ensureSelected(currentKey);
|
||||
}
|
||||
if (finalNode && scroll) {
|
||||
await this.scrollToKey(currentKey);
|
||||
}
|
||||
|
||||
externalSignal?.removeEventListener?.('abort', abortIfExternal);
|
||||
return { found: Boolean(finalNode), key: currentKey, node: finalNode };
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user