@@ -74,8 +74,10 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
7474 protected loaderHeight : number ;
7575 protected placeholderHeight : number ;
7676 protected enableEmptyState : boolean ;
77- protected lastValidRect : Rect ;
77+ /** The rectangle containing currently valid layout infos. */
7878 protected validRect : Rect ;
79+ /** The rectangle of requested layout infos so far. */
80+ protected requestedRect : Rect ;
7981
8082 /**
8183 * Creates a new ListLayout with options. See the list of properties below for a description
@@ -98,8 +100,8 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
98100 this . rootNodes = [ ] ;
99101 this . lastWidth = 0 ;
100102 this . lastCollection = null ;
101- this . lastValidRect = new Rect ( ) ;
102103 this . validRect = new Rect ( ) ;
104+ this . requestedRect = new Rect ( ) ;
103105 this . contentSize = new Size ( ) ;
104106 }
105107
@@ -147,9 +149,8 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
147149 return ;
148150 }
149151
150- if ( ! this . validRect . containsRect ( rect ) ) {
151- this . lastValidRect = this . validRect ;
152- this . validRect = this . validRect . union ( rect ) ;
152+ if ( ! this . requestedRect . containsRect ( rect ) ) {
153+ this . requestedRect = this . requestedRect . union ( rect ) ;
153154 this . rootNodes = this . buildCollection ( ) ;
154155 } else {
155156 // Ensure all of the persisted keys are available.
@@ -165,11 +166,10 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
165166 // If the layout info wasn't found, it might be outside the bounds of the area that we've
166167 // computed layout for so far. This can happen when accessing a random key, e.g pressing Home/End.
167168 // Compute the full layout and try again.
168- if ( ! this . layoutInfos . has ( key ) && this . validRect . area < this . contentSize . area && this . lastCollection ) {
169- this . lastValidRect = this . validRect ;
170- this . validRect = new Rect ( 0 , 0 , Infinity , Infinity ) ;
169+ if ( ! this . layoutInfos . has ( key ) && this . requestedRect . area < this . contentSize . area && this . lastCollection ) {
170+ this . requestedRect = new Rect ( 0 , 0 , Infinity , Infinity ) ;
171171 this . rootNodes = this . buildCollection ( ) ;
172- this . validRect = new Rect ( 0 , 0 , this . contentSize . width , this . contentSize . height ) ;
172+ this . requestedRect = new Rect ( 0 , 0 , this . contentSize . width , this . contentSize . height ) ;
173173 return true ;
174174 }
175175
@@ -194,8 +194,7 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
194194 // Otherwise we can reuse cached layout infos outside the current visible rect.
195195 this . invalidateEverything = this . shouldInvalidateEverything ( invalidationContext ) ;
196196 if ( this . invalidateEverything ) {
197- this . lastValidRect = this . validRect ;
198- this . validRect = this . virtualizer . visibleRect . copy ( ) ;
197+ this . requestedRect = this . virtualizer . visibleRect . copy ( ) ;
199198 }
200199
201200 this . rootNodes = this . buildCollection ( ) ;
@@ -217,6 +216,7 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
217216 this . lastWidth = this . virtualizer . visibleRect . width ;
218217 this . lastCollection = this . collection ;
219218 this . invalidateEverything = false ;
219+ this . validRect = this . requestedRect . copy ( ) ;
220220 }
221221
222222 protected buildCollection ( ) : LayoutNode [ ] {
@@ -227,7 +227,7 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
227227 let rowHeight = ( this . rowHeight ?? this . estimatedRowHeight ) ;
228228
229229 // Skip rows before the valid rectangle unless they are already cached.
230- if ( node . type === 'item' && y + rowHeight < this . validRect . y && ! this . isValid ( node , y ) ) {
230+ if ( node . type === 'item' && y + rowHeight < this . requestedRect . y && ! this . isValid ( node , y ) ) {
231231 y += rowHeight ;
232232 skipped ++ ;
233233 continue ;
@@ -237,7 +237,7 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
237237 y = layoutNode . layoutInfo . rect . maxY ;
238238 nodes . push ( layoutNode ) ;
239239
240- if ( node . type === 'item' && y > this . validRect . maxY ) {
240+ if ( node . type === 'item' && y > this . requestedRect . maxY ) {
241241 y += ( this . collection . size - ( nodes . length + skipped ) ) * rowHeight ;
242242 break ;
243243 }
@@ -272,8 +272,8 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
272272 cached &&
273273 cached . node === node &&
274274 y === ( cached . header || cached . layoutInfo ) . rect . y &&
275- cached . layoutInfo . rect . intersects ( this . lastValidRect ) &&
276- cached . validRect . containsRect ( cached . layoutInfo . rect . intersection ( this . validRect ) )
275+ cached . layoutInfo . rect . intersects ( this . validRect ) &&
276+ cached . validRect . containsRect ( cached . layoutInfo . rect . intersection ( this . requestedRect ) )
277277 ) ;
278278 }
279279
@@ -327,7 +327,7 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
327327 let rowHeight = ( this . rowHeight ?? this . estimatedRowHeight ) ;
328328
329329 // Skip rows before the valid rectangle unless they are already cached.
330- if ( y + rowHeight < this . validRect . y && ! this . isValid ( node , y ) ) {
330+ if ( y + rowHeight < this . requestedRect . y && ! this . isValid ( node , y ) ) {
331331 y += rowHeight ;
332332 skipped ++ ;
333333 continue ;
@@ -337,7 +337,7 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
337337 y = layoutNode . layoutInfo . rect . maxY ;
338338 children . push ( layoutNode ) ;
339339
340- if ( y > this . validRect . maxY ) {
340+ if ( y > this . requestedRect . maxY ) {
341341 // Estimate the remaining height for rows that we don't need to layout right now.
342342 y += ( [ ...getChildNodes ( node , this . collection ) ] . length - ( children . length + skipped ) ) * rowHeight ;
343343 break ;
@@ -350,7 +350,7 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
350350 header,
351351 layoutInfo,
352352 children,
353- validRect : layoutInfo . rect . intersection ( this . validRect )
353+ validRect : layoutInfo . rect . intersection ( this . requestedRect )
354354 } ;
355355 }
356356
@@ -387,7 +387,7 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
387387 return {
388388 layoutInfo : header ,
389389 children : [ ] ,
390- validRect : header . rect . intersection ( this . validRect )
390+ validRect : header . rect . intersection ( this . requestedRect )
391391 } ;
392392 }
393393
@@ -444,6 +444,13 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
444444 newLayoutInfo . rect . height = size . height ;
445445 this . layoutInfos . set ( key , newLayoutInfo ) ;
446446
447+ // Items after this layoutInfo will need to be repositioned to account for the new height.
448+ // Adjust the validRect so that only items above remain valid.
449+ this . validRect . height = Math . min ( this . validRect . height , layoutInfo . rect . y - this . validRect . y ) ;
450+
451+ // The requestedRect also needs to be adjusted to account for the height difference.
452+ this . requestedRect . height += newLayoutInfo . rect . height - layoutInfo . rect . height ;
453+
447454 // Invalidate layout for this layout node and all parents
448455 this . updateLayoutNode ( key , layoutInfo , newLayoutInfo ) ;
449456
@@ -462,8 +469,8 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
462469 private updateLayoutNode ( key : Key , oldLayoutInfo : LayoutInfo , newLayoutInfo : LayoutInfo ) {
463470 let n = this . layoutNodes . get ( key ) ;
464471 if ( n ) {
465- // Invalidate by reseting validRect.
466- n . validRect = new Rect ( ) ;
472+ // Invalidate by intersecting the validRect of this node with the overall validRect.
473+ n . validRect = n . validRect . intersection ( this . validRect ) ;
467474
468475 // Replace layout info in LayoutNode
469476 if ( n . header === oldLayoutInfo ) {
0 commit comments