@@ -96,6 +96,20 @@ export class CdkTree<T, K = T> implements AfterContentChecked, CollectionViewer,
9696 /** Level of nodes */
9797 private _levels : Map < T , number > = new Map < T , number > ( ) ;
9898
99+ /** The immediate parents for a node. This is `null` if there is no parent. */
100+ private _parents : Map < T , T | null > = new Map < T , T | null > ( ) ;
101+
102+ /**
103+ * The internal node groupings for each node; we use this, primarily for flattened trees, to
104+ * determine where a particular node is within each group.
105+ *
106+ * The structure of this is that:
107+ * - the outer index is the level
108+ * - the inner index is the parent node for this particular group. If there is no parent node, we
109+ * use `null`.
110+ */
111+ private _groups : Map < number , Map < T | null , T [ ] > > = new Map < number , Map < T | null , T [ ] > > ( ) ;
112+
99113 /**
100114 * Provides a stream containing the latest data array to render. Influenced by the tree's
101115 * stream of view window (what dataNodes are currently on screen).
@@ -339,7 +353,10 @@ export class CdkTree<T, K = T> implements AfterContentChecked, CollectionViewer,
339353 this . insertNode ( data [ currentIndex ! ] , currentIndex ! , viewContainer , parentData ) ;
340354 } else if ( currentIndex == null ) {
341355 viewContainer . remove ( adjustedPreviousIndex ! ) ;
356+ const group = this . _getNodeGroup ( item . item ) ;
342357 this . _levels . delete ( item . item ) ;
358+ this . _parents . delete ( item . item ) ;
359+ group . splice ( group . indexOf ( item . item ) , 1 ) ;
343360 } else {
344361 const view = viewContainer . get ( adjustedPreviousIndex ! ) ;
345362 viewContainer . move ( view ! , currentIndex ) ;
@@ -393,6 +410,19 @@ export class CdkTree<T, K = T> implements AfterContentChecked, CollectionViewer,
393410 context . level = 0 ;
394411 }
395412 this . _levels . set ( nodeData , context . level ) ;
413+ const parent = parentData ?? this . _findParentForNode ( nodeData , index ) ;
414+ this . _parents . set ( nodeData , parent ) ;
415+
416+ // Determine where to insert this new node into the group, then insert it.
417+ // We do this by looking at the previous node in our flattened node list. If it's in the same
418+ // group, we place the current node after. Otherwise, we place it at the start of the group.
419+ const currentGroup = this . _groups . get ( context . level ) ?? new Map < T | null , T [ ] > ( ) ;
420+ const group = currentGroup . get ( parent ) ?? [ ] ;
421+ const previousNode = this . _dataNodes ?. [ index - 1 ] ;
422+ const groupInsertionIndex = ( previousNode && group . indexOf ( previousNode ) + 1 ) ?? 0 ;
423+ group . splice ( groupInsertionIndex , 0 , nodeData ) ;
424+ currentGroup . set ( parent , group ) ;
425+ this . _groups . set ( context . level , currentGroup ) ;
396426
397427 // Use default tree nodeOutlet, or nested node's nodeOutlet
398428 const container = viewContainer ? viewContainer : this . _nodeOutlet . viewContainer ;
@@ -706,6 +736,32 @@ export class CdkTree<T, K = T> implements AfterContentChecked, CollectionViewer,
706736 ) ;
707737 }
708738 }
739+
740+ private _getNodeGroup ( node : T ) {
741+ const level = this . _levels . get ( node ) ;
742+ const parent = this . _parents . get ( node ) ;
743+ const group = this . _groups . get ( level ?? 0 ) ?. get ( parent ?? null ) ;
744+ return group ?? [ node ] ;
745+ }
746+
747+ private _findParentForNode ( node : T , index : number ) {
748+ // In all cases, we have a mapping from node to level; all we need to do here is backtrack in
749+ // our flattened list of nodes to determine the first node that's of a level lower than the
750+ // provided node.
751+ if ( ! this . _dataNodes ) {
752+ return null ;
753+ }
754+ const currentLevel = this . _levels . get ( node ) ?? 0 ;
755+ for ( let parentIndex = index ; parentIndex >= 0 ; parentIndex -- ) {
756+ const parentNode = this . _dataNodes [ parentIndex ] ;
757+ const parentLevel = this . _levels . get ( parentNode ) ?? 0 ;
758+
759+ if ( parentLevel < currentLevel ) {
760+ return parentNode ;
761+ }
762+ }
763+ return null ;
764+ }
709765}
710766
711767/**
0 commit comments