@legendapp/list 3.2.0 → 3.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/react-native.js CHANGED
@@ -1116,32 +1116,34 @@ function scheduleAdaptiveRenderExit(ctx, exitDelay) {
1116
1116
  const state = ctx.state;
1117
1117
  clearAdaptiveRenderExitTimeout(ctx);
1118
1118
  if (exitDelay <= 0) {
1119
- setAdaptiveRender(ctx, "normal");
1119
+ setAdaptiveRender(ctx, "normal", "scroll");
1120
1120
  } else {
1121
1121
  const timeout = setTimeout(() => {
1122
1122
  state.timeouts.delete(timeout);
1123
1123
  state.timeoutAdaptiveRender = void 0;
1124
- setAdaptiveRender(ctx, "normal");
1124
+ setAdaptiveRender(ctx, "normal", "scroll");
1125
1125
  }, exitDelay);
1126
1126
  state.timeoutAdaptiveRender = timeout;
1127
1127
  state.timeouts.add(timeout);
1128
1128
  }
1129
1129
  }
1130
- function setAdaptiveRender(ctx, mode) {
1130
+ function setAdaptiveRender(ctx, mode, reason) {
1131
1131
  var _a3, _b;
1132
1132
  const previousMode = peek$(ctx, "adaptiveRender");
1133
1133
  if (previousMode !== mode) {
1134
1134
  set$(ctx, "adaptiveRender", mode);
1135
- (_b = (_a3 = ctx.state.props.adaptiveRender) == null ? void 0 : _a3.onChange) == null ? void 0 : _b.call(_a3, mode);
1135
+ (_b = (_a3 = ctx.state.props.adaptiveRender) == null ? void 0 : _a3.onChange) == null ? void 0 : _b.call(_a3, mode, reason);
1136
1136
  }
1137
1137
  }
1138
1138
  function resetAdaptiveRender(ctx) {
1139
+ var _a3, _b;
1139
1140
  clearAdaptiveRenderExitTimeout(ctx);
1140
- if (peek$(ctx, "adaptiveRender") !== "normal") {
1141
- setAdaptiveRender(ctx, "normal");
1141
+ const mode = (_b = (_a3 = ctx.state.props.adaptiveRender) == null ? void 0 : _a3.initialMode) != null ? _b : "normal";
1142
+ if (peek$(ctx, "adaptiveRender") !== mode) {
1143
+ setAdaptiveRender(ctx, mode, "initial");
1142
1144
  }
1143
1145
  }
1144
- function updateAdaptiveRender(ctx, scrollVelocity) {
1146
+ function updateAdaptiveRender(ctx, scrollVelocity, options) {
1145
1147
  var _a3, _b, _c;
1146
1148
  const state = ctx.state;
1147
1149
  const adaptiveRender = state.props.adaptiveRender;
@@ -1153,11 +1155,11 @@ function updateAdaptiveRender(ctx, scrollVelocity) {
1153
1155
  const exitVelocity = (_b = adaptiveRender.exitVelocity) != null ? _b : isWeb ? DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_VELOCITY : DEFAULT_ADAPTIVE_RENDER_EXIT_VELOCITY;
1154
1156
  const exitDelay = (_c = adaptiveRender.exitDelay) != null ? _c : isWeb ? DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_DELAY : DEFAULT_ADAPTIVE_RENDER_EXIT_DELAY;
1155
1157
  const threshold = currentMode === "light" ? exitVelocity : enterVelocity;
1156
- const nextMode = Math.abs(scrollVelocity) > threshold ? "light" : "normal";
1158
+ const nextMode = (options == null ? void 0 : options.forceLight) || Math.abs(scrollVelocity) > threshold ? "light" : "normal";
1157
1159
  const previousMode = state.timeoutAdaptiveRender !== void 0 ? "normal" : currentMode;
1158
1160
  if (nextMode !== previousMode) {
1159
1161
  if (nextMode === "light") {
1160
- setAdaptiveRender(ctx, "light");
1162
+ setAdaptiveRender(ctx, "light", "scroll");
1161
1163
  scheduleAdaptiveRenderExit(ctx, exitDelay);
1162
1164
  } else if (currentMode === "light") {
1163
1165
  scheduleAdaptiveRenderExit(ctx, exitDelay);
@@ -1170,16 +1172,43 @@ function updateAdaptiveRender(ctx, scrollVelocity) {
1170
1172
  }
1171
1173
 
1172
1174
  // src/utils/getEffectiveDrawDistance.ts
1173
- var INITIAL_DRAW_DISTANCE = 100;
1174
- function getEffectiveDrawDistance(ctx) {
1175
+ var INITIAL_DRAW_DISTANCE = 50;
1176
+ function getEffectiveDrawDistance(ctx, mode) {
1175
1177
  var _a3;
1176
1178
  const drawDistance = ctx.state.props.drawDistance;
1177
1179
  const initialScroll = ctx.state.initialScroll;
1178
1180
  const needsFullInitialDrawDistance = initialScroll !== void 0 && ((_a3 = initialScroll.viewPosition) != null ? _a3 : 0) > 0;
1179
- return peek$(ctx, "readyToRender") || needsFullInitialDrawDistance ? drawDistance : Math.min(drawDistance, INITIAL_DRAW_DISTANCE);
1181
+ const shouldCapDrawDistance = mode === "visible-first" || mode !== "full" && !peek$(ctx, "readyToRender") && !needsFullInitialDrawDistance;
1182
+ return shouldCapDrawDistance ? Math.min(drawDistance, INITIAL_DRAW_DISTANCE) : drawDistance;
1183
+ }
1184
+ function scheduleFullDrawDistancePrewarm(ctx) {
1185
+ const { state } = ctx;
1186
+ if (state.props.drawDistance <= INITIAL_DRAW_DISTANCE || state.queuedFullDrawDistancePrewarm !== void 0) {
1187
+ return;
1188
+ }
1189
+ state.queuedFullDrawDistancePrewarm = requestAnimationFrame(() => {
1190
+ var _a3;
1191
+ state.queuedFullDrawDistancePrewarm = void 0;
1192
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
1193
+ });
1180
1194
  }
1181
1195
 
1182
1196
  // src/utils/setInitialRenderState.ts
1197
+ function resetInitialRenderState(ctx, {
1198
+ resetLayout,
1199
+ resetInitialScroll
1200
+ }) {
1201
+ const { state } = ctx;
1202
+ if (resetLayout) {
1203
+ state.didContainersLayout = false;
1204
+ state.queuedInitialLayout = false;
1205
+ }
1206
+ if (resetInitialScroll) {
1207
+ state.didFinishInitialScroll = false;
1208
+ }
1209
+ set$(ctx, "readyToRender", false);
1210
+ resetAdaptiveRender(ctx);
1211
+ }
1183
1212
  function setInitialRenderState(ctx, {
1184
1213
  didLayout,
1185
1214
  didInitialScroll
@@ -1198,15 +1227,15 @@ function setInitialRenderState(ctx, {
1198
1227
  const isReadyToRender = Boolean(state.didContainersLayout && state.didFinishInitialScroll);
1199
1228
  if (isReadyToRender && !peek$(ctx, "readyToRender")) {
1200
1229
  set$(ctx, "readyToRender", true);
1201
- setAdaptiveRender(ctx, "normal");
1230
+ setAdaptiveRender(ctx, "normal", "ready");
1202
1231
  if (state.props.drawDistance > INITIAL_DRAW_DISTANCE) {
1203
- requestAnimationFrame(() => {
1204
- var _a3;
1205
- (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
1206
- });
1232
+ scheduleFullDrawDistancePrewarm(ctx);
1207
1233
  }
1208
- if (onLoad) {
1209
- onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1234
+ if (!state.didLoad) {
1235
+ state.didLoad = true;
1236
+ if (onLoad) {
1237
+ onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1238
+ }
1210
1239
  }
1211
1240
  }
1212
1241
  }
@@ -1505,6 +1534,7 @@ function finishScrollTo(ctx) {
1505
1534
  const scrollingTo = state.scrollingTo;
1506
1535
  state.scrollHistory.length = 0;
1507
1536
  state.scrollingTo = void 0;
1537
+ state.scrollTargetPinnedRange = void 0;
1508
1538
  if (state.pendingTotalSize !== void 0) {
1509
1539
  addTotalSize(ctx, null, state.pendingTotalSize);
1510
1540
  }
@@ -1735,8 +1765,8 @@ function doMaintainScrollAtEnd(ctx) {
1735
1765
  state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
1736
1766
  return false;
1737
1767
  }
1738
- state.pendingMaintainScrollAtEnd = false;
1739
1768
  if (shouldMaintainScrollAtEnd) {
1769
+ state.pendingMaintainScrollAtEnd = false;
1740
1770
  const contentSize = getContentSize(ctx);
1741
1771
  if (contentSize < state.scrollLength) {
1742
1772
  state.scroll = 0;
@@ -1767,6 +1797,9 @@ function doMaintainScrollAtEnd(ctx) {
1767
1797
  () => {
1768
1798
  if (state.maintainingScrollAtEnd === activeState) {
1769
1799
  state.maintainingScrollAtEnd = void 0;
1800
+ if (state.pendingMaintainScrollAtEnd) {
1801
+ doMaintainScrollAtEnd(ctx);
1802
+ }
1770
1803
  }
1771
1804
  },
1772
1805
  maintainScrollAtEnd.animated ? 500 : 0
@@ -1775,9 +1808,12 @@ function doMaintainScrollAtEnd(ctx) {
1775
1808
  state.maintainingScrollAtEnd = void 0;
1776
1809
  }
1777
1810
  });
1811
+ } else {
1812
+ state.pendingMaintainScrollAtEnd = true;
1778
1813
  }
1779
1814
  return true;
1780
1815
  }
1816
+ state.pendingMaintainScrollAtEnd = false;
1781
1817
  return false;
1782
1818
  }
1783
1819
 
@@ -2138,6 +2174,8 @@ var flushSync = (fn) => {
2138
2174
  };
2139
2175
 
2140
2176
  // src/utils/getScrollVelocity.ts
2177
+ var MAX_SCROLL_VELOCITY_WINDOW_MS = 1e3;
2178
+ var SCROLL_VELOCITY_HALF_LIFE_MS = 200;
2141
2179
  var getScrollVelocity = (state) => {
2142
2180
  const { scrollHistory } = state;
2143
2181
  const newestIndex = scrollHistory.length - 1;
@@ -2145,35 +2183,37 @@ var getScrollVelocity = (state) => {
2145
2183
  return 0;
2146
2184
  }
2147
2185
  const newest = scrollHistory[newestIndex];
2148
- const now = Date.now();
2149
- let direction = 0;
2150
- for (let i = newestIndex; i > 0; i--) {
2151
- const delta = scrollHistory[i].scroll - scrollHistory[i - 1].scroll;
2152
- if (delta !== 0) {
2153
- direction = Math.sign(delta);
2154
- break;
2155
- }
2156
- }
2157
- if (direction === 0) {
2186
+ if (Date.now() - newest.time > MAX_SCROLL_VELOCITY_WINDOW_MS) {
2158
2187
  return 0;
2159
2188
  }
2160
- let oldest = newest;
2161
- for (let i = newestIndex - 1; i >= 0; i--) {
2189
+ let direction = 0;
2190
+ let weightedVelocity = 0;
2191
+ let totalWeight = 0;
2192
+ for (let i = newestIndex; i > 0; i--) {
2162
2193
  const current = scrollHistory[i];
2163
- const next = scrollHistory[i + 1];
2164
- const delta = next.scroll - current.scroll;
2165
- const deltaSign = Math.sign(delta);
2166
- if (deltaSign !== 0 && deltaSign !== direction) {
2167
- break;
2194
+ const previous = scrollHistory[i - 1];
2195
+ const scrollDiff = current.scroll - previous.scroll;
2196
+ const timeDiff = current.time - previous.time;
2197
+ const deltaSign = Math.sign(scrollDiff);
2198
+ if (deltaSign !== 0) {
2199
+ if (direction === 0) {
2200
+ direction = deltaSign;
2201
+ } else if (deltaSign !== direction) {
2202
+ break;
2203
+ }
2168
2204
  }
2169
- if (now - current.time > 1e3) {
2205
+ if (newest.time - previous.time > MAX_SCROLL_VELOCITY_WINDOW_MS) {
2170
2206
  break;
2171
2207
  }
2172
- oldest = current;
2208
+ if (scrollDiff === 0 || timeDiff <= 0) {
2209
+ continue;
2210
+ }
2211
+ const age = newest.time - current.time;
2212
+ const weight = Math.exp(-age / SCROLL_VELOCITY_HALF_LIFE_MS);
2213
+ weightedVelocity += scrollDiff / timeDiff * weight;
2214
+ totalWeight += weight;
2173
2215
  }
2174
- const scrollDiff = newest.scroll - oldest.scroll;
2175
- const timeDiff = newest.time - oldest.time;
2176
- return timeDiff > 0 ? scrollDiff / timeDiff : 0;
2216
+ return totalWeight > 0 ? weightedVelocity / totalWeight : 0;
2177
2217
  };
2178
2218
 
2179
2219
  // src/core/updateScroll.ts
@@ -2201,8 +2241,6 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2201
2241
  if (scrollHistory.length > 5) {
2202
2242
  scrollHistory.shift();
2203
2243
  }
2204
- const scrollVelocity = getScrollVelocity(state);
2205
- updateAdaptiveRender(ctx, scrollVelocity);
2206
2244
  if (ignoreScrollFromMVCP && !scrollingTo) {
2207
2245
  const { lt, gt } = ignoreScrollFromMVCP;
2208
2246
  if (lt && newScroll < lt || gt && newScroll > gt) {
@@ -2217,6 +2255,9 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2217
2255
  const scrollDelta = Math.abs(newScroll - prevScroll);
2218
2256
  const didResolvePendingNativeMVCPAdjust = resolvePendingNativeMVCPAdjust(ctx, newScroll);
2219
2257
  const scrollLength = state.scrollLength;
2258
+ const isLargeUserScrollJump = scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength && !state.pendingNativeMVCPAdjust;
2259
+ const scrollVelocity = getScrollVelocity(state);
2260
+ updateAdaptiveRender(ctx, scrollVelocity, { forceLight: isLargeUserScrollJump });
2220
2261
  const lastCalculated = state.scrollLastCalculate;
2221
2262
  const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
2222
2263
  const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
@@ -2226,10 +2267,17 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2226
2267
  state.lastScrollDelta = scrollDelta;
2227
2268
  const runCalculateItems = () => {
2228
2269
  var _a4;
2229
- (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { doMVCP: scrollingTo !== void 0, scrollVelocity });
2270
+ const calculateItemsParams = {
2271
+ doMVCP: scrollingTo !== void 0,
2272
+ scrollVelocity
2273
+ };
2274
+ if (isLargeUserScrollJump) {
2275
+ calculateItemsParams.drawDistanceMode = "visible-first";
2276
+ }
2277
+ (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, calculateItemsParams);
2230
2278
  checkThresholds(ctx);
2231
2279
  };
2232
- if (scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength && !state.pendingNativeMVCPAdjust) {
2280
+ if (isLargeUserScrollJump) {
2233
2281
  state.mvcpAnchorLock = void 0;
2234
2282
  state.pendingNativeMVCPAdjust = void 0;
2235
2283
  state.userScrollAnchorReset = { keys: /* @__PURE__ */ new Set() };
@@ -2238,6 +2286,7 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2238
2286
  state.queuedMVCPRecalculate = void 0;
2239
2287
  }
2240
2288
  flushSync(runCalculateItems);
2289
+ scheduleFullDrawDistancePrewarm(ctx);
2241
2290
  } else {
2242
2291
  runCalculateItems();
2243
2292
  }
@@ -2281,8 +2330,93 @@ function syncInitialScrollNativeWatchdog(state, options) {
2281
2330
  initialScrollWatchdog.clear(state);
2282
2331
  }
2283
2332
  }
2284
- function scrollTo(ctx, params) {
2333
+ function findPositionIndexAtOrBeforeOffset(ctx, offset) {
2334
+ const state = ctx.state;
2335
+ const dataLength = state.props.data.length;
2336
+ let low = 0;
2337
+ let high = dataLength - 1;
2338
+ let match;
2339
+ while (low <= high) {
2340
+ const mid = Math.floor((low + high) / 2);
2341
+ const top = state.positions[mid];
2342
+ if (top === void 0) {
2343
+ high = mid - 1;
2344
+ } else {
2345
+ if (top <= offset) {
2346
+ match = mid;
2347
+ low = mid + 1;
2348
+ } else {
2349
+ high = mid - 1;
2350
+ }
2351
+ }
2352
+ }
2353
+ return match;
2354
+ }
2355
+ function getItemBottom(ctx, index) {
2285
2356
  var _a3;
2357
+ const top = ctx.state.positions[index];
2358
+ if (top === void 0) {
2359
+ return void 0;
2360
+ }
2361
+ const itemSize = (_a3 = getItemSizeAtIndex(ctx, index)) != null ? _a3 : 0;
2362
+ return top + (Number.isFinite(itemSize) ? itemSize : 0);
2363
+ }
2364
+ function getTargetViewportRenderRange(ctx, targetOffset, targetIndex) {
2365
+ const state = ctx.state;
2366
+ const dataLength = state.props.data.length;
2367
+ if (dataLength === 0) {
2368
+ return void 0;
2369
+ }
2370
+ const viewportStart = Math.max(0, targetOffset);
2371
+ const viewportEnd = Math.max(viewportStart, targetOffset + state.scrollLength);
2372
+ let start = targetIndex !== void 0 ? Math.max(0, Math.min(dataLength - 1, targetIndex)) : findPositionIndexAtOrBeforeOffset(ctx, viewportStart);
2373
+ if (start === void 0) {
2374
+ return void 0;
2375
+ }
2376
+ if (targetIndex !== void 0 && state.positions[start] === void 0) {
2377
+ return { end: start, start };
2378
+ }
2379
+ if (targetIndex === void 0) {
2380
+ const startBottom = getItemBottom(ctx, start);
2381
+ if (startBottom === void 0 || startBottom <= viewportStart) {
2382
+ return void 0;
2383
+ }
2384
+ }
2385
+ while (start > 0) {
2386
+ const top = state.positions[start];
2387
+ if (top === void 0 || top <= viewportStart || state.positions[start - 1] === void 0) {
2388
+ break;
2389
+ }
2390
+ start--;
2391
+ }
2392
+ while (start > 0) {
2393
+ const previousBottom = getItemBottom(ctx, start - 1);
2394
+ if (previousBottom === void 0 || previousBottom <= viewportStart) {
2395
+ break;
2396
+ }
2397
+ start--;
2398
+ }
2399
+ let end = start;
2400
+ while (end + 1 < dataLength) {
2401
+ const nextTop = state.positions[end + 1];
2402
+ if (nextTop === void 0 || nextTop > viewportEnd) {
2403
+ break;
2404
+ }
2405
+ end++;
2406
+ }
2407
+ return { end, start };
2408
+ }
2409
+ function pinScrollTargetRenderRange(ctx, targetOffset, targetIndex) {
2410
+ const range = getTargetViewportRenderRange(ctx, targetOffset, targetIndex);
2411
+ if (range) {
2412
+ ctx.state.scrollTargetPinnedRange = range;
2413
+ ctx.state.scrollForNextCalculateItemsInView = void 0;
2414
+ } else {
2415
+ ctx.state.scrollTargetPinnedRange = void 0;
2416
+ }
2417
+ }
2418
+ function scrollTo(ctx, params) {
2419
+ var _a3, _b;
2286
2420
  const state = ctx.state;
2287
2421
  const { noScrollingTo, forceScroll, ...scrollTarget } = params;
2288
2422
  const {
@@ -2317,11 +2451,20 @@ function scrollTo(ctx, params) {
2317
2451
  targetOffset,
2318
2452
  waitForInitialScrollCompletionFrame
2319
2453
  };
2454
+ if (!isInitialScroll) {
2455
+ pinScrollTargetRenderRange(ctx, targetOffset, scrollTarget.index);
2456
+ }
2320
2457
  }
2321
2458
  state.scrollPending = targetOffset;
2322
2459
  syncInitialScrollNativeWatchdog(state, { isInitialScroll, requestedOffset: offset, targetOffset });
2323
- if (!animated && !isInitialScroll && !noScrollingTo && Math.abs(state.scroll - targetOffset) > 1) {
2324
- updateScroll(ctx, targetOffset, false, { markHasScrolled: false });
2460
+ if (!isInitialScroll && !noScrollingTo && Math.abs(state.scroll - targetOffset) > 1) {
2461
+ if (animated) {
2462
+ if (state.scrollTargetPinnedRange) {
2463
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state);
2464
+ }
2465
+ } else {
2466
+ updateScroll(ctx, targetOffset, true, { markHasScrolled: false });
2467
+ }
2325
2468
  }
2326
2469
  if (forceScroll || !isInitialScroll || Platform.OS === "android") {
2327
2470
  doScrollTo(ctx, { animated, horizontal, isInitialScroll, offset });
@@ -2392,16 +2535,17 @@ function dispatchInitialScroll(ctx, params) {
2392
2535
  waitForInitialScrollCompletionFrame: waitForCompletionFrame
2393
2536
  });
2394
2537
  }
2395
- function setInitialScrollTarget(state, target, options) {
2538
+ function setInitialScrollTarget(ctx, target, options) {
2396
2539
  var _a3;
2540
+ const { state } = ctx;
2397
2541
  state.clearPreservedInitialScrollOnNextFinish = void 0;
2398
2542
  if (state.timeoutPreservedInitialScrollClear !== void 0) {
2399
2543
  clearTimeout(state.timeoutPreservedInitialScrollClear);
2400
2544
  state.timeoutPreservedInitialScrollClear = void 0;
2401
2545
  }
2402
2546
  state.initialScroll = target;
2403
- if ((options == null ? void 0 : options.resetDidFinish) && state.didFinishInitialScroll) {
2404
- state.didFinishInitialScroll = false;
2547
+ if (options == null ? void 0 : options.resetDidFinish) {
2548
+ resetInitialRenderState(ctx, { resetInitialScroll: true });
2405
2549
  }
2406
2550
  setInitialScrollSession(state, {
2407
2551
  kind: ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" ? "offset" : "bootstrap"
@@ -2455,7 +2599,7 @@ function advanceMeasuredInitialScroll(ctx, options) {
2455
2599
  return false;
2456
2600
  }
2457
2601
  if (didOffsetChange && ((_b = state.initialScrollSession) == null ? void 0 : _b.kind) !== "offset") {
2458
- setInitialScrollTarget(state, { ...initialScroll, contentOffset: resolvedOffset });
2602
+ setInitialScrollTarget(ctx, { ...initialScroll, contentOffset: resolvedOffset });
2459
2603
  }
2460
2604
  const forceScroll = (_c = options == null ? void 0 : options.forceScroll) != null ? _c : !!queuedInitialLayout || isInitialScrollInProgress && didOffsetChange;
2461
2605
  dispatchInitialScroll(ctx, {
@@ -2728,7 +2872,6 @@ function areEquivalentBootstrapInitialScrollTargets(current, next) {
2728
2872
  }
2729
2873
  function clearPendingInitialScrollFooterLayout(ctx, options) {
2730
2874
  const { dataLength, stylePaddingBottom, target } = options;
2731
- const state = ctx.state;
2732
2875
  if (!shouldPreserveInitialScrollForFooterLayout(target)) {
2733
2876
  return;
2734
2877
  }
@@ -2738,7 +2881,7 @@ function clearPendingInitialScrollFooterLayout(ctx, options) {
2738
2881
  preserveForFooterLayout: void 0,
2739
2882
  stylePaddingBottom
2740
2883
  });
2741
- setInitialScrollTarget(state, clearedFooterTarget);
2884
+ setInitialScrollTarget(ctx, clearedFooterTarget);
2742
2885
  }
2743
2886
  function clearFinishedViewportRetargetableInitialScroll(state) {
2744
2887
  clearPreservedInitialScrollTarget(state);
@@ -2885,7 +3028,7 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
2885
3028
  return;
2886
3029
  }
2887
3030
  if (!areEquivalentBootstrapInitialScrollTargets(initialScroll, updatedInitialScroll) || !!bootstrapInitialScroll || shouldResetDidFinish || didDataChange) {
2888
- setInitialScrollTarget(state, updatedInitialScroll, {
3031
+ setInitialScrollTarget(ctx, updatedInitialScroll, {
2889
3032
  resetDidFinish: shouldResetDidFinish
2890
3033
  });
2891
3034
  rearmBootstrapInitialScroll(ctx, {
@@ -2900,7 +3043,7 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
2900
3043
  return;
2901
3044
  }
2902
3045
  if (bootstrapInitialScroll || shouldResetDidFinish) {
2903
- setInitialScrollTarget(state, initialScroll, {
3046
+ setInitialScrollTarget(ctx, initialScroll, {
2904
3047
  resetDidFinish: shouldResetDidFinish
2905
3048
  });
2906
3049
  rearmBootstrapInitialScroll(ctx, {
@@ -2946,7 +3089,7 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2946
3089
  });
2947
3090
  } else {
2948
3091
  const didFinishInitialScroll = !!state.didFinishInitialScroll;
2949
- setInitialScrollTarget(state, updatedInitialScroll, {
3092
+ setInitialScrollTarget(ctx, updatedInitialScroll, {
2950
3093
  resetDidFinish: didFinishInitialScroll
2951
3094
  });
2952
3095
  rearmBootstrapInitialScroll(ctx, {
@@ -3135,7 +3278,7 @@ function initializeInitialScrollOnMount(ctx, options) {
3135
3278
  const resolvedInitialContentOffset = initialContentOffset != null ? initialContentOffset : 0;
3136
3279
  const preserveForFooterLayout = useBootstrapInitialScroll && initialScrollAtEnd && hasFooterComponent;
3137
3280
  if (initialScroll && (initialScroll.contentOffset === void 0 || !!initialScroll.preserveForFooterLayout !== preserveForFooterLayout && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) !== "offset")) {
3138
- setInitialScrollTarget(state, {
3281
+ setInitialScrollTarget(ctx, {
3139
3282
  ...initialScroll,
3140
3283
  contentOffset: resolvedInitialContentOffset,
3141
3284
  preserveForFooterLayout
@@ -3164,6 +3307,7 @@ function handleInitialScrollDataChange(ctx, options) {
3164
3307
  const {
3165
3308
  dataLength,
3166
3309
  didDataChange,
3310
+ didStartFreshData,
3167
3311
  initialScrollAtEnd,
3168
3312
  latestInitialScroll,
3169
3313
  latestInitialScrollSessionKind,
@@ -3171,17 +3315,17 @@ function handleInitialScrollDataChange(ctx, options) {
3171
3315
  useBootstrapInitialScroll
3172
3316
  } = options;
3173
3317
  const state = ctx.state;
3174
- const previousDataLength = (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.previousDataLength) != null ? _b : 0;
3175
- const isFirstNonEmptyData = !state.hasHadNonEmptyData && dataLength > 0;
3318
+ const previousInitialScrollDataLength = (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.previousDataLength) != null ? _b : 0;
3319
+ const shouldUseLatestInitialScroll = dataLength > 0 && (!state.hasHadNonEmptyData || didStartFreshData);
3176
3320
  if (dataLength > 0) {
3177
3321
  state.hasHadNonEmptyData = true;
3178
3322
  }
3179
- if (isFirstNonEmptyData) {
3323
+ if (shouldUseLatestInitialScroll) {
3180
3324
  if (latestInitialScroll) {
3181
- setInitialScrollTarget(state, latestInitialScroll);
3325
+ setInitialScrollTarget(ctx, latestInitialScroll);
3182
3326
  setInitialScrollSession(state, {
3183
3327
  kind: latestInitialScrollSessionKind,
3184
- previousDataLength
3328
+ previousDataLength: previousInitialScrollDataLength
3185
3329
  });
3186
3330
  } else {
3187
3331
  clearPreservedInitialScrollTarget(state);
@@ -3196,17 +3340,17 @@ function handleInitialScrollDataChange(ctx, options) {
3196
3340
  dataLength,
3197
3341
  didDataChange,
3198
3342
  initialScrollAtEnd,
3199
- previousDataLength,
3343
+ previousDataLength: previousInitialScrollDataLength,
3200
3344
  stylePaddingBottom
3201
3345
  });
3202
3346
  return;
3203
3347
  }
3204
- const shouldReplayFinishedOffsetInitialScroll = previousDataLength === 0 && dataLength > 0 && !!state.initialScroll && ((_c = ctx.state.initialScrollSession) == null ? void 0 : _c.kind) === "offset" && !!state.didFinishInitialScroll;
3205
- if (previousDataLength !== 0 || dataLength === 0 || !state.initialScroll || !state.queuedInitialLayout || state.didFinishInitialScroll && !shouldReplayFinishedOffsetInitialScroll) {
3348
+ const shouldReplayFinishedOffsetInitialScroll = previousInitialScrollDataLength === 0 && dataLength > 0 && !!state.initialScroll && ((_c = ctx.state.initialScrollSession) == null ? void 0 : _c.kind) === "offset" && !!state.didFinishInitialScroll;
3349
+ if (previousInitialScrollDataLength !== 0 || dataLength === 0 || !state.initialScroll || !state.queuedInitialLayout || state.didFinishInitialScroll && !shouldReplayFinishedOffsetInitialScroll) {
3206
3350
  return;
3207
3351
  }
3208
3352
  if (shouldReplayFinishedOffsetInitialScroll) {
3209
- state.didFinishInitialScroll = false;
3353
+ resetInitialRenderState(ctx, { resetInitialScroll: true });
3210
3354
  }
3211
3355
  advanceCurrentInitialScrollSession(ctx);
3212
3356
  }
@@ -3992,6 +4136,32 @@ function setDidLayout(ctx) {
3992
4136
  }
3993
4137
 
3994
4138
  // src/core/calculateItemsInView.ts
4139
+ var RENDER_RANGE_PROJECTION_FULL_VELOCITY = 4;
4140
+ var RENDER_RANGE_PROJECTION_SETTLE_DELAY = 100;
4141
+ function getProjectedBufferAdjustment(scrollVelocity, trailingBuffer) {
4142
+ if (trailingBuffer <= 0) {
4143
+ return 0;
4144
+ }
4145
+ const velocityProgress = Math.min(1, Math.abs(scrollVelocity) / RENDER_RANGE_PROJECTION_FULL_VELOCITY);
4146
+ return Math.sign(scrollVelocity) * trailingBuffer * velocityProgress;
4147
+ }
4148
+ function scheduleRenderRangeProjectionSettle(ctx) {
4149
+ const state = ctx.state;
4150
+ const previousTimeout = state.timeoutRenderRangeProjectionSettle;
4151
+ if (previousTimeout !== void 0) {
4152
+ clearTimeout(previousTimeout);
4153
+ state.timeouts.delete(previousTimeout);
4154
+ }
4155
+ const timeout = setTimeout(() => {
4156
+ var _a3;
4157
+ state.timeoutRenderRangeProjectionSettle = void 0;
4158
+ state.timeouts.delete(timeout);
4159
+ state.scrollHistory.length = 0;
4160
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
4161
+ }, RENDER_RANGE_PROJECTION_SETTLE_DELAY);
4162
+ state.timeoutRenderRangeProjectionSettle = timeout;
4163
+ state.timeouts.add(timeout);
4164
+ }
3995
4165
  function findCurrentStickyIndex(stickyArray, scroll, state) {
3996
4166
  const positions = state.positions;
3997
4167
  for (let i = stickyArray.length - 1; i >= 0; i--) {
@@ -4032,14 +4202,14 @@ function handleStickyActivation(ctx, stickyArray, currentStickyIdx, needNewConta
4032
4202
  }
4033
4203
  }
4034
4204
  }
4035
- function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentStickyIdx, pendingRemoval, alwaysRenderIndicesSet) {
4205
+ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentStickyIdx, pendingRemoval, isPinnedRenderIndex) {
4036
4206
  var _a3, _b;
4037
4207
  const state = ctx.state;
4038
4208
  for (const containerIndex of state.stickyContainerPool) {
4039
4209
  const itemKey = peek$(ctx, `containerItemKey${containerIndex}`);
4040
4210
  const itemIndex = itemKey ? state.indexByKey.get(itemKey) : void 0;
4041
4211
  if (itemIndex === void 0) continue;
4042
- if (alwaysRenderIndicesSet.has(itemIndex)) continue;
4212
+ if (isPinnedRenderIndex(itemIndex)) continue;
4043
4213
  const arrayIdx = stickyArray.indexOf(itemIndex);
4044
4214
  if (arrayIdx === -1) {
4045
4215
  state.stickyContainerPool.delete(containerIndex);
@@ -4203,9 +4373,7 @@ function calculateItemsInView(ctx, params = {}) {
4203
4373
  const { data } = state.props;
4204
4374
  const stickyHeaderIndicesArr = state.props.stickyHeaderIndicesArr || [];
4205
4375
  const stickyHeaderIndicesSet = state.props.stickyHeaderIndicesSet || /* @__PURE__ */ new Set();
4206
- const alwaysRenderArr = alwaysRenderIndicesArr || [];
4207
- const alwaysRenderSet = alwaysRenderIndicesSet || /* @__PURE__ */ new Set();
4208
- const drawDistance = getEffectiveDrawDistance(ctx);
4376
+ const drawDistance = getEffectiveDrawDistance(ctx, params.drawDistanceMode);
4209
4377
  const { dataChanged, doMVCP, forceFullItemPositions } = params;
4210
4378
  const bootstrapInitialScrollState = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" ? state.initialScrollSession.bootstrap : void 0;
4211
4379
  const suppressInitialScrollSideEffects = !!bootstrapInitialScrollState;
@@ -4271,14 +4439,19 @@ function calculateItemsInView(ctx, params = {}) {
4271
4439
  scrollBufferTop = drawDistance * 1.5;
4272
4440
  scrollBufferBottom = drawDistance * 0.5;
4273
4441
  }
4442
+ const shouldProjectRenderRange = !dataChanged && !forceFullItemPositions && !suppressInitialScrollSideEffects && !hasActiveInitialScroll(state) && !state.scrollingTo && !state.pendingNativeMVCPAdjust && !!peek$(ctx, "readyToRender");
4443
+ const projectedBufferAdjustment = shouldProjectRenderRange ? getProjectedBufferAdjustment(speed, Math.min(scrollBufferTop, scrollBufferBottom)) : 0;
4274
4444
  const updateScrollRange = () => {
4275
4445
  const scrollStart = Math.max(0, scroll);
4276
4446
  const overscrollBeforeContent = Math.max(0, -nativeScrollState);
4277
- scrollTopBuffered = scrollStart - scrollBufferTop;
4278
4447
  scrollBottom = Math.max(scrollStart, scroll + scrollLength + overscrollBeforeContent);
4279
- scrollBottomBuffered = scrollBottom + scrollBufferBottom;
4448
+ scrollTopBuffered = scrollStart - scrollBufferTop + projectedBufferAdjustment;
4449
+ scrollBottomBuffered = scrollBottom + scrollBufferBottom + projectedBufferAdjustment;
4280
4450
  };
4281
4451
  updateScrollRange();
4452
+ if (projectedBufferAdjustment !== 0) {
4453
+ scheduleRenderRangeProjectionSettle(ctx);
4454
+ }
4282
4455
  if (enableScrollForNextCalculateItemsInView && !suppressInitialScrollSideEffects && !dataChanged && !forceFullItemPositions && scrollForNextCalculateItemsInView) {
4283
4456
  const { top, bottom } = scrollForNextCalculateItemsInView;
4284
4457
  if (top === null && bottom === null) {
@@ -4436,9 +4609,34 @@ function calculateItemsInView(ctx, params = {}) {
4436
4609
  }
4437
4610
  }
4438
4611
  }
4612
+ const scrollTargetPinnedRange = state.scrollTargetPinnedRange;
4613
+ let scrollTargetPinnedStart = 0;
4614
+ let scrollTargetPinnedEnd = -1;
4615
+ if (scrollTargetPinnedRange) {
4616
+ scrollTargetPinnedStart = Math.max(0, Math.min(scrollTargetPinnedRange.start, scrollTargetPinnedRange.end));
4617
+ scrollTargetPinnedEnd = Math.min(
4618
+ dataLength - 1,
4619
+ Math.max(scrollTargetPinnedRange.start, scrollTargetPinnedRange.end)
4620
+ );
4621
+ }
4622
+ const hasScrollTargetPinnedRange = scrollTargetPinnedStart <= scrollTargetPinnedEnd;
4623
+ const isPinnedRenderIndex = (index) => alwaysRenderIndicesSet.has(index) || hasScrollTargetPinnedRange && index >= scrollTargetPinnedStart && index <= scrollTargetPinnedEnd;
4439
4624
  if (startBuffered !== null && endBuffered !== null) {
4440
4625
  const needNewContainers = [];
4441
4626
  const needNewContainersSet = /* @__PURE__ */ new Set();
4627
+ const addPinnedIndex = (index) => {
4628
+ var _a4;
4629
+ if (index >= 0 && index < dataLength) {
4630
+ const id = (_a4 = idCache[index]) != null ? _a4 : getId(state, index);
4631
+ const containerIndex = containerItemKeys.get(id);
4632
+ if (containerIndex !== void 0) {
4633
+ state.stickyContainerPool.add(containerIndex);
4634
+ } else if (!isNullOrUndefined(id) && !needNewContainersSet.has(index)) {
4635
+ needNewContainersSet.add(index);
4636
+ needNewContainers.push(index);
4637
+ }
4638
+ }
4639
+ };
4442
4640
  for (let i = startBuffered; i <= endBuffered; i++) {
4443
4641
  const id = (_m = idCache[i]) != null ? _m : getId(state, i);
4444
4642
  if (!containerItemKeys.has(id)) {
@@ -4446,21 +4644,19 @@ function calculateItemsInView(ctx, params = {}) {
4446
4644
  needNewContainers.push(i);
4447
4645
  }
4448
4646
  }
4449
- if (alwaysRenderArr.length > 0) {
4450
- for (const index of alwaysRenderArr) {
4451
- if (index < 0 || index >= dataLength) continue;
4452
- const id = (_n = idCache[index]) != null ? _n : getId(state, index);
4453
- if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
4454
- needNewContainersSet.add(index);
4455
- needNewContainers.push(index);
4456
- }
4647
+ for (const index of alwaysRenderIndicesArr) {
4648
+ addPinnedIndex(index);
4649
+ }
4650
+ if (hasScrollTargetPinnedRange) {
4651
+ for (let index = scrollTargetPinnedStart; index <= scrollTargetPinnedEnd; index++) {
4652
+ addPinnedIndex(index);
4457
4653
  }
4458
4654
  }
4459
4655
  if (stickyHeaderIndicesArr.length > 0) {
4460
4656
  handleStickyActivation(
4461
4657
  ctx,
4462
4658
  stickyHeaderIndicesArr,
4463
- (_o = stickyState == null ? void 0 : stickyState.currentStickyIdx) != null ? _o : -1,
4659
+ (_n = stickyState == null ? void 0 : stickyState.currentStickyIdx) != null ? _n : -1,
4464
4660
  needNewContainers,
4465
4661
  needNewContainersSet,
4466
4662
  startBuffered,
@@ -4486,7 +4682,7 @@ function calculateItemsInView(ctx, params = {}) {
4486
4682
  for (const allocation of availableContainerAllocations) {
4487
4683
  const i = allocation.itemIndex;
4488
4684
  const containerIndex = allocation.containerIndex;
4489
- const id = (_p = idCache[i]) != null ? _p : getId(state, i);
4685
+ const id = (_o = idCache[i]) != null ? _o : getId(state, i);
4490
4686
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
4491
4687
  if (oldKey && oldKey !== id) {
4492
4688
  containerItemKeys.delete(oldKey);
@@ -4497,10 +4693,14 @@ function calculateItemsInView(ctx, params = {}) {
4497
4693
  state.containerItemTypes.set(containerIndex, allocation.itemType);
4498
4694
  }
4499
4695
  containerItemKeys.set(id, containerIndex);
4500
- (_q = state.userScrollAnchorReset) == null ? void 0 : _q.keys.add(id);
4696
+ (_p = state.userScrollAnchorReset) == null ? void 0 : _p.keys.add(id);
4697
+ if (IsNewArchitecture) {
4698
+ (_q = state.pendingLayoutEffectMeasurements) != null ? _q : state.pendingLayoutEffectMeasurements = /* @__PURE__ */ new Set();
4699
+ state.pendingLayoutEffectMeasurements.add(id);
4700
+ }
4501
4701
  const containerSticky = `containerSticky${containerIndex}`;
4502
4702
  const isSticky = stickyHeaderIndicesSet.has(i);
4503
- const isAlwaysRender = alwaysRenderSet.has(i);
4703
+ const isPinnedRender = isPinnedRenderIndex(i);
4504
4704
  if (isSticky) {
4505
4705
  set$(ctx, containerSticky, true);
4506
4706
  state.stickyContainerPool.add(containerIndex);
@@ -4508,9 +4708,9 @@ function calculateItemsInView(ctx, params = {}) {
4508
4708
  if (peek$(ctx, containerSticky)) {
4509
4709
  set$(ctx, containerSticky, false);
4510
4710
  }
4511
- if (isAlwaysRender) {
4711
+ if (isPinnedRender) {
4512
4712
  state.stickyContainerPool.add(containerIndex);
4513
- } else if (state.stickyContainerPool.has(containerIndex)) {
4713
+ } else {
4514
4714
  state.stickyContainerPool.delete(containerIndex);
4515
4715
  }
4516
4716
  }
@@ -4525,20 +4725,8 @@ function calculateItemsInView(ctx, params = {}) {
4525
4725
  }
4526
4726
  }
4527
4727
  }
4528
- if (state.userScrollAnchorReset) {
4529
- if (state.userScrollAnchorReset.keys.size === 0) {
4530
- state.userScrollAnchorReset = void 0;
4531
- }
4532
- }
4533
- if (alwaysRenderArr.length > 0) {
4534
- for (const index of alwaysRenderArr) {
4535
- if (index < 0 || index >= dataLength) continue;
4536
- const id = (_r = idCache[index]) != null ? _r : getId(state, index);
4537
- const containerIndex = containerItemKeys.get(id);
4538
- if (containerIndex !== void 0) {
4539
- state.stickyContainerPool.add(containerIndex);
4540
- }
4541
- }
4728
+ if (((_r = state.userScrollAnchorReset) == null ? void 0 : _r.keys.size) === 0) {
4729
+ state.userScrollAnchorReset = void 0;
4542
4730
  }
4543
4731
  }
4544
4732
  if (state.stickyContainerPool.size > 0) {
@@ -4549,7 +4737,7 @@ function calculateItemsInView(ctx, params = {}) {
4549
4737
  drawDistance,
4550
4738
  (_s = stickyState == null ? void 0 : stickyState.currentStickyIdx) != null ? _s : -1,
4551
4739
  pendingRemoval,
4552
- alwaysRenderSet
4740
+ isPinnedRenderIndex
4553
4741
  );
4554
4742
  }
4555
4743
  const pendingRemovalSet = pendingRemoval.length > 0 ? new Set(pendingRemoval) : void 0;
@@ -4721,6 +4909,36 @@ function mergeItemSizeUpdateResult(result, next) {
4721
4909
  result.needsRecalculate || (result.needsRecalculate = next.needsRecalculate);
4722
4910
  result.shouldMaintainScrollAtEnd || (result.shouldMaintainScrollAtEnd = next.shouldMaintainScrollAtEnd);
4723
4911
  }
4912
+ var batchedItemSizeRecalculates = /* @__PURE__ */ new WeakMap();
4913
+ function flushBatchedItemSizeRecalculate(ctx, didFallback = false, expectedResult) {
4914
+ const result = batchedItemSizeRecalculates.get(ctx);
4915
+ if (!result || expectedResult && result !== expectedResult) {
4916
+ return;
4917
+ }
4918
+ batchedItemSizeRecalculates.delete(ctx);
4919
+ if (didFallback) {
4920
+ ctx.state.pendingLayoutEffectMeasurements = void 0;
4921
+ }
4922
+ flushItemSizeUpdates(ctx, result);
4923
+ }
4924
+ function queueItemSizeRecalculate(ctx, result) {
4925
+ var _a3, _b;
4926
+ const batch = batchedItemSizeRecalculates.get(ctx);
4927
+ if (batch) {
4928
+ mergeItemSizeUpdateResult(batch, result);
4929
+ } else {
4930
+ const nextBatch = { ...result };
4931
+ batchedItemSizeRecalculates.set(ctx, nextBatch);
4932
+ if ((_a3 = ctx.state.pendingLayoutEffectMeasurements) == null ? void 0 : _a3.size) {
4933
+ requestAnimationFrame(() => {
4934
+ flushBatchedItemSizeRecalculate(ctx, true, nextBatch);
4935
+ });
4936
+ }
4937
+ }
4938
+ if (!((_b = ctx.state.pendingLayoutEffectMeasurements) == null ? void 0 : _b.size)) {
4939
+ flushBatchedItemSizeRecalculate(ctx);
4940
+ }
4941
+ }
4724
4942
  function flushItemSizeUpdates(ctx, result) {
4725
4943
  var _a3;
4726
4944
  const state = ctx.state;
@@ -4737,12 +4955,22 @@ function flushItemSizeUpdates(ctx, result) {
4737
4955
  function updateItemSizes(ctx, measurement) {
4738
4956
  var _a3, _b, _c, _d;
4739
4957
  const state = ctx.state;
4958
+ let didDrainLayoutEffectMeasurements = false;
4959
+ if (IsNewArchitecture && measurement.fromLayoutEffect) {
4960
+ const pendingLayoutEffectMeasurements = state.pendingLayoutEffectMeasurements;
4961
+ if ((pendingLayoutEffectMeasurements == null ? void 0 : pendingLayoutEffectMeasurements.delete(measurement.itemKey)) && pendingLayoutEffectMeasurements.size === 0) {
4962
+ state.pendingLayoutEffectMeasurements = void 0;
4963
+ didDrainLayoutEffectMeasurements = true;
4964
+ }
4965
+ }
4740
4966
  const pendingKeys = (_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys;
4741
4967
  const shouldBatchPendingMeasurements = IsNewArchitecture && measurement.fromLayoutEffect && !!(pendingKeys == null ? void 0 : pendingKeys.size);
4968
+ const shouldQueueRecalculate = IsNewArchitecture && !!measurement.fromLayoutEffect;
4969
+ let result;
4742
4970
  if (!shouldBatchPendingMeasurements) {
4743
- flushItemSizeUpdates(ctx, applyItemSize(ctx, measurement.itemKey, measurement.size));
4971
+ result = applyItemSize(ctx, measurement.itemKey, measurement.size);
4744
4972
  } else {
4745
- const result = {};
4973
+ result = {};
4746
4974
  const updateContainerItemSize = (itemKey, containerId, size) => {
4747
4975
  if (containerId !== void 0 && peek$(ctx, `containerItemKey${containerId}`) === itemKey) {
4748
4976
  mergeItemSizeUpdateResult(result, applyItemSize(ctx, itemKey, size));
@@ -4760,8 +4988,15 @@ function updateItemSizes(ctx, measurement) {
4760
4988
  });
4761
4989
  }
4762
4990
  }
4991
+ }
4992
+ if (shouldQueueRecalculate && result.needsRecalculate) {
4993
+ queueItemSizeRecalculate(ctx, result);
4994
+ } else {
4763
4995
  flushItemSizeUpdates(ctx, result);
4764
4996
  }
4997
+ if (didDrainLayoutEffectMeasurements) {
4998
+ flushBatchedItemSizeRecalculate(ctx);
4999
+ }
4765
5000
  }
4766
5001
  function applyItemSize(ctx, itemKey, sizeObj) {
4767
5002
  var _a3;
@@ -6442,7 +6677,7 @@ var LegendList = typedMemo(
6442
6677
  })
6443
6678
  );
6444
6679
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
6445
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
6680
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
6446
6681
  const noopOnScroll = React2.useCallback((_event) => {
6447
6682
  }, []);
6448
6683
  if (props.recycleItems === void 0) {
@@ -6459,6 +6694,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6459
6694
  contentContainerStyle: contentContainerStyleProp,
6460
6695
  contentInset,
6461
6696
  data: dataProp = [],
6697
+ dataKey,
6462
6698
  dataVersion,
6463
6699
  drawDistance = 250,
6464
6700
  contentInsetEndAdjustment,
@@ -6589,6 +6825,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6589
6825
  (_j = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _j.join(","),
6590
6826
  (_k = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _k.join(","),
6591
6827
  dataProp,
6828
+ dataKey,
6592
6829
  dataVersion,
6593
6830
  keyExtractor
6594
6831
  ]);
@@ -6611,6 +6848,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6611
6848
  dataChangeNeedsScrollUpdate: false,
6612
6849
  didColumnsChange: false,
6613
6850
  didDataChange: false,
6851
+ didLoad: false,
6614
6852
  enableScrollForNextCalculateItemsInView: true,
6615
6853
  endBuffered: -1,
6616
6854
  endNoBuffer: -1,
@@ -6640,6 +6878,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6640
6878
  positions: [],
6641
6879
  props: {},
6642
6880
  queuedCalculateItemsInView: 0,
6881
+ queuedFullDrawDistancePrewarm: void 0,
6643
6882
  refScroller: { current: null },
6644
6883
  scroll: 0,
6645
6884
  scrollAdjustHandler: new ScrollAdjustHandler(ctx),
@@ -6682,10 +6921,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6682
6921
  const didScrollAxisGapChange = !isFirstLocal && ctx.scrollAxisGap !== nextScrollAxisGap;
6683
6922
  ctx.scrollAxisGap = nextScrollAxisGap;
6684
6923
  state.didColumnsChange = numColumnsProp !== previousNumColumnsProp || didScrollAxisGapChange;
6924
+ const previousDataLength = (_n = (_m = state.props.data) == null ? void 0 : _m.length) != null ? _n : 0;
6685
6925
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
6926
+ const didDataKeyChangeLocal = state.props.dataKey !== dataKey;
6686
6927
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
6687
- const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
6688
- if (didDataChangeLocal && !initialScrollAtEnd && state.didFinishInitialScroll && ((_m = state.initialScroll) == null ? void 0 : _m.viewPosition) === 1 && state.props.data.length > 0) {
6928
+ const didDataChangeLocal = didDataKeyChangeLocal || didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
6929
+ const shouldResetFreshDataLayout = !isFirstLocal && didDataChangeLocal && state.hasHadNonEmptyData && (didDataKeyChangeLocal || previousDataLength === 0) && dataProp.length > 0;
6930
+ if (didDataChangeLocal && !initialScrollAtEnd && state.didFinishInitialScroll && ((_o = state.initialScroll) == null ? void 0 : _o.viewPosition) === 1 && state.props.data.length > 0) {
6689
6931
  clearPreservedInitialScrollTarget(state);
6690
6932
  }
6691
6933
  if (didDataChangeLocal) {
@@ -6697,7 +6939,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6697
6939
  const throttledOnScroll = useThrottledOnScroll(onScrollProp != null ? onScrollProp : noopOnScroll, scrollEventThrottle != null ? scrollEventThrottle : 0);
6698
6940
  const throttleScrollFn = scrollEventThrottle && onScrollProp ? throttledOnScroll : onScrollProp;
6699
6941
  const anchoredEndSpaceResolved = Platform.OS === "web" && anchoredEndSpace ? { ...anchoredEndSpace, includeInEndInset: true } : anchoredEndSpace;
6700
- const didAnchoredEndSpaceAnchorIndexChange = !isFirstLocal && !didDataChangeLocal && ((_n = state.props.anchoredEndSpace) == null ? void 0 : _n.anchorIndex) !== (anchoredEndSpaceResolved == null ? void 0 : anchoredEndSpaceResolved.anchorIndex);
6942
+ const didAnchoredEndSpaceAnchorIndexChange = !isFirstLocal && !didDataChangeLocal && ((_p = state.props.anchoredEndSpace) == null ? void 0 : _p.anchorIndex) !== (anchoredEndSpaceResolved == null ? void 0 : anchoredEndSpaceResolved.anchorIndex);
6701
6943
  state.props = {
6702
6944
  adaptiveRender: experimental_adaptiveRender,
6703
6945
  alignItemsAtEnd,
@@ -6711,6 +6953,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6711
6953
  contentInset,
6712
6954
  contentInsetEndAdjustment: contentInsetEndAdjustmentResolved,
6713
6955
  data: dataProp,
6956
+ dataKey,
6714
6957
  dataVersion,
6715
6958
  drawDistance,
6716
6959
  estimatedItemSize,
@@ -6751,13 +6994,19 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6751
6994
  if (!isFirstLocal && previousAdaptiveRender && !experimental_adaptiveRender) {
6752
6995
  resetAdaptiveRender(ctx);
6753
6996
  }
6997
+ if (shouldResetFreshDataLayout) {
6998
+ resetInitialRenderState(ctx, {
6999
+ resetInitialScroll: !!initialScrollProp,
7000
+ resetLayout: true
7001
+ });
7002
+ }
6754
7003
  const memoizedLastItemKeys = React2.useMemo(() => {
6755
7004
  if (!dataProp.length) return [];
6756
7005
  return Array.from(
6757
7006
  { length: Math.min(numColumnsProp, dataProp.length) },
6758
7007
  (_, i) => getId(state, dataProp.length - 1 - i)
6759
7008
  );
6760
- }, [dataProp, dataVersion, numColumnsProp]);
7009
+ }, [dataProp, dataKey, dataVersion, numColumnsProp]);
6761
7010
  const initializeStateVars = (shouldAdjustPadding) => {
6762
7011
  set$(ctx, "lastItemKeys", memoizedLastItemKeys);
6763
7012
  set$(ctx, "numColumns", numColumnsProp);
@@ -6817,13 +7066,22 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6817
7066
  handleInitialScrollDataChange(ctx, {
6818
7067
  dataLength: dataProp.length,
6819
7068
  didDataChange: didDataChangeLocal,
7069
+ didStartFreshData: shouldResetFreshDataLayout,
6820
7070
  initialScrollAtEnd,
6821
7071
  latestInitialScroll: initialScrollProp,
6822
7072
  latestInitialScrollSessionKind: initialScrollUsesOffsetOnly ? "offset" : "bootstrap",
6823
7073
  stylePaddingBottom: stylePaddingBottomState,
6824
7074
  useBootstrapInitialScroll: usesBootstrapInitialScroll
6825
7075
  });
6826
- }, [dataProp.length, didDataChangeLocal, initialScrollAtEnd, stylePaddingBottomState, usesBootstrapInitialScroll]);
7076
+ }, [
7077
+ dataProp.length,
7078
+ dataKey,
7079
+ didDataChangeLocal,
7080
+ shouldResetFreshDataLayout,
7081
+ initialScrollAtEnd,
7082
+ stylePaddingBottomState,
7083
+ usesBootstrapInitialScroll
7084
+ ]);
6827
7085
  React2.useLayoutEffect(() => {
6828
7086
  var _a4;
6829
7087
  if (didAnchoredEndSpaceAnchorIndexChange) {
@@ -6891,6 +7149,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6891
7149
  React2.useLayoutEffect(
6892
7150
  () => initializeStateVars(true),
6893
7151
  [
7152
+ dataKey,
6894
7153
  dataVersion,
6895
7154
  memoizedLastItemKeys.join(","),
6896
7155
  numColumnsProp,
@@ -6917,7 +7176,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6917
7176
  state.didColumnsChange = false;
6918
7177
  state.didDataChange = false;
6919
7178
  state.isFirst = false;
6920
- }, [dataProp, dataVersion, numColumnsProp, nextScrollAxisGap]);
7179
+ }, [dataProp, dataKey, dataVersion, numColumnsProp, nextScrollAxisGap]);
6921
7180
  React2.useLayoutEffect(() => {
6922
7181
  var _a4;
6923
7182
  set$(ctx, "extraData", extraData);
@@ -6970,6 +7229,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6970
7229
  React2.useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx, scheduleImperativeScrollCommit), []);
6971
7230
  React2.useEffect(() => {
6972
7231
  return () => {
7232
+ if (state.queuedFullDrawDistancePrewarm !== void 0) {
7233
+ cancelAnimationFrame(state.queuedFullDrawDistancePrewarm);
7234
+ state.queuedFullDrawDistancePrewarm = void 0;
7235
+ }
6973
7236
  for (const timeout of state.timeouts) {
6974
7237
  clearTimeout(timeout);
6975
7238
  }
@@ -7022,7 +7285,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7022
7285
  onScroll: onScrollHandler,
7023
7286
  recycleItems,
7024
7287
  refreshControl: refreshControlElement ? stylePaddingTopState > 0 ? React2__namespace.cloneElement(refreshControlElement, {
7025
- progressViewOffset: ((_o = refreshControlElement.props.progressViewOffset) != null ? _o : 0) + stylePaddingTopState
7288
+ progressViewOffset: ((_q = refreshControlElement.props.progressViewOffset) != null ? _q : 0) + stylePaddingTopState
7026
7289
  }) : refreshControlElement : onRefresh && /* @__PURE__ */ React2__namespace.createElement(
7027
7290
  ReactNative.RefreshControl,
7028
7291
  {
@@ -7033,7 +7296,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7033
7296
  ),
7034
7297
  refScrollView: combinedRef,
7035
7298
  renderScrollComponent,
7036
- scrollAdjustHandler: (_p = refState.current) == null ? void 0 : _p.scrollAdjustHandler,
7299
+ scrollAdjustHandler: (_r = refState.current) == null ? void 0 : _r.scrollAdjustHandler,
7037
7300
  scrollEventThrottle: 0,
7038
7301
  snapToIndices,
7039
7302
  stickyHeaderIndices,