@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/react-native.js CHANGED
@@ -160,7 +160,10 @@ function useSelector$(signalName, selector) {
160
160
  }
161
161
 
162
162
  // src/state/getContentInsetEnd.ts
163
- function getContentInsetEnd(ctx) {
163
+ function getContentInsetEndAdjustmentEnd(adjustment) {
164
+ return Math.max(0, adjustment != null ? adjustment : 0);
165
+ }
166
+ function getContentInsetEnd(ctx, contentInsetEndAdjustmentOverride) {
164
167
  var _a3, _b;
165
168
  const state = ctx.state;
166
169
  const { props } = state;
@@ -168,14 +171,21 @@ function getContentInsetEnd(ctx) {
168
171
  const contentInset = props.contentInset;
169
172
  const baseInset = contentInset != null ? contentInset : state.nativeContentInset;
170
173
  const baseEndInset = (horizontal ? baseInset == null ? void 0 : baseInset.right : baseInset == null ? void 0 : baseInset.bottom) || 0;
174
+ const contentInsetEndAdjustment = getContentInsetEndAdjustmentEnd(
175
+ contentInsetEndAdjustmentOverride != null ? contentInsetEndAdjustmentOverride : props.contentInsetEndAdjustment
176
+ );
171
177
  const anchoredEndSpaceSize = peek$(ctx, "anchoredEndSpaceSize");
172
178
  const anchoredEndInset = ((_a3 = props.anchoredEndSpace) == null ? void 0 : _a3.includeInEndInset) && anchoredEndSpaceSize ? anchoredEndSpaceSize : 0;
173
179
  const overrideInset = (_b = state.contentInsetOverride) != null ? _b : void 0;
180
+ const adjustedBaseEndInset = baseEndInset + contentInsetEndAdjustment;
174
181
  if (overrideInset) {
175
182
  const mergedInset = { bottom: 0, right: 0, ...baseInset, ...overrideInset };
176
- return Math.max((horizontal ? mergedInset.right : mergedInset.bottom) || 0, anchoredEndInset);
183
+ return Math.max(
184
+ ((horizontal ? mergedInset.right : mergedInset.bottom) || 0) + contentInsetEndAdjustment,
185
+ anchoredEndInset
186
+ );
177
187
  }
178
- return Math.max(baseEndInset, anchoredEndInset);
188
+ return Math.max(adjustedBaseEndInset, anchoredEndInset);
179
189
  }
180
190
 
181
191
  // src/state/getContentSize.ts
@@ -667,8 +677,52 @@ function isInMVCPActiveMode(state) {
667
677
  }
668
678
 
669
679
  // src/components/Container.tsx
680
+ function getContainerPositionStyle({
681
+ columnWrapperStyle,
682
+ horizontal,
683
+ hasItemSeparator,
684
+ numColumns,
685
+ otherAxisPos,
686
+ otherAxisSize
687
+ }) {
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: hasItemSeparator ? "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
+ }
670
723
  var Container = typedMemo(function Container2({
671
724
  id,
725
+ itemKey,
672
726
  recycleItems,
673
727
  horizontal,
674
728
  getRenderedItem: getRenderedItem2,
@@ -680,11 +734,10 @@ var Container = typedMemo(function Container2({
680
734
  const { columnWrapperStyle, animatedScrollY } = ctx;
681
735
  const positionComponentInternal = ctx.state.props.positionComponentInternal;
682
736
  const stickyPositionComponentInternal = ctx.state.props.stickyPositionComponentInternal;
683
- const [column = 0, span = 1, data, itemKey, numColumns = 1, extraData, isSticky] = useArr$([
737
+ const [column = 0, span = 1, data, numColumns = 1, extraData, isSticky] = useArr$([
684
738
  `containerColumn${id}`,
685
739
  `containerSpan${id}`,
686
740
  `containerItemData${id}`,
687
- `containerItemKey${id}`,
688
741
  "numColumns",
689
742
  "extraData",
690
743
  `containerSticky${id}`
@@ -705,42 +758,17 @@ var Container = typedMemo(function Container2({
705
758
  const resolvedSpan = Math.min(Math.max(span || 1, 1), numColumns);
706
759
  const otherAxisPos = numColumns > 1 ? `${(resolvedColumn - 1) / numColumns * 100}%` : 0;
707
760
  const otherAxisSize = numColumns > 1 ? `${resolvedSpan / numColumns * 100}%` : void 0;
708
- const style = React2.useMemo(() => {
709
- let paddingStyles;
710
- if (columnWrapperStyle) {
711
- const { columnGap, rowGap, gap } = columnWrapperStyle;
712
- if (horizontal) {
713
- paddingStyles = {
714
- paddingBottom: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0,
715
- paddingRight: columnGap || gap || void 0,
716
- paddingTop: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
717
- };
718
- } else {
719
- paddingStyles = {
720
- paddingBottom: rowGap || gap || void 0,
721
- paddingLeft: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0,
722
- paddingRight: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
723
- };
724
- }
725
- }
726
- return horizontal ? {
727
- boxSizing: paddingStyles ? "border-box" : void 0,
728
- flexDirection: ItemSeparatorComponent ? "row" : void 0,
729
- height: otherAxisSize,
730
- left: 0,
731
- position: "absolute",
732
- top: otherAxisPos,
733
- ...paddingStyles || {}
734
- } : {
735
- boxSizing: paddingStyles ? "border-box" : void 0,
736
- left: otherAxisPos,
737
- position: "absolute",
738
- right: numColumns > 1 ? null : 0,
739
- top: 0,
740
- width: otherAxisSize,
741
- ...paddingStyles || {}
742
- };
743
- }, [horizontal, otherAxisPos, otherAxisSize, columnWrapperStyle, numColumns]);
761
+ const style = React2.useMemo(
762
+ () => getContainerPositionStyle({
763
+ columnWrapperStyle,
764
+ hasItemSeparator: !!ItemSeparatorComponent,
765
+ horizontal,
766
+ numColumns,
767
+ otherAxisPos,
768
+ otherAxisSize
769
+ }),
770
+ [horizontal, otherAxisPos, otherAxisSize, columnWrapperStyle, numColumns, ItemSeparatorComponent]
771
+ );
744
772
  const renderedItemInfo = React2.useMemo(
745
773
  () => itemKey !== void 0 ? getRenderedItem2(itemKey) : null,
746
774
  [itemKey, data, extraData]
@@ -854,6 +882,39 @@ var Container = typedMemo(function Container2({
854
882
  );
855
883
  });
856
884
 
885
+ // src/components/ContainerSlot.tsx
886
+ function ContainerSlotBase({
887
+ id,
888
+ horizontal,
889
+ recycleItems,
890
+ ItemSeparatorComponent,
891
+ updateItemSize: updateItemSize2,
892
+ getRenderedItem: getRenderedItem2,
893
+ stickyHeaderConfig,
894
+ ContainerComponent = Container
895
+ }) {
896
+ const [itemKey] = useArr$([`containerItemKey${id}`]);
897
+ if (itemKey === void 0) {
898
+ return null;
899
+ }
900
+ return /* @__PURE__ */ React2__namespace.createElement(
901
+ ContainerComponent,
902
+ {
903
+ getRenderedItem: getRenderedItem2,
904
+ horizontal,
905
+ ItemSeparatorComponent,
906
+ id,
907
+ itemKey,
908
+ recycleItems,
909
+ stickyHeaderConfig,
910
+ updateItemSize: updateItemSize2
911
+ }
912
+ );
913
+ }
914
+ var ContainerSlot = typedMemo(function ContainerSlot2(props) {
915
+ return /* @__PURE__ */ React2__namespace.createElement(ContainerSlotBase, { ...props });
916
+ });
917
+
857
918
  // src/components/Containers.native.tsx
858
919
  var ContainersLayer = typedMemo(function ContainersLayer2({ children, horizontal }) {
859
920
  const ctx = useStateContext();
@@ -892,12 +953,12 @@ var Containers = typedMemo(function Containers2({
892
953
  updateItemSize: updateItemSize2,
893
954
  getRenderedItem: getRenderedItem2
894
955
  }) {
895
- const [numContainers] = useArr$(["numContainersPooled"]);
956
+ const [numContainersPooled] = useArr$(["numContainersPooled"]);
896
957
  const containers = [];
897
- for (let i = 0; i < numContainers; i++) {
958
+ for (let i = 0; i < numContainersPooled; i++) {
898
959
  containers.push(
899
960
  /* @__PURE__ */ React2__namespace.createElement(
900
- Container,
961
+ ContainerSlot,
901
962
  {
902
963
  getRenderedItem: getRenderedItem2,
903
964
  horizontal,
@@ -1224,10 +1285,9 @@ var initialScrollWatchdog = {
1224
1285
  clear(state) {
1225
1286
  initialScrollWatchdog.set(state, void 0);
1226
1287
  },
1227
- didObserveProgress(newScroll, watchdog) {
1228
- const previousDistance = Math.abs(watchdog.startScroll - watchdog.targetOffset);
1288
+ didReachTarget(newScroll, watchdog) {
1229
1289
  const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
1230
- return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET || nextDistance + INITIAL_SCROLL_MIN_TARGET_OFFSET < previousDistance;
1290
+ return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
1231
1291
  },
1232
1292
  get(state) {
1233
1293
  var _a3, _b;
@@ -1252,19 +1312,19 @@ var initialScrollWatchdog = {
1252
1312
  }
1253
1313
  };
1254
1314
  function setInitialScrollSession(state, options = {}) {
1255
- var _a3, _b, _c;
1315
+ var _a3, _b, _c, _d;
1256
1316
  const existingSession = state.initialScrollSession;
1257
1317
  const kind = (_a3 = options.kind) != null ? _a3 : existingSession == null ? void 0 : existingSession.kind;
1258
1318
  const completion = existingSession == null ? void 0 : existingSession.completion;
1259
- const hasBootstrapOverride = Object.hasOwn(options, "bootstrap");
1260
- const bootstrap = kind === "bootstrap" ? hasBootstrapOverride ? options.bootstrap : (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0 : void 0;
1319
+ const existingBootstrap = (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0;
1320
+ const bootstrap = kind === "bootstrap" ? options.bootstrap === null ? void 0 : (_b = options.bootstrap) != null ? _b : existingBootstrap : void 0;
1261
1321
  if (!kind) {
1262
1322
  return clearInitialScrollSession(state);
1263
1323
  }
1264
1324
  if (!state.initialScroll && !bootstrap && !hasInitialScrollSessionCompletion(completion)) {
1265
1325
  return clearInitialScrollSession(state);
1266
1326
  }
1267
- const previousDataLength = (_c = (_b = options.previousDataLength) != null ? _b : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _c : 0;
1327
+ const previousDataLength = (_d = (_c = options.previousDataLength) != null ? _c : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _d : 0;
1268
1328
  state.initialScrollSession = createInitialScrollSession({
1269
1329
  bootstrap,
1270
1330
  completion,
@@ -1846,7 +1906,7 @@ function checkFinishedScrollFallback(ctx) {
1846
1906
  state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, delay);
1847
1907
  };
1848
1908
  const checkHasScrolled = () => {
1849
- var _a3, _b, _c;
1909
+ var _a3, _b, _c, _d;
1850
1910
  state.timeoutCheckFinishedScrollFallback = void 0;
1851
1911
  const isStillScrollingTo = state.scrollingTo;
1852
1912
  if (isStillScrollingTo) {
@@ -1859,8 +1919,10 @@ function checkFinishedScrollFallback(ctx) {
1859
1919
  isStillScrollingTo
1860
1920
  );
1861
1921
  const completionState = getResolvedScrollCompletionState(ctx, isStillScrollingTo);
1862
- const canFinishAfterSilentNativeDispatch = silentInitialDispatch && completionState.isAtResolvedTarget && numChecks >= 1;
1922
+ const canFinishAfterSilentNativeDispatch = Platform.OS === "android" && silentInitialDispatch && completionState.isAtResolvedTarget && numChecks >= 1;
1863
1923
  const shouldRetrySilentInitialNativeScroll = Platform.OS === "android" && canFinishAfterSilentNativeDispatch && !initialScrollCompletion.didRetrySilentInitialScroll(state);
1924
+ const shouldFinishAfterObservedScroll = state.hasScrolled && (!isStillScrollingTo.isInitialScroll || completionState.isAtResolvedTarget);
1925
+ const shouldRetryUnalignedInitialScroll = isStillScrollingTo.isInitialScroll && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
1864
1926
  if (shouldRetrySilentInitialNativeScroll) {
1865
1927
  const targetOffset = (_b = (_a3 = getInitialScrollWatchdogTargetOffset(state)) != null ? _a3 : isStillScrollingTo.targetOffset) != null ? _b : 0;
1866
1928
  const jiggleOffset = targetOffset >= SILENT_INITIAL_SCROLL_TARGET_EPSILON ? targetOffset - SILENT_INITIAL_SCROLL_TARGET_EPSILON : targetOffset + SILENT_INITIAL_SCROLL_TARGET_EPSILON;
@@ -1870,10 +1932,10 @@ function checkFinishedScrollFallback(ctx) {
1870
1932
  scrollToFallbackOffset(ctx, targetOffset);
1871
1933
  });
1872
1934
  scheduleFallbackCheck(SILENT_INITIAL_SCROLL_RETRY_DELAY_MS);
1873
- } else if (shouldFinishZeroTarget || state.hasScrolled || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
1935
+ } else if (shouldFinishZeroTarget || shouldFinishAfterObservedScroll || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
1874
1936
  finishScrollTo(ctx);
1875
- } else if (isNativeInitialPending && numChecks <= maxChecks) {
1876
- const targetOffset = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : state.scrollPending;
1937
+ } else if ((isNativeInitialPending || shouldRetryUnalignedInitialScroll) && numChecks <= maxChecks) {
1938
+ const targetOffset = (_d = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : isStillScrollingTo.targetOffset) != null ? _d : state.scrollPending;
1877
1939
  scrollToFallbackOffset(ctx, targetOffset);
1878
1940
  scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
1879
1941
  } else {
@@ -2102,7 +2164,7 @@ function advanceMeasuredInitialScroll(ctx, options) {
2102
2164
  const activeInitialTargetOffset = scrollingTo ? (_a3 = scrollingTo.targetOffset) != null ? _a3 : scrollingTo.offset : void 0;
2103
2165
  const didOffsetChange = initialScroll.contentOffset === void 0 || Math.abs(initialScroll.contentOffset - resolvedOffset) > 1;
2104
2166
  const didActiveInitialTargetChange = activeInitialTargetOffset !== void 0 && Math.abs(activeInitialTargetOffset - resolvedOffset) > 1;
2105
- const isAlreadyAtDesiredInitialTarget = activeInitialTargetOffset !== void 0 && Math.abs(state.scroll - activeInitialTargetOffset) <= 1 && Math.abs(state.scrollPending - activeInitialTargetOffset) <= 1;
2167
+ const isAlreadyAtDesiredInitialTarget = activeInitialTargetOffset !== void 0 && Math.abs(state.scroll - resolvedOffset) <= 1 && Math.abs(state.scrollPending - resolvedOffset) <= 1;
2106
2168
  if (!(options == null ? void 0 : options.forceScroll) && !didOffsetChange && isInitialScrollInProgress && !didActiveInitialTargetChange) {
2107
2169
  return false;
2108
2170
  }
@@ -2174,6 +2236,61 @@ function checkAllSizesKnown(state, indices) {
2174
2236
  });
2175
2237
  }
2176
2238
 
2239
+ // src/utils/requestAdjust.ts
2240
+ function requestAdjust(ctx, positionDiff, dataChanged) {
2241
+ const state = ctx.state;
2242
+ if (Math.abs(positionDiff) > 0.1) {
2243
+ const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
2244
+ const doit = () => {
2245
+ if (needsScrollWorkaround) {
2246
+ scrollTo(ctx, {
2247
+ noScrollingTo: true,
2248
+ offset: state.scroll
2249
+ });
2250
+ } else {
2251
+ state.scrollAdjustHandler.requestAdjust(positionDiff);
2252
+ if (state.adjustingFromInitialMount) {
2253
+ state.adjustingFromInitialMount--;
2254
+ }
2255
+ }
2256
+ };
2257
+ state.scroll += positionDiff;
2258
+ state.scrollForNextCalculateItemsInView = void 0;
2259
+ const readyToRender = peek$(ctx, "readyToRender");
2260
+ if (readyToRender) {
2261
+ doit();
2262
+ if (Platform.OS !== "web") {
2263
+ const threshold = state.scroll - positionDiff / 2;
2264
+ if (!state.ignoreScrollFromMVCP) {
2265
+ state.ignoreScrollFromMVCP = {};
2266
+ }
2267
+ if (positionDiff > 0) {
2268
+ state.ignoreScrollFromMVCP.lt = threshold;
2269
+ } else {
2270
+ state.ignoreScrollFromMVCP.gt = threshold;
2271
+ }
2272
+ if (state.ignoreScrollFromMVCPTimeout) {
2273
+ clearTimeout(state.ignoreScrollFromMVCPTimeout);
2274
+ }
2275
+ const delay = needsScrollWorkaround ? 250 : 100;
2276
+ state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
2277
+ var _a3;
2278
+ state.ignoreScrollFromMVCP = void 0;
2279
+ const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
2280
+ if (shouldForceUpdate) {
2281
+ state.ignoreScrollFromMVCPIgnored = false;
2282
+ state.scrollPending = state.scroll;
2283
+ (_a3 = state.reprocessCurrentScroll) == null ? void 0 : _a3.call(state);
2284
+ }
2285
+ }, delay);
2286
+ }
2287
+ } else {
2288
+ state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
2289
+ requestAnimationFrame(doit);
2290
+ }
2291
+ }
2292
+ }
2293
+
2177
2294
  // src/core/bootstrapInitialScroll.ts
2178
2295
  var DEFAULT_BOOTSTRAP_REVEAL_EPSILON = 1;
2179
2296
  var DEFAULT_BOOTSTRAP_REVEAL_MAX_FRAMES = 8;
@@ -2267,7 +2384,7 @@ function clearBootstrapInitialScrollSession(state) {
2267
2384
  bootstrapInitialScroll.frameHandle = void 0;
2268
2385
  }
2269
2386
  setInitialScrollSession(state, {
2270
- bootstrap: void 0,
2387
+ bootstrap: null,
2271
2388
  kind: (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind
2272
2389
  });
2273
2390
  }
@@ -2423,15 +2540,18 @@ function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {
2423
2540
  return;
2424
2541
  }
2425
2542
  if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2426
- if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {
2427
- clearPendingInitialScrollFooterLayout(ctx, {
2428
- dataLength: state.props.data.length,
2429
- stylePaddingBottom: (_b = state.props.stylePaddingBottom) != null ? _b : 0,
2430
- target: initialScroll
2431
- });
2432
- return;
2543
+ const shouldKeepEndTargetAlive = isRetargetableBottomAlignedInitialScrollTarget(initialScroll) && peek$(ctx, "isAtEnd");
2544
+ if (!shouldKeepEndTargetAlive) {
2545
+ if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {
2546
+ clearPendingInitialScrollFooterLayout(ctx, {
2547
+ dataLength: state.props.data.length,
2548
+ stylePaddingBottom: (_b = state.props.stylePaddingBottom) != null ? _b : 0,
2549
+ target: initialScroll
2550
+ });
2551
+ } else {
2552
+ clearFinishedViewportRetargetableInitialScroll(state);
2553
+ }
2433
2554
  }
2434
- clearFinishedViewportRetargetableInitialScroll(state);
2435
2555
  }
2436
2556
  }
2437
2557
  function startBootstrapInitialScrollOnMount(ctx, options) {
@@ -2470,7 +2590,7 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
2470
2590
  }
2471
2591
  const shouldResetDidFinish = !!(state.didFinishInitialScroll && previousDataLength === 0 && dataLength > 0 && initialScroll.index !== void 0);
2472
2592
  const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2473
- const shouldClearFinishedResizePreservation = didDataChange && dataLength > 0 && state.didFinishInitialScroll && !bootstrapInitialScroll && !shouldResetDidFinish;
2593
+ const shouldClearFinishedResizePreservation = !initialScrollAtEnd && didDataChange && dataLength > 0 && state.didFinishInitialScroll && !bootstrapInitialScroll && !shouldResetDidFinish;
2474
2594
  if (shouldClearFinishedResizePreservation) {
2475
2595
  clearPreservedInitialScrollTarget(state);
2476
2596
  return;
@@ -2573,27 +2693,46 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2573
2693
  }
2574
2694
  }
2575
2695
  function handleBootstrapInitialScrollLayoutChange(ctx) {
2696
+ var _a3, _b, _c, _d;
2576
2697
  const state = ctx.state;
2577
2698
  const initialScroll = state.initialScroll;
2578
- if (isOffsetInitialScrollSession(state) || state.props.data.length === 0 || !initialScroll) {
2579
- return;
2580
- }
2581
2699
  const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2582
- if (!bootstrapInitialScroll && initialScroll.viewPosition !== 1) {
2583
- return;
2584
- }
2585
- const didFinishInitialScroll = state.didFinishInitialScroll;
2586
- if (didFinishInitialScroll) {
2587
- setInitialScrollTarget(state, initialScroll, {
2588
- resetDidFinish: true
2589
- });
2590
- state.clearPreservedInitialScrollOnNextFinish = true;
2700
+ if (initialScroll && state.props.data.length > 0 && !isOffsetInitialScrollSession(state) && (bootstrapInitialScroll || initialScroll.viewPosition === 1)) {
2701
+ const resolvedOffset = resolveInitialScrollOffset(ctx, initialScroll);
2702
+ const scrollingTo = ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) ? state.scrollingTo : void 0;
2703
+ if (!bootstrapInitialScroll && (scrollingTo || state.didFinishInitialScroll)) {
2704
+ const currentOffset = scrollingTo ? (_b = scrollingTo.targetOffset) != null ? _b : scrollingTo.offset : getObservedBootstrapInitialScrollOffset(state);
2705
+ const offsetDiff = resolvedOffset - currentOffset;
2706
+ if (Math.abs(offsetDiff) > DEFAULT_BOOTSTRAP_REVEAL_EPSILON) {
2707
+ if (scrollingTo) {
2708
+ const existingWatchdog = initialScrollWatchdog.get(state);
2709
+ scrollingTo.offset = resolvedOffset;
2710
+ scrollingTo.targetOffset = resolvedOffset;
2711
+ state.initialScroll = {
2712
+ ...initialScroll,
2713
+ contentOffset: resolvedOffset
2714
+ };
2715
+ state.hasScrolled = false;
2716
+ initialScrollWatchdog.set(state, {
2717
+ startScroll: (_c = existingWatchdog == null ? void 0 : existingWatchdog.startScroll) != null ? _c : state.scroll,
2718
+ targetOffset: resolvedOffset
2719
+ });
2720
+ }
2721
+ requestAdjust(ctx, offsetDiff);
2722
+ if (state.didFinishInitialScroll) {
2723
+ (_d = state.triggerCalculateItemsInView) == null ? void 0 : _d.call(state, { forceFullItemPositions: true });
2724
+ }
2725
+ }
2726
+ if (state.didFinishInitialScroll) {
2727
+ clearFinishedViewportRetargetableInitialScroll(state);
2728
+ }
2729
+ } else {
2730
+ rearmBootstrapInitialScroll(ctx, {
2731
+ scroll: resolvedOffset,
2732
+ targetIndexSeed: initialScroll.index
2733
+ });
2734
+ }
2591
2735
  }
2592
- rearmBootstrapInitialScroll(ctx, {
2593
- scroll: resolveInitialScrollOffset(ctx, initialScroll),
2594
- seedContentOffset: didFinishInitialScroll && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
2595
- targetIndexSeed: initialScroll.index
2596
- });
2597
2736
  }
2598
2737
  function evaluateBootstrapInitialScroll(ctx) {
2599
2738
  var _a3, _b;
@@ -2698,6 +2837,15 @@ function abortBootstrapInitialScroll(ctx) {
2698
2837
  }
2699
2838
 
2700
2839
  // src/core/initialScrollLifecycle.ts
2840
+ function retargetActiveInitialScrollAtEnd(ctx) {
2841
+ var _a3;
2842
+ const state = ctx.state;
2843
+ const initialScroll = state.initialScroll;
2844
+ if (!initialScroll || state.didFinishInitialScroll || ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" || initialScroll.viewPosition !== 1 || state.props.data.length === 0) {
2845
+ return false;
2846
+ }
2847
+ return advanceCurrentInitialScrollSession(ctx, { forceScroll: true });
2848
+ }
2701
2849
  function handleInitialScrollLayoutReady(ctx) {
2702
2850
  var _a3;
2703
2851
  if (!ctx.state.initialScroll) {
@@ -2771,61 +2919,6 @@ function handleInitialScrollDataChange(ctx, options) {
2771
2919
  advanceCurrentInitialScrollSession(ctx);
2772
2920
  }
2773
2921
 
2774
- // src/utils/requestAdjust.ts
2775
- function requestAdjust(ctx, positionDiff, dataChanged) {
2776
- const state = ctx.state;
2777
- if (Math.abs(positionDiff) > 0.1) {
2778
- const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
2779
- const doit = () => {
2780
- if (needsScrollWorkaround) {
2781
- scrollTo(ctx, {
2782
- noScrollingTo: true,
2783
- offset: state.scroll
2784
- });
2785
- } else {
2786
- state.scrollAdjustHandler.requestAdjust(positionDiff);
2787
- if (state.adjustingFromInitialMount) {
2788
- state.adjustingFromInitialMount--;
2789
- }
2790
- }
2791
- };
2792
- state.scroll += positionDiff;
2793
- state.scrollForNextCalculateItemsInView = void 0;
2794
- const readyToRender = peek$(ctx, "readyToRender");
2795
- if (readyToRender) {
2796
- doit();
2797
- if (Platform.OS !== "web") {
2798
- const threshold = state.scroll - positionDiff / 2;
2799
- if (!state.ignoreScrollFromMVCP) {
2800
- state.ignoreScrollFromMVCP = {};
2801
- }
2802
- if (positionDiff > 0) {
2803
- state.ignoreScrollFromMVCP.lt = threshold;
2804
- } else {
2805
- state.ignoreScrollFromMVCP.gt = threshold;
2806
- }
2807
- if (state.ignoreScrollFromMVCPTimeout) {
2808
- clearTimeout(state.ignoreScrollFromMVCPTimeout);
2809
- }
2810
- const delay = needsScrollWorkaround ? 250 : 100;
2811
- state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
2812
- var _a3;
2813
- state.ignoreScrollFromMVCP = void 0;
2814
- const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
2815
- if (shouldForceUpdate) {
2816
- state.ignoreScrollFromMVCPIgnored = false;
2817
- state.scrollPending = state.scroll;
2818
- (_a3 = state.reprocessCurrentScroll) == null ? void 0 : _a3.call(state);
2819
- }
2820
- }, delay);
2821
- }
2822
- } else {
2823
- state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
2824
- requestAnimationFrame(doit);
2825
- }
2826
- }
2827
- }
2828
-
2829
2922
  // src/core/mvcp.ts
2830
2923
  var MVCP_POSITION_EPSILON = 0.1;
2831
2924
  var MVCP_ANCHOR_LOCK_TTL_MS = 300;
@@ -3109,7 +3202,10 @@ function prepareMVCP(ctx, dataChanged) {
3109
3202
  return;
3110
3203
  }
3111
3204
  if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
3112
- requestAdjust(ctx, positionDiff, dataChanged && mvcpData);
3205
+ const shouldSkipAdjustForMaintainedEnd = state.maintainingScrollAtEnd && peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
3206
+ if (!shouldSkipAdjustForMaintainedEnd) {
3207
+ requestAdjust(ctx, positionDiff, dataChanged && mvcpData);
3208
+ }
3113
3209
  }
3114
3210
  };
3115
3211
  }
@@ -3728,6 +3824,30 @@ function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
3728
3824
  var unstableBatchedUpdates = ReactNative__namespace.unstable_batchedUpdates;
3729
3825
  var batchedUpdates = typeof unstableBatchedUpdates === "function" ? unstableBatchedUpdates : (fn) => fn();
3730
3826
 
3827
+ // src/utils/containerPool.ts
3828
+ var MIN_INITIAL_CONTAINER_POOL_SIZE = 32;
3829
+ var MAX_INITIAL_SPARE_CONTAINERS = 64;
3830
+ function getInitialContainerPoolSize(dataLength, numContainers, initialContainerPoolRatio) {
3831
+ if (dataLength <= 0 || numContainers <= 0) {
3832
+ return 0;
3833
+ }
3834
+ const ratioPoolSize = Math.ceil(numContainers * initialContainerPoolRatio);
3835
+ const cappedSparePoolSize = numContainers + MAX_INITIAL_SPARE_CONTAINERS;
3836
+ const targetPoolSize = Math.max(
3837
+ numContainers,
3838
+ Math.min(ratioPoolSize, cappedSparePoolSize),
3839
+ Math.min(dataLength, MIN_INITIAL_CONTAINER_POOL_SIZE)
3840
+ );
3841
+ const maxUsefulPoolSize = Math.max(dataLength, numContainers);
3842
+ return Math.min(maxUsefulPoolSize, targetPoolSize);
3843
+ }
3844
+ function getExpandedContainerPoolSize(dataLength, numContainers) {
3845
+ if (dataLength <= 0 || numContainers <= 0) {
3846
+ return 0;
3847
+ }
3848
+ return Math.min(Math.max(dataLength, numContainers), Math.max(numContainers, Math.ceil(numContainers * 1.5)));
3849
+ }
3850
+
3731
3851
  // src/utils/findAvailableContainers.ts
3732
3852
  function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers, protectedKeys) {
3733
3853
  const numContainers = peek$(ctx, "numContainers");
@@ -3933,7 +4053,7 @@ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentSt
3933
4053
  function calculateItemsInView(ctx, params = {}) {
3934
4054
  const state = ctx.state;
3935
4055
  batchedUpdates(() => {
3936
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
4056
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q;
3937
4057
  const {
3938
4058
  columns,
3939
4059
  columnSpans,
@@ -3980,12 +4100,22 @@ function calculateItemsInView(ctx, params = {}) {
3980
4100
  // current initial-scroll target instead of transient native adjustments.
3981
4101
  resolveInitialScrollOffset(ctx, initialScroll)
3982
4102
  ) : state.scroll;
3983
- const scrollAdjustPending = (_c = peek$(ctx, "scrollAdjustPending")) != null ? _c : 0;
3984
- const scrollAdjustPad = scrollAdjustPending - topPad;
3985
- let scroll = Math.round(scrollState + scrollExtra + scrollAdjustPad);
3986
- if (scroll + scrollLength > totalSize) {
3987
- scroll = Math.max(0, totalSize - scrollLength);
3988
- }
4103
+ let scrollAdjustPending = 0;
4104
+ let scrollAdjustPad = 0;
4105
+ let scroll = 0;
4106
+ let scrollTopBuffered = 0;
4107
+ let scrollBottom = 0;
4108
+ let scrollBottomBuffered = 0;
4109
+ const updateScroll2 = (nextScrollState) => {
4110
+ var _a4;
4111
+ scrollAdjustPending = (_a4 = peek$(ctx, "scrollAdjustPending")) != null ? _a4 : 0;
4112
+ scrollAdjustPad = scrollAdjustPending - topPad;
4113
+ scroll = Math.round(nextScrollState + scrollExtra + scrollAdjustPad);
4114
+ if (scroll + scrollLength > totalSize) {
4115
+ scroll = Math.max(0, totalSize - scrollLength);
4116
+ }
4117
+ };
4118
+ updateScroll2(scrollState);
3989
4119
  const previousStickyIndex = peek$(ctx, "activeStickyIndex");
3990
4120
  const currentStickyIdx = stickyIndicesArr.length > 0 ? findCurrentStickyIndex(stickyIndicesArr, scroll, state) : -1;
3991
4121
  const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyIndicesArr[currentStickyIdx] : -1;
@@ -4001,9 +4131,12 @@ function calculateItemsInView(ctx, params = {}) {
4001
4131
  scrollBufferTop = drawDistance * 1.5;
4002
4132
  scrollBufferBottom = drawDistance * 0.5;
4003
4133
  }
4004
- const scrollTopBuffered = scroll - scrollBufferTop;
4005
- const scrollBottom = scroll + scrollLength + (scroll < 0 ? -scroll : 0);
4006
- const scrollBottomBuffered = scrollBottom + scrollBufferBottom;
4134
+ const updateScrollRange = () => {
4135
+ scrollTopBuffered = scroll - scrollBufferTop;
4136
+ scrollBottom = scroll + scrollLength + (scroll < 0 ? -scroll : 0);
4137
+ scrollBottomBuffered = scrollBottom + scrollBufferBottom;
4138
+ };
4139
+ updateScrollRange();
4007
4140
  if (!suppressInitialScrollSideEffects && !dataChanged && !forceFullItemPositions && scrollForNextCalculateItemsInView) {
4008
4141
  const { top, bottom } = scrollForNextCalculateItemsInView;
4009
4142
  if (top === null && bottom === null) {
@@ -4022,7 +4155,7 @@ function calculateItemsInView(ctx, params = {}) {
4022
4155
  columns.length = 0;
4023
4156
  columnSpans.length = 0;
4024
4157
  }
4025
- const startIndex = forceFullItemPositions || dataChanged ? 0 : (_d = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _d : 0;
4158
+ const startIndex = forceFullItemPositions || dataChanged ? 0 : (_c = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _c : 0;
4026
4159
  const optimizeForVisibleWindow = !forceFullItemPositions && !dataChanged && numColumns > 1 && minIndexSizeChanged !== void 0;
4027
4160
  updateItemPositions(ctx, dataChanged, {
4028
4161
  doMVCP,
@@ -4047,21 +4180,25 @@ function calculateItemsInView(ctx, params = {}) {
4047
4180
  }
4048
4181
  }
4049
4182
  const scrollBeforeMVCP = state.scroll;
4050
- const scrollAdjustPendingBeforeMVCP = (_e = peek$(ctx, "scrollAdjustPending")) != null ? _e : 0;
4183
+ const scrollAdjustPendingBeforeMVCP = (_d = peek$(ctx, "scrollAdjustPending")) != null ? _d : 0;
4051
4184
  checkMVCP == null ? void 0 : checkMVCP();
4052
- const didMVCPAdjustScroll = !!checkMVCP && (state.scroll !== scrollBeforeMVCP || ((_f = peek$(ctx, "scrollAdjustPending")) != null ? _f : 0) !== scrollAdjustPendingBeforeMVCP);
4185
+ const didMVCPAdjustScroll = !!checkMVCP && (state.scroll !== scrollBeforeMVCP || ((_e = peek$(ctx, "scrollAdjustPending")) != null ? _e : 0) !== scrollAdjustPendingBeforeMVCP);
4186
+ if (didMVCPAdjustScroll && initialScroll) {
4187
+ updateScroll2(state.scroll);
4188
+ updateScrollRange();
4189
+ }
4053
4190
  let startNoBuffer = null;
4054
4191
  let startBuffered = null;
4055
4192
  let startBufferedId = null;
4056
4193
  let endNoBuffer = null;
4057
4194
  let endBuffered = null;
4058
- let loopStart = (_g = suppressInitialScrollSideEffects ? bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.targetIndexSeed : void 0) != null ? _g : !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
4195
+ let loopStart = (_f = suppressInitialScrollSideEffects ? bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.targetIndexSeed : void 0) != null ? _f : !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
4059
4196
  for (let i = loopStart; i >= 0; i--) {
4060
- const id = (_h = idCache[i]) != null ? _h : getId(state, i);
4197
+ const id = (_g = idCache[i]) != null ? _g : getId(state, i);
4061
4198
  const top = positions[i];
4062
- const size = (_i = sizes.get(id)) != null ? _i : getItemSize(ctx, id, i, data[i]);
4199
+ const size = (_h = sizes.get(id)) != null ? _h : getItemSize(ctx, id, i, data[i]);
4063
4200
  const bottom = top + size;
4064
- if (bottom > scroll - scrollBufferTop) {
4201
+ if (bottom > scrollTopBuffered) {
4065
4202
  loopStart = i;
4066
4203
  } else {
4067
4204
  break;
@@ -4090,8 +4227,8 @@ function calculateItemsInView(ctx, params = {}) {
4090
4227
  let firstFullyOnScreenIndex;
4091
4228
  const dataLength = data.length;
4092
4229
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
4093
- const id = (_j = idCache[i]) != null ? _j : getId(state, i);
4094
- const size = (_k = sizes.get(id)) != null ? _k : getItemSize(ctx, id, i, data[i]);
4230
+ const id = (_i = idCache[i]) != null ? _i : getId(state, i);
4231
+ const size = (_j = sizes.get(id)) != null ? _j : getItemSize(ctx, id, i, data[i]);
4095
4232
  const top = positions[i];
4096
4233
  if (!foundEnd) {
4097
4234
  if (startNoBuffer === null && top + size > scroll) {
@@ -4130,7 +4267,7 @@ function calculateItemsInView(ctx, params = {}) {
4130
4267
  const firstVisibleAnchorIndex = firstFullyOnScreenIndex != null ? firstFullyOnScreenIndex : startNoBuffer;
4131
4268
  if (firstVisibleAnchorIndex !== null && firstVisibleAnchorIndex !== void 0 && endNoBuffer !== null) {
4132
4269
  for (let i = firstVisibleAnchorIndex; i <= endNoBuffer; i++) {
4133
- const id = (_l = idCache[i]) != null ? _l : getId(state, i);
4270
+ const id = (_k = idCache[i]) != null ? _k : getId(state, i);
4134
4271
  idsInView.push(id);
4135
4272
  }
4136
4273
  }
@@ -4163,7 +4300,7 @@ function calculateItemsInView(ctx, params = {}) {
4163
4300
  const needNewContainers = [];
4164
4301
  const needNewContainersSet = /* @__PURE__ */ new Set();
4165
4302
  for (let i = startBuffered; i <= endBuffered; i++) {
4166
- const id = (_m = idCache[i]) != null ? _m : getId(state, i);
4303
+ const id = (_l = idCache[i]) != null ? _l : getId(state, i);
4167
4304
  if (!containerItemKeys.has(id)) {
4168
4305
  needNewContainersSet.add(i);
4169
4306
  needNewContainers.push(i);
@@ -4172,7 +4309,7 @@ function calculateItemsInView(ctx, params = {}) {
4172
4309
  if (alwaysRenderArr.length > 0) {
4173
4310
  for (const index of alwaysRenderArr) {
4174
4311
  if (index < 0 || index >= dataLength) continue;
4175
- const id = (_n = idCache[index]) != null ? _n : getId(state, index);
4312
+ const id = (_m = idCache[index]) != null ? _m : getId(state, index);
4176
4313
  if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
4177
4314
  needNewContainersSet.add(index);
4178
4315
  needNewContainers.push(index);
@@ -4211,7 +4348,7 @@ function calculateItemsInView(ctx, params = {}) {
4211
4348
  for (let idx = 0; idx < needNewContainers.length; idx++) {
4212
4349
  const i = needNewContainers[idx];
4213
4350
  const containerIndex = availableContainers[idx];
4214
- const id = (_o = idCache[i]) != null ? _o : getId(state, i);
4351
+ const id = (_n = idCache[i]) != null ? _n : getId(state, i);
4215
4352
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
4216
4353
  if (oldKey && oldKey !== id) {
4217
4354
  containerItemKeys.delete(oldKey);
@@ -4222,7 +4359,7 @@ function calculateItemsInView(ctx, params = {}) {
4222
4359
  state.containerItemTypes.set(containerIndex, requiredItemTypes[idx]);
4223
4360
  }
4224
4361
  containerItemKeys.set(id, containerIndex);
4225
- (_p = state.userScrollAnchorResetKeys) == null ? void 0 : _p.add(id);
4362
+ (_o = state.userScrollAnchorResetKeys) == null ? void 0 : _o.add(id);
4226
4363
  const containerSticky = `containerSticky${containerIndex}`;
4227
4364
  const isSticky = stickyIndicesSet.has(i);
4228
4365
  const isAlwaysRender = alwaysRenderSet.has(i);
@@ -4246,17 +4383,17 @@ function calculateItemsInView(ctx, params = {}) {
4246
4383
  if (numContainers !== prevNumContainers) {
4247
4384
  set$(ctx, "numContainers", numContainers);
4248
4385
  if (numContainers > peek$(ctx, "numContainersPooled")) {
4249
- set$(ctx, "numContainersPooled", Math.ceil(numContainers * 1.5));
4386
+ set$(ctx, "numContainersPooled", getExpandedContainerPoolSize(dataLength, numContainers));
4250
4387
  }
4251
4388
  }
4252
4389
  }
4253
- if (((_q = state.userScrollAnchorResetKeys) == null ? void 0 : _q.size) === 0) {
4390
+ if (((_p = state.userScrollAnchorResetKeys) == null ? void 0 : _p.size) === 0) {
4254
4391
  state.userScrollAnchorResetKeys = void 0;
4255
4392
  }
4256
4393
  if (alwaysRenderArr.length > 0) {
4257
4394
  for (const index of alwaysRenderArr) {
4258
4395
  if (index < 0 || index >= dataLength) continue;
4259
- const id = (_r = idCache[index]) != null ? _r : getId(state, index);
4396
+ const id = (_q = idCache[index]) != null ? _q : getId(state, index);
4260
4397
  const containerIndex = containerItemKeys.get(id);
4261
4398
  if (containerIndex !== void 0) {
4262
4399
  state.stickyContainerPool.add(containerIndex);
@@ -4360,21 +4497,25 @@ function doMaintainScrollAtEnd(ctx) {
4360
4497
  if (contentSize < state.scrollLength) {
4361
4498
  state.scroll = 0;
4362
4499
  }
4363
- requestAnimationFrame(() => {
4364
- var _a3;
4365
- if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
4366
- state.maintainingScrollAtEnd = true;
4367
- (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
4368
- animated: maintainScrollAtEnd.animated
4369
- });
4370
- setTimeout(
4371
- () => {
4372
- state.maintainingScrollAtEnd = false;
4373
- },
4374
- maintainScrollAtEnd.animated ? 500 : 0
4375
- );
4376
- }
4377
- });
4500
+ if (!state.maintainingScrollAtEnd) {
4501
+ state.maintainingScrollAtEnd = true;
4502
+ requestAnimationFrame(() => {
4503
+ var _a3;
4504
+ if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
4505
+ (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
4506
+ animated: maintainScrollAtEnd.animated
4507
+ });
4508
+ setTimeout(
4509
+ () => {
4510
+ state.maintainingScrollAtEnd = false;
4511
+ },
4512
+ maintainScrollAtEnd.animated ? 500 : 0
4513
+ );
4514
+ } else {
4515
+ state.maintainingScrollAtEnd = false;
4516
+ }
4517
+ });
4518
+ }
4378
4519
  return true;
4379
4520
  }
4380
4521
  return false;
@@ -4486,14 +4627,21 @@ function doInitialAllocateContainers(ctx) {
4486
4627
  } else {
4487
4628
  averageItemSize = estimatedItemSize;
4488
4629
  }
4489
- const numContainers = Math.ceil((scrollLength + drawDistance * 2) / averageItemSize * numColumns);
4630
+ const numContainers = Math.max(
4631
+ 1,
4632
+ Math.ceil((scrollLength + drawDistance * 2) / averageItemSize * numColumns)
4633
+ );
4490
4634
  for (let i = 0; i < numContainers; i++) {
4491
4635
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
4492
4636
  set$(ctx, `containerColumn${i}`, -1);
4493
4637
  set$(ctx, `containerSpan${i}`, 1);
4494
4638
  }
4495
4639
  set$(ctx, "numContainers", numContainers);
4496
- set$(ctx, "numContainersPooled", numContainers * state.props.initialContainerPoolRatio);
4640
+ set$(
4641
+ ctx,
4642
+ "numContainersPooled",
4643
+ getInitialContainerPoolSize(data.length, numContainers, state.props.initialContainerPoolRatio)
4644
+ );
4497
4645
  if (!IsNewArchitecture || state.lastLayout) {
4498
4646
  if (state.initialScroll) {
4499
4647
  requestAnimationFrame(() => {
@@ -4645,8 +4793,8 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
4645
4793
  // src/core/onScroll.ts
4646
4794
  function trackInitialScrollNativeProgress(state, newScroll) {
4647
4795
  const initialNativeScrollWatchdog = initialScrollWatchdog.get(state);
4648
- const didInitialScrollProgress = !!initialNativeScrollWatchdog && initialScrollWatchdog.didObserveProgress(newScroll, initialNativeScrollWatchdog);
4649
- if (didInitialScrollProgress) {
4796
+ const didInitialScrollReachTarget = !!initialNativeScrollWatchdog && initialScrollWatchdog.didReachTarget(newScroll, initialNativeScrollWatchdog);
4797
+ if (didInitialScrollReachTarget) {
4650
4798
  initialScrollWatchdog.clear(state);
4651
4799
  return;
4652
4800
  }
@@ -4784,16 +4932,20 @@ function maybeUpdateAnchoredEndSpace(ctx) {
4784
4932
  let contentBelowAnchor = 0;
4785
4933
  const footerSize = ctx.values.get("footerSize") || 0;
4786
4934
  const stylePaddingBottom = state.props.stylePaddingBottom || 0;
4935
+ let hasUnknownTailSize = false;
4787
4936
  for (let index = anchorIndex; index < data.length; index++) {
4788
4937
  const itemKey = getId(state, index);
4789
4938
  const size = itemKey ? state.sizesKnown.get(itemKey) : void 0;
4790
4939
  const effectiveSize = index === anchorIndex && anchorMaxSize !== void 0 ? Math.min(size || 0, Math.max(0, anchorMaxSize)) : size;
4940
+ if (size === void 0) {
4941
+ hasUnknownTailSize = true;
4942
+ }
4791
4943
  if (effectiveSize !== null && effectiveSize !== void 0 && effectiveSize > 0) {
4792
4944
  contentBelowAnchor += effectiveSize;
4793
4945
  }
4794
4946
  }
4795
4947
  contentBelowAnchor += footerSize + stylePaddingBottom;
4796
- nextSize = Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
4948
+ nextSize = hasUnknownTailSize ? previousSize || 0 : Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
4797
4949
  }
4798
4950
  }
4799
4951
  if (previousSize !== nextSize) {
@@ -4806,6 +4958,22 @@ function maybeUpdateAnchoredEndSpace(ctx) {
4806
4958
  return nextSize;
4807
4959
  }
4808
4960
 
4961
+ // src/core/updateContentInsetEndAdjustment.ts
4962
+ function updateContentInsetEndAdjustment(ctx, previousContentInsetEndAdjustment) {
4963
+ const state = ctx.state;
4964
+ const previousContentInsetEnd = getContentInsetEnd(ctx, previousContentInsetEndAdjustment);
4965
+ const nextContentInsetEnd = getContentInsetEnd(ctx);
4966
+ const insetDiff = nextContentInsetEnd - previousContentInsetEnd;
4967
+ if (insetDiff !== 0) {
4968
+ const wasWithinEndThreshold = !!peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
4969
+ updateScroll(ctx, state.scroll, true, { markHasScrolled: false });
4970
+ const didRetargetInitialScroll = retargetActiveInitialScrollAtEnd(ctx);
4971
+ if (!didRetargetInitialScroll && wasWithinEndThreshold && (Platform.OS !== "web" || insetDiff > 0)) {
4972
+ requestAdjust(ctx, insetDiff);
4973
+ }
4974
+ }
4975
+ }
4976
+
4809
4977
  // src/core/updateItemSize.ts
4810
4978
  function runOrScheduleMVCPRecalculate(ctx) {
4811
4979
  const state = ctx.state;
@@ -4851,15 +5019,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
4851
5019
  const {
4852
5020
  didContainersLayout,
4853
5021
  sizesKnown,
4854
- props: {
4855
- getFixedItemSize,
4856
- getItemType,
4857
- horizontal,
4858
- suggestEstimatedItemSize,
4859
- onItemSizeChanged,
4860
- data,
4861
- maintainScrollAtEnd
4862
- }
5022
+ props: { getFixedItemSize, getItemType, horizontal, onItemSizeChanged, data, maintainScrollAtEnd }
4863
5023
  } = state;
4864
5024
  if (!data) return;
4865
5025
  const index = state.indexByKey.get(itemKey);
@@ -4910,18 +5070,6 @@ function updateItemSize(ctx, itemKey, sizeObj) {
4910
5070
  if (minIndexSizeChanged !== void 0) {
4911
5071
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
4912
5072
  }
4913
- if (IS_DEV && suggestEstimatedItemSize && minIndexSizeChanged !== void 0) {
4914
- if (state.timeoutSizeMessage) clearTimeout(state.timeoutSizeMessage);
4915
- state.timeoutSizeMessage = setTimeout(() => {
4916
- var _a4;
4917
- state.timeoutSizeMessage = void 0;
4918
- const num = state.sizesKnown.size;
4919
- const avg = (_a4 = state.averageSizes[""]) == null ? void 0 : _a4.avg;
4920
- console.warn(
4921
- `[legend-list] Based on the ${num} items rendered so far, the optimal estimated size is ${avg}.`
4922
- );
4923
- }, 1e3);
4924
- }
4925
5073
  const cur = peek$(ctx, "otherAxisSize");
4926
5074
  if (!cur || maxOtherAxisSize > cur) {
4927
5075
  set$(ctx, "otherAxisSize", maxOtherAxisSize);
@@ -5039,12 +5187,47 @@ function createColumnWrapperStyle(contentContainerStyle) {
5039
5187
  }
5040
5188
 
5041
5189
  // src/utils/createImperativeHandle.ts
5190
+ var DEFAULT_AVERAGE_ITEM_SIZE_TYPE = "default";
5191
+ function getAverageItemSizes(state) {
5192
+ const averageItemSizes = {};
5193
+ for (const itemType in state.averageSizes) {
5194
+ const averageSize = state.averageSizes[itemType];
5195
+ if (averageSize) {
5196
+ averageItemSizes[itemType || DEFAULT_AVERAGE_ITEM_SIZE_TYPE] = {
5197
+ average: averageSize.avg,
5198
+ count: averageSize.num
5199
+ };
5200
+ }
5201
+ }
5202
+ return averageItemSizes;
5203
+ }
5042
5204
  function createImperativeHandle(ctx) {
5043
5205
  const state = ctx.state;
5044
5206
  const IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS = 800;
5045
5207
  const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
5046
5208
  let imperativeScrollToken = 0;
5047
5209
  const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0;
5210
+ const isScrollToIndexReady = (targetIndex, allowEmpty = false) => {
5211
+ var _a3;
5212
+ const props = state.props;
5213
+ const dataLength = props.data.length;
5214
+ const anchorIndex = (_a3 = props.anchoredEndSpace) == null ? void 0 : _a3.anchorIndex;
5215
+ if (targetIndex < 0) {
5216
+ return allowEmpty;
5217
+ }
5218
+ if (targetIndex >= dataLength) {
5219
+ return false;
5220
+ }
5221
+ if (anchorIndex === void 0 || anchorIndex < 0 || anchorIndex >= dataLength || targetIndex < anchorIndex || props.getFixedItemSize) {
5222
+ return true;
5223
+ }
5224
+ for (let index = anchorIndex; index < dataLength; index++) {
5225
+ if (!state.sizesKnown.has(getId(state, index))) {
5226
+ return false;
5227
+ }
5228
+ }
5229
+ return true;
5230
+ };
5048
5231
  const runWhenReady = (token, run, isReady) => {
5049
5232
  const startedAt = Date.now();
5050
5233
  let stableFrames = 0;
@@ -5066,11 +5249,10 @@ function createImperativeHandle(ctx) {
5066
5249
  };
5067
5250
  requestAnimationFrame(check);
5068
5251
  };
5069
- const runScrollWithPromise = (run, options) => new Promise((resolve) => {
5070
- var _a3, _b;
5252
+ const runScrollWithPromise = (run, isReady = () => true) => new Promise((resolve) => {
5253
+ var _a3;
5071
5254
  const token = ++imperativeScrollToken;
5072
- const isReady = (_a3 = options == null ? void 0 : options.isReady) != null ? _a3 : (() => true);
5073
- (_b = state.pendingScrollResolve) == null ? void 0 : _b.call(state);
5255
+ (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
5074
5256
  state.pendingScrollResolve = resolve;
5075
5257
  const runNow = () => {
5076
5258
  if (token !== imperativeScrollToken) {
@@ -5145,6 +5327,7 @@ function createImperativeHandle(ctx) {
5145
5327
  },
5146
5328
  end: state.endNoBuffer,
5147
5329
  endBuffered: state.endBuffered,
5330
+ getAverageItemSizes: () => getAverageItemSizes(state),
5148
5331
  isAtEnd: peek$(ctx, "isAtEnd"),
5149
5332
  isAtStart: peek$(ctx, "isAtStart"),
5150
5333
  isEndReached: state.isEndReached,
@@ -5168,8 +5351,14 @@ function createImperativeHandle(ctx) {
5168
5351
  startBuffered: state.startBuffered
5169
5352
  }),
5170
5353
  reportContentInset: (inset) => {
5354
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
5355
+ const previousInset = state.contentInsetOverride;
5171
5356
  state.contentInsetOverride = inset != null ? inset : void 0;
5357
+ 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);
5172
5358
  updateScroll(ctx, state.scroll, true, { markHasScrolled: false });
5359
+ if (didChange) {
5360
+ retargetActiveInitialScrollAtEnd(ctx);
5361
+ }
5173
5362
  },
5174
5363
  scrollIndexIntoView: (options) => runScrollWithPromise(() => scrollIndexIntoView(options)),
5175
5364
  scrollItemIntoView: ({ item, ...props }) => runScrollWithPromise(() => {
@@ -5181,40 +5370,34 @@ function createImperativeHandle(ctx) {
5181
5370
  }
5182
5371
  return false;
5183
5372
  }),
5184
- scrollToEnd: (options) => runScrollWithPromise(() => {
5185
- const data = state.props.data;
5186
- const stylePaddingBottom = state.props.stylePaddingBottom;
5187
- const index = data.length - 1;
5188
- if (index !== -1) {
5189
- const paddingBottom = stylePaddingBottom || 0;
5190
- const footerSize = peek$(ctx, "footerSize") || 0;
5191
- scrollToIndex(ctx, {
5192
- ...options,
5193
- index,
5194
- viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
5195
- viewPosition: 1
5196
- });
5197
- return true;
5198
- }
5199
- return false;
5200
- }),
5201
- scrollToIndex: (params) => {
5202
- const shouldWaitForOutOfRangeTarget = params.index >= 0 && params.index >= state.props.data.length;
5203
- const options = shouldWaitForOutOfRangeTarget ? {
5204
- isReady: () => {
5205
- var _a3;
5206
- const props = state.props;
5207
- const anchorIndex = (_a3 = props.anchoredEndSpace) == null ? void 0 : _a3.anchorIndex;
5208
- const lastIndex = props.data.length - 1;
5209
- const isInRange = params.index < props.data.length;
5210
- const shouldWaitForAnchorSize = isInRange && anchorIndex !== void 0 && anchorIndex >= 0 && params.index >= anchorIndex && !props.getFixedItemSize && !state.sizesKnown.has(getId(state, lastIndex));
5211
- return isInRange && !shouldWaitForAnchorSize;
5373
+ scrollToEnd: (options) => runScrollWithPromise(
5374
+ () => {
5375
+ const data = state.props.data;
5376
+ const stylePaddingBottom = state.props.stylePaddingBottom;
5377
+ const index = data.length - 1;
5378
+ if (index !== -1) {
5379
+ const paddingBottom = stylePaddingBottom || 0;
5380
+ const footerSize = peek$(ctx, "footerSize") || 0;
5381
+ scrollToIndex(ctx, {
5382
+ ...options,
5383
+ index,
5384
+ viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
5385
+ viewPosition: 1
5386
+ });
5387
+ return true;
5212
5388
  }
5213
- } : void 0;
5214
- return runScrollWithPromise(() => {
5215
- scrollToIndex(ctx, params);
5216
- return true;
5217
- }, options);
5389
+ return false;
5390
+ },
5391
+ () => isScrollToIndexReady(state.props.data.length - 1, true)
5392
+ ),
5393
+ scrollToIndex: (params) => {
5394
+ return runScrollWithPromise(
5395
+ () => {
5396
+ scrollToIndex(ctx, params);
5397
+ return true;
5398
+ },
5399
+ params.index >= 0 ? () => isScrollToIndexReady(params.index) : void 0
5400
+ );
5218
5401
  },
5219
5402
  scrollToItem: ({ item, ...props }) => runScrollWithPromise(() => {
5220
5403
  const data = state.props.data;
@@ -5470,6 +5653,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5470
5653
  data: dataProp = [],
5471
5654
  dataVersion,
5472
5655
  drawDistance = 250,
5656
+ contentInsetEndAdjustment,
5473
5657
  estimatedItemSize = 100,
5474
5658
  estimatedListSize,
5475
5659
  extraData,
@@ -5477,7 +5661,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5477
5661
  getFixedItemSize,
5478
5662
  getItemType,
5479
5663
  horizontal,
5480
- initialContainerPoolRatio = 2,
5664
+ initialContainerPoolRatio = 3,
5481
5665
  initialScrollAtEnd = false,
5482
5666
  initialScrollIndex: initialScrollIndexProp,
5483
5667
  initialScrollOffset: initialScrollOffsetProp,
@@ -5518,7 +5702,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5518
5702
  stickyIndices: stickyIndicesDeprecated,
5519
5703
  // TODOV3: Remove from v3 release
5520
5704
  style: styleProp,
5521
- suggestEstimatedItemSize,
5522
5705
  useWindowScroll = false,
5523
5706
  viewabilityConfig,
5524
5707
  viewabilityConfigCallbackPairs,
@@ -5579,6 +5762,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5579
5762
  const combinedRef = useCombinedRef(refScroller, refScrollView);
5580
5763
  const keyExtractor = keyExtractorProp != null ? keyExtractorProp : ((_item, index) => index.toString());
5581
5764
  const stickyHeaderIndices = stickyHeaderIndicesProp != null ? stickyHeaderIndicesProp : stickyIndicesDeprecated;
5765
+ const contentInsetEndAdjustmentResolved = Platform.OS === "web" ? contentInsetEndAdjustment : void 0;
5766
+ const previousContentInsetEndAdjustmentRef = React2.useRef(contentInsetEndAdjustmentResolved);
5582
5767
  const alwaysRenderIndices = React2.useMemo(() => {
5583
5768
  const indices = getAlwaysRenderIndices(alwaysRender, dataProp, keyExtractor, anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex);
5584
5769
  return { arr: indices, set: new Set(indices) };
@@ -5657,7 +5842,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5657
5842
  startReachedSnapshotDataChangeEpoch: void 0,
5658
5843
  stickyContainerPool: /* @__PURE__ */ new Set(),
5659
5844
  stickyContainers: /* @__PURE__ */ new Map(),
5660
- timeoutSizeMessage: 0,
5661
5845
  timeouts: /* @__PURE__ */ new Set(),
5662
5846
  totalSize: 0,
5663
5847
  viewabilityConfigCallbackPairs: void 0
@@ -5677,7 +5861,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5677
5861
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
5678
5862
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
5679
5863
  const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
5680
- if (didDataChangeLocal && state.didFinishInitialScroll && ((_f = state.initialScroll) == null ? void 0 : _f.viewPosition) === 1 && state.props.data.length > 0) {
5864
+ if (didDataChangeLocal && !initialScrollAtEnd && state.didFinishInitialScroll && ((_f = state.initialScroll) == null ? void 0 : _f.viewPosition) === 1 && state.props.data.length > 0) {
5681
5865
  clearPreservedInitialScrollTarget(state);
5682
5866
  }
5683
5867
  if (didDataChangeLocal) {
@@ -5698,6 +5882,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5698
5882
  anchoredEndSpace: anchoredEndSpaceResolved,
5699
5883
  animatedProps: animatedPropsInternal,
5700
5884
  contentInset,
5885
+ contentInsetEndAdjustment: contentInsetEndAdjustmentResolved,
5701
5886
  data: dataProp,
5702
5887
  dataVersion,
5703
5888
  drawDistance,
@@ -5731,7 +5916,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5731
5916
  stickyPositionComponentInternal,
5732
5917
  stylePaddingBottom: stylePaddingBottomState,
5733
5918
  stylePaddingTop: stylePaddingTopState,
5734
- suggestEstimatedItemSize: !!suggestEstimatedItemSize,
5735
5919
  useWindowScroll: useWindowScrollResolved
5736
5920
  };
5737
5921
  state.refScroller = refScroller;
@@ -5820,6 +6004,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5820
6004
  didAnchoredEndSpaceAnchorIndexChange,
5821
6005
  numColumnsProp
5822
6006
  ]);
6007
+ React2.useLayoutEffect(() => {
6008
+ const previousContentInsetEndAdjustment = previousContentInsetEndAdjustmentRef.current;
6009
+ previousContentInsetEndAdjustmentRef.current = contentInsetEndAdjustmentResolved;
6010
+ updateContentInsetEndAdjustment(ctx, previousContentInsetEndAdjustment);
6011
+ }, [ctx, contentInsetEndAdjustmentResolved]);
5823
6012
  const onLayoutFooter = React2.useCallback(
5824
6013
  (layout) => {
5825
6014
  if (!usesBootstrapInitialScroll) {