@legendapp/list 3.0.0-beta.40 → 3.0.0-beta.42

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
@@ -359,6 +359,9 @@ var PositionViewSticky = typedMemo(function PositionViewSticky2({
359
359
  );
360
360
  });
361
361
  var PositionView = PositionViewState;
362
+
363
+ // src/constants-platform.ts
364
+ var IsNewArchitecture = true;
362
365
  function useInit(cb) {
363
366
  useState(() => cb());
364
367
  }
@@ -983,6 +986,47 @@ var StyleSheet = {
983
986
  create: (styles) => styles,
984
987
  flatten: (style) => flattenStyles(style)
985
988
  };
989
+ function useRafCoalescer(callback) {
990
+ const callbackRef = useRef(callback);
991
+ const rafIdRef = useRef(void 0);
992
+ callbackRef.current = callback;
993
+ const coalescer = useMemo(
994
+ () => ({
995
+ cancel() {
996
+ if (rafIdRef.current !== void 0) {
997
+ cancelAnimationFrame(rafIdRef.current);
998
+ rafIdRef.current = void 0;
999
+ }
1000
+ },
1001
+ flush() {
1002
+ coalescer.cancel();
1003
+ callbackRef.current();
1004
+ },
1005
+ schedule() {
1006
+ if (rafIdRef.current !== void 0) {
1007
+ return false;
1008
+ }
1009
+ const rafId = requestAnimationFrame(() => {
1010
+ if (rafIdRef.current !== rafId) {
1011
+ return;
1012
+ }
1013
+ rafIdRef.current = void 0;
1014
+ callbackRef.current();
1015
+ });
1016
+ rafIdRef.current = rafId;
1017
+ return true;
1018
+ }
1019
+ }),
1020
+ []
1021
+ );
1022
+ useEffect(
1023
+ () => () => {
1024
+ coalescer.cancel();
1025
+ },
1026
+ [coalescer]
1027
+ );
1028
+ return coalescer;
1029
+ }
986
1030
 
987
1031
  // src/components/webScrollUtils.ts
988
1032
  function getDocumentScrollerNode() {
@@ -1084,6 +1128,7 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
1084
1128
  onLayout,
1085
1129
  ...props
1086
1130
  }, ref) {
1131
+ const ctx = useStateContext();
1087
1132
  const scrollRef = useRef(null);
1088
1133
  const contentRef = useRef(null);
1089
1134
  const isWindowScroll = useWindowScroll;
@@ -1177,37 +1222,46 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
1177
1222
  };
1178
1223
  return api;
1179
1224
  }, [getCurrentScrollOffset, getMaxScrollOffset, getScrollTarget, horizontal, isWindowScroll, scrollToLocalOffset]);
