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

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/index.js CHANGED
@@ -937,7 +937,7 @@ var ContainersInner = typedMemo(function ContainersInner2({ horizontal, numColum
937
937
  const ref = React3.useRef(null);
938
938
  const ctx = useStateContext();
939
939
  const columnWrapperStyle = ctx.columnWrapperStyle;
940
- const [totalSize, otherAxisSize] = useArr$(["totalSize", "otherAxisSize"]);
940
+ const [otherAxisSize, totalSize] = useArr$(["otherAxisSize", "totalSize"]);
941
941
  useDOMOrder(ref);
942
942
  const style = horizontal ? { minHeight: otherAxisSize, position: "relative", width: totalSize } : { height: totalSize, minWidth: otherAxisSize, position: "relative" };
943
943
  if (columnWrapperStyle && numColumns > 1) {
@@ -966,7 +966,6 @@ var Containers = typedMemo(function Containers2({
966
966
  horizontal,
967
967
  recycleItems,
968
968
  ItemSeparatorComponent,
969
- waitForInitialLayout,
970
969
  updateItemSize: updateItemSize2,
971
970
  getRenderedItem: getRenderedItem2,
972
971
  stickyHeaderConfig
@@ -990,7 +989,7 @@ var Containers = typedMemo(function Containers2({
990
989
  )
991
990
  );
992
991
  }
993
- return /* @__PURE__ */ React3__namespace.createElement(ContainersInner, { horizontal, numColumns, waitForInitialLayout }, containers);
992
+ return /* @__PURE__ */ React3__namespace.createElement(ContainersInner, { horizontal, numColumns }, containers);
994
993
  });
995
994
 
996
995
  // src/platform/StyleSheet.tsx
@@ -1441,7 +1440,6 @@ var ListComponent = typedMemo(function ListComponent2({
1441
1440
  recycleItems,
1442
1441
  ItemSeparatorComponent,
1443
1442
  alignItemsAtEnd: _alignItemsAtEnd,
1444
- waitForInitialLayout,
1445
1443
  onScroll: onScroll2,
1446
1444
  onLayout,
1447
1445
  ListHeaderComponent,
@@ -1504,7 +1502,7 @@ var ListComponent = typedMemo(function ListComponent2({
1504
1502
  height: "100%"
1505
1503
  } : {}
1506
1504
  ],
1507
- contentOffset: initialContentOffset ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
1505
+ contentOffset: initialContentOffset !== void 0 ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
1508
1506
  horizontal,
1509
1507
  maintainVisibleContentPosition: maintainVisibleContentPosition.size || maintainVisibleContentPosition.data ? { minIndexForVisible: 0 } : void 0,
1510
1508
  onLayout,
@@ -1524,8 +1522,7 @@ var ListComponent = typedMemo(function ListComponent2({
1524
1522
  ItemSeparatorComponent,
1525
1523
  recycleItems,
1526
1524
  stickyHeaderConfig,
1527
- updateItemSize: updateItemSize2,
1528
- waitForInitialLayout
1525
+ updateItemSize: updateItemSize2
1529
1526
  }
1530
1527
  ),
1531
1528
  ListFooterComponent && /* @__PURE__ */ React3__namespace.createElement(LayoutView, { onLayoutChange: onLayoutFooterInternal, style: ListFooterComponentStyle }, getComponent(ListFooterComponent)),
@@ -1638,6 +1635,13 @@ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
1638
1635
  setSize(ctx, key, size);
1639
1636
  return size;
1640
1637
  }
1638
+ function getItemSizeAtIndex(ctx, index) {
1639
+ if (index === void 0 || index < 0) {
1640
+ return void 0;
1641
+ }
1642
+ const targetId = getId(ctx.state, index);
1643
+ return getItemSize(ctx, targetId, index, ctx.state.props.data[index]);
1644
+ }
1641
1645
 
1642
1646
  // src/core/calculateOffsetWithOffsetPosition.ts
1643
1647
  function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
@@ -1688,6 +1692,373 @@ function clampScrollOffset(ctx, offset, scrollTarget) {
1688
1692
  return clampedOffset;
1689
1693
  }
1690
1694
 
1695
+ // src/core/deferredPublicOnScroll.ts
1696
+ function withResolvedContentOffset(state, event, resolvedOffset) {
1697
+ return {
1698
+ ...event,
1699
+ nativeEvent: {
1700
+ ...event.nativeEvent,
1701
+ contentOffset: state.props.horizontal ? { x: resolvedOffset, y: 0 } : { x: 0, y: resolvedOffset }
1702
+ }
1703
+ };
1704
+ }
1705
+ function releaseDeferredPublicOnScroll(ctx, resolvedOffset) {
1706
+ var _a3, _b, _c, _d;
1707
+ const state = ctx.state;
1708
+ const deferredEvent = state.deferredPublicOnScrollEvent;
1709
+ state.deferredPublicOnScrollEvent = void 0;
1710
+ if (deferredEvent) {
1711
+ (_d = (_c = state.props).onScroll) == null ? void 0 : _d.call(
1712
+ _c,
1713
+ withResolvedContentOffset(
1714
+ state,
1715
+ deferredEvent,
1716
+ (_b = (_a3 = resolvedOffset != null ? resolvedOffset : state.scrollPending) != null ? _a3 : state.scroll) != null ? _b : 0
1717
+ )
1718
+ );
1719
+ }
1720
+ }
1721
+
1722
+ // src/core/initialScrollSession.ts
1723
+ var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
1724
+ function hasInitialScrollSessionCompletion(completion) {
1725
+ return !!((completion == null ? void 0 : completion.didDispatchNativeScroll) || (completion == null ? void 0 : completion.didRetrySilentInitialScroll) || (completion == null ? void 0 : completion.watchdog));
1726
+ }
1727
+ function clearInitialScrollSession(state) {
1728
+ state.initialScrollSession = void 0;
1729
+ return void 0;
1730
+ }
1731
+ function createInitialScrollSession(options) {
1732
+ const { bootstrap, completion, kind, previousDataLength } = options;
1733
+ return kind === "offset" ? {
1734
+ completion,
1735
+ kind,
1736
+ previousDataLength
1737
+ } : {
1738
+ bootstrap,
1739
+ completion,
1740
+ kind,
1741
+ previousDataLength
1742
+ };
1743
+ }
1744
+ function ensureInitialScrollSessionCompletion(state, kind = ((_b) => (_b = ((_a3) => (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind)()) != null ? _b : "bootstrap")()) {
1745
+ var _a4, _b2;
1746
+ if (!state.initialScrollSession) {
1747
+ state.initialScrollSession = createInitialScrollSession({
1748
+ completion: {},
1749
+ kind,
1750
+ previousDataLength: 0
1751
+ });
1752
+ } else if (state.initialScrollSession.kind !== kind) {
1753
+ state.initialScrollSession = createInitialScrollSession({
1754
+ bootstrap: state.initialScrollSession.kind === "bootstrap" ? state.initialScrollSession.bootstrap : void 0,
1755
+ completion: state.initialScrollSession.completion,
1756
+ kind,
1757
+ previousDataLength: state.initialScrollSession.previousDataLength
1758
+ });
1759
+ }
1760
+ (_b2 = (_a4 = state.initialScrollSession).completion) != null ? _b2 : _a4.completion = {};
1761
+ return state.initialScrollSession.completion;
1762
+ }
1763
+ var initialScrollCompletion = {
1764
+ didDispatchNativeScroll(state) {
1765
+ var _a3, _b;
1766
+ return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didDispatchNativeScroll);
1767
+ },
1768
+ didRetrySilentInitialScroll(state) {
1769
+ var _a3, _b;
1770
+ return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didRetrySilentInitialScroll);
1771
+ },
1772
+ markInitialScrollNativeDispatch(state) {
1773
+ ensureInitialScrollSessionCompletion(state).didDispatchNativeScroll = true;
1774
+ },
1775
+ markSilentInitialScrollRetry(state) {
1776
+ ensureInitialScrollSessionCompletion(state).didRetrySilentInitialScroll = true;
1777
+ },
1778
+ resetFlags(state) {
1779
+ if (!state.initialScrollSession) {
1780
+ return;
1781
+ }
1782
+ const completion = ensureInitialScrollSessionCompletion(state, state.initialScrollSession.kind);
1783
+ completion.didDispatchNativeScroll = void 0;
1784
+ completion.didRetrySilentInitialScroll = void 0;
1785
+ }
1786
+ };
1787
+ var initialScrollWatchdog = {
1788
+ clear(state) {
1789
+ initialScrollWatchdog.set(state, void 0);
1790
+ },
1791
+ didObserveProgress(newScroll, watchdog) {
1792
+ const previousDistance = Math.abs(watchdog.startScroll - watchdog.targetOffset);
1793
+ const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
1794
+ return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET || nextDistance + INITIAL_SCROLL_MIN_TARGET_OFFSET < previousDistance;
1795
+ },
1796
+ get(state) {
1797
+ var _a3, _b;
1798
+ return (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog;
1799
+ },
1800
+ hasNonZeroTargetOffset(targetOffset) {
1801
+ return targetOffset !== void 0 && targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
1802
+ },
1803
+ isAtZeroTargetOffset(targetOffset) {
1804
+ return targetOffset <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
1805
+ },
1806
+ set(state, watchdog) {
1807
+ var _a3, _b;
1808
+ if (!watchdog && !((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog)) {
1809
+ return;
1810
+ }
1811
+ const completion = ensureInitialScrollSessionCompletion(state);
1812
+ completion.watchdog = watchdog ? {
1813
+ startScroll: watchdog.startScroll,
1814
+ targetOffset: watchdog.targetOffset
1815
+ } : void 0;
1816
+ }
1817
+ };
1818
+ function setInitialScrollSession(state, options = {}) {
1819
+ var _a3, _b, _c;
1820
+ const existingSession = state.initialScrollSession;
1821
+ const kind = (_a3 = options.kind) != null ? _a3 : existingSession == null ? void 0 : existingSession.kind;
1822
+ const completion = existingSession == null ? void 0 : existingSession.completion;
1823
+ const hasBootstrapOverride = Object.hasOwn(options, "bootstrap");
1824
+ const bootstrap = kind === "bootstrap" ? hasBootstrapOverride ? options.bootstrap : (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0 : void 0;
1825
+ if (!kind) {
1826
+ return clearInitialScrollSession(state);
1827
+ }
1828
+ if (!state.initialScroll && !bootstrap && !hasInitialScrollSessionCompletion(completion)) {
1829
+ return clearInitialScrollSession(state);
1830
+ }
1831
+ const previousDataLength = (_c = (_b = options.previousDataLength) != null ? _b : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _c : 0;
1832
+ state.initialScrollSession = createInitialScrollSession({
1833
+ bootstrap,
1834
+ completion,
1835
+ kind,
1836
+ previousDataLength
1837
+ });
1838
+ return state.initialScrollSession;
1839
+ }
1840
+
1841
+ // src/core/finishScrollTo.ts
1842
+ function finishScrollTo(ctx) {
1843
+ var _a3, _b;
1844
+ const state = ctx.state;
1845
+ if (state == null ? void 0 : state.scrollingTo) {
1846
+ const resolvePendingScroll = state.pendingScrollResolve;
1847
+ state.pendingScrollResolve = void 0;
1848
+ const scrollingTo = state.scrollingTo;
1849
+ state.scrollHistory.length = 0;
1850
+ state.scrollingTo = void 0;
1851
+ if (state.pendingTotalSize !== void 0) {
1852
+ addTotalSize(ctx, null, state.pendingTotalSize);
1853
+ }
1854
+ {
1855
+ state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
1856
+ }
1857
+ if (scrollingTo.isInitialScroll || state.initialScroll) {
1858
+ const isOffsetSession = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
1859
+ finishInitialScroll(ctx, {
1860
+ onFinished: resolvePendingScroll,
1861
+ preserveTarget: isOffsetSession && state.props.data.length === 0 || !!scrollingTo.isInitialScroll && !!((_b = state.initialScroll) == null ? void 0 : _b.preserveForFooterLayout),
1862
+ recalculateItems: true,
1863
+ syncObservedOffset: isOffsetSession,
1864
+ waitForCompletionFrame: !!scrollingTo.waitForInitialScrollCompletionFrame
1865
+ });
1866
+ return;
1867
+ }
1868
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1869
+ }
1870
+ }
1871
+
1872
+ // src/core/doScrollTo.ts
1873
+ var SCROLL_END_IDLE_MS = 80;
1874
+ var SCROLL_END_MAX_MS = 1500;
1875
+ var SMOOTH_SCROLL_DURATION_MS = 320;
1876
+ var SCROLL_END_TARGET_EPSILON = 1;
1877
+ function doScrollTo(ctx, params) {
1878
+ const state = ctx.state;
1879
+ const { animated, horizontal, offset } = params;
1880
+ const scroller = state.refScroller.current;
1881
+ const node = scroller == null ? void 0 : scroller.getScrollableNode();
1882
+ if (!scroller || !node) {
1883
+ return;
1884
+ }
1885
+ const isAnimated = !!animated;
1886
+ const isHorizontal = !!horizontal;
1887
+ const left = isHorizontal ? offset : 0;
1888
+ const top = isHorizontal ? 0 : offset;
1889
+ scroller.scrollTo({ animated: isAnimated, x: left, y: top });
1890
+ if (isAnimated) {
1891
+ const target = scroller.getScrollEventTarget();
1892
+ listenForScrollEnd(ctx, {
1893
+ readOffset: () => scroller.getCurrentScrollOffset(),
1894
+ target,
1895
+ targetOffset: offset
1896
+ });
1897
+ } else {
1898
+ state.scroll = offset;
1899
+ setTimeout(() => {
1900
+ finishScrollTo(ctx);
1901
+ }, 100);
1902
+ }
1903
+ }
1904
+ function listenForScrollEnd(ctx, params) {
1905
+ const { readOffset, target, targetOffset } = params;
1906
+ if (!target) {
1907
+ finishScrollTo(ctx);
1908
+ return;
1909
+ }
1910
+ const supportsScrollEnd = "onscrollend" in target;
1911
+ let idleTimeout;
1912
+ let settled = false;
1913
+ const targetToken = ctx.state.scrollingTo;
1914
+ const maxTimeout = setTimeout(() => finish("max"), SCROLL_END_MAX_MS);
1915
+ const cleanup = () => {
1916
+ target.removeEventListener("scroll", onScroll2);
1917
+ if (supportsScrollEnd) {
1918
+ target.removeEventListener("scrollend", onScrollEnd);
1919
+ }
1920
+ if (idleTimeout) {
1921
+ clearTimeout(idleTimeout);
1922
+ }
1923
+ clearTimeout(maxTimeout);
1924
+ };
1925
+ const finish = (reason) => {
1926
+ if (settled) return;
1927
+ if (targetToken !== ctx.state.scrollingTo) {
1928
+ settled = true;
1929
+ cleanup();
1930
+ return;
1931
+ }
1932
+ const currentOffset = readOffset();
1933
+ const isNearTarget = Math.abs(currentOffset - targetOffset) <= SCROLL_END_TARGET_EPSILON;
1934
+ if (reason === "scrollend" && !isNearTarget) {
1935
+ return;
1936
+ }
1937
+ settled = true;
1938
+ cleanup();
1939
+ finishScrollTo(ctx);
1940
+ };
1941
+ const onScroll2 = () => {
1942
+ if (idleTimeout) {
1943
+ clearTimeout(idleTimeout);
1944
+ }
1945
+ idleTimeout = setTimeout(() => finish("idle"), SCROLL_END_IDLE_MS);
1946
+ };
1947
+ const onScrollEnd = () => finish("scrollend");
1948
+ target.addEventListener("scroll", onScroll2);
1949
+ if (supportsScrollEnd) {
1950
+ target.addEventListener("scrollend", onScrollEnd);
1951
+ } else {
1952
+ idleTimeout = setTimeout(() => finish("idle"), SMOOTH_SCROLL_DURATION_MS);
1953
+ }
1954
+ }
1955
+
1956
+ // src/core/scrollTo.ts
1957
+ function syncInitialScrollNativeWatchdog(state, options) {
1958
+ var _a3;
1959
+ const { isInitialScroll, requestedOffset, targetOffset } = options;
1960
+ const existingWatchdog = initialScrollWatchdog.get(state);
1961
+ const shouldWatchInitialNativeScroll = !state.didFinishInitialScroll && (isInitialScroll || !!existingWatchdog) && initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset);
1962
+ const shouldClearInitialNativeScrollWatchdog = !state.didFinishInitialScroll && !!existingWatchdog && initialScrollWatchdog.isAtZeroTargetOffset(requestedOffset);
1963
+ if (shouldWatchInitialNativeScroll) {
1964
+ state.hasScrolled = false;
1965
+ initialScrollWatchdog.set(state, {
1966
+ startScroll: (_a3 = existingWatchdog == null ? void 0 : existingWatchdog.startScroll) != null ? _a3 : state.scroll,
1967
+ targetOffset
1968
+ });
1969
+ return;
1970
+ }
1971
+ if (shouldClearInitialNativeScrollWatchdog) {
1972
+ initialScrollWatchdog.clear(state);
1973
+ }
1974
+ }
1975
+ function scrollTo(ctx, params) {
1976
+ var _a3;
1977
+ const state = ctx.state;
1978
+ const { noScrollingTo, forceScroll, ...scrollTarget } = params;
1979
+ const {
1980
+ animated,
1981
+ isInitialScroll,
1982
+ offset: scrollTargetOffset,
1983
+ precomputedWithViewOffset,
1984
+ waitForInitialScrollCompletionFrame
1985
+ } = scrollTarget;
1986
+ const {
1987
+ props: { horizontal }
1988
+ } = state;
1989
+ if (state.animFrameCheckFinishedScroll) {
1990
+ cancelAnimationFrame(ctx.state.animFrameCheckFinishedScroll);
1991
+ }
1992
+ if (state.timeoutCheckFinishedScrollFallback) {
1993
+ clearTimeout(ctx.state.timeoutCheckFinishedScrollFallback);
1994
+ }
1995
+ const requestedOffset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, scrollTargetOffset, scrollTarget);
1996
+ const shouldPreserveRawInitialOffsetRequest = !!isInitialScroll && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
1997
+ const targetOffset = clampScrollOffset(ctx, requestedOffset, scrollTarget);
1998
+ const offset = shouldPreserveRawInitialOffsetRequest ? requestedOffset : targetOffset;
1999
+ state.scrollHistory.length = 0;
2000
+ if (!noScrollingTo) {
2001
+ if (isInitialScroll) {
2002
+ initialScrollCompletion.resetFlags(state);
2003
+ }
2004
+ state.scrollingTo = {
2005
+ ...scrollTarget,
2006
+ targetOffset,
2007
+ waitForInitialScrollCompletionFrame
2008
+ };
2009
+ }
2010
+ state.scrollPending = targetOffset;
2011
+ syncInitialScrollNativeWatchdog(state, { isInitialScroll, requestedOffset: offset, targetOffset });
2012
+ if (forceScroll || !isInitialScroll || Platform.OS === "android") {
2013
+ doScrollTo(ctx, { animated, horizontal, offset });
2014
+ } else {
2015
+ state.scroll = offset;
2016
+ }
2017
+ }
2018
+
2019
+ // src/core/scrollToIndex.ts
2020
+ function clampScrollIndex(index, dataLength) {
2021
+ if (dataLength <= 0) {
2022
+ return -1;
2023
+ }
2024
+ if (index >= dataLength) {
2025
+ return dataLength - 1;
2026
+ }
2027
+ if (index < 0) {
2028
+ return 0;
2029
+ }
2030
+ return index;
2031
+ }
2032
+ function scrollToIndex(ctx, {
2033
+ index,
2034
+ viewOffset = 0,
2035
+ animated = true,
2036
+ forceScroll,
2037
+ isInitialScroll,
2038
+ viewPosition
2039
+ }) {
2040
+ const state = ctx.state;
2041
+ const { data } = state.props;
2042
+ index = clampScrollIndex(index, data.length);
2043
+ const itemSize = getItemSizeAtIndex(ctx, index);
2044
+ const firstIndexOffset = calculateOffsetForIndex(ctx, index);
2045
+ const isLast = index === data.length - 1;
2046
+ if (isLast && viewPosition === void 0) {
2047
+ viewPosition = 1;
2048
+ }
2049
+ state.scrollForNextCalculateItemsInView = void 0;
2050
+ scrollTo(ctx, {
2051
+ animated,
2052
+ forceScroll,
2053
+ index,
2054
+ isInitialScroll,
2055
+ itemSize,
2056
+ offset: firstIndexOffset,
2057
+ viewOffset,
2058
+ viewPosition: viewPosition != null ? viewPosition : 0
2059
+ });
2060
+ }
2061
+
1691
2062
  // src/utils/checkThreshold.ts
1692
2063
  var HYSTERESIS_MULTIPLIER = 1.3;
1693
2064
  var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot, allowReentryOnChange) => {
@@ -1856,202 +2227,867 @@ function setInitialRenderState(ctx, {
1856
2227
  }
1857
2228
  }
1858
2229
 
1859
- // src/core/finishScrollTo.ts
1860
- function finishScrollTo(ctx) {
1861
- var _a3, _b;
2230
+ // src/core/initialScroll.ts
2231
+ function syncInitialScrollOffset(state, offset) {
2232
+ state.scroll = offset;
2233
+ state.scrollPending = offset;
2234
+ state.scrollPrev = offset;
2235
+ }
2236
+ function dispatchInitialScroll(ctx, params) {
2237
+ const { forceScroll, resolvedOffset, target, waitForCompletionFrame } = params;
2238
+ const requestedIndex = target.index;
2239
+ const index = requestedIndex !== void 0 ? clampScrollIndex(requestedIndex, ctx.state.props.data.length) : void 0;
2240
+ const itemSize = getItemSizeAtIndex(ctx, index);
2241
+ scrollTo(ctx, {
2242
+ animated: false,
2243
+ forceScroll,
2244
+ index: index !== void 0 && index >= 0 ? index : void 0,
2245
+ isInitialScroll: true,
2246
+ itemSize,
2247
+ offset: resolvedOffset,
2248
+ precomputedWithViewOffset: true,
2249
+ viewOffset: target.viewOffset,
2250
+ viewPosition: target.viewPosition,
2251
+ waitForInitialScrollCompletionFrame: waitForCompletionFrame
2252
+ });
2253
+ }
2254
+ function setInitialScrollTarget(state, target, options) {
2255
+ var _a3;
2256
+ state.initialScroll = target;
2257
+ if ((options == null ? void 0 : options.resetDidFinish) && state.didFinishInitialScroll) {
2258
+ state.didFinishInitialScroll = false;
2259
+ }
2260
+ setInitialScrollSession(state, {
2261
+ kind: ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" ? "offset" : "bootstrap"
2262
+ });
2263
+ }
2264
+ function finishInitialScroll(ctx, options) {
2265
+ var _a3, _b, _c;
1862
2266
  const state = ctx.state;
1863
- if (state == null ? void 0 : state.scrollingTo) {
1864
- const resolvePendingScroll = state.pendingScrollResolve;
1865
- state.pendingScrollResolve = void 0;
1866
- const scrollingTo = state.scrollingTo;
1867
- state.scrollHistory.length = 0;
1868
- state.initialScroll = void 0;
1869
- state.initialScrollUsesOffset = false;
1870
- state.initialAnchor = void 0;
1871
- state.initialNativeScrollWatchdog = void 0;
1872
- state.scrollingTo = void 0;
1873
- if (state.pendingTotalSize !== void 0) {
1874
- addTotalSize(ctx, null, state.pendingTotalSize);
1875
- }
1876
- if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1877
- (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1878
- }
1879
- {
1880
- state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
2267
+ if ((options == null ? void 0 : options.resolvedOffset) !== void 0) {
2268
+ syncInitialScrollOffset(state, options.resolvedOffset);
2269
+ } else if ((options == null ? void 0 : options.syncObservedOffset) && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
2270
+ const observedOffset = (_c = (_b = state.refScroller.current) == null ? void 0 : _b.getCurrentScrollOffset) == null ? void 0 : _c.call(_b);
2271
+ if (typeof observedOffset === "number" && Number.isFinite(observedOffset)) {
2272
+ syncInitialScrollOffset(state, observedOffset);
2273
+ }
2274
+ }
2275
+ const complete = () => {
2276
+ var _a4, _b2, _c2, _d, _e, _f, _g;
2277
+ const shouldReleaseDeferredPublicOnScroll = ((_a4 = state.initialScrollSession) == null ? void 0 : _a4.kind) === "bootstrap";
2278
+ const finalScrollOffset = (_d = (_c2 = (_b2 = options == null ? void 0 : options.resolvedOffset) != null ? _b2 : state.scrollPending) != null ? _c2 : state.scroll) != null ? _d : 0;
2279
+ initialScrollWatchdog.clear(state);
2280
+ if (!(options == null ? void 0 : options.preserveTarget)) {
2281
+ state.initialScroll = void 0;
2282
+ }
2283
+ setInitialScrollSession(state);
2284
+ if ((options == null ? void 0 : options.recalculateItems) && ((_e = state.props) == null ? void 0 : _e.data)) {
2285
+ (_f = state.triggerCalculateItemsInView) == null ? void 0 : _f.call(state, { forceFullItemPositions: true });
2286
+ }
2287
+ if (options == null ? void 0 : options.recalculateItems) {
2288
+ checkThresholds(ctx);
1881
2289
  }
1882
2290
  setInitialRenderState(ctx, { didInitialScroll: true });
1883
- checkThresholds(ctx);
1884
- resolvePendingScroll == null ? void 0 : resolvePendingScroll();
2291
+ if (shouldReleaseDeferredPublicOnScroll) {
2292
+ releaseDeferredPublicOnScroll(ctx, finalScrollOffset);
2293
+ }
2294
+ (_g = options == null ? void 0 : options.onFinished) == null ? void 0 : _g.call(options);
2295
+ };
2296
+ if (options == null ? void 0 : options.waitForCompletionFrame) {
2297
+ requestAnimationFrame(complete);
2298
+ return;
1885
2299
  }
2300
+ complete();
1886
2301
  }
1887
-
1888
- // src/core/doScrollTo.ts
1889
- var SCROLL_END_IDLE_MS = 80;
1890
- var SCROLL_END_MAX_MS = 1500;
1891
- var SMOOTH_SCROLL_DURATION_MS = 320;
1892
- var SCROLL_END_TARGET_EPSILON = 1;
1893
- function doScrollTo(ctx, params) {
2302
+ function resolveInitialScrollOffset(ctx, initialScroll) {
2303
+ var _a3, _b;
1894
2304
  const state = ctx.state;
1895
- const { animated, horizontal, offset } = params;
1896
- const scroller = state.refScroller.current;
1897
- const node = scroller == null ? void 0 : scroller.getScrollableNode();
1898
- if (!scroller || !node) {
2305
+ if (((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
2306
+ return (_b = initialScroll.contentOffset) != null ? _b : 0;
2307
+ }
2308
+ const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, initialScroll.index) : 0;
2309
+ const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, initialScroll);
2310
+ return clampScrollOffset(ctx, resolvedOffset, initialScroll);
2311
+ }
2312
+ function getAdvanceableInitialScrollState(state, options) {
2313
+ const { didFinishInitialScroll, queuedInitialLayout, scrollingTo } = state;
2314
+ const initialScroll = state.initialScroll;
2315
+ const isInitialScrollInProgress = !!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll);
2316
+ const shouldWaitForInitialLayout = !!(options == null ? void 0 : options.requiresMeasuredLayout) && !queuedInitialLayout && !isInitialScrollInProgress;
2317
+ if (!initialScroll || shouldWaitForInitialLayout || didFinishInitialScroll || scrollingTo && !isInitialScrollInProgress) {
2318
+ return void 0;
2319
+ }
2320
+ return {
2321
+ initialScroll,
2322
+ isInitialScrollInProgress,
2323
+ queuedInitialLayout,
2324
+ scrollingTo
2325
+ };
2326
+ }
2327
+ function advanceMeasuredInitialScroll(ctx, options) {
2328
+ var _a3, _b, _c;
2329
+ const state = ctx.state;
2330
+ const advanceableState = getAdvanceableInitialScrollState(state, {
2331
+ requiresMeasuredLayout: true
2332
+ });
2333
+ if (!advanceableState) {
2334
+ return false;
2335
+ }
2336
+ const { initialScroll, isInitialScrollInProgress, queuedInitialLayout } = advanceableState;
2337
+ const scrollingTo = isInitialScrollInProgress ? advanceableState.scrollingTo : void 0;
2338
+ const resolvedOffset = resolveInitialScrollOffset(ctx, initialScroll);
2339
+ const activeInitialTargetOffset = scrollingTo ? (_a3 = scrollingTo.targetOffset) != null ? _a3 : scrollingTo.offset : void 0;
2340
+ const didOffsetChange = initialScroll.contentOffset === void 0 || Math.abs(initialScroll.contentOffset - resolvedOffset) > 1;
2341
+ const didActiveInitialTargetChange = activeInitialTargetOffset !== void 0 && Math.abs(activeInitialTargetOffset - resolvedOffset) > 1;
2342
+ const isAlreadyAtDesiredInitialTarget = activeInitialTargetOffset !== void 0 && Math.abs(state.scroll - activeInitialTargetOffset) <= 1 && Math.abs(state.scrollPending - activeInitialTargetOffset) <= 1;
2343
+ if (!(options == null ? void 0 : options.forceScroll) && !didOffsetChange && isInitialScrollInProgress && !didActiveInitialTargetChange) {
2344
+ return false;
2345
+ }
2346
+ if ((options == null ? void 0 : options.forceScroll) && isAlreadyAtDesiredInitialTarget) {
2347
+ return false;
2348
+ }
2349
+ if (didOffsetChange && ((_b = state.initialScrollSession) == null ? void 0 : _b.kind) !== "offset") {
2350
+ setInitialScrollTarget(state, { ...initialScroll, contentOffset: resolvedOffset });
2351
+ }
2352
+ const forceScroll = (_c = options == null ? void 0 : options.forceScroll) != null ? _c : !!queuedInitialLayout || isInitialScrollInProgress && didOffsetChange;
2353
+ dispatchInitialScroll(ctx, {
2354
+ forceScroll,
2355
+ resolvedOffset,
2356
+ target: initialScroll
2357
+ });
2358
+ return true;
2359
+ }
2360
+ function advanceOffsetInitialScroll(ctx, options) {
2361
+ var _a3, _b;
2362
+ const state = ctx.state;
2363
+ const advanceableState = getAdvanceableInitialScrollState(state);
2364
+ if (!advanceableState) {
2365
+ return false;
2366
+ }
2367
+ const { initialScroll, queuedInitialLayout } = advanceableState;
2368
+ const resolvedOffset = (_a3 = initialScroll.contentOffset) != null ? _a3 : 0;
2369
+ const isAlreadyAtDesiredInitialTarget = Math.abs(state.scroll - resolvedOffset) <= 1 && Math.abs(state.scrollPending - resolvedOffset) <= 1;
2370
+ if ((options == null ? void 0 : options.forceScroll) && isAlreadyAtDesiredInitialTarget) {
2371
+ return false;
2372
+ }
2373
+ const hasMeasuredScrollLayout = !!state.lastLayout && state.scrollLength > 0;
2374
+ const forceScroll = (_b = options == null ? void 0 : options.forceScroll) != null ? _b : hasMeasuredScrollLayout || !!queuedInitialLayout;
2375
+ dispatchInitialScroll(ctx, {
2376
+ forceScroll,
2377
+ resolvedOffset,
2378
+ target: initialScroll
2379
+ });
2380
+ return true;
2381
+ }
2382
+ function advanceCurrentInitialScrollSession(ctx, options) {
2383
+ var _a3;
2384
+ return ((_a3 = ctx.state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" ? advanceOffsetInitialScroll(ctx, {
2385
+ forceScroll: options == null ? void 0 : options.forceScroll
2386
+ }) : advanceMeasuredInitialScroll(ctx, {
2387
+ forceScroll: options == null ? void 0 : options.forceScroll
2388
+ });
2389
+ }
2390
+
2391
+ // src/utils/checkAllSizesKnown.ts
2392
+ function isNullOrUndefined2(value) {
2393
+ return value === null || value === void 0;
2394
+ }
2395
+ function getMountedBufferedIndices(state) {
2396
+ const { startBuffered, endBuffered } = state;
2397
+ if (!isNullOrUndefined2(endBuffered) && !isNullOrUndefined2(startBuffered) && startBuffered >= 0 && endBuffered >= 0) {
2398
+ return Array.from(state.containerItemKeys.keys()).map((key) => state.indexByKey.get(key)).filter((index) => index !== void 0 && index >= startBuffered && index <= endBuffered).sort((a, b) => a - b);
2399
+ }
2400
+ return [];
2401
+ }
2402
+ function checkAllSizesKnown(state, indices = getMountedBufferedIndices(state)) {
2403
+ return indices.length > 0 && indices.every((index) => {
2404
+ const key = getId(state, index);
2405
+ return state.sizesKnown.has(key);
2406
+ });
2407
+ }
2408
+
2409
+ // src/core/bootstrapInitialScroll.ts
2410
+ var DEFAULT_BOOTSTRAP_REVEAL_EPSILON = 1;
2411
+ var DEFAULT_BOOTSTRAP_REVEAL_MAX_FRAMES = 8;
2412
+ var DEFAULT_BOOTSTRAP_REVEAL_MAX_PASSES = 24;
2413
+ var BOOTSTRAP_REVEAL_ABORT_WARNING = "LegendList bootstrap initial scroll aborted after exceeding convergence bounds.";
2414
+ function getBootstrapInitialScrollSession(state) {
2415
+ var _a3;
2416
+ return ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" ? state.initialScrollSession.bootstrap : void 0;
2417
+ }
2418
+ function isOffsetInitialScrollSession(state) {
2419
+ var _a3;
2420
+ return ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
2421
+ }
2422
+ function doVisibleIndicesMatch(previous, next) {
2423
+ if (!previous || previous.length !== next.length) {
2424
+ return false;
2425
+ }
2426
+ for (let i = 0; i < previous.length; i++) {
2427
+ if (previous[i] !== next[i]) {
2428
+ return false;
2429
+ }
2430
+ }
2431
+ return true;
2432
+ }
2433
+ function getBootstrapRevealVisibleIndices(options) {
2434
+ const { dataLength, getSize, offset, positions, scrollLength, startIndex: requestedStartIndex } = options;
2435
+ const endOffset = offset + scrollLength;
2436
+ const visibleIndices = [];
2437
+ let index = requestedStartIndex !== void 0 ? Math.max(0, Math.min(dataLength - 1, requestedStartIndex)) : 0;
2438
+ while (index > 0) {
2439
+ const previousIndex = index - 1;
2440
+ const previousPosition = positions[previousIndex];
2441
+ if (previousPosition === void 0) {
2442
+ index = previousIndex;
2443
+ continue;
2444
+ }
2445
+ const previousSize = getSize(previousIndex);
2446
+ if (previousSize === void 0) {
2447
+ index = previousIndex;
2448
+ continue;
2449
+ }
2450
+ if (previousPosition + previousSize <= offset) {
2451
+ break;
2452
+ }
2453
+ index = previousIndex;
2454
+ }
2455
+ for (; index < dataLength; index++) {
2456
+ const position = positions[index];
2457
+ if (position === void 0) {
2458
+ continue;
2459
+ }
2460
+ const size = getSize(index);
2461
+ if (size === void 0) {
2462
+ continue;
2463
+ }
2464
+ if (position < endOffset && position + size > offset) {
2465
+ visibleIndices.push(index);
2466
+ } else if (visibleIndices.length > 0 && position >= endOffset) {
2467
+ break;
2468
+ }
2469
+ }
2470
+ return visibleIndices;
2471
+ }
2472
+ function shouldAbortBootstrapReveal(options) {
2473
+ const {
2474
+ mountFrameCount,
2475
+ maxFrames = DEFAULT_BOOTSTRAP_REVEAL_MAX_FRAMES,
2476
+ maxPasses = DEFAULT_BOOTSTRAP_REVEAL_MAX_PASSES,
2477
+ passCount
2478
+ } = options;
2479
+ return mountFrameCount >= maxFrames || passCount >= maxPasses;
2480
+ }
2481
+ function abortBootstrapRevealIfNeeded(ctx, options) {
2482
+ if (!shouldAbortBootstrapReveal(options)) {
2483
+ return false;
2484
+ }
2485
+ if (IS_DEV) {
2486
+ console.warn(BOOTSTRAP_REVEAL_ABORT_WARNING);
2487
+ }
2488
+ abortBootstrapInitialScroll(ctx);
2489
+ return true;
2490
+ }
2491
+ function clearBootstrapInitialScrollSession(state) {
2492
+ var _a3;
2493
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2494
+ const frameHandle = bootstrapInitialScroll == null ? void 0 : bootstrapInitialScroll.frameHandle;
2495
+ if (frameHandle !== void 0 && typeof cancelAnimationFrame === "function") {
2496
+ cancelAnimationFrame(frameHandle);
2497
+ }
2498
+ if (bootstrapInitialScroll) {
2499
+ bootstrapInitialScroll.frameHandle = void 0;
2500
+ }
2501
+ setInitialScrollSession(state, {
2502
+ bootstrap: void 0,
2503
+ kind: (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind
2504
+ });
2505
+ }
2506
+ function startBootstrapInitialScrollSession(state, options) {
2507
+ var _a3, _b, _c;
2508
+ const previousBootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2509
+ setInitialScrollSession(state, {
2510
+ bootstrap: {
2511
+ frameHandle: previousBootstrapInitialScroll == null ? void 0 : previousBootstrapInitialScroll.frameHandle,
2512
+ // Re-arming during the initial mount should spend from the same watchdog budget.
2513
+ mountFrameCount: (_a3 = previousBootstrapInitialScroll == null ? void 0 : previousBootstrapInitialScroll.mountFrameCount) != null ? _a3 : 0,
2514
+ passCount: 0,
2515
+ previousResolvedOffset: void 0,
2516
+ scroll: options.scroll,
2517
+ seedContentOffset: (_c = (_b = options.seedContentOffset) != null ? _b : previousBootstrapInitialScroll == null ? void 0 : previousBootstrapInitialScroll.seedContentOffset) != null ? _c : options.scroll,
2518
+ targetIndexSeed: options.targetIndexSeed,
2519
+ visibleIndices: void 0
2520
+ },
2521
+ kind: "bootstrap"
2522
+ });
2523
+ }
2524
+ function resetBootstrapInitialScrollSession(state, options) {
2525
+ var _a3, _b, _c;
2526
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2527
+ if (!bootstrapInitialScroll) {
2528
+ if ((options == null ? void 0 : options.scroll) !== void 0) {
2529
+ startBootstrapInitialScrollSession(state, {
2530
+ scroll: options.scroll,
2531
+ seedContentOffset: options.seedContentOffset,
2532
+ targetIndexSeed: options.targetIndexSeed
2533
+ });
2534
+ }
2535
+ } else {
2536
+ bootstrapInitialScroll.passCount = 0;
2537
+ bootstrapInitialScroll.previousResolvedOffset = void 0;
2538
+ bootstrapInitialScroll.scroll = (_a3 = options == null ? void 0 : options.scroll) != null ? _a3 : bootstrapInitialScroll.scroll;
2539
+ bootstrapInitialScroll.seedContentOffset = (_b = options == null ? void 0 : options.seedContentOffset) != null ? _b : bootstrapInitialScroll.seedContentOffset;
2540
+ bootstrapInitialScroll.targetIndexSeed = (_c = options == null ? void 0 : options.targetIndexSeed) != null ? _c : bootstrapInitialScroll.targetIndexSeed;
2541
+ bootstrapInitialScroll.visibleIndices = void 0;
2542
+ setInitialScrollSession(state, {
2543
+ bootstrap: bootstrapInitialScroll,
2544
+ kind: "bootstrap"
2545
+ });
2546
+ }
2547
+ }
2548
+ function queueBootstrapInitialScrollReevaluation(state) {
2549
+ requestAnimationFrame(() => {
2550
+ var _a3;
2551
+ if (getBootstrapInitialScrollSession(state)) {
2552
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { forceFullItemPositions: true });
2553
+ }
2554
+ });
2555
+ }
2556
+ function ensureBootstrapInitialScrollFrameTicker(ctx) {
2557
+ const state = ctx.state;
2558
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2559
+ if (!bootstrapInitialScroll || bootstrapInitialScroll.frameHandle !== void 0) {
1899
2560
  return;
1900
2561
  }
1901
- const isAnimated = !!animated;
1902
- const isHorizontal = !!horizontal;
1903
- const left = isHorizontal ? offset : 0;
1904
- const top = isHorizontal ? 0 : offset;
1905
- scroller.scrollTo({ animated: isAnimated, x: left, y: top });
1906
- if (isAnimated) {
1907
- const target = scroller.getScrollEventTarget();
1908
- listenForScrollEnd(ctx, {
1909
- readOffset: () => scroller.getCurrentScrollOffset(),
1910
- target,
1911
- targetOffset: offset
2562
+ const tick = () => {
2563
+ const activeBootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2564
+ if (!activeBootstrapInitialScroll) {
2565
+ return;
2566
+ }
2567
+ activeBootstrapInitialScroll.frameHandle = void 0;
2568
+ activeBootstrapInitialScroll.mountFrameCount += 1;
2569
+ if (abortBootstrapRevealIfNeeded(ctx, {
2570
+ mountFrameCount: activeBootstrapInitialScroll.mountFrameCount,
2571
+ passCount: activeBootstrapInitialScroll.passCount
2572
+ })) {
2573
+ return;
2574
+ }
2575
+ ensureBootstrapInitialScrollFrameTicker(ctx);
2576
+ };
2577
+ bootstrapInitialScroll.frameHandle = requestAnimationFrame(tick);
2578
+ }
2579
+ function rearmBootstrapInitialScroll(ctx, options) {
2580
+ resetBootstrapInitialScrollSession(ctx.state, options);
2581
+ ensureBootstrapInitialScrollFrameTicker(ctx);
2582
+ queueBootstrapInitialScrollReevaluation(ctx.state);
2583
+ }
2584
+ function createInitialScrollAtEndTarget(options) {
2585
+ const { dataLength, footerSize, preserveForFooterLayout, stylePaddingBottom } = options;
2586
+ return {
2587
+ contentOffset: void 0,
2588
+ index: Math.max(0, dataLength - 1),
2589
+ preserveForBottomPadding: true,
2590
+ preserveForFooterLayout,
2591
+ viewOffset: -stylePaddingBottom - footerSize,
2592
+ viewPosition: 1
2593
+ };
2594
+ }
2595
+ function shouldPreserveInitialScrollForBottomPadding(target) {
2596
+ return !!(target == null ? void 0 : target.preserveForBottomPadding);
2597
+ }
2598
+ function shouldPreserveInitialScrollForFooterLayout(target) {
2599
+ return !!(target == null ? void 0 : target.preserveForFooterLayout);
2600
+ }
2601
+ function isRetargetableBottomAlignedInitialScrollTarget(target) {
2602
+ return !!(target && target.viewPosition === 1 && (shouldPreserveInitialScrollForBottomPadding(target) || shouldPreserveInitialScrollForFooterLayout(target)));
2603
+ }
2604
+ function createRetargetedBottomAlignedInitialScroll(options) {
2605
+ const { dataLength, footerSize, initialScrollAtEnd, stylePaddingBottom, target } = options;
2606
+ const preserveForFooterLayout = shouldPreserveInitialScrollForFooterLayout(target);
2607
+ return {
2608
+ ...target,
2609
+ contentOffset: void 0,
2610
+ index: initialScrollAtEnd ? Math.max(0, dataLength - 1) : target.index,
2611
+ preserveForBottomPadding: true,
2612
+ preserveForFooterLayout,
2613
+ viewOffset: -stylePaddingBottom - (preserveForFooterLayout ? footerSize : 0),
2614
+ viewPosition: 1
2615
+ };
2616
+ }
2617
+ function areEquivalentBootstrapInitialScrollTargets(current, next) {
2618
+ return current.index === next.index && current.preserveForBottomPadding === next.preserveForBottomPadding && current.preserveForFooterLayout === next.preserveForFooterLayout && current.viewOffset === next.viewOffset && current.viewPosition === next.viewPosition;
2619
+ }
2620
+ function clearPendingInitialScrollFooterLayout(state, target) {
2621
+ if (!shouldPreserveInitialScrollForFooterLayout(target)) {
2622
+ return;
2623
+ }
2624
+ if (state.didFinishInitialScroll && !getBootstrapInitialScrollSession(state)) {
2625
+ state.initialScroll = void 0;
2626
+ setInitialScrollSession(state);
2627
+ return;
2628
+ }
2629
+ setInitialScrollTarget(state, { ...target, preserveForFooterLayout: void 0 });
2630
+ }
2631
+ function didFinishedInitialScrollMoveAwayFromTarget(ctx, target, epsilon = DEFAULT_BOOTSTRAP_REVEAL_EPSILON) {
2632
+ const state = ctx.state;
2633
+ if (!state.didFinishInitialScroll) {
2634
+ return false;
2635
+ }
2636
+ const currentOffset = getObservedBootstrapInitialScrollOffset(state);
2637
+ return Math.abs(currentOffset - resolveInitialScrollOffset(ctx, target)) > epsilon;
2638
+ }
2639
+ function getObservedBootstrapInitialScrollOffset(state) {
2640
+ var _a3, _b, _c, _d;
2641
+ const observedOffset = (_b = (_a3 = state.refScroller.current) == null ? void 0 : _a3.getCurrentScrollOffset) == null ? void 0 : _b.call(_a3);
2642
+ return typeof observedOffset === "number" && Number.isFinite(observedOffset) ? observedOffset : (_d = (_c = state.scrollPending) != null ? _c : state.scroll) != null ? _d : 0;
2643
+ }
2644
+ function startBootstrapInitialScrollOnMount(ctx, options) {
2645
+ var _a3, _b, _c;
2646
+ const { initialScrollAtEnd, target } = options;
2647
+ const state = ctx.state;
2648
+ const offset = resolveInitialScrollOffset(ctx, target);
2649
+ const shouldFinishAtOrigin = offset === 0 && !initialScrollAtEnd && (isOffsetInitialScrollSession(state) ? Math.abs((_a3 = target.contentOffset) != null ? _a3 : 0) <= 1 : target.index === 0 && ((_b = target.viewPosition) != null ? _b : 0) === 0 && Math.abs((_c = target.viewOffset) != null ? _c : 0) <= 1);
2650
+ const shouldFinishWithPreservedTarget = state.props.data.length === 0 && target.index !== void 0;
2651
+ if (shouldFinishAtOrigin) {
2652
+ clearBootstrapInitialScrollSession(state);
2653
+ finishInitialScroll(ctx, {
2654
+ resolvedOffset: offset
2655
+ });
2656
+ } else if (shouldFinishWithPreservedTarget) {
2657
+ clearBootstrapInitialScrollSession(state);
2658
+ finishInitialScroll(ctx, {
2659
+ preserveTarget: true,
2660
+ resolvedOffset: offset
1912
2661
  });
1913
2662
  } else {
1914
- state.scroll = offset;
1915
- setTimeout(() => {
1916
- finishScrollTo(ctx);
1917
- }, 100);
2663
+ startBootstrapInitialScrollSession(state, {
2664
+ scroll: offset,
2665
+ seedContentOffset: 0 ,
2666
+ targetIndexSeed: target.index
2667
+ });
2668
+ ensureBootstrapInitialScrollFrameTicker(ctx);
1918
2669
  }
1919
2670
  }
1920
- function listenForScrollEnd(ctx, params) {
1921
- const { readOffset, target, targetOffset } = params;
1922
- if (!target) {
1923
- finishScrollTo(ctx);
2671
+ function handleBootstrapInitialScrollDataChange(ctx, options) {
2672
+ const { dataLength, didDataChange, initialScrollAtEnd, previousDataLength, stylePaddingBottom } = options;
2673
+ const state = ctx.state;
2674
+ const initialScroll = state.initialScroll;
2675
+ if (isOffsetInitialScrollSession(state) || !initialScroll) {
1924
2676
  return;
1925
2677
  }
1926
- const supportsScrollEnd = "onscrollend" in target;
1927
- let idleTimeout;
1928
- let settled = false;
1929
- const targetToken = ctx.state.scrollingTo;
1930
- const maxTimeout = setTimeout(() => finish("max"), SCROLL_END_MAX_MS);
1931
- const cleanup = () => {
1932
- target.removeEventListener("scroll", onScroll2);
1933
- if (supportsScrollEnd) {
1934
- target.removeEventListener("scrollend", onScrollEnd);
2678
+ const shouldResetDidFinish = !!(state.didFinishInitialScroll && previousDataLength === 0 && dataLength > 0 && initialScroll.index !== void 0);
2679
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2680
+ const shouldRetargetBottomAligned = dataLength > 0 && (initialScrollAtEnd || isRetargetableBottomAlignedInitialScrollTarget(initialScroll));
2681
+ if (!didDataChange && !shouldResetDidFinish && !shouldRetargetBottomAligned) {
2682
+ return;
2683
+ }
2684
+ if (shouldRetargetBottomAligned) {
2685
+ if (!shouldResetDidFinish && didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2686
+ clearPendingInitialScrollFooterLayout(state, initialScroll);
2687
+ return;
1935
2688
  }
1936
- if (idleTimeout) {
1937
- clearTimeout(idleTimeout);
2689
+ const updatedInitialScroll = initialScrollAtEnd ? createInitialScrollAtEndTarget({
2690
+ dataLength,
2691
+ footerSize: peek$(ctx, "footerSize") || 0,
2692
+ preserveForFooterLayout: shouldPreserveInitialScrollForFooterLayout(initialScroll),
2693
+ stylePaddingBottom
2694
+ }) : createRetargetedBottomAlignedInitialScroll({
2695
+ dataLength,
2696
+ footerSize: peek$(ctx, "footerSize") || 0,
2697
+ initialScrollAtEnd,
2698
+ stylePaddingBottom,
2699
+ target: initialScroll
2700
+ });
2701
+ if (!areEquivalentBootstrapInitialScrollTargets(initialScroll, updatedInitialScroll) || !!bootstrapInitialScroll || shouldResetDidFinish || didDataChange) {
2702
+ setInitialScrollTarget(state, updatedInitialScroll, {
2703
+ resetDidFinish: shouldResetDidFinish
2704
+ });
2705
+ rearmBootstrapInitialScroll(ctx, {
2706
+ scroll: resolveInitialScrollOffset(ctx, updatedInitialScroll),
2707
+ seedContentOffset: shouldResetDidFinish && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
2708
+ targetIndexSeed: updatedInitialScroll.index
2709
+ });
2710
+ return;
1938
2711
  }
1939
- clearTimeout(maxTimeout);
1940
- };
1941
- const finish = (reason) => {
1942
- if (settled) return;
1943
- if (targetToken !== ctx.state.scrollingTo) {
1944
- settled = true;
1945
- cleanup();
2712
+ }
2713
+ if (!didDataChange) {
2714
+ return;
2715
+ }
2716
+ if (bootstrapInitialScroll || shouldResetDidFinish) {
2717
+ setInitialScrollTarget(state, initialScroll, {
2718
+ resetDidFinish: shouldResetDidFinish
2719
+ });
2720
+ rearmBootstrapInitialScroll(ctx, {
2721
+ scroll: resolveInitialScrollOffset(ctx, initialScroll),
2722
+ seedContentOffset: shouldResetDidFinish && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
2723
+ targetIndexSeed: initialScroll.index
2724
+ });
2725
+ }
2726
+ }
2727
+ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2728
+ const { dataLength, footerSize, initialScrollAtEnd, stylePaddingBottom } = options;
2729
+ const state = ctx.state;
2730
+ if (!initialScrollAtEnd) {
2731
+ return;
2732
+ }
2733
+ const initialScroll = state.initialScroll;
2734
+ if (isOffsetInitialScrollSession(state) || dataLength === 0 || !initialScroll) {
2735
+ return;
2736
+ }
2737
+ const shouldProcessFooterLayout = !!getBootstrapInitialScrollSession(state) || shouldPreserveInitialScrollForFooterLayout(initialScroll);
2738
+ if (!shouldProcessFooterLayout) {
2739
+ return;
2740
+ }
2741
+ if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2742
+ clearPendingInitialScrollFooterLayout(state, initialScroll);
2743
+ } else {
2744
+ const updatedInitialScroll = createInitialScrollAtEndTarget({
2745
+ dataLength,
2746
+ footerSize,
2747
+ preserveForFooterLayout: shouldPreserveInitialScrollForFooterLayout(initialScroll),
2748
+ stylePaddingBottom
2749
+ });
2750
+ const didTargetChange = initialScroll.index !== updatedInitialScroll.index || initialScroll.viewPosition !== updatedInitialScroll.viewPosition || initialScroll.viewOffset !== updatedInitialScroll.viewOffset;
2751
+ if (!didTargetChange) {
2752
+ clearPendingInitialScrollFooterLayout(state, initialScroll);
2753
+ } else {
2754
+ setInitialScrollTarget(state, updatedInitialScroll, {
2755
+ resetDidFinish: !!state.didFinishInitialScroll
2756
+ });
2757
+ rearmBootstrapInitialScroll(ctx, {
2758
+ scroll: resolveInitialScrollOffset(ctx, updatedInitialScroll),
2759
+ targetIndexSeed: updatedInitialScroll.index
2760
+ });
2761
+ }
2762
+ }
2763
+ }
2764
+ function evaluateBootstrapInitialScroll(ctx) {
2765
+ var _a3, _b;
2766
+ const state = ctx.state;
2767
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2768
+ const initialScroll = state.initialScroll;
2769
+ if (!bootstrapInitialScroll || !initialScroll || isOffsetInitialScrollSession(state) || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll)) {
2770
+ return;
2771
+ }
2772
+ bootstrapInitialScroll.passCount += 1;
2773
+ if (abortBootstrapRevealIfNeeded(ctx, {
2774
+ mountFrameCount: bootstrapInitialScroll.mountFrameCount,
2775
+ passCount: bootstrapInitialScroll.passCount
2776
+ })) {
2777
+ return;
2778
+ }
2779
+ if (initialScroll.index !== void 0 && state.startBuffered >= 0 && state.endBuffered >= 0 && initialScroll.index >= state.startBuffered && initialScroll.index <= state.endBuffered) {
2780
+ bootstrapInitialScroll.targetIndexSeed = void 0;
2781
+ }
2782
+ const resolvedOffset = resolveInitialScrollOffset(ctx, initialScroll);
2783
+ const mountedBufferedIndices = getMountedBufferedIndices(state);
2784
+ const areMountedBufferedIndicesMeasured = checkAllSizesKnown(state, mountedBufferedIndices);
2785
+ const didResolvedOffsetChange = Math.abs(bootstrapInitialScroll.scroll - resolvedOffset) > 1;
2786
+ const { data } = state.props;
2787
+ const visibleIndices = getBootstrapRevealVisibleIndices({
2788
+ dataLength: data.length,
2789
+ getSize: (index) => {
2790
+ var _a4, _b2;
2791
+ const id = (_a4 = state.idCache[index]) != null ? _a4 : getId(state, index);
2792
+ return (_b2 = state.sizes.get(id)) != null ? _b2 : getItemSize(ctx, id, index, data[index]);
2793
+ },
2794
+ offset: resolvedOffset,
2795
+ positions: state.positions,
2796
+ scrollLength: state.scrollLength,
2797
+ startIndex: (_b = bootstrapInitialScroll.targetIndexSeed) != null ? _b : state.startBuffered >= 0 ? state.startBuffered : void 0
2798
+ });
2799
+ const areVisibleIndicesMeasured = visibleIndices.length > 0 && visibleIndices.every((index) => {
2800
+ var _a4;
2801
+ const id = (_a4 = state.idCache[index]) != null ? _a4 : getId(state, index);
2802
+ return state.sizesKnown.has(id);
2803
+ });
2804
+ const previousResolvedOffset = bootstrapInitialScroll.previousResolvedOffset;
2805
+ const previousVisibleIndices = bootstrapInitialScroll.visibleIndices;
2806
+ bootstrapInitialScroll.previousResolvedOffset = resolvedOffset;
2807
+ bootstrapInitialScroll.visibleIndices = visibleIndices;
2808
+ if (didResolvedOffsetChange) {
2809
+ bootstrapInitialScroll.scroll = resolvedOffset;
2810
+ queueBootstrapInitialScrollReevaluation(state);
2811
+ return;
2812
+ }
2813
+ if (!areMountedBufferedIndicesMeasured || !areVisibleIndicesMeasured) {
2814
+ return;
2815
+ }
2816
+ const didRevealSettle = previousResolvedOffset !== void 0 && Math.abs(previousResolvedOffset - resolvedOffset) <= DEFAULT_BOOTSTRAP_REVEAL_EPSILON && doVisibleIndicesMatch(previousVisibleIndices, visibleIndices);
2817
+ if (!didRevealSettle) {
2818
+ queueBootstrapInitialScrollReevaluation(state);
2819
+ return;
2820
+ }
2821
+ {
2822
+ clearBootstrapInitialScrollSession(state);
2823
+ dispatchInitialScroll(ctx, {
2824
+ forceScroll: true,
2825
+ resolvedOffset,
2826
+ target: initialScroll,
2827
+ waitForCompletionFrame: Platform.OS === "web"
2828
+ });
2829
+ }
2830
+ }
2831
+ function finishBootstrapInitialScrollWithoutScroll(ctx, resolvedOffset) {
2832
+ const state = ctx.state;
2833
+ clearBootstrapInitialScrollSession(state);
2834
+ finishInitialScroll(ctx, {
2835
+ preserveTarget: shouldPreserveInitialScrollForFooterLayout(state.initialScroll),
2836
+ recalculateItems: true,
2837
+ resolvedOffset
2838
+ });
2839
+ }
2840
+ function abortBootstrapInitialScroll(ctx) {
2841
+ var _a3, _b, _c, _d;
2842
+ const state = ctx.state;
2843
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2844
+ const initialScroll = state.initialScroll;
2845
+ if (bootstrapInitialScroll && initialScroll && !isOffsetInitialScrollSession(state) && state.refScroller.current) {
2846
+ clearBootstrapInitialScrollSession(state);
2847
+ dispatchInitialScroll(ctx, {
2848
+ forceScroll: true,
2849
+ resolvedOffset: bootstrapInitialScroll.scroll,
2850
+ target: initialScroll,
2851
+ waitForCompletionFrame: Platform.OS === "web"
2852
+ });
2853
+ } else {
2854
+ finishBootstrapInitialScrollWithoutScroll(
2855
+ ctx,
2856
+ (_d = (_c = (_b = (_a3 = getBootstrapInitialScrollSession(state)) == null ? void 0 : _a3.scroll) != null ? _b : state.scrollPending) != null ? _c : state.scroll) != null ? _d : 0
2857
+ );
2858
+ }
2859
+ }
2860
+
2861
+ // src/core/checkFinishedScroll.ts
2862
+ var INITIAL_SCROLL_MAX_FALLBACK_CHECKS = 20;
2863
+ var INITIAL_SCROLL_COMPLETION_TARGET_EPSILON = 1;
2864
+ var INITIAL_SCROLL_ZERO_TARGET_EPSILON = 1;
2865
+ var SILENT_INITIAL_SCROLL_RETRY_DELAY_MS = 16;
2866
+ function checkFinishedScroll(ctx, options) {
2867
+ const scrollingTo = ctx.state.scrollingTo;
2868
+ if (options == null ? void 0 : options.onlyIfAligned) {
2869
+ if (!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) || scrollingTo.animated) {
1946
2870
  return;
1947
2871
  }
1948
- const currentOffset = readOffset();
1949
- const isNearTarget = Math.abs(currentOffset - targetOffset) <= SCROLL_END_TARGET_EPSILON;
1950
- if (reason === "scrollend" && !isNearTarget) {
2872
+ if (!getResolvedScrollCompletionState(ctx, scrollingTo).isAtResolvedTarget) {
1951
2873
  return;
1952
2874
  }
1953
- settled = true;
1954
- cleanup();
1955
- finishScrollTo(ctx);
2875
+ }
2876
+ ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
2877
+ }
2878
+ function hasScrollCompletionOwnership(state, options) {
2879
+ const { clampedTargetOffset, scrollingTo } = options;
2880
+ return !scrollingTo.isInitialScroll || state.hasScrolled || clampedTargetOffset <= INITIAL_SCROLL_COMPLETION_TARGET_EPSILON;
2881
+ }
2882
+ function isSilentInitialDispatch(state, scrollingTo) {
2883
+ return !!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && initialScrollCompletion.didDispatchNativeScroll(state) && !state.hasScrolled;
2884
+ }
2885
+ function getInitialScrollWatchdogTargetOffset(state) {
2886
+ var _a3;
2887
+ return (_a3 = initialScrollWatchdog.get(state)) == null ? void 0 : _a3.targetOffset;
2888
+ }
2889
+ function isNativeInitialNonZeroTarget(state) {
2890
+ const targetOffset = getInitialScrollWatchdogTargetOffset(state);
2891
+ return !state.didFinishInitialScroll && initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset);
2892
+ }
2893
+ function shouldFinishInitialScrollWithoutNativeProgress(state, scrollingTo) {
2894
+ var _a3, _b;
2895
+ if (!scrollingTo.isInitialScroll || scrollingTo.animated || !state.didContainersLayout) {
2896
+ return false;
2897
+ }
2898
+ if (((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap") {
2899
+ return false;
2900
+ }
2901
+ const targetOffset = (_b = scrollingTo.targetOffset) != null ? _b : scrollingTo.offset;
2902
+ if (initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset) && initialScrollCompletion.didDispatchNativeScroll(state) && !state.hasScrolled) {
2903
+ return false;
2904
+ }
2905
+ if (initialScrollWatchdog.isAtZeroTargetOffset(targetOffset) || Math.abs(state.scroll - targetOffset) > 1 || Math.abs(state.scrollPending - targetOffset) > 1) {
2906
+ return false;
2907
+ }
2908
+ return !!scrollingTo.waitForInitialScrollCompletionFrame || isNativeInitialNonZeroTarget(state);
2909
+ }
2910
+ function shouldFinishInitialZeroTargetScroll(ctx) {
2911
+ var _a3;
2912
+ const { state } = ctx;
2913
+ 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;
2914
+ }
2915
+ function getResolvedScrollCompletionState(ctx, scrollingTo) {
2916
+ var _a3;
2917
+ const { state } = ctx;
2918
+ const scroll = state.scrollPending;
2919
+ const adjust = state.scrollAdjustHandler.getAdjust();
2920
+ const clampedTargetOffset = (_a3 = scrollingTo.targetOffset) != null ? _a3 : clampScrollOffset(ctx, scrollingTo.offset - (scrollingTo.viewOffset || 0), scrollingTo);
2921
+ const maxOffset = clampScrollOffset(ctx, scroll, scrollingTo);
2922
+ const diff1 = Math.abs(scroll - clampedTargetOffset);
2923
+ const diff2 = Math.abs(diff1 - adjust);
2924
+ return {
2925
+ clampedTargetOffset,
2926
+ isAtResolvedTarget: Math.abs(scroll - maxOffset) < 1 && (diff1 < 1 || !scrollingTo.animated && diff2 < 1)
1956
2927
  };
1957
- const onScroll2 = () => {
1958
- if (idleTimeout) {
1959
- clearTimeout(idleTimeout);
2928
+ }
2929
+ function checkFinishedScrollFrame(ctx) {
2930
+ const scrollingTo = ctx.state.scrollingTo;
2931
+ if (!scrollingTo) {
2932
+ return;
2933
+ }
2934
+ const { state } = ctx;
2935
+ state.animFrameCheckFinishedScroll = void 0;
2936
+ const completionState = getResolvedScrollCompletionState(ctx, scrollingTo);
2937
+ if (completionState.isAtResolvedTarget && hasScrollCompletionOwnership(state, {
2938
+ clampedTargetOffset: completionState.clampedTargetOffset,
2939
+ scrollingTo
2940
+ })) {
2941
+ finishScrollTo(ctx);
2942
+ }
2943
+ }
2944
+ function scrollToFallbackOffset(ctx, offset) {
2945
+ var _a3;
2946
+ (_a3 = ctx.state.refScroller.current) == null ? void 0 : _a3.scrollTo({
2947
+ animated: false,
2948
+ x: ctx.state.props.horizontal ? offset : 0,
2949
+ y: ctx.state.props.horizontal ? 0 : offset
2950
+ });
2951
+ }
2952
+ function checkFinishedScrollFallback(ctx) {
2953
+ const state = ctx.state;
2954
+ const scrollingTo = state.scrollingTo;
2955
+ const shouldFinishInitialZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
2956
+ const silentInitialDispatch = isSilentInitialDispatch(state, scrollingTo);
2957
+ const canFinishInitialWithoutNativeProgress = scrollingTo !== void 0 ? shouldFinishInitialScrollWithoutNativeProgress(state, scrollingTo) : false;
2958
+ const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && !shouldFinishInitialZeroTarget && !canFinishInitialWithoutNativeProgress || !state.didContainersLayout;
2959
+ const initialDelay = shouldFinishInitialZeroTarget || canFinishInitialWithoutNativeProgress ? 0 : silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : slowTimeout ? 500 : 100;
2960
+ state.timeoutCheckFinishedScrollFallback = setTimeout(() => {
2961
+ let numChecks = 0;
2962
+ const scheduleFallbackCheck = (delay) => {
2963
+ state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, delay);
2964
+ };
2965
+ const checkHasScrolled = () => {
2966
+ var _c;
2967
+ state.timeoutCheckFinishedScrollFallback = void 0;
2968
+ const isStillScrollingTo = state.scrollingTo;
2969
+ if (isStillScrollingTo) {
2970
+ numChecks++;
2971
+ const isNativeInitialPending = isNativeInitialNonZeroTarget(state) && !state.hasScrolled;
2972
+ const maxChecks = silentInitialDispatch ? 5 : isNativeInitialPending ? INITIAL_SCROLL_MAX_FALLBACK_CHECKS : 5;
2973
+ const shouldFinishZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
2974
+ const canFinishInitialScrollWithoutNativeProgress = shouldFinishInitialScrollWithoutNativeProgress(
2975
+ state,
2976
+ isStillScrollingTo
2977
+ );
2978
+ const completionState = getResolvedScrollCompletionState(ctx, isStillScrollingTo);
2979
+ const canFinishAfterSilentNativeDispatch = silentInitialDispatch && completionState.isAtResolvedTarget && numChecks >= 1;
2980
+ if (shouldFinishZeroTarget || state.hasScrolled || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
2981
+ finishScrollTo(ctx);
2982
+ } else if (isNativeInitialPending && numChecks <= maxChecks) {
2983
+ const targetOffset = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : state.scrollPending;
2984
+ scrollToFallbackOffset(ctx, targetOffset);
2985
+ scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
2986
+ } else {
2987
+ scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
2988
+ }
2989
+ }
2990
+ };
2991
+ checkHasScrolled();
2992
+ }, initialDelay);
2993
+ }
2994
+
2995
+ // src/core/initialScrollLifecycle.ts
2996
+ function handleInitialScrollLayoutReady(ctx) {
2997
+ var _a3;
2998
+ if (!ctx.state.initialScroll) {
2999
+ return;
3000
+ }
3001
+ const runScroll = () => advanceCurrentInitialScrollSession(ctx, { forceScroll: true });
3002
+ runScroll();
3003
+ if (((_a3 = ctx.state.initialScrollSession) == null ? void 0 : _a3.kind) !== "offset") {
3004
+ requestAnimationFrame(runScroll);
3005
+ }
3006
+ checkFinishedScroll(ctx, { onlyIfAligned: true });
3007
+ }
3008
+ function initializeInitialScrollOnMount(ctx, options) {
3009
+ var _a3, _b;
3010
+ const { dataLength, hasFooterComponent, initialContentOffset, initialScrollAtEnd, useBootstrapInitialScroll } = options;
3011
+ const state = ctx.state;
3012
+ const initialScroll = state.initialScroll;
3013
+ const resolvedInitialContentOffset = initialContentOffset != null ? initialContentOffset : 0;
3014
+ const preserveForFooterLayout = useBootstrapInitialScroll && initialScrollAtEnd && hasFooterComponent;
3015
+ if (initialScroll && (initialScroll.contentOffset === void 0 || !!initialScroll.preserveForFooterLayout !== preserveForFooterLayout && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) !== "offset")) {
3016
+ setInitialScrollTarget(state, {
3017
+ ...initialScroll,
3018
+ contentOffset: resolvedInitialContentOffset,
3019
+ preserveForFooterLayout
3020
+ });
3021
+ }
3022
+ if (useBootstrapInitialScroll && initialScroll && ((_b = state.initialScrollSession) == null ? void 0 : _b.kind) !== "offset") {
3023
+ startBootstrapInitialScrollOnMount(ctx, {
3024
+ initialScrollAtEnd,
3025
+ target: state.initialScroll
3026
+ });
3027
+ return;
3028
+ }
3029
+ const hasPendingDataDependentInitialScroll = !!initialScroll && dataLength === 0 && !(resolvedInitialContentOffset === 0 && !initialScrollAtEnd);
3030
+ if (!resolvedInitialContentOffset && !hasPendingDataDependentInitialScroll) {
3031
+ if (initialScroll && !initialScrollAtEnd) {
3032
+ finishInitialScroll(ctx, {
3033
+ resolvedOffset: resolvedInitialContentOffset
3034
+ });
3035
+ } else {
3036
+ setInitialRenderState(ctx, { didInitialScroll: true });
1960
3037
  }
1961
- idleTimeout = setTimeout(() => finish("idle"), SCROLL_END_IDLE_MS);
1962
- };
1963
- const onScrollEnd = () => finish("scrollend");
1964
- target.addEventListener("scroll", onScroll2);
1965
- if (supportsScrollEnd) {
1966
- target.addEventListener("scrollend", onScrollEnd);
1967
- } else {
1968
- idleTimeout = setTimeout(() => finish("idle"), SMOOTH_SCROLL_DURATION_MS);
1969
3038
  }
1970
3039
  }
1971
-
1972
- // src/core/scrollTo.ts
1973
- var WATCHDOG_OFFSET_EPSILON = 1;
1974
- function scrollTo(ctx, params) {
1975
- var _a3, _b;
3040
+ function handleInitialScrollDataChange(ctx, options) {
3041
+ var _a3, _b, _c;
3042
+ const { dataLength, didDataChange, initialScrollAtEnd, stylePaddingBottom, useBootstrapInitialScroll } = options;
1976
3043
  const state = ctx.state;
1977
- const { noScrollingTo, forceScroll, ...scrollTarget } = params;
1978
- const { animated, isInitialScroll, offset: scrollTargetOffset, precomputedWithViewOffset } = scrollTarget;
1979
- const {
1980
- props: { horizontal }
1981
- } = state;
1982
- if (state.animFrameCheckFinishedScroll) {
1983
- cancelAnimationFrame(ctx.state.animFrameCheckFinishedScroll);
1984
- }
1985
- if (state.timeoutCheckFinishedScrollFallback) {
1986
- clearTimeout(ctx.state.timeoutCheckFinishedScrollFallback);
3044
+ const previousDataLength = (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.previousDataLength) != null ? _b : 0;
3045
+ if (state.initialScrollSession) {
3046
+ state.initialScrollSession.previousDataLength = dataLength;
1987
3047
  }
1988
- let offset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, scrollTargetOffset, scrollTarget);
1989
- offset = clampScrollOffset(ctx, offset, scrollTarget);
1990
- state.scrollHistory.length = 0;
1991
- if (!noScrollingTo) {
1992
- state.scrollingTo = {
1993
- ...scrollTarget,
1994
- targetOffset: offset
1995
- };
3048
+ setInitialScrollSession(state);
3049
+ if (useBootstrapInitialScroll) {
3050
+ handleBootstrapInitialScrollDataChange(ctx, {
3051
+ dataLength,
3052
+ didDataChange,
3053
+ initialScrollAtEnd,
3054
+ previousDataLength,
3055
+ stylePaddingBottom
3056
+ });
3057
+ return;
1996
3058
  }
1997
- state.scrollPending = offset;
1998
- const shouldWatchInitialNativeScroll = !state.didFinishInitialScroll && (isInitialScroll || !!state.initialNativeScrollWatchdog) && offset > WATCHDOG_OFFSET_EPSILON;
1999
- const shouldClearInitialNativeScrollWatchdog = !state.didFinishInitialScroll && !!state.initialNativeScrollWatchdog && offset <= WATCHDOG_OFFSET_EPSILON;
2000
- if (shouldWatchInitialNativeScroll) {
2001
- state.hasScrolled = false;
2002
- state.initialNativeScrollWatchdog = {
2003
- startScroll: (_b = (_a3 = state.initialNativeScrollWatchdog) == null ? void 0 : _a3.startScroll) != null ? _b : state.scroll,
2004
- targetOffset: offset
2005
- };
2006
- } else if (shouldClearInitialNativeScrollWatchdog) {
2007
- state.initialNativeScrollWatchdog = void 0;
3059
+ const shouldReplayFinishedOffsetInitialScroll = previousDataLength === 0 && dataLength > 0 && !!state.initialScroll && ((_c = ctx.state.initialScrollSession) == null ? void 0 : _c.kind) === "offset" && !!state.didFinishInitialScroll;
3060
+ if (previousDataLength !== 0 || dataLength === 0 || !state.initialScroll || !state.queuedInitialLayout || state.didFinishInitialScroll && !shouldReplayFinishedOffsetInitialScroll) {
3061
+ return;
2008
3062
  }
2009
- if (forceScroll || !isInitialScroll || Platform.OS === "android") {
2010
- doScrollTo(ctx, { animated, horizontal, offset });
2011
- } else {
2012
- state.scroll = offset;
3063
+ if (shouldReplayFinishedOffsetInitialScroll) {
3064
+ state.didFinishInitialScroll = false;
2013
3065
  }
3066
+ advanceCurrentInitialScrollSession(ctx);
2014
3067
  }
2015
3068
 
2016
- // src/core/doMaintainScrollAtEnd.ts
2017
- function doMaintainScrollAtEnd(ctx) {
3069
+ // src/utils/requestAdjust.ts
3070
+ function requestAdjust(ctx, positionDiff, dataChanged) {
2018
3071
  const state = ctx.state;
2019
- const {
2020
- didContainersLayout,
2021
- isAtEnd,
2022
- pendingNativeMVCPAdjust,
2023
- refScroller,
2024
- props: { maintainScrollAtEnd }
2025
- } = state;
2026
- const shouldMaintainScrollAtEnd = !!(isAtEnd && maintainScrollAtEnd && didContainersLayout);
2027
- if (pendingNativeMVCPAdjust) {
2028
- state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
2029
- return false;
2030
- }
2031
- state.pendingMaintainScrollAtEnd = false;
2032
- if (shouldMaintainScrollAtEnd) {
2033
- const contentSize = getContentSize(ctx);
2034
- if (contentSize < state.scrollLength) {
2035
- state.scroll = 0;
2036
- }
2037
- requestAnimationFrame(() => {
2038
- var _a3;
2039
- if (state.isAtEnd) {
2040
- state.maintainingScrollAtEnd = true;
2041
- (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
2042
- animated: maintainScrollAtEnd.animated
2043
- });
2044
- setTimeout(
2045
- () => {
2046
- state.maintainingScrollAtEnd = false;
2047
- },
2048
- maintainScrollAtEnd.animated ? 500 : 0
2049
- );
3072
+ if (Math.abs(positionDiff) > 0.1) {
3073
+ const doit = () => {
3074
+ {
3075
+ state.scrollAdjustHandler.requestAdjust(positionDiff);
3076
+ if (state.adjustingFromInitialMount) {
3077
+ state.adjustingFromInitialMount--;
3078
+ }
2050
3079
  }
2051
- });
2052
- return true;
3080
+ };
3081
+ state.scroll += positionDiff;
3082
+ state.scrollForNextCalculateItemsInView = void 0;
3083
+ const readyToRender = peek$(ctx, "readyToRender");
3084
+ if (readyToRender) {
3085
+ doit();
3086
+ } else {
3087
+ state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
3088
+ requestAnimationFrame(doit);
3089
+ }
2053
3090
  }
2054
- return false;
2055
3091
  }
2056
3092
 
2057
3093
  // src/core/mvcp.ts
@@ -2333,94 +3369,6 @@ function prepareMVCP(ctx, dataChanged) {
2333
3369
  }
2334
3370
  }
2335
3371
 
2336
- // src/core/updateScroll.ts
2337
- function updateScroll(ctx, newScroll, forceUpdate) {
2338
- var _a3;
2339
- const state = ctx.state;
2340
- const { ignoreScrollFromMVCP, lastScrollAdjustForHistory, scrollAdjustHandler, scrollHistory, scrollingTo } = state;
2341
- const prevScroll = state.scroll;
2342
- state.hasScrolled = true;
2343
- state.lastBatchingAction = Date.now();
2344
- const currentTime = Date.now();
2345
- const adjust = scrollAdjustHandler.getAdjust();
2346
- const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
2347
- if (adjustChanged) {
2348
- scrollHistory.length = 0;
2349
- }
2350
- state.lastScrollAdjustForHistory = adjust;
2351
- if (scrollingTo === void 0 && !(scrollHistory.length === 0 && newScroll === state.scroll)) {
2352
- if (!adjustChanged) {
2353
- scrollHistory.push({ scroll: newScroll, time: currentTime });
2354
- }
2355
- }
2356
- if (scrollHistory.length > 5) {
2357
- scrollHistory.shift();
2358
- }
2359
- if (ignoreScrollFromMVCP && !scrollingTo) {
2360
- const { lt, gt } = ignoreScrollFromMVCP;
2361
- if (lt && newScroll < lt || gt && newScroll > gt) {
2362
- state.ignoreScrollFromMVCPIgnored = true;
2363
- return;
2364
- }
2365
- }
2366
- state.scrollPrev = prevScroll;
2367
- state.scrollPrevTime = state.scrollTime;
2368
- state.scroll = newScroll;
2369
- state.scrollTime = currentTime;
2370
- const scrollDelta = Math.abs(newScroll - prevScroll);
2371
- const didResolvePendingNativeMVCPAdjust = resolvePendingNativeMVCPAdjust(ctx, newScroll);
2372
- const scrollLength = state.scrollLength;
2373
- const lastCalculated = state.scrollLastCalculate;
2374
- const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
2375
- const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
2376
- if (shouldUpdate) {
2377
- state.scrollLastCalculate = state.scroll;
2378
- state.ignoreScrollFromMVCPIgnored = false;
2379
- state.lastScrollDelta = scrollDelta;
2380
- const runCalculateItems = () => {
2381
- var _a4;
2382
- (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { doMVCP: scrollingTo !== void 0 });
2383
- checkThresholds(ctx);
2384
- };
2385
- if (scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength) {
2386
- ReactDOM.flushSync(runCalculateItems);
2387
- } else {
2388
- runCalculateItems();
2389
- }
2390
- const shouldMaintainScrollAtEndAfterPendingSettle = !!state.pendingMaintainScrollAtEnd || !!((_a3 = state.props.maintainScrollAtEnd) == null ? void 0 : _a3.onDataChange);
2391
- if (didResolvePendingNativeMVCPAdjust && shouldMaintainScrollAtEndAfterPendingSettle) {
2392
- state.pendingMaintainScrollAtEnd = false;
2393
- doMaintainScrollAtEnd(ctx);
2394
- }
2395
- state.dataChangeNeedsScrollUpdate = false;
2396
- state.lastScrollDelta = 0;
2397
- }
2398
- }
2399
-
2400
- // src/utils/requestAdjust.ts
2401
- function requestAdjust(ctx, positionDiff, dataChanged) {
2402
- const state = ctx.state;
2403
- if (Math.abs(positionDiff) > 0.1) {
2404
- const doit = () => {
2405
- {
2406
- state.scrollAdjustHandler.requestAdjust(positionDiff);
2407
- if (state.adjustingFromInitialMount) {
2408
- state.adjustingFromInitialMount--;
2409
- }
2410
- }
2411
- };
2412
- state.scroll += positionDiff;
2413
- state.scrollForNextCalculateItemsInView = void 0;
2414
- const readyToRender = peek$(ctx, "readyToRender");
2415
- if (readyToRender) {
2416
- doit();
2417
- } else {
2418
- state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
2419
- requestAnimationFrame(doit);
2420
- }
2421
- }
2422
- }
2423
-
2424
3372
  // src/core/prepareColumnStartState.ts
2425
3373
  function prepareColumnStartState(ctx, startIndex, useAverageSize) {
2426
3374
  var _a3;
@@ -2934,23 +3882,6 @@ function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
2934
3882
  var unstableBatchedUpdates = ReactDOM__namespace.unstable_batchedUpdates;
2935
3883
  var batchedUpdates = typeof unstableBatchedUpdates === "function" ? unstableBatchedUpdates : (fn) => fn();
2936
3884
 
2937
- // src/utils/checkAllSizesKnown.ts
2938
- function isNullOrUndefined2(value) {
2939
- return value === null || value === void 0;
2940
- }
2941
- function checkAllSizesKnown(state) {
2942
- const { startBuffered, endBuffered, sizesKnown } = state;
2943
- if (!isNullOrUndefined2(endBuffered) && !isNullOrUndefined2(startBuffered) && startBuffered >= 0 && endBuffered >= 0) {
2944
- let areAllKnown = true;
2945
- for (let i = startBuffered; areAllKnown && i <= endBuffered; i++) {
2946
- const key = getId(state, i);
2947
- areAllKnown && (areAllKnown = sizesKnown.has(key));
2948
- }
2949
- return areAllKnown;
2950
- }
2951
- return false;
2952
- }
2953
-
2954
3885
  // src/utils/findAvailableContainers.ts
2955
3886
  function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers) {
2956
3887
  const numContainers = peek$(ctx, "numContainers");
@@ -3073,97 +4004,11 @@ function comparatorByDistance(a, b) {
3073
4004
  return b.distance - a.distance;
3074
4005
  }
3075
4006
 
3076
- // src/core/scrollToIndex.ts
3077
- function scrollToIndex(ctx, {
3078
- index,
3079
- viewOffset = 0,
3080
- animated = true,
3081
- forceScroll,
3082
- isInitialScroll,
3083
- viewPosition
3084
- }) {
3085
- const state = ctx.state;
3086
- const { data } = state.props;
3087
- if (index >= data.length) {
3088
- index = data.length - 1;
3089
- } else if (index < 0) {
3090
- index = 0;
3091
- }
3092
- const firstIndexOffset = calculateOffsetForIndex(ctx, index);
3093
- const isLast = index === data.length - 1;
3094
- if (isLast && viewPosition === void 0) {
3095
- viewPosition = 1;
3096
- }
3097
- state.scrollForNextCalculateItemsInView = void 0;
3098
- const targetId = getId(state, index);
3099
- const itemSize = getItemSize(ctx, targetId, index, state.props.data[index]);
3100
- scrollTo(ctx, {
3101
- animated,
3102
- forceScroll,
3103
- index,
3104
- isInitialScroll,
3105
- itemSize,
3106
- offset: firstIndexOffset,
3107
- viewOffset,
3108
- viewPosition: viewPosition != null ? viewPosition : 0
3109
- });
3110
- }
3111
-
3112
- // src/utils/performInitialScroll.ts
3113
- function performInitialScroll(ctx, params) {
3114
- var _a3;
3115
- const { forceScroll, initialScrollUsesOffset, resolvedOffset, target } = params;
3116
- if (initialScrollUsesOffset || resolvedOffset !== void 0) {
3117
- scrollTo(ctx, {
3118
- animated: false,
3119
- forceScroll,
3120
- index: initialScrollUsesOffset ? void 0 : target.index,
3121
- isInitialScroll: true,
3122
- offset: (_a3 = resolvedOffset != null ? resolvedOffset : target.contentOffset) != null ? _a3 : 0,
3123
- precomputedWithViewOffset: resolvedOffset !== void 0
3124
- });
3125
- return;
3126
- }
3127
- if (target.index === void 0) {
3128
- return;
3129
- }
3130
- scrollToIndex(ctx, {
3131
- ...target,
3132
- animated: false,
3133
- forceScroll,
3134
- isInitialScroll: true
3135
- });
3136
- }
3137
-
3138
4007
  // src/utils/setDidLayout.ts
3139
4008
  function setDidLayout(ctx) {
3140
4009
  const state = ctx.state;
3141
- const { initialScroll } = state;
3142
4010
  state.queuedInitialLayout = true;
3143
4011
  checkAtBottom(ctx);
3144
- if (initialScroll) {
3145
- const runScroll = () => {
3146
- var _a3, _b;
3147
- const target = state.initialScroll;
3148
- if (!target) {
3149
- return;
3150
- }
3151
- const activeInitialTargetOffset = ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) ? (_b = state.scrollingTo.targetOffset) != null ? _b : state.scrollingTo.offset : void 0;
3152
- const desiredInitialTargetOffset = state.initialScrollUsesOffset ? target.contentOffset : activeInitialTargetOffset;
3153
- const isAlreadyAtDesiredInitialTarget = desiredInitialTargetOffset !== void 0 && Math.abs(state.scroll - desiredInitialTargetOffset) <= 1 && Math.abs(state.scrollPending - desiredInitialTargetOffset) <= 1;
3154
- if (!isAlreadyAtDesiredInitialTarget) {
3155
- performInitialScroll(ctx, {
3156
- forceScroll: true,
3157
- initialScrollUsesOffset: state.initialScrollUsesOffset,
3158
- // Offset-based initial scrolls do not need item lookup, so they can run even before data exists.
3159
- // Re-run on the next frame to pick up measured viewport size without waiting for index resolution.
3160
- target
3161
- });
3162
- }
3163
- };
3164
- runScroll();
3165
- requestAnimationFrame(runScroll);
3166
- }
3167
4012
  setInitialRenderState(ctx, { didLayout: true });
3168
4013
  }
3169
4014
 
@@ -3238,7 +4083,7 @@ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentSt
3238
4083
  function calculateItemsInView(ctx, params = {}) {
3239
4084
  const state = ctx.state;
3240
4085
  batchedUpdates(() => {
3241
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
4086
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
3242
4087
  const {
3243
4088
  columns,
3244
4089
  columnSpans,
@@ -3246,7 +4091,6 @@ function calculateItemsInView(ctx, params = {}) {
3246
4091
  enableScrollForNextCalculateItemsInView,
3247
4092
  idCache,
3248
4093
  indexByKey,
3249
- initialScroll,
3250
4094
  minIndexSizeChanged,
3251
4095
  positions,
3252
4096
  props: {
@@ -3270,6 +4114,8 @@ function calculateItemsInView(ctx, params = {}) {
3270
4114
  const alwaysRenderArr = alwaysRenderIndicesArr || [];
3271
4115
  const alwaysRenderSet = alwaysRenderIndicesSet || /* @__PURE__ */ new Set();
3272
4116
  const { dataChanged, doMVCP, forceFullItemPositions } = params;
4117
+ const bootstrapInitialScrollState = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" ? state.initialScrollSession.bootstrap : void 0;
4118
+ const suppressInitialScrollSideEffects = !!bootstrapInitialScrollState;
3273
4119
  const prevNumContainers = peek$(ctx, "numContainers");
3274
4120
  if (!data || scrollLength === 0 || !prevNumContainers) {
3275
4121
  return;
@@ -3280,16 +4126,12 @@ function calculateItemsInView(ctx, params = {}) {
3280
4126
  const speed = getScrollVelocity(state);
3281
4127
  const scrollExtra = 0;
3282
4128
  const { queuedInitialLayout } = state;
3283
- let { scroll: scrollState } = state;
3284
- if (!queuedInitialLayout && initialScroll) {
3285
- const updatedOffset = state.initialScrollUsesOffset ? (_a3 = initialScroll.contentOffset) != null ? _a3 : 0 : calculateOffsetWithOffsetPosition(
3286
- ctx,
3287
- calculateOffsetForIndex(ctx, initialScroll.index),
3288
- initialScroll
3289
- );
3290
- scrollState = updatedOffset;
3291
- }
3292
- const scrollAdjustPending = (_b = peek$(ctx, "scrollAdjustPending")) != null ? _b : 0;
4129
+ const scrollState = suppressInitialScrollSideEffects ? (_b = bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.scroll) != null ? _b : state.scroll : !queuedInitialLayout && state.initialScroll ? (
4130
+ // Before the initial layout settles, keep viewport math anchored to the
4131
+ // current initial-scroll target instead of transient native adjustments.
4132
+ resolveInitialScrollOffset(ctx, state.initialScroll)
4133
+ ) : state.scroll;
4134
+ const scrollAdjustPending = (_c = peek$(ctx, "scrollAdjustPending")) != null ? _c : 0;
3293
4135
  const scrollAdjustPad = scrollAdjustPending - topPad;
3294
4136
  let scroll = Math.round(scrollState + scrollExtra + scrollAdjustPad);
3295
4137
  if (scroll + scrollLength > totalSize) {
@@ -3313,7 +4155,7 @@ function calculateItemsInView(ctx, params = {}) {
3313
4155
  const scrollTopBuffered = scroll - scrollBufferTop;
3314
4156
  const scrollBottom = scroll + scrollLength + (scroll < 0 ? -scroll : 0);
3315
4157
  const scrollBottomBuffered = scrollBottom + scrollBufferBottom;
3316
- if (!dataChanged && !forceFullItemPositions && scrollForNextCalculateItemsInView) {
4158
+ if (!suppressInitialScrollSideEffects && !dataChanged && !forceFullItemPositions && scrollForNextCalculateItemsInView) {
3317
4159
  const { top, bottom } = scrollForNextCalculateItemsInView;
3318
4160
  if (top === null && bottom === null) {
3319
4161
  state.scrollForNextCalculateItemsInView = void 0;
@@ -3323,7 +4165,7 @@ function calculateItemsInView(ctx, params = {}) {
3323
4165
  }
3324
4166
  }
3325
4167
  }
3326
- const checkMVCP = doMVCP ? prepareMVCP(ctx, dataChanged) : void 0;
4168
+ const checkMVCP = doMVCP && !suppressInitialScrollSideEffects ? prepareMVCP(ctx, dataChanged) : void 0;
3327
4169
  if (dataChanged) {
3328
4170
  indexByKey.clear();
3329
4171
  idCache.length = 0;
@@ -3331,7 +4173,7 @@ function calculateItemsInView(ctx, params = {}) {
3331
4173
  columns.length = 0;
3332
4174
  columnSpans.length = 0;
3333
4175
  }
3334
- const startIndex = forceFullItemPositions || dataChanged ? 0 : (_c = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _c : 0;
4176
+ const startIndex = forceFullItemPositions || dataChanged ? 0 : (_d = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _d : 0;
3335
4177
  updateItemPositions(ctx, dataChanged, {
3336
4178
  doMVCP,
3337
4179
  forceFullUpdate: !!forceFullItemPositions,
@@ -3348,11 +4190,11 @@ function calculateItemsInView(ctx, params = {}) {
3348
4190
  let startBufferedId = null;
3349
4191
  let endNoBuffer = null;
3350
4192
  let endBuffered = null;
3351
- let loopStart = !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
4193
+ let loopStart = (_e = suppressInitialScrollSideEffects ? bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.targetIndexSeed : void 0) != null ? _e : !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
3352
4194
  for (let i = loopStart; i >= 0; i--) {
3353
- const id = (_d = idCache[i]) != null ? _d : getId(state, i);
4195
+ const id = (_f = idCache[i]) != null ? _f : getId(state, i);
3354
4196
  const top = positions[i];
3355
- const size = (_e = sizes.get(id)) != null ? _e : getItemSize(ctx, id, i, data[i]);
4197
+ const size = (_g = sizes.get(id)) != null ? _g : getItemSize(ctx, id, i, data[i]);
3356
4198
  const bottom = top + size;
3357
4199
  if (bottom > scroll - scrollBufferTop) {
3358
4200
  loopStart = i;
@@ -3383,8 +4225,8 @@ function calculateItemsInView(ctx, params = {}) {
3383
4225
  let firstFullyOnScreenIndex;
3384
4226
  const dataLength = data.length;
3385
4227
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
3386
- const id = (_f = idCache[i]) != null ? _f : getId(state, i);
3387
- const size = (_g = sizes.get(id)) != null ? _g : getItemSize(ctx, id, i, data[i]);
4228
+ const id = (_h = idCache[i]) != null ? _h : getId(state, i);
4229
+ const size = (_i = sizes.get(id)) != null ? _i : getItemSize(ctx, id, i, data[i]);
3388
4230
  const top = positions[i];
3389
4231
  if (!foundEnd) {
3390
4232
  if (startNoBuffer === null && top + size > scroll) {
@@ -3423,7 +4265,7 @@ function calculateItemsInView(ctx, params = {}) {
3423
4265
  const firstVisibleAnchorIndex = firstFullyOnScreenIndex != null ? firstFullyOnScreenIndex : startNoBuffer;
3424
4266
  if (firstVisibleAnchorIndex !== null && firstVisibleAnchorIndex !== void 0 && endNoBuffer !== null) {
3425
4267
  for (let i = firstVisibleAnchorIndex; i <= endNoBuffer; i++) {
3426
- const id = (_h = idCache[i]) != null ? _h : getId(state, i);
4268
+ const id = (_j = idCache[i]) != null ? _j : getId(state, i);
3427
4269
  idsInView.push(id);
3428
4270
  }
3429
4271
  }
@@ -3456,7 +4298,7 @@ function calculateItemsInView(ctx, params = {}) {
3456
4298
  const needNewContainers = [];
3457
4299
  const needNewContainersSet = /* @__PURE__ */ new Set();
3458
4300
  for (let i = startBuffered; i <= endBuffered; i++) {
3459
- const id = (_i = idCache[i]) != null ? _i : getId(state, i);
4301
+ const id = (_k = idCache[i]) != null ? _k : getId(state, i);
3460
4302
  if (!containerItemKeys.has(id)) {
3461
4303
  needNewContainersSet.add(i);
3462
4304
  needNewContainers.push(i);
@@ -3465,7 +4307,7 @@ function calculateItemsInView(ctx, params = {}) {
3465
4307
  if (alwaysRenderArr.length > 0) {
3466
4308
  for (const index of alwaysRenderArr) {
3467
4309
  if (index < 0 || index >= dataLength) continue;
3468
- const id = (_j = idCache[index]) != null ? _j : getId(state, index);
4310
+ const id = (_l = idCache[index]) != null ? _l : getId(state, index);
3469
4311
  if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
3470
4312
  needNewContainersSet.add(index);
3471
4313
  needNewContainers.push(index);
@@ -3503,7 +4345,7 @@ function calculateItemsInView(ctx, params = {}) {
3503
4345
  for (let idx = 0; idx < needNewContainers.length; idx++) {
3504
4346
  const i = needNewContainers[idx];
3505
4347
  const containerIndex = availableContainers[idx];
3506
- const id = (_k = idCache[i]) != null ? _k : getId(state, i);
4348
+ const id = (_m = idCache[i]) != null ? _m : getId(state, i);
3507
4349
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
3508
4350
  if (oldKey && oldKey !== id) {
3509
4351
  containerItemKeys.delete(oldKey);
@@ -3544,7 +4386,7 @@ function calculateItemsInView(ctx, params = {}) {
3544
4386
  if (alwaysRenderArr.length > 0) {
3545
4387
  for (const index of alwaysRenderArr) {
3546
4388
  if (index < 0 || index >= dataLength) continue;
3547
- const id = (_l = idCache[index]) != null ? _l : getId(state, index);
4389
+ const id = (_n = idCache[index]) != null ? _n : getId(state, index);
3548
4390
  const containerIndex = containerItemKeys.get(id);
3549
4391
  if (containerIndex !== void 0) {
3550
4392
  state.stickyContainerPool.add(containerIndex);
@@ -3615,13 +4457,23 @@ function calculateItemsInView(ctx, params = {}) {
3615
4457
  if (didChangePositions) {
3616
4458
  set$(ctx, "lastPositionUpdate", Date.now());
3617
4459
  }
3618
- if (!queuedInitialLayout && endBuffered !== null) {
3619
- if (checkAllSizesKnown(state)) {
3620
- setDidLayout(ctx);
3621
- }
4460
+ if (suppressInitialScrollSideEffects) {
4461
+ evaluateBootstrapInitialScroll(ctx);
4462
+ return;
3622
4463
  }
3623
- if (viewabilityConfigCallbackPairs) {
3624
- updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollLength, startNoBuffer, endNoBuffer);
4464
+ if (!queuedInitialLayout && endBuffered !== null && checkAllSizesKnown(state)) {
4465
+ setDidLayout(ctx);
4466
+ handleInitialScrollLayoutReady(ctx);
4467
+ }
4468
+ if (viewabilityConfigCallbackPairs && startNoBuffer !== null && endNoBuffer !== null) {
4469
+ updateViewableItems(
4470
+ ctx.state,
4471
+ ctx,
4472
+ viewabilityConfigCallbackPairs,
4473
+ scrollLength,
4474
+ startNoBuffer,
4475
+ endNoBuffer
4476
+ );
3625
4477
  }
3626
4478
  if (onStickyHeaderChange && stickyIndicesArr.length > 0 && nextActiveStickyIndex !== void 0 && nextActiveStickyIndex !== previousStickyIndex) {
3627
4479
  const item = data[nextActiveStickyIndex];
@@ -3652,79 +4504,45 @@ function checkActualChange(state, dataProp, previousData) {
3652
4504
  return false;
3653
4505
  }
3654
4506
 
3655
- // src/core/checkFinishedScroll.ts
3656
- var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
3657
- var INITIAL_SCROLL_MAX_FALLBACK_CHECKS = 20;
3658
- var INITIAL_SCROLL_ZERO_TARGET_EPSILON = 1;
3659
- function checkFinishedScroll(ctx) {
3660
- ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
3661
- }
3662
- function checkFinishedScrollFrame(ctx) {
3663
- var _a3;
3664
- const scrollingTo = ctx.state.scrollingTo;
3665
- if (scrollingTo) {
3666
- const { state } = ctx;
3667
- state.animFrameCheckFinishedScroll = void 0;
3668
- const scroll = state.scrollPending;
3669
- const adjust = state.scrollAdjustHandler.getAdjust();
3670
- const clampedTargetOffset = (_a3 = scrollingTo.targetOffset) != null ? _a3 : clampScrollOffset(ctx, scrollingTo.offset - (scrollingTo.viewOffset || 0), scrollingTo);
3671
- const maxOffset = clampScrollOffset(ctx, scroll, scrollingTo);
3672
- const diff1 = Math.abs(scroll - clampedTargetOffset);
3673
- const diff2 = Math.abs(diff1 - adjust);
3674
- const isNotOverscrolled = Math.abs(scroll - maxOffset) < 1;
3675
- const isAtTarget = diff1 < 1 || !scrollingTo.animated && diff2 < 1;
3676
- if (isNotOverscrolled && isAtTarget) {
3677
- finishScrollTo(ctx);
4507
+ // src/core/doMaintainScrollAtEnd.ts
4508
+ function doMaintainScrollAtEnd(ctx) {
4509
+ const state = ctx.state;
4510
+ const {
4511
+ didContainersLayout,
4512
+ isAtEnd,
4513
+ pendingNativeMVCPAdjust,
4514
+ refScroller,
4515
+ props: { maintainScrollAtEnd }
4516
+ } = state;
4517
+ const shouldMaintainScrollAtEnd = !!(isAtEnd && maintainScrollAtEnd && didContainersLayout);
4518
+ if (pendingNativeMVCPAdjust) {
4519
+ state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
4520
+ return false;
4521
+ }
4522
+ state.pendingMaintainScrollAtEnd = false;
4523
+ if (shouldMaintainScrollAtEnd) {
4524
+ const contentSize = getContentSize(ctx);
4525
+ if (contentSize < state.scrollLength) {
4526
+ state.scroll = 0;
3678
4527
  }
4528
+ requestAnimationFrame(() => {
4529
+ var _a3;
4530
+ if (state.isAtEnd) {
4531
+ state.maintainingScrollAtEnd = true;
4532
+ (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
4533
+ animated: maintainScrollAtEnd.animated
4534
+ });
4535
+ setTimeout(
4536
+ () => {
4537
+ state.maintainingScrollAtEnd = false;
4538
+ },
4539
+ maintainScrollAtEnd.animated ? 500 : 0
4540
+ );
4541
+ }
4542
+ });
4543
+ return true;
3679
4544
  }
3680
- }
3681
- function checkFinishedScrollFallback(ctx) {
3682
- const state = ctx.state;
3683
- const scrollingTo = state.scrollingTo;
3684
- const shouldFinishInitialZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
3685
- const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && !shouldFinishInitialZeroTarget || !state.didContainersLayout;
3686
- state.timeoutCheckFinishedScrollFallback = setTimeout(
3687
- () => {
3688
- let numChecks = 0;
3689
- const checkHasScrolled = () => {
3690
- var _a3, _b;
3691
- state.timeoutCheckFinishedScrollFallback = void 0;
3692
- const isStillScrollingTo = state.scrollingTo;
3693
- if (isStillScrollingTo) {
3694
- numChecks++;
3695
- const isNativeInitialPending = isNativeInitialNonZeroTarget(state) && !state.hasScrolled;
3696
- const maxChecks = isNativeInitialPending ? INITIAL_SCROLL_MAX_FALLBACK_CHECKS : 5;
3697
- const shouldFinishZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
3698
- if (shouldFinishZeroTarget || state.hasScrolled || numChecks > maxChecks) {
3699
- finishScrollTo(ctx);
3700
- } else if (isNativeInitialPending && numChecks <= maxChecks) {
3701
- const targetOffset = (_b = (_a3 = state.initialNativeScrollWatchdog) == null ? void 0 : _a3.targetOffset) != null ? _b : state.scrollPending;
3702
- const scroller = state.refScroller.current;
3703
- if (scroller) {
3704
- scroller.scrollTo({
3705
- animated: false,
3706
- x: state.props.horizontal ? targetOffset : 0,
3707
- y: state.props.horizontal ? 0 : targetOffset
3708
- });
3709
- }
3710
- state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, 100);
3711
- } else {
3712
- state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, 100);
3713
- }
3714
- }
3715
- };
3716
- checkHasScrolled();
3717
- },
3718
- slowTimeout ? 500 : 100
3719
- );
3720
- }
3721
- function isNativeInitialNonZeroTarget(state) {
3722
- return !state.didFinishInitialScroll && !!state.initialNativeScrollWatchdog && state.initialNativeScrollWatchdog.targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
3723
- }
3724
- function shouldFinishInitialZeroTargetScroll(ctx) {
3725
- var _a3;
3726
- const { state } = ctx;
3727
- 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;
4545
+ return false;
3728
4546
  }
3729
4547
 
3730
4548
  // src/utils/updateAveragesOnDataChange.ts
@@ -3914,20 +4732,102 @@ function handleLayout(ctx, layoutParam, setCanRender) {
3914
4732
  setCanRender(true);
3915
4733
  }
3916
4734
 
4735
+ // src/core/updateScroll.ts
4736
+ function updateScroll(ctx, newScroll, forceUpdate) {
4737
+ var _a3;
4738
+ const state = ctx.state;
4739
+ const { ignoreScrollFromMVCP, lastScrollAdjustForHistory, scrollAdjustHandler, scrollHistory, scrollingTo } = state;
4740
+ const prevScroll = state.scroll;
4741
+ state.hasScrolled = true;
4742
+ state.lastBatchingAction = Date.now();
4743
+ const currentTime = Date.now();
4744
+ const adjust = scrollAdjustHandler.getAdjust();
4745
+ const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
4746
+ if (adjustChanged) {
4747
+ scrollHistory.length = 0;
4748
+ }
4749
+ state.lastScrollAdjustForHistory = adjust;
4750
+ if (scrollingTo === void 0 && !(scrollHistory.length === 0 && newScroll === state.scroll)) {
4751
+ if (!adjustChanged) {
4752
+ scrollHistory.push({ scroll: newScroll, time: currentTime });
4753
+ }
4754
+ }
4755
+ if (scrollHistory.length > 5) {
4756
+ scrollHistory.shift();
4757
+ }
4758
+ if (ignoreScrollFromMVCP && !scrollingTo) {
4759
+ const { lt, gt } = ignoreScrollFromMVCP;
4760
+ if (lt && newScroll < lt || gt && newScroll > gt) {
4761
+ state.ignoreScrollFromMVCPIgnored = true;
4762
+ return;
4763
+ }
4764
+ }
4765
+ state.scrollPrev = prevScroll;
4766
+ state.scrollPrevTime = state.scrollTime;
4767
+ state.scroll = newScroll;
4768
+ state.scrollTime = currentTime;
4769
+ const scrollDelta = Math.abs(newScroll - prevScroll);
4770
+ const didResolvePendingNativeMVCPAdjust = resolvePendingNativeMVCPAdjust(ctx, newScroll);
4771
+ const scrollLength = state.scrollLength;
4772
+ const lastCalculated = state.scrollLastCalculate;
4773
+ const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
4774
+ const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
4775
+ if (shouldUpdate) {
4776
+ state.scrollLastCalculate = state.scroll;
4777
+ state.ignoreScrollFromMVCPIgnored = false;
4778
+ state.lastScrollDelta = scrollDelta;
4779
+ const runCalculateItems = () => {
4780
+ var _a4;
4781
+ (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { doMVCP: scrollingTo !== void 0 });
4782
+ checkThresholds(ctx);
4783
+ };
4784
+ if (scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength) {
4785
+ ReactDOM.flushSync(runCalculateItems);
4786
+ } else {
4787
+ runCalculateItems();
4788
+ }
4789
+ const shouldMaintainScrollAtEndAfterPendingSettle = !!state.pendingMaintainScrollAtEnd || !!((_a3 = state.props.maintainScrollAtEnd) == null ? void 0 : _a3.onDataChange);
4790
+ if (didResolvePendingNativeMVCPAdjust && shouldMaintainScrollAtEndAfterPendingSettle) {
4791
+ state.pendingMaintainScrollAtEnd = false;
4792
+ doMaintainScrollAtEnd(ctx);
4793
+ }
4794
+ state.dataChangeNeedsScrollUpdate = false;
4795
+ state.lastScrollDelta = 0;
4796
+ }
4797
+ }
4798
+
3917
4799
  // src/core/onScroll.ts
3918
- var INITIAL_SCROLL_PROGRESS_EPSILON = 1;
3919
- function didObserveInitialScrollProgress(newScroll, watchdog) {
3920
- const previousDistance = Math.abs(watchdog.startScroll - watchdog.targetOffset);
3921
- const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
3922
- return nextDistance <= INITIAL_SCROLL_PROGRESS_EPSILON || nextDistance + INITIAL_SCROLL_PROGRESS_EPSILON < previousDistance;
4800
+ function trackInitialScrollNativeProgress(state, newScroll) {
4801
+ const initialNativeScrollWatchdog = initialScrollWatchdog.get(state);
4802
+ const didInitialScrollProgress = !!initialNativeScrollWatchdog && initialScrollWatchdog.didObserveProgress(newScroll, initialNativeScrollWatchdog);
4803
+ if (didInitialScrollProgress) {
4804
+ initialScrollWatchdog.clear(state);
4805
+ return;
4806
+ }
4807
+ if (initialNativeScrollWatchdog) {
4808
+ state.hasScrolled = false;
4809
+ initialScrollWatchdog.set(state, {
4810
+ startScroll: initialNativeScrollWatchdog.startScroll,
4811
+ targetOffset: initialNativeScrollWatchdog.targetOffset
4812
+ });
4813
+ }
4814
+ }
4815
+ function shouldDeferPublicOnScroll(state) {
4816
+ var _a3;
4817
+ return !!state.initialScroll && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" && !state.didFinishInitialScroll;
4818
+ }
4819
+ function cloneScrollEvent(event) {
4820
+ return {
4821
+ ...event,
4822
+ nativeEvent: {
4823
+ ...event.nativeEvent
4824
+ }
4825
+ };
3923
4826
  }
3924
4827
  function onScroll(ctx, event) {
3925
4828
  var _a3, _b, _c, _d;
3926
4829
  const state = ctx.state;
3927
- const {
3928
- scrollProcessingEnabled,
3929
- props: { onScroll: onScrollProp }
3930
- } = state;
4830
+ const { scrollProcessingEnabled } = state;
3931
4831
  if (scrollProcessingEnabled === false) {
3932
4832
  return;
3933
4833
  }
@@ -3958,20 +4858,18 @@ function onScroll(ctx, event) {
3958
4858
  }
3959
4859
  }
3960
4860
  state.scrollPending = newScroll;
3961
- const initialNativeScrollWatchdog = state.initialNativeScrollWatchdog;
3962
- const didInitialScrollProgress = !!initialNativeScrollWatchdog && didObserveInitialScrollProgress(newScroll, initialNativeScrollWatchdog);
3963
- if (didInitialScrollProgress) {
3964
- state.initialNativeScrollWatchdog = void 0;
3965
- }
3966
4861
  updateScroll(ctx, newScroll, insetChanged);
3967
- if (initialNativeScrollWatchdog && !didInitialScrollProgress) {
3968
- state.hasScrolled = false;
3969
- state.initialNativeScrollWatchdog = initialNativeScrollWatchdog;
3970
- }
4862
+ trackInitialScrollNativeProgress(state, newScroll);
3971
4863
  if (state.scrollingTo) {
3972
4864
  checkFinishedScroll(ctx);
3973
4865
  }
3974
- onScrollProp == null ? void 0 : onScrollProp(event);
4866
+ if (state.props.onScroll) {
4867
+ if (shouldDeferPublicOnScroll(state)) {
4868
+ state.deferredPublicOnScrollEvent = cloneScrollEvent(event);
4869
+ } else {
4870
+ state.props.onScroll(event);
4871
+ }
4872
+ }
3975
4873
  }
3976
4874
 
3977
4875
  // src/core/ScrollAdjustHandler.ts
@@ -4612,7 +5510,7 @@ var LegendList = typedMemo(
4612
5510
  })
4613
5511
  );
4614
5512
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
4615
- var _a3, _b, _c, _d, _e, _f, _g, _h;
5513
+ var _a3, _b, _c, _d, _e, _f, _g;
4616
5514
  const {
4617
5515
  alignItemsAtEnd = false,
4618
5516
  alwaysRender,
@@ -4636,6 +5534,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4636
5534
  itemsAreEqual,
4637
5535
  keyExtractor: keyExtractorProp,
4638
5536
  ListEmptyComponent,
5537
+ ListFooterComponent,
5538
+ ListFooterComponentStyle,
4639
5539
  ListHeaderComponent,
4640
5540
  maintainScrollAtEnd = false,
4641
5541
  maintainScrollAtEndThreshold = 0.1,
@@ -4672,7 +5572,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4672
5572
  useWindowScroll = false,
4673
5573
  viewabilityConfig,
4674
5574
  viewabilityConfigCallbackPairs,
4675
- waitForInitialLayout = true,
4676
5575
  ...rest
4677
5576
  } = props;
4678
5577
  const animatedPropsInternal = props.animatedPropsInternal;
@@ -4705,8 +5604,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4705
5604
  const hasInitialScrollIndex = initialScrollIndexProp !== void 0 && initialScrollIndexProp !== null;
4706
5605
  const hasInitialScrollOffset = initialScrollOffsetProp !== void 0 && initialScrollOffsetProp !== null;
4707
5606
  const initialScrollUsesOffsetOnly = !initialScrollAtEnd && !hasInitialScrollIndex && hasInitialScrollOffset;
4708
- const initialScrollProp = initialScrollAtEnd ? { index: Math.max(0, dataProp.length - 1), viewOffset: -stylePaddingBottomState, viewPosition: 1 } : hasInitialScrollIndex ? typeof initialScrollIndexProp === "object" ? {
5607
+ const usesBootstrapInitialScroll = initialScrollAtEnd || hasInitialScrollIndex;
5608
+ const initialScrollProp = initialScrollAtEnd ? {
5609
+ index: Math.max(0, dataProp.length - 1),
5610
+ preserveForBottomPadding: true,
5611
+ viewOffset: -stylePaddingBottomState,
5612
+ viewPosition: 1
5613
+ } : hasInitialScrollIndex ? typeof initialScrollIndexProp === "object" ? {
4709
5614
  index: (_a3 = initialScrollIndexProp.index) != null ? _a3 : 0,
5615
+ preserveForBottomPadding: initialScrollIndexProp.viewOffset === void 0 && initialScrollIndexProp.viewPosition === 1 ? true : void 0,
4710
5616
  viewOffset: (_b = initialScrollIndexProp.viewOffset) != null ? _b : initialScrollIndexProp.viewPosition === 1 ? -stylePaddingBottomState : 0,
4711
5617
  viewPosition: (_c = initialScrollIndexProp.viewPosition) != null ? _c : 0
4712
5618
  } : {
@@ -4775,22 +5681,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4775
5681
  idCache: [],
4776
5682
  idsInView: [],
4777
5683
  indexByKey: /* @__PURE__ */ new Map(),
4778
- initialAnchor: !initialScrollUsesOffsetOnly && (initialScrollProp == null ? void 0 : initialScrollProp.index) !== void 0 && (initialScrollProp == null ? void 0 : initialScrollProp.viewPosition) !== void 0 ? {
4779
- attempts: 0,
4780
- index: initialScrollProp.index,
4781
- settledTicks: 0,
4782
- viewOffset: (_f = initialScrollProp.viewOffset) != null ? _f : 0,
4783
- viewPosition: initialScrollProp.viewPosition
4784
- } : void 0,
4785
- initialNativeScrollWatchdog: void 0,
4786
5684
  initialScroll: initialScrollProp,
4787
- initialScrollLastDidFinish: false,
4788
- initialScrollLastTarget: initialScrollProp,
4789
- initialScrollLastTargetUsesOffset: initialScrollUsesOffsetOnly,
4790
- initialScrollPreviousDataLength: dataProp.length,
4791
- initialScrollRetryLastLength: void 0,
4792
- initialScrollRetryWindowUntil: 0,
4793
- initialScrollUsesOffset: initialScrollUsesOffsetOnly,
5685
+ initialScrollSession: initialScrollProp ? {
5686
+ kind: initialScrollUsesOffsetOnly ? "offset" : "bootstrap",
5687
+ previousDataLength: dataProp.length
5688
+ } : void 0,
4794
5689
  isAtEnd: false,
4795
5690
  isAtStart: false,
4796
5691
  isEndReached: null,
@@ -4833,6 +5728,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4833
5728
  };
4834
5729
  const internalState = ctx.state;
4835
5730
  internalState.triggerCalculateItemsInView = (params) => calculateItemsInView(ctx, params);
5731
+ internalState.reprocessCurrentScroll = () => updateScroll(ctx, internalState.scroll, true);
4836
5732
  set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPositionConfig);
4837
5733
  set$(ctx, "extraData", extraData);
4838
5734
  }
@@ -4919,96 +5815,23 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4919
5815
  true
4920
5816
  );
4921
5817
  }
4922
- const resolveInitialScrollOffset = React3.useCallback((initialScroll) => {
4923
- var _a4;
4924
- if (state.initialScrollUsesOffset) {
4925
- return clampScrollOffset(ctx, (_a4 = initialScroll.contentOffset) != null ? _a4 : 0);
4926
- }
4927
- const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, initialScroll.index) : 0;
4928
- const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, initialScroll);
4929
- return clampScrollOffset(ctx, resolvedOffset, initialScroll);
4930
- }, []);
4931
- const finishInitialScrollWithoutScroll = React3.useCallback(() => {
4932
- refState.current.initialAnchor = void 0;
4933
- refState.current.initialScroll = void 0;
4934
- state.initialAnchor = void 0;
4935
- state.initialScroll = void 0;
4936
- state.initialScrollUsesOffset = false;
4937
- state.initialScrollLastTarget = void 0;
4938
- state.initialScrollLastTargetUsesOffset = false;
4939
- setInitialRenderState(ctx, { didInitialScroll: true });
4940
- }, []);
4941
- const setActiveInitialScrollTarget = React3.useCallback(
4942
- (target, options) => {
4943
- const usesOffset = !!(options == null ? void 0 : options.usesOffset);
4944
- state.initialScrollUsesOffset = usesOffset;
4945
- state.initialScrollLastTarget = target;
4946
- state.initialScrollLastTargetUsesOffset = usesOffset;
4947
- refState.current.initialScroll = target;
4948
- state.initialScroll = target;
4949
- if ((options == null ? void 0 : options.resetDidFinish) && state.didFinishInitialScroll) {
4950
- state.didFinishInitialScroll = false;
4951
- }
4952
- if (!(options == null ? void 0 : options.syncAnchor)) {
4953
- return;
4954
- }
4955
- },
4956
- []
4957
- );
4958
- const shouldFinishInitialScrollAtOrigin = React3.useCallback(
4959
- (initialScroll, offset) => {
4960
- var _a4, _b2, _c2;
4961
- if (offset !== 0 || initialScrollAtEnd) {
4962
- return false;
4963
- }
4964
- if (state.initialScrollUsesOffset) {
4965
- return Math.abs((_a4 = initialScroll.contentOffset) != null ? _a4 : 0) <= 1;
4966
- }
4967
- return initialScroll.index === 0 && ((_b2 = initialScroll.viewPosition) != null ? _b2 : 0) === 0 && Math.abs((_c2 = initialScroll.viewOffset) != null ? _c2 : 0) <= 1;
4968
- },
4969
- [initialScrollAtEnd]
4970
- );
4971
- const shouldFinishEmptyInitialScrollAtEnd = React3.useCallback(
4972
- (initialScroll, offset) => {
4973
- return dataProp.length === 0 && initialScrollAtEnd && offset === 0 && initialScroll.viewPosition === 1;
4974
- },
4975
- [dataProp.length, initialScrollAtEnd]
4976
- );
4977
- const shouldRearmFinishedEmptyInitialScrollAtEnd = React3.useCallback(
4978
- (initialScroll) => {
4979
- var _a4;
4980
- return !!(state.didFinishInitialScroll && dataProp.length > 0 && initialScroll && !state.initialScrollUsesOffset && initialScroll.index === 0 && initialScroll.viewPosition === 1 && ((_a4 = initialScroll.contentOffset) != null ? _a4 : 0) === 0);
4981
- },
4982
- [dataProp.length]
4983
- );
4984
5818
  const initialContentOffset = React3.useMemo(() => {
4985
- let value;
4986
- const { initialScroll, initialAnchor } = refState.current;
4987
- if (initialScroll) {
4988
- if (!state.initialScrollUsesOffset && false) ;
4989
- if (initialScroll.contentOffset !== void 0) {
4990
- value = initialScroll.contentOffset;
4991
- } else {
4992
- const clampedOffset = resolveInitialScrollOffset(initialScroll);
4993
- const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
4994
- setActiveInitialScrollTarget(updatedInitialScroll, {
4995
- usesOffset: state.initialScrollUsesOffset
4996
- });
4997
- value = clampedOffset;
4998
- }
4999
- } else {
5000
- refState.current.initialAnchor = void 0;
5001
- value = 0;
5002
- }
5003
- const hasPendingDataDependentInitialScroll = !!initialScroll && dataProp.length === 0 && !shouldFinishInitialScrollAtOrigin(initialScroll, value) && !shouldFinishEmptyInitialScrollAtEnd(initialScroll, value);
5004
- if (!value && !hasPendingDataDependentInitialScroll) {
5005
- if (initialScroll && shouldFinishInitialScrollAtOrigin(initialScroll, value)) {
5006
- finishInitialScrollWithoutScroll();
5007
- } else {
5008
- setInitialRenderState(ctx, { didInitialScroll: true });
5009
- }
5819
+ var _a4, _b2;
5820
+ const initialScroll = state.initialScroll;
5821
+ if (!initialScroll) {
5822
+ return void 0;
5010
5823
  }
5011
- return value;
5824
+ const resolvedOffset = (_a4 = initialScroll.contentOffset) != null ? _a4 : resolveInitialScrollOffset(ctx, initialScroll);
5825
+ return usesBootstrapInitialScroll && ((_b2 = state.initialScrollSession) == null ? void 0 : _b2.kind) === "bootstrap" && Platform.OS === "web" ? void 0 : resolvedOffset;
5826
+ }, [usesBootstrapInitialScroll]);
5827
+ React3.useLayoutEffect(() => {
5828
+ initializeInitialScrollOnMount(ctx, {
5829
+ dataLength: dataProp.length,
5830
+ hasFooterComponent: !!ListFooterComponent,
5831
+ initialContentOffset,
5832
+ initialScrollAtEnd,
5833
+ useBootstrapInitialScroll: usesBootstrapInitialScroll
5834
+ });
5012
5835
  }, []);
5013
5836
  if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
5014
5837
  refState.current.lastBatchingAction = Date.now();
@@ -5023,188 +5846,39 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5023
5846
  set$(ctx, "totalSize", 0);
5024
5847
  }
5025
5848
  }
5026
- const doInitialScroll = React3.useCallback((options) => {
5027
- var _a4, _b2;
5028
- const allowPostFinishRetry = !!(options == null ? void 0 : options.allowPostFinishRetry);
5029
- const { didFinishInitialScroll, queuedInitialLayout, scrollingTo } = state;
5030
- const initialScroll = (_a4 = state.initialScroll) != null ? _a4 : allowPostFinishRetry ? state.initialScrollLastTarget : void 0;
5031
- const isInitialScrollInProgress = !!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll);
5032
- const needsContainerLayoutForInitialScroll = !state.initialScrollUsesOffset;
5033
- const shouldWaitForInitialLayout = waitForInitialLayout && needsContainerLayoutForInitialScroll && !queuedInitialLayout && !allowPostFinishRetry && !isInitialScrollInProgress;
5034
- if (!initialScroll || shouldWaitForInitialLayout || didFinishInitialScroll && !allowPostFinishRetry || scrollingTo && !isInitialScrollInProgress) {
5035
- return;
5036
- }
5037
- if (allowPostFinishRetry && state.initialScrollLastTargetUsesOffset) {
5038
- return;
5039
- }
5040
- const didMoveAwayFromInitialTarget = allowPostFinishRetry && initialScroll.contentOffset !== void 0 && Math.abs(state.scroll - initialScroll.contentOffset) > 1;
5041
- if (didMoveAwayFromInitialTarget) {
5042
- state.initialScrollRetryWindowUntil = 0;
5043
- return;
5044
- }
5045
- const offset = resolveInitialScrollOffset(initialScroll);
5046
- const activeInitialTargetOffset = isInitialScrollInProgress ? (_b2 = scrollingTo.targetOffset) != null ? _b2 : scrollingTo.offset : void 0;
5047
- const didOffsetChange = initialScroll.contentOffset === void 0 || Math.abs(initialScroll.contentOffset - offset) > 1;
5048
- const didActiveInitialTargetChange = activeInitialTargetOffset !== void 0 && Math.abs(activeInitialTargetOffset - offset) > 1;
5049
- if (!didOffsetChange && (allowPostFinishRetry || isInitialScrollInProgress && !didActiveInitialTargetChange)) {
5050
- return;
5051
- }
5052
- if (didOffsetChange) {
5053
- const updatedInitialScroll = { ...initialScroll, contentOffset: offset };
5054
- if (!state.initialScrollUsesOffset) {
5055
- state.initialScrollLastTarget = updatedInitialScroll;
5056
- state.initialScrollLastTargetUsesOffset = false;
5057
- if (state.initialScroll) {
5058
- refState.current.initialScroll = updatedInitialScroll;
5059
- state.initialScroll = updatedInitialScroll;
5060
- }
5061
- }
5062
- }
5063
- const hasMeasuredScrollLayout = !!state.lastLayout && state.scrollLength > 0;
5064
- const shouldForceNativeInitialScroll = state.initialScrollUsesOffset && hasMeasuredScrollLayout || allowPostFinishRetry || !!queuedInitialLayout || isInitialScrollInProgress && didOffsetChange;
5065
- performInitialScroll(ctx, {
5066
- forceScroll: shouldForceNativeInitialScroll,
5067
- initialScrollUsesOffset: state.initialScrollUsesOffset,
5068
- resolvedOffset: offset,
5069
- target: initialScroll
5070
- });
5071
- }, []);
5072
- React3.useLayoutEffect(() => {
5073
- var _a4;
5074
- const previousDataLength = state.initialScrollPreviousDataLength;
5075
- state.initialScrollPreviousDataLength = dataProp.length;
5076
- if (previousDataLength !== 0 || dataProp.length === 0 || !state.initialScroll || !state.queuedInitialLayout) {
5077
- return;
5078
- }
5079
- if (initialScrollAtEnd) {
5080
- const lastIndex = Math.max(0, dataProp.length - 1);
5081
- const initialScroll = state.initialScroll;
5082
- const shouldRearm = shouldRearmFinishedEmptyInitialScrollAtEnd(initialScroll);
5083
- if (state.didFinishInitialScroll && !shouldRearm) {
5084
- return;
5085
- }
5086
- if (initialScroll && !state.initialScrollUsesOffset && initialScroll.index === lastIndex && initialScroll.viewPosition === 1 && !shouldRearm) {
5087
- return;
5088
- }
5089
- const updatedInitialScroll = {
5090
- contentOffset: void 0,
5091
- index: lastIndex,
5092
- viewOffset: (_a4 = initialScroll == null ? void 0 : initialScroll.viewOffset) != null ? _a4 : -stylePaddingBottomState,
5093
- viewPosition: 1
5094
- };
5095
- setActiveInitialScrollTarget(updatedInitialScroll, {
5096
- resetDidFinish: shouldRearm,
5097
- syncAnchor: true
5098
- });
5099
- doInitialScroll();
5100
- return;
5101
- }
5102
- if (state.didFinishInitialScroll) {
5103
- return;
5104
- }
5105
- doInitialScroll();
5106
- }, [
5107
- dataProp.length,
5108
- doInitialScroll,
5109
- initialScrollAtEnd,
5110
- shouldRearmFinishedEmptyInitialScrollAtEnd,
5111
- stylePaddingBottomState
5112
- ]);
5113
5849
  React3.useLayoutEffect(() => {
5114
- var _a4;
5115
- if (!initialScrollAtEnd) {
5116
- return;
5117
- }
5118
- const lastIndex = Math.max(0, dataProp.length - 1);
5119
- const initialScroll = state.initialScroll;
5120
- const shouldRearm = shouldRearmFinishedEmptyInitialScrollAtEnd(initialScroll);
5121
- if (state.didFinishInitialScroll && !shouldRearm) {
5122
- return;
5123
- }
5124
- if (shouldRearm) {
5125
- state.didFinishInitialScroll = false;
5126
- }
5127
- if (initialScroll && !state.initialScrollUsesOffset && initialScroll.index === lastIndex && initialScroll.viewPosition === 1 && !shouldRearm) {
5128
- return;
5129
- }
5130
- const updatedInitialScroll = {
5131
- contentOffset: void 0,
5132
- index: lastIndex,
5133
- viewOffset: (_a4 = initialScroll == null ? void 0 : initialScroll.viewOffset) != null ? _a4 : -stylePaddingBottomState,
5134
- viewPosition: 1
5135
- };
5136
- setActiveInitialScrollTarget(updatedInitialScroll, {
5137
- resetDidFinish: shouldRearm,
5138
- syncAnchor: true
5850
+ handleInitialScrollDataChange(ctx, {
5851
+ dataLength: dataProp.length,
5852
+ didDataChange: didDataChangeLocal,
5853
+ initialScrollAtEnd,
5854
+ stylePaddingBottom: stylePaddingBottomState,
5855
+ useBootstrapInitialScroll: usesBootstrapInitialScroll
5139
5856
  });
5140
- doInitialScroll();
5141
- }, [
5142
- dataProp.length,
5143
- doInitialScroll,
5144
- initialScrollAtEnd,
5145
- shouldRearmFinishedEmptyInitialScrollAtEnd,
5146
- stylePaddingBottomState
5147
- ]);
5857
+ }, [dataProp.length, didDataChangeLocal, initialScrollAtEnd, stylePaddingBottomState, usesBootstrapInitialScroll]);
5148
5858
  const onLayoutFooter = React3.useCallback(
5149
5859
  (layout) => {
5150
- var _a4;
5151
- if (!initialScrollAtEnd) {
5152
- return;
5153
- }
5154
- const { initialScroll } = state;
5155
- if (!initialScroll) {
5860
+ if (!usesBootstrapInitialScroll) {
5156
5861
  return;
5157
5862
  }
5158
- const lastIndex = Math.max(0, dataProp.length - 1);
5159
- if (initialScroll.index !== lastIndex || initialScroll.viewPosition !== 1) {
5863
+ handleBootstrapInitialScrollFooterLayout(ctx, {
5864
+ dataLength: dataProp.length,
5865
+ footerSize: layout[horizontal ? "width" : "height"],
5866
+ initialScrollAtEnd,
5867
+ stylePaddingBottom: stylePaddingBottomState
5868
+ });
5869
+ },
5870
+ [dataProp.length, initialScrollAtEnd, horizontal, stylePaddingBottomState, usesBootstrapInitialScroll]
5871
+ );
5872
+ const onLayoutChange = React3.useCallback(
5873
+ (layout) => {
5874
+ handleLayout(ctx, layout, setCanRender);
5875
+ if (usesBootstrapInitialScroll) {
5160
5876
  return;
5161
5877
  }
5162
- const footerSize = layout[horizontal ? "width" : "height"];
5163
- const viewOffset = -stylePaddingBottomState - footerSize;
5164
- if (initialScroll.viewOffset !== viewOffset) {
5165
- const previousTargetOffset = (_a4 = initialScroll.contentOffset) != null ? _a4 : resolveInitialScrollOffset(initialScroll);
5166
- const didMoveAwayFromFinishedInitialTarget = state.didFinishInitialScroll && Math.abs(state.scroll - previousTargetOffset) > 1;
5167
- if (didMoveAwayFromFinishedInitialTarget) {
5168
- return;
5169
- }
5170
- const updatedInitialScroll = { ...initialScroll, viewOffset };
5171
- setActiveInitialScrollTarget(updatedInitialScroll, {
5172
- resetDidFinish: true
5173
- });
5174
- doInitialScroll();
5175
- }
5878
+ advanceCurrentInitialScrollSession(ctx);
5176
5879
  },
5177
- [
5178
- dataProp.length,
5179
- doInitialScroll,
5180
- horizontal,
5181
- initialScrollAtEnd,
5182
- resolveInitialScrollOffset,
5183
- stylePaddingBottomState
5184
- ]
5880
+ [usesBootstrapInitialScroll]
5185
5881
  );
5186
- const onLayoutChange = React3.useCallback((layout) => {
5187
- var _a4;
5188
- handleLayout(ctx, layout, setCanRender);
5189
- const SCROLL_LENGTH_RETRY_WINDOW_MS = 600;
5190
- const now = Date.now();
5191
- const didFinishInitialScroll = !!state.didFinishInitialScroll;
5192
- if (didFinishInitialScroll && !state.initialScrollLastDidFinish) {
5193
- state.initialScrollRetryWindowUntil = now + SCROLL_LENGTH_RETRY_WINDOW_MS;
5194
- }
5195
- state.initialScrollLastDidFinish = didFinishInitialScroll;
5196
- const previousScrollLength = state.initialScrollRetryLastLength;
5197
- const currentScrollLength = state.scrollLength;
5198
- const didScrollLengthChange = previousScrollLength === void 0 || Math.abs(currentScrollLength - previousScrollLength) > 1;
5199
- if (didScrollLengthChange) {
5200
- state.initialScrollRetryLastLength = currentScrollLength;
5201
- }
5202
- if (didFinishInitialScroll && didScrollLengthChange && now <= state.initialScrollRetryWindowUntil && !state.initialScrollLastTargetUsesOffset && ((_a4 = state.initialScrollLastTarget) == null ? void 0 : _a4.index) !== void 0) {
5203
- doInitialScroll({ allowPostFinishRetry: true });
5204
- return;
5205
- }
5206
- doInitialScroll();
5207
- }, []);
5208
5882
  const { onLayout } = useOnLayoutSync({
5209
5883
  onLayoutChange,
5210
5884
  onLayoutProp,
@@ -5278,7 +5952,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5278
5952
  }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
5279
5953
  React3.useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx), []);
5280
5954
  {
5281
- React3.useEffect(doInitialScroll, []);
5955
+ React3.useEffect(() => {
5956
+ if (usesBootstrapInitialScroll) {
5957
+ return;
5958
+ }
5959
+ advanceCurrentInitialScrollSession(ctx);
5960
+ }, [usesBootstrapInitialScroll]);
5282
5961
  }
5283
5962
  const fns = React3.useMemo(
5284
5963
  () => ({
@@ -5308,6 +5987,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5308
5987
  horizontal,
5309
5988
  initialContentOffset,
5310
5989
  ListEmptyComponent: dataProp.length === 0 ? ListEmptyComponent : void 0,
5990
+ ListFooterComponent,
5991
+ ListFooterComponentStyle,
5311
5992
  ListHeaderComponent,
5312
5993
  onLayout,
5313
5994
  onLayoutFooter,
@@ -5315,7 +5996,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5315
5996
  onScroll: onScrollHandler,
5316
5997
  recycleItems,
5317
5998
  refreshControl: refreshControlElement ? stylePaddingTopState > 0 ? React3__namespace.cloneElement(refreshControlElement, {
5318
- progressViewOffset: ((_g = refreshControlElement.props.progressViewOffset) != null ? _g : 0) + stylePaddingTopState
5999
+ progressViewOffset: ((_f = refreshControlElement.props.progressViewOffset) != null ? _f : 0) + stylePaddingTopState
5319
6000
  }) : refreshControlElement : onRefresh && /* @__PURE__ */ React3__namespace.createElement(
5320
6001
  RefreshControl,
5321
6002
  {
@@ -5326,14 +6007,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5326
6007
  ),
5327
6008
  refScrollView: combinedRef,
5328
6009
  renderScrollComponent,
5329
- scrollAdjustHandler: (_h = refState.current) == null ? void 0 : _h.scrollAdjustHandler,
6010
+ scrollAdjustHandler: (_g = refState.current) == null ? void 0 : _g.scrollAdjustHandler,
5330
6011
  scrollEventThrottle: 0,
5331
6012
  snapToIndices,
5332
6013
  stickyHeaderIndices,
5333
6014
  style,
5334
6015
  updateItemSize: fns.updateItemSize,
5335
- useWindowScroll: useWindowScrollResolved,
5336
- waitForInitialLayout
6016
+ useWindowScroll: useWindowScrollResolved
5337
6017
  }
5338
6018
  ), IS_DEV && ENABLE_DEBUG_VIEW);
5339
6019
  });