@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.mjs CHANGED
@@ -1048,32 +1048,34 @@ function scheduleAdaptiveRenderExit(ctx, exitDelay) {
1048
1048
  const state = ctx.state;
1049
1049
  clearAdaptiveRenderExitTimeout(ctx);
1050
1050
  if (exitDelay <= 0) {
1051
- setAdaptiveRender(ctx, "normal");
1051
+ setAdaptiveRender(ctx, "normal", "scroll");
1052
1052
  } else {
1053
1053
  const timeout = setTimeout(() => {
1054
1054
  state.timeouts.delete(timeout);
1055
1055
  state.timeoutAdaptiveRender = void 0;
1056
- setAdaptiveRender(ctx, "normal");
1056
+ setAdaptiveRender(ctx, "normal", "scroll");
1057
1057
  }, exitDelay);
1058
1058
  state.timeoutAdaptiveRender = timeout;
1059
1059
  state.timeouts.add(timeout);
1060
1060
  }
1061
1061
  }
1062
- function setAdaptiveRender(ctx, mode) {
1062
+ function setAdaptiveRender(ctx, mode, reason) {
1063
1063
  var _a3, _b;
1064
1064
  const previousMode = peek$(ctx, "adaptiveRender");
1065
1065
  if (previousMode !== mode) {
1066
1066
  set$(ctx, "adaptiveRender", mode);
1067
- (_b = (_a3 = ctx.state.props.adaptiveRender) == null ? void 0 : _a3.onChange) == null ? void 0 : _b.call(_a3, mode);
1067
+ (_b = (_a3 = ctx.state.props.adaptiveRender) == null ? void 0 : _a3.onChange) == null ? void 0 : _b.call(_a3, mode, reason);
1068
1068
  }
1069
1069
  }
1070
1070
  function resetAdaptiveRender(ctx) {
1071
+ var _a3, _b;
1071
1072
  clearAdaptiveRenderExitTimeout(ctx);
1072
- if (peek$(ctx, "adaptiveRender") !== "normal") {
1073
- setAdaptiveRender(ctx, "normal");
1073
+ const mode = (_b = (_a3 = ctx.state.props.adaptiveRender) == null ? void 0 : _a3.initialMode) != null ? _b : "normal";
1074
+ if (peek$(ctx, "adaptiveRender") !== mode) {
1075
+ setAdaptiveRender(ctx, mode, "initial");
1074
1076
  }
1075
1077
  }
1076
- function updateAdaptiveRender(ctx, scrollVelocity) {
1078
+ function updateAdaptiveRender(ctx, scrollVelocity, options) {
1077
1079
  var _a3, _b, _c;
1078
1080
  const state = ctx.state;
1079
1081
  const adaptiveRender = state.props.adaptiveRender;
@@ -1084,11 +1086,11 @@ function updateAdaptiveRender(ctx, scrollVelocity) {
1084
1086
  const exitVelocity = (_b = adaptiveRender.exitVelocity) != null ? _b : DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_VELOCITY ;
1085
1087
  const exitDelay = (_c = adaptiveRender.exitDelay) != null ? _c : DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_DELAY ;
1086
1088
  const threshold = currentMode === "light" ? exitVelocity : enterVelocity;
1087
- const nextMode = Math.abs(scrollVelocity) > threshold ? "light" : "normal";
1089
+ const nextMode = (options == null ? void 0 : options.forceLight) || Math.abs(scrollVelocity) > threshold ? "light" : "normal";
1088
1090
  const previousMode = state.timeoutAdaptiveRender !== void 0 ? "normal" : currentMode;
1089
1091
  if (nextMode !== previousMode) {
1090
1092
  if (nextMode === "light") {
1091
- setAdaptiveRender(ctx, "light");
1093
+ setAdaptiveRender(ctx, "light", "scroll");
1092
1094
  scheduleAdaptiveRenderExit(ctx, exitDelay);
1093
1095
  } else if (currentMode === "light") {
1094
1096
  scheduleAdaptiveRenderExit(ctx, exitDelay);
@@ -1101,16 +1103,43 @@ function updateAdaptiveRender(ctx, scrollVelocity) {
1101
1103
  }
1102
1104
 
1103
1105
  // src/utils/getEffectiveDrawDistance.ts
1104
- var INITIAL_DRAW_DISTANCE = 100;
1105
- function getEffectiveDrawDistance(ctx) {
1106
+ var INITIAL_DRAW_DISTANCE = 50;
1107
+ function getEffectiveDrawDistance(ctx, mode) {
1106
1108
  var _a3;
1107
1109
  const drawDistance = ctx.state.props.drawDistance;
1108
1110
  const initialScroll = ctx.state.initialScroll;
1109
1111
  const needsFullInitialDrawDistance = initialScroll !== void 0 && ((_a3 = initialScroll.viewPosition) != null ? _a3 : 0) > 0;
1110
- return peek$(ctx, "readyToRender") || needsFullInitialDrawDistance ? drawDistance : Math.min(drawDistance, INITIAL_DRAW_DISTANCE);
1112
+ const shouldCapDrawDistance = mode === "visible-first" || mode !== "full" && !peek$(ctx, "readyToRender") && !needsFullInitialDrawDistance;
1113
+ return shouldCapDrawDistance ? Math.min(drawDistance, INITIAL_DRAW_DISTANCE) : drawDistance;
1114
+ }
1115
+ function scheduleFullDrawDistancePrewarm(ctx) {
1116
+ const { state } = ctx;
1117
+ if (state.props.drawDistance <= INITIAL_DRAW_DISTANCE || state.queuedFullDrawDistancePrewarm !== void 0) {
1118
+ return;
1119
+ }
1120
+ state.queuedFullDrawDistancePrewarm = requestAnimationFrame(() => {
1121
+ var _a3;
1122
+ state.queuedFullDrawDistancePrewarm = void 0;
1123
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
1124
+ });
1111
1125
  }
1112
1126
 
1113
1127
  // src/utils/setInitialRenderState.ts
1128
+ function resetInitialRenderState(ctx, {
1129
+ resetLayout,
1130
+ resetInitialScroll
1131
+ }) {
1132
+ const { state } = ctx;
1133
+ if (resetLayout) {
1134
+ state.didContainersLayout = false;
1135
+ state.queuedInitialLayout = false;
1136
+ }
1137
+ if (resetInitialScroll) {
1138
+ state.didFinishInitialScroll = false;
1139
+ }
1140
+ set$(ctx, "readyToRender", false);
1141
+ resetAdaptiveRender(ctx);
1142
+ }
1114
1143
  function setInitialRenderState(ctx, {
1115
1144
  didLayout,
1116
1145
  didInitialScroll
@@ -1129,15 +1158,15 @@ function setInitialRenderState(ctx, {
1129
1158
  const isReadyToRender = Boolean(state.didContainersLayout && state.didFinishInitialScroll);
1130
1159
  if (isReadyToRender && !peek$(ctx, "readyToRender")) {
1131
1160
  set$(ctx, "readyToRender", true);
1132
- setAdaptiveRender(ctx, "normal");
1161
+ setAdaptiveRender(ctx, "normal", "ready");
1133
1162
  if (state.props.drawDistance > INITIAL_DRAW_DISTANCE) {
1134
- requestAnimationFrame(() => {
1135
- var _a3;
1136
- (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
1137
- });
1163
+ scheduleFullDrawDistancePrewarm(ctx);
1138
1164
  }
1139
- if (onLoad) {
1140
- onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1165
+ if (!state.didLoad) {
1166
+ state.didLoad = true;
1167
+ if (onLoad) {
1168
+ onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1169
+ }
1141
1170
  }
1142
1171
  }
1143
1172
  }
@@ -1434,6 +1463,7 @@ function finishScrollTo(ctx) {
1434
1463
  const scrollingTo = state.scrollingTo;
1435
1464
  state.scrollHistory.length = 0;
1436
1465
  state.scrollingTo = void 0;
1466
+ state.scrollTargetPinnedRange = void 0;
1437
1467
  if (state.pendingTotalSize !== void 0) {
1438
1468
  addTotalSize(ctx, null, state.pendingTotalSize);
1439
1469
  }
@@ -1561,8 +1591,8 @@ function doMaintainScrollAtEnd(ctx) {
1561
1591
  state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
1562
1592
  return false;
1563
1593
  }
1564
- state.pendingMaintainScrollAtEnd = false;
1565
1594
  if (shouldMaintainScrollAtEnd) {
1595
+ state.pendingMaintainScrollAtEnd = false;
1566
1596
  const contentSize = getContentSize(ctx);
1567
1597
  if (contentSize < state.scrollLength) {
1568
1598
  state.scroll = 0;
@@ -1593,6 +1623,9 @@ function doMaintainScrollAtEnd(ctx) {
1593
1623
  () => {
1594
1624
  if (state.maintainingScrollAtEnd === activeState) {
1595
1625
  state.maintainingScrollAtEnd = void 0;
1626
+ if (state.pendingMaintainScrollAtEnd) {
1627
+ doMaintainScrollAtEnd(ctx);
1628
+ }
1596
1629
  }
1597
1630
  },
1598
1631
  maintainScrollAtEnd.animated ? 500 : 0
@@ -1601,9 +1634,12 @@ function doMaintainScrollAtEnd(ctx) {
1601
1634
  state.maintainingScrollAtEnd = void 0;
1602
1635
  }
1603
1636
  });
1637
+ } else {
1638
+ state.pendingMaintainScrollAtEnd = true;
1604
1639
  }
1605
1640
  return true;
1606
1641
  }
1642
+ state.pendingMaintainScrollAtEnd = false;
1607
1643
  return false;
1608
1644
  }
1609
1645
 
@@ -1921,6 +1957,8 @@ function prepareMVCP(ctx, dataChanged) {
1921
1957
  }
1922
1958
 
1923
1959
  // src/utils/getScrollVelocity.ts
1960
+ var MAX_SCROLL_VELOCITY_WINDOW_MS = 1e3;
1961
+ var SCROLL_VELOCITY_HALF_LIFE_MS = 200;
1924
1962
  var getScrollVelocity = (state) => {
1925
1963
  const { scrollHistory } = state;
1926
1964
  const newestIndex = scrollHistory.length - 1;
@@ -1928,35 +1966,37 @@ var getScrollVelocity = (state) => {
1928
1966
  return 0;
1929
1967
  }
1930
1968
  const newest = scrollHistory[newestIndex];
1931
- const now = Date.now();
1932
- let direction = 0;
1933
- for (let i = newestIndex; i > 0; i--) {
1934
- const delta = scrollHistory[i].scroll - scrollHistory[i - 1].scroll;
1935
- if (delta !== 0) {
1936
- direction = Math.sign(delta);
1937
- break;
1938
- }
1939
- }
1940
- if (direction === 0) {
1969
+ if (Date.now() - newest.time > MAX_SCROLL_VELOCITY_WINDOW_MS) {
1941
1970
  return 0;
1942
1971
  }
1943
- let oldest = newest;
1944
- for (let i = newestIndex - 1; i >= 0; i--) {
1972
+ let direction = 0;
1973
+ let weightedVelocity = 0;
1974
+ let totalWeight = 0;
1975
+ for (let i = newestIndex; i > 0; i--) {
1945
1976
  const current = scrollHistory[i];
1946
- const next = scrollHistory[i + 1];
1947
- const delta = next.scroll - current.scroll;
1948
- const deltaSign = Math.sign(delta);
1949
- if (deltaSign !== 0 && deltaSign !== direction) {
1950
- break;
1977
+ const previous = scrollHistory[i - 1];
1978
+ const scrollDiff = current.scroll - previous.scroll;
1979
+ const timeDiff = current.time - previous.time;
1980
+ const deltaSign = Math.sign(scrollDiff);
1981
+ if (deltaSign !== 0) {
1982
+ if (direction === 0) {
1983
+ direction = deltaSign;
1984
+ } else if (deltaSign !== direction) {
1985
+ break;
1986
+ }
1951
1987
  }
1952
- if (now - current.time > 1e3) {
1988
+ if (newest.time - previous.time > MAX_SCROLL_VELOCITY_WINDOW_MS) {
1953
1989
  break;
1954
1990
  }
1955
- oldest = current;
1991
+ if (scrollDiff === 0 || timeDiff <= 0) {
1992
+ continue;
1993
+ }
1994
+ const age = newest.time - current.time;
1995
+ const weight = Math.exp(-age / SCROLL_VELOCITY_HALF_LIFE_MS);
1996
+ weightedVelocity += scrollDiff / timeDiff * weight;
1997
+ totalWeight += weight;
1956
1998
  }
1957
- const scrollDiff = newest.scroll - oldest.scroll;
1958
- const timeDiff = newest.time - oldest.time;
1959
- return timeDiff > 0 ? scrollDiff / timeDiff : 0;
1999
+ return totalWeight > 0 ? weightedVelocity / totalWeight : 0;
1960
2000
  };
1961
2001
 
1962
2002
  // src/core/updateScroll.ts
@@ -1984,8 +2024,6 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
1984
2024
  if (scrollHistory.length > 5) {
1985
2025
  scrollHistory.shift();
1986
2026
  }
1987
- const scrollVelocity = getScrollVelocity(state);
1988
- updateAdaptiveRender(ctx, scrollVelocity);
1989
2027
  if (ignoreScrollFromMVCP && !scrollingTo) {
1990
2028
  const { lt, gt } = ignoreScrollFromMVCP;
1991
2029
  if (lt && newScroll < lt || gt && newScroll > gt) {
@@ -2000,6 +2038,9 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2000
2038
  const scrollDelta = Math.abs(newScroll - prevScroll);
2001
2039
  const didResolvePendingNativeMVCPAdjust = resolvePendingNativeMVCPAdjust(ctx, newScroll);
2002
2040
  const scrollLength = state.scrollLength;
2041
+ const isLargeUserScrollJump = scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength && !state.pendingNativeMVCPAdjust;
2042
+ const scrollVelocity = getScrollVelocity(state);
2043
+ updateAdaptiveRender(ctx, scrollVelocity, { forceLight: isLargeUserScrollJump });
2003
2044
  const lastCalculated = state.scrollLastCalculate;
2004
2045
  const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
2005
2046
  const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
@@ -2009,10 +2050,17 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2009
2050
  state.lastScrollDelta = scrollDelta;
2010
2051
  const runCalculateItems = () => {
2011
2052
  var _a4;
2012
- (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { doMVCP: scrollingTo !== void 0, scrollVelocity });
2053
+ const calculateItemsParams = {
2054
+ doMVCP: scrollingTo !== void 0,
2055
+ scrollVelocity
2056
+ };
2057
+ if (isLargeUserScrollJump) {
2058
+ calculateItemsParams.drawDistanceMode = "visible-first";
2059
+ }
2060
+ (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, calculateItemsParams);
2013
2061
  checkThresholds(ctx);
2014
2062
  };
2015
- if (scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength && !state.pendingNativeMVCPAdjust) {
2063
+ if (isLargeUserScrollJump) {
2016
2064
  state.mvcpAnchorLock = void 0;
2017
2065
  state.pendingNativeMVCPAdjust = void 0;
2018
2066
  state.userScrollAnchorReset = { keys: /* @__PURE__ */ new Set() };
@@ -2021,6 +2069,7 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2021
2069
  state.queuedMVCPRecalculate = void 0;
2022
2070
  }
2023
2071
  flushSync(runCalculateItems);
2072
+ scheduleFullDrawDistancePrewarm(ctx);
2024
2073
  } else {
2025
2074
  runCalculateItems();
2026
2075
  }
@@ -2064,8 +2113,93 @@ function syncInitialScrollNativeWatchdog(state, options) {
2064
2113
  initialScrollWatchdog.clear(state);
2065
2114
  }
2066
2115
  }
2067
- function scrollTo(ctx, params) {
2116
+ function findPositionIndexAtOrBeforeOffset(ctx, offset) {
2117
+ const state = ctx.state;
2118
+ const dataLength = state.props.data.length;
2119
+ let low = 0;
2120
+ let high = dataLength - 1;
2121
+ let match;
2122
+ while (low <= high) {
2123
+ const mid = Math.floor((low + high) / 2);
2124
+ const top = state.positions[mid];
2125
+ if (top === void 0) {
2126
+ high = mid - 1;
2127
+ } else {
2128
+ if (top <= offset) {
2129
+ match = mid;
2130
+ low = mid + 1;
2131
+ } else {
2132
+ high = mid - 1;
2133
+ }
2134
+ }
2135
+ }
2136
+ return match;
2137
+ }
2138
+ function getItemBottom(ctx, index) {
2068
2139
  var _a3;
2140
+ const top = ctx.state.positions[index];
2141
+ if (top === void 0) {
2142
+ return void 0;
2143
+ }
2144
+ const itemSize = (_a3 = getItemSizeAtIndex(ctx, index)) != null ? _a3 : 0;
2145
+ return top + (Number.isFinite(itemSize) ? itemSize : 0);
2146
+ }
2147
+ function getTargetViewportRenderRange(ctx, targetOffset, targetIndex) {
2148
+ const state = ctx.state;
2149
+ const dataLength = state.props.data.length;
2150
+ if (dataLength === 0) {
2151
+ return void 0;
2152
+ }
2153
+ const viewportStart = Math.max(0, targetOffset);
2154
+ const viewportEnd = Math.max(viewportStart, targetOffset + state.scrollLength);
2155
+ let start = targetIndex !== void 0 ? Math.max(0, Math.min(dataLength - 1, targetIndex)) : findPositionIndexAtOrBeforeOffset(ctx, viewportStart);
2156
+ if (start === void 0) {
2157
+ return void 0;
2158
+ }
2159
+ if (targetIndex !== void 0 && state.positions[start] === void 0) {
2160
+ return { end: start, start };
2161
+ }
2162
+ if (targetIndex === void 0) {
2163
+ const startBottom = getItemBottom(ctx, start);
2164
+ if (startBottom === void 0 || startBottom <= viewportStart) {
2165
+ return void 0;
2166
+ }
2167
+ }
2168
+ while (start > 0) {
2169
+ const top = state.positions[start];
2170
+ if (top === void 0 || top <= viewportStart || state.positions[start - 1] === void 0) {
2171
+ break;
2172
+ }
2173
+ start--;
2174
+ }
2175
+ while (start > 0) {
2176
+ const previousBottom = getItemBottom(ctx, start - 1);
2177
+ if (previousBottom === void 0 || previousBottom <= viewportStart) {
2178
+ break;
2179
+ }
2180
+ start--;
2181
+ }
2182
+ let end = start;
2183
+ while (end + 1 < dataLength) {
2184
+ const nextTop = state.positions[end + 1];
2185
+ if (nextTop === void 0 || nextTop > viewportEnd) {
2186
+ break;
2187
+ }
2188
+ end++;
2189
+ }
2190
+ return { end, start };
2191
+ }
2192
+ function pinScrollTargetRenderRange(ctx, targetOffset, targetIndex) {
2193
+ const range = getTargetViewportRenderRange(ctx, targetOffset, targetIndex);
2194
+ if (range) {
2195
+ ctx.state.scrollTargetPinnedRange = range;
2196
+ ctx.state.scrollForNextCalculateItemsInView = void 0;
2197
+ } else {
2198
+ ctx.state.scrollTargetPinnedRange = void 0;
2199
+ }
2200
+ }
2201
+ function scrollTo(ctx, params) {
2202
+ var _a3, _b;
2069
2203
  const state = ctx.state;
2070
2204
  const { noScrollingTo, forceScroll, ...scrollTarget } = params;
2071
2205
  const {
@@ -2100,11 +2234,20 @@ function scrollTo(ctx, params) {
2100
2234
  targetOffset,
2101
2235
  waitForInitialScrollCompletionFrame
2102
2236
  };
2237
+ if (!isInitialScroll) {
2238
+ pinScrollTargetRenderRange(ctx, targetOffset, scrollTarget.index);
2239
+ }
2103
2240
  }
2104
2241
  state.scrollPending = targetOffset;
2105
2242
  syncInitialScrollNativeWatchdog(state, { isInitialScroll, requestedOffset: offset, targetOffset });
2106
- if (!animated && !isInitialScroll && !noScrollingTo && Math.abs(state.scroll - targetOffset) > 1) {
2107
- updateScroll(ctx, targetOffset, false, { markHasScrolled: false });
2243
+ if (!isInitialScroll && !noScrollingTo && Math.abs(state.scroll - targetOffset) > 1) {
2244
+ if (animated) {
2245
+ if (state.scrollTargetPinnedRange) {
2246
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state);
2247
+ }
2248
+ } else {
2249
+ updateScroll(ctx, targetOffset, true, { markHasScrolled: false });
2250
+ }
2108
2251
  }
2109
2252
  if (forceScroll || !isInitialScroll || Platform.OS === "android") {
2110
2253
  doScrollTo(ctx, { animated, horizontal, offset });
@@ -2175,16 +2318,17 @@ function dispatchInitialScroll(ctx, params) {
2175
2318
  waitForInitialScrollCompletionFrame: waitForCompletionFrame
2176
2319
  });
2177
2320
  }
2178
- function setInitialScrollTarget(state, target, options) {
2321
+ function setInitialScrollTarget(ctx, target, options) {
2179
2322
  var _a3;
2323
+ const { state } = ctx;
2180
2324
  state.clearPreservedInitialScrollOnNextFinish = void 0;
2181
2325
  if (state.timeoutPreservedInitialScrollClear !== void 0) {
2182
2326
  clearTimeout(state.timeoutPreservedInitialScrollClear);
2183
2327
  state.timeoutPreservedInitialScrollClear = void 0;
2184
2328
  }
2185
2329
  state.initialScroll = target;
2186
- if ((options == null ? void 0 : options.resetDidFinish) && state.didFinishInitialScroll) {
2187
- state.didFinishInitialScroll = false;
2330
+ if (options == null ? void 0 : options.resetDidFinish) {
2331
+ resetInitialRenderState(ctx, { resetInitialScroll: true });
2188
2332
  }
2189
2333
  setInitialScrollSession(state, {
2190
2334
  kind: ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" ? "offset" : "bootstrap"
@@ -2238,7 +2382,7 @@ function advanceMeasuredInitialScroll(ctx, options) {
2238
2382
  return false;
2239
2383
  }
2240
2384
  if (didOffsetChange && ((_b = state.initialScrollSession) == null ? void 0 : _b.kind) !== "offset") {
2241
- setInitialScrollTarget(state, { ...initialScroll, contentOffset: resolvedOffset });
2385
+ setInitialScrollTarget(ctx, { ...initialScroll, contentOffset: resolvedOffset });
2242
2386
  }
2243
2387
  const forceScroll = (_c = options == null ? void 0 : options.forceScroll) != null ? _c : !!queuedInitialLayout || isInitialScrollInProgress && didOffsetChange;
2244
2388
  dispatchInitialScroll(ctx, {
@@ -2511,7 +2655,6 @@ function areEquivalentBootstrapInitialScrollTargets(current, next) {
2511
2655
  }
2512
2656
  function clearPendingInitialScrollFooterLayout(ctx, options) {
2513
2657
  const { dataLength, stylePaddingBottom, target } = options;
2514
- const state = ctx.state;
2515
2658
  if (!shouldPreserveInitialScrollForFooterLayout(target)) {
2516
2659
  return;
2517
2660
  }
@@ -2521,7 +2664,7 @@ function clearPendingInitialScrollFooterLayout(ctx, options) {
2521
2664
  preserveForFooterLayout: void 0,
2522
2665
  stylePaddingBottom
2523
2666
  });
2524
- setInitialScrollTarget(state, clearedFooterTarget);
2667
+ setInitialScrollTarget(ctx, clearedFooterTarget);
2525
2668
  }
2526
2669
  function clearFinishedViewportRetargetableInitialScroll(state) {
2527
2670
  clearPreservedInitialScrollTarget(state);
@@ -2668,7 +2811,7 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
2668
2811
  return;
2669
2812
  }
2670
2813
  if (!areEquivalentBootstrapInitialScrollTargets(initialScroll, updatedInitialScroll) || !!bootstrapInitialScroll || shouldResetDidFinish || didDataChange) {
2671
- setInitialScrollTarget(state, updatedInitialScroll, {
2814
+ setInitialScrollTarget(ctx, updatedInitialScroll, {
2672
2815
  resetDidFinish: shouldResetDidFinish
2673
2816
  });
2674
2817
  rearmBootstrapInitialScroll(ctx, {
@@ -2683,7 +2826,7 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
2683
2826
  return;
2684
2827
  }
2685
2828
  if (bootstrapInitialScroll || shouldResetDidFinish) {
2686
- setInitialScrollTarget(state, initialScroll, {
2829
+ setInitialScrollTarget(ctx, initialScroll, {
2687
2830
  resetDidFinish: shouldResetDidFinish
2688
2831
  });
2689
2832
  rearmBootstrapInitialScroll(ctx, {
@@ -2729,7 +2872,7 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2729
2872
  });
2730
2873
  } else {
2731
2874
  const didFinishInitialScroll = !!state.didFinishInitialScroll;
2732
- setInitialScrollTarget(state, updatedInitialScroll, {
2875
+ setInitialScrollTarget(ctx, updatedInitialScroll, {
2733
2876
  resetDidFinish: didFinishInitialScroll
2734
2877
  });
2735
2878
  rearmBootstrapInitialScroll(ctx, {
@@ -3063,7 +3206,7 @@ function initializeInitialScrollOnMount(ctx, options) {
3063
3206
  const resolvedInitialContentOffset = initialContentOffset != null ? initialContentOffset : 0;
3064
3207
  const preserveForFooterLayout = useBootstrapInitialScroll && initialScrollAtEnd && hasFooterComponent;
3065
3208
  if (initialScroll && (initialScroll.contentOffset === void 0 || !!initialScroll.preserveForFooterLayout !== preserveForFooterLayout && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) !== "offset")) {
3066
- setInitialScrollTarget(state, {
3209
+ setInitialScrollTarget(ctx, {
3067
3210
  ...initialScroll,
3068
3211
  contentOffset: resolvedInitialContentOffset,
3069
3212
  preserveForFooterLayout
@@ -3092,6 +3235,7 @@ function handleInitialScrollDataChange(ctx, options) {
3092
3235
  const {
3093
3236
  dataLength,
3094
3237
  didDataChange,
3238
+ didStartFreshData,
3095
3239
  initialScrollAtEnd,
3096
3240
  latestInitialScroll,
3097
3241
  latestInitialScrollSessionKind,
@@ -3099,17 +3243,17 @@ function handleInitialScrollDataChange(ctx, options) {
3099
3243
  useBootstrapInitialScroll
3100
3244
  } = options;
3101
3245
  const state = ctx.state;
3102
- const previousDataLength = (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.previousDataLength) != null ? _b : 0;
3103
- const isFirstNonEmptyData = !state.hasHadNonEmptyData && dataLength > 0;
3246
+ const previousInitialScrollDataLength = (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.previousDataLength) != null ? _b : 0;
3247
+ const shouldUseLatestInitialScroll = dataLength > 0 && (!state.hasHadNonEmptyData || didStartFreshData);
3104
3248
  if (dataLength > 0) {
3105
3249
  state.hasHadNonEmptyData = true;
3106
3250
  }
3107
- if (isFirstNonEmptyData) {
3251
+ if (shouldUseLatestInitialScroll) {
3108
3252
  if (latestInitialScroll) {
3109
- setInitialScrollTarget(state, latestInitialScroll);
3253
+ setInitialScrollTarget(ctx, latestInitialScroll);
3110
3254
  setInitialScrollSession(state, {
3111
3255
  kind: latestInitialScrollSessionKind,
3112
- previousDataLength
3256
+ previousDataLength: previousInitialScrollDataLength
3113
3257
  });
3114
3258
  } else {
3115
3259
  clearPreservedInitialScrollTarget(state);
@@ -3124,17 +3268,17 @@ function handleInitialScrollDataChange(ctx, options) {
3124
3268
  dataLength,
3125
3269
  didDataChange,
3126
3270
  initialScrollAtEnd,
3127
- previousDataLength,
3271
+ previousDataLength: previousInitialScrollDataLength,
3128
3272
  stylePaddingBottom
3129
3273
  });
3130
3274
  return;
3131
3275
  }
3132
- const shouldReplayFinishedOffsetInitialScroll = previousDataLength === 0 && dataLength > 0 && !!state.initialScroll && ((_c = ctx.state.initialScrollSession) == null ? void 0 : _c.kind) === "offset" && !!state.didFinishInitialScroll;
3133
- if (previousDataLength !== 0 || dataLength === 0 || !state.initialScroll || !state.queuedInitialLayout || state.didFinishInitialScroll && !shouldReplayFinishedOffsetInitialScroll) {
3276
+ const shouldReplayFinishedOffsetInitialScroll = previousInitialScrollDataLength === 0 && dataLength > 0 && !!state.initialScroll && ((_c = ctx.state.initialScrollSession) == null ? void 0 : _c.kind) === "offset" && !!state.didFinishInitialScroll;
3277
+ if (previousInitialScrollDataLength !== 0 || dataLength === 0 || !state.initialScroll || !state.queuedInitialLayout || state.didFinishInitialScroll && !shouldReplayFinishedOffsetInitialScroll) {
3134
3278
  return;
3135
3279
  }
3136
3280
  if (shouldReplayFinishedOffsetInitialScroll) {
3137
- state.didFinishInitialScroll = false;
3281
+ resetInitialRenderState(ctx, { resetInitialScroll: true });
3138
3282
  }
3139
3283
  advanceCurrentInitialScrollSession(ctx);
3140
3284
  }
@@ -3920,6 +4064,32 @@ function setDidLayout(ctx) {
3920
4064
  }
3921
4065
 
3922
4066
  // src/core/calculateItemsInView.ts
4067
+ var RENDER_RANGE_PROJECTION_FULL_VELOCITY = 4;
4068
+ var RENDER_RANGE_PROJECTION_SETTLE_DELAY = 100;
4069
+ function getProjectedBufferAdjustment(scrollVelocity, trailingBuffer) {
4070
+ if (trailingBuffer <= 0) {
4071
+ return 0;
4072
+ }
4073
+ const velocityProgress = Math.min(1, Math.abs(scrollVelocity) / RENDER_RANGE_PROJECTION_FULL_VELOCITY);
4074
+ return Math.sign(scrollVelocity) * trailingBuffer * velocityProgress;
4075
+ }
4076
+ function scheduleRenderRangeProjectionSettle(ctx) {
4077
+ const state = ctx.state;
4078
+ const previousTimeout = state.timeoutRenderRangeProjectionSettle;
4079
+ if (previousTimeout !== void 0) {
4080
+ clearTimeout(previousTimeout);
4081
+ state.timeouts.delete(previousTimeout);
4082
+ }
4083
+ const timeout = setTimeout(() => {
4084
+ var _a3;
4085
+ state.timeoutRenderRangeProjectionSettle = void 0;
4086
+ state.timeouts.delete(timeout);
4087
+ state.scrollHistory.length = 0;
4088
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
4089
+ }, RENDER_RANGE_PROJECTION_SETTLE_DELAY);
4090
+ state.timeoutRenderRangeProjectionSettle = timeout;
4091
+ state.timeouts.add(timeout);
4092
+ }
3923
4093
  function findCurrentStickyIndex(stickyArray, scroll, state) {
3924
4094
  const positions = state.positions;
3925
4095
  for (let i = stickyArray.length - 1; i >= 0; i--) {
@@ -3960,14 +4130,14 @@ function handleStickyActivation(ctx, stickyArray, currentStickyIdx, needNewConta
3960
4130
  }
3961
4131
  }
3962
4132
  }
3963
- function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentStickyIdx, pendingRemoval, alwaysRenderIndicesSet) {
4133
+ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentStickyIdx, pendingRemoval, isPinnedRenderIndex) {
3964
4134
  var _a3, _b;
3965
4135
  const state = ctx.state;
3966
4136
  for (const containerIndex of state.stickyContainerPool) {
3967
4137
  const itemKey = peek$(ctx, `containerItemKey${containerIndex}`);
3968
4138
  const itemIndex = itemKey ? state.indexByKey.get(itemKey) : void 0;
3969
4139
  if (itemIndex === void 0) continue;
3970
- if (alwaysRenderIndicesSet.has(itemIndex)) continue;
4140
+ if (isPinnedRenderIndex(itemIndex)) continue;
3971
4141
  const arrayIdx = stickyArray.indexOf(itemIndex);
3972
4142
  if (arrayIdx === -1) {
3973
4143
  state.stickyContainerPool.delete(containerIndex);
@@ -4131,9 +4301,7 @@ function calculateItemsInView(ctx, params = {}) {
4131
4301
  const { data } = state.props;
4132
4302
  const stickyHeaderIndicesArr = state.props.stickyHeaderIndicesArr || [];
4133
4303
  const stickyHeaderIndicesSet = state.props.stickyHeaderIndicesSet || /* @__PURE__ */ new Set();
4134
- const alwaysRenderArr = alwaysRenderIndicesArr || [];
4135
- const alwaysRenderSet = alwaysRenderIndicesSet || /* @__PURE__ */ new Set();
4136
- const drawDistance = getEffectiveDrawDistance(ctx);
4304
+ const drawDistance = getEffectiveDrawDistance(ctx, params.drawDistanceMode);
4137
4305
  const { dataChanged, doMVCP, forceFullItemPositions } = params;
4138
4306
  const bootstrapInitialScrollState = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" ? state.initialScrollSession.bootstrap : void 0;
4139
4307
  const suppressInitialScrollSideEffects = !!bootstrapInitialScrollState;
@@ -4199,14 +4367,19 @@ function calculateItemsInView(ctx, params = {}) {
4199
4367
  scrollBufferTop = drawDistance * 1.5;
4200
4368
  scrollBufferBottom = drawDistance * 0.5;
4201
4369
  }
4370
+ const shouldProjectRenderRange = !dataChanged && !forceFullItemPositions && !suppressInitialScrollSideEffects && !hasActiveInitialScroll(state) && !state.scrollingTo && !state.pendingNativeMVCPAdjust && !!peek$(ctx, "readyToRender");
4371
+ const projectedBufferAdjustment = shouldProjectRenderRange ? getProjectedBufferAdjustment(speed, Math.min(scrollBufferTop, scrollBufferBottom)) : 0;
4202
4372
  const updateScrollRange = () => {
4203
4373
  const scrollStart = Math.max(0, scroll);
4204
4374
  const overscrollBeforeContent = Math.max(0, -nativeScrollState);
4205
- scrollTopBuffered = scrollStart - scrollBufferTop;
4206
4375
  scrollBottom = Math.max(scrollStart, scroll + scrollLength + overscrollBeforeContent);
4207
- scrollBottomBuffered = scrollBottom + scrollBufferBottom;
4376
+ scrollTopBuffered = scrollStart - scrollBufferTop + projectedBufferAdjustment;
4377
+ scrollBottomBuffered = scrollBottom + scrollBufferBottom + projectedBufferAdjustment;
4208
4378
  };
4209
4379
  updateScrollRange();
4380
+ if (projectedBufferAdjustment !== 0) {
4381
+ scheduleRenderRangeProjectionSettle(ctx);
4382
+ }
4210
4383
  if (enableScrollForNextCalculateItemsInView && !suppressInitialScrollSideEffects && !dataChanged && !forceFullItemPositions && scrollForNextCalculateItemsInView) {
4211
4384
  const { top, bottom } = scrollForNextCalculateItemsInView;
4212
4385
  if (top === null && bottom === null) {
@@ -4364,9 +4537,34 @@ function calculateItemsInView(ctx, params = {}) {
4364
4537
  }
4365
4538
  }
4366
4539
  }
4540
+ const scrollTargetPinnedRange = state.scrollTargetPinnedRange;
4541
+ let scrollTargetPinnedStart = 0;
4542
+ let scrollTargetPinnedEnd = -1;
4543
+ if (scrollTargetPinnedRange) {
4544
+ scrollTargetPinnedStart = Math.max(0, Math.min(scrollTargetPinnedRange.start, scrollTargetPinnedRange.end));
4545
+ scrollTargetPinnedEnd = Math.min(
4546
+ dataLength - 1,
4547
+ Math.max(scrollTargetPinnedRange.start, scrollTargetPinnedRange.end)
4548
+ );
4549
+ }
4550
+ const hasScrollTargetPinnedRange = scrollTargetPinnedStart <= scrollTargetPinnedEnd;
4551
+ const isPinnedRenderIndex = (index) => alwaysRenderIndicesSet.has(index) || hasScrollTargetPinnedRange && index >= scrollTargetPinnedStart && index <= scrollTargetPinnedEnd;
4367
4552
  if (startBuffered !== null && endBuffered !== null) {
4368
4553
  const needNewContainers = [];
4369
4554
  const needNewContainersSet = /* @__PURE__ */ new Set();
4555
+ const addPinnedIndex = (index) => {
4556
+ var _a4;
4557
+ if (index >= 0 && index < dataLength) {
4558
+ const id = (_a4 = idCache[index]) != null ? _a4 : getId(state, index);
4559
+ const containerIndex = containerItemKeys.get(id);
4560
+ if (containerIndex !== void 0) {
4561
+ state.stickyContainerPool.add(containerIndex);
4562
+ } else if (!isNullOrUndefined(id) && !needNewContainersSet.has(index)) {
4563
+ needNewContainersSet.add(index);
4564
+ needNewContainers.push(index);
4565
+ }
4566
+ }
4567
+ };
4370
4568
  for (let i = startBuffered; i <= endBuffered; i++) {
4371
4569
  const id = (_m = idCache[i]) != null ? _m : getId(state, i);
4372
4570
  if (!containerItemKeys.has(id)) {
@@ -4374,21 +4572,19 @@ function calculateItemsInView(ctx, params = {}) {
4374
4572
  needNewContainers.push(i);
4375
4573
  }
4376
4574
  }
4377
- if (alwaysRenderArr.length > 0) {
4378
- for (const index of alwaysRenderArr) {
4379
- if (index < 0 || index >= dataLength) continue;
4380
- const id = (_n = idCache[index]) != null ? _n : getId(state, index);
4381
- if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
4382
- needNewContainersSet.add(index);
4383
- needNewContainers.push(index);
4384
- }
4575
+ for (const index of alwaysRenderIndicesArr) {
4576
+ addPinnedIndex(index);
4577
+ }
4578
+ if (hasScrollTargetPinnedRange) {
4579
+ for (let index = scrollTargetPinnedStart; index <= scrollTargetPinnedEnd; index++) {
4580
+ addPinnedIndex(index);
4385
4581
  }
4386
4582
  }
4387
4583
  if (stickyHeaderIndicesArr.length > 0) {
4388
4584
  handleStickyActivation(
4389
4585
  ctx,
4390
4586
  stickyHeaderIndicesArr,
4391
- (_o = stickyState == null ? void 0 : stickyState.currentStickyIdx) != null ? _o : -1,
4587
+ (_n = stickyState == null ? void 0 : stickyState.currentStickyIdx) != null ? _n : -1,
4392
4588
  needNewContainers,
4393
4589
  needNewContainersSet,
4394
4590
  startBuffered,
@@ -4414,7 +4610,7 @@ function calculateItemsInView(ctx, params = {}) {
4414
4610
  for (const allocation of availableContainerAllocations) {
4415
4611
  const i = allocation.itemIndex;
4416
4612
  const containerIndex = allocation.containerIndex;
4417
- const id = (_p = idCache[i]) != null ? _p : getId(state, i);
4613
+ const id = (_o = idCache[i]) != null ? _o : getId(state, i);
4418
4614
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
4419
4615
  if (oldKey && oldKey !== id) {
4420
4616
  containerItemKeys.delete(oldKey);
@@ -4425,10 +4621,14 @@ function calculateItemsInView(ctx, params = {}) {
4425
4621
  state.containerItemTypes.set(containerIndex, allocation.itemType);
4426
4622
  }
4427
4623
  containerItemKeys.set(id, containerIndex);
4428
- (_q = state.userScrollAnchorReset) == null ? void 0 : _q.keys.add(id);
4624
+ (_p = state.userScrollAnchorReset) == null ? void 0 : _p.keys.add(id);
4625
+ {
4626
+ (_q = state.pendingLayoutEffectMeasurements) != null ? _q : state.pendingLayoutEffectMeasurements = /* @__PURE__ */ new Set();
4627
+ state.pendingLayoutEffectMeasurements.add(id);
4628
+ }
4429
4629
  const containerSticky = `containerSticky${containerIndex}`;
4430
4630
  const isSticky = stickyHeaderIndicesSet.has(i);
4431
- const isAlwaysRender = alwaysRenderSet.has(i);
4631
+ const isPinnedRender = isPinnedRenderIndex(i);
4432
4632
  if (isSticky) {
4433
4633
  set$(ctx, containerSticky, true);
4434
4634
  state.stickyContainerPool.add(containerIndex);
@@ -4436,9 +4636,9 @@ function calculateItemsInView(ctx, params = {}) {
4436
4636
  if (peek$(ctx, containerSticky)) {
4437
4637
  set$(ctx, containerSticky, false);
4438
4638
  }
4439
- if (isAlwaysRender) {
4639
+ if (isPinnedRender) {
4440
4640
  state.stickyContainerPool.add(containerIndex);
4441
- } else if (state.stickyContainerPool.has(containerIndex)) {
4641
+ } else {
4442
4642
  state.stickyContainerPool.delete(containerIndex);
4443
4643
  }
4444
4644
  }
@@ -4453,20 +4653,8 @@ function calculateItemsInView(ctx, params = {}) {
4453
4653
  }
4454
4654
  }
4455
4655
  }
4456
- if (state.userScrollAnchorReset) {
4457
- if (state.userScrollAnchorReset.keys.size === 0) {
4458
- state.userScrollAnchorReset = void 0;
4459
- }
4460
- }
4461
- if (alwaysRenderArr.length > 0) {
4462
- for (const index of alwaysRenderArr) {
4463
- if (index < 0 || index >= dataLength) continue;
4464
- const id = (_r = idCache[index]) != null ? _r : getId(state, index);
4465
- const containerIndex = containerItemKeys.get(id);
4466
- if (containerIndex !== void 0) {
4467
- state.stickyContainerPool.add(containerIndex);
4468
- }
4469
- }
4656
+ if (((_r = state.userScrollAnchorReset) == null ? void 0 : _r.keys.size) === 0) {
4657
+ state.userScrollAnchorReset = void 0;
4470
4658
  }
4471
4659
  }
4472
4660
  if (state.stickyContainerPool.size > 0) {
@@ -4477,7 +4665,7 @@ function calculateItemsInView(ctx, params = {}) {
4477
4665
  drawDistance,
4478
4666
  (_s = stickyState == null ? void 0 : stickyState.currentStickyIdx) != null ? _s : -1,
4479
4667
  pendingRemoval,
4480
- alwaysRenderSet
4668
+ isPinnedRenderIndex
4481
4669
  );
4482
4670
  }
4483
4671
  const pendingRemovalSet = pendingRemoval.length > 0 ? new Set(pendingRemoval) : void 0;
@@ -4636,6 +4824,36 @@ function mergeItemSizeUpdateResult(result, next) {
4636
4824
  result.needsRecalculate || (result.needsRecalculate = next.needsRecalculate);
4637
4825
  result.shouldMaintainScrollAtEnd || (result.shouldMaintainScrollAtEnd = next.shouldMaintainScrollAtEnd);
4638
4826
  }
4827
+ var batchedItemSizeRecalculates = /* @__PURE__ */ new WeakMap();
4828
+ function flushBatchedItemSizeRecalculate(ctx, didFallback = false, expectedResult) {
4829
+ const result = batchedItemSizeRecalculates.get(ctx);
4830
+ if (!result || expectedResult && result !== expectedResult) {
4831
+ return;
4832
+ }
4833
+ batchedItemSizeRecalculates.delete(ctx);
4834
+ if (didFallback) {
4835
+ ctx.state.pendingLayoutEffectMeasurements = void 0;
4836
+ }
4837
+ flushItemSizeUpdates(ctx, result);
4838
+ }
4839
+ function queueItemSizeRecalculate(ctx, result) {
4840
+ var _a3, _b;
4841
+ const batch = batchedItemSizeRecalculates.get(ctx);
4842
+ if (batch) {
4843
+ mergeItemSizeUpdateResult(batch, result);
4844
+ } else {
4845
+ const nextBatch = { ...result };
4846
+ batchedItemSizeRecalculates.set(ctx, nextBatch);
4847
+ if ((_a3 = ctx.state.pendingLayoutEffectMeasurements) == null ? void 0 : _a3.size) {
4848
+ requestAnimationFrame(() => {
4849
+ flushBatchedItemSizeRecalculate(ctx, true, nextBatch);
4850
+ });
4851
+ }
4852
+ }
4853
+ if (!((_b = ctx.state.pendingLayoutEffectMeasurements) == null ? void 0 : _b.size)) {
4854
+ flushBatchedItemSizeRecalculate(ctx);
4855
+ }
4856
+ }
4639
4857
  function flushItemSizeUpdates(ctx, result) {
4640
4858
  var _a3;
4641
4859
  const state = ctx.state;
@@ -4652,12 +4870,22 @@ function flushItemSizeUpdates(ctx, result) {
4652
4870
  function updateItemSizes(ctx, measurement) {
4653
4871
  var _a3, _b, _c, _d;
4654
4872
  const state = ctx.state;
4873
+ let didDrainLayoutEffectMeasurements = false;
4874
+ if (measurement.fromLayoutEffect) {
4875
+ const pendingLayoutEffectMeasurements = state.pendingLayoutEffectMeasurements;
4876
+ if ((pendingLayoutEffectMeasurements == null ? void 0 : pendingLayoutEffectMeasurements.delete(measurement.itemKey)) && pendingLayoutEffectMeasurements.size === 0) {
4877
+ state.pendingLayoutEffectMeasurements = void 0;
4878
+ didDrainLayoutEffectMeasurements = true;
4879
+ }
4880
+ }
4655
4881
  const pendingKeys = (_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys;
4656
4882
  const shouldBatchPendingMeasurements = measurement.fromLayoutEffect && !!(pendingKeys == null ? void 0 : pendingKeys.size);
4883
+ const shouldQueueRecalculate = !!measurement.fromLayoutEffect;
4884
+ let result;
4657
4885
  if (!shouldBatchPendingMeasurements) {
4658
- flushItemSizeUpdates(ctx, applyItemSize(ctx, measurement.itemKey, measurement.size));
4886
+ result = applyItemSize(ctx, measurement.itemKey, measurement.size);
4659
4887
  } else {
4660
- const result = {};
4888
+ result = {};
4661
4889
  const updateContainerItemSize = (itemKey, containerId, size) => {
4662
4890
  if (containerId !== void 0 && peek$(ctx, `containerItemKey${containerId}`) === itemKey) {
4663
4891
  mergeItemSizeUpdateResult(result, applyItemSize(ctx, itemKey, size));
@@ -4675,8 +4903,15 @@ function updateItemSizes(ctx, measurement) {
4675
4903
  });
4676
4904
  }
4677
4905
  }
4906
+ }
4907
+ if (shouldQueueRecalculate && result.needsRecalculate) {
4908
+ queueItemSizeRecalculate(ctx, result);
4909
+ } else {
4678
4910
  flushItemSizeUpdates(ctx, result);
4679
4911
  }
4912
+ if (didDrainLayoutEffectMeasurements) {
4913
+ flushBatchedItemSizeRecalculate(ctx);
4914
+ }
4680
4915
  }
4681
4916
  function applyItemSize(ctx, itemKey, sizeObj) {
4682
4917
  var _a3;
@@ -7046,7 +7281,7 @@ var LegendList = typedMemo(
7046
7281
  })
7047
7282
  );
7048
7283
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
7049
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
7284
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
7050
7285
  const noopOnScroll = useCallback((_event) => {
7051
7286
  }, []);
7052
7287
  if (props.recycleItems === void 0) {
@@ -7063,6 +7298,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7063
7298
  contentContainerStyle: contentContainerStyleProp,
7064
7299
  contentInset,
7065
7300
  data: dataProp = [],
7301
+ dataKey,
7066
7302
  dataVersion,
7067
7303
  drawDistance = 250,
7068
7304
  contentInsetEndAdjustment,
@@ -7193,6 +7429,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7193
7429
  (_j = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _j.join(","),
7194
7430
  (_k = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _k.join(","),
7195
7431
  dataProp,
7432
+ dataKey,
7196
7433
  dataVersion,
7197
7434
  keyExtractor
7198
7435
  ]);
@@ -7215,6 +7452,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7215
7452
  dataChangeNeedsScrollUpdate: false,
7216
7453
  didColumnsChange: false,
7217
7454
  didDataChange: false,
7455
+ didLoad: false,
7218
7456
  enableScrollForNextCalculateItemsInView: true,
7219
7457
  endBuffered: -1,
7220
7458
  endNoBuffer: -1,
@@ -7244,6 +7482,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7244
7482
  positions: [],
7245
7483
  props: {},
7246
7484
  queuedCalculateItemsInView: 0,
7485
+ queuedFullDrawDistancePrewarm: void 0,
7247
7486
  refScroller: { current: null },
7248
7487
  scroll: 0,
7249
7488
  scrollAdjustHandler: new ScrollAdjustHandler(ctx),
@@ -7286,10 +7525,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7286
7525
  const didScrollAxisGapChange = !isFirstLocal && ctx.scrollAxisGap !== nextScrollAxisGap;
7287
7526
  ctx.scrollAxisGap = nextScrollAxisGap;
7288
7527
  state.didColumnsChange = numColumnsProp !== previousNumColumnsProp || didScrollAxisGapChange;
7528
+ const previousDataLength = (_n = (_m = state.props.data) == null ? void 0 : _m.length) != null ? _n : 0;
7289
7529
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
7530
+ const didDataKeyChangeLocal = state.props.dataKey !== dataKey;
7290
7531
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
7291
- const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
7292
- if (didDataChangeLocal && !initialScrollAtEnd && state.didFinishInitialScroll && ((_m = state.initialScroll) == null ? void 0 : _m.viewPosition) === 1 && state.props.data.length > 0) {
7532
+ const didDataChangeLocal = didDataKeyChangeLocal || didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
7533
+ const shouldResetFreshDataLayout = !isFirstLocal && didDataChangeLocal && state.hasHadNonEmptyData && (didDataKeyChangeLocal || previousDataLength === 0) && dataProp.length > 0;
7534
+ if (didDataChangeLocal && !initialScrollAtEnd && state.didFinishInitialScroll && ((_o = state.initialScroll) == null ? void 0 : _o.viewPosition) === 1 && state.props.data.length > 0) {
7293
7535
  clearPreservedInitialScrollTarget(state);
7294
7536
  }
7295
7537
  if (didDataChangeLocal) {
@@ -7301,7 +7543,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7301
7543
  const throttledOnScroll = useThrottledOnScroll(onScrollProp != null ? onScrollProp : noopOnScroll, scrollEventThrottle != null ? scrollEventThrottle : 0);
7302
7544
  const throttleScrollFn = scrollEventThrottle && onScrollProp ? throttledOnScroll : onScrollProp;
7303
7545
  const anchoredEndSpaceResolved = anchoredEndSpace ? { ...anchoredEndSpace, includeInEndInset: true } : anchoredEndSpace;
7304
- const didAnchoredEndSpaceAnchorIndexChange = !isFirstLocal && !didDataChangeLocal && ((_n = state.props.anchoredEndSpace) == null ? void 0 : _n.anchorIndex) !== (anchoredEndSpaceResolved == null ? void 0 : anchoredEndSpaceResolved.anchorIndex);
7546
+ const didAnchoredEndSpaceAnchorIndexChange = !isFirstLocal && !didDataChangeLocal && ((_p = state.props.anchoredEndSpace) == null ? void 0 : _p.anchorIndex) !== (anchoredEndSpaceResolved == null ? void 0 : anchoredEndSpaceResolved.anchorIndex);
7305
7547
  state.props = {
7306
7548
  adaptiveRender: experimental_adaptiveRender,
7307
7549
  alignItemsAtEnd,
@@ -7315,6 +7557,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7315
7557
  contentInset,
7316
7558
  contentInsetEndAdjustment: contentInsetEndAdjustmentResolved,
7317
7559
  data: dataProp,
7560
+ dataKey,
7318
7561
  dataVersion,
7319
7562
  drawDistance,
7320
7563
  estimatedItemSize,
@@ -7355,13 +7598,19 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7355
7598
  if (!isFirstLocal && previousAdaptiveRender && !experimental_adaptiveRender) {
7356
7599
  resetAdaptiveRender(ctx);
7357
7600
  }
7601
+ if (shouldResetFreshDataLayout) {
7602
+ resetInitialRenderState(ctx, {
7603
+ resetInitialScroll: !!initialScrollProp,
7604
+ resetLayout: true
7605
+ });
7606
+ }
7358
7607
  const memoizedLastItemKeys = useMemo(() => {
7359
7608
  if (!dataProp.length) return [];
7360
7609
  return Array.from(
7361
7610
  { length: Math.min(numColumnsProp, dataProp.length) },
7362
7611
  (_, i) => getId(state, dataProp.length - 1 - i)
7363
7612
  );
7364
- }, [dataProp, dataVersion, numColumnsProp]);
7613
+ }, [dataProp, dataKey, dataVersion, numColumnsProp]);
7365
7614
  const initializeStateVars = (shouldAdjustPadding) => {
7366
7615
  set$(ctx, "lastItemKeys", memoizedLastItemKeys);
7367
7616
  set$(ctx, "numColumns", numColumnsProp);
@@ -7416,13 +7665,22 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7416
7665
  handleInitialScrollDataChange(ctx, {
7417
7666
  dataLength: dataProp.length,
7418
7667
  didDataChange: didDataChangeLocal,
7668
+ didStartFreshData: shouldResetFreshDataLayout,
7419
7669
  initialScrollAtEnd,
7420
7670
  latestInitialScroll: initialScrollProp,
7421
7671
  latestInitialScrollSessionKind: initialScrollUsesOffsetOnly ? "offset" : "bootstrap",
7422
7672
  stylePaddingBottom: stylePaddingBottomState,
7423
7673
  useBootstrapInitialScroll: usesBootstrapInitialScroll
7424
7674
  });
7425
- }, [dataProp.length, didDataChangeLocal, initialScrollAtEnd, stylePaddingBottomState, usesBootstrapInitialScroll]);
7675
+ }, [
7676
+ dataProp.length,
7677
+ dataKey,
7678
+ didDataChangeLocal,
7679
+ shouldResetFreshDataLayout,
7680
+ initialScrollAtEnd,
7681
+ stylePaddingBottomState,
7682
+ usesBootstrapInitialScroll
7683
+ ]);
7426
7684
  useLayoutEffect(() => {
7427
7685
  var _a4;
7428
7686
  if (didAnchoredEndSpaceAnchorIndexChange) {
@@ -7490,6 +7748,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7490
7748
  useLayoutEffect(
7491
7749
  () => initializeStateVars(true),
7492
7750
  [
7751
+ dataKey,
7493
7752
  dataVersion,
7494
7753
  memoizedLastItemKeys.join(","),
7495
7754
  numColumnsProp,
@@ -7516,7 +7775,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7516
7775
  state.didColumnsChange = false;
7517
7776
  state.didDataChange = false;
7518
7777
  state.isFirst = false;
7519
- }, [dataProp, dataVersion, numColumnsProp, nextScrollAxisGap]);
7778
+ }, [dataProp, dataKey, dataVersion, numColumnsProp, nextScrollAxisGap]);
7520
7779
  useLayoutEffect(() => {
7521
7780
  var _a4;
7522
7781
  set$(ctx, "extraData", extraData);
@@ -7566,6 +7825,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7566
7825
  useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx, scheduleImperativeScrollCommit), []);
7567
7826
  useEffect(() => {
7568
7827
  return () => {
7828
+ if (state.queuedFullDrawDistancePrewarm !== void 0) {
7829
+ cancelAnimationFrame(state.queuedFullDrawDistancePrewarm);
7830
+ state.queuedFullDrawDistancePrewarm = void 0;
7831
+ }
7569
7832
  for (const timeout of state.timeouts) {
7570
7833
  clearTimeout(timeout);
7571
7834
  }
@@ -7618,7 +7881,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7618
7881
  onScroll: onScrollHandler,
7619
7882
  recycleItems,
7620
7883
  refreshControl: refreshControlElement ? stylePaddingTopState > 0 ? React3.cloneElement(refreshControlElement, {
7621
- progressViewOffset: ((_o = refreshControlElement.props.progressViewOffset) != null ? _o : 0) + stylePaddingTopState
7884
+ progressViewOffset: ((_q = refreshControlElement.props.progressViewOffset) != null ? _q : 0) + stylePaddingTopState
7622
7885
  }) : refreshControlElement : onRefresh && /* @__PURE__ */ React3.createElement(
7623
7886
  RefreshControl,
7624
7887
  {
@@ -7629,7 +7892,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7629
7892
  ),
7630
7893
  refScrollView: combinedRef,
7631
7894
  renderScrollComponent,
7632
- scrollAdjustHandler: (_p = refState.current) == null ? void 0 : _p.scrollAdjustHandler,
7895
+ scrollAdjustHandler: (_r = refState.current) == null ? void 0 : _r.scrollAdjustHandler,
7633
7896
  scrollEventThrottle: 0,
7634
7897
  snapToIndices,
7635
7898
  stickyHeaderIndices,