@@ -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).
@@ -328,7 +342,10 @@ export class CdkTree<T, K = T> implements AfterContentChecked, CollectionViewer,
328342 this . insertNode ( data [ currentIndex ! ] , currentIndex ! , viewContainer , parentData ) ;
329343 } else if ( currentIndex == null ) {
330344 viewContainer . remove ( adjustedPreviousIndex ! ) ;
345+ const group = this . _getNodeGroup ( item . item ) ;
331346 this . _levels . delete ( item . item ) ;
347+ this . _parents . delete ( item . item ) ;
348+ group . splice ( group . indexOf ( item . item ) , 1 ) ;
332349 } else {
333350 const view = viewContainer . get ( adjustedPreviousIndex ! ) ;
334351 viewContainer . move ( view ! , currentIndex ) ;
@@ -382,6 +399,19 @@ export class CdkTree<T, K = T> implements AfterContentChecked, CollectionViewer,
382399 context . level = 0 ;
383400 }
384401 this . _levels . set ( nodeData , context . level ) ;
402+ const parent = parentData ?? this . _findParentForNode ( nodeData , index ) ;
403+ this . _parents . set ( nodeData , parent ) ;
404+
405+ // Determine where to insert this new node into the group, then insert it.
406+ // We do this by looking at the previous node in our flattened node list. If it's in the same
407+ // group, we place the current node after. Otherwise, we place it at the start of the group.
408+ const currentGroup = this . _groups . get ( context . level ) ?? new Map < T | null , T [ ] > ( ) ;
409+ const group = currentGroup . get ( parent ) ?? [ ] ;
410+ const previousNode = this . _dataNodes ?. [ index - 1 ] ;
411+ const groupInsertionIndex = ( previousNode && group . indexOf ( previousNode ) + 1 ) ?? 0 ;
412+ group . splice ( groupInsertionIndex , 0 , nodeData ) ;
413+ currentGroup . set ( parent , group ) ;
414+ this . _groups . set ( context . level , currentGroup ) ;
385415
386416 // Use default tree nodeOutlet, or nested node's nodeOutlet
387417 const container = viewContainer ? viewContainer : this . _nodeOutlet . viewContainer ;
@@ -712,6 +742,32 @@ export class CdkTree<T, K = T> implements AfterContentChecked, CollectionViewer,
712742 return observableOf ( nodes ) ;
713743 }
714744 }
745+
746+ private _getNodeGroup ( node : T ) {
747+ const level = this . _levels . get ( node ) ;
748+ const parent = this . _parents . get ( node ) ;
749+ const group = this . _groups . get ( level ?? 0 ) ?. get ( parent ?? null ) ;
750+ return group ?? [ node ] ;
751+ }
752+
753+ private _findParentForNode ( node : T , index : number ) {
754+ // In all cases, we have a mapping from node to level; all we need to do here is backtrack in
755+ // our flattened list of nodes to determine the first node that's of a level lower than the
756+ // provided node.
757+ if ( ! this . _dataNodes ) {
758+ return null ;
759+ }
760+ const currentLevel = this . _levels . get ( node ) ?? 0 ;
761+ for ( let parentIndex = index ; parentIndex >= 0 ; parentIndex -- ) {
762+ const parentNode = this . _dataNodes [ parentIndex ] ;
763+ const parentLevel = this . _levels . get ( parentNode ) ?? 0 ;
764+
765+ if ( parentLevel < currentLevel ) {
766+ return parentNode ;
767+ }
768+ }
769+ return null ;
770+ }
715771}
716772
717773/**
0 commit comments