@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.mjs CHANGED
@@ -315,27 +315,20 @@ function getContentInsetEnd(ctx, contentInsetEndAdjustmentOverride) {
315
315
  const horizontal = props.horizontal;
316
316
  const contentInset = Platform.OS === "ios" ? props.contentInset : void 0;
317
317
  const baseInset = contentInset != null ? contentInset : state.nativeContentInset;
318
- const baseEndInset = (horizontal ? getHorizontalInsetEnd(state, baseInset) : baseInset == null ? void 0 : baseInset.bottom) || 0;
319
318
  const contentInsetEndAdjustment = getContentInsetEndAdjustmentEnd(
320
319
  contentInsetEndAdjustmentOverride != null ? contentInsetEndAdjustmentOverride : props.contentInsetEndAdjustment
321
320
  );
322
321
  const anchoredEndSpaceSize = peek$(ctx, "anchoredEndSpaceSize");
323
322
  const anchoredEndInset = props.anchoredEndSpace && anchoredEndSpaceSize ? anchoredEndSpaceSize : 0;
324
323
  const overrideInset = (_a3 = state.contentInsetOverride) != null ? _a3 : void 0;
325
- const adjustedBaseEndInset = baseEndInset + contentInsetEndAdjustment;
326
- if (overrideInset) {
327
- const mergedInset = { bottom: 0, left: 0, right: 0, ...baseInset, ...overrideInset };
328
- return Math.max(
329
- ((horizontal ? getHorizontalInsetEnd(state, mergedInset) : mergedInset.bottom) || 0) + contentInsetEndAdjustment,
330
- anchoredEndInset
331
- );
332
- }
333
- return Math.max(adjustedBaseEndInset, anchoredEndInset);
324
+ const mergedInset = overrideInset ? { bottom: 0, left: 0, right: 0, ...baseInset, ...overrideInset } : baseInset;
325
+ const endInset = ((horizontal ? getHorizontalInsetEnd(state, mergedInset) : mergedInset == null ? void 0 : mergedInset.bottom) || 0) + contentInsetEndAdjustment;
326
+ return Platform.OS !== "web" && props.anchoredEndSpaceOwner === "list" && !isHorizontalRTL(state) ? endInset + anchoredEndInset : Math.max(endInset, anchoredEndInset);
334
327
  }
335
328
 
336
329
  // src/state/getContentSize.ts
337
- function getContentSize(ctx) {
338
- var _a3, _b;
330
+ function getContentSize(ctx, includeContentContainerMinSize = true) {
331
+ var _a3, _b, _c;
339
332
  const { values, state } = ctx;
340
333
  const stylePaddingStart = state.props.horizontal ? state.props.stylePaddingLeft || 0 : values.get("stylePaddingTop") || 0;
341
334
  const stylePaddingEnd = state.props.horizontal ? state.props.stylePaddingRight || 0 : state.props.stylePaddingBottom || 0;
@@ -345,7 +338,10 @@ function getContentSize(ctx) {
345
338
  const contentInsetEnd = getContentInsetEnd(ctx);
346
339
  const totalSize = (_b = (_a3 = state.pendingTotalSize) != null ? _a3 : state.totalSize) != null ? _b : values.get("totalSize");
347
340
  const layoutSize = Math.max(0, totalSize - (state.props.data.length > 0 ? ctx.scrollAxisGap : 0));
348
- return headerSize + footerSize + layoutSize + stylePaddingStart + alignItemsAtEndPadding + stylePaddingEnd + (contentInsetEnd || 0);
341
+ const contentSize = headerSize + footerSize + layoutSize + stylePaddingStart + alignItemsAtEndPadding + stylePaddingEnd;
342
+ const inContentInset = Platform.OS === "web" || isHorizontalRTL(state) ? contentInsetEnd : state.props.anchoredEndSpace && state.props.anchoredEndSpaceOwner === "list" ? values.get("anchoredEndSpaceSize") || 0 : 0;
343
+ const containerSize = contentSize + inContentInset;
344
+ return (includeContentContainerMinSize ? Math.max(containerSize, (_c = state.props.contentContainerMinSize) != null ? _c : 0) : containerSize) + contentInsetEnd - inContentInset;
349
345
  }
350
346
 
351
347
  // src/components/DebugView.tsx
@@ -500,20 +496,240 @@ var USER_MOMENTUM_STALL_TIMEOUT_MS = 500;
500
496
  var ENABLE_DEVMODE = IS_DEV && false;
501
497
  var ENABLE_DEBUG_VIEW = IS_DEV && false;
502
498
 
503
- // src/core/calculateOffsetForIndex.ts
504
- function calculateOffsetForIndex(ctx, index) {
499
+ // src/core/cancelImperativeScroll.ts
500
+ function cancelScrollCompletionChecks({ scheduledWork }) {
501
+ scheduledWork.cancel("checkFinishedScrollFrame");
502
+ scheduledWork.cancel("checkFinishedScrollRetryFrame");
503
+ scheduledWork.cancel("checkFinishedScrollFallback");
504
+ scheduledWork.cancel("platformScrollCompletion");
505
+ }
506
+ function syncScrollActive(ctx) {
507
+ const { state } = ctx;
508
+ set$(
509
+ ctx,
510
+ "isScrollActive",
511
+ !!state.isDragging || !!state.isMomentumScrolling || !!state.scrollingTo || !!state.pendingScrollResolve
512
+ );
513
+ }
514
+ function settlePendingImperativeScroll(state) {
515
+ const resolvePendingScroll = state.pendingScrollResolve;
516
+ state.pendingScrollResolve = void 0;
517
+ state.pendingScrollToEnd = void 0;
518
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
519
+ }
520
+ function cancelImperativeScroll(state) {
521
+ cancelScrollCompletionChecks(state);
522
+ state.scheduledWork.cancel("imperativeScrollReady");
523
+ state.scrollingTo = void 0;
524
+ state.scrollTargetPinnedRange = void 0;
525
+ settlePendingImperativeScroll(state);
526
+ }
527
+
528
+ // src/core/clampScrollOffset.ts
529
+ function getContentInsetTop(ctx) {
530
+ var _a3, _b, _c, _d;
505
531
  const state = ctx.state;
506
- return index !== void 0 ? state.positions[index] || 0 : 0;
532
+ if (Platform.OS !== "ios" || state.props.horizontal) {
533
+ return 0;
534
+ }
535
+ const baseInset = (_a3 = state.props.contentInset) != null ? _a3 : state.nativeContentInset;
536
+ 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);
537
+ }
538
+ function getScrollRangeEnd(ctx, contentSize = getContentSize(ctx)) {
539
+ return Math.max(0 - getContentInsetTop(ctx), contentSize - ctx.state.scrollLength);
540
+ }
541
+ function clampScrollOffset(ctx, offset, scrollTarget) {
542
+ var _a3;
543
+ const state = ctx.state;
544
+ const minOffset = 0 - getContentInsetTop(ctx);
545
+ const contentSize = getContentSize(ctx, !scrollTarget || !!scrollTarget.clampToContentContainerMinSize);
546
+ let clampedOffset = offset;
547
+ if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform.OS !== "android" || state.lastLayout)) {
548
+ const baseMaxOffset = getScrollRangeEnd(ctx, contentSize);
549
+ const viewOffset = Platform.OS === "web" && (scrollTarget == null ? void 0 : scrollTarget.isInitialScroll) ? void 0 : scrollTarget == null ? void 0 : scrollTarget.viewOffset;
550
+ const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
551
+ 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");
552
+ const maxOffset = Math.max(isRawOffset ? 0 : minOffset, baseMaxOffset + extraEndOffset);
553
+ clampedOffset = Math.min(offset, maxOffset);
554
+ }
555
+ return Math.max(minOffset, clampedOffset);
507
556
  }
508
557
 
509
- // src/core/getStartOffsetAdjustment.ts
510
- function getStartOffsetAdjustment(ctx) {
558
+ // src/core/doScrollParams.ts
559
+ function getNativeScrollPoint(ctx, offset, horizontal = ctx.state.props.horizontal) {
560
+ var _a3;
511
561
  const { state } = ctx;
512
- const stylePaddingStart = state.props.horizontal ? (isHorizontalRTL(state) ? state.props.stylePaddingRight : state.props.stylePaddingLeft) || 0 : peek$(ctx, "stylePaddingTop") || 0;
513
- return stylePaddingStart + (peek$(ctx, "alignItemsAtEndPadding") || 0) + (peek$(ctx, "headerSize") || 0);
562
+ return horizontal ? {
563
+ x: toNativeHorizontalOffset(
564
+ state,
565
+ offset,
566
+ Math.max(getContentSize(ctx), (_a3 = state.props.contentContainerMinSize) != null ? _a3 : 0)
567
+ ),
568
+ y: 0
569
+ } : { x: 0, y: offset };
514
570
  }
515
- function getItemSpaceOffset(ctx, scrollOffset, pending = ((_a3) => (_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0)(), startOffset = getStartOffsetAdjustment(ctx)) {
516
- return Math.round(scrollOffset + (pending - startOffset));
571
+
572
+ // src/core/deferredPublicOnScroll.ts
573
+ function withResolvedContentOffset(state, event, resolvedOffset) {
574
+ return {
575
+ ...event,
576
+ nativeEvent: {
577
+ ...event.nativeEvent,
578
+ contentOffset: state.props.horizontal ? { x: resolvedOffset, y: 0 } : { x: 0, y: resolvedOffset }
579
+ }
580
+ };
581
+ }
582
+ function releaseDeferredPublicOnScroll(ctx, resolvedOffset) {
583
+ var _a3, _b, _c, _d;
584
+ const state = ctx.state;
585
+ const deferredEvent = state.deferredPublicOnScrollEvent;
586
+ state.deferredPublicOnScrollEvent = void 0;
587
+ if (deferredEvent) {
588
+ (_d = (_c = state.props).onScroll) == null ? void 0 : _d.call(
589
+ _c,
590
+ withResolvedContentOffset(
591
+ state,
592
+ deferredEvent,
593
+ (_b = (_a3 = resolvedOffset != null ? resolvedOffset : state.scrollPending) != null ? _a3 : state.scroll) != null ? _b : 0
594
+ )
595
+ );
596
+ }
597
+ }
598
+
599
+ // src/core/endOfContentTarget.ts
600
+ function getEndAlignedViewOffset(stylePaddingEnd, footerSize = 0) {
601
+ return -stylePaddingEnd - footerSize;
602
+ }
603
+ function isEndOfContentTarget(target, dataLength) {
604
+ return !!target && dataLength > 0 && target.viewPosition === 1 && !!target.preserveForBottomPadding && target.index !== void 0 && target.index >= dataLength - 1;
605
+ }
606
+
607
+ // src/core/initialScrollSession.ts
608
+ var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
609
+ function hasInitialScrollSessionCompletion(completion) {
610
+ return !!((completion == null ? void 0 : completion.didDispatchNativeScroll) || (completion == null ? void 0 : completion.didRetrySilentInitialScroll) || (completion == null ? void 0 : completion.watchdog));
611
+ }
612
+ function clearInitialScrollSession(state) {
613
+ state.initialScrollSession = void 0;
614
+ return void 0;
615
+ }
616
+ function createInitialScrollSession(options) {
617
+ const { bootstrap, completion, kind, previousDataLength } = options;
618
+ return kind === "offset" ? {
619
+ completion,
620
+ kind,
621
+ previousDataLength
622
+ } : {
623
+ bootstrap,
624
+ completion,
625
+ kind,
626
+ previousDataLength
627
+ };
628
+ }
629
+ function ensureInitialScrollSessionCompletion(state, kind = ((_b) => (_b = ((_a3) => (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind)()) != null ? _b : "bootstrap")()) {
630
+ var _a4, _b2;
631
+ if (!state.initialScrollSession) {
632
+ state.initialScrollSession = createInitialScrollSession({
633
+ completion: {},
634
+ kind,
635
+ previousDataLength: 0
636
+ });
637
+ } else if (state.initialScrollSession.kind !== kind) {
638
+ state.initialScrollSession = createInitialScrollSession({
639
+ bootstrap: state.initialScrollSession.kind === "bootstrap" ? state.initialScrollSession.bootstrap : void 0,
640
+ completion: state.initialScrollSession.completion,
641
+ kind,
642
+ previousDataLength: state.initialScrollSession.previousDataLength
643
+ });
644
+ }
645
+ (_b2 = (_a4 = state.initialScrollSession).completion) != null ? _b2 : _a4.completion = {};
646
+ return state.initialScrollSession.completion;
647
+ }
648
+ var initialScrollCompletion = {
649
+ didDispatchNativeScroll(state) {
650
+ var _a3, _b;
651
+ return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didDispatchNativeScroll);
652
+ },
653
+ didRetrySilentInitialScroll(state) {
654
+ var _a3, _b;
655
+ return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didRetrySilentInitialScroll);
656
+ },
657
+ markInitialScrollNativeDispatch(state) {
658
+ ensureInitialScrollSessionCompletion(state).didDispatchNativeScroll = true;
659
+ },
660
+ markSilentInitialScrollRetry(state) {
661
+ ensureInitialScrollSessionCompletion(state).didRetrySilentInitialScroll = true;
662
+ },
663
+ resetFlags(state) {
664
+ if (!state.initialScrollSession) {
665
+ return;
666
+ }
667
+ const completion = ensureInitialScrollSessionCompletion(state, state.initialScrollSession.kind);
668
+ completion.didDispatchNativeScroll = void 0;
669
+ completion.didRetrySilentInitialScroll = void 0;
670
+ }
671
+ };
672
+ var initialScrollWatchdog = {
673
+ clear(state) {
674
+ initialScrollWatchdog.set(state, void 0);
675
+ },
676
+ didReachTarget(newScroll, watchdog) {
677
+ const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
678
+ return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
679
+ },
680
+ get(state) {
681
+ var _a3, _b;
682
+ return (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog;
683
+ },
684
+ hasNonZeroTargetOffset(targetOffset) {
685
+ return targetOffset !== void 0 && targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
686
+ },
687
+ isAtZeroTargetOffset(targetOffset) {
688
+ return targetOffset <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
689
+ },
690
+ set(state, watchdog) {
691
+ var _a3, _b;
692
+ if (!watchdog && !((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog)) {
693
+ return;
694
+ }
695
+ const completion = ensureInitialScrollSessionCompletion(state);
696
+ completion.watchdog = watchdog ? {
697
+ startScroll: watchdog.startScroll,
698
+ targetOffset: watchdog.targetOffset
699
+ } : void 0;
700
+ }
701
+ };
702
+ function setInitialScrollSession(state, options = {}) {
703
+ var _a3, _b, _c, _d;
704
+ const existingSession = state.initialScrollSession;
705
+ const kind = (_a3 = options.kind) != null ? _a3 : existingSession == null ? void 0 : existingSession.kind;
706
+ const completion = existingSession == null ? void 0 : existingSession.completion;
707
+ const existingBootstrap = (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0;
708
+ const bootstrap = kind === "bootstrap" ? options.bootstrap === null ? void 0 : (_b = options.bootstrap) != null ? _b : existingBootstrap : void 0;
709
+ if (!kind) {
710
+ return clearInitialScrollSession(state);
711
+ }
712
+ if (!state.initialScroll && !bootstrap && !hasInitialScrollSessionCompletion(completion)) {
713
+ return clearInitialScrollSession(state);
714
+ }
715
+ const previousDataLength = (_d = (_c = options.previousDataLength) != null ? _c : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _d : 0;
716
+ state.initialScrollSession = createInitialScrollSession({
717
+ bootstrap,
718
+ completion,
719
+ kind,
720
+ previousDataLength
721
+ });
722
+ return state.initialScrollSession;
723
+ }
724
+
725
+ // src/core/observedScroll.ts
726
+ function getObservedScrollOffset(ctx, fallbackOffset = ctx.state.scroll) {
727
+ var _a3, _b, _c;
728
+ const { state } = ctx;
729
+ const scroller = state.refScroller.current;
730
+ const observed = (_a3 = scroller == null ? void 0 : scroller.getCurrentScrollOffset) == null ? void 0 : _a3.call(scroller);
731
+ if (observed === void 0) return (_b = state.lastNativeScroll) != null ? _b : fallbackOffset;
732
+ return state.props.horizontal ? peekLogicalHorizontalOffset(state, observed, (_c = scroller.getMaxScrollOffset) == null ? void 0 : _c.call(scroller)) : observed;
517
733
  }
518
734
 
519
735
  // src/core/updateContentMetricsState.ts
@@ -525,7 +741,7 @@ function getRawContentLength(ctx) {
525
741
  function getAlignItemsAtEndPadding(ctx) {
526
742
  const { state } = ctx;
527
743
  const shouldPad = !!state.props.alignItemsAtEndPaddingEnabled && !state.props.horizontal && state.props.data.length > 0 && state.scrollLength > 0;
528
- return shouldPad ? Math.max(0, state.scrollLength - getRawContentLength(ctx) - getContentInsetEnd(ctx)) : 0;
744
+ return shouldPad ? Math.max(0, state.scrollLength - getRawContentLength(ctx) - getContentInsetTop(ctx) - getContentInsetEnd(ctx)) : 0;
529
745
  }
530
746
  function updateContentMetricsState(ctx) {
531
747
  var _a3;
@@ -547,1012 +763,785 @@ function updateContentMetricsState(ctx) {
547
763
  }
548
764
  }
549
765
 
550
- // src/core/addTotalSize.ts
551
- function addTotalSize(ctx, key, add, notifyTotalSize = true) {
766
+ // src/utils/requestAdjust.ts
767
+ function requestAdjust(ctx, positionDiff, source) {
768
+ var _a3;
552
769
  const state = ctx.state;
553
- const prevTotalSize = state.totalSize;
554
- let totalSize = state.totalSize;
555
- if (key === null) {
556
- totalSize = add;
557
- if (state.timeoutSetPaddingTop) {
558
- clearTimeout(state.timeoutSetPaddingTop);
559
- state.timeoutSetPaddingTop = void 0;
560
- }
561
- } else {
562
- totalSize += add;
563
- }
564
- if (prevTotalSize !== totalSize) {
565
- if (!IsNewArchitecture && state.initialScroll && totalSize < prevTotalSize) {
566
- state.pendingTotalSize = totalSize;
567
- } else {
568
- state.pendingTotalSize = void 0;
569
- state.totalSize = totalSize;
570
- if (notifyTotalSize) {
571
- set$(ctx, "totalSize", totalSize);
572
- }
573
- updateContentMetricsState(ctx);
770
+ if (Math.abs(positionDiff) > 0.1) {
771
+ state.layoutAdjustmentEpoch = ((_a3 = state.layoutAdjustmentEpoch) != null ? _a3 : 0) + 1;
772
+ const dataChanged = source === "data";
773
+ const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
774
+ const doit = () => {
775
+ if (needsScrollWorkaround) {
776
+ ctx.doScrollTo({ horizontal: state.props.horizontal, isAdjustment: true, offset: state.scroll });
777
+ } else {
778
+ state.scrollAdjustHandler.requestAdjust(positionDiff);
779
+ if (state.adjustingFromInitialMount) {
780
+ state.adjustingFromInitialMount--;
781
+ }
782
+ }
783
+ };
784
+ state.scroll += positionDiff;
785
+ if (state.userScrollContentSize !== void 0) {
786
+ state.userScrollContentSize += positionDiff;
787
+ }
788
+ state.scrollForNextCalculateItemsInView = void 0;
789
+ const readyToRender = peek$(ctx, "readyToRender");
790
+ if (readyToRender) {
791
+ doit();
792
+ if (Platform.OS !== "web" && source !== "item-size") {
793
+ const threshold = state.scroll - positionDiff / 2;
794
+ if (!state.ignoreScrollFromMVCP) {
795
+ state.ignoreScrollFromMVCP = {};
796
+ }
797
+ if (positionDiff > 0) {
798
+ state.ignoreScrollFromMVCP.lt = threshold;
799
+ } else {
800
+ state.ignoreScrollFromMVCP.gt = threshold;
801
+ }
802
+ const delay = needsScrollWorkaround ? 250 : 100;
803
+ state.scheduledWork.timeout(
804
+ () => {
805
+ var _a4;
806
+ state.ignoreScrollFromMVCP = void 0;
807
+ const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
808
+ if (shouldForceUpdate) {
809
+ state.ignoreScrollFromMVCPIgnored = false;
810
+ state.scrollPending = state.scroll;
811
+ (_a4 = state.reprocessCurrentScroll) == null ? void 0 : _a4.call(state);
812
+ }
813
+ },
814
+ delay,
815
+ "ignoreScrollFromMVCP"
816
+ );
817
+ }
818
+ } else {
819
+ state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
820
+ requestAnimationFrame(doit);
574
821
  }
575
- } else if (notifyTotalSize && ctx.values.get("totalSize") !== totalSize) {
576
- set$(ctx, "totalSize", totalSize);
577
822
  }
578
823
  }
579
824
 
580
- // src/core/setSize.ts
581
- function setSize(ctx, itemKey, size, notifyTotalSize = true) {
582
- const state = ctx.state;
583
- const { sizes } = state;
584
- const previousSize = sizes.get(itemKey);
585
- const diff = previousSize !== void 0 ? size - previousSize : size;
586
- if (diff !== 0) {
587
- state.positionsAreCurrent = false;
588
- addTotalSize(ctx, itemKey, diff, notifyTotalSize);
825
+ // src/core/finishMaintainScrollAtEnd.ts
826
+ function interruptMaintainScrollAtEnd(ctx) {
827
+ var _a3;
828
+ const { state } = ctx;
829
+ const maintaining = state.maintainingScrollAtEnd;
830
+ state.endFollowInterrupted = true;
831
+ set$(ctx, "isFollowingEnd", false);
832
+ if (maintaining === "animated" || maintaining === "instant" || maintaining && ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isScrollToEnd)) {
833
+ cancelImperativeScroll(state);
834
+ syncScrollActive(ctx);
589
835
  }
590
- sizes.set(itemKey, size);
591
- }
592
-
593
- // src/utils/helpers.ts
594
- function isFunction(obj) {
595
- return typeof obj === "function";
596
- }
597
- function isArray(obj) {
598
- return Array.isArray(obj);
599
- }
600
- var warned = /* @__PURE__ */ new Set();
601
- function warnDevOnce(id, text) {
602
- if (IS_DEV && !warned.has(id)) {
603
- warned.add(id);
604
- console.warn(`[legend-list] ${text}`);
836
+ if (maintaining || state.pendingMaintainScrollAtEnd) {
837
+ finishMaintainScrollAtEnd(ctx);
605
838
  }
606
839
  }
607
- function roundSize(size) {
608
- return Math.floor(size * 8) / 8;
609
- }
610
- function isNullOrUndefined(value) {
611
- return value === null || value === void 0;
612
- }
613
- function getPadding(s, type) {
614
- var _a3, _b, _c;
615
- const axisPadding = type === "Left" || type === "Right" ? s.paddingHorizontal : s.paddingVertical;
616
- return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : axisPadding) != null ? _b : s.padding) != null ? _c : 0;
840
+ function armAppScrollRearm(ctx) {
841
+ ctx.state.scheduledWork.timeout(
842
+ () => rearmEndFollowIfSettled(ctx),
843
+ USER_MOMENTUM_STALL_TIMEOUT_MS,
844
+ "appScrollRearm"
845
+ );
617
846
  }
618
- function extractPadding(style, contentContainerStyle, type) {
619
- return getPadding(style, type) + getPadding(contentContainerStyle, type);
847
+ function rearmEndFollowIfSettled(ctx) {
848
+ const { state } = ctx;
849
+ 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")) {
850
+ state.endFollowInterrupted = false;
851
+ if (state.props.maintainScrollAtEnd) set$(ctx, "isFollowingEnd", true);
852
+ }
620
853
  }
621
- function findContainerId(ctx, key) {
854
+ function finishMaintainScrollAtEnd(ctx) {
622
855
  var _a3, _b;
623
- const directMatch = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.containerItemKeys) == null ? void 0 : _b.get(key);
624
- if (directMatch !== void 0) {
625
- return directMatch;
856
+ const { state } = ctx;
857
+ const currentPadding = peek$(ctx, "alignItemsAtEndPadding") || 0;
858
+ const nextPadding = getAlignItemsAtEndPadding(ctx);
859
+ state.maintainingScrollAtEnd = void 0;
860
+ state.pendingMaintainScrollAtEnd = false;
861
+ for (const key of (_a3 = state.pendingEndMeasurements) != null ? _a3 : []) {
862
+ if (!state.containerItemKeys.has(key)) state.pendingEndMeasurements.delete(key);
626
863
  }
627
- const numContainers = peek$(ctx, "numContainers");
628
- for (let i = 0; i < numContainers; i++) {
629
- const itemKey = peek$(ctx, `containerItemKey${i}`);
630
- if (itemKey === key) {
631
- return i;
632
- }
864
+ if (currentPadding !== nextPadding) {
865
+ set$(ctx, "alignItemsAtEndPadding", nextPadding);
866
+ state.scrollForNextCalculateItemsInView = void 0;
867
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
868
+ const nextScroll = clampScrollOffset(ctx, state.scroll + nextPadding - currentPadding);
869
+ const nativeScroll = Platform.OS === "ios" ? clampScrollOffset(ctx, state.scroll) : state.scroll;
870
+ state.scroll = nativeScroll;
871
+ requestAdjust(ctx, nextScroll - nativeScroll);
633
872
  }
634
- return -1;
635
873
  }
636
874
 
637
- // src/utils/getItemSize.ts
638
- function getKnownOrFixedSize(ctx, key, index, data, resolved) {
639
- var _a3, _b, _c, _d;
640
- const state = ctx.state;
641
- const { getFixedItemSize, getItemType } = state.props;
642
- let size = key ? state.sizesKnown.get(key) : void 0;
643
- if (size === void 0 && key && getFixedItemSize) {
644
- const itemType = (_b = resolved == null ? void 0 : resolved.itemType) != null ? _b : getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
645
- const fixedSize = (resolved == null ? void 0 : resolved.didResolveFixedItemSize) ? resolved.fixedItemSize : getFixedItemSize(data, index, itemType);
646
- if (fixedSize !== void 0) {
647
- size = fixedSize + ctx.scrollAxisGap;
648
- state.positionsAreCurrent = false;
649
- state.positionRecalculationStartIndex = Math.min((_c = state.positionRecalculationStartIndex) != null ? _c : index, index);
650
- state.scrollForNextCalculateItemsInView = void 0;
651
- state.sizesKnown.set(key, size);
652
- (_d = state.fixedItemSizeKeys) != null ? _d : state.fixedItemSizeKeys = /* @__PURE__ */ new Set();
653
- state.fixedItemSizeKeys.add(key);
654
- }
655
- }
656
- return size;
875
+ // src/utils/checkThreshold.ts
876
+ var HYSTERESIS_MULTIPLIER = 1.3;
877
+ function isOutsideThresholdHysteresis(distance, atThreshold, threshold) {
878
+ const absDistance = Math.abs(distance);
879
+ return !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
657
880
  }
658
- function getKnownOrFixedItemSize(ctx, index) {
659
- const key = getId(ctx.state, index);
660
- return getKnownOrFixedSize(ctx, key, index, ctx.state.props.data[index]);
881
+ function didThresholdDataChange(snapshot, context) {
882
+ return !!snapshot && (snapshot.dataLength !== context.dataLength || snapshot.data !== context.data && snapshot.contentSize !== context.contentSize);
661
883
  }
662
- function areKnownOrFixedItemSizesAvailable(ctx, startIndex, endIndex) {
663
- for (let index = startIndex; index <= endIndex; index++) {
664
- if (getKnownOrFixedItemSize(ctx, index) === void 0) {
665
- return false;
884
+ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot) => {
885
+ const absDistance = Math.abs(distance);
886
+ const within = context.dataLength !== 0 && (atThreshold || threshold > 0 && absDistance <= threshold);
887
+ const didScroll = (snapshot == null ? void 0 : snapshot.scroll) !== context.scroll;
888
+ if (snapshot) snapshot.scroll = context.scroll;
889
+ if (!within) {
890
+ if ((context.dataLength === 0 || didScroll && isOutsideThresholdHysteresis(distance, atThreshold, threshold)) && (wasReached || snapshot)) {
891
+ setSnapshot(void 0);
666
892
  }
893
+ return false;
667
894
  }
668
- return true;
895
+ if ((!wasReached || didThresholdDataChange(snapshot, context)) && onReached(distance)) {
896
+ return true;
897
+ }
898
+ return !!wasReached;
899
+ };
900
+
901
+ // src/utils/edgeReachedGate.ts
902
+ function getDistanceFromStart(ctx) {
903
+ return ctx.state.scroll + getContentInsetTop(ctx);
669
904
  }
670
- function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize, notifyTotalSize, resolved) {
671
- var _a3, _b, _c, _d;
905
+ function resetSharedEdgeGateIfOutsideHysteresis(ctx) {
672
906
  const state = ctx.state;
673
- const {
674
- sizes,
675
- averageSizes,
676
- props: { estimatedItemSize, getItemType },
677
- scrollingTo
678
- } = state;
679
- const sizeKnown = state.sizesKnown.get(key);
680
- if (sizeKnown !== void 0) {
681
- return sizeKnown;
682
- }
683
- const renderedSize = sizes.get(key);
684
- let size = getKnownOrFixedSize(ctx, key, index, data, resolved);
685
- if (size !== void 0) {
686
- setSize(ctx, key, size, notifyTotalSize);
687
- return size;
688
- }
689
- if (preferCachedSize && renderedSize !== void 0) {
690
- return renderedSize;
691
- }
692
- const itemType = (_b = resolved == null ? void 0 : resolved.itemType) != null ? _b : getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
693
- if (useAverageSize && !scrollingTo) {
694
- const averageSizeForType = (_c = averageSizes[itemType]) == null ? void 0 : _c.avg;
695
- if (averageSizeForType !== void 0) {
696
- size = roundSize(averageSizeForType);
697
- }
698
- }
699
- if (size === void 0 && renderedSize !== void 0) {
700
- return renderedSize;
907
+ if (!state.edgeReachedGate || state.edgeReachedGateScroll === state.scroll) {
908
+ return;
701
909
  }
702
- if (size === void 0 && useAverageSize && scrollingTo) {
703
- const averageSizeForType = (_d = scrollingTo.averageSizeSnapshot) == null ? void 0 : _d[itemType];
704
- if (averageSizeForType !== void 0) {
705
- size = roundSize(averageSizeForType);
706
- }
910
+ state.edgeReachedGateScroll = state.scroll;
911
+ const contentSize = getContentSize(ctx);
912
+ const endDistance = contentSize - state.scroll - state.scrollLength;
913
+ const isContentLess = contentSize < state.scrollLength;
914
+ const startThreshold = state.props.onStartReachedThreshold * state.scrollLength;
915
+ const endThreshold = state.props.onEndReachedThreshold * state.scrollLength;
916
+ const isOutsideStart = isOutsideThresholdHysteresis(getDistanceFromStart(ctx), false, startThreshold);
917
+ const isOutsideEnd = isOutsideThresholdHysteresis(endDistance, isContentLess, endThreshold);
918
+ if (isOutsideStart && isOutsideEnd) {
919
+ state.edgeReachedGate = void 0;
707
920
  }
708
- if (size === void 0) {
709
- size = estimatedItemSize + ctx.scrollAxisGap;
921
+ }
922
+ function canDispatchReachedEdge(ctx, edge, allowedEdge, allowGateCreatedInCurrentCheck) {
923
+ const state = ctx.state;
924
+ return !state.edgeReachedGate || allowedEdge === edge || !!allowGateCreatedInCurrentCheck || didThresholdDataChange(
925
+ edge === "start" ? state.startReachedSnapshot : state.endReachedSnapshot,
926
+ getThresholdSnapshot(ctx)
927
+ );
928
+ }
929
+ function markReachedEdge(ctx, edge) {
930
+ const state = ctx.state;
931
+ state.edgeReachedGate = "closed";
932
+ state.edgeReachedGateScroll = state.scroll;
933
+ const snapshotKey = edge === "start" ? "startReachedSnapshot" : "endReachedSnapshot";
934
+ const snapshot = getThresholdSnapshot(ctx);
935
+ snapshot.didRearmForData = didThresholdDataChange(state[snapshotKey], snapshot);
936
+ state[snapshotKey] = snapshot;
937
+ }
938
+ function getThresholdSnapshot(ctx) {
939
+ const { data } = ctx.state.props;
940
+ return { contentSize: getContentSize(ctx), data, dataLength: data.length, scroll: ctx.state.scroll };
941
+ }
942
+ function prepareReachedEdgeForNextUserScroll(ctx) {
943
+ if (ctx.state.edgeReachedGate) {
944
+ ctx.state.edgeReachedGate = "prepared";
710
945
  }
711
- setSize(ctx, key, size, notifyTotalSize);
712
- return size;
713
946
  }
714
- function getItemSizeAtIndex(ctx, index) {
715
- if (index === void 0 || index < 0) {
947
+ function beginReachedEdgeUserScroll(ctx, scrollDelta) {
948
+ const state = ctx.state;
949
+ const allowedEdge = scrollDelta < 0 ? "start" : "end";
950
+ if (state.edgeReachedGate !== "prepared" && state.edgeReachedGate !== (allowedEdge === "start" ? "consumed-end" : "consumed-start")) {
716
951
  return void 0;
717
952
  }
718
- const targetId = getId(ctx.state, index);
719
- return getItemSize(ctx, targetId, index, ctx.state.props.data[index]);
953
+ const snapshotKey = allowedEdge === "start" ? "startReachedSnapshot" : "endReachedSnapshot";
954
+ const snapshot = state[snapshotKey];
955
+ if ((snapshot == null ? void 0 : snapshot.didRearmForData) && snapshot.data === state.props.data) {
956
+ snapshot.didRearmForData = false;
957
+ state.edgeReachedGate = state.edgeReachedGate === "prepared" ? `consumed-${allowedEdge}` : "closed";
958
+ return void 0;
959
+ }
960
+ state[allowedEdge === "start" ? "isStartReached" : "isEndReached"] = false;
961
+ state[snapshotKey] = void 0;
962
+ return allowedEdge;
720
963
  }
721
964
 
722
- // src/core/calculateOffsetWithOffsetPosition.ts
723
- function getViewPositionOffset(availableSpace, viewPosition, viewPositionFallback) {
724
- let alignment = viewPosition;
725
- if (availableSpace < 0 && viewPositionFallback !== void 0) {
726
- alignment = viewPositionFallback === "start" ? 0 : 1;
727
- }
728
- return -alignment * availableSpace;
965
+ // src/utils/hasActiveInitialScroll.ts
966
+ function hasActiveInitialScroll(state) {
967
+ return !!(state == null ? void 0 : state.initialScroll) && !state.didFinishInitialScroll;
729
968
  }
730
- function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
731
- var _a3;
969
+
970
+ // src/utils/checkAtBottom.ts
971
+ function checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
732
972
  const state = ctx.state;
733
- const { index, viewOffset, viewPosition, viewPositionFallback } = params;
734
- let offset = offsetParam;
735
- if (viewOffset) {
736
- offset -= viewOffset;
973
+ if (!state) {
974
+ return;
737
975
  }
738
- if (index !== void 0) {
739
- const startOffsetAdjustment = getStartOffsetAdjustment(ctx);
740
- if (startOffsetAdjustment) {
741
- offset += startOffsetAdjustment;
742
- }
976
+ const {
977
+ queuedInitialLayout,
978
+ scrollLength,
979
+ scroll,
980
+ maintainingScrollAtEnd,
981
+ props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
982
+ } = state;
983
+ const contentSize = getContentSize(ctx);
984
+ const distanceFromEnd = contentSize - scroll - scrollLength;
985
+ resetSharedEdgeGateIfOutsideHysteresis(ctx);
986
+ if (state.props.data.length === 0 && scrollLength > 0 && contentSize <= scrollLength) {
987
+ set$(ctx, "isAtEnd", true);
988
+ set$(ctx, "isNearEnd", true);
989
+ set$(ctx, "isWithinMaintainScrollAtEndThreshold", true);
990
+ return;
743
991
  }
744
- if (viewPosition !== void 0 && index !== void 0) {
745
- const dataLength = state.props.data.length;
746
- if (dataLength === 0) {
747
- return offset;
992
+ if (contentSize > 0 && queuedInitialLayout) {
993
+ const isContentLess = contentSize < scrollLength;
994
+ set$(ctx, "isAtEnd", isContentLess || distanceFromEnd <= EDGE_POSITION_EPSILON);
995
+ set$(ctx, "isNearEnd", isContentLess || distanceFromEnd <= onEndReachedThreshold * scrollLength);
996
+ set$(
997
+ ctx,
998
+ "isWithinMaintainScrollAtEndThreshold",
999
+ isContentLess || // A zero threshold still includes fractional offsets considered
1000
+ // at the end; otherwise following can stop after rounding/layout.
1001
+ distanceFromEnd <= Math.max(EDGE_POSITION_EPSILON, maintainScrollAtEndThreshold * scrollLength) || state.pendingMaintainScrollAtEnd || !!maintainingScrollAtEnd || !!peek$(ctx, "isFollowingEnd")
1002
+ );
1003
+ const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || maintainingScrollAtEnd;
1004
+ if (!shouldSkipThresholdChecks) {
1005
+ state.isEndReached = checkThreshold(
1006
+ distanceFromEnd,
1007
+ isContentLess,
1008
+ onEndReachedThreshold * scrollLength,
1009
+ state.isEndReached,
1010
+ state.endReachedSnapshot,
1011
+ { contentSize, data: state.props.data, dataLength: state.props.data.length, scroll },
1012
+ (distance) => {
1013
+ if (state.props.onEndReached && canDispatchReachedEdge(ctx, "end", allowedEdge, allowGateCreatedInCurrentCheck)) {
1014
+ markReachedEdge(ctx, "end");
1015
+ state.props.onEndReached({ distanceFromEnd: distance });
1016
+ return true;
1017
+ }
1018
+ return false;
1019
+ },
1020
+ (snapshot) => {
1021
+ state.endReachedSnapshot = snapshot;
1022
+ }
1023
+ );
748
1024
  }
749
- const isOutOfBounds = index < 0 || index >= dataLength;
750
- const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
751
- const measuredItemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
752
- const itemSize = Math.max(0, measuredItemSize - (isOutOfBounds ? 0 : ctx.scrollAxisGap));
753
- const trailingInset = getContentInsetEnd(ctx);
754
- const availableSpace = state.scrollLength - trailingInset - itemSize;
755
- offset += getViewPositionOffset(availableSpace, viewPosition, viewPositionFallback);
756
1025
  }
757
- return offset;
758
1026
  }
759
1027
 
760
- // src/core/cancelImperativeScroll.ts
761
- function cancelScrollCompletionChecks({ scheduledWork }) {
762
- scheduledWork.cancel("checkFinishedScrollFrame");
763
- scheduledWork.cancel("checkFinishedScrollRetryFrame");
764
- scheduledWork.cancel("checkFinishedScrollFallback");
765
- scheduledWork.cancel("platformScrollCompletion");
766
- }
767
- function syncScrollActive(ctx) {
768
- const { state } = ctx;
769
- set$(
770
- ctx,
771
- "isScrollActive",
772
- !!state.isDragging || !!state.isMomentumScrolling || !!state.scrollingTo || !!state.pendingScrollResolve
773
- );
774
- }
775
- function settlePendingImperativeScroll(state) {
776
- const resolvePendingScroll = state.pendingScrollResolve;
777
- state.pendingScrollResolve = void 0;
778
- state.pendingScrollToEnd = void 0;
779
- resolvePendingScroll == null ? void 0 : resolvePendingScroll();
780
- }
781
- function cancelImperativeScroll(state) {
782
- cancelScrollCompletionChecks(state);
783
- state.scheduledWork.cancel("imperativeScrollReady");
784
- state.scrollingTo = void 0;
785
- state.scrollTargetPinnedRange = void 0;
786
- settlePendingImperativeScroll(state);
1028
+ // src/utils/checkAtTop.ts
1029
+ function checkAtTop(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
1030
+ const state = ctx == null ? void 0 : ctx.state;
1031
+ if (!state) {
1032
+ return;
1033
+ }
1034
+ const {
1035
+ props: { data, onStartReachedThreshold },
1036
+ scrollLength
1037
+ } = state;
1038
+ const threshold = onStartReachedThreshold * scrollLength;
1039
+ const distanceFromStart = getDistanceFromStart(ctx);
1040
+ resetSharedEdgeGateIfOutsideHysteresis(ctx);
1041
+ set$(ctx, "isAtStart", distanceFromStart <= EDGE_POSITION_EPSILON);
1042
+ set$(ctx, "isNearStart", distanceFromStart <= threshold);
1043
+ const shouldSkipThresholdChecks = !state.queuedInitialLayout || hasActiveInitialScroll(state) || !!state.scrollingTo;
1044
+ if (!shouldSkipThresholdChecks) {
1045
+ state.isStartReached = checkThreshold(
1046
+ distanceFromStart,
1047
+ false,
1048
+ threshold,
1049
+ state.isStartReached,
1050
+ state.startReachedSnapshot,
1051
+ { contentSize: getContentSize(ctx), data, dataLength: data.length, scroll: state.scroll },
1052
+ (distance) => {
1053
+ if (state.props.onStartReached && canDispatchReachedEdge(ctx, "start", allowedEdge, allowGateCreatedInCurrentCheck)) {
1054
+ markReachedEdge(ctx, "start");
1055
+ state.props.onStartReached({ distanceFromStart: distance });
1056
+ return true;
1057
+ }
1058
+ return false;
1059
+ },
1060
+ (snapshot) => {
1061
+ state.startReachedSnapshot = snapshot;
1062
+ }
1063
+ );
1064
+ }
787
1065
  }
788
1066
 
789
- // src/core/doScrollParams.ts
790
- function getNativeScrollPoint(ctx, offset, horizontal = ctx.state.props.horizontal) {
791
- var _a3;
792
- const { state } = ctx;
793
- return horizontal ? {
794
- x: toNativeHorizontalOffset(
795
- state,
796
- offset,
797
- Math.max(getContentSize(ctx), (_a3 = state.props.contentContainerMinSize) != null ? _a3 : 0)
798
- ),
799
- y: 0
800
- } : { x: 0, y: offset };
1067
+ // src/utils/checkThresholds.ts
1068
+ function checkThresholds(ctx, allowedEdge) {
1069
+ const allowGateCreatedInCurrentCheck = !ctx.state.edgeReachedGate;
1070
+ checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck);
1071
+ checkAtTop(ctx, allowedEdge, allowGateCreatedInCurrentCheck);
1072
+ rearmEndFollowIfSettled(ctx);
801
1073
  }
802
1074
 
803
- // src/core/deferredPublicOnScroll.ts
804
- function withResolvedContentOffset(state, event, resolvedOffset) {
805
- return {
806
- ...event,
807
- nativeEvent: {
808
- ...event.nativeEvent,
809
- contentOffset: state.props.horizontal ? { x: resolvedOffset, y: 0 } : { x: 0, y: resolvedOffset }
810
- }
811
- };
812
- }
813
- function releaseDeferredPublicOnScroll(ctx, resolvedOffset) {
814
- var _a3, _b, _c, _d;
1075
+ // src/core/recalculateSettledScroll.ts
1076
+ function recalculateSettledScroll(ctx) {
1077
+ var _a3, _b;
815
1078
  const state = ctx.state;
816
- const deferredEvent = state.deferredPublicOnScrollEvent;
817
- state.deferredPublicOnScrollEvent = void 0;
818
- if (deferredEvent) {
819
- (_d = (_c = state.props).onScroll) == null ? void 0 : _d.call(
820
- _c,
821
- withResolvedContentOffset(
822
- state,
823
- deferredEvent,
824
- (_b = (_a3 = resolvedOffset != null ? resolvedOffset : state.scrollPending) != null ? _a3 : state.scroll) != null ? _b : 0
825
- )
826
- );
1079
+ if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1080
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
827
1081
  }
1082
+ checkThresholds(ctx);
828
1083
  }
829
1084
 
830
- // src/core/endOfContentTarget.ts
831
- function getEndAlignedViewOffset(stylePaddingEnd, footerSize = 0) {
832
- return -stylePaddingEnd - footerSize;
833
- }
834
- function isEndOfContentTarget(target, dataLength) {
835
- return !!target && dataLength > 0 && target.viewPosition === 1 && !!target.preserveForBottomPadding && target.index !== void 0 && target.index >= dataLength - 1;
836
- }
837
-
838
- // src/core/initialScrollSession.ts
839
- var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
840
- function hasInitialScrollSessionCompletion(completion) {
841
- return !!((completion == null ? void 0 : completion.didDispatchNativeScroll) || (completion == null ? void 0 : completion.didRetrySilentInitialScroll) || (completion == null ? void 0 : completion.watchdog));
1085
+ // src/core/adaptiveRender.ts
1086
+ var DEFAULT_ADAPTIVE_RENDER_ENTER_VELOCITY = 3;
1087
+ var DEFAULT_ADAPTIVE_RENDER_EXIT_VELOCITY = 1;
1088
+ var DEFAULT_ADAPTIVE_RENDER_EXIT_DELAY = 250;
1089
+ var DEFAULT_WEB_ADAPTIVE_RENDER_ENTER_VELOCITY = 6;
1090
+ var DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_VELOCITY = 3;
1091
+ var DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_DELAY = 250;
1092
+ function clearAdaptiveRenderExitTimeout(ctx) {
1093
+ ctx.state.scheduledWork.cancel("adaptiveRender");
842
1094
  }
843
- function clearInitialScrollSession(state) {
844
- state.initialScrollSession = void 0;
845
- return void 0;
1095
+ function scheduleAdaptiveRenderExit(ctx, exitDelay) {
1096
+ const state = ctx.state;
1097
+ clearAdaptiveRenderExitTimeout(ctx);
1098
+ if (exitDelay <= 0) {
1099
+ setAdaptiveRender(ctx, "normal", "scroll");
1100
+ } else {
1101
+ state.scheduledWork.timeout(() => setAdaptiveRender(ctx, "normal", "scroll"), exitDelay, "adaptiveRender");
1102
+ }
846
1103
  }
847
- function createInitialScrollSession(options) {
848
- const { bootstrap, completion, kind, previousDataLength } = options;
849
- return kind === "offset" ? {
850
- completion,
851
- kind,
852
- previousDataLength
853
- } : {
854
- bootstrap,
855
- completion,
856
- kind,
857
- previousDataLength
858
- };
1104
+ function setAdaptiveRender(ctx, mode, reason) {
1105
+ var _a3, _b;
1106
+ const previousMode = peek$(ctx, "adaptiveRender");
1107
+ if (previousMode !== mode) {
1108
+ set$(ctx, "adaptiveRender", mode);
1109
+ (_b = (_a3 = ctx.state.props.adaptiveRender) == null ? void 0 : _a3.onChange) == null ? void 0 : _b.call(_a3, mode, reason);
1110
+ }
859
1111
  }
860
- function ensureInitialScrollSessionCompletion(state, kind = ((_b) => (_b = ((_a3) => (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind)()) != null ? _b : "bootstrap")()) {
861
- var _a4, _b2;
862
- if (!state.initialScrollSession) {
863
- state.initialScrollSession = createInitialScrollSession({
864
- completion: {},
865
- kind,
866
- previousDataLength: 0
867
- });
868
- } else if (state.initialScrollSession.kind !== kind) {
869
- state.initialScrollSession = createInitialScrollSession({
870
- bootstrap: state.initialScrollSession.kind === "bootstrap" ? state.initialScrollSession.bootstrap : void 0,
871
- completion: state.initialScrollSession.completion,
872
- kind,
873
- previousDataLength: state.initialScrollSession.previousDataLength
874
- });
1112
+ function resetAdaptiveRender(ctx) {
1113
+ var _a3, _b;
1114
+ clearAdaptiveRenderExitTimeout(ctx);
1115
+ const mode = (_b = (_a3 = ctx.state.props.adaptiveRender) == null ? void 0 : _a3.initialMode) != null ? _b : "normal";
1116
+ if (peek$(ctx, "adaptiveRender") !== mode) {
1117
+ setAdaptiveRender(ctx, mode, "initial");
875
1118
  }
876
- (_b2 = (_a4 = state.initialScrollSession).completion) != null ? _b2 : _a4.completion = {};
877
- return state.initialScrollSession.completion;
878
1119
  }
879
- var initialScrollCompletion = {
880
- didDispatchNativeScroll(state) {
881
- var _a3, _b;
882
- return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didDispatchNativeScroll);
883
- },
884
- didRetrySilentInitialScroll(state) {
885
- var _a3, _b;
886
- return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didRetrySilentInitialScroll);
887
- },
888
- markInitialScrollNativeDispatch(state) {
889
- ensureInitialScrollSessionCompletion(state).didDispatchNativeScroll = true;
890
- },
891
- markSilentInitialScrollRetry(state) {
892
- ensureInitialScrollSessionCompletion(state).didRetrySilentInitialScroll = true;
893
- },
894
- resetFlags(state) {
895
- if (!state.initialScrollSession) {
896
- return;
897
- }
898
- const completion = ensureInitialScrollSessionCompletion(state, state.initialScrollSession.kind);
899
- completion.didDispatchNativeScroll = void 0;
900
- completion.didRetrySilentInitialScroll = void 0;
901
- }
902
- };
903
- var initialScrollWatchdog = {
904
- clear(state) {
905
- initialScrollWatchdog.set(state, void 0);
906
- },
907
- didReachTarget(newScroll, watchdog) {
908
- const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
909
- return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
910
- },
911
- get(state) {
912
- var _a3, _b;
913
- return (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog;
914
- },
915
- hasNonZeroTargetOffset(targetOffset) {
916
- return targetOffset !== void 0 && targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
917
- },
918
- isAtZeroTargetOffset(targetOffset) {
919
- return targetOffset <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
920
- },
921
- set(state, watchdog) {
922
- var _a3, _b;
923
- if (!watchdog && !((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog)) {
924
- return;
925
- }
926
- const completion = ensureInitialScrollSessionCompletion(state);
927
- completion.watchdog = watchdog ? {
928
- startScroll: watchdog.startScroll,
929
- targetOffset: watchdog.targetOffset
930
- } : void 0;
931
- }
932
- };
933
- function setInitialScrollSession(state, options = {}) {
934
- var _a3, _b, _c, _d;
935
- const existingSession = state.initialScrollSession;
936
- const kind = (_a3 = options.kind) != null ? _a3 : existingSession == null ? void 0 : existingSession.kind;
937
- const completion = existingSession == null ? void 0 : existingSession.completion;
938
- const existingBootstrap = (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0;
939
- const bootstrap = kind === "bootstrap" ? options.bootstrap === null ? void 0 : (_b = options.bootstrap) != null ? _b : existingBootstrap : void 0;
940
- if (!kind) {
941
- return clearInitialScrollSession(state);
942
- }
943
- if (!state.initialScroll && !bootstrap && !hasInitialScrollSessionCompletion(completion)) {
944
- return clearInitialScrollSession(state);
945
- }
946
- const previousDataLength = (_d = (_c = options.previousDataLength) != null ? _c : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _d : 0;
947
- state.initialScrollSession = createInitialScrollSession({
948
- bootstrap,
949
- completion,
950
- kind,
951
- previousDataLength
952
- });
953
- return state.initialScrollSession;
954
- }
955
-
956
- // src/core/observedScroll.ts
957
- function getObservedScrollOffset(ctx, fallbackOffset = ctx.state.scroll) {
1120
+ function updateAdaptiveRender(ctx, scrollVelocity, options) {
958
1121
  var _a3, _b, _c;
959
- const { state } = ctx;
960
- const scroller = state.refScroller.current;
961
- const observed = (_a3 = scroller == null ? void 0 : scroller.getCurrentScrollOffset) == null ? void 0 : _a3.call(scroller);
962
- if (observed === void 0) return (_b = state.lastNativeScroll) != null ? _b : fallbackOffset;
963
- return state.props.horizontal ? peekLogicalHorizontalOffset(state, observed, (_c = scroller.getMaxScrollOffset) == null ? void 0 : _c.call(scroller)) : observed;
964
- }
965
-
966
- // src/core/clampScrollOffset.ts
967
- function clampScrollOffset(ctx, offset, scrollTarget) {
968
- const state = ctx.state;
969
- const minSize = (scrollTarget == null ? void 0 : scrollTarget.clampToContentContainerMinSize) ? state.props.contentContainerMinSize : void 0;
970
- const contentSize = minSize === void 0 ? getContentSize(ctx) : Math.max(getContentSize(ctx), minSize);
971
- let clampedOffset = offset;
972
- if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform.OS !== "android" || state.lastLayout)) {
973
- const baseMaxOffset = Math.max(0, contentSize - state.scrollLength);
974
- const viewOffset = Platform.OS === "web" && (scrollTarget == null ? void 0 : scrollTarget.isInitialScroll) ? void 0 : scrollTarget == null ? void 0 : scrollTarget.viewOffset;
975
- const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
976
- const maxOffset = baseMaxOffset + extraEndOffset;
977
- clampedOffset = Math.min(offset, maxOffset);
978
- }
979
- clampedOffset = Math.max(0, clampedOffset);
980
- return clampedOffset;
981
- }
982
-
983
- // src/utils/requestAdjust.ts
984
- function requestAdjust(ctx, positionDiff, source) {
985
- var _a3;
986
1122
  const state = ctx.state;
987
- if (Math.abs(positionDiff) > 0.1) {
988
- state.layoutAdjustmentEpoch = ((_a3 = state.layoutAdjustmentEpoch) != null ? _a3 : 0) + 1;
989
- const dataChanged = source === "data";
990
- const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
991
- const doit = () => {
992
- if (needsScrollWorkaround) {
993
- ctx.doScrollTo({ horizontal: state.props.horizontal, isAdjustment: true, offset: state.scroll });
994
- } else {
995
- state.scrollAdjustHandler.requestAdjust(positionDiff);
996
- if (state.adjustingFromInitialMount) {
997
- state.adjustingFromInitialMount--;
998
- }
999
- }
1000
- };
1001
- state.scroll += positionDiff;
1002
- if (state.userScrollContentSize !== void 0) {
1003
- state.userScrollContentSize += positionDiff;
1004
- }
1005
- state.scrollForNextCalculateItemsInView = void 0;
1006
- const readyToRender = peek$(ctx, "readyToRender");
1007
- if (readyToRender) {
1008
- doit();
1009
- if (Platform.OS !== "web" && source !== "item-size") {
1010
- const threshold = state.scroll - positionDiff / 2;
1011
- if (!state.ignoreScrollFromMVCP) {
1012
- state.ignoreScrollFromMVCP = {};
1013
- }
1014
- if (positionDiff > 0) {
1015
- state.ignoreScrollFromMVCP.lt = threshold;
1016
- } else {
1017
- state.ignoreScrollFromMVCP.gt = threshold;
1123
+ const adaptiveRender = state.props.adaptiveRender;
1124
+ const currentMode = peek$(ctx, "adaptiveRender");
1125
+ if (peek$(ctx, "readyToRender")) {
1126
+ if (adaptiveRender) {
1127
+ const isWeb = Platform.OS === "web";
1128
+ const enterVelocity = (_a3 = adaptiveRender.enterVelocity) != null ? _a3 : isWeb ? DEFAULT_WEB_ADAPTIVE_RENDER_ENTER_VELOCITY : DEFAULT_ADAPTIVE_RENDER_ENTER_VELOCITY;
1129
+ const exitVelocity = (_b = adaptiveRender.exitVelocity) != null ? _b : isWeb ? DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_VELOCITY : DEFAULT_ADAPTIVE_RENDER_EXIT_VELOCITY;
1130
+ const exitDelay = (_c = adaptiveRender.exitDelay) != null ? _c : isWeb ? DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_DELAY : DEFAULT_ADAPTIVE_RENDER_EXIT_DELAY;
1131
+ const threshold = currentMode === "light" ? exitVelocity : enterVelocity;
1132
+ const nextMode = (options == null ? void 0 : options.forceLight) || Math.abs(scrollVelocity) > threshold ? "light" : "normal";
1133
+ const previousMode = state.scheduledWork.has("adaptiveRender") ? "normal" : currentMode;
1134
+ if (nextMode !== previousMode) {
1135
+ if (nextMode === "light") {
1136
+ setAdaptiveRender(ctx, "light", "scroll");
1137
+ scheduleAdaptiveRenderExit(ctx, exitDelay);
1138
+ } else if (currentMode === "light") {
1139
+ scheduleAdaptiveRenderExit(ctx, exitDelay);
1018
1140
  }
1019
- const delay = needsScrollWorkaround ? 250 : 100;
1020
- state.scheduledWork.timeout(
1021
- () => {
1022
- var _a4;
1023
- state.ignoreScrollFromMVCP = void 0;
1024
- const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
1025
- if (shouldForceUpdate) {
1026
- state.ignoreScrollFromMVCPIgnored = false;
1027
- state.scrollPending = state.scroll;
1028
- (_a4 = state.reprocessCurrentScroll) == null ? void 0 : _a4.call(state);
1029
- }
1030
- },
1031
- delay,
1032
- "ignoreScrollFromMVCP"
1033
- );
1034
1141
  }
1035
1142
  } else {
1036
- state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
1037
- requestAnimationFrame(doit);
1143
+ resetAdaptiveRender(ctx);
1038
1144
  }
1039
1145
  }
1040
1146
  }
1041
1147
 
1042
- // src/core/finishMaintainScrollAtEnd.ts
1043
- function interruptMaintainScrollAtEnd(ctx) {
1148
+ // src/utils/getEffectiveDrawDistance.ts
1149
+ var INITIAL_DRAW_DISTANCE = 50;
1150
+ function getEffectiveDrawDistance(ctx, mode) {
1044
1151
  var _a3;
1152
+ const drawDistance = ctx.state.props.drawDistance;
1153
+ const initialScroll = ctx.state.initialScroll;
1154
+ const needsFullInitialDrawDistance = initialScroll !== void 0 && ((_a3 = initialScroll.viewPosition) != null ? _a3 : 0) > 0;
1155
+ const shouldCapDrawDistance = mode === "visible-first" || mode !== "full" && !peek$(ctx, "readyToRender") && !needsFullInitialDrawDistance;
1156
+ return shouldCapDrawDistance ? Math.min(drawDistance, INITIAL_DRAW_DISTANCE) : drawDistance;
1157
+ }
1158
+ function scheduleFullDrawDistancePrewarm(ctx) {
1045
1159
  const { state } = ctx;
1046
- const maintaining = state.maintainingScrollAtEnd;
1047
- state.endFollowInterrupted = true;
1048
- set$(ctx, "isFollowingEnd", false);
1049
- if (maintaining === "animated" || maintaining === "instant" || maintaining && ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isScrollToEnd)) {
1050
- cancelImperativeScroll(state);
1051
- syncScrollActive(ctx);
1052
- }
1053
- if (maintaining || state.pendingMaintainScrollAtEnd) {
1054
- finishMaintainScrollAtEnd(ctx);
1160
+ if (state.props.drawDistance <= INITIAL_DRAW_DISTANCE || state.scheduledWork.has("fullDrawDistancePrewarm")) {
1161
+ return;
1055
1162
  }
1163
+ state.scheduledWork.frame(() => {
1164
+ var _a3, _b, _c;
1165
+ if (state.props.hasExternalScroll && !hasActiveInitialScroll(state) && !state.scrollingTo && !state.pendingNativeMVCPAdjust) {
1166
+ const offset = (_b = (_a3 = state.refScroller.current) == null ? void 0 : _a3.getRawScrollOffset) == null ? void 0 : _b.call(_a3);
1167
+ if (offset !== void 0) {
1168
+ state.scroll = state.scrollPending = state.props.horizontal ? toLogicalHorizontalOffset(state, offset, state.totalSize) : offset;
1169
+ }
1170
+ }
1171
+ (_c = state.triggerCalculateItemsInView) == null ? void 0 : _c.call(state);
1172
+ }, "fullDrawDistancePrewarm");
1056
1173
  }
1057
- function armAppScrollRearm(ctx) {
1058
- ctx.state.scheduledWork.timeout(
1059
- () => rearmEndFollowIfSettled(ctx),
1060
- USER_MOMENTUM_STALL_TIMEOUT_MS,
1061
- "appScrollRearm"
1062
- );
1063
- }
1064
- function rearmEndFollowIfSettled(ctx) {
1174
+
1175
+ // src/utils/setInitialRenderState.ts
1176
+ function resetInitialRenderState(ctx, {
1177
+ resetLayout,
1178
+ resetInitialScroll
1179
+ }) {
1065
1180
  const { state } = ctx;
1066
- 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")) {
1067
- state.endFollowInterrupted = false;
1068
- if (state.props.maintainScrollAtEnd) set$(ctx, "isFollowingEnd", true);
1181
+ if (resetLayout) {
1182
+ state.didContainersLayout = false;
1183
+ state.queuedInitialLayout = false;
1184
+ state.scrollBufferDirection = void 0;
1185
+ state.scrollHistory.length = 0;
1186
+ state.scheduledWork.cancel("renderRangeProjection");
1187
+ state.edgeReachedGate = void 0;
1188
+ state.isStartReached = null;
1189
+ state.isEndReached = null;
1190
+ state.startReachedSnapshot = void 0;
1191
+ state.endReachedSnapshot = void 0;
1192
+ }
1193
+ if (resetInitialScroll) {
1194
+ state.didFinishInitialScroll = false;
1069
1195
  }
1196
+ set$(ctx, "readyToRender", false);
1197
+ resetAdaptiveRender(ctx);
1070
1198
  }
1071
- function finishMaintainScrollAtEnd(ctx) {
1072
- var _a3, _b;
1199
+ function setInitialRenderState(ctx, {
1200
+ didLayout,
1201
+ didInitialScroll
1202
+ }) {
1073
1203
  const { state } = ctx;
1074
- const currentPadding = peek$(ctx, "alignItemsAtEndPadding") || 0;
1075
- const nextPadding = getAlignItemsAtEndPadding(ctx);
1076
- state.maintainingScrollAtEnd = void 0;
1077
- state.pendingMaintainScrollAtEnd = false;
1078
- for (const key of (_a3 = state.pendingEndMeasurements) != null ? _a3 : []) {
1079
- if (!state.containerItemKeys.has(key)) state.pendingEndMeasurements.delete(key);
1204
+ const {
1205
+ loadStartTime,
1206
+ props: { onLoad, onReady }
1207
+ } = state;
1208
+ if (didLayout) {
1209
+ state.didContainersLayout = true;
1080
1210
  }
1081
- if (currentPadding !== nextPadding) {
1082
- set$(ctx, "alignItemsAtEndPadding", nextPadding);
1083
- state.scrollForNextCalculateItemsInView = void 0;
1084
- (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1085
- const nextScroll = clampScrollOffset(ctx, state.scroll + nextPadding - currentPadding);
1086
- const nativeScroll = Platform.OS === "ios" ? clampScrollOffset(ctx, state.scroll) : state.scroll;
1087
- state.scroll = nativeScroll;
1088
- requestAdjust(ctx, nextScroll - nativeScroll);
1211
+ if (didInitialScroll) {
1212
+ state.didFinishInitialScroll = true;
1089
1213
  }
1090
- }
1091
-
1092
- // src/utils/checkThreshold.ts
1093
- var HYSTERESIS_MULTIPLIER = 1.3;
1094
- function isOutsideThresholdHysteresis(distance, atThreshold, threshold) {
1095
- const absDistance = Math.abs(distance);
1096
- return !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
1097
- }
1098
- var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot) => {
1099
- const absDistance = Math.abs(distance);
1100
- const within = atThreshold || threshold > 0 && absDistance <= threshold;
1101
- const updateSnapshot = () => {
1102
- setSnapshot({
1103
- atThreshold,
1104
- contentSize: context.contentSize,
1105
- dataLength: context.dataLength,
1106
- scrollPosition: context.scrollPosition
1107
- });
1108
- };
1109
- let reached = !!wasReached;
1110
- if (!wasReached) {
1111
- if (within && onReached(distance)) {
1112
- updateSnapshot();
1113
- reached = true;
1214
+ const isReadyToRender = Boolean(state.didContainersLayout && state.didFinishInitialScroll);
1215
+ if (isReadyToRender && !peek$(ctx, "readyToRender")) {
1216
+ set$(ctx, "readyToRender", true);
1217
+ setAdaptiveRender(ctx, "normal", "ready");
1218
+ if (state.props.drawDistance > INITIAL_DRAW_DISTANCE) {
1219
+ scheduleFullDrawDistancePrewarm(ctx);
1114
1220
  }
1115
- } else {
1116
- const reset = isOutsideThresholdHysteresis(distance, atThreshold, threshold);
1117
- if (reset) {
1118
- setSnapshot(void 0);
1119
- reached = false;
1120
- } else if (within) {
1121
- const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
1122
- if (changed) {
1123
- updateSnapshot();
1221
+ if (!state.didLoad) {
1222
+ state.didLoad = true;
1223
+ if (onLoad) {
1224
+ onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1124
1225
  }
1125
1226
  }
1126
- }
1127
- return reached;
1128
- };
1129
-
1130
- // src/utils/edgeReachedGate.ts
1131
- function resetEdgeLatch(ctx, edge) {
1132
- const state = ctx.state;
1133
- if (edge === "start") {
1134
- state.isStartReached = false;
1135
- state.startReachedSnapshot = void 0;
1136
- } else {
1137
- state.isEndReached = false;
1138
- state.endReachedSnapshot = void 0;
1139
- }
1140
- }
1141
- function resetSharedEdgeGateIfOutsideHysteresis(ctx) {
1142
- const state = ctx.state;
1143
- if (!state.edgeReachedGate) {
1144
- return;
1145
- }
1146
- const contentSize = getContentSize(ctx);
1147
- const endDistance = contentSize - state.scroll - state.scrollLength;
1148
- const isContentLess = contentSize < state.scrollLength;
1149
- const startThreshold = state.props.onStartReachedThreshold * state.scrollLength;
1150
- const endThreshold = state.props.onEndReachedThreshold * state.scrollLength;
1151
- const isOutsideStart = isOutsideThresholdHysteresis(state.scroll, false, startThreshold);
1152
- const isOutsideEnd = isOutsideThresholdHysteresis(endDistance, isContentLess, endThreshold);
1153
- if (isOutsideStart && isOutsideEnd) {
1154
- state.edgeReachedGate = void 0;
1227
+ onReady == null ? void 0 : onReady();
1155
1228
  }
1156
1229
  }
1157
- function canDispatchReachedEdge(ctx, edge, allowedEdge, allowGateCreatedInCurrentCheck) {
1158
- return !ctx.state.edgeReachedGate || allowedEdge === edge || !!allowGateCreatedInCurrentCheck;
1159
- }
1160
- function markReachedEdge(ctx) {
1161
- ctx.state.edgeReachedGate = "closed";
1230
+
1231
+ // src/core/finishInitialScroll.ts
1232
+ var PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS = 2e3;
1233
+ function syncInitialScrollOffset(state, offset) {
1234
+ state.scroll = offset;
1235
+ state.scrollPending = offset;
1236
+ state.scrollPrev = offset;
1162
1237
  }
1163
- function prepareReachedEdgeForNextUserScroll(ctx) {
1164
- if (ctx.state.edgeReachedGate) {
1165
- ctx.state.edgeReachedGate = "prepared";
1166
- }
1238
+ function clearPreservedInitialScrollTarget(state) {
1239
+ state.scheduledWork.cancel("preservedInitialScroll");
1240
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
1241
+ state.initialScroll = void 0;
1242
+ state.preservedEndAnchorCorrection = void 0;
1243
+ setInitialScrollSession(state);
1167
1244
  }
1168
- function beginReachedEdgeUserScroll(ctx, scrollDelta) {
1245
+ function supersedeInitialScroll(ctx) {
1246
+ var _a3, _b, _c;
1169
1247
  const state = ctx.state;
1170
- if (state.edgeReachedGate !== "prepared") {
1171
- return void 0;
1248
+ const bootstrapInitialScroll = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" ? state.initialScrollSession.bootstrap : void 0;
1249
+ if (state.initialScroll || bootstrapInitialScroll || ((_b = state.scrollingTo) == null ? void 0 : _b.isInitialScroll)) {
1250
+ if ((bootstrapInitialScroll == null ? void 0 : bootstrapInitialScroll.frameHandle) !== void 0 && typeof cancelAnimationFrame === "function") {
1251
+ cancelAnimationFrame(bootstrapInitialScroll.frameHandle);
1252
+ }
1253
+ if ((_c = state.scrollingTo) == null ? void 0 : _c.isInitialScroll) {
1254
+ cancelScrollCompletionChecks(state);
1255
+ state.scrollingTo = void 0;
1256
+ state.scrollTargetPinnedRange = void 0;
1257
+ }
1258
+ initialScrollCompletion.resetFlags(state);
1259
+ setInitialScrollSession(state, { bootstrap: null });
1260
+ finishInitialScroll(ctx);
1172
1261
  }
1173
- const allowedEdge = scrollDelta < 0 ? "start" : "end";
1174
- resetEdgeLatch(ctx, allowedEdge);
1175
- return allowedEdge;
1176
- }
1177
-
1178
- // src/utils/hasActiveInitialScroll.ts
1179
- function hasActiveInitialScroll(state) {
1180
- return !!(state == null ? void 0 : state.initialScroll) && !state.didFinishInitialScroll;
1181
1262
  }
1182
-
1183
- // src/utils/checkAtBottom.ts
1184
- function checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
1263
+ function finishInitialScroll(ctx, options) {
1185
1264
  var _a3;
1186
1265
  const state = ctx.state;
1187
- if (!state) {
1188
- return;
1189
- }
1190
- const {
1191
- queuedInitialLayout,
1192
- scrollLength,
1193
- scroll,
1194
- maintainingScrollAtEnd,
1195
- props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
1196
- } = state;
1197
- const contentSize = getContentSize(ctx);
1198
- const distanceFromEnd = contentSize - scroll - scrollLength;
1199
- resetSharedEdgeGateIfOutsideHysteresis(ctx);
1200
- if (state.props.data.length === 0 && scrollLength > 0 && contentSize <= scrollLength) {
1201
- set$(ctx, "isAtEnd", true);
1202
- set$(ctx, "isNearEnd", true);
1203
- set$(ctx, "isWithinMaintainScrollAtEndThreshold", true);
1204
- return;
1266
+ const freshDataTransitionEpoch = state.freshDataTransitionEpoch;
1267
+ if ((options == null ? void 0 : options.resolvedOffset) !== void 0) {
1268
+ syncInitialScrollOffset(state, options.resolvedOffset);
1269
+ } else if ((options == null ? void 0 : options.syncObservedOffset) && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
1270
+ const observedOffset = getObservedScrollOffset(ctx);
1271
+ if (Number.isFinite(observedOffset)) {
1272
+ syncInitialScrollOffset(state, observedOffset);
1273
+ }
1205
1274
  }
1206
- if (contentSize > 0 && queuedInitialLayout) {
1207
- const isContentLess = contentSize < scrollLength;
1208
- set$(ctx, "isAtEnd", isContentLess || distanceFromEnd <= EDGE_POSITION_EPSILON);
1209
- set$(ctx, "isNearEnd", isContentLess || distanceFromEnd <= onEndReachedThreshold * scrollLength);
1210
- set$(
1211
- ctx,
1212
- "isWithinMaintainScrollAtEndThreshold",
1213
- isContentLess || // A zero threshold still includes fractional offsets considered
1214
- // at the end; otherwise following can stop after rounding/layout.
1215
- distanceFromEnd <= Math.max(EDGE_POSITION_EPSILON, maintainScrollAtEndThreshold * scrollLength) || state.pendingMaintainScrollAtEnd || !!maintainingScrollAtEnd || !!peek$(ctx, "isFollowingEnd")
1216
- );
1217
- const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || maintainingScrollAtEnd;
1218
- if (!shouldSkipThresholdChecks) {
1219
- state.isEndReached = checkThreshold(
1220
- distanceFromEnd,
1221
- isContentLess,
1222
- onEndReachedThreshold * scrollLength,
1223
- state.isEndReached,
1224
- state.endReachedSnapshot,
1225
- {
1226
- contentSize,
1227
- dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1228
- scrollPosition: scroll
1229
- },
1230
- (distance) => {
1231
- let dispatched = false;
1232
- if (state.props.onEndReached && canDispatchReachedEdge(ctx, "end", allowedEdge, allowGateCreatedInCurrentCheck)) {
1233
- markReachedEdge(ctx);
1234
- state.props.onEndReached({ distanceFromEnd: distance });
1235
- dispatched = true;
1236
- }
1237
- return dispatched;
1238
- },
1239
- (snapshot) => {
1240
- state.endReachedSnapshot = snapshot;
1241
- }
1242
- );
1275
+ const complete = () => {
1276
+ var _a4, _b, _c, _d;
1277
+ if (state.isUnmounted) {
1278
+ return;
1279
+ }
1280
+ if (freshDataTransitionEpoch !== state.freshDataTransitionEpoch) {
1281
+ (_a4 = options == null ? void 0 : options.onFinished) == null ? void 0 : _a4.call(options);
1282
+ return;
1283
+ }
1284
+ const shouldReleaseDeferredPublicOnScroll = Platform.OS === "web" && ((_b = state.initialScrollSession) == null ? void 0 : _b.kind) === "bootstrap";
1285
+ if (isEndOfContentTarget(state.initialScroll, state.props.data.length) && state.props.maintainScrollAtEnd && !state.endFollowInterrupted) {
1286
+ set$(ctx, "isFollowingEnd", true);
1243
1287
  }
1288
+ const finalScrollOffset = (_c = options == null ? void 0 : options.resolvedOffset) != null ? _c : state.scrollPending;
1289
+ initialScrollWatchdog.clear(state);
1290
+ if ((options == null ? void 0 : options.preserveTarget) && state.initialScroll) {
1291
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
1292
+ setInitialScrollSession(state);
1293
+ state.scheduledWork.cancel("preservedInitialScroll");
1294
+ if (options == null ? void 0 : options.schedulePreservedTargetClear) {
1295
+ state.scheduledWork.timeout(
1296
+ () => {
1297
+ var _a5;
1298
+ if (!state.didFinishInitialScroll || ((_a5 = state.scrollingTo) == null ? void 0 : _a5.isInitialScroll) || !state.initialScroll) {
1299
+ return;
1300
+ }
1301
+ clearPreservedInitialScrollTarget(state);
1302
+ },
1303
+ PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS,
1304
+ "preservedInitialScroll"
1305
+ );
1306
+ }
1307
+ } else {
1308
+ clearPreservedInitialScrollTarget(state);
1309
+ }
1310
+ if (options == null ? void 0 : options.recalculateItems) {
1311
+ recalculateSettledScroll(ctx);
1312
+ }
1313
+ setInitialRenderState(ctx, { didInitialScroll: true });
1314
+ if (shouldReleaseDeferredPublicOnScroll) {
1315
+ releaseDeferredPublicOnScroll(ctx, finalScrollOffset);
1316
+ }
1317
+ (_d = options == null ? void 0 : options.onFinished) == null ? void 0 : _d.call(options);
1318
+ };
1319
+ if (options == null ? void 0 : options.waitForCompletionFrame) {
1320
+ requestAnimationFrame(complete);
1321
+ return;
1244
1322
  }
1323
+ complete();
1245
1324
  }
1246
1325
 
1247
- // src/utils/checkAtTop.ts
1248
- function checkAtTop(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
1249
- const state = ctx == null ? void 0 : ctx.state;
1250
- if (!state) {
1251
- return;
1252
- }
1253
- const {
1254
- isStartReached,
1255
- props: { data, onStartReachedThreshold },
1256
- scroll,
1257
- scrollLength,
1258
- startReachedSnapshot,
1259
- totalSize
1260
- } = state;
1261
- const dataLength = data.length;
1262
- const threshold = onStartReachedThreshold * scrollLength;
1263
- resetSharedEdgeGateIfOutsideHysteresis(ctx);
1264
- if (isStartReached && threshold > 0 && scroll > threshold && startReachedSnapshot && (startReachedSnapshot.contentSize !== totalSize || startReachedSnapshot.dataLength !== dataLength)) {
1265
- state.isStartReached = false;
1266
- state.startReachedSnapshot = void 0;
1267
- }
1268
- set$(ctx, "isAtStart", scroll <= EDGE_POSITION_EPSILON);
1269
- set$(ctx, "isNearStart", scroll <= threshold);
1270
- const shouldSkipThresholdChecks = !state.queuedInitialLayout || hasActiveInitialScroll(state) || !!state.scrollingTo;
1271
- if (!shouldSkipThresholdChecks) {
1272
- state.isStartReached = checkThreshold(
1273
- scroll,
1274
- false,
1275
- threshold,
1276
- state.isStartReached,
1277
- startReachedSnapshot,
1278
- {
1279
- contentSize: totalSize,
1280
- dataLength,
1281
- scrollPosition: scroll
1282
- },
1283
- (distance) => {
1284
- let dispatched = false;
1285
- if (state.props.onStartReached && canDispatchReachedEdge(ctx, "start", allowedEdge, allowGateCreatedInCurrentCheck)) {
1286
- markReachedEdge(ctx);
1287
- state.props.onStartReached({ distanceFromStart: distance });
1288
- dispatched = true;
1289
- }
1290
- return dispatched;
1291
- },
1292
- (snapshot) => {
1293
- state.startReachedSnapshot = snapshot;
1294
- }
1295
- );
1296
- }
1326
+ // src/core/getStartOffsetAdjustment.ts
1327
+ function getStartOffsetAdjustment(ctx) {
1328
+ const { state } = ctx;
1329
+ const stylePaddingStart = state.props.horizontal ? (isHorizontalRTL(state) ? state.props.stylePaddingRight : state.props.stylePaddingLeft) || 0 : peek$(ctx, "stylePaddingTop") || 0;
1330
+ return stylePaddingStart + (peek$(ctx, "alignItemsAtEndPadding") || 0) + (peek$(ctx, "headerSize") || 0);
1331
+ }
1332
+ function getItemSpaceOffset(ctx, scrollOffset, pending = ((_a3) => (_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0)(), startOffset = getStartOffsetAdjustment(ctx)) {
1333
+ return Math.round(scrollOffset + (pending - startOffset));
1297
1334
  }
1298
1335
 
1299
- // src/utils/checkThresholds.ts
1300
- function checkThresholds(ctx, allowedEdge) {
1301
- const allowGateCreatedInCurrentCheck = !ctx.state.edgeReachedGate;
1302
- checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck);
1303
- checkAtTop(ctx, allowedEdge, allowGateCreatedInCurrentCheck);
1304
- rearmEndFollowIfSettled(ctx);
1336
+ // src/core/addTotalSize.ts
1337
+ function addTotalSize(ctx, key, add, notifyTotalSize = true) {
1338
+ const state = ctx.state;
1339
+ const prevTotalSize = state.totalSize;
1340
+ let totalSize = state.totalSize;
1341
+ if (key === null) {
1342
+ totalSize = add;
1343
+ if (state.timeoutSetPaddingTop) {
1344
+ clearTimeout(state.timeoutSetPaddingTop);
1345
+ state.timeoutSetPaddingTop = void 0;
1346
+ }
1347
+ } else {
1348
+ totalSize += add;
1349
+ }
1350
+ if (prevTotalSize !== totalSize) {
1351
+ if (!IsNewArchitecture && state.initialScroll && totalSize < prevTotalSize) {
1352
+ state.pendingTotalSize = totalSize;
1353
+ } else {
1354
+ state.pendingTotalSize = void 0;
1355
+ state.totalSize = totalSize;
1356
+ if (notifyTotalSize) {
1357
+ set$(ctx, "totalSize", totalSize);
1358
+ }
1359
+ updateContentMetricsState(ctx);
1360
+ }
1361
+ } else if (notifyTotalSize && ctx.values.get("totalSize") !== totalSize) {
1362
+ set$(ctx, "totalSize", totalSize);
1363
+ }
1305
1364
  }
1306
1365
 
1307
- // src/core/recalculateSettledScroll.ts
1308
- function recalculateSettledScroll(ctx) {
1309
- var _a3, _b;
1366
+ // src/core/setSize.ts
1367
+ function setSize(ctx, itemKey, size, notifyTotalSize = true) {
1310
1368
  const state = ctx.state;
1311
- if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1312
- (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1369
+ const { sizes } = state;
1370
+ const previousSize = sizes.get(itemKey);
1371
+ const diff = previousSize !== void 0 ? size - previousSize : size;
1372
+ if (diff !== 0) {
1373
+ state.positionsAreCurrent = false;
1374
+ addTotalSize(ctx, itemKey, diff, notifyTotalSize);
1313
1375
  }
1314
- checkThresholds(ctx);
1376
+ sizes.set(itemKey, size);
1315
1377
  }
1316
1378
 
1317
- // src/core/adaptiveRender.ts
1318
- var DEFAULT_ADAPTIVE_RENDER_ENTER_VELOCITY = 3;
1319
- var DEFAULT_ADAPTIVE_RENDER_EXIT_VELOCITY = 1;
1320
- var DEFAULT_ADAPTIVE_RENDER_EXIT_DELAY = 250;
1321
- var DEFAULT_WEB_ADAPTIVE_RENDER_ENTER_VELOCITY = 6;
1322
- var DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_VELOCITY = 3;
1323
- var DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_DELAY = 250;
1324
- function clearAdaptiveRenderExitTimeout(ctx) {
1325
- ctx.state.scheduledWork.cancel("adaptiveRender");
1379
+ // src/utils/helpers.ts
1380
+ function isFunction(obj) {
1381
+ return typeof obj === "function";
1326
1382
  }
1327
- function scheduleAdaptiveRenderExit(ctx, exitDelay) {
1328
- const state = ctx.state;
1329
- clearAdaptiveRenderExitTimeout(ctx);
1330
- if (exitDelay <= 0) {
1331
- setAdaptiveRender(ctx, "normal", "scroll");
1332
- } else {
1333
- state.scheduledWork.timeout(() => setAdaptiveRender(ctx, "normal", "scroll"), exitDelay, "adaptiveRender");
1334
- }
1383
+ function isArray(obj) {
1384
+ return Array.isArray(obj);
1335
1385
  }
1336
- function setAdaptiveRender(ctx, mode, reason) {
1337
- var _a3, _b;
1338
- const previousMode = peek$(ctx, "adaptiveRender");
1339
- if (previousMode !== mode) {
1340
- set$(ctx, "adaptiveRender", mode);
1341
- (_b = (_a3 = ctx.state.props.adaptiveRender) == null ? void 0 : _a3.onChange) == null ? void 0 : _b.call(_a3, mode, reason);
1386
+ var warned = /* @__PURE__ */ new Set();
1387
+ function warnDevOnce(id, text) {
1388
+ if (IS_DEV && !warned.has(id)) {
1389
+ warned.add(id);
1390
+ console.warn(`[legend-list] ${text}`);
1342
1391
  }
1343
1392
  }
1344
- function resetAdaptiveRender(ctx) {
1345
- var _a3, _b;
1346
- clearAdaptiveRenderExitTimeout(ctx);
1347
- const mode = (_b = (_a3 = ctx.state.props.adaptiveRender) == null ? void 0 : _a3.initialMode) != null ? _b : "normal";
1348
- if (peek$(ctx, "adaptiveRender") !== mode) {
1349
- setAdaptiveRender(ctx, mode, "initial");
1350
- }
1393
+ function roundSize(size) {
1394
+ return Math.floor(size * 8) / 8;
1351
1395
  }
1352
- function updateAdaptiveRender(ctx, scrollVelocity, options) {
1396
+ function isNullOrUndefined(value) {
1397
+ return value === null || value === void 0;
1398
+ }
1399
+ function getPadding(s, type) {
1353
1400
  var _a3, _b, _c;
1354
- const state = ctx.state;
1355
- const adaptiveRender = state.props.adaptiveRender;
1356
- const currentMode = peek$(ctx, "adaptiveRender");
1357
- if (peek$(ctx, "readyToRender")) {
1358
- if (adaptiveRender) {
1359
- const isWeb = Platform.OS === "web";
1360
- const enterVelocity = (_a3 = adaptiveRender.enterVelocity) != null ? _a3 : isWeb ? DEFAULT_WEB_ADAPTIVE_RENDER_ENTER_VELOCITY : DEFAULT_ADAPTIVE_RENDER_ENTER_VELOCITY;
1361
- const exitVelocity = (_b = adaptiveRender.exitVelocity) != null ? _b : isWeb ? DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_VELOCITY : DEFAULT_ADAPTIVE_RENDER_EXIT_VELOCITY;
1362
- const exitDelay = (_c = adaptiveRender.exitDelay) != null ? _c : isWeb ? DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_DELAY : DEFAULT_ADAPTIVE_RENDER_EXIT_DELAY;
1363
- const threshold = currentMode === "light" ? exitVelocity : enterVelocity;
1364
- const nextMode = (options == null ? void 0 : options.forceLight) || Math.abs(scrollVelocity) > threshold ? "light" : "normal";
1365
- const previousMode = state.scheduledWork.has("adaptiveRender") ? "normal" : currentMode;
1366
- if (nextMode !== previousMode) {
1367
- if (nextMode === "light") {
1368
- setAdaptiveRender(ctx, "light", "scroll");
1369
- scheduleAdaptiveRenderExit(ctx, exitDelay);
1370
- } else if (currentMode === "light") {
1371
- scheduleAdaptiveRenderExit(ctx, exitDelay);
1372
- }
1373
- }
1374
- } else {
1375
- resetAdaptiveRender(ctx);
1376
- }
1377
- }
1401
+ const axisPadding = type === "Left" || type === "Right" ? s.paddingHorizontal : s.paddingVertical;
1402
+ return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : axisPadding) != null ? _b : s.padding) != null ? _c : 0;
1378
1403
  }
1379
-
1380
- // src/utils/getEffectiveDrawDistance.ts
1381
- var INITIAL_DRAW_DISTANCE = 50;
1382
- function getEffectiveDrawDistance(ctx, mode) {
1383
- var _a3;
1384
- const drawDistance = ctx.state.props.drawDistance;
1385
- const initialScroll = ctx.state.initialScroll;
1386
- const needsFullInitialDrawDistance = initialScroll !== void 0 && ((_a3 = initialScroll.viewPosition) != null ? _a3 : 0) > 0;
1387
- const shouldCapDrawDistance = mode === "visible-first" || mode !== "full" && !peek$(ctx, "readyToRender") && !needsFullInitialDrawDistance;
1388
- return shouldCapDrawDistance ? Math.min(drawDistance, INITIAL_DRAW_DISTANCE) : drawDistance;
1404
+ function extractPadding(style, contentContainerStyle, type) {
1405
+ return getPadding(style, type) + getPadding(contentContainerStyle, type);
1389
1406
  }
1390
- function scheduleFullDrawDistancePrewarm(ctx) {
1391
- const { state } = ctx;
1392
- if (state.props.drawDistance <= INITIAL_DRAW_DISTANCE || state.scheduledWork.has("fullDrawDistancePrewarm")) {
1393
- return;
1407
+ function findContainerId(ctx, key) {
1408
+ var _a3, _b;
1409
+ const directMatch = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.containerItemKeys) == null ? void 0 : _b.get(key);
1410
+ if (directMatch !== void 0) {
1411
+ return directMatch;
1394
1412
  }
1395
- state.scheduledWork.frame(() => {
1396
- var _a3, _b, _c;
1397
- if (state.props.hasExternalScroll && !hasActiveInitialScroll(state) && !state.scrollingTo && !state.pendingNativeMVCPAdjust) {
1398
- const offset = (_b = (_a3 = state.refScroller.current) == null ? void 0 : _a3.getRawScrollOffset) == null ? void 0 : _b.call(_a3);
1399
- if (offset !== void 0) {
1400
- state.scroll = state.scrollPending = state.props.horizontal ? toLogicalHorizontalOffset(state, offset, state.totalSize) : offset;
1401
- }
1413
+ const numContainers = peek$(ctx, "numContainers");
1414
+ for (let i = 0; i < numContainers; i++) {
1415
+ const itemKey = peek$(ctx, `containerItemKey${i}`);
1416
+ if (itemKey === key) {
1417
+ return i;
1402
1418
  }
1403
- (_c = state.triggerCalculateItemsInView) == null ? void 0 : _c.call(state);
1404
- }, "fullDrawDistancePrewarm");
1419
+ }
1420
+ return -1;
1405
1421
  }
1406
1422
 
1407
- // src/utils/setInitialRenderState.ts
1408
- function resetInitialRenderState(ctx, {
1409
- resetLayout,
1410
- resetInitialScroll
1411
- }) {
1412
- const { state } = ctx;
1413
- if (resetLayout) {
1414
- state.didContainersLayout = false;
1415
- state.queuedInitialLayout = false;
1416
- state.scrollBufferDirection = void 0;
1417
- state.scrollHistory.length = 0;
1418
- state.scheduledWork.cancel("renderRangeProjection");
1419
- state.edgeReachedGate = void 0;
1420
- state.isStartReached = null;
1421
- state.isEndReached = null;
1422
- state.startReachedSnapshot = void 0;
1423
- state.endReachedSnapshot = void 0;
1423
+ // src/utils/getItemSize.ts
1424
+ function getKnownOrFixedSize(ctx, key, index, data, resolved) {
1425
+ var _a3, _b, _c, _d;
1426
+ const state = ctx.state;
1427
+ const { getFixedItemSize, getItemType } = state.props;
1428
+ let size = key ? state.sizesKnown.get(key) : void 0;
1429
+ if (size === void 0 && key && getFixedItemSize) {
1430
+ const itemType = (_b = resolved == null ? void 0 : resolved.itemType) != null ? _b : getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1431
+ const fixedSize = (resolved == null ? void 0 : resolved.didResolveFixedItemSize) ? resolved.fixedItemSize : getFixedItemSize(data, index, itemType);
1432
+ if (fixedSize !== void 0) {
1433
+ size = fixedSize + ctx.scrollAxisGap;
1434
+ state.positionsAreCurrent = false;
1435
+ state.positionRecalculationStartIndex = Math.min((_c = state.positionRecalculationStartIndex) != null ? _c : index, index);
1436
+ state.scrollForNextCalculateItemsInView = void 0;
1437
+ state.sizesKnown.set(key, size);
1438
+ (_d = state.fixedItemSizeKeys) != null ? _d : state.fixedItemSizeKeys = /* @__PURE__ */ new Set();
1439
+ state.fixedItemSizeKeys.add(key);
1440
+ }
1424
1441
  }
1425
- if (resetInitialScroll) {
1426
- state.didFinishInitialScroll = false;
1442
+ return size;
1443
+ }
1444
+ function getKnownOrFixedItemSize(ctx, index) {
1445
+ const key = getId(ctx.state, index);
1446
+ return getKnownOrFixedSize(ctx, key, index, ctx.state.props.data[index]);
1447
+ }
1448
+ function areKnownOrFixedItemSizesAvailable(ctx, startIndex, endIndex) {
1449
+ for (let index = startIndex; index <= endIndex; index++) {
1450
+ if (getKnownOrFixedItemSize(ctx, index) === void 0) {
1451
+ return false;
1452
+ }
1427
1453
  }
1428
- set$(ctx, "readyToRender", false);
1429
- resetAdaptiveRender(ctx);
1454
+ return true;
1430
1455
  }
1431
- function setInitialRenderState(ctx, {
1432
- didLayout,
1433
- didInitialScroll
1434
- }) {
1435
- const { state } = ctx;
1456
+ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize, notifyTotalSize, resolved) {
1457
+ var _a3, _b, _c, _d;
1458
+ const state = ctx.state;
1436
1459
  const {
1437
- loadStartTime,
1438
- props: { onLoad, onReady }
1460
+ sizes,
1461
+ averageSizes,
1462
+ props: { estimatedItemSize, getItemType },
1463
+ scrollingTo
1439
1464
  } = state;
1440
- if (didLayout) {
1441
- state.didContainersLayout = true;
1465
+ const sizeKnown = state.sizesKnown.get(key);
1466
+ if (sizeKnown !== void 0) {
1467
+ return sizeKnown;
1442
1468
  }
1443
- if (didInitialScroll) {
1444
- state.didFinishInitialScroll = true;
1469
+ const renderedSize = sizes.get(key);
1470
+ let size = getKnownOrFixedSize(ctx, key, index, data, resolved);
1471
+ if (size !== void 0) {
1472
+ setSize(ctx, key, size, notifyTotalSize);
1473
+ return size;
1445
1474
  }
1446
- const isReadyToRender = Boolean(state.didContainersLayout && state.didFinishInitialScroll);
1447
- if (isReadyToRender && !peek$(ctx, "readyToRender")) {
1448
- set$(ctx, "readyToRender", true);
1449
- setAdaptiveRender(ctx, "normal", "ready");
1450
- if (state.props.drawDistance > INITIAL_DRAW_DISTANCE) {
1451
- scheduleFullDrawDistancePrewarm(ctx);
1452
- }
1453
- if (!state.didLoad) {
1454
- state.didLoad = true;
1455
- if (onLoad) {
1456
- onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1457
- }
1475
+ if (preferCachedSize && renderedSize !== void 0) {
1476
+ return renderedSize;
1477
+ }
1478
+ const itemType = (_b = resolved == null ? void 0 : resolved.itemType) != null ? _b : getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1479
+ if (useAverageSize && !scrollingTo) {
1480
+ const averageSizeForType = (_c = averageSizes[itemType]) == null ? void 0 : _c.avg;
1481
+ if (averageSizeForType !== void 0) {
1482
+ size = roundSize(averageSizeForType);
1458
1483
  }
1459
- onReady == null ? void 0 : onReady();
1460
1484
  }
1485
+ if (size === void 0 && renderedSize !== void 0) {
1486
+ return renderedSize;
1487
+ }
1488
+ if (size === void 0 && useAverageSize && scrollingTo) {
1489
+ const averageSizeForType = (_d = scrollingTo.averageSizeSnapshot) == null ? void 0 : _d[itemType];
1490
+ if (averageSizeForType !== void 0) {
1491
+ size = roundSize(averageSizeForType);
1492
+ }
1493
+ }
1494
+ if (size === void 0) {
1495
+ size = estimatedItemSize + ctx.scrollAxisGap;
1496
+ }
1497
+ setSize(ctx, key, size, notifyTotalSize);
1498
+ return size;
1461
1499
  }
1462
-
1463
- // src/core/finishInitialScroll.ts
1464
- var PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS = 2e3;
1465
- function syncInitialScrollOffset(state, offset) {
1466
- state.scroll = offset;
1467
- state.scrollPending = offset;
1468
- state.scrollPrev = offset;
1469
- }
1470
- function clearPreservedInitialScrollTarget(state) {
1471
- state.scheduledWork.cancel("preservedInitialScroll");
1472
- state.clearPreservedInitialScrollOnNextFinish = void 0;
1473
- state.initialScroll = void 0;
1474
- state.preservedEndAnchorCorrection = void 0;
1475
- setInitialScrollSession(state);
1500
+ function getItemSizeAtIndex(ctx, index) {
1501
+ if (index === void 0 || index < 0) {
1502
+ return void 0;
1503
+ }
1504
+ const targetId = getId(ctx.state, index);
1505
+ return getItemSize(ctx, targetId, index, ctx.state.props.data[index]);
1476
1506
  }
1477
- function supersedeInitialScroll(ctx) {
1478
- var _a3, _b, _c;
1479
- const state = ctx.state;
1480
- const bootstrapInitialScroll = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" ? state.initialScrollSession.bootstrap : void 0;
1481
- if (state.initialScroll || bootstrapInitialScroll || ((_b = state.scrollingTo) == null ? void 0 : _b.isInitialScroll)) {
1482
- if ((bootstrapInitialScroll == null ? void 0 : bootstrapInitialScroll.frameHandle) !== void 0 && typeof cancelAnimationFrame === "function") {
1483
- cancelAnimationFrame(bootstrapInitialScroll.frameHandle);
1484
- }
1485
- if ((_c = state.scrollingTo) == null ? void 0 : _c.isInitialScroll) {
1486
- cancelScrollCompletionChecks(state);
1487
- state.scrollingTo = void 0;
1488
- state.scrollTargetPinnedRange = void 0;
1489
- }
1490
- initialScrollCompletion.resetFlags(state);
1491
- setInitialScrollSession(state, { bootstrap: null });
1492
- finishInitialScroll(ctx);
1507
+
1508
+ // src/core/calculateOffsetWithOffsetPosition.ts
1509
+ function getViewPositionOffset(availableSpace, viewPosition, viewPositionFallback) {
1510
+ let alignment = viewPosition;
1511
+ if (availableSpace < 0 && viewPositionFallback !== void 0) {
1512
+ alignment = viewPositionFallback === "start" ? 0 : 1;
1493
1513
  }
1514
+ return -alignment * availableSpace;
1494
1515
  }
1495
- function finishInitialScroll(ctx, options) {
1516
+ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1496
1517
  var _a3;
1497
1518
  const state = ctx.state;
1498
- const freshDataTransitionEpoch = state.freshDataTransitionEpoch;
1499
- if ((options == null ? void 0 : options.resolvedOffset) !== void 0) {
1500
- syncInitialScrollOffset(state, options.resolvedOffset);
1501
- } else if ((options == null ? void 0 : options.syncObservedOffset) && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
1502
- const observedOffset = getObservedScrollOffset(ctx);
1503
- if (Number.isFinite(observedOffset)) {
1504
- syncInitialScrollOffset(state, observedOffset);
1505
- }
1519
+ const { index, viewOffset, viewPosition, viewPositionFallback } = params;
1520
+ let offset = offsetParam;
1521
+ if (viewOffset) {
1522
+ offset -= viewOffset;
1506
1523
  }
1507
- const complete = () => {
1508
- var _a4, _b, _c, _d;
1509
- if (state.isUnmounted) {
1510
- return;
1511
- }
1512
- if (freshDataTransitionEpoch !== state.freshDataTransitionEpoch) {
1513
- (_a4 = options == null ? void 0 : options.onFinished) == null ? void 0 : _a4.call(options);
1514
- return;
1515
- }
1516
- const shouldReleaseDeferredPublicOnScroll = Platform.OS === "web" && ((_b = state.initialScrollSession) == null ? void 0 : _b.kind) === "bootstrap";
1517
- if (isEndOfContentTarget(state.initialScroll, state.props.data.length) && state.props.maintainScrollAtEnd && !state.endFollowInterrupted) {
1518
- set$(ctx, "isFollowingEnd", true);
1519
- }
1520
- const finalScrollOffset = (_c = options == null ? void 0 : options.resolvedOffset) != null ? _c : state.scrollPending;
1521
- initialScrollWatchdog.clear(state);
1522
- if ((options == null ? void 0 : options.preserveTarget) && state.initialScroll) {
1523
- state.clearPreservedInitialScrollOnNextFinish = void 0;
1524
- setInitialScrollSession(state);
1525
- state.scheduledWork.cancel("preservedInitialScroll");
1526
- if (options == null ? void 0 : options.schedulePreservedTargetClear) {
1527
- state.scheduledWork.timeout(
1528
- () => {
1529
- var _a5;
1530
- if (!state.didFinishInitialScroll || ((_a5 = state.scrollingTo) == null ? void 0 : _a5.isInitialScroll) || !state.initialScroll) {
1531
- return;
1532
- }
1533
- clearPreservedInitialScrollTarget(state);
1534
- },
1535
- PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS,
1536
- "preservedInitialScroll"
1537
- );
1538
- }
1539
- } else {
1540
- clearPreservedInitialScrollTarget(state);
1541
- }
1542
- if (options == null ? void 0 : options.recalculateItems) {
1543
- recalculateSettledScroll(ctx);
1544
- }
1545
- setInitialRenderState(ctx, { didInitialScroll: true });
1546
- if (shouldReleaseDeferredPublicOnScroll) {
1547
- releaseDeferredPublicOnScroll(ctx, finalScrollOffset);
1524
+ const leadingInset = index !== void 0 ? getContentInsetTop(ctx) : 0;
1525
+ if (index !== void 0) {
1526
+ offset += getStartOffsetAdjustment(ctx) - leadingInset;
1527
+ }
1528
+ if (viewPosition !== void 0 && index !== void 0) {
1529
+ const dataLength = state.props.data.length;
1530
+ if (dataLength === 0) {
1531
+ return offset;
1548
1532
  }
1549
- (_d = options == null ? void 0 : options.onFinished) == null ? void 0 : _d.call(options);
1550
- };
1551
- if (options == null ? void 0 : options.waitForCompletionFrame) {
1552
- requestAnimationFrame(complete);
1553
- return;
1533
+ const isOutOfBounds = index < 0 || index >= dataLength;
1534
+ const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
1535
+ const measuredItemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1536
+ const itemSize = Math.max(0, measuredItemSize - (isOutOfBounds ? 0 : ctx.scrollAxisGap));
1537
+ const trailingInset = getContentInsetEnd(ctx);
1538
+ const availableSpace = state.scrollLength - leadingInset - trailingInset - itemSize;
1539
+ offset += getViewPositionOffset(availableSpace, viewPosition, viewPositionFallback);
1554
1540
  }
1555
- complete();
1541
+ if (index !== void 0 && params.clampToContentContainerMinSize) {
1542
+ offset += getContentSize(ctx) - getContentSize(ctx, false);
1543
+ }
1544
+ return offset;
1556
1545
  }
1557
1546
 
1558
1547
  // src/core/mvcp.ts
@@ -1606,11 +1595,12 @@ function shouldQueueNativeMVCPAdjust(dataChanged, state, positionDiff, prevTotal
1606
1595
  const distanceFromEnd = prevTotalSize - prevScroll - state.scrollLength;
1607
1596
  return distanceFromEnd < Math.abs(positionDiff) - MVCP_POSITION_EPSILON;
1608
1597
  }
1609
- function getPredictedNativeClamp(state, unresolvedAmount, totalSize) {
1598
+ function getPredictedNativeClamp(ctx, unresolvedAmount, totalSize) {
1610
1599
  if (Math.abs(unresolvedAmount) <= MVCP_POSITION_EPSILON) {
1611
1600
  return 0;
1612
1601
  }
1613
- const maxScroll = Math.max(0, totalSize - state.scrollLength);
1602
+ const state = ctx.state;
1603
+ const maxScroll = getScrollRangeEnd(ctx, totalSize);
1614
1604
  const clampDelta = maxScroll - state.scroll;
1615
1605
  if (unresolvedAmount < 0) {
1616
1606
  return Math.max(unresolvedAmount, Math.min(0, clampDelta));
@@ -1638,7 +1628,7 @@ function maybeApplyPredictedNativeMVCPAdjust(ctx) {
1638
1628
  return;
1639
1629
  }
1640
1630
  const totalSize = getContentSize(ctx);
1641
- const predictedNativeClamp = getPredictedNativeClamp(state, pending.amount, totalSize);
1631
+ const predictedNativeClamp = getPredictedNativeClamp(ctx, pending.amount, totalSize);
1642
1632
  if (Math.abs(predictedNativeClamp) <= MVCP_POSITION_EPSILON) {
1643
1633
  return;
1644
1634
  }
@@ -1672,7 +1662,7 @@ function resolvePendingNativeMVCPAdjust(ctx, newScroll) {
1672
1662
  settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
1673
1663
  return true;
1674
1664
  }
1675
- const expectedNativeClampScroll = Math.max(0, getContentSize(ctx) - state.scrollLength);
1665
+ const expectedNativeClampScroll = getScrollRangeEnd(ctx);
1676
1666
  const distanceToClamp = Math.abs(newScroll - expectedNativeClampScroll);
1677
1667
  const isAtExpectedNativeClamp = distanceToClamp <= NATIVE_END_CLAMP_EPSILON;
1678
1668
  if (isAtExpectedNativeClamp) {
@@ -1821,7 +1811,7 @@ function prepareMVCP(ctx, dataChanged, adjustmentSource) {
1821
1811
  if (diff > 0) {
1822
1812
  diff = Math.max(0, totalSize - state.scroll - state.scrollLength);
1823
1813
  } else {
1824
- const maxScroll = Math.max(0, totalSize - state.scrollLength);
1814
+ const maxScroll = getScrollRangeEnd(ctx, totalSize);
1825
1815
  state.scroll = maxScroll;
1826
1816
  state.scrollPending = maxScroll;
1827
1817
  diff = 0;
@@ -1838,7 +1828,7 @@ function prepareMVCP(ctx, dataChanged, adjustmentSource) {
1838
1828
  const prevSize = scrollingTo.itemSize;
1839
1829
  if (prevSize !== void 0 && newSize !== prevSize) {
1840
1830
  if (scrollingTo.viewPositionFallback) {
1841
- const viewport = state.scrollLength - getContentInsetEnd(ctx);
1831
+ const viewport = state.scrollLength - getContentInsetTop(ctx) - getContentInsetEnd(ctx);
1842
1832
  const previousSpace = viewport - Math.max(0, prevSize - ctx.scrollAxisGap);
1843
1833
  const nextSpace = viewport - Math.max(0, newSize - ctx.scrollAxisGap);
1844
1834
  positionDiff += getViewPositionOffset(
@@ -1863,6 +1853,15 @@ function prepareMVCP(ctx, dataChanged, adjustmentSource) {
1863
1853
  now,
1864
1854
  positionDiff
1865
1855
  });
1856
+ if (Platform.OS === "android" && !dataChanged && scrollTarget === void 0) {
1857
+ const maxScroll = getScrollRangeEnd(ctx);
1858
+ if (state.scroll > maxScroll + MVCP_POSITION_EPSILON) {
1859
+ const targetScroll = Math.max(0, Math.min(state.scroll + positionDiff, maxScroll));
1860
+ state.scroll = maxScroll;
1861
+ state.scrollPending = maxScroll;
1862
+ positionDiff = targetScroll - maxScroll;
1863
+ }
1864
+ }
1866
1865
  if (shouldQueueNativeMVCPAdjust(dataChanged, state, positionDiff, prevTotalSize, prevScroll, scrollTarget)) {
1867
1866
  state.pendingNativeMVCPAdjust = {
1868
1867
  amount: positionDiff,
@@ -2042,27 +2041,31 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2042
2041
  }
2043
2042
 
2044
2043
  // src/core/cancelScroll.ts
2045
- function cancelScroll(ctx, stopAnimation = true, commitAdjust = true) {
2044
+ function cancelScroll(ctx, stopAnimation = true, commitAdjust = true, inputDelta = 0) {
2046
2045
  var _a3, _b;
2047
2046
  const { state } = ctx;
2048
2047
  const observed = getObservedScrollOffset(ctx, peek$(ctx, "scrollAdjustPending") ? state.scrollPending : void 0);
2049
2048
  const wasAnimating = !!((_a3 = state.scrollingTo) == null ? void 0 : _a3.animated) || !!((_b = ctx.scrollRequestTracker) == null ? void 0 : _b.cancel());
2049
+ const stopped = wasAnimating && stopAnimation;
2050
+ const stopOffset = stopped && inputDelta ? clampScrollOffset(ctx, observed + inputDelta) : observed;
2050
2051
  releaseScrollOwners(ctx, true);
2051
- state.scrollPending = observed;
2052
+ state.scrollPending = stopOffset;
2052
2053
  const ignoreScrollFromMVCP = state.ignoreScrollFromMVCP;
2053
2054
  if (peek$(ctx, "scrollAdjustPending")) state.ignoreScrollFromMVCP = void 0;
2054
- updateScroll(ctx, observed, true, { markHasScrolled: false });
2055
+ updateScroll(ctx, stopOffset, true, { markHasScrolled: false });
2055
2056
  state.ignoreScrollFromMVCP = ignoreScrollFromMVCP;
2056
- if (wasAnimating && stopAnimation) stopScrollAnimation(ctx, observed);
2057
+ if (stopped) stopScrollAnimation(ctx, observed, inputDelta);
2057
2058
  if (commitAdjust) state.scrollAdjustHandler.commitPendingAdjust();
2058
2059
  else state.scrollAdjustHandler.supersedePendingAdjust();
2060
+ return stopped;
2059
2061
  }
2060
- function stopScrollAnimation(ctx, observed = getObservedScrollOffset(ctx)) {
2062
+ function stopScrollAnimation(ctx, observed = getObservedScrollOffset(ctx), inputDelta = 0) {
2061
2063
  var _a3, _b;
2062
2064
  const { state } = ctx;
2063
2065
  const scroller = state.refScroller.current;
2064
- 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;
2065
- scroller == null ? void 0 : scroller.scrollTo({ animated: false, x, y: state.props.horizontal ? 0 : observed });
2066
+ const offset = observed + inputDelta;
2067
+ 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;
2068
+ scroller == null ? void 0 : scroller.scrollTo({ animated: false, x, y: state.props.horizontal ? 0 : offset });
2066
2069
  }
2067
2070
  function releaseScrollOwners(ctx, supersedeInitial) {
2068
2071
  var _a3;
@@ -2076,12 +2079,45 @@ function releaseScrollOwners(ctx, supersedeInitial) {
2076
2079
  clearPreservedInitialScrollTarget(state);
2077
2080
  syncScrollActive(ctx);
2078
2081
  }
2079
- function interruptUserScroll(ctx) {
2080
- if (ctx.state.scrollingTo || ctx.state.initialScroll && !ctx.state.didFinishInitialScroll) {
2081
- cancelScroll(ctx, Platform.OS === "web");
2082
- } else {
2083
- releaseScrollOwners(ctx, false);
2082
+ function interruptUserScroll(ctx, inputDelta = 0) {
2083
+ var _a3;
2084
+ if (ctx.state.scrollingTo || ((_a3 = ctx.scrollRequestTracker) == null ? void 0 : _a3.isWaitingToRun()) || ctx.state.initialScroll && !ctx.state.didFinishInitialScroll) {
2085
+ return cancelScroll(ctx, Platform.OS === "web", true, inputDelta) && inputDelta !== 0;
2086
+ }
2087
+ releaseScrollOwners(ctx, false);
2088
+ return false;
2089
+ }
2090
+
2091
+ // src/core/calculateOffsetForIndex.ts
2092
+ function calculateOffsetForIndex(ctx, index) {
2093
+ const state = ctx.state;
2094
+ return index !== void 0 ? state.positions[index] || 0 : 0;
2095
+ }
2096
+
2097
+ // src/utils/clampScrollIndex.ts
2098
+ function clampScrollIndex(index, dataLength) {
2099
+ if (dataLength <= 0) {
2100
+ return -1;
2084
2101
  }
2102
+ if (index >= dataLength) {
2103
+ return dataLength - 1;
2104
+ }
2105
+ if (index < 0) {
2106
+ return 0;
2107
+ }
2108
+ return index;
2109
+ }
2110
+
2111
+ // src/core/getCurrentTargetOffset.ts
2112
+ function isEndAlignedLastItemTarget(ctx, scrollingTo) {
2113
+ return (scrollingTo.isScrollToEnd || scrollingTo.index === ctx.state.props.data.length - 1) && scrollingTo.viewPosition === 1;
2114
+ }
2115
+ function getCurrentTargetOffset(ctx, scrollingTo) {
2116
+ var _a3, _b;
2117
+ const index = scrollingTo.isScrollToEnd ? ctx.state.props.data.length - 1 : scrollingTo.index === void 0 ? void 0 : clampScrollIndex(scrollingTo.index, ctx.state.props.data.length);
2118
+ const isRetargetingWebScroll = Platform.OS === "web" && (!scrollingTo.isInitialScroll || ((_a3 = ctx.state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap");
2119
+ 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);
2120
+ return clampScrollOffset(ctx, requestedTargetOffset, scrollingTo);
2085
2121
  }
2086
2122
 
2087
2123
  // src/core/scrollRequestTracker.ts
@@ -2125,7 +2161,7 @@ function getScrollRequestTracker(ctx) {
2125
2161
  state.scrollAdjustHandler.commitPendingAdjust();
2126
2162
  }
2127
2163
  }
2128
- if (didStartScroll === "unreachable") {
2164
+ if (didStartScroll === "unreachable" || !didStartScroll && state.scrollingTo) {
2129
2165
  cancelScroll(ctx);
2130
2166
  return;
2131
2167
  }
@@ -2166,6 +2202,18 @@ function getScrollRequestTracker(ctx) {
2166
2202
  }
2167
2203
 
2168
2204
  // src/core/doMaintainScrollAtEnd.ts
2205
+ function reissueClampedNativeEndScroll(ctx) {
2206
+ var _a3, _b, _c;
2207
+ const state = ctx.state;
2208
+ const target = state.scrollingTo;
2209
+ if ((target == null ? void 0 : target.isScrollToEnd) && !target.animated && !((_a3 = ctx.scrollRequestTracker) == null ? void 0 : _a3.isWaitingToRun())) {
2210
+ const targetOffset = getCurrentTargetOffset(ctx, target);
2211
+ const distanceToTarget = targetOffset - ((_b = state.lastNativeScroll) != null ? _b : state.scroll);
2212
+ if (distanceToTarget > 1 || state.lastNativeScroll === void 0 && distanceToTarget < -1) {
2213
+ (_c = state.refScroller.current) == null ? void 0 : _c.scrollTo({ animated: false, ...getNativeScrollPoint(ctx, targetOffset) });
2214
+ }
2215
+ }
2216
+ }
2169
2217
  function doMaintainScrollAtEnd(ctx, options) {
2170
2218
  var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
2171
2219
  const state = ctx.state;
@@ -2214,7 +2262,7 @@ function doMaintainScrollAtEnd(ctx, options) {
2214
2262
  return false;
2215
2263
  }
2216
2264
  if (state.scrollingTo || state.pendingScrollResolve) {
2217
- if (immediate && ((_k = state.scrollingTo) == null ? void 0 : _k.isScrollToEnd) && !state.scrollingTo.animated) {
2265
+ if ((options == null ? void 0 : options.immediate) && !maintainScrollAtEnd.animated && ((_k = state.scrollingTo) == null ? void 0 : _k.isScrollToEnd) && !state.scrollingTo.animated) {
2218
2266
  state.pendingMaintainScrollAtEnd = false;
2219
2267
  ctx.scrollToEnd({ animated: false });
2220
2268
  return true;
@@ -2225,7 +2273,7 @@ function doMaintainScrollAtEnd(ctx, options) {
2225
2273
  state.pendingMaintainScrollAtEnd = false;
2226
2274
  const contentSize = getContentSize(ctx);
2227
2275
  if (contentSize < state.scrollLength) {
2228
- state.scroll = 0;
2276
+ state.scroll = clampScrollOffset(ctx, 0);
2229
2277
  }
2230
2278
  if (!state.maintainingScrollAtEnd || immediate && (state.maintainingScrollAtEnd === "pending-instant" || state.maintainingScrollAtEnd === "instant")) {
2231
2279
  const pendingState = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
@@ -2318,20 +2366,6 @@ function finishScrollTo(ctx) {
2318
2366
  }
2319
2367
  }
2320
2368
 
2321
- // src/utils/clampScrollIndex.ts
2322
- function clampScrollIndex(index, dataLength) {
2323
- if (dataLength <= 0) {
2324
- return -1;
2325
- }
2326
- if (index >= dataLength) {
2327
- return dataLength - 1;
2328
- }
2329
- if (index < 0) {
2330
- return 0;
2331
- }
2332
- return index;
2333
- }
2334
-
2335
2369
  // src/core/checkFinishedScroll.ts
2336
2370
  var INITIAL_SCROLL_MAX_FALLBACK_CHECKS = 20;
2337
2371
  var INITIAL_SCROLL_COMPLETION_TARGET_EPSILON = 1;
@@ -2380,16 +2414,6 @@ function shouldFinishInitialZeroTargetScroll(ctx) {
2380
2414
  const { state } = ctx;
2381
2415
  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;
2382
2416
  }
2383
- function isEndAlignedLastItemTarget(ctx, scrollingTo) {
2384
- return (scrollingTo.isScrollToEnd || scrollingTo.index === ctx.state.props.data.length - 1) && scrollingTo.viewPosition === 1;
2385
- }
2386
- function getCurrentTargetOffset(ctx, scrollingTo) {
2387
- var _a3, _b;
2388
- const index = scrollingTo.isScrollToEnd ? ctx.state.props.data.length - 1 : scrollingTo.index === void 0 ? void 0 : clampScrollIndex(scrollingTo.index, ctx.state.props.data.length);
2389
- const isRetargetingWebScroll = Platform.OS === "web" && (!scrollingTo.isInitialScroll || ((_a3 = ctx.state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap");
2390
- 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);
2391
- return clampScrollOffset(ctx, requestedTargetOffset, scrollingTo);
2392
- }
2393
2417
  function getResolvedScrollCompletionState(ctx, scrollingTo) {
2394
2418
  const { state } = ctx;
2395
2419
  const isWebInitialScroll = Platform.OS === "web" && scrollingTo.isInitialScroll;
@@ -2401,9 +2425,10 @@ function getResolvedScrollCompletionState(ctx, scrollingTo) {
2401
2425
  const adjustedTargetOffset = clampedTargetOffset + adjust;
2402
2426
  const diff2 = Math.abs(scroll - adjustedTargetOffset);
2403
2427
  const canUseAdjustedCompletion = !isWebInitialScroll && (!scrollingTo.animated || Platform.OS === "ios");
2428
+ const isWaitingForNativeEnd = Platform.OS !== "web" && scrollingTo.isScrollToEnd && !state.maintainingScrollAtEnd && !scrollingTo.isInitialScroll && !scrollingTo.animated && state.lastNativeScroll !== void 0 && state.lastNativeScroll < Math.min(clampedTargetOffset, adjustedTargetOffset) - 1;
2404
2429
  return {
2405
2430
  clampedTargetOffset,
2406
- isAtResolvedTarget: Math.abs(scroll - maxOffset) < 1 && (diff1 < 1 || canUseAdjustedCompletion && diff2 < 1)
2431
+ isAtResolvedTarget: !isWaitingForNativeEnd && Math.abs(scroll - maxOffset) < 1 && (diff1 < 1 || canUseAdjustedCompletion && diff2 < 1)
2407
2432
  };
2408
2433
  }
2409
2434
  function checkFinishedScrollFrame(ctx) {
@@ -2678,18 +2703,20 @@ function scrollTo(ctx, params) {
2678
2703
  if (forceScroll || !isInitialScroll || Platform.OS === "android") {
2679
2704
  doScrollTo(ctx, { animated, horizontal, isInitialScroll, offset });
2680
2705
  } else {
2681
- state.scroll = offset;
2706
+ state.scroll = targetOffset;
2682
2707
  }
2683
2708
  }
2684
2709
 
2685
2710
  // src/core/initialScroll.ts
2686
2711
  function dispatchInitialScroll(ctx, params) {
2712
+ var _a3;
2687
2713
  const { forceScroll, resolvedOffset, target, waitForCompletionFrame } = params;
2688
2714
  const requestedIndex = target.index;
2689
2715
  const index = requestedIndex !== void 0 ? clampScrollIndex(requestedIndex, ctx.state.props.data.length) : void 0;
2690
2716
  const itemSize = getItemSizeAtIndex(ctx, index);
2691
2717
  scrollTo(ctx, {
2692
2718
  animated: false,
2719
+ clampToContentContainerMinSize: ((_a3 = ctx.state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" || isEndOfContentTarget(target, ctx.state.props.data.length),
2693
2720
  forceScroll,
2694
2721
  index: index !== void 0 && index >= 0 ? index : void 0,
2695
2722
  isInitialScroll: true,
@@ -2722,8 +2749,12 @@ function resolveInitialScrollOffset(ctx, initialScroll) {
2722
2749
  return (_b = initialScroll.contentOffset) != null ? _b : 0;
2723
2750
  }
2724
2751
  const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, initialScroll.index) : 0;
2725
- const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, initialScroll);
2726
- return clampScrollOffset(ctx, resolvedOffset, initialScroll);
2752
+ const target = {
2753
+ ...initialScroll,
2754
+ clampToContentContainerMinSize: isEndOfContentTarget(initialScroll, state.props.data.length)
2755
+ };
2756
+ const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, target);
2757
+ return clampScrollOffset(ctx, resolvedOffset, target);
2727
2758
  }
2728
2759
  function getAdvanceableInitialScrollState(state, options) {
2729
2760
  const { didFinishInitialScroll, queuedInitialLayout, scrollingTo } = state;
@@ -3475,7 +3506,7 @@ function updateSnapToOffsets(ctx) {
3475
3506
  const {
3476
3507
  props: { snapToIndices }
3477
3508
  } = state;
3478
- const contentSize = state.props.horizontal ? getContentSize(ctx) : void 0;
3509
+ const contentSize = state.props.horizontal ? getContentSize(ctx, false) : void 0;
3479
3510
  const snapToOffsets = Array(snapToIndices.length);
3480
3511
  for (let i = 0; i < snapToIndices.length; i++) {
3481
3512
  const idx = snapToIndices[i];
@@ -4534,31 +4565,7 @@ function maybeEmitFirstVisibleItemChanged(state, index) {
4534
4565
  state.lastFirstVisibleItemCallback = { index, key };
4535
4566
  onFirstVisibleItemChanged({ index, item: state.props.data[index], key });
4536
4567
  }
4537
- function findFirstVisibleIndexInCachedRange(ctx, scroll) {
4538
- var _a3, _b;
4539
- const state = ctx.state;
4540
- const {
4541
- endBuffered,
4542
- idCache,
4543
- positions,
4544
- props: { data },
4545
- sizes,
4546
- startBuffered
4547
- } = state;
4548
- if (startBuffered === null || endBuffered === null || startBuffered < 0 || endBuffered < startBuffered) {
4549
- return null;
4550
- }
4551
- for (let i = startBuffered; i <= endBuffered && i < data.length; i++) {
4552
- const id = (_a3 = idCache[i]) != null ? _a3 : getId(state, i);
4553
- const size = (_b = sizes.get(id)) != null ? _b : getItemSize(ctx, id, i, data[i]);
4554
- const top = positions[i];
4555
- if (top + size > scroll) {
4556
- return i;
4557
- }
4558
- }
4559
- return null;
4560
- }
4561
- function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, scrollLength, scroll, scrollBottom) {
4568
+ function updateVisibleRangeForCachedRange(ctx, viewabilityConfigCallbackPairs, scrollLength, scroll, scrollBottom) {
4562
4569
  var _a3, _b, _c, _d;
4563
4570
  const state = ctx.state;
4564
4571
  const {
@@ -4586,14 +4593,11 @@ function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, sc
4586
4593
  break;
4587
4594
  }
4588
4595
  }
4589
- Object.assign(state, {
4590
- endNoBuffer: visibleRange.endNoBuffer,
4591
- firstFullyOnScreenIndex: visibleRange.firstFullyOnScreenIndex,
4592
- idsInView: getIdsInVisibleRange(state, visibleRange),
4593
- startNoBuffer: visibleRange.startNoBuffer
4596
+ Object.assign(state, visibleRange, {
4597
+ idsInView: getIdsInVisibleRange(state, visibleRange)
4594
4598
  });
4595
4599
  maybeEmitFirstVisibleItemChanged(state, visibleRange.startNoBuffer);
4596
- if (state.props.hasExternalScroll || visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null) {
4600
+ if (viewabilityConfigCallbackPairs && (state.props.hasExternalScroll || visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null)) {
4597
4601
  updateViewableItems(
4598
4602
  state,
4599
4603
  ctx,
@@ -4636,7 +4640,8 @@ function calculateItemsInView(ctx, params = {}) {
4636
4640
  if (!data || scrollLength === 0 || !prevNumContainers) {
4637
4641
  return;
4638
4642
  }
4639
- let totalSize = getContentSize(ctx);
4643
+ const includeContentContainerMinSize = Platform.OS === "web" || !isHorizontalRTL(state);
4644
+ let totalSize = getContentSize(ctx, includeContentContainerMinSize);
4640
4645
  let changedContainerIds;
4641
4646
  const topPad = getStartOffsetAdjustment(ctx);
4642
4647
  const numColumns = peek$(ctx, "numColumns");
@@ -4703,8 +4708,9 @@ function calculateItemsInView(ctx, params = {}) {
4703
4708
  const scrollStart = state.props.hasExternalScroll ? scroll : Math.max(0, scroll);
4704
4709
  const overscrollBeforeContent = state.props.hasExternalScroll ? 0 : Math.max(0, -nativeScrollState);
4705
4710
  scrollBottom = Math.max(scrollStart, scroll + scrollLength + overscrollBeforeContent);
4711
+ const unusedTopBuffer = state.props.hasExternalScroll ? 0 : Math.max(0, scrollBufferTop - scroll);
4706
4712
  scrollTopBuffered = scrollStart - scrollBufferTop + projectedBufferAdjustment;
4707
- scrollBottomBuffered = scrollBottom + scrollBufferBottom + projectedBufferAdjustment;
4713
+ scrollBottomBuffered = scrollBottom + scrollBufferBottom + unusedTopBuffer + projectedBufferAdjustment;
4708
4714
  };
4709
4715
  updateScrollRange();
4710
4716
  if (projectedBufferAdjustment !== 0) {
@@ -4716,17 +4722,13 @@ function calculateItemsInView(ctx, params = {}) {
4716
4722
  state.scrollForNextCalculateItemsInView = void 0;
4717
4723
  } else if ((top === null || scrollTopBuffered > top) && (bottom === null || scrollBottomBuffered < bottom)) {
4718
4724
  if (Platform.OS !== "web" || !isInMVCPActiveMode(state)) {
4719
- if (viewabilityConfigCallbackPairs) {
4720
- updateViewabilityForCachedRange(
4721
- ctx,
4722
- viewabilityConfigCallbackPairs,
4723
- scrollLength,
4724
- scroll,
4725
- scrollBottom
4726
- );
4727
- } else if (state.props.onFirstVisibleItemChanged) {
4728
- maybeEmitFirstVisibleItemChanged(state, findFirstVisibleIndexInCachedRange(ctx, scroll));
4729
- }
4725
+ updateVisibleRangeForCachedRange(
4726
+ ctx,
4727
+ viewabilityConfigCallbackPairs,
4728
+ scrollLength,
4729
+ scroll,
4730
+ scrollBottom
4731
+ );
4730
4732
  (_d = stickyState == null ? void 0 : stickyState.finishCalculateItemsInView) == null ? void 0 : _d.call(stickyState);
4731
4733
  return;
4732
4734
  }
@@ -4748,7 +4750,7 @@ function calculateItemsInView(ctx, params = {}) {
4748
4750
  scrollVelocity: speed,
4749
4751
  startIndex
4750
4752
  });
4751
- totalSize = getContentSize(ctx);
4753
+ totalSize = getContentSize(ctx, includeContentContainerMinSize);
4752
4754
  if (positionRecalculationStartIndex !== void 0) {
4753
4755
  state.positionRecalculationStartIndex = void 0;
4754
4756
  }
@@ -4846,14 +4848,11 @@ function calculateItemsInView(ctx, params = {}) {
4846
4848
  }
4847
4849
  }
4848
4850
  }
4849
- Object.assign(state, {
4851
+ Object.assign(state, visibleRange, {
4850
4852
  endBuffered,
4851
- endNoBuffer: visibleRange.endNoBuffer,
4852
- firstFullyOnScreenIndex: visibleRange.firstFullyOnScreenIndex,
4853
4853
  idsInView: getIdsInVisibleRange(state, visibleRange),
4854
4854
  startBuffered,
4855
- startBufferedId,
4856
- startNoBuffer: visibleRange.startNoBuffer
4855
+ startBufferedId
4857
4856
  });
4858
4857
  if (enableScrollForNextCalculateItemsInView && nextTop !== void 0 && nextBottom !== void 0) {
4859
4858
  state.scrollForNextCalculateItemsInView = isNullOrUndefined(nextTop) && isNullOrUndefined(nextBottom) ? void 0 : {
@@ -5116,7 +5115,11 @@ function maybeUpdateAnchoredEndSpace(ctx) {
5116
5115
  contentBelowAnchor = Math.max(0, contentBelowAnchor - ctx.scrollAxisGap);
5117
5116
  contentBelowAnchor += (ctx.values.get("footerSize") || 0) + getStylePaddingEnd(state.props);
5118
5117
  isReady = !hasUnknownTailSize;
5119
- const knownSizeBound = Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
5118
+ const externalEndInset = Platform.OS !== "web" && state.props.anchoredEndSpaceOwner === "list" && !isHorizontalRTL(state) ? getContentInsetEnd(ctx) - (previousSize || 0) : 0;
5119
+ const knownSizeBound = Math.max(
5120
+ 0,
5121
+ state.scrollLength - contentBelowAnchor - anchorOffset - externalEndInset
5122
+ );
5120
5123
  nextSize = hasUnknownTailSize ? Math.min(previousSize || 0, knownSizeBound) : knownSizeBound;
5121
5124
  } else if (anchorIndex >= 0) {
5122
5125
  isReady = false;
@@ -5247,13 +5250,27 @@ function updateItemSizes(ctx, measurement) {
5247
5250
  updateItemSizesBatch(ctx, [measurement]);
5248
5251
  }
5249
5252
  }
5253
+ function isContainerItemCurrent(ctx, containerId, itemKey) {
5254
+ if (peek$(ctx, `containerItemKey${containerId}`) !== itemKey) return false;
5255
+ const state = ctx.state;
5256
+ const index = state.indexByKey.get(itemKey);
5257
+ if (index !== void 0 && index < state.props.data.length && getId(state, index) === itemKey) return true;
5258
+ state.scheduledWork.microtask(
5259
+ () => {
5260
+ var _a3;
5261
+ return (_a3 = ctx.containerLayoutTriggers.get(containerId)) == null ? void 0 : _a3();
5262
+ },
5263
+ `containerMeasurementRetry${containerId}`
5264
+ );
5265
+ return false;
5266
+ }
5250
5267
  function updateItemSizesBatch(ctx, measurements) {
5251
5268
  var _a3;
5252
5269
  const state = ctx.state;
5253
5270
  const result = {};
5254
5271
  const previousTotalSize = state.totalSize;
5255
5272
  for (const measurement of measurements) {
5256
- const ownsMeasuredItem = measurement.containerId === void 0 || peek$(ctx, `containerItemKey${measurement.containerId}`) === measurement.itemKey;
5273
+ const ownsMeasuredItem = measurement.containerId === void 0 || isContainerItemCurrent(ctx, measurement.containerId, measurement.itemKey);
5257
5274
  if (ownsMeasuredItem) {
5258
5275
  const index = state.indexByKey.get(measurement.itemKey);
5259
5276
  const itemData = index === void 0 ? void 0 : (_a3 = state.props.data) == null ? void 0 : _a3[index];
@@ -5406,20 +5423,6 @@ function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem, notif
5406
5423
  }
5407
5424
 
5408
5425
  // src/core/measureContainersInLayoutEffect.native.ts
5409
- function resolveFixedItemSize(ctx, containerId, itemKey) {
5410
- var _a3;
5411
- const state = ctx.state;
5412
- const { data, getFixedItemSize } = state.props;
5413
- const index = state.indexByKey.get(itemKey);
5414
- let fixedItemSize;
5415
- if (data && getFixedItemSize && index !== void 0) {
5416
- const itemData = data[index];
5417
- if (itemData !== void 0) {
5418
- fixedItemSize = (_a3 = resolveContainerItemMetadata(state, containerId, index, itemData)) == null ? void 0 : _a3.fixedItemSize;
5419
- }
5420
- }
5421
- return fixedItemSize;
5422
- }
5423
5426
  function resolveSkippedAnchorReset(ctx, itemKey) {
5424
5427
  const state = ctx.state;
5425
5428
  const anchorReset = state.userScrollAnchorReset;
@@ -5439,7 +5442,7 @@ function revealMeasuredGeneration(ctx, containerId, itemKey, generation) {
5439
5442
  }
5440
5443
  }
5441
5444
  function measureContainersInLayoutEffect(ctx, targetContainerIds = null) {
5442
- var _a3, _b;
5445
+ var _a3, _b, _c;
5443
5446
  const state = ctx.state;
5444
5447
  const measurements = [];
5445
5448
  const measuredGenerations = state.props.hideItemsUntilMeasured ? [] : void 0;
@@ -5451,7 +5454,10 @@ function measureContainersInLayoutEffect(ctx, targetContainerIds = null) {
5451
5454
  if (itemKey !== void 0) {
5452
5455
  const generation = ((_b = state.containerItemGenerations[containerId]) != null ? _b : 0) + 1;
5453
5456
  state.containerItemGenerations[containerId] = generation;
5454
- const fixedItemSize = resolveFixedItemSize(ctx, containerId, itemKey);
5457
+ if (!isContainerItemCurrent(ctx, containerId, itemKey)) continue;
5458
+ const index = state.indexByKey.get(itemKey);
5459
+ const itemData = state.props.data[index];
5460
+ const fixedItemSize = state.props.getFixedItemSize && itemData !== void 0 ? (_c = resolveContainerItemMetadata(state, containerId, index, itemData)) == null ? void 0 : _c.fixedItemSize : void 0;
5455
5461
  const canSkipMeasurement = !state.needsOtherAxisSize && fixedItemSize !== void 0 && state.sizesKnown.get(itemKey) === fixedItemSize + ctx.scrollAxisGap;
5456
5462
  if (canSkipMeasurement) {
5457
5463
  resolveSkippedAnchorReset(ctx, itemKey);
@@ -6413,6 +6419,9 @@ function setContentInsetOverride(ctx, inset) {
6413
6419
  const didChange = !areInsetsEqual(previousInset, nextInset);
6414
6420
  state.contentInsetOverride = nextInset;
6415
6421
  if (didChange) {
6422
+ if (Platform.OS !== "web" && state.props.anchoredEndSpaceOwner === "list") {
6423
+ maybeUpdateAnchoredEndSpace(ctx);
6424
+ }
6416
6425
  updateContentMetricsState(ctx);
6417
6426
  }
6418
6427
  return didChange;
@@ -6571,6 +6580,14 @@ var ListComponent = typedMemo(function ListComponent2({
6571
6580
  },
6572
6581
  [ctx, onScrollToTop]
6573
6582
  );
6583
+ const { onContentSizeChange } = rest;
6584
+ const onContentSizeChangeInternal = useCallback(
6585
+ (width, height) => {
6586
+ reissueClampedNativeEndScroll(ctx);
6587
+ onContentSizeChange == null ? void 0 : onContentSizeChange(width, height);
6588
+ },
6589
+ [ctx, onContentSizeChange]
6590
+ );
6574
6591
  const onLayoutHeader = useCallback(
6575
6592
  (rect) => {
6576
6593
  const size = rect[horizontal ? "width" : "height"];
@@ -6592,6 +6609,7 @@ var ListComponent = typedMemo(function ListComponent2({
6592
6609
  {
6593
6610
  ...rest,
6594
6611
  ...Platform.OS === "web" ? ScrollComponent === ListComponentScrollView ? { onInternalScrollEnd, scrollElement, useWindowScroll } : {} : {
6612
+ onContentSizeChange: onContentSizeChangeInternal,
6595
6613
  onScrollBeginDrag: onInternalScrollBeginDrag,
6596
6614
  onScrollEndDrag: onInternalScrollEndDrag,
6597
6615
  onScrollToTop: onScrollToTopInternal
@@ -6730,6 +6748,7 @@ function checkResetContainers(ctx, dataProp, { didColumnsChange = false } = {})
6730
6748
  state.positionRecalculationStartIndex = 0;
6731
6749
  state.scrollForNextCalculateItemsInView = void 0;
6732
6750
  }
6751
+ state.positionsAreCurrent = false;
6733
6752
  calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
6734
6753
  if ((_a3 = state.pendingEndMeasurements) == null ? void 0 : _a3.size) {
6735
6754
  const removedMeasurements = new Set(state.pendingEndMeasurements);
@@ -6752,9 +6771,6 @@ function checkResetContainers(ctx, dataProp, { didColumnsChange = false } = {})
6752
6771
  if (!didMaintainScrollAtEnd && !state.pendingMaintainScrollAtEnd) {
6753
6772
  state.pendingEndMeasurements = void 0;
6754
6773
  }
6755
- if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
6756
- state.isEndReached = false;
6757
- }
6758
6774
  if (!didMaintainScrollAtEnd) {
6759
6775
  checkThresholds(ctx);
6760
6776
  }
@@ -6877,6 +6893,7 @@ function scrollToIndex(ctx, {
6877
6893
  animated = true,
6878
6894
  forceScroll,
6879
6895
  isInitialScroll,
6896
+ clampToContentContainerMinSize,
6880
6897
  viewPosition,
6881
6898
  viewPositionFallback
6882
6899
  }) {
@@ -6892,6 +6909,7 @@ function scrollToIndex(ctx, {
6892
6909
  state.scrollForNextCalculateItemsInView = void 0;
6893
6910
  scrollTo(ctx, {
6894
6911
  animated,
6912
+ clampToContentContainerMinSize,
6895
6913
  forceScroll,
6896
6914
  index,
6897
6915
  isInitialScroll,
@@ -7011,6 +7029,9 @@ function onScroll(ctx, event, notify = true) {
7011
7029
  if (!prevInset || prevInset.top !== contentInset.top || prevInset.bottom !== contentInset.bottom || prevInset.left !== contentInset.left || prevInset.right !== contentInset.right) {
7012
7030
  state.nativeContentInset = contentInset;
7013
7031
  insetChanged = true;
7032
+ if (Platform.OS !== "web" && state.props.anchoredEndSpaceOwner === "list") {
7033
+ maybeUpdateAnchoredEndSpace(ctx);
7034
+ }
7014
7035
  }
7015
7036
  }
7016
7037
  let newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
@@ -7217,7 +7238,7 @@ var ScrollAdjustHandler = class {
7217
7238
  targetScroll = clampScrollOffset(this.ctx, targetScroll, scrollTarget);
7218
7239
  } else {
7219
7240
  targetScroll = clampScrollOffset(this.ctx, state.scroll + pending - superseded, {
7220
- clampToContentContainerMinSize: scrollTarget == null ? void 0 : scrollTarget.clampToContentContainerMinSize
7241
+ clampToContentContainerMinSize: !scrollTarget || scrollTarget.clampToContentContainerMinSize
7221
7242
  });
7222
7243
  }
7223
7244
  const adjustment = targetScroll - state.scroll;
@@ -7246,6 +7267,7 @@ function scrollToEnd(ctx, options) {
7246
7267
  const footerSize = peek$(ctx, "footerSize") || 0;
7247
7268
  scrollToIndex(ctx, {
7248
7269
  ...options,
7270
+ clampToContentContainerMinSize: true,
7249
7271
  index,
7250
7272
  viewOffset: getEndAlignedViewOffset(paddingBottom, footerSize) + ((options == null ? void 0 : options.viewOffset) || 0),
7251
7273
  viewPosition: 1
@@ -7481,9 +7503,10 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
7481
7503
  const observed = getObservedScrollOffset(ctx);
7482
7504
  const position = calculateOffsetForIndex(ctx, index) + getStartOffsetAdjustment(ctx) + (itemOffset != null ? itemOffset : 0);
7483
7505
  const size = hasRegion ? itemLength != null ? itemLength : 0 : ((_a3 = getItemSizeAtIndex(ctx, index)) != null ? _a3 : 0) - ctx.scrollAxisGap;
7484
- const viewportLength = state.scrollLength - getContentInsetEnd(ctx);
7485
- const viewportStart = observed + ((_b = viewportMargin == null ? void 0 : viewportMargin.start) != null ? _b : 0);
7486
- const viewportEnd = observed + viewportLength - ((_c = viewportMargin == null ? void 0 : viewportMargin.end) != null ? _c : 0);
7506
+ const insetTop = getContentInsetTop(ctx);
7507
+ const viewportLength = state.scrollLength - insetTop - getContentInsetEnd(ctx);
7508
+ const viewportStart = observed + insetTop + ((_b = viewportMargin == null ? void 0 : viewportMargin.start) != null ? _b : 0);
7509
+ const viewportEnd = observed + insetTop + viewportLength - ((_c = viewportMargin == null ? void 0 : viewportMargin.end) != null ? _c : 0);
7487
7510
  if (!hasRegion && position <= viewportStart && position + size >= viewportEnd) return false;
7488
7511
  const above = position < viewportStart;
7489
7512
  const below = position + size > viewportEnd;
@@ -7919,7 +7942,7 @@ var LegendList = typedMemo(
7919
7942
  })
7920
7943
  );
7921
7944
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
7922
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s;
7945
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t;
7923
7946
  useEffect(subscribeScreenReader, []);
7924
7947
  const noopOnScroll = useCallback((_event) => {
7925
7948
  }, []);
@@ -8044,7 +8067,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8044
8067
  const hasInitialScrollIndex = initialScrollIndexProp !== void 0 && initialScrollIndexProp !== null;
8045
8068
  const hasInitialScrollOffset = initialScrollOffsetProp !== void 0 && initialScrollOffsetProp !== null;
8046
8069
  const shouldInitializeHorizontalRTL = !initialScrollAtEnd && !hasInitialScrollIndex && !hasInitialScrollOffset && isHorizontalRTLProps({ horizontal, rtl });
8047
- const initialScrollUsesOffsetOnly = !initialScrollAtEnd && !hasInitialScrollIndex && (hasInitialScrollOffset || shouldInitializeHorizontalRTL);
8070
+ const initialInsetTop = Platform.OS === "ios" && !horizontal ? Math.max(0, (_b = contentInset == null ? void 0 : contentInset.top) != null ? _b : 0) : 0;
8071
+ const initialScrollUsesOffsetOnly = !initialScrollAtEnd && !hasInitialScrollIndex && (hasInitialScrollOffset || shouldInitializeHorizontalRTL || initialInsetTop > 0);
8048
8072
  const usesBootstrapInitialScroll = initialScrollAtEnd || hasInitialScrollIndex;
8049
8073
  const shouldBottomAlignNumericInitialScrollIndex = typeof initialScrollIndexProp === "number" && !hasInitialScrollOffset && dataProp.length > 1 && initialScrollIndexProp >= dataProp.length - 1;
8050
8074
  const initialScrollProp = initialScrollAtEnd ? {
@@ -8053,10 +8077,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8053
8077
  viewOffset: getEndAlignedViewOffset(stylePaddingEndState),
8054
8078
  viewPosition: 1
8055
8079
  } : hasInitialScrollIndex ? typeof initialScrollIndexProp === "object" ? {
8056
- index: (_b = initialScrollIndexProp.index) != null ? _b : 0,
8080
+ index: (_c = initialScrollIndexProp.index) != null ? _c : 0,
8057
8081
  preserveForBottomPadding: initialScrollIndexProp.viewOffset === void 0 && initialScrollIndexProp.viewPosition === 1 && initialScrollIndexProp.viewPositionFallback === void 0 ? true : void 0,
8058
- viewOffset: (_c = initialScrollIndexProp.viewOffset) != null ? _c : initialScrollIndexProp.viewPosition === 1 && initialScrollIndexProp.viewPositionFallback === void 0 ? getEndAlignedViewOffset(stylePaddingEndState) : 0,
8059
- viewPosition: (_d = initialScrollIndexProp.viewPosition) != null ? _d : 0,
8082
+ viewOffset: (_d = initialScrollIndexProp.viewOffset) != null ? _d : initialScrollIndexProp.viewPosition === 1 && initialScrollIndexProp.viewPositionFallback === void 0 ? getEndAlignedViewOffset(stylePaddingEndState) : 0,
8083
+ viewPosition: (_e = initialScrollIndexProp.viewPosition) != null ? _e : 0,
8060
8084
  viewPositionFallback: initialScrollIndexProp.viewPositionFallback
8061
8085
  } : {
8062
8086
  index: initialScrollIndexProp != null ? initialScrollIndexProp : 0,
@@ -8064,7 +8088,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8064
8088
  viewOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : shouldBottomAlignNumericInitialScrollIndex ? getEndAlignedViewOffset(stylePaddingEndState) : 0,
8065
8089
  viewPosition: shouldBottomAlignNumericInitialScrollIndex ? 1 : void 0
8066
8090
  } : initialScrollUsesOffsetOnly ? {
8067
- contentOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : 0,
8091
+ contentOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : -initialInsetTop,
8068
8092
  index: 0,
8069
8093
  viewOffset: 0
8070
8094
  } : void 0;
@@ -8072,7 +8096,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8072
8096
  const [, scheduleImperativeScrollCommit] = React2.useReducer((value) => value + 1, 0);
8073
8097
  const ctx = useStateContext();
8074
8098
  ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
8075
- 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;
8099
+ 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;
8076
8100
  const nextScrollAxisGap = typeof scrollAxisGap === "number" && Number.isFinite(scrollAxisGap) ? scrollAxisGap : 0;
8077
8101
  const refScroller = useRef(null);
8078
8102
  const combinedRef = useCombinedRef(refScroller, refScrollView);
@@ -8087,8 +8111,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8087
8111
  anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex,
8088
8112
  alwaysRender == null ? void 0 : alwaysRender.top,
8089
8113
  alwaysRender == null ? void 0 : alwaysRender.bottom,
8090
- (_k = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _k.join(","),
8091
- (_l = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _l.join(","),
8114
+ (_l = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _l.join(","),
8115
+ (_m = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _m.join(","),
8092
8116
  dataProp,
8093
8117
  dataKey,
8094
8118
  dataVersion,
@@ -8103,7 +8127,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8103
8127
  if (!refState.current) {
8104
8128
  if (!ctx.state) {
8105
8129
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : IsNewArchitecture ? { height: 0, width: 0 } : getWindowSize())[horizontal ? "width" : "height"];
8106
- ctx.values.set("adaptiveRender", (_m = experimental_adaptiveRender == null ? void 0 : experimental_adaptiveRender.initialMode) != null ? _m : "normal");
8130
+ ctx.values.set("adaptiveRender", (_n = experimental_adaptiveRender == null ? void 0 : experimental_adaptiveRender.initialMode) != null ? _n : "normal");
8107
8131
  ctx.state = {
8108
8132
  averageSizes: {},
8109
8133
  columnSpans: [],
@@ -8195,13 +8219,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8195
8219
  const didScrollAxisGapChange = !isFirstLocal && ctx.scrollAxisGap !== nextScrollAxisGap;
8196
8220
  ctx.scrollAxisGap = nextScrollAxisGap;
8197
8221
  state.didColumnsChange = numColumnsProp !== previousNumColumnsProp || didScrollAxisGapChange;
8198
- const previousDataLength = (_o = (_n = state.props.data) == null ? void 0 : _n.length) != null ? _o : 0;
8222
+ const previousDataLength = (_p = (_o = state.props.data) == null ? void 0 : _o.length) != null ? _p : 0;
8199
8223
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
8200
8224
  const didDataKeyChangeLocal = state.props.dataKey !== dataKey;
8201
8225
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
8202
8226
  const didDataChangeLocal = didDataKeyChangeLocal || didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
8203
8227
  const shouldResetFreshDataLayout = !isFirstLocal && didDataChangeLocal && state.hasHadNonEmptyData && (didDataKeyChangeLocal || previousDataLength === 0) && dataProp.length > 0;
8204
- if (didDataChangeLocal && !initialScrollAtEnd && state.didFinishInitialScroll && ((_p = state.initialScroll) == null ? void 0 : _p.viewPosition) === 1 && state.props.data.length > 0) {
8228
+ if (didDataChangeLocal && !initialScrollAtEnd && state.didFinishInitialScroll && ((_q = state.initialScroll) == null ? void 0 : _q.viewPosition) === 1 && state.props.data.length > 0) {
8205
8229
  clearPreservedInitialScrollTarget(state);
8206
8230
  }
8207
8231
  if (didDataChangeLocal) {
@@ -8217,7 +8241,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8217
8241
  const freshDataTransitionEpoch = state.freshDataTransitionEpoch;
8218
8242
  const throttledOnScroll = useThrottledOnScroll(onScrollProp != null ? onScrollProp : noopOnScroll, scrollEventThrottle != null ? scrollEventThrottle : 0);
8219
8243
  const throttleScrollFn = scrollEventThrottle && onScrollProp ? throttledOnScroll : onScrollProp;
8220
- const didAnchoredEndSpaceAnchorIndexChange = !isFirstLocal && !didDataChangeLocal && ((_q = state.props.anchoredEndSpace) == null ? void 0 : _q.anchorIndex) !== (anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex);
8244
+ const didAnchoredEndSpaceAnchorIndexChange = !isFirstLocal && !didDataChangeLocal && ((_r = state.props.anchoredEndSpace) == null ? void 0 : _r.anchorIndex) !== (anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex);
8221
8245
  state.props = {
8222
8246
  adaptiveRender: experimental_adaptiveRender,
8223
8247
  alignItemsAtEnd,
@@ -8302,8 +8326,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8302
8326
  updateContentMetricsState(ctx);
8303
8327
  let paddingDiff = stylePaddingTopState - prevPaddingTop;
8304
8328
  if (shouldAdjustPadding && maintainVisibleContentPositionConfig.size && paddingDiff && prevPaddingTop !== void 0 && Platform.OS === "ios") {
8305
- if (state.scroll < 0) {
8306
- paddingDiff += state.scroll;
8329
+ const distanceFromStart = state.scroll + getContentInsetTop(ctx);
8330
+ if (distanceFromStart < 0) {
8331
+ paddingDiff += distanceFromStart;
8307
8332
  }
8308
8333
  requestAdjust(ctx, paddingDiff);
8309
8334
  }
@@ -8405,6 +8430,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8405
8430
  anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex,
8406
8431
  anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorMaxSize,
8407
8432
  anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorOffset,
8433
+ anchoredEndSpaceOwner,
8434
+ contentInset == null ? void 0 : contentInset.bottom,
8435
+ contentInset == null ? void 0 : contentInset.left,
8436
+ contentInset == null ? void 0 : contentInset.right,
8408
8437
  didAnchoredEndSpaceAnchorIndexChange,
8409
8438
  horizontal,
8410
8439
  numColumnsProp,
@@ -8461,6 +8490,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8461
8490
  useLayoutEffect(
8462
8491
  () => initializeStateVars(true),
8463
8492
  [
8493
+ contentInset == null ? void 0 : contentInset.bottom,
8494
+ contentInset == null ? void 0 : contentInset.top,
8464
8495
  dataKey,
8465
8496
  dataVersion,
8466
8497
  memoizedLastItemKeys.join(","),
@@ -8471,6 +8502,25 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8471
8502
  useAlignItemsAtEndPadding
8472
8503
  ]
8473
8504
  );
8505
+ const previousContentInsetTop = useRef(contentInset == null ? void 0 : contentInset.top);
8506
+ useLayoutEffect(() => {
8507
+ var _a4, _b2;
8508
+ const prevTop = Math.max(0, (_a4 = previousContentInsetTop.current) != null ? _a4 : 0);
8509
+ const didChange = previousContentInsetTop.current !== (contentInset == null ? void 0 : contentInset.top);
8510
+ previousContentInsetTop.current = contentInset == null ? void 0 : contentInset.top;
8511
+ if (didChange && Platform.OS === "ios" && !horizontal && !state.contentInsetOverride && !state.isDragging && !state.isMomentumScrolling) {
8512
+ const offset = -getContentInsetTop(ctx);
8513
+ if (!state.didFinishInitialScroll && !initialScrollAtEnd && !hasInitialScrollIndex && !hasInitialScrollOffset && ((_b2 = state.initialScrollSession) == null ? void 0 : _b2.kind) === "offset" && state.initialScroll && (!state.scrollingTo || state.scrollingTo.isInitialScroll)) {
8514
+ state.initialScroll.contentOffset = offset;
8515
+ advanceCurrentInitialScrollSession(ctx, { forceScroll: true });
8516
+ } else if (state.didFinishInitialScroll && !state.scrollingTo && Math.abs(state.scroll + prevTop) < 1) {
8517
+ scrollTo(ctx, { animated: false, offset });
8518
+ }
8519
+ }
8520
+ }, [contentInset == null ? void 0 : contentInset.top]);
8521
+ useLayoutEffect(() => {
8522
+ checkThresholds(ctx);
8523
+ }, [maintainScrollAtEndThreshold]);
8474
8524
  useLayoutEffect(() => {
8475
8525
  if (!maintainScrollAtEndConfig) {
8476
8526
  finishMaintainScrollAtEnd(ctx);
@@ -8649,7 +8699,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8649
8699
  onScroll: onScrollHandler,
8650
8700
  recycleItems,
8651
8701
  refreshControl: refreshControlElement ? stylePaddingTopState > 0 ? React2.cloneElement(refreshControlElement, {
8652
- progressViewOffset: ((_r = refreshControlElement.props.progressViewOffset) != null ? _r : 0) + stylePaddingTopState
8702
+ progressViewOffset: ((_s = refreshControlElement.props.progressViewOffset) != null ? _s : 0) + stylePaddingTopState
8653
8703
  }) : refreshControlElement : onRefresh && /* @__PURE__ */ React2.createElement(
8654
8704
  RefreshControl,
8655
8705
  {
@@ -8660,7 +8710,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8660
8710
  ),
8661
8711
  refScrollView: combinedRef,
8662
8712
  renderScrollComponent,
8663
- scrollAdjustHandler: (_s = refState.current) == null ? void 0 : _s.scrollAdjustHandler,
8713
+ scrollAdjustHandler: (_t = refState.current) == null ? void 0 : _t.scrollAdjustHandler,
8664
8714
  scrollElement: scrollElementResolved,
8665
8715
  scrollEventThrottle: 0,
8666
8716
  snapToIndices,