@@ -1036,16 +1036,25 @@ export class Virtualizer<
10361036
10371037 let end : number
10381038 // If there are no measurements, set the end to paddingStart
1039+ // If there is only one lane, use the last measurement's end
1040+ // Otherwise find the maximum end value among all measurements
10391041 if ( measurements . length === 0 ) {
10401042 end = this . options . paddingStart
1043+ } else if ( this . options . lanes === 1 ) {
1044+ end = measurements [ measurements . length - 1 ] ?. end ?? 0
10411045 } else {
1042- // If lanes is 1, use the last measurement's end, otherwise find the maximum end value among all measurements
1043- end =
1044- this . options . lanes === 1
1045- ? ( measurements [ measurements . length - 1 ] ?. end ?? 0 )
1046- : Math . max (
1047- ...measurements . slice ( - this . options . lanes ) . map ( ( m ) => m . end ) ,
1048- )
1046+ const endByLane = Array < number | null > ( this . options . lanes ) . fill ( null )
1047+ let endIndex = measurements . length - 1
1048+ while ( endIndex > 0 && endByLane . some ( ( val ) => val === null ) ) {
1049+ const item = measurements [ endIndex ] !
1050+ if ( endByLane [ item . lane ] === null ) {
1051+ endByLane [ item . lane ] = item . end
1052+ }
1053+
1054+ endIndex --
1055+ }
1056+
1057+ end = Math . max ( ...endByLane . filter ( ( val ) : val is number => val !== null ) )
10491058 }
10501059
10511060 return Math . max (
@@ -1121,14 +1130,35 @@ function calculateRange({
11211130 )
11221131 let endIndex = startIndex
11231132
1124- while (
1125- endIndex < lastIndex &&
1126- measurements [ endIndex ] ! . end < scrollOffset + outerSize
1127- ) {
1128- endIndex ++
1129- }
1133+ if ( lanes === 1 ) {
1134+ while (
1135+ endIndex < lastIndex &&
1136+ measurements [ endIndex ] ! . end < scrollOffset + outerSize
1137+ ) {
1138+ endIndex ++
1139+ }
1140+ } else if ( lanes > 1 ) {
1141+ // Expand forward until we include the visible items from all lanes
1142+ // which are closer to the end of the virtualizer window
1143+ const endPerLane = Array ( lanes ) . fill ( 0 )
1144+ while (
1145+ endIndex < lastIndex &&
1146+ endPerLane . some ( ( pos ) => pos < scrollOffset + outerSize )
1147+ ) {
1148+ const item = measurements [ endIndex ] !
1149+ endPerLane [ item . lane ] = item . end
1150+ endIndex ++
1151+ }
1152+
1153+ // Expand backward until we include all lanes' visible items
1154+ // closer to the top
1155+ const startPerLane = Array ( lanes ) . fill ( scrollOffset + outerSize )
1156+ while ( startIndex > 0 && startPerLane . some ( ( pos ) => pos >= scrollOffset ) ) {
1157+ const item = measurements [ startIndex ] !
1158+ startPerLane [ item . lane ] = item . start
1159+ startIndex --
1160+ }
11301161
1131- if ( lanes > 1 ) {
11321162 // Align startIndex to the beginning of its lane
11331163 startIndex = Math . max ( 0 , startIndex - ( startIndex % lanes ) )
11341164 // Align endIndex to the end of its lane
0 commit comments