@legendapp/list 3.3.11 → 3.4.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;
@@ -918,6 +928,11 @@ function resetInitialRenderState(ctx, {
918
928
  if (resetLayout) {
919
929
  state.didContainersLayout = false;
920
930
  state.queuedInitialLayout = false;
931
+ state.edgeReachedGate = void 0;
932
+ state.isStartReached = null;
933
+ state.isEndReached = null;
934
+ state.startReachedSnapshot = void 0;
935
+ state.endReachedSnapshot = void 0;
921
936
  }
922
937
  if (resetInitialScroll) {
923
938
  state.didFinishInitialScroll = false;
@@ -932,7 +947,7 @@ function setInitialRenderState(ctx, {
932
947
  const { state } = ctx;
933
948
  const {
934
949
  loadStartTime,
935
- props: { onLoad }
950
+ props: { onLoad, onReady }
936
951
  } = state;
937
952
  if (didLayout) {
938
953
  state.didContainersLayout = true;
@@ -953,6 +968,7 @@ function setInitialRenderState(ctx, {
953
968
  onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
954
969
  }
955
970
  }
971
+ onReady == null ? void 0 : onReady();
956
972
  }
957
973
  }
958
974
 
@@ -1298,10 +1314,6 @@ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1298
1314
  const itemSize = Math.max(0, measuredItemSize - (isOutOfBounds ? 0 : ctx.scrollAxisGap));
1299
1315
  const trailingInset = getContentInsetEnd(ctx);
1300
1316
  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
1317
  }
1306
1318
  return offset;
1307
1319
  }
@@ -1817,7 +1829,7 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
1817
1829
  state.scrollTime = currentTime;
1818
1830
  const scrollDelta = Math.abs(newScroll - prevScroll);
1819
1831
  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);
1832
+ const didCancelMaintainScrollAtEnd = Platform.OS === "web" && isUserScrollEvent && newScroll < prevScroll && !!(state.maintainingScrollAtEnd || state.pendingMaintainScrollAtEnd);
1821
1833
  if (didCancelMaintainScrollAtEnd) {
1822
1834
  finishMaintainScrollAtEnd(ctx);
1823
1835
  }
@@ -1895,89 +1907,103 @@ function finishMaintainScrollAtEnd(ctx) {
1895
1907
  requestAdjust(ctx, nextScroll - state.scroll);
1896
1908
  }
1897
1909
  }
1898
- function doMaintainScrollAtEnd(ctx) {
1899
- var _a3, _b, _c, _d, _e;
1910
+ function doMaintainScrollAtEnd(ctx, options) {
1911
+ var _a3, _b, _c, _d, _e, _f;
1900
1912
  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 });
1913
+ let didMaintain = false;
1914
+ if (state.isDragging) {
1915
+ finishMaintainScrollAtEnd(ctx);
1916
+ } else {
1917
+ const {
1918
+ didContainersLayout,
1919
+ didFinishInitialScroll,
1920
+ pendingNativeMVCPAdjust,
1921
+ props: { maintainScrollAtEnd }
1922
+ } = state;
1923
+ const immediate = (options == null ? void 0 : options.immediate) && Platform.OS === "web" && !(maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.animated);
1924
+ const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
1925
+ const isReplayingPendingMaintain = !!state.pendingMaintainScrollAtEnd;
1926
+ 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);
1927
+ if (shouldMaintainScrollAtEnd && !state.scrollingTo) {
1928
+ const scroller = state.refScroller.current;
1929
+ const nativeMax = (_c = scroller == null ? void 0 : scroller.getMaxScrollOffset) == null ? void 0 : _c.call(scroller);
1930
+ const observed = (_d = scroller == null ? void 0 : scroller.getCurrentScrollOffset) == null ? void 0 : _d.call(scroller);
1931
+ if (nativeMax !== void 0 && observed !== void 0 && nativeMax < state.scroll - 1 && Math.abs(observed - nativeMax) <= 1) {
1932
+ state.pendingMaintainScrollAtEnd = true;
1933
+ state.scrollPending = observed;
1934
+ updateScroll(ctx, observed, true, { markHasScrolled: false });
1935
+ }
1918
1936
  }
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 {
1937
+ if (shouldMaintainScrollAtEnd && !didContainersLayout) {
1938
+ state.pendingMaintainScrollAtEnd = true;
1939
+ } else if (pendingNativeMVCPAdjust) {
1940
+ state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
1941
+ } else if (shouldMaintainScrollAtEnd && (state.scrollingTo || state.pendingScrollResolve)) {
1942
+ if (immediate && ((_e = state.scrollingTo) == null ? void 0 : _e.isScrollToEnd) && !state.scrollingTo.animated) {
1943
+ state.pendingMaintainScrollAtEnd = false;
1944
+ ctx.scrollToEnd({ animated: false });
1945
+ didMaintain = true;
1946
+ } else {
1947
+ state.pendingMaintainScrollAtEnd = true;
1948
+ (_f = state.maintainingScrollAtEnd) != null ? _f : state.maintainingScrollAtEnd = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
1949
+ }
1950
+ } else if (shouldMaintainScrollAtEnd && didContainersLayout) {
1951
+ state.pendingMaintainScrollAtEnd = false;
1952
+ const contentSize = getContentSize(ctx);
1953
+ if (contentSize < state.scrollLength) {
1954
+ state.scroll = 0;
1955
+ }
1956
+ if (!state.maintainingScrollAtEnd || immediate && state.maintainingScrollAtEnd === "pending-instant") {
1957
+ const pendingState = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
1958
+ const activeState = maintainScrollAtEnd.animated ? "animated" : "instant";
1959
+ const scrollAtRequest = state.scroll;
1960
+ state.maintainingScrollAtEnd = pendingState;
1961
+ const run = () => {
1962
+ if (state.maintainingScrollAtEnd === pendingState) {
1963
+ if (state.isDragging) {
1967
1964
  finishMaintainScrollAtEnd(ctx);
1965
+ } else {
1966
+ const isStillWithinThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
1967
+ const didScrollSinceRequest = Platform.OS === "web" && state.scroll !== scrollAtRequest;
1968
+ if (isReplayingPendingMaintain || isStillWithinThreshold || !didScrollSinceRequest) {
1969
+ if (state.scrollingTo || state.pendingScrollResolve) {
1970
+ state.pendingMaintainScrollAtEnd = true;
1971
+ } else {
1972
+ state.maintainingScrollAtEnd = activeState;
1973
+ const scrollPromise = getScrollRequestTracker(ctx).runNowIfIdle(
1974
+ () => ctx.scrollToEnd({ animated: maintainScrollAtEnd.animated })
1975
+ );
1976
+ void scrollPromise.then(() => {
1977
+ if (state.maintainingScrollAtEnd === activeState) {
1978
+ if (state.pendingMaintainScrollAtEnd) {
1979
+ state.maintainingScrollAtEnd = void 0;
1980
+ doMaintainScrollAtEnd(ctx);
1981
+ } else {
1982
+ finishMaintainScrollAtEnd(ctx);
1983
+ }
1984
+ }
1985
+ });
1986
+ }
1987
+ } else if (state.maintainingScrollAtEnd === pendingState) {
1988
+ finishMaintainScrollAtEnd(ctx);
1989
+ }
1968
1990
  }
1969
- });
1970
- } else if (state.maintainingScrollAtEnd === pendingState) {
1971
- finishMaintainScrollAtEnd(ctx);
1991
+ }
1992
+ };
1993
+ if (immediate) {
1994
+ run();
1995
+ } else {
1996
+ requestAnimationFrame(run);
1972
1997
  }
1973
- });
1998
+ } else {
1999
+ state.pendingMaintainScrollAtEnd = true;
2000
+ }
2001
+ didMaintain = true;
1974
2002
  } else {
1975
- state.pendingMaintainScrollAtEnd = true;
2003
+ finishMaintainScrollAtEnd(ctx);
1976
2004
  }
1977
- return true;
1978
2005
  }
1979
- finishMaintainScrollAtEnd(ctx);
1980
- return false;
2006
+ return didMaintain;
1981
2007
  }
1982
2008
 
1983
2009
  // src/core/finishScrollTo.ts
@@ -2366,7 +2392,9 @@ function scrollTo(ctx, params) {
2366
2392
  pinScrollTargetRenderRange(ctx, targetOffset, scrollTarget.index);
2367
2393
  }
2368
2394
  }
2369
- state.scrollPending = targetOffset;
2395
+ if (!animated || isInitialScroll) {
2396
+ state.scrollPending = targetOffset;
2397
+ }
2370
2398
  syncInitialScrollNativeWatchdog(state, { isInitialScroll, requestedOffset: offset, targetOffset });
2371
2399
  if (!isInitialScroll && !noScrollingTo && Math.abs(state.scroll - targetOffset) > 1) {
2372
2400
  if (animated) {
@@ -2743,13 +2771,13 @@ function rearmBootstrapInitialScroll(ctx, options) {
2743
2771
  queueBootstrapInitialScrollReevaluation(ctx.state);
2744
2772
  }
2745
2773
  function createInitialScrollAtEndTarget(options) {
2746
- const { dataLength, footerSize, preserveForFooterLayout, stylePaddingEnd } = options;
2774
+ const { dataLength, footerSize, index, preserveForFooterLayout, stylePaddingEnd } = options;
2747
2775
  return {
2748
2776
  contentOffset: void 0,
2749
- index: Math.max(0, dataLength - 1),
2777
+ index: index != null ? index : Math.max(0, dataLength - 1),
2750
2778
  preserveForBottomPadding: true,
2751
2779
  preserveForFooterLayout,
2752
- viewOffset: -stylePaddingEnd - footerSize,
2780
+ viewOffset: getEndAlignedViewOffset(stylePaddingEnd, footerSize),
2753
2781
  viewPosition: 1
2754
2782
  };
2755
2783
  }
@@ -2765,13 +2793,14 @@ function isRetargetableBottomAlignedInitialScrollTarget(target) {
2765
2793
  function createRetargetedBottomAlignedInitialScroll(options) {
2766
2794
  const { dataLength, footerSize, initialScrollAtEnd, stylePaddingEnd, target } = options;
2767
2795
  const preserveForFooterLayout = shouldPreserveInitialScrollForFooterLayout(target);
2796
+ const alignsToContentEnd = initialScrollAtEnd || isEndOfContentTarget(target, dataLength);
2768
2797
  return {
2769
2798
  ...target,
2770
2799
  contentOffset: void 0,
2771
2800
  index: initialScrollAtEnd ? Math.max(0, dataLength - 1) : target.index,
2772
2801
  preserveForBottomPadding: true,
2773
2802
  preserveForFooterLayout,
2774
- viewOffset: -stylePaddingEnd - (preserveForFooterLayout ? footerSize : 0),
2803
+ viewOffset: getEndAlignedViewOffset(stylePaddingEnd, alignsToContentEnd ? footerSize : 0),
2775
2804
  viewPosition: 1
2776
2805
  };
2777
2806
  }
@@ -2785,7 +2814,7 @@ function clearPendingInitialScrollFooterLayout(ctx, options) {
2785
2814
  }
2786
2815
  const clearedFooterTarget = createInitialScrollAtEndTarget({
2787
2816
  dataLength,
2788
- footerSize: 0,
2817
+ footerSize: peek$(ctx, "footerSize") || 0,
2789
2818
  preserveForFooterLayout: void 0,
2790
2819
  stylePaddingEnd
2791
2820
  });
@@ -2808,13 +2837,12 @@ function getObservedBootstrapInitialScrollOffset(state) {
2808
2837
  return typeof observedOffset === "number" && Number.isFinite(observedOffset) ? observedOffset : (_d = (_c = state.scrollPending) != null ? _c : state.scroll) != null ? _d : 0;
2809
2838
  }
2810
2839
  function getPreservedEndAnchorOffsetDiff(ctx) {
2811
- var _a3;
2812
2840
  const state = ctx.state;
2813
2841
  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)) {
2842
+ if (!state.didFinishInitialScroll || state.scrollingTo || state.pendingScrollResolve || state.maintainingScrollAtEnd || state.isDragging || !initialScroll || initialScroll.viewPosition !== 1 || state.props.data.length === 0 || isOffsetInitialScrollSession(state)) {
2815
2843
  return;
2816
2844
  }
2817
- const currentOffset = typeof state.lastNativeScroll === "number" && Number.isFinite(state.lastNativeScroll) ? state.lastNativeScroll : getObservedBootstrapInitialScrollOffset(state);
2845
+ const currentOffset = Platform.OS !== "web" && typeof state.lastNativeScroll === "number" && Number.isFinite(state.lastNativeScroll) ? state.lastNativeScroll : getObservedBootstrapInitialScrollOffset(state);
2818
2846
  return resolveInitialScrollOffset(ctx, initialScroll) - currentOffset;
2819
2847
  }
2820
2848
  function schedulePreservedEndAnchorCorrection(ctx) {
@@ -2842,6 +2870,9 @@ function schedulePreservedEndAnchorCorrectionFrame(ctx, correction) {
2842
2870
  const hasObservedNativeScrollAfterRequest = !activeCorrection.lastRequestTime || ((_a3 = state.lastNativeScrollTime) != null ? _a3 : 0) > activeCorrection.lastRequestTime;
2843
2871
  if (hasObservedNativeScrollAfterRequest) {
2844
2872
  activeCorrection.lastRequestTime = Date.now();
2873
+ if (Platform.OS === "web") {
2874
+ state.scroll = getObservedBootstrapInitialScrollOffset(state);
2875
+ }
2845
2876
  requestAdjust(ctx, offsetDiff);
2846
2877
  }
2847
2878
  schedulePreservedEndAnchorCorrectionFrame(ctx, correction);
@@ -2964,10 +2995,10 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
2964
2995
  function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2965
2996
  const { dataLength, footerSize, initialScrollAtEnd, stylePaddingEnd } = options;
2966
2997
  const state = ctx.state;
2967
- if (!initialScrollAtEnd) {
2998
+ const initialScroll = state.initialScroll;
2999
+ if (!initialScrollAtEnd && !isEndOfContentTarget(initialScroll, dataLength)) {
2968
3000
  return;
2969
3001
  }
2970
- const initialScroll = state.initialScroll;
2971
3002
  if (isOffsetInitialScrollSession(state) || dataLength === 0 || !initialScroll) {
2972
3003
  return;
2973
3004
  }
@@ -2985,6 +3016,7 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2985
3016
  const updatedInitialScroll = createInitialScrollAtEndTarget({
2986
3017
  dataLength,
2987
3018
  footerSize,
3019
+ index: initialScrollAtEnd ? void 0 : initialScroll.index,
2988
3020
  preserveForFooterLayout: shouldPreserveInitialScrollForFooterLayout(initialScroll),
2989
3021
  stylePaddingEnd
2990
3022
  });
@@ -3944,7 +3976,7 @@ function getExpandedContainerPoolSize(dataLength, numContainers) {
3944
3976
  }
3945
3977
 
3946
3978
  // src/utils/findAvailableContainers.ts
3947
- function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffered, pendingRemoval, getRequiredItemType, protectedKeys) {
3979
+ function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffered, pendingRemoval, protectedKeys, reverseItemOrder = false) {
3948
3980
  var _a3;
3949
3981
  const numNeeded = needNewContainers.length;
3950
3982
  if (numNeeded === 0) {
@@ -3953,16 +3985,27 @@ function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffe
3953
3985
  const numContainers = peek$(ctx, "numContainers");
3954
3986
  const state = ctx.state;
3955
3987
  const { containerItemMetadata, stickyContainerPool } = state;
3988
+ const { data, getItemType } = state.props;
3989
+ const hasItemTypes = !!getItemType;
3956
3990
  const shouldAvoidAssignedContainerReuse = state.props.recycleItems && !!state.props.positionComponentInternal;
3957
3991
  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);
3992
+ const requests = new Array(numNeeded);
3993
+ const normalRequests = [];
3994
+ const stickyRequests = [];
3995
+ for (let order = 0; order < numNeeded; order++) {
3996
+ const itemIndex = needNewContainers[order];
3997
+ const itemType = getItemType == null ? void 0 : getItemType(data[itemIndex], itemIndex);
3998
+ const request = {
3999
+ isBuffered: itemIndex >= startBuffered && itemIndex <= endBuffered,
4000
+ isSticky: state.props.stickyHeaderIndicesSet.has(itemIndex),
4001
+ itemIndex,
4002
+ itemType: hasItemTypes ? itemType !== void 0 ? String(itemType) : "" : void 0,
4003
+ order,
4004
+ renderPriority: reverseItemOrder ? -itemIndex : itemIndex
4005
+ };
4006
+ requests[order] = request;
4007
+ (request.isSticky ? stickyRequests : normalRequests).push(request);
4008
+ }
3966
4009
  const normalCandidates = [];
3967
4010
  const stickyCandidates = [];
3968
4011
  for (let containerIndex = 0; containerIndex < numContainers; containerIndex++) {
@@ -4018,7 +4061,7 @@ function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffe
4018
4061
  }
4019
4062
  };
4020
4063
  const assignFromPool = (pendingRequests, candidates, allowCrossType) => {
4021
- if (getRequiredItemType) {
4064
+ if (hasItemTypes) {
4022
4065
  assignMatching(
4023
4066
  pendingRequests,
4024
4067
  candidates,
@@ -4042,6 +4085,11 @@ function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffe
4042
4085
  }
4043
4086
  assign(request, containerIndex);
4044
4087
  }
4088
+ if (normalRequests.length > 1 || stickyRequests.length > 1) {
4089
+ const containerIndices = [];
4090
+ reorderPoolAssignments(normalRequests, allocations, containerIndices, hasItemTypes);
4091
+ reorderPoolAssignments(stickyRequests, allocations, containerIndices, hasItemTypes);
4092
+ }
4045
4093
  if (pendingRemovalChanged) {
4046
4094
  pendingRemoval.length = 0;
4047
4095
  if (pendingRemovalSet) {
@@ -4071,12 +4119,53 @@ function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffe
4071
4119
  function comparatorByDistance(a, b) {
4072
4120
  return b.distance - a.distance;
4073
4121
  }
4122
+ function comparatorByRenderPriority(a, b) {
4123
+ return Number(b.isBuffered) - Number(a.isBuffered) || a.renderPriority - b.renderPriority;
4124
+ }
4125
+ function comparatorByIndex(a, b) {
4126
+ return a - b;
4127
+ }
4128
+ function reorderPoolAssignments(requests, allocations, containerIndices, groupByType) {
4129
+ if (requests.length >= 2) {
4130
+ if (groupByType) {
4131
+ const groups = /* @__PURE__ */ new Map();
4132
+ for (const request of requests) {
4133
+ const group = groups.get(request.itemType);
4134
+ if (group) {
4135
+ group.push(request);
4136
+ } else {
4137
+ groups.set(request.itemType, [request]);
4138
+ }
4139
+ }
4140
+ for (const group of groups.values()) {
4141
+ reorderPoolAssignments(group, allocations, containerIndices, false);
4142
+ }
4143
+ } else {
4144
+ containerIndices.length = 0;
4145
+ let indicesSorted = true;
4146
+ for (let i = 0; i < requests.length; i++) {
4147
+ const containerIndex = allocations[requests[i].order].containerIndex;
4148
+ if (i > 0 && containerIndex < containerIndices[i - 1]) {
4149
+ indicesSorted = false;
4150
+ }
4151
+ containerIndices.push(containerIndex);
4152
+ }
4153
+ if (!indicesSorted) {
4154
+ containerIndices.sort(comparatorByIndex);
4155
+ }
4156
+ requests.sort(comparatorByRenderPriority);
4157
+ for (let i = 0; i < requests.length; i++) {
4158
+ allocations[requests[i].order].containerIndex = containerIndices[i];
4159
+ }
4160
+ }
4161
+ }
4162
+ }
4074
4163
 
4075
4164
  // src/utils/setDidLayout.ts
4076
4165
  function setDidLayout(ctx) {
4077
4166
  const state = ctx.state;
4078
4167
  state.queuedInitialLayout = true;
4079
- checkAtBottom(ctx);
4168
+ checkThresholds(ctx);
4080
4169
  setInitialRenderState(ctx, { didLayout: true });
4081
4170
  if (state.pendingMaintainScrollAtEnd) {
4082
4171
  doMaintainScrollAtEnd(ctx);
@@ -4304,9 +4393,9 @@ function calculateItemsInView(ctx, params = {}) {
4304
4393
  enableScrollForNextCalculateItemsInView,
4305
4394
  idCache,
4306
4395
  indexByKey,
4307
- minIndexSizeChanged,
4396
+ positionRecalculationStartIndex,
4308
4397
  positions,
4309
- props: { alwaysRenderIndicesArr, alwaysRenderIndicesSet, getItemType, keyExtractor, onStickyHeaderChange },
4398
+ props: { alwaysRenderIndicesArr, alwaysRenderIndicesSet, keyExtractor, onStickyHeaderChange },
4310
4399
  scrollForNextCalculateItemsInView,
4311
4400
  scrollLength,
4312
4401
  sizes,
@@ -4422,21 +4511,21 @@ function calculateItemsInView(ctx, params = {}) {
4422
4511
  if (dataChanged) {
4423
4512
  resetLayoutCachesForDataChange(state);
4424
4513
  }
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;
4514
+ const startIndex = forceFullItemPositions || dataChanged ? 0 : (_e = positionRecalculationStartIndex != null ? positionRecalculationStartIndex : state.startBuffered) != null ? _e : 0;
4515
+ const optimizeForVisibleWindow = !forceFullItemPositions && !dataChanged && numColumns > 1 && positionRecalculationStartIndex !== void 0;
4427
4516
  updateItemPositions(ctx, dataChanged, {
4428
4517
  doMVCP,
4429
4518
  // A changed size shifts every following position. The scrolling early-exit
4430
4519
  // would leave the untouched suffix in the old coordinate space.
4431
- forceFullUpdate: !!forceFullItemPositions || minIndexSizeChanged !== void 0,
4520
+ forceFullUpdate: !!forceFullItemPositions || positionRecalculationStartIndex !== void 0,
4432
4521
  optimizeForVisibleWindow,
4433
4522
  scrollBottomBuffered,
4434
4523
  scrollVelocity: speed,
4435
4524
  startIndex
4436
4525
  });
4437
4526
  totalSize = getContentSize(ctx);
4438
- if (minIndexSizeChanged !== void 0) {
4439
- state.minIndexSizeChanged = void 0;
4527
+ if (positionRecalculationStartIndex !== void 0) {
4528
+ state.positionRecalculationStartIndex = void 0;
4440
4529
  }
4441
4530
  let protectedContainerKeys;
4442
4531
  if (dataChanged && doMVCP && state.props.maintainVisibleContentPosition.data && state.didContainersLayout && state.idsInView.length > 0) {
@@ -4612,18 +4701,18 @@ function calculateItemsInView(ctx, params = {}) {
4612
4701
  set$(ctx, "activeStickyIndex", -1);
4613
4702
  }
4614
4703
  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
4704
  const availableContainerAllocations = findAvailableContainers(
4620
4705
  ctx,
4621
4706
  needNewContainers,
4622
4707
  startBuffered,
4623
4708
  endBuffered,
4624
4709
  pendingRemoval,
4625
- getRequiredItemType,
4626
- protectedContainerKeys
4710
+ protectedContainerKeys,
4711
+ // Initial end alignment has no scroll velocity yet. Keep its
4712
+ // target-side rows first until the initial scroll settles.
4713
+ // After a pause velocity can be zero on the first movement;
4714
+ // use its displacement without treating initial insets as motion.
4715
+ hasActiveInitialScroll(state) ? (initialScroll == null ? void 0 : initialScroll.viewPosition) === 1 : (speed || (state.hasScrolled ? state.scroll - state.scrollPrev : 0)) < 0
4627
4716
  );
4628
4717
  for (const allocation of availableContainerAllocations) {
4629
4718
  const i = allocation.itemIndex;
@@ -4919,6 +5008,7 @@ function updateItemSizesBatch(ctx, measurements) {
4919
5008
  var _a3;
4920
5009
  const state = ctx.state;
4921
5010
  const result = {};
5011
+ const previousTotalSize = state.totalSize;
4922
5012
  for (const measurement of measurements) {
4923
5013
  const ownsMeasuredItem = measurement.containerId === void 0 || peek$(ctx, `containerItemKey${measurement.containerId}`) === measurement.itemKey;
4924
5014
  if (ownsMeasuredItem) {
@@ -4929,6 +5019,9 @@ function updateItemSizesBatch(ctx, measurements) {
4929
5019
  mergeItemSizeUpdateResult(result, nextResult);
4930
5020
  }
4931
5021
  }
5022
+ if (state.totalSize !== previousTotalSize) {
5023
+ set$(ctx, "totalSize", state.totalSize);
5024
+ }
4932
5025
  flushItemSizeUpdates(ctx, result);
4933
5026
  }
4934
5027
  function applyItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
@@ -4967,12 +5060,12 @@ function applyItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
4967
5060
  }
4968
5061
  let needsRecalculate = !didContainersLayout;
4969
5062
  let shouldMaintainScrollAtEnd = false;
4970
- let minIndexSizeChanged;
5063
+ let positionRecalculationStartIndex;
4971
5064
  const prevSizeKnown = state.sizesKnown.get(itemKey);
4972
- const diff = updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem);
5065
+ const diff = updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem, false);
4973
5066
  const size = roundSize(horizontal ? sizeObj.width : sizeObj.height);
4974
5067
  if (diff !== 0) {
4975
- minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
5068
+ positionRecalculationStartIndex = positionRecalculationStartIndex !== void 0 ? Math.min(positionRecalculationStartIndex, index) : index;
4976
5069
  const { startBuffered, endBuffered } = state;
4977
5070
  needsRecalculate || (needsRecalculate = index >= startBuffered && index <= endBuffered);
4978
5071
  if (!needsRecalculate && state.containerItemKeys.has(itemKey)) {
@@ -4989,8 +5082,8 @@ function applyItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
4989
5082
  size
4990
5083
  });
4991
5084
  }
4992
- if (minIndexSizeChanged !== void 0) {
4993
- state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
5085
+ if (positionRecalculationStartIndex !== void 0) {
5086
+ state.positionRecalculationStartIndex = state.positionRecalculationStartIndex !== void 0 ? Math.min(state.positionRecalculationStartIndex, positionRecalculationStartIndex) : positionRecalculationStartIndex;
4994
5087
  }
4995
5088
  updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
4996
5089
  if (didContainersLayout || checkAllSizesKnown(state, state.startBuffered, state.endBuffered)) {
@@ -5007,7 +5100,7 @@ function applyItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
5007
5100
  didMeasureUserScrollAnchorResetItem
5008
5101
  };
5009
5102
  }
5010
- function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
5103
+ function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem, notifyTotalSize = true) {
5011
5104
  var _a3, _b;
5012
5105
  const state = ctx.state;
5013
5106
  const {
@@ -5030,7 +5123,16 @@ function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
5030
5123
  fixedItemSize,
5031
5124
  itemType
5032
5125
  } : void 0;
5033
- const prevSize = getItemSize(ctx, itemKey, index, itemData, void 0, void 0, void 0, resolvedItemSize);
5126
+ const prevSize = getItemSize(
5127
+ ctx,
5128
+ itemKey,
5129
+ index,
5130
+ itemData,
5131
+ void 0,
5132
+ void 0,
5133
+ notifyTotalSize,
5134
+ resolvedItemSize
5135
+ );
5034
5136
  const rawSize = horizontal ? sizeObj.width : sizeObj.height;
5035
5137
  const prevSizeKnown = sizesKnown.get(itemKey);
5036
5138
  if (Platform.OS !== "web" && prevSizeKnown !== void 0 && isNativeLayoutNoise(rawSize - prevSizeKnown)) {
@@ -5055,7 +5157,7 @@ function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
5055
5157
  }
5056
5158
  }
5057
5159
  if (!prevSize || Math.abs(prevSize - size) > 0.1) {
5058
- setSize(ctx, itemKey, size);
5160
+ setSize(ctx, itemKey, size, notifyTotalSize);
5059
5161
  return size - prevSize;
5060
5162
  }
5061
5163
  return 0;
@@ -5206,10 +5308,16 @@ function useValue$(key, params) {
5206
5308
  var _a3;
5207
5309
  return (_a3 = getValue ? getValue(peek$(ctx, key)) : peek$(ctx, key)) != null ? _a3 : 0;
5208
5310
  };
5209
- const animValue = useAnimatedValue(getNewValue());
5311
+ const initialValue = getNewValue();
5312
+ const animValue = useAnimatedValue(initialValue);
5313
+ const lastValue = useRef(initialValue);
5210
5314
  useLayoutEffect(() => {
5211
5315
  const syncCurrentValue = () => {
5212
- animValue.setValue(getNewValue());
5316
+ const nextValue = getNewValue();
5317
+ if (lastValue.current !== nextValue) {
5318
+ lastValue.current = nextValue;
5319
+ animValue.setValue(nextValue);
5320
+ }
5213
5321
  };
5214
5322
  const unsubscribe = listen$(ctx, key, syncCurrentValue);
5215
5323
  syncCurrentValue();
@@ -6124,6 +6232,7 @@ var ListComponent = typedMemo(function ListComponent2({
6124
6232
  renderScrollComponent,
6125
6233
  onLayoutFooter,
6126
6234
  onInternalScrollBeginDrag,
6235
+ onInternalScrollEndDrag,
6127
6236
  onInternalScrollEnd,
6128
6237
  scrollAdjustHandler,
6129
6238
  snapToIndices,
@@ -6195,7 +6304,7 @@ var ListComponent = typedMemo(function ListComponent2({
6195
6304
  SnapOrScroll,
6196
6305
  {
6197
6306
  ...rest,
6198
- ...Platform.OS === "web" ? ScrollComponent === ListComponentScrollView ? { onInternalScrollEnd, useWindowScroll } : {} : { onScrollBeginDrag: onInternalScrollBeginDrag },
6307
+ ...Platform.OS === "web" ? ScrollComponent === ListComponentScrollView ? { onInternalScrollEnd, useWindowScroll } : {} : { onScrollBeginDrag: onInternalScrollBeginDrag, onScrollEndDrag: onInternalScrollEndDrag },
6199
6308
  contentContainerStyle: [
6200
6309
  horizontal ? { height: "100%" } : {},
6201
6310
  contentContainerStyle,
@@ -6314,7 +6423,7 @@ function checkResetContainers(ctx, dataProp, { didColumnsChange = false } = {})
6314
6423
  for (const key in state.averageSizes) {
6315
6424
  delete state.averageSizes[key];
6316
6425
  }
6317
- state.minIndexSizeChanged = 0;
6426
+ state.positionRecalculationStartIndex = 0;
6318
6427
  state.scrollForNextCalculateItemsInView = void 0;
6319
6428
  }
6320
6429
  calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
@@ -6478,7 +6587,7 @@ function handleLayout(ctx, layoutParam, setCanRender) {
6478
6587
  set$(ctx, "scrollSize", { height: layout.height, width: layout.width });
6479
6588
  }
6480
6589
  if (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onLayout) {
6481
- doMaintainScrollAtEnd(ctx);
6590
+ doMaintainScrollAtEnd(ctx, { immediate: true });
6482
6591
  }
6483
6592
  checkThresholds(ctx);
6484
6593
  if (state) {
@@ -6711,12 +6820,12 @@ function scrollToEnd(ctx, options) {
6711
6820
  if (index === -1) {
6712
6821
  return false;
6713
6822
  }
6714
- const paddingBottom = state.props.stylePaddingBottom || 0;
6823
+ const paddingBottom = getStylePaddingEnd(state.props);
6715
6824
  const footerSize = peek$(ctx, "footerSize") || 0;
6716
6825
  scrollToIndex(ctx, {
6717
6826
  ...options,
6718
6827
  index,
6719
- viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
6828
+ viewOffset: getEndAlignedViewOffset(paddingBottom, footerSize) + ((options == null ? void 0 : options.viewOffset) || 0),
6720
6829
  viewPosition: 1
6721
6830
  });
6722
6831
  if (state.scrollingTo) {
@@ -6919,7 +7028,7 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
6919
7028
  for (const key in state.averageSizes) {
6920
7029
  delete state.averageSizes[key];
6921
7030
  }
6922
- state.minIndexSizeChanged = 0;
7031
+ state.positionRecalculationStartIndex = 0;
6923
7032
  state.scrollForNextCalculateItemsInView = void 0;
6924
7033
  state.pendingTotalSize = void 0;
6925
7034
  state.totalSize = 0;
@@ -7313,10 +7422,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7313
7422
  onMetricsChange,
7314
7423
  onLayout: onLayoutProp,
7315
7424
  onLoad,
7425
+ onReady,
7316
7426
  onMomentumScrollEnd,
7317
7427
  onRefresh,
7318
7428
  onScroll: onScrollProp,
7319
7429
  onScrollBeginDrag,
7430
+ onScrollEndDrag,
7320
7431
  onStartReached,
7321
7432
  onStartReachedThreshold = 0.5,
7322
7433
  onStickyHeaderChange,
@@ -7385,17 +7496,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7385
7496
  const initialScrollProp = initialScrollAtEnd ? {
7386
7497
  index: Math.max(0, dataProp.length - 1),
7387
7498
  preserveForBottomPadding: true,
7388
- viewOffset: -stylePaddingEndState,
7499
+ viewOffset: getEndAlignedViewOffset(stylePaddingEndState),
7389
7500
  viewPosition: 1
7390
7501
  } : hasInitialScrollIndex ? typeof initialScrollIndexProp === "object" ? {
7391
7502
  index: (_b = initialScrollIndexProp.index) != null ? _b : 0,
7392
7503
  preserveForBottomPadding: initialScrollIndexProp.viewOffset === void 0 && initialScrollIndexProp.viewPosition === 1 ? true : void 0,
7393
- viewOffset: (_c = initialScrollIndexProp.viewOffset) != null ? _c : initialScrollIndexProp.viewPosition === 1 ? -stylePaddingEndState : 0,
7504
+ viewOffset: (_c = initialScrollIndexProp.viewOffset) != null ? _c : initialScrollIndexProp.viewPosition === 1 ? getEndAlignedViewOffset(stylePaddingEndState) : 0,
7394
7505
  viewPosition: (_d = initialScrollIndexProp.viewPosition) != null ? _d : 0
7395
7506
  } : {
7396
7507
  index: initialScrollIndexProp != null ? initialScrollIndexProp : 0,
7397
7508
  preserveForBottomPadding: shouldBottomAlignNumericInitialScrollIndex ? true : void 0,
7398
- viewOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : shouldBottomAlignNumericInitialScrollIndex ? -stylePaddingEndState : 0,
7509
+ viewOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : shouldBottomAlignNumericInitialScrollIndex ? getEndAlignedViewOffset(stylePaddingEndState) : 0,
7399
7510
  viewPosition: shouldBottomAlignNumericInitialScrollIndex ? 1 : void 0
7400
7511
  } : initialScrollUsesOffsetOnly ? {
7401
7512
  contentOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : 0,
@@ -7473,11 +7584,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7473
7584
  lastLayout: void 0,
7474
7585
  lastScrollDelta: 0,
7475
7586
  loadStartTime: Date.now(),
7476
- minIndexSizeChanged: 0,
7477
7587
  nativeContentInset: void 0,
7478
7588
  nativeMarginTop: 0,
7479
7589
  pendingDataComparison: void 0,
7480
7590
  pendingNativeMVCPAdjust: void 0,
7591
+ positionRecalculationStartIndex: 0,
7481
7592
  positions: [],
7482
7593
  props: {},
7483
7594
  queuedCalculateItemsInView: 0,
@@ -7580,8 +7691,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7580
7691
  onItemSizeChanged,
7581
7692
  onLoad,
7582
7693
  onMomentumScrollEnd,
7694
+ onReady,
7583
7695
  onScroll: throttleScrollFn,
7584
7696
  onScrollBeginDrag,
7697
+ onScrollEndDrag,
7585
7698
  onStartReached,
7586
7699
  onStartReachedThreshold,
7587
7700
  onStickyHeaderChange,
@@ -7877,11 +7990,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7877
7990
  onScroll: (event) => onScroll(ctx, event),
7878
7991
  onScrollBeginDrag: (event) => {
7879
7992
  var _a4, _b2;
7993
+ state.isDragging = true;
7880
7994
  interruptMaintainScrollAtEnd(ctx);
7881
7995
  prepareReachedEdgeForNextUserScroll(ctx);
7882
7996
  (_b2 = (_a4 = state.props).onScrollBeginDrag) == null ? void 0 : _b2.call(_a4, event);
7883
7997
  },
7884
- onScrollEnd: () => prepareReachedEdgeForNextUserScroll(ctx)
7998
+ onScrollEnd: () => prepareReachedEdgeForNextUserScroll(ctx),
7999
+ onScrollEndDrag: (event) => {
8000
+ var _a4, _b2;
8001
+ state.isDragging = false;
8002
+ (_b2 = (_a4 = state.props).onScrollEndDrag) == null ? void 0 : _b2.call(_a4, event);
8003
+ }
7885
8004
  }),
7886
8005
  []
7887
8006
  );
@@ -7905,6 +8024,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7905
8024
  ListHeaderComponent,
7906
8025
  onInternalScrollBeginDrag: fns.onScrollBeginDrag,
7907
8026
  onInternalScrollEnd: fns.onScrollEnd,
8027
+ onInternalScrollEndDrag: fns.onScrollEndDrag,
7908
8028
  onLayout,
7909
8029
  onLayoutFooter,
7910
8030
  onMomentumScrollEnd: fns.onMomentumScrollEnd,