1225
+ const emitScroll = useCallback(() => {
1226
+ if (!onScroll2 || !scrollRef.current) {
1227
+ return;
1228
+ }
1229
+ const contentSize = getContentSize2(contentRef.current);
1230
+ const layoutMeasurement = getLayoutMeasurement(scrollRef.current, isWindowScroll, horizontal);
1231
+ const offset = getCurrentScrollOffset();
1232
+ const scrollEvent = {
1233
+ nativeEvent: {
1234
+ contentOffset: {
1235
+ x: horizontal ? offset : 0,
1236
+ y: horizontal ? 0 : offset
1237
+ },
1238
+ contentSize: {
1239
+ height: contentSize.height,
1240
+ width: contentSize.width
1241
+ },
1242
+ layoutMeasurement: {
1243
+ height: layoutMeasurement.height,
1244
+ width: layoutMeasurement.width
1245
+ }
1246
+ }
1247
+ };
1248
+ onScroll2(scrollEvent);
1249
+ }, [getCurrentScrollOffset, horizontal, isWindowScroll, onScroll2]);
1250
+ const scrollEventCoalescer = useRafCoalescer(emitScroll);
1180
1251
  const handleScroll = useCallback(
1181
1252
  (_event) => {
1253
+ var _a3;
1182
1254
  if (!onScroll2) {
1183
1255
  return;
1184
1256
  }
1185
- const target = scrollRef.current;
1186
- if (!target) {
1187
- return;
1257
+ const scrollingTo = (_a3 = ctx.state) == null ? void 0 : _a3.scrollingTo;
1258
+ if (scrollingTo && !scrollingTo.animated) {
1259
+ scrollEventCoalescer.flush();
1260
+ } else {
1261
+ scrollEventCoalescer.schedule();
1188
1262
  }
1189
- const contentSize = getContentSize2(contentRef.current);
1190
- const layoutMeasurement = getLayoutMeasurement(scrollRef.current, isWindowScroll, horizontal);
1191
- const offset = getCurrentScrollOffset();
1192
- const scrollEvent = {
1193
- nativeEvent: {
1194
- contentOffset: {
1195
- x: horizontal ? offset : 0,
1196
- y: horizontal ? 0 : offset
1197
- },
1198
- contentSize: {
1199
- height: contentSize.height,
1200
- width: contentSize.width
1201
- },
1202
- layoutMeasurement: {
1203
- height: layoutMeasurement.height,
1204
- width: layoutMeasurement.width
1205
- }
1206
- }
1207
- };
1208
- onScroll2(scrollEvent);
1209
1263
  },
1210
- [getCurrentScrollOffset, horizontal, isWindowScroll, onScroll2]
1264
+ [onScroll2, scrollEventCoalescer]
1211
1265
  );
1212
1266
  useLayoutEffect(() => {
1213
1267
  const target = getScrollTarget();
@@ -1215,8 +1269,9 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
1215
1269
  target.addEventListener("scroll", handleScroll, { passive: true });
1216
1270
  return () => {
1217
1271
  target.removeEventListener("scroll", handleScroll);
1272
+ scrollEventCoalescer.cancel();
1218
1273
  };
1219
- }, [getScrollTarget, handleScroll]);
1274
+ }, [getScrollTarget, handleScroll, scrollEventCoalescer]);
1220
1275
  useEffect(() => {
1221
1276
  const doScroll = () => {
1222
1277
  if (contentOffset) {
@@ -1568,6 +1623,7 @@ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
1568
1623
 
1569
1624
  // src/core/calculateOffsetWithOffsetPosition.ts
1570
1625
  function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1626
+ var _a3;
1571
1627
  const state = ctx.state;
1572
1628
  const { index, viewOffset, viewPosition } = params;
1573
1629
  let offset = offsetParam;
@@ -1581,10 +1637,16 @@ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1581
1637
  }
1582
1638
  }
1583
1639
  if (viewPosition !== void 0 && index !== void 0) {
1584
- const itemSize = getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1640
+ const dataLength = state.props.data.length;
1641
+ if (dataLength === 0) {
1642
+ return offset;
1643
+ }
1644
+ const isOutOfBounds = index < 0 || index >= dataLength;
1645
+ const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
1646
+ const itemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1585
1647
  const trailingInset = getContentInsetEnd(state);
1586
1648
  offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1587
- if (index === state.props.data.length - 1) {
1649
+ if (!isOutOfBounds && index === state.props.data.length - 1) {
1588
1650
  const footerSize = peek$(ctx, "footerSize") || 0;
1589
1651
  offset += footerSize;
1590
1652
  }
@@ -1786,7 +1848,9 @@ function finishScrollTo(ctx) {
1786
1848
  const scrollingTo = state.scrollingTo;
1787
1849
  state.scrollHistory.length = 0;
1788
1850
  state.initialScroll = void 0;
1851
+ state.initialScrollUsesOffset = false;
1789
1852
  state.initialAnchor = void 0;
1853
+ state.initialNativeScrollWatchdog = void 0;
1790
1854
  state.scrollingTo = void 0;
1791
1855
  if (state.pendingTotalSize !== void 0) {
1792
1856
  addTotalSize(ctx, null, state.pendingTotalSize);
@@ -1888,7 +1952,9 @@ function listenForScrollEnd(ctx, params) {
1888
1952
  }
1889
1953
 
1890
1954
  // src/core/scrollTo.ts
1955
+ var WATCHDOG_OFFSET_EPSILON = 1;
1891
1956
  function scrollTo(ctx, params) {
1957
+ var _a3, _b;
1892
1958
  const state = ctx.state;
1893
1959
  const { noScrollingTo, forceScroll, ...scrollTarget } = params;
1894
1960
  const { animated, isInitialScroll, offset: scrollTargetOffset, precomputedWithViewOffset } = scrollTarget;
@@ -1905,9 +1971,23 @@ function scrollTo(ctx, params) {
1905
1971
  offset = clampScrollOffset(ctx, offset, scrollTarget);
1906
1972
  state.scrollHistory.length = 0;
1907
1973
  if (!noScrollingTo) {
1908
- state.scrollingTo = scrollTarget;
1974
+ state.scrollingTo = {
1975
+ ...scrollTarget,
1976
+ targetOffset: offset
1977
+ };
1909
1978
  }
1910
1979
  state.scrollPending = offset;
1980
+ const shouldWatchInitialNativeScroll = !state.didFinishInitialScroll && (isInitialScroll || !!state.initialNativeScrollWatchdog) && offset > WATCHDOG_OFFSET_EPSILON;
1981
+ const shouldClearInitialNativeScrollWatchdog = !state.didFinishInitialScroll && !!state.initialNativeScrollWatchdog && offset <= WATCHDOG_OFFSET_EPSILON;
1982
+ if (shouldWatchInitialNativeScroll) {
1983
+ state.hasScrolled = false;
1984
+ state.initialNativeScrollWatchdog = {
1985
+ startScroll: (_b = (_a3 = state.initialNativeScrollWatchdog) == null ? void 0 : _a3.startScroll) != null ? _b : state.scroll,
1986
+ targetOffset: offset
1987
+ };
1988
+ } else if (shouldClearInitialNativeScrollWatchdog) {
1989
+ state.initialNativeScrollWatchdog = void 0;
1990
+ }
1911
1991
  if (forceScroll || !isInitialScroll || Platform.OS === "android") {
1912
1992
  doScrollTo(ctx, { animated, horizontal, offset });
1913
1993
  } else {
@@ -1915,91 +1995,52 @@ function scrollTo(ctx, params) {
1915
1995
  }
1916
1996
  }
1917
1997
 
1918
- // src/core/updateScroll.ts
1919
- function updateScroll(ctx, newScroll, forceUpdate) {
1998
+ // src/core/doMaintainScrollAtEnd.ts
1999
+ function doMaintainScrollAtEnd(ctx) {
1920
2000
  const state = ctx.state;
1921
- const { ignoreScrollFromMVCP, lastScrollAdjustForHistory, scrollAdjustHandler, scrollHistory, scrollingTo } = state;
1922
- const prevScroll = state.scroll;
1923
- state.hasScrolled = true;
1924
- state.lastBatchingAction = Date.now();
1925
- const currentTime = Date.now();
1926
- const adjust = scrollAdjustHandler.getAdjust();
1927
- const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
1928
- if (adjustChanged) {
1929
- scrollHistory.length = 0;
1930
- }
1931
- state.lastScrollAdjustForHistory = adjust;
1932
- if (scrollingTo === void 0 && !(scrollHistory.length === 0 && newScroll === state.scroll)) {
1933
- if (!adjustChanged) {
1934
- scrollHistory.push({ scroll: newScroll, time: currentTime });
1935
- }
1936
- }
1937
- if (scrollHistory.length > 5) {
1938
- scrollHistory.shift();
2001
+ const {
2002
+ didContainersLayout,
2003
+ isAtEnd,
2004
+ pendingNativeMVCPAdjust,
2005
+ refScroller,
2006
+ props: { maintainScrollAtEnd }
2007
+ } = state;
2008
+ const shouldMaintainScrollAtEnd = !!(isAtEnd && maintainScrollAtEnd && didContainersLayout);
2009
+ if (pendingNativeMVCPAdjust) {
2010
+ state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
2011
+ return false;
1939
2012
  }
1940
- if (ignoreScrollFromMVCP && !scrollingTo) {
1941
- const { lt, gt } = ignoreScrollFromMVCP;
1942
- if (lt && newScroll < lt || gt && newScroll > gt) {
1943
- state.ignoreScrollFromMVCPIgnored = true;
1944
- return;
2013
+ state.pendingMaintainScrollAtEnd = false;
2014
+ if (shouldMaintainScrollAtEnd) {
2015
+ const contentSize = getContentSize(ctx);
2016
+ if (contentSize < state.scrollLength) {
2017
+ state.scroll = 0;
1945
2018
  }
1946
- }
1947
- state.scrollPrev = prevScroll;
1948
- state.scrollPrevTime = state.scrollTime;
1949
- state.scroll = newScroll;
1950
- state.scrollTime = currentTime;
1951
- const scrollDelta = Math.abs(newScroll - prevScroll);
1952
- const scrollLength = state.scrollLength;
1953
- const lastCalculated = state.scrollLastCalculate;
1954
- const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
1955
- const shouldUpdate = useAggressiveItemRecalculation || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
1956
- if (shouldUpdate) {
1957
- state.scrollLastCalculate = state.scroll;
1958
- state.ignoreScrollFromMVCPIgnored = false;
1959
- state.lastScrollDelta = scrollDelta;
1960
- const runCalculateItems = () => {
2019
+ requestAnimationFrame(() => {
1961
2020
  var _a3;
1962
- (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { doMVCP: scrollingTo !== void 0 });
1963
- checkThresholds(ctx);
1964
- };
1965
- if (scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength) {
1966
- flushSync(runCalculateItems);
1967
- } else {
1968
- runCalculateItems();
1969
- }
1970
- state.dataChangeNeedsScrollUpdate = false;
1971
- state.lastScrollDelta = 0;
1972
- }
1973
- }
1974
-
1975
- // src/utils/requestAdjust.ts
1976
- function requestAdjust(ctx, positionDiff, dataChanged) {
1977
- const state = ctx.state;
1978
- if (Math.abs(positionDiff) > 0.1) {
1979
- const doit = () => {
1980
- {
1981
- state.scrollAdjustHandler.requestAdjust(positionDiff);
1982
- if (state.adjustingFromInitialMount) {
1983
- state.adjustingFromInitialMount--;
1984
- }
2021
+ if (state.isAtEnd) {
2022
+ state.maintainingScrollAtEnd = true;
2023
+ (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
2024
+ animated: maintainScrollAtEnd.animated
2025
+ });
2026
+ setTimeout(
2027
+ () => {
2028
+ state.maintainingScrollAtEnd = false;
2029
+ },
2030
+ maintainScrollAtEnd.animated ? 500 : 0
2031
+ );
1985
2032
  }
1986
- };
1987
- state.scroll += positionDiff;
1988
- state.scrollForNextCalculateItemsInView = void 0;
1989
- const readyToRender = peek$(ctx, "readyToRender");
1990
- if (readyToRender) {
1991
- doit();
1992
- } else {
1993
- state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
1994
- requestAnimationFrame(doit);
1995
- }
2033
+ });
2034
+ return true;
1996
2035
  }
2036
+ return false;
1997
2037
  }
1998
2038
 
1999
2039
  // src/core/mvcp.ts
2000
2040
  var MVCP_POSITION_EPSILON = 0.1;
2001
2041
  var MVCP_ANCHOR_LOCK_TTL_MS = 300;
2002
2042
  var MVCP_ANCHOR_LOCK_QUIET_PASSES_TO_RELEASE = 2;
2043
+ var NATIVE_END_CLAMP_EPSILON = 1;
2003
2044
  function resolveAnchorLock(state, enableMVCPAnchorLock, mvcpData, now) {
2004
2045
  if (!enableMVCPAnchorLock) {
2005
2046
  state.mvcpAnchorLock = void 0;
@@ -2039,6 +2080,98 @@ function updateAnchorLock(state, params) {
2039
2080
  };
2040
2081
  }
2041
2082
  }
2083
+ function shouldQueueNativeMVCPAdjust(dataChanged, state, positionDiff, prevTotalSize, prevScroll, scrollTarget) {
2084
+ {
2085
+ return false;
2086
+ }
2087
+ }
2088
+ function getPredictedNativeClamp(state, unresolvedAmount, totalSize) {
2089
+ if (Math.abs(unresolvedAmount) <= MVCP_POSITION_EPSILON) {
2090
+ return 0;
2091
+ }
2092
+ const maxScroll = Math.max(0, totalSize - state.scrollLength);
2093
+ const clampDelta = maxScroll - state.scroll;
2094
+ if (unresolvedAmount < 0) {
2095
+ return Math.max(unresolvedAmount, Math.min(0, clampDelta));
2096
+ }
2097
+ if (unresolvedAmount > 0) {
2098
+ return Math.min(unresolvedAmount, Math.max(0, clampDelta));
2099
+ }
2100
+ return 0;
2101
+ }
2102
+ function getProgressTowardAmount(targetDelta, nativeDelta) {
2103
+ return targetDelta < 0 ? -nativeDelta : nativeDelta;
2104
+ }
2105
+ function settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta) {
2106
+ const state = ctx.state;
2107
+ state.pendingNativeMVCPAdjust = void 0;
2108
+ const remaining = remainingAfterManual - nativeDelta;
2109
+ if (Math.abs(remaining) > MVCP_POSITION_EPSILON) {
2110
+ requestAdjust(ctx, remaining);
2111
+ }
2112
+ }
2113
+ function maybeApplyPredictedNativeMVCPAdjust(ctx) {
2114
+ const state = ctx.state;
2115
+ const pending = state.pendingNativeMVCPAdjust;
2116
+ if (!pending || Math.abs(pending.manualApplied) > MVCP_POSITION_EPSILON) {
2117
+ return;
2118
+ }
2119
+ const totalSize = getContentSize(ctx);
2120
+ const predictedNativeClamp = getPredictedNativeClamp(state, pending.amount, totalSize);
2121
+ if (Math.abs(predictedNativeClamp) <= MVCP_POSITION_EPSILON) {
2122
+ return;
2123
+ }
2124
+ const manualDesired = pending.amount - predictedNativeClamp;
2125
+ if (Math.abs(manualDesired) <= MVCP_POSITION_EPSILON) {
2126
+ return;
2127
+ }
2128
+ pending.manualApplied = manualDesired;
2129
+ requestAdjust(ctx, manualDesired);
2130
+ pending.furthestProgressTowardAmount = 0;
2131
+ }
2132
+ function resolvePendingNativeMVCPAdjust(ctx, newScroll) {
2133
+ const state = ctx.state;
2134
+ const pending = state.pendingNativeMVCPAdjust;
2135
+ if (!pending) {
2136
+ return false;
2137
+ }
2138
+ const remainingAfterManual = pending.amount - pending.manualApplied;
2139
+ const nativeDelta = newScroll - (pending.startScroll + pending.manualApplied);
2140
+ const isWrongDirection = remainingAfterManual < 0 && nativeDelta > MVCP_POSITION_EPSILON || remainingAfterManual > 0 && nativeDelta < -MVCP_POSITION_EPSILON;
2141
+ const progressTowardAmount = getProgressTowardAmount(remainingAfterManual, nativeDelta);
2142
+ if (Math.abs(remainingAfterManual) <= MVCP_POSITION_EPSILON) {
2143
+ state.pendingNativeMVCPAdjust = void 0;
2144
+ return true;
2145
+ }
2146
+ if (isWrongDirection) {
2147
+ state.pendingNativeMVCPAdjust = void 0;
2148
+ return false;
2149
+ }
2150
+ if (progressTowardAmount + MVCP_POSITION_EPSILON >= Math.abs(remainingAfterManual)) {
2151
+ settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
2152
+ return true;
2153
+ }
2154
+ const expectedNativeClampScroll = Math.max(0, getContentSize(ctx) - state.scrollLength);
2155
+ const distanceToClamp = Math.abs(newScroll - expectedNativeClampScroll);
2156
+ const isAtExpectedNativeClamp = distanceToClamp <= NATIVE_END_CLAMP_EPSILON;
2157
+ if (isAtExpectedNativeClamp) {
2158
+ settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
2159
+ return true;
2160
+ }
2161
+ if (state.pendingMaintainScrollAtEnd && state.isAtEnd && progressTowardAmount > MVCP_POSITION_EPSILON) {
2162
+ settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
2163
+ return true;
2164
+ }
2165
+ if (progressTowardAmount > pending.furthestProgressTowardAmount + MVCP_POSITION_EPSILON) {
2166
+ pending.furthestProgressTowardAmount = progressTowardAmount;
2167
+ return false;
2168
+ }
2169
+ if (pending.furthestProgressTowardAmount > MVCP_POSITION_EPSILON && progressTowardAmount < pending.furthestProgressTowardAmount - MVCP_POSITION_EPSILON) {
2170
+ state.pendingNativeMVCPAdjust = void 0;
2171
+ return false;
2172
+ }
2173
+ return false;
2174
+ }
2042
2175
  function prepareMVCP(ctx, dataChanged) {
2043
2176
  const state = ctx.state;
2044
2177
  const { idsInView, positions, props } = state;
@@ -2057,6 +2190,8 @@ function prepareMVCP(ctx, dataChanged) {
2057
2190
  const isEndAnchoredScrollTarget = scrollTarget !== void 0 && state.props.data.length > 0 && scrollTarget >= state.props.data.length - 1 && (scrollingToViewPosition != null ? scrollingToViewPosition : 0) > 0;
2058
2191
  const shouldMVCP = dataChanged ? mvcpData : mvcpScroll;
2059
2192
  const indexByKey = state.indexByKey;
2193
+ const prevScroll = state.scroll;
2194
+ getContentSize(ctx);
2060
2195
  if (shouldMVCP) {
2061
2196
  if (anchorLock && scrollTarget === void 0) {
2062
2197
  targetId = anchorLock.id;
@@ -2163,6 +2298,16 @@ function prepareMVCP(ctx, dataChanged) {
2163
2298
  now,
2164
2299
  positionDiff
2165
2300
  });
2301
+ if (shouldQueueNativeMVCPAdjust()) {
2302
+ state.pendingNativeMVCPAdjust = {
2303
+ amount: positionDiff,
2304
+ furthestProgressTowardAmount: 0,
2305
+ manualApplied: 0,
2306
+ startScroll: prevScroll
2307
+ };
2308
+ maybeApplyPredictedNativeMVCPAdjust(ctx);
2309
+ return;
2310
+ }
2166
2311
  if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
2167
2312
  requestAdjust(ctx, positionDiff);
2168
2313
  }
@@ -2170,6 +2315,94 @@ function prepareMVCP(ctx, dataChanged) {
2170
2315
  }
2171
2316
  }
2172
2317
 
2318
+ // src/core/updateScroll.ts
2319
+ function updateScroll(ctx, newScroll, forceUpdate) {
2320
+ var _a3;
2321
+ const state = ctx.state;
2322
+ const { ignoreScrollFromMVCP, lastScrollAdjustForHistory, scrollAdjustHandler, scrollHistory, scrollingTo } = state;
2323
+ const prevScroll = state.scroll;
2324
+ state.hasScrolled = true;
2325
+ state.lastBatchingAction = Date.now();
2326
+ const currentTime = Date.now();
2327
+ const adjust = scrollAdjustHandler.getAdjust();
2328
+ const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
2329
+ if (adjustChanged) {
2330
+ scrollHistory.length = 0;
2331
+ }
2332
+ state.lastScrollAdjustForHistory = adjust;
2333
+ if (scrollingTo === void 0 && !(scrollHistory.length === 0 && newScroll === state.scroll)) {
2334
+ if (!adjustChanged) {
2335
+ scrollHistory.push({ scroll: newScroll, time: currentTime });
2336
+ }
2337
+ }
2338
+ if (scrollHistory.length > 5) {
2339
+ scrollHistory.shift();
2340
+ }
2341
+ if (ignoreScrollFromMVCP && !scrollingTo) {
2342
+ const { lt, gt } = ignoreScrollFromMVCP;
2343
+ if (lt && newScroll < lt || gt && newScroll > gt) {
2344
+ state.ignoreScrollFromMVCPIgnored = true;
2345
+ return;
2346
+ }
2347
+ }
2348
+ state.scrollPrev = prevScroll;
2349
+ state.scrollPrevTime = state.scrollTime;
2350
+ state.scroll = newScroll;
2351
+ state.scrollTime = currentTime;
2352
+ const scrollDelta = Math.abs(newScroll - prevScroll);
2353
+ const didResolvePendingNativeMVCPAdjust = resolvePendingNativeMVCPAdjust(ctx, newScroll);
2354
+ const scrollLength = state.scrollLength;
2355
+ const lastCalculated = state.scrollLastCalculate;
2356
+ const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
2357
+ const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
2358
+ if (shouldUpdate) {
2359
+ state.scrollLastCalculate = state.scroll;
2360
+ state.ignoreScrollFromMVCPIgnored = false;
2361
+ state.lastScrollDelta = scrollDelta;
2362
+ const runCalculateItems = () => {
2363
+ var _a4;
2364
+ (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { doMVCP: scrollingTo !== void 0 });
2365
+ checkThresholds(ctx);
2366
+ };
2367
+ if (scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength) {
2368
+ flushSync(runCalculateItems);
2369
+ } else {
2370
+ runCalculateItems();
2371
+ }
2372
+ const shouldMaintainScrollAtEndAfterPendingSettle = !!state.pendingMaintainScrollAtEnd || !!((_a3 = state.props.maintainScrollAtEnd) == null ? void 0 : _a3.onDataChange);
2373
+ if (didResolvePendingNativeMVCPAdjust && shouldMaintainScrollAtEndAfterPendingSettle) {
2374
+ state.pendingMaintainScrollAtEnd = false;
2375
+ doMaintainScrollAtEnd(ctx);
2376
+ }
2377
+ state.dataChangeNeedsScrollUpdate = false;
2378
+ state.lastScrollDelta = 0;
2379
+ }
2380
+ }
2381
+
2382
+ // src/utils/requestAdjust.ts
2383
+ function requestAdjust(ctx, positionDiff, dataChanged) {
2384
+ const state = ctx.state;
2385
+ if (Math.abs(positionDiff) > 0.1) {
2386
+ const doit = () => {
2387
+ {
2388
+ state.scrollAdjustHandler.requestAdjust(positionDiff);
2389
+ if (state.adjustingFromInitialMount) {
2390
+ state.adjustingFromInitialMount--;
2391
+ }
2392
+ }
2393
+ };
2394
+ state.scroll += positionDiff;
2395
+ state.scrollForNextCalculateItemsInView = void 0;
2396
+ const readyToRender = peek$(ctx, "readyToRender");
2397
+ if (readyToRender) {
2398
+ doit();
2399
+ } else {
2400
+ state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
2401
+ requestAnimationFrame(doit);
2402
+ }
2403
+ }
2404
+ }
2405
+
2173
2406
  // src/core/prepareColumnStartState.ts
2174
2407
  function prepareColumnStartState(ctx, startIndex, useAverageSize) {
2175
2408
  var _a3;
@@ -2823,7 +3056,14 @@ function comparatorByDistance(a, b) {
2823
3056
  }
2824
3057
 
2825
3058
  // src/core/scrollToIndex.ts
2826
- function scrollToIndex(ctx, { index, viewOffset = 0, animated = true, viewPosition }) {
3059
+ function scrollToIndex(ctx, {
3060
+ index,
3061
+ viewOffset = 0,
3062
+ animated = true,
3063
+ forceScroll,
3064
+ isInitialScroll,
3065
+ viewPosition
3066
+ }) {
2827
3067
  const state = ctx.state;
2828
3068
  const { data } = state.props;
2829
3069
  if (index >= data.length) {
@@ -2841,7 +3081,9 @@ function scrollToIndex(ctx, { index, viewOffset = 0, animated = true, viewPositi
2841
3081
  const itemSize = getItemSize(ctx, targetId, index, state.props.data[index]);
2842
3082
  scrollTo(ctx, {
2843
3083
  animated,
3084
+ forceScroll,
2844
3085
  index,
3086
+ isInitialScroll,
2845
3087
  itemSize,
2846
3088
  offset: firstIndexOffset,
2847
3089
  viewOffset,
@@ -2849,15 +3091,58 @@ function scrollToIndex(ctx, { index, viewOffset = 0, animated = true, viewPositi
2849
3091
  });
2850
3092
  }
2851
3093
 
3094
+ // src/utils/performInitialScroll.ts
3095
+ function performInitialScroll(ctx, params) {
3096
+ var _a3;
3097
+ const { forceScroll, initialScrollUsesOffset, resolvedOffset, target } = params;
3098
+ if (initialScrollUsesOffset || resolvedOffset !== void 0) {
3099
+ scrollTo(ctx, {
3100
+ animated: false,
3101
+ forceScroll,
3102
+ index: initialScrollUsesOffset ? void 0 : target.index,
3103
+ isInitialScroll: true,
3104
+ offset: (_a3 = resolvedOffset != null ? resolvedOffset : target.contentOffset) != null ? _a3 : 0,
3105
+ precomputedWithViewOffset: resolvedOffset !== void 0
3106
+ });
3107
+ return;
3108
+ }
3109
+ if (target.index === void 0) {
3110
+ return;
3111
+ }
3112
+ scrollToIndex(ctx, {
3113
+ ...target,
3114
+ animated: false,
3115
+ forceScroll,
3116
+ isInitialScroll: true
3117
+ });
3118
+ }
3119
+
2852
3120
  // src/utils/setDidLayout.ts
2853
3121
  function setDidLayout(ctx) {
2854
3122
  const state = ctx.state;
2855
3123
  const { initialScroll } = state;
2856
3124
  state.queuedInitialLayout = true;
2857
3125
  checkAtBottom(ctx);
2858
- if ((initialScroll == null ? void 0 : initialScroll.index) !== void 0) {
2859
- const target = initialScroll;
2860
- const runScroll = () => scrollToIndex(ctx, { ...target, animated: false });
3126
+ if (initialScroll) {
3127
+ const runScroll = () => {
3128
+ var _a3, _b;
3129
+ const target = state.initialScroll;
3130
+ if (!target) {
3131
+ return;
3132
+ }
3133
+ const activeInitialTargetOffset = ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) ? (_b = state.scrollingTo.targetOffset) != null ? _b : state.scrollingTo.offset : void 0;
3134
+ const desiredInitialTargetOffset = state.initialScrollUsesOffset ? target.contentOffset : activeInitialTargetOffset;
3135
+ const isAlreadyAtDesiredInitialTarget = desiredInitialTargetOffset !== void 0 && Math.abs(state.scroll - desiredInitialTargetOffset) <= 1 && Math.abs(state.scrollPending - desiredInitialTargetOffset) <= 1;
3136
+ if (!isAlreadyAtDesiredInitialTarget) {
3137
+ performInitialScroll(ctx, {
3138
+ forceScroll: true,
3139
+ initialScrollUsesOffset: state.initialScrollUsesOffset,
3140
+ // Offset-based initial scrolls do not need item lookup, so they can run even before data exists.
3141
+ // Re-run on the next frame to pick up measured viewport size without waiting for index resolution.
3142
+ target
3143
+ });
3144
+ }
3145
+ };
2861
3146
  runScroll();
2862
3147
  requestAnimationFrame(runScroll);
2863
3148
  }
@@ -2935,7 +3220,7 @@ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentSt
2935
3220
  function calculateItemsInView(ctx, params = {}) {
2936
3221
  const state = ctx.state;
2937
3222
  batchedUpdates(() => {
2938
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
3223
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
2939
3224
  const {
2940
3225
  columns,
2941
3226
  columnSpans,
@@ -2971,7 +3256,7 @@ function calculateItemsInView(ctx, params = {}) {
2971
3256
  if (!data || scrollLength === 0 || !prevNumContainers) {
2972
3257
  return;
2973
3258
  }
2974
- const totalSize = getContentSize(ctx);
3259
+ let totalSize = getContentSize(ctx);
2975
3260
  const topPad = peek$(ctx, "stylePaddingTop") + peek$(ctx, "headerSize");
2976
3261
  const numColumns = peek$(ctx, "numColumns");
2977
3262
  const speed = getScrollVelocity(state);
@@ -2979,14 +3264,14 @@ function calculateItemsInView(ctx, params = {}) {
2979
3264
  const { queuedInitialLayout } = state;
2980
3265
  let { scroll: scrollState } = state;
2981
3266
  if (!queuedInitialLayout && initialScroll) {
2982
- const updatedOffset = calculateOffsetWithOffsetPosition(
3267
+ const updatedOffset = state.initialScrollUsesOffset ? (_a3 = initialScroll.contentOffset) != null ? _a3 : 0 : calculateOffsetWithOffsetPosition(
2983
3268
  ctx,
2984
3269
  calculateOffsetForIndex(ctx, initialScroll.index),
2985
3270
  initialScroll
2986
3271
  );
2987
3272
  scrollState = updatedOffset;
2988
3273
  }
2989
- const scrollAdjustPending = (_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0;
3274
+ const scrollAdjustPending = (_b = peek$(ctx, "scrollAdjustPending")) != null ? _b : 0;
2990
3275
  const scrollAdjustPad = scrollAdjustPending - topPad;
2991
3276
  let scroll = Math.round(scrollState + scrollExtra + scrollAdjustPad);
2992
3277
  if (scroll + scrollLength > totalSize) {
@@ -3028,13 +3313,14 @@ function calculateItemsInView(ctx, params = {}) {
3028
3313
  columns.length = 0;
3029
3314
  columnSpans.length = 0;
3030
3315
  }
3031
- const startIndex = forceFullItemPositions || dataChanged ? 0 : (_b = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _b : 0;
3316
+ const startIndex = forceFullItemPositions || dataChanged ? 0 : (_c = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _c : 0;
3032
3317
  updateItemPositions(ctx, dataChanged, {
3033
3318
  doMVCP,
3034
3319
  forceFullUpdate: !!forceFullItemPositions,
3035
3320
  scrollBottomBuffered,
3036
3321
  startIndex
3037
3322
  });
3323
+ totalSize = getContentSize(ctx);
3038
3324
  if (minIndexSizeChanged !== void 0) {
3039
3325
  state.minIndexSizeChanged = void 0;
3040
3326
  }
@@ -3046,9 +3332,9 @@ function calculateItemsInView(ctx, params = {}) {
3046
3332
  let endBuffered = null;
3047
3333
  let loopStart = !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
3048
3334
  for (let i = loopStart; i >= 0; i--) {
3049
- const id = (_c = idCache[i]) != null ? _c : getId(state, i);
3335
+ const id = (_d = idCache[i]) != null ? _d : getId(state, i);
3050
3336
  const top = positions[i];
3051
- const size = (_d = sizes.get(id)) != null ? _d : getItemSize(ctx, id, i, data[i]);
3337
+ const size = (_e = sizes.get(id)) != null ? _e : getItemSize(ctx, id, i, data[i]);
3052
3338
  const bottom = top + size;
3053
3339
  if (bottom > scroll - scrollBufferTop) {
3054
3340
  loopStart = i;
@@ -3079,14 +3365,14 @@ function calculateItemsInView(ctx, params = {}) {
3079
3365
  let firstFullyOnScreenIndex;
3080
3366
  const dataLength = data.length;
3081
3367
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
3082
- const id = (_e = idCache[i]) != null ? _e : getId(state, i);
3083
- const size = (_f = sizes.get(id)) != null ? _f : getItemSize(ctx, id, i, data[i]);
3368
+ const id = (_f = idCache[i]) != null ? _f : getId(state, i);
3369
+ const size = (_g = sizes.get(id)) != null ? _g : getItemSize(ctx, id, i, data[i]);
3084
3370
  const top = positions[i];
3085
3371
  if (!foundEnd) {
3086
3372
  if (startNoBuffer === null && top + size > scroll) {
3087
3373
  startNoBuffer = i;
3088
3374
  }
3089
- if (firstFullyOnScreenIndex === void 0 && top >= scroll - 10) {
3375
+ if (firstFullyOnScreenIndex === void 0 && top >= scroll - 10 && top <= scrollBottom) {
3090
3376
  firstFullyOnScreenIndex = i;
3091
3377
  }
3092
3378
  if (startBuffered === null && top + size > scrollTopBuffered) {
@@ -3116,9 +3402,12 @@ function calculateItemsInView(ctx, params = {}) {
3116
3402
  }
3117
3403
  }
3118
3404
  const idsInView = [];
3119
- for (let i = firstFullyOnScreenIndex; i <= endNoBuffer; i++) {
3120
- const id = (_g = idCache[i]) != null ? _g : getId(state, i);
3121
- idsInView.push(id);
3405
+ const firstVisibleAnchorIndex = firstFullyOnScreenIndex != null ? firstFullyOnScreenIndex : startNoBuffer;
3406
+ if (firstVisibleAnchorIndex !== null && firstVisibleAnchorIndex !== void 0 && endNoBuffer !== null) {
3407
+ for (let i = firstVisibleAnchorIndex; i <= endNoBuffer; i++) {
3408
+ const id = (_h = idCache[i]) != null ? _h : getId(state, i);
3409
+ idsInView.push(id);
3410
+ }
3122
3411
  }
3123
3412
  Object.assign(state, {
3124
3413
  endBuffered,
@@ -3149,7 +3438,7 @@ function calculateItemsInView(ctx, params = {}) {
3149
3438
  const needNewContainers = [];
3150
3439
  const needNewContainersSet = /* @__PURE__ */ new Set();
3151
3440
  for (let i = startBuffered; i <= endBuffered; i++) {
3152
- const id = (_h = idCache[i]) != null ? _h : getId(state, i);
3441
+ const id = (_i = idCache[i]) != null ? _i : getId(state, i);
3153
3442
  if (!containerItemKeys.has(id)) {
3154
3443
  needNewContainersSet.add(i);
3155
3444
  needNewContainers.push(i);
@@ -3158,7 +3447,7 @@ function calculateItemsInView(ctx, params = {}) {
3158
3447
  if (alwaysRenderArr.length > 0) {
3159
3448
  for (const index of alwaysRenderArr) {
3160
3449
  if (index < 0 || index >= dataLength) continue;
3161
- const id = (_i = idCache[index]) != null ? _i : getId(state, index);
3450
+ const id = (_j = idCache[index]) != null ? _j : getId(state, index);
3162
3451
  if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
3163
3452
  needNewContainersSet.add(index);
3164
3453
  needNewContainers.push(index);
@@ -3196,7 +3485,7 @@ function calculateItemsInView(ctx, params = {}) {
3196
3485
  for (let idx = 0; idx < needNewContainers.length; idx++) {
3197
3486
  const i = needNewContainers[idx];
3198
3487
  const containerIndex = availableContainers[idx];
3199
- const id = (_j = idCache[i]) != null ? _j : getId(state, i);
3488
+ const id = (_k = idCache[i]) != null ? _k : getId(state, i);
3200
3489
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
3201
3490
  if (oldKey && oldKey !== id) {
3202
3491
  containerItemKeys.delete(oldKey);
@@ -3237,7 +3526,7 @@ function calculateItemsInView(ctx, params = {}) {
3237
3526
  if (alwaysRenderArr.length > 0) {
3238
3527
  for (const index of alwaysRenderArr) {
3239
3528
  if (index < 0 || index >= dataLength) continue;
3240
- const id = (_k = idCache[index]) != null ? _k : getId(state, index);
3529
+ const id = (_l = idCache[index]) != null ? _l : getId(state, index);
3241
3530
  const containerIndex = containerItemKeys.get(id);
3242
3531
  if (containerIndex !== void 0) {
3243
3532
  state.stickyContainerPool.add(containerIndex);
@@ -3346,21 +3635,21 @@ function checkActualChange(state, dataProp, previousData) {
3346
3635
  }
3347
3636
 
3348
3637
  // src/core/checkFinishedScroll.ts
3638
+ var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
3639
+ var INITIAL_SCROLL_MAX_FALLBACK_CHECKS = 20;
3640
+ var INITIAL_SCROLL_ZERO_TARGET_EPSILON = 1;
3349
3641
  function checkFinishedScroll(ctx) {
3350
3642
  ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
3351
3643
  }
3352
3644
  function checkFinishedScrollFrame(ctx) {
3645
+ var _a3;
3353
3646
  const scrollingTo = ctx.state.scrollingTo;
3354
3647
  if (scrollingTo) {
3355
3648
  const { state } = ctx;
3356
3649
  state.animFrameCheckFinishedScroll = void 0;
3357
3650
  const scroll = state.scrollPending;
3358
3651
  const adjust = state.scrollAdjustHandler.getAdjust();
3359
- const clampedTargetOffset = clampScrollOffset(
3360
- ctx,
3361
- scrollingTo.offset - (scrollingTo.viewOffset || 0),
3362
- scrollingTo
3363
- );
3652
+ const clampedTargetOffset = (_a3 = scrollingTo.targetOffset) != null ? _a3 : clampScrollOffset(ctx, scrollingTo.offset - (scrollingTo.viewOffset || 0), scrollingTo);
3364
3653
  const maxOffset = clampScrollOffset(ctx, scroll, scrollingTo);
3365
3654
  const diff1 = Math.abs(scroll - clampedTargetOffset);
3366
3655
  const diff2 = Math.abs(diff1 - adjust);
@@ -3374,17 +3663,33 @@ function checkFinishedScrollFrame(ctx) {
3374
3663
  function checkFinishedScrollFallback(ctx) {
3375
3664
  const state = ctx.state;
3376
3665
  const scrollingTo = state.scrollingTo;
3377
- const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) || !state.didContainersLayout;
3666
+ const shouldFinishInitialZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
3667
+ const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && !shouldFinishInitialZeroTarget || !state.didContainersLayout;
3378
3668
  state.timeoutCheckFinishedScrollFallback = setTimeout(
3379
3669
  () => {
3380
3670
  let numChecks = 0;
3381
3671
  const checkHasScrolled = () => {
3672
+ var _a3, _b;
3382
3673
  state.timeoutCheckFinishedScrollFallback = void 0;
3383
3674
  const isStillScrollingTo = state.scrollingTo;
3384
3675
  if (isStillScrollingTo) {
3385
3676
  numChecks++;
3386
- if (state.hasScrolled || numChecks > 5) {
3677
+ const isNativeInitialPending = isNativeInitialNonZeroTarget(state) && !state.hasScrolled;
3678
+ const maxChecks = isNativeInitialPending ? INITIAL_SCROLL_MAX_FALLBACK_CHECKS : 5;
3679
+ const shouldFinishZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
3680
+ if (shouldFinishZeroTarget || state.hasScrolled || numChecks > maxChecks) {
3387
3681
  finishScrollTo(ctx);
3682
+ } else if (isNativeInitialPending && numChecks <= maxChecks) {
3683
+ const targetOffset = (_b = (_a3 = state.initialNativeScrollWatchdog) == null ? void 0 : _a3.targetOffset) != null ? _b : state.scrollPending;
3684
+ const scroller = state.refScroller.current;
3685
+ if (scroller) {
3686
+ scroller.scrollTo({
3687
+ animated: false,
3688
+ x: state.props.horizontal ? targetOffset : 0,
3689
+ y: state.props.horizontal ? 0 : targetOffset
3690
+ });
3691
+ }
3692
+ state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, 100);
3388
3693
  } else {
3389
3694
  state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, 100);
3390
3695
  }
@@ -3395,39 +3700,13 @@ function checkFinishedScrollFallback(ctx) {
3395
3700
  slowTimeout ? 500 : 100
3396
3701
  );
3397
3702
  }
3398
-
3399
- // src/core/doMaintainScrollAtEnd.ts
3400
- function doMaintainScrollAtEnd(ctx, animated) {
3401
- const state = ctx.state;
3402
- const {
3403
- didContainersLayout,
3404
- isAtEnd,
3405
- refScroller,
3406
- props: { maintainScrollAtEnd }
3407
- } = state;
3408
- if (isAtEnd && maintainScrollAtEnd && didContainersLayout) {
3409
- const contentSize = getContentSize(ctx);
3410
- if (contentSize < state.scrollLength) {
3411
- state.scroll = 0;
3412
- }
3413
- requestAnimationFrame(() => {
3414
- var _a3;
3415
- if (state.isAtEnd) {
3416
- state.maintainingScrollAtEnd = true;
3417
- (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
3418
- animated
3419
- });
3420
- setTimeout(
3421
- () => {
3422
- state.maintainingScrollAtEnd = false;
3423
- },
3424
- 0
3425
- );
3426
- }
3427
- });
3428
- return true;
3429
- }
3430
- return false;
3703
+ function isNativeInitialNonZeroTarget(state) {
3704
+ return !state.didFinishInitialScroll && !!state.initialNativeScrollWatchdog && state.initialNativeScrollWatchdog.targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
3705
+ }
3706
+ function shouldFinishInitialZeroTargetScroll(ctx) {
3707
+ var _a3;
3708
+ const { state } = ctx;
3709
+ return !!((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) && state.props.data.length > 0 && getContentSize(ctx) <= state.scrollLength && state.scrollPending <= INITIAL_SCROLL_ZERO_TARGET_EPSILON;
3431
3710
  }
3432
3711
 
3433
3712
  // src/utils/updateAveragesOnDataChange.ts
@@ -3491,8 +3770,8 @@ function checkResetContainers(ctx, dataProp) {
3491
3770
  }
3492
3771
  const { maintainScrollAtEnd } = state.props;
3493
3772
  calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
3494
- const shouldMaintainScrollAtEnd = maintainScrollAtEnd === true || maintainScrollAtEnd.onDataChange;
3495
- const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, false);
3773
+ const shouldMaintainScrollAtEnd = maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onDataChange;
3774
+ const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx);
3496
3775
  if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
3497
3776
  state.isEndReached = false;
3498
3777
  }
@@ -3600,8 +3879,8 @@ function handleLayout(ctx, layoutParam, setCanRender) {
3600
3879
  if (didChange || otherAxisSize !== prevOtherAxisSize) {
3601
3880
  set$(ctx, "scrollSize", { height: layout.height, width: layout.width });
3602
3881
  }
3603
- if (maintainScrollAtEnd === true || maintainScrollAtEnd.onLayout) {
3604
- doMaintainScrollAtEnd(ctx, false);
3882
+ if (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onLayout) {
3883
+ doMaintainScrollAtEnd(ctx);
3605
3884
  }
3606
3885
  checkThresholds(ctx);
3607
3886
  if (state) {
@@ -3618,6 +3897,12 @@ function handleLayout(ctx, layoutParam, setCanRender) {
3618
3897
  }
3619
3898
 
3620
3899
  // src/core/onScroll.ts
3900
+ var INITIAL_SCROLL_PROGRESS_EPSILON = 1;
3901
+ function didObserveInitialScrollProgress(newScroll, watchdog) {
3902
+ const previousDistance = Math.abs(watchdog.startScroll - watchdog.targetOffset);
3903
+ const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
3904
+ return nextDistance <= INITIAL_SCROLL_PROGRESS_EPSILON || nextDistance + INITIAL_SCROLL_PROGRESS_EPSILON < previousDistance;
3905
+ }
3621
3906
  function onScroll(ctx, event) {
3622
3907
  var _a3, _b, _c, _d;
3623
3908
  const state = ctx.state;
@@ -3655,7 +3940,16 @@ function onScroll(ctx, event) {
3655
3940
  }
3656
3941
  }
3657
3942
  state.scrollPending = newScroll;
3943
+ const initialNativeScrollWatchdog = state.initialNativeScrollWatchdog;
3944
+ const didInitialScrollProgress = !!initialNativeScrollWatchdog && didObserveInitialScrollProgress(newScroll, initialNativeScrollWatchdog);
3945
+ if (didInitialScrollProgress) {
3946
+ state.initialNativeScrollWatchdog = void 0;
3947
+ }
3658
3948
  updateScroll(ctx, newScroll, insetChanged);
3949
+ if (initialNativeScrollWatchdog && !didInitialScrollProgress) {
3950
+ state.hasScrolled = false;
3951
+ state.initialNativeScrollWatchdog = initialNativeScrollWatchdog;
3952
+ }
3659
3953
  if (state.scrollingTo) {
3660
3954
  checkFinishedScroll(ctx);
3661
3955
  }
@@ -3820,8 +4114,8 @@ function updateItemSize(ctx, itemKey, sizeObj) {
3820
4114
  runOrScheduleMVCPRecalculate(ctx);
3821
4115
  }
3822
4116
  if (shouldMaintainScrollAtEnd) {
3823
- if (maintainScrollAtEnd === true || maintainScrollAtEnd.onItemLayout) {
3824
- doMaintainScrollAtEnd(ctx, false);
4117
+ if (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onItemLayout) {
4118
+ doMaintainScrollAtEnd(ctx);
3825
4119
  }
3826
4120
  }
3827
4121
  }
@@ -4171,6 +4465,34 @@ function getRenderedItem(ctx, key) {
4171
4465
  return { index, item: data[index], renderedItem };
4172
4466
  }
4173
4467
 
4468
+ // src/utils/normalizeMaintainScrollAtEnd.ts
4469
+ function normalizeMaintainScrollAtEndOn(on, hasExplicitOn) {
4470
+ var _a3, _b, _c;
4471
+ return {
4472
+ animated: false,
4473
+ onDataChange: hasExplicitOn ? (_a3 = on == null ? void 0 : on.dataChange) != null ? _a3 : false : true,
4474
+ onItemLayout: hasExplicitOn ? (_b = on == null ? void 0 : on.itemLayout) != null ? _b : false : true,
4475
+ onLayout: hasExplicitOn ? (_c = on == null ? void 0 : on.layout) != null ? _c : false : true
4476
+ };
4477
+ }
4478
+ function normalizeMaintainScrollAtEnd(value) {
4479
+ var _a3;
4480
+ if (!value) {
4481
+ return void 0;
4482
+ }
4483
+ if (value === true) {
4484
+ return {
4485
+ ...normalizeMaintainScrollAtEndOn(void 0, false),
4486
+ animated: false
4487
+ };
4488
+ }
4489
+ const normalizedTriggers = normalizeMaintainScrollAtEndOn(value.on, "on" in value);
4490
+ return {
4491
+ ...normalizedTriggers,
4492
+ animated: (_a3 = value.animated) != null ? _a3 : false
4493
+ };
4494
+ }
4495
+
4174
4496
  // src/utils/normalizeMaintainVisibleContentPosition.ts
4175
4497
  function normalizeMaintainVisibleContentPosition(value) {
4176
4498
  var _a3, _b;
@@ -4272,7 +4594,7 @@ var LegendList = typedMemo(
4272
4594
  })
4273
4595
  );
4274
4596
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
4275
- var _a3, _b, _c, _d, _e;
4597
+ var _a3, _b, _c, _d, _e, _f, _g, _h;
4276
4598
  const {
4277
4599
  alignItemsAtEnd = false,
4278
4600
  alwaysRender,
@@ -4358,18 +4680,26 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4358
4680
  const style = { ...StyleSheet.flatten(styleProp) };
4359
4681
  const stylePaddingTopState = extractPadding(style, contentContainerStyle, "Top");
4360
4682
  const stylePaddingBottomState = extractPadding(style, contentContainerStyle, "Bottom");
4683
+ const maintainScrollAtEndConfig = normalizeMaintainScrollAtEnd(maintainScrollAtEnd);
4361
4684
  const maintainVisibleContentPositionConfig = normalizeMaintainVisibleContentPosition(
4362
4685
  maintainVisibleContentPositionProp
4363
4686
  );
4364
- const initialScrollProp = initialScrollAtEnd ? { index: Math.max(0, dataProp.length - 1), viewOffset: -stylePaddingBottomState, viewPosition: 1 } : initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? {
4365
- index: initialScrollIndexProp.index || 0,
4366
- viewOffset: initialScrollIndexProp.viewOffset || (initialScrollIndexProp.viewPosition === 1 ? -stylePaddingBottomState : 0),
4367
- viewPosition: initialScrollIndexProp.viewPosition || 0
4687
+ const hasInitialScrollIndex = initialScrollIndexProp !== void 0 && initialScrollIndexProp !== null;
4688
+ const hasInitialScrollOffset = initialScrollOffsetProp !== void 0 && initialScrollOffsetProp !== null;
4689
+ const initialScrollUsesOffsetOnly = !initialScrollAtEnd && !hasInitialScrollIndex && hasInitialScrollOffset;
4690
+ const initialScrollProp = initialScrollAtEnd ? { index: Math.max(0, dataProp.length - 1), viewOffset: -stylePaddingBottomState, viewPosition: 1 } : hasInitialScrollIndex ? typeof initialScrollIndexProp === "object" ? {
4691
+ index: (_a3 = initialScrollIndexProp.index) != null ? _a3 : 0,
4692
+ viewOffset: (_b = initialScrollIndexProp.viewOffset) != null ? _b : initialScrollIndexProp.viewPosition === 1 ? -stylePaddingBottomState : 0,
4693
+ viewPosition: (_c = initialScrollIndexProp.viewPosition) != null ? _c : 0
4368
4694
  } : {
4369
- index: initialScrollIndexProp || 0,
4370
- viewOffset: initialScrollOffsetProp || 0
4695
+ index: initialScrollIndexProp != null ? initialScrollIndexProp : 0,
4696
+ viewOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : 0
4697
+ } : initialScrollUsesOffsetOnly ? {
4698
+ contentOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : 0,
4699
+ index: 0,
4700
+ viewOffset: 0
4371
4701
  } : void 0;
4372
- const [canRender, setCanRender] = React3.useState(false);
4702
+ const [canRender, setCanRender] = React3.useState(!IsNewArchitecture);
4373
4703
  const ctx = useStateContext();
4374
4704
  ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
4375
4705
  const refScroller = useRef(null);
@@ -4382,8 +4712,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4382
4712
  }, [
4383
4713
  alwaysRender == null ? void 0 : alwaysRender.top,
4384
4714
  alwaysRender == null ? void 0 : alwaysRender.bottom,
4385
- (_a3 = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _a3.join(","),
4386
- (_b = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _b.join(","),
4715
+ (_d = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _d.join(","),
4716
+ (_e = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _e.join(","),
4387
4717
  dataProp,
4388
4718
  dataVersion,
4389
4719
  keyExtractor
@@ -4427,14 +4757,22 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4427
4757
  idCache: [],
4428
4758
  idsInView: [],
4429
4759
  indexByKey: /* @__PURE__ */ new Map(),
4430
- initialAnchor: (initialScrollProp == null ? void 0 : initialScrollProp.index) !== void 0 && (initialScrollProp == null ? void 0 : initialScrollProp.viewPosition) !== void 0 ? {
4760
+ initialAnchor: !initialScrollUsesOffsetOnly && (initialScrollProp == null ? void 0 : initialScrollProp.index) !== void 0 && (initialScrollProp == null ? void 0 : initialScrollProp.viewPosition) !== void 0 ? {
4431
4761
  attempts: 0,
4432
4762
  index: initialScrollProp.index,
4433
4763
  settledTicks: 0,
4434
- viewOffset: (_c = initialScrollProp.viewOffset) != null ? _c : 0,
4764
+ viewOffset: (_f = initialScrollProp.viewOffset) != null ? _f : 0,
4435
4765
  viewPosition: initialScrollProp.viewPosition
4436
4766
  } : void 0,
4767
+ initialNativeScrollWatchdog: void 0,
4437
4768
  initialScroll: initialScrollProp,
4769
+ initialScrollLastDidFinish: false,
4770
+ initialScrollLastTarget: initialScrollProp,
4771
+ initialScrollLastTargetUsesOffset: initialScrollUsesOffsetOnly,
4772
+ initialScrollPreviousDataLength: dataProp.length,
4773
+ initialScrollRetryLastLength: void 0,
4774
+ initialScrollRetryWindowUntil: 0,
4775
+ initialScrollUsesOffset: initialScrollUsesOffsetOnly,
4438
4776
  isAtEnd: false,
4439
4777
  isAtStart: false,
4440
4778
  isEndReached: null,
@@ -4447,6 +4785,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4447
4785
  minIndexSizeChanged: 0,
4448
4786
  nativeContentInset: void 0,
4449
4787
  nativeMarginTop: 0,
4788
+ pendingNativeMVCPAdjust: void 0,
4450
4789
  positions: [],
4451
4790
  props: {},
4452
4791
  queuedCalculateItemsInView: 0,
@@ -4484,7 +4823,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4484
4823
  const state = refState.current;
4485
4824
  const isFirstLocal = state.isFirst;
4486
4825
  state.didColumnsChange = numColumnsProp !== state.props.numColumns;
4487
- const didDataChangeLocal = state.props.dataVersion !== dataVersion || state.props.data !== dataProp && checkActualChange(state, dataProp, state.props.data);
4826
+ const didDataReferenceChangeLocal = state.props.data !== dataProp;
4827
+ const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
4828
+ const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkActualChange(state, dataProp, state.props.data);
4488
4829
  if (didDataChangeLocal) {
4489
4830
  state.dataChangeEpoch += 1;
4490
4831
  state.dataChangeNeedsScrollUpdate = true;
@@ -4510,7 +4851,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4510
4851
  initialContainerPoolRatio,
4511
4852
  itemsAreEqual,
4512
4853
  keyExtractor: useWrapIfItem(keyExtractor),
4513
- maintainScrollAtEnd,
4854
+ maintainScrollAtEnd: maintainScrollAtEndConfig,
4514
4855
  maintainScrollAtEndThreshold,
4515
4856
  maintainVisibleContentPosition: maintainVisibleContentPositionConfig,
4516
4857
  numColumns: numColumnsProp,
@@ -4561,29 +4902,93 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4561
4902
  );
4562
4903
  }
4563
4904
  const resolveInitialScrollOffset = useCallback((initialScroll) => {
4905
+ var _a4;
4906
+ if (state.initialScrollUsesOffset) {
4907
+ return clampScrollOffset(ctx, (_a4 = initialScroll.contentOffset) != null ? _a4 : 0);
4908
+ }
4564
4909
  const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, initialScroll.index) : 0;
4565
4910
  const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, initialScroll);
4566
4911
  return clampScrollOffset(ctx, resolvedOffset, initialScroll);
4567
4912
  }, []);
4913
+ const finishInitialScrollWithoutScroll = useCallback(() => {
4914
+ refState.current.initialAnchor = void 0;
4915
+ refState.current.initialScroll = void 0;
4916
+ state.initialAnchor = void 0;
4917
+ state.initialScroll = void 0;
4918
+ state.initialScrollUsesOffset = false;
4919
+ state.initialScrollLastTarget = void 0;
4920
+ state.initialScrollLastTargetUsesOffset = false;
4921
+ setInitialRenderState(ctx, { didInitialScroll: true });
4922
+ }, []);
4923
+ const setActiveInitialScrollTarget = useCallback(
4924
+ (target, options) => {
4925
+ const usesOffset = !!(options == null ? void 0 : options.usesOffset);
4926
+ state.initialScrollUsesOffset = usesOffset;
4927
+ state.initialScrollLastTarget = target;
4928
+ state.initialScrollLastTargetUsesOffset = usesOffset;
4929
+ refState.current.initialScroll = target;
4930
+ state.initialScroll = target;
4931
+ if ((options == null ? void 0 : options.resetDidFinish) && state.didFinishInitialScroll) {
4932
+ state.didFinishInitialScroll = false;
4933
+ }
4934
+ if (!(options == null ? void 0 : options.syncAnchor)) {
4935
+ return;
4936
+ }
4937
+ },
4938
+ []
4939
+ );
4940
+ const shouldFinishInitialScrollAtOrigin = useCallback(
4941
+ (initialScroll, offset) => {
4942
+ var _a4, _b2, _c2;
4943
+ if (offset !== 0 || initialScrollAtEnd) {
4944
+ return false;
4945
+ }
4946
+ if (state.initialScrollUsesOffset) {
4947
+ return Math.abs((_a4 = initialScroll.contentOffset) != null ? _a4 : 0) <= 1;
4948
+ }
4949
+ return initialScroll.index === 0 && ((_b2 = initialScroll.viewPosition) != null ? _b2 : 0) === 0 && Math.abs((_c2 = initialScroll.viewOffset) != null ? _c2 : 0) <= 1;
4950
+ },
4951
+ [initialScrollAtEnd]
4952
+ );
4953
+ const shouldFinishEmptyInitialScrollAtEnd = useCallback(
4954
+ (initialScroll, offset) => {
4955
+ return dataProp.length === 0 && initialScrollAtEnd && offset === 0 && initialScroll.viewPosition === 1;
4956
+ },
4957
+ [dataProp.length, initialScrollAtEnd]
4958
+ );
4959
+ const shouldRearmFinishedEmptyInitialScrollAtEnd = useCallback(
4960
+ (initialScroll) => {
4961
+ var _a4;
4962
+ return !!(state.didFinishInitialScroll && dataProp.length > 0 && initialScroll && !state.initialScrollUsesOffset && initialScroll.index === 0 && initialScroll.viewPosition === 1 && ((_a4 = initialScroll.contentOffset) != null ? _a4 : 0) === 0);
4963
+ },
4964
+ [dataProp.length]
4965
+ );
4568
4966
  const initialContentOffset = useMemo(() => {
4569
4967
  let value;
4570
4968
  const { initialScroll, initialAnchor } = refState.current;
4571
4969
  if (initialScroll) {
4970
+ if (!state.initialScrollUsesOffset && !IsNewArchitecture) ;
4572
4971
  if (initialScroll.contentOffset !== void 0) {
4573
4972
  value = initialScroll.contentOffset;
4574
4973
  } else {
4575
4974
  const clampedOffset = resolveInitialScrollOffset(initialScroll);
4576
4975
  const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
4577
- refState.current.initialScroll = updatedInitialScroll;
4578
- state.initialScroll = updatedInitialScroll;
4976
+ setActiveInitialScrollTarget(updatedInitialScroll, {
4977
+ usesOffset: state.initialScrollUsesOffset
4978
+ });
4579
4979
  value = clampedOffset;
4580
4980
  }
4581
4981
  } else {
4582
4982
  refState.current.initialAnchor = void 0;
4583
4983
  value = 0;
4584
4984
  }
4585
- if (!value) {
4586
- setInitialRenderState(ctx, { didInitialScroll: true });
4985
+ const hasPendingDataDependentInitialScroll = !!initialScroll && dataProp.length === 0 && !shouldFinishInitialScrollAtOrigin(initialScroll, value) && !shouldFinishEmptyInitialScrollAtEnd(initialScroll, value);
4986
+ if (!value && !hasPendingDataDependentInitialScroll) {
4987
+ if (initialScroll && shouldFinishInitialScrollAtOrigin(initialScroll, value)) {
4988
+ finishInitialScrollWithoutScroll();
4989
+ } else {
4990
+ setInitialRenderState(ctx, { didInitialScroll: true });
4991
+ }
4587
4992
  }
4588
4993
  return value;
4589
4994
  }, []);
@@ -4600,24 +5005,131 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4600
5005
  set$(ctx, "totalSize", 0);
4601
5006
  }
4602
5007
  }
4603
- const doInitialScroll = useCallback(() => {
4604
- const { initialScroll, didFinishInitialScroll, queuedInitialLayout, scrollingTo } = state;
4605
- if (initialScroll && !queuedInitialLayout && !didFinishInitialScroll && !scrollingTo) {
4606
- const offset = resolveInitialScrollOffset(initialScroll);
5008
+ const doInitialScroll = useCallback((options) => {
5009
+ var _a4, _b2;
5010
+ const allowPostFinishRetry = !!(options == null ? void 0 : options.allowPostFinishRetry);
5011
+ const { didFinishInitialScroll, queuedInitialLayout, scrollingTo } = state;
5012
+ const initialScroll = (_a4 = state.initialScroll) != null ? _a4 : allowPostFinishRetry ? state.initialScrollLastTarget : void 0;
5013
+ const isInitialScrollInProgress = !!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll);
5014
+ const needsContainerLayoutForInitialScroll = !state.initialScrollUsesOffset;
5015
+ const shouldWaitForInitialLayout = waitForInitialLayout && needsContainerLayoutForInitialScroll && !queuedInitialLayout && !allowPostFinishRetry && !isInitialScrollInProgress;
5016
+ if (!initialScroll || shouldWaitForInitialLayout || didFinishInitialScroll && !allowPostFinishRetry || scrollingTo && !isInitialScrollInProgress) {
5017
+ return;
5018
+ }
5019
+ if (allowPostFinishRetry && state.initialScrollLastTargetUsesOffset) {
5020
+ return;
5021
+ }
5022
+ const didMoveAwayFromInitialTarget = allowPostFinishRetry && initialScroll.contentOffset !== void 0 && Math.abs(state.scroll - initialScroll.contentOffset) > 1;
5023
+ if (didMoveAwayFromInitialTarget) {
5024
+ state.initialScrollRetryWindowUntil = 0;
5025
+ return;
5026
+ }
5027
+ const offset = resolveInitialScrollOffset(initialScroll);
5028
+ const activeInitialTargetOffset = isInitialScrollInProgress ? (_b2 = scrollingTo.targetOffset) != null ? _b2 : scrollingTo.offset : void 0;
5029
+ const didOffsetChange = initialScroll.contentOffset === void 0 || Math.abs(initialScroll.contentOffset - offset) > 1;
5030
+ const didActiveInitialTargetChange = activeInitialTargetOffset !== void 0 && Math.abs(activeInitialTargetOffset - offset) > 1;
5031
+ if (!didOffsetChange && (allowPostFinishRetry || isInitialScrollInProgress && !didActiveInitialTargetChange)) {
5032
+ return;
5033
+ }
5034
+ if (didOffsetChange) {
4607
5035
  const updatedInitialScroll = { ...initialScroll, contentOffset: offset };
4608
- refState.current.initialScroll = updatedInitialScroll;
4609
- state.initialScroll = updatedInitialScroll;
4610
- scrollTo(ctx, {
4611
- animated: false,
4612
- index: initialScroll.index,
4613
- isInitialScroll: true,
4614
- offset,
4615
- precomputedWithViewOffset: true
4616
- });
5036
+ if (!state.initialScrollUsesOffset) {
5037
+ state.initialScrollLastTarget = updatedInitialScroll;
5038
+ state.initialScrollLastTargetUsesOffset = false;
5039
+ if (state.initialScroll) {
5040
+ refState.current.initialScroll = updatedInitialScroll;
5041
+ state.initialScroll = updatedInitialScroll;
5042
+ }
5043
+ }
4617
5044
  }
5045
+ const hasMeasuredScrollLayout = !!state.lastLayout && state.scrollLength > 0;
5046
+ const shouldForceNativeInitialScroll = state.initialScrollUsesOffset && hasMeasuredScrollLayout || allowPostFinishRetry || !!queuedInitialLayout || isInitialScrollInProgress && didOffsetChange;
5047
+ performInitialScroll(ctx, {
5048
+ forceScroll: shouldForceNativeInitialScroll,
5049
+ initialScrollUsesOffset: state.initialScrollUsesOffset,
5050
+ resolvedOffset: offset,
5051
+ target: initialScroll
5052
+ });
4618
5053
  }, []);
5054
+ useLayoutEffect(() => {
5055
+ var _a4;
5056
+ const previousDataLength = state.initialScrollPreviousDataLength;
5057
+ state.initialScrollPreviousDataLength = dataProp.length;
5058
+ if (previousDataLength !== 0 || dataProp.length === 0 || !state.initialScroll || !state.queuedInitialLayout) {
5059
+ return;
5060
+ }
5061
+ if (initialScrollAtEnd) {
5062
+ const lastIndex = Math.max(0, dataProp.length - 1);
5063
+ const initialScroll = state.initialScroll;
5064
+ const shouldRearm = shouldRearmFinishedEmptyInitialScrollAtEnd(initialScroll);
5065
+ if (state.didFinishInitialScroll && !shouldRearm) {
5066
+ return;
5067
+ }
5068
+ if (initialScroll && !state.initialScrollUsesOffset && initialScroll.index === lastIndex && initialScroll.viewPosition === 1 && !shouldRearm) {
5069
+ return;
5070
+ }
5071
+ const updatedInitialScroll = {
5072
+ contentOffset: void 0,
5073
+ index: lastIndex,
5074
+ viewOffset: (_a4 = initialScroll == null ? void 0 : initialScroll.viewOffset) != null ? _a4 : -stylePaddingBottomState,
5075
+ viewPosition: 1
5076
+ };
5077
+ setActiveInitialScrollTarget(updatedInitialScroll, {
5078
+ resetDidFinish: shouldRearm,
5079
+ syncAnchor: true
5080
+ });
5081
+ doInitialScroll();
5082
+ return;
5083
+ }
5084
+ if (state.didFinishInitialScroll) {
5085
+ return;
5086
+ }
5087
+ doInitialScroll();
5088
+ }, [
5089
+ dataProp.length,
5090
+ doInitialScroll,
5091
+ initialScrollAtEnd,
5092
+ shouldRearmFinishedEmptyInitialScrollAtEnd,
5093
+ stylePaddingBottomState
5094
+ ]);
5095
+ useLayoutEffect(() => {
5096
+ var _a4;
5097
+ if (!initialScrollAtEnd) {
5098
+ return;
5099
+ }
5100
+ const lastIndex = Math.max(0, dataProp.length - 1);
5101
+ const initialScroll = state.initialScroll;
5102
+ const shouldRearm = shouldRearmFinishedEmptyInitialScrollAtEnd(initialScroll);
5103
+ if (state.didFinishInitialScroll && !shouldRearm) {
5104
+ return;
5105
+ }
5106
+ if (shouldRearm) {
5107
+ state.didFinishInitialScroll = false;
5108
+ }
5109
+ if (initialScroll && !state.initialScrollUsesOffset && initialScroll.index === lastIndex && initialScroll.viewPosition === 1 && !shouldRearm) {
5110
+ return;
5111
+ }
5112
+ const updatedInitialScroll = {
5113
+ contentOffset: void 0,
5114
+ index: lastIndex,
5115
+ viewOffset: (_a4 = initialScroll == null ? void 0 : initialScroll.viewOffset) != null ? _a4 : -stylePaddingBottomState,
5116
+ viewPosition: 1
5117
+ };
5118
+ setActiveInitialScrollTarget(updatedInitialScroll, {
5119
+ resetDidFinish: shouldRearm,
5120
+ syncAnchor: true
5121
+ });
5122
+ doInitialScroll();
5123
+ }, [
5124
+ dataProp.length,
5125
+ doInitialScroll,
5126
+ initialScrollAtEnd,
5127
+ shouldRearmFinishedEmptyInitialScrollAtEnd,
5128
+ stylePaddingBottomState
5129
+ ]);
4619
5130
  const onLayoutFooter = useCallback(
4620
5131
  (layout) => {
5132
+ var _a4;
4621
5133
  if (!initialScrollAtEnd) {
4622
5134
  return;
4623
5135
  }
@@ -4632,16 +5144,48 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4632
5144
  const footerSize = layout[horizontal ? "width" : "height"];
4633
5145
  const viewOffset = -stylePaddingBottomState - footerSize;
4634
5146
  if (initialScroll.viewOffset !== viewOffset) {
5147
+ const previousTargetOffset = (_a4 = initialScroll.contentOffset) != null ? _a4 : resolveInitialScrollOffset(initialScroll);
5148
+ const didMoveAwayFromFinishedInitialTarget = state.didFinishInitialScroll && Math.abs(state.scroll - previousTargetOffset) > 1;
5149
+ if (didMoveAwayFromFinishedInitialTarget) {
5150
+ return;
5151
+ }
4635
5152
  const updatedInitialScroll = { ...initialScroll, viewOffset };
4636
- refState.current.initialScroll = updatedInitialScroll;
4637
- state.initialScroll = updatedInitialScroll;
5153
+ setActiveInitialScrollTarget(updatedInitialScroll, {
5154
+ resetDidFinish: true
5155
+ });
5156
+ doInitialScroll();
4638
5157
  }
4639
5158
  },
4640
- [dataProp.length, horizontal, initialScrollAtEnd, stylePaddingBottomState]
5159
+ [
5160
+ dataProp.length,
5161
+ doInitialScroll,
5162
+ horizontal,
5163
+ initialScrollAtEnd,
5164
+ resolveInitialScrollOffset,
5165
+ stylePaddingBottomState
5166
+ ]
4641
5167
  );
4642
5168
  const onLayoutChange = useCallback((layout) => {
4643
- doInitialScroll();
5169
+ var _a4;
4644
5170
  handleLayout(ctx, layout, setCanRender);
5171
+ const SCROLL_LENGTH_RETRY_WINDOW_MS = 600;
5172
+ const now = Date.now();
5173
+ const didFinishInitialScroll = !!state.didFinishInitialScroll;
5174
+ if (didFinishInitialScroll && !state.initialScrollLastDidFinish) {
5175
+ state.initialScrollRetryWindowUntil = now + SCROLL_LENGTH_RETRY_WINDOW_MS;
5176
+ }
5177
+ state.initialScrollLastDidFinish = didFinishInitialScroll;
5178
+ const previousScrollLength = state.initialScrollRetryLastLength;
5179
+ const currentScrollLength = state.scrollLength;
5180
+ const didScrollLengthChange = previousScrollLength === void 0 || Math.abs(currentScrollLength - previousScrollLength) > 1;
5181
+ if (didScrollLengthChange) {
5182
+ state.initialScrollRetryLastLength = currentScrollLength;
5183
+ }
5184
+ if (didFinishInitialScroll && didScrollLengthChange && now <= state.initialScrollRetryWindowUntil && !state.initialScrollLastTargetUsesOffset && ((_a4 = state.initialScrollLastTarget) == null ? void 0 : _a4.index) !== void 0) {
5185
+ doInitialScroll({ allowPostFinishRetry: true });
5186
+ return;
5187
+ }
5188
+ doInitialScroll();
4645
5189
  }, []);
4646
5190
  const { onLayout } = useOnLayoutSync({
4647
5191
  onLayoutChange,
@@ -4753,7 +5297,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4753
5297
  onScroll: onScrollHandler,
4754
5298
  recycleItems,
4755
5299
  refreshControl: refreshControlElement ? stylePaddingTopState > 0 ? React3.cloneElement(refreshControlElement, {
4756
- progressViewOffset: ((_d = refreshControlElement.props.progressViewOffset) != null ? _d : 0) + stylePaddingTopState
5300
+ progressViewOffset: ((_g = refreshControlElement.props.progressViewOffset) != null ? _g : 0) + stylePaddingTopState
4757
5301
  }) : refreshControlElement : onRefresh && /* @__PURE__ */ React3.createElement(
4758
5302
  RefreshControl,
4759
5303
  {
@@ -4764,7 +5308,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4764
5308
  ),
4765
5309
  refScrollView: combinedRef,
4766
5310
  renderScrollComponent,
4767
- scrollAdjustHandler: (_e = refState.current) == null ? void 0 : _e.scrollAdjustHandler,
5311
+ scrollAdjustHandler: (_h = refState.current) == null ? void 0 : _h.scrollAdjustHandler,
4768
5312
  scrollEventThrottle: 0,
4769
5313
  snapToIndices,
4770
5314
  stickyHeaderIndices,
@@ -4776,7 +5320,20 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4776
5320
  ), IS_DEV && ENABLE_DEBUG_VIEW);
4777
5321
  });
4778
5322
 
5323
+ // src/entrypoints/shared.ts
5324
+ var LegendListRuntime = LegendList;
5325
+ var internal = {
5326
+ getComponent,
5327
+ IsNewArchitecture,
5328
+ POSITION_OUT_OF_VIEW,
5329
+ peek$,
5330
+ useArr$,
5331
+ useCombinedRef,
5332
+ useStateContext
5333
+ };
5334
+
4779
5335
  // src/react.ts
4780
- var LegendList3 = LegendList;
5336
+ var LegendList3 = LegendListRuntime;
5337
+ var internal2 = internal;
4781
5338
 
4782
- export { LegendList3 as LegendList, typedForwardRef, typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };
5339
+ export { LegendList3 as LegendList, internal2 as internal, typedForwardRef, typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };