@@ -10,64 +10,43 @@ type onIndeterminateChange = (e: {
1010 tableInstance : TableInstance ;
1111} ) => void ;
1212
13- const getParentRow = ( id : string , rowsById : TableInstance [ 'rowsById' ] ) : [ RowType , number ] => {
14- let lastDotIndex = id . lastIndexOf ( '.' ) ;
15- if ( lastDotIndex === - 1 ) {
16- lastDotIndex = Infinity ;
17- }
18- const parentRowId = id . slice ( 0 , lastDotIndex ) ;
19- return [ rowsById [ parentRowId ] , lastDotIndex ] ;
20- } ;
21-
22- const getIndeterminateRowIds = ( id : string ) : Record < string , boolean > => {
23- const indeterminateRowsById : Record < string , boolean > = { } ;
24- const lastDotIndex = id . lastIndexOf ( '.' ) ;
25- indeterminateRowsById [ id ] = true ;
26- if ( lastDotIndex !== - 1 ) {
27- // set all parent rows to indeterminate
28- Object . assign ( indeterminateRowsById , getIndeterminateRowIds ( id . slice ( 0 , lastDotIndex ) ) ) ;
29- }
30- return indeterminateRowsById ;
31- } ;
32-
13+ /**
14+ * Marks a row indeterminate if its subtree contains a node whose direct sub-rows are partially selected (some, not all).
15+ * A single O(n) post-order traversal visits each row once and reads every sub-row reference once.
16+ */
3317const getIndeterminate = (
3418 rows : RowType [ ] ,
35- rowsById : TableInstance [ 'rowsById' ] ,
3619 state : { selectedRowIds : AnalyticalTableState [ 'selectedRowIds' ] } ,
3720) : Record < string , boolean > => {
3821 const indeterminateRowsById : Record < string , boolean > = { } ;
39- let usedParentIndex = '' ;
40- const getIndeterminateRecursive = ( subRows : RowType [ ] , rowIdScope : string | null = null ) => {
41- for ( const row of subRows ) {
42- if ( row . subRows . length > 0 ) {
43- // find leaf nodes
44- getIndeterminateRecursive ( row . subRows , row . id ) ;
45- } else if ( rowIdScope !== null && usedParentIndex !== rowIdScope ) {
46- usedParentIndex = rowIdScope ;
47- const checkIndeterminate = ( rowId : string ) => {
48- const [ parentRow , dotIndex ] = getParentRow ( rowId , rowsById ) ;
49- const selectedRows = parentRow . subRows . filter ( ( item ) => state . selectedRowIds [ item . id ] ) ;
50- const areAllSelected = parentRow . subRows . length === selectedRows . length ;
51- const isOneSelected = selectedRows . length > 0 ;
52-
53- // if not all, but at least one subRow is selected, set the parent row's state to indeterminate
54- if ( isOneSelected && ! areAllSelected ) {
55- const parentRowId = parentRow . id ;
56- Object . assign ( indeterminateRowsById , getIndeterminateRowIds ( parentRowId ) ) ;
57- return ;
58- }
59- if ( dotIndex !== Infinity ) {
60- // recursively check indeterminate state until root nodes are reached
61- checkIndeterminate ( parentRow . id ) ;
62- }
63- return ;
64- } ;
22+ const { selectedRowIds } = state ;
6523
66- checkIndeterminate ( row . id ) ;
24+ const markSubtree = ( row : RowType ) : boolean => {
25+ const subRows = row . subRows ;
26+ if ( ! subRows ?. length ) {
27+ return false ;
28+ }
29+ let selectedCount = 0 ;
30+ let subtreeHasIndeterminate = false ;
31+ for ( const subRow of subRows ) {
32+ if ( selectedRowIds [ subRow . id ] ) {
33+ selectedCount ++ ;
6734 }
35+ if ( markSubtree ( subRow ) ) {
36+ subtreeHasIndeterminate = true ;
37+ }
38+ }
39+ const isPartiallySelected = selectedCount > 0 && selectedCount < subRows . length ;
40+ if ( isPartiallySelected || subtreeHasIndeterminate ) {
41+ indeterminateRowsById [ row . id ] = true ;
42+ return true ;
6843 }
44+ return false ;
6945 } ;
70- getIndeterminateRecursive ( rows ) ;
46+
47+ for ( const row of rows ) {
48+ markSubtree ( row ) ;
49+ }
7150 return indeterminateRowsById ;
7251} ;
7352
@@ -138,7 +117,7 @@ export const useIndeterminateRowSelection = (onIndeterminateChange?: onIndetermi
138117 } ;
139118 }
140119
141- const indeterminateRowsById = getIndeterminate ( rows , rowsById , { selectedRowIds : newState . selectedRowIds } ) ;
120+ const indeterminateRowsById = getIndeterminate ( rows , { selectedRowIds : newState . selectedRowIds } ) ;
142121
143122 return {
144123 ...newState ,
0 commit comments