@legendapp/list 3.7.0 → 3.7.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.
package/react-native.js CHANGED
@@ -336,27 +336,20 @@ function getContentInsetEnd(ctx, contentInsetEndAdjustmentOverride) {
336
336
  const horizontal = props.horizontal;
337
337
  const contentInset = Platform.OS === "ios" ? props.contentInset : void 0;
338
338
  const baseInset = contentInset != null ? contentInset : state.nativeContentInset;
339
- const baseEndInset = (horizontal ? getHorizontalInsetEnd(state, baseInset) : baseInset == null ? void 0 : baseInset.bottom) || 0;
340
339
  const contentInsetEndAdjustment = getContentInsetEndAdjustmentEnd(
341
340
  contentInsetEndAdjustmentOverride != null ? contentInsetEndAdjustmentOverride : props.contentInsetEndAdjustment
342
341
  );
343
342
  const anchoredEndSpaceSize = peek$(ctx, "anchoredEndSpaceSize");
344
343
  const anchoredEndInset = props.anchoredEndSpace && anchoredEndSpaceSize ? anchoredEndSpaceSize : 0;
345
344
  const overrideInset = (_a3 = state.contentInsetOverride) != null ? _a3 : void 0;
346
- const adjustedBaseEndInset = baseEndInset + contentInsetEndAdjustment;
347
- if (overrideInset) {
348
- const mergedInset = { bottom: 0, left: 0, right: 0, ...baseInset, ...overrideInset };
349
- return Math.max(
350
- ((horizontal ? getHorizontalInsetEnd(state, mergedInset) : mergedInset.bottom) || 0) + contentInsetEndAdjustment,
351
- anchoredEndInset
352
- );
353
- }
354
- return Math.max(adjustedBaseEndInset, anchoredEndInset);
345
+ const mergedInset = overrideInset ? { bottom: 0, left: 0, right: 0, ...baseInset, ...overrideInset } : baseInset;
346
+ const endInset = ((horizontal ? getHorizontalInsetEnd(state, mergedInset) : mergedInset == null ? void 0 : mergedInset.bottom) || 0) + contentInsetEndAdjustment;
347
+ return Platform.OS !== "web" && props.anchoredEndSpaceOwner === "list" && !isHorizontalRTL(state) ? endInset + anchoredEndInset : Math.max(endInset, anchoredEndInset);
355
348
  }
356
349
 
357
350
  // src/state/getContentSize.ts
358
- function getContentSize(ctx) {
359
- var _a3, _b;
351
+ function getContentSize(ctx, includeContentContainerMinSize = true) {
352
+ var _a3, _b, _c;
360
353
  const { values, state } = ctx;
361
354
  const stylePaddingStart = state.props.horizontal ? state.props.stylePaddingLeft || 0 : values.get("stylePaddingTop") || 0;
362
355
  const stylePaddingEnd = state.props.horizontal ? state.props.stylePaddingRight || 0 : state.props.stylePaddingBottom || 0;
@@ -366,7 +359,10 @@ function getContentSize(ctx) {
366
359
  const contentInsetEnd = getContentInsetEnd(ctx);
367
360
  const totalSize = (_b = (_a3 = state.pendingTotalSize) != null ? _a3 : state.totalSize) != null ? _b : values.get("totalSize");
368
361
  const layoutSize = Math.max(0, totalSize - (state.props.data.length > 0 ? ctx.scrollAxisGap : 0));
369
- return headerSize + footerSize + layoutSize + stylePaddingStart + alignItemsAtEndPadding + stylePaddingEnd + (contentInsetEnd || 0);
362
+ const contentSize = headerSize + footerSize + layoutSize + stylePaddingStart + alignItemsAtEndPadding + stylePaddingEnd;
363
+ const inContentInset = Platform.OS === "web" || isHorizontalRTL(state) ? contentInsetEnd : state.props.anchoredEndSpace && state.props.anchoredEndSpaceOwner === "list" ? values.get("anchoredEndSpaceSize") || 0 : 0;
364
+ const containerSize = contentSize + inContentInset;
365
+ return (includeContentContainerMinSize ? Math.max(containerSize, (_c = state.props.contentContainerMinSize) != null ? _c : 0) : containerSize) + contentInsetEnd - inContentInset;
370
366
  }
371
367
 
372
368
  // src/components/DebugView.tsx
@@ -521,20 +517,240 @@ var USER_MOMENTUM_STALL_TIMEOUT_MS = 500;
521
517
  var ENABLE_DEVMODE = IS_DEV && false;
522
518
  var ENABLE_DEBUG_VIEW = IS_DEV && false;
523
519
 
524
- // src/core/calculateOffsetForIndex.ts
525
- function calculateOffsetForIndex(ctx, index) {
520
+ // src/core/cancelImperativeScroll.ts
521
+ function cancelScrollCompletionChecks({ scheduledWork }) {
522
+ scheduledWork.cancel("checkFinishedScrollFrame");
523
+ scheduledWork.cancel("checkFinishedScrollRetryFrame");
524
+ scheduledWork.cancel("checkFinishedScrollFallback");
525
+ scheduledWork.cancel("platformScrollCompletion");
526
+ }
527
+ function syncScrollActive(ctx) {
528
+ const { state } = ctx;
529
+ set$(
530
+ ctx,
531
+ "isScrollActive",
532
+ !!state.isDragging || !!state.isMomentumScrolling || !!state.scrollingTo || !!state.pendingScrollResolve
533
+ );
534
+ }
535
+ function settlePendingImperativeScroll(state) {
536
+ const resolvePendingScroll = state.pendingScrollResolve;
537
+ state.pendingScrollResolve = void 0;
538
+ state.pendingScrollToEnd = void 0;
539
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
540
+ }
541
+ function cancelImperativeScroll(state) {
542
+ cancelScrollCompletionChecks(state);
543
+ state.scheduledWork.cancel("imperativeScrollReady");
544
+ state.scrollingTo = void 0;
545
+ state.scrollTargetPinnedRange = void 0;
546
+ settlePendingImperativeScroll(state);
547
+ }
548
+
549
+ // src/core/clampScrollOffset.ts
550
+ function getContentInsetTop(ctx) {
551
+ var _a3, _b, _c, _d;
526
552
  const state = ctx.state;
527
- return index !== void 0 ? state.positions[index] || 0 : 0;
553
+ if (Platform.OS !== "ios" || state.props.horizontal) {
554
+ return 0;
555
+ }
556
+ const baseInset = (_a3 = state.props.contentInset) != null ? _a3 : state.nativeContentInset;
557
+ return Math.max(0, (_d = (_c = (_b = state.contentInsetOverride) == null ? void 0 : _b.top) != null ? _c : baseInset == null ? void 0 : baseInset.top) != null ? _d : 0);
558
+ }
559
+ function getScrollRangeEnd(ctx, contentSize = getContentSize(ctx)) {
560
+ return Math.max(0 - getContentInsetTop(ctx), contentSize - ctx.state.scrollLength);
561
+ }
562
+ function clampScrollOffset(ctx, offset, scrollTarget) {
563
+ var _a3;
564
+ const state = ctx.state;
565
+ const minOffset = 0 - getContentInsetTop(ctx);
566
+ const contentSize = getContentSize(ctx, !scrollTarget || !!scrollTarget.clampToContentContainerMinSize);
567
+ let clampedOffset = offset;
568
+ if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform.OS !== "android" || state.lastLayout)) {
569
+ const baseMaxOffset = getScrollRangeEnd(ctx, contentSize);
570
+ const viewOffset = Platform.OS === "web" && (scrollTarget == null ? void 0 : scrollTarget.isInitialScroll) ? void 0 : scrollTarget == null ? void 0 : scrollTarget.viewOffset;
571
+ const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
572
+ const isRawOffset = (scrollTarget == null ? void 0 : scrollTarget.offset) !== void 0 && (scrollTarget.index === void 0 || scrollTarget.isInitialScroll && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset");
573
+ const maxOffset = Math.max(isRawOffset ? 0 : minOffset, baseMaxOffset + extraEndOffset);
574
+ clampedOffset = Math.min(offset, maxOffset);
575
+ }
576
+ return Math.max(minOffset, clampedOffset);
528
577
  }
529
578
 
530
- // src/core/getStartOffsetAdjustment.ts
531
- function getStartOffsetAdjustment(ctx) {
579
+ // src/core/doScrollParams.ts
580
+ function getNativeScrollPoint(ctx, offset, horizontal = ctx.state.props.horizontal) {
581
+ var _a3;
532
582
  const { state } = ctx;
533
- const stylePaddingStart = state.props.horizontal ? (isHorizontalRTL(state) ? state.props.stylePaddingRight : state.props.stylePaddingLeft) || 0 : peek$(ctx, "stylePaddingTop") || 0;
534
- return stylePaddingStart + (peek$(ctx, "alignItemsAtEndPadding") || 0) + (peek$(ctx, "headerSize") || 0);
583
+ return horizontal ? {
584
+ x: toNativeHorizontalOffset(
585
+ state,
586
+ offset,
587
+ Math.max(getContentSize(ctx), (_a3 = state.props.contentContainerMinSize) != null ? _a3 : 0)
588
+ ),
589
+ y: 0
590
+ } : { x: 0, y: offset };
535
591
  }
536
- function getItemSpaceOffset(ctx, scrollOffset, pending = ((_a3) => (_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0)(), startOffset = getStartOffsetAdjustment(ctx)) {
537
- return Math.round(scrollOffset + (pending - startOffset));
592
+
593
+ // src/core/deferredPublicOnScroll.ts
594
+ function withResolvedContentOffset(state, event, resolvedOffset) {
595
+ return {
596
+ ...event,
597
+ nativeEvent: {
598
+ ...event.nativeEvent,
599
+ contentOffset: state.props.horizontal ? { x: resolvedOffset, y: 0 } : { x: 0, y: resolvedOffset }
600
+ }
601
+ };
602
+ }
603
+ function releaseDeferredPublicOnScroll(ctx, resolvedOffset) {
604
+ var _a3, _b, _c, _d;
605
+ const state = ctx.state;
606
+ const deferredEvent = state.deferredPublicOnScrollEvent;
607
+ state.deferredPublicOnScrollEvent = void 0;
608
+ if (deferredEvent) {
609
+ (_d = (_c = state.props).onScroll) == null ? void 0 : _d.call(
610
+ _c,
611
+ withResolvedContentOffset(
612
+ state,
613
+ deferredEvent,
614
+ (_b = (_a3 = resolvedOffset != null ? resolvedOffset : state.scrollPending) != null ? _a3 : state.scroll) != null ? _b : 0
615
+ )
616
+ );
617
+ }
618
+ }
619
+
620
+ // src/core/endOfContentTarget.ts
621
+ function getEndAlignedViewOffset(stylePaddingEnd, footerSize = 0) {
622
+ return -stylePaddingEnd - footerSize;
623
+ }
624
+ function isEndOfContentTarget(target, dataLength) {
625
+ return !!target && dataLength > 0 && target.viewPosition === 1 && !!target.preserveForBottomPadding && target.index !== void 0 && target.index >= dataLength - 1;
626
+ }
627
+
628
+ // src/core/initialScrollSession.ts
629
+ var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
630
+ function hasInitialScrollSessionCompletion(completion) {
631
+ return !!((completion == null ? void 0 : completion.didDispatchNativeScroll) || (completion == null ? void 0 : completion.didRetrySilentInitialScroll) || (completion == null ? void 0 : completion.watchdog));
632
+ }
633
+ function clearInitialScrollSession(state) {
634
+ state.initialScrollSession = void 0;
635
+ return void 0;
636
+ }
637
+ function createInitialScrollSession(options) {
638
+ const { bootstrap, completion, kind, previousDataLength } = options;
639
+ return kind === "offset" ? {
640
+ completion,
641
+ kind,
642
+ previousDataLength
643
+ } : {
644
+ bootstrap,
645
+ completion,
646
+ kind,
647
+ previousDataLength
648
+ };
649
+ }
650
+ function ensureInitialScrollSessionCompletion(state, kind = ((_b) => (_b = ((_a3) => (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind)()) != null ? _b : "bootstrap")()) {
651
+ var _a4, _b2;
652
+ if (!state.initialScrollSession) {
653
+ state.initialScrollSession = createInitialScrollSession({
654
+ completion: {},
655
+ kind,
656
+ previousDataLength: 0
657
+ });
658
+ } else if (state.initialScrollSession.kind !== kind) {
659
+ state.initialScrollSession = createInitialScrollSession({
660
+ bootstrap: state.initialScrollSession.kind === "bootstrap" ? state.initialScrollSession.bootstrap : void 0,
661
+ completion: state.initialScrollSession.completion,
662
+ kind,
663
+ previousDataLength: state.initialScrollSession.previousDataLength
664
+ });
665
+ }
666
+ (_b2 = (_a4 = state.initialScrollSession).completion) != null ? _b2 : _a4.completion = {};
667
+ return state.initialScrollSession.completion;
668
+ }
669
+ var initialScrollCompletion = {
670
+ didDispatchNativeScroll(state) {
671
+ var _a3, _b;
672
+ return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didDispatchNativeScroll);
673
+ },
674
+ didRetrySilentInitialScroll(state) {
675
+ var _a3, _b;
676
+ return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didRetrySilentInitialScroll);
677
+ },
678
+ markInitialScrollNativeDispatch(state) {
679
+ ensureInitialScrollSessionCompletion(state).didDispatchNativeScroll = true;
680
+ },
681
+ markSilentInitialScrollRetry(state) {
682
+ ensureInitialScrollSessionCompletion(state).didRetrySilentInitialScroll = true;
683
+ },
684
+ resetFlags(state) {
685
+ if (!state.initialScrollSession) {
686
+ return;
687
+ }
688
+ const completion = ensureInitialScrollSessionCompletion(state, state.initialScrollSession.kind);
689
+ completion.didDispatchNativeScroll = void 0;
690
+ completion.didRetrySilentInitialScroll = void 0;
691
+ }
692
+ };
693
+ var initialScrollWatchdog = {
694
+ clear(state) {
695
+ initialScrollWatchdog.set(state, void 0);
696
+ },
697
+ didReachTarget(newScroll, watchdog) {
698
+ const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
699
+ return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
700
+ },
701
+ get(state) {
702
+ var _a3, _b;
703
+ return (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog;
704
+ },
705
+ hasNonZeroTargetOffset(targetOffset) {
706
+ return targetOffset !== void 0 && targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
707
+ },
708
+ isAtZeroTargetOffset(targetOffset) {
709
+ return targetOffset <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
710
+ },
711
+ set(state, watchdog) {
712
+ var _a3, _b;
713
+ if (!watchdog && !((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog)) {
714
+ return;
715
+ }
716
+ const completion = ensureInitialScrollSessionCompletion(state);
717
+ completion.watchdog = watchdog ? {
718
+ startScroll: watchdog.startScroll,
719
+ targetOffset: watchdog.targetOffset
720
+ } : void 0;
721
+ }
722
+ };
723
+ function setInitialScrollSession(state, options = {}) {
724
+ var _a3, _b, _c, _d;
725
+ const existingSession = state.initialScrollSession;
726
+ const kind = (_a3 = options.kind) != null ? _a3 : existingSession == null ? void 0 : existingSession.kind;
727
+ const completion = existingSession == null ? void 0 : existingSession.completion;
728
+ const existingBootstrap = (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0;
729
+ const bootstrap = kind === "bootstrap" ? options.bootstrap === null ? void 0 : (_b = options.bootstrap) != null ? _b : existingBootstrap : void 0;
730
+ if (!kind) {
731
+ return clearInitialScrollSession(state);
732
+ }
733
+ if (!state.initialScroll && !bootstrap && !hasInitialScrollSessionCompletion(completion)) {
734
+ return clearInitialScrollSession(state);
735
+ }
736
+ const previousDataLength = (_d = (_c = options.previousDataLength) != null ? _c : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _d : 0;
737
+ state.initialScrollSession = createInitialScrollSession({
738
+ bootstrap,
739
+ completion,
740
+ kind,
741
+ previousDataLength
742
+ });
743
+ return state.initialScrollSession;
744
+ }
745
+
746
+ // src/core/observedScroll.ts
747
+ function getObservedScrollOffset(ctx, fallbackOffset = ctx.state.scroll) {
748
+ var _a3, _b, _c;
749
+ const { state } = ctx;
750
+ const scroller = state.refScroller.current;
751
+ const observed = (_a3 = scroller == null ? void 0 : scroller.getCurrentScrollOffset) == null ? void 0 : _a3.call(scroller);
752
+ if (observed === void 0) return (_b = state.lastNativeScroll) != null ? _b : fallbackOffset;
753
+ return state.props.horizontal ? peekLogicalHorizontalOffset(state, observed, (_c = scroller.getMaxScrollOffset) == null ? void 0 : _c.call(scroller)) : observed;
538
754
  }
539
755
 
540
756
  // src/core/updateContentMetricsState.ts
@@ -546,7 +762,7 @@ function getRawContentLength(ctx) {
546
762
  function getAlignItemsAtEndPadding(ctx) {
547
763
  const { state } = ctx;
548
764
  const shouldPad = !!state.props.alignItemsAtEndPaddingEnabled && !state.props.horizontal && state.props.data.length > 0 && state.scrollLength > 0;
549
- return shouldPad ? Math.max(0, state.scrollLength - getRawContentLength(ctx) - getContentInsetEnd(ctx)) : 0;
765
+ return shouldPad ? Math.max(0, state.scrollLength - getRawContentLength(ctx) - getContentInsetTop(ctx) - getContentInsetEnd(ctx)) : 0;
550
766
  }
551
767
  function updateContentMetricsState(ctx) {
552
768
  var _a3;
@@ -568,1012 +784,785 @@ function updateContentMetricsState(ctx) {
568
784
  }
569
785
  }
570
786
 
571
- // src/core/addTotalSize.ts
572
- function addTotalSize(ctx, key, add, notifyTotalSize = true) {
787
+ // src/utils/requestAdjust.ts
788
+ function requestAdjust(ctx, positionDiff, source) {
789
+ var _a3;
573
790
  const state = ctx.state;
574
- const prevTotalSize = state.totalSize;
575
- let totalSize = state.totalSize;
576
- if (key === null) {
577
- totalSize = add;
578
- if (state.timeoutSetPaddingTop) {
579
- clearTimeout(state.timeoutSetPaddingTop);
580
- state.timeoutSetPaddingTop = void 0;
581
- }
582
- } else {
583
- totalSize += add;
584
- }
585
- if (prevTotalSize !== totalSize) {
586
- if (!IsNewArchitecture && state.initialScroll && totalSize < prevTotalSize) {
587
- state.pendingTotalSize = totalSize;
588
- } else {
589
- state.pendingTotalSize = void 0;
590
- state.totalSize = totalSize;
591
- if (notifyTotalSize) {
592
- set$(ctx, "totalSize", totalSize);
593
- }
594
- updateContentMetricsState(ctx);
791
+ if (Math.abs(positionDiff) > 0.1) {
792
+ state.layoutAdjustmentEpoch = ((_a3 = state.layoutAdjustmentEpoch) != null ? _a3 : 0) + 1;
793
+ const dataChanged = source === "data";
794
+ const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
795
+ const doit = () => {
796
+ if (needsScrollWorkaround) {
797
+ ctx.doScrollTo({ horizontal: state.props.horizontal, isAdjustment: true, offset: state.scroll });
798
+ } else {
799
+ state.scrollAdjustHandler.requestAdjust(positionDiff);
800
+ if (state.adjustingFromInitialMount) {
801
+ state.adjustingFromInitialMount--;
802
+ }
803
+ }
804
+ };
805
+ state.scroll += positionDiff;
806
+ if (state.userScrollContentSize !== void 0) {
807
+ state.userScrollContentSize += positionDiff;
808
+ }
809
+ state.scrollForNextCalculateItemsInView = void 0;
810
+ const readyToRender = peek$(ctx, "readyToRender");
811
+ if (readyToRender) {
812
+ doit();
813
+ if (Platform.OS !== "web" && source !== "item-size") {
814
+ const threshold = state.scroll - positionDiff / 2;
815
+ if (!state.ignoreScrollFromMVCP) {
816
+ state.ignoreScrollFromMVCP = {};
817
+ }
818
+ if (positionDiff > 0) {
819
+ state.ignoreScrollFromMVCP.lt = threshold;
820
+ } else {
821
+ state.ignoreScrollFromMVCP.gt = threshold;
822
+ }
823
+ const delay = needsScrollWorkaround ? 250 : 100;
824
+ state.scheduledWork.timeout(
825
+ () => {
826
+ var _a4;
827
+ state.ignoreScrollFromMVCP = void 0;
828
+ const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
829
+ if (shouldForceUpdate) {
830
+ state.ignoreScrollFromMVCPIgnored = false;
831
+ state.scrollPending = state.scroll;
832
+ (_a4 = state.reprocessCurrentScroll) == null ? void 0 : _a4.call(state);
833
+ }
834
+ },
835
+ delay,
836
+ "ignoreScrollFromMVCP"
837
+ );
838
+ }
839
+ } else {
840
+ state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
841
+ requestAnimationFrame(doit);
595
842
  }
596
- } else if (notifyTotalSize && ctx.values.get("totalSize") !== totalSize) {
597
- set$(ctx, "totalSize", totalSize);
598
843
  }
599
844
  }
600
845
 
601
- // src/core/setSize.ts
602
- function setSize(ctx, itemKey, size, notifyTotalSize = true) {
603
- const state = ctx.state;
604
- const { sizes } = state;
605
- const previousSize = sizes.get(itemKey);
606
- const diff = previousSize !== void 0 ? size - previousSize : size;
607
- if (diff !== 0) {
608
- state.positionsAreCurrent = false;
609
- addTotalSize(ctx, itemKey, diff, notifyTotalSize);
846
+ // src/core/finishMaintainScrollAtEnd.ts
847
+ function interruptMaintainScrollAtEnd(ctx) {
848
+ var _a3;
849
+ const { state } = ctx;
850
+ const maintaining = state.maintainingScrollAtEnd;
851
+ state.endFollowInterrupted = true;
852
+ set$(ctx, "isFollowingEnd", false);
853
+ if (maintaining === "animated" || maintaining === "instant" || maintaining && ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isScrollToEnd)) {
854
+ cancelImperativeScroll(state);
855
+ syncScrollActive(ctx);
610
856
  }
611
- sizes.set(itemKey, size);
612
- }
613
-
614
- // src/utils/helpers.ts
615
- function isFunction(obj) {
616
- return typeof obj === "function";
617
- }
618
- function isArray(obj) {
619
- return Array.isArray(obj);
620
- }
621
- var warned = /* @__PURE__ */ new Set();
622
- function warnDevOnce(id, text) {
623
- if (IS_DEV && !warned.has(id)) {
624
- warned.add(id);
625
- console.warn(`[legend-list] ${text}`);
857
+ if (maintaining || state.pendingMaintainScrollAtEnd) {
858
+ finishMaintainScrollAtEnd(ctx);
626
859
  }
627
860
  }
628
- function roundSize(size) {
629
- return Math.floor(size * 8) / 8;
630
- }
631
- function isNullOrUndefined(value) {
632
- return value === null || value === void 0;
633
- }
634
- function getPadding(s, type) {
635
- var _a3, _b, _c;
636
- const axisPadding = type === "Left" || type === "Right" ? s.paddingHorizontal : s.paddingVertical;
637
- return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : axisPadding) != null ? _b : s.padding) != null ? _c : 0;
861
+ function armAppScrollRearm(ctx) {
862
+ ctx.state.scheduledWork.timeout(
863
+ () => rearmEndFollowIfSettled(ctx),
864
+ USER_MOMENTUM_STALL_TIMEOUT_MS,
865
+ "appScrollRearm"
866
+ );
638
867
  }
639
- function extractPadding(style, contentContainerStyle, type) {
640
- return getPadding(style, type) + getPadding(contentContainerStyle, type);
868
+ function rearmEndFollowIfSettled(ctx) {
869
+ const { state } = ctx;
870
+ if (state.endFollowInterrupted && (!state.visibleContentAnchor || state.visibleContentAnchor.sizeOnly) && !state.isDragging && state.userScrollContentSize === void 0 && !state.scheduledWork.has("appScrollRearm") && !state.scrollingTo && !state.pendingScrollResolve && peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
871
+ state.endFollowInterrupted = false;
872
+ if (state.props.maintainScrollAtEnd) set$(ctx, "isFollowingEnd", true);
873
+ }
641
874
  }
642
- function findContainerId(ctx, key) {
875
+ function finishMaintainScrollAtEnd(ctx) {
643
876
  var _a3, _b;
644
- const directMatch = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.containerItemKeys) == null ? void 0 : _b.get(key);
645
- if (directMatch !== void 0) {
646
- return directMatch;
877
+ const { state } = ctx;
878
+ const currentPadding = peek$(ctx, "alignItemsAtEndPadding") || 0;
879
+ const nextPadding = getAlignItemsAtEndPadding(ctx);
880
+ state.maintainingScrollAtEnd = void 0;
881
+ state.pendingMaintainScrollAtEnd = false;
882
+ for (const key of (_a3 = state.pendingEndMeasurements) != null ? _a3 : []) {
883
+ if (!state.containerItemKeys.has(key)) state.pendingEndMeasurements.delete(key);
647
884
  }
648
- const numContainers = peek$(ctx, "numContainers");
649
- for (let i = 0; i < numContainers; i++) {
650
- const itemKey = peek$(ctx, `containerItemKey${i}`);
651
- if (itemKey === key) {
652
- return i;
653
- }
885
+ if (currentPadding !== nextPadding) {
886
+ set$(ctx, "alignItemsAtEndPadding", nextPadding);
887
+ state.scrollForNextCalculateItemsInView = void 0;
888
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
889
+ const nextScroll = clampScrollOffset(ctx, state.scroll + nextPadding - currentPadding);
890
+ const nativeScroll = Platform.OS === "ios" ? clampScrollOffset(ctx, state.scroll) : state.scroll;
891
+ state.scroll = nativeScroll;
892
+ requestAdjust(ctx, nextScroll - nativeScroll);
654
893
  }
655
- return -1;
656
894
  }
657
895
 
658
- // src/utils/getItemSize.ts
659
- function getKnownOrFixedSize(ctx, key, index, data, resolved) {
660
- var _a3, _b, _c, _d;
661
- const state = ctx.state;
662
- const { getFixedItemSize, getItemType } = state.props;
663
- let size = key ? state.sizesKnown.get(key) : void 0;
664
- if (size === void 0 && key && getFixedItemSize) {
665
- const itemType = (_b = resolved == null ? void 0 : resolved.itemType) != null ? _b : getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
666
- const fixedSize = (resolved == null ? void 0 : resolved.didResolveFixedItemSize) ? resolved.fixedItemSize : getFixedItemSize(data, index, itemType);
667
- if (fixedSize !== void 0) {
668
- size = fixedSize + ctx.scrollAxisGap;
669
- state.positionsAreCurrent = false;
670
- state.positionRecalculationStartIndex = Math.min((_c = state.positionRecalculationStartIndex) != null ? _c : index, index);
671
- state.scrollForNextCalculateItemsInView = void 0;
672
- state.sizesKnown.set(key, size);
673
- (_d = state.fixedItemSizeKeys) != null ? _d : state.fixedItemSizeKeys = /* @__PURE__ */ new Set();
674
- state.fixedItemSizeKeys.add(key);
675
- }
676
- }
677
- return size;
896
+ // src/utils/checkThreshold.ts
897
+ var HYSTERESIS_MULTIPLIER = 1.3;
898
+ function isOutsideThresholdHysteresis(distance, atThreshold, threshold) {
899
+ const absDistance = Math.abs(distance);
900
+ return !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
678
901
  }
679
- function getKnownOrFixedItemSize(ctx, index) {
680
- const key = getId(ctx.state, index);
681
- return getKnownOrFixedSize(ctx, key, index, ctx.state.props.data[index]);
902
+ function didThresholdDataChange(snapshot, context) {
903
+ return !!snapshot && (snapshot.dataLength !== context.dataLength || snapshot.data !== context.data && snapshot.contentSize !== context.contentSize);
682
904
  }
683
- function areKnownOrFixedItemSizesAvailable(ctx, startIndex, endIndex) {
684
- for (let index = startIndex; index <= endIndex; index++) {
685
- if (getKnownOrFixedItemSize(ctx, index) === void 0) {
686
- return false;
905
+ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot) => {
906
+ const absDistance = Math.abs(distance);
907
+ const within = context.dataLength !== 0 && (atThreshold || threshold > 0 && absDistance <= threshold);
908
+ const didScroll = (snapshot == null ? void 0 : snapshot.scroll) !== context.scroll;
909
+ if (snapshot) snapshot.scroll = context.scroll;
910
+ if (!within) {
911
+ if ((context.dataLength === 0 || didScroll && isOutsideThresholdHysteresis(distance, atThreshold, threshold)) && (wasReached || snapshot)) {
912
+ setSnapshot(void 0);
687
913
  }
914
+ return false;
688
915
  }
689
- return true;
916
+ if ((!wasReached || didThresholdDataChange(snapshot, context)) && onReached(distance)) {
917
+ return true;
918
+ }
919
+ return !!wasReached;
920
+ };
921
+
922
+ // src/utils/edgeReachedGate.ts
923
+ function getDistanceFromStart(ctx) {
924
+ return ctx.state.scroll + getContentInsetTop(ctx);
690
925
  }
691
- function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize, notifyTotalSize, resolved) {
692
- var _a3, _b, _c, _d;
926
+ function resetSharedEdgeGateIfOutsideHysteresis(ctx) {
693
927
  const state = ctx.state;
694
- const {
695
- sizes,
696
- averageSizes,
697
- props: { estimatedItemSize, getItemType },
698
- scrollingTo
699
- } = state;
700
- const sizeKnown = state.sizesKnown.get(key);
701
- if (sizeKnown !== void 0) {
702
- return sizeKnown;
703
- }
704
- const renderedSize = sizes.get(key);
705
- let size = getKnownOrFixedSize(ctx, key, index, data, resolved);
706
- if (size !== void 0) {
707
- setSize(ctx, key, size, notifyTotalSize);
708
- return size;
709
- }
710
- if (preferCachedSize && renderedSize !== void 0) {
711
- return renderedSize;
712
- }
713
- const itemType = (_b = resolved == null ? void 0 : resolved.itemType) != null ? _b : getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
714
- if (useAverageSize && !scrollingTo) {
715
- const averageSizeForType = (_c = averageSizes[itemType]) == null ? void 0 : _c.avg;
716
- if (averageSizeForType !== void 0) {
717
- size = roundSize(averageSizeForType);
718
- }
719
- }
720
- if (size === void 0 && renderedSize !== void 0) {
721
- return renderedSize;
928
+ if (!state.edgeReachedGate || state.edgeReachedGateScroll === state.scroll) {
929
+ return;
722
930
  }
723
- if (size === void 0 && useAverageSize && scrollingTo) {
724
- const averageSizeForType = (_d = scrollingTo.averageSizeSnapshot) == null ? void 0 : _d[itemType];
725
- if (averageSizeForType !== void 0) {
726
- size = roundSize(averageSizeForType);
727
- }
931
+ state.edgeReachedGateScroll = state.scroll;
932
+ const contentSize = getContentSize(ctx);
933
+ const endDistance = contentSize - state.scroll - state.scrollLength;
934
+ const isContentLess = contentSize < state.scrollLength;
935
+ const startThreshold = state.props.onStartReachedThreshold * state.scrollLength;
936
+ const endThreshold = state.props.onEndReachedThreshold * state.scrollLength;
937
+ const isOutsideStart = isOutsideThresholdHysteresis(getDistanceFromStart(ctx), false, startThreshold);
938
+ const isOutsideEnd = isOutsideThresholdHysteresis(endDistance, isContentLess, endThreshold);
939
+ if (isOutsideStart && isOutsideEnd) {
940
+ state.edgeReachedGate = void 0;
728
941
  }
729
- if (size === void 0) {
730
- size = estimatedItemSize + ctx.scrollAxisGap;
942
+ }
943
+ function canDispatchReachedEdge(ctx, edge, allowedEdge, allowGateCreatedInCurrentCheck) {
944
+ const state = ctx.state;
945
+ return !state.edgeReachedGate || allowedEdge === edge || !!allowGateCreatedInCurrentCheck || didThresholdDataChange(
946
+ edge === "start" ? state.startReachedSnapshot : state.endReachedSnapshot,
947
+ getThresholdSnapshot(ctx)
948
+ );
949
+ }
950
+ function markReachedEdge(ctx, edge) {
951
+ const state = ctx.state;
952
+ state.edgeReachedGate = "closed";
953
+ state.edgeReachedGateScroll = state.scroll;
954
+ const snapshotKey = edge === "start" ? "startReachedSnapshot" : "endReachedSnapshot";
955
+ const snapshot = getThresholdSnapshot(ctx);
956
+ snapshot.didRearmForData = didThresholdDataChange(state[snapshotKey], snapshot);
957
+ state[snapshotKey] = snapshot;
958
+ }
959
+ function getThresholdSnapshot(ctx) {
960
+ const { data } = ctx.state.props;
961
+ return { contentSize: getContentSize(ctx), data, dataLength: data.length, scroll: ctx.state.scroll };
962
+ }
963
+ function prepareReachedEdgeForNextUserScroll(ctx) {
964
+ if (ctx.state.edgeReachedGate) {
965
+ ctx.state.edgeReachedGate = "prepared";
731
966
  }
732
- setSize(ctx, key, size, notifyTotalSize);
733
- return size;
734
967
  }
735
- function getItemSizeAtIndex(ctx, index) {
736
- if (index === void 0 || index < 0) {
968
+ function beginReachedEdgeUserScroll(ctx, scrollDelta) {
969
+ const state = ctx.state;
970
+ const allowedEdge = scrollDelta < 0 ? "start" : "end";
971
+ if (state.edgeReachedGate !== "prepared" && state.edgeReachedGate !== (allowedEdge === "start" ? "consumed-end" : "consumed-start")) {
737
972
  return void 0;
738
973
  }
739
- const targetId = getId(ctx.state, index);
740
- return getItemSize(ctx, targetId, index, ctx.state.props.data[index]);
974
+ const snapshotKey = allowedEdge === "start" ? "startReachedSnapshot" : "endReachedSnapshot";
975
+ const snapshot = state[snapshotKey];
976
+ if ((snapshot == null ? void 0 : snapshot.didRearmForData) && snapshot.data === state.props.data) {
977
+ snapshot.didRearmForData = false;
978
+ state.edgeReachedGate = state.edgeReachedGate === "prepared" ? `consumed-${allowedEdge}` : "closed";
979
+ return void 0;
980
+ }
981
+ state[allowedEdge === "start" ? "isStartReached" : "isEndReached"] = false;
982
+ state[snapshotKey] = void 0;
983
+ return allowedEdge;
741
984
  }
742
985
 
743
- // src/core/calculateOffsetWithOffsetPosition.ts
744
- function getViewPositionOffset(availableSpace, viewPosition, viewPositionFallback) {
745
- let alignment = viewPosition;
746
- if (availableSpace < 0 && viewPositionFallback !== void 0) {
747
- alignment = viewPositionFallback === "start" ? 0 : 1;
748
- }
749
- return -alignment * availableSpace;
986
+ // src/utils/hasActiveInitialScroll.ts
987
+ function hasActiveInitialScroll(state) {
988
+ return !!(state == null ? void 0 : state.initialScroll) && !state.didFinishInitialScroll;
750
989
  }
751
- function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
752
- var _a3;
990
+
991
+ // src/utils/checkAtBottom.ts
992
+ function checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
753
993
  const state = ctx.state;
754
- const { index, viewOffset, viewPosition, viewPositionFallback } = params;
755
- let offset = offsetParam;
756
- if (viewOffset) {
757
- offset -= viewOffset;
994
+ if (!state) {
995
+ return;
758
996
  }
759
- if (index !== void 0) {
760
- const startOffsetAdjustment = getStartOffsetAdjustment(ctx);
761
- if (startOffsetAdjustment) {
762
- offset += startOffsetAdjustment;
763
- }
997
+ const {
998
+ queuedInitialLayout,
999
+ scrollLength,
1000
+ scroll,
1001
+ maintainingScrollAtEnd,
1002
+ props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
1003
+ } = state;
1004
+ const contentSize = getContentSize(ctx);
1005
+ const distanceFromEnd = contentSize - scroll - scrollLength;
1006
+ resetSharedEdgeGateIfOutsideHysteresis(ctx);
1007
+ if (state.props.data.length === 0 && scrollLength > 0 && contentSize <= scrollLength) {
1008
+ set$(ctx, "isAtEnd", true);
1009
+ set$(ctx, "isNearEnd", true);
1010
+ set$(ctx, "isWithinMaintainScrollAtEndThreshold", true);
1011
+ return;
764
1012
  }
765
- if (viewPosition !== void 0 && index !== void 0) {
766
- const dataLength = state.props.data.length;
767
- if (dataLength === 0) {
768
- return offset;
1013
+ if (contentSize > 0 && queuedInitialLayout) {
1014
+ const isContentLess = contentSize < scrollLength;
1015
+ set$(ctx, "isAtEnd", isContentLess || distanceFromEnd <= EDGE_POSITION_EPSILON);
1016
+ set$(ctx, "isNearEnd", isContentLess || distanceFromEnd <= onEndReachedThreshold * scrollLength);
1017
+ set$(
1018
+ ctx,
1019
+ "isWithinMaintainScrollAtEndThreshold",
1020
+ isContentLess || // A zero threshold still includes fractional offsets considered
1021
+ // at the end; otherwise following can stop after rounding/layout.
1022
+ distanceFromEnd <= Math.max(EDGE_POSITION_EPSILON, maintainScrollAtEndThreshold * scrollLength) || state.pendingMaintainScrollAtEnd || !!maintainingScrollAtEnd || !!peek$(ctx, "isFollowingEnd")
1023
+ );
1024
+ const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || maintainingScrollAtEnd;
1025
+ if (!shouldSkipThresholdChecks) {
1026
+ state.isEndReached = checkThreshold(
1027
+ distanceFromEnd,
1028
+ isContentLess,
1029
+ onEndReachedThreshold * scrollLength,
1030
+ state.isEndReached,
1031
+ state.endReachedSnapshot,
1032
+ { contentSize, data: state.props.data, dataLength: state.props.data.length, scroll },
1033
+ (distance) => {
1034
+ if (state.props.onEndReached && canDispatchReachedEdge(ctx, "end", allowedEdge, allowGateCreatedInCurrentCheck)) {
1035
+ markReachedEdge(ctx, "end");
1036
+ state.props.onEndReached({ distanceFromEnd: distance });
1037
+ return true;
1038
+ }
1039
+ return false;
1040
+ },
1041
+ (snapshot) => {
1042
+ state.endReachedSnapshot = snapshot;
1043
+ }
1044
+ );
769
1045
  }
770
- const isOutOfBounds = index < 0 || index >= dataLength;
771
- const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
772
- const measuredItemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
773
- const itemSize = Math.max(0, measuredItemSize - (isOutOfBounds ? 0 : ctx.scrollAxisGap));
774
- const trailingInset = getContentInsetEnd(ctx);
775
- const availableSpace = state.scrollLength - trailingInset - itemSize;
776
- offset += getViewPositionOffset(availableSpace, viewPosition, viewPositionFallback);
777
1046
  }
778
- return offset;
779
1047
  }
780
1048
 
781
- // src/core/cancelImperativeScroll.ts
782
- function cancelScrollCompletionChecks({ scheduledWork }) {
783
- scheduledWork.cancel("checkFinishedScrollFrame");
784
- scheduledWork.cancel("checkFinishedScrollRetryFrame");
785
- scheduledWork.cancel("checkFinishedScrollFallback");
786
- scheduledWork.cancel("platformScrollCompletion");
787
- }
788
- function syncScrollActive(ctx) {
789
- const { state } = ctx;
790
- set$(
791
- ctx,
792
- "isScrollActive",
793
- !!state.isDragging || !!state.isMomentumScrolling || !!state.scrollingTo || !!state.pendingScrollResolve
794
- );
795
- }
796
- function settlePendingImperativeScroll(state) {
797
- const resolvePendingScroll = state.pendingScrollResolve;
798
- state.pendingScrollResolve = void 0;
799
- state.pendingScrollToEnd = void 0;
800
- resolvePendingScroll == null ? void 0 : resolvePendingScroll();
801
- }
802
- function cancelImperativeScroll(state) {
803
- cancelScrollCompletionChecks(state);
804
- state.scheduledWork.cancel("imperativeScrollReady");
805
- state.scrollingTo = void 0;
806
- state.scrollTargetPinnedRange = void 0;
807
- settlePendingImperativeScroll(state);
1049
+ // src/utils/checkAtTop.ts
1050
+ function checkAtTop(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
1051
+ const state = ctx == null ? void 0 : ctx.state;
1052
+ if (!state) {
1053
+ return;
1054
+ }
1055
+ const {
1056
+ props: { data, onStartReachedThreshold },
1057
+ scrollLength
1058
+ } = state;
1059
+ const threshold = onStartReachedThreshold * scrollLength;
1060
+ const distanceFromStart = getDistanceFromStart(ctx);
1061
+ resetSharedEdgeGateIfOutsideHysteresis(ctx);
1062
+ set$(ctx, "isAtStart", distanceFromStart <= EDGE_POSITION_EPSILON);
1063
+ set$(ctx, "isNearStart", distanceFromStart <= threshold);
1064
+ const shouldSkipThresholdChecks = !state.queuedInitialLayout || hasActiveInitialScroll(state) || !!state.scrollingTo;
1065
+ if (!shouldSkipThresholdChecks) {
1066
+ state.isStartReached = checkThreshold(
1067
+ distanceFromStart,
1068
+ false,
1069
+ threshold,
1070
+ state.isStartReached,
1071
+ state.startReachedSnapshot,
1072
+ { contentSize: getContentSize(ctx), data, dataLength: data.length, scroll: state.scroll },
1073
+ (distance) => {
1074
+ if (state.props.onStartReached && canDispatchReachedEdge(ctx, "start", allowedEdge, allowGateCreatedInCurrentCheck)) {
1075
+ markReachedEdge(ctx, "start");
1076
+ state.props.onStartReached({ distanceFromStart: distance });
1077
+ return true;
1078
+ }
1079
+ return false;
1080
+ },
1081
+ (snapshot) => {
1082
+ state.startReachedSnapshot = snapshot;
1083
+ }
1084
+ );
1085
+ }
808
1086
  }
809
1087
 
810
- // src/core/doScrollParams.ts
811
- function getNativeScrollPoint(ctx, offset, horizontal = ctx.state.props.horizontal) {
812
- var _a3;
813
- const { state } = ctx;
814
- return horizontal ? {
815
- x: toNativeHorizontalOffset(
816
- state,
817
- offset,
818
- Math.max(getContentSize(ctx), (_a3 = state.props.contentContainerMinSize) != null ? _a3 : 0)
819
- ),
820
- y: 0
821
- } : { x: 0, y: offset };
1088
+ // src/utils/checkThresholds.ts
1089
+ function checkThresholds(ctx, allowedEdge) {
1090
+ const allowGateCreatedInCurrentCheck = !ctx.state.edgeReachedGate;
1091
+ checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck);
1092
+ checkAtTop(ctx, allowedEdge, allowGateCreatedInCurrentCheck);
1093
+ rearmEndFollowIfSettled(ctx);
822
1094
  }
823
1095
 
824
- // src/core/deferredPublicOnScroll.ts
825
- function withResolvedContentOffset(state, event, resolvedOffset) {
826
- return {
827
- ...event,
828
- nativeEvent: {
829
- ...event.nativeEvent,
830
- contentOffset: state.props.horizontal ? { x: resolvedOffset, y: 0 } : { x: 0, y: resolvedOffset }
831
- }
832
- };
833
- }
834
- function releaseDeferredPublicOnScroll(ctx, resolvedOffset) {
835
- var _a3, _b, _c, _d;
1096
+ // src/core/recalculateSettledScroll.ts
1097
+ function recalculateSettledScroll(ctx) {
1098
+ var _a3, _b;
836
1099
  const state = ctx.state;
837
- const deferredEvent = state.deferredPublicOnScrollEvent;
838
- state.deferredPublicOnScrollEvent = void 0;
839
- if (deferredEvent) {
840
- (_d = (_c = state.props).onScroll) == null ? void 0 : _d.call(
841
- _c,
842
- withResolvedContentOffset(
843
- state,
844
- deferredEvent,
845
- (_b = (_a3 = resolvedOffset != null ? resolvedOffset : state.scrollPending) != null ? _a3 : state.scroll) != null ? _b : 0
846
- )
847
- );
1100
+ if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1101
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
848
1102
  }
1103
+ checkThresholds(ctx);
849
1104
  }
850
1105
 
851
- // src/core/endOfContentTarget.ts
852
- function getEndAlignedViewOffset(stylePaddingEnd, footerSize = 0) {
853
- return -stylePaddingEnd - footerSize;
854
- }
855
- function isEndOfContentTarget(target, dataLength) {
856
- return !!target && dataLength > 0 && target.viewPosition === 1 && !!target.preserveForBottomPadding && target.index !== void 0 && target.index >= dataLength - 1;
857
- }
858
-
859
- // src/core/initialScrollSession.ts
860
- var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
861
- function hasInitialScrollSessionCompletion(completion) {
862
- return !!((completion == null ? void 0 : completion.didDispatchNativeScroll) || (completion == null ? void 0 : completion.didRetrySilentInitialScroll) || (completion == null ? void 0 : completion.watchdog));
1106
+ // src/core/adaptiveRender.ts
1107
+ var DEFAULT_ADAPTIVE_RENDER_ENTER_VELOCITY = 3;
1108
+ var DEFAULT_ADAPTIVE_RENDER_EXIT_VELOCITY = 1;
1109
+ var DEFAULT_ADAPTIVE_RENDER_EXIT_DELAY = 250;
1110
+ var DEFAULT_WEB_ADAPTIVE_RENDER_ENTER_VELOCITY = 6;
1111
+ var DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_VELOCITY = 3;
1112
+ var DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_DELAY = 250;
1113
+ function clearAdaptiveRenderExitTimeout(ctx) {
1114
+ ctx.state.scheduledWork.cancel("adaptiveRender");
863
1115
  }
864
- function clearInitialScrollSession(state) {
865
- state.initialScrollSession = void 0;
866
- return void 0;
1116
+ function scheduleAdaptiveRenderExit(ctx, exitDelay) {
1117
+ const state = ctx.state;
1118
+ clearAdaptiveRenderExitTimeout(ctx);
1119
+ if (exitDelay <= 0) {
1120
+ setAdaptiveRender(ctx, "normal", "scroll");
1121
+ } else {
1122
+ state.scheduledWork.timeout(() => setAdaptiveRender(ctx, "normal", "scroll"), exitDelay, "adaptiveRender");
1123
+ }
867
1124
  }
868
- function createInitialScrollSession(options) {
869
- const { bootstrap, completion, kind, previousDataLength } = options;
870
- return kind === "offset" ? {
871
- completion,
872
- kind,
873
- previousDataLength
874
- } : {
875
- bootstrap,
876
- completion,
877
- kind,
878
- previousDataLength
879
- };
1125
+ function setAdaptiveRender(ctx, mode, reason) {
1126
+ var _a3, _b;
1127
+ const previousMode = peek$(ctx, "adaptiveRender");
1128
+ if (previousMode !== mode) {
1129
+ set$(ctx, "adaptiveRender", mode);
1130
+ (_b = (_a3 = ctx.state.props.adaptiveRender) == null ? void 0 : _a3.onChange) == null ? void 0 : _b.call(_a3, mode, reason);
1131
+ }
880
1132
  }
881
- function ensureInitialScrollSessionCompletion(state, kind = ((_b) => (_b = ((_a3) => (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind)()) != null ? _b : "bootstrap")()) {
882
- var _a4, _b2;
883
- if (!state.initialScrollSession) {
884
- state.initialScrollSession = createInitialScrollSession({
885
- completion: {},
886
- kind,
887
- previousDataLength: 0
888
- });
889
- } else if (state.initialScrollSession.kind !== kind) {
890
- state.initialScrollSession = createInitialScrollSession({
891
- bootstrap: state.initialScrollSession.kind === "bootstrap" ? state.initialScrollSession.bootstrap : void 0,
892
- completion: state.initialScrollSession.completion,
893
- kind,
894
- previousDataLength: state.initialScrollSession.previousDataLength
895
- });
1133
+ function resetAdaptiveRender(ctx) {
1134
+ var _a3, _b;
1135
+ clearAdaptiveRenderExitTimeout(ctx);
1136
+ const mode = (_b = (_a3 = ctx.state.props.adaptiveRender) == null ? void 0 : _a3.initialMode) != null ? _b : "normal";
1137
+ if (peek$(ctx, "adaptiveRender") !== mode) {
1138
+ setAdaptiveRender(ctx, mode, "initial");
896
1139
  }
897
- (_b2 = (_a4 = state.initialScrollSession).completion) != null ? _b2 : _a4.completion = {};
898
- return state.initialScrollSession.completion;
899
1140
  }
900
- var initialScrollCompletion = {
901
- didDispatchNativeScroll(state) {
902
- var _a3, _b;
903
- return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didDispatchNativeScroll);
904
- },
905
- didRetrySilentInitialScroll(state) {
906
- var _a3, _b;
907
- return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didRetrySilentInitialScroll);
908
- },
909
- markInitialScrollNativeDispatch(state) {
910
- ensureInitialScrollSessionCompletion(state).didDispatchNativeScroll = true;
911
- },
912
- markSilentInitialScrollRetry(state) {
913
- ensureInitialScrollSessionCompletion(state).didRetrySilentInitialScroll = true;
914
- },
915
- resetFlags(state) {
916
- if (!state.initialScrollSession) {
917
- return;
918
- }
919
- const completion = ensureInitialScrollSessionCompletion(state, state.initialScrollSession.kind);
920
- completion.didDispatchNativeScroll = void 0;
921
- completion.didRetrySilentInitialScroll = void 0;
922
- }
923
- };
924
- var initialScrollWatchdog = {
925
- clear(state) {
926
- initialScrollWatchdog.set(state, void 0);
927
- },
928
- didReachTarget(newScroll, watchdog) {
929
- const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
930
- return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
931
- },
932
- get(state) {
933
- var _a3, _b;
934
- return (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog;
935
- },
936
- hasNonZeroTargetOffset(targetOffset) {
937
- return targetOffset !== void 0 && targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
938
- },
939
- isAtZeroTargetOffset(targetOffset) {
940
- return targetOffset <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
941
- },
942
- set(state, watchdog) {
943
- var _a3, _b;
944
- if (!watchdog && !((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog)) {
945
- return;
946
- }
947
- const completion = ensureInitialScrollSessionCompletion(state);
948
- completion.watchdog = watchdog ? {
949
- startScroll: watchdog.startScroll,
950
- targetOffset: watchdog.targetOffset
951
- } : void 0;
952
- }
953
- };
954
- function setInitialScrollSession(state, options = {}) {
955
- var _a3, _b, _c, _d;
956
- const existingSession = state.initialScrollSession;
957
- const kind = (_a3 = options.kind) != null ? _a3 : existingSession == null ? void 0 : existingSession.kind;
958
- const completion = existingSession == null ? void 0 : existingSession.completion;
959
- const existingBootstrap = (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0;
960
- const bootstrap = kind === "bootstrap" ? options.bootstrap === null ? void 0 : (_b = options.bootstrap) != null ? _b : existingBootstrap : void 0;
961
- if (!kind) {
962
- return clearInitialScrollSession(state);
963
- }
964
- if (!state.initialScroll && !bootstrap && !hasInitialScrollSessionCompletion(completion)) {
965
- return clearInitialScrollSession(state);
966
- }
967
- const previousDataLength = (_d = (_c = options.previousDataLength) != null ? _c : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _d : 0;
968
- state.initialScrollSession = createInitialScrollSession({
969
- bootstrap,
970
- completion,
971
- kind,
972
- previousDataLength
973
- });
974
- return state.initialScrollSession;
975
- }
976
-
977
- // src/core/observedScroll.ts
978
- function getObservedScrollOffset(ctx, fallbackOffset = ctx.state.scroll) {
1141
+ function updateAdaptiveRender(ctx, scrollVelocity, options) {
979
1142
  var _a3, _b, _c;
980
- const { state } = ctx;
981
- const scroller = state.refScroller.current;
982
- const observed = (_a3 = scroller == null ? void 0 : scroller.getCurrentScrollOffset) == null ? void 0 : _a3.call(scroller);
983
- if (observed === void 0) return (_b = state.lastNativeScroll) != null ? _b : fallbackOffset;
984
- return state.props.horizontal ? peekLogicalHorizontalOffset(state, observed, (_c = scroller.getMaxScrollOffset) == null ? void 0 : _c.call(scroller)) : observed;
985
- }
986
-
987
- // src/core/clampScrollOffset.ts
988
- function clampScrollOffset(ctx, offset, scrollTarget) {
989
- const state = ctx.state;
990
- const minSize = (scrollTarget == null ? void 0 : scrollTarget.clampToContentContainerMinSize) ? state.props.contentContainerMinSize : void 0;
991
- const contentSize = minSize === void 0 ? getContentSize(ctx) : Math.max(getContentSize(ctx), minSize);
992
- let clampedOffset = offset;
993
- if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform.OS !== "android" || state.lastLayout)) {
994
- const baseMaxOffset = Math.max(0, contentSize - state.scrollLength);
995
- const viewOffset = Platform.OS === "web" && (scrollTarget == null ? void 0 : scrollTarget.isInitialScroll) ? void 0 : scrollTarget == null ? void 0 : scrollTarget.viewOffset;
996
- const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
997
- const maxOffset = baseMaxOffset + extraEndOffset;
998
- clampedOffset = Math.min(offset, maxOffset);
999
- }
1000
- clampedOffset = Math.max(0, clampedOffset);
1001
- return clampedOffset;
1002
- }
1003
-
1004
- // src/utils/requestAdjust.ts
1005
- function requestAdjust(ctx, positionDiff, source) {
1006
- var _a3;
1007
1143
  const state = ctx.state;
1008
- if (Math.abs(positionDiff) > 0.1) {
1009
- state.layoutAdjustmentEpoch = ((_a3 = state.layoutAdjustmentEpoch) != null ? _a3 : 0) + 1;
1010
- const dataChanged = source === "data";
1011
- const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
1012
- const doit = () => {
1013
- if (needsScrollWorkaround) {
1014
- ctx.doScrollTo({ horizontal: state.props.horizontal, isAdjustment: true, offset: state.scroll });
1015
- } else {
1016
- state.scrollAdjustHandler.requestAdjust(positionDiff);
1017
- if (state.adjustingFromInitialMount) {
1018
- state.adjustingFromInitialMount--;
1019
- }
1020
- }
1021
- };
1022
- state.scroll += positionDiff;
1023
- if (state.userScrollContentSize !== void 0) {
1024
- state.userScrollContentSize += positionDiff;
1025
- }
1026
- state.scrollForNextCalculateItemsInView = void 0;
1027
- const readyToRender = peek$(ctx, "readyToRender");
1028
- if (readyToRender) {
1029
- doit();
1030
- if (Platform.OS !== "web" && source !== "item-size") {
1031
- const threshold = state.scroll - positionDiff / 2;
1032
- if (!state.ignoreScrollFromMVCP) {
1033
- state.ignoreScrollFromMVCP = {};
1034
- }
1035
- if (positionDiff > 0) {
1036
- state.ignoreScrollFromMVCP.lt = threshold;
1037
- } else {
1038
- state.ignoreScrollFromMVCP.gt = threshold;
1144
+ const adaptiveRender = state.props.adaptiveRender;
1145
+ const currentMode = peek$(ctx, "adaptiveRender");
1146
+ if (peek$(ctx, "readyToRender")) {
1147
+ if (adaptiveRender) {
1148
+ const isWeb = Platform.OS === "web";
1149
+ const enterVelocity = (_a3 = adaptiveRender.enterVelocity) != null ? _a3 : isWeb ? DEFAULT_WEB_ADAPTIVE_RENDER_ENTER_VELOCITY : DEFAULT_ADAPTIVE_RENDER_ENTER_VELOCITY;
1150
+ const exitVelocity = (_b = adaptiveRender.exitVelocity) != null ? _b : isWeb ? DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_VELOCITY : DEFAULT_ADAPTIVE_RENDER_EXIT_VELOCITY;
1151
+ const exitDelay = (_c = adaptiveRender.exitDelay) != null ? _c : isWeb ? DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_DELAY : DEFAULT_ADAPTIVE_RENDER_EXIT_DELAY;
1152
+ const threshold = currentMode === "light" ? exitVelocity : enterVelocity;
1153
+ const nextMode = (options == null ? void 0 : options.forceLight) || Math.abs(scrollVelocity) > threshold ? "light" : "normal";
1154
+ const previousMode = state.scheduledWork.has("adaptiveRender") ? "normal" : currentMode;
1155
+ if (nextMode !== previousMode) {
1156
+ if (nextMode === "light") {
1157
+ setAdaptiveRender(ctx, "light", "scroll");
1158
+ scheduleAdaptiveRenderExit(ctx, exitDelay);
1159
+ } else if (currentMode === "light") {
1160
+ scheduleAdaptiveRenderExit(ctx, exitDelay);
1039
1161
  }
1040
- const delay = needsScrollWorkaround ? 250 : 100;
1041
- state.scheduledWork.timeout(
1042
- () => {
1043
- var _a4;
1044
- state.ignoreScrollFromMVCP = void 0;
1045
- const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
1046
- if (shouldForceUpdate) {
1047
- state.ignoreScrollFromMVCPIgnored = false;
1048
- state.scrollPending = state.scroll;
1049
- (_a4 = state.reprocessCurrentScroll) == null ? void 0 : _a4.call(state);
1050
- }
1051
- },
1052
- delay,
1053
- "ignoreScrollFromMVCP"
1054
- );
1055
1162
  }
1056
1163
  } else {
1057
- state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
1058
- requestAnimationFrame(doit);
1164
+ resetAdaptiveRender(ctx);
1059
1165
  }
1060
1166
  }
1061
1167
  }
1062
1168
 
1063
- // src/core/finishMaintainScrollAtEnd.ts
1064
- function interruptMaintainScrollAtEnd(ctx) {
1169
+ // src/utils/getEffectiveDrawDistance.ts
1170
+ var INITIAL_DRAW_DISTANCE = 50;
1171
+ function getEffectiveDrawDistance(ctx, mode) {
1065
1172
  var _a3;
1173
+ const drawDistance = ctx.state.props.drawDistance;
1174
+ const initialScroll = ctx.state.initialScroll;
1175
+ const needsFullInitialDrawDistance = initialScroll !== void 0 && ((_a3 = initialScroll.viewPosition) != null ? _a3 : 0) > 0;
1176
+ const shouldCapDrawDistance = mode === "visible-first" || mode !== "full" && !peek$(ctx, "readyToRender") && !needsFullInitialDrawDistance;
1177
+ return shouldCapDrawDistance ? Math.min(drawDistance, INITIAL_DRAW_DISTANCE) : drawDistance;
1178
+ }
1179
+ function scheduleFullDrawDistancePrewarm(ctx) {
1066
1180
  const { state } = ctx;
1067
- const maintaining = state.maintainingScrollAtEnd;
1068
- state.endFollowInterrupted = true;
1069
- set$(ctx, "isFollowingEnd", false);
1070
- if (maintaining === "animated" || maintaining === "instant" || maintaining && ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isScrollToEnd)) {
1071
- cancelImperativeScroll(state);
1072
- syncScrollActive(ctx);
1073
- }
1074
- if (maintaining || state.pendingMaintainScrollAtEnd) {
1075
- finishMaintainScrollAtEnd(ctx);
1181
+ if (state.props.drawDistance <= INITIAL_DRAW_DISTANCE || state.scheduledWork.has("fullDrawDistancePrewarm")) {
1182
+ return;
1076
1183
  }
1184
+ state.scheduledWork.frame(() => {
1185
+ var _a3, _b, _c;
1186
+ if (state.props.hasExternalScroll && !hasActiveInitialScroll(state) && !state.scrollingTo && !state.pendingNativeMVCPAdjust) {
1187
+ const offset = (_b = (_a3 = state.refScroller.current) == null ? void 0 : _a3.getRawScrollOffset) == null ? void 0 : _b.call(_a3);
1188
+ if (offset !== void 0) {
1189
+ state.scroll = state.scrollPending = state.props.horizontal ? toLogicalHorizontalOffset(state, offset, state.totalSize) : offset;
1190
+ }
1191
+ }
1192
+ (_c = state.triggerCalculateItemsInView) == null ? void 0 : _c.call(state);
1193
+ }, "fullDrawDistancePrewarm");
1077
1194
  }
1078
- function armAppScrollRearm(ctx) {
1079
- ctx.state.scheduledWork.timeout(
1080
- () => rearmEndFollowIfSettled(ctx),
1081
- USER_MOMENTUM_STALL_TIMEOUT_MS,
1082
- "appScrollRearm"
1083
- );
1084
- }
1085
- function rearmEndFollowIfSettled(ctx) {
1195
+
1196
+ // src/utils/setInitialRenderState.ts
1197
+ function resetInitialRenderState(ctx, {
1198
+ resetLayout,
1199
+ resetInitialScroll
1200
+ }) {
1086
1201
  const { state } = ctx;
1087
- if (state.endFollowInterrupted && (!state.visibleContentAnchor || state.visibleContentAnchor.sizeOnly) && !state.isDragging && state.userScrollContentSize === void 0 && !state.scheduledWork.has("appScrollRearm") && !state.scrollingTo && !state.pendingScrollResolve && peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
1088
- state.endFollowInterrupted = false;
1089
- if (state.props.maintainScrollAtEnd) set$(ctx, "isFollowingEnd", true);
1202
+ if (resetLayout) {
1203
+ state.didContainersLayout = false;
1204
+ state.queuedInitialLayout = false;
1205
+ state.scrollBufferDirection = void 0;
1206
+ state.scrollHistory.length = 0;
1207
+ state.scheduledWork.cancel("renderRangeProjection");
1208
+ state.edgeReachedGate = void 0;
1209
+ state.isStartReached = null;
1210
+ state.isEndReached = null;
1211
+ state.startReachedSnapshot = void 0;
1212
+ state.endReachedSnapshot = void 0;
1213
+ }
1214
+ if (resetInitialScroll) {
1215
+ state.didFinishInitialScroll = false;
1090
1216
  }
1217
+ set$(ctx, "readyToRender", false);
1218
+ resetAdaptiveRender(ctx);
1091
1219
  }
1092
- function finishMaintainScrollAtEnd(ctx) {
1093
- var _a3, _b;
1220
+ function setInitialRenderState(ctx, {
1221
+ didLayout,
1222
+ didInitialScroll
1223
+ }) {
1094
1224
  const { state } = ctx;
1095
- const currentPadding = peek$(ctx, "alignItemsAtEndPadding") || 0;
1096
- const nextPadding = getAlignItemsAtEndPadding(ctx);
1097
- state.maintainingScrollAtEnd = void 0;
1098
- state.pendingMaintainScrollAtEnd = false;
1099
- for (const key of (_a3 = state.pendingEndMeasurements) != null ? _a3 : []) {
1100
- if (!state.containerItemKeys.has(key)) state.pendingEndMeasurements.delete(key);
1225
+ const {
1226
+ loadStartTime,
1227
+ props: { onLoad, onReady }
1228
+ } = state;
1229
+ if (didLayout) {
1230
+ state.didContainersLayout = true;
1101
1231
  }
1102
- if (currentPadding !== nextPadding) {
1103
- set$(ctx, "alignItemsAtEndPadding", nextPadding);
1104
- state.scrollForNextCalculateItemsInView = void 0;
1105
- (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1106
- const nextScroll = clampScrollOffset(ctx, state.scroll + nextPadding - currentPadding);
1107
- const nativeScroll = Platform.OS === "ios" ? clampScrollOffset(ctx, state.scroll) : state.scroll;
1108
- state.scroll = nativeScroll;
1109
- requestAdjust(ctx, nextScroll - nativeScroll);
1232
+ if (didInitialScroll) {
1233
+ state.didFinishInitialScroll = true;
1110
1234
  }
1111
- }
1112
-
1113
- // src/utils/checkThreshold.ts
1114
- var HYSTERESIS_MULTIPLIER = 1.3;
1115
- function isOutsideThresholdHysteresis(distance, atThreshold, threshold) {
1116
- const absDistance = Math.abs(distance);
1117
- return !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
1118
- }
1119
- var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot) => {
1120
- const absDistance = Math.abs(distance);
1121
- const within = atThreshold || threshold > 0 && absDistance <= threshold;
1122
- const updateSnapshot = () => {
1123
- setSnapshot({
1124
- atThreshold,
1125
- contentSize: context.contentSize,
1126
- dataLength: context.dataLength,
1127
- scrollPosition: context.scrollPosition
1128
- });
1129
- };
1130
- let reached = !!wasReached;
1131
- if (!wasReached) {
1132
- if (within && onReached(distance)) {
1133
- updateSnapshot();
1134
- reached = true;
1235
+ const isReadyToRender = Boolean(state.didContainersLayout && state.didFinishInitialScroll);
1236
+ if (isReadyToRender && !peek$(ctx, "readyToRender")) {
1237
+ set$(ctx, "readyToRender", true);
1238
+ setAdaptiveRender(ctx, "normal", "ready");
1239
+ if (state.props.drawDistance > INITIAL_DRAW_DISTANCE) {
1240
+ scheduleFullDrawDistancePrewarm(ctx);
1135
1241
  }
1136
- } else {
1137
- const reset = isOutsideThresholdHysteresis(distance, atThreshold, threshold);
1138
- if (reset) {
1139
- setSnapshot(void 0);
1140
- reached = false;
1141
- } else if (within) {
1142
- const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
1143
- if (changed) {
1144
- updateSnapshot();
1242
+ if (!state.didLoad) {
1243
+ state.didLoad = true;
1244
+ if (onLoad) {
1245
+ onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1145
1246
  }
1146
1247
  }
1147
- }
1148
- return reached;
1149
- };
1150
-
1151
- // src/utils/edgeReachedGate.ts
1152
- function resetEdgeLatch(ctx, edge) {
1153
- const state = ctx.state;
1154
- if (edge === "start") {
1155
- state.isStartReached = false;
1156
- state.startReachedSnapshot = void 0;
1157
- } else {
1158
- state.isEndReached = false;
1159
- state.endReachedSnapshot = void 0;
1160
- }
1161
- }
1162
- function resetSharedEdgeGateIfOutsideHysteresis(ctx) {
1163
- const state = ctx.state;
1164
- if (!state.edgeReachedGate) {
1165
- return;
1166
- }
1167
- const contentSize = getContentSize(ctx);
1168
- const endDistance = contentSize - state.scroll - state.scrollLength;
1169
- const isContentLess = contentSize < state.scrollLength;
1170
- const startThreshold = state.props.onStartReachedThreshold * state.scrollLength;
1171
- const endThreshold = state.props.onEndReachedThreshold * state.scrollLength;
1172
- const isOutsideStart = isOutsideThresholdHysteresis(state.scroll, false, startThreshold);
1173
- const isOutsideEnd = isOutsideThresholdHysteresis(endDistance, isContentLess, endThreshold);
1174
- if (isOutsideStart && isOutsideEnd) {
1175
- state.edgeReachedGate = void 0;
1248
+ onReady == null ? void 0 : onReady();
1176
1249
  }
1177
1250
  }
1178
- function canDispatchReachedEdge(ctx, edge, allowedEdge, allowGateCreatedInCurrentCheck) {
1179
- return !ctx.state.edgeReachedGate || allowedEdge === edge || !!allowGateCreatedInCurrentCheck;
1180
- }
1181
- function markReachedEdge(ctx) {
1182
- ctx.state.edgeReachedGate = "closed";
1251
+
1252
+ // src/core/finishInitialScroll.ts
1253
+ var PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS = 2e3;
1254
+ function syncInitialScrollOffset(state, offset) {
1255
+ state.scroll = offset;
1256
+ state.scrollPending = offset;
1257
+ state.scrollPrev = offset;
1183
1258
  }
1184
- function prepareReachedEdgeForNextUserScroll(ctx) {
1185
- if (ctx.state.edgeReachedGate) {
1186
- ctx.state.edgeReachedGate = "prepared";
1187
- }
1259
+ function clearPreservedInitialScrollTarget(state) {
1260
+ state.scheduledWork.cancel("preservedInitialScroll");
1261
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
1262
+ state.initialScroll = void 0;
1263
+ state.preservedEndAnchorCorrection = void 0;
1264
+ setInitialScrollSession(state);
1188
1265
  }
1189
- function beginReachedEdgeUserScroll(ctx, scrollDelta) {
1266
+ function supersedeInitialScroll(ctx) {
1267
+ var _a3, _b, _c;
1190
1268
  const state = ctx.state;
1191
- if (state.edgeReachedGate !== "prepared") {
1192
- return void 0;
1269
+ const bootstrapInitialScroll = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" ? state.initialScrollSession.bootstrap : void 0;
1270
+ if (state.initialScroll || bootstrapInitialScroll || ((_b = state.scrollingTo) == null ? void 0 : _b.isInitialScroll)) {
1271
+ if ((bootstrapInitialScroll == null ? void 0 : bootstrapInitialScroll.frameHandle) !== void 0 && typeof cancelAnimationFrame === "function") {
1272
+ cancelAnimationFrame(bootstrapInitialScroll.frameHandle);
1273
+ }
1274
+ if ((_c = state.scrollingTo) == null ? void 0 : _c.isInitialScroll) {
1275
+ cancelScrollCompletionChecks(state);
1276
+ state.scrollingTo = void 0;
1277
+ state.scrollTargetPinnedRange = void 0;
1278
+ }
1279
+ initialScrollCompletion.resetFlags(state);
1280
+ setInitialScrollSession(state, { bootstrap: null });
1281
+ finishInitialScroll(ctx);
1193
1282
  }
1194
- const allowedEdge = scrollDelta < 0 ? "start" : "end";
1195
- resetEdgeLatch(ctx, allowedEdge);
1196
- return allowedEdge;
1197
- }
1198
-
1199
- // src/utils/hasActiveInitialScroll.ts
1200
- function hasActiveInitialScroll(state) {
1201
- return !!(state == null ? void 0 : state.initialScroll) && !state.didFinishInitialScroll;
1202
1283
  }
1203
-
1204
- // src/utils/checkAtBottom.ts
1205
- function checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
1284
+ function finishInitialScroll(ctx, options) {
1206
1285
  var _a3;
1207
1286
  const state = ctx.state;
1208
- if (!state) {
1209
- return;
1210
- }
1211
- const {
1212
- queuedInitialLayout,
1213
- scrollLength,
1214
- scroll,
1215
- maintainingScrollAtEnd,
1216
- props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
1217
- } = state;
1218
- const contentSize = getContentSize(ctx);
1219
- const distanceFromEnd = contentSize - scroll - scrollLength;
1220
- resetSharedEdgeGateIfOutsideHysteresis(ctx);
1221
- if (state.props.data.length === 0 && scrollLength > 0 && contentSize <= scrollLength) {
1222
- set$(ctx, "isAtEnd", true);
1223
- set$(ctx, "isNearEnd", true);
1224
- set$(ctx, "isWithinMaintainScrollAtEndThreshold", true);
1225
- return;
1287
+ const freshDataTransitionEpoch = state.freshDataTransitionEpoch;
1288
+ if ((options == null ? void 0 : options.resolvedOffset) !== void 0) {
1289
+ syncInitialScrollOffset(state, options.resolvedOffset);
1290
+ } else if ((options == null ? void 0 : options.syncObservedOffset) && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
1291
+ const observedOffset = getObservedScrollOffset(ctx);
1292
+ if (Number.isFinite(observedOffset)) {
1293
+ syncInitialScrollOffset(state, observedOffset);
1294
+ }
1226
1295
  }
1227
- if (contentSize > 0 && queuedInitialLayout) {
1228
- const isContentLess = contentSize < scrollLength;
1229
- set$(ctx, "isAtEnd", isContentLess || distanceFromEnd <= EDGE_POSITION_EPSILON);
1230
- set$(ctx, "isNearEnd", isContentLess || distanceFromEnd <= onEndReachedThreshold * scrollLength);
1231
- set$(
1232
- ctx,
1233
- "isWithinMaintainScrollAtEndThreshold",
1234
- isContentLess || // A zero threshold still includes fractional offsets considered
1235
- // at the end; otherwise following can stop after rounding/layout.
1236
- distanceFromEnd <= Math.max(EDGE_POSITION_EPSILON, maintainScrollAtEndThreshold * scrollLength) || state.pendingMaintainScrollAtEnd || !!maintainingScrollAtEnd || !!peek$(ctx, "isFollowingEnd")
1237
- );
1238
- const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || maintainingScrollAtEnd;
1239
- if (!shouldSkipThresholdChecks) {
1240
- state.isEndReached = checkThreshold(
1241
- distanceFromEnd,
1242
- isContentLess,
1243
- onEndReachedThreshold * scrollLength,
1244
- state.isEndReached,
1245
- state.endReachedSnapshot,
1246
- {
1247
- contentSize,
1248
- dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1249
- scrollPosition: scroll
1250
- },
1251
- (distance) => {
1252
- let dispatched = false;
1253
- if (state.props.onEndReached && canDispatchReachedEdge(ctx, "end", allowedEdge, allowGateCreatedInCurrentCheck)) {
1254
- markReachedEdge(ctx);
1255
- state.props.onEndReached({ distanceFromEnd: distance });
1256
- dispatched = true;
1257
- }
1258
- return dispatched;
1259
- },
1260
- (snapshot) => {
1261
- state.endReachedSnapshot = snapshot;
1262
- }
1263
- );
1296
+ const complete = () => {
1297
+ var _a4, _b, _c, _d;
1298
+ if (state.isUnmounted) {
1299
+ return;
1300
+ }
1301
+ if (freshDataTransitionEpoch !== state.freshDataTransitionEpoch) {
1302
+ (_a4 = options == null ? void 0 : options.onFinished) == null ? void 0 : _a4.call(options);
1303
+ return;
1304
+ }
1305
+ const shouldReleaseDeferredPublicOnScroll = Platform.OS === "web" && ((_b = state.initialScrollSession) == null ? void 0 : _b.kind) === "bootstrap";
1306
+ if (isEndOfContentTarget(state.initialScroll, state.props.data.length) && state.props.maintainScrollAtEnd && !state.endFollowInterrupted) {
1307
+ set$(ctx, "isFollowingEnd", true);
1264
1308
  }
1309
+ const finalScrollOffset = (_c = options == null ? void 0 : options.resolvedOffset) != null ? _c : state.scrollPending;
1310
+ initialScrollWatchdog.clear(state);
1311
+ if ((options == null ? void 0 : options.preserveTarget) && state.initialScroll) {
1312
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
1313
+ setInitialScrollSession(state);
1314
+ state.scheduledWork.cancel("preservedInitialScroll");
1315
+ if (options == null ? void 0 : options.schedulePreservedTargetClear) {
1316
+ state.scheduledWork.timeout(
1317
+ () => {
1318
+ var _a5;
1319
+ if (!state.didFinishInitialScroll || ((_a5 = state.scrollingTo) == null ? void 0 : _a5.isInitialScroll) || !state.initialScroll) {
1320
+ return;
1321
+ }
1322
+ clearPreservedInitialScrollTarget(state);
1323
+ },
1324
+ PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS,
1325
+ "preservedInitialScroll"
1326
+ );
1327
+ }
1328
+ } else {
1329
+ clearPreservedInitialScrollTarget(state);
1330
+ }
1331
+ if (options == null ? void 0 : options.recalculateItems) {
1332
+ recalculateSettledScroll(ctx);
1333
+ }
1334
+ setInitialRenderState(ctx, { didInitialScroll: true });
1335
+ if (shouldReleaseDeferredPublicOnScroll) {
1336
+ releaseDeferredPublicOnScroll(ctx, finalScrollOffset);
1337
+ }
1338
+ (_d = options == null ? void 0 : options.onFinished) == null ? void 0 : _d.call(options);
1339
+ };
1340
+ if (options == null ? void 0 : options.waitForCompletionFrame) {
1341
+ requestAnimationFrame(complete);
1342
+ return;
1265
1343
  }
1344
+ complete();
1266
1345
  }
1267
1346
 
1268
- // src/utils/checkAtTop.ts
1269
- function checkAtTop(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
1270
- const state = ctx == null ? void 0 : ctx.state;
1271
- if (!state) {
1272
- return;
1273
- }
1274
- const {
1275
- isStartReached,
1276
- props: { data, onStartReachedThreshold },
1277
- scroll,
1278
- scrollLength,
1279
- startReachedSnapshot,
1280
- totalSize
1281
- } = state;
1282
- const dataLength = data.length;
1283
- const threshold = onStartReachedThreshold * scrollLength;
1284
- resetSharedEdgeGateIfOutsideHysteresis(ctx);
1285
- if (isStartReached && threshold > 0 && scroll > threshold && startReachedSnapshot && (startReachedSnapshot.contentSize !== totalSize || startReachedSnapshot.dataLength !== dataLength)) {
1286
- state.isStartReached = false;
1287
- state.startReachedSnapshot = void 0;
1288
- }
1289
- set$(ctx, "isAtStart", scroll <= EDGE_POSITION_EPSILON);
1290
- set$(ctx, "isNearStart", scroll <= threshold);
1291
- const shouldSkipThresholdChecks = !state.queuedInitialLayout || hasActiveInitialScroll(state) || !!state.scrollingTo;
1292
- if (!shouldSkipThresholdChecks) {
1293
- state.isStartReached = checkThreshold(
1294
- scroll,
1295
- false,
1296
- threshold,
1297
- state.isStartReached,
1298
- startReachedSnapshot,
1299
- {
1300
- contentSize: totalSize,
1301
- dataLength,
1302
- scrollPosition: scroll
1303
- },
1304
- (distance) => {
1305
- let dispatched = false;
1306
- if (state.props.onStartReached && canDispatchReachedEdge(ctx, "start", allowedEdge, allowGateCreatedInCurrentCheck)) {
1307
- markReachedEdge(ctx);
1308
- state.props.onStartReached({ distanceFromStart: distance });
1309
- dispatched = true;
1310
- }
1311
- return dispatched;
1312
- },
1313
- (snapshot) => {
1314
- state.startReachedSnapshot = snapshot;
1315
- }
1316
- );
1317
- }
1347
+ // src/core/getStartOffsetAdjustment.ts
1348
+ function getStartOffsetAdjustment(ctx) {
1349
+ const { state } = ctx;
1350
+ const stylePaddingStart = state.props.horizontal ? (isHorizontalRTL(state) ? state.props.stylePaddingRight : state.props.stylePaddingLeft) || 0 : peek$(ctx, "stylePaddingTop") || 0;
1351
+ return stylePaddingStart + (peek$(ctx, "alignItemsAtEndPadding") || 0) + (peek$(ctx, "headerSize") || 0);
1352
+ }
1353
+ function getItemSpaceOffset(ctx, scrollOffset, pending = ((_a3) => (_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0)(), startOffset = getStartOffsetAdjustment(ctx)) {
1354
+ return Math.round(scrollOffset + (pending - startOffset));
1318
1355
  }
1319
1356
 
1320
- // src/utils/checkThresholds.ts
1321
- function checkThresholds(ctx, allowedEdge) {
1322
- const allowGateCreatedInCurrentCheck = !ctx.state.edgeReachedGate;
1323
- checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck);
1324
- checkAtTop(ctx, allowedEdge, allowGateCreatedInCurrentCheck);
1325
- rearmEndFollowIfSettled(ctx);
1357
+ // src/core/addTotalSize.ts
1358
+ function addTotalSize(ctx, key, add, notifyTotalSize = true) {
1359
+ const state = ctx.state;
1360
+ const prevTotalSize = state.totalSize;
1361
+ let totalSize = state.totalSize;
1362
+ if (key === null) {
1363
+ totalSize = add;
1364
+ if (state.timeoutSetPaddingTop) {
1365
+ clearTimeout(state.timeoutSetPaddingTop);
1366
+ state.timeoutSetPaddingTop = void 0;
1367
+ }
1368
+ } else {
1369
+ totalSize += add;
1370
+ }
1371
+ if (prevTotalSize !== totalSize) {
1372
+ if (!IsNewArchitecture && state.initialScroll && totalSize < prevTotalSize) {
1373
+ state.pendingTotalSize = totalSize;
1374
+ } else {
1375
+ state.pendingTotalSize = void 0;
1376
+ state.totalSize = totalSize;
1377
+ if (notifyTotalSize) {
1378
+ set$(ctx, "totalSize", totalSize);
1379
+ }
1380
+ updateContentMetricsState(ctx);
1381
+ }
1382
+ } else if (notifyTotalSize && ctx.values.get("totalSize") !== totalSize) {
1383
+ set$(ctx, "totalSize", totalSize);
1384
+ }
1326
1385
  }
1327
1386
 
1328
- // src/core/recalculateSettledScroll.ts
1329
- function recalculateSettledScroll(ctx) {
1330
- var _a3, _b;
1387
+ // src/core/setSize.ts
1388
+ function setSize(ctx, itemKey, size, notifyTotalSize = true) {
1331
1389
  const state = ctx.state;
1332
- if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1333
- (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1390
+ const { sizes } = state;
1391
+ const previousSize = sizes.get(itemKey);
1392
+ const diff = previousSize !== void 0 ? size - previousSize : size;
1393
+ if (diff !== 0) {
1394
+ state.positionsAreCurrent = false;
1395
+ addTotalSize(ctx, itemKey, diff, notifyTotalSize);
1334
1396
  }
1335
- checkThresholds(ctx);
1397
+ sizes.set(itemKey, size);
1336
1398
  }
1337
1399
 
1338
- // src/core/adaptiveRender.ts
1339
- var DEFAULT_ADAPTIVE_RENDER_ENTER_VELOCITY = 3;
1340
- var DEFAULT_ADAPTIVE_RENDER_EXIT_VELOCITY = 1;
1341
- var DEFAULT_ADAPTIVE_RENDER_EXIT_DELAY = 250;
1342
- var DEFAULT_WEB_ADAPTIVE_RENDER_ENTER_VELOCITY = 6;
1343
- var DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_VELOCITY = 3;
1344
- var DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_DELAY = 250;
1345
- function clearAdaptiveRenderExitTimeout(ctx) {
1346
- ctx.state.scheduledWork.cancel("adaptiveRender");
1400
+ // src/utils/helpers.ts
1401
+ function isFunction(obj) {
1402
+ return typeof obj === "function";
1347
1403
  }
1348
- function scheduleAdaptiveRenderExit(ctx, exitDelay) {
1349
- const state = ctx.state;
1350
- clearAdaptiveRenderExitTimeout(ctx);
1351
- if (exitDelay <= 0) {
1352
- setAdaptiveRender(ctx, "normal", "scroll");
1353
- } else {
1354
- state.scheduledWork.timeout(() => setAdaptiveRender(ctx, "normal", "scroll"), exitDelay, "adaptiveRender");
1355
- }
1404
+ function isArray(obj) {
1405
+ return Array.isArray(obj);
1356
1406
  }
1357
- function setAdaptiveRender(ctx, mode, reason) {
1358
- var _a3, _b;
1359
- const previousMode = peek$(ctx, "adaptiveRender");
1360
- if (previousMode !== mode) {
1361
- set$(ctx, "adaptiveRender", mode);
1362
- (_b = (_a3 = ctx.state.props.adaptiveRender) == null ? void 0 : _a3.onChange) == null ? void 0 : _b.call(_a3, mode, reason);
1407
+ var warned = /* @__PURE__ */ new Set();
1408
+ function warnDevOnce(id, text) {
1409
+ if (IS_DEV && !warned.has(id)) {
1410
+ warned.add(id);
1411
+ console.warn(`[legend-list] ${text}`);
1363
1412
  }
1364
1413
  }
1365
- function resetAdaptiveRender(ctx) {
1366
- var _a3, _b;
1367
- clearAdaptiveRenderExitTimeout(ctx);
1368
- const mode = (_b = (_a3 = ctx.state.props.adaptiveRender) == null ? void 0 : _a3.initialMode) != null ? _b : "normal";
1369
- if (peek$(ctx, "adaptiveRender") !== mode) {
1370
- setAdaptiveRender(ctx, mode, "initial");
1371
- }
1414
+ function roundSize(size) {
1415
+ return Math.floor(size * 8) / 8;
1372
1416
  }
1373
- function updateAdaptiveRender(ctx, scrollVelocity, options) {
1417
+ function isNullOrUndefined(value) {
1418
+ return value === null || value === void 0;
1419
+ }
1420
+ function getPadding(s, type) {
1374
1421
  var _a3, _b, _c;
1375
- const state = ctx.state;
1376
- const adaptiveRender = state.props.adaptiveRender;
1377
- const currentMode = peek$(ctx, "adaptiveRender");
1378
- if (peek$(ctx, "readyToRender")) {
1379
- if (adaptiveRender) {
1380
- const isWeb = Platform.OS === "web";
1381
- const enterVelocity = (_a3 = adaptiveRender.enterVelocity) != null ? _a3 : isWeb ? DEFAULT_WEB_ADAPTIVE_RENDER_ENTER_VELOCITY : DEFAULT_ADAPTIVE_RENDER_ENTER_VELOCITY;
1382
- const exitVelocity = (_b = adaptiveRender.exitVelocity) != null ? _b : isWeb ? DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_VELOCITY : DEFAULT_ADAPTIVE_RENDER_EXIT_VELOCITY;
1383
- const exitDelay = (_c = adaptiveRender.exitDelay) != null ? _c : isWeb ? DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_DELAY : DEFAULT_ADAPTIVE_RENDER_EXIT_DELAY;
1384
- const threshold = currentMode === "light" ? exitVelocity : enterVelocity;
1385
- const nextMode = (options == null ? void 0 : options.forceLight) || Math.abs(scrollVelocity) > threshold ? "light" : "normal";
1386
- const previousMode = state.scheduledWork.has("adaptiveRender") ? "normal" : currentMode;
1387
- if (nextMode !== previousMode) {
1388
- if (nextMode === "light") {
1389
- setAdaptiveRender(ctx, "light", "scroll");
1390
- scheduleAdaptiveRenderExit(ctx, exitDelay);
1391
- } else if (currentMode === "light") {
1392
- scheduleAdaptiveRenderExit(ctx, exitDelay);
1393
- }
1394
- }
1395
- } else {
1396
- resetAdaptiveRender(ctx);
1397
- }
1398
- }
1422
+ const axisPadding = type === "Left" || type === "Right" ? s.paddingHorizontal : s.paddingVertical;
1423
+ return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : axisPadding) != null ? _b : s.padding) != null ? _c : 0;
1399
1424
  }
1400
-
1401
- // src/utils/getEffectiveDrawDistance.ts
1402
- var INITIAL_DRAW_DISTANCE = 50;
1403
- function getEffectiveDrawDistance(ctx, mode) {
1404
- var _a3;
1405
- const drawDistance = ctx.state.props.drawDistance;
1406
- const initialScroll = ctx.state.initialScroll;
1407
- const needsFullInitialDrawDistance = initialScroll !== void 0 && ((_a3 = initialScroll.viewPosition) != null ? _a3 : 0) > 0;
1408
- const shouldCapDrawDistance = mode === "visible-first" || mode !== "full" && !peek$(ctx, "readyToRender") && !needsFullInitialDrawDistance;
1409
- return shouldCapDrawDistance ? Math.min(drawDistance, INITIAL_DRAW_DISTANCE) : drawDistance;
1425
+ function extractPadding(style, contentContainerStyle, type) {
1426
+ return getPadding(style, type) + getPadding(contentContainerStyle, type);
1410
1427
  }
1411
- function scheduleFullDrawDistancePrewarm(ctx) {
1412
- const { state } = ctx;
1413
- if (state.props.drawDistance <= INITIAL_DRAW_DISTANCE || state.scheduledWork.has("fullDrawDistancePrewarm")) {
1414
- return;
1428
+ function findContainerId(ctx, key) {
1429
+ var _a3, _b;
1430
+ const directMatch = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.containerItemKeys) == null ? void 0 : _b.get(key);
1431
+ if (directMatch !== void 0) {
1432
+ return directMatch;
1415
1433
  }
1416
- state.scheduledWork.frame(() => {
1417
- var _a3, _b, _c;
1418
- if (state.props.hasExternalScroll && !hasActiveInitialScroll(state) && !state.scrollingTo && !state.pendingNativeMVCPAdjust) {
1419
- const offset = (_b = (_a3 = state.refScroller.current) == null ? void 0 : _a3.getRawScrollOffset) == null ? void 0 : _b.call(_a3);
1420
- if (offset !== void 0) {
1421
- state.scroll = state.scrollPending = state.props.horizontal ? toLogicalHorizontalOffset(state, offset, state.totalSize) : offset;
1422
- }
1434
+ const numContainers = peek$(ctx, "numContainers");
1435
+ for (let i = 0; i < numContainers; i++) {
1436
+ const itemKey = peek$(ctx, `containerItemKey${i}`);
1437
+ if (itemKey === key) {
1438
+ return i;
1423
1439
  }
1424
- (_c = state.triggerCalculateItemsInView) == null ? void 0 : _c.call(state);
1425
- }, "fullDrawDistancePrewarm");
1440
+ }
1441
+ return -1;
1426
1442
  }
1427
1443
 
1428
- // src/utils/setInitialRenderState.ts
1429
- function resetInitialRenderState(ctx, {
1430
- resetLayout,
1431
- resetInitialScroll
1432
- }) {
1433
- const { state } = ctx;
1434
- if (resetLayout) {
1435
- state.didContainersLayout = false;
1436
- state.queuedInitialLayout = false;
1437
- state.scrollBufferDirection = void 0;
1438
- state.scrollHistory.length = 0;
1439
- state.scheduledWork.cancel("renderRangeProjection");
1440
- state.edgeReachedGate = void 0;
1441
- state.isStartReached = null;
1442
- state.isEndReached = null;
1443
- state.startReachedSnapshot = void 0;
1444
- state.endReachedSnapshot = void 0;
1444
+ // src/utils/getItemSize.ts
1445
+ function getKnownOrFixedSize(ctx, key, index, data, resolved) {
1446
+ var _a3, _b, _c, _d;
1447
+ const state = ctx.state;
1448
+ const { getFixedItemSize, getItemType } = state.props;
1449
+ let size = key ? state.sizesKnown.get(key) : void 0;
1450
+ if (size === void 0 && key && getFixedItemSize) {
1451
+ const itemType = (_b = resolved == null ? void 0 : resolved.itemType) != null ? _b : getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1452
+ const fixedSize = (resolved == null ? void 0 : resolved.didResolveFixedItemSize) ? resolved.fixedItemSize : getFixedItemSize(data, index, itemType);
1453
+ if (fixedSize !== void 0) {
1454
+ size = fixedSize + ctx.scrollAxisGap;
1455
+ state.positionsAreCurrent = false;
1456
+ state.positionRecalculationStartIndex = Math.min((_c = state.positionRecalculationStartIndex) != null ? _c : index, index);
1457
+ state.scrollForNextCalculateItemsInView = void 0;
1458
+ state.sizesKnown.set(key, size);
1459
+ (_d = state.fixedItemSizeKeys) != null ? _d : state.fixedItemSizeKeys = /* @__PURE__ */ new Set();
1460
+ state.fixedItemSizeKeys.add(key);
1461
+ }
1445
1462
  }
1446
- if (resetInitialScroll) {
1447
- state.didFinishInitialScroll = false;
1463
+ return size;
1464
+ }
1465
+ function getKnownOrFixedItemSize(ctx, index) {
1466
+ const key = getId(ctx.state, index);
1467
+ return getKnownOrFixedSize(ctx, key, index, ctx.state.props.data[index]);
1468
+ }
1469
+ function areKnownOrFixedItemSizesAvailable(ctx, startIndex, endIndex) {
1470
+ for (let index = startIndex; index <= endIndex; index++) {
1471
+ if (getKnownOrFixedItemSize(ctx, index) === void 0) {
1472
+ return false;
1473
+ }
1448
1474
  }
1449
- set$(ctx, "readyToRender", false);
1450
- resetAdaptiveRender(ctx);
1475
+ return true;
1451
1476
  }
1452
- function setInitialRenderState(ctx, {
1453
- didLayout,
1454
- didInitialScroll
1455
- }) {
1456
- const { state } = ctx;
1477
+ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize, notifyTotalSize, resolved) {
1478
+ var _a3, _b, _c, _d;
1479
+ const state = ctx.state;
1457
1480
  const {
1458
- loadStartTime,
1459
- props: { onLoad, onReady }
1481
+ sizes,
1482
+ averageSizes,
1483
+ props: { estimatedItemSize, getItemType },
1484
+ scrollingTo
1460
1485
  } = state;
1461
- if (didLayout) {
1462
- state.didContainersLayout = true;
1486
+ const sizeKnown = state.sizesKnown.get(key);
1487
+ if (sizeKnown !== void 0) {
1488
+ return sizeKnown;
1463
1489
  }
1464
- if (didInitialScroll) {
1465
- state.didFinishInitialScroll = true;
1490
+ const renderedSize = sizes.get(key);
1491
+ let size = getKnownOrFixedSize(ctx, key, index, data, resolved);
1492
+ if (size !== void 0) {
1493
+ setSize(ctx, key, size, notifyTotalSize);
1494
+ return size;
1466
1495
  }
1467
- const isReadyToRender = Boolean(state.didContainersLayout && state.didFinishInitialScroll);
1468
- if (isReadyToRender && !peek$(ctx, "readyToRender")) {
1469
- set$(ctx, "readyToRender", true);
1470
- setAdaptiveRender(ctx, "normal", "ready");
1471
- if (state.props.drawDistance > INITIAL_DRAW_DISTANCE) {
1472
- scheduleFullDrawDistancePrewarm(ctx);
1473
- }
1474
- if (!state.didLoad) {
1475
- state.didLoad = true;
1476
- if (onLoad) {
1477
- onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1478
- }
1496
+ if (preferCachedSize && renderedSize !== void 0) {
1497
+ return renderedSize;
1498
+ }
1499
+ const itemType = (_b = resolved == null ? void 0 : resolved.itemType) != null ? _b : getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1500
+ if (useAverageSize && !scrollingTo) {
1501
+ const averageSizeForType = (_c = averageSizes[itemType]) == null ? void 0 : _c.avg;
1502
+ if (averageSizeForType !== void 0) {
1503
+ size = roundSize(averageSizeForType);
1479
1504
  }
1480
- onReady == null ? void 0 : onReady();
1481
1505
  }
1506
+ if (size === void 0 && renderedSize !== void 0) {
1507
+ return renderedSize;
1508
+ }
1509
+ if (size === void 0 && useAverageSize && scrollingTo) {
1510
+ const averageSizeForType = (_d = scrollingTo.averageSizeSnapshot) == null ? void 0 : _d[itemType];
1511
+ if (averageSizeForType !== void 0) {
1512
+ size = roundSize(averageSizeForType);
1513
+ }
1514
+ }
1515
+ if (size === void 0) {
1516
+ size = estimatedItemSize + ctx.scrollAxisGap;
1517
+ }
1518
+ setSize(ctx, key, size, notifyTotalSize);
1519
+ return size;
1482
1520
  }
1483
-
1484
- // src/core/finishInitialScroll.ts
1485
- var PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS = 2e3;
1486
- function syncInitialScrollOffset(state, offset) {
1487
- state.scroll = offset;
1488
- state.scrollPending = offset;
1489
- state.scrollPrev = offset;
1490
- }
1491
- function clearPreservedInitialScrollTarget(state) {
1492
- state.scheduledWork.cancel("preservedInitialScroll");
1493
- state.clearPreservedInitialScrollOnNextFinish = void 0;
1494
- state.initialScroll = void 0;
1495
- state.preservedEndAnchorCorrection = void 0;
1496
- setInitialScrollSession(state);
1521
+ function getItemSizeAtIndex(ctx, index) {
1522
+ if (index === void 0 || index < 0) {
1523
+ return void 0;
1524
+ }
1525
+ const targetId = getId(ctx.state, index);
1526
+ return getItemSize(ctx, targetId, index, ctx.state.props.data[index]);
1497
1527
  }
1498
- function supersedeInitialScroll(ctx) {
1499
- var _a3, _b, _c;
1500
- const state = ctx.state;
1501
- const bootstrapInitialScroll = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" ? state.initialScrollSession.bootstrap : void 0;
1502
- if (state.initialScroll || bootstrapInitialScroll || ((_b = state.scrollingTo) == null ? void 0 : _b.isInitialScroll)) {
1503
- if ((bootstrapInitialScroll == null ? void 0 : bootstrapInitialScroll.frameHandle) !== void 0 && typeof cancelAnimationFrame === "function") {
1504
- cancelAnimationFrame(bootstrapInitialScroll.frameHandle);
1505
- }
1506
- if ((_c = state.scrollingTo) == null ? void 0 : _c.isInitialScroll) {
1507
- cancelScrollCompletionChecks(state);
1508
- state.scrollingTo = void 0;
1509
- state.scrollTargetPinnedRange = void 0;
1510
- }
1511
- initialScrollCompletion.resetFlags(state);
1512
- setInitialScrollSession(state, { bootstrap: null });
1513
- finishInitialScroll(ctx);
1528
+
1529
+ // src/core/calculateOffsetWithOffsetPosition.ts
1530
+ function getViewPositionOffset(availableSpace, viewPosition, viewPositionFallback) {
1531
+ let alignment = viewPosition;
1532
+ if (availableSpace < 0 && viewPositionFallback !== void 0) {
1533
+ alignment = viewPositionFallback === "start" ? 0 : 1;
1514
1534
  }
1535
+ return -alignment * availableSpace;
1515
1536
  }
1516
- function finishInitialScroll(ctx, options) {
1537
+ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1517
1538
  var _a3;
1518
1539
  const state = ctx.state;
1519
- const freshDataTransitionEpoch = state.freshDataTransitionEpoch;
1520
- if ((options == null ? void 0 : options.resolvedOffset) !== void 0) {
1521
- syncInitialScrollOffset(state, options.resolvedOffset);
1522
- } else if ((options == null ? void 0 : options.syncObservedOffset) && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
1523
- const observedOffset = getObservedScrollOffset(ctx);
1524
- if (Number.isFinite(observedOffset)) {
1525
- syncInitialScrollOffset(state, observedOffset);
1526
- }
1540
+ const { index, viewOffset, viewPosition, viewPositionFallback } = params;
1541
+ let offset = offsetParam;
1542
+ if (viewOffset) {
1543
+ offset -= viewOffset;
1527
1544
  }
1528
- const complete = () => {
1529
- var _a4, _b, _c, _d;
1530
- if (state.isUnmounted) {
1531
- return;
1532
- }
1533
- if (freshDataTransitionEpoch !== state.freshDataTransitionEpoch) {
1534
- (_a4 = options == null ? void 0 : options.onFinished) == null ? void 0 : _a4.call(options);
1535
- return;
1536
- }
1537
- const shouldReleaseDeferredPublicOnScroll = Platform.OS === "web" && ((_b = state.initialScrollSession) == null ? void 0 : _b.kind) === "bootstrap";
1538
- if (isEndOfContentTarget(state.initialScroll, state.props.data.length) && state.props.maintainScrollAtEnd && !state.endFollowInterrupted) {
1539
- set$(ctx, "isFollowingEnd", true);
1540
- }
1541
- const finalScrollOffset = (_c = options == null ? void 0 : options.resolvedOffset) != null ? _c : state.scrollPending;
1542
- initialScrollWatchdog.clear(state);
1543
- if ((options == null ? void 0 : options.preserveTarget) && state.initialScroll) {
1544
- state.clearPreservedInitialScrollOnNextFinish = void 0;
1545
- setInitialScrollSession(state);
1546
- state.scheduledWork.cancel("preservedInitialScroll");
1547
- if (options == null ? void 0 : options.schedulePreservedTargetClear) {
1548
- state.scheduledWork.timeout(
1549
- () => {
1550
- var _a5;
1551
- if (!state.didFinishInitialScroll || ((_a5 = state.scrollingTo) == null ? void 0 : _a5.isInitialScroll) || !state.initialScroll) {
1552
- return;
1553
- }
1554
- clearPreservedInitialScrollTarget(state);
1555
- },
1556
- PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS,
1557
- "preservedInitialScroll"
1558
- );
1559
- }
1560
- } else {
1561
- clearPreservedInitialScrollTarget(state);
1562
- }
1563
- if (options == null ? void 0 : options.recalculateItems) {
1564
- recalculateSettledScroll(ctx);
1565
- }
1566
- setInitialRenderState(ctx, { didInitialScroll: true });
1567
- if (shouldReleaseDeferredPublicOnScroll) {
1568
- releaseDeferredPublicOnScroll(ctx, finalScrollOffset);
1545
+ const leadingInset = index !== void 0 ? getContentInsetTop(ctx) : 0;
1546
+ if (index !== void 0) {
1547
+ offset += getStartOffsetAdjustment(ctx) - leadingInset;
1548
+ }
1549
+ if (viewPosition !== void 0 && index !== void 0) {
1550
+ const dataLength = state.props.data.length;
1551
+ if (dataLength === 0) {
1552
+ return offset;
1569
1553
  }
1570
- (_d = options == null ? void 0 : options.onFinished) == null ? void 0 : _d.call(options);
1571
- };
1572
- if (options == null ? void 0 : options.waitForCompletionFrame) {
1573
- requestAnimationFrame(complete);
1574
- return;
1554
+ const isOutOfBounds = index < 0 || index >= dataLength;
1555
+ const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
1556
+ const measuredItemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1557
+ const itemSize = Math.max(0, measuredItemSize - (isOutOfBounds ? 0 : ctx.scrollAxisGap));
1558
+ const trailingInset = getContentInsetEnd(ctx);
1559
+ const availableSpace = state.scrollLength - leadingInset - trailingInset - itemSize;
1560
+ offset += getViewPositionOffset(availableSpace, viewPosition, viewPositionFallback);
1575
1561
  }
1576
- complete();
1562
+ if (index !== void 0 && params.clampToContentContainerMinSize) {
1563
+ offset += getContentSize(ctx) - getContentSize(ctx, false);
1564
+ }
1565
+ return offset;
1577
1566
  }
1578
1567
 
1579
1568
  // src/core/mvcp.ts
@@ -1627,11 +1616,12 @@ function shouldQueueNativeMVCPAdjust(dataChanged, state, positionDiff, prevTotal
1627
1616
  const distanceFromEnd = prevTotalSize - prevScroll - state.scrollLength;
1628
1617
  return distanceFromEnd < Math.abs(positionDiff) - MVCP_POSITION_EPSILON;
1629
1618
  }
1630
- function getPredictedNativeClamp(state, unresolvedAmount, totalSize) {
1619
+ function getPredictedNativeClamp(ctx, unresolvedAmount, totalSize) {
1631
1620
  if (Math.abs(unresolvedAmount) <= MVCP_POSITION_EPSILON) {
1632
1621
  return 0;
1633
1622
  }
1634
- const maxScroll = Math.max(0, totalSize - state.scrollLength);
1623
+ const state = ctx.state;
1624
+ const maxScroll = getScrollRangeEnd(ctx, totalSize);
1635
1625
  const clampDelta = maxScroll - state.scroll;
1636
1626
  if (unresolvedAmount < 0) {
1637
1627
  return Math.max(unresolvedAmount, Math.min(0, clampDelta));
@@ -1659,7 +1649,7 @@ function maybeApplyPredictedNativeMVCPAdjust(ctx) {
1659
1649
  return;
1660
1650
  }
1661
1651
  const totalSize = getContentSize(ctx);
1662
- const predictedNativeClamp = getPredictedNativeClamp(state, pending.amount, totalSize);
1652
+ const predictedNativeClamp = getPredictedNativeClamp(ctx, pending.amount, totalSize);
1663
1653
  if (Math.abs(predictedNativeClamp) <= MVCP_POSITION_EPSILON) {
1664
1654
  return;
1665
1655
  }
@@ -1693,7 +1683,7 @@ function resolvePendingNativeMVCPAdjust(ctx, newScroll) {
1693
1683
  settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
1694
1684
  return true;
1695
1685
  }
1696
- const expectedNativeClampScroll = Math.max(0, getContentSize(ctx) - state.scrollLength);
1686
+ const expectedNativeClampScroll = getScrollRangeEnd(ctx);
1697
1687
  const distanceToClamp = Math.abs(newScroll - expectedNativeClampScroll);
1698
1688
  const isAtExpectedNativeClamp = distanceToClamp <= NATIVE_END_CLAMP_EPSILON;
1699
1689
  if (isAtExpectedNativeClamp) {
@@ -1842,7 +1832,7 @@ function prepareMVCP(ctx, dataChanged, adjustmentSource) {
1842
1832
  if (diff > 0) {
1843
1833
  diff = Math.max(0, totalSize - state.scroll - state.scrollLength);
1844
1834
  } else {
1845
- const maxScroll = Math.max(0, totalSize - state.scrollLength);
1835
+ const maxScroll = getScrollRangeEnd(ctx, totalSize);
1846
1836
  state.scroll = maxScroll;
1847
1837
  state.scrollPending = maxScroll;
1848
1838
  diff = 0;
@@ -1859,7 +1849,7 @@ function prepareMVCP(ctx, dataChanged, adjustmentSource) {
1859
1849
  const prevSize = scrollingTo.itemSize;
1860
1850
  if (prevSize !== void 0 && newSize !== prevSize) {
1861
1851
  if (scrollingTo.viewPositionFallback) {
1862
- const viewport = state.scrollLength - getContentInsetEnd(ctx);
1852
+ const viewport = state.scrollLength - getContentInsetTop(ctx) - getContentInsetEnd(ctx);
1863
1853
  const previousSpace = viewport - Math.max(0, prevSize - ctx.scrollAxisGap);
1864
1854
  const nextSpace = viewport - Math.max(0, newSize - ctx.scrollAxisGap);
1865
1855
  positionDiff += getViewPositionOffset(
@@ -1884,6 +1874,15 @@ function prepareMVCP(ctx, dataChanged, adjustmentSource) {
1884
1874
  now,
1885
1875
  positionDiff
1886
1876
  });
1877
+ if (Platform.OS === "android" && !dataChanged && scrollTarget === void 0) {
1878
+ const maxScroll = getScrollRangeEnd(ctx);
1879
+ if (state.scroll > maxScroll + MVCP_POSITION_EPSILON) {
1880
+ const targetScroll = Math.max(0, Math.min(state.scroll + positionDiff, maxScroll));
1881
+ state.scroll = maxScroll;
1882
+ state.scrollPending = maxScroll;
1883
+ positionDiff = targetScroll - maxScroll;
1884
+ }
1885
+ }
1887
1886
  if (shouldQueueNativeMVCPAdjust(dataChanged, state, positionDiff, prevTotalSize, prevScroll, scrollTarget)) {
1888
1887
  state.pendingNativeMVCPAdjust = {
1889
1888
  amount: positionDiff,
@@ -2063,27 +2062,31 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2063
2062
  }
2064
2063
 
2065
2064
  // src/core/cancelScroll.ts
2066
- function cancelScroll(ctx, stopAnimation = true, commitAdjust = true) {
2065
+ function cancelScroll(ctx, stopAnimation = true, commitAdjust = true, inputDelta = 0) {
2067
2066
  var _a3, _b;
2068
2067
  const { state } = ctx;
2069
2068
  const observed = getObservedScrollOffset(ctx, peek$(ctx, "scrollAdjustPending") ? state.scrollPending : void 0);
2070
2069
  const wasAnimating = !!((_a3 = state.scrollingTo) == null ? void 0 : _a3.animated) || !!((_b = ctx.scrollRequestTracker) == null ? void 0 : _b.cancel());
2070
+ const stopped = wasAnimating && stopAnimation;
2071
+ const stopOffset = stopped && inputDelta ? clampScrollOffset(ctx, observed + inputDelta) : observed;
2071
2072
  releaseScrollOwners(ctx, true);
2072
- state.scrollPending = observed;
2073
+ state.scrollPending = stopOffset;
2073
2074
  const ignoreScrollFromMVCP = state.ignoreScrollFromMVCP;
2074
2075
  if (peek$(ctx, "scrollAdjustPending")) state.ignoreScrollFromMVCP = void 0;
2075
- updateScroll(ctx, observed, true, { markHasScrolled: false });
2076
+ updateScroll(ctx, stopOffset, true, { markHasScrolled: false });
2076
2077
  state.ignoreScrollFromMVCP = ignoreScrollFromMVCP;
2077
- if (wasAnimating && stopAnimation) stopScrollAnimation(ctx, observed);
2078
+ if (stopped) stopScrollAnimation(ctx, observed, inputDelta);
2078
2079
  if (commitAdjust) state.scrollAdjustHandler.commitPendingAdjust();
2079
2080
  else state.scrollAdjustHandler.supersedePendingAdjust();
2081
+ return stopped;
2080
2082
  }
2081
- function stopScrollAnimation(ctx, observed = getObservedScrollOffset(ctx)) {
2083
+ function stopScrollAnimation(ctx, observed = getObservedScrollOffset(ctx), inputDelta = 0) {
2082
2084
  var _a3, _b;
2083
2085
  const { state } = ctx;
2084
2086
  const scroller = state.refScroller.current;
2085
- const x = state.props.horizontal ? (_b = (_a3 = scroller == null ? void 0 : scroller.getCurrentScrollOffset) == null ? void 0 : _a3.call(scroller)) != null ? _b : getNativeScrollPoint(ctx, observed).x : 0;
2086
- scroller == null ? void 0 : scroller.scrollTo({ animated: false, x, y: state.props.horizontal ? 0 : observed });
2087
+ const offset = observed + inputDelta;
2088
+ const x = !state.props.horizontal ? 0 : inputDelta ? getNativeScrollPoint(ctx, offset).x : (_b = (_a3 = scroller == null ? void 0 : scroller.getCurrentScrollOffset) == null ? void 0 : _a3.call(scroller)) != null ? _b : getNativeScrollPoint(ctx, observed).x;
2089
+ scroller == null ? void 0 : scroller.scrollTo({ animated: false, x, y: state.props.horizontal ? 0 : offset });
2087
2090
  }
2088
2091
  function releaseScrollOwners(ctx, supersedeInitial) {
2089
2092
  var _a3;
@@ -2097,12 +2100,45 @@ function releaseScrollOwners(ctx, supersedeInitial) {
2097
2100
  clearPreservedInitialScrollTarget(state);
2098
2101
  syncScrollActive(ctx);
2099
2102
  }
2100
- function interruptUserScroll(ctx) {
2101
- if (ctx.state.scrollingTo || ctx.state.initialScroll && !ctx.state.didFinishInitialScroll) {
2102
- cancelScroll(ctx, Platform.OS === "web");
2103
- } else {
2104
- releaseScrollOwners(ctx, false);
2103
+ function interruptUserScroll(ctx, inputDelta = 0) {
2104
+ var _a3;
2105
+ if (ctx.state.scrollingTo || ((_a3 = ctx.scrollRequestTracker) == null ? void 0 : _a3.isWaitingToRun()) || ctx.state.initialScroll && !ctx.state.didFinishInitialScroll) {
2106
+ return cancelScroll(ctx, Platform.OS === "web", true, inputDelta) && inputDelta !== 0;
2107
+ }
2108
+ releaseScrollOwners(ctx, false);
2109
+ return false;
2110
+ }
2111
+
2112
+ // src/core/calculateOffsetForIndex.ts
2113
+ function calculateOffsetForIndex(ctx, index) {
2114
+ const state = ctx.state;
2115
+ return index !== void 0 ? state.positions[index] || 0 : 0;
2116
+ }
2117
+
2118
+ // src/utils/clampScrollIndex.ts
2119
+ function clampScrollIndex(index, dataLength) {
2120
+ if (dataLength <= 0) {
2121
+ return -1;
2105
2122
  }
2123
+ if (index >= dataLength) {
2124
+ return dataLength - 1;
2125
+ }
2126
+ if (index < 0) {
2127
+ return 0;
2128
+ }
2129
+ return index;
2130
+ }
2131
+
2132
+ // src/core/getCurrentTargetOffset.ts
2133
+ function isEndAlignedLastItemTarget(ctx, scrollingTo) {
2134
+ return (scrollingTo.isScrollToEnd || scrollingTo.index === ctx.state.props.data.length - 1) && scrollingTo.viewPosition === 1;
2135
+ }
2136
+ function getCurrentTargetOffset(ctx, scrollingTo) {
2137
+ var _a3, _b;
2138
+ const index = scrollingTo.isScrollToEnd ? ctx.state.props.data.length - 1 : scrollingTo.index === void 0 ? void 0 : clampScrollIndex(scrollingTo.index, ctx.state.props.data.length);
2139
+ const isRetargetingWebScroll = Platform.OS === "web" && (!scrollingTo.isInitialScroll || ((_a3 = ctx.state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap");
2140
+ const requestedTargetOffset = index !== void 0 && (isRetargetingWebScroll || isEndAlignedLastItemTarget(ctx, scrollingTo)) ? calculateOffsetWithOffsetPosition(ctx, calculateOffsetForIndex(ctx, index), { ...scrollingTo, index }) : isRetargetingWebScroll ? calculateOffsetWithOffsetPosition(ctx, scrollingTo.offset, scrollingTo) : (_b = scrollingTo.targetOffset) != null ? _b : scrollingTo.offset - (scrollingTo.viewOffset || 0);
2141
+ return clampScrollOffset(ctx, requestedTargetOffset, scrollingTo);
2106
2142
  }
2107
2143
 
2108
2144
  // src/core/scrollRequestTracker.ts
@@ -2146,7 +2182,7 @@ function getScrollRequestTracker(ctx) {
2146
2182
  state.scrollAdjustHandler.commitPendingAdjust();
2147
2183
  }
2148
2184
  }
2149
- if (didStartScroll === "unreachable") {
2185
+ if (didStartScroll === "unreachable" || !didStartScroll && state.scrollingTo) {
2150
2186
  cancelScroll(ctx);
2151
2187
  return;
2152
2188
  }
@@ -2187,6 +2223,18 @@ function getScrollRequestTracker(ctx) {
2187
2223
  }
2188
2224
 
2189
2225
  // src/core/doMaintainScrollAtEnd.ts
2226
+ function reissueClampedNativeEndScroll(ctx) {
2227
+ var _a3, _b, _c;
2228
+ const state = ctx.state;
2229
+ const target = state.scrollingTo;
2230
+ if ((target == null ? void 0 : target.isScrollToEnd) && !target.animated && !((_a3 = ctx.scrollRequestTracker) == null ? void 0 : _a3.isWaitingToRun())) {
2231
+ const targetOffset = getCurrentTargetOffset(ctx, target);
2232
+ const distanceToTarget = targetOffset - ((_b = state.lastNativeScroll) != null ? _b : state.scroll);
2233
+ if (distanceToTarget > 1 || state.lastNativeScroll === void 0 && distanceToTarget < -1) {
2234
+ (_c = state.refScroller.current) == null ? void 0 : _c.scrollTo({ animated: false, ...getNativeScrollPoint(ctx, targetOffset) });
2235
+ }
2236
+ }
2237
+ }
2190
2238
  function doMaintainScrollAtEnd(ctx, options) {
2191
2239
  var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
2192
2240
  const state = ctx.state;
@@ -2235,7 +2283,7 @@ function doMaintainScrollAtEnd(ctx, options) {
2235
2283
  return false;
2236
2284
  }
2237
2285
  if (state.scrollingTo || state.pendingScrollResolve) {
2238
- if (immediate && ((_k = state.scrollingTo) == null ? void 0 : _k.isScrollToEnd) && !state.scrollingTo.animated) {
2286
+ if ((options == null ? void 0 : options.immediate) && !maintainScrollAtEnd.animated && ((_k = state.scrollingTo) == null ? void 0 : _k.isScrollToEnd) && !state.scrollingTo.animated) {
2239
2287
  state.pendingMaintainScrollAtEnd = false;
2240
2288
  ctx.scrollToEnd({ animated: false });
2241
2289
  return true;
@@ -2246,7 +2294,7 @@ function doMaintainScrollAtEnd(ctx, options) {
2246
2294
  state.pendingMaintainScrollAtEnd = false;
2247
2295
  const contentSize = getContentSize(ctx);
2248
2296
  if (contentSize < state.scrollLength) {
2249
- state.scroll = 0;
2297
+ state.scroll = clampScrollOffset(ctx, 0);
2250
2298
  }
2251
2299
  if (!state.maintainingScrollAtEnd || immediate && (state.maintainingScrollAtEnd === "pending-instant" || state.maintainingScrollAtEnd === "instant")) {
2252
2300
  const pendingState = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
@@ -2339,20 +2387,6 @@ function finishScrollTo(ctx) {
2339
2387
  }
2340
2388
  }
2341
2389
 
2342
- // src/utils/clampScrollIndex.ts
2343
- function clampScrollIndex(index, dataLength) {
2344
- if (dataLength <= 0) {
2345
- return -1;
2346
- }
2347
- if (index >= dataLength) {
2348
- return dataLength - 1;
2349
- }
2350
- if (index < 0) {
2351
- return 0;
2352
- }
2353
- return index;
2354
- }
2355
-
2356
2390
  // src/core/checkFinishedScroll.ts
2357
2391
  var INITIAL_SCROLL_MAX_FALLBACK_CHECKS = 20;
2358
2392
  var INITIAL_SCROLL_COMPLETION_TARGET_EPSILON = 1;
@@ -2401,16 +2435,6 @@ function shouldFinishInitialZeroTargetScroll(ctx) {
2401
2435
  const { state } = ctx;
2402
2436
  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;
2403
2437
  }
2404
- function isEndAlignedLastItemTarget(ctx, scrollingTo) {
2405
- return (scrollingTo.isScrollToEnd || scrollingTo.index === ctx.state.props.data.length - 1) && scrollingTo.viewPosition === 1;
2406
- }
2407
- function getCurrentTargetOffset(ctx, scrollingTo) {
2408
- var _a3, _b;
2409
- const index = scrollingTo.isScrollToEnd ? ctx.state.props.data.length - 1 : scrollingTo.index === void 0 ? void 0 : clampScrollIndex(scrollingTo.index, ctx.state.props.data.length);
2410
- const isRetargetingWebScroll = Platform.OS === "web" && (!scrollingTo.isInitialScroll || ((_a3 = ctx.state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap");
2411
- const requestedTargetOffset = index !== void 0 && (isRetargetingWebScroll || isEndAlignedLastItemTarget(ctx, scrollingTo)) ? calculateOffsetWithOffsetPosition(ctx, calculateOffsetForIndex(ctx, index), { ...scrollingTo, index }) : isRetargetingWebScroll ? calculateOffsetWithOffsetPosition(ctx, scrollingTo.offset, scrollingTo) : (_b = scrollingTo.targetOffset) != null ? _b : scrollingTo.offset - (scrollingTo.viewOffset || 0);
2412
- return clampScrollOffset(ctx, requestedTargetOffset, scrollingTo);
2413
- }
2414
2438
  function getResolvedScrollCompletionState(ctx, scrollingTo) {
2415
2439
  const { state } = ctx;
2416
2440
  const isWebInitialScroll = Platform.OS === "web" && scrollingTo.isInitialScroll;
@@ -2422,9 +2446,10 @@ function getResolvedScrollCompletionState(ctx, scrollingTo) {
2422
2446
  const adjustedTargetOffset = clampedTargetOffset + adjust;
2423
2447
  const diff2 = Math.abs(scroll - adjustedTargetOffset);
2424
2448
  const canUseAdjustedCompletion = !isWebInitialScroll && (!scrollingTo.animated || Platform.OS === "ios");
2449
+ const isWaitingForNativeEnd = Platform.OS !== "web" && scrollingTo.isScrollToEnd && !state.maintainingScrollAtEnd && !scrollingTo.isInitialScroll && !scrollingTo.animated && state.lastNativeScroll !== void 0 && state.lastNativeScroll < Math.min(clampedTargetOffset, adjustedTargetOffset) - 1;
2425
2450
  return {
2426
2451
  clampedTargetOffset,
2427
- isAtResolvedTarget: Math.abs(scroll - maxOffset) < 1 && (diff1 < 1 || canUseAdjustedCompletion && diff2 < 1)
2452
+ isAtResolvedTarget: !isWaitingForNativeEnd && Math.abs(scroll - maxOffset) < 1 && (diff1 < 1 || canUseAdjustedCompletion && diff2 < 1)
2428
2453
  };
2429
2454
  }
2430
2455
  function checkFinishedScrollFrame(ctx) {
@@ -2699,18 +2724,20 @@ function scrollTo(ctx, params) {
2699
2724
  if (forceScroll || !isInitialScroll || Platform.OS === "android") {
2700
2725
  doScrollTo(ctx, { animated, horizontal, isInitialScroll, offset });
2701
2726
  } else {
2702
- state.scroll = offset;
2727
+ state.scroll = targetOffset;
2703
2728
  }
2704
2729
  }
2705
2730
 
2706
2731
  // src/core/initialScroll.ts
2707
2732
  function dispatchInitialScroll(ctx, params) {
2733
+ var _a3;
2708
2734
  const { forceScroll, resolvedOffset, target, waitForCompletionFrame } = params;
2709
2735
  const requestedIndex = target.index;
2710
2736
  const index = requestedIndex !== void 0 ? clampScrollIndex(requestedIndex, ctx.state.props.data.length) : void 0;
2711
2737
  const itemSize = getItemSizeAtIndex(ctx, index);
2712
2738
  scrollTo(ctx, {
2713
2739
  animated: false,
2740
+ clampToContentContainerMinSize: ((_a3 = ctx.state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" || isEndOfContentTarget(target, ctx.state.props.data.length),
2714
2741
  forceScroll,
2715
2742
  index: index !== void 0 && index >= 0 ? index : void 0,
2716
2743
  isInitialScroll: true,
@@ -2743,8 +2770,12 @@ function resolveInitialScrollOffset(ctx, initialScroll) {
2743
2770
  return (_b = initialScroll.contentOffset) != null ? _b : 0;
2744
2771
  }
2745
2772
  const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, initialScroll.index) : 0;
2746
- const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, initialScroll);
2747
- return clampScrollOffset(ctx, resolvedOffset, initialScroll);
2773
+ const target = {
2774
+ ...initialScroll,
2775
+ clampToContentContainerMinSize: isEndOfContentTarget(initialScroll, state.props.data.length)
2776
+ };
2777
+ const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, target);
2778
+ return clampScrollOffset(ctx, resolvedOffset, target);
2748
2779
  }
2749
2780
  function getAdvanceableInitialScrollState(state, options) {
2750
2781
  const { didFinishInitialScroll, queuedInitialLayout, scrollingTo } = state;
@@ -3496,7 +3527,7 @@ function updateSnapToOffsets(ctx) {
3496
3527
  const {
3497
3528
  props: { snapToIndices }
3498
3529
  } = state;
3499
- const contentSize = state.props.horizontal ? getContentSize(ctx) : void 0;
3530
+ const contentSize = state.props.horizontal ? getContentSize(ctx, false) : void 0;
3500
3531
  const snapToOffsets = Array(snapToIndices.length);
3501
3532
  for (let i = 0; i < snapToIndices.length; i++) {
3502
3533
  const idx = snapToIndices[i];
@@ -4555,31 +4586,7 @@ function maybeEmitFirstVisibleItemChanged(state, index) {
4555
4586
  state.lastFirstVisibleItemCallback = { index, key };
4556
4587
  onFirstVisibleItemChanged({ index, item: state.props.data[index], key });
4557
4588
  }
4558
- function findFirstVisibleIndexInCachedRange(ctx, scroll) {
4559
- var _a3, _b;
4560
- const state = ctx.state;
4561
- const {
4562
- endBuffered,
4563
- idCache,
4564
- positions,
4565
- props: { data },
4566
- sizes,
4567
- startBuffered
4568
- } = state;
4569
- if (startBuffered === null || endBuffered === null || startBuffered < 0 || endBuffered < startBuffered) {
4570
- return null;
4571
- }
4572
- for (let i = startBuffered; i <= endBuffered && i < data.length; i++) {
4573
- const id = (_a3 = idCache[i]) != null ? _a3 : getId(state, i);
4574
- const size = (_b = sizes.get(id)) != null ? _b : getItemSize(ctx, id, i, data[i]);
4575
- const top = positions[i];
4576
- if (top + size > scroll) {
4577
- return i;
4578
- }
4579
- }
4580
- return null;
4581
- }
4582
- function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, scrollLength, scroll, scrollBottom) {
4589
+ function updateVisibleRangeForCachedRange(ctx, viewabilityConfigCallbackPairs, scrollLength, scroll, scrollBottom) {
4583
4590
  var _a3, _b, _c, _d;
4584
4591
  const state = ctx.state;
4585
4592
  const {
@@ -4607,14 +4614,11 @@ function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, sc
4607
4614
  break;
4608
4615
  }
4609
4616
  }
4610
- Object.assign(state, {
4611
- endNoBuffer: visibleRange.endNoBuffer,
4612
- firstFullyOnScreenIndex: visibleRange.firstFullyOnScreenIndex,
4613
- idsInView: getIdsInVisibleRange(state, visibleRange),
4614
- startNoBuffer: visibleRange.startNoBuffer
4617
+ Object.assign(state, visibleRange, {
4618
+ idsInView: getIdsInVisibleRange(state, visibleRange)
4615
4619
  });
4616
4620
  maybeEmitFirstVisibleItemChanged(state, visibleRange.startNoBuffer);
4617
- if (state.props.hasExternalScroll || visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null) {
4621
+ if (viewabilityConfigCallbackPairs && (state.props.hasExternalScroll || visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null)) {
4618
4622
  updateViewableItems(
4619
4623
  state,
4620
4624
  ctx,
@@ -4657,7 +4661,8 @@ function calculateItemsInView(ctx, params = {}) {
4657
4661
  if (!data || scrollLength === 0 || !prevNumContainers) {
4658
4662
  return;
4659
4663
  }
4660
- let totalSize = getContentSize(ctx);
4664
+ const includeContentContainerMinSize = Platform.OS === "web" || !isHorizontalRTL(state);
4665
+ let totalSize = getContentSize(ctx, includeContentContainerMinSize);
4661
4666
  let changedContainerIds;
4662
4667
  const topPad = getStartOffsetAdjustment(ctx);
4663
4668
  const numColumns = peek$(ctx, "numColumns");
@@ -4724,8 +4729,9 @@ function calculateItemsInView(ctx, params = {}) {
4724
4729
  const scrollStart = state.props.hasExternalScroll ? scroll : Math.max(0, scroll);
4725
4730
  const overscrollBeforeContent = state.props.hasExternalScroll ? 0 : Math.max(0, -nativeScrollState);
4726
4731
  scrollBottom = Math.max(scrollStart, scroll + scrollLength + overscrollBeforeContent);
4732
+ const unusedTopBuffer = state.props.hasExternalScroll ? 0 : Math.max(0, scrollBufferTop - scroll);
4727
4733
  scrollTopBuffered = scrollStart - scrollBufferTop + projectedBufferAdjustment;
4728
- scrollBottomBuffered = scrollBottom + scrollBufferBottom + projectedBufferAdjustment;
4734
+ scrollBottomBuffered = scrollBottom + scrollBufferBottom + unusedTopBuffer + projectedBufferAdjustment;
4729
4735
  };
4730
4736
  updateScrollRange();
4731
4737
  if (projectedBufferAdjustment !== 0) {
@@ -4737,17 +4743,13 @@ function calculateItemsInView(ctx, params = {}) {
4737
4743
  state.scrollForNextCalculateItemsInView = void 0;
4738
4744
  } else if ((top === null || scrollTopBuffered > top) && (bottom === null || scrollBottomBuffered < bottom)) {
4739
4745
  if (Platform.OS !== "web" || !isInMVCPActiveMode(state)) {
4740
- if (viewabilityConfigCallbackPairs) {
4741
- updateViewabilityForCachedRange(
4742
- ctx,
4743
- viewabilityConfigCallbackPairs,
4744
- scrollLength,
4745
- scroll,
4746
- scrollBottom
4747
- );
4748
- } else if (state.props.onFirstVisibleItemChanged) {
4749
- maybeEmitFirstVisibleItemChanged(state, findFirstVisibleIndexInCachedRange(ctx, scroll));
4750
- }
4746
+ updateVisibleRangeForCachedRange(
4747
+ ctx,
4748
+ viewabilityConfigCallbackPairs,
4749
+ scrollLength,
4750
+ scroll,
4751
+ scrollBottom
4752
+ );
4751
4753
  (_d = stickyState == null ? void 0 : stickyState.finishCalculateItemsInView) == null ? void 0 : _d.call(stickyState);
4752
4754
  return;
4753
4755
  }
@@ -4769,7 +4771,7 @@ function calculateItemsInView(ctx, params = {}) {
4769
4771
  scrollVelocity: speed,
4770
4772
  startIndex
4771
4773
  });
4772
- totalSize = getContentSize(ctx);
4774
+ totalSize = getContentSize(ctx, includeContentContainerMinSize);
4773
4775
  if (positionRecalculationStartIndex !== void 0) {
4774
4776
  state.positionRecalculationStartIndex = void 0;
4775
4777
  }
@@ -4867,14 +4869,11 @@ function calculateItemsInView(ctx, params = {}) {
4867
4869
  }
4868
4870
  }
4869
4871
  }
4870
- Object.assign(state, {
4872
+ Object.assign(state, visibleRange, {
4871
4873
  endBuffered,
4872
- endNoBuffer: visibleRange.endNoBuffer,
4873
- firstFullyOnScreenIndex: visibleRange.firstFullyOnScreenIndex,
4874
4874
  idsInView: getIdsInVisibleRange(state, visibleRange),
4875
4875
  startBuffered,
4876
- startBufferedId,
4877
- startNoBuffer: visibleRange.startNoBuffer
4876
+ startBufferedId
4878
4877
  });
4879
4878
  if (enableScrollForNextCalculateItemsInView && nextTop !== void 0 && nextBottom !== void 0) {
4880
4879
  state.scrollForNextCalculateItemsInView = isNullOrUndefined(nextTop) && isNullOrUndefined(nextBottom) ? void 0 : {
@@ -5137,7 +5136,11 @@ function maybeUpdateAnchoredEndSpace(ctx) {
5137
5136
  contentBelowAnchor = Math.max(0, contentBelowAnchor - ctx.scrollAxisGap);
5138
5137
  contentBelowAnchor += (ctx.values.get("footerSize") || 0) + getStylePaddingEnd(state.props);
5139
5138
  isReady = !hasUnknownTailSize;
5140
- const knownSizeBound = Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
5139
+ const externalEndInset = Platform.OS !== "web" && state.props.anchoredEndSpaceOwner === "list" && !isHorizontalRTL(state) ? getContentInsetEnd(ctx) - (previousSize || 0) : 0;
5140
+ const knownSizeBound = Math.max(
5141
+ 0,
5142
+ state.scrollLength - contentBelowAnchor - anchorOffset - externalEndInset
5143
+ );
5141
5144
  nextSize = hasUnknownTailSize ? Math.min(previousSize || 0, knownSizeBound) : knownSizeBound;
5142
5145
  } else if (anchorIndex >= 0) {
5143
5146
  isReady = false;
@@ -5268,13 +5271,27 @@ function updateItemSizes(ctx, measurement) {
5268
5271
  updateItemSizesBatch(ctx, [measurement]);
5269
5272
  }
5270
5273
  }
5274
+ function isContainerItemCurrent(ctx, containerId, itemKey) {
5275
+ if (peek$(ctx, `containerItemKey${containerId}`) !== itemKey) return false;
5276
+ const state = ctx.state;
5277
+ const index = state.indexByKey.get(itemKey);
5278
+ if (index !== void 0 && index < state.props.data.length && getId(state, index) === itemKey) return true;
5279
+ state.scheduledWork.microtask(
5280
+ () => {
5281
+ var _a3;
5282
+ return (_a3 = ctx.containerLayoutTriggers.get(containerId)) == null ? void 0 : _a3();
5283
+ },
5284
+ `containerMeasurementRetry${containerId}`
5285
+ );
5286
+ return false;
5287
+ }
5271
5288
  function updateItemSizesBatch(ctx, measurements) {
5272
5289
  var _a3;
5273
5290
  const state = ctx.state;
5274
5291
  const result = {};
5275
5292
  const previousTotalSize = state.totalSize;
5276
5293
  for (const measurement of measurements) {
5277
- const ownsMeasuredItem = measurement.containerId === void 0 || peek$(ctx, `containerItemKey${measurement.containerId}`) === measurement.itemKey;
5294
+ const ownsMeasuredItem = measurement.containerId === void 0 || isContainerItemCurrent(ctx, measurement.containerId, measurement.itemKey);
5278
5295
  if (ownsMeasuredItem) {
5279
5296
  const index = state.indexByKey.get(measurement.itemKey);
5280
5297
  const itemData = index === void 0 ? void 0 : (_a3 = state.props.data) == null ? void 0 : _a3[index];
@@ -5427,20 +5444,6 @@ function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem, notif
5427
5444
  }
5428
5445
 
5429
5446
  // src/core/measureContainersInLayoutEffect.native.ts
5430
- function resolveFixedItemSize(ctx, containerId, itemKey) {
5431
- var _a3;
5432
- const state = ctx.state;
5433
- const { data, getFixedItemSize } = state.props;
5434
- const index = state.indexByKey.get(itemKey);
5435
- let fixedItemSize;
5436
- if (data && getFixedItemSize && index !== void 0) {
5437
- const itemData = data[index];
5438
- if (itemData !== void 0) {
5439
- fixedItemSize = (_a3 = resolveContainerItemMetadata(state, containerId, index, itemData)) == null ? void 0 : _a3.fixedItemSize;
5440
- }
5441
- }
5442
- return fixedItemSize;
5443
- }
5444
5447
  function resolveSkippedAnchorReset(ctx, itemKey) {
5445
5448
  const state = ctx.state;
5446
5449
  const anchorReset = state.userScrollAnchorReset;
@@ -5460,7 +5463,7 @@ function revealMeasuredGeneration(ctx, containerId, itemKey, generation) {
5460
5463
  }
5461
5464
  }
5462
5465
  function measureContainersInLayoutEffect(ctx, targetContainerIds = null) {
5463
- var _a3, _b;
5466
+ var _a3, _b, _c;
5464
5467
  const state = ctx.state;
5465
5468
  const measurements = [];
5466
5469
  const measuredGenerations = state.props.hideItemsUntilMeasured ? [] : void 0;
@@ -5472,7 +5475,10 @@ function measureContainersInLayoutEffect(ctx, targetContainerIds = null) {
5472
5475
  if (itemKey !== void 0) {
5473
5476
  const generation = ((_b = state.containerItemGenerations[containerId]) != null ? _b : 0) + 1;
5474
5477
  state.containerItemGenerations[containerId] = generation;
5475
- const fixedItemSize = resolveFixedItemSize(ctx, containerId, itemKey);
5478
+ if (!isContainerItemCurrent(ctx, containerId, itemKey)) continue;
5479
+ const index = state.indexByKey.get(itemKey);
5480
+ const itemData = state.props.data[index];
5481
+ const fixedItemSize = state.props.getFixedItemSize && itemData !== void 0 ? (_c = resolveContainerItemMetadata(state, containerId, index, itemData)) == null ? void 0 : _c.fixedItemSize : void 0;
5476
5482
  const canSkipMeasurement = !state.needsOtherAxisSize && fixedItemSize !== void 0 && state.sizesKnown.get(itemKey) === fixedItemSize + ctx.scrollAxisGap;
5477
5483
  if (canSkipMeasurement) {
5478
5484
  resolveSkippedAnchorReset(ctx, itemKey);
@@ -6434,6 +6440,9 @@ function setContentInsetOverride(ctx, inset) {
6434
6440
  const didChange = !areInsetsEqual(previousInset, nextInset);
6435
6441
  state.contentInsetOverride = nextInset;
6436
6442
  if (didChange) {
6443
+ if (Platform.OS !== "web" && state.props.anchoredEndSpaceOwner === "list") {
6444
+ maybeUpdateAnchoredEndSpace(ctx);
6445
+ }
6437
6446
  updateContentMetricsState(ctx);
6438
6447
  }
6439
6448
  return didChange;
@@ -6592,6 +6601,14 @@ var ListComponent = typedMemo(function ListComponent2({
6592
6601
  },
6593
6602
  [ctx, onScrollToTop]
6594
6603
  );
6604
+ const { onContentSizeChange } = rest;
6605
+ const onContentSizeChangeInternal = React2.useCallback(
6606
+ (width, height) => {
6607
+ reissueClampedNativeEndScroll(ctx);
6608
+ onContentSizeChange == null ? void 0 : onContentSizeChange(width, height);
6609
+ },
6610
+ [ctx, onContentSizeChange]
6611
+ );
6595
6612
  const onLayoutHeader = React2.useCallback(
6596
6613
  (rect) => {
6597
6614
  const size = rect[horizontal ? "width" : "height"];
@@ -6613,6 +6630,7 @@ var ListComponent = typedMemo(function ListComponent2({
6613
6630
  {
6614
6631
  ...rest,
6615
6632
  ...Platform.OS === "web" ? ScrollComponent === ListComponentScrollView ? { onInternalScrollEnd, scrollElement, useWindowScroll } : {} : {
6633
+ onContentSizeChange: onContentSizeChangeInternal,
6616
6634
  onScrollBeginDrag: onInternalScrollBeginDrag,
6617
6635
  onScrollEndDrag: onInternalScrollEndDrag,
6618
6636
  onScrollToTop: onScrollToTopInternal
@@ -6751,6 +6769,7 @@ function checkResetContainers(ctx, dataProp, { didColumnsChange = false } = {})
6751
6769
  state.positionRecalculationStartIndex = 0;
6752
6770
  state.scrollForNextCalculateItemsInView = void 0;
6753
6771
  }
6772
+ state.positionsAreCurrent = false;
6754
6773
  calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
6755
6774
  if ((_a3 = state.pendingEndMeasurements) == null ? void 0 : _a3.size) {
6756
6775
  const removedMeasurements = new Set(state.pendingEndMeasurements);
@@ -6773,9 +6792,6 @@ function checkResetContainers(ctx, dataProp, { didColumnsChange = false } = {})
6773
6792
  if (!didMaintainScrollAtEnd && !state.pendingMaintainScrollAtEnd) {
6774
6793
  state.pendingEndMeasurements = void 0;
6775
6794
  }
6776
- if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
6777
- state.isEndReached = false;
6778
- }
6779
6795
  if (!didMaintainScrollAtEnd) {
6780
6796
  checkThresholds(ctx);
6781
6797
  }
@@ -6898,6 +6914,7 @@ function scrollToIndex(ctx, {
6898
6914
  animated = true,
6899
6915
  forceScroll,
6900
6916
  isInitialScroll,
6917
+ clampToContentContainerMinSize,
6901
6918
  viewPosition,
6902
6919
  viewPositionFallback
6903
6920
  }) {
@@ -6913,6 +6930,7 @@ function scrollToIndex(ctx, {
6913
6930
  state.scrollForNextCalculateItemsInView = void 0;
6914
6931
  scrollTo(ctx, {
6915
6932
  animated,
6933
+ clampToContentContainerMinSize,
6916
6934
  forceScroll,
6917
6935
  index,
6918
6936
  isInitialScroll,
@@ -7032,6 +7050,9 @@ function onScroll(ctx, event, notify = true) {
7032
7050
  if (!prevInset || prevInset.top !== contentInset.top || prevInset.bottom !== contentInset.bottom || prevInset.left !== contentInset.left || prevInset.right !== contentInset.right) {
7033
7051
  state.nativeContentInset = contentInset;
7034
7052
  insetChanged = true;
7053
+ if (Platform.OS !== "web" && state.props.anchoredEndSpaceOwner === "list") {
7054
+ maybeUpdateAnchoredEndSpace(ctx);
7055
+ }
7035
7056
  }
7036
7057
  }
7037
7058
  let newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
@@ -7238,7 +7259,7 @@ var ScrollAdjustHandler = class {
7238
7259
  targetScroll = clampScrollOffset(this.ctx, targetScroll, scrollTarget);
7239
7260
  } else {
7240
7261
  targetScroll = clampScrollOffset(this.ctx, state.scroll + pending - superseded, {
7241
- clampToContentContainerMinSize: scrollTarget == null ? void 0 : scrollTarget.clampToContentContainerMinSize
7262
+ clampToContentContainerMinSize: !scrollTarget || scrollTarget.clampToContentContainerMinSize
7242
7263
  });
7243
7264
  }
7244
7265
  const adjustment = targetScroll - state.scroll;
@@ -7267,6 +7288,7 @@ function scrollToEnd(ctx, options) {
7267
7288
  const footerSize = peek$(ctx, "footerSize") || 0;
7268
7289
  scrollToIndex(ctx, {
7269
7290
  ...options,
7291
+ clampToContentContainerMinSize: true,
7270
7292
  index,
7271
7293
  viewOffset: getEndAlignedViewOffset(paddingBottom, footerSize) + ((options == null ? void 0 : options.viewOffset) || 0),
7272
7294
  viewPosition: 1
@@ -7502,9 +7524,10 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
7502
7524
  const observed = getObservedScrollOffset(ctx);
7503
7525
  const position = calculateOffsetForIndex(ctx, index) + getStartOffsetAdjustment(ctx) + (itemOffset != null ? itemOffset : 0);
7504
7526
  const size = hasRegion ? itemLength != null ? itemLength : 0 : ((_a3 = getItemSizeAtIndex(ctx, index)) != null ? _a3 : 0) - ctx.scrollAxisGap;
7505
- const viewportLength = state.scrollLength - getContentInsetEnd(ctx);
7506
- const viewportStart = observed + ((_b = viewportMargin == null ? void 0 : viewportMargin.start) != null ? _b : 0);
7507
- const viewportEnd = observed + viewportLength - ((_c = viewportMargin == null ? void 0 : viewportMargin.end) != null ? _c : 0);
7527
+ const insetTop = getContentInsetTop(ctx);
7528
+ const viewportLength = state.scrollLength - insetTop - getContentInsetEnd(ctx);
7529
+ const viewportStart = observed + insetTop + ((_b = viewportMargin == null ? void 0 : viewportMargin.start) != null ? _b : 0);
7530
+ const viewportEnd = observed + insetTop + viewportLength - ((_c = viewportMargin == null ? void 0 : viewportMargin.end) != null ? _c : 0);
7508
7531
  if (!hasRegion && position <= viewportStart && position + size >= viewportEnd) return false;
7509
7532
  const above = position < viewportStart;
7510
7533
  const below = position + size > viewportEnd;
@@ -7940,7 +7963,7 @@ var LegendList = typedMemo(
7940
7963
  })
7941
7964
  );
7942
7965
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
7943
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s;
7966
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t;
7944
7967
  React2.useEffect(subscribeScreenReader, []);
7945
7968
  const noopOnScroll = React2.useCallback((_event) => {
7946
7969
  }, []);
@@ -8065,7 +8088,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8065
8088
  const hasInitialScrollIndex = initialScrollIndexProp !== void 0 && initialScrollIndexProp !== null;
8066
8089
  const hasInitialScrollOffset = initialScrollOffsetProp !== void 0 && initialScrollOffsetProp !== null;
8067
8090
  const shouldInitializeHorizontalRTL = !initialScrollAtEnd && !hasInitialScrollIndex && !hasInitialScrollOffset && isHorizontalRTLProps({ horizontal, rtl });
8068
- const initialScrollUsesOffsetOnly = !initialScrollAtEnd && !hasInitialScrollIndex && (hasInitialScrollOffset || shouldInitializeHorizontalRTL);
8091
+ const initialInsetTop = Platform.OS === "ios" && !horizontal ? Math.max(0, (_b = contentInset == null ? void 0 : contentInset.top) != null ? _b : 0) : 0;
8092
+ const initialScrollUsesOffsetOnly = !initialScrollAtEnd && !hasInitialScrollIndex && (hasInitialScrollOffset || shouldInitializeHorizontalRTL || initialInsetTop > 0);
8069
8093
  const usesBootstrapInitialScroll = initialScrollAtEnd || hasInitialScrollIndex;
8070
8094
  const shouldBottomAlignNumericInitialScrollIndex = typeof initialScrollIndexProp === "number" && !hasInitialScrollOffset && dataProp.length > 1 && initialScrollIndexProp >= dataProp.length - 1;
8071
8095
  const initialScrollProp = initialScrollAtEnd ? {
@@ -8074,10 +8098,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8074
8098
  viewOffset: getEndAlignedViewOffset(stylePaddingEndState),
8075
8099
  viewPosition: 1
8076
8100
  } : hasInitialScrollIndex ? typeof initialScrollIndexProp === "object" ? {
8077
- index: (_b = initialScrollIndexProp.index) != null ? _b : 0,
8101
+ index: (_c = initialScrollIndexProp.index) != null ? _c : 0,
8078
8102
  preserveForBottomPadding: initialScrollIndexProp.viewOffset === void 0 && initialScrollIndexProp.viewPosition === 1 && initialScrollIndexProp.viewPositionFallback === void 0 ? true : void 0,
8079
- viewOffset: (_c = initialScrollIndexProp.viewOffset) != null ? _c : initialScrollIndexProp.viewPosition === 1 && initialScrollIndexProp.viewPositionFallback === void 0 ? getEndAlignedViewOffset(stylePaddingEndState) : 0,
8080
- viewPosition: (_d = initialScrollIndexProp.viewPosition) != null ? _d : 0,
8103
+ viewOffset: (_d = initialScrollIndexProp.viewOffset) != null ? _d : initialScrollIndexProp.viewPosition === 1 && initialScrollIndexProp.viewPositionFallback === void 0 ? getEndAlignedViewOffset(stylePaddingEndState) : 0,
8104
+ viewPosition: (_e = initialScrollIndexProp.viewPosition) != null ? _e : 0,
8081
8105
  viewPositionFallback: initialScrollIndexProp.viewPositionFallback
8082
8106
  } : {
8083
8107
  index: initialScrollIndexProp != null ? initialScrollIndexProp : 0,
@@ -8085,7 +8109,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8085
8109
  viewOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : shouldBottomAlignNumericInitialScrollIndex ? getEndAlignedViewOffset(stylePaddingEndState) : 0,
8086
8110
  viewPosition: shouldBottomAlignNumericInitialScrollIndex ? 1 : void 0
8087
8111
  } : initialScrollUsesOffsetOnly ? {
8088
- contentOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : 0,
8112
+ contentOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : -initialInsetTop,
8089
8113
  index: 0,
8090
8114
  viewOffset: 0
8091
8115
  } : void 0;
@@ -8093,7 +8117,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8093
8117
  const [, scheduleImperativeScrollCommit] = React2__namespace.useReducer((value) => value + 1, 0);
8094
8118
  const ctx = useStateContext();
8095
8119
  ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
8096
- const scrollAxisGap = horizontal ? (_g = (_e = ctx.columnWrapperStyle) == null ? void 0 : _e.columnGap) != null ? _g : (_f = ctx.columnWrapperStyle) == null ? void 0 : _f.gap : (_j = (_h = ctx.columnWrapperStyle) == null ? void 0 : _h.rowGap) != null ? _j : (_i = ctx.columnWrapperStyle) == null ? void 0 : _i.gap;
8120
+ const scrollAxisGap = horizontal ? (_h = (_f = ctx.columnWrapperStyle) == null ? void 0 : _f.columnGap) != null ? _h : (_g = ctx.columnWrapperStyle) == null ? void 0 : _g.gap : (_k = (_i = ctx.columnWrapperStyle) == null ? void 0 : _i.rowGap) != null ? _k : (_j = ctx.columnWrapperStyle) == null ? void 0 : _j.gap;
8097
8121
  const nextScrollAxisGap = typeof scrollAxisGap === "number" && Number.isFinite(scrollAxisGap) ? scrollAxisGap : 0;
8098
8122
  const refScroller = React2.useRef(null);
8099
8123
  const combinedRef = useCombinedRef(refScroller, refScrollView);
@@ -8108,8 +8132,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8108
8132
  anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex,
8109
8133
  alwaysRender == null ? void 0 : alwaysRender.top,
8110
8134
  alwaysRender == null ? void 0 : alwaysRender.bottom,
8111
- (_k = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _k.join(","),
8112
- (_l = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _l.join(","),
8135
+ (_l = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _l.join(","),
8136
+ (_m = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _m.join(","),
8113
8137
  dataProp,
8114
8138
  dataKey,
8115
8139
  dataVersion,
@@ -8124,7 +8148,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8124
8148
  if (!refState.current) {
8125
8149
  if (!ctx.state) {
8126
8150
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : IsNewArchitecture ? { height: 0, width: 0 } : getWindowSize())[horizontal ? "width" : "height"];
8127
- ctx.values.set("adaptiveRender", (_m = experimental_adaptiveRender == null ? void 0 : experimental_adaptiveRender.initialMode) != null ? _m : "normal");
8151
+ ctx.values.set("adaptiveRender", (_n = experimental_adaptiveRender == null ? void 0 : experimental_adaptiveRender.initialMode) != null ? _n : "normal");
8128
8152
  ctx.state = {
8129
8153
  averageSizes: {},
8130
8154
  columnSpans: [],
@@ -8216,13 +8240,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8216
8240
  const didScrollAxisGapChange = !isFirstLocal && ctx.scrollAxisGap !== nextScrollAxisGap;
8217
8241
  ctx.scrollAxisGap = nextScrollAxisGap;
8218
8242
  state.didColumnsChange = numColumnsProp !== previousNumColumnsProp || didScrollAxisGapChange;
8219
- const previousDataLength = (_o = (_n = state.props.data) == null ? void 0 : _n.length) != null ? _o : 0;
8243
+ const previousDataLength = (_p = (_o = state.props.data) == null ? void 0 : _o.length) != null ? _p : 0;
8220
8244
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
8221
8245
  const didDataKeyChangeLocal = state.props.dataKey !== dataKey;
8222
8246
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
8223
8247
  const didDataChangeLocal = didDataKeyChangeLocal || didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
8224
8248
  const shouldResetFreshDataLayout = !isFirstLocal && didDataChangeLocal && state.hasHadNonEmptyData && (didDataKeyChangeLocal || previousDataLength === 0) && dataProp.length > 0;
8225
- if (didDataChangeLocal && !initialScrollAtEnd && state.didFinishInitialScroll && ((_p = state.initialScroll) == null ? void 0 : _p.viewPosition) === 1 && state.props.data.length > 0) {
8249
+ if (didDataChangeLocal && !initialScrollAtEnd && state.didFinishInitialScroll && ((_q = state.initialScroll) == null ? void 0 : _q.viewPosition) === 1 && state.props.data.length > 0) {
8226
8250
  clearPreservedInitialScrollTarget(state);
8227
8251
  }
8228
8252
  if (didDataChangeLocal) {
@@ -8238,7 +8262,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8238
8262
  const freshDataTransitionEpoch = state.freshDataTransitionEpoch;
8239
8263
  const throttledOnScroll = useThrottledOnScroll(onScrollProp != null ? onScrollProp : noopOnScroll, scrollEventThrottle != null ? scrollEventThrottle : 0);
8240
8264
  const throttleScrollFn = scrollEventThrottle && onScrollProp ? throttledOnScroll : onScrollProp;
8241
- const didAnchoredEndSpaceAnchorIndexChange = !isFirstLocal && !didDataChangeLocal && ((_q = state.props.anchoredEndSpace) == null ? void 0 : _q.anchorIndex) !== (anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex);
8265
+ const didAnchoredEndSpaceAnchorIndexChange = !isFirstLocal && !didDataChangeLocal && ((_r = state.props.anchoredEndSpace) == null ? void 0 : _r.anchorIndex) !== (anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex);
8242
8266
  state.props = {
8243
8267
  adaptiveRender: experimental_adaptiveRender,
8244
8268
  alignItemsAtEnd,
@@ -8323,8 +8347,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8323
8347
  updateContentMetricsState(ctx);
8324
8348
  let paddingDiff = stylePaddingTopState - prevPaddingTop;
8325
8349
  if (shouldAdjustPadding && maintainVisibleContentPositionConfig.size && paddingDiff && prevPaddingTop !== void 0 && Platform.OS === "ios") {
8326
- if (state.scroll < 0) {
8327
- paddingDiff += state.scroll;
8350
+ const distanceFromStart = state.scroll + getContentInsetTop(ctx);
8351
+ if (distanceFromStart < 0) {
8352
+ paddingDiff += distanceFromStart;
8328
8353
  }
8329
8354
  requestAdjust(ctx, paddingDiff);
8330
8355
  }
@@ -8426,6 +8451,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8426
8451
  anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex,
8427
8452
  anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorMaxSize,
8428
8453
  anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorOffset,
8454
+ anchoredEndSpaceOwner,
8455
+ contentInset == null ? void 0 : contentInset.bottom,
8456
+ contentInset == null ? void 0 : contentInset.left,
8457
+ contentInset == null ? void 0 : contentInset.right,
8429
8458
  didAnchoredEndSpaceAnchorIndexChange,
8430
8459
  horizontal,
8431
8460
  numColumnsProp,
@@ -8482,6 +8511,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8482
8511
  React2.useLayoutEffect(
8483
8512
  () => initializeStateVars(true),
8484
8513
  [
8514
+ contentInset == null ? void 0 : contentInset.bottom,
8515
+ contentInset == null ? void 0 : contentInset.top,
8485
8516
  dataKey,
8486
8517
  dataVersion,
8487
8518
  memoizedLastItemKeys.join(","),
@@ -8492,6 +8523,25 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8492
8523
  useAlignItemsAtEndPadding
8493
8524
  ]
8494
8525
  );
8526
+ const previousContentInsetTop = React2.useRef(contentInset == null ? void 0 : contentInset.top);
8527
+ React2.useLayoutEffect(() => {
8528
+ var _a4, _b2;
8529
+ const prevTop = Math.max(0, (_a4 = previousContentInsetTop.current) != null ? _a4 : 0);
8530
+ const didChange = previousContentInsetTop.current !== (contentInset == null ? void 0 : contentInset.top);
8531
+ previousContentInsetTop.current = contentInset == null ? void 0 : contentInset.top;
8532
+ if (didChange && Platform.OS === "ios" && !horizontal && !state.contentInsetOverride && !state.isDragging && !state.isMomentumScrolling) {
8533
+ const offset = -getContentInsetTop(ctx);
8534
+ if (!state.didFinishInitialScroll && !initialScrollAtEnd && !hasInitialScrollIndex && !hasInitialScrollOffset && ((_b2 = state.initialScrollSession) == null ? void 0 : _b2.kind) === "offset" && state.initialScroll && (!state.scrollingTo || state.scrollingTo.isInitialScroll)) {
8535
+ state.initialScroll.contentOffset = offset;
8536
+ advanceCurrentInitialScrollSession(ctx, { forceScroll: true });
8537
+ } else if (state.didFinishInitialScroll && !state.scrollingTo && Math.abs(state.scroll + prevTop) < 1) {
8538
+ scrollTo(ctx, { animated: false, offset });
8539
+ }
8540
+ }
8541
+ }, [contentInset == null ? void 0 : contentInset.top]);
8542
+ React2.useLayoutEffect(() => {
8543
+ checkThresholds(ctx);
8544
+ }, [maintainScrollAtEndThreshold]);
8495
8545
  React2.useLayoutEffect(() => {
8496
8546
  if (!maintainScrollAtEndConfig) {
8497
8547
  finishMaintainScrollAtEnd(ctx);
@@ -8670,7 +8720,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8670
8720
  onScroll: onScrollHandler,
8671
8721
  recycleItems,
8672
8722
  refreshControl: refreshControlElement ? stylePaddingTopState > 0 ? React2__namespace.cloneElement(refreshControlElement, {
8673
- progressViewOffset: ((_r = refreshControlElement.props.progressViewOffset) != null ? _r : 0) + stylePaddingTopState
8723
+ progressViewOffset: ((_s = refreshControlElement.props.progressViewOffset) != null ? _s : 0) + stylePaddingTopState
8674
8724
  }) : refreshControlElement : onRefresh && /* @__PURE__ */ React2__namespace.createElement(
8675
8725
  ReactNative.RefreshControl,
8676
8726
  {
@@ -8681,7 +8731,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8681
8731
  ),
8682
8732
  refScrollView: combinedRef,
8683
8733
  renderScrollComponent,
8684
- scrollAdjustHandler: (_s = refState.current) == null ? void 0 : _s.scrollAdjustHandler,
8734
+ scrollAdjustHandler: (_t = refState.current) == null ? void 0 : _t.scrollAdjustHandler,
8685
8735
  scrollElement: scrollElementResolved,
8686
8736
  scrollEventThrottle: 0,
8687
8737
  snapToIndices,