1- /* eslint-disable react/no-unused-state,@typescript-eslint/consistent-type-assertions, no-labels,max-depth,complexity */
1+ /* eslint-disable no-labels,max-depth,complexity */
22import React , {
33 Component ,
44 ComponentType ,
@@ -19,15 +19,13 @@ import {
1919 DefaultTreeState ,
2020 noop ,
2121 RequestIdleCallbackDeadline ,
22- revisitRecord ,
23- visitRecord ,
2422} from './utils' ;
2523
2624export type NodeData = Readonly < {
2725 /**
2826 * Unique ID of the current node.
2927 */
30- id : string | symbol ;
28+ id : string ;
3129
3230 /**
3331 * Default node openness state. If the Tree component performs building a new
@@ -100,7 +98,7 @@ export type OpennessState<
10098export type TreeProps <
10199 TData extends NodeData ,
102100 TNodePublicState extends NodePublicState < TData >
103- > = Readonly < Omit < ListProps , 'children' | 'itemCount' > > &
101+ > = Readonly < Omit < ListProps , 'children' | 'itemCount' | 'itemKey' > > &
104102 Readonly < {
105103 buildingTaskTimeout ?: number ;
106104 children : ComponentType < NodeComponentProps < TData , TNodePublicState > > ;
@@ -114,7 +112,7 @@ export type TreeState<
114112 TData extends NodeData ,
115113 TNodePublicState extends NodePublicState < TData >
116114> = Readonly < {
117- order ?: Array < string | symbol > ;
115+ order ?: string [ ] ;
118116 computeTree : TreeComputer < any , any , any , any > ;
119117 records : ReadonlyMap < string | symbol , NodeRecord < TNodePublicState > > ;
120118 recomputeTree : (
@@ -233,7 +231,7 @@ const generateNewTree = <
233231 async && state . records !== undefined ;
234232 const { records : previousRecords } = state ;
235233
236- const order : Array < string | symbol > = [ ] ;
234+ const order : string [ ] = [ ] ;
237235 const records = new Map < string | symbol , NodeRecord < TNodePublicState > > ( ) ;
238236 const requestIdleCallbackOptions = buildingTaskTimeout
239237 ? { timeout : buildingTaskTimeout }
@@ -299,7 +297,14 @@ const generateNewTree = <
299297 order . push ( currentRecord . public . data . id ) ;
300298 }
301299
302- currentRecord = visitRecord ( currentRecord ) ;
300+ currentRecord . visited = currentRecord . child !== null ;
301+
302+ currentRecord =
303+ currentRecord . child !== null
304+ ? currentRecord . child
305+ : currentRecord . sibling !== null
306+ ? currentRecord . sibling
307+ : currentRecord . parent ;
303308 }
304309
305310 tempRecord = currentRecord ;
@@ -325,7 +330,11 @@ const generateNewTree = <
325330
326331 tempRecord = childRecord ;
327332 } else {
328- currentRecord = revisitRecord ( currentRecord ) ;
333+ currentRecord . visited = false ;
334+ currentRecord =
335+ currentRecord . sibling !== null
336+ ? currentRecord . sibling
337+ : currentRecord . parent ;
329338 tempRecord = currentRecord ;
330339 }
331340 }
@@ -399,14 +408,40 @@ const updateExistingTree = <
399408 let apply : ( ) => void = noop ;
400409
401410 if ( ownerRecord . isShown ) {
402- if ( open && ! ownerRecord . public . isOpen ) {
403- // If received rules require us to open the subtree that is not currently
404- // open, we have to add new ids to the order list.
411+ if ( open ) {
412+ // If received rules require us to open the subtree, we have 2 cases:
413+ // 1. The node is not opened yet. In this case we simply have to
414+ // calculate and add new ids.
415+ // 2. The node is opened already. In this case we have to remove all
416+ // existing ids and replace them with new ids.
405417
406418 const index = order ! . indexOf ( id ) ;
419+
420+ // Here we calculate a count of visible subtree nodes to remove from
421+ // `order`. Then we will replace the gap with the updated list of
422+ // subtree nodes.
423+ let recordNextToSubtree : NodeRecord <
424+ TNodePublicState
425+ > | null = ownerRecord ;
426+
427+ while ( recordNextToSubtree !== null ) {
428+ if ( recordNextToSubtree . sibling !== null ) {
429+ recordNextToSubtree = recordNextToSubtree . sibling ;
430+ break ;
431+ }
432+
433+ recordNextToSubtree = recordNextToSubtree . parent ;
434+ }
435+
436+ const countToRemove =
437+ recordNextToSubtree === null
438+ ? order ! . length - 1 - index
439+ : order ! . indexOf ( recordNextToSubtree . public . data . id ) - 1 - index ;
440+
407441 const orderParts : Array < Array < number | string | symbol > > = [
408- [ index + 1 , 0 ] ,
442+ [ index + 1 , countToRemove ] ,
409443 ] ;
444+
410445 let orderPartsCursor = 0 ;
411446
412447 // Unfortunately, splice cannot work with big arrays. If array exceeds
@@ -445,7 +480,7 @@ const updateExistingTree = <
445480 order ! . splice ( ...orderParts [ i ] ) ;
446481 }
447482 } ;
448- } else if ( ! open && ownerRecord . public . isOpen ) {
483+ } else if ( ownerRecord . public . isOpen ) {
449484 // If received rules require us to close the subtree, we have to remove
450485 // all subtree ids from the order list.
451486
@@ -488,9 +523,31 @@ const updateExistingTree = <
488523 update ( currentRecord ) ;
489524 }
490525
491- currentRecord = visitRecord ( currentRecord ) ;
526+ currentRecord . visited = currentRecord . child !== null ;
527+
528+ // This algorithm is a bit different from the visit algorithm in the
529+ // tree generator. We are restricted with the bounds of a subtree and
530+ // shouldn't go over it. So we cannot search for the ownerRecord's
531+ // parent or sibling because it will lead us out of the subtree.
532+ currentRecord =
533+ // Look for child in any case
534+ currentRecord . child !== null
535+ ? currentRecord . child
536+ : // Stop looking for next element if currentRecord is root.
537+ currentRecord === ownerRecord
538+ ? null
539+ : // Otherwise, look for sibling or parent
540+ currentRecord . sibling !== null
541+ ? currentRecord . sibling
542+ : currentRecord . parent ;
492543 } else {
493- currentRecord = revisitRecord ( currentRecord , ownerRecord ) ;
544+ currentRecord . visited = false ;
545+ currentRecord =
546+ currentRecord === ownerRecord
547+ ? null
548+ : currentRecord . sibling !== null
549+ ? currentRecord . sibling
550+ : currentRecord . parent ;
494551 }
495552 }
496553
@@ -553,10 +610,12 @@ class Tree<
553610
554611 this . getRecordData = this . getRecordData . bind ( this ) ;
555612
613+ /* eslint-disable react/no-unused-state,@typescript-eslint/consistent-type-assertions */
556614 this . state = {
557615 recomputeTree : this . recomputeTree . bind ( this ) ,
558616 setState : this . setState . bind ( this ) ,
559617 } as TState ;
618+ /* eslint-enable react/no-unused-state,@typescript-eslint/consistent-type-assertions */
560619 }
561620
562621 protected getItemData ( ) : TypedListChildComponentData <
@@ -597,7 +656,7 @@ class Tree<
597656 this . list . current ?. scrollTo ( scrollOffset ) ;
598657 }
599658
600- public scrollToItem ( id : string | symbol , align ?: Align ) : void {
659+ public scrollToItem ( id : string , align ?: Align ) : void {
601660 // eslint-disable-next-line react/destructuring-assignment
602661 this . list . current ?. scrollToItem ( this . state . order ! . indexOf ( id ) , align ) ;
603662 }
0 commit comments