@legendapp/list 3.3.11 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/react-native.mjs CHANGED
@@ -437,6 +437,14 @@ var EDGE_POSITION_EPSILON = 1;
437
437
  var ENABLE_DEVMODE = IS_DEV && false;
438
438
  var ENABLE_DEBUG_VIEW = IS_DEV && false;
439
439
 
440
+ // src/core/endOfContentTarget.ts
441
+ function getEndAlignedViewOffset(stylePaddingEnd, footerSize = 0) {
442
+ return -stylePaddingEnd - footerSize;
443
+ }
444
+ function isEndOfContentTarget(target, dataLength) {
445
+ return !!target && dataLength > 0 && target.viewPosition === 1 && !!target.preserveForBottomPadding && target.index !== void 0 && target.index >= dataLength - 1;
446
+ }
447
+
440
448
  // src/core/cancelImperativeScroll.ts
441
449
  function cancelScrollCompletionChecks({ scheduledWork }) {
442
450
  scheduledWork.cancel("checkFinishedScrollFrame");
@@ -621,26 +629,25 @@ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, co
621
629
  scrollPosition: context.scrollPosition
622
630
  });
623
631
  };
632
+ let reached = !!wasReached;
624
633
  if (!wasReached) {
625
- if (!within) {
626
- return false;
627
- }
628
- onReached(distance);
629
- updateSnapshot();
630
- return true;
631
- }
632
- const reset = isOutsideThresholdHysteresis(distance, atThreshold, threshold);
633
- if (reset) {
634
- setSnapshot(void 0);
635
- return false;
636
- }
637
- if (within) {
638
- const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
639
- if (changed) {
634
+ if (within && onReached(distance)) {
640
635
  updateSnapshot();
636
+ reached = true;
637
+ }
638
+ } else {
639
+ const reset = isOutsideThresholdHysteresis(distance, atThreshold, threshold);
640
+ if (reset) {
641
+ setSnapshot(void 0);
642
+ reached = false;
643
+ } else if (within) {
644
+ const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
645
+ if (changed) {
646
+ updateSnapshot();
647
+ }
641
648
  }
642
649
  }
643
- return true;
650
+ return reached;
644
651
  };
645
652
 
646
653
  // src/utils/edgeReachedGate.ts
@@ -687,7 +694,6 @@ function beginReachedEdgeUserScroll(ctx, scrollDelta) {
687
694
  return void 0;
688
695
  }
689
696
  const allowedEdge = scrollDelta < 0 ? "start" : "end";
690
- state.edgeReachedGate = "closed";
691
697
  resetEdgeLatch(ctx, allowedEdge);
692
698
  return allowedEdge;
693
699
  }
@@ -728,7 +734,7 @@ function checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
728
734
  set$(
729
735
  ctx,
730
736
  "isWithinMaintainScrollAtEndThreshold",
731
- isContentLess || distanceFromEnd <= maintainScrollAtEndThreshold * scrollLength || state.pendingMaintainScrollAtEnd || maintainingScrollAtEnd === "animated" || maintainingScrollAtEnd === "instant"
737
+ isContentLess || distanceFromEnd <= maintainScrollAtEndThreshold * scrollLength || state.pendingMaintainScrollAtEnd || !!maintainingScrollAtEnd
732
738
  );
733
739
  const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || maintainingScrollAtEnd;
734
740
  if (!shouldSkipThresholdChecks) {
@@ -744,11 +750,13 @@ function checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
744
750
  scrollPosition: scroll
745
751
  },
746
752
  (distance) => {
747
- var _a4, _b;
748
- if (canDispatchReachedEdge(ctx, "end", allowedEdge, allowGateCreatedInCurrentCheck)) {
753
+ let dispatched = false;
754
+ if (state.props.onEndReached && canDispatchReachedEdge(ctx, "end", allowedEdge, allowGateCreatedInCurrentCheck)) {
749
755
  markReachedEdge(ctx);
750
- (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
756
+ state.props.onEndReached({ distanceFromEnd: distance });
757
+ dispatched = true;
751
758
  }
759
+ return dispatched;
752
760
  },
753
761
  (snapshot) => {
754
762
  state.endReachedSnapshot = snapshot;
@@ -781,7 +789,7 @@ function checkAtTop(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
781
789
  }
782
790
  set$(ctx, "isAtStart", scroll <= EDGE_POSITION_EPSILON);
783
791
  set$(ctx, "isNearStart", scroll <= threshold);
784
- const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || !!state.scrollingTo;
792
+ const shouldSkipThresholdChecks = !state.queuedInitialLayout || hasActiveInitialScroll(state) || !!state.scrollingTo;
785
793
  if (!shouldSkipThresholdChecks) {
786
794
  state.isStartReached = checkThreshold(
787
795
  scroll,
@@ -795,11 +803,13 @@ function checkAtTop(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
795
803
  scrollPosition: scroll
796
804
  },
797
805
  (distance) => {
798
- var _a3, _b;
799
- if (canDispatchReachedEdge(ctx, "start", allowedEdge, allowGateCreatedInCurrentCheck)) {
806
+ let dispatched = false;
807
+ if (state.props.onStartReached && canDispatchReachedEdge(ctx, "start", allowedEdge, allowGateCreatedInCurrentCheck)) {
800
808
  markReachedEdge(ctx);
801
- (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
809
+ state.props.onStartReached({ distanceFromStart: distance });
810
+ dispatched = true;
802
811
  }
812
+ return dispatched;
803
813
  },
804
814
  (snapshot) => {
805
815
  state.startReachedSnapshot = snapshot;
@@ -904,8 +914,14 @@ function scheduleFullDrawDistancePrewarm(ctx) {
904
914
  return;
905
915
  }
906
916
  state.scheduledWork.frame(() => {
907
- var _a3;
908
- return (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
917
+ var _a3, _b, _c;
918
+ if (state.props.hasExternalScroll && !hasActiveInitialScroll(state) && !state.scrollingTo && !state.pendingNativeMVCPAdjust) {
919
+ const offset = (_b = (_a3 = state.refScroller.current) == null ? void 0 : _a3.getRawScrollOffset) == null ? void 0 : _b.call(_a3);
920
+ if (offset !== void 0) {
921
+ state.scroll = state.scrollPending = state.props.horizontal ? toLogicalHorizontalOffset(state, offset, state.totalSize) : offset;
922
+ }
923
+ }
924
+ (_c = state.triggerCalculateItemsInView) == null ? void 0 : _c.call(state);
909
925
  }, "fullDrawDistancePrewarm");
910
926
  }
911
927
 
@@ -918,6 +934,14 @@ function resetInitialRenderState(ctx, {
918
934
  if (resetLayout) {
919
935
  state.didContainersLayout = false;
920
936
  state.queuedInitialLayout = false;
937
+ state.scrollBufferDirection = void 0;
938
+ state.scrollHistory.length = 0;
939
+ state.scheduledWork.cancel("renderRangeProjection");
940
+ state.edgeReachedGate = void 0;
941
+ state.isStartReached = null;
942
+ state.isEndReached = null;
943
+ state.startReachedSnapshot = void 0;
944
+ state.endReachedSnapshot = void 0;
921
945
  }
922
946
  if (resetInitialScroll) {
923
947
  state.didFinishInitialScroll = false;
@@ -932,7 +956,7 @@ function setInitialRenderState(ctx, {
932
956
  const { state } = ctx;
933
957
  const {
934
958
  loadStartTime,
935
- props: { onLoad }
959
+ props: { onLoad, onReady }
936
960
  } = state;
937
961
  if (didLayout) {
938
962
  state.didContainersLayout = true;
@@ -953,6 +977,7 @@ function setInitialRenderState(ctx, {
953
977
  onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
954
978
  }
955
979
  }
980
+ onReady == null ? void 0 : onReady();
956
981
  }
957
982
  }
958
983
 
@@ -1140,6 +1165,7 @@ function setSize(ctx, itemKey, size, notifyTotalSize = true) {
1140
1165
  const previousSize = sizes.get(itemKey);
1141
1166
  const diff = previousSize !== void 0 ? size - previousSize : size;
1142
1167
  if (diff !== 0) {
1168
+ state.positionsAreCurrent = false;
1143
1169
  addTotalSize(ctx, itemKey, diff, notifyTotalSize);
1144
1170
  }
1145
1171
  sizes.set(itemKey, size);
@@ -1200,6 +1226,7 @@ function getKnownOrFixedSize(ctx, key, index, data, resolved) {
1200
1226
  const fixedSize = (resolved == null ? void 0 : resolved.didResolveFixedItemSize) ? resolved.fixedItemSize : getFixedItemSize(data, index, itemType);
1201
1227
  if (fixedSize !== void 0) {
1202
1228
  size = fixedSize + ctx.scrollAxisGap;
1229
+ state.positionsAreCurrent = false;
1203
1230
  state.sizesKnown.set(key, size);
1204
1231
  }
1205
1232
  }
@@ -1298,10 +1325,6 @@ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1298
1325
  const itemSize = Math.max(0, measuredItemSize - (isOutOfBounds ? 0 : ctx.scrollAxisGap));
1299
1326
  const trailingInset = getContentInsetEnd(ctx);
1300
1327
  offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1301
- if (!isOutOfBounds && index === state.props.data.length - 1) {
1302
- const footerSize = peek$(ctx, "footerSize") || 0;
1303
- offset += footerSize;
1304
- }
1305
1328
  }
1306
1329
  return offset;
1307
1330
  }
@@ -1817,7 +1840,10 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
1817
1840
  state.scrollTime = currentTime;
1818
1841
  const scrollDelta = Math.abs(newScroll - prevScroll);
1819
1842
  const isUserScrollEvent = !!(options == null ? void 0 : options.fromNativeScrollEvent) && scrollDelta > 0.1 && !adjustChanged && scrollingTo === void 0 && !state.pendingNativeMVCPAdjust;
1820
- const didCancelMaintainScrollAtEnd = isUserScrollEvent && newScroll < prevScroll && !!(state.maintainingScrollAtEnd || state.pendingMaintainScrollAtEnd);
1843
+ if (isUserScrollEvent) {
1844
+ state.scrollBufferDirection = newScroll > prevScroll ? 1 : -1;
1845
+ }
1846
+ const didCancelMaintainScrollAtEnd = Platform.OS === "web" && isUserScrollEvent && newScroll < prevScroll && !!(state.maintainingScrollAtEnd || state.pendingMaintainScrollAtEnd);
1821
1847
  if (didCancelMaintainScrollAtEnd) {
1822
1848
  finishMaintainScrollAtEnd(ctx);
1823
1849
  }
@@ -1895,89 +1921,104 @@ function finishMaintainScrollAtEnd(ctx) {
1895
1921
  requestAdjust(ctx, nextScroll - state.scroll);
1896
1922
  }
1897
1923
  }
1898
- function doMaintainScrollAtEnd(ctx) {
1899
- var _a3, _b, _c, _d, _e;
1924
+ function doMaintainScrollAtEnd(ctx, options) {
1925
+ var _a3, _b, _c, _d, _e, _f, _g, _h;
1900
1926
  const state = ctx.state;
1901
- const {
1902
- didContainersLayout,
1903
- didFinishInitialScroll,
1904
- pendingNativeMVCPAdjust,
1905
- props: { maintainScrollAtEnd }
1906
- } = state;
1907
- const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
1908
- const isReplayingPendingMaintain = !!state.pendingMaintainScrollAtEnd;
1909
- const shouldMaintainScrollAtEnd = !!(!((_a3 = ctx.scrollRequestTracker) == null ? void 0 : _a3.isWaitingToRun()) && (!state.scrollingTo || state.scrollingTo.isScrollToEnd) && (isWithinMaintainScrollAtEndThreshold || isReplayingPendingMaintain || ((_b = state.scrollingTo) == null ? void 0 : _b.isScrollToEnd)) && maintainScrollAtEnd && didFinishInitialScroll);
1910
- if (shouldMaintainScrollAtEnd && !state.scrollingTo) {
1911
- const scroller = state.refScroller.current;
1912
- const nativeMax = (_c = scroller == null ? void 0 : scroller.getMaxScrollOffset) == null ? void 0 : _c.call(scroller);
1913
- const observed = (_d = scroller == null ? void 0 : scroller.getCurrentScrollOffset) == null ? void 0 : _d.call(scroller);
1914
- if (nativeMax !== void 0 && observed !== void 0 && nativeMax < state.scroll - 1 && Math.abs(observed - nativeMax) <= 1) {
1915
- state.pendingMaintainScrollAtEnd = true;
1916
- state.scrollPending = observed;
1917
- updateScroll(ctx, observed, true, { markHasScrolled: false });
1927
+ let didMaintain = false;
1928
+ if (state.isDragging || ((_b = (_a3 = state.refScroller.current) == null ? void 0 : _a3.isScrollInRange) == null ? void 0 : _b.call(_a3)) === false) {
1929
+ finishMaintainScrollAtEnd(ctx);
1930
+ } else {
1931
+ const {
1932
+ didContainersLayout,
1933
+ didFinishInitialScroll,
1934
+ pendingNativeMVCPAdjust,
1935
+ props: { maintainScrollAtEnd }
1936
+ } = state;
1937
+ const immediate = (options == null ? void 0 : options.immediate) && Platform.OS === "web" && !(maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.animated);
1938
+ const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
1939
+ const isReplayingPendingMaintain = !!state.pendingMaintainScrollAtEnd;
1940
+ const shouldMaintainScrollAtEnd = !!(!((_c = ctx.scrollRequestTracker) == null ? void 0 : _c.isWaitingToRun()) && (!state.scrollingTo || state.scrollingTo.isScrollToEnd) && (isWithinMaintainScrollAtEndThreshold || isReplayingPendingMaintain || ((_d = state.scrollingTo) == null ? void 0 : _d.isScrollToEnd)) && maintainScrollAtEnd && didFinishInitialScroll);
1941
+ if (shouldMaintainScrollAtEnd && !state.scrollingTo) {
1942
+ const scroller = state.refScroller.current;
1943
+ const nativeMax = (_e = scroller == null ? void 0 : scroller.getMaxScrollOffset) == null ? void 0 : _e.call(scroller);
1944
+ const observed = (_f = scroller == null ? void 0 : scroller.getCurrentScrollOffset) == null ? void 0 : _f.call(scroller);
1945
+ if (nativeMax !== void 0 && observed !== void 0 && nativeMax < state.scroll - 1 && Math.abs(observed - nativeMax) <= 1) {
1946
+ state.pendingMaintainScrollAtEnd = true;
1947
+ state.scrollPending = observed;
1948
+ updateScroll(ctx, observed, true, { markHasScrolled: false });
1949
+ }
1918
1950
  }
1919
- }
1920
- if (shouldMaintainScrollAtEnd && !didContainersLayout) {
1921
- state.pendingMaintainScrollAtEnd = true;
1922
- return false;
1923
- }
1924
- if (pendingNativeMVCPAdjust) {
1925
- state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
1926
- return false;
1927
- }
1928
- if (shouldMaintainScrollAtEnd && (state.scrollingTo || state.pendingScrollResolve)) {
1929
- state.pendingMaintainScrollAtEnd = true;
1930
- (_e = state.maintainingScrollAtEnd) != null ? _e : state.maintainingScrollAtEnd = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
1931
- return false;
1932
- }
1933
- if (shouldMaintainScrollAtEnd && didContainersLayout) {
1934
- state.pendingMaintainScrollAtEnd = false;
1935
- const contentSize = getContentSize(ctx);
1936
- if (contentSize < state.scrollLength) {
1937
- state.scroll = 0;
1938
- }
1939
- if (!state.maintainingScrollAtEnd) {
1940
- const pendingState = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
1941
- const activeState = maintainScrollAtEnd.animated ? "animated" : "instant";
1942
- const scrollAtRequest = state.scroll;
1943
- state.maintainingScrollAtEnd = pendingState;
1944
- requestAnimationFrame(() => {
1945
- if (state.maintainingScrollAtEnd !== pendingState) {
1946
- return;
1947
- }
1948
- const isStillWithinThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
1949
- const didScrollSinceRequest = state.scroll !== scrollAtRequest;
1950
- if (isReplayingPendingMaintain || isStillWithinThreshold || !didScrollSinceRequest) {
1951
- if (state.scrollingTo || state.pendingScrollResolve) {
1952
- state.pendingMaintainScrollAtEnd = true;
1953
- return;
1954
- }
1955
- state.maintainingScrollAtEnd = activeState;
1956
- const scrollPromise = getScrollRequestTracker(ctx).runNowIfIdle(
1957
- () => ctx.scrollToEnd({ animated: maintainScrollAtEnd.animated })
1958
- );
1959
- void scrollPromise.then(() => {
1960
- if (state.maintainingScrollAtEnd !== activeState) {
1961
- return;
1962
- }
1963
- if (state.pendingMaintainScrollAtEnd) {
1964
- state.maintainingScrollAtEnd = void 0;
1965
- doMaintainScrollAtEnd(ctx);
1966
- } else {
1951
+ if (shouldMaintainScrollAtEnd && !didContainersLayout) {
1952
+ state.pendingMaintainScrollAtEnd = true;
1953
+ } else if (pendingNativeMVCPAdjust) {
1954
+ state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
1955
+ } else if (shouldMaintainScrollAtEnd && (state.scrollingTo || state.pendingScrollResolve)) {
1956
+ if (immediate && ((_g = state.scrollingTo) == null ? void 0 : _g.isScrollToEnd) && !state.scrollingTo.animated) {
1957
+ state.pendingMaintainScrollAtEnd = false;
1958
+ ctx.scrollToEnd({ animated: false });
1959
+ didMaintain = true;
1960
+ } else {
1961
+ state.pendingMaintainScrollAtEnd = true;
1962
+ (_h = state.maintainingScrollAtEnd) != null ? _h : state.maintainingScrollAtEnd = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
1963
+ }
1964
+ } else if (shouldMaintainScrollAtEnd && didContainersLayout) {
1965
+ state.pendingMaintainScrollAtEnd = false;
1966
+ const contentSize = getContentSize(ctx);
1967
+ if (contentSize < state.scrollLength) {
1968
+ state.scroll = 0;
1969
+ }
1970
+ if (!state.maintainingScrollAtEnd || immediate && state.maintainingScrollAtEnd === "pending-instant") {
1971
+ const pendingState = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
1972
+ const activeState = maintainScrollAtEnd.animated ? "animated" : "instant";
1973
+ const scrollAtRequest = state.scroll;
1974
+ state.maintainingScrollAtEnd = pendingState;
1975
+ const run = () => {
1976
+ var _a4, _b2;
1977
+ if (state.maintainingScrollAtEnd === pendingState) {
1978
+ if (state.isDragging || ((_b2 = (_a4 = state.refScroller.current) == null ? void 0 : _a4.isScrollInRange) == null ? void 0 : _b2.call(_a4)) === false) {
1967
1979
  finishMaintainScrollAtEnd(ctx);
1980
+ } else {
1981
+ const isStillWithinThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
1982
+ const didScrollSinceRequest = Platform.OS === "web" && state.scroll !== scrollAtRequest;
1983
+ if (isReplayingPendingMaintain || isStillWithinThreshold || !didScrollSinceRequest) {
1984
+ if (state.scrollingTo || state.pendingScrollResolve) {
1985
+ state.pendingMaintainScrollAtEnd = true;
1986
+ } else {
1987
+ state.maintainingScrollAtEnd = activeState;
1988
+ const scrollPromise = getScrollRequestTracker(ctx).runNowIfIdle(
1989
+ () => ctx.scrollToEnd({ animated: maintainScrollAtEnd.animated })
1990
+ );
1991
+ void scrollPromise.then(() => {
1992
+ if (state.maintainingScrollAtEnd === activeState) {
1993
+ if (state.pendingMaintainScrollAtEnd) {
1994
+ state.maintainingScrollAtEnd = void 0;
1995
+ doMaintainScrollAtEnd(ctx);
1996
+ } else {
1997
+ finishMaintainScrollAtEnd(ctx);
1998
+ }
1999
+ }
2000
+ });
2001
+ }
2002
+ } else if (state.maintainingScrollAtEnd === pendingState) {
2003
+ finishMaintainScrollAtEnd(ctx);
2004
+ }
1968
2005
  }
1969
- });
1970
- } else if (state.maintainingScrollAtEnd === pendingState) {
1971
- finishMaintainScrollAtEnd(ctx);
2006
+ }
2007
+ };
2008
+ if (immediate) {
2009
+ run();
2010
+ } else {
2011
+ requestAnimationFrame(run);
1972
2012
  }
1973
- });
2013
+ } else {
2014
+ state.pendingMaintainScrollAtEnd = true;
2015
+ }
2016
+ didMaintain = true;
1974
2017
  } else {
1975
- state.pendingMaintainScrollAtEnd = true;
2018
+ finishMaintainScrollAtEnd(ctx);
1976
2019
  }
1977
- return true;
1978
2020
  }
1979
- finishMaintainScrollAtEnd(ctx);
1980
- return false;
2021
+ return didMaintain;
1981
2022
  }
1982
2023
 
1983
2024
  // src/core/finishScrollTo.ts
@@ -2366,7 +2407,9 @@ function scrollTo(ctx, params) {
2366
2407
  pinScrollTargetRenderRange(ctx, targetOffset, scrollTarget.index);
2367
2408
  }
2368
2409
  }
2369
- state.scrollPending = targetOffset;
2410
+ if (!animated || isInitialScroll) {
2411
+ state.scrollPending = targetOffset;
2412
+ }
2370
2413
  syncInitialScrollNativeWatchdog(state, { isInitialScroll, requestedOffset: offset, targetOffset });
2371
2414
  if (!isInitialScroll && !noScrollingTo && Math.abs(state.scroll - targetOffset) > 1) {
2372
2415
  if (animated) {
@@ -2743,13 +2786,13 @@ function rearmBootstrapInitialScroll(ctx, options) {
2743
2786
  queueBootstrapInitialScrollReevaluation(ctx.state);
2744
2787
  }
2745
2788
  function createInitialScrollAtEndTarget(options) {
2746
- const { dataLength, footerSize, preserveForFooterLayout, stylePaddingEnd } = options;
2789
+ const { dataLength, footerSize, index, preserveForFooterLayout, stylePaddingEnd } = options;
2747
2790
  return {
2748
2791
  contentOffset: void 0,
2749
- index: Math.max(0, dataLength - 1),
2792
+ index: index != null ? index : Math.max(0, dataLength - 1),
2750
2793
  preserveForBottomPadding: true,
2751
2794
  preserveForFooterLayout,
2752
- viewOffset: -stylePaddingEnd - footerSize,
2795
+ viewOffset: getEndAlignedViewOffset(stylePaddingEnd, footerSize),
2753
2796
  viewPosition: 1
2754
2797
  };
2755
2798
  }
@@ -2765,13 +2808,14 @@ function isRetargetableBottomAlignedInitialScrollTarget(target) {
2765
2808
  function createRetargetedBottomAlignedInitialScroll(options) {
2766
2809
  const { dataLength, footerSize, initialScrollAtEnd, stylePaddingEnd, target } = options;
2767
2810
  const preserveForFooterLayout = shouldPreserveInitialScrollForFooterLayout(target);
2811
+ const alignsToContentEnd = initialScrollAtEnd || isEndOfContentTarget(target, dataLength);
2768
2812
  return {
2769
2813
  ...target,
2770
2814
  contentOffset: void 0,
2771
2815
  index: initialScrollAtEnd ? Math.max(0, dataLength - 1) : target.index,
2772
2816
  preserveForBottomPadding: true,
2773
2817
  preserveForFooterLayout,
2774
- viewOffset: -stylePaddingEnd - (preserveForFooterLayout ? footerSize : 0),
2818
+ viewOffset: getEndAlignedViewOffset(stylePaddingEnd, alignsToContentEnd ? footerSize : 0),
2775
2819
  viewPosition: 1
2776
2820
  };
2777
2821
  }
@@ -2785,7 +2829,7 @@ function clearPendingInitialScrollFooterLayout(ctx, options) {
2785
2829
  }
2786
2830
  const clearedFooterTarget = createInitialScrollAtEndTarget({
2787
2831
  dataLength,
2788
- footerSize: 0,
2832
+ footerSize: peek$(ctx, "footerSize") || 0,
2789
2833
  preserveForFooterLayout: void 0,
2790
2834
  stylePaddingEnd
2791
2835
  });
@@ -2808,13 +2852,12 @@ function getObservedBootstrapInitialScrollOffset(state) {
2808
2852
  return typeof observedOffset === "number" && Number.isFinite(observedOffset) ? observedOffset : (_d = (_c = state.scrollPending) != null ? _c : state.scroll) != null ? _d : 0;
2809
2853
  }
2810
2854
  function getPreservedEndAnchorOffsetDiff(ctx) {
2811
- var _a3;
2812
2855
  const state = ctx.state;
2813
2856
  const initialScroll = state.initialScroll;
2814
- if (!state.didFinishInitialScroll || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) || !initialScroll || initialScroll.viewPosition !== 1 || state.props.data.length === 0 || isOffsetInitialScrollSession(state)) {
2857
+ if (!state.didFinishInitialScroll || state.scrollingTo || state.pendingScrollResolve || state.maintainingScrollAtEnd || state.isDragging || !initialScroll || initialScroll.viewPosition !== 1 || state.props.data.length === 0 || isOffsetInitialScrollSession(state)) {
2815
2858
  return;
2816
2859
  }
2817
- const currentOffset = typeof state.lastNativeScroll === "number" && Number.isFinite(state.lastNativeScroll) ? state.lastNativeScroll : getObservedBootstrapInitialScrollOffset(state);
2860
+ const currentOffset = Platform.OS !== "web" && typeof state.lastNativeScroll === "number" && Number.isFinite(state.lastNativeScroll) ? state.lastNativeScroll : getObservedBootstrapInitialScrollOffset(state);
2818
2861
  return resolveInitialScrollOffset(ctx, initialScroll) - currentOffset;
2819
2862
  }
2820
2863
  function schedulePreservedEndAnchorCorrection(ctx) {
@@ -2842,6 +2885,9 @@ function schedulePreservedEndAnchorCorrectionFrame(ctx, correction) {
2842
2885
  const hasObservedNativeScrollAfterRequest = !activeCorrection.lastRequestTime || ((_a3 = state.lastNativeScrollTime) != null ? _a3 : 0) > activeCorrection.lastRequestTime;
2843
2886
  if (hasObservedNativeScrollAfterRequest) {
2844
2887
  activeCorrection.lastRequestTime = Date.now();
2888
+ if (Platform.OS === "web") {
2889
+ state.scroll = getObservedBootstrapInitialScrollOffset(state);
2890
+ }
2845
2891
  requestAdjust(ctx, offsetDiff);
2846
2892
  }
2847
2893
  schedulePreservedEndAnchorCorrectionFrame(ctx, correction);
@@ -2964,10 +3010,10 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
2964
3010
  function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2965
3011
  const { dataLength, footerSize, initialScrollAtEnd, stylePaddingEnd } = options;
2966
3012
  const state = ctx.state;
2967
- if (!initialScrollAtEnd) {
3013
+ const initialScroll = state.initialScroll;
3014
+ if (!initialScrollAtEnd && !isEndOfContentTarget(initialScroll, dataLength)) {
2968
3015
  return;
2969
3016
  }
2970
- const initialScroll = state.initialScroll;
2971
3017
  if (isOffsetInitialScrollSession(state) || dataLength === 0 || !initialScroll) {
2972
3018
  return;
2973
3019
  }
@@ -2985,6 +3031,7 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2985
3031
  const updatedInitialScroll = createInitialScrollAtEndTarget({
2986
3032
  dataLength,
2987
3033
  footerSize,
3034
+ index: initialScrollAtEnd ? void 0 : initialScroll.index,
2988
3035
  preserveForFooterLayout: shouldPreserveInitialScrollForFooterLayout(initialScroll),
2989
3036
  stylePaddingEnd
2990
3037
  });
@@ -3266,6 +3313,7 @@ function handleInitialScrollDataChange(ctx, options) {
3266
3313
 
3267
3314
  // src/core/resetLayoutCachesForDataChange.ts
3268
3315
  function resetLayoutCachesForDataChange(state) {
3316
+ state.positionsAreCurrent = false;
3269
3317
  state.indexByKey.clear();
3270
3318
  state.idCache.length = 0;
3271
3319
  state.positions.length = 0;
@@ -3546,7 +3594,7 @@ function updateItemPositions(ctx, dataChanged, {
3546
3594
  scrollBottomBuffered: -1,
3547
3595
  startIndex: 0
3548
3596
  }) {
3549
- var _a3, _b, _c, _d, _e;
3597
+ var _a3, _b, _c, _d, _e, _f;
3550
3598
  const state = ctx.state;
3551
3599
  const hasPositionListeners = ctx.positionListeners.size > 0;
3552
3600
  const {
@@ -3562,6 +3610,11 @@ function updateItemPositions(ctx, dataChanged, {
3562
3610
  const dataLength = data.length;
3563
3611
  const numColumns = (_a3 = peek$(ctx, "numColumns")) != null ? _a3 : 1;
3564
3612
  const hasColumns = numColumns > 1;
3613
+ const canReuseEstimates = !doMVCP || !state.didContainersLayout && Object.keys(state.averageSizes).length === 0;
3614
+ if (state.positionsAreCurrent && !dataChanged && !forceFullUpdate && state.positionRecalculationStartIndex === void 0 && !scrollingTo && !hasColumns && !snapToIndices && positions.length === dataLength && canReuseEstimates) {
3615
+ return;
3616
+ }
3617
+ state.positionsAreCurrent = false;
3565
3618
  const indexByKeyForChecking = IS_DEV ? /* @__PURE__ */ new Map() : void 0;
3566
3619
  const extraData = peek$(ctx, "extraData");
3567
3620
  const layoutConfig = overrideItemLayout ? { span: 1 } : void 0;
@@ -3676,6 +3729,10 @@ function updateItemPositions(ctx, dataChanged, {
3676
3729
  }
3677
3730
  if (!didBreakEarly) {
3678
3731
  updateTotalSize(ctx);
3732
+ state.positionsAreCurrent = !hasColumns;
3733
+ if (startIndex <= ((_f = state.positionRecalculationStartIndex) != null ? _f : -1)) {
3734
+ state.positionRecalculationStartIndex = void 0;
3735
+ }
3679
3736
  }
3680
3737
  if (snapToIndices) {
3681
3738
  updateSnapToOffsets(ctx);
@@ -3944,7 +4001,7 @@ function getExpandedContainerPoolSize(dataLength, numContainers) {
3944
4001
  }
3945
4002
 
3946
4003
  // src/utils/findAvailableContainers.ts
3947
- function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffered, pendingRemoval, getRequiredItemType, protectedKeys) {
4004
+ function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffered, pendingRemoval, protectedKeys, reverseItemOrder = false) {
3948
4005
  var _a3;
3949
4006
  const numNeeded = needNewContainers.length;
3950
4007
  if (numNeeded === 0) {
@@ -3953,16 +4010,27 @@ function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffe
3953
4010
  const numContainers = peek$(ctx, "numContainers");
3954
4011
  const state = ctx.state;
3955
4012
  const { containerItemMetadata, stickyContainerPool } = state;
4013
+ const { data, getItemType } = state.props;
4014
+ const hasItemTypes = !!getItemType;
3956
4015
  const shouldAvoidAssignedContainerReuse = state.props.recycleItems && !!state.props.positionComponentInternal;
3957
4016
  const pendingRemovalSet = pendingRemoval.length > 0 ? new Set(pendingRemoval) : void 0;
3958
- const requests = needNewContainers.map((itemIndex, order) => ({
3959
- isSticky: state.props.stickyHeaderIndicesSet.has(itemIndex),
3960
- itemIndex,
3961
- itemType: getRequiredItemType == null ? void 0 : getRequiredItemType(itemIndex),
3962
- order
3963
- }));
3964
- const normalRequests = requests.filter((request) => !request.isSticky);
3965
- const stickyRequests = requests.filter((request) => request.isSticky);
4017
+ const requests = new Array(numNeeded);
4018
+ const normalRequests = [];
4019
+ const stickyRequests = [];
4020
+ for (let order = 0; order < numNeeded; order++) {
4021
+ const itemIndex = needNewContainers[order];
4022
+ const itemType = getItemType == null ? void 0 : getItemType(data[itemIndex], itemIndex);
4023
+ const request = {
4024
+ isBuffered: itemIndex >= startBuffered && itemIndex <= endBuffered,
4025
+ isSticky: state.props.stickyHeaderIndicesSet.has(itemIndex),
4026
+ itemIndex,
4027
+ itemType: hasItemTypes ? itemType !== void 0 ? String(itemType) : "" : void 0,
4028
+ order,
4029
+ renderPriority: reverseItemOrder ? -itemIndex : itemIndex
4030
+ };
4031
+ requests[order] = request;
4032
+ (request.isSticky ? stickyRequests : normalRequests).push(request);
4033
+ }
3966
4034
  const normalCandidates = [];
3967
4035
  const stickyCandidates = [];
3968
4036
  for (let containerIndex = 0; containerIndex < numContainers; containerIndex++) {
@@ -4018,7 +4086,7 @@ function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffe
4018
4086
  }
4019
4087
  };
4020
4088
  const assignFromPool = (pendingRequests, candidates, allowCrossType) => {
4021
- if (getRequiredItemType) {
4089
+ if (hasItemTypes) {
4022
4090
  assignMatching(
4023
4091
  pendingRequests,
4024
4092
  candidates,
@@ -4042,6 +4110,11 @@ function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffe
4042
4110
  }
4043
4111
  assign(request, containerIndex);
4044
4112
  }
4113
+ if (normalRequests.length > 1 || stickyRequests.length > 1) {
4114
+ const containerIndices = [];
4115
+ reorderPoolAssignments(normalRequests, allocations, containerIndices, hasItemTypes);
4116
+ reorderPoolAssignments(stickyRequests, allocations, containerIndices, hasItemTypes);
4117
+ }
4045
4118
  if (pendingRemovalChanged) {
4046
4119
  pendingRemoval.length = 0;
4047
4120
  if (pendingRemovalSet) {
@@ -4071,12 +4144,53 @@ function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffe
4071
4144
  function comparatorByDistance(a, b) {
4072
4145
  return b.distance - a.distance;
4073
4146
  }
4147
+ function comparatorByRenderPriority(a, b) {
4148
+ return Number(b.isBuffered) - Number(a.isBuffered) || a.renderPriority - b.renderPriority;
4149
+ }
4150
+ function comparatorByIndex(a, b) {
4151
+ return a - b;
4152
+ }
4153
+ function reorderPoolAssignments(requests, allocations, containerIndices, groupByType) {
4154
+ if (requests.length >= 2) {
4155
+ if (groupByType) {
4156
+ const groups = /* @__PURE__ */ new Map();
4157
+ for (const request of requests) {
4158
+ const group = groups.get(request.itemType);
4159
+ if (group) {
4160
+ group.push(request);
4161
+ } else {
4162
+ groups.set(request.itemType, [request]);
4163
+ }
4164
+ }
4165
+ for (const group of groups.values()) {
4166
+ reorderPoolAssignments(group, allocations, containerIndices, false);
4167
+ }
4168
+ } else {
4169
+ containerIndices.length = 0;
4170
+ let indicesSorted = true;
4171
+ for (let i = 0; i < requests.length; i++) {
4172
+ const containerIndex = allocations[requests[i].order].containerIndex;
4173
+ if (i > 0 && containerIndex < containerIndices[i - 1]) {
4174
+ indicesSorted = false;
4175
+ }
4176
+ containerIndices.push(containerIndex);
4177
+ }
4178
+ if (!indicesSorted) {
4179
+ containerIndices.sort(comparatorByIndex);
4180
+ }
4181
+ requests.sort(comparatorByRenderPriority);
4182
+ for (let i = 0; i < requests.length; i++) {
4183
+ allocations[requests[i].order].containerIndex = containerIndices[i];
4184
+ }
4185
+ }
4186
+ }
4187
+ }
4074
4188
 
4075
4189
  // src/utils/setDidLayout.ts
4076
4190
  function setDidLayout(ctx) {
4077
4191
  const state = ctx.state;
4078
4192
  state.queuedInitialLayout = true;
4079
- checkAtBottom(ctx);
4193
+ checkThresholds(ctx);
4080
4194
  setInitialRenderState(ctx, { didLayout: true });
4081
4195
  if (state.pendingMaintainScrollAtEnd) {
4082
4196
  doMaintainScrollAtEnd(ctx);
@@ -4181,7 +4295,7 @@ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentSt
4181
4295
  }
4182
4296
  function trackVisibleRange(range, i, top, size, scroll, scrollBottom) {
4183
4297
  let didPassVisibleEnd = false;
4184
- if (range.startNoBuffer === null && top + size > scroll) {
4298
+ if (range.startNoBuffer === null && top + size > scroll && top <= scrollBottom) {
4185
4299
  range.startNoBuffer = i;
4186
4300
  }
4187
4301
  if (range.firstFullyOnScreenIndex === void 0 && top >= scroll - 10 && top <= scrollBottom) {
@@ -4247,7 +4361,7 @@ function findFirstVisibleIndexInCachedRange(ctx, scroll) {
4247
4361
  return null;
4248
4362
  }
4249
4363
  function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, scrollLength, scroll, scrollBottom) {
4250
- var _a3, _b;
4364
+ var _a3, _b, _c, _d;
4251
4365
  const state = ctx.state;
4252
4366
  const {
4253
4367
  endBuffered,
@@ -4281,14 +4395,14 @@ function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, sc
4281
4395
  startNoBuffer: visibleRange.startNoBuffer
4282
4396
  });
4283
4397
  maybeEmitFirstVisibleItemChanged(state, visibleRange.startNoBuffer);
4284
- if (visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null) {
4398
+ if (state.props.hasExternalScroll || visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null) {
4285
4399
  updateViewableItems(
4286
4400
  state,
4287
4401
  ctx,
4288
4402
  viewabilityConfigCallbackPairs,
4289
4403
  scrollLength,
4290
- visibleRange.startNoBuffer,
4291
- visibleRange.endNoBuffer,
4404
+ (_c = visibleRange.startNoBuffer) != null ? _c : 0,
4405
+ (_d = visibleRange.endNoBuffer) != null ? _d : -1,
4292
4406
  startBuffered,
4293
4407
  endBuffered
4294
4408
  );
@@ -4297,16 +4411,16 @@ function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, sc
4297
4411
  function calculateItemsInView(ctx, params = {}) {
4298
4412
  const state = ctx.state;
4299
4413
  batchedUpdates(() => {
4300
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u;
4414
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
4301
4415
  const {
4302
4416
  columns,
4303
4417
  containerItemKeys,
4304
4418
  enableScrollForNextCalculateItemsInView,
4305
4419
  idCache,
4306
4420
  indexByKey,
4307
- minIndexSizeChanged,
4421
+ positionRecalculationStartIndex,
4308
4422
  positions,
4309
- props: { alwaysRenderIndicesArr, alwaysRenderIndicesSet, getItemType, keyExtractor, onStickyHeaderChange },
4423
+ props: { alwaysRenderIndicesArr, alwaysRenderIndicesSet, keyExtractor, onStickyHeaderChange },
4310
4424
  scrollForNextCalculateItemsInView,
4311
4425
  scrollLength,
4312
4426
  sizes,
@@ -4349,7 +4463,7 @@ function calculateItemsInView(ctx, params = {}) {
4349
4463
  scrollAdjustPending = (_a4 = peek$(ctx, "scrollAdjustPending")) != null ? _a4 : 0;
4350
4464
  scrollAdjustPad = scrollAdjustPending - topPad;
4351
4465
  scroll = Math.round(nextScrollState + scrollExtra + scrollAdjustPad);
4352
- if (scroll + scrollLength > totalSize) {
4466
+ if (!state.props.hasExternalScroll && scroll + scrollLength > totalSize) {
4353
4467
  scroll = Math.max(0, totalSize - scrollLength);
4354
4468
  }
4355
4469
  };
@@ -4376,7 +4490,11 @@ function calculateItemsInView(ctx, params = {}) {
4376
4490
  let stickyState = dataChanged ? void 0 : resolveStickyState();
4377
4491
  let scrollBufferTop = drawDistance;
4378
4492
  let scrollBufferBottom = drawDistance;
4379
- if (speed > 0 || speed === 0 && scroll < Math.max(50, drawDistance)) {
4493
+ if (speed !== 0) {
4494
+ state.scrollBufferDirection = speed > 0 ? 1 : -1;
4495
+ }
4496
+ const bufferForward = state.scrollBufferDirection === 1 || state.scrollBufferDirection === void 0 && scroll < Math.max(50, drawDistance);
4497
+ if (bufferForward) {
4380
4498
  scrollBufferTop = drawDistance * 0.5;
4381
4499
  scrollBufferBottom = drawDistance * 1.5;
4382
4500
  } else {
@@ -4386,8 +4504,8 @@ function calculateItemsInView(ctx, params = {}) {
4386
4504
  const shouldProjectRenderRange = !dataChanged && !forceFullItemPositions && !suppressInitialScrollSideEffects && !hasActiveInitialScroll(state) && !state.scrollingTo && !state.pendingNativeMVCPAdjust && !!peek$(ctx, "readyToRender");
4387
4505
  const projectedBufferAdjustment = shouldProjectRenderRange ? getProjectedBufferAdjustment(speed, Math.min(scrollBufferTop, scrollBufferBottom)) : 0;
4388
4506
  const updateScrollRange = () => {
4389
- const scrollStart = Math.max(0, scroll);
4390
- const overscrollBeforeContent = Math.max(0, -nativeScrollState);
4507
+ const scrollStart = state.props.hasExternalScroll ? scroll : Math.max(0, scroll);
4508
+ const overscrollBeforeContent = state.props.hasExternalScroll ? 0 : Math.max(0, -nativeScrollState);
4391
4509
  scrollBottom = Math.max(scrollStart, scroll + scrollLength + overscrollBeforeContent);
4392
4510
  scrollTopBuffered = scrollStart - scrollBufferTop + projectedBufferAdjustment;
4393
4511
  scrollBottomBuffered = scrollBottom + scrollBufferBottom + projectedBufferAdjustment;
@@ -4422,21 +4540,21 @@ function calculateItemsInView(ctx, params = {}) {
4422
4540
  if (dataChanged) {
4423
4541
  resetLayoutCachesForDataChange(state);
4424
4542
  }
4425
- const startIndex = forceFullItemPositions || dataChanged ? 0 : (_e = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _e : 0;
4426
- const optimizeForVisibleWindow = !forceFullItemPositions && !dataChanged && numColumns > 1 && minIndexSizeChanged !== void 0;
4543
+ const startIndex = forceFullItemPositions || dataChanged ? 0 : (_e = positionRecalculationStartIndex != null ? positionRecalculationStartIndex : state.startBuffered) != null ? _e : 0;
4544
+ const optimizeForVisibleWindow = !forceFullItemPositions && !dataChanged && numColumns > 1 && positionRecalculationStartIndex !== void 0;
4427
4545
  updateItemPositions(ctx, dataChanged, {
4428
4546
  doMVCP,
4429
4547
  // A changed size shifts every following position. The scrolling early-exit
4430
4548
  // would leave the untouched suffix in the old coordinate space.
4431
- forceFullUpdate: !!forceFullItemPositions || minIndexSizeChanged !== void 0,
4549
+ forceFullUpdate: !!forceFullItemPositions || positionRecalculationStartIndex !== void 0,
4432
4550
  optimizeForVisibleWindow,
4433
4551
  scrollBottomBuffered,
4434
4552
  scrollVelocity: speed,
4435
4553
  startIndex
4436
4554
  });
4437
4555
  totalSize = getContentSize(ctx);
4438
- if (minIndexSizeChanged !== void 0) {
4439
- state.minIndexSizeChanged = void 0;
4556
+ if (positionRecalculationStartIndex !== void 0) {
4557
+ state.positionRecalculationStartIndex = void 0;
4440
4558
  }
4441
4559
  let protectedContainerKeys;
4442
4560
  if (dataChanged && doMVCP && state.props.maintainVisibleContentPosition.data && state.didContainersLayout && state.idsInView.length > 0) {
@@ -4516,7 +4634,7 @@ function calculateItemsInView(ctx, params = {}) {
4516
4634
  nextTop = top;
4517
4635
  }
4518
4636
  }
4519
- if (visibleRange.startNoBuffer !== null) {
4637
+ if (startBuffered !== null) {
4520
4638
  if (top <= scrollBottomBuffered) {
4521
4639
  endBuffered = i;
4522
4640
  if (scrollBottomBuffered > totalSize) {
@@ -4527,6 +4645,8 @@ function calculateItemsInView(ctx, params = {}) {
4527
4645
  } else {
4528
4646
  foundEnd = true;
4529
4647
  }
4648
+ } else if (top > scrollBottomBuffered) {
4649
+ foundEnd = true;
4530
4650
  }
4531
4651
  }
4532
4652
  }
@@ -4544,6 +4664,12 @@ function calculateItemsInView(ctx, params = {}) {
4544
4664
  bottom: nextBottom,
4545
4665
  top: nextTop
4546
4666
  };
4667
+ } else if (enableScrollForNextCalculateItemsInView && state.props.hasExternalScroll) {
4668
+ if (scrollBottomBuffered < 0) {
4669
+ state.scrollForNextCalculateItemsInView = { bottom: 0, top: null };
4670
+ } else if (scrollTopBuffered > totalSize) {
4671
+ state.scrollForNextCalculateItemsInView = { bottom: null, top: totalSize };
4672
+ }
4547
4673
  }
4548
4674
  let numContainers = prevNumContainers;
4549
4675
  const pendingRemoval = [];
@@ -4567,7 +4693,9 @@ function calculateItemsInView(ctx, params = {}) {
4567
4693
  }
4568
4694
  const hasScrollTargetPinnedRange = scrollTargetPinnedStart <= scrollTargetPinnedEnd;
4569
4695
  const isPinnedRenderIndex = (index) => alwaysRenderIndicesSet.has(index) || hasScrollTargetPinnedRange && index >= scrollTargetPinnedStart && index <= scrollTargetPinnedEnd;
4570
- if (startBuffered !== null && endBuffered !== null) {
4696
+ if (startBuffered !== null && endBuffered !== null || alwaysRenderIndicesArr.length || hasScrollTargetPinnedRange) {
4697
+ const allocationStart = startBuffered != null ? startBuffered : 0;
4698
+ const allocationEnd = endBuffered != null ? endBuffered : -1;
4571
4699
  const needNewContainers = [];
4572
4700
  const needNewContainersSet = /* @__PURE__ */ new Set();
4573
4701
  const addPinnedIndex = (index) => {
@@ -4583,7 +4711,7 @@ function calculateItemsInView(ctx, params = {}) {
4583
4711
  }
4584
4712
  }
4585
4713
  };
4586
- for (let i = startBuffered; i <= endBuffered; i++) {
4714
+ for (let i = allocationStart; i <= allocationEnd; i++) {
4587
4715
  const id = (_m = idCache[i]) != null ? _m : getId(state, i);
4588
4716
  if (!containerItemKeys.has(id)) {
4589
4717
  needNewContainersSet.add(i);
@@ -4605,25 +4733,25 @@ function calculateItemsInView(ctx, params = {}) {
4605
4733
  (_n = stickyState == null ? void 0 : stickyState.currentStickyIdx) != null ? _n : -1,
4606
4734
  needNewContainers,
4607
4735
  needNewContainersSet,
4608
- startBuffered,
4609
- endBuffered
4736
+ allocationStart,
4737
+ allocationEnd
4610
4738
  );
4611
4739
  } else if (previousStickyIndex !== -1) {
4612
4740
  set$(ctx, "activeStickyIndex", -1);
4613
4741
  }
4614
4742
  if (needNewContainers.length > 0) {
4615
- const getRequiredItemType = getItemType ? (i) => {
4616
- const itemType = getItemType(data[i], i);
4617
- return itemType !== void 0 ? String(itemType) : "";
4618
- } : void 0;
4619
4743
  const availableContainerAllocations = findAvailableContainers(
4620
4744
  ctx,
4621
4745
  needNewContainers,
4622
- startBuffered,
4623
- endBuffered,
4746
+ allocationStart,
4747
+ allocationEnd,
4624
4748
  pendingRemoval,
4625
- getRequiredItemType,
4626
- protectedContainerKeys
4749
+ protectedContainerKeys,
4750
+ // Initial end alignment has no scroll velocity yet. Keep its
4751
+ // target-side rows first until the initial scroll settles.
4752
+ // After a pause velocity can be zero on the first movement;
4753
+ // use its displacement without treating initial insets as motion.
4754
+ hasActiveInitialScroll(state) ? (initialScroll == null ? void 0 : initialScroll.viewPosition) === 1 : (speed || (state.hasScrolled ? state.scroll - state.scrollPrev : 0)) < 0
4627
4755
  );
4628
4756
  for (const allocation of availableContainerAllocations) {
4629
4757
  const i = allocation.itemIndex;
@@ -4743,19 +4871,19 @@ function calculateItemsInView(ctx, params = {}) {
4743
4871
  handleInitialScrollLayoutReady(ctx);
4744
4872
  }
4745
4873
  }
4746
- if (viewabilityConfigCallbackPairs && visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null) {
4874
+ if (viewabilityConfigCallbackPairs && (state.props.hasExternalScroll || visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null)) {
4747
4875
  updateViewableItems(
4748
4876
  ctx.state,
4749
4877
  ctx,
4750
4878
  viewabilityConfigCallbackPairs,
4751
4879
  scrollLength,
4752
- visibleRange.startNoBuffer,
4753
- visibleRange.endNoBuffer,
4754
- startBuffered != null ? startBuffered : visibleRange.startNoBuffer,
4755
- endBuffered != null ? endBuffered : visibleRange.endNoBuffer
4880
+ (_u = visibleRange.startNoBuffer) != null ? _u : 0,
4881
+ (_v = visibleRange.endNoBuffer) != null ? _v : -1,
4882
+ (_w = startBuffered != null ? startBuffered : visibleRange.startNoBuffer) != null ? _w : 0,
4883
+ (_x = endBuffered != null ? endBuffered : visibleRange.endNoBuffer) != null ? _x : -1
4756
4884
  );
4757
4885
  }
4758
- (_u = stickyState == null ? void 0 : stickyState.finishCalculateItemsInView) == null ? void 0 : _u.call(stickyState);
4886
+ (_y = stickyState == null ? void 0 : stickyState.finishCalculateItemsInView) == null ? void 0 : _y.call(stickyState);
4759
4887
  });
4760
4888
  }
4761
4889
 
@@ -4919,6 +5047,7 @@ function updateItemSizesBatch(ctx, measurements) {
4919
5047
  var _a3;
4920
5048
  const state = ctx.state;
4921
5049
  const result = {};
5050
+ const previousTotalSize = state.totalSize;
4922
5051
  for (const measurement of measurements) {
4923
5052
  const ownsMeasuredItem = measurement.containerId === void 0 || peek$(ctx, `containerItemKey${measurement.containerId}`) === measurement.itemKey;
4924
5053
  if (ownsMeasuredItem) {
@@ -4929,6 +5058,9 @@ function updateItemSizesBatch(ctx, measurements) {
4929
5058
  mergeItemSizeUpdateResult(result, nextResult);
4930
5059
  }
4931
5060
  }
5061
+ if (state.totalSize !== previousTotalSize) {
5062
+ set$(ctx, "totalSize", state.totalSize);
5063
+ }
4932
5064
  flushItemSizeUpdates(ctx, result);
4933
5065
  }
4934
5066
  function applyItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
@@ -4967,12 +5099,12 @@ function applyItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
4967
5099
  }
4968
5100
  let needsRecalculate = !didContainersLayout;
4969
5101
  let shouldMaintainScrollAtEnd = false;
4970
- let minIndexSizeChanged;
5102
+ let positionRecalculationStartIndex;
4971
5103
  const prevSizeKnown = state.sizesKnown.get(itemKey);
4972
- const diff = updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem);
5104
+ const diff = updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem, false);
4973
5105
  const size = roundSize(horizontal ? sizeObj.width : sizeObj.height);
4974
5106
  if (diff !== 0) {
4975
- minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
5107
+ positionRecalculationStartIndex = positionRecalculationStartIndex !== void 0 ? Math.min(positionRecalculationStartIndex, index) : index;
4976
5108
  const { startBuffered, endBuffered } = state;
4977
5109
  needsRecalculate || (needsRecalculate = index >= startBuffered && index <= endBuffered);
4978
5110
  if (!needsRecalculate && state.containerItemKeys.has(itemKey)) {
@@ -4989,8 +5121,8 @@ function applyItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
4989
5121
  size
4990
5122
  });
4991
5123
  }
4992
- if (minIndexSizeChanged !== void 0) {
4993
- state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
5124
+ if (positionRecalculationStartIndex !== void 0) {
5125
+ state.positionRecalculationStartIndex = state.positionRecalculationStartIndex !== void 0 ? Math.min(state.positionRecalculationStartIndex, positionRecalculationStartIndex) : positionRecalculationStartIndex;
4994
5126
  }
4995
5127
  updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
4996
5128
  if (didContainersLayout || checkAllSizesKnown(state, state.startBuffered, state.endBuffered)) {
@@ -5007,7 +5139,7 @@ function applyItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
5007
5139
  didMeasureUserScrollAnchorResetItem
5008
5140
  };
5009
5141
  }
5010
- function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
5142
+ function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem, notifyTotalSize = true) {
5011
5143
  var _a3, _b;
5012
5144
  const state = ctx.state;
5013
5145
  const {
@@ -5030,13 +5162,23 @@ function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
5030
5162
  fixedItemSize,
5031
5163
  itemType
5032
5164
  } : void 0;
5033
- const prevSize = getItemSize(ctx, itemKey, index, itemData, void 0, void 0, void 0, resolvedItemSize);
5165
+ const prevSize = getItemSize(
5166
+ ctx,
5167
+ itemKey,
5168
+ index,
5169
+ itemData,
5170
+ void 0,
5171
+ void 0,
5172
+ notifyTotalSize,
5173
+ resolvedItemSize
5174
+ );
5034
5175
  const rawSize = horizontal ? sizeObj.width : sizeObj.height;
5035
5176
  const prevSizeKnown = sizesKnown.get(itemKey);
5036
5177
  if (Platform.OS !== "web" && prevSizeKnown !== void 0 && isNativeLayoutNoise(rawSize - prevSizeKnown)) {
5037
5178
  return 0;
5038
5179
  }
5039
5180
  const size = Platform.OS === "web" ? Math.round(rawSize) : roundSize(rawSize);
5181
+ if (prevSizeKnown !== size) state.positionsAreCurrent = false;
5040
5182
  sizesKnown.set(itemKey, size);
5041
5183
  if (fixedItemSize === void 0 && size > 0) {
5042
5184
  itemType != null ? itemType : itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
@@ -5055,7 +5197,7 @@ function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
5055
5197
  }
5056
5198
  }
5057
5199
  if (!prevSize || Math.abs(prevSize - size) > 0.1) {
5058
- setSize(ctx, itemKey, size);
5200
+ setSize(ctx, itemKey, size, notifyTotalSize);
5059
5201
  return size - prevSize;
5060
5202
  }
5061
5203
  return 0;
@@ -5206,10 +5348,16 @@ function useValue$(key, params) {
5206
5348
  var _a3;
5207
5349
  return (_a3 = getValue ? getValue(peek$(ctx, key)) : peek$(ctx, key)) != null ? _a3 : 0;
5208
5350
  };
5209
- const animValue = useAnimatedValue(getNewValue());
5351
+ const initialValue = getNewValue();
5352
+ const animValue = useAnimatedValue(initialValue);
5353
+ const lastValue = useRef(initialValue);
5210
5354
  useLayoutEffect(() => {
5211
5355
  const syncCurrentValue = () => {
5212
- animValue.setValue(getNewValue());
5356
+ const nextValue = getNewValue();
5357
+ if (lastValue.current !== nextValue) {
5358
+ lastValue.current = nextValue;
5359
+ animValue.setValue(nextValue);
5360
+ }
5213
5361
  };
5214
5362
  const unsubscribe = listen$(ctx, key, syncCurrentValue);
5215
5363
  syncCurrentValue();
@@ -6124,12 +6272,14 @@ var ListComponent = typedMemo(function ListComponent2({
6124
6272
  renderScrollComponent,
6125
6273
  onLayoutFooter,
6126
6274
  onInternalScrollBeginDrag,
6275
+ onInternalScrollEndDrag,
6127
6276
  onInternalScrollEnd,
6128
6277
  scrollAdjustHandler,
6129
6278
  snapToIndices,
6130
6279
  stickyHeaderConfig,
6131
6280
  stickyHeaderIndices,
6132
6281
  useWindowScroll = false,
6282
+ scrollElement,
6133
6283
  ...rest
6134
6284
  }) {
6135
6285
  const ctx = useStateContext();
@@ -6195,7 +6345,7 @@ var ListComponent = typedMemo(function ListComponent2({
6195
6345
  SnapOrScroll,
6196
6346
  {
6197
6347
  ...rest,
6198
- ...Platform.OS === "web" ? ScrollComponent === ListComponentScrollView ? { onInternalScrollEnd, useWindowScroll } : {} : { onScrollBeginDrag: onInternalScrollBeginDrag },
6348
+ ...Platform.OS === "web" ? ScrollComponent === ListComponentScrollView ? { onInternalScrollEnd, scrollElement, useWindowScroll } : {} : { onScrollBeginDrag: onInternalScrollBeginDrag, onScrollEndDrag: onInternalScrollEndDrag },
6199
6349
  contentContainerStyle: [
6200
6350
  horizontal ? { height: "100%" } : {},
6201
6351
  contentContainerStyle,
@@ -6235,7 +6385,15 @@ var WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO = 0.9;
6235
6385
  var WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO = 0.9;
6236
6386
  function useDevChecksImpl(props) {
6237
6387
  const ctx = useStateContext();
6238
- const { anchoredEndSpace, childrenMode, keyExtractor, numColumns, renderScrollComponent, useWindowScroll } = props;
6388
+ const {
6389
+ anchoredEndSpace,
6390
+ childrenMode,
6391
+ keyExtractor,
6392
+ numColumns,
6393
+ renderScrollComponent,
6394
+ scrollElement,
6395
+ useWindowScroll
6396
+ } = props;
6239
6397
  const hasAnchoredEndSpace = !!anchoredEndSpace;
6240
6398
  useEffect(() => {
6241
6399
  if (hasAnchoredEndSpace && (numColumns != null ? numColumns : 1) > 1) {
@@ -6246,13 +6404,13 @@ function useDevChecksImpl(props) {
6246
6404
  }
6247
6405
  }, [hasAnchoredEndSpace, numColumns]);
6248
6406
  useEffect(() => {
6249
- if (useWindowScroll && renderScrollComponent) {
6407
+ if ((useWindowScroll || scrollElement !== void 0) && renderScrollComponent) {
6250
6408
  warnDevOnce(
6251
6409
  "useWindowScrollRenderScrollComponent",
6252
- "useWindowScroll is not supported when renderScrollComponent is provided."
6410
+ "useWindowScroll and scrollElement are not supported when renderScrollComponent is provided."
6253
6411
  );
6254
6412
  }
6255
- }, [renderScrollComponent, useWindowScroll]);
6413
+ }, [renderScrollComponent, scrollElement, useWindowScroll]);
6256
6414
  useEffect(() => {
6257
6415
  if (!keyExtractor && !ctx.state.isFirst && ctx.state.didDataChange && !childrenMode) {
6258
6416
  warnDevOnce(
@@ -6264,8 +6422,8 @@ function useDevChecksImpl(props) {
6264
6422
  useEffect(() => {
6265
6423
  const state = ctx.state;
6266
6424
  const dataLength = state.props.data.length;
6267
- const useWindowScrollResolved = state.props.useWindowScroll;
6268
- if (Platform.OS !== "web" || useWindowScrollResolved || dataLength < WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH) {
6425
+ const usesExternalScroll = state.props.hasExternalScroll;
6426
+ if (Platform.OS !== "web" || usesExternalScroll || dataLength < WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH) {
6269
6427
  return;
6270
6428
  }
6271
6429
  const warnIfUnboundedOuterSize = () => {
@@ -6314,7 +6472,7 @@ function checkResetContainers(ctx, dataProp, { didColumnsChange = false } = {})
6314
6472
  for (const key in state.averageSizes) {
6315
6473
  delete state.averageSizes[key];
6316
6474
  }
6317
- state.minIndexSizeChanged = 0;
6475
+ state.positionRecalculationStartIndex = 0;
6318
6476
  state.scrollForNextCalculateItemsInView = void 0;
6319
6477
  }
6320
6478
  calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
@@ -6429,7 +6587,8 @@ function doInitialAllocateContainers(ctx) {
6429
6587
  calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
6430
6588
  });
6431
6589
  } else {
6432
- calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
6590
+ const reuseInitialPositions = !state.didLoad && state.positionsAreCurrent && !state.didDataChange && !state.didColumnsChange;
6591
+ calculateItemsInView(ctx, { dataChanged: !reuseInitialPositions, doMVCP: true });
6433
6592
  }
6434
6593
  }
6435
6594
  return true;
@@ -6468,17 +6627,15 @@ function handleLayout(ctx, layoutParam, setCanRender) {
6468
6627
  updateContentMetricsState(ctx);
6469
6628
  state.lastBatchingAction = Date.now();
6470
6629
  state.scrollForNextCalculateItemsInView = void 0;
6471
- if (scrollLength > 0) {
6472
- doInitialAllocateContainers(ctx);
6473
- }
6474
- if (needsCalculate) {
6630
+ const didAllocate = scrollLength > 0 && doInitialAllocateContainers(ctx);
6631
+ if (needsCalculate && (!didAllocate || state.initialScroll)) {
6475
6632
  calculateItemsInView(ctx, { doMVCP: true });
6476
6633
  }
6477
6634
  if (didChange || otherAxisSize !== prevOtherAxisSize) {
6478
6635
  set$(ctx, "scrollSize", { height: layout.height, width: layout.width });
6479
6636
  }
6480
6637
  if (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onLayout) {
6481
- doMaintainScrollAtEnd(ctx);
6638
+ doMaintainScrollAtEnd(ctx, { immediate: true });
6482
6639
  }
6483
6640
  checkThresholds(ctx);
6484
6641
  if (state) {
@@ -6553,7 +6710,7 @@ function onScroll(ctx, event) {
6553
6710
  }
6554
6711
  state.lastNativeScroll = newScroll;
6555
6712
  state.lastNativeScrollTime = Date.now();
6556
- if (state.scrollingTo && state.scrollingTo.offset >= newScroll) {
6713
+ if (!state.props.hasExternalScroll && state.scrollingTo && state.scrollingTo.offset >= newScroll) {
6557
6714
  const maxOffset = clampScrollOffset(ctx, newScroll, state.scrollingTo);
6558
6715
  if (newScroll !== maxOffset && Math.abs(newScroll - maxOffset) > 1) {
6559
6716
  newScroll = maxOffset;
@@ -6711,12 +6868,12 @@ function scrollToEnd(ctx, options) {
6711
6868
  if (index === -1) {
6712
6869
  return false;
6713
6870
  }
6714
- const paddingBottom = state.props.stylePaddingBottom || 0;
6871
+ const paddingBottom = getStylePaddingEnd(state.props);
6715
6872
  const footerSize = peek$(ctx, "footerSize") || 0;
6716
6873
  scrollToIndex(ctx, {
6717
6874
  ...options,
6718
6875
  index,
6719
- viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
6876
+ viewOffset: getEndAlignedViewOffset(paddingBottom, footerSize) + ((options == null ? void 0 : options.viewOffset) || 0),
6720
6877
  viewPosition: 1
6721
6878
  });
6722
6879
  if (state.scrollingTo) {
@@ -6919,7 +7076,7 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
6919
7076
  for (const key in state.averageSizes) {
6920
7077
  delete state.averageSizes[key];
6921
7078
  }
6922
- state.minIndexSizeChanged = 0;
7079
+ state.positionRecalculationStartIndex = 0;
6923
7080
  state.scrollForNextCalculateItemsInView = void 0;
6924
7081
  state.pendingTotalSize = void 0;
6925
7082
  state.totalSize = 0;
@@ -7313,10 +7470,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7313
7470
  onMetricsChange,
7314
7471
  onLayout: onLayoutProp,
7315
7472
  onLoad,
7473
+ onReady,
7316
7474
  onMomentumScrollEnd,
7317
7475
  onRefresh,
7318
7476
  onScroll: onScrollProp,
7319
7477
  onScrollBeginDrag,
7478
+ onScrollEndDrag,
7320
7479
  onStartReached,
7321
7480
  onStartReachedThreshold = 0.5,
7322
7481
  onStickyHeaderChange,
@@ -7334,6 +7493,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7334
7493
  stickyHeaderIndices: stickyHeaderIndicesProp,
7335
7494
  style: styleProp,
7336
7495
  useWindowScroll = false,
7496
+ scrollElement,
7337
7497
  viewabilityConfig,
7338
7498
  viewabilityConfigCallbackPairs,
7339
7499
  ...rest
@@ -7385,17 +7545,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7385
7545
  const initialScrollProp = initialScrollAtEnd ? {
7386
7546
  index: Math.max(0, dataProp.length - 1),
7387
7547
  preserveForBottomPadding: true,
7388
- viewOffset: -stylePaddingEndState,
7548
+ viewOffset: getEndAlignedViewOffset(stylePaddingEndState),
7389
7549
  viewPosition: 1
7390
7550
  } : hasInitialScrollIndex ? typeof initialScrollIndexProp === "object" ? {
7391
7551
  index: (_b = initialScrollIndexProp.index) != null ? _b : 0,
7392
7552
  preserveForBottomPadding: initialScrollIndexProp.viewOffset === void 0 && initialScrollIndexProp.viewPosition === 1 ? true : void 0,
7393
- viewOffset: (_c = initialScrollIndexProp.viewOffset) != null ? _c : initialScrollIndexProp.viewPosition === 1 ? -stylePaddingEndState : 0,
7553
+ viewOffset: (_c = initialScrollIndexProp.viewOffset) != null ? _c : initialScrollIndexProp.viewPosition === 1 ? getEndAlignedViewOffset(stylePaddingEndState) : 0,
7394
7554
  viewPosition: (_d = initialScrollIndexProp.viewPosition) != null ? _d : 0
7395
7555
  } : {
7396
7556
  index: initialScrollIndexProp != null ? initialScrollIndexProp : 0,
7397
7557
  preserveForBottomPadding: shouldBottomAlignNumericInitialScrollIndex ? true : void 0,
7398
- viewOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : shouldBottomAlignNumericInitialScrollIndex ? -stylePaddingEndState : 0,
7558
+ viewOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : shouldBottomAlignNumericInitialScrollIndex ? getEndAlignedViewOffset(stylePaddingEndState) : 0,
7399
7559
  viewPosition: shouldBottomAlignNumericInitialScrollIndex ? 1 : void 0
7400
7560
  } : initialScrollUsesOffsetOnly ? {
7401
7561
  contentOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : 0,
@@ -7428,7 +7588,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7428
7588
  dataVersion,
7429
7589
  keyExtractor
7430
7590
  ]);
7431
- const useWindowScrollResolved = Platform.OS === "web" && !!useWindowScroll && !renderScrollComponent;
7591
+ const scrollElementResolved = Platform.OS === "web" && !renderScrollComponent ? scrollElement : void 0;
7592
+ const useWindowScrollResolved = Platform.OS === "web" && !!useWindowScroll && scrollElementResolved === void 0 && !renderScrollComponent;
7593
+ const hasExternalScroll = useWindowScrollResolved || scrollElementResolved !== void 0;
7432
7594
  const refState = useRef(void 0);
7433
7595
  const hasOverrideItemLayout = !!overrideItemLayout;
7434
7596
  const prevHasOverrideItemLayout = useRef(hasOverrideItemLayout);
@@ -7473,11 +7635,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7473
7635
  lastLayout: void 0,
7474
7636
  lastScrollDelta: 0,
7475
7637
  loadStartTime: Date.now(),
7476
- minIndexSizeChanged: 0,
7477
7638
  nativeContentInset: void 0,
7478
7639
  nativeMarginTop: 0,
7479
7640
  pendingDataComparison: void 0,
7480
7641
  pendingNativeMVCPAdjust: void 0,
7642
+ positionRecalculationStartIndex: 0,
7481
7643
  positions: [],
7482
7644
  props: {},
7483
7645
  queuedCalculateItemsInView: 0,
@@ -7566,6 +7728,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7566
7728
  estimatedItemSize,
7567
7729
  getFixedItemSize: useWrapIfItem(getFixedItemSize),
7568
7730
  getItemType: useWrapIfItem(getItemType),
7731
+ hasExternalScroll,
7569
7732
  hideItemsUntilMeasured: experimental_hideItemsUntilMeasured,
7570
7733
  horizontal: !!horizontal,
7571
7734
  itemsAreEqual,
@@ -7580,8 +7743,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7580
7743
  onItemSizeChanged,
7581
7744
  onLoad,
7582
7745
  onMomentumScrollEnd,
7746
+ onReady,
7583
7747
  onScroll: throttleScrollFn,
7584
7748
  onScrollBeginDrag,
7749
+ onScrollEndDrag,
7585
7750
  onStartReached,
7586
7751
  onStartReachedThreshold,
7587
7752
  onStickyHeaderChange,
@@ -7760,9 +7925,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7760
7925
  const { onLayout } = useOnLayoutSync({
7761
7926
  onLayoutChange,
7762
7927
  onLayoutProp,
7763
- ref: refScroller
7764
- // the type of ScrollView doesn't include measure?
7765
- });
7928
+ ref: refScroller});
7766
7929
  useLayoutEffect(() => {
7767
7930
  if (snapToIndices) {
7768
7931
  updateSnapToOffsets(ctx);
@@ -7877,11 +8040,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7877
8040
  onScroll: (event) => onScroll(ctx, event),
7878
8041
  onScrollBeginDrag: (event) => {
7879
8042
  var _a4, _b2;
8043
+ state.isDragging = true;
7880
8044
  interruptMaintainScrollAtEnd(ctx);
7881
8045
  prepareReachedEdgeForNextUserScroll(ctx);
7882
8046
  (_b2 = (_a4 = state.props).onScrollBeginDrag) == null ? void 0 : _b2.call(_a4, event);
7883
8047
  },
7884
- onScrollEnd: () => prepareReachedEdgeForNextUserScroll(ctx)
8048
+ onScrollEnd: () => prepareReachedEdgeForNextUserScroll(ctx),
8049
+ onScrollEndDrag: (event) => {
8050
+ var _a4, _b2;
8051
+ state.isDragging = false;
8052
+ (_b2 = (_a4 = state.props).onScrollEndDrag) == null ? void 0 : _b2.call(_a4, event);
8053
+ }
7885
8054
  }),
7886
8055
  []
7887
8056
  );
@@ -7905,6 +8074,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7905
8074
  ListHeaderComponent,
7906
8075
  onInternalScrollBeginDrag: fns.onScrollBeginDrag,
7907
8076
  onInternalScrollEnd: fns.onScrollEnd,
8077
+ onInternalScrollEndDrag: fns.onScrollEndDrag,
7908
8078
  onLayout,
7909
8079
  onLayoutFooter,
7910
8080
  onMomentumScrollEnd: fns.onMomentumScrollEnd,
@@ -7923,6 +8093,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7923
8093
  refScrollView: combinedRef,
7924
8094
  renderScrollComponent,
7925
8095
  scrollAdjustHandler: (_s = refState.current) == null ? void 0 : _s.scrollAdjustHandler,
8096
+ scrollElement: scrollElementResolved,
7926
8097
  scrollEventThrottle: 0,
7927
8098
  snapToIndices,
7928
8099
  stickyHeaderIndices,