@legendapp/list 3.0.0 → 3.0.1

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.
@@ -2690,1383 +2690,1526 @@ function listenForScrollEnd(ctx, params) {
2690
2690
  }
2691
2691
  }
2692
2692
 
2693
- // src/core/scrollTo.ts
2694
- function getAverageSizeSnapshot(state) {
2695
- if (Object.keys(state.averageSizes).length === 0) {
2696
- return void 0;
2693
+ // src/core/doMaintainScrollAtEnd.ts
2694
+ function doMaintainScrollAtEnd(ctx) {
2695
+ const state = ctx.state;
2696
+ const {
2697
+ didContainersLayout,
2698
+ pendingNativeMVCPAdjust,
2699
+ refScroller,
2700
+ props: { maintainScrollAtEnd }
2701
+ } = state;
2702
+ const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
2703
+ const shouldMaintainScrollAtEnd = !!(isWithinMaintainScrollAtEndThreshold && maintainScrollAtEnd && didContainersLayout);
2704
+ if (pendingNativeMVCPAdjust) {
2705
+ state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
2706
+ return false;
2697
2707
  }
2698
- const snapshot = {};
2699
- for (const itemType in state.averageSizes) {
2700
- const averages = state.averageSizes[itemType];
2701
- snapshot[itemType] = averages.avg;
2708
+ state.pendingMaintainScrollAtEnd = false;
2709
+ if (shouldMaintainScrollAtEnd) {
2710
+ const contentSize = getContentSize(ctx);
2711
+ if (contentSize < state.scrollLength) {
2712
+ state.scroll = 0;
2713
+ }
2714
+ if (!state.maintainingScrollAtEnd) {
2715
+ state.maintainingScrollAtEnd = true;
2716
+ requestAnimationFrame(() => {
2717
+ if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
2718
+ const scroller = refScroller.current;
2719
+ if (state.props.horizontal && isHorizontalRTL(state)) {
2720
+ const currentContentSize = getContentSize(ctx);
2721
+ const logicalEndOffset = getLogicalHorizontalMaxOffset(state, currentContentSize);
2722
+ const nativeOffset = toNativeHorizontalOffset(state, logicalEndOffset, currentContentSize);
2723
+ scroller == null ? void 0 : scroller.scrollTo({
2724
+ animated: maintainScrollAtEnd.animated,
2725
+ x: nativeOffset,
2726
+ y: 0
2727
+ });
2728
+ } else {
2729
+ scroller == null ? void 0 : scroller.scrollToEnd({
2730
+ animated: maintainScrollAtEnd.animated
2731
+ });
2732
+ }
2733
+ setTimeout(
2734
+ () => {
2735
+ state.maintainingScrollAtEnd = false;
2736
+ },
2737
+ maintainScrollAtEnd.animated ? 500 : 0
2738
+ );
2739
+ } else {
2740
+ state.maintainingScrollAtEnd = false;
2741
+ }
2742
+ });
2743
+ }
2744
+ return true;
2702
2745
  }
2703
- return snapshot;
2746
+ return false;
2704
2747
  }
2705
- function syncInitialScrollNativeWatchdog(state, options) {
2706
- var _a3;
2707
- const { isInitialScroll, requestedOffset, targetOffset } = options;
2708
- const existingWatchdog = initialScrollWatchdog.get(state);
2709
- const shouldWatchInitialNativeScroll = !state.didFinishInitialScroll && (isInitialScroll || !!existingWatchdog) && initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset);
2710
- const shouldClearInitialNativeScrollWatchdog = !state.didFinishInitialScroll && !!existingWatchdog && initialScrollWatchdog.isAtZeroTargetOffset(requestedOffset);
2711
- if (shouldWatchInitialNativeScroll) {
2712
- state.hasScrolled = false;
2713
- initialScrollWatchdog.set(state, {
2714
- startScroll: (_a3 = existingWatchdog == null ? void 0 : existingWatchdog.startScroll) != null ? _a3 : state.scroll,
2715
- targetOffset
2716
- });
2717
- return;
2718
- }
2719
- if (shouldClearInitialNativeScrollWatchdog) {
2720
- initialScrollWatchdog.clear(state);
2748
+
2749
+ // src/utils/requestAdjust.ts
2750
+ function requestAdjust(ctx, positionDiff, dataChanged) {
2751
+ const state = ctx.state;
2752
+ if (Math.abs(positionDiff) > 0.1) {
2753
+ const doit = () => {
2754
+ {
2755
+ state.scrollAdjustHandler.requestAdjust(positionDiff);
2756
+ if (state.adjustingFromInitialMount) {
2757
+ state.adjustingFromInitialMount--;
2758
+ }
2759
+ }
2760
+ };
2761
+ state.scroll += positionDiff;
2762
+ state.scrollForNextCalculateItemsInView = void 0;
2763
+ const readyToRender = peek$(ctx, "readyToRender");
2764
+ if (readyToRender) {
2765
+ doit();
2766
+ } else {
2767
+ state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
2768
+ requestAnimationFrame(doit);
2769
+ }
2721
2770
  }
2722
2771
  }
2723
- function scrollTo(ctx, params) {
2724
- var _a3;
2725
- const state = ctx.state;
2726
- const { noScrollingTo, forceScroll, ...scrollTarget } = params;
2727
- const {
2728
- animated,
2729
- isInitialScroll,
2730
- offset: scrollTargetOffset,
2731
- precomputedWithViewOffset,
2732
- waitForInitialScrollCompletionFrame
2733
- } = scrollTarget;
2734
- const {
2735
- props: { horizontal }
2736
- } = state;
2737
- if (state.animFrameCheckFinishedScroll) {
2738
- cancelAnimationFrame(ctx.state.animFrameCheckFinishedScroll);
2772
+
2773
+ // src/core/mvcp.ts
2774
+ var MVCP_POSITION_EPSILON = 0.1;
2775
+ var MVCP_ANCHOR_LOCK_TTL_MS = 300;
2776
+ var MVCP_ANCHOR_LOCK_QUIET_PASSES_TO_RELEASE = 2;
2777
+ var NATIVE_END_CLAMP_EPSILON = 1;
2778
+ function resolveAnchorLock(state, enableMVCPAnchorLock, mvcpData, now) {
2779
+ if (!enableMVCPAnchorLock) {
2780
+ state.mvcpAnchorLock = void 0;
2781
+ return void 0;
2739
2782
  }
2740
- if (state.timeoutCheckFinishedScrollFallback) {
2741
- clearTimeout(ctx.state.timeoutCheckFinishedScrollFallback);
2783
+ const lock = state.mvcpAnchorLock;
2784
+ if (!lock) {
2785
+ return void 0;
2742
2786
  }
2743
- const requestedOffset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, scrollTargetOffset, scrollTarget);
2744
- const shouldPreserveRawInitialOffsetRequest = !!isInitialScroll && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
2745
- const targetOffset = clampScrollOffset(ctx, requestedOffset, scrollTarget);
2746
- const offset = shouldPreserveRawInitialOffsetRequest ? requestedOffset : targetOffset;
2747
- state.scrollHistory.length = 0;
2748
- if (!noScrollingTo) {
2749
- if (isInitialScroll) {
2750
- initialScrollCompletion.resetFlags(state);
2787
+ const isExpired = now > lock.expiresAt;
2788
+ const isMissing = state.indexByKey.get(lock.id) === void 0;
2789
+ if (isExpired || isMissing || !mvcpData) {
2790
+ state.mvcpAnchorLock = void 0;
2791
+ return void 0;
2792
+ }
2793
+ return lock;
2794
+ }
2795
+ function updateAnchorLock(state, params) {
2796
+ {
2797
+ const { anchorId, anchorPosition, dataChanged, now, positionDiff } = params;
2798
+ const enableMVCPAnchorLock = !!dataChanged || !!state.mvcpAnchorLock;
2799
+ const mvcpData = state.props.maintainVisibleContentPosition.data;
2800
+ if (!enableMVCPAnchorLock || !mvcpData || state.scrollingTo || !anchorId || anchorPosition === void 0) {
2801
+ return;
2751
2802
  }
2752
- const averageSizeSnapshot = getAverageSizeSnapshot(state);
2753
- state.scrollingTo = {
2754
- ...scrollTarget,
2755
- ...averageSizeSnapshot ? { averageSizeSnapshot } : {},
2756
- targetOffset,
2757
- waitForInitialScrollCompletionFrame
2803
+ const existingLock = state.mvcpAnchorLock;
2804
+ const quietPasses = !dataChanged && Math.abs(positionDiff) <= MVCP_POSITION_EPSILON && (existingLock == null ? void 0 : existingLock.id) === anchorId ? existingLock.quietPasses + 1 : 0;
2805
+ if (!dataChanged && quietPasses >= MVCP_ANCHOR_LOCK_QUIET_PASSES_TO_RELEASE) {
2806
+ state.mvcpAnchorLock = void 0;
2807
+ return;
2808
+ }
2809
+ state.mvcpAnchorLock = {
2810
+ expiresAt: now + MVCP_ANCHOR_LOCK_TTL_MS,
2811
+ id: anchorId,
2812
+ position: anchorPosition,
2813
+ quietPasses
2758
2814
  };
2759
2815
  }
2760
- state.scrollPending = targetOffset;
2761
- syncInitialScrollNativeWatchdog(state, { isInitialScroll, requestedOffset: offset, targetOffset });
2762
- if (forceScroll || !isInitialScroll || Platform.OS === "android") {
2763
- doScrollTo(ctx, { animated, horizontal, offset });
2764
- } else {
2765
- state.scroll = offset;
2816
+ }
2817
+ function shouldQueueNativeMVCPAdjust(dataChanged, state, positionDiff, prevTotalSize, prevScroll, scrollTarget) {
2818
+ {
2819
+ return false;
2766
2820
  }
2767
2821
  }
2768
-
2769
- // src/core/scrollToIndex.ts
2770
- function clampScrollIndex(index, dataLength) {
2771
- if (dataLength <= 0) {
2772
- return -1;
2822
+ function getPredictedNativeClamp(state, unresolvedAmount, totalSize) {
2823
+ if (Math.abs(unresolvedAmount) <= MVCP_POSITION_EPSILON) {
2824
+ return 0;
2773
2825
  }
2774
- if (index >= dataLength) {
2775
- return dataLength - 1;
2826
+ const maxScroll = Math.max(0, totalSize - state.scrollLength);
2827
+ const clampDelta = maxScroll - state.scroll;
2828
+ if (unresolvedAmount < 0) {
2829
+ return Math.max(unresolvedAmount, Math.min(0, clampDelta));
2776
2830
  }
2777
- if (index < 0) {
2778
- return 0;
2831
+ if (unresolvedAmount > 0) {
2832
+ return Math.min(unresolvedAmount, Math.max(0, clampDelta));
2779
2833
  }
2780
- return index;
2834
+ return 0;
2781
2835
  }
2782
- function scrollToIndex(ctx, {
2783
- index,
2784
- viewOffset = 0,
2785
- animated = true,
2786
- forceScroll,
2787
- isInitialScroll,
2788
- viewPosition
2789
- }) {
2836
+ function getProgressTowardAmount(targetDelta, nativeDelta) {
2837
+ return targetDelta < 0 ? -nativeDelta : nativeDelta;
2838
+ }
2839
+ function settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta) {
2790
2840
  const state = ctx.state;
2791
- const { data } = state.props;
2792
- index = clampScrollIndex(index, data.length);
2793
- const itemSize = getItemSizeAtIndex(ctx, index);
2794
- const firstIndexOffset = calculateOffsetForIndex(ctx, index);
2795
- const isLast = index === data.length - 1;
2796
- if (isLast && viewPosition === void 0) {
2797
- viewPosition = 1;
2841
+ state.pendingNativeMVCPAdjust = void 0;
2842
+ const remaining = remainingAfterManual - nativeDelta;
2843
+ if (Math.abs(remaining) > MVCP_POSITION_EPSILON) {
2844
+ requestAdjust(ctx, remaining);
2798
2845
  }
2799
- state.scrollForNextCalculateItemsInView = void 0;
2800
- scrollTo(ctx, {
2801
- animated,
2802
- forceScroll,
2803
- index,
2804
- isInitialScroll,
2805
- itemSize,
2806
- offset: firstIndexOffset,
2807
- viewOffset,
2808
- viewPosition: viewPosition != null ? viewPosition : 0
2809
- });
2810
- }
2811
-
2812
- // src/core/initialScroll.ts
2813
- function dispatchInitialScroll(ctx, params) {
2814
- const { forceScroll, resolvedOffset, target, waitForCompletionFrame } = params;
2815
- const requestedIndex = target.index;
2816
- const index = requestedIndex !== void 0 ? clampScrollIndex(requestedIndex, ctx.state.props.data.length) : void 0;
2817
- const itemSize = getItemSizeAtIndex(ctx, index);
2818
- scrollTo(ctx, {
2819
- animated: false,
2820
- forceScroll,
2821
- index: index !== void 0 && index >= 0 ? index : void 0,
2822
- isInitialScroll: true,
2823
- itemSize,
2824
- offset: resolvedOffset,
2825
- precomputedWithViewOffset: true,
2826
- viewOffset: target.viewOffset,
2827
- viewPosition: target.viewPosition,
2828
- waitForInitialScrollCompletionFrame: waitForCompletionFrame
2829
- });
2830
2846
  }
2831
- function setInitialScrollTarget(state, target, options) {
2832
- var _a3;
2833
- state.clearPreservedInitialScrollOnNextFinish = void 0;
2834
- if (state.timeoutPreservedInitialScrollClear !== void 0) {
2835
- clearTimeout(state.timeoutPreservedInitialScrollClear);
2836
- state.timeoutPreservedInitialScrollClear = void 0;
2837
- }
2838
- state.initialScroll = target;
2839
- if ((options == null ? void 0 : options.resetDidFinish) && state.didFinishInitialScroll) {
2840
- state.didFinishInitialScroll = false;
2841
- }
2842
- setInitialScrollSession(state, {
2843
- kind: ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" ? "offset" : "bootstrap"
2844
- });
2845
- }
2846
- function resolveInitialScrollOffset(ctx, initialScroll) {
2847
- var _a3, _b;
2847
+ function maybeApplyPredictedNativeMVCPAdjust(ctx) {
2848
2848
  const state = ctx.state;
2849
- if (((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
2850
- return (_b = initialScroll.contentOffset) != null ? _b : 0;
2849
+ const pending = state.pendingNativeMVCPAdjust;
2850
+ if (!pending || Math.abs(pending.manualApplied) > MVCP_POSITION_EPSILON) {
2851
+ return;
2851
2852
  }
2852
- const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, initialScroll.index) : 0;
2853
- const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, initialScroll);
2854
- return clampScrollOffset(ctx, resolvedOffset, initialScroll);
2855
- }
2856
- function getAdvanceableInitialScrollState(state, options) {
2857
- const { didFinishInitialScroll, queuedInitialLayout, scrollingTo } = state;
2858
- const initialScroll = state.initialScroll;
2859
- const isInitialScrollInProgress = !!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll);
2860
- const shouldWaitForInitialLayout = !!(options == null ? void 0 : options.requiresMeasuredLayout) && !queuedInitialLayout && !isInitialScrollInProgress;
2861
- if (!initialScroll || shouldWaitForInitialLayout || didFinishInitialScroll || scrollingTo && !isInitialScrollInProgress) {
2862
- return void 0;
2853
+ const totalSize = getContentSize(ctx);
2854
+ const predictedNativeClamp = getPredictedNativeClamp(state, pending.amount, totalSize);
2855
+ if (Math.abs(predictedNativeClamp) <= MVCP_POSITION_EPSILON) {
2856
+ return;
2863
2857
  }
2864
- return {
2865
- initialScroll,
2866
- isInitialScrollInProgress,
2867
- queuedInitialLayout,
2868
- scrollingTo
2869
- };
2858
+ const manualDesired = pending.amount - predictedNativeClamp;
2859
+ if (Math.abs(manualDesired) <= MVCP_POSITION_EPSILON) {
2860
+ return;
2861
+ }
2862
+ pending.manualApplied = manualDesired;
2863
+ requestAdjust(ctx, manualDesired);
2864
+ pending.furthestProgressTowardAmount = 0;
2870
2865
  }
2871
- function advanceMeasuredInitialScroll(ctx, options) {
2872
- var _a3, _b, _c;
2866
+ function resolvePendingNativeMVCPAdjust(ctx, newScroll) {
2873
2867
  const state = ctx.state;
2874
- const advanceableState = getAdvanceableInitialScrollState(state, {
2875
- requiresMeasuredLayout: true
2876
- });
2877
- if (!advanceableState) {
2868
+ const pending = state.pendingNativeMVCPAdjust;
2869
+ if (!pending) {
2878
2870
  return false;
2879
2871
  }
2880
- const { initialScroll, isInitialScrollInProgress, queuedInitialLayout } = advanceableState;
2881
- const scrollingTo = isInitialScrollInProgress ? advanceableState.scrollingTo : void 0;
2882
- const resolvedOffset = resolveInitialScrollOffset(ctx, initialScroll);
2883
- const activeInitialTargetOffset = scrollingTo ? (_a3 = scrollingTo.targetOffset) != null ? _a3 : scrollingTo.offset : void 0;
2884
- const didOffsetChange = initialScroll.contentOffset === void 0 || Math.abs(initialScroll.contentOffset - resolvedOffset) > 1;
2885
- const didActiveInitialTargetChange = activeInitialTargetOffset !== void 0 && Math.abs(activeInitialTargetOffset - resolvedOffset) > 1;
2886
- const isAlreadyAtDesiredInitialTarget = activeInitialTargetOffset !== void 0 && Math.abs(state.scroll - resolvedOffset) <= 1 && Math.abs(state.scrollPending - resolvedOffset) <= 1;
2887
- if (!(options == null ? void 0 : options.forceScroll) && !didOffsetChange && isInitialScrollInProgress && !didActiveInitialTargetChange) {
2888
- return false;
2872
+ const remainingAfterManual = pending.amount - pending.manualApplied;
2873
+ const nativeDelta = newScroll - (pending.startScroll + pending.manualApplied);
2874
+ const isWrongDirection = remainingAfterManual < 0 && nativeDelta > MVCP_POSITION_EPSILON || remainingAfterManual > 0 && nativeDelta < -MVCP_POSITION_EPSILON;
2875
+ const progressTowardAmount = getProgressTowardAmount(remainingAfterManual, nativeDelta);
2876
+ if (Math.abs(remainingAfterManual) <= MVCP_POSITION_EPSILON) {
2877
+ state.pendingNativeMVCPAdjust = void 0;
2878
+ return true;
2889
2879
  }
2890
- if ((options == null ? void 0 : options.forceScroll) && isAlreadyAtDesiredInitialTarget) {
2880
+ if (isWrongDirection) {
2881
+ state.pendingNativeMVCPAdjust = void 0;
2891
2882
  return false;
2892
2883
  }
2893
- if (didOffsetChange && ((_b = state.initialScrollSession) == null ? void 0 : _b.kind) !== "offset") {
2894
- setInitialScrollTarget(state, { ...initialScroll, contentOffset: resolvedOffset });
2884
+ if (progressTowardAmount + MVCP_POSITION_EPSILON >= Math.abs(remainingAfterManual)) {
2885
+ settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
2886
+ return true;
2895
2887
  }
2896
- const forceScroll = (_c = options == null ? void 0 : options.forceScroll) != null ? _c : !!queuedInitialLayout || isInitialScrollInProgress && didOffsetChange;
2897
- dispatchInitialScroll(ctx, {
2898
- forceScroll,
2899
- resolvedOffset,
2900
- target: initialScroll
2901
- });
2902
- return true;
2903
- }
2904
- function advanceOffsetInitialScroll(ctx, options) {
2905
- var _a3, _b;
2906
- const state = ctx.state;
2907
- const advanceableState = getAdvanceableInitialScrollState(state);
2908
- if (!advanceableState) {
2909
- return false;
2888
+ const expectedNativeClampScroll = Math.max(0, getContentSize(ctx) - state.scrollLength);
2889
+ const distanceToClamp = Math.abs(newScroll - expectedNativeClampScroll);
2890
+ const isAtExpectedNativeClamp = distanceToClamp <= NATIVE_END_CLAMP_EPSILON;
2891
+ if (isAtExpectedNativeClamp) {
2892
+ settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
2893
+ return true;
2910
2894
  }
2911
- const { initialScroll, queuedInitialLayout } = advanceableState;
2912
- const resolvedOffset = (_a3 = initialScroll.contentOffset) != null ? _a3 : 0;
2913
- const isAlreadyAtDesiredInitialTarget = Math.abs(state.scroll - resolvedOffset) <= 1 && Math.abs(state.scrollPending - resolvedOffset) <= 1;
2914
- if ((options == null ? void 0 : options.forceScroll) && isAlreadyAtDesiredInitialTarget) {
2895
+ if (state.pendingMaintainScrollAtEnd && peek$(ctx, "isWithinMaintainScrollAtEndThreshold") && progressTowardAmount > MVCP_POSITION_EPSILON) {
2896
+ settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
2897
+ return true;
2898
+ }
2899
+ if (progressTowardAmount > pending.furthestProgressTowardAmount + MVCP_POSITION_EPSILON) {
2900
+ pending.furthestProgressTowardAmount = progressTowardAmount;
2915
2901
  return false;
2916
2902
  }
2917
- const hasMeasuredScrollLayout = !!state.lastLayout && state.scrollLength > 0;
2918
- const forceScroll = (_b = options == null ? void 0 : options.forceScroll) != null ? _b : hasMeasuredScrollLayout || !!queuedInitialLayout;
2919
- dispatchInitialScroll(ctx, {
2920
- forceScroll,
2921
- resolvedOffset,
2922
- target: initialScroll
2923
- });
2924
- return true;
2925
- }
2926
- function advanceCurrentInitialScrollSession(ctx, options) {
2927
- var _a3;
2928
- return ((_a3 = ctx.state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" ? advanceOffsetInitialScroll(ctx, {
2929
- forceScroll: options == null ? void 0 : options.forceScroll
2930
- }) : advanceMeasuredInitialScroll(ctx, {
2931
- forceScroll: options == null ? void 0 : options.forceScroll
2932
- });
2933
- }
2934
-
2935
- // src/utils/checkAllSizesKnown.ts
2936
- function isNullOrUndefined2(value) {
2937
- return value === null || value === void 0;
2938
- }
2939
- function getMountedIndicesInRange(state, start, end) {
2940
- if (!isNullOrUndefined2(end) && !isNullOrUndefined2(start) && start >= 0 && end >= 0) {
2941
- return Array.from(state.containerItemKeys.keys()).map((key) => state.indexByKey.get(key)).filter((index) => index !== void 0 && index >= start && index <= end).sort((a, b) => a - b);
2903
+ if (pending.furthestProgressTowardAmount > MVCP_POSITION_EPSILON && progressTowardAmount < pending.furthestProgressTowardAmount - MVCP_POSITION_EPSILON) {
2904
+ state.pendingNativeMVCPAdjust = void 0;
2905
+ return false;
2942
2906
  }
2943
- return [];
2944
- }
2945
- function getMountedBufferedIndices(state) {
2946
- return getMountedIndicesInRange(state, state.startBuffered, state.endBuffered);
2947
- }
2948
- function getMountedNoBufferIndices(state) {
2949
- return getMountedIndicesInRange(state, state.startNoBuffer, state.endNoBuffer);
2950
- }
2951
- function checkAllSizesKnown(state, indices) {
2952
- return indices.length > 0 && indices.every((index) => {
2953
- const key = getId(state, index);
2954
- return key !== void 0 && state.sizesKnown.has(key);
2955
- });
2907
+ return false;
2956
2908
  }
2957
-
2958
- // src/utils/requestAdjust.ts
2959
- function requestAdjust(ctx, positionDiff, dataChanged) {
2909
+ function prepareMVCP(ctx, dataChanged) {
2960
2910
  const state = ctx.state;
2961
- if (Math.abs(positionDiff) > 0.1) {
2962
- const doit = () => {
2963
- {
2964
- state.scrollAdjustHandler.requestAdjust(positionDiff);
2965
- if (state.adjustingFromInitialMount) {
2966
- state.adjustingFromInitialMount--;
2911
+ const { idsInView, positions, props } = state;
2912
+ const {
2913
+ maintainVisibleContentPosition: { data: mvcpData, size: mvcpScroll, shouldRestorePosition }
2914
+ } = props;
2915
+ const now = Date.now();
2916
+ const enableMVCPAnchorLock = (!!dataChanged || !!state.mvcpAnchorLock);
2917
+ const scrollingTo = state.scrollingTo;
2918
+ const anchorLock = resolveAnchorLock(state, enableMVCPAnchorLock, mvcpData, now) ;
2919
+ let prevPosition;
2920
+ let targetId;
2921
+ const idsInViewWithPositions = [];
2922
+ const scrollTarget = scrollingTo == null ? void 0 : scrollingTo.index;
2923
+ const scrollingToViewPosition = scrollingTo == null ? void 0 : scrollingTo.viewPosition;
2924
+ const isEndAnchoredScrollTarget = scrollTarget !== void 0 && state.props.data.length > 0 && scrollTarget >= state.props.data.length - 1 && (scrollingToViewPosition != null ? scrollingToViewPosition : 0) > 0;
2925
+ const shouldMVCP = dataChanged ? mvcpData : mvcpScroll;
2926
+ const indexByKey = state.indexByKey;
2927
+ const prevScroll = state.scroll;
2928
+ getContentSize(ctx);
2929
+ if (shouldMVCP) {
2930
+ if (anchorLock && scrollTarget === void 0) {
2931
+ targetId = anchorLock.id;
2932
+ prevPosition = anchorLock.position;
2933
+ } else if (scrollTarget !== void 0) {
2934
+ targetId = getId(state, scrollTarget);
2935
+ } else if (idsInView.length > 0 && state.didContainersLayout && !dataChanged) {
2936
+ targetId = idsInView.find((id) => indexByKey.get(id) !== void 0);
2937
+ }
2938
+ if (dataChanged && idsInView.length > 0 && state.didContainersLayout) {
2939
+ for (let i = 0; i < idsInView.length; i++) {
2940
+ const id = idsInView[i];
2941
+ const index = indexByKey.get(id);
2942
+ if (index !== void 0) {
2943
+ const position = positions[index];
2944
+ if (position !== void 0) {
2945
+ idsInViewWithPositions.push({ id, position });
2946
+ }
2967
2947
  }
2968
2948
  }
2969
- };
2970
- state.scroll += positionDiff;
2971
- state.scrollForNextCalculateItemsInView = void 0;
2972
- const readyToRender = peek$(ctx, "readyToRender");
2973
- if (readyToRender) {
2974
- doit();
2975
- } else {
2976
- state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
2977
- requestAnimationFrame(doit);
2978
- }
2979
- }
2980
- }
2981
-
2982
- // src/core/bootstrapInitialScroll.ts
2983
- var DEFAULT_BOOTSTRAP_REVEAL_EPSILON = 1;
2984
- var DEFAULT_BOOTSTRAP_REVEAL_MAX_FRAMES = 8;
2985
- var DEFAULT_BOOTSTRAP_REVEAL_MAX_PASSES = 24;
2986
- var BOOTSTRAP_REVEAL_ABORT_WARNING = "LegendList bootstrap initial scroll aborted after exceeding convergence bounds.";
2987
- function getBootstrapInitialScrollSession(state) {
2988
- var _a3;
2989
- return ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" ? state.initialScrollSession.bootstrap : void 0;
2990
- }
2991
- function isOffsetInitialScrollSession(state) {
2992
- var _a3;
2993
- return ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
2994
- }
2995
- function doVisibleIndicesMatch(previous, next) {
2996
- if (!previous || previous.length !== next.length) {
2997
- return false;
2998
- }
2999
- for (let i = 0; i < previous.length; i++) {
3000
- if (previous[i] !== next[i]) {
3001
- return false;
3002
2949
  }
3003
- }
3004
- return true;
3005
- }
3006
- function getBootstrapRevealVisibleIndices(options) {
3007
- const { dataLength, getSize, offset, positions, scrollLength, startIndex: requestedStartIndex } = options;
3008
- const endOffset = offset + scrollLength;
3009
- const visibleIndices = [];
3010
- let index = requestedStartIndex !== void 0 ? Math.max(0, Math.min(dataLength - 1, requestedStartIndex)) : 0;
3011
- while (index > 0) {
3012
- const previousIndex = index - 1;
3013
- const previousPosition = positions[previousIndex];
3014
- if (previousPosition === void 0) {
3015
- index = previousIndex;
3016
- continue;
2950
+ if (targetId !== void 0 && prevPosition === void 0) {
2951
+ const targetIndex = indexByKey.get(targetId);
2952
+ if (targetIndex !== void 0) {
2953
+ prevPosition = positions[targetIndex];
2954
+ }
3017
2955
  }
3018
- const previousSize = getSize(previousIndex);
3019
- if (previousSize === void 0) {
3020
- index = previousIndex;
3021
- continue;
3022
- }
3023
- if (previousPosition + previousSize <= offset) {
3024
- break;
2956
+ return () => {
2957
+ let positionDiff = 0;
2958
+ let anchorIdForLock = anchorLock == null ? void 0 : anchorLock.id;
2959
+ let anchorPositionForLock;
2960
+ let skipTargetAnchor = false;
2961
+ const data = state.props.data;
2962
+ const shouldValidateLockedAnchor = dataChanged && mvcpData && scrollTarget === void 0 && targetId !== void 0 && (anchorLock == null ? void 0 : anchorLock.id) === targetId && shouldRestorePosition !== void 0;
2963
+ if (shouldValidateLockedAnchor && targetId !== void 0) {
2964
+ const index = indexByKey.get(targetId);
2965
+ if (index !== void 0) {
2966
+ const item = data[index];
2967
+ skipTargetAnchor = item === void 0 || !shouldRestorePosition(item, index, data);
2968
+ if (skipTargetAnchor && (anchorLock == null ? void 0 : anchorLock.id) === targetId) {
2969
+ state.mvcpAnchorLock = void 0;
2970
+ }
2971
+ }
2972
+ }
2973
+ const shouldUseFallbackVisibleAnchor = dataChanged && mvcpData && scrollTarget === void 0 && (() => {
2974
+ if (targetId === void 0 || skipTargetAnchor) {
2975
+ return true;
2976
+ }
2977
+ const targetIndex = indexByKey.get(targetId);
2978
+ return targetIndex === void 0 || positions[targetIndex] === void 0;
2979
+ })();
2980
+ if (shouldUseFallbackVisibleAnchor) {
2981
+ for (let i = 0; i < idsInViewWithPositions.length; i++) {
2982
+ const { id, position } = idsInViewWithPositions[i];
2983
+ const index = indexByKey.get(id);
2984
+ if (index !== void 0 && shouldRestorePosition) {
2985
+ const item = data[index];
2986
+ if (item === void 0 || !shouldRestorePosition(item, index, data)) {
2987
+ continue;
2988
+ }
2989
+ }
2990
+ const newPosition = index !== void 0 ? positions[index] : void 0;
2991
+ if (newPosition !== void 0) {
2992
+ positionDiff = newPosition - position;
2993
+ anchorIdForLock = id;
2994
+ anchorPositionForLock = newPosition;
2995
+ break;
2996
+ }
2997
+ }
2998
+ }
2999
+ if (!skipTargetAnchor && targetId !== void 0 && prevPosition !== void 0) {
3000
+ const targetIndex = indexByKey.get(targetId);
3001
+ const newPosition = targetIndex !== void 0 ? positions[targetIndex] : void 0;
3002
+ if (newPosition !== void 0) {
3003
+ const totalSize = getContentSize(ctx);
3004
+ let diff = newPosition - prevPosition;
3005
+ if (diff !== 0 && isEndAnchoredScrollTarget && state.scroll + state.scrollLength > totalSize) {
3006
+ if (diff > 0) {
3007
+ diff = Math.max(0, totalSize - state.scroll - state.scrollLength);
3008
+ } else {
3009
+ diff = 0;
3010
+ }
3011
+ }
3012
+ positionDiff = diff;
3013
+ anchorIdForLock = targetId;
3014
+ anchorPositionForLock = newPosition;
3015
+ }
3016
+ }
3017
+ if (scrollingToViewPosition && scrollingToViewPosition > 0) {
3018
+ const newSize = getItemSize(ctx, targetId, scrollTarget, state.props.data[scrollTarget]);
3019
+ const prevSize = scrollingTo == null ? void 0 : scrollingTo.itemSize;
3020
+ if (newSize !== void 0 && prevSize !== void 0 && newSize !== prevSize) {
3021
+ const diff = newSize - prevSize;
3022
+ if (diff !== 0) {
3023
+ positionDiff += diff * scrollingToViewPosition;
3024
+ scrollingTo.itemSize = newSize;
3025
+ }
3026
+ }
3027
+ }
3028
+ updateAnchorLock(state, {
3029
+ anchorId: anchorIdForLock,
3030
+ anchorPosition: anchorPositionForLock,
3031
+ dataChanged,
3032
+ now,
3033
+ positionDiff
3034
+ });
3035
+ if (shouldQueueNativeMVCPAdjust()) {
3036
+ state.pendingNativeMVCPAdjust = {
3037
+ amount: positionDiff,
3038
+ furthestProgressTowardAmount: 0,
3039
+ manualApplied: 0,
3040
+ startScroll: prevScroll
3041
+ };
3042
+ maybeApplyPredictedNativeMVCPAdjust(ctx);
3043
+ return;
3044
+ }
3045
+ if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
3046
+ const shouldSkipAdjustForMaintainedEnd = state.maintainingScrollAtEnd && peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
3047
+ if (!shouldSkipAdjustForMaintainedEnd) {
3048
+ requestAdjust(ctx, positionDiff);
3049
+ }
3050
+ }
3051
+ };
3052
+ }
3053
+ }
3054
+
3055
+ // src/core/updateScroll.ts
3056
+ function updateScroll(ctx, newScroll, forceUpdate, options) {
3057
+ var _a3;
3058
+ const state = ctx.state;
3059
+ const { ignoreScrollFromMVCP, lastScrollAdjustForHistory, scrollAdjustHandler, scrollHistory, scrollingTo } = state;
3060
+ const prevScroll = state.scroll;
3061
+ if ((options == null ? void 0 : options.markHasScrolled) !== false) {
3062
+ state.hasScrolled = true;
3063
+ }
3064
+ state.lastBatchingAction = Date.now();
3065
+ const currentTime = Date.now();
3066
+ const adjust = scrollAdjustHandler.getAdjust();
3067
+ const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
3068
+ if (adjustChanged) {
3069
+ scrollHistory.length = 0;
3070
+ }
3071
+ state.lastScrollAdjustForHistory = adjust;
3072
+ if (scrollingTo === void 0 && !(scrollHistory.length === 0 && newScroll === state.scroll)) {
3073
+ if (!adjustChanged) {
3074
+ scrollHistory.push({ scroll: newScroll, time: currentTime });
3025
3075
  }
3026
- index = previousIndex;
3027
3076
  }
3028
- for (; index < dataLength; index++) {
3029
- const position = positions[index];
3030
- if (position === void 0) {
3031
- continue;
3077
+ if (scrollHistory.length > 5) {
3078
+ scrollHistory.shift();
3079
+ }
3080
+ if (ignoreScrollFromMVCP && !scrollingTo) {
3081
+ const { lt, gt } = ignoreScrollFromMVCP;
3082
+ if (lt && newScroll < lt || gt && newScroll > gt) {
3083
+ state.ignoreScrollFromMVCPIgnored = true;
3084
+ return;
3032
3085
  }
3033
- const size = getSize(index);
3034
- if (size === void 0) {
3035
- continue;
3086
+ }
3087
+ state.scrollPrev = prevScroll;
3088
+ state.scrollPrevTime = state.scrollTime;
3089
+ state.scroll = newScroll;
3090
+ state.scrollTime = currentTime;
3091
+ const scrollDelta = Math.abs(newScroll - prevScroll);
3092
+ const didResolvePendingNativeMVCPAdjust = resolvePendingNativeMVCPAdjust(ctx, newScroll);
3093
+ const scrollLength = state.scrollLength;
3094
+ const lastCalculated = state.scrollLastCalculate;
3095
+ const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
3096
+ const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
3097
+ if (shouldUpdate) {
3098
+ state.scrollLastCalculate = state.scroll;
3099
+ state.ignoreScrollFromMVCPIgnored = false;
3100
+ state.lastScrollDelta = scrollDelta;
3101
+ const runCalculateItems = () => {
3102
+ var _a4;
3103
+ (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { doMVCP: scrollingTo !== void 0 });
3104
+ checkThresholds(ctx);
3105
+ };
3106
+ if (scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength && !state.pendingNativeMVCPAdjust) {
3107
+ state.mvcpAnchorLock = void 0;
3108
+ state.pendingNativeMVCPAdjust = void 0;
3109
+ state.userScrollAnchorResetKeys = /* @__PURE__ */ new Set();
3110
+ if (state.queuedMVCPRecalculate !== void 0) {
3111
+ cancelAnimationFrame(state.queuedMVCPRecalculate);
3112
+ state.queuedMVCPRecalculate = void 0;
3113
+ }
3114
+ ReactDOM.flushSync(runCalculateItems);
3115
+ } else {
3116
+ runCalculateItems();
3036
3117
  }
3037
- if (position < endOffset && position + size > offset) {
3038
- visibleIndices.push(index);
3039
- } else if (visibleIndices.length > 0 && position >= endOffset) {
3040
- break;
3118
+ const shouldMaintainScrollAtEndAfterPendingSettle = !!state.pendingMaintainScrollAtEnd || !!((_a3 = state.props.maintainScrollAtEnd) == null ? void 0 : _a3.onDataChange);
3119
+ if (didResolvePendingNativeMVCPAdjust && shouldMaintainScrollAtEndAfterPendingSettle) {
3120
+ state.pendingMaintainScrollAtEnd = false;
3121
+ doMaintainScrollAtEnd(ctx);
3041
3122
  }
3123
+ state.dataChangeNeedsScrollUpdate = false;
3124
+ state.lastScrollDelta = 0;
3042
3125
  }
3043
- return visibleIndices;
3044
3126
  }
3045
- function shouldAbortBootstrapReveal(options) {
3127
+
3128
+ // src/core/scrollTo.ts
3129
+ function getAverageSizeSnapshot(state) {
3130
+ if (Object.keys(state.averageSizes).length === 0) {
3131
+ return void 0;
3132
+ }
3133
+ const snapshot = {};
3134
+ for (const itemType in state.averageSizes) {
3135
+ const averages = state.averageSizes[itemType];
3136
+ snapshot[itemType] = averages.avg;
3137
+ }
3138
+ return snapshot;
3139
+ }
3140
+ function syncInitialScrollNativeWatchdog(state, options) {
3141
+ var _a3;
3142
+ const { isInitialScroll, requestedOffset, targetOffset } = options;
3143
+ const existingWatchdog = initialScrollWatchdog.get(state);
3144
+ const shouldWatchInitialNativeScroll = !state.didFinishInitialScroll && (isInitialScroll || !!existingWatchdog) && initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset);
3145
+ const shouldClearInitialNativeScrollWatchdog = !state.didFinishInitialScroll && !!existingWatchdog && initialScrollWatchdog.isAtZeroTargetOffset(requestedOffset);
3146
+ if (shouldWatchInitialNativeScroll) {
3147
+ state.hasScrolled = false;
3148
+ initialScrollWatchdog.set(state, {
3149
+ startScroll: (_a3 = existingWatchdog == null ? void 0 : existingWatchdog.startScroll) != null ? _a3 : state.scroll,
3150
+ targetOffset
3151
+ });
3152
+ return;
3153
+ }
3154
+ if (shouldClearInitialNativeScrollWatchdog) {
3155
+ initialScrollWatchdog.clear(state);
3156
+ }
3157
+ }
3158
+ function scrollTo(ctx, params) {
3159
+ var _a3;
3160
+ const state = ctx.state;
3161
+ const { noScrollingTo, forceScroll, ...scrollTarget } = params;
3046
3162
  const {
3047
- mountFrameCount,
3048
- maxFrames = DEFAULT_BOOTSTRAP_REVEAL_MAX_FRAMES,
3049
- maxPasses = DEFAULT_BOOTSTRAP_REVEAL_MAX_PASSES,
3050
- passCount
3051
- } = options;
3052
- return mountFrameCount >= maxFrames || passCount >= maxPasses;
3163
+ animated,
3164
+ isInitialScroll,
3165
+ offset: scrollTargetOffset,
3166
+ precomputedWithViewOffset,
3167
+ waitForInitialScrollCompletionFrame
3168
+ } = scrollTarget;
3169
+ const {
3170
+ props: { horizontal }
3171
+ } = state;
3172
+ if (state.animFrameCheckFinishedScroll) {
3173
+ cancelAnimationFrame(ctx.state.animFrameCheckFinishedScroll);
3174
+ }
3175
+ if (state.timeoutCheckFinishedScrollFallback) {
3176
+ clearTimeout(ctx.state.timeoutCheckFinishedScrollFallback);
3177
+ }
3178
+ const requestedOffset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, scrollTargetOffset, scrollTarget);
3179
+ const shouldPreserveRawInitialOffsetRequest = !!isInitialScroll && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
3180
+ const targetOffset = clampScrollOffset(ctx, requestedOffset, scrollTarget);
3181
+ const offset = shouldPreserveRawInitialOffsetRequest ? requestedOffset : targetOffset;
3182
+ state.scrollHistory.length = 0;
3183
+ if (!noScrollingTo) {
3184
+ if (isInitialScroll) {
3185
+ initialScrollCompletion.resetFlags(state);
3186
+ }
3187
+ const averageSizeSnapshot = getAverageSizeSnapshot(state);
3188
+ state.scrollingTo = {
3189
+ ...scrollTarget,
3190
+ ...averageSizeSnapshot ? { averageSizeSnapshot } : {},
3191
+ targetOffset,
3192
+ waitForInitialScrollCompletionFrame
3193
+ };
3194
+ }
3195
+ state.scrollPending = targetOffset;
3196
+ syncInitialScrollNativeWatchdog(state, { isInitialScroll, requestedOffset: offset, targetOffset });
3197
+ if (!animated && !isInitialScroll && !noScrollingTo && Math.abs(state.scroll - targetOffset) > 1) {
3198
+ updateScroll(ctx, targetOffset, false, { markHasScrolled: false });
3199
+ }
3200
+ if (forceScroll || !isInitialScroll || Platform.OS === "android") {
3201
+ doScrollTo(ctx, { animated, horizontal, offset });
3202
+ } else {
3203
+ state.scroll = offset;
3204
+ }
3053
3205
  }
3054
- function abortBootstrapRevealIfNeeded(ctx, options) {
3055
- if (!shouldAbortBootstrapReveal(options)) {
3056
- return false;
3206
+
3207
+ // src/core/scrollToIndex.ts
3208
+ function clampScrollIndex(index, dataLength) {
3209
+ if (dataLength <= 0) {
3210
+ return -1;
3057
3211
  }
3058
- if (IS_DEV) {
3059
- console.warn(BOOTSTRAP_REVEAL_ABORT_WARNING);
3212
+ if (index >= dataLength) {
3213
+ return dataLength - 1;
3060
3214
  }
3061
- abortBootstrapInitialScroll(ctx);
3062
- return true;
3215
+ if (index < 0) {
3216
+ return 0;
3217
+ }
3218
+ return index;
3063
3219
  }
3064
- function clearBootstrapInitialScrollSession(state) {
3220
+ function scrollToIndex(ctx, {
3221
+ index,
3222
+ viewOffset = 0,
3223
+ animated = true,
3224
+ forceScroll,
3225
+ isInitialScroll,
3226
+ viewPosition
3227
+ }) {
3228
+ const state = ctx.state;
3229
+ const { data } = state.props;
3230
+ index = clampScrollIndex(index, data.length);
3231
+ const itemSize = getItemSizeAtIndex(ctx, index);
3232
+ const firstIndexOffset = calculateOffsetForIndex(ctx, index);
3233
+ const isLast = index === data.length - 1;
3234
+ if (isLast && viewPosition === void 0) {
3235
+ viewPosition = 1;
3236
+ }
3237
+ state.scrollForNextCalculateItemsInView = void 0;
3238
+ scrollTo(ctx, {
3239
+ animated,
3240
+ forceScroll,
3241
+ index,
3242
+ isInitialScroll,
3243
+ itemSize,
3244
+ offset: firstIndexOffset,
3245
+ viewOffset,
3246
+ viewPosition: viewPosition != null ? viewPosition : 0
3247
+ });
3248
+ }
3249
+
3250
+ // src/core/initialScroll.ts
3251
+ function dispatchInitialScroll(ctx, params) {
3252
+ const { forceScroll, resolvedOffset, target, waitForCompletionFrame } = params;
3253
+ const requestedIndex = target.index;
3254
+ const index = requestedIndex !== void 0 ? clampScrollIndex(requestedIndex, ctx.state.props.data.length) : void 0;
3255
+ const itemSize = getItemSizeAtIndex(ctx, index);
3256
+ scrollTo(ctx, {
3257
+ animated: false,
3258
+ forceScroll,
3259
+ index: index !== void 0 && index >= 0 ? index : void 0,
3260
+ isInitialScroll: true,
3261
+ itemSize,
3262
+ offset: resolvedOffset,
3263
+ precomputedWithViewOffset: true,
3264
+ viewOffset: target.viewOffset,
3265
+ viewPosition: target.viewPosition,
3266
+ waitForInitialScrollCompletionFrame: waitForCompletionFrame
3267
+ });
3268
+ }
3269
+ function setInitialScrollTarget(state, target, options) {
3065
3270
  var _a3;
3066
- const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
3067
- const frameHandle = bootstrapInitialScroll == null ? void 0 : bootstrapInitialScroll.frameHandle;
3068
- if (frameHandle !== void 0 && typeof cancelAnimationFrame === "function") {
3069
- cancelAnimationFrame(frameHandle);
3271
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
3272
+ if (state.timeoutPreservedInitialScrollClear !== void 0) {
3273
+ clearTimeout(state.timeoutPreservedInitialScrollClear);
3274
+ state.timeoutPreservedInitialScrollClear = void 0;
3070
3275
  }
3071
- if (bootstrapInitialScroll) {
3072
- bootstrapInitialScroll.frameHandle = void 0;
3276
+ state.initialScroll = target;
3277
+ if ((options == null ? void 0 : options.resetDidFinish) && state.didFinishInitialScroll) {
3278
+ state.didFinishInitialScroll = false;
3073
3279
  }
3074
3280
  setInitialScrollSession(state, {
3075
- bootstrap: null,
3076
- kind: (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind
3281
+ kind: ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" ? "offset" : "bootstrap"
3077
3282
  });
3078
3283
  }
3079
- function startBootstrapInitialScrollSession(state, options) {
3080
- var _a3, _b, _c;
3081
- const previousBootstrapInitialScroll = getBootstrapInitialScrollSession(state);
3082
- setInitialScrollSession(state, {
3083
- bootstrap: {
3084
- frameHandle: previousBootstrapInitialScroll == null ? void 0 : previousBootstrapInitialScroll.frameHandle,
3085
- // Re-arming during the initial mount should spend from the same watchdog budget.
3086
- mountFrameCount: (_a3 = previousBootstrapInitialScroll == null ? void 0 : previousBootstrapInitialScroll.mountFrameCount) != null ? _a3 : 0,
3087
- passCount: 0,
3088
- previousResolvedOffset: void 0,
3089
- scroll: options.scroll,
3090
- seedContentOffset: (_c = (_b = options.seedContentOffset) != null ? _b : previousBootstrapInitialScroll == null ? void 0 : previousBootstrapInitialScroll.seedContentOffset) != null ? _c : options.scroll,
3091
- targetIndexSeed: options.targetIndexSeed,
3092
- visibleIndices: void 0
3093
- },
3094
- kind: "bootstrap"
3095
- });
3284
+ function resolveInitialScrollOffset(ctx, initialScroll) {
3285
+ var _a3, _b;
3286
+ const state = ctx.state;
3287
+ if (((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
3288
+ return (_b = initialScroll.contentOffset) != null ? _b : 0;
3289
+ }
3290
+ const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, initialScroll.index) : 0;
3291
+ const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, initialScroll);
3292
+ return clampScrollOffset(ctx, resolvedOffset, initialScroll);
3096
3293
  }
3097
- function resetBootstrapInitialScrollSession(state, options) {
3098
- var _a3, _b, _c;
3099
- const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
3100
- if (!bootstrapInitialScroll) {
3101
- if ((options == null ? void 0 : options.scroll) !== void 0) {
3102
- startBootstrapInitialScrollSession(state, {
3103
- scroll: options.scroll,
3104
- seedContentOffset: options.seedContentOffset,
3105
- targetIndexSeed: options.targetIndexSeed
3106
- });
3107
- }
3108
- } else {
3109
- bootstrapInitialScroll.passCount = 0;
3110
- bootstrapInitialScroll.previousResolvedOffset = void 0;
3111
- bootstrapInitialScroll.scroll = (_a3 = options == null ? void 0 : options.scroll) != null ? _a3 : bootstrapInitialScroll.scroll;
3112
- bootstrapInitialScroll.seedContentOffset = (_b = options == null ? void 0 : options.seedContentOffset) != null ? _b : bootstrapInitialScroll.seedContentOffset;
3113
- bootstrapInitialScroll.targetIndexSeed = (_c = options == null ? void 0 : options.targetIndexSeed) != null ? _c : bootstrapInitialScroll.targetIndexSeed;
3114
- bootstrapInitialScroll.visibleIndices = void 0;
3115
- setInitialScrollSession(state, {
3116
- bootstrap: bootstrapInitialScroll,
3117
- kind: "bootstrap"
3118
- });
3294
+ function getAdvanceableInitialScrollState(state, options) {
3295
+ const { didFinishInitialScroll, queuedInitialLayout, scrollingTo } = state;
3296
+ const initialScroll = state.initialScroll;
3297
+ const isInitialScrollInProgress = !!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll);
3298
+ const shouldWaitForInitialLayout = !!(options == null ? void 0 : options.requiresMeasuredLayout) && !queuedInitialLayout && !isInitialScrollInProgress;
3299
+ if (!initialScroll || shouldWaitForInitialLayout || didFinishInitialScroll || scrollingTo && !isInitialScrollInProgress) {
3300
+ return void 0;
3119
3301
  }
3302
+ return {
3303
+ initialScroll,
3304
+ isInitialScrollInProgress,
3305
+ queuedInitialLayout,
3306
+ scrollingTo
3307
+ };
3120
3308
  }
3121
- function queueBootstrapInitialScrollReevaluation(state) {
3122
- requestAnimationFrame(() => {
3123
- var _a3;
3124
- if (getBootstrapInitialScrollSession(state)) {
3125
- (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { forceFullItemPositions: true });
3126
- }
3309
+ function advanceMeasuredInitialScroll(ctx, options) {
3310
+ var _a3, _b, _c;
3311
+ const state = ctx.state;
3312
+ const advanceableState = getAdvanceableInitialScrollState(state, {
3313
+ requiresMeasuredLayout: true
3314
+ });
3315
+ if (!advanceableState) {
3316
+ return false;
3317
+ }
3318
+ const { initialScroll, isInitialScrollInProgress, queuedInitialLayout } = advanceableState;
3319
+ const scrollingTo = isInitialScrollInProgress ? advanceableState.scrollingTo : void 0;
3320
+ const resolvedOffset = resolveInitialScrollOffset(ctx, initialScroll);
3321
+ const activeInitialTargetOffset = scrollingTo ? (_a3 = scrollingTo.targetOffset) != null ? _a3 : scrollingTo.offset : void 0;
3322
+ const didOffsetChange = initialScroll.contentOffset === void 0 || Math.abs(initialScroll.contentOffset - resolvedOffset) > 1;
3323
+ const didActiveInitialTargetChange = activeInitialTargetOffset !== void 0 && Math.abs(activeInitialTargetOffset - resolvedOffset) > 1;
3324
+ const isAlreadyAtDesiredInitialTarget = activeInitialTargetOffset !== void 0 && Math.abs(state.scroll - resolvedOffset) <= 1 && Math.abs(state.scrollPending - resolvedOffset) <= 1;
3325
+ if (!(options == null ? void 0 : options.forceScroll) && !didOffsetChange && isInitialScrollInProgress && !didActiveInitialTargetChange) {
3326
+ return false;
3327
+ }
3328
+ if ((options == null ? void 0 : options.forceScroll) && isAlreadyAtDesiredInitialTarget) {
3329
+ return false;
3330
+ }
3331
+ if (didOffsetChange && ((_b = state.initialScrollSession) == null ? void 0 : _b.kind) !== "offset") {
3332
+ setInitialScrollTarget(state, { ...initialScroll, contentOffset: resolvedOffset });
3333
+ }
3334
+ const forceScroll = (_c = options == null ? void 0 : options.forceScroll) != null ? _c : !!queuedInitialLayout || isInitialScrollInProgress && didOffsetChange;
3335
+ dispatchInitialScroll(ctx, {
3336
+ forceScroll,
3337
+ resolvedOffset,
3338
+ target: initialScroll
3127
3339
  });
3340
+ return true;
3128
3341
  }
3129
- function ensureBootstrapInitialScrollFrameTicker(ctx) {
3342
+ function advanceOffsetInitialScroll(ctx, options) {
3343
+ var _a3, _b;
3130
3344
  const state = ctx.state;
3131
- const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
3132
- if (!bootstrapInitialScroll || bootstrapInitialScroll.frameHandle !== void 0) {
3133
- return;
3345
+ const advanceableState = getAdvanceableInitialScrollState(state);
3346
+ if (!advanceableState) {
3347
+ return false;
3134
3348
  }
3135
- const tick = () => {
3136
- const activeBootstrapInitialScroll = getBootstrapInitialScrollSession(state);
3137
- if (!activeBootstrapInitialScroll) {
3138
- return;
3139
- }
3140
- activeBootstrapInitialScroll.frameHandle = void 0;
3141
- activeBootstrapInitialScroll.mountFrameCount += 1;
3142
- if (abortBootstrapRevealIfNeeded(ctx, {
3143
- mountFrameCount: activeBootstrapInitialScroll.mountFrameCount,
3144
- passCount: activeBootstrapInitialScroll.passCount
3145
- })) {
3146
- return;
3147
- }
3148
- ensureBootstrapInitialScrollFrameTicker(ctx);
3149
- };
3150
- bootstrapInitialScroll.frameHandle = requestAnimationFrame(tick);
3151
- }
3152
- function rearmBootstrapInitialScroll(ctx, options) {
3153
- resetBootstrapInitialScrollSession(ctx.state, options);
3154
- ensureBootstrapInitialScrollFrameTicker(ctx);
3155
- queueBootstrapInitialScrollReevaluation(ctx.state);
3156
- }
3157
- function createInitialScrollAtEndTarget(options) {
3158
- const { dataLength, footerSize, preserveForFooterLayout, stylePaddingBottom } = options;
3159
- return {
3160
- contentOffset: void 0,
3161
- index: Math.max(0, dataLength - 1),
3162
- preserveForBottomPadding: true,
3163
- preserveForFooterLayout,
3164
- viewOffset: -stylePaddingBottom - footerSize,
3165
- viewPosition: 1
3166
- };
3349
+ const { initialScroll, queuedInitialLayout } = advanceableState;
3350
+ const resolvedOffset = (_a3 = initialScroll.contentOffset) != null ? _a3 : 0;
3351
+ const isAlreadyAtDesiredInitialTarget = Math.abs(state.scroll - resolvedOffset) <= 1 && Math.abs(state.scrollPending - resolvedOffset) <= 1;
3352
+ if ((options == null ? void 0 : options.forceScroll) && isAlreadyAtDesiredInitialTarget) {
3353
+ return false;
3354
+ }
3355
+ const hasMeasuredScrollLayout = !!state.lastLayout && state.scrollLength > 0;
3356
+ const forceScroll = (_b = options == null ? void 0 : options.forceScroll) != null ? _b : hasMeasuredScrollLayout || !!queuedInitialLayout;
3357
+ dispatchInitialScroll(ctx, {
3358
+ forceScroll,
3359
+ resolvedOffset,
3360
+ target: initialScroll
3361
+ });
3362
+ return true;
3167
3363
  }
3168
- function shouldPreserveInitialScrollForBottomPadding(target) {
3169
- return !!(target == null ? void 0 : target.preserveForBottomPadding);
3364
+ function advanceCurrentInitialScrollSession(ctx, options) {
3365
+ var _a3;
3366
+ return ((_a3 = ctx.state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" ? advanceOffsetInitialScroll(ctx, {
3367
+ forceScroll: options == null ? void 0 : options.forceScroll
3368
+ }) : advanceMeasuredInitialScroll(ctx, {
3369
+ forceScroll: options == null ? void 0 : options.forceScroll
3370
+ });
3170
3371
  }
3171
- function shouldPreserveInitialScrollForFooterLayout(target) {
3172
- return !!(target == null ? void 0 : target.preserveForFooterLayout);
3372
+
3373
+ // src/utils/checkAllSizesKnown.ts
3374
+ function isNullOrUndefined2(value) {
3375
+ return value === null || value === void 0;
3173
3376
  }
3174
- function isRetargetableBottomAlignedInitialScrollTarget(target) {
3175
- return !!(target && target.viewPosition === 1 && (shouldPreserveInitialScrollForBottomPadding(target) || shouldPreserveInitialScrollForFooterLayout(target)));
3377
+ function getMountedIndicesInRange(state, start, end) {
3378
+ if (!isNullOrUndefined2(end) && !isNullOrUndefined2(start) && start >= 0 && end >= 0) {
3379
+ return Array.from(state.containerItemKeys.keys()).map((key) => state.indexByKey.get(key)).filter((index) => index !== void 0 && index >= start && index <= end).sort((a, b) => a - b);
3380
+ }
3381
+ return [];
3176
3382
  }
3177
- function createRetargetedBottomAlignedInitialScroll(options) {
3178
- const { dataLength, footerSize, initialScrollAtEnd, stylePaddingBottom, target } = options;
3179
- const preserveForFooterLayout = shouldPreserveInitialScrollForFooterLayout(target);
3180
- return {
3181
- ...target,
3182
- contentOffset: void 0,
3183
- index: initialScrollAtEnd ? Math.max(0, dataLength - 1) : target.index,
3184
- preserveForBottomPadding: true,
3185
- preserveForFooterLayout,
3186
- viewOffset: -stylePaddingBottom - (preserveForFooterLayout ? footerSize : 0),
3187
- viewPosition: 1
3188
- };
3383
+ function getMountedBufferedIndices(state) {
3384
+ return getMountedIndicesInRange(state, state.startBuffered, state.endBuffered);
3189
3385
  }
3190
- function areEquivalentBootstrapInitialScrollTargets(current, next) {
3191
- return current.index === next.index && current.preserveForBottomPadding === next.preserveForBottomPadding && current.preserveForFooterLayout === next.preserveForFooterLayout && current.viewOffset === next.viewOffset && current.viewPosition === next.viewPosition;
3386
+ function getMountedNoBufferIndices(state) {
3387
+ return getMountedIndicesInRange(state, state.startNoBuffer, state.endNoBuffer);
3192
3388
  }
3193
- function clearPendingInitialScrollFooterLayout(ctx, options) {
3194
- const { dataLength, stylePaddingBottom, target } = options;
3195
- const state = ctx.state;
3196
- if (!shouldPreserveInitialScrollForFooterLayout(target)) {
3197
- return;
3198
- }
3199
- const clearedFooterTarget = createInitialScrollAtEndTarget({
3200
- dataLength,
3201
- footerSize: 0,
3202
- preserveForFooterLayout: void 0,
3203
- stylePaddingBottom
3389
+ function checkAllSizesKnown(state, indices) {
3390
+ return indices.length > 0 && indices.every((index) => {
3391
+ const key = getId(state, index);
3392
+ return key !== void 0 && state.sizesKnown.has(key);
3204
3393
  });
3205
- setInitialScrollTarget(state, clearedFooterTarget);
3206
- }
3207
- function clearFinishedViewportRetargetableInitialScroll(state) {
3208
- clearPreservedInitialScrollTarget(state);
3209
- }
3210
- function didFinishedInitialScrollMoveAwayFromTarget(ctx, target, epsilon = DEFAULT_BOOTSTRAP_REVEAL_EPSILON) {
3211
- const state = ctx.state;
3212
- if (!state.didFinishInitialScroll) {
3213
- return false;
3214
- }
3215
- const currentOffset = getObservedBootstrapInitialScrollOffset(state);
3216
- return Math.abs(currentOffset - resolveInitialScrollOffset(ctx, target)) > epsilon;
3217
3394
  }
3218
- function getObservedBootstrapInitialScrollOffset(state) {
3219
- var _a3, _b, _c, _d;
3220
- const observedOffset = (_b = (_a3 = state.refScroller.current) == null ? void 0 : _a3.getCurrentScrollOffset) == null ? void 0 : _b.call(_a3);
3221
- return typeof observedOffset === "number" && Number.isFinite(observedOffset) ? observedOffset : (_d = (_c = state.scrollPending) != null ? _c : state.scroll) != null ? _d : 0;
3395
+
3396
+ // src/core/bootstrapInitialScroll.ts
3397
+ var DEFAULT_BOOTSTRAP_REVEAL_EPSILON = 1;
3398
+ var DEFAULT_BOOTSTRAP_REVEAL_MAX_FRAMES = 8;
3399
+ var DEFAULT_BOOTSTRAP_REVEAL_MAX_PASSES = 24;
3400
+ var BOOTSTRAP_REVEAL_ABORT_WARNING = "LegendList bootstrap initial scroll aborted after exceeding convergence bounds.";
3401
+ function getBootstrapInitialScrollSession(state) {
3402
+ var _a3;
3403
+ return ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" ? state.initialScrollSession.bootstrap : void 0;
3222
3404
  }
3223
- function getPreservedEndAnchorOffsetDiff(ctx) {
3405
+ function isOffsetInitialScrollSession(state) {
3224
3406
  var _a3;
3225
- const state = ctx.state;
3226
- const initialScroll = state.initialScroll;
3227
- if (!state.didFinishInitialScroll || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) || !initialScroll || initialScroll.viewPosition !== 1 || state.props.data.length === 0 || isOffsetInitialScrollSession(state)) {
3228
- return;
3229
- }
3230
- const currentOffset = typeof state.lastNativeScroll === "number" && Number.isFinite(state.lastNativeScroll) ? state.lastNativeScroll : getObservedBootstrapInitialScrollOffset(state);
3231
- return resolveInitialScrollOffset(ctx, initialScroll) - currentOffset;
3407
+ return ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
3232
3408
  }
3233
- function schedulePreservedEndAnchorCorrection(ctx) {
3234
- if (getPreservedEndAnchorOffsetDiff(ctx) === void 0) {
3409
+ function doVisibleIndicesMatch(previous, next) {
3410
+ if (!previous || previous.length !== next.length) {
3235
3411
  return false;
3236
3412
  }
3237
- const correction = {};
3238
- schedulePreservedEndAnchorCorrectionFrame(ctx, correction);
3413
+ for (let i = 0; i < previous.length; i++) {
3414
+ if (previous[i] !== next[i]) {
3415
+ return false;
3416
+ }
3417
+ }
3239
3418
  return true;
3240
3419
  }
3241
- function schedulePreservedEndAnchorCorrectionFrame(ctx, correction) {
3242
- const state = ctx.state;
3243
- state.preservedEndAnchorCorrection = correction;
3244
- requestAnimationFrame(() => {
3245
- var _a3;
3246
- const activeCorrection = state.preservedEndAnchorCorrection;
3247
- if (activeCorrection !== correction) {
3248
- return;
3420
+ function getBootstrapRevealVisibleIndices(options) {
3421
+ const { dataLength, getSize, offset, positions, scrollLength, startIndex: requestedStartIndex } = options;
3422
+ const endOffset = offset + scrollLength;
3423
+ const visibleIndices = [];
3424
+ let index = requestedStartIndex !== void 0 ? Math.max(0, Math.min(dataLength - 1, requestedStartIndex)) : 0;
3425
+ while (index > 0) {
3426
+ const previousIndex = index - 1;
3427
+ const previousPosition = positions[previousIndex];
3428
+ if (previousPosition === void 0) {
3429
+ index = previousIndex;
3430
+ continue;
3249
3431
  }
3250
- const offsetDiff = getPreservedEndAnchorOffsetDiff(ctx);
3251
- if (offsetDiff === void 0 || Math.abs(offsetDiff) <= DEFAULT_BOOTSTRAP_REVEAL_EPSILON) {
3252
- state.preservedEndAnchorCorrection = void 0;
3253
- return;
3432
+ const previousSize = getSize(previousIndex);
3433
+ if (previousSize === void 0) {
3434
+ index = previousIndex;
3435
+ continue;
3254
3436
  }
3255
- const hasObservedNativeScrollAfterRequest = !activeCorrection.lastRequestTime || ((_a3 = state.lastNativeScrollTime) != null ? _a3 : 0) > activeCorrection.lastRequestTime;
3256
- if (hasObservedNativeScrollAfterRequest) {
3257
- activeCorrection.lastRequestTime = Date.now();
3258
- requestAdjust(ctx, offsetDiff);
3437
+ if (previousPosition + previousSize <= offset) {
3438
+ break;
3259
3439
  }
3260
- schedulePreservedEndAnchorCorrectionFrame(ctx, correction);
3261
- });
3262
- }
3263
- function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {
3264
- var _a3, _b;
3265
- const state = ctx.state;
3266
- const initialScroll = state.initialScroll;
3267
- if (!state.didFinishInitialScroll || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) || (initialScroll == null ? void 0 : initialScroll.viewPosition) !== 1 || state.preservedEndAnchorCorrection) {
3268
- return;
3440
+ index = previousIndex;
3269
3441
  }
3270
- if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
3271
- const shouldKeepEndTargetAlive = isRetargetableBottomAlignedInitialScrollTarget(initialScroll) && peek$(ctx, "isAtEnd");
3272
- if (!shouldKeepEndTargetAlive) {
3273
- if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {
3274
- clearPendingInitialScrollFooterLayout(ctx, {
3275
- dataLength: state.props.data.length,
3276
- stylePaddingBottom: (_b = state.props.stylePaddingBottom) != null ? _b : 0,
3277
- target: initialScroll
3278
- });
3279
- } else {
3280
- clearFinishedViewportRetargetableInitialScroll(state);
3281
- }
3442
+ for (; index < dataLength; index++) {
3443
+ const position = positions[index];
3444
+ if (position === void 0) {
3445
+ continue;
3282
3446
  }
3283
- }
3284
- }
3285
- function startBootstrapInitialScrollOnMount(ctx, options) {
3286
- var _a3, _b, _c;
3287
- const { initialScrollAtEnd, target } = options;
3288
- const state = ctx.state;
3289
- const offset = resolveInitialScrollOffset(ctx, target);
3290
- const shouldFinishAtOrigin = offset === 0 && !initialScrollAtEnd && (isOffsetInitialScrollSession(state) ? Math.abs((_a3 = target.contentOffset) != null ? _a3 : 0) <= 1 : target.index === 0 && ((_b = target.viewPosition) != null ? _b : 0) === 0 && Math.abs((_c = target.viewOffset) != null ? _c : 0) <= 1);
3291
- const shouldFinishWithPreservedTarget = state.props.data.length === 0 && target.index !== void 0;
3292
- if (shouldFinishAtOrigin) {
3293
- clearBootstrapInitialScrollSession(state);
3294
- finishInitialScroll(ctx, {
3295
- resolvedOffset: offset
3296
- });
3297
- } else if (shouldFinishWithPreservedTarget) {
3298
- clearBootstrapInitialScrollSession(state);
3299
- finishInitialScroll(ctx, {
3300
- preserveTarget: true,
3301
- resolvedOffset: offset
3302
- });
3303
- } else {
3304
- startBootstrapInitialScrollSession(state, {
3305
- scroll: offset,
3306
- seedContentOffset: 0 ,
3307
- targetIndexSeed: target.index
3308
- });
3309
- ensureBootstrapInitialScrollFrameTicker(ctx);
3310
- }
3311
- }
3312
- function handleBootstrapInitialScrollDataChange(ctx, options) {
3313
- const { dataLength, didDataChange, initialScrollAtEnd, previousDataLength, stylePaddingBottom } = options;
3314
- const state = ctx.state;
3315
- const initialScroll = state.initialScroll;
3316
- if (isOffsetInitialScrollSession(state) || !initialScroll) {
3317
- return;
3318
- }
3319
- const shouldResetDidFinish = !!(state.didFinishInitialScroll && previousDataLength === 0 && dataLength > 0 && initialScroll.index !== void 0);
3320
- const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
3321
- const shouldClearFinishedResizePreservation = !initialScrollAtEnd && didDataChange && dataLength > 0 && state.didFinishInitialScroll && !bootstrapInitialScroll && !shouldResetDidFinish;
3322
- if (shouldClearFinishedResizePreservation) {
3323
- clearPreservedInitialScrollTarget(state);
3324
- return;
3325
- }
3326
- const shouldRetargetBottomAligned = dataLength > 0 && (initialScrollAtEnd || isRetargetableBottomAlignedInitialScrollTarget(initialScroll));
3327
- if (!didDataChange && !shouldResetDidFinish && !shouldRetargetBottomAligned) {
3328
- return;
3329
- }
3330
- if (shouldRetargetBottomAligned) {
3331
- const updatedInitialScroll = initialScrollAtEnd ? createInitialScrollAtEndTarget({
3332
- dataLength,
3333
- footerSize: peek$(ctx, "footerSize") || 0,
3334
- preserveForFooterLayout: shouldPreserveInitialScrollForFooterLayout(initialScroll),
3335
- stylePaddingBottom
3336
- }) : createRetargetedBottomAlignedInitialScroll({
3337
- dataLength,
3338
- footerSize: peek$(ctx, "footerSize") || 0,
3339
- initialScrollAtEnd,
3340
- stylePaddingBottom,
3341
- target: initialScroll
3342
- });
3343
- if (!shouldResetDidFinish && didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
3344
- clearPendingInitialScrollFooterLayout(ctx, {
3345
- dataLength,
3346
- stylePaddingBottom,
3347
- target: initialScroll
3348
- });
3349
- return;
3447
+ const size = getSize(index);
3448
+ if (size === void 0) {
3449
+ continue;
3350
3450
  }
3351
- if (!areEquivalentBootstrapInitialScrollTargets(initialScroll, updatedInitialScroll) || !!bootstrapInitialScroll || shouldResetDidFinish || didDataChange) {
3352
- setInitialScrollTarget(state, updatedInitialScroll, {
3353
- resetDidFinish: shouldResetDidFinish
3354
- });
3355
- rearmBootstrapInitialScroll(ctx, {
3356
- scroll: resolveInitialScrollOffset(ctx, updatedInitialScroll),
3357
- seedContentOffset: shouldResetDidFinish && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
3358
- targetIndexSeed: updatedInitialScroll.index
3359
- });
3360
- return;
3451
+ if (position < endOffset && position + size > offset) {
3452
+ visibleIndices.push(index);
3453
+ } else if (visibleIndices.length > 0 && position >= endOffset) {
3454
+ break;
3361
3455
  }
3362
3456
  }
3363
- if (!didDataChange) {
3364
- return;
3365
- }
3366
- if (bootstrapInitialScroll || shouldResetDidFinish) {
3367
- setInitialScrollTarget(state, initialScroll, {
3368
- resetDidFinish: shouldResetDidFinish
3369
- });
3370
- rearmBootstrapInitialScroll(ctx, {
3371
- scroll: resolveInitialScrollOffset(ctx, initialScroll),
3372
- seedContentOffset: shouldResetDidFinish && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
3373
- targetIndexSeed: initialScroll.index
3374
- });
3375
- }
3457
+ return visibleIndices;
3376
3458
  }
3377
- function handleBootstrapInitialScrollFooterLayout(ctx, options) {
3378
- const { dataLength, footerSize, initialScrollAtEnd, stylePaddingBottom } = options;
3379
- const state = ctx.state;
3380
- if (!initialScrollAtEnd) {
3381
- return;
3382
- }
3383
- const initialScroll = state.initialScroll;
3384
- if (isOffsetInitialScrollSession(state) || dataLength === 0 || !initialScroll) {
3385
- return;
3386
- }
3387
- const shouldProcessFooterLayout = !!getBootstrapInitialScrollSession(state) || shouldPreserveInitialScrollForFooterLayout(initialScroll);
3388
- if (!shouldProcessFooterLayout) {
3389
- return;
3390
- }
3391
- if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
3392
- clearPendingInitialScrollFooterLayout(ctx, {
3393
- dataLength,
3394
- stylePaddingBottom,
3395
- target: initialScroll
3396
- });
3397
- } else {
3398
- const updatedInitialScroll = createInitialScrollAtEndTarget({
3399
- dataLength,
3400
- footerSize,
3401
- preserveForFooterLayout: shouldPreserveInitialScrollForFooterLayout(initialScroll),
3402
- stylePaddingBottom
3403
- });
3404
- const didTargetChange = initialScroll.index !== updatedInitialScroll.index || initialScroll.viewPosition !== updatedInitialScroll.viewPosition || initialScroll.viewOffset !== updatedInitialScroll.viewOffset;
3405
- if (!didTargetChange) {
3406
- clearPendingInitialScrollFooterLayout(ctx, {
3407
- dataLength,
3408
- stylePaddingBottom,
3409
- target: initialScroll
3410
- });
3411
- } else {
3412
- const didFinishInitialScroll = !!state.didFinishInitialScroll;
3413
- setInitialScrollTarget(state, updatedInitialScroll, {
3414
- resetDidFinish: didFinishInitialScroll
3415
- });
3416
- rearmBootstrapInitialScroll(ctx, {
3417
- scroll: resolveInitialScrollOffset(ctx, updatedInitialScroll),
3418
- targetIndexSeed: updatedInitialScroll.index
3419
- });
3420
- }
3421
- }
3459
+ function shouldAbortBootstrapReveal(options) {
3460
+ const {
3461
+ mountFrameCount,
3462
+ maxFrames = DEFAULT_BOOTSTRAP_REVEAL_MAX_FRAMES,
3463
+ maxPasses = DEFAULT_BOOTSTRAP_REVEAL_MAX_PASSES,
3464
+ passCount
3465
+ } = options;
3466
+ return mountFrameCount >= maxFrames || passCount >= maxPasses;
3422
3467
  }
3423
- function handleBootstrapInitialScrollLayoutChange(ctx) {
3424
- var _a3, _b, _c;
3425
- const state = ctx.state;
3426
- const initialScroll = state.initialScroll;
3427
- const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
3428
- if (initialScroll && state.props.data.length > 0 && !isOffsetInitialScrollSession(state) && (bootstrapInitialScroll || initialScroll.viewPosition === 1)) {
3429
- const resolvedOffset = resolveInitialScrollOffset(ctx, initialScroll);
3430
- const scrollingTo = ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) ? state.scrollingTo : void 0;
3431
- if (!bootstrapInitialScroll && (scrollingTo || state.didFinishInitialScroll)) {
3432
- const currentOffset = scrollingTo ? (_b = scrollingTo.targetOffset) != null ? _b : scrollingTo.offset : getObservedBootstrapInitialScrollOffset(state);
3433
- const offsetDiff = resolvedOffset - currentOffset;
3434
- if (Math.abs(offsetDiff) > DEFAULT_BOOTSTRAP_REVEAL_EPSILON) {
3435
- if (state.didFinishInitialScroll) {
3436
- schedulePreservedEndAnchorCorrection(ctx);
3437
- } else if (scrollingTo) {
3438
- const existingWatchdog = initialScrollWatchdog.get(state);
3439
- scrollingTo.offset = resolvedOffset;
3440
- scrollingTo.targetOffset = resolvedOffset;
3441
- state.initialScroll = {
3442
- ...initialScroll,
3443
- contentOffset: resolvedOffset
3444
- };
3445
- state.hasScrolled = false;
3446
- initialScrollWatchdog.set(state, {
3447
- startScroll: (_c = existingWatchdog == null ? void 0 : existingWatchdog.startScroll) != null ? _c : state.scroll,
3448
- targetOffset: resolvedOffset
3449
- });
3450
- requestAdjust(ctx, offsetDiff);
3451
- }
3452
- }
3453
- } else {
3454
- rearmBootstrapInitialScroll(ctx, {
3455
- scroll: resolvedOffset,
3456
- targetIndexSeed: initialScroll.index
3457
- });
3458
- }
3468
+ function abortBootstrapRevealIfNeeded(ctx, options) {
3469
+ if (!shouldAbortBootstrapReveal(options)) {
3470
+ return false;
3471
+ }
3472
+ if (IS_DEV) {
3473
+ console.warn(BOOTSTRAP_REVEAL_ABORT_WARNING);
3459
3474
  }
3475
+ abortBootstrapInitialScroll(ctx);
3476
+ return true;
3460
3477
  }
3461
- function evaluateBootstrapInitialScroll(ctx) {
3462
- var _a3, _b;
3463
- const state = ctx.state;
3478
+ function clearBootstrapInitialScrollSession(state) {
3479
+ var _a3;
3464
3480
  const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
3465
- const initialScroll = state.initialScroll;
3466
- if (!bootstrapInitialScroll || !initialScroll || isOffsetInitialScrollSession(state) || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll)) {
3467
- return;
3468
- }
3469
- bootstrapInitialScroll.passCount += 1;
3470
- if (abortBootstrapRevealIfNeeded(ctx, {
3471
- mountFrameCount: bootstrapInitialScroll.mountFrameCount,
3472
- passCount: bootstrapInitialScroll.passCount
3473
- })) {
3474
- return;
3481
+ const frameHandle = bootstrapInitialScroll == null ? void 0 : bootstrapInitialScroll.frameHandle;
3482
+ if (frameHandle !== void 0 && typeof cancelAnimationFrame === "function") {
3483
+ cancelAnimationFrame(frameHandle);
3475
3484
  }
3476
- if (initialScroll.index !== void 0 && state.startBuffered >= 0 && state.endBuffered >= 0 && initialScroll.index >= state.startBuffered && initialScroll.index <= state.endBuffered) {
3477
- bootstrapInitialScroll.targetIndexSeed = void 0;
3485
+ if (bootstrapInitialScroll) {
3486
+ bootstrapInitialScroll.frameHandle = void 0;
3478
3487
  }
3479
- const resolvedOffset = resolveInitialScrollOffset(ctx, initialScroll);
3480
- const mountedBufferedIndices = getMountedBufferedIndices(state);
3481
- const areMountedBufferedIndicesMeasured = checkAllSizesKnown(state, mountedBufferedIndices);
3482
- const didResolvedOffsetChange = Math.abs(bootstrapInitialScroll.scroll - resolvedOffset) > 1;
3483
- const { data } = state.props;
3484
- const visibleIndices = getBootstrapRevealVisibleIndices({
3485
- dataLength: data.length,
3486
- getSize: (index) => {
3487
- var _a4, _b2;
3488
- const id = (_a4 = state.idCache[index]) != null ? _a4 : getId(state, index);
3489
- return (_b2 = state.sizes.get(id)) != null ? _b2 : getItemSize(ctx, id, index, data[index]);
3490
- },
3491
- offset: resolvedOffset,
3492
- positions: state.positions,
3493
- scrollLength: state.scrollLength,
3494
- startIndex: (_b = bootstrapInitialScroll.targetIndexSeed) != null ? _b : state.startBuffered >= 0 ? state.startBuffered : void 0
3488
+ setInitialScrollSession(state, {
3489
+ bootstrap: null,
3490
+ kind: (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind
3495
3491
  });
3496
- const areVisibleIndicesMeasured = visibleIndices.length > 0 && visibleIndices.every((index) => {
3497
- var _a4;
3498
- const id = (_a4 = state.idCache[index]) != null ? _a4 : getId(state, index);
3499
- return state.sizesKnown.has(id);
3492
+ }
3493
+ function startBootstrapInitialScrollSession(state, options) {
3494
+ var _a3, _b, _c;
3495
+ const previousBootstrapInitialScroll = getBootstrapInitialScrollSession(state);
3496
+ setInitialScrollSession(state, {
3497
+ bootstrap: {
3498
+ frameHandle: previousBootstrapInitialScroll == null ? void 0 : previousBootstrapInitialScroll.frameHandle,
3499
+ // Re-arming during the initial mount should spend from the same watchdog budget.
3500
+ mountFrameCount: (_a3 = previousBootstrapInitialScroll == null ? void 0 : previousBootstrapInitialScroll.mountFrameCount) != null ? _a3 : 0,
3501
+ passCount: 0,
3502
+ previousResolvedOffset: void 0,
3503
+ scroll: options.scroll,
3504
+ seedContentOffset: (_c = (_b = options.seedContentOffset) != null ? _b : previousBootstrapInitialScroll == null ? void 0 : previousBootstrapInitialScroll.seedContentOffset) != null ? _c : options.scroll,
3505
+ targetIndexSeed: options.targetIndexSeed,
3506
+ visibleIndices: void 0
3507
+ },
3508
+ kind: "bootstrap"
3500
3509
  });
3501
- const previousResolvedOffset = bootstrapInitialScroll.previousResolvedOffset;
3502
- const previousVisibleIndices = bootstrapInitialScroll.visibleIndices;
3503
- bootstrapInitialScroll.previousResolvedOffset = resolvedOffset;
3504
- bootstrapInitialScroll.visibleIndices = visibleIndices;
3505
- if (didResolvedOffsetChange) {
3506
- bootstrapInitialScroll.scroll = resolvedOffset;
3507
- queueBootstrapInitialScrollReevaluation(state);
3508
- return;
3509
- }
3510
- if (!areMountedBufferedIndicesMeasured || !areVisibleIndicesMeasured) {
3511
- return;
3512
- }
3513
- const didRevealSettle = previousResolvedOffset !== void 0 && Math.abs(previousResolvedOffset - resolvedOffset) <= DEFAULT_BOOTSTRAP_REVEAL_EPSILON && doVisibleIndicesMatch(previousVisibleIndices, visibleIndices);
3514
- if (!didRevealSettle) {
3515
- queueBootstrapInitialScrollReevaluation(state);
3516
- return;
3517
- }
3518
- {
3519
- clearBootstrapInitialScrollSession(state);
3520
- dispatchInitialScroll(ctx, {
3521
- forceScroll: true,
3522
- resolvedOffset,
3523
- target: initialScroll,
3524
- waitForCompletionFrame: Platform.OS === "web"
3510
+ }
3511
+ function resetBootstrapInitialScrollSession(state, options) {
3512
+ var _a3, _b, _c;
3513
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
3514
+ if (!bootstrapInitialScroll) {
3515
+ if ((options == null ? void 0 : options.scroll) !== void 0) {
3516
+ startBootstrapInitialScrollSession(state, {
3517
+ scroll: options.scroll,
3518
+ seedContentOffset: options.seedContentOffset,
3519
+ targetIndexSeed: options.targetIndexSeed
3520
+ });
3521
+ }
3522
+ } else {
3523
+ bootstrapInitialScroll.passCount = 0;
3524
+ bootstrapInitialScroll.previousResolvedOffset = void 0;
3525
+ bootstrapInitialScroll.scroll = (_a3 = options == null ? void 0 : options.scroll) != null ? _a3 : bootstrapInitialScroll.scroll;
3526
+ bootstrapInitialScroll.seedContentOffset = (_b = options == null ? void 0 : options.seedContentOffset) != null ? _b : bootstrapInitialScroll.seedContentOffset;
3527
+ bootstrapInitialScroll.targetIndexSeed = (_c = options == null ? void 0 : options.targetIndexSeed) != null ? _c : bootstrapInitialScroll.targetIndexSeed;
3528
+ bootstrapInitialScroll.visibleIndices = void 0;
3529
+ setInitialScrollSession(state, {
3530
+ bootstrap: bootstrapInitialScroll,
3531
+ kind: "bootstrap"
3525
3532
  });
3526
3533
  }
3527
3534
  }
3528
- function finishBootstrapInitialScrollWithoutScroll(ctx, resolvedOffset) {
3529
- var _a3;
3530
- const state = ctx.state;
3531
- clearBootstrapInitialScrollSession(state);
3532
- const shouldPreserveResizeTarget = !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_a3 = state.initialScroll) == null ? void 0 : _a3.viewPosition) === 1;
3533
- finishInitialScroll(ctx, {
3534
- preserveTarget: shouldPreserveResizeTarget,
3535
- recalculateItems: true,
3536
- resolvedOffset,
3537
- schedulePreservedTargetClear: shouldPreserveResizeTarget
3535
+ function queueBootstrapInitialScrollReevaluation(state) {
3536
+ requestAnimationFrame(() => {
3537
+ var _a3;
3538
+ if (getBootstrapInitialScrollSession(state)) {
3539
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { forceFullItemPositions: true });
3540
+ }
3538
3541
  });
3539
3542
  }
3540
- function abortBootstrapInitialScroll(ctx) {
3541
- var _a3, _b, _c, _d;
3543
+ function ensureBootstrapInitialScrollFrameTicker(ctx) {
3542
3544
  const state = ctx.state;
3543
3545
  const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
3544
- const initialScroll = state.initialScroll;
3545
- if (bootstrapInitialScroll && initialScroll && !isOffsetInitialScrollSession(state) && state.refScroller.current) {
3546
- clearBootstrapInitialScrollSession(state);
3547
- dispatchInitialScroll(ctx, {
3548
- forceScroll: true,
3549
- resolvedOffset: bootstrapInitialScroll.scroll,
3550
- target: initialScroll,
3551
- waitForCompletionFrame: Platform.OS === "web"
3552
- });
3553
- } else {
3554
- finishBootstrapInitialScrollWithoutScroll(
3555
- ctx,
3556
- (_d = (_c = (_b = (_a3 = getBootstrapInitialScrollSession(state)) == null ? void 0 : _a3.scroll) != null ? _b : state.scrollPending) != null ? _c : state.scroll) != null ? _d : 0
3557
- );
3546
+ if (!bootstrapInitialScroll || bootstrapInitialScroll.frameHandle !== void 0) {
3547
+ return;
3558
3548
  }
3559
- }
3560
-
3561
- // src/core/checkFinishedScroll.ts
3562
- var INITIAL_SCROLL_MAX_FALLBACK_CHECKS = 20;
3563
- var INITIAL_SCROLL_COMPLETION_TARGET_EPSILON = 1;
3564
- var INITIAL_SCROLL_ZERO_TARGET_EPSILON = 1;
3565
- var SILENT_INITIAL_SCROLL_RETRY_DELAY_MS = 16;
3566
- function checkFinishedScroll(ctx, options) {
3567
- const scrollingTo = ctx.state.scrollingTo;
3568
- if (options == null ? void 0 : options.onlyIfAligned) {
3569
- if (!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) || scrollingTo.animated) {
3549
+ const tick = () => {
3550
+ const activeBootstrapInitialScroll = getBootstrapInitialScrollSession(state);
3551
+ if (!activeBootstrapInitialScroll) {
3570
3552
  return;
3571
3553
  }
3572
- if (!getResolvedScrollCompletionState(ctx, scrollingTo).isAtResolvedTarget) {
3554
+ activeBootstrapInitialScroll.frameHandle = void 0;
3555
+ activeBootstrapInitialScroll.mountFrameCount += 1;
3556
+ if (abortBootstrapRevealIfNeeded(ctx, {
3557
+ mountFrameCount: activeBootstrapInitialScroll.mountFrameCount,
3558
+ passCount: activeBootstrapInitialScroll.passCount
3559
+ })) {
3573
3560
  return;
3574
3561
  }
3575
- }
3576
- ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
3562
+ ensureBootstrapInitialScrollFrameTicker(ctx);
3563
+ };
3564
+ bootstrapInitialScroll.frameHandle = requestAnimationFrame(tick);
3577
3565
  }
3578
- function hasScrollCompletionOwnership(state, options) {
3579
- const { clampedTargetOffset, scrollingTo } = options;
3580
- return !scrollingTo.isInitialScroll || state.hasScrolled || clampedTargetOffset <= INITIAL_SCROLL_COMPLETION_TARGET_EPSILON;
3566
+ function rearmBootstrapInitialScroll(ctx, options) {
3567
+ resetBootstrapInitialScrollSession(ctx.state, options);
3568
+ ensureBootstrapInitialScrollFrameTicker(ctx);
3569
+ queueBootstrapInitialScrollReevaluation(ctx.state);
3581
3570
  }
3582
- function isSilentInitialDispatch(state, scrollingTo) {
3583
- return !!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && initialScrollCompletion.didDispatchNativeScroll(state) && !state.hasScrolled;
3571
+ function createInitialScrollAtEndTarget(options) {
3572
+ const { dataLength, footerSize, preserveForFooterLayout, stylePaddingBottom } = options;
3573
+ return {
3574
+ contentOffset: void 0,
3575
+ index: Math.max(0, dataLength - 1),
3576
+ preserveForBottomPadding: true,
3577
+ preserveForFooterLayout,
3578
+ viewOffset: -stylePaddingBottom - footerSize,
3579
+ viewPosition: 1
3580
+ };
3584
3581
  }
3585
- function getInitialScrollWatchdogTargetOffset(state) {
3586
- var _a3;
3587
- return (_a3 = initialScrollWatchdog.get(state)) == null ? void 0 : _a3.targetOffset;
3582
+ function shouldPreserveInitialScrollForBottomPadding(target) {
3583
+ return !!(target == null ? void 0 : target.preserveForBottomPadding);
3588
3584
  }
3589
- function isNativeInitialNonZeroTarget(state) {
3590
- const targetOffset = getInitialScrollWatchdogTargetOffset(state);
3591
- return !state.didFinishInitialScroll && initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset);
3585
+ function shouldPreserveInitialScrollForFooterLayout(target) {
3586
+ return !!(target == null ? void 0 : target.preserveForFooterLayout);
3592
3587
  }
3593
- function shouldFinishInitialScrollWithoutNativeProgress(state, scrollingTo) {
3594
- var _a3, _b;
3595
- if (!scrollingTo.isInitialScroll || scrollingTo.animated || !state.didContainersLayout) {
3596
- return false;
3597
- }
3598
- if (((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap") {
3599
- return false;
3600
- }
3601
- const targetOffset = (_b = scrollingTo.targetOffset) != null ? _b : scrollingTo.offset;
3602
- if (initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset) && initialScrollCompletion.didDispatchNativeScroll(state) && !state.hasScrolled) {
3603
- return false;
3588
+ function isRetargetableBottomAlignedInitialScrollTarget(target) {
3589
+ return !!(target && target.viewPosition === 1 && (shouldPreserveInitialScrollForBottomPadding(target) || shouldPreserveInitialScrollForFooterLayout(target)));
3590
+ }
3591
+ function createRetargetedBottomAlignedInitialScroll(options) {
3592
+ const { dataLength, footerSize, initialScrollAtEnd, stylePaddingBottom, target } = options;
3593
+ const preserveForFooterLayout = shouldPreserveInitialScrollForFooterLayout(target);
3594
+ return {
3595
+ ...target,
3596
+ contentOffset: void 0,
3597
+ index: initialScrollAtEnd ? Math.max(0, dataLength - 1) : target.index,
3598
+ preserveForBottomPadding: true,
3599
+ preserveForFooterLayout,
3600
+ viewOffset: -stylePaddingBottom - (preserveForFooterLayout ? footerSize : 0),
3601
+ viewPosition: 1
3602
+ };
3603
+ }
3604
+ function areEquivalentBootstrapInitialScrollTargets(current, next) {
3605
+ return current.index === next.index && current.preserveForBottomPadding === next.preserveForBottomPadding && current.preserveForFooterLayout === next.preserveForFooterLayout && current.viewOffset === next.viewOffset && current.viewPosition === next.viewPosition;
3606
+ }
3607
+ function clearPendingInitialScrollFooterLayout(ctx, options) {
3608
+ const { dataLength, stylePaddingBottom, target } = options;
3609
+ const state = ctx.state;
3610
+ if (!shouldPreserveInitialScrollForFooterLayout(target)) {
3611
+ return;
3604
3612
  }
3605
- if (initialScrollWatchdog.isAtZeroTargetOffset(targetOffset) || Math.abs(state.scroll - targetOffset) > 1 || Math.abs(state.scrollPending - targetOffset) > 1) {
3613
+ const clearedFooterTarget = createInitialScrollAtEndTarget({
3614
+ dataLength,
3615
+ footerSize: 0,
3616
+ preserveForFooterLayout: void 0,
3617
+ stylePaddingBottom
3618
+ });
3619
+ setInitialScrollTarget(state, clearedFooterTarget);
3620
+ }
3621
+ function clearFinishedViewportRetargetableInitialScroll(state) {
3622
+ clearPreservedInitialScrollTarget(state);
3623
+ }
3624
+ function didFinishedInitialScrollMoveAwayFromTarget(ctx, target, epsilon = DEFAULT_BOOTSTRAP_REVEAL_EPSILON) {
3625
+ const state = ctx.state;
3626
+ if (!state.didFinishInitialScroll) {
3606
3627
  return false;
3607
3628
  }
3608
- return !!scrollingTo.waitForInitialScrollCompletionFrame || isNativeInitialNonZeroTarget(state);
3629
+ const currentOffset = getObservedBootstrapInitialScrollOffset(state);
3630
+ return Math.abs(currentOffset - resolveInitialScrollOffset(ctx, target)) > epsilon;
3609
3631
  }
3610
- function shouldFinishInitialZeroTargetScroll(ctx) {
3611
- var _a3;
3612
- const { state } = ctx;
3613
- return !!((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) && state.props.data.length > 0 && getContentSize(ctx) <= state.scrollLength && state.scrollPending <= INITIAL_SCROLL_ZERO_TARGET_EPSILON;
3632
+ function getObservedBootstrapInitialScrollOffset(state) {
3633
+ var _a3, _b, _c, _d;
3634
+ const observedOffset = (_b = (_a3 = state.refScroller.current) == null ? void 0 : _a3.getCurrentScrollOffset) == null ? void 0 : _b.call(_a3);
3635
+ return typeof observedOffset === "number" && Number.isFinite(observedOffset) ? observedOffset : (_d = (_c = state.scrollPending) != null ? _c : state.scroll) != null ? _d : 0;
3614
3636
  }
3615
- function getResolvedScrollCompletionState(ctx, scrollingTo) {
3637
+ function getPreservedEndAnchorOffsetDiff(ctx) {
3616
3638
  var _a3;
3617
- const { state } = ctx;
3618
- const scroll = state.scrollPending;
3619
- const adjust = state.scrollAdjustHandler.getAdjust();
3620
- const clampedTargetOffset = (_a3 = scrollingTo.targetOffset) != null ? _a3 : clampScrollOffset(ctx, scrollingTo.offset - (scrollingTo.viewOffset || 0), scrollingTo);
3621
- const maxOffset = clampScrollOffset(ctx, scroll, scrollingTo);
3622
- const diff1 = Math.abs(scroll - clampedTargetOffset);
3623
- const diff2 = Math.abs(diff1 - adjust);
3624
- return {
3625
- clampedTargetOffset,
3626
- isAtResolvedTarget: Math.abs(scroll - maxOffset) < 1 && (diff1 < 1 || !scrollingTo.animated && diff2 < 1)
3627
- };
3628
- }
3629
- function checkFinishedScrollFrame(ctx) {
3630
- const scrollingTo = ctx.state.scrollingTo;
3631
- if (!scrollingTo) {
3639
+ const state = ctx.state;
3640
+ const initialScroll = state.initialScroll;
3641
+ if (!state.didFinishInitialScroll || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) || !initialScroll || initialScroll.viewPosition !== 1 || state.props.data.length === 0 || isOffsetInitialScrollSession(state)) {
3632
3642
  return;
3633
3643
  }
3634
- const { state } = ctx;
3635
- state.animFrameCheckFinishedScroll = void 0;
3636
- const completionState = getResolvedScrollCompletionState(ctx, scrollingTo);
3637
- if (completionState.isAtResolvedTarget && hasScrollCompletionOwnership(state, {
3638
- clampedTargetOffset: completionState.clampedTargetOffset,
3639
- scrollingTo
3640
- })) {
3641
- finishScrollTo(ctx);
3644
+ const currentOffset = typeof state.lastNativeScroll === "number" && Number.isFinite(state.lastNativeScroll) ? state.lastNativeScroll : getObservedBootstrapInitialScrollOffset(state);
3645
+ return resolveInitialScrollOffset(ctx, initialScroll) - currentOffset;
3646
+ }
3647
+ function schedulePreservedEndAnchorCorrection(ctx) {
3648
+ if (getPreservedEndAnchorOffsetDiff(ctx) === void 0) {
3649
+ return false;
3642
3650
  }
3651
+ const correction = {};
3652
+ schedulePreservedEndAnchorCorrectionFrame(ctx, correction);
3653
+ return true;
3643
3654
  }
3644
- function scrollToFallbackOffset(ctx, offset) {
3645
- var _a3;
3646
- (_a3 = ctx.state.refScroller.current) == null ? void 0 : _a3.scrollTo({
3647
- animated: false,
3648
- x: ctx.state.props.horizontal ? offset : 0,
3649
- y: ctx.state.props.horizontal ? 0 : offset
3655
+ function schedulePreservedEndAnchorCorrectionFrame(ctx, correction) {
3656
+ const state = ctx.state;
3657
+ state.preservedEndAnchorCorrection = correction;
3658
+ requestAnimationFrame(() => {
3659
+ var _a3;
3660
+ const activeCorrection = state.preservedEndAnchorCorrection;
3661
+ if (activeCorrection !== correction) {
3662
+ return;
3663
+ }
3664
+ const offsetDiff = getPreservedEndAnchorOffsetDiff(ctx);
3665
+ if (offsetDiff === void 0 || Math.abs(offsetDiff) <= DEFAULT_BOOTSTRAP_REVEAL_EPSILON) {
3666
+ state.preservedEndAnchorCorrection = void 0;
3667
+ return;
3668
+ }
3669
+ const hasObservedNativeScrollAfterRequest = !activeCorrection.lastRequestTime || ((_a3 = state.lastNativeScrollTime) != null ? _a3 : 0) > activeCorrection.lastRequestTime;
3670
+ if (hasObservedNativeScrollAfterRequest) {
3671
+ activeCorrection.lastRequestTime = Date.now();
3672
+ requestAdjust(ctx, offsetDiff);
3673
+ }
3674
+ schedulePreservedEndAnchorCorrectionFrame(ctx, correction);
3650
3675
  });
3651
3676
  }
3652
- function checkFinishedScrollFallback(ctx) {
3677
+ function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {
3678
+ var _a3, _b;
3653
3679
  const state = ctx.state;
3654
- const scrollingTo = state.scrollingTo;
3655
- const shouldFinishInitialZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
3656
- const silentInitialDispatch = isSilentInitialDispatch(state, scrollingTo);
3657
- const canFinishInitialWithoutNativeProgress = scrollingTo !== void 0 ? shouldFinishInitialScrollWithoutNativeProgress(state, scrollingTo) : false;
3658
- const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && !shouldFinishInitialZeroTarget && !canFinishInitialWithoutNativeProgress || !state.didContainersLayout;
3659
- const initialDelay = shouldFinishInitialZeroTarget || canFinishInitialWithoutNativeProgress ? 0 : silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : slowTimeout ? 500 : 100;
3660
- state.timeoutCheckFinishedScrollFallback = setTimeout(() => {
3661
- let numChecks = 0;
3662
- const scheduleFallbackCheck = (delay) => {
3663
- state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, delay);
3664
- };
3665
- const checkHasScrolled = () => {
3666
- var _c, _d;
3667
- state.timeoutCheckFinishedScrollFallback = void 0;
3668
- const isStillScrollingTo = state.scrollingTo;
3669
- if (isStillScrollingTo) {
3670
- numChecks++;
3671
- const isNativeInitialPending = isNativeInitialNonZeroTarget(state) && !state.hasScrolled;
3672
- const maxChecks = silentInitialDispatch ? 5 : isNativeInitialPending ? INITIAL_SCROLL_MAX_FALLBACK_CHECKS : 5;
3673
- const shouldFinishZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
3674
- const canFinishInitialScrollWithoutNativeProgress = shouldFinishInitialScrollWithoutNativeProgress(
3675
- state,
3676
- isStillScrollingTo
3677
- );
3678
- const completionState = getResolvedScrollCompletionState(ctx, isStillScrollingTo);
3679
- const canFinishAfterSilentNativeDispatch = Platform.OS === "android";
3680
- const shouldFinishAfterObservedScroll = state.hasScrolled && (!isStillScrollingTo.isInitialScroll || completionState.isAtResolvedTarget);
3681
- const shouldRetryUnalignedInitialScroll = isStillScrollingTo.isInitialScroll && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
3682
- if (shouldFinishZeroTarget || shouldFinishAfterObservedScroll || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
3683
- finishScrollTo(ctx);
3684
- } else if ((isNativeInitialPending || shouldRetryUnalignedInitialScroll) && numChecks <= maxChecks) {
3685
- const targetOffset = (_d = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : isStillScrollingTo.targetOffset) != null ? _d : state.scrollPending;
3686
- scrollToFallbackOffset(ctx, targetOffset);
3687
- scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
3688
- } else {
3689
- scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
3690
- }
3680
+ const initialScroll = state.initialScroll;
3681
+ if (!state.didFinishInitialScroll || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) || (initialScroll == null ? void 0 : initialScroll.viewPosition) !== 1 || state.preservedEndAnchorCorrection) {
3682
+ return;
3683
+ }
3684
+ if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
3685
+ const shouldKeepEndTargetAlive = isRetargetableBottomAlignedInitialScrollTarget(initialScroll) && peek$(ctx, "isAtEnd");
3686
+ if (!shouldKeepEndTargetAlive) {
3687
+ if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {
3688
+ clearPendingInitialScrollFooterLayout(ctx, {
3689
+ dataLength: state.props.data.length,
3690
+ stylePaddingBottom: (_b = state.props.stylePaddingBottom) != null ? _b : 0,
3691
+ target: initialScroll
3692
+ });
3693
+ } else {
3694
+ clearFinishedViewportRetargetableInitialScroll(state);
3691
3695
  }
3692
- };
3693
- checkHasScrolled();
3694
- }, initialDelay);
3696
+ }
3697
+ }
3695
3698
  }
3696
-
3697
- // src/core/initialScrollLifecycle.ts
3698
- function retargetActiveInitialScrollAtEnd(ctx) {
3699
- var _a3;
3699
+ function startBootstrapInitialScrollOnMount(ctx, options) {
3700
+ var _a3, _b, _c;
3701
+ const { initialScrollAtEnd, target } = options;
3702
+ const state = ctx.state;
3703
+ const offset = resolveInitialScrollOffset(ctx, target);
3704
+ const shouldFinishAtOrigin = offset === 0 && !initialScrollAtEnd && (isOffsetInitialScrollSession(state) ? Math.abs((_a3 = target.contentOffset) != null ? _a3 : 0) <= 1 : target.index === 0 && ((_b = target.viewPosition) != null ? _b : 0) === 0 && Math.abs((_c = target.viewOffset) != null ? _c : 0) <= 1);
3705
+ const shouldFinishWithPreservedTarget = state.props.data.length === 0 && target.index !== void 0;
3706
+ if (shouldFinishAtOrigin) {
3707
+ clearBootstrapInitialScrollSession(state);
3708
+ finishInitialScroll(ctx, {
3709
+ resolvedOffset: offset
3710
+ });
3711
+ } else if (shouldFinishWithPreservedTarget) {
3712
+ clearBootstrapInitialScrollSession(state);
3713
+ finishInitialScroll(ctx, {
3714
+ preserveTarget: true,
3715
+ resolvedOffset: offset
3716
+ });
3717
+ } else {
3718
+ startBootstrapInitialScrollSession(state, {
3719
+ scroll: offset,
3720
+ seedContentOffset: 0 ,
3721
+ targetIndexSeed: target.index
3722
+ });
3723
+ ensureBootstrapInitialScrollFrameTicker(ctx);
3724
+ }
3725
+ }
3726
+ function handleBootstrapInitialScrollDataChange(ctx, options) {
3727
+ const { dataLength, didDataChange, initialScrollAtEnd, previousDataLength, stylePaddingBottom } = options;
3700
3728
  const state = ctx.state;
3701
3729
  const initialScroll = state.initialScroll;
3702
- if (state.didFinishInitialScroll) {
3703
- return schedulePreservedEndAnchorCorrection(ctx);
3730
+ if (isOffsetInitialScrollSession(state) || !initialScroll) {
3731
+ return;
3704
3732
  }
3705
- if (!initialScroll || ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" || initialScroll.viewPosition !== 1 || state.props.data.length === 0) {
3706
- return false;
3733
+ const shouldResetDidFinish = !!(state.didFinishInitialScroll && previousDataLength === 0 && dataLength > 0 && initialScroll.index !== void 0);
3734
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
3735
+ const shouldClearFinishedResizePreservation = !initialScrollAtEnd && didDataChange && dataLength > 0 && state.didFinishInitialScroll && !bootstrapInitialScroll && !shouldResetDidFinish;
3736
+ if (shouldClearFinishedResizePreservation) {
3737
+ clearPreservedInitialScrollTarget(state);
3738
+ return;
3707
3739
  }
3708
- return advanceCurrentInitialScrollSession(ctx, { forceScroll: true });
3709
- }
3710
- function handleInitialScrollLayoutReady(ctx) {
3711
- var _a3;
3712
- if (!ctx.state.initialScroll) {
3740
+ const shouldRetargetBottomAligned = dataLength > 0 && (initialScrollAtEnd || isRetargetableBottomAlignedInitialScrollTarget(initialScroll));
3741
+ if (!didDataChange && !shouldResetDidFinish && !shouldRetargetBottomAligned) {
3713
3742
  return;
3714
3743
  }
3715
- const runScroll = () => advanceCurrentInitialScrollSession(ctx, { forceScroll: true });
3716
- runScroll();
3717
- if (((_a3 = ctx.state.initialScrollSession) == null ? void 0 : _a3.kind) !== "offset") {
3718
- requestAnimationFrame(runScroll);
3744
+ if (shouldRetargetBottomAligned) {
3745
+ const updatedInitialScroll = initialScrollAtEnd ? createInitialScrollAtEndTarget({
3746
+ dataLength,
3747
+ footerSize: peek$(ctx, "footerSize") || 0,
3748
+ preserveForFooterLayout: shouldPreserveInitialScrollForFooterLayout(initialScroll),
3749
+ stylePaddingBottom
3750
+ }) : createRetargetedBottomAlignedInitialScroll({
3751
+ dataLength,
3752
+ footerSize: peek$(ctx, "footerSize") || 0,
3753
+ initialScrollAtEnd,
3754
+ stylePaddingBottom,
3755
+ target: initialScroll
3756
+ });
3757
+ if (!shouldResetDidFinish && didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
3758
+ clearPendingInitialScrollFooterLayout(ctx, {
3759
+ dataLength,
3760
+ stylePaddingBottom,
3761
+ target: initialScroll
3762
+ });
3763
+ return;
3764
+ }
3765
+ if (!areEquivalentBootstrapInitialScrollTargets(initialScroll, updatedInitialScroll) || !!bootstrapInitialScroll || shouldResetDidFinish || didDataChange) {
3766
+ setInitialScrollTarget(state, updatedInitialScroll, {
3767
+ resetDidFinish: shouldResetDidFinish
3768
+ });
3769
+ rearmBootstrapInitialScroll(ctx, {
3770
+ scroll: resolveInitialScrollOffset(ctx, updatedInitialScroll),
3771
+ seedContentOffset: shouldResetDidFinish && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
3772
+ targetIndexSeed: updatedInitialScroll.index
3773
+ });
3774
+ return;
3775
+ }
3776
+ }
3777
+ if (!didDataChange) {
3778
+ return;
3779
+ }
3780
+ if (bootstrapInitialScroll || shouldResetDidFinish) {
3781
+ setInitialScrollTarget(state, initialScroll, {
3782
+ resetDidFinish: shouldResetDidFinish
3783
+ });
3784
+ rearmBootstrapInitialScroll(ctx, {
3785
+ scroll: resolveInitialScrollOffset(ctx, initialScroll),
3786
+ seedContentOffset: shouldResetDidFinish && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
3787
+ targetIndexSeed: initialScroll.index
3788
+ });
3719
3789
  }
3720
- checkFinishedScroll(ctx, { onlyIfAligned: true });
3721
3790
  }
3722
- function initializeInitialScrollOnMount(ctx, options) {
3723
- var _a3, _b;
3724
- const {
3725
- alwaysDispatchInitialScroll,
3726
- dataLength,
3727
- hasFooterComponent,
3728
- initialContentOffset,
3729
- initialScrollAtEnd,
3730
- useBootstrapInitialScroll
3731
- } = options;
3791
+ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
3792
+ const { dataLength, footerSize, initialScrollAtEnd, stylePaddingBottom } = options;
3732
3793
  const state = ctx.state;
3794
+ if (!initialScrollAtEnd) {
3795
+ return;
3796
+ }
3733
3797
  const initialScroll = state.initialScroll;
3734
- const resolvedInitialContentOffset = initialContentOffset != null ? initialContentOffset : 0;
3735
- const preserveForFooterLayout = useBootstrapInitialScroll && initialScrollAtEnd && hasFooterComponent;
3736
- if (initialScroll && (initialScroll.contentOffset === void 0 || !!initialScroll.preserveForFooterLayout !== preserveForFooterLayout && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) !== "offset")) {
3737
- setInitialScrollTarget(state, {
3738
- ...initialScroll,
3739
- contentOffset: resolvedInitialContentOffset,
3740
- preserveForFooterLayout
3741
- });
3798
+ if (isOffsetInitialScrollSession(state) || dataLength === 0 || !initialScroll) {
3799
+ return;
3742
3800
  }
3743
- if (useBootstrapInitialScroll && initialScroll && ((_b = state.initialScrollSession) == null ? void 0 : _b.kind) !== "offset") {
3744
- startBootstrapInitialScrollOnMount(ctx, {
3745
- initialScrollAtEnd,
3746
- target: state.initialScroll
3747
- });
3801
+ const shouldProcessFooterLayout = !!getBootstrapInitialScrollSession(state) || shouldPreserveInitialScrollForFooterLayout(initialScroll);
3802
+ if (!shouldProcessFooterLayout) {
3748
3803
  return;
3749
3804
  }
3750
- const hasPendingDataDependentInitialScroll = !!initialScroll && dataLength === 0 && !(resolvedInitialContentOffset === 0 && !initialScrollAtEnd);
3751
- if (!alwaysDispatchInitialScroll && !resolvedInitialContentOffset && !hasPendingDataDependentInitialScroll) {
3752
- if (initialScroll && !initialScrollAtEnd) {
3753
- finishInitialScroll(ctx, {
3754
- resolvedOffset: resolvedInitialContentOffset
3805
+ if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
3806
+ clearPendingInitialScrollFooterLayout(ctx, {
3807
+ dataLength,
3808
+ stylePaddingBottom,
3809
+ target: initialScroll
3810
+ });
3811
+ } else {
3812
+ const updatedInitialScroll = createInitialScrollAtEndTarget({
3813
+ dataLength,
3814
+ footerSize,
3815
+ preserveForFooterLayout: shouldPreserveInitialScrollForFooterLayout(initialScroll),
3816
+ stylePaddingBottom
3817
+ });
3818
+ const didTargetChange = initialScroll.index !== updatedInitialScroll.index || initialScroll.viewPosition !== updatedInitialScroll.viewPosition || initialScroll.viewOffset !== updatedInitialScroll.viewOffset;
3819
+ if (!didTargetChange) {
3820
+ clearPendingInitialScrollFooterLayout(ctx, {
3821
+ dataLength,
3822
+ stylePaddingBottom,
3823
+ target: initialScroll
3755
3824
  });
3756
3825
  } else {
3757
- setInitialRenderState(ctx, { didInitialScroll: true });
3826
+ const didFinishInitialScroll = !!state.didFinishInitialScroll;
3827
+ setInitialScrollTarget(state, updatedInitialScroll, {
3828
+ resetDidFinish: didFinishInitialScroll
3829
+ });
3830
+ rearmBootstrapInitialScroll(ctx, {
3831
+ scroll: resolveInitialScrollOffset(ctx, updatedInitialScroll),
3832
+ targetIndexSeed: updatedInitialScroll.index
3833
+ });
3758
3834
  }
3759
3835
  }
3760
3836
  }
3761
- function handleInitialScrollDataChange(ctx, options) {
3837
+ function handleBootstrapInitialScrollLayoutChange(ctx) {
3762
3838
  var _a3, _b, _c;
3763
- const { dataLength, didDataChange, initialScrollAtEnd, stylePaddingBottom, useBootstrapInitialScroll } = options;
3764
3839
  const state = ctx.state;
3765
- const previousDataLength = (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.previousDataLength) != null ? _b : 0;
3766
- if (state.initialScrollSession) {
3767
- state.initialScrollSession.previousDataLength = dataLength;
3840
+ const initialScroll = state.initialScroll;
3841
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
3842
+ if (initialScroll && state.props.data.length > 0 && !isOffsetInitialScrollSession(state) && (bootstrapInitialScroll || initialScroll.viewPosition === 1)) {
3843
+ const resolvedOffset = resolveInitialScrollOffset(ctx, initialScroll);
3844
+ const scrollingTo = ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) ? state.scrollingTo : void 0;
3845
+ if (!bootstrapInitialScroll && (scrollingTo || state.didFinishInitialScroll)) {
3846
+ const currentOffset = scrollingTo ? (_b = scrollingTo.targetOffset) != null ? _b : scrollingTo.offset : getObservedBootstrapInitialScrollOffset(state);
3847
+ const offsetDiff = resolvedOffset - currentOffset;
3848
+ if (Math.abs(offsetDiff) > DEFAULT_BOOTSTRAP_REVEAL_EPSILON) {
3849
+ if (state.didFinishInitialScroll) {
3850
+ schedulePreservedEndAnchorCorrection(ctx);
3851
+ } else if (scrollingTo) {
3852
+ const existingWatchdog = initialScrollWatchdog.get(state);
3853
+ scrollingTo.offset = resolvedOffset;
3854
+ scrollingTo.targetOffset = resolvedOffset;
3855
+ state.initialScroll = {
3856
+ ...initialScroll,
3857
+ contentOffset: resolvedOffset
3858
+ };
3859
+ state.hasScrolled = false;
3860
+ initialScrollWatchdog.set(state, {
3861
+ startScroll: (_c = existingWatchdog == null ? void 0 : existingWatchdog.startScroll) != null ? _c : state.scroll,
3862
+ targetOffset: resolvedOffset
3863
+ });
3864
+ requestAdjust(ctx, offsetDiff);
3865
+ }
3866
+ }
3867
+ } else {
3868
+ rearmBootstrapInitialScroll(ctx, {
3869
+ scroll: resolvedOffset,
3870
+ targetIndexSeed: initialScroll.index
3871
+ });
3872
+ }
3768
3873
  }
3769
- setInitialScrollSession(state);
3770
- if (useBootstrapInitialScroll) {
3771
- handleBootstrapInitialScrollDataChange(ctx, {
3772
- dataLength,
3773
- didDataChange,
3774
- initialScrollAtEnd,
3775
- previousDataLength,
3776
- stylePaddingBottom
3777
- });
3874
+ }
3875
+ function evaluateBootstrapInitialScroll(ctx) {
3876
+ var _a3, _b;
3877
+ const state = ctx.state;
3878
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
3879
+ const initialScroll = state.initialScroll;
3880
+ if (!bootstrapInitialScroll || !initialScroll || isOffsetInitialScrollSession(state) || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll)) {
3778
3881
  return;
3779
3882
  }
3780
- const shouldReplayFinishedOffsetInitialScroll = previousDataLength === 0 && dataLength > 0 && !!state.initialScroll && ((_c = ctx.state.initialScrollSession) == null ? void 0 : _c.kind) === "offset" && !!state.didFinishInitialScroll;
3781
- if (previousDataLength !== 0 || dataLength === 0 || !state.initialScroll || !state.queuedInitialLayout || state.didFinishInitialScroll && !shouldReplayFinishedOffsetInitialScroll) {
3883
+ bootstrapInitialScroll.passCount += 1;
3884
+ if (abortBootstrapRevealIfNeeded(ctx, {
3885
+ mountFrameCount: bootstrapInitialScroll.mountFrameCount,
3886
+ passCount: bootstrapInitialScroll.passCount
3887
+ })) {
3782
3888
  return;
3783
3889
  }
3784
- if (shouldReplayFinishedOffsetInitialScroll) {
3785
- state.didFinishInitialScroll = false;
3890
+ if (initialScroll.index !== void 0 && state.startBuffered >= 0 && state.endBuffered >= 0 && initialScroll.index >= state.startBuffered && initialScroll.index <= state.endBuffered) {
3891
+ bootstrapInitialScroll.targetIndexSeed = void 0;
3786
3892
  }
3787
- advanceCurrentInitialScrollSession(ctx);
3788
- }
3789
-
3790
- // src/core/mvcp.ts
3791
- var MVCP_POSITION_EPSILON = 0.1;
3792
- var MVCP_ANCHOR_LOCK_TTL_MS = 300;
3793
- var MVCP_ANCHOR_LOCK_QUIET_PASSES_TO_RELEASE = 2;
3794
- var NATIVE_END_CLAMP_EPSILON = 1;
3795
- function resolveAnchorLock(state, enableMVCPAnchorLock, mvcpData, now) {
3796
- if (!enableMVCPAnchorLock) {
3797
- state.mvcpAnchorLock = void 0;
3798
- return void 0;
3893
+ const resolvedOffset = resolveInitialScrollOffset(ctx, initialScroll);
3894
+ const mountedBufferedIndices = getMountedBufferedIndices(state);
3895
+ const areMountedBufferedIndicesMeasured = checkAllSizesKnown(state, mountedBufferedIndices);
3896
+ const didResolvedOffsetChange = Math.abs(bootstrapInitialScroll.scroll - resolvedOffset) > 1;
3897
+ const { data } = state.props;
3898
+ const visibleIndices = getBootstrapRevealVisibleIndices({
3899
+ dataLength: data.length,
3900
+ getSize: (index) => {
3901
+ var _a4, _b2;
3902
+ const id = (_a4 = state.idCache[index]) != null ? _a4 : getId(state, index);
3903
+ return (_b2 = state.sizes.get(id)) != null ? _b2 : getItemSize(ctx, id, index, data[index]);
3904
+ },
3905
+ offset: resolvedOffset,
3906
+ positions: state.positions,
3907
+ scrollLength: state.scrollLength,
3908
+ startIndex: (_b = bootstrapInitialScroll.targetIndexSeed) != null ? _b : state.startBuffered >= 0 ? state.startBuffered : void 0
3909
+ });
3910
+ const areVisibleIndicesMeasured = visibleIndices.length > 0 && visibleIndices.every((index) => {
3911
+ var _a4;
3912
+ const id = (_a4 = state.idCache[index]) != null ? _a4 : getId(state, index);
3913
+ return state.sizesKnown.has(id);
3914
+ });
3915
+ const previousResolvedOffset = bootstrapInitialScroll.previousResolvedOffset;
3916
+ const previousVisibleIndices = bootstrapInitialScroll.visibleIndices;
3917
+ bootstrapInitialScroll.previousResolvedOffset = resolvedOffset;
3918
+ bootstrapInitialScroll.visibleIndices = visibleIndices;
3919
+ if (didResolvedOffsetChange) {
3920
+ bootstrapInitialScroll.scroll = resolvedOffset;
3921
+ queueBootstrapInitialScrollReevaluation(state);
3922
+ return;
3799
3923
  }
3800
- const lock = state.mvcpAnchorLock;
3801
- if (!lock) {
3802
- return void 0;
3924
+ if (!areMountedBufferedIndicesMeasured || !areVisibleIndicesMeasured) {
3925
+ return;
3803
3926
  }
3804
- const isExpired = now > lock.expiresAt;
3805
- const isMissing = state.indexByKey.get(lock.id) === void 0;
3806
- if (isExpired || isMissing || !mvcpData) {
3807
- state.mvcpAnchorLock = void 0;
3808
- return void 0;
3927
+ const didRevealSettle = previousResolvedOffset !== void 0 && Math.abs(previousResolvedOffset - resolvedOffset) <= DEFAULT_BOOTSTRAP_REVEAL_EPSILON && doVisibleIndicesMatch(previousVisibleIndices, visibleIndices);
3928
+ if (!didRevealSettle) {
3929
+ queueBootstrapInitialScrollReevaluation(state);
3930
+ return;
3809
3931
  }
3810
- return lock;
3811
- }
3812
- function updateAnchorLock(state, params) {
3813
3932
  {
3814
- const { anchorId, anchorPosition, dataChanged, now, positionDiff } = params;
3815
- const enableMVCPAnchorLock = !!dataChanged || !!state.mvcpAnchorLock;
3816
- const mvcpData = state.props.maintainVisibleContentPosition.data;
3817
- if (!enableMVCPAnchorLock || !mvcpData || state.scrollingTo || !anchorId || anchorPosition === void 0) {
3933
+ clearBootstrapInitialScrollSession(state);
3934
+ dispatchInitialScroll(ctx, {
3935
+ forceScroll: true,
3936
+ resolvedOffset,
3937
+ target: initialScroll,
3938
+ waitForCompletionFrame: Platform.OS === "web"
3939
+ });
3940
+ }
3941
+ }
3942
+ function finishBootstrapInitialScrollWithoutScroll(ctx, resolvedOffset) {
3943
+ var _a3;
3944
+ const state = ctx.state;
3945
+ clearBootstrapInitialScrollSession(state);
3946
+ const shouldPreserveResizeTarget = !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_a3 = state.initialScroll) == null ? void 0 : _a3.viewPosition) === 1;
3947
+ finishInitialScroll(ctx, {
3948
+ preserveTarget: shouldPreserveResizeTarget,
3949
+ recalculateItems: true,
3950
+ resolvedOffset,
3951
+ schedulePreservedTargetClear: shouldPreserveResizeTarget
3952
+ });
3953
+ }
3954
+ function abortBootstrapInitialScroll(ctx) {
3955
+ var _a3, _b, _c, _d;
3956
+ const state = ctx.state;
3957
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
3958
+ const initialScroll = state.initialScroll;
3959
+ if (bootstrapInitialScroll && initialScroll && !isOffsetInitialScrollSession(state) && state.refScroller.current) {
3960
+ clearBootstrapInitialScrollSession(state);
3961
+ dispatchInitialScroll(ctx, {
3962
+ forceScroll: true,
3963
+ resolvedOffset: bootstrapInitialScroll.scroll,
3964
+ target: initialScroll,
3965
+ waitForCompletionFrame: Platform.OS === "web"
3966
+ });
3967
+ } else {
3968
+ finishBootstrapInitialScrollWithoutScroll(
3969
+ ctx,
3970
+ (_d = (_c = (_b = (_a3 = getBootstrapInitialScrollSession(state)) == null ? void 0 : _a3.scroll) != null ? _b : state.scrollPending) != null ? _c : state.scroll) != null ? _d : 0
3971
+ );
3972
+ }
3973
+ }
3974
+
3975
+ // src/core/checkFinishedScroll.ts
3976
+ var INITIAL_SCROLL_MAX_FALLBACK_CHECKS = 20;
3977
+ var INITIAL_SCROLL_COMPLETION_TARGET_EPSILON = 1;
3978
+ var INITIAL_SCROLL_ZERO_TARGET_EPSILON = 1;
3979
+ var SILENT_INITIAL_SCROLL_RETRY_DELAY_MS = 16;
3980
+ function checkFinishedScroll(ctx, options) {
3981
+ const scrollingTo = ctx.state.scrollingTo;
3982
+ if (options == null ? void 0 : options.onlyIfAligned) {
3983
+ if (!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) || scrollingTo.animated) {
3818
3984
  return;
3819
3985
  }
3820
- const existingLock = state.mvcpAnchorLock;
3821
- const quietPasses = !dataChanged && Math.abs(positionDiff) <= MVCP_POSITION_EPSILON && (existingLock == null ? void 0 : existingLock.id) === anchorId ? existingLock.quietPasses + 1 : 0;
3822
- if (!dataChanged && quietPasses >= MVCP_ANCHOR_LOCK_QUIET_PASSES_TO_RELEASE) {
3823
- state.mvcpAnchorLock = void 0;
3986
+ if (!getResolvedScrollCompletionState(ctx, scrollingTo).isAtResolvedTarget) {
3824
3987
  return;
3825
3988
  }
3826
- state.mvcpAnchorLock = {
3827
- expiresAt: now + MVCP_ANCHOR_LOCK_TTL_MS,
3828
- id: anchorId,
3829
- position: anchorPosition,
3830
- quietPasses
3831
- };
3832
- }
3833
- }
3834
- function shouldQueueNativeMVCPAdjust(dataChanged, state, positionDiff, prevTotalSize, prevScroll, scrollTarget) {
3835
- {
3836
- return false;
3837
3989
  }
3990
+ ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
3838
3991
  }
3839
- function getPredictedNativeClamp(state, unresolvedAmount, totalSize) {
3840
- if (Math.abs(unresolvedAmount) <= MVCP_POSITION_EPSILON) {
3841
- return 0;
3842
- }
3843
- const maxScroll = Math.max(0, totalSize - state.scrollLength);
3844
- const clampDelta = maxScroll - state.scroll;
3845
- if (unresolvedAmount < 0) {
3846
- return Math.max(unresolvedAmount, Math.min(0, clampDelta));
3847
- }
3848
- if (unresolvedAmount > 0) {
3849
- return Math.min(unresolvedAmount, Math.max(0, clampDelta));
3850
- }
3851
- return 0;
3992
+ function hasScrollCompletionOwnership(state, options) {
3993
+ const { clampedTargetOffset, scrollingTo } = options;
3994
+ return !scrollingTo.isInitialScroll || state.hasScrolled || clampedTargetOffset <= INITIAL_SCROLL_COMPLETION_TARGET_EPSILON;
3852
3995
  }
3853
- function getProgressTowardAmount(targetDelta, nativeDelta) {
3854
- return targetDelta < 0 ? -nativeDelta : nativeDelta;
3996
+ function isSilentInitialDispatch(state, scrollingTo) {
3997
+ return !!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && initialScrollCompletion.didDispatchNativeScroll(state) && !state.hasScrolled;
3855
3998
  }
3856
- function settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta) {
3857
- const state = ctx.state;
3858
- state.pendingNativeMVCPAdjust = void 0;
3859
- const remaining = remainingAfterManual - nativeDelta;
3860
- if (Math.abs(remaining) > MVCP_POSITION_EPSILON) {
3861
- requestAdjust(ctx, remaining);
3862
- }
3999
+ function getInitialScrollWatchdogTargetOffset(state) {
4000
+ var _a3;
4001
+ return (_a3 = initialScrollWatchdog.get(state)) == null ? void 0 : _a3.targetOffset;
3863
4002
  }
3864
- function maybeApplyPredictedNativeMVCPAdjust(ctx) {
3865
- const state = ctx.state;
3866
- const pending = state.pendingNativeMVCPAdjust;
3867
- if (!pending || Math.abs(pending.manualApplied) > MVCP_POSITION_EPSILON) {
3868
- return;
3869
- }
3870
- const totalSize = getContentSize(ctx);
3871
- const predictedNativeClamp = getPredictedNativeClamp(state, pending.amount, totalSize);
3872
- if (Math.abs(predictedNativeClamp) <= MVCP_POSITION_EPSILON) {
3873
- return;
3874
- }
3875
- const manualDesired = pending.amount - predictedNativeClamp;
3876
- if (Math.abs(manualDesired) <= MVCP_POSITION_EPSILON) {
3877
- return;
3878
- }
3879
- pending.manualApplied = manualDesired;
3880
- requestAdjust(ctx, manualDesired);
3881
- pending.furthestProgressTowardAmount = 0;
4003
+ function isNativeInitialNonZeroTarget(state) {
4004
+ const targetOffset = getInitialScrollWatchdogTargetOffset(state);
4005
+ return !state.didFinishInitialScroll && initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset);
3882
4006
  }
3883
- function resolvePendingNativeMVCPAdjust(ctx, newScroll) {
3884
- const state = ctx.state;
3885
- const pending = state.pendingNativeMVCPAdjust;
3886
- if (!pending) {
4007
+ function shouldFinishInitialScrollWithoutNativeProgress(state, scrollingTo) {
4008
+ var _a3, _b;
4009
+ if (!scrollingTo.isInitialScroll || scrollingTo.animated || !state.didContainersLayout) {
3887
4010
  return false;
3888
4011
  }
3889
- const remainingAfterManual = pending.amount - pending.manualApplied;
3890
- const nativeDelta = newScroll - (pending.startScroll + pending.manualApplied);
3891
- const isWrongDirection = remainingAfterManual < 0 && nativeDelta > MVCP_POSITION_EPSILON || remainingAfterManual > 0 && nativeDelta < -MVCP_POSITION_EPSILON;
3892
- const progressTowardAmount = getProgressTowardAmount(remainingAfterManual, nativeDelta);
3893
- if (Math.abs(remainingAfterManual) <= MVCP_POSITION_EPSILON) {
3894
- state.pendingNativeMVCPAdjust = void 0;
3895
- return true;
3896
- }
3897
- if (isWrongDirection) {
3898
- state.pendingNativeMVCPAdjust = void 0;
4012
+ if (((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap") {
3899
4013
  return false;
3900
4014
  }
3901
- if (progressTowardAmount + MVCP_POSITION_EPSILON >= Math.abs(remainingAfterManual)) {
3902
- settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
3903
- return true;
3904
- }
3905
- const expectedNativeClampScroll = Math.max(0, getContentSize(ctx) - state.scrollLength);
3906
- const distanceToClamp = Math.abs(newScroll - expectedNativeClampScroll);
3907
- const isAtExpectedNativeClamp = distanceToClamp <= NATIVE_END_CLAMP_EPSILON;
3908
- if (isAtExpectedNativeClamp) {
3909
- settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
3910
- return true;
3911
- }
3912
- if (state.pendingMaintainScrollAtEnd && peek$(ctx, "isWithinMaintainScrollAtEndThreshold") && progressTowardAmount > MVCP_POSITION_EPSILON) {
3913
- settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
3914
- return true;
3915
- }
3916
- if (progressTowardAmount > pending.furthestProgressTowardAmount + MVCP_POSITION_EPSILON) {
3917
- pending.furthestProgressTowardAmount = progressTowardAmount;
4015
+ const targetOffset = (_b = scrollingTo.targetOffset) != null ? _b : scrollingTo.offset;
4016
+ if (initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset) && initialScrollCompletion.didDispatchNativeScroll(state) && !state.hasScrolled) {
3918
4017
  return false;
3919
4018
  }
3920
- if (pending.furthestProgressTowardAmount > MVCP_POSITION_EPSILON && progressTowardAmount < pending.furthestProgressTowardAmount - MVCP_POSITION_EPSILON) {
3921
- state.pendingNativeMVCPAdjust = void 0;
4019
+ if (initialScrollWatchdog.isAtZeroTargetOffset(targetOffset) || Math.abs(state.scroll - targetOffset) > 1 || Math.abs(state.scrollPending - targetOffset) > 1) {
3922
4020
  return false;
3923
4021
  }
3924
- return false;
4022
+ return !!scrollingTo.waitForInitialScrollCompletionFrame || isNativeInitialNonZeroTarget(state);
3925
4023
  }
3926
- function prepareMVCP(ctx, dataChanged) {
4024
+ function shouldFinishInitialZeroTargetScroll(ctx) {
4025
+ var _a3;
4026
+ const { state } = ctx;
4027
+ return !!((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) && state.props.data.length > 0 && getContentSize(ctx) <= state.scrollLength && state.scrollPending <= INITIAL_SCROLL_ZERO_TARGET_EPSILON;
4028
+ }
4029
+ function isEndAlignedLastItemTarget(ctx, scrollingTo) {
4030
+ return scrollingTo.index === ctx.state.props.data.length - 1 && scrollingTo.viewPosition === 1;
4031
+ }
4032
+ function getCurrentTargetOffset(ctx, scrollingTo) {
4033
+ var _a3;
4034
+ const index = scrollingTo.index;
4035
+ const shouldRecomputeEndTarget = isEndAlignedLastItemTarget(ctx, scrollingTo);
4036
+ const requestedTargetOffset = shouldRecomputeEndTarget && index !== void 0 ? calculateOffsetWithOffsetPosition(ctx, calculateOffsetForIndex(ctx, index), scrollingTo) : (_a3 = scrollingTo.targetOffset) != null ? _a3 : clampScrollOffset(ctx, scrollingTo.offset - (scrollingTo.viewOffset || 0), scrollingTo);
4037
+ return clampScrollOffset(ctx, requestedTargetOffset, scrollingTo);
4038
+ }
4039
+ function getResolvedScrollCompletionState(ctx, scrollingTo) {
4040
+ const { state } = ctx;
4041
+ const scroll = state.scrollPending;
4042
+ const adjust = state.scrollAdjustHandler.getAdjust();
4043
+ const clampedTargetOffset = getCurrentTargetOffset(ctx, scrollingTo);
4044
+ const maxOffset = clampScrollOffset(ctx, scroll, scrollingTo);
4045
+ const diff1 = Math.abs(scroll - clampedTargetOffset);
4046
+ const adjustedTargetOffset = clampedTargetOffset + adjust;
4047
+ const diff2 = Math.abs(scroll - adjustedTargetOffset);
4048
+ const canUseAdjustedCompletion = !scrollingTo.animated || Platform.OS === "ios";
4049
+ return {
4050
+ clampedTargetOffset,
4051
+ isAtResolvedTarget: Math.abs(scroll - maxOffset) < 1 && (diff1 < 1 || canUseAdjustedCompletion && diff2 < 1)
4052
+ };
4053
+ }
4054
+ function checkFinishedScrollFrame(ctx) {
4055
+ const scrollingTo = ctx.state.scrollingTo;
4056
+ if (!scrollingTo) {
4057
+ return;
4058
+ }
4059
+ const { state } = ctx;
4060
+ state.animFrameCheckFinishedScroll = void 0;
4061
+ const completionState = getResolvedScrollCompletionState(ctx, scrollingTo);
4062
+ if (completionState.isAtResolvedTarget && hasScrollCompletionOwnership(state, {
4063
+ clampedTargetOffset: completionState.clampedTargetOffset,
4064
+ scrollingTo
4065
+ })) {
4066
+ finishScrollTo(ctx);
4067
+ }
4068
+ }
4069
+ function scrollToFallbackOffset(ctx, offset) {
4070
+ var _a3;
4071
+ (_a3 = ctx.state.refScroller.current) == null ? void 0 : _a3.scrollTo({
4072
+ animated: false,
4073
+ x: ctx.state.props.horizontal ? offset : 0,
4074
+ y: ctx.state.props.horizontal ? 0 : offset
4075
+ });
4076
+ }
4077
+ function checkFinishedScrollFallback(ctx) {
3927
4078
  const state = ctx.state;
3928
- const { idsInView, positions, props } = state;
3929
- const {
3930
- maintainVisibleContentPosition: { data: mvcpData, size: mvcpScroll, shouldRestorePosition }
3931
- } = props;
3932
- const now = Date.now();
3933
- const enableMVCPAnchorLock = (!!dataChanged || !!state.mvcpAnchorLock);
3934
4079
  const scrollingTo = state.scrollingTo;
3935
- const anchorLock = resolveAnchorLock(state, enableMVCPAnchorLock, mvcpData, now) ;
3936
- let prevPosition;
3937
- let targetId;
3938
- const idsInViewWithPositions = [];
3939
- const scrollTarget = scrollingTo == null ? void 0 : scrollingTo.index;
3940
- const scrollingToViewPosition = scrollingTo == null ? void 0 : scrollingTo.viewPosition;
3941
- const isEndAnchoredScrollTarget = scrollTarget !== void 0 && state.props.data.length > 0 && scrollTarget >= state.props.data.length - 1 && (scrollingToViewPosition != null ? scrollingToViewPosition : 0) > 0;
3942
- const shouldMVCP = dataChanged ? mvcpData : mvcpScroll;
3943
- const indexByKey = state.indexByKey;
3944
- const prevScroll = state.scroll;
3945
- getContentSize(ctx);
3946
- if (shouldMVCP) {
3947
- if (anchorLock && scrollTarget === void 0) {
3948
- targetId = anchorLock.id;
3949
- prevPosition = anchorLock.position;
3950
- } else if (scrollTarget !== void 0) {
3951
- targetId = getId(state, scrollTarget);
3952
- } else if (idsInView.length > 0 && state.didContainersLayout && !dataChanged) {
3953
- targetId = idsInView.find((id) => indexByKey.get(id) !== void 0);
3954
- }
3955
- if (dataChanged && idsInView.length > 0 && state.didContainersLayout) {
3956
- for (let i = 0; i < idsInView.length; i++) {
3957
- const id = idsInView[i];
3958
- const index = indexByKey.get(id);
3959
- if (index !== void 0) {
3960
- const position = positions[index];
3961
- if (position !== void 0) {
3962
- idsInViewWithPositions.push({ id, position });
3963
- }
3964
- }
3965
- }
3966
- }
3967
- if (targetId !== void 0 && prevPosition === void 0) {
3968
- const targetIndex = indexByKey.get(targetId);
3969
- if (targetIndex !== void 0) {
3970
- prevPosition = positions[targetIndex];
3971
- }
3972
- }
3973
- return () => {
3974
- let positionDiff = 0;
3975
- let anchorIdForLock = anchorLock == null ? void 0 : anchorLock.id;
3976
- let anchorPositionForLock;
3977
- let skipTargetAnchor = false;
3978
- const data = state.props.data;
3979
- const shouldValidateLockedAnchor = dataChanged && mvcpData && scrollTarget === void 0 && targetId !== void 0 && (anchorLock == null ? void 0 : anchorLock.id) === targetId && shouldRestorePosition !== void 0;
3980
- if (shouldValidateLockedAnchor && targetId !== void 0) {
3981
- const index = indexByKey.get(targetId);
3982
- if (index !== void 0) {
3983
- const item = data[index];
3984
- skipTargetAnchor = item === void 0 || !shouldRestorePosition(item, index, data);
3985
- if (skipTargetAnchor && (anchorLock == null ? void 0 : anchorLock.id) === targetId) {
3986
- state.mvcpAnchorLock = void 0;
3987
- }
3988
- }
3989
- }
3990
- const shouldUseFallbackVisibleAnchor = dataChanged && mvcpData && scrollTarget === void 0 && (() => {
3991
- if (targetId === void 0 || skipTargetAnchor) {
3992
- return true;
3993
- }
3994
- const targetIndex = indexByKey.get(targetId);
3995
- return targetIndex === void 0 || positions[targetIndex] === void 0;
3996
- })();
3997
- if (shouldUseFallbackVisibleAnchor) {
3998
- for (let i = 0; i < idsInViewWithPositions.length; i++) {
3999
- const { id, position } = idsInViewWithPositions[i];
4000
- const index = indexByKey.get(id);
4001
- if (index !== void 0 && shouldRestorePosition) {
4002
- const item = data[index];
4003
- if (item === void 0 || !shouldRestorePosition(item, index, data)) {
4004
- continue;
4005
- }
4006
- }
4007
- const newPosition = index !== void 0 ? positions[index] : void 0;
4008
- if (newPosition !== void 0) {
4009
- positionDiff = newPosition - position;
4010
- anchorIdForLock = id;
4011
- anchorPositionForLock = newPosition;
4012
- break;
4013
- }
4014
- }
4015
- }
4016
- if (!skipTargetAnchor && targetId !== void 0 && prevPosition !== void 0) {
4017
- const targetIndex = indexByKey.get(targetId);
4018
- const newPosition = targetIndex !== void 0 ? positions[targetIndex] : void 0;
4019
- if (newPosition !== void 0) {
4020
- const totalSize = getContentSize(ctx);
4021
- let diff = newPosition - prevPosition;
4022
- if (diff !== 0 && isEndAnchoredScrollTarget && state.scroll + state.scrollLength > totalSize) {
4023
- if (diff > 0) {
4024
- diff = Math.max(0, totalSize - state.scroll - state.scrollLength);
4025
- } else {
4026
- diff = 0;
4027
- }
4028
- }
4029
- positionDiff = diff;
4030
- anchorIdForLock = targetId;
4031
- anchorPositionForLock = newPosition;
4032
- }
4033
- }
4034
- if (scrollingToViewPosition && scrollingToViewPosition > 0) {
4035
- const newSize = getItemSize(ctx, targetId, scrollTarget, state.props.data[scrollTarget]);
4036
- const prevSize = scrollingTo == null ? void 0 : scrollingTo.itemSize;
4037
- if (newSize !== void 0 && prevSize !== void 0 && newSize !== prevSize) {
4038
- const diff = newSize - prevSize;
4039
- if (diff !== 0) {
4040
- positionDiff += diff * scrollingToViewPosition;
4041
- scrollingTo.itemSize = newSize;
4042
- }
4043
- }
4044
- }
4045
- updateAnchorLock(state, {
4046
- anchorId: anchorIdForLock,
4047
- anchorPosition: anchorPositionForLock,
4048
- dataChanged,
4049
- now,
4050
- positionDiff
4051
- });
4052
- if (shouldQueueNativeMVCPAdjust()) {
4053
- state.pendingNativeMVCPAdjust = {
4054
- amount: positionDiff,
4055
- furthestProgressTowardAmount: 0,
4056
- manualApplied: 0,
4057
- startScroll: prevScroll
4058
- };
4059
- maybeApplyPredictedNativeMVCPAdjust(ctx);
4060
- return;
4061
- }
4062
- if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
4063
- const shouldSkipAdjustForMaintainedEnd = state.maintainingScrollAtEnd && peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
4064
- if (!shouldSkipAdjustForMaintainedEnd) {
4065
- requestAdjust(ctx, positionDiff);
4080
+ const shouldFinishInitialZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
4081
+ const silentInitialDispatch = isSilentInitialDispatch(state, scrollingTo);
4082
+ const canFinishInitialWithoutNativeProgress = scrollingTo !== void 0 ? shouldFinishInitialScrollWithoutNativeProgress(state, scrollingTo) : false;
4083
+ const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && !shouldFinishInitialZeroTarget && !canFinishInitialWithoutNativeProgress || !state.didContainersLayout;
4084
+ const initialDelay = shouldFinishInitialZeroTarget || canFinishInitialWithoutNativeProgress ? 0 : silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : slowTimeout ? 500 : 100;
4085
+ state.timeoutCheckFinishedScrollFallback = setTimeout(() => {
4086
+ let numChecks = 0;
4087
+ const scheduleFallbackCheck = (delay) => {
4088
+ state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, delay);
4089
+ };
4090
+ const checkHasScrolled = () => {
4091
+ var _c, _d;
4092
+ state.timeoutCheckFinishedScrollFallback = void 0;
4093
+ const isStillScrollingTo = state.scrollingTo;
4094
+ if (isStillScrollingTo) {
4095
+ numChecks++;
4096
+ const isNativeInitialPending = isNativeInitialNonZeroTarget(state) && !state.hasScrolled;
4097
+ const maxChecks = silentInitialDispatch ? 5 : isNativeInitialPending ? INITIAL_SCROLL_MAX_FALLBACK_CHECKS : 5;
4098
+ const shouldFinishZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
4099
+ const canFinishInitialScrollWithoutNativeProgress = shouldFinishInitialScrollWithoutNativeProgress(
4100
+ state,
4101
+ isStillScrollingTo
4102
+ );
4103
+ const completionState = getResolvedScrollCompletionState(ctx, isStillScrollingTo);
4104
+ const canFinishAfterSilentNativeDispatch = Platform.OS === "android";
4105
+ const shouldFinishAfterObservedScroll = state.hasScrolled && (!isStillScrollingTo.isInitialScroll || completionState.isAtResolvedTarget);
4106
+ const shouldRetryUnalignedInitialScroll = isStillScrollingTo.isInitialScroll && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
4107
+ if (shouldFinishZeroTarget || shouldFinishAfterObservedScroll || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
4108
+ finishScrollTo(ctx);
4109
+ } else if ((isNativeInitialPending || shouldRetryUnalignedInitialScroll) && numChecks <= maxChecks) {
4110
+ const targetOffset = (_d = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : isStillScrollingTo.targetOffset) != null ? _d : state.scrollPending;
4111
+ scrollToFallbackOffset(ctx, targetOffset);
4112
+ scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
4113
+ } else {
4114
+ scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
4066
4115
  }
4067
4116
  }
4068
4117
  };
4118
+ checkHasScrolled();
4119
+ }, initialDelay);
4120
+ }
4121
+
4122
+ // src/core/initialScrollLifecycle.ts
4123
+ function retargetActiveInitialScrollAtEnd(ctx) {
4124
+ var _a3;
4125
+ const state = ctx.state;
4126
+ const initialScroll = state.initialScroll;
4127
+ if (state.didFinishInitialScroll) {
4128
+ return schedulePreservedEndAnchorCorrection(ctx);
4129
+ }
4130
+ if (!initialScroll || ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" || initialScroll.viewPosition !== 1 || state.props.data.length === 0) {
4131
+ return false;
4132
+ }
4133
+ return advanceCurrentInitialScrollSession(ctx, { forceScroll: true });
4134
+ }
4135
+ function handleInitialScrollLayoutReady(ctx) {
4136
+ var _a3;
4137
+ if (!ctx.state.initialScroll) {
4138
+ return;
4139
+ }
4140
+ const runScroll = () => advanceCurrentInitialScrollSession(ctx, { forceScroll: true });
4141
+ runScroll();
4142
+ if (((_a3 = ctx.state.initialScrollSession) == null ? void 0 : _a3.kind) !== "offset") {
4143
+ requestAnimationFrame(runScroll);
4144
+ }
4145
+ checkFinishedScroll(ctx, { onlyIfAligned: true });
4146
+ }
4147
+ function initializeInitialScrollOnMount(ctx, options) {
4148
+ var _a3, _b;
4149
+ const {
4150
+ alwaysDispatchInitialScroll,
4151
+ dataLength,
4152
+ hasFooterComponent,
4153
+ initialContentOffset,
4154
+ initialScrollAtEnd,
4155
+ useBootstrapInitialScroll
4156
+ } = options;
4157
+ const state = ctx.state;
4158
+ const initialScroll = state.initialScroll;
4159
+ const resolvedInitialContentOffset = initialContentOffset != null ? initialContentOffset : 0;
4160
+ const preserveForFooterLayout = useBootstrapInitialScroll && initialScrollAtEnd && hasFooterComponent;
4161
+ if (initialScroll && (initialScroll.contentOffset === void 0 || !!initialScroll.preserveForFooterLayout !== preserveForFooterLayout && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) !== "offset")) {
4162
+ setInitialScrollTarget(state, {
4163
+ ...initialScroll,
4164
+ contentOffset: resolvedInitialContentOffset,
4165
+ preserveForFooterLayout
4166
+ });
4167
+ }
4168
+ if (useBootstrapInitialScroll && initialScroll && ((_b = state.initialScrollSession) == null ? void 0 : _b.kind) !== "offset") {
4169
+ startBootstrapInitialScrollOnMount(ctx, {
4170
+ initialScrollAtEnd,
4171
+ target: state.initialScroll
4172
+ });
4173
+ return;
4174
+ }
4175
+ const hasPendingDataDependentInitialScroll = !!initialScroll && dataLength === 0 && !(resolvedInitialContentOffset === 0 && !initialScrollAtEnd);
4176
+ if (!alwaysDispatchInitialScroll && !resolvedInitialContentOffset && !hasPendingDataDependentInitialScroll) {
4177
+ if (initialScroll && !initialScrollAtEnd) {
4178
+ finishInitialScroll(ctx, {
4179
+ resolvedOffset: resolvedInitialContentOffset
4180
+ });
4181
+ } else {
4182
+ setInitialRenderState(ctx, { didInitialScroll: true });
4183
+ }
4184
+ }
4185
+ }
4186
+ function handleInitialScrollDataChange(ctx, options) {
4187
+ var _a3, _b, _c;
4188
+ const { dataLength, didDataChange, initialScrollAtEnd, stylePaddingBottom, useBootstrapInitialScroll } = options;
4189
+ const state = ctx.state;
4190
+ const previousDataLength = (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.previousDataLength) != null ? _b : 0;
4191
+ if (state.initialScrollSession) {
4192
+ state.initialScrollSession.previousDataLength = dataLength;
4193
+ }
4194
+ setInitialScrollSession(state);
4195
+ if (useBootstrapInitialScroll) {
4196
+ handleBootstrapInitialScrollDataChange(ctx, {
4197
+ dataLength,
4198
+ didDataChange,
4199
+ initialScrollAtEnd,
4200
+ previousDataLength,
4201
+ stylePaddingBottom
4202
+ });
4203
+ return;
4204
+ }
4205
+ const shouldReplayFinishedOffsetInitialScroll = previousDataLength === 0 && dataLength > 0 && !!state.initialScroll && ((_c = ctx.state.initialScrollSession) == null ? void 0 : _c.kind) === "offset" && !!state.didFinishInitialScroll;
4206
+ if (previousDataLength !== 0 || dataLength === 0 || !state.initialScroll || !state.queuedInitialLayout || state.didFinishInitialScroll && !shouldReplayFinishedOffsetInitialScroll) {
4207
+ return;
4208
+ }
4209
+ if (shouldReplayFinishedOffsetInitialScroll) {
4210
+ state.didFinishInitialScroll = false;
4069
4211
  }
4212
+ advanceCurrentInitialScrollSession(ctx);
4070
4213
  }
4071
4214
 
4072
4215
  // src/core/resetLayoutCachesForDataChange.ts
@@ -5352,62 +5495,6 @@ function calculateItemsInView(ctx, params = {}) {
5352
5495
  });
5353
5496
  }
5354
5497
 
5355
- // src/core/doMaintainScrollAtEnd.ts
5356
- function doMaintainScrollAtEnd(ctx) {
5357
- const state = ctx.state;
5358
- const {
5359
- didContainersLayout,
5360
- pendingNativeMVCPAdjust,
5361
- refScroller,
5362
- props: { maintainScrollAtEnd }
5363
- } = state;
5364
- const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
5365
- const shouldMaintainScrollAtEnd = !!(isWithinMaintainScrollAtEndThreshold && maintainScrollAtEnd && didContainersLayout);
5366
- if (pendingNativeMVCPAdjust) {
5367
- state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
5368
- return false;
5369
- }
5370
- state.pendingMaintainScrollAtEnd = false;
5371
- if (shouldMaintainScrollAtEnd) {
5372
- const contentSize = getContentSize(ctx);
5373
- if (contentSize < state.scrollLength) {
5374
- state.scroll = 0;
5375
- }
5376
- if (!state.maintainingScrollAtEnd) {
5377
- state.maintainingScrollAtEnd = true;
5378
- requestAnimationFrame(() => {
5379
- if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
5380
- const scroller = refScroller.current;
5381
- if (state.props.horizontal && isHorizontalRTL(state)) {
5382
- const currentContentSize = getContentSize(ctx);
5383
- const logicalEndOffset = getLogicalHorizontalMaxOffset(state, currentContentSize);
5384
- const nativeOffset = toNativeHorizontalOffset(state, logicalEndOffset, currentContentSize);
5385
- scroller == null ? void 0 : scroller.scrollTo({
5386
- animated: maintainScrollAtEnd.animated,
5387
- x: nativeOffset,
5388
- y: 0
5389
- });
5390
- } else {
5391
- scroller == null ? void 0 : scroller.scrollToEnd({
5392
- animated: maintainScrollAtEnd.animated
5393
- });
5394
- }
5395
- setTimeout(
5396
- () => {
5397
- state.maintainingScrollAtEnd = false;
5398
- },
5399
- maintainScrollAtEnd.animated ? 500 : 0
5400
- );
5401
- } else {
5402
- state.maintainingScrollAtEnd = false;
5403
- }
5404
- });
5405
- }
5406
- return true;
5407
- }
5408
- return false;
5409
- }
5410
-
5411
5498
  // src/core/checkResetContainers.ts
5412
5499
  function checkResetContainers(ctx, dataProp, { didColumnsChange = false } = {}) {
5413
5500
  const state = ctx.state;
@@ -5592,79 +5679,6 @@ function handleLayout(ctx, layoutParam, setCanRender) {
5592
5679
  setCanRender(true);
5593
5680
  }
5594
5681
 
5595
- // src/core/updateScroll.ts
5596
- function updateScroll(ctx, newScroll, forceUpdate, options) {
5597
- var _a3;
5598
- const state = ctx.state;
5599
- const { ignoreScrollFromMVCP, lastScrollAdjustForHistory, scrollAdjustHandler, scrollHistory, scrollingTo } = state;
5600
- const prevScroll = state.scroll;
5601
- if ((options == null ? void 0 : options.markHasScrolled) !== false) {
5602
- state.hasScrolled = true;
5603
- }
5604
- state.lastBatchingAction = Date.now();
5605
- const currentTime = Date.now();
5606
- const adjust = scrollAdjustHandler.getAdjust();
5607
- const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
5608
- if (adjustChanged) {
5609
- scrollHistory.length = 0;
5610
- }
5611
- state.lastScrollAdjustForHistory = adjust;
5612
- if (scrollingTo === void 0 && !(scrollHistory.length === 0 && newScroll === state.scroll)) {
5613
- if (!adjustChanged) {
5614
- scrollHistory.push({ scroll: newScroll, time: currentTime });
5615
- }
5616
- }
5617
- if (scrollHistory.length > 5) {
5618
- scrollHistory.shift();
5619
- }
5620
- if (ignoreScrollFromMVCP && !scrollingTo) {
5621
- const { lt, gt } = ignoreScrollFromMVCP;
5622
- if (lt && newScroll < lt || gt && newScroll > gt) {
5623
- state.ignoreScrollFromMVCPIgnored = true;
5624
- return;
5625
- }
5626
- }
5627
- state.scrollPrev = prevScroll;
5628
- state.scrollPrevTime = state.scrollTime;
5629
- state.scroll = newScroll;
5630
- state.scrollTime = currentTime;
5631
- const scrollDelta = Math.abs(newScroll - prevScroll);
5632
- const didResolvePendingNativeMVCPAdjust = resolvePendingNativeMVCPAdjust(ctx, newScroll);
5633
- const scrollLength = state.scrollLength;
5634
- const lastCalculated = state.scrollLastCalculate;
5635
- const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
5636
- const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
5637
- if (shouldUpdate) {
5638
- state.scrollLastCalculate = state.scroll;
5639
- state.ignoreScrollFromMVCPIgnored = false;
5640
- state.lastScrollDelta = scrollDelta;
5641
- const runCalculateItems = () => {
5642
- var _a4;
5643
- (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { doMVCP: scrollingTo !== void 0 });
5644
- checkThresholds(ctx);
5645
- };
5646
- if (scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength && !state.pendingNativeMVCPAdjust) {
5647
- state.mvcpAnchorLock = void 0;
5648
- state.pendingNativeMVCPAdjust = void 0;
5649
- state.userScrollAnchorResetKeys = /* @__PURE__ */ new Set();
5650
- if (state.queuedMVCPRecalculate !== void 0) {
5651
- cancelAnimationFrame(state.queuedMVCPRecalculate);
5652
- state.queuedMVCPRecalculate = void 0;
5653
- }
5654
- ReactDOM.flushSync(runCalculateItems);
5655
- } else {
5656
- runCalculateItems();
5657
- }
5658
- const shouldMaintainScrollAtEndAfterPendingSettle = !!state.pendingMaintainScrollAtEnd || !!((_a3 = state.props.maintainScrollAtEnd) == null ? void 0 : _a3.onDataChange);
5659
- if (didResolvePendingNativeMVCPAdjust && shouldMaintainScrollAtEndAfterPendingSettle) {
5660
- state.pendingMaintainScrollAtEnd = false;
5661
- doMaintainScrollAtEnd(ctx);
5662
- }
5663
- state.dataChangeNeedsScrollUpdate = false;
5664
- state.lastScrollDelta = 0;
5665
- }
5666
- }
5667
-
5668
5682
  // src/core/onScroll.ts
5669
5683
  function trackInitialScrollNativeProgress(state, newScroll) {
5670
5684
  const initialNativeScrollWatchdog = initialScrollWatchdog.get(state);