@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.js CHANGED
@@ -458,6 +458,14 @@ var EDGE_POSITION_EPSILON = 1;
458
458
  var ENABLE_DEVMODE = IS_DEV && false;
459
459
  var ENABLE_DEBUG_VIEW = IS_DEV && false;
460
460
 
461
+ // src/core/endOfContentTarget.ts
462
+ function getEndAlignedViewOffset(stylePaddingEnd, footerSize = 0) {
463
+ return -stylePaddingEnd - footerSize;
464
+ }
465
+ function isEndOfContentTarget(target, dataLength) {
466
+ return !!target && dataLength > 0 && target.viewPosition === 1 && !!target.preserveForBottomPadding && target.index !== void 0 && target.index >= dataLength - 1;
467
+ }
468
+
461
469
  // src/core/cancelImperativeScroll.ts
462
470
  function cancelScrollCompletionChecks({ scheduledWork }) {
463
471
  scheduledWork.cancel("checkFinishedScrollFrame");
@@ -642,26 +650,25 @@ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, co
642
650
  scrollPosition: context.scrollPosition
643
651
  });
644
652
  };
653
+ let reached = !!wasReached;
645
654
  if (!wasReached) {
646
- if (!within) {
647
- return false;
648
- }
649
- onReached(distance);
650
- updateSnapshot();
651
- return true;
652
- }
653
- const reset = isOutsideThresholdHysteresis(distance, atThreshold, threshold);
654
- if (reset) {
655
- setSnapshot(void 0);
656
- return false;
657
- }
658
- if (within) {
659
- const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
660
- if (changed) {
655
+ if (within && onReached(distance)) {
661
656
  updateSnapshot();
657
+ reached = true;
658
+ }
659
+ } else {
660
+ const reset = isOutsideThresholdHysteresis(distance, atThreshold, threshold);
661
+ if (reset) {
662
+ setSnapshot(void 0);
663
+ reached = false;
664
+ } else if (within) {
665
+ const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
666
+ if (changed) {
667
+ updateSnapshot();
668
+ }
662
669
  }
663
670
  }
664
- return true;
671
+ return reached;
665
672
  };
666
673
 
667
674
  // src/utils/edgeReachedGate.ts
@@ -708,7 +715,6 @@ function beginReachedEdgeUserScroll(ctx, scrollDelta) {
708
715
  return void 0;
709
716
  }
710
717
  const allowedEdge = scrollDelta < 0 ? "start" : "end";
711
- state.edgeReachedGate = "closed";
712
718
  resetEdgeLatch(ctx, allowedEdge);
713
719
  return allowedEdge;
714
720
  }
@@ -749,7 +755,7 @@ function checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
749
755
  set$(
750
756
  ctx,
751
757
  "isWithinMaintainScrollAtEndThreshold",
752
- isContentLess || distanceFromEnd <= maintainScrollAtEndThreshold * scrollLength || state.pendingMaintainScrollAtEnd || maintainingScrollAtEnd === "animated" || maintainingScrollAtEnd === "instant"
758
+ isContentLess || distanceFromEnd <= maintainScrollAtEndThreshold * scrollLength || state.pendingMaintainScrollAtEnd || !!maintainingScrollAtEnd
753
759
  );
754
760
  const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || maintainingScrollAtEnd;
755
761
  if (!shouldSkipThresholdChecks) {
@@ -765,11 +771,13 @@ function checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
765
771
  scrollPosition: scroll
766
772
  },
767
773
  (distance) => {
768
- var _a4, _b;
769
- if (canDispatchReachedEdge(ctx, "end", allowedEdge, allowGateCreatedInCurrentCheck)) {
774
+ let dispatched = false;
775
+ if (state.props.onEndReached && canDispatchReachedEdge(ctx, "end", allowedEdge, allowGateCreatedInCurrentCheck)) {
770
776
  markReachedEdge(ctx);
771
- (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
777
+ state.props.onEndReached({ distanceFromEnd: distance });
778
+ dispatched = true;
772
779
  }
780
+ return dispatched;
773
781
  },
774
782
  (snapshot) => {
775
783
  state.endReachedSnapshot = snapshot;
@@ -802,7 +810,7 @@ function checkAtTop(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
802
810
  }
803
811
  set$(ctx, "isAtStart", scroll <= EDGE_POSITION_EPSILON);
804
812
  set$(ctx, "isNearStart", scroll <= threshold);
805
- const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || !!state.scrollingTo;
813
+ const shouldSkipThresholdChecks = !state.queuedInitialLayout || hasActiveInitialScroll(state) || !!state.scrollingTo;
806
814
  if (!shouldSkipThresholdChecks) {
807
815
  state.isStartReached = checkThreshold(
808
816
  scroll,
@@ -816,11 +824,13 @@ function checkAtTop(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
816
824
  scrollPosition: scroll
817
825
  },
818
826
  (distance) => {
819
- var _a3, _b;
820
- if (canDispatchReachedEdge(ctx, "start", allowedEdge, allowGateCreatedInCurrentCheck)) {
827
+ let dispatched = false;
828
+ if (state.props.onStartReached && canDispatchReachedEdge(ctx, "start", allowedEdge, allowGateCreatedInCurrentCheck)) {
821
829
  markReachedEdge(ctx);
822
- (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
830
+ state.props.onStartReached({ distanceFromStart: distance });
831
+ dispatched = true;
823
832
  }
833
+ return dispatched;
824
834
  },
825
835
  (snapshot) => {
826
836
  state.startReachedSnapshot = snapshot;
@@ -939,6 +949,11 @@ function resetInitialRenderState(ctx, {
939
949
  if (resetLayout) {
940
950
  state.didContainersLayout = false;
941
951
  state.queuedInitialLayout = false;
952
+ state.edgeReachedGate = void 0;
953
+ state.isStartReached = null;
954
+ state.isEndReached = null;
955
+ state.startReachedSnapshot = void 0;
956
+ state.endReachedSnapshot = void 0;
942
957
  }
943
958
  if (resetInitialScroll) {
944
959
  state.didFinishInitialScroll = false;
@@ -953,7 +968,7 @@ function setInitialRenderState(ctx, {
953
968
  const { state } = ctx;
954
969
  const {
955
970
  loadStartTime,
956
- props: { onLoad }
971
+ props: { onLoad, onReady }
957
972
  } = state;
958
973
  if (didLayout) {
959
974
  state.didContainersLayout = true;
@@ -974,6 +989,7 @@ function setInitialRenderState(ctx, {
974
989
  onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
975
990
  }
976
991
  }
992
+ onReady == null ? void 0 : onReady();
977
993
  }
978
994
  }
979
995
 
@@ -1319,10 +1335,6 @@ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1319
1335
  const itemSize = Math.max(0, measuredItemSize - (isOutOfBounds ? 0 : ctx.scrollAxisGap));
1320
1336
  const trailingInset = getContentInsetEnd(ctx);
1321
1337
  offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1322
- if (!isOutOfBounds && index === state.props.data.length - 1) {
1323
- const footerSize = peek$(ctx, "footerSize") || 0;
1324
- offset += footerSize;
1325
- }
1326
1338
  }
1327
1339
  return offset;
1328
1340
  }
@@ -1838,7 +1850,7 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
1838
1850
  state.scrollTime = currentTime;
1839
1851
  const scrollDelta = Math.abs(newScroll - prevScroll);
1840
1852
  const isUserScrollEvent = !!(options == null ? void 0 : options.fromNativeScrollEvent) && scrollDelta > 0.1 && !adjustChanged && scrollingTo === void 0 && !state.pendingNativeMVCPAdjust;
1841
- const didCancelMaintainScrollAtEnd = isUserScrollEvent && newScroll < prevScroll && !!(state.maintainingScrollAtEnd || state.pendingMaintainScrollAtEnd);
1853
+ const didCancelMaintainScrollAtEnd = Platform.OS === "web" && isUserScrollEvent && newScroll < prevScroll && !!(state.maintainingScrollAtEnd || state.pendingMaintainScrollAtEnd);
1842
1854
  if (didCancelMaintainScrollAtEnd) {
1843
1855
  finishMaintainScrollAtEnd(ctx);
1844
1856
  }
@@ -1916,89 +1928,103 @@ function finishMaintainScrollAtEnd(ctx) {
1916
1928
  requestAdjust(ctx, nextScroll - state.scroll);
1917
1929
  }
1918
1930
  }
1919
- function doMaintainScrollAtEnd(ctx) {
1920
- var _a3, _b, _c, _d, _e;
1931
+ function doMaintainScrollAtEnd(ctx, options) {
1932
+ var _a3, _b, _c, _d, _e, _f;
1921
1933
  const state = ctx.state;
1922
- const {
1923
- didContainersLayout,
1924
- didFinishInitialScroll,
1925
- pendingNativeMVCPAdjust,
1926
- props: { maintainScrollAtEnd }
1927
- } = state;
1928
- const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
1929
- const isReplayingPendingMaintain = !!state.pendingMaintainScrollAtEnd;
1930
- 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);
1931
- if (shouldMaintainScrollAtEnd && !state.scrollingTo) {
1932
- const scroller = state.refScroller.current;
1933
- const nativeMax = (_c = scroller == null ? void 0 : scroller.getMaxScrollOffset) == null ? void 0 : _c.call(scroller);
1934
- const observed = (_d = scroller == null ? void 0 : scroller.getCurrentScrollOffset) == null ? void 0 : _d.call(scroller);
1935
- if (nativeMax !== void 0 && observed !== void 0 && nativeMax < state.scroll - 1 && Math.abs(observed - nativeMax) <= 1) {
1936
- state.pendingMaintainScrollAtEnd = true;
1937
- state.scrollPending = observed;
1938
- updateScroll(ctx, observed, true, { markHasScrolled: false });
1934
+ let didMaintain = false;
1935
+ if (state.isDragging) {
1936
+ finishMaintainScrollAtEnd(ctx);
1937
+ } else {
1938
+ const {
1939
+ didContainersLayout,
1940
+ didFinishInitialScroll,
1941
+ pendingNativeMVCPAdjust,
1942
+ props: { maintainScrollAtEnd }
1943
+ } = state;
1944
+ const immediate = (options == null ? void 0 : options.immediate) && Platform.OS === "web" && !(maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.animated);
1945
+ const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
1946
+ const isReplayingPendingMaintain = !!state.pendingMaintainScrollAtEnd;
1947
+ 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);
1948
+ if (shouldMaintainScrollAtEnd && !state.scrollingTo) {
1949
+ const scroller = state.refScroller.current;
1950
+ const nativeMax = (_c = scroller == null ? void 0 : scroller.getMaxScrollOffset) == null ? void 0 : _c.call(scroller);
1951
+ const observed = (_d = scroller == null ? void 0 : scroller.getCurrentScrollOffset) == null ? void 0 : _d.call(scroller);
1952
+ if (nativeMax !== void 0 && observed !== void 0 && nativeMax < state.scroll - 1 && Math.abs(observed - nativeMax) <= 1) {
1953
+ state.pendingMaintainScrollAtEnd = true;
1954
+ state.scrollPending = observed;
1955
+ updateScroll(ctx, observed, true, { markHasScrolled: false });
1956
+ }
1939
1957
  }
1940
- }
1941
- if (shouldMaintainScrollAtEnd && !didContainersLayout) {
1942
- state.pendingMaintainScrollAtEnd = true;
1943
- return false;
1944
- }
1945
- if (pendingNativeMVCPAdjust) {
1946
- state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
1947
- return false;
1948
- }
1949
- if (shouldMaintainScrollAtEnd && (state.scrollingTo || state.pendingScrollResolve)) {
1950
- state.pendingMaintainScrollAtEnd = true;
1951
- (_e = state.maintainingScrollAtEnd) != null ? _e : state.maintainingScrollAtEnd = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
1952
- return false;
1953
- }
1954
- if (shouldMaintainScrollAtEnd && didContainersLayout) {
1955
- state.pendingMaintainScrollAtEnd = false;
1956
- const contentSize = getContentSize(ctx);
1957
- if (contentSize < state.scrollLength) {
1958
- state.scroll = 0;
1959
- }
1960
- if (!state.maintainingScrollAtEnd) {
1961
- const pendingState = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
1962
- const activeState = maintainScrollAtEnd.animated ? "animated" : "instant";
1963
- const scrollAtRequest = state.scroll;
1964
- state.maintainingScrollAtEnd = pendingState;
1965
- requestAnimationFrame(() => {
1966
- if (state.maintainingScrollAtEnd !== pendingState) {
1967
- return;
1968
- }
1969
- const isStillWithinThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
1970
- const didScrollSinceRequest = state.scroll !== scrollAtRequest;
1971
- if (isReplayingPendingMaintain || isStillWithinThreshold || !didScrollSinceRequest) {
1972
- if (state.scrollingTo || state.pendingScrollResolve) {
1973
- state.pendingMaintainScrollAtEnd = true;
1974
- return;
1975
- }
1976
- state.maintainingScrollAtEnd = activeState;
1977
- const scrollPromise = getScrollRequestTracker(ctx).runNowIfIdle(
1978
- () => ctx.scrollToEnd({ animated: maintainScrollAtEnd.animated })
1979
- );
1980
- void scrollPromise.then(() => {
1981
- if (state.maintainingScrollAtEnd !== activeState) {
1982
- return;
1983
- }
1984
- if (state.pendingMaintainScrollAtEnd) {
1985
- state.maintainingScrollAtEnd = void 0;
1986
- doMaintainScrollAtEnd(ctx);
1987
- } else {
1958
+ if (shouldMaintainScrollAtEnd && !didContainersLayout) {
1959
+ state.pendingMaintainScrollAtEnd = true;
1960
+ } else if (pendingNativeMVCPAdjust) {
1961
+ state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
1962
+ } else if (shouldMaintainScrollAtEnd && (state.scrollingTo || state.pendingScrollResolve)) {
1963
+ if (immediate && ((_e = state.scrollingTo) == null ? void 0 : _e.isScrollToEnd) && !state.scrollingTo.animated) {
1964
+ state.pendingMaintainScrollAtEnd = false;
1965
+ ctx.scrollToEnd({ animated: false });
1966
+ didMaintain = true;
1967
+ } else {
1968
+ state.pendingMaintainScrollAtEnd = true;
1969
+ (_f = state.maintainingScrollAtEnd) != null ? _f : state.maintainingScrollAtEnd = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
1970
+ }
1971
+ } else if (shouldMaintainScrollAtEnd && didContainersLayout) {
1972
+ state.pendingMaintainScrollAtEnd = false;
1973
+ const contentSize = getContentSize(ctx);
1974
+ if (contentSize < state.scrollLength) {
1975
+ state.scroll = 0;
1976
+ }
1977
+ if (!state.maintainingScrollAtEnd || immediate && state.maintainingScrollAtEnd === "pending-instant") {
1978
+ const pendingState = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
1979
+ const activeState = maintainScrollAtEnd.animated ? "animated" : "instant";
1980
+ const scrollAtRequest = state.scroll;
1981
+ state.maintainingScrollAtEnd = pendingState;
1982
+ const run = () => {
1983
+ if (state.maintainingScrollAtEnd === pendingState) {
1984
+ if (state.isDragging) {
1988
1985
  finishMaintainScrollAtEnd(ctx);
1986
+ } else {
1987
+ const isStillWithinThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
1988
+ const didScrollSinceRequest = Platform.OS === "web" && state.scroll !== scrollAtRequest;
1989
+ if (isReplayingPendingMaintain || isStillWithinThreshold || !didScrollSinceRequest) {
1990
+ if (state.scrollingTo || state.pendingScrollResolve) {
1991
+ state.pendingMaintainScrollAtEnd = true;
1992
+ } else {
1993
+ state.maintainingScrollAtEnd = activeState;
1994
+ const scrollPromise = getScrollRequestTracker(ctx).runNowIfIdle(
1995
+ () => ctx.scrollToEnd({ animated: maintainScrollAtEnd.animated })
1996
+ );
1997
+ void scrollPromise.then(() => {
1998
+ if (state.maintainingScrollAtEnd === activeState) {
1999
+ if (state.pendingMaintainScrollAtEnd) {
2000
+ state.maintainingScrollAtEnd = void 0;
2001
+ doMaintainScrollAtEnd(ctx);
2002
+ } else {
2003
+ finishMaintainScrollAtEnd(ctx);
2004
+ }
2005
+ }
2006
+ });
2007
+ }
2008
+ } else if (state.maintainingScrollAtEnd === pendingState) {
2009
+ finishMaintainScrollAtEnd(ctx);
2010
+ }
1989
2011
  }
1990
- });
1991
- } else if (state.maintainingScrollAtEnd === pendingState) {
1992
- finishMaintainScrollAtEnd(ctx);
2012
+ }
2013
+ };
2014
+ if (immediate) {
2015
+ run();
2016
+ } else {
2017
+ requestAnimationFrame(run);
1993
2018
  }
1994
- });
2019
+ } else {
2020
+ state.pendingMaintainScrollAtEnd = true;
2021
+ }
2022
+ didMaintain = true;
1995
2023
  } else {
1996
- state.pendingMaintainScrollAtEnd = true;
2024
+ finishMaintainScrollAtEnd(ctx);
1997
2025
  }
1998
- return true;
1999
2026
  }
2000
- finishMaintainScrollAtEnd(ctx);
2001
- return false;
2027
+ return didMaintain;
2002
2028
  }
2003
2029
 
2004
2030
  // src/core/finishScrollTo.ts
@@ -2387,7 +2413,9 @@ function scrollTo(ctx, params) {
2387
2413
  pinScrollTargetRenderRange(ctx, targetOffset, scrollTarget.index);
2388
2414
  }
2389
2415
  }
2390
- state.scrollPending = targetOffset;
2416
+ if (!animated || isInitialScroll) {
2417
+ state.scrollPending = targetOffset;
2418
+ }
2391
2419
  syncInitialScrollNativeWatchdog(state, { isInitialScroll, requestedOffset: offset, targetOffset });
2392
2420
  if (!isInitialScroll && !noScrollingTo && Math.abs(state.scroll - targetOffset) > 1) {
2393
2421
  if (animated) {
@@ -2764,13 +2792,13 @@ function rearmBootstrapInitialScroll(ctx, options) {
2764
2792
  queueBootstrapInitialScrollReevaluation(ctx.state);
2765
2793
  }
2766
2794
  function createInitialScrollAtEndTarget(options) {
2767
- const { dataLength, footerSize, preserveForFooterLayout, stylePaddingEnd } = options;
2795
+ const { dataLength, footerSize, index, preserveForFooterLayout, stylePaddingEnd } = options;
2768
2796
  return {
2769
2797
  contentOffset: void 0,
2770
- index: Math.max(0, dataLength - 1),
2798
+ index: index != null ? index : Math.max(0, dataLength - 1),
2771
2799
  preserveForBottomPadding: true,
2772
2800
  preserveForFooterLayout,
2773
- viewOffset: -stylePaddingEnd - footerSize,
2801
+ viewOffset: getEndAlignedViewOffset(stylePaddingEnd, footerSize),
2774
2802
  viewPosition: 1
2775
2803
  };
2776
2804
  }
@@ -2786,13 +2814,14 @@ function isRetargetableBottomAlignedInitialScrollTarget(target) {
2786
2814
  function createRetargetedBottomAlignedInitialScroll(options) {
2787
2815
  const { dataLength, footerSize, initialScrollAtEnd, stylePaddingEnd, target } = options;
2788
2816
  const preserveForFooterLayout = shouldPreserveInitialScrollForFooterLayout(target);
2817
+ const alignsToContentEnd = initialScrollAtEnd || isEndOfContentTarget(target, dataLength);
2789
2818
  return {
2790
2819
  ...target,
2791
2820
  contentOffset: void 0,
2792
2821
  index: initialScrollAtEnd ? Math.max(0, dataLength - 1) : target.index,
2793
2822
  preserveForBottomPadding: true,
2794
2823
  preserveForFooterLayout,
2795
- viewOffset: -stylePaddingEnd - (preserveForFooterLayout ? footerSize : 0),
2824
+ viewOffset: getEndAlignedViewOffset(stylePaddingEnd, alignsToContentEnd ? footerSize : 0),
2796
2825
  viewPosition: 1
2797
2826
  };
2798
2827
  }
@@ -2806,7 +2835,7 @@ function clearPendingInitialScrollFooterLayout(ctx, options) {
2806
2835
  }
2807
2836
  const clearedFooterTarget = createInitialScrollAtEndTarget({
2808
2837
  dataLength,
2809
- footerSize: 0,
2838
+ footerSize: peek$(ctx, "footerSize") || 0,
2810
2839
  preserveForFooterLayout: void 0,
2811
2840
  stylePaddingEnd
2812
2841
  });
@@ -2829,13 +2858,12 @@ function getObservedBootstrapInitialScrollOffset(state) {
2829
2858
  return typeof observedOffset === "number" && Number.isFinite(observedOffset) ? observedOffset : (_d = (_c = state.scrollPending) != null ? _c : state.scroll) != null ? _d : 0;
2830
2859
  }
2831
2860
  function getPreservedEndAnchorOffsetDiff(ctx) {
2832
- var _a3;
2833
2861
  const state = ctx.state;
2834
2862
  const initialScroll = state.initialScroll;
2835
- if (!state.didFinishInitialScroll || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) || !initialScroll || initialScroll.viewPosition !== 1 || state.props.data.length === 0 || isOffsetInitialScrollSession(state)) {
2863
+ if (!state.didFinishInitialScroll || state.scrollingTo || state.pendingScrollResolve || state.maintainingScrollAtEnd || state.isDragging || !initialScroll || initialScroll.viewPosition !== 1 || state.props.data.length === 0 || isOffsetInitialScrollSession(state)) {
2836
2864
  return;
2837
2865
  }
2838
- const currentOffset = typeof state.lastNativeScroll === "number" && Number.isFinite(state.lastNativeScroll) ? state.lastNativeScroll : getObservedBootstrapInitialScrollOffset(state);
2866
+ const currentOffset = Platform.OS !== "web" && typeof state.lastNativeScroll === "number" && Number.isFinite(state.lastNativeScroll) ? state.lastNativeScroll : getObservedBootstrapInitialScrollOffset(state);
2839
2867
  return resolveInitialScrollOffset(ctx, initialScroll) - currentOffset;
2840
2868
  }
2841
2869
  function schedulePreservedEndAnchorCorrection(ctx) {
@@ -2863,6 +2891,9 @@ function schedulePreservedEndAnchorCorrectionFrame(ctx, correction) {
2863
2891
  const hasObservedNativeScrollAfterRequest = !activeCorrection.lastRequestTime || ((_a3 = state.lastNativeScrollTime) != null ? _a3 : 0) > activeCorrection.lastRequestTime;
2864
2892
  if (hasObservedNativeScrollAfterRequest) {
2865
2893
  activeCorrection.lastRequestTime = Date.now();
2894
+ if (Platform.OS === "web") {
2895
+ state.scroll = getObservedBootstrapInitialScrollOffset(state);
2896
+ }
2866
2897
  requestAdjust(ctx, offsetDiff);
2867
2898
  }
2868
2899
  schedulePreservedEndAnchorCorrectionFrame(ctx, correction);
@@ -2985,10 +3016,10 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
2985
3016
  function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2986
3017
  const { dataLength, footerSize, initialScrollAtEnd, stylePaddingEnd } = options;
2987
3018
  const state = ctx.state;
2988
- if (!initialScrollAtEnd) {
3019
+ const initialScroll = state.initialScroll;
3020
+ if (!initialScrollAtEnd && !isEndOfContentTarget(initialScroll, dataLength)) {
2989
3021
  return;
2990
3022
  }
2991
- const initialScroll = state.initialScroll;
2992
3023
  if (isOffsetInitialScrollSession(state) || dataLength === 0 || !initialScroll) {
2993
3024
  return;
2994
3025
  }
@@ -3006,6 +3037,7 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
3006
3037
  const updatedInitialScroll = createInitialScrollAtEndTarget({
3007
3038
  dataLength,
3008
3039
  footerSize,
3040
+ index: initialScrollAtEnd ? void 0 : initialScroll.index,
3009
3041
  preserveForFooterLayout: shouldPreserveInitialScrollForFooterLayout(initialScroll),
3010
3042
  stylePaddingEnd
3011
3043
  });
@@ -3965,7 +3997,7 @@ function getExpandedContainerPoolSize(dataLength, numContainers) {
3965
3997
  }
3966
3998
 
3967
3999
  // src/utils/findAvailableContainers.ts
3968
- function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffered, pendingRemoval, getRequiredItemType, protectedKeys) {
4000
+ function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffered, pendingRemoval, protectedKeys, reverseItemOrder = false) {
3969
4001
  var _a3;
3970
4002
  const numNeeded = needNewContainers.length;
3971
4003
  if (numNeeded === 0) {
@@ -3974,16 +4006,27 @@ function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffe
3974
4006
  const numContainers = peek$(ctx, "numContainers");
3975
4007
  const state = ctx.state;
3976
4008
  const { containerItemMetadata, stickyContainerPool } = state;
4009
+ const { data, getItemType } = state.props;
4010
+ const hasItemTypes = !!getItemType;
3977
4011
  const shouldAvoidAssignedContainerReuse = state.props.recycleItems && !!state.props.positionComponentInternal;
3978
4012
  const pendingRemovalSet = pendingRemoval.length > 0 ? new Set(pendingRemoval) : void 0;
3979
- const requests = needNewContainers.map((itemIndex, order) => ({
3980
- isSticky: state.props.stickyHeaderIndicesSet.has(itemIndex),
3981
- itemIndex,
3982
- itemType: getRequiredItemType == null ? void 0 : getRequiredItemType(itemIndex),
3983
- order
3984
- }));
3985
- const normalRequests = requests.filter((request) => !request.isSticky);
3986
- const stickyRequests = requests.filter((request) => request.isSticky);
4013
+ const requests = new Array(numNeeded);
4014
+ const normalRequests = [];
4015
+ const stickyRequests = [];
4016
+ for (let order = 0; order < numNeeded; order++) {
4017
+ const itemIndex = needNewContainers[order];
4018
+ const itemType = getItemType == null ? void 0 : getItemType(data[itemIndex], itemIndex);
4019
+ const request = {
4020
+ isBuffered: itemIndex >= startBuffered && itemIndex <= endBuffered,
4021
+ isSticky: state.props.stickyHeaderIndicesSet.has(itemIndex),
4022
+ itemIndex,
4023
+ itemType: hasItemTypes ? itemType !== void 0 ? String(itemType) : "" : void 0,
4024
+ order,
4025
+ renderPriority: reverseItemOrder ? -itemIndex : itemIndex
4026
+ };
4027
+ requests[order] = request;
4028
+ (request.isSticky ? stickyRequests : normalRequests).push(request);
4029
+ }
3987
4030
  const normalCandidates = [];
3988
4031
  const stickyCandidates = [];
3989
4032
  for (let containerIndex = 0; containerIndex < numContainers; containerIndex++) {
@@ -4039,7 +4082,7 @@ function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffe
4039
4082
  }
4040
4083
  };
4041
4084
  const assignFromPool = (pendingRequests, candidates, allowCrossType) => {
4042
- if (getRequiredItemType) {
4085
+ if (hasItemTypes) {
4043
4086
  assignMatching(
4044
4087
  pendingRequests,
4045
4088
  candidates,
@@ -4063,6 +4106,11 @@ function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffe
4063
4106
  }
4064
4107
  assign(request, containerIndex);
4065
4108
  }
4109
+ if (normalRequests.length > 1 || stickyRequests.length > 1) {
4110
+ const containerIndices = [];
4111
+ reorderPoolAssignments(normalRequests, allocations, containerIndices, hasItemTypes);
4112
+ reorderPoolAssignments(stickyRequests, allocations, containerIndices, hasItemTypes);
4113
+ }
4066
4114
  if (pendingRemovalChanged) {
4067
4115
  pendingRemoval.length = 0;
4068
4116
  if (pendingRemovalSet) {
@@ -4092,12 +4140,53 @@ function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffe
4092
4140
  function comparatorByDistance(a, b) {
4093
4141
  return b.distance - a.distance;
4094
4142
  }
4143
+ function comparatorByRenderPriority(a, b) {
4144
+ return Number(b.isBuffered) - Number(a.isBuffered) || a.renderPriority - b.renderPriority;
4145
+ }
4146
+ function comparatorByIndex(a, b) {
4147
+ return a - b;
4148
+ }
4149
+ function reorderPoolAssignments(requests, allocations, containerIndices, groupByType) {
4150
+ if (requests.length >= 2) {
4151
+ if (groupByType) {
4152
+ const groups = /* @__PURE__ */ new Map();
4153
+ for (const request of requests) {
4154
+ const group = groups.get(request.itemType);
4155
+ if (group) {
4156
+ group.push(request);
4157
+ } else {
4158
+ groups.set(request.itemType, [request]);
4159
+ }
4160
+ }
4161
+ for (const group of groups.values()) {
4162
+ reorderPoolAssignments(group, allocations, containerIndices, false);
4163
+ }
4164
+ } else {
4165
+ containerIndices.length = 0;
4166
+ let indicesSorted = true;
4167
+ for (let i = 0; i < requests.length; i++) {
4168
+ const containerIndex = allocations[requests[i].order].containerIndex;
4169
+ if (i > 0 && containerIndex < containerIndices[i - 1]) {
4170
+ indicesSorted = false;
4171
+ }
4172
+ containerIndices.push(containerIndex);
4173
+ }
4174
+ if (!indicesSorted) {
4175
+ containerIndices.sort(comparatorByIndex);
4176
+ }
4177
+ requests.sort(comparatorByRenderPriority);
4178
+ for (let i = 0; i < requests.length; i++) {
4179
+ allocations[requests[i].order].containerIndex = containerIndices[i];
4180
+ }
4181
+ }
4182
+ }
4183
+ }
4095
4184
 
4096
4185
  // src/utils/setDidLayout.ts
4097
4186
  function setDidLayout(ctx) {
4098
4187
  const state = ctx.state;
4099
4188
  state.queuedInitialLayout = true;
4100
- checkAtBottom(ctx);
4189
+ checkThresholds(ctx);
4101
4190
  setInitialRenderState(ctx, { didLayout: true });
4102
4191
  if (state.pendingMaintainScrollAtEnd) {
4103
4192
  doMaintainScrollAtEnd(ctx);
@@ -4325,9 +4414,9 @@ function calculateItemsInView(ctx, params = {}) {
4325
4414
  enableScrollForNextCalculateItemsInView,
4326
4415
  idCache,
4327
4416
  indexByKey,
4328
- minIndexSizeChanged,
4417
+ positionRecalculationStartIndex,
4329
4418
  positions,
4330
- props: { alwaysRenderIndicesArr, alwaysRenderIndicesSet, getItemType, keyExtractor, onStickyHeaderChange },
4419
+ props: { alwaysRenderIndicesArr, alwaysRenderIndicesSet, keyExtractor, onStickyHeaderChange },
4331
4420
  scrollForNextCalculateItemsInView,
4332
4421
  scrollLength,
4333
4422
  sizes,
@@ -4443,21 +4532,21 @@ function calculateItemsInView(ctx, params = {}) {
4443
4532
  if (dataChanged) {
4444
4533
  resetLayoutCachesForDataChange(state);
4445
4534
  }
4446
- const startIndex = forceFullItemPositions || dataChanged ? 0 : (_e = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _e : 0;
4447
- const optimizeForVisibleWindow = !forceFullItemPositions && !dataChanged && numColumns > 1 && minIndexSizeChanged !== void 0;
4535
+ const startIndex = forceFullItemPositions || dataChanged ? 0 : (_e = positionRecalculationStartIndex != null ? positionRecalculationStartIndex : state.startBuffered) != null ? _e : 0;
4536
+ const optimizeForVisibleWindow = !forceFullItemPositions && !dataChanged && numColumns > 1 && positionRecalculationStartIndex !== void 0;
4448
4537
  updateItemPositions(ctx, dataChanged, {
4449
4538
  doMVCP,
4450
4539
  // A changed size shifts every following position. The scrolling early-exit
4451
4540
  // would leave the untouched suffix in the old coordinate space.
4452
- forceFullUpdate: !!forceFullItemPositions || minIndexSizeChanged !== void 0,
4541
+ forceFullUpdate: !!forceFullItemPositions || positionRecalculationStartIndex !== void 0,
4453
4542
  optimizeForVisibleWindow,
4454
4543
  scrollBottomBuffered,
4455
4544
  scrollVelocity: speed,
4456
4545
  startIndex
4457
4546
  });
4458
4547
  totalSize = getContentSize(ctx);
4459
- if (minIndexSizeChanged !== void 0) {
4460
- state.minIndexSizeChanged = void 0;
4548
+ if (positionRecalculationStartIndex !== void 0) {
4549
+ state.positionRecalculationStartIndex = void 0;
4461
4550
  }
4462
4551
  let protectedContainerKeys;
4463
4552
  if (dataChanged && doMVCP && state.props.maintainVisibleContentPosition.data && state.didContainersLayout && state.idsInView.length > 0) {
@@ -4633,18 +4722,18 @@ function calculateItemsInView(ctx, params = {}) {
4633
4722
  set$(ctx, "activeStickyIndex", -1);
4634
4723
  }
4635
4724
  if (needNewContainers.length > 0) {
4636
- const getRequiredItemType = getItemType ? (i) => {
4637
- const itemType = getItemType(data[i], i);
4638
- return itemType !== void 0 ? String(itemType) : "";
4639
- } : void 0;
4640
4725
  const availableContainerAllocations = findAvailableContainers(
4641
4726
  ctx,
4642
4727
  needNewContainers,
4643
4728
  startBuffered,
4644
4729
  endBuffered,
4645
4730
  pendingRemoval,
4646
- getRequiredItemType,
4647
- protectedContainerKeys
4731
+ protectedContainerKeys,
4732
+ // Initial end alignment has no scroll velocity yet. Keep its
4733
+ // target-side rows first until the initial scroll settles.
4734
+ // After a pause velocity can be zero on the first movement;
4735
+ // use its displacement without treating initial insets as motion.
4736
+ hasActiveInitialScroll(state) ? (initialScroll == null ? void 0 : initialScroll.viewPosition) === 1 : (speed || (state.hasScrolled ? state.scroll - state.scrollPrev : 0)) < 0
4648
4737
  );
4649
4738
  for (const allocation of availableContainerAllocations) {
4650
4739
  const i = allocation.itemIndex;
@@ -4940,6 +5029,7 @@ function updateItemSizesBatch(ctx, measurements) {
4940
5029
  var _a3;
4941
5030
  const state = ctx.state;
4942
5031
  const result = {};
5032
+ const previousTotalSize = state.totalSize;
4943
5033
  for (const measurement of measurements) {
4944
5034
  const ownsMeasuredItem = measurement.containerId === void 0 || peek$(ctx, `containerItemKey${measurement.containerId}`) === measurement.itemKey;
4945
5035
  if (ownsMeasuredItem) {
@@ -4950,6 +5040,9 @@ function updateItemSizesBatch(ctx, measurements) {
4950
5040
  mergeItemSizeUpdateResult(result, nextResult);
4951
5041
  }
4952
5042
  }
5043
+ if (state.totalSize !== previousTotalSize) {
5044
+ set$(ctx, "totalSize", state.totalSize);
5045
+ }
4953
5046
  flushItemSizeUpdates(ctx, result);
4954
5047
  }
4955
5048
  function applyItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
@@ -4988,12 +5081,12 @@ function applyItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
4988
5081
  }
4989
5082
  let needsRecalculate = !didContainersLayout;
4990
5083
  let shouldMaintainScrollAtEnd = false;
4991
- let minIndexSizeChanged;
5084
+ let positionRecalculationStartIndex;
4992
5085
  const prevSizeKnown = state.sizesKnown.get(itemKey);
4993
- const diff = updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem);
5086
+ const diff = updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem, false);
4994
5087
  const size = roundSize(horizontal ? sizeObj.width : sizeObj.height);
4995
5088
  if (diff !== 0) {
4996
- minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
5089
+ positionRecalculationStartIndex = positionRecalculationStartIndex !== void 0 ? Math.min(positionRecalculationStartIndex, index) : index;
4997
5090
  const { startBuffered, endBuffered } = state;
4998
5091
  needsRecalculate || (needsRecalculate = index >= startBuffered && index <= endBuffered);
4999
5092
  if (!needsRecalculate && state.containerItemKeys.has(itemKey)) {
@@ -5010,8 +5103,8 @@ function applyItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
5010
5103
  size
5011
5104
  });
5012
5105
  }
5013
- if (minIndexSizeChanged !== void 0) {
5014
- state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
5106
+ if (positionRecalculationStartIndex !== void 0) {
5107
+ state.positionRecalculationStartIndex = state.positionRecalculationStartIndex !== void 0 ? Math.min(state.positionRecalculationStartIndex, positionRecalculationStartIndex) : positionRecalculationStartIndex;
5015
5108
  }
5016
5109
  updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
5017
5110
  if (didContainersLayout || checkAllSizesKnown(state, state.startBuffered, state.endBuffered)) {
@@ -5028,7 +5121,7 @@ function applyItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
5028
5121
  didMeasureUserScrollAnchorResetItem
5029
5122
  };
5030
5123
  }
5031
- function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
5124
+ function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem, notifyTotalSize = true) {
5032
5125
  var _a3, _b;
5033
5126
  const state = ctx.state;
5034
5127
  const {
@@ -5051,7 +5144,16 @@ function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
5051
5144
  fixedItemSize,
5052
5145
  itemType
5053
5146
  } : void 0;
5054
- const prevSize = getItemSize(ctx, itemKey, index, itemData, void 0, void 0, void 0, resolvedItemSize);
5147
+ const prevSize = getItemSize(
5148
+ ctx,
5149
+ itemKey,
5150
+ index,
5151
+ itemData,
5152
+ void 0,
5153
+ void 0,
5154
+ notifyTotalSize,
5155
+ resolvedItemSize
5156
+ );
5055
5157
  const rawSize = horizontal ? sizeObj.width : sizeObj.height;
5056
5158
  const prevSizeKnown = sizesKnown.get(itemKey);
5057
5159
  if (Platform.OS !== "web" && prevSizeKnown !== void 0 && isNativeLayoutNoise(rawSize - prevSizeKnown)) {
@@ -5076,7 +5178,7 @@ function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
5076
5178
  }
5077
5179
  }
5078
5180
  if (!prevSize || Math.abs(prevSize - size) > 0.1) {
5079
- setSize(ctx, itemKey, size);
5181
+ setSize(ctx, itemKey, size, notifyTotalSize);
5080
5182
  return size - prevSize;
5081
5183
  }
5082
5184
  return 0;
@@ -5227,10 +5329,16 @@ function useValue$(key, params) {
5227
5329
  var _a3;
5228
5330
  return (_a3 = getValue ? getValue(peek$(ctx, key)) : peek$(ctx, key)) != null ? _a3 : 0;
5229
5331
  };
5230
- const animValue = useAnimatedValue(getNewValue());
5332
+ const initialValue = getNewValue();
5333
+ const animValue = useAnimatedValue(initialValue);
5334
+ const lastValue = React2.useRef(initialValue);
5231
5335
  React2.useLayoutEffect(() => {
5232
5336
  const syncCurrentValue = () => {
5233
- animValue.setValue(getNewValue());
5337
+ const nextValue = getNewValue();
5338
+ if (lastValue.current !== nextValue) {
5339
+ lastValue.current = nextValue;
5340
+ animValue.setValue(nextValue);
5341
+ }
5234
5342
  };
5235
5343
  const unsubscribe = listen$(ctx, key, syncCurrentValue);
5236
5344
  syncCurrentValue();
@@ -6145,6 +6253,7 @@ var ListComponent = typedMemo(function ListComponent2({
6145
6253
  renderScrollComponent,
6146
6254
  onLayoutFooter,
6147
6255
  onInternalScrollBeginDrag,
6256
+ onInternalScrollEndDrag,
6148
6257
  onInternalScrollEnd,
6149
6258
  scrollAdjustHandler,
6150
6259
  snapToIndices,
@@ -6216,7 +6325,7 @@ var ListComponent = typedMemo(function ListComponent2({
6216
6325
  SnapOrScroll,
6217
6326
  {
6218
6327
  ...rest,
6219
- ...Platform.OS === "web" ? ScrollComponent === ListComponentScrollView ? { onInternalScrollEnd, useWindowScroll } : {} : { onScrollBeginDrag: onInternalScrollBeginDrag },
6328
+ ...Platform.OS === "web" ? ScrollComponent === ListComponentScrollView ? { onInternalScrollEnd, useWindowScroll } : {} : { onScrollBeginDrag: onInternalScrollBeginDrag, onScrollEndDrag: onInternalScrollEndDrag },
6220
6329
  contentContainerStyle: [
6221
6330
  horizontal ? { height: "100%" } : {},
6222
6331
  contentContainerStyle,
@@ -6335,7 +6444,7 @@ function checkResetContainers(ctx, dataProp, { didColumnsChange = false } = {})
6335
6444
  for (const key in state.averageSizes) {
6336
6445
  delete state.averageSizes[key];
6337
6446
  }
6338
- state.minIndexSizeChanged = 0;
6447
+ state.positionRecalculationStartIndex = 0;
6339
6448
  state.scrollForNextCalculateItemsInView = void 0;
6340
6449
  }
6341
6450
  calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
@@ -6499,7 +6608,7 @@ function handleLayout(ctx, layoutParam, setCanRender) {
6499
6608
  set$(ctx, "scrollSize", { height: layout.height, width: layout.width });
6500
6609
  }
6501
6610
  if (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onLayout) {
6502
- doMaintainScrollAtEnd(ctx);
6611
+ doMaintainScrollAtEnd(ctx, { immediate: true });
6503
6612
  }
6504
6613
  checkThresholds(ctx);
6505
6614
  if (state) {
@@ -6732,12 +6841,12 @@ function scrollToEnd(ctx, options) {
6732
6841
  if (index === -1) {
6733
6842
  return false;
6734
6843
  }
6735
- const paddingBottom = state.props.stylePaddingBottom || 0;
6844
+ const paddingBottom = getStylePaddingEnd(state.props);
6736
6845
  const footerSize = peek$(ctx, "footerSize") || 0;
6737
6846
  scrollToIndex(ctx, {
6738
6847
  ...options,
6739
6848
  index,
6740
- viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
6849
+ viewOffset: getEndAlignedViewOffset(paddingBottom, footerSize) + ((options == null ? void 0 : options.viewOffset) || 0),
6741
6850
  viewPosition: 1
6742
6851
  });
6743
6852
  if (state.scrollingTo) {
@@ -6940,7 +7049,7 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
6940
7049
  for (const key in state.averageSizes) {
6941
7050
  delete state.averageSizes[key];
6942
7051
  }
6943
- state.minIndexSizeChanged = 0;
7052
+ state.positionRecalculationStartIndex = 0;
6944
7053
  state.scrollForNextCalculateItemsInView = void 0;
6945
7054
  state.pendingTotalSize = void 0;
6946
7055
  state.totalSize = 0;
@@ -7334,10 +7443,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7334
7443
  onMetricsChange,
7335
7444
  onLayout: onLayoutProp,
7336
7445
  onLoad,
7446
+ onReady,
7337
7447
  onMomentumScrollEnd,
7338
7448
  onRefresh,
7339
7449
  onScroll: onScrollProp,
7340
7450
  onScrollBeginDrag,
7451
+ onScrollEndDrag,
7341
7452
  onStartReached,
7342
7453
  onStartReachedThreshold = 0.5,
7343
7454
  onStickyHeaderChange,
@@ -7406,17 +7517,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7406
7517
  const initialScrollProp = initialScrollAtEnd ? {
7407
7518
  index: Math.max(0, dataProp.length - 1),
7408
7519
  preserveForBottomPadding: true,
7409
- viewOffset: -stylePaddingEndState,
7520
+ viewOffset: getEndAlignedViewOffset(stylePaddingEndState),
7410
7521
  viewPosition: 1
7411
7522
  } : hasInitialScrollIndex ? typeof initialScrollIndexProp === "object" ? {
7412
7523
  index: (_b = initialScrollIndexProp.index) != null ? _b : 0,
7413
7524
  preserveForBottomPadding: initialScrollIndexProp.viewOffset === void 0 && initialScrollIndexProp.viewPosition === 1 ? true : void 0,
7414
- viewOffset: (_c = initialScrollIndexProp.viewOffset) != null ? _c : initialScrollIndexProp.viewPosition === 1 ? -stylePaddingEndState : 0,
7525
+ viewOffset: (_c = initialScrollIndexProp.viewOffset) != null ? _c : initialScrollIndexProp.viewPosition === 1 ? getEndAlignedViewOffset(stylePaddingEndState) : 0,
7415
7526
  viewPosition: (_d = initialScrollIndexProp.viewPosition) != null ? _d : 0
7416
7527
  } : {
7417
7528
  index: initialScrollIndexProp != null ? initialScrollIndexProp : 0,
7418
7529
  preserveForBottomPadding: shouldBottomAlignNumericInitialScrollIndex ? true : void 0,
7419
- viewOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : shouldBottomAlignNumericInitialScrollIndex ? -stylePaddingEndState : 0,
7530
+ viewOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : shouldBottomAlignNumericInitialScrollIndex ? getEndAlignedViewOffset(stylePaddingEndState) : 0,
7420
7531
  viewPosition: shouldBottomAlignNumericInitialScrollIndex ? 1 : void 0
7421
7532
  } : initialScrollUsesOffsetOnly ? {
7422
7533
  contentOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : 0,
@@ -7494,11 +7605,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7494
7605
  lastLayout: void 0,
7495
7606
  lastScrollDelta: 0,
7496
7607
  loadStartTime: Date.now(),
7497
- minIndexSizeChanged: 0,
7498
7608
  nativeContentInset: void 0,
7499
7609
  nativeMarginTop: 0,
7500
7610
  pendingDataComparison: void 0,
7501
7611
  pendingNativeMVCPAdjust: void 0,
7612
+ positionRecalculationStartIndex: 0,
7502
7613
  positions: [],
7503
7614
  props: {},
7504
7615
  queuedCalculateItemsInView: 0,
@@ -7601,8 +7712,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7601
7712
  onItemSizeChanged,
7602
7713
  onLoad,
7603
7714
  onMomentumScrollEnd,
7715
+ onReady,
7604
7716
  onScroll: throttleScrollFn,
7605
7717
  onScrollBeginDrag,
7718
+ onScrollEndDrag,
7606
7719
  onStartReached,
7607
7720
  onStartReachedThreshold,
7608
7721
  onStickyHeaderChange,
@@ -7898,11 +8011,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7898
8011
  onScroll: (event) => onScroll(ctx, event),
7899
8012
  onScrollBeginDrag: (event) => {
7900
8013
  var _a4, _b2;
8014
+ state.isDragging = true;
7901
8015
  interruptMaintainScrollAtEnd(ctx);
7902
8016
  prepareReachedEdgeForNextUserScroll(ctx);
7903
8017
  (_b2 = (_a4 = state.props).onScrollBeginDrag) == null ? void 0 : _b2.call(_a4, event);
7904
8018
  },
7905
- onScrollEnd: () => prepareReachedEdgeForNextUserScroll(ctx)
8019
+ onScrollEnd: () => prepareReachedEdgeForNextUserScroll(ctx),
8020
+ onScrollEndDrag: (event) => {
8021
+ var _a4, _b2;
8022
+ state.isDragging = false;
8023
+ (_b2 = (_a4 = state.props).onScrollEndDrag) == null ? void 0 : _b2.call(_a4, event);
8024
+ }
7906
8025
  }),
7907
8026
  []
7908
8027
  );
@@ -7926,6 +8045,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7926
8045
  ListHeaderComponent,
7927
8046
  onInternalScrollBeginDrag: fns.onScrollBeginDrag,
7928
8047
  onInternalScrollEnd: fns.onScrollEnd,
8048
+ onInternalScrollEndDrag: fns.onScrollEndDrag,
7929
8049
  onLayout,
7930
8050
  onLayoutFooter,
7931
8051
  onMomentumScrollEnd: fns.onMomentumScrollEnd,