| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231 |
- import { type Disposable, Emitter } from '@flowgram.ai/utils';
- /**
- * 存储节点的 tree 结构信息
- * 策略是 "重修改轻查询",即修改时候做的事情更多,查询都通过指针来操作
- */
- export class FlowVirtualTree<T extends { id: string }> implements Disposable {
- protected onTreeChangeEmitter = new Emitter<void>();
- /**
- * tree 结构变化时候触发
- */
- onTreeChange = this.onTreeChangeEmitter.event;
- protected map: Map<T, FlowVirtualTree.NodeInfo<T>> = new Map();
- constructor(readonly root: T) {}
- dispose() {
- this.map.clear();
- this.onTreeChangeEmitter.dispose();
- }
- getInfo(node: T): FlowVirtualTree.NodeInfo<T> {
- let res: FlowVirtualTree.NodeInfo<T> | undefined = this.map.get(node);
- if (!res) {
- res = { children: [] };
- this.map.set(node, res);
- }
- return res;
- }
- clear(): void {
- this.map.clear();
- }
- cloneMap(): Map<T, FlowVirtualTree.NodeInfo<T>> {
- const newMap: Map<T, FlowVirtualTree.NodeInfo<T>> = new Map();
- for (const [key, value] of this.map) {
- newMap.set(key, {
- ...value,
- children: value.children.slice(),
- });
- }
- return newMap;
- }
- clone(): FlowVirtualTree<T> {
- const newTree = new FlowVirtualTree<T>(this.root);
- newTree.map = this.cloneMap();
- return newTree;
- }
- remove(node: T, withChildren = true): void {
- this.removeParent(node);
- if (withChildren) {
- this._removeChildren(node);
- }
- this.map.delete(node);
- this.fireTreeChange();
- }
- addChild(parent: T, child: T, index?: number): T {
- const parentInfo = this.getInfo(parent);
- const childInfo = this.getInfo(child);
- if (childInfo.parent) {
- if (childInfo.parent === parent) return child;
- if (childInfo.parent !== parent) {
- this.removeParent(child);
- }
- }
- const len = parentInfo.children.length;
- const idx = typeof index === 'undefined' ? len - 1 : index - 1;
- const lastChild = parentInfo.children[idx];
- const nextChild = parentInfo.children[idx + 1];
- if (lastChild) this.getInfo(lastChild).next = child;
- if (nextChild) this.getInfo(nextChild).pre = child;
- childInfo.pre = lastChild;
- childInfo.next = nextChild;
- parentInfo.children.splice(idx + 1, 0, child);
- childInfo.parent = parent;
- this.fireTreeChange();
- return child;
- }
- moveChilds(parent: T, childs: T[], index?: number): T[] {
- const parentInfo = this.getInfo(parent);
- const len = parentInfo.children.length;
- let childIndex: number = index ?? len;
- childs.forEach((child) => {
- const childInfo = this.getInfo(child);
- if (childInfo.parent) {
- this.removeParent(child);
- }
- });
- childs.forEach((child) => {
- const childInfo = this.getInfo(child);
- let lastChild: T | undefined = parentInfo.children[childIndex - 1];
- let nextChild: T | undefined = parentInfo.children[childIndex];
- if (lastChild) this.getInfo(lastChild).next = child;
- if (nextChild) this.getInfo(nextChild).pre = child;
- childInfo.pre = lastChild;
- childInfo.next = nextChild;
- parentInfo.children.splice(childIndex, 0, child);
- childInfo.parent = parent;
- childIndex++;
- });
- this.fireTreeChange();
- return childs;
- }
- getById(id: string): T | undefined {
- for (const node of this.map.keys()) {
- if (node.id === id) return node;
- }
- }
- /**
- * 插入节点到后边
- * @param before
- * @param after
- */
- insertAfter(before: T, after: T) {
- const beforeInfo = this.getInfo(before);
- const afterInfo = this.getInfo(after);
- this.removeParent(after);
- if (beforeInfo.parent) {
- const parentInfo = this.getInfo(beforeInfo.parent);
- parentInfo.children.splice(parentInfo.children.indexOf(before) + 1, 0, after);
- const { next } = beforeInfo;
- if (next) {
- this.getInfo(next).pre = after;
- }
- afterInfo.next = next;
- beforeInfo.next = after;
- afterInfo.pre = before;
- afterInfo.parent = beforeInfo.parent;
- }
- this.fireTreeChange();
- }
- removeParent(node: T): void {
- const info = this.getInfo(node);
- if (!info.parent) return;
- const parentInfo = this.getInfo(info.parent);
- const index = parentInfo.children.indexOf(node);
- parentInfo.children.splice(index, 1);
- const { pre, next } = info;
- if (pre) this.getInfo(pre).next = next;
- if (next) this.getInfo(next).pre = pre;
- this.fireTreeChange();
- }
- private _removeChildren(node: T): void {
- // 删除子节点
- const children = this.getChildren(node);
- if (children.length > 0) {
- children.forEach((child) => {
- this._removeChildren(child);
- this.map.delete(child);
- });
- }
- }
- getParent(node: T): T | undefined {
- return this.getInfo(node).parent;
- }
- getPre(node: T): T | undefined {
- return this.getInfo(node).pre;
- }
- getNext(node: T): T | undefined {
- return this.getInfo(node).next;
- }
- getChildren(node: T): T[] {
- return this.getInfo(node).children;
- }
- traverse(
- fn: (node: T, depth: number, index: number) => boolean | void,
- node = this.root,
- depth = 0,
- index = 0
- ): boolean | void {
- const breaked = fn(node, depth, index);
- if (breaked) return true;
- const info = this.getInfo(node);
- const shouldBreak = info.children.find((child, i) => this.traverse(fn, child, depth + 1, i));
- if (shouldBreak) return true;
- }
- /**
- * 通知文档树结构更新
- */
- fireTreeChange(): void {
- this.onTreeChangeEmitter.fire();
- }
- get size(): number {
- return this.map.size;
- }
- toString(): string {
- const ret: string[] = [];
- this.traverse((node, depth) => {
- if (depth === 0) {
- ret.push(node.id);
- } else {
- ret.push(`|${new Array(depth).fill('--').join('')} ${node.id}`);
- }
- });
- return `${ret.join('\n')}`;
- }
- }
- export namespace FlowVirtualTree {
- export interface NodeInfo<T> {
- parent?: T;
- next?: T;
- pre?: T;
- children: T[];
- }
- }
|