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

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,82 @@ 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 maybeApplyPredictedNativeMVCPAdjust(ctx) {
2103
+ const state = ctx.state;
2104
+ const pending = state.pendingNativeMVCPAdjust;
2105
+ if (!pending || Math.abs(pending.manualApplied) > MVCP_POSITION_EPSILON) {
2106
+ return;
2107
+ }
2108
+ const totalSize = getContentSize(ctx);
2109
+ const predictedNativeClamp = getPredictedNativeClamp(state, pending.amount, totalSize);
2110
+ if (Math.abs(predictedNativeClamp) <= MVCP_POSITION_EPSILON) {
2111
+ return;
2112
+ }
2113
+ const manualDesired = pending.amount - predictedNativeClamp;
2114
+ if (Math.abs(manualDesired) <= MVCP_POSITION_EPSILON) {
2115
+ return;
2116
+ }
2117
+ pending.manualApplied = manualDesired;
2118
+ requestAdjust(ctx, manualDesired);
2119
+ }
2120
+ function resolvePendingNativeMVCPAdjust(ctx, newScroll) {
2121
+ const state = ctx.state;
2122
+ const pending = state.pendingNativeMVCPAdjust;
2123
+ if (!pending) {
2124
+ return false;
2125
+ }
2126
+ const remainingAfterManual = pending.amount - pending.manualApplied;
2127
+ const nativeDelta = newScroll - (pending.startScroll + pending.manualApplied);
2128
+ const isWrongDirection = remainingAfterManual < 0 && nativeDelta > MVCP_POSITION_EPSILON || remainingAfterManual > 0 && nativeDelta < -MVCP_POSITION_EPSILON;
2129
+ if (Math.abs(remainingAfterManual) <= MVCP_POSITION_EPSILON) {
2130
+ state.pendingNativeMVCPAdjust = void 0;
2131
+ return true;
2132
+ }
2133
+ if (isWrongDirection) {
2134
+ state.pendingNativeMVCPAdjust = void 0;
2135
+ return false;
2136
+ }
2137
+ const expectedNativeClampScroll = Math.max(0, getContentSize(ctx) - state.scrollLength);
2138
+ const distanceToClamp = Math.abs(newScroll - expectedNativeClampScroll);
2139
+ const didApproachClamp = distanceToClamp < pending.closestDistanceToClamp - MVCP_POSITION_EPSILON;
2140
+ const didMoveAwayAfterApproach = pending.hasApproachedClamp && distanceToClamp > pending.closestDistanceToClamp + MVCP_POSITION_EPSILON;
2141
+ if (didApproachClamp) {
2142
+ pending.closestDistanceToClamp = distanceToClamp;
2143
+ pending.hasApproachedClamp = true;
2144
+ } else if (didMoveAwayAfterApproach) {
2145
+ state.pendingNativeMVCPAdjust = void 0;
2146
+ return false;
2147
+ }
2148
+ const isAtExpectedNativeClamp = distanceToClamp <= NATIVE_END_CLAMP_EPSILON;
2149
+ if (!isAtExpectedNativeClamp) {
2150
+ return false;
2151
+ }
2152
+ state.pendingNativeMVCPAdjust = void 0;
2153
+ const remaining = remainingAfterManual - nativeDelta;
2154
+ if (Math.abs(remaining) > MVCP_POSITION_EPSILON) {
2155
+ requestAdjust(ctx, remaining);
2156
+ }
2157
+ return true;
2158
+ }
2042
2159
  function prepareMVCP(ctx, dataChanged) {
2043
2160
  const state = ctx.state;
2044
2161
  const { idsInView, positions, props } = state;
@@ -2057,6 +2174,8 @@ function prepareMVCP(ctx, dataChanged) {
2057
2174
  const isEndAnchoredScrollTarget = scrollTarget !== void 0 && state.props.data.length > 0 && scrollTarget >= state.props.data.length - 1 && (scrollingToViewPosition != null ? scrollingToViewPosition : 0) > 0;
2058
2175
  const shouldMVCP = dataChanged ? mvcpData : mvcpScroll;
2059
2176
  const indexByKey = state.indexByKey;
2177
+ const prevScroll = state.scroll;
2178
+ getContentSize(ctx);
2060
2179
  if (shouldMVCP) {
2061
2180
  if (anchorLock && scrollTarget === void 0) {
2062
2181
  targetId = anchorLock.id;
@@ -2163,6 +2282,19 @@ function prepareMVCP(ctx, dataChanged) {
2163
2282
  now,
2164
2283
  positionDiff
2165
2284
  });
2285
+ if (shouldQueueNativeMVCPAdjust()) {
2286
+ state.pendingNativeMVCPAdjust = {
2287
+ amount: positionDiff,
2288
+ closestDistanceToClamp: Math.abs(
2289
+ prevScroll - Math.max(0, getContentSize(ctx) - state.scrollLength)
2290
+ ),
2291
+ hasApproachedClamp: false,
2292
+ manualApplied: 0,
2293
+ startScroll: prevScroll
2294
+ };
2295
+ maybeApplyPredictedNativeMVCPAdjust(ctx);
2296
+ return;
2297
+ }
2166
2298
  if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
2167
2299
  requestAdjust(ctx, positionDiff);
2168
2300
  }
@@ -2170,6 +2302,94 @@ function prepareMVCP(ctx, dataChanged) {
2170
2302
  }
2171
2303
  }
2172
2304
 
2305
+ // src/core/updateScroll.ts
2306
+ function updateScroll(ctx, newScroll, forceUpdate) {
2307
+ var _a3;
2308
+ const state = ctx.state;
2309
+ const { ignoreScrollFromMVCP, lastScrollAdjustForHistory, scrollAdjustHandler, scrollHistory, scrollingTo } = state;
2310
+ const prevScroll = state.scroll;
2311
+ state.hasScrolled = true;
2312
+ state.lastBatchingAction = Date.now();
2313
+ const currentTime = Date.now();
2314
+ const adjust = scrollAdjustHandler.getAdjust();
2315
+ const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
2316
+ if (adjustChanged) {
2317
+ scrollHistory.length = 0;
2318
+ }
2319
+ state.lastScrollAdjustForHistory = adjust;
2320
+ if (scrollingTo === void 0 && !(scrollHistory.length === 0 && newScroll === state.scroll)) {
2321
+ if (!adjustChanged) {
2322
+ scrollHistory.push({ scroll: newScroll, time: currentTime });
2323
+ }
2324
+ }
2325
+ if (scrollHistory.length > 5) {
2326
+ scrollHistory.shift();
2327
+ }
2328
+ if (ignoreScrollFromMVCP && !scrollingTo) {
2329
+ const { lt, gt } = ignoreScrollFromMVCP;
2330
+ if (lt && newScroll < lt || gt && newScroll > gt) {
2331
+ state.ignoreScrollFromMVCPIgnored = true;
2332
+ return;
2333
+ }
2334
+ }
2335
+ state.scrollPrev = prevScroll;
2336
+ state.scrollPrevTime = state.scrollTime;
2337
+ state.scroll = newScroll;
2338
+ state.scrollTime = currentTime;
2339
+ const scrollDelta = Math.abs(newScroll - prevScroll);
2340
+ const didResolvePendingNativeMVCPAdjust = resolvePendingNativeMVCPAdjust(ctx, newScroll);
2341
+ const scrollLength = state.scrollLength;
2342
+ const lastCalculated = state.scrollLastCalculate;
2343
+ const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
2344
+ const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
2345
+ if (shouldUpdate) {
2346
+ state.scrollLastCalculate = state.scroll;
2347
+ state.ignoreScrollFromMVCPIgnored = false;
2348
+ state.lastScrollDelta = scrollDelta;
2349
+ const runCalculateItems = () => {
2350
+ var _a4;
2351
+ (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { doMVCP: scrollingTo !== void 0 });
2352
+ checkThresholds(ctx);
2353
+ };
2354
+ if (scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength) {
2355
+ flushSync(runCalculateItems);
2356
+ } else {
2357
+ runCalculateItems();
2358
+ }
2359
+ const shouldMaintainScrollAtEndAfterPendingSettle = !!state.pendingMaintainScrollAtEnd || !!((_a3 = state.props.maintainScrollAtEnd) == null ? void 0 : _a3.onDataChange);
2360
+ if (didResolvePendingNativeMVCPAdjust && shouldMaintainScrollAtEndAfterPendingSettle) {
2361
+ state.pendingMaintainScrollAtEnd = false;
2362
+ doMaintainScrollAtEnd(ctx);
2363
+ }
2364
+ state.dataChangeNeedsScrollUpdate = false;
2365
+ state.lastScrollDelta = 0;
2366
+ }
2367
+ }
2368
+
2369
+ // src/utils/requestAdjust.ts
2370
+ function requestAdjust(ctx, positionDiff, dataChanged) {
2371
+ const state = ctx.state;
2372
+ if (Math.abs(positionDiff) > 0.1) {
2373
+ const doit = () => {
2374
+ {
2375
+ state.scrollAdjustHandler.requestAdjust(positionDiff);
2376
+ if (state.adjustingFromInitialMount) {
2377
+ state.adjustingFromInitialMount--;
2378
+ }
2379
+ }
2380
+ };
2381
+ state.scroll += positionDiff;
2382
+ state.scrollForNextCalculateItemsInView = void 0;
2383
+ const readyToRender = peek$(ctx, "readyToRender");
2384
+ if (readyToRender) {
2385
+ doit();
2386
+ } else {
2387
+ state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
2388
+ requestAnimationFrame(doit);
2389
+ }
2390
+ }
2391
+ }
2392
+
2173
2393
  // src/core/prepareColumnStartState.ts
2174
2394
  function prepareColumnStartState(ctx, startIndex, useAverageSize) {
2175
2395
  var _a3;
@@ -2823,7 +3043,14 @@ function comparatorByDistance(a, b) {
2823
3043
  }
2824
3044
 
2825
3045
  // src/core/scrollToIndex.ts
2826
- function scrollToIndex(ctx, { index, viewOffset = 0, animated = true, viewPosition }) {
3046
+ function scrollToIndex(ctx, {
3047
+ index,
3048
+ viewOffset = 0,
3049
+ animated = true,
3050
+ forceScroll,
3051
+ isInitialScroll,
3052
+ viewPosition
3053
+ }) {
2827
3054
  const state = ctx.state;
2828
3055
  const { data } = state.props;
2829
3056
  if (index >= data.length) {
@@ -2841,7 +3068,9 @@ function scrollToIndex(ctx, { index, viewOffset = 0, animated = true, viewPositi
2841
3068
  const itemSize = getItemSize(ctx, targetId, index, state.props.data[index]);
2842
3069
  scrollTo(ctx, {
2843
3070
  animated,
3071
+ forceScroll,
2844
3072
  index,
3073
+ isInitialScroll,
2845
3074
  itemSize,
2846
3075
  offset: firstIndexOffset,
2847
3076
  viewOffset,
@@ -2849,15 +3078,58 @@ function scrollToIndex(ctx, { index, viewOffset = 0, animated = true, viewPositi
2849
3078
  });
2850
3079
  }
2851
3080
 
3081
+ // src/utils/performInitialScroll.ts
3082
+ function performInitialScroll(ctx, params) {
3083
+ var _a3;
3084
+ const { forceScroll, initialScrollUsesOffset, resolvedOffset, target } = params;
3085
+ if (initialScrollUsesOffset || resolvedOffset !== void 0) {
3086
+ scrollTo(ctx, {
3087
+ animated: false,
3088
+ forceScroll,
3089
+ index: initialScrollUsesOffset ? void 0 : target.index,
3090
+ isInitialScroll: true,
3091
+ offset: (_a3 = resolvedOffset != null ? resolvedOffset : target.contentOffset) != null ? _a3 : 0,
3092
+ precomputedWithViewOffset: resolvedOffset !== void 0
3093
+ });
3094
+ return;
3095
+ }
3096
+ if (target.index === void 0) {
3097
+ return;
3098
+ }
3099
+ scrollToIndex(ctx, {
3100
+ ...target,
3101
+ animated: false,
3102
+ forceScroll,
3103
+ isInitialScroll: true
3104
+ });
3105
+ }
3106
+
2852
3107
  // src/utils/setDidLayout.ts
2853
3108
  function setDidLayout(ctx) {
2854
3109
  const state = ctx.state;
2855
3110
  const { initialScroll } = state;
2856
3111
  state.queuedInitialLayout = true;
2857
3112
  checkAtBottom(ctx);
2858
- if ((initialScroll == null ? void 0 : initialScroll.index) !== void 0) {
2859
- const target = initialScroll;
2860
- const runScroll = () => scrollToIndex(ctx, { ...target, animated: false });
3113
+ if (initialScroll) {
3114
+ const runScroll = () => {
3115
+ var _a3, _b;
3116
+ const target = state.initialScroll;
3117
+ if (!target) {
3118
+ return;
3119
+ }
3120
+ const activeInitialTargetOffset = ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) ? (_b = state.scrollingTo.targetOffset) != null ? _b : state.scrollingTo.offset : void 0;
3121
+ const desiredInitialTargetOffset = state.initialScrollUsesOffset ? target.contentOffset : activeInitialTargetOffset;
3122
+ const isAlreadyAtDesiredInitialTarget = desiredInitialTargetOffset !== void 0 && Math.abs(state.scroll - desiredInitialTargetOffset) <= 1 && Math.abs(state.scrollPending - desiredInitialTargetOffset) <= 1;
3123
+ if (!isAlreadyAtDesiredInitialTarget) {
3124
+ performInitialScroll(ctx, {
3125
+ forceScroll: true,
3126
+ initialScrollUsesOffset: state.initialScrollUsesOffset,
3127
+ // Offset-based initial scrolls do not need item lookup, so they can run even before data exists.
3128
+ // Re-run on the next frame to pick up measured viewport size without waiting for index resolution.
3129
+ target
3130
+ });
3131
+ }
3132
+ };
2861
3133
  runScroll();
2862
3134
  requestAnimationFrame(runScroll);
2863
3135
  }
@@ -2935,7 +3207,7 @@ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentSt
2935
3207
  function calculateItemsInView(ctx, params = {}) {
2936
3208
  const state = ctx.state;
2937
3209
  batchedUpdates(() => {
2938
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
3210
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
2939
3211
  const {
2940
3212
  columns,
2941
3213
  columnSpans,
@@ -2971,7 +3243,7 @@ function calculateItemsInView(ctx, params = {}) {
2971
3243
  if (!data || scrollLength === 0 || !prevNumContainers) {
2972
3244
  return;
2973
3245
  }
2974
- const totalSize = getContentSize(ctx);
3246
+ let totalSize = getContentSize(ctx);
2975
3247
  const topPad = peek$(ctx, "stylePaddingTop") + peek$(ctx, "headerSize");
2976
3248
  const numColumns = peek$(ctx, "numColumns");
2977
3249
  const speed = getScrollVelocity(state);
@@ -2979,14 +3251,14 @@ function calculateItemsInView(ctx, params = {}) {
2979
3251
  const { queuedInitialLayout } = state;
2980
3252
  let { scroll: scrollState } = state;
2981
3253
  if (!queuedInitialLayout && initialScroll) {
2982
- const updatedOffset = calculateOffsetWithOffsetPosition(
3254
+ const updatedOffset = state.initialScrollUsesOffset ? (_a3 = initialScroll.contentOffset) != null ? _a3 : 0 : calculateOffsetWithOffsetPosition(
2983
3255
  ctx,
2984
3256
  calculateOffsetForIndex(ctx, initialScroll.index),
2985
3257
  initialScroll
2986
3258
  );
2987
3259
  scrollState = updatedOffset;
2988
3260
  }
2989
- const scrollAdjustPending = (_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0;
3261
+ const scrollAdjustPending = (_b = peek$(ctx, "scrollAdjustPending")) != null ? _b : 0;
2990
3262
  const scrollAdjustPad = scrollAdjustPending - topPad;
2991
3263
  let scroll = Math.round(scrollState + scrollExtra + scrollAdjustPad);
2992
3264
  if (scroll + scrollLength > totalSize) {
@@ -3028,13 +3300,14 @@ function calculateItemsInView(ctx, params = {}) {
3028
3300
  columns.length = 0;
3029
3301
  columnSpans.length = 0;
3030
3302
  }
3031
- const startIndex = forceFullItemPositions || dataChanged ? 0 : (_b = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _b : 0;
3303
+ const startIndex = forceFullItemPositions || dataChanged ? 0 : (_c = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _c : 0;
3032
3304
  updateItemPositions(ctx, dataChanged, {
3033
3305
  doMVCP,
3034
3306
  forceFullUpdate: !!forceFullItemPositions,
3035
3307
  scrollBottomBuffered,
3036
3308
  startIndex
3037
3309
  });
3310
+ totalSize = getContentSize(ctx);
3038
3311
  if (minIndexSizeChanged !== void 0) {
3039
3312
  state.minIndexSizeChanged = void 0;
3040
3313
  }
@@ -3046,9 +3319,9 @@ function calculateItemsInView(ctx, params = {}) {
3046
3319
  let endBuffered = null;
3047
3320
  let loopStart = !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
3048
3321
  for (let i = loopStart; i >= 0; i--) {
3049
- const id = (_c = idCache[i]) != null ? _c : getId(state, i);
3322
+ const id = (_d = idCache[i]) != null ? _d : getId(state, i);
3050
3323
  const top = positions[i];
3051
- const size = (_d = sizes.get(id)) != null ? _d : getItemSize(ctx, id, i, data[i]);
3324
+ const size = (_e = sizes.get(id)) != null ? _e : getItemSize(ctx, id, i, data[i]);
3052
3325
  const bottom = top + size;
3053
3326
  if (bottom > scroll - scrollBufferTop) {
3054
3327
  loopStart = i;
@@ -3079,14 +3352,14 @@ function calculateItemsInView(ctx, params = {}) {
3079
3352
  let firstFullyOnScreenIndex;
3080
3353
  const dataLength = data.length;
3081
3354
  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]);
3355
+ const id = (_f = idCache[i]) != null ? _f : getId(state, i);
3356
+ const size = (_g = sizes.get(id)) != null ? _g : getItemSize(ctx, id, i, data[i]);
3084
3357
  const top = positions[i];
3085
3358
  if (!foundEnd) {
3086
3359
  if (startNoBuffer === null && top + size > scroll) {
3087
3360
  startNoBuffer = i;
3088
3361
  }
3089
- if (firstFullyOnScreenIndex === void 0 && top >= scroll - 10) {
3362
+ if (firstFullyOnScreenIndex === void 0 && top >= scroll - 10 && top <= scrollBottom) {
3090
3363
  firstFullyOnScreenIndex = i;
3091
3364
  }
3092
3365
  if (startBuffered === null && top + size > scrollTopBuffered) {
@@ -3116,9 +3389,12 @@ function calculateItemsInView(ctx, params = {}) {
3116
3389
  }
3117
3390
  }
3118
3391
  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);
3392
+ const firstVisibleAnchorIndex = firstFullyOnScreenIndex != null ? firstFullyOnScreenIndex : startNoBuffer;
3393
+ if (firstVisibleAnchorIndex !== null && firstVisibleAnchorIndex !== void 0 && endNoBuffer !== null) {
3394
+ for (let i = firstVisibleAnchorIndex; i <= endNoBuffer; i++) {
3395
+ const id = (_h = idCache[i]) != null ? _h : getId(state, i);
3396
+ idsInView.push(id);
3397
+ }
3122
3398
  }
3123
3399
  Object.assign(state, {
3124
3400
  endBuffered,
@@ -3149,7 +3425,7 @@ function calculateItemsInView(ctx, params = {}) {
3149
3425
  const needNewContainers = [];
3150
3426
  const needNewContainersSet = /* @__PURE__ */ new Set();
3151
3427
  for (let i = startBuffered; i <= endBuffered; i++) {
3152
- const id = (_h = idCache[i]) != null ? _h : getId(state, i);
3428
+ const id = (_i = idCache[i]) != null ? _i : getId(state, i);
3153
3429
  if (!containerItemKeys.has(id)) {
3154
3430
  needNewContainersSet.add(i);
3155
3431
  needNewContainers.push(i);
@@ -3158,7 +3434,7 @@ function calculateItemsInView(ctx, params = {}) {
3158
3434
  if (alwaysRenderArr.length > 0) {
3159
3435
  for (const index of alwaysRenderArr) {
3160
3436
  if (index < 0 || index >= dataLength) continue;
3161
- const id = (_i = idCache[index]) != null ? _i : getId(state, index);
3437
+ const id = (_j = idCache[index]) != null ? _j : getId(state, index);
3162
3438
  if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
3163
3439
  needNewContainersSet.add(index);
3164
3440
  needNewContainers.push(index);
@@ -3196,7 +3472,7 @@ function calculateItemsInView(ctx, params = {}) {
3196
3472
  for (let idx = 0; idx < needNewContainers.length; idx++) {
3197
3473
  const i = needNewContainers[idx];
3198
3474
  const containerIndex = availableContainers[idx];
3199
- const id = (_j = idCache[i]) != null ? _j : getId(state, i);
3475
+ const id = (_k = idCache[i]) != null ? _k : getId(state, i);
3200
3476
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
3201
3477
  if (oldKey && oldKey !== id) {
3202
3478
  containerItemKeys.delete(oldKey);
@@ -3237,7 +3513,7 @@ function calculateItemsInView(ctx, params = {}) {
3237
3513
  if (alwaysRenderArr.length > 0) {
3238
3514
  for (const index of alwaysRenderArr) {
3239
3515
  if (index < 0 || index >= dataLength) continue;
3240
- const id = (_k = idCache[index]) != null ? _k : getId(state, index);
3516
+ const id = (_l = idCache[index]) != null ? _l : getId(state, index);
3241
3517
  const containerIndex = containerItemKeys.get(id);
3242
3518
  if (containerIndex !== void 0) {
3243
3519
  state.stickyContainerPool.add(containerIndex);
@@ -3346,21 +3622,21 @@ function checkActualChange(state, dataProp, previousData) {
3346
3622
  }
3347
3623
 
3348
3624
  // src/core/checkFinishedScroll.ts
3625
+ var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
3626
+ var INITIAL_SCROLL_MAX_FALLBACK_CHECKS = 20;
3627
+ var INITIAL_SCROLL_ZERO_TARGET_EPSILON = 1;
3349
3628
  function checkFinishedScroll(ctx) {
3350
3629
  ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
3351
3630
  }
3352
3631
  function checkFinishedScrollFrame(ctx) {
3632
+ var _a3;
3353
3633
  const scrollingTo = ctx.state.scrollingTo;
3354
3634
  if (scrollingTo) {
3355
3635
  const { state } = ctx;
3356
3636
  state.animFrameCheckFinishedScroll = void 0;
3357
3637
  const scroll = state.scrollPending;
3358
3638
  const adjust = state.scrollAdjustHandler.getAdjust();
3359
- const clampedTargetOffset = clampScrollOffset(
3360
- ctx,
3361
- scrollingTo.offset - (scrollingTo.viewOffset || 0),
3362
- scrollingTo
3363
- );
3639
+ const clampedTargetOffset = (_a3 = scrollingTo.targetOffset) != null ? _a3 : clampScrollOffset(ctx, scrollingTo.offset - (scrollingTo.viewOffset || 0), scrollingTo);
3364
3640
  const maxOffset = clampScrollOffset(ctx, scroll, scrollingTo);
3365
3641
  const diff1 = Math.abs(scroll - clampedTargetOffset);
3366
3642
  const diff2 = Math.abs(diff1 - adjust);
@@ -3374,17 +3650,33 @@ function checkFinishedScrollFrame(ctx) {
3374
3650
  function checkFinishedScrollFallback(ctx) {
3375
3651
  const state = ctx.state;
3376
3652
  const scrollingTo = state.scrollingTo;
3377
- const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) || !state.didContainersLayout;
3653
+ const shouldFinishInitialZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
3654
+ const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && !shouldFinishInitialZeroTarget || !state.didContainersLayout;
3378
3655
  state.timeoutCheckFinishedScrollFallback = setTimeout(
3379
3656
  () => {
3380
3657
  let numChecks = 0;
3381
3658
  const checkHasScrolled = () => {
3659
+ var _a3, _b;
3382
3660
  state.timeoutCheckFinishedScrollFallback = void 0;
3383
3661
  const isStillScrollingTo = state.scrollingTo;
3384
3662
  if (isStillScrollingTo) {
3385
3663
  numChecks++;
3386
- if (state.hasScrolled || numChecks > 5) {
3664
+ const isNativeInitialPending = isNativeInitialNonZeroTarget(state) && !state.hasScrolled;
3665
+ const maxChecks = isNativeInitialPending ? INITIAL_SCROLL_MAX_FALLBACK_CHECKS : 5;
3666
+ const shouldFinishZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
3667
+ if (shouldFinishZeroTarget || state.hasScrolled || numChecks > maxChecks) {
3387
3668
  finishScrollTo(ctx);
3669
+ } else if (isNativeInitialPending && numChecks <= maxChecks) {
3670
+ const targetOffset = (_b = (_a3 = state.initialNativeScrollWatchdog) == null ? void 0 : _a3.targetOffset) != null ? _b : state.scrollPending;
3671
+ const scroller = state.refScroller.current;
3672
+ if (scroller) {
3673
+ scroller.scrollTo({
3674
+ animated: false,
3675
+ x: state.props.horizontal ? targetOffset : 0,
3676
+ y: state.props.horizontal ? 0 : targetOffset
3677
+ });
3678
+ }
3679
+ state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, 100);
3388
3680
  } else {
3389
3681
  state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, 100);
3390
3682
  }
@@ -3395,39 +3687,13 @@ function checkFinishedScrollFallback(ctx) {
3395
3687
  slowTimeout ? 500 : 100
3396
3688
  );
3397
3689
  }
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;
3690
+ function isNativeInitialNonZeroTarget(state) {
3691
+ return !state.didFinishInitialScroll && !!state.initialNativeScrollWatchdog && state.initialNativeScrollWatchdog.targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
3692
+ }
3693
+ function shouldFinishInitialZeroTargetScroll(ctx) {
3694
+ var _a3;
3695
+ const { state } = ctx;
3696
+ 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
3697
  }
3432
3698
 
3433
3699
  // src/utils/updateAveragesOnDataChange.ts
@@ -3491,8 +3757,8 @@ function checkResetContainers(ctx, dataProp) {
3491
3757
  }
3492
3758
  const { maintainScrollAtEnd } = state.props;
3493
3759
  calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
3494
- const shouldMaintainScrollAtEnd = maintainScrollAtEnd === true || maintainScrollAtEnd.onDataChange;
3495
- const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, false);
3760
+ const shouldMaintainScrollAtEnd = maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onDataChange;
3761
+ const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx);
3496
3762
  if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
3497
3763
  state.isEndReached = false;
3498
3764
  }
@@ -3600,8 +3866,8 @@ function handleLayout(ctx, layoutParam, setCanRender) {
3600
3866
  if (didChange || otherAxisSize !== prevOtherAxisSize) {
3601
3867
  set$(ctx, "scrollSize", { height: layout.height, width: layout.width });
3602
3868
  }
3603
- if (maintainScrollAtEnd === true || maintainScrollAtEnd.onLayout) {
3604
- doMaintainScrollAtEnd(ctx, false);
3869
+ if (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onLayout) {
3870
+ doMaintainScrollAtEnd(ctx);
3605
3871
  }
3606
3872
  checkThresholds(ctx);
3607
3873
  if (state) {
@@ -3618,6 +3884,12 @@ function handleLayout(ctx, layoutParam, setCanRender) {
3618
3884
  }
3619
3885
 
3620
3886
  // src/core/onScroll.ts
3887
+ var INITIAL_SCROLL_PROGRESS_EPSILON = 1;
3888
+ function didObserveInitialScrollProgress(newScroll, watchdog) {
3889
+ const previousDistance = Math.abs(watchdog.startScroll - watchdog.targetOffset);
3890
+ const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
3891
+ return nextDistance <= INITIAL_SCROLL_PROGRESS_EPSILON || nextDistance + INITIAL_SCROLL_PROGRESS_EPSILON < previousDistance;
3892
+ }
3621
3893
  function onScroll(ctx, event) {
3622
3894
  var _a3, _b, _c, _d;
3623
3895
  const state = ctx.state;
@@ -3655,7 +3927,16 @@ function onScroll(ctx, event) {
3655
3927
  }
3656
3928
  }
3657
3929
  state.scrollPending = newScroll;
3930
+ const initialNativeScrollWatchdog = state.initialNativeScrollWatchdog;
3931
+ const didInitialScrollProgress = !!initialNativeScrollWatchdog && didObserveInitialScrollProgress(newScroll, initialNativeScrollWatchdog);
3932
+ if (didInitialScrollProgress) {
3933
+ state.initialNativeScrollWatchdog = void 0;
3934
+ }
3658
3935
  updateScroll(ctx, newScroll, insetChanged);
3936
+ if (initialNativeScrollWatchdog && !didInitialScrollProgress) {
3937
+ state.hasScrolled = false;
3938
+ state.initialNativeScrollWatchdog = initialNativeScrollWatchdog;
3939
+ }
3659
3940
  if (state.scrollingTo) {
3660
3941
  checkFinishedScroll(ctx);
3661
3942
  }
@@ -3820,8 +4101,8 @@ function updateItemSize(ctx, itemKey, sizeObj) {
3820
4101
  runOrScheduleMVCPRecalculate(ctx);
3821
4102
  }
3822
4103
  if (shouldMaintainScrollAtEnd) {
3823
- if (maintainScrollAtEnd === true || maintainScrollAtEnd.onItemLayout) {
3824
- doMaintainScrollAtEnd(ctx, false);
4104
+ if (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onItemLayout) {
4105
+ doMaintainScrollAtEnd(ctx);
3825
4106
  }
3826
4107
  }
3827
4108
  }
@@ -4171,6 +4452,34 @@ function getRenderedItem(ctx, key) {
4171
4452
  return { index, item: data[index], renderedItem };
4172
4453
  }
4173
4454
 
4455
+ // src/utils/normalizeMaintainScrollAtEnd.ts
4456
+ function normalizeMaintainScrollAtEndOn(on, hasExplicitOn) {
4457
+ var _a3, _b, _c;
4458
+ return {
4459
+ animated: false,
4460
+ onDataChange: hasExplicitOn ? (_a3 = on == null ? void 0 : on.dataChange) != null ? _a3 : false : true,
4461
+ onItemLayout: hasExplicitOn ? (_b = on == null ? void 0 : on.itemLayout) != null ? _b : false : true,
4462
+ onLayout: hasExplicitOn ? (_c = on == null ? void 0 : on.layout) != null ? _c : false : true
4463
+ };
4464
+ }
4465
+ function normalizeMaintainScrollAtEnd(value) {
4466
+ var _a3;
4467
+ if (!value) {
4468
+ return void 0;
4469
+ }
4470
+ if (value === true) {
4471
+ return {
4472
+ ...normalizeMaintainScrollAtEndOn(void 0, false),
4473
+ animated: false
4474
+ };
4475
+ }
4476
+ const normalizedTriggers = normalizeMaintainScrollAtEndOn(value.on, "on" in value);
4477
+ return {
4478
+ ...normalizedTriggers,
4479
+ animated: (_a3 = value.animated) != null ? _a3 : false
4480
+ };
4481
+ }
4482
+
4174
4483
  // src/utils/normalizeMaintainVisibleContentPosition.ts
4175
4484
  function normalizeMaintainVisibleContentPosition(value) {
4176
4485
  var _a3, _b;
@@ -4272,7 +4581,7 @@ var LegendList = typedMemo(
4272
4581
  })
4273
4582
  );
4274
4583
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
4275
- var _a3, _b, _c, _d, _e;
4584
+ var _a3, _b, _c, _d, _e, _f, _g, _h;
4276
4585
  const {
4277
4586
  alignItemsAtEnd = false,
4278
4587
  alwaysRender,
@@ -4358,18 +4667,26 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4358
4667
  const style = { ...StyleSheet.flatten(styleProp) };
4359
4668
  const stylePaddingTopState = extractPadding(style, contentContainerStyle, "Top");
4360
4669
  const stylePaddingBottomState = extractPadding(style, contentContainerStyle, "Bottom");
4670
+ const maintainScrollAtEndConfig = normalizeMaintainScrollAtEnd(maintainScrollAtEnd);
4361
4671
  const maintainVisibleContentPositionConfig = normalizeMaintainVisibleContentPosition(
4362
4672
  maintainVisibleContentPositionProp
4363
4673
  );
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
4674
+ const hasInitialScrollIndex = initialScrollIndexProp !== void 0 && initialScrollIndexProp !== null;
4675
+ const hasInitialScrollOffset = initialScrollOffsetProp !== void 0 && initialScrollOffsetProp !== null;
4676
+ const initialScrollUsesOffsetOnly = !initialScrollAtEnd && !hasInitialScrollIndex && hasInitialScrollOffset;
4677
+ const initialScrollProp = initialScrollAtEnd ? { index: Math.max(0, dataProp.length - 1), viewOffset: -stylePaddingBottomState, viewPosition: 1 } : hasInitialScrollIndex ? typeof initialScrollIndexProp === "object" ? {
4678
+ index: (_a3 = initialScrollIndexProp.index) != null ? _a3 : 0,
4679
+ viewOffset: (_b = initialScrollIndexProp.viewOffset) != null ? _b : initialScrollIndexProp.viewPosition === 1 ? -stylePaddingBottomState : 0,
4680
+ viewPosition: (_c = initialScrollIndexProp.viewPosition) != null ? _c : 0
4368
4681
  } : {
4369
- index: initialScrollIndexProp || 0,
4370
- viewOffset: initialScrollOffsetProp || 0
4682
+ index: initialScrollIndexProp != null ? initialScrollIndexProp : 0,
4683
+ viewOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : 0
4684
+ } : initialScrollUsesOffsetOnly ? {
4685
+ contentOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : 0,
4686
+ index: 0,
4687
+ viewOffset: 0
4371
4688
  } : void 0;
4372
- const [canRender, setCanRender] = React3.useState(false);
4689
+ const [canRender, setCanRender] = React3.useState(!IsNewArchitecture);
4373
4690
  const ctx = useStateContext();
4374
4691
  ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
4375
4692
  const refScroller = useRef(null);
@@ -4382,8 +4699,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4382
4699
  }, [
4383
4700
  alwaysRender == null ? void 0 : alwaysRender.top,
4384
4701
  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(","),
4702
+ (_d = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _d.join(","),
4703
+ (_e = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _e.join(","),
4387
4704
  dataProp,
4388
4705
  dataVersion,
4389
4706
  keyExtractor
@@ -4427,14 +4744,22 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4427
4744
  idCache: [],
4428
4745
  idsInView: [],
4429
4746
  indexByKey: /* @__PURE__ */ new Map(),
4430
- initialAnchor: (initialScrollProp == null ? void 0 : initialScrollProp.index) !== void 0 && (initialScrollProp == null ? void 0 : initialScrollProp.viewPosition) !== void 0 ? {
4747
+ initialAnchor: !initialScrollUsesOffsetOnly && (initialScrollProp == null ? void 0 : initialScrollProp.index) !== void 0 && (initialScrollProp == null ? void 0 : initialScrollProp.viewPosition) !== void 0 ? {
4431
4748
  attempts: 0,
4432
4749
  index: initialScrollProp.index,
4433
4750
  settledTicks: 0,
4434
- viewOffset: (_c = initialScrollProp.viewOffset) != null ? _c : 0,
4751
+ viewOffset: (_f = initialScrollProp.viewOffset) != null ? _f : 0,
4435
4752
  viewPosition: initialScrollProp.viewPosition
4436
4753
  } : void 0,
4754
+ initialNativeScrollWatchdog: void 0,
4437
4755
  initialScroll: initialScrollProp,
4756
+ initialScrollLastDidFinish: false,
4757
+ initialScrollLastTarget: initialScrollProp,
4758
+ initialScrollLastTargetUsesOffset: initialScrollUsesOffsetOnly,
4759
+ initialScrollPreviousDataLength: dataProp.length,
4760
+ initialScrollRetryLastLength: void 0,
4761
+ initialScrollRetryWindowUntil: 0,
4762
+ initialScrollUsesOffset: initialScrollUsesOffsetOnly,
4438
4763
  isAtEnd: false,
4439
4764
  isAtStart: false,
4440
4765
  isEndReached: null,
@@ -4447,6 +4772,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4447
4772
  minIndexSizeChanged: 0,
4448
4773
  nativeContentInset: void 0,
4449
4774
  nativeMarginTop: 0,
4775
+ pendingNativeMVCPAdjust: void 0,
4450
4776
  positions: [],
4451
4777
  props: {},
4452
4778
  queuedCalculateItemsInView: 0,
@@ -4484,7 +4810,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4484
4810
  const state = refState.current;
4485
4811
  const isFirstLocal = state.isFirst;
4486
4812
  state.didColumnsChange = numColumnsProp !== state.props.numColumns;
4487
- const didDataChangeLocal = state.props.dataVersion !== dataVersion || state.props.data !== dataProp && checkActualChange(state, dataProp, state.props.data);
4813
+ const didDataReferenceChangeLocal = state.props.data !== dataProp;
4814
+ const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
4815
+ const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkActualChange(state, dataProp, state.props.data);
4488
4816
  if (didDataChangeLocal) {
4489
4817
  state.dataChangeEpoch += 1;
4490
4818
  state.dataChangeNeedsScrollUpdate = true;
@@ -4510,7 +4838,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4510
4838
  initialContainerPoolRatio,
4511
4839
  itemsAreEqual,
4512
4840
  keyExtractor: useWrapIfItem(keyExtractor),
4513
- maintainScrollAtEnd,
4841
+ maintainScrollAtEnd: maintainScrollAtEndConfig,
4514
4842
  maintainScrollAtEndThreshold,
4515
4843
  maintainVisibleContentPosition: maintainVisibleContentPositionConfig,
4516
4844
  numColumns: numColumnsProp,
@@ -4561,29 +4889,93 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4561
4889
  );
4562
4890
  }
4563
4891
  const resolveInitialScrollOffset = useCallback((initialScroll) => {
4892
+ var _a4;
4893
+ if (state.initialScrollUsesOffset) {
4894
+ return clampScrollOffset(ctx, (_a4 = initialScroll.contentOffset) != null ? _a4 : 0);
4895
+ }
4564
4896
  const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, initialScroll.index) : 0;
4565
4897
  const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, initialScroll);
4566
4898
  return clampScrollOffset(ctx, resolvedOffset, initialScroll);
4567
4899
  }, []);
4900
+ const finishInitialScrollWithoutScroll = useCallback(() => {
4901
+ refState.current.initialAnchor = void 0;
4902
+ refState.current.initialScroll = void 0;
4903
+ state.initialAnchor = void 0;
4904
+ state.initialScroll = void 0;
4905
+ state.initialScrollUsesOffset = false;
4906
+ state.initialScrollLastTarget = void 0;
4907
+ state.initialScrollLastTargetUsesOffset = false;
4908
+ setInitialRenderState(ctx, { didInitialScroll: true });
4909
+ }, []);
4910
+ const setActiveInitialScrollTarget = useCallback(
4911
+ (target, options) => {
4912
+ const usesOffset = !!(options == null ? void 0 : options.usesOffset);
4913
+ state.initialScrollUsesOffset = usesOffset;
4914
+ state.initialScrollLastTarget = target;
4915
+ state.initialScrollLastTargetUsesOffset = usesOffset;
4916
+ refState.current.initialScroll = target;
4917
+ state.initialScroll = target;
4918
+ if ((options == null ? void 0 : options.resetDidFinish) && state.didFinishInitialScroll) {
4919
+ state.didFinishInitialScroll = false;
4920
+ }
4921
+ if (!(options == null ? void 0 : options.syncAnchor)) {
4922
+ return;
4923
+ }
4924
+ },
4925
+ []
4926
+ );
4927
+ const shouldFinishInitialScrollAtOrigin = useCallback(
4928
+ (initialScroll, offset) => {
4929
+ var _a4, _b2, _c2;
4930
+ if (offset !== 0 || initialScrollAtEnd) {
4931
+ return false;
4932
+ }
4933
+ if (state.initialScrollUsesOffset) {
4934
+ return Math.abs((_a4 = initialScroll.contentOffset) != null ? _a4 : 0) <= 1;
4935
+ }
4936
+ return initialScroll.index === 0 && ((_b2 = initialScroll.viewPosition) != null ? _b2 : 0) === 0 && Math.abs((_c2 = initialScroll.viewOffset) != null ? _c2 : 0) <= 1;
4937
+ },
4938
+ [initialScrollAtEnd]
4939
+ );
4940
+ const shouldFinishEmptyInitialScrollAtEnd = useCallback(
4941
+ (initialScroll, offset) => {
4942
+ return dataProp.length === 0 && initialScrollAtEnd && offset === 0 && initialScroll.viewPosition === 1;
4943
+ },
4944
+ [dataProp.length, initialScrollAtEnd]
4945
+ );
4946
+ const shouldRearmFinishedEmptyInitialScrollAtEnd = useCallback(
4947
+ (initialScroll) => {
4948
+ var _a4;
4949
+ return !!(state.didFinishInitialScroll && dataProp.length > 0 && initialScroll && !state.initialScrollUsesOffset && initialScroll.index === 0 && initialScroll.viewPosition === 1 && ((_a4 = initialScroll.contentOffset) != null ? _a4 : 0) === 0);
4950
+ },
4951
+ [dataProp.length]
4952
+ );
4568
4953
  const initialContentOffset = useMemo(() => {
4569
4954
  let value;
4570
4955
  const { initialScroll, initialAnchor } = refState.current;
4571
4956
  if (initialScroll) {
4957
+ if (!state.initialScrollUsesOffset && !IsNewArchitecture) ;
4572
4958
  if (initialScroll.contentOffset !== void 0) {
4573
4959
  value = initialScroll.contentOffset;
4574
4960
  } else {
4575
4961
  const clampedOffset = resolveInitialScrollOffset(initialScroll);
4576
4962
  const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
4577
- refState.current.initialScroll = updatedInitialScroll;
4578
- state.initialScroll = updatedInitialScroll;
4963
+ setActiveInitialScrollTarget(updatedInitialScroll, {
4964
+ usesOffset: state.initialScrollUsesOffset
4965
+ });
4579
4966
  value = clampedOffset;
4580
4967
  }
4581
4968
  } else {
4582
4969
  refState.current.initialAnchor = void 0;
4583
4970
  value = 0;
4584
4971
  }
4585
- if (!value) {
4586
- setInitialRenderState(ctx, { didInitialScroll: true });
4972
+ const hasPendingDataDependentInitialScroll = !!initialScroll && dataProp.length === 0 && !shouldFinishInitialScrollAtOrigin(initialScroll, value) && !shouldFinishEmptyInitialScrollAtEnd(initialScroll, value);
4973
+ if (!value && !hasPendingDataDependentInitialScroll) {
4974
+ if (initialScroll && shouldFinishInitialScrollAtOrigin(initialScroll, value)) {
4975
+ finishInitialScrollWithoutScroll();
4976
+ } else {
4977
+ setInitialRenderState(ctx, { didInitialScroll: true });
4978
+ }
4587
4979
  }
4588
4980
  return value;
4589
4981
  }, []);
@@ -4600,24 +4992,131 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4600
4992
  set$(ctx, "totalSize", 0);
4601
4993
  }
4602
4994
  }
4603
- const doInitialScroll = useCallback(() => {
4604
- const { initialScroll, didFinishInitialScroll, queuedInitialLayout, scrollingTo } = state;
4605
- if (initialScroll && !queuedInitialLayout && !didFinishInitialScroll && !scrollingTo) {
4606
- const offset = resolveInitialScrollOffset(initialScroll);
4995
+ const doInitialScroll = useCallback((options) => {
4996
+ var _a4, _b2;
4997
+ const allowPostFinishRetry = !!(options == null ? void 0 : options.allowPostFinishRetry);
4998
+ const { didFinishInitialScroll, queuedInitialLayout, scrollingTo } = state;
4999
+ const initialScroll = (_a4 = state.initialScroll) != null ? _a4 : allowPostFinishRetry ? state.initialScrollLastTarget : void 0;
5000
+ const isInitialScrollInProgress = !!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll);
5001
+ const needsContainerLayoutForInitialScroll = !state.initialScrollUsesOffset;
5002
+ const shouldWaitForInitialLayout = waitForInitialLayout && needsContainerLayoutForInitialScroll && !queuedInitialLayout && !allowPostFinishRetry && !isInitialScrollInProgress;
5003
+ if (!initialScroll || shouldWaitForInitialLayout || didFinishInitialScroll && !allowPostFinishRetry || scrollingTo && !isInitialScrollInProgress) {
5004
+ return;
5005
+ }
5006
+ if (allowPostFinishRetry && state.initialScrollLastTargetUsesOffset) {
5007
+ return;
5008
+ }
5009
+ const didMoveAwayFromInitialTarget = allowPostFinishRetry && initialScroll.contentOffset !== void 0 && Math.abs(state.scroll - initialScroll.contentOffset) > 1;
5010
+ if (didMoveAwayFromInitialTarget) {
5011
+ state.initialScrollRetryWindowUntil = 0;
5012
+ return;
5013
+ }
5014
+ const offset = resolveInitialScrollOffset(initialScroll);
5015
+ const activeInitialTargetOffset = isInitialScrollInProgress ? (_b2 = scrollingTo.targetOffset) != null ? _b2 : scrollingTo.offset : void 0;
5016
+ const didOffsetChange = initialScroll.contentOffset === void 0 || Math.abs(initialScroll.contentOffset - offset) > 1;
5017
+ const didActiveInitialTargetChange = activeInitialTargetOffset !== void 0 && Math.abs(activeInitialTargetOffset - offset) > 1;
5018
+ if (!didOffsetChange && (allowPostFinishRetry || isInitialScrollInProgress && !didActiveInitialTargetChange)) {
5019
+ return;
5020
+ }
5021
+ if (didOffsetChange) {
4607
5022
  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
- });
5023
+ if (!state.initialScrollUsesOffset) {
5024
+ state.initialScrollLastTarget = updatedInitialScroll;
5025
+ state.initialScrollLastTargetUsesOffset = false;
5026
+ if (state.initialScroll) {
5027
+ refState.current.initialScroll = updatedInitialScroll;
5028
+ state.initialScroll = updatedInitialScroll;
5029
+ }
5030
+ }
4617
5031
  }
5032
+ const hasMeasuredScrollLayout = !!state.lastLayout && state.scrollLength > 0;
5033
+ const shouldForceNativeInitialScroll = state.initialScrollUsesOffset && hasMeasuredScrollLayout || allowPostFinishRetry || !!queuedInitialLayout || isInitialScrollInProgress && didOffsetChange;
5034
+ performInitialScroll(ctx, {
5035
+ forceScroll: shouldForceNativeInitialScroll,
5036
+ initialScrollUsesOffset: state.initialScrollUsesOffset,
5037
+ resolvedOffset: offset,
5038
+ target: initialScroll
5039
+ });
4618
5040
  }, []);
5041
+ useLayoutEffect(() => {
5042
+ var _a4;
5043
+ const previousDataLength = state.initialScrollPreviousDataLength;
5044
+ state.initialScrollPreviousDataLength = dataProp.length;
5045
+ if (previousDataLength !== 0 || dataProp.length === 0 || !state.initialScroll || !state.queuedInitialLayout) {
5046
+ return;
5047
+ }
5048
+ if (initialScrollAtEnd) {
5049
+ const lastIndex = Math.max(0, dataProp.length - 1);
5050
+ const initialScroll = state.initialScroll;
5051
+ const shouldRearm = shouldRearmFinishedEmptyInitialScrollAtEnd(initialScroll);
5052
+ if (state.didFinishInitialScroll && !shouldRearm) {
5053
+ return;
5054
+ }
5055
+ if (initialScroll && !state.initialScrollUsesOffset && initialScroll.index === lastIndex && initialScroll.viewPosition === 1 && !shouldRearm) {
5056
+ return;
5057
+ }
5058
+ const updatedInitialScroll = {
5059
+ contentOffset: void 0,
5060
+ index: lastIndex,
5061
+ viewOffset: (_a4 = initialScroll == null ? void 0 : initialScroll.viewOffset) != null ? _a4 : -stylePaddingBottomState,
5062
+ viewPosition: 1
5063
+ };
5064
+ setActiveInitialScrollTarget(updatedInitialScroll, {
5065
+ resetDidFinish: shouldRearm,
5066
+ syncAnchor: true
5067
+ });
5068
+ doInitialScroll();
5069
+ return;
5070
+ }
5071
+ if (state.didFinishInitialScroll) {
5072
+ return;
5073
+ }
5074
+ doInitialScroll();
5075
+ }, [
5076
+ dataProp.length,
5077
+ doInitialScroll,
5078
+ initialScrollAtEnd,
5079
+ shouldRearmFinishedEmptyInitialScrollAtEnd,
5080
+ stylePaddingBottomState
5081
+ ]);
5082
+ useLayoutEffect(() => {
5083
+ var _a4;
5084
+ if (!initialScrollAtEnd) {
5085
+ return;
5086
+ }
5087
+ const lastIndex = Math.max(0, dataProp.length - 1);
5088
+ const initialScroll = state.initialScroll;
5089
+ const shouldRearm = shouldRearmFinishedEmptyInitialScrollAtEnd(initialScroll);
5090
+ if (state.didFinishInitialScroll && !shouldRearm) {
5091
+ return;
5092
+ }
5093
+ if (shouldRearm) {
5094
+ state.didFinishInitialScroll = false;
5095
+ }
5096
+ if (initialScroll && !state.initialScrollUsesOffset && initialScroll.index === lastIndex && initialScroll.viewPosition === 1 && !shouldRearm) {
5097
+ return;
5098
+ }
5099
+ const updatedInitialScroll = {
5100
+ contentOffset: void 0,
5101
+ index: lastIndex,
5102
+ viewOffset: (_a4 = initialScroll == null ? void 0 : initialScroll.viewOffset) != null ? _a4 : -stylePaddingBottomState,
5103
+ viewPosition: 1
5104
+ };
5105
+ setActiveInitialScrollTarget(updatedInitialScroll, {
5106
+ resetDidFinish: shouldRearm,
5107
+ syncAnchor: true
5108
+ });
5109
+ doInitialScroll();
5110
+ }, [
5111
+ dataProp.length,
5112
+ doInitialScroll,
5113
+ initialScrollAtEnd,
5114
+ shouldRearmFinishedEmptyInitialScrollAtEnd,
5115
+ stylePaddingBottomState
5116
+ ]);
4619
5117
  const onLayoutFooter = useCallback(
4620
5118
  (layout) => {
5119
+ var _a4;
4621
5120
  if (!initialScrollAtEnd) {
4622
5121
  return;
4623
5122
  }
@@ -4632,16 +5131,48 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4632
5131
  const footerSize = layout[horizontal ? "width" : "height"];
4633
5132
  const viewOffset = -stylePaddingBottomState - footerSize;
4634
5133
  if (initialScroll.viewOffset !== viewOffset) {
5134
+ const previousTargetOffset = (_a4 = initialScroll.contentOffset) != null ? _a4 : resolveInitialScrollOffset(initialScroll);
5135
+ const didMoveAwayFromFinishedInitialTarget = state.didFinishInitialScroll && Math.abs(state.scroll - previousTargetOffset) > 1;
5136
+ if (didMoveAwayFromFinishedInitialTarget) {
5137
+ return;
5138
+ }
4635
5139
  const updatedInitialScroll = { ...initialScroll, viewOffset };
4636
- refState.current.initialScroll = updatedInitialScroll;
4637
- state.initialScroll = updatedInitialScroll;
5140
+ setActiveInitialScrollTarget(updatedInitialScroll, {
5141
+ resetDidFinish: true
5142
+ });
5143
+ doInitialScroll();
4638
5144
  }
4639
5145
  },
4640
- [dataProp.length, horizontal, initialScrollAtEnd, stylePaddingBottomState]
5146
+ [
5147
+ dataProp.length,
5148
+ doInitialScroll,
5149
+ horizontal,
5150
+ initialScrollAtEnd,
5151
+ resolveInitialScrollOffset,
5152
+ stylePaddingBottomState
5153
+ ]
4641
5154
  );
4642
5155
  const onLayoutChange = useCallback((layout) => {
4643
- doInitialScroll();
5156
+ var _a4;
4644
5157
  handleLayout(ctx, layout, setCanRender);
5158
+ const SCROLL_LENGTH_RETRY_WINDOW_MS = 600;
5159
+ const now = Date.now();
5160
+ const didFinishInitialScroll = !!state.didFinishInitialScroll;
5161
+ if (didFinishInitialScroll && !state.initialScrollLastDidFinish) {
5162
+ state.initialScrollRetryWindowUntil = now + SCROLL_LENGTH_RETRY_WINDOW_MS;
5163
+ }
5164
+ state.initialScrollLastDidFinish = didFinishInitialScroll;
5165
+ const previousScrollLength = state.initialScrollRetryLastLength;
5166
+ const currentScrollLength = state.scrollLength;
5167
+ const didScrollLengthChange = previousScrollLength === void 0 || Math.abs(currentScrollLength - previousScrollLength) > 1;
5168
+ if (didScrollLengthChange) {
5169
+ state.initialScrollRetryLastLength = currentScrollLength;
5170
+ }
5171
+ if (didFinishInitialScroll && didScrollLengthChange && now <= state.initialScrollRetryWindowUntil && !state.initialScrollLastTargetUsesOffset && ((_a4 = state.initialScrollLastTarget) == null ? void 0 : _a4.index) !== void 0) {
5172
+ doInitialScroll({ allowPostFinishRetry: true });
5173
+ return;
5174
+ }
5175
+ doInitialScroll();
4645
5176
  }, []);
4646
5177
  const { onLayout } = useOnLayoutSync({
4647
5178
  onLayoutChange,
@@ -4753,7 +5284,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4753
5284
  onScroll: onScrollHandler,
4754
5285
  recycleItems,
4755
5286
  refreshControl: refreshControlElement ? stylePaddingTopState > 0 ? React3.cloneElement(refreshControlElement, {
4756
- progressViewOffset: ((_d = refreshControlElement.props.progressViewOffset) != null ? _d : 0) + stylePaddingTopState
5287
+ progressViewOffset: ((_g = refreshControlElement.props.progressViewOffset) != null ? _g : 0) + stylePaddingTopState
4757
5288
  }) : refreshControlElement : onRefresh && /* @__PURE__ */ React3.createElement(
4758
5289
  RefreshControl,
4759
5290
  {
@@ -4764,7 +5295,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4764
5295
  ),
4765
5296
  refScrollView: combinedRef,
4766
5297
  renderScrollComponent,
4767
- scrollAdjustHandler: (_e = refState.current) == null ? void 0 : _e.scrollAdjustHandler,
5298
+ scrollAdjustHandler: (_h = refState.current) == null ? void 0 : _h.scrollAdjustHandler,
4768
5299
  scrollEventThrottle: 0,
4769
5300
  snapToIndices,
4770
5301
  stickyHeaderIndices,
@@ -4776,7 +5307,20 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4776
5307
  ), IS_DEV && ENABLE_DEBUG_VIEW);
4777
5308
  });
4778
5309
 
5310
+ // src/entrypoints/shared.ts
5311
+ var LegendListRuntime = LegendList;
5312
+ var internal = {
5313
+ getComponent,
5314
+ IsNewArchitecture,
5315
+ POSITION_OUT_OF_VIEW,
5316
+ peek$,
5317
+ useArr$,
5318
+ useCombinedRef,
5319
+ useStateContext
5320
+ };
5321
+
4779
5322
  // src/react.ts
4780
- var LegendList3 = LegendList;
5323
+ var LegendList3 = LegendListRuntime;
5324
+ var internal2 = internal;
4781
5325
 
4782
- export { LegendList3 as LegendList, typedForwardRef, typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };
5326
+ export { LegendList3 as LegendList, internal2 as internal, typedForwardRef, typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };