@@ -3,6 +3,7 @@ import type React from "react"
33import {
44 createRef ,
55 type MouseEvent ,
6+ type MutableRefObject ,
67 useCallback ,
78 useLayoutEffect ,
89 useMemo ,
@@ -76,8 +77,8 @@ function renderGroupRows<G extends TimeTableGroup, I extends TimeSlotBooking>(
7677 g : number ,
7778 refCollection : React . MutableRefObject < HTMLElement > [ ] ,
7879 groupRowsRendered : JSX . Element [ ] ,
79- renderedCells : Set < number > ,
80- changedGroupRows : Set < number > ,
80+ renderedGroupsRef : MutableRefObject < Set < number > > ,
81+ changedGroupRowsRef : MutableRefObject < Set < number > > ,
8182 onGroupClick : ( ( _ : G ) => void ) | undefined ,
8283 placeHolderHeight : number ,
8384 columnWidth : number ,
@@ -107,7 +108,7 @@ function renderGroupRows<G extends TimeTableGroup, I extends TimeSlotBooking>(
107108 groupEntry ,
108109 groupRows ,
109110 groupEntriesArray ,
110- changedGroupRows ,
111+ changedGroupRowsRef ,
111112 renderCells ,
112113 )
113114 throw new Error ( "TimeTable - group entry not found" )
@@ -130,16 +131,10 @@ function renderGroupRows<G extends TimeTableGroup, I extends TimeSlotBooking>(
130131 refCollection [ g ] = mref
131132 }
132133 const rendering = g >= renderCells [ 0 ] && g <= renderCells [ 1 ]
133- if ( rendering ) {
134- renderedCells . add ( g )
135- } else {
136- renderedCells . delete ( g )
137- }
138- changedGroupRows . delete ( g )
139134
140135 groupRowsRendered [ g ] = (
141136 < GroupRows < G , I >
142- key = { `${ groupEntry . title } ${ g } -${ rendering } ` }
137+ key = { `${ g } - ${ groupEntry . id } -${ rendering } ` }
143138 group = { groupEntry }
144139 groupNumber = { g }
145140 itemRows = { rows }
@@ -154,6 +149,10 @@ function renderGroupRows<G extends TimeTableGroup, I extends TimeSlotBooking>(
154149 slotsArray = { slotsArray }
155150 timeFrameDay = { timeFrameDay }
156151 viewType = { viewType }
152+ // side effect props
153+ renderedGroupsRef = { renderedGroupsRef }
154+ changedGroupRowsRef = { changedGroupRowsRef }
155+ //
157156 />
158157 )
159158}
@@ -165,7 +164,6 @@ export default function TimeTableRows<
165164 G extends TimeTableGroup ,
166165 I extends TimeSlotBooking ,
167166> ( {
168- //entries,
169167 groupRows,
170168 onGroupClick,
171169 onTimeSlotItemClick,
@@ -200,18 +198,21 @@ export default function TimeTableRows<
200198 const changedGroupRows = useRef < Set < number > > ( new Set < number > ( ) )
201199
202200 // groupRowsRendered is the array of rendered group rows JSX Elements, which is returned from the component
203- const groupRowsRendered = useRef < JSX . Element [ ] > ( [ ] )
201+ //const groupRowsRendered = useRef<JSX.Element[]>([])
202+ const [ groupRowsRendered , setGroupRowsRendered ] = useState < JSX . Element [ ] > (
203+ [ ] ,
204+ )
204205
205206 const slotsArrayCurrent = useRef ( slotsArray )
206207 const viewTypeCurrent = useRef ( viewType )
207208 const timeFrameDayCurrent = useRef ( timeFrameDay )
208209
209210 // groupRowsRenderedIdx is the index of the group row which is currently rendered using batch rendering
210- const [ groupRowsRenderedIdx , setGroupRowsRenderedIdx ] = useState ( - 1 )
211+ // const [groupRowsRenderedIdx, setGroupRowsRenderedIdx] = useState(-1)
211212 // this is a reference to the current groupRowsRenderedIdx to avoid changing the handleIntersections callback on groupRowsRenderedIdx change
212213 // and to know how far we are with the initial rendering... this is needed to know when to start the intersection observer
213214 // and it should be only set to 0 when the group rows change
214- const groupRowsRenderedIdxRef = useRef ( groupRowsRenderedIdx )
215+ // const groupRowsRenderedIdxRef = useRef(groupRowsRenderedIdx)
215216
216217 if (
217218 slotsArrayCurrent . current !== slotsArray ||
@@ -223,13 +224,13 @@ export default function TimeTableRows<
223224 slotsArrayCurrent . current = slotsArray
224225 viewTypeCurrent . current = viewType
225226 timeFrameDayCurrent . current = timeFrameDay
226- setGroupRowsRenderedIdx ( - 1 )
227- groupRowsRenderedIdxRef . current = 0
227+ setGroupRowsRendered ( [ ] )
228228 }
229229
230230 const rateLimiterIntersection = useIdleRateLimitHelper ( renderIdleTimeout )
231231 const rateLimiterRendering = useIdleRateLimitHelper ( renderIdleTimeout )
232232 const debounceIntersection = useDebounceHelper ( intersectionStackDelay )
233+ const allRenderedPlaceholderCommitedToDOM = useRef ( false )
233234
234235 // handle intersection is called after intersectionStackDelay ms to avoid too many calls
235236 // and checks which groups are intersecting with the intersection container.
@@ -253,8 +254,10 @@ export default function TimeTableRows<
253254 const bottom = intersectionbb . bottom + rowsMargin * rowHeight
254255
255256 // find the last rendered group row
256- let lastIdx = groupRowsRenderedIdxRef . current
257+ let lastIdx = refCollection . current . length - 1
258+ allRenderedPlaceholderCommitedToDOM . current = true
257259 while ( ! refCollection . current [ lastIdx ] ?. current && lastIdx > 0 ) {
260+ allRenderedPlaceholderCommitedToDOM . current = false
258261 lastIdx --
259262 }
260263
@@ -338,6 +341,7 @@ export default function TimeTableRows<
338341 renderGroupRangeRef . current = newRenderCells
339342 return newRenderCells
340343 }
344+ //console.log("TimeTable - intersected group rows not changed", prev)
341345 return prev
342346 } )
343347 } , [ intersectionContainerRef . current , headerRef . current , rowHeight ] )
@@ -351,25 +355,20 @@ export default function TimeTableRows<
351355 setCurrentGroupRows ( ( currentGroupRows ) => {
352356 changedGroupRows . current . clear ( )
353357 if ( ! groupRows ) {
354- setGroupRowsRenderedIdx ( - 1 )
355- groupRowsRenderedIdxRef . current = 0
356- groupRowsRendered . current = [ ]
357358 renderedGroups . current . clear ( )
358359 refCollection . current = [ ]
360+ setGroupRowsRendered ( [ ] )
359361 console . log ( "TimeTable - group rows are null" )
360362 return groupRows
361363 }
362364
363- if ( groupRowsRendered . current . length > groupRows . size ) {
365+ if ( groupRowsRendered . length > groupRows . size ) {
364366 // shorten and remove rendered elements array, if too long
365367 console . info (
366- `Timetable - shorten rendered elements array from ${ groupRowsRendered . current . length } to ${ groupRows . size } ` ,
368+ `Timetable - shorten rendered elements array from ${ groupRowsRendered . length } to ${ groupRows . size } ` ,
367369 )
368- groupRowsRendered . current . length = groupRows . size
370+ setGroupRowsRendered ( groupRowsRendered . slice ( 0 , groupRows . size ) )
369371 refCollection . current . length = groupRows . size
370- if ( groupRowsRenderedIdxRef . current >= groupRows . size ) {
371- groupRowsRenderedIdxRef . current = groupRows . size - 1
372- }
373372 }
374373
375374 // determine when new ones start
@@ -410,6 +409,9 @@ export default function TimeTableRows<
410409 if ( updateCounter ) {
411410 console . log (
412411 `TimeTable - group rows require updated rendering ${ updateCounter } , with first ${ changedFound } ` ,
412+ renderGroupRangeRef . current ,
413+ currentGroupRowsRef . current . size ,
414+ groupRows . size ,
413415 )
414416 }
415417 currentGroupRowsRef . current = groupRows
@@ -454,7 +456,8 @@ export default function TimeTableRows<
454456
455457 //** ------- RENDERING ------ */
456458 const renderBatch = useCallback ( ( ) => {
457- setGroupRowsRenderedIdx ( ( groupRowsRenderedIdx ) => {
459+ setGroupRowsRendered ( ( groupRowsRenderedPrev ) => {
460+ const groupRowsRendered = [ ...groupRowsRenderedPrev ]
458461 if ( changedGroupRows . current . size ) {
459462 let counter = 0
460463 if ( renderGroupRangeRef . current [ 0 ] > - 1 ) {
@@ -474,9 +477,9 @@ export default function TimeTableRows<
474477 currentGroupRowsRef . current ,
475478 i ,
476479 refCollection . current ,
477- groupRowsRendered . current ,
478- renderedGroups . current ,
479- changedGroupRows . current ,
480+ groupRowsRendered ,
481+ renderedGroups ,
482+ changedGroupRows ,
480483 onGroupClick ,
481484 placeHolderHeight ,
482485 columnWidth ,
@@ -489,15 +492,15 @@ export default function TimeTableRows<
489492 )
490493 counter ++
491494 if ( counter > timeTableGroupRenderBatchSize ) {
492- return groupRowsRenderedIdx - 1
495+ return groupRowsRendered
493496 }
494497 }
495498 }
496499 }
497500 for ( const g of changedGroupRows . current ) {
498501 if (
499502 g > currentGroupRowsRef . current . size - 1 ||
500- g > groupRowsRenderedIdxRef . current
503+ g > groupRowsRendered . length - 1
501504 ) {
502505 changedGroupRows . current . delete ( g )
503506 continue
@@ -508,9 +511,9 @@ export default function TimeTableRows<
508511 currentGroupRowsRef . current ,
509512 g ,
510513 refCollection . current ,
511- groupRowsRendered . current ,
512- renderedGroups . current ,
513- changedGroupRows . current ,
514+ groupRowsRendered ,
515+ renderedGroups ,
516+ changedGroupRows ,
514517 onGroupClick ,
515518 placeHolderHeight ,
516519 columnWidth ,
@@ -523,26 +526,24 @@ export default function TimeTableRows<
523526 )
524527 counter ++
525528 if ( counter > timeTableGroupRenderBatchSize ) {
526- return groupRowsRenderedIdx - 1
529+ return groupRowsRendered
527530 }
528531 }
529532 }
530533
531- //normal placeholder rendering
532- let ret = groupRowsRendered . current . length
533534 let counter = 0
534535 while (
535- ret < currentGroupRowsRef . current . size &&
536+ groupRowsRendered . length < currentGroupRowsRef . current . size &&
536537 counter < timeTableGroupRenderBatchSize
537538 ) {
538539 renderGroupRows (
539540 renderGroupRangeRef . current ,
540541 currentGroupRowsRef . current ,
541- ret ,
542+ groupRowsRendered . length ,
542543 refCollection . current ,
543- groupRowsRendered . current ,
544- renderedGroups . current ,
545- changedGroupRows . current ,
544+ groupRowsRendered ,
545+ renderedGroups ,
546+ changedGroupRows ,
546547 onGroupClick ,
547548 placeHolderHeight ,
548549 columnWidth ,
@@ -554,11 +555,9 @@ export default function TimeTableRows<
554555 viewType ,
555556 )
556557 ++ counter
557- ++ ret
558558 }
559- groupRowsRenderedIdxRef . current = ret
560559 rateLimiterIntersection ( handleIntersections )
561- return ret
560+ return groupRowsRendered
562561 } )
563562 } , [
564563 onGroupClick ,
@@ -576,14 +575,26 @@ export default function TimeTableRows<
576575
577576 if (
578577 changedGroupRows . current . size ||
579- groupRowsRenderedIdx < groupRows . size - 1 ||
578+ groupRowsRendered . length < groupRows . size ||
580579 renderedGroups . current . size <
581580 renderGroupRangeRef . current [ 1 ] - renderGroupRangeRef . current [ 0 ] + 1
582581 ) {
583582 rateLimiterRendering ( renderBatch )
583+ } else {
584+ // final rendering and intersection once all groups are rendered and committed to the DOM
585+ if ( ! allRenderedPlaceholderCommitedToDOM . current ) {
586+ rateLimiterIntersection ( handleIntersections )
587+ rateLimiterRendering ( renderBatch )
588+ // need to use flush sync in case of only very very few groups that we really render all groups
589+ // handleIntersections sets the allRenderedPlaceholderCommitedToDOM to true if all placeholders are found
590+ /*window.setTimeout(() => {
591+ rateLimiterIntersection(() => flushSync(handleIntersections))
592+ rateLimiterRendering(() => flushSync(renderBatch))
593+ }, 0)*/
594+ }
584595 }
585596
586- return groupRowsRendered . current
597+ return groupRowsRendered
587598}
588599
589600/**
@@ -963,6 +974,9 @@ type GroupRowsProps<G extends TimeTableGroup, I extends TimeSlotBooking> = {
963974 slotsArray : readonly Dayjs [ ]
964975 timeFrameDay : TimeFrameDay
965976 viewType : TimeTableViewType
977+ // this is a side effect to make sure that only the rendered groups are are set, because React sometimes optimizies the rendering out (so I have to keep track of the rendered groups in the actual invocation)
978+ renderedGroupsRef : React . MutableRefObject < Set < number > >
979+ changedGroupRowsRef : React . MutableRefObject < Set < number > >
966980}
967981
968982function GroupRows < G extends TimeTableGroup , I extends TimeSlotBooking > ( {
@@ -980,7 +994,20 @@ function GroupRows<G extends TimeTableGroup, I extends TimeSlotBooking>({
980994 timeFrameDay,
981995 viewType,
982996 mref,
997+ // ugly side effect props
998+ renderedGroupsRef,
999+ changedGroupRowsRef,
1000+ //
9831001} : GroupRowsProps < G , I > ) {
1002+ // ugly SIDE EFFECTs for now to make sure the rendered groups are set
1003+ changedGroupRowsRef . current . delete ( groupNumber )
1004+ if ( renderCells ) {
1005+ renderedGroupsRef . current . add ( groupNumber )
1006+ } else {
1007+ renderedGroupsRef . current . delete ( groupNumber )
1008+ }
1009+ //
1010+
9841011 const storeIdent = useTimeTableIdent ( )
9851012 const timeSlotSelectionDisabled =
9861013 useTTCTimeSlotSelectionDisabled ( storeIdent )
@@ -1039,6 +1066,7 @@ function GroupRows<G extends TimeTableGroup, I extends TimeSlotBooking>({
10391066 } `
10401067 : undefined
10411068 }
1069+ key = { `${ group . id } _h_${ renderCells } ` }
10421070 >
10431071 { renderCells && (
10441072 < div
@@ -1133,7 +1161,7 @@ function GroupRows<G extends TimeTableGroup, I extends TimeSlotBooking>({
11331161
11341162 tds . push (
11351163 < TableCell < G , I >
1136- key = { `${ groupNumber } -${ timeSlotNumber } -${ viewType } ` }
1164+ key = { `${ group . id } - ${ groupNumber } -${ timeSlotNumber } -${ viewType } ` }
11371165 timeSlotNumber = { timeSlotNumber }
11381166 isLastGroupRow = { r === rowCount - 1 }
11391167 isFirstRow = { r === 0 }
@@ -1174,6 +1202,7 @@ function GroupRows<G extends TimeTableGroup, I extends TimeSlotBooking>({
11741202 } }
11751203 data-group-id = { group . id }
11761204 data-test = { `unrendered-table-row_${ group . id } ` }
1205+ key = { `unrendered-table-row_${ group . id } ` }
11771206 ref = { mref as React . Ref < HTMLTableRowElement > }
11781207 className = "box-border m-0"
11791208 >
0 commit comments