Skip to content

Commit a1e3d51

Browse files
committed
feat(cdk/tree): maintain a cache of parents and node groups
1 parent 2e1a9fe commit a1e3d51

File tree

1 file changed

+56
-0
lines changed

1 file changed

+56
-0
lines changed

src/cdk/tree/tree.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,20 @@ export class CdkTree<T, K = T> implements AfterContentChecked, CollectionViewer,
9393
/** Level of nodes */
9494
private _levels: Map<T, number> = new Map<T, number>();
9595

96+
/** The immediate parents for a node. This is `null` if there is no parent. */
97+
private _parents: Map<T, T | null> = new Map<T, T | null>();
98+
99+
/**
100+
* The internal node groupings for each node; we use this, primarily for flattened trees, to
101+
* determine where a particular node is within each group.
102+
*
103+
* The structure of this is that:
104+
* - the outer index is the level
105+
* - the inner index is the parent node for this particular group. If there is no parent node, we
106+
* use `null`.
107+
*/
108+
private _groups: Map<number, Map<T | null, T[]>> = new Map<number, Map<T | null, T[]>>();
109+
96110
/**
97111
* Provides a stream containing the latest data array to render. Influenced by the tree's
98112
* stream of view window (what dataNodes are currently on screen).
@@ -295,7 +309,10 @@ export class CdkTree<T, K = T> implements AfterContentChecked, CollectionViewer,
295309
this.insertNode(data[currentIndex!], currentIndex!, viewContainer, parentData);
296310
} else if (currentIndex == null) {
297311
viewContainer.remove(adjustedPreviousIndex!);
312+
const group = this._getNodeGroup(item.item);
298313
this._levels.delete(item.item);
314+
this._parents.delete(item.item);
315+
group.splice(group.indexOf(item.item), 1);
299316
} else {
300317
const view = viewContainer.get(adjustedPreviousIndex!);
301318
viewContainer.move(view!, currentIndex);
@@ -348,6 +365,19 @@ export class CdkTree<T, K = T> implements AfterContentChecked, CollectionViewer,
348365
context.level = 0;
349366
}
350367
this._levels.set(nodeData, context.level);
368+
const parent = parentData ?? this._findParentForNode(nodeData, index);
369+
this._parents.set(nodeData, parent);
370+
371+
// Determine where to insert this new node into the group, then insert it.
372+
// We do this by looking at the previous node in our flattened node list. If it's in the same
373+
// group, we place the current node after. Otherwise, we place it at the start of the group.
374+
const currentGroup = this._groups.get(context.level) ?? new Map<T | null, T[]>();
375+
const group = currentGroup.get(parent) ?? [];
376+
const previousNode = this._dataNodes?.[index - 1];
377+
const groupInsertionIndex = (previousNode && group.indexOf(previousNode) + 1) ?? 0;
378+
group.splice(groupInsertionIndex, 0, nodeData);
379+
currentGroup.set(parent, group);
380+
this._groups.set(context.level, currentGroup);
351381

352382
// Use default tree nodeOutlet, or nested node's nodeOutlet
353383
const container = viewContainer ? viewContainer : this._nodeOutlet.viewContainer;
@@ -546,6 +576,32 @@ export class CdkTree<T, K = T> implements AfterContentChecked, CollectionViewer,
546576
private _trackExpansionKey(dataNode: T): K {
547577
return this.expansionKey?.(dataNode) ?? (dataNode as unknown as K);
548578
}
579+
580+
private _getNodeGroup(node: T) {
581+
const level = this._levels.get(node);
582+
const parent = this._parents.get(node);
583+
const group = this._groups.get(level ?? 0)?.get(parent ?? null);
584+
return group ?? [node];
585+
}
586+
587+
private _findParentForNode(node: T, index: number) {
588+
// In all cases, we have a mapping from node to level; all we need to do here is backtrack in
589+
// our flattened list of nodes to determine the first node that's of a level lower than the
590+
// provided node.
591+
if (!this._dataNodes) {
592+
return null;
593+
}
594+
const currentLevel = this._levels.get(node) ?? 0;
595+
for (let parentIndex = index; parentIndex >= 0; parentIndex--) {
596+
const parentNode = this._dataNodes[parentIndex];
597+
const parentLevel = this._levels.get(parentNode) ?? 0;
598+
599+
if (parentLevel < currentLevel) {
600+
return parentNode;
601+
}
602+
}
603+
return null;
604+
}
549605
}
550606

551607
/**

0 commit comments

Comments
 (0)