@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.mjs CHANGED
@@ -1095,32 +1095,34 @@ function scheduleAdaptiveRenderExit(ctx, exitDelay) {
1095
1095
  const state = ctx.state;
1096
1096
  clearAdaptiveRenderExitTimeout(ctx);
1097
1097
  if (exitDelay <= 0) {
1098
- setAdaptiveRender(ctx, "normal");
1098
+ setAdaptiveRender(ctx, "normal", "scroll");
1099
1099
  } else {
1100
1100
  const timeout = setTimeout(() => {
1101
1101
  state.timeouts.delete(timeout);
1102
1102
  state.timeoutAdaptiveRender = void 0;
1103
- setAdaptiveRender(ctx, "normal");
1103
+ setAdaptiveRender(ctx, "normal", "scroll");
1104
1104
  }, exitDelay);
1105
1105
  state.timeoutAdaptiveRender = timeout;
1106
1106
  state.timeouts.add(timeout);
1107
1107
  }
1108
1108
  }
1109
- function setAdaptiveRender(ctx, mode) {
1109
+ function setAdaptiveRender(ctx, mode, reason) {
1110
1110
  var _a3, _b;
1111
1111
  const previousMode = peek$(ctx, "adaptiveRender");
1112
1112
  if (previousMode !== mode) {
1113
1113
  set$(ctx, "adaptiveRender", mode);
1114
- (_b = (_a3 = ctx.state.props.adaptiveRender) == null ? void 0 : _a3.onChange) == null ? void 0 : _b.call(_a3, mode);
1114
+ (_b = (_a3 = ctx.state.props.adaptiveRender) == null ? void 0 : _a3.onChange) == null ? void 0 : _b.call(_a3, mode, reason);
1115
1115
  }
1116
1116
  }
1117
1117
  function resetAdaptiveRender(ctx) {
1118
+ var _a3, _b;
1118
1119
  clearAdaptiveRenderExitTimeout(ctx);
1119
- if (peek$(ctx, "adaptiveRender") !== "normal") {
1120
- setAdaptiveRender(ctx, "normal");
1120
+ const mode = (_b = (_a3 = ctx.state.props.adaptiveRender) == null ? void 0 : _a3.initialMode) != null ? _b : "normal";
1121
+ if (peek$(ctx, "adaptiveRender") !== mode) {
1122
+ setAdaptiveRender(ctx, mode, "initial");
1121
1123
  }
1122
1124
  }
1123
- function updateAdaptiveRender(ctx, scrollVelocity) {
1125
+ function updateAdaptiveRender(ctx, scrollVelocity, options) {
1124
1126
  var _a3, _b, _c;
1125
1127
  const state = ctx.state;
1126
1128
  const adaptiveRender = state.props.adaptiveRender;
@@ -1132,11 +1134,11 @@ function updateAdaptiveRender(ctx, scrollVelocity) {
1132
1134
  const exitVelocity = (_b = adaptiveRender.exitVelocity) != null ? _b : isWeb ? DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_VELOCITY : DEFAULT_ADAPTIVE_RENDER_EXIT_VELOCITY;
1133
1135
  const exitDelay = (_c = adaptiveRender.exitDelay) != null ? _c : isWeb ? DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_DELAY : DEFAULT_ADAPTIVE_RENDER_EXIT_DELAY;
1134
1136
  const threshold = currentMode === "light" ? exitVelocity : enterVelocity;
1135
- const nextMode = Math.abs(scrollVelocity) > threshold ? "light" : "normal";
1137
+ const nextMode = (options == null ? void 0 : options.forceLight) || Math.abs(scrollVelocity) > threshold ? "light" : "normal";
1136
1138
  const previousMode = state.timeoutAdaptiveRender !== void 0 ? "normal" : currentMode;
1137
1139
  if (nextMode !== previousMode) {
1138
1140
  if (nextMode === "light") {
1139
- setAdaptiveRender(ctx, "light");
1141
+ setAdaptiveRender(ctx, "light", "scroll");
1140
1142
  scheduleAdaptiveRenderExit(ctx, exitDelay);
1141
1143
  } else if (currentMode === "light") {
1142
1144
  scheduleAdaptiveRenderExit(ctx, exitDelay);
@@ -1149,16 +1151,43 @@ function updateAdaptiveRender(ctx, scrollVelocity) {
1149
1151
  }
1150
1152
 
1151
1153
  // src/utils/getEffectiveDrawDistance.ts
1152
- var INITIAL_DRAW_DISTANCE = 100;
1153
- function getEffectiveDrawDistance(ctx) {
1154
+ var INITIAL_DRAW_DISTANCE = 50;
1155
+ function getEffectiveDrawDistance(ctx, mode) {
1154
1156
  var _a3;
1155
1157
  const drawDistance = ctx.state.props.drawDistance;
1156
1158
  const initialScroll = ctx.state.initialScroll;
1157
1159
  const needsFullInitialDrawDistance = initialScroll !== void 0 && ((_a3 = initialScroll.viewPosition) != null ? _a3 : 0) > 0;
1158
- return peek$(ctx, "readyToRender") || needsFullInitialDrawDistance ? drawDistance : Math.min(drawDistance, INITIAL_DRAW_DISTANCE);
1160
+ const shouldCapDrawDistance = mode === "visible-first" || mode !== "full" && !peek$(ctx, "readyToRender") && !needsFullInitialDrawDistance;
1161
+ return shouldCapDrawDistance ? Math.min(drawDistance, INITIAL_DRAW_DISTANCE) : drawDistance;
1162
+ }
1163
+ function scheduleFullDrawDistancePrewarm(ctx) {
1164
+ const { state } = ctx;
1165
+ if (state.props.drawDistance <= INITIAL_DRAW_DISTANCE || state.queuedFullDrawDistancePrewarm !== void 0) {
1166
+ return;
1167
+ }
1168
+ state.queuedFullDrawDistancePrewarm = requestAnimationFrame(() => {
1169
+ var _a3;
1170
+ state.queuedFullDrawDistancePrewarm = void 0;
1171
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
1172
+ });
1159
1173
  }
1160
1174
 
1161
1175
  // src/utils/setInitialRenderState.ts
1176
+ function resetInitialRenderState(ctx, {
1177
+ resetLayout,
1178
+ resetInitialScroll
1179
+ }) {
1180
+ const { state } = ctx;
1181
+ if (resetLayout) {
1182
+ state.didContainersLayout = false;
1183
+ state.queuedInitialLayout = false;
1184
+ }
1185
+ if (resetInitialScroll) {
1186
+ state.didFinishInitialScroll = false;
1187
+ }
1188
+ set$(ctx, "readyToRender", false);
1189
+ resetAdaptiveRender(ctx);
1190
+ }
1162
1191
  function setInitialRenderState(ctx, {
1163
1192
  didLayout,
1164
1193
  didInitialScroll
@@ -1177,15 +1206,15 @@ function setInitialRenderState(ctx, {
1177
1206
  const isReadyToRender = Boolean(state.didContainersLayout && state.didFinishInitialScroll);
1178
1207
  if (isReadyToRender && !peek$(ctx, "readyToRender")) {
1179
1208
  set$(ctx, "readyToRender", true);
1180
- setAdaptiveRender(ctx, "normal");
1209
+ setAdaptiveRender(ctx, "normal", "ready");
1181
1210
  if (state.props.drawDistance > INITIAL_DRAW_DISTANCE) {
1182
- requestAnimationFrame(() => {
1183
- var _a3;
1184
- (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
1185
- });
1211
+ scheduleFullDrawDistancePrewarm(ctx);
1186
1212
  }
1187
- if (onLoad) {
1188
- onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1213
+ if (!state.didLoad) {
1214
+ state.didLoad = true;
1215
+ if (onLoad) {
1216
+ onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1217
+ }
1189
1218
  }
1190
1219
  }
1191
1220
  }
@@ -1484,6 +1513,7 @@ function finishScrollTo(ctx) {
1484
1513
  const scrollingTo = state.scrollingTo;
1485
1514
  state.scrollHistory.length = 0;
1486
1515
  state.scrollingTo = void 0;
1516
+ state.scrollTargetPinnedRange = void 0;
1487
1517
  if (state.pendingTotalSize !== void 0) {
1488
1518
  addTotalSize(ctx, null, state.pendingTotalSize);
1489
1519
  }
@@ -1714,8 +1744,8 @@ function doMaintainScrollAtEnd(ctx) {
1714
1744
  state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
1715
1745
  return false;
1716
1746
  }
1717
- state.pendingMaintainScrollAtEnd = false;
1718
1747
  if (shouldMaintainScrollAtEnd) {
1748
+ state.pendingMaintainScrollAtEnd = false;
1719
1749
  const contentSize = getContentSize(ctx);
1720
1750
  if (contentSize < state.scrollLength) {
1721
1751
  state.scroll = 0;
@@ -1746,6 +1776,9 @@ function doMaintainScrollAtEnd(ctx) {
1746
1776
  () => {
1747
1777
  if (state.maintainingScrollAtEnd === activeState) {
1748
1778
  state.maintainingScrollAtEnd = void 0;
1779
+ if (state.pendingMaintainScrollAtEnd) {
1780
+ doMaintainScrollAtEnd(ctx);
1781
+ }
1749
1782
  }
1750
1783
  },
1751
1784
  maintainScrollAtEnd.animated ? 500 : 0
@@ -1754,9 +1787,12 @@ function doMaintainScrollAtEnd(ctx) {
1754
1787
  state.maintainingScrollAtEnd = void 0;
1755
1788
  }
1756
1789
  });
1790
+ } else {
1791
+ state.pendingMaintainScrollAtEnd = true;
1757
1792
  }
1758
1793
  return true;
1759
1794
  }
1795
+ state.pendingMaintainScrollAtEnd = false;
1760
1796
  return false;
1761
1797
  }
1762
1798
 
@@ -2117,6 +2153,8 @@ var flushSync = (fn) => {
2117
2153
  };
2118
2154
 
2119
2155
  // src/utils/getScrollVelocity.ts
2156
+ var MAX_SCROLL_VELOCITY_WINDOW_MS = 1e3;
2157
+ var SCROLL_VELOCITY_HALF_LIFE_MS = 200;
2120
2158
  var getScrollVelocity = (state) => {
2121
2159
  const { scrollHistory } = state;
2122
2160
  const newestIndex = scrollHistory.length - 1;
@@ -2124,35 +2162,37 @@ var getScrollVelocity = (state) => {
2124
2162
  return 0;
2125
2163
  }
2126
2164
  const newest = scrollHistory[newestIndex];
2127
- const now = Date.now();
2128
- let direction = 0;
2129
- for (let i = newestIndex; i > 0; i--) {
2130
- const delta = scrollHistory[i].scroll - scrollHistory[i - 1].scroll;
2131
- if (delta !== 0) {
2132
- direction = Math.sign(delta);
2133
- break;
2134
- }
2135
- }
2136
- if (direction === 0) {
2165
+ if (Date.now() - newest.time > MAX_SCROLL_VELOCITY_WINDOW_MS) {
2137
2166
  return 0;
2138
2167
  }
2139
- let oldest = newest;
2140
- for (let i = newestIndex - 1; i >= 0; i--) {
2168
+ let direction = 0;
2169
+ let weightedVelocity = 0;
2170
+ let totalWeight = 0;
2171
+ for (let i = newestIndex; i > 0; i--) {
2141
2172
  const current = scrollHistory[i];
2142
- const next = scrollHistory[i + 1];
2143
- const delta = next.scroll - current.scroll;
2144
- const deltaSign = Math.sign(delta);
2145
- if (deltaSign !== 0 && deltaSign !== direction) {
2146
- break;
2173
+ const previous = scrollHistory[i - 1];
2174
+ const scrollDiff = current.scroll - previous.scroll;
2175
+ const timeDiff = current.time - previous.time;
2176
+ const deltaSign = Math.sign(scrollDiff);
2177
+ if (deltaSign !== 0) {
2178
+ if (direction === 0) {
2179
+ direction = deltaSign;
2180
+ } else if (deltaSign !== direction) {
2181
+ break;
2182
+ }
2147
2183
  }
2148
- if (now - current.time > 1e3) {
2184
+ if (newest.time - previous.time > MAX_SCROLL_VELOCITY_WINDOW_MS) {
2149
2185
  break;
2150
2186
  }
2151
- oldest = current;
2187
+ if (scrollDiff === 0 || timeDiff <= 0) {
2188
+ continue;
2189
+ }
2190
+ const age = newest.time - current.time;
2191
+ const weight = Math.exp(-age / SCROLL_VELOCITY_HALF_LIFE_MS);
2192
+ weightedVelocity += scrollDiff / timeDiff * weight;
2193
+ totalWeight += weight;
2152
2194
  }
2153
- const scrollDiff = newest.scroll - oldest.scroll;
2154
- const timeDiff = newest.time - oldest.time;
2155
- return timeDiff > 0 ? scrollDiff / timeDiff : 0;
2195
+ return totalWeight > 0 ? weightedVelocity / totalWeight : 0;
2156
2196
  };
2157
2197
 
2158
2198
  // src/core/updateScroll.ts
@@ -2180,8 +2220,6 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2180
2220
  if (scrollHistory.length > 5) {
2181
2221
  scrollHistory.shift();
2182
2222
  }
2183
- const scrollVelocity = getScrollVelocity(state);
2184
- updateAdaptiveRender(ctx, scrollVelocity);
2185
2223
  if (ignoreScrollFromMVCP && !scrollingTo) {
2186
2224
  const { lt, gt } = ignoreScrollFromMVCP;
2187
2225
  if (lt && newScroll < lt || gt && newScroll > gt) {
@@ -2196,6 +2234,9 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2196
2234
  const scrollDelta = Math.abs(newScroll - prevScroll);
2197
2235
  const didResolvePendingNativeMVCPAdjust = resolvePendingNativeMVCPAdjust(ctx, newScroll);
2198
2236
  const scrollLength = state.scrollLength;
2237
+ const isLargeUserScrollJump = scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength && !state.pendingNativeMVCPAdjust;
2238
+ const scrollVelocity = getScrollVelocity(state);
2239
+ updateAdaptiveRender(ctx, scrollVelocity, { forceLight: isLargeUserScrollJump });
2199
2240
  const lastCalculated = state.scrollLastCalculate;
2200
2241
  const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
2201
2242
  const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
@@ -2205,10 +2246,17 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2205
2246
  state.lastScrollDelta = scrollDelta;
2206
2247
  const runCalculateItems = () => {
2207
2248
  var _a4;
2208
- (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { doMVCP: scrollingTo !== void 0, scrollVelocity });
2249
+ const calculateItemsParams = {
2250
+ doMVCP: scrollingTo !== void 0,
2251
+ scrollVelocity
2252
+ };
2253
+ if (isLargeUserScrollJump) {
2254
+ calculateItemsParams.drawDistanceMode = "visible-first";
2255
+ }
2256
+ (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, calculateItemsParams);
2209
2257
  checkThresholds(ctx);
2210
2258
  };
2211
- if (scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength && !state.pendingNativeMVCPAdjust) {
2259
+ if (isLargeUserScrollJump) {
2212
2260
  state.mvcpAnchorLock = void 0;
2213
2261
  state.pendingNativeMVCPAdjust = void 0;
2214
2262
  state.userScrollAnchorReset = { keys: /* @__PURE__ */ new Set() };
@@ -2217,6 +2265,7 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2217
2265
  state.queuedMVCPRecalculate = void 0;
2218
2266
  }
2219
2267
  flushSync(runCalculateItems);
2268
+ scheduleFullDrawDistancePrewarm(ctx);
2220
2269
  } else {
2221
2270
  runCalculateItems();
2222
2271
  }
@@ -2260,8 +2309,93 @@ function syncInitialScrollNativeWatchdog(state, options) {
2260
2309
  initialScrollWatchdog.clear(state);
2261
2310
  }
2262
2311
  }
2263
- function scrollTo(ctx, params) {
2312
+ function findPositionIndexAtOrBeforeOffset(ctx, offset) {
2313
+ const state = ctx.state;
2314
+ const dataLength = state.props.data.length;
2315
+ let low = 0;
2316
+ let high = dataLength - 1;
2317
+ let match;
2318
+ while (low <= high) {
2319
+ const mid = Math.floor((low + high) / 2);
2320
+ const top = state.positions[mid];
2321
+ if (top === void 0) {
2322
+ high = mid - 1;
2323
+ } else {
2324
+ if (top <= offset) {
2325
+ match = mid;
2326
+ low = mid + 1;
2327
+ } else {
2328
+ high = mid - 1;
2329
+ }
2330
+ }
2331
+ }
2332
+ return match;
2333
+ }
2334
+ function getItemBottom(ctx, index) {
2264
2335
  var _a3;
2336
+ const top = ctx.state.positions[index];
2337
+ if (top === void 0) {
2338
+ return void 0;
2339
+ }
2340
+ const itemSize = (_a3 = getItemSizeAtIndex(ctx, index)) != null ? _a3 : 0;
2341
+ return top + (Number.isFinite(itemSize) ? itemSize : 0);
2342
+ }
2343
+ function getTargetViewportRenderRange(ctx, targetOffset, targetIndex) {
2344
+ const state = ctx.state;
2345
+ const dataLength = state.props.data.length;
2346
+ if (dataLength === 0) {
2347
+ return void 0;
2348
+ }
2349
+ const viewportStart = Math.max(0, targetOffset);
2350
+ const viewportEnd = Math.max(viewportStart, targetOffset + state.scrollLength);
2351
+ let start = targetIndex !== void 0 ? Math.max(0, Math.min(dataLength - 1, targetIndex)) : findPositionIndexAtOrBeforeOffset(ctx, viewportStart);
2352
+ if (start === void 0) {
2353
+ return void 0;
2354
+ }
2355
+ if (targetIndex !== void 0 && state.positions[start] === void 0) {
2356
+ return { end: start, start };
2357
+ }
2358
+ if (targetIndex === void 0) {
2359
+ const startBottom = getItemBottom(ctx, start);
2360
+ if (startBottom === void 0 || startBottom <= viewportStart) {
2361
+ return void 0;
2362
+ }
2363
+ }
2364
+ while (start > 0) {
2365
+ const top = state.positions[start];
2366
+ if (top === void 0 || top <= viewportStart || state.positions[start - 1] === void 0) {
2367
+ break;
2368
+ }
2369
+ start--;
2370
+ }
2371
+ while (start > 0) {
2372
+ const previousBottom = getItemBottom(ctx, start - 1);
2373
+ if (previousBottom === void 0 || previousBottom <= viewportStart) {
2374
+ break;
2375
+ }
2376
+ start--;
2377
+ }
2378
+ let end = start;
2379
+ while (end + 1 < dataLength) {
2380
+ const nextTop = state.positions[end + 1];
2381
+ if (nextTop === void 0 || nextTop > viewportEnd) {
2382
+ break;
2383
+ }
2384
+ end++;
2385
+ }
2386
+ return { end, start };
2387
+ }
2388
+ function pinScrollTargetRenderRange(ctx, targetOffset, targetIndex) {
2389
+ const range = getTargetViewportRenderRange(ctx, targetOffset, targetIndex);
2390
+ if (range) {
2391
+ ctx.state.scrollTargetPinnedRange = range;
2392
+ ctx.state.scrollForNextCalculateItemsInView = void 0;
2393
+ } else {
2394
+ ctx.state.scrollTargetPinnedRange = void 0;
2395
+ }
2396
+ }
2397
+ function scrollTo(ctx, params) {
2398
+ var _a3, _b;
2265
2399
  const state = ctx.state;
2266
2400
  const { noScrollingTo, forceScroll, ...scrollTarget } = params;
2267
2401
  const {
@@ -2296,11 +2430,20 @@ function scrollTo(ctx, params) {
2296
2430
  targetOffset,
2297
2431
  waitForInitialScrollCompletionFrame
2298
2432
  };
2433
+ if (!isInitialScroll) {
2434
+ pinScrollTargetRenderRange(ctx, targetOffset, scrollTarget.index);
2435
+ }
2299
2436
  }
2300
2437
  state.scrollPending = targetOffset;
2301
2438
  syncInitialScrollNativeWatchdog(state, { isInitialScroll, requestedOffset: offset, targetOffset });
2302
- if (!animated && !isInitialScroll && !noScrollingTo && Math.abs(state.scroll - targetOffset) > 1) {
2303
- updateScroll(ctx, targetOffset, false, { markHasScrolled: false });
2439
+ if (!isInitialScroll && !noScrollingTo && Math.abs(state.scroll - targetOffset) > 1) {
2440
+ if (animated) {
2441
+ if (state.scrollTargetPinnedRange) {
2442
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state);
2443
+ }
2444
+ } else {
2445
+ updateScroll(ctx, targetOffset, true, { markHasScrolled: false });
2446
+ }
2304
2447
  }
2305
2448
  if (forceScroll || !isInitialScroll || Platform.OS === "android") {
2306
2449
  doScrollTo(ctx, { animated, horizontal, isInitialScroll, offset });
@@ -2371,16 +2514,17 @@ function dispatchInitialScroll(ctx, params) {
2371
2514
  waitForInitialScrollCompletionFrame: waitForCompletionFrame
2372
2515
  });
2373
2516
  }
2374
- function setInitialScrollTarget(state, target, options) {
2517
+ function setInitialScrollTarget(ctx, target, options) {
2375
2518
  var _a3;
2519
+ const { state } = ctx;
2376
2520
  state.clearPreservedInitialScrollOnNextFinish = void 0;
2377
2521
  if (state.timeoutPreservedInitialScrollClear !== void 0) {
2378
2522
  clearTimeout(state.timeoutPreservedInitialScrollClear);
2379
2523
  state.timeoutPreservedInitialScrollClear = void 0;
2380
2524
  }
2381
2525
  state.initialScroll = target;
2382
- if ((options == null ? void 0 : options.resetDidFinish) && state.didFinishInitialScroll) {
2383
- state.didFinishInitialScroll = false;
2526
+ if (options == null ? void 0 : options.resetDidFinish) {
2527
+ resetInitialRenderState(ctx, { resetInitialScroll: true });
2384
2528
  }
2385
2529
  setInitialScrollSession(state, {
2386
2530
  kind: ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" ? "offset" : "bootstrap"
@@ -2434,7 +2578,7 @@ function advanceMeasuredInitialScroll(ctx, options) {
2434
2578
  return false;
2435
2579
  }
2436
2580
  if (didOffsetChange && ((_b = state.initialScrollSession) == null ? void 0 : _b.kind) !== "offset") {
2437
- setInitialScrollTarget(state, { ...initialScroll, contentOffset: resolvedOffset });
2581
+ setInitialScrollTarget(ctx, { ...initialScroll, contentOffset: resolvedOffset });
2438
2582
  }
2439
2583
  const forceScroll = (_c = options == null ? void 0 : options.forceScroll) != null ? _c : !!queuedInitialLayout || isInitialScrollInProgress && didOffsetChange;
2440
2584
  dispatchInitialScroll(ctx, {
@@ -2707,7 +2851,6 @@ function areEquivalentBootstrapInitialScrollTargets(current, next) {
2707
2851
  }
2708
2852
  function clearPendingInitialScrollFooterLayout(ctx, options) {
2709
2853
  const { dataLength, stylePaddingBottom, target } = options;
2710
- const state = ctx.state;
2711
2854
  if (!shouldPreserveInitialScrollForFooterLayout(target)) {
2712
2855
  return;
2713
2856
  }
@@ -2717,7 +2860,7 @@ function clearPendingInitialScrollFooterLayout(ctx, options) {
2717
2860
  preserveForFooterLayout: void 0,
2718
2861
  stylePaddingBottom
2719
2862
  });
2720
- setInitialScrollTarget(state, clearedFooterTarget);
2863
+ setInitialScrollTarget(ctx, clearedFooterTarget);
2721
2864
  }
2722
2865
  function clearFinishedViewportRetargetableInitialScroll(state) {
2723
2866
  clearPreservedInitialScrollTarget(state);
@@ -2864,7 +3007,7 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
2864
3007
  return;
2865
3008
  }
2866
3009
  if (!areEquivalentBootstrapInitialScrollTargets(initialScroll, updatedInitialScroll) || !!bootstrapInitialScroll || shouldResetDidFinish || didDataChange) {
2867
- setInitialScrollTarget(state, updatedInitialScroll, {
3010
+ setInitialScrollTarget(ctx, updatedInitialScroll, {
2868
3011
  resetDidFinish: shouldResetDidFinish
2869
3012
  });
2870
3013
  rearmBootstrapInitialScroll(ctx, {
@@ -2879,7 +3022,7 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
2879
3022
  return;
2880
3023
  }
2881
3024
  if (bootstrapInitialScroll || shouldResetDidFinish) {
2882
- setInitialScrollTarget(state, initialScroll, {
3025
+ setInitialScrollTarget(ctx, initialScroll, {
2883
3026
  resetDidFinish: shouldResetDidFinish
2884
3027
  });
2885
3028
  rearmBootstrapInitialScroll(ctx, {
@@ -2925,7 +3068,7 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2925
3068
  });
2926
3069
  } else {
2927
3070
  const didFinishInitialScroll = !!state.didFinishInitialScroll;
2928
- setInitialScrollTarget(state, updatedInitialScroll, {
3071
+ setInitialScrollTarget(ctx, updatedInitialScroll, {
2929
3072
  resetDidFinish: didFinishInitialScroll
2930
3073
  });
2931
3074
  rearmBootstrapInitialScroll(ctx, {
@@ -3114,7 +3257,7 @@ function initializeInitialScrollOnMount(ctx, options) {
3114
3257
  const resolvedInitialContentOffset = initialContentOffset != null ? initialContentOffset : 0;
3115
3258
  const preserveForFooterLayout = useBootstrapInitialScroll && initialScrollAtEnd && hasFooterComponent;
3116
3259
  if (initialScroll && (initialScroll.contentOffset === void 0 || !!initialScroll.preserveForFooterLayout !== preserveForFooterLayout && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) !== "offset")) {
3117
- setInitialScrollTarget(state, {
3260
+ setInitialScrollTarget(ctx, {
3118
3261
  ...initialScroll,
3119
3262
  contentOffset: resolvedInitialContentOffset,
3120
3263
  preserveForFooterLayout
@@ -3143,6 +3286,7 @@ function handleInitialScrollDataChange(ctx, options) {
3143
3286
  const {
3144
3287
  dataLength,
3145
3288
  didDataChange,
3289
+ didStartFreshData,
3146
3290
  initialScrollAtEnd,
3147
3291
  latestInitialScroll,
3148
3292
  latestInitialScrollSessionKind,
@@ -3150,17 +3294,17 @@ function handleInitialScrollDataChange(ctx, options) {
3150
3294
  useBootstrapInitialScroll
3151
3295
  } = options;
3152
3296
  const state = ctx.state;
3153
- const previousDataLength = (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.previousDataLength) != null ? _b : 0;
3154
- const isFirstNonEmptyData = !state.hasHadNonEmptyData && dataLength > 0;
3297
+ const previousInitialScrollDataLength = (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.previousDataLength) != null ? _b : 0;
3298
+ const shouldUseLatestInitialScroll = dataLength > 0 && (!state.hasHadNonEmptyData || didStartFreshData);
3155
3299
  if (dataLength > 0) {
3156
3300
  state.hasHadNonEmptyData = true;
3157
3301
  }
3158
- if (isFirstNonEmptyData) {
3302
+ if (shouldUseLatestInitialScroll) {
3159
3303
  if (latestInitialScroll) {
3160
- setInitialScrollTarget(state, latestInitialScroll);
3304
+ setInitialScrollTarget(ctx, latestInitialScroll);
3161
3305
  setInitialScrollSession(state, {
3162
3306
  kind: latestInitialScrollSessionKind,
3163
- previousDataLength
3307
+ previousDataLength: previousInitialScrollDataLength
3164
3308
  });
3165
3309
  } else {
3166
3310
  clearPreservedInitialScrollTarget(state);
@@ -3175,17 +3319,17 @@ function handleInitialScrollDataChange(ctx, options) {
3175
3319
  dataLength,
3176
3320
  didDataChange,
3177
3321
  initialScrollAtEnd,
3178
- previousDataLength,
3322
+ previousDataLength: previousInitialScrollDataLength,
3179
3323
  stylePaddingBottom
3180
3324
  });
3181
3325
  return;
3182
3326
  }
3183
- const shouldReplayFinishedOffsetInitialScroll = previousDataLength === 0 && dataLength > 0 && !!state.initialScroll && ((_c = ctx.state.initialScrollSession) == null ? void 0 : _c.kind) === "offset" && !!state.didFinishInitialScroll;
3184
- if (previousDataLength !== 0 || dataLength === 0 || !state.initialScroll || !state.queuedInitialLayout || state.didFinishInitialScroll && !shouldReplayFinishedOffsetInitialScroll) {
3327
+ const shouldReplayFinishedOffsetInitialScroll = previousInitialScrollDataLength === 0 && dataLength > 0 && !!state.initialScroll && ((_c = ctx.state.initialScrollSession) == null ? void 0 : _c.kind) === "offset" && !!state.didFinishInitialScroll;
3328
+ if (previousInitialScrollDataLength !== 0 || dataLength === 0 || !state.initialScroll || !state.queuedInitialLayout || state.didFinishInitialScroll && !shouldReplayFinishedOffsetInitialScroll) {
3185
3329
  return;
3186
3330
  }
3187
3331
  if (shouldReplayFinishedOffsetInitialScroll) {
3188
- state.didFinishInitialScroll = false;
3332
+ resetInitialRenderState(ctx, { resetInitialScroll: true });
3189
3333
  }
3190
3334
  advanceCurrentInitialScrollSession(ctx);
3191
3335
  }
@@ -3971,6 +4115,32 @@ function setDidLayout(ctx) {
3971
4115
  }
3972
4116
 
3973
4117
  // src/core/calculateItemsInView.ts
4118
+ var RENDER_RANGE_PROJECTION_FULL_VELOCITY = 4;
4119
+ var RENDER_RANGE_PROJECTION_SETTLE_DELAY = 100;
4120
+ function getProjectedBufferAdjustment(scrollVelocity, trailingBuffer) {
4121
+ if (trailingBuffer <= 0) {
4122
+ return 0;
4123
+ }
4124
+ const velocityProgress = Math.min(1, Math.abs(scrollVelocity) / RENDER_RANGE_PROJECTION_FULL_VELOCITY);
4125
+ return Math.sign(scrollVelocity) * trailingBuffer * velocityProgress;
4126
+ }
4127
+ function scheduleRenderRangeProjectionSettle(ctx) {
4128
+ const state = ctx.state;
4129
+ const previousTimeout = state.timeoutRenderRangeProjectionSettle;
4130
+ if (previousTimeout !== void 0) {
4131
+ clearTimeout(previousTimeout);
4132
+ state.timeouts.delete(previousTimeout);
4133
+ }
4134
+ const timeout = setTimeout(() => {
4135
+ var _a3;
4136
+ state.timeoutRenderRangeProjectionSettle = void 0;
4137
+ state.timeouts.delete(timeout);
4138
+ state.scrollHistory.length = 0;
4139
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
4140
+ }, RENDER_RANGE_PROJECTION_SETTLE_DELAY);
4141
+ state.timeoutRenderRangeProjectionSettle = timeout;
4142
+ state.timeouts.add(timeout);
4143
+ }
3974
4144
  function findCurrentStickyIndex(stickyArray, scroll, state) {
3975
4145
  const positions = state.positions;
3976
4146
  for (let i = stickyArray.length - 1; i >= 0; i--) {
@@ -4011,14 +4181,14 @@ function handleStickyActivation(ctx, stickyArray, currentStickyIdx, needNewConta
4011
4181
  }
4012
4182
  }
4013
4183
  }
4014
- function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentStickyIdx, pendingRemoval, alwaysRenderIndicesSet) {
4184
+ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentStickyIdx, pendingRemoval, isPinnedRenderIndex) {
4015
4185
  var _a3, _b;
4016
4186
  const state = ctx.state;
4017
4187
  for (const containerIndex of state.stickyContainerPool) {
4018
4188
  const itemKey = peek$(ctx, `containerItemKey${containerIndex}`);
4019
4189
  const itemIndex = itemKey ? state.indexByKey.get(itemKey) : void 0;
4020
4190
  if (itemIndex === void 0) continue;
4021
- if (alwaysRenderIndicesSet.has(itemIndex)) continue;
4191
+ if (isPinnedRenderIndex(itemIndex)) continue;
4022
4192
  const arrayIdx = stickyArray.indexOf(itemIndex);
4023
4193
  if (arrayIdx === -1) {
4024
4194
  state.stickyContainerPool.delete(containerIndex);
@@ -4182,9 +4352,7 @@ function calculateItemsInView(ctx, params = {}) {
4182
4352
  const { data } = state.props;
4183
4353
  const stickyHeaderIndicesArr = state.props.stickyHeaderIndicesArr || [];
4184
4354
  const stickyHeaderIndicesSet = state.props.stickyHeaderIndicesSet || /* @__PURE__ */ new Set();
4185
- const alwaysRenderArr = alwaysRenderIndicesArr || [];
4186
- const alwaysRenderSet = alwaysRenderIndicesSet || /* @__PURE__ */ new Set();
4187
- const drawDistance = getEffectiveDrawDistance(ctx);
4355
+ const drawDistance = getEffectiveDrawDistance(ctx, params.drawDistanceMode);
4188
4356
  const { dataChanged, doMVCP, forceFullItemPositions } = params;
4189
4357
  const bootstrapInitialScrollState = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" ? state.initialScrollSession.bootstrap : void 0;
4190
4358
  const suppressInitialScrollSideEffects = !!bootstrapInitialScrollState;
@@ -4250,14 +4418,19 @@ function calculateItemsInView(ctx, params = {}) {
4250
4418
  scrollBufferTop = drawDistance * 1.5;
4251
4419
  scrollBufferBottom = drawDistance * 0.5;
4252
4420
  }
4421
+ const shouldProjectRenderRange = !dataChanged && !forceFullItemPositions && !suppressInitialScrollSideEffects && !hasActiveInitialScroll(state) && !state.scrollingTo && !state.pendingNativeMVCPAdjust && !!peek$(ctx, "readyToRender");
4422
+ const projectedBufferAdjustment = shouldProjectRenderRange ? getProjectedBufferAdjustment(speed, Math.min(scrollBufferTop, scrollBufferBottom)) : 0;
4253
4423
  const updateScrollRange = () => {
4254
4424
  const scrollStart = Math.max(0, scroll);
4255
4425
  const overscrollBeforeContent = Math.max(0, -nativeScrollState);
4256
- scrollTopBuffered = scrollStart - scrollBufferTop;
4257
4426
  scrollBottom = Math.max(scrollStart, scroll + scrollLength + overscrollBeforeContent);
4258
- scrollBottomBuffered = scrollBottom + scrollBufferBottom;
4427
+ scrollTopBuffered = scrollStart - scrollBufferTop + projectedBufferAdjustment;
4428
+ scrollBottomBuffered = scrollBottom + scrollBufferBottom + projectedBufferAdjustment;
4259
4429
  };
4260
4430
  updateScrollRange();
4431
+ if (projectedBufferAdjustment !== 0) {
4432
+ scheduleRenderRangeProjectionSettle(ctx);
4433
+ }
4261
4434
  if (enableScrollForNextCalculateItemsInView && !suppressInitialScrollSideEffects && !dataChanged && !forceFullItemPositions && scrollForNextCalculateItemsInView) {
4262
4435
  const { top, bottom } = scrollForNextCalculateItemsInView;
4263
4436
  if (top === null && bottom === null) {
@@ -4415,9 +4588,34 @@ function calculateItemsInView(ctx, params = {}) {
4415
4588
  }
4416
4589
  }
4417
4590
  }
4591
+ const scrollTargetPinnedRange = state.scrollTargetPinnedRange;
4592
+ let scrollTargetPinnedStart = 0;
4593
+ let scrollTargetPinnedEnd = -1;
4594
+ if (scrollTargetPinnedRange) {
4595
+ scrollTargetPinnedStart = Math.max(0, Math.min(scrollTargetPinnedRange.start, scrollTargetPinnedRange.end));
4596
+ scrollTargetPinnedEnd = Math.min(
4597
+ dataLength - 1,
4598
+ Math.max(scrollTargetPinnedRange.start, scrollTargetPinnedRange.end)
4599
+ );
4600
+ }
4601
+ const hasScrollTargetPinnedRange = scrollTargetPinnedStart <= scrollTargetPinnedEnd;
4602
+ const isPinnedRenderIndex = (index) => alwaysRenderIndicesSet.has(index) || hasScrollTargetPinnedRange && index >= scrollTargetPinnedStart && index <= scrollTargetPinnedEnd;
4418
4603
  if (startBuffered !== null && endBuffered !== null) {
4419
4604
  const needNewContainers = [];
4420
4605
  const needNewContainersSet = /* @__PURE__ */ new Set();
4606
+ const addPinnedIndex = (index) => {
4607
+ var _a4;
4608
+ if (index >= 0 && index < dataLength) {
4609
+ const id = (_a4 = idCache[index]) != null ? _a4 : getId(state, index);
4610
+ const containerIndex = containerItemKeys.get(id);
4611
+ if (containerIndex !== void 0) {
4612
+ state.stickyContainerPool.add(containerIndex);
4613
+ } else if (!isNullOrUndefined(id) && !needNewContainersSet.has(index)) {
4614
+ needNewContainersSet.add(index);
4615
+ needNewContainers.push(index);
4616
+ }
4617
+ }
4618
+ };
4421
4619
  for (let i = startBuffered; i <= endBuffered; i++) {
4422
4620
  const id = (_m = idCache[i]) != null ? _m : getId(state, i);
4423
4621
  if (!containerItemKeys.has(id)) {
@@ -4425,21 +4623,19 @@ function calculateItemsInView(ctx, params = {}) {
4425
4623
  needNewContainers.push(i);
4426
4624
  }
4427
4625
  }
4428
- if (alwaysRenderArr.length > 0) {
4429
- for (const index of alwaysRenderArr) {
4430
- if (index < 0 || index >= dataLength) continue;
4431
- const id = (_n = idCache[index]) != null ? _n : getId(state, index);
4432
- if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
4433
- needNewContainersSet.add(index);
4434
- needNewContainers.push(index);
4435
- }
4626
+ for (const index of alwaysRenderIndicesArr) {
4627
+ addPinnedIndex(index);
4628
+ }
4629
+ if (hasScrollTargetPinnedRange) {
4630
+ for (let index = scrollTargetPinnedStart; index <= scrollTargetPinnedEnd; index++) {
4631
+ addPinnedIndex(index);
4436
4632
  }
4437
4633
  }
4438
4634
  if (stickyHeaderIndicesArr.length > 0) {
4439
4635
  handleStickyActivation(
4440
4636
  ctx,
4441
4637
  stickyHeaderIndicesArr,
4442
- (_o = stickyState == null ? void 0 : stickyState.currentStickyIdx) != null ? _o : -1,
4638
+ (_n = stickyState == null ? void 0 : stickyState.currentStickyIdx) != null ? _n : -1,
4443
4639
  needNewContainers,
4444
4640
  needNewContainersSet,
4445
4641
  startBuffered,
@@ -4465,7 +4661,7 @@ function calculateItemsInView(ctx, params = {}) {
4465
4661
  for (const allocation of availableContainerAllocations) {
4466
4662
  const i = allocation.itemIndex;
4467
4663
  const containerIndex = allocation.containerIndex;
4468
- const id = (_p = idCache[i]) != null ? _p : getId(state, i);
4664
+ const id = (_o = idCache[i]) != null ? _o : getId(state, i);
4469
4665
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
4470
4666
  if (oldKey && oldKey !== id) {
4471
4667
  containerItemKeys.delete(oldKey);
@@ -4476,10 +4672,14 @@ function calculateItemsInView(ctx, params = {}) {
4476
4672
  state.containerItemTypes.set(containerIndex, allocation.itemType);
4477
4673
  }
4478
4674
  containerItemKeys.set(id, containerIndex);
4479
- (_q = state.userScrollAnchorReset) == null ? void 0 : _q.keys.add(id);
4675
+ (_p = state.userScrollAnchorReset) == null ? void 0 : _p.keys.add(id);
4676
+ if (IsNewArchitecture) {
4677
+ (_q = state.pendingLayoutEffectMeasurements) != null ? _q : state.pendingLayoutEffectMeasurements = /* @__PURE__ */ new Set();
4678
+ state.pendingLayoutEffectMeasurements.add(id);
4679
+ }
4480
4680
  const containerSticky = `containerSticky${containerIndex}`;
4481
4681
  const isSticky = stickyHeaderIndicesSet.has(i);
4482
- const isAlwaysRender = alwaysRenderSet.has(i);
4682
+ const isPinnedRender = isPinnedRenderIndex(i);
4483
4683
  if (isSticky) {
4484
4684
  set$(ctx, containerSticky, true);
4485
4685
  state.stickyContainerPool.add(containerIndex);
@@ -4487,9 +4687,9 @@ function calculateItemsInView(ctx, params = {}) {
4487
4687
  if (peek$(ctx, containerSticky)) {
4488
4688
  set$(ctx, containerSticky, false);
4489
4689
  }
4490
- if (isAlwaysRender) {
4690
+ if (isPinnedRender) {
4491
4691
  state.stickyContainerPool.add(containerIndex);
4492
- } else if (state.stickyContainerPool.has(containerIndex)) {
4692
+ } else {
4493
4693
  state.stickyContainerPool.delete(containerIndex);
4494
4694
  }
4495
4695
  }
@@ -4504,20 +4704,8 @@ function calculateItemsInView(ctx, params = {}) {
4504
4704
  }
4505
4705
  }
4506
4706
  }
4507
- if (state.userScrollAnchorReset) {
4508
- if (state.userScrollAnchorReset.keys.size === 0) {
4509
- state.userScrollAnchorReset = void 0;
4510
- }
4511
- }
4512
- if (alwaysRenderArr.length > 0) {
4513
- for (const index of alwaysRenderArr) {
4514
- if (index < 0 || index >= dataLength) continue;
4515
- const id = (_r = idCache[index]) != null ? _r : getId(state, index);
4516
- const containerIndex = containerItemKeys.get(id);
4517
- if (containerIndex !== void 0) {
4518
- state.stickyContainerPool.add(containerIndex);
4519
- }
4520
- }
4707
+ if (((_r = state.userScrollAnchorReset) == null ? void 0 : _r.keys.size) === 0) {
4708
+ state.userScrollAnchorReset = void 0;
4521
4709
  }
4522
4710
  }
4523
4711
  if (state.stickyContainerPool.size > 0) {
@@ -4528,7 +4716,7 @@ function calculateItemsInView(ctx, params = {}) {
4528
4716
  drawDistance,
4529
4717
  (_s = stickyState == null ? void 0 : stickyState.currentStickyIdx) != null ? _s : -1,
4530
4718
  pendingRemoval,
4531
- alwaysRenderSet
4719
+ isPinnedRenderIndex
4532
4720
  );
4533
4721
  }
4534
4722
  const pendingRemovalSet = pendingRemoval.length > 0 ? new Set(pendingRemoval) : void 0;
@@ -4700,6 +4888,36 @@ function mergeItemSizeUpdateResult(result, next) {
4700
4888
  result.needsRecalculate || (result.needsRecalculate = next.needsRecalculate);
4701
4889
  result.shouldMaintainScrollAtEnd || (result.shouldMaintainScrollAtEnd = next.shouldMaintainScrollAtEnd);
4702
4890
  }
4891
+ var batchedItemSizeRecalculates = /* @__PURE__ */ new WeakMap();
4892
+ function flushBatchedItemSizeRecalculate(ctx, didFallback = false, expectedResult) {
4893
+ const result = batchedItemSizeRecalculates.get(ctx);
4894
+ if (!result || expectedResult && result !== expectedResult) {
4895
+ return;
4896
+ }
4897
+ batchedItemSizeRecalculates.delete(ctx);
4898
+ if (didFallback) {
4899
+ ctx.state.pendingLayoutEffectMeasurements = void 0;
4900
+ }
4901
+ flushItemSizeUpdates(ctx, result);
4902
+ }
4903
+ function queueItemSizeRecalculate(ctx, result) {
4904
+ var _a3, _b;
4905
+ const batch = batchedItemSizeRecalculates.get(ctx);
4906
+ if (batch) {
4907
+ mergeItemSizeUpdateResult(batch, result);
4908
+ } else {
4909
+ const nextBatch = { ...result };
4910
+ batchedItemSizeRecalculates.set(ctx, nextBatch);
4911
+ if ((_a3 = ctx.state.pendingLayoutEffectMeasurements) == null ? void 0 : _a3.size) {
4912
+ requestAnimationFrame(() => {
4913
+ flushBatchedItemSizeRecalculate(ctx, true, nextBatch);
4914
+ });
4915
+ }
4916
+ }
4917
+ if (!((_b = ctx.state.pendingLayoutEffectMeasurements) == null ? void 0 : _b.size)) {
4918
+ flushBatchedItemSizeRecalculate(ctx);
4919
+ }
4920
+ }
4703
4921
  function flushItemSizeUpdates(ctx, result) {
4704
4922
  var _a3;
4705
4923
  const state = ctx.state;
@@ -4716,12 +4934,22 @@ function flushItemSizeUpdates(ctx, result) {
4716
4934
  function updateItemSizes(ctx, measurement) {
4717
4935
  var _a3, _b, _c, _d;
4718
4936
  const state = ctx.state;
4937
+ let didDrainLayoutEffectMeasurements = false;
4938
+ if (IsNewArchitecture && measurement.fromLayoutEffect) {
4939
+ const pendingLayoutEffectMeasurements = state.pendingLayoutEffectMeasurements;
4940
+ if ((pendingLayoutEffectMeasurements == null ? void 0 : pendingLayoutEffectMeasurements.delete(measurement.itemKey)) && pendingLayoutEffectMeasurements.size === 0) {
4941
+ state.pendingLayoutEffectMeasurements = void 0;
4942
+ didDrainLayoutEffectMeasurements = true;
4943
+ }
4944
+ }
4719
4945
  const pendingKeys = (_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys;
4720
4946
  const shouldBatchPendingMeasurements = IsNewArchitecture && measurement.fromLayoutEffect && !!(pendingKeys == null ? void 0 : pendingKeys.size);
4947
+ const shouldQueueRecalculate = IsNewArchitecture && !!measurement.fromLayoutEffect;
4948
+ let result;
4721
4949
  if (!shouldBatchPendingMeasurements) {
4722
- flushItemSizeUpdates(ctx, applyItemSize(ctx, measurement.itemKey, measurement.size));
4950
+ result = applyItemSize(ctx, measurement.itemKey, measurement.size);
4723
4951
  } else {
4724
- const result = {};
4952
+ result = {};
4725
4953
  const updateContainerItemSize = (itemKey, containerId, size) => {
4726
4954
  if (containerId !== void 0 && peek$(ctx, `containerItemKey${containerId}`) === itemKey) {
4727
4955
  mergeItemSizeUpdateResult(result, applyItemSize(ctx, itemKey, size));
@@ -4739,8 +4967,15 @@ function updateItemSizes(ctx, measurement) {
4739
4967
  });
4740
4968
  }
4741
4969
  }
4970
+ }
4971
+ if (shouldQueueRecalculate && result.needsRecalculate) {
4972
+ queueItemSizeRecalculate(ctx, result);
4973
+ } else {
4742
4974
  flushItemSizeUpdates(ctx, result);
4743
4975
  }
4976
+ if (didDrainLayoutEffectMeasurements) {
4977
+ flushBatchedItemSizeRecalculate(ctx);
4978
+ }
4744
4979
  }
4745
4980
  function applyItemSize(ctx, itemKey, sizeObj) {
4746
4981
  var _a3;
@@ -6421,7 +6656,7 @@ var LegendList = typedMemo(
6421
6656
  })
6422
6657
  );
6423
6658
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
6424
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
6659
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
6425
6660
  const noopOnScroll = useCallback((_event) => {
6426
6661
  }, []);
6427
6662
  if (props.recycleItems === void 0) {
@@ -6438,6 +6673,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6438
6673
  contentContainerStyle: contentContainerStyleProp,
6439
6674
  contentInset,
6440
6675
  data: dataProp = [],
6676
+ dataKey,
6441
6677
  dataVersion,
6442
6678
  drawDistance = 250,
6443
6679
  contentInsetEndAdjustment,
@@ -6568,6 +6804,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6568
6804
  (_j = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _j.join(","),
6569
6805
  (_k = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _k.join(","),
6570
6806
  dataProp,
6807
+ dataKey,
6571
6808
  dataVersion,
6572
6809
  keyExtractor
6573
6810
  ]);
@@ -6590,6 +6827,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6590
6827
  dataChangeNeedsScrollUpdate: false,
6591
6828
  didColumnsChange: false,
6592
6829
  didDataChange: false,
6830
+ didLoad: false,
6593
6831
  enableScrollForNextCalculateItemsInView: true,
6594
6832
  endBuffered: -1,
6595
6833
  endNoBuffer: -1,
@@ -6619,6 +6857,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6619
6857
  positions: [],
6620
6858
  props: {},
6621
6859
  queuedCalculateItemsInView: 0,
6860
+ queuedFullDrawDistancePrewarm: void 0,
6622
6861
  refScroller: { current: null },
6623
6862
  scroll: 0,
6624
6863
  scrollAdjustHandler: new ScrollAdjustHandler(ctx),
@@ -6661,10 +6900,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6661
6900
  const didScrollAxisGapChange = !isFirstLocal && ctx.scrollAxisGap !== nextScrollAxisGap;
6662
6901
  ctx.scrollAxisGap = nextScrollAxisGap;
6663
6902
  state.didColumnsChange = numColumnsProp !== previousNumColumnsProp || didScrollAxisGapChange;
6903
+ const previousDataLength = (_n = (_m = state.props.data) == null ? void 0 : _m.length) != null ? _n : 0;
6664
6904
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
6905
+ const didDataKeyChangeLocal = state.props.dataKey !== dataKey;
6665
6906
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
6666
- const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
6667
- if (didDataChangeLocal && !initialScrollAtEnd && state.didFinishInitialScroll && ((_m = state.initialScroll) == null ? void 0 : _m.viewPosition) === 1 && state.props.data.length > 0) {
6907
+ const didDataChangeLocal = didDataKeyChangeLocal || didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
6908
+ const shouldResetFreshDataLayout = !isFirstLocal && didDataChangeLocal && state.hasHadNonEmptyData && (didDataKeyChangeLocal || previousDataLength === 0) && dataProp.length > 0;
6909
+ if (didDataChangeLocal && !initialScrollAtEnd && state.didFinishInitialScroll && ((_o = state.initialScroll) == null ? void 0 : _o.viewPosition) === 1 && state.props.data.length > 0) {
6668
6910
  clearPreservedInitialScrollTarget(state);
6669
6911
  }
6670
6912
  if (didDataChangeLocal) {
@@ -6676,7 +6918,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6676
6918
  const throttledOnScroll = useThrottledOnScroll(onScrollProp != null ? onScrollProp : noopOnScroll, scrollEventThrottle != null ? scrollEventThrottle : 0);
6677
6919
  const throttleScrollFn = scrollEventThrottle && onScrollProp ? throttledOnScroll : onScrollProp;
6678
6920
  const anchoredEndSpaceResolved = Platform.OS === "web" && anchoredEndSpace ? { ...anchoredEndSpace, includeInEndInset: true } : anchoredEndSpace;
6679
- const didAnchoredEndSpaceAnchorIndexChange = !isFirstLocal && !didDataChangeLocal && ((_n = state.props.anchoredEndSpace) == null ? void 0 : _n.anchorIndex) !== (anchoredEndSpaceResolved == null ? void 0 : anchoredEndSpaceResolved.anchorIndex);
6921
+ const didAnchoredEndSpaceAnchorIndexChange = !isFirstLocal && !didDataChangeLocal && ((_p = state.props.anchoredEndSpace) == null ? void 0 : _p.anchorIndex) !== (anchoredEndSpaceResolved == null ? void 0 : anchoredEndSpaceResolved.anchorIndex);
6680
6922
  state.props = {
6681
6923
  adaptiveRender: experimental_adaptiveRender,
6682
6924
  alignItemsAtEnd,
@@ -6690,6 +6932,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6690
6932
  contentInset,
6691
6933
  contentInsetEndAdjustment: contentInsetEndAdjustmentResolved,
6692
6934
  data: dataProp,
6935
+ dataKey,
6693
6936
  dataVersion,
6694
6937
  drawDistance,
6695
6938
  estimatedItemSize,
@@ -6730,13 +6973,19 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6730
6973
  if (!isFirstLocal && previousAdaptiveRender && !experimental_adaptiveRender) {
6731
6974
  resetAdaptiveRender(ctx);
6732
6975
  }
6976
+ if (shouldResetFreshDataLayout) {
6977
+ resetInitialRenderState(ctx, {
6978
+ resetInitialScroll: !!initialScrollProp,
6979
+ resetLayout: true
6980
+ });
6981
+ }
6733
6982
  const memoizedLastItemKeys = useMemo(() => {
6734
6983
  if (!dataProp.length) return [];
6735
6984
  return Array.from(
6736
6985
  { length: Math.min(numColumnsProp, dataProp.length) },
6737
6986
  (_, i) => getId(state, dataProp.length - 1 - i)
6738
6987
  );
6739
- }, [dataProp, dataVersion, numColumnsProp]);
6988
+ }, [dataProp, dataKey, dataVersion, numColumnsProp]);
6740
6989
  const initializeStateVars = (shouldAdjustPadding) => {
6741
6990
  set$(ctx, "lastItemKeys", memoizedLastItemKeys);
6742
6991
  set$(ctx, "numColumns", numColumnsProp);
@@ -6796,13 +7045,22 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6796
7045
  handleInitialScrollDataChange(ctx, {
6797
7046
  dataLength: dataProp.length,
6798
7047
  didDataChange: didDataChangeLocal,
7048
+ didStartFreshData: shouldResetFreshDataLayout,
6799
7049
  initialScrollAtEnd,
6800
7050
  latestInitialScroll: initialScrollProp,
6801
7051
  latestInitialScrollSessionKind: initialScrollUsesOffsetOnly ? "offset" : "bootstrap",
6802
7052
  stylePaddingBottom: stylePaddingBottomState,
6803
7053
  useBootstrapInitialScroll: usesBootstrapInitialScroll
6804
7054
  });
6805
- }, [dataProp.length, didDataChangeLocal, initialScrollAtEnd, stylePaddingBottomState, usesBootstrapInitialScroll]);
7055
+ }, [
7056
+ dataProp.length,
7057
+ dataKey,
7058
+ didDataChangeLocal,
7059
+ shouldResetFreshDataLayout,
7060
+ initialScrollAtEnd,
7061
+ stylePaddingBottomState,
7062
+ usesBootstrapInitialScroll
7063
+ ]);
6806
7064
  useLayoutEffect(() => {
6807
7065
  var _a4;
6808
7066
  if (didAnchoredEndSpaceAnchorIndexChange) {
@@ -6870,6 +7128,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6870
7128
  useLayoutEffect(
6871
7129
  () => initializeStateVars(true),
6872
7130
  [
7131
+ dataKey,
6873
7132
  dataVersion,
6874
7133
  memoizedLastItemKeys.join(","),
6875
7134
  numColumnsProp,
@@ -6896,7 +7155,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6896
7155
  state.didColumnsChange = false;
6897
7156
  state.didDataChange = false;
6898
7157
  state.isFirst = false;
6899
- }, [dataProp, dataVersion, numColumnsProp, nextScrollAxisGap]);
7158
+ }, [dataProp, dataKey, dataVersion, numColumnsProp, nextScrollAxisGap]);
6900
7159
  useLayoutEffect(() => {
6901
7160
  var _a4;
6902
7161
  set$(ctx, "extraData", extraData);
@@ -6949,6 +7208,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6949
7208
  useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx, scheduleImperativeScrollCommit), []);
6950
7209
  useEffect(() => {
6951
7210
  return () => {
7211
+ if (state.queuedFullDrawDistancePrewarm !== void 0) {
7212
+ cancelAnimationFrame(state.queuedFullDrawDistancePrewarm);
7213
+ state.queuedFullDrawDistancePrewarm = void 0;
7214
+ }
6952
7215
  for (const timeout of state.timeouts) {
6953
7216
  clearTimeout(timeout);
6954
7217
  }
@@ -7001,7 +7264,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7001
7264
  onScroll: onScrollHandler,
7002
7265
  recycleItems,
7003
7266
  refreshControl: refreshControlElement ? stylePaddingTopState > 0 ? React2.cloneElement(refreshControlElement, {
7004
- progressViewOffset: ((_o = refreshControlElement.props.progressViewOffset) != null ? _o : 0) + stylePaddingTopState
7267
+ progressViewOffset: ((_q = refreshControlElement.props.progressViewOffset) != null ? _q : 0) + stylePaddingTopState
7005
7268
  }) : refreshControlElement : onRefresh && /* @__PURE__ */ React2.createElement(
7006
7269
  RefreshControl,
7007
7270
  {
@@ -7012,7 +7275,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7012
7275
  ),
7013
7276
  refScrollView: combinedRef,
7014
7277
  renderScrollComponent,
7015
- scrollAdjustHandler: (_p = refState.current) == null ? void 0 : _p.scrollAdjustHandler,
7278
+ scrollAdjustHandler: (_r = refState.current) == null ? void 0 : _r.scrollAdjustHandler,
7016
7279
  scrollEventThrottle: 0,
7017
7280
  snapToIndices,
7018
7281
  stickyHeaderIndices,