@legendapp/list 3.0.0-beta.52 → 3.0.0-beta.54

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/index.native.mjs CHANGED
@@ -139,7 +139,10 @@ function useSelector$(signalName, selector) {
139
139
  }
140
140
 
141
141
  // src/state/getContentInsetEnd.ts
142
- function getContentInsetEnd(ctx) {
142
+ function getContentInsetEndAdjustmentEnd(adjustment) {
143
+ return Math.max(0, adjustment != null ? adjustment : 0);
144
+ }
145
+ function getContentInsetEnd(ctx, contentInsetEndAdjustmentOverride) {
143
146
  var _a3, _b;
144
147
  const state = ctx.state;
145
148
  const { props } = state;
@@ -147,14 +150,21 @@ function getContentInsetEnd(ctx) {
147
150
  const contentInset = props.contentInset;
148
151
  const baseInset = contentInset != null ? contentInset : state.nativeContentInset;
149
152
  const baseEndInset = (horizontal ? baseInset == null ? void 0 : baseInset.right : baseInset == null ? void 0 : baseInset.bottom) || 0;
153
+ const contentInsetEndAdjustment = getContentInsetEndAdjustmentEnd(
154
+ contentInsetEndAdjustmentOverride != null ? contentInsetEndAdjustmentOverride : props.contentInsetEndAdjustment
155
+ );
150
156
  const anchoredEndSpaceSize = peek$(ctx, "anchoredEndSpaceSize");
151
157
  const anchoredEndInset = ((_a3 = props.anchoredEndSpace) == null ? void 0 : _a3.includeInEndInset) && anchoredEndSpaceSize ? anchoredEndSpaceSize : 0;
152
158
  const overrideInset = (_b = state.contentInsetOverride) != null ? _b : void 0;
159
+ const adjustedBaseEndInset = baseEndInset + contentInsetEndAdjustment;
153
160
  if (overrideInset) {
154
161
  const mergedInset = { bottom: 0, right: 0, ...baseInset, ...overrideInset };
155
- return Math.max((horizontal ? mergedInset.right : mergedInset.bottom) || 0, anchoredEndInset);
162
+ return Math.max(
163
+ ((horizontal ? mergedInset.right : mergedInset.bottom) || 0) + contentInsetEndAdjustment,
164
+ anchoredEndInset
165
+ );
156
166
  }
157
- return Math.max(baseEndInset, anchoredEndInset);
167
+ return Math.max(adjustedBaseEndInset, anchoredEndInset);
158
168
  }
159
169
 
160
170
  // src/state/getContentSize.ts
@@ -646,8 +656,52 @@ function isInMVCPActiveMode(state) {
646
656
  }
647
657
 
648
658
  // src/components/Container.tsx
659
+ function getContainerPositionStyle({
660
+ columnWrapperStyle,
661
+ horizontal,
662
+ hasItemSeparator,
663
+ numColumns,
664
+ otherAxisPos,
665
+ otherAxisSize
666
+ }) {
667
+ let paddingStyles;
668
+ if (columnWrapperStyle) {
669
+ const { columnGap, rowGap, gap } = columnWrapperStyle;
670
+ if (horizontal) {
671
+ paddingStyles = {
672
+ paddingBottom: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0,
673
+ paddingRight: columnGap || gap || void 0,
674
+ paddingTop: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
675
+ };
676
+ } else {
677
+ paddingStyles = {
678
+ paddingBottom: rowGap || gap || void 0,
679
+ paddingLeft: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0,
680
+ paddingRight: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
681
+ };
682
+ }
683
+ }
684
+ return horizontal ? {
685
+ boxSizing: paddingStyles ? "border-box" : void 0,
686
+ flexDirection: hasItemSeparator ? "row" : void 0,
687
+ height: otherAxisSize,
688
+ left: 0,
689
+ position: "absolute",
690
+ top: otherAxisPos,
691
+ ...paddingStyles || {}
692
+ } : {
693
+ boxSizing: paddingStyles ? "border-box" : void 0,
694
+ left: otherAxisPos,
695
+ position: "absolute",
696
+ right: numColumns > 1 ? null : 0,
697
+ top: 0,
698
+ width: otherAxisSize,
699
+ ...paddingStyles || {}
700
+ };
701
+ }
649
702
  var Container = typedMemo(function Container2({
650
703
  id,
704
+ itemKey,
651
705
  recycleItems,
652
706
  horizontal,
653
707
  getRenderedItem: getRenderedItem2,
@@ -659,11 +713,10 @@ var Container = typedMemo(function Container2({
659
713
  const { columnWrapperStyle, animatedScrollY } = ctx;
660
714
  const positionComponentInternal = ctx.state.props.positionComponentInternal;
661
715
  const stickyPositionComponentInternal = ctx.state.props.stickyPositionComponentInternal;
662
- const [column = 0, span = 1, data, itemKey, numColumns = 1, extraData, isSticky] = useArr$([
716
+ const [column = 0, span = 1, data, numColumns = 1, extraData, isSticky] = useArr$([
663
717
  `containerColumn${id}`,
664
718
  `containerSpan${id}`,
665
719
  `containerItemData${id}`,
666
- `containerItemKey${id}`,
667
720
  "numColumns",
668
721
  "extraData",
669
722
  `containerSticky${id}`
@@ -684,42 +737,17 @@ var Container = typedMemo(function Container2({
684
737
  const resolvedSpan = Math.min(Math.max(span || 1, 1), numColumns);
685
738
  const otherAxisPos = numColumns > 1 ? `${(resolvedColumn - 1) / numColumns * 100}%` : 0;
686
739
  const otherAxisSize = numColumns > 1 ? `${resolvedSpan / numColumns * 100}%` : void 0;
687
- const style = useMemo(() => {
688
- let paddingStyles;
689
- if (columnWrapperStyle) {
690
- const { columnGap, rowGap, gap } = columnWrapperStyle;
691
- if (horizontal) {
692
- paddingStyles = {
693
- paddingBottom: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0,
694
- paddingRight: columnGap || gap || void 0,
695
- paddingTop: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
696
- };
697
- } else {
698
- paddingStyles = {
699
- paddingBottom: rowGap || gap || void 0,
700
- paddingLeft: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0,
701
- paddingRight: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
702
- };
703
- }
704
- }
705
- return horizontal ? {
706
- boxSizing: paddingStyles ? "border-box" : void 0,
707
- flexDirection: ItemSeparatorComponent ? "row" : void 0,
708
- height: otherAxisSize,
709
- left: 0,
710
- position: "absolute",
711
- top: otherAxisPos,
712
- ...paddingStyles || {}
713
- } : {
714
- boxSizing: paddingStyles ? "border-box" : void 0,
715
- left: otherAxisPos,
716
- position: "absolute",
717
- right: numColumns > 1 ? null : 0,
718
- top: 0,
719
- width: otherAxisSize,
720
- ...paddingStyles || {}
721
- };
722
- }, [horizontal, otherAxisPos, otherAxisSize, columnWrapperStyle, numColumns]);
740
+ const style = useMemo(
741
+ () => getContainerPositionStyle({
742
+ columnWrapperStyle,
743
+ hasItemSeparator: !!ItemSeparatorComponent,
744
+ horizontal,
745
+ numColumns,
746
+ otherAxisPos,
747
+ otherAxisSize
748
+ }),
749
+ [horizontal, otherAxisPos, otherAxisSize, columnWrapperStyle, numColumns, ItemSeparatorComponent]
750
+ );
723
751
  const renderedItemInfo = useMemo(
724
752
  () => itemKey !== void 0 ? getRenderedItem2(itemKey) : null,
725
753
  [itemKey, data, extraData]
@@ -833,6 +861,39 @@ var Container = typedMemo(function Container2({
833
861
  );
834
862
  });
835
863
 
864
+ // src/components/ContainerSlot.tsx
865
+ function ContainerSlotBase({
866
+ id,
867
+ horizontal,
868
+ recycleItems,
869
+ ItemSeparatorComponent,
870
+ updateItemSize: updateItemSize2,
871
+ getRenderedItem: getRenderedItem2,
872
+ stickyHeaderConfig,
873
+ ContainerComponent = Container
874
+ }) {
875
+ const [itemKey] = useArr$([`containerItemKey${id}`]);
876
+ if (itemKey === void 0) {
877
+ return null;
878
+ }
879
+ return /* @__PURE__ */ React2.createElement(
880
+ ContainerComponent,
881
+ {
882
+ getRenderedItem: getRenderedItem2,
883
+ horizontal,
884
+ ItemSeparatorComponent,
885
+ id,
886
+ itemKey,
887
+ recycleItems,
888
+ stickyHeaderConfig,
889
+ updateItemSize: updateItemSize2
890
+ }
891
+ );
892
+ }
893
+ var ContainerSlot = typedMemo(function ContainerSlot2(props) {
894
+ return /* @__PURE__ */ React2.createElement(ContainerSlotBase, { ...props });
895
+ });
896
+
836
897
  // src/components/Containers.native.tsx
837
898
  var ContainersLayer = typedMemo(function ContainersLayer2({ children, horizontal }) {
838
899
  const ctx = useStateContext();
@@ -871,12 +932,12 @@ var Containers = typedMemo(function Containers2({
871
932
  updateItemSize: updateItemSize2,
872
933
  getRenderedItem: getRenderedItem2
873
934
  }) {
874
- const [numContainers] = useArr$(["numContainersPooled"]);
935
+ const [numContainersPooled] = useArr$(["numContainersPooled"]);
875
936
  const containers = [];
876
- for (let i = 0; i < numContainers; i++) {
937
+ for (let i = 0; i < numContainersPooled; i++) {
877
938
  containers.push(
878
939
  /* @__PURE__ */ React2.createElement(
879
- Container,
940
+ ContainerSlot,
880
941
  {
881
942
  getRenderedItem: getRenderedItem2,
882
943
  horizontal,
@@ -1203,10 +1264,9 @@ var initialScrollWatchdog = {
1203
1264
  clear(state) {
1204
1265
  initialScrollWatchdog.set(state, void 0);
1205
1266
  },
1206
- didObserveProgress(newScroll, watchdog) {
1207
- const previousDistance = Math.abs(watchdog.startScroll - watchdog.targetOffset);
1267
+ didReachTarget(newScroll, watchdog) {
1208
1268
  const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
1209
- return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET || nextDistance + INITIAL_SCROLL_MIN_TARGET_OFFSET < previousDistance;
1269
+ return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
1210
1270
  },
1211
1271
  get(state) {
1212
1272
  var _a3, _b;
@@ -1231,19 +1291,19 @@ var initialScrollWatchdog = {
1231
1291
  }
1232
1292
  };
1233
1293
  function setInitialScrollSession(state, options = {}) {
1234
- var _a3, _b, _c;
1294
+ var _a3, _b, _c, _d;
1235
1295
  const existingSession = state.initialScrollSession;
1236
1296
  const kind = (_a3 = options.kind) != null ? _a3 : existingSession == null ? void 0 : existingSession.kind;
1237
1297
  const completion = existingSession == null ? void 0 : existingSession.completion;
1238
- const hasBootstrapOverride = Object.hasOwn(options, "bootstrap");
1239
- const bootstrap = kind === "bootstrap" ? hasBootstrapOverride ? options.bootstrap : (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0 : void 0;
1298
+ const existingBootstrap = (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0;
1299
+ const bootstrap = kind === "bootstrap" ? options.bootstrap === null ? void 0 : (_b = options.bootstrap) != null ? _b : existingBootstrap : void 0;
1240
1300
  if (!kind) {
1241
1301
  return clearInitialScrollSession(state);
1242
1302
  }
1243
1303
  if (!state.initialScroll && !bootstrap && !hasInitialScrollSessionCompletion(completion)) {
1244
1304
  return clearInitialScrollSession(state);
1245
1305
  }
1246
- const previousDataLength = (_c = (_b = options.previousDataLength) != null ? _b : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _c : 0;
1306
+ const previousDataLength = (_d = (_c = options.previousDataLength) != null ? _c : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _d : 0;
1247
1307
  state.initialScrollSession = createInitialScrollSession({
1248
1308
  bootstrap,
1249
1309
  completion,
@@ -1825,7 +1885,7 @@ function checkFinishedScrollFallback(ctx) {
1825
1885
  state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, delay);
1826
1886
  };
1827
1887
  const checkHasScrolled = () => {
1828
- var _a3, _b, _c;
1888
+ var _a3, _b, _c, _d;
1829
1889
  state.timeoutCheckFinishedScrollFallback = void 0;
1830
1890
  const isStillScrollingTo = state.scrollingTo;
1831
1891
  if (isStillScrollingTo) {
@@ -1838,8 +1898,10 @@ function checkFinishedScrollFallback(ctx) {
1838
1898
  isStillScrollingTo
1839
1899
  );
1840
1900
  const completionState = getResolvedScrollCompletionState(ctx, isStillScrollingTo);
1841
- const canFinishAfterSilentNativeDispatch = silentInitialDispatch && completionState.isAtResolvedTarget && numChecks >= 1;
1901
+ const canFinishAfterSilentNativeDispatch = Platform.OS === "android" && silentInitialDispatch && completionState.isAtResolvedTarget && numChecks >= 1;
1842
1902
  const shouldRetrySilentInitialNativeScroll = Platform.OS === "android" && canFinishAfterSilentNativeDispatch && !initialScrollCompletion.didRetrySilentInitialScroll(state);
1903
+ const shouldFinishAfterObservedScroll = state.hasScrolled && (!isStillScrollingTo.isInitialScroll || completionState.isAtResolvedTarget);
1904
+ const shouldRetryUnalignedInitialScroll = isStillScrollingTo.isInitialScroll && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
1843
1905
  if (shouldRetrySilentInitialNativeScroll) {
1844
1906
  const targetOffset = (_b = (_a3 = getInitialScrollWatchdogTargetOffset(state)) != null ? _a3 : isStillScrollingTo.targetOffset) != null ? _b : 0;
1845
1907
  const jiggleOffset = targetOffset >= SILENT_INITIAL_SCROLL_TARGET_EPSILON ? targetOffset - SILENT_INITIAL_SCROLL_TARGET_EPSILON : targetOffset + SILENT_INITIAL_SCROLL_TARGET_EPSILON;
@@ -1849,10 +1911,10 @@ function checkFinishedScrollFallback(ctx) {
1849
1911
  scrollToFallbackOffset(ctx, targetOffset);
1850
1912
  });
1851
1913
  scheduleFallbackCheck(SILENT_INITIAL_SCROLL_RETRY_DELAY_MS);
1852
- } else if (shouldFinishZeroTarget || state.hasScrolled || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
1914
+ } else if (shouldFinishZeroTarget || shouldFinishAfterObservedScroll || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
1853
1915
  finishScrollTo(ctx);
1854
- } else if (isNativeInitialPending && numChecks <= maxChecks) {
1855
- const targetOffset = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : state.scrollPending;
1916
+ } else if ((isNativeInitialPending || shouldRetryUnalignedInitialScroll) && numChecks <= maxChecks) {
1917
+ const targetOffset = (_d = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : isStillScrollingTo.targetOffset) != null ? _d : state.scrollPending;
1856
1918
  scrollToFallbackOffset(ctx, targetOffset);
1857
1919
  scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
1858
1920
  } else {
@@ -2081,7 +2143,7 @@ function advanceMeasuredInitialScroll(ctx, options) {
2081
2143
  const activeInitialTargetOffset = scrollingTo ? (_a3 = scrollingTo.targetOffset) != null ? _a3 : scrollingTo.offset : void 0;
2082
2144
  const didOffsetChange = initialScroll.contentOffset === void 0 || Math.abs(initialScroll.contentOffset - resolvedOffset) > 1;
2083
2145
  const didActiveInitialTargetChange = activeInitialTargetOffset !== void 0 && Math.abs(activeInitialTargetOffset - resolvedOffset) > 1;
2084
- const isAlreadyAtDesiredInitialTarget = activeInitialTargetOffset !== void 0 && Math.abs(state.scroll - activeInitialTargetOffset) <= 1 && Math.abs(state.scrollPending - activeInitialTargetOffset) <= 1;
2146
+ const isAlreadyAtDesiredInitialTarget = activeInitialTargetOffset !== void 0 && Math.abs(state.scroll - resolvedOffset) <= 1 && Math.abs(state.scrollPending - resolvedOffset) <= 1;
2085
2147
  if (!(options == null ? void 0 : options.forceScroll) && !didOffsetChange && isInitialScrollInProgress && !didActiveInitialTargetChange) {
2086
2148
  return false;
2087
2149
  }
@@ -2153,6 +2215,61 @@ function checkAllSizesKnown(state, indices) {
2153
2215
  });
2154
2216
  }
2155
2217
 
2218
+ // src/utils/requestAdjust.ts
2219
+ function requestAdjust(ctx, positionDiff, dataChanged) {
2220
+ const state = ctx.state;
2221
+ if (Math.abs(positionDiff) > 0.1) {
2222
+ const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
2223
+ const doit = () => {
2224
+ if (needsScrollWorkaround) {
2225
+ scrollTo(ctx, {
2226
+ noScrollingTo: true,
2227
+ offset: state.scroll
2228
+ });
2229
+ } else {
2230
+ state.scrollAdjustHandler.requestAdjust(positionDiff);
2231
+ if (state.adjustingFromInitialMount) {
2232
+ state.adjustingFromInitialMount--;
2233
+ }
2234
+ }
2235
+ };
2236
+ state.scroll += positionDiff;
2237
+ state.scrollForNextCalculateItemsInView = void 0;
2238
+ const readyToRender = peek$(ctx, "readyToRender");
2239
+ if (readyToRender) {
2240
+ doit();
2241
+ if (Platform.OS !== "web") {
2242
+ const threshold = state.scroll - positionDiff / 2;
2243
+ if (!state.ignoreScrollFromMVCP) {
2244
+ state.ignoreScrollFromMVCP = {};
2245
+ }
2246
+ if (positionDiff > 0) {
2247
+ state.ignoreScrollFromMVCP.lt = threshold;
2248
+ } else {
2249
+ state.ignoreScrollFromMVCP.gt = threshold;
2250
+ }
2251
+ if (state.ignoreScrollFromMVCPTimeout) {
2252
+ clearTimeout(state.ignoreScrollFromMVCPTimeout);
2253
+ }
2254
+ const delay = needsScrollWorkaround ? 250 : 100;
2255
+ state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
2256
+ var _a3;
2257
+ state.ignoreScrollFromMVCP = void 0;
2258
+ const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
2259
+ if (shouldForceUpdate) {
2260
+ state.ignoreScrollFromMVCPIgnored = false;
2261
+ state.scrollPending = state.scroll;
2262
+ (_a3 = state.reprocessCurrentScroll) == null ? void 0 : _a3.call(state);
2263
+ }
2264
+ }, delay);
2265
+ }
2266
+ } else {
2267
+ state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
2268
+ requestAnimationFrame(doit);
2269
+ }
2270
+ }
2271
+ }
2272
+
2156
2273
  // src/core/bootstrapInitialScroll.ts
2157
2274
  var DEFAULT_BOOTSTRAP_REVEAL_EPSILON = 1;
2158
2275
  var DEFAULT_BOOTSTRAP_REVEAL_MAX_FRAMES = 8;
@@ -2246,7 +2363,7 @@ function clearBootstrapInitialScrollSession(state) {
2246
2363
  bootstrapInitialScroll.frameHandle = void 0;
2247
2364
  }
2248
2365
  setInitialScrollSession(state, {
2249
- bootstrap: void 0,
2366
+ bootstrap: null,
2250
2367
  kind: (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind
2251
2368
  });
2252
2369
  }
@@ -2402,15 +2519,18 @@ function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {
2402
2519
  return;
2403
2520
  }
2404
2521
  if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2405
- if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {
2406
- clearPendingInitialScrollFooterLayout(ctx, {
2407
- dataLength: state.props.data.length,
2408
- stylePaddingBottom: (_b = state.props.stylePaddingBottom) != null ? _b : 0,
2409
- target: initialScroll
2410
- });
2411
- return;
2522
+ const shouldKeepEndTargetAlive = isRetargetableBottomAlignedInitialScrollTarget(initialScroll) && peek$(ctx, "isAtEnd");
2523
+ if (!shouldKeepEndTargetAlive) {
2524
+ if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {
2525
+ clearPendingInitialScrollFooterLayout(ctx, {
2526
+ dataLength: state.props.data.length,
2527
+ stylePaddingBottom: (_b = state.props.stylePaddingBottom) != null ? _b : 0,
2528
+ target: initialScroll
2529
+ });
2530
+ } else {
2531
+ clearFinishedViewportRetargetableInitialScroll(state);
2532
+ }
2412
2533
  }
2413
- clearFinishedViewportRetargetableInitialScroll(state);
2414
2534
  }
2415
2535
  }
2416
2536
  function startBootstrapInitialScrollOnMount(ctx, options) {
@@ -2449,7 +2569,7 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
2449
2569
  }
2450
2570
  const shouldResetDidFinish = !!(state.didFinishInitialScroll && previousDataLength === 0 && dataLength > 0 && initialScroll.index !== void 0);
2451
2571
  const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2452
- const shouldClearFinishedResizePreservation = didDataChange && dataLength > 0 && state.didFinishInitialScroll && !bootstrapInitialScroll && !shouldResetDidFinish;
2572
+ const shouldClearFinishedResizePreservation = !initialScrollAtEnd && didDataChange && dataLength > 0 && state.didFinishInitialScroll && !bootstrapInitialScroll && !shouldResetDidFinish;
2453
2573
  if (shouldClearFinishedResizePreservation) {
2454
2574
  clearPreservedInitialScrollTarget(state);
2455
2575
  return;
@@ -2552,27 +2672,46 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2552
2672
  }
2553
2673
  }
2554
2674
  function handleBootstrapInitialScrollLayoutChange(ctx) {
2675
+ var _a3, _b, _c, _d;
2555
2676
  const state = ctx.state;
2556
2677
  const initialScroll = state.initialScroll;
2557
- if (isOffsetInitialScrollSession(state) || state.props.data.length === 0 || !initialScroll) {
2558
- return;
2559
- }
2560
2678
  const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2561
- if (!bootstrapInitialScroll && initialScroll.viewPosition !== 1) {
2562
- return;
2563
- }
2564
- const didFinishInitialScroll = state.didFinishInitialScroll;
2565
- if (didFinishInitialScroll) {
2566
- setInitialScrollTarget(state, initialScroll, {
2567
- resetDidFinish: true
2568
- });
2569
- state.clearPreservedInitialScrollOnNextFinish = true;
2679
+ if (initialScroll && state.props.data.length > 0 && !isOffsetInitialScrollSession(state) && (bootstrapInitialScroll || initialScroll.viewPosition === 1)) {
2680
+ const resolvedOffset = resolveInitialScrollOffset(ctx, initialScroll);
2681
+ const scrollingTo = ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) ? state.scrollingTo : void 0;
2682
+ if (!bootstrapInitialScroll && (scrollingTo || state.didFinishInitialScroll)) {
2683
+ const currentOffset = scrollingTo ? (_b = scrollingTo.targetOffset) != null ? _b : scrollingTo.offset : getObservedBootstrapInitialScrollOffset(state);
2684
+ const offsetDiff = resolvedOffset - currentOffset;
2685
+ if (Math.abs(offsetDiff) > DEFAULT_BOOTSTRAP_REVEAL_EPSILON) {
2686
+ if (scrollingTo) {
2687
+ const existingWatchdog = initialScrollWatchdog.get(state);
2688
+ scrollingTo.offset = resolvedOffset;
2689
+ scrollingTo.targetOffset = resolvedOffset;
2690
+ state.initialScroll = {
2691
+ ...initialScroll,
2692
+ contentOffset: resolvedOffset
2693
+ };
2694
+ state.hasScrolled = false;
2695
+ initialScrollWatchdog.set(state, {
2696
+ startScroll: (_c = existingWatchdog == null ? void 0 : existingWatchdog.startScroll) != null ? _c : state.scroll,
2697
+ targetOffset: resolvedOffset
2698
+ });
2699
+ }
2700
+ requestAdjust(ctx, offsetDiff);
2701
+ if (state.didFinishInitialScroll) {
2702
+ (_d = state.triggerCalculateItemsInView) == null ? void 0 : _d.call(state, { forceFullItemPositions: true });
2703
+ }
2704
+ }
2705
+ if (state.didFinishInitialScroll) {
2706
+ clearFinishedViewportRetargetableInitialScroll(state);
2707
+ }
2708
+ } else {
2709
+ rearmBootstrapInitialScroll(ctx, {
2710
+ scroll: resolvedOffset,
2711
+ targetIndexSeed: initialScroll.index
2712
+ });
2713
+ }
2570
2714
  }
2571
- rearmBootstrapInitialScroll(ctx, {
2572
- scroll: resolveInitialScrollOffset(ctx, initialScroll),
2573
- seedContentOffset: didFinishInitialScroll && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
2574
- targetIndexSeed: initialScroll.index
2575
- });
2576
2715
  }
2577
2716
  function evaluateBootstrapInitialScroll(ctx) {
2578
2717
  var _a3, _b;
@@ -2677,6 +2816,15 @@ function abortBootstrapInitialScroll(ctx) {
2677
2816
  }
2678
2817
 
2679
2818
  // src/core/initialScrollLifecycle.ts
2819
+ function retargetActiveInitialScrollAtEnd(ctx) {
2820
+ var _a3;
2821
+ const state = ctx.state;
2822
+ const initialScroll = state.initialScroll;
2823
+ if (!initialScroll || state.didFinishInitialScroll || ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" || initialScroll.viewPosition !== 1 || state.props.data.length === 0) {
2824
+ return false;
2825
+ }
2826
+ return advanceCurrentInitialScrollSession(ctx, { forceScroll: true });
2827
+ }
2680
2828
  function handleInitialScrollLayoutReady(ctx) {
2681
2829
  var _a3;
2682
2830
  if (!ctx.state.initialScroll) {
@@ -2750,61 +2898,6 @@ function handleInitialScrollDataChange(ctx, options) {
2750
2898
  advanceCurrentInitialScrollSession(ctx);
2751
2899
  }
2752
2900
 
2753
- // src/utils/requestAdjust.ts
2754
- function requestAdjust(ctx, positionDiff, dataChanged) {
2755
- const state = ctx.state;
2756
- if (Math.abs(positionDiff) > 0.1) {
2757
- const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
2758
- const doit = () => {
2759
- if (needsScrollWorkaround) {
2760
- scrollTo(ctx, {
2761
- noScrollingTo: true,
2762
- offset: state.scroll
2763
- });
2764
- } else {
2765
- state.scrollAdjustHandler.requestAdjust(positionDiff);
2766
- if (state.adjustingFromInitialMount) {
2767
- state.adjustingFromInitialMount--;
2768
- }
2769
- }
2770
- };
2771
- state.scroll += positionDiff;
2772
- state.scrollForNextCalculateItemsInView = void 0;
2773
- const readyToRender = peek$(ctx, "readyToRender");
2774
- if (readyToRender) {
2775
- doit();
2776
- if (Platform.OS !== "web") {
2777
- const threshold = state.scroll - positionDiff / 2;
2778
- if (!state.ignoreScrollFromMVCP) {
2779
- state.ignoreScrollFromMVCP = {};
2780
- }
2781
- if (positionDiff > 0) {
2782
- state.ignoreScrollFromMVCP.lt = threshold;
2783
- } else {
2784
- state.ignoreScrollFromMVCP.gt = threshold;
2785
- }
2786
- if (state.ignoreScrollFromMVCPTimeout) {
2787
- clearTimeout(state.ignoreScrollFromMVCPTimeout);
2788
- }
2789
- const delay = needsScrollWorkaround ? 250 : 100;
2790
- state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
2791
- var _a3;
2792
- state.ignoreScrollFromMVCP = void 0;
2793
- const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
2794
- if (shouldForceUpdate) {
2795
- state.ignoreScrollFromMVCPIgnored = false;
2796
- state.scrollPending = state.scroll;
2797
- (_a3 = state.reprocessCurrentScroll) == null ? void 0 : _a3.call(state);
2798
- }
2799
- }, delay);
2800
- }
2801
- } else {
2802
- state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
2803
- requestAnimationFrame(doit);
2804
- }
2805
- }
2806
- }
2807
-
2808
2901
  // src/core/mvcp.ts
2809
2902
  var MVCP_POSITION_EPSILON = 0.1;
2810
2903
  var MVCP_ANCHOR_LOCK_TTL_MS = 300;
@@ -3088,7 +3181,10 @@ function prepareMVCP(ctx, dataChanged) {
3088
3181
  return;
3089
3182
  }
3090
3183
  if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
3091
- requestAdjust(ctx, positionDiff, dataChanged && mvcpData);
3184
+ const shouldSkipAdjustForMaintainedEnd = state.maintainingScrollAtEnd && peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
3185
+ if (!shouldSkipAdjustForMaintainedEnd) {
3186
+ requestAdjust(ctx, positionDiff, dataChanged && mvcpData);
3187
+ }
3092
3188
  }
3093
3189
  };
3094
3190
  }
@@ -3707,6 +3803,30 @@ function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
3707
3803
  var unstableBatchedUpdates = ReactNative.unstable_batchedUpdates;
3708
3804
  var batchedUpdates = typeof unstableBatchedUpdates === "function" ? unstableBatchedUpdates : (fn) => fn();
3709
3805
 
3806
+ // src/utils/containerPool.ts
3807
+ var MIN_INITIAL_CONTAINER_POOL_SIZE = 32;
3808
+ var MAX_INITIAL_SPARE_CONTAINERS = 64;
3809
+ function getInitialContainerPoolSize(dataLength, numContainers, initialContainerPoolRatio) {
3810
+ if (dataLength <= 0 || numContainers <= 0) {
3811
+ return 0;
3812
+ }
3813
+ const ratioPoolSize = Math.ceil(numContainers * initialContainerPoolRatio);
3814
+ const cappedSparePoolSize = numContainers + MAX_INITIAL_SPARE_CONTAINERS;
3815
+ const targetPoolSize = Math.max(
3816
+ numContainers,
3817
+ Math.min(ratioPoolSize, cappedSparePoolSize),
3818
+ Math.min(dataLength, MIN_INITIAL_CONTAINER_POOL_SIZE)
3819
+ );
3820
+ const maxUsefulPoolSize = Math.max(dataLength, numContainers);
3821
+ return Math.min(maxUsefulPoolSize, targetPoolSize);
3822
+ }
3823
+ function getExpandedContainerPoolSize(dataLength, numContainers) {
3824
+ if (dataLength <= 0 || numContainers <= 0) {
3825
+ return 0;
3826
+ }
3827
+ return Math.min(Math.max(dataLength, numContainers), Math.max(numContainers, Math.ceil(numContainers * 1.5)));
3828
+ }
3829
+
3710
3830
  // src/utils/findAvailableContainers.ts
3711
3831
  function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers, protectedKeys) {
3712
3832
  const numContainers = peek$(ctx, "numContainers");
@@ -3912,7 +4032,7 @@ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentSt
3912
4032
  function calculateItemsInView(ctx, params = {}) {
3913
4033
  const state = ctx.state;
3914
4034
  batchedUpdates(() => {
3915
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
4035
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q;
3916
4036
  const {
3917
4037
  columns,
3918
4038
  columnSpans,
@@ -3959,12 +4079,22 @@ function calculateItemsInView(ctx, params = {}) {
3959
4079
  // current initial-scroll target instead of transient native adjustments.
3960
4080
  resolveInitialScrollOffset(ctx, initialScroll)
3961
4081
  ) : state.scroll;
3962
- const scrollAdjustPending = (_c = peek$(ctx, "scrollAdjustPending")) != null ? _c : 0;
3963
- const scrollAdjustPad = scrollAdjustPending - topPad;
3964
- let scroll = Math.round(scrollState + scrollExtra + scrollAdjustPad);
3965
- if (scroll + scrollLength > totalSize) {
3966
- scroll = Math.max(0, totalSize - scrollLength);
3967
- }
4082
+ let scrollAdjustPending = 0;
4083
+ let scrollAdjustPad = 0;
4084
+ let scroll = 0;
4085
+ let scrollTopBuffered = 0;
4086
+ let scrollBottom = 0;
4087
+ let scrollBottomBuffered = 0;
4088
+ const updateScroll2 = (nextScrollState) => {
4089
+ var _a4;
4090
+ scrollAdjustPending = (_a4 = peek$(ctx, "scrollAdjustPending")) != null ? _a4 : 0;
4091
+ scrollAdjustPad = scrollAdjustPending - topPad;
4092
+ scroll = Math.round(nextScrollState + scrollExtra + scrollAdjustPad);
4093
+ if (scroll + scrollLength > totalSize) {
4094
+ scroll = Math.max(0, totalSize - scrollLength);
4095
+ }
4096
+ };
4097
+ updateScroll2(scrollState);
3968
4098
  const previousStickyIndex = peek$(ctx, "activeStickyIndex");
3969
4099
  const currentStickyIdx = stickyIndicesArr.length > 0 ? findCurrentStickyIndex(stickyIndicesArr, scroll, state) : -1;
3970
4100
  const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyIndicesArr[currentStickyIdx] : -1;
@@ -3980,9 +4110,12 @@ function calculateItemsInView(ctx, params = {}) {
3980
4110
  scrollBufferTop = drawDistance * 1.5;
3981
4111
  scrollBufferBottom = drawDistance * 0.5;
3982
4112
  }
3983
- const scrollTopBuffered = scroll - scrollBufferTop;
3984
- const scrollBottom = scroll + scrollLength + (scroll < 0 ? -scroll : 0);
3985
- const scrollBottomBuffered = scrollBottom + scrollBufferBottom;
4113
+ const updateScrollRange = () => {
4114
+ scrollTopBuffered = scroll - scrollBufferTop;
4115
+ scrollBottom = scroll + scrollLength + (scroll < 0 ? -scroll : 0);
4116
+ scrollBottomBuffered = scrollBottom + scrollBufferBottom;
4117
+ };
4118
+ updateScrollRange();
3986
4119
  if (!suppressInitialScrollSideEffects && !dataChanged && !forceFullItemPositions && scrollForNextCalculateItemsInView) {
3987
4120
  const { top, bottom } = scrollForNextCalculateItemsInView;
3988
4121
  if (top === null && bottom === null) {
@@ -4001,7 +4134,7 @@ function calculateItemsInView(ctx, params = {}) {
4001
4134
  columns.length = 0;
4002
4135
  columnSpans.length = 0;
4003
4136
  }
4004
- const startIndex = forceFullItemPositions || dataChanged ? 0 : (_d = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _d : 0;
4137
+ const startIndex = forceFullItemPositions || dataChanged ? 0 : (_c = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _c : 0;
4005
4138
  const optimizeForVisibleWindow = !forceFullItemPositions && !dataChanged && numColumns > 1 && minIndexSizeChanged !== void 0;
4006
4139
  updateItemPositions(ctx, dataChanged, {
4007
4140
  doMVCP,
@@ -4026,21 +4159,25 @@ function calculateItemsInView(ctx, params = {}) {
4026
4159
  }
4027
4160
  }
4028
4161
  const scrollBeforeMVCP = state.scroll;
4029
- const scrollAdjustPendingBeforeMVCP = (_e = peek$(ctx, "scrollAdjustPending")) != null ? _e : 0;
4162
+ const scrollAdjustPendingBeforeMVCP = (_d = peek$(ctx, "scrollAdjustPending")) != null ? _d : 0;
4030
4163
  checkMVCP == null ? void 0 : checkMVCP();
4031
- const didMVCPAdjustScroll = !!checkMVCP && (state.scroll !== scrollBeforeMVCP || ((_f = peek$(ctx, "scrollAdjustPending")) != null ? _f : 0) !== scrollAdjustPendingBeforeMVCP);
4164
+ const didMVCPAdjustScroll = !!checkMVCP && (state.scroll !== scrollBeforeMVCP || ((_e = peek$(ctx, "scrollAdjustPending")) != null ? _e : 0) !== scrollAdjustPendingBeforeMVCP);
4165
+ if (didMVCPAdjustScroll && initialScroll) {
4166
+ updateScroll2(state.scroll);
4167
+ updateScrollRange();
4168
+ }
4032
4169
  let startNoBuffer = null;
4033
4170
  let startBuffered = null;
4034
4171
  let startBufferedId = null;
4035
4172
  let endNoBuffer = null;
4036
4173
  let endBuffered = null;
4037
- let loopStart = (_g = suppressInitialScrollSideEffects ? bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.targetIndexSeed : void 0) != null ? _g : !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
4174
+ let loopStart = (_f = suppressInitialScrollSideEffects ? bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.targetIndexSeed : void 0) != null ? _f : !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
4038
4175
  for (let i = loopStart; i >= 0; i--) {
4039
- const id = (_h = idCache[i]) != null ? _h : getId(state, i);
4176
+ const id = (_g = idCache[i]) != null ? _g : getId(state, i);
4040
4177
  const top = positions[i];
4041
- const size = (_i = sizes.get(id)) != null ? _i : getItemSize(ctx, id, i, data[i]);
4178
+ const size = (_h = sizes.get(id)) != null ? _h : getItemSize(ctx, id, i, data[i]);
4042
4179
  const bottom = top + size;
4043
- if (bottom > scroll - scrollBufferTop) {
4180
+ if (bottom > scrollTopBuffered) {
4044
4181
  loopStart = i;
4045
4182
  } else {
4046
4183
  break;
@@ -4069,8 +4206,8 @@ function calculateItemsInView(ctx, params = {}) {
4069
4206
  let firstFullyOnScreenIndex;
4070
4207
  const dataLength = data.length;
4071
4208
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
4072
- const id = (_j = idCache[i]) != null ? _j : getId(state, i);
4073
- const size = (_k = sizes.get(id)) != null ? _k : getItemSize(ctx, id, i, data[i]);
4209
+ const id = (_i = idCache[i]) != null ? _i : getId(state, i);
4210
+ const size = (_j = sizes.get(id)) != null ? _j : getItemSize(ctx, id, i, data[i]);
4074
4211
  const top = positions[i];
4075
4212
  if (!foundEnd) {
4076
4213
  if (startNoBuffer === null && top + size > scroll) {
@@ -4109,7 +4246,7 @@ function calculateItemsInView(ctx, params = {}) {
4109
4246
  const firstVisibleAnchorIndex = firstFullyOnScreenIndex != null ? firstFullyOnScreenIndex : startNoBuffer;
4110
4247
  if (firstVisibleAnchorIndex !== null && firstVisibleAnchorIndex !== void 0 && endNoBuffer !== null) {
4111
4248
  for (let i = firstVisibleAnchorIndex; i <= endNoBuffer; i++) {
4112
- const id = (_l = idCache[i]) != null ? _l : getId(state, i);
4249
+ const id = (_k = idCache[i]) != null ? _k : getId(state, i);
4113
4250
  idsInView.push(id);
4114
4251
  }
4115
4252
  }
@@ -4142,7 +4279,7 @@ function calculateItemsInView(ctx, params = {}) {
4142
4279
  const needNewContainers = [];
4143
4280
  const needNewContainersSet = /* @__PURE__ */ new Set();
4144
4281
  for (let i = startBuffered; i <= endBuffered; i++) {
4145
- const id = (_m = idCache[i]) != null ? _m : getId(state, i);
4282
+ const id = (_l = idCache[i]) != null ? _l : getId(state, i);
4146
4283
  if (!containerItemKeys.has(id)) {
4147
4284
  needNewContainersSet.add(i);
4148
4285
  needNewContainers.push(i);
@@ -4151,7 +4288,7 @@ function calculateItemsInView(ctx, params = {}) {
4151
4288
  if (alwaysRenderArr.length > 0) {
4152
4289
  for (const index of alwaysRenderArr) {
4153
4290
  if (index < 0 || index >= dataLength) continue;
4154
- const id = (_n = idCache[index]) != null ? _n : getId(state, index);
4291
+ const id = (_m = idCache[index]) != null ? _m : getId(state, index);
4155
4292
  if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
4156
4293
  needNewContainersSet.add(index);
4157
4294
  needNewContainers.push(index);
@@ -4190,7 +4327,7 @@ function calculateItemsInView(ctx, params = {}) {
4190
4327
  for (let idx = 0; idx < needNewContainers.length; idx++) {
4191
4328
  const i = needNewContainers[idx];
4192
4329
  const containerIndex = availableContainers[idx];
4193
- const id = (_o = idCache[i]) != null ? _o : getId(state, i);
4330
+ const id = (_n = idCache[i]) != null ? _n : getId(state, i);
4194
4331
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
4195
4332
  if (oldKey && oldKey !== id) {
4196
4333
  containerItemKeys.delete(oldKey);
@@ -4201,7 +4338,7 @@ function calculateItemsInView(ctx, params = {}) {
4201
4338
  state.containerItemTypes.set(containerIndex, requiredItemTypes[idx]);
4202
4339
  }
4203
4340
  containerItemKeys.set(id, containerIndex);
4204
- (_p = state.userScrollAnchorResetKeys) == null ? void 0 : _p.add(id);
4341
+ (_o = state.userScrollAnchorResetKeys) == null ? void 0 : _o.add(id);
4205
4342
  const containerSticky = `containerSticky${containerIndex}`;
4206
4343
  const isSticky = stickyIndicesSet.has(i);
4207
4344
  const isAlwaysRender = alwaysRenderSet.has(i);
@@ -4225,17 +4362,17 @@ function calculateItemsInView(ctx, params = {}) {
4225
4362
  if (numContainers !== prevNumContainers) {
4226
4363
  set$(ctx, "numContainers", numContainers);
4227
4364
  if (numContainers > peek$(ctx, "numContainersPooled")) {
4228
- set$(ctx, "numContainersPooled", Math.ceil(numContainers * 1.5));
4365
+ set$(ctx, "numContainersPooled", getExpandedContainerPoolSize(dataLength, numContainers));
4229
4366
  }
4230
4367
  }
4231
4368
  }
4232
- if (((_q = state.userScrollAnchorResetKeys) == null ? void 0 : _q.size) === 0) {
4369
+ if (((_p = state.userScrollAnchorResetKeys) == null ? void 0 : _p.size) === 0) {
4233
4370
  state.userScrollAnchorResetKeys = void 0;
4234
4371
  }
4235
4372
  if (alwaysRenderArr.length > 0) {
4236
4373
  for (const index of alwaysRenderArr) {
4237
4374
  if (index < 0 || index >= dataLength) continue;
4238
- const id = (_r = idCache[index]) != null ? _r : getId(state, index);
4375
+ const id = (_q = idCache[index]) != null ? _q : getId(state, index);
4239
4376
  const containerIndex = containerItemKeys.get(id);
4240
4377
  if (containerIndex !== void 0) {
4241
4378
  state.stickyContainerPool.add(containerIndex);
@@ -4339,21 +4476,25 @@ function doMaintainScrollAtEnd(ctx) {
4339
4476
  if (contentSize < state.scrollLength) {
4340
4477
  state.scroll = 0;
4341
4478
  }
4342
- requestAnimationFrame(() => {
4343
- var _a3;
4344
- if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
4345
- state.maintainingScrollAtEnd = true;
4346
- (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
4347
- animated: maintainScrollAtEnd.animated
4348
- });
4349
- setTimeout(
4350
- () => {
4351
- state.maintainingScrollAtEnd = false;
4352
- },
4353
- maintainScrollAtEnd.animated ? 500 : 0
4354
- );
4355
- }
4356
- });
4479
+ if (!state.maintainingScrollAtEnd) {
4480
+ state.maintainingScrollAtEnd = true;
4481
+ requestAnimationFrame(() => {
4482
+ var _a3;
4483
+ if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
4484
+ (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
4485
+ animated: maintainScrollAtEnd.animated
4486
+ });
4487
+ setTimeout(
4488
+ () => {
4489
+ state.maintainingScrollAtEnd = false;
4490
+ },
4491
+ maintainScrollAtEnd.animated ? 500 : 0
4492
+ );
4493
+ } else {
4494
+ state.maintainingScrollAtEnd = false;
4495
+ }
4496
+ });
4497
+ }
4357
4498
  return true;
4358
4499
  }
4359
4500
  return false;
@@ -4465,14 +4606,21 @@ function doInitialAllocateContainers(ctx) {
4465
4606
  } else {
4466
4607
  averageItemSize = estimatedItemSize;
4467
4608
  }
4468
- const numContainers = Math.ceil((scrollLength + drawDistance * 2) / averageItemSize * numColumns);
4609
+ const numContainers = Math.max(
4610
+ 1,
4611
+ Math.ceil((scrollLength + drawDistance * 2) / averageItemSize * numColumns)
4612
+ );
4469
4613
  for (let i = 0; i < numContainers; i++) {
4470
4614
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
4471
4615
  set$(ctx, `containerColumn${i}`, -1);
4472
4616
  set$(ctx, `containerSpan${i}`, 1);
4473
4617
  }
4474
4618
  set$(ctx, "numContainers", numContainers);
4475
- set$(ctx, "numContainersPooled", numContainers * state.props.initialContainerPoolRatio);
4619
+ set$(
4620
+ ctx,
4621
+ "numContainersPooled",
4622
+ getInitialContainerPoolSize(data.length, numContainers, state.props.initialContainerPoolRatio)
4623
+ );
4476
4624
  if (!IsNewArchitecture || state.lastLayout) {
4477
4625
  if (state.initialScroll) {
4478
4626
  requestAnimationFrame(() => {
@@ -4624,8 +4772,8 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
4624
4772
  // src/core/onScroll.ts
4625
4773
  function trackInitialScrollNativeProgress(state, newScroll) {
4626
4774
  const initialNativeScrollWatchdog = initialScrollWatchdog.get(state);
4627
- const didInitialScrollProgress = !!initialNativeScrollWatchdog && initialScrollWatchdog.didObserveProgress(newScroll, initialNativeScrollWatchdog);
4628
- if (didInitialScrollProgress) {
4775
+ const didInitialScrollReachTarget = !!initialNativeScrollWatchdog && initialScrollWatchdog.didReachTarget(newScroll, initialNativeScrollWatchdog);
4776
+ if (didInitialScrollReachTarget) {
4629
4777
  initialScrollWatchdog.clear(state);
4630
4778
  return;
4631
4779
  }
@@ -4763,16 +4911,20 @@ function maybeUpdateAnchoredEndSpace(ctx) {
4763
4911
  let contentBelowAnchor = 0;
4764
4912
  const footerSize = ctx.values.get("footerSize") || 0;
4765
4913
  const stylePaddingBottom = state.props.stylePaddingBottom || 0;
4914
+ let hasUnknownTailSize = false;
4766
4915
  for (let index = anchorIndex; index < data.length; index++) {
4767
4916
  const itemKey = getId(state, index);
4768
4917
  const size = itemKey ? state.sizesKnown.get(itemKey) : void 0;
4769
4918
  const effectiveSize = index === anchorIndex && anchorMaxSize !== void 0 ? Math.min(size || 0, Math.max(0, anchorMaxSize)) : size;
4919
+ if (size === void 0) {
4920
+ hasUnknownTailSize = true;
4921
+ }
4770
4922
  if (effectiveSize !== null && effectiveSize !== void 0 && effectiveSize > 0) {
4771
4923
  contentBelowAnchor += effectiveSize;
4772
4924
  }
4773
4925
  }
4774
4926
  contentBelowAnchor += footerSize + stylePaddingBottom;
4775
- nextSize = Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
4927
+ nextSize = hasUnknownTailSize ? previousSize || 0 : Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
4776
4928
  }
4777
4929
  }
4778
4930
  if (previousSize !== nextSize) {
@@ -4785,6 +4937,22 @@ function maybeUpdateAnchoredEndSpace(ctx) {
4785
4937
  return nextSize;
4786
4938
  }
4787
4939
 
4940
+ // src/core/updateContentInsetEndAdjustment.ts
4941
+ function updateContentInsetEndAdjustment(ctx, previousContentInsetEndAdjustment) {
4942
+ const state = ctx.state;
4943
+ const previousContentInsetEnd = getContentInsetEnd(ctx, previousContentInsetEndAdjustment);
4944
+ const nextContentInsetEnd = getContentInsetEnd(ctx);
4945
+ const insetDiff = nextContentInsetEnd - previousContentInsetEnd;
4946
+ if (insetDiff !== 0) {
4947
+ const wasWithinEndThreshold = !!peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
4948
+ updateScroll(ctx, state.scroll, true, { markHasScrolled: false });
4949
+ const didRetargetInitialScroll = retargetActiveInitialScrollAtEnd(ctx);
4950
+ if (!didRetargetInitialScroll && wasWithinEndThreshold && (Platform.OS !== "web" || insetDiff > 0)) {
4951
+ requestAdjust(ctx, insetDiff);
4952
+ }
4953
+ }
4954
+ }
4955
+
4788
4956
  // src/core/updateItemSize.ts
4789
4957
  function runOrScheduleMVCPRecalculate(ctx) {
4790
4958
  const state = ctx.state;
@@ -4830,15 +4998,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
4830
4998
  const {
4831
4999
  didContainersLayout,
4832
5000
  sizesKnown,
4833
- props: {
4834
- getFixedItemSize,
4835
- getItemType,
4836
- horizontal,
4837
- suggestEstimatedItemSize,
4838
- onItemSizeChanged,
4839
- data,
4840
- maintainScrollAtEnd
4841
- }
5001
+ props: { getFixedItemSize, getItemType, horizontal, onItemSizeChanged, data, maintainScrollAtEnd }
4842
5002
  } = state;
4843
5003
  if (!data) return;
4844
5004
  const index = state.indexByKey.get(itemKey);
@@ -4889,18 +5049,6 @@ function updateItemSize(ctx, itemKey, sizeObj) {
4889
5049
  if (minIndexSizeChanged !== void 0) {
4890
5050
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
4891
5051
  }
4892
- if (IS_DEV && suggestEstimatedItemSize && minIndexSizeChanged !== void 0) {
4893
- if (state.timeoutSizeMessage) clearTimeout(state.timeoutSizeMessage);
4894
- state.timeoutSizeMessage = setTimeout(() => {
4895
- var _a4;
4896
- state.timeoutSizeMessage = void 0;
4897
- const num = state.sizesKnown.size;
4898
- const avg = (_a4 = state.averageSizes[""]) == null ? void 0 : _a4.avg;
4899
- console.warn(
4900
- `[legend-list] Based on the ${num} items rendered so far, the optimal estimated size is ${avg}.`
4901
- );
4902
- }, 1e3);
4903
- }
4904
5052
  const cur = peek$(ctx, "otherAxisSize");
4905
5053
  if (!cur || maxOtherAxisSize > cur) {
4906
5054
  set$(ctx, "otherAxisSize", maxOtherAxisSize);
@@ -5018,12 +5166,47 @@ function createColumnWrapperStyle(contentContainerStyle) {
5018
5166
  }
5019
5167
 
5020
5168
  // src/utils/createImperativeHandle.ts
5169
+ var DEFAULT_AVERAGE_ITEM_SIZE_TYPE = "default";
5170
+ function getAverageItemSizes(state) {
5171
+ const averageItemSizes = {};
5172
+ for (const itemType in state.averageSizes) {
5173
+ const averageSize = state.averageSizes[itemType];
5174
+ if (averageSize) {
5175
+ averageItemSizes[itemType || DEFAULT_AVERAGE_ITEM_SIZE_TYPE] = {
5176
+ average: averageSize.avg,
5177
+ count: averageSize.num
5178
+ };
5179
+ }
5180
+ }
5181
+ return averageItemSizes;
5182
+ }
5021
5183
  function createImperativeHandle(ctx) {
5022
5184
  const state = ctx.state;
5023
5185
  const IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS = 800;
5024
5186
  const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
5025
5187
  let imperativeScrollToken = 0;
5026
5188
  const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0;
5189
+ const isScrollToIndexReady = (targetIndex, allowEmpty = false) => {
5190
+ var _a3;
5191
+ const props = state.props;
5192
+ const dataLength = props.data.length;
5193
+ const anchorIndex = (_a3 = props.anchoredEndSpace) == null ? void 0 : _a3.anchorIndex;
5194
+ if (targetIndex < 0) {
5195
+ return allowEmpty;
5196
+ }
5197
+ if (targetIndex >= dataLength) {
5198
+ return false;
5199
+ }
5200
+ if (anchorIndex === void 0 || anchorIndex < 0 || anchorIndex >= dataLength || targetIndex < anchorIndex || props.getFixedItemSize) {
5201
+ return true;
5202
+ }
5203
+ for (let index = anchorIndex; index < dataLength; index++) {
5204
+ if (!state.sizesKnown.has(getId(state, index))) {
5205
+ return false;
5206
+ }
5207
+ }
5208
+ return true;
5209
+ };
5027
5210
  const runWhenReady = (token, run, isReady) => {
5028
5211
  const startedAt = Date.now();
5029
5212
  let stableFrames = 0;
@@ -5045,11 +5228,10 @@ function createImperativeHandle(ctx) {
5045
5228
  };
5046
5229
  requestAnimationFrame(check);
5047
5230
  };
5048
- const runScrollWithPromise = (run, options) => new Promise((resolve) => {
5049
- var _a3, _b;
5231
+ const runScrollWithPromise = (run, isReady = () => true) => new Promise((resolve) => {
5232
+ var _a3;
5050
5233
  const token = ++imperativeScrollToken;
5051
- const isReady = (_a3 = options == null ? void 0 : options.isReady) != null ? _a3 : (() => true);
5052
- (_b = state.pendingScrollResolve) == null ? void 0 : _b.call(state);
5234
+ (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
5053
5235
  state.pendingScrollResolve = resolve;
5054
5236
  const runNow = () => {
5055
5237
  if (token !== imperativeScrollToken) {
@@ -5124,6 +5306,7 @@ function createImperativeHandle(ctx) {
5124
5306
  },
5125
5307
  end: state.endNoBuffer,
5126
5308
  endBuffered: state.endBuffered,
5309
+ getAverageItemSizes: () => getAverageItemSizes(state),
5127
5310
  isAtEnd: peek$(ctx, "isAtEnd"),
5128
5311
  isAtStart: peek$(ctx, "isAtStart"),
5129
5312
  isEndReached: state.isEndReached,
@@ -5147,8 +5330,14 @@ function createImperativeHandle(ctx) {
5147
5330
  startBuffered: state.startBuffered
5148
5331
  }),
5149
5332
  reportContentInset: (inset) => {
5333
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
5334
+ const previousInset = state.contentInsetOverride;
5150
5335
  state.contentInsetOverride = inset != null ? inset : void 0;
5336
+ const didChange = ((_a3 = previousInset == null ? void 0 : previousInset.top) != null ? _a3 : 0) !== ((_c = (_b = state.contentInsetOverride) == null ? void 0 : _b.top) != null ? _c : 0) || ((_d = previousInset == null ? void 0 : previousInset.bottom) != null ? _d : 0) !== ((_f = (_e = state.contentInsetOverride) == null ? void 0 : _e.bottom) != null ? _f : 0) || ((_g = previousInset == null ? void 0 : previousInset.left) != null ? _g : 0) !== ((_i = (_h = state.contentInsetOverride) == null ? void 0 : _h.left) != null ? _i : 0) || ((_j = previousInset == null ? void 0 : previousInset.right) != null ? _j : 0) !== ((_l = (_k = state.contentInsetOverride) == null ? void 0 : _k.right) != null ? _l : 0);
5151
5337
  updateScroll(ctx, state.scroll, true, { markHasScrolled: false });
5338
+ if (didChange) {
5339
+ retargetActiveInitialScrollAtEnd(ctx);
5340
+ }
5152
5341
  },
5153
5342
  scrollIndexIntoView: (options) => runScrollWithPromise(() => scrollIndexIntoView(options)),
5154
5343
  scrollItemIntoView: ({ item, ...props }) => runScrollWithPromise(() => {
@@ -5160,40 +5349,34 @@ function createImperativeHandle(ctx) {
5160
5349
  }
5161
5350
  return false;
5162
5351
  }),
5163
- scrollToEnd: (options) => runScrollWithPromise(() => {
5164
- const data = state.props.data;
5165
- const stylePaddingBottom = state.props.stylePaddingBottom;
5166
- const index = data.length - 1;
5167
- if (index !== -1) {
5168
- const paddingBottom = stylePaddingBottom || 0;
5169
- const footerSize = peek$(ctx, "footerSize") || 0;
5170
- scrollToIndex(ctx, {
5171
- ...options,
5172
- index,
5173
- viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
5174
- viewPosition: 1
5175
- });
5176
- return true;
5177
- }
5178
- return false;
5179
- }),
5180
- scrollToIndex: (params) => {
5181
- const shouldWaitForOutOfRangeTarget = params.index >= 0 && params.index >= state.props.data.length;
5182
- const options = shouldWaitForOutOfRangeTarget ? {
5183
- isReady: () => {
5184
- var _a3;
5185
- const props = state.props;
5186
- const anchorIndex = (_a3 = props.anchoredEndSpace) == null ? void 0 : _a3.anchorIndex;
5187
- const lastIndex = props.data.length - 1;
5188
- const isInRange = params.index < props.data.length;
5189
- const shouldWaitForAnchorSize = isInRange && anchorIndex !== void 0 && anchorIndex >= 0 && params.index >= anchorIndex && !props.getFixedItemSize && !state.sizesKnown.has(getId(state, lastIndex));
5190
- return isInRange && !shouldWaitForAnchorSize;
5352
+ scrollToEnd: (options) => runScrollWithPromise(
5353
+ () => {
5354
+ const data = state.props.data;
5355
+ const stylePaddingBottom = state.props.stylePaddingBottom;
5356
+ const index = data.length - 1;
5357
+ if (index !== -1) {
5358
+ const paddingBottom = stylePaddingBottom || 0;
5359
+ const footerSize = peek$(ctx, "footerSize") || 0;
5360
+ scrollToIndex(ctx, {
5361
+ ...options,
5362
+ index,
5363
+ viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
5364
+ viewPosition: 1
5365
+ });
5366
+ return true;
5191
5367
  }
5192
- } : void 0;
5193
- return runScrollWithPromise(() => {
5194
- scrollToIndex(ctx, params);
5195
- return true;
5196
- }, options);
5368
+ return false;
5369
+ },
5370
+ () => isScrollToIndexReady(state.props.data.length - 1, true)
5371
+ ),
5372
+ scrollToIndex: (params) => {
5373
+ return runScrollWithPromise(
5374
+ () => {
5375
+ scrollToIndex(ctx, params);
5376
+ return true;
5377
+ },
5378
+ params.index >= 0 ? () => isScrollToIndexReady(params.index) : void 0
5379
+ );
5197
5380
  },
5198
5381
  scrollToItem: ({ item, ...props }) => runScrollWithPromise(() => {
5199
5382
  const data = state.props.data;
@@ -5449,6 +5632,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5449
5632
  data: dataProp = [],
5450
5633
  dataVersion,
5451
5634
  drawDistance = 250,
5635
+ contentInsetEndAdjustment,
5452
5636
  estimatedItemSize = 100,
5453
5637
  estimatedListSize,
5454
5638
  extraData,
@@ -5456,7 +5640,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5456
5640
  getFixedItemSize,
5457
5641
  getItemType,
5458
5642
  horizontal,
5459
- initialContainerPoolRatio = 2,
5643
+ initialContainerPoolRatio = 3,
5460
5644
  initialScrollAtEnd = false,
5461
5645
  initialScrollIndex: initialScrollIndexProp,
5462
5646
  initialScrollOffset: initialScrollOffsetProp,
@@ -5497,7 +5681,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5497
5681
  stickyIndices: stickyIndicesDeprecated,
5498
5682
  // TODOV3: Remove from v3 release
5499
5683
  style: styleProp,
5500
- suggestEstimatedItemSize,
5501
5684
  useWindowScroll = false,
5502
5685
  viewabilityConfig,
5503
5686
  viewabilityConfigCallbackPairs,
@@ -5558,6 +5741,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5558
5741
  const combinedRef = useCombinedRef(refScroller, refScrollView);
5559
5742
  const keyExtractor = keyExtractorProp != null ? keyExtractorProp : ((_item, index) => index.toString());
5560
5743
  const stickyHeaderIndices = stickyHeaderIndicesProp != null ? stickyHeaderIndicesProp : stickyIndicesDeprecated;
5744
+ const contentInsetEndAdjustmentResolved = Platform.OS === "web" ? contentInsetEndAdjustment : void 0;
5745
+ const previousContentInsetEndAdjustmentRef = useRef(contentInsetEndAdjustmentResolved);
5561
5746
  const alwaysRenderIndices = useMemo(() => {
5562
5747
  const indices = getAlwaysRenderIndices(alwaysRender, dataProp, keyExtractor, anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex);
5563
5748
  return { arr: indices, set: new Set(indices) };
@@ -5636,7 +5821,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5636
5821
  startReachedSnapshotDataChangeEpoch: void 0,
5637
5822
  stickyContainerPool: /* @__PURE__ */ new Set(),
5638
5823
  stickyContainers: /* @__PURE__ */ new Map(),
5639
- timeoutSizeMessage: 0,
5640
5824
  timeouts: /* @__PURE__ */ new Set(),
5641
5825
  totalSize: 0,
5642
5826
  viewabilityConfigCallbackPairs: void 0
@@ -5656,7 +5840,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5656
5840
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
5657
5841
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
5658
5842
  const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
5659
- if (didDataChangeLocal && state.didFinishInitialScroll && ((_f = state.initialScroll) == null ? void 0 : _f.viewPosition) === 1 && state.props.data.length > 0) {
5843
+ if (didDataChangeLocal && !initialScrollAtEnd && state.didFinishInitialScroll && ((_f = state.initialScroll) == null ? void 0 : _f.viewPosition) === 1 && state.props.data.length > 0) {
5660
5844
  clearPreservedInitialScrollTarget(state);
5661
5845
  }
5662
5846
  if (didDataChangeLocal) {
@@ -5677,6 +5861,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5677
5861
  anchoredEndSpace: anchoredEndSpaceResolved,
5678
5862
  animatedProps: animatedPropsInternal,
5679
5863
  contentInset,
5864
+ contentInsetEndAdjustment: contentInsetEndAdjustmentResolved,
5680
5865
  data: dataProp,
5681
5866
  dataVersion,
5682
5867
  drawDistance,
@@ -5710,7 +5895,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5710
5895
  stickyPositionComponentInternal,
5711
5896
  stylePaddingBottom: stylePaddingBottomState,
5712
5897
  stylePaddingTop: stylePaddingTopState,
5713
- suggestEstimatedItemSize: !!suggestEstimatedItemSize,
5714
5898
  useWindowScroll: useWindowScrollResolved
5715
5899
  };
5716
5900
  state.refScroller = refScroller;
@@ -5799,6 +5983,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5799
5983
  didAnchoredEndSpaceAnchorIndexChange,
5800
5984
  numColumnsProp
5801
5985
  ]);
5986
+ useLayoutEffect(() => {
5987
+ const previousContentInsetEndAdjustment = previousContentInsetEndAdjustmentRef.current;
5988
+ previousContentInsetEndAdjustmentRef.current = contentInsetEndAdjustmentResolved;
5989
+ updateContentInsetEndAdjustment(ctx, previousContentInsetEndAdjustment);
5990
+ }, [ctx, contentInsetEndAdjustmentResolved]);
5802
5991
  const onLayoutFooter = useCallback(
5803
5992
  (layout) => {
5804
5993
  if (!usesBootstrapInitialScroll) {