@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.mjs CHANGED
@@ -145,7 +145,10 @@ function useSelector$(signalName, selector) {
145
145
  }
146
146
 
147
147
  // src/state/getContentInsetEnd.ts
148
- function getContentInsetEnd(ctx) {
148
+ function getContentInsetEndAdjustmentEnd(adjustment) {
149
+ return Math.max(0, adjustment != null ? adjustment : 0);
150
+ }
151
+ function getContentInsetEnd(ctx, contentInsetEndAdjustmentOverride) {
149
152
  var _a3, _b;
150
153
  const state = ctx.state;
151
154
  const { props } = state;
@@ -153,14 +156,21 @@ function getContentInsetEnd(ctx) {
153
156
  const contentInset = props.contentInset;
154
157
  const baseInset = contentInset != null ? contentInset : state.nativeContentInset;
155
158
  const baseEndInset = (horizontal ? baseInset == null ? void 0 : baseInset.right : baseInset == null ? void 0 : baseInset.bottom) || 0;
159
+ const contentInsetEndAdjustment = getContentInsetEndAdjustmentEnd(
160
+ contentInsetEndAdjustmentOverride != null ? contentInsetEndAdjustmentOverride : props.contentInsetEndAdjustment
161
+ );
156
162
  const anchoredEndSpaceSize = peek$(ctx, "anchoredEndSpaceSize");
157
163
  const anchoredEndInset = ((_a3 = props.anchoredEndSpace) == null ? void 0 : _a3.includeInEndInset) && anchoredEndSpaceSize ? anchoredEndSpaceSize : 0;
158
164
  const overrideInset = (_b = state.contentInsetOverride) != null ? _b : void 0;
165
+ const adjustedBaseEndInset = baseEndInset + contentInsetEndAdjustment;
159
166
  if (overrideInset) {
160
167
  const mergedInset = { bottom: 0, right: 0, ...baseInset, ...overrideInset };
161
- return Math.max((horizontal ? mergedInset.right : mergedInset.bottom) || 0, anchoredEndInset);
168
+ return Math.max(
169
+ ((horizontal ? mergedInset.right : mergedInset.bottom) || 0) + contentInsetEndAdjustment,
170
+ anchoredEndInset
171
+ );
162
172
  }
163
- return Math.max(baseEndInset, anchoredEndInset);
173
+ return Math.max(adjustedBaseEndInset, anchoredEndInset);
164
174
  }
165
175
 
166
176
  // src/state/getContentSize.ts
@@ -658,8 +668,52 @@ function isInMVCPActiveMode(state) {
658
668
  }
659
669
 
660
670
  // src/components/Container.tsx
671
+ function getContainerPositionStyle({
672
+ columnWrapperStyle,
673
+ horizontal,
674
+ hasItemSeparator,
675
+ numColumns,
676
+ otherAxisPos,
677
+ otherAxisSize
678
+ }) {
679
+ let paddingStyles;
680
+ if (columnWrapperStyle) {
681
+ const { columnGap, rowGap, gap } = columnWrapperStyle;
682
+ if (horizontal) {
683
+ paddingStyles = {
684
+ paddingBottom: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0,
685
+ paddingRight: columnGap || gap || void 0,
686
+ paddingTop: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
687
+ };
688
+ } else {
689
+ paddingStyles = {
690
+ paddingBottom: rowGap || gap || void 0,
691
+ paddingLeft: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0,
692
+ paddingRight: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
693
+ };
694
+ }
695
+ }
696
+ return horizontal ? {
697
+ boxSizing: paddingStyles ? "border-box" : void 0,
698
+ flexDirection: hasItemSeparator ? "row" : void 0,
699
+ height: otherAxisSize,
700
+ left: 0,
701
+ position: "absolute",
702
+ top: otherAxisPos,
703
+ ...paddingStyles || {}
704
+ } : {
705
+ boxSizing: paddingStyles ? "border-box" : void 0,
706
+ left: otherAxisPos,
707
+ position: "absolute",
708
+ right: numColumns > 1 ? null : 0,
709
+ top: 0,
710
+ width: otherAxisSize,
711
+ ...paddingStyles || {}
712
+ };
713
+ }
661
714
  var Container = typedMemo(function Container2({
662
715
  id,
716
+ itemKey,
663
717
  recycleItems,
664
718
  horizontal,
665
719
  getRenderedItem: getRenderedItem2,
@@ -671,11 +725,10 @@ var Container = typedMemo(function Container2({
671
725
  const { columnWrapperStyle, animatedScrollY } = ctx;
672
726
  const positionComponentInternal = ctx.state.props.positionComponentInternal;
673
727
  const stickyPositionComponentInternal = ctx.state.props.stickyPositionComponentInternal;
674
- const [column = 0, span = 1, data, itemKey, numColumns = 1, extraData, isSticky] = useArr$([
728
+ const [column = 0, span = 1, data, numColumns = 1, extraData, isSticky] = useArr$([
675
729
  `containerColumn${id}`,
676
730
  `containerSpan${id}`,
677
731
  `containerItemData${id}`,
678
- `containerItemKey${id}`,
679
732
  "numColumns",
680
733
  "extraData",
681
734
  `containerSticky${id}`
@@ -696,42 +749,17 @@ var Container = typedMemo(function Container2({
696
749
  const resolvedSpan = Math.min(Math.max(span || 1, 1), numColumns);
697
750
  const otherAxisPos = numColumns > 1 ? `${(resolvedColumn - 1) / numColumns * 100}%` : 0;
698
751
  const otherAxisSize = numColumns > 1 ? `${resolvedSpan / numColumns * 100}%` : void 0;
699
- const style = useMemo(() => {
700
- let paddingStyles;
701
- if (columnWrapperStyle) {
702
- const { columnGap, rowGap, gap } = columnWrapperStyle;
703
- if (horizontal) {
704
- paddingStyles = {
705
- paddingBottom: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0,
706
- paddingRight: columnGap || gap || void 0,
707
- paddingTop: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
708
- };
709
- } else {
710
- paddingStyles = {
711
- paddingBottom: rowGap || gap || void 0,
712
- paddingLeft: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0,
713
- paddingRight: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
714
- };
715
- }
716
- }
717
- return horizontal ? {
718
- boxSizing: paddingStyles ? "border-box" : void 0,
719
- flexDirection: ItemSeparatorComponent ? "row" : void 0,
720
- height: otherAxisSize,
721
- left: 0,
722
- position: "absolute",
723
- top: otherAxisPos,
724
- ...paddingStyles || {}
725
- } : {
726
- boxSizing: paddingStyles ? "border-box" : void 0,
727
- left: otherAxisPos,
728
- position: "absolute",
729
- right: numColumns > 1 ? null : 0,
730
- top: 0,
731
- width: otherAxisSize,
732
- ...paddingStyles || {}
733
- };
734
- }, [horizontal, otherAxisPos, otherAxisSize, columnWrapperStyle, numColumns]);
752
+ const style = useMemo(
753
+ () => getContainerPositionStyle({
754
+ columnWrapperStyle,
755
+ hasItemSeparator: !!ItemSeparatorComponent,
756
+ horizontal,
757
+ numColumns,
758
+ otherAxisPos,
759
+ otherAxisSize
760
+ }),
761
+ [horizontal, otherAxisPos, otherAxisSize, columnWrapperStyle, numColumns, ItemSeparatorComponent]
762
+ );
735
763
  const renderedItemInfo = useMemo(
736
764
  () => itemKey !== void 0 ? getRenderedItem2(itemKey) : null,
737
765
  [itemKey, data, extraData]
@@ -818,6 +846,39 @@ var Container = typedMemo(function Container2({
818
846
  );
819
847
  });
820
848
 
849
+ // src/components/ContainerSlot.tsx
850
+ function ContainerSlotBase({
851
+ id,
852
+ horizontal,
853
+ recycleItems,
854
+ ItemSeparatorComponent,
855
+ updateItemSize: updateItemSize2,
856
+ getRenderedItem: getRenderedItem2,
857
+ stickyHeaderConfig,
858
+ ContainerComponent = Container
859
+ }) {
860
+ const [itemKey] = useArr$([`containerItemKey${id}`]);
861
+ if (itemKey === void 0) {
862
+ return null;
863
+ }
864
+ return /* @__PURE__ */ React3.createElement(
865
+ ContainerComponent,
866
+ {
867
+ getRenderedItem: getRenderedItem2,
868
+ horizontal,
869
+ ItemSeparatorComponent,
870
+ id,
871
+ itemKey,
872
+ recycleItems,
873
+ stickyHeaderConfig,
874
+ updateItemSize: updateItemSize2
875
+ }
876
+ );
877
+ }
878
+ var ContainerSlot = typedMemo(function ContainerSlot2(props) {
879
+ return /* @__PURE__ */ React3.createElement(ContainerSlotBase, { ...props });
880
+ });
881
+
821
882
  // src/utils/reordering.ts
822
883
  var mapFn = (element) => {
823
884
  const indexStr = element.getAttribute("data-index");
@@ -937,9 +998,12 @@ var ContainersInner = typedMemo(function ContainersInner2({ horizontal, numColum
937
998
  const ref = useRef(null);
938
999
  const ctx = useStateContext();
939
1000
  const columnWrapperStyle = ctx.columnWrapperStyle;
940
- const [otherAxisSize, totalSize] = useArr$(["otherAxisSize", "totalSize"]);
1001
+ const [otherAxisSize, readyToRender, totalSize] = useArr$(["otherAxisSize", "readyToRender", "totalSize"]);
941
1002
  useDOMOrder(ref);
942
- const style = horizontal ? { minHeight: otherAxisSize, position: "relative", width: totalSize } : { height: totalSize, minWidth: otherAxisSize, position: "relative" };
1003
+ const style = horizontal ? { minHeight: otherAxisSize, opacity: readyToRender ? 1 : 0, position: "relative", width: totalSize } : { height: totalSize, minWidth: otherAxisSize, opacity: readyToRender ? 1 : 0, position: "relative" };
1004
+ if (!readyToRender) {
1005
+ style.pointerEvents = "none";
1006
+ }
943
1007
  if (columnWrapperStyle && numColumns > 1) {
944
1008
  const { columnGap, rowGap, gap } = columnWrapperStyle;
945
1009
  const gapX = columnGap || gap || 0;
@@ -967,12 +1031,12 @@ var Containers = typedMemo(function Containers2({
967
1031
  getRenderedItem: getRenderedItem2,
968
1032
  stickyHeaderConfig
969
1033
  }) {
970
- const [numContainers, numColumns] = useArr$(["numContainersPooled", "numColumns"]);
1034
+ const [numContainersPooled, numColumns] = useArr$(["numContainersPooled", "numColumns"]);
971
1035
  const containers = [];
972
- for (let i = 0; i < numContainers; i++) {
1036
+ for (let i = 0; i < numContainersPooled; i++) {
973
1037
  containers.push(
974
1038
  /* @__PURE__ */ React3.createElement(
975
- Container,
1039
+ ContainerSlot,
976
1040
  {
977
1041
  getRenderedItem: getRenderedItem2,
978
1042
  horizontal,
@@ -1042,6 +1106,11 @@ function useRafCoalescer(callback) {
1042
1106
  return coalescer;
1043
1107
  }
1044
1108
 
1109
+ // src/components/webConstants.ts
1110
+ var LEGEND_LIST_CONTENT_CONTAINER_CLASS = "legend-list-content-container";
1111
+ var LEGEND_LIST_SCROLLBAR_X_HIDDEN_CLASS = "legend-list-scrollbar-x-hidden";
1112
+ var LEGEND_LIST_SCROLLBAR_Y_HIDDEN_CLASS = "legend-list-scrollbar-y-hidden";
1113
+
1045
1114
  // src/components/webScrollUtils.ts
1046
1115
  function getDocumentScrollerNode() {
1047
1116
  if (typeof document === "undefined") {
@@ -1126,6 +1195,22 @@ function resolveWindowScrollTarget({ clampedOffset, horizontal, listPos, scroll
1126
1195
  }
1127
1196
 
1128
1197
  // src/components/ListComponentScrollView.tsx
1198
+ var SCROLLBAR_HIDDEN_STYLE_ID = "legend-list-scrollbar-axis-hidden-style";
1199
+ var SCROLLBAR_HIDDEN_STYLE = `.${LEGEND_LIST_SCROLLBAR_Y_HIDDEN_CLASS}::-webkit-scrollbar:vertical{width:0;display:none;}.${LEGEND_LIST_SCROLLBAR_X_HIDDEN_CLASS}::-webkit-scrollbar:horizontal{height:0;display:none;}`;
1200
+ function ensureScrollbarHiddenStyle() {
1201
+ if (typeof document === "undefined" || document.getElementById(SCROLLBAR_HIDDEN_STYLE_ID)) {
1202
+ return;
1203
+ }
1204
+ const styleElement = document.createElement("style");
1205
+ styleElement.id = SCROLLBAR_HIDDEN_STYLE_ID;
1206
+ styleElement.textContent = SCROLLBAR_HIDDEN_STYLE;
1207
+ document.head.appendChild(styleElement);
1208
+ }
1209
+ function getContentInsetEndAdjustmentEnd2(ctx) {
1210
+ var _a3, _b;
1211
+ const adjustment = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.props) == null ? void 0 : _b.contentInsetEndAdjustment;
1212
+ return Math.max(0, adjustment != null ? adjustment : 0);
1213
+ }
1129
1214
  var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
1130
1215
  children,
1131
1216
  style,
@@ -1143,7 +1228,9 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
1143
1228
  onLayout,
1144
1229
  ...props
1145
1230
  }, ref) {
1231
+ var _a3, _b, _c;
1146
1232
  const ctx = useStateContext();
1233
+ const [anchoredEndSpaceSize] = useArr$(["anchoredEndSpaceSize"]);
1147
1234
  const scrollRef = useRef(null);
1148
1235
  const contentRef = useRef(null);
1149
1236
  const isWindowScroll = useWindowScroll;
@@ -1205,10 +1292,9 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
1205
1292
  useImperativeHandle(ref, () => {
1206
1293
  const api = {
1207
1294
  getBoundingClientRect: () => {
1208
- var _a3;
1209
- return (_a3 = scrollRef.current) == null ? void 0 : _a3.getBoundingClientRect();
1295
+ var _a4;
1296
+ return (_a4 = scrollRef.current) == null ? void 0 : _a4.getBoundingClientRect();
1210
1297
  },
1211
- getContentNode: () => contentRef.current,
1212
1298
  getCurrentScrollOffset,
1213
1299
  getScrollableNode: () => resolveScrollableNode(scrollRef.current, isWindowScroll),
1214
1300
  getScrollEventTarget: () => getScrollTarget(),
@@ -1324,6 +1410,12 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
1324
1410
  }
1325
1411
  };
1326
1412
  }, [isWindowScroll, onLayout]);
1413
+ const hiddenScrollIndicatorClassName = !isWindowScroll && (horizontal ? !showsHorizontalScrollIndicator && LEGEND_LIST_SCROLLBAR_X_HIDDEN_CLASS : !showsVerticalScrollIndicator && LEGEND_LIST_SCROLLBAR_Y_HIDDEN_CLASS);
1414
+ useLayoutEffect(() => {
1415
+ if (hiddenScrollIndicatorClassName) {
1416
+ ensureScrollbarHiddenStyle();
1417
+ }
1418
+ }, [hiddenScrollIndicatorClassName]);
1327
1419
  const scrollViewStyle = {
1328
1420
  ...isWindowScroll ? {} : {
1329
1421
  overflow: "auto",
@@ -1334,6 +1426,10 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
1334
1426
  },
1335
1427
  ...StyleSheet.flatten(style)
1336
1428
  };
1429
+ const contentInsetEndAdjustment = getContentInsetEndAdjustmentEnd2(ctx);
1430
+ const anchoredEndInset = ((_c = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.props) == null ? void 0 : _b.anchoredEndSpace) == null ? void 0 : _c.includeInEndInset) && anchoredEndSpaceSize ? anchoredEndSpaceSize : 0;
1431
+ const renderedContentInsetEndAdjustment = Math.max(0, contentInsetEndAdjustment - anchoredEndInset);
1432
+ const contentInsetEndAdjustmentSpacerStyle = renderedContentInsetEndAdjustment ? horizontal ? { flexShrink: 0, width: renderedContentInsetEndAdjustment } : { height: renderedContentInsetEndAdjustment } : void 0;
1337
1433
  const contentStyle = {
1338
1434
  display: horizontal ? "flex" : "block",
1339
1435
  flexDirection: horizontal ? "row" : void 0,
@@ -1341,15 +1437,38 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
1341
1437
  minWidth: horizontal ? "100%" : void 0,
1342
1438
  ...StyleSheet.flatten(contentContainerStyle)
1343
1439
  };
1440
+ const className = contentContainerClassName ? `${LEGEND_LIST_CONTENT_CONTAINER_CLASS} ${contentContainerClassName}` : LEGEND_LIST_CONTENT_CONTAINER_CLASS;
1344
1441
  const {
1345
1442
  contentContainerClassName: _contentContainerClassName,
1346
1443
  contentInset: _contentInset,
1347
1444
  scrollEventThrottle: _scrollEventThrottle,
1348
1445
  ScrollComponent: _ScrollComponent,
1349
1446
  useWindowScroll: _useWindowScroll,
1447
+ className: scrollViewClassNameProp,
1350
1448
  ...webProps
1351
1449
  } = props;
1352
- return /* @__PURE__ */ React3.createElement("div", { ref: scrollRef, ...webProps, style: scrollViewStyle }, refreshControl, /* @__PURE__ */ React3.createElement("div", { className: contentContainerClassName, ref: contentRef, style: contentStyle }, children));
1450
+ const scrollViewClassName = hiddenScrollIndicatorClassName ? scrollViewClassNameProp ? `${scrollViewClassNameProp} ${hiddenScrollIndicatorClassName}` : hiddenScrollIndicatorClassName : scrollViewClassNameProp;
1451
+ if (IS_DEV) {
1452
+ if (/(?:^|\s)(?:[a-z0-9_-]+:)*gap(?:-[xy])?-(?:\[[^\]]+\]|[^\s]+)/.test(
1453
+ `${contentContainerClassName != null ? contentContainerClassName : ""} ${scrollViewClassNameProp != null ? scrollViewClassNameProp : ""}`
1454
+ )) {
1455
+ warnDevOnce(
1456
+ "className-gap",
1457
+ "className/contentContainerClassName gap classes are not supported in LegendList because it needs to use exact values internally. Use contentContainerStyle={{ gap: ... }} or columnWrapperStyle instead."
1458
+ );
1459
+ }
1460
+ }
1461
+ return /* @__PURE__ */ React3.createElement(
1462
+ "div",
1463
+ {
1464
+ className: scrollViewClassName,
1465
+ ref: scrollRef,
1466
+ ...webProps,
1467
+ style: scrollViewStyle
1468
+ },
1469
+ refreshControl,
1470
+ /* @__PURE__ */ React3.createElement("div", { className, ref: contentRef, style: contentStyle }, children, contentInsetEndAdjustmentSpacerStyle ? /* @__PURE__ */ React3.createElement("div", { "aria-hidden": true, style: contentInsetEndAdjustmentSpacerStyle }) : null)
1471
+ );
1353
1472
  });
1354
1473
  function useValueListener$(key, callback) {
1355
1474
  const ctx = useStateContext();
@@ -1377,48 +1496,63 @@ function getScrollAdjustAxis(horizontal) {
1377
1496
  y: 1
1378
1497
  };
1379
1498
  }
1499
+ function getScrollAdjustTarget(ctx, contentNode) {
1500
+ var _a3, _b, _c;
1501
+ const scrollView = (_a3 = ctx.state) == null ? void 0 : _a3.refScroller.current;
1502
+ const scrollElement = (_c = (_b = scrollView == null ? void 0 : scrollView.getScrollableNode) == null ? void 0 : _b.call(scrollView)) != null ? _c : null;
1503
+ let resolvedContentNode = null;
1504
+ if (scrollElement) {
1505
+ resolvedContentNode = (contentNode == null ? void 0 : contentNode.isConnected) && contentNode.parentElement === scrollElement ? contentNode : scrollElement.querySelector(`:scope > .${LEGEND_LIST_CONTENT_CONTAINER_CLASS}`);
1506
+ }
1507
+ return scrollElement ? { contentNode: resolvedContentNode, scrollElement } : null;
1508
+ }
1509
+ function scrollAdjustBy(el, left, top) {
1510
+ el.scrollBy({ behavior: "auto", left, top });
1511
+ }
1380
1512
  function ScrollAdjust() {
1381
1513
  const ctx = useStateContext();
1382
1514
  const lastScrollOffsetRef = React3.useRef(0);
1383
1515
  const resetPaddingRafRef = React3.useRef(void 0);
1384
1516
  const resetPaddingBaselineRef = React3.useRef(void 0);
1517
+ const contentNodeRef = React3.useRef(null);
1385
1518
  const callback = React3.useCallback(() => {
1386
- var _a3, _b;
1519
+ var _a3;
1387
1520
  const scrollAdjust = peek$(ctx, "scrollAdjust");
1388
1521
  const scrollAdjustUserOffset = peek$(ctx, "scrollAdjustUserOffset");
1389
1522
  const scrollOffset = (scrollAdjust || 0) + (scrollAdjustUserOffset || 0);
1390
- const scrollView = (_a3 = ctx.state) == null ? void 0 : _a3.refScroller.current;
1391
- if (scrollView && scrollOffset !== lastScrollOffsetRef.current) {
1392
- const scrollDelta = scrollOffset - lastScrollOffsetRef.current;
1393
- if (scrollDelta !== 0) {
1394
- const axis = getScrollAdjustAxis(!!ctx.state.props.horizontal);
1395
- const contentNode = scrollView.getContentNode();
1396
- const prevScroll = scrollView.getCurrentScrollOffset();
1397
- const el = scrollView.getScrollableNode();
1398
- const scrollBy = () => scrollView.scrollBy(axis.x * scrollDelta, axis.y * scrollDelta);
1399
- if (!contentNode) {
1400
- scrollBy();
1401
- lastScrollOffsetRef.current = scrollOffset;
1402
- return;
1403
- }
1404
- const totalSize = contentNode[axis.contentSizeKey];
1405
- const viewportSize = el[axis.viewportSizeKey];
1406
- const nextScroll = prevScroll + scrollDelta;
1407
- if (scrollDelta > 0 && !ctx.state.adjustingFromInitialMount && totalSize < nextScroll + viewportSize) {
1408
- const previousPaddingEnd = (_b = resetPaddingBaselineRef.current) != null ? _b : contentNode.style[axis.paddingEndProp];
1409
- resetPaddingBaselineRef.current = previousPaddingEnd;
1410
- const pad = (nextScroll + viewportSize - totalSize) * 2;
1411
- contentNode.style[axis.paddingEndProp] = `${pad}px`;
1412
- void contentNode.offsetHeight;
1413
- scrollBy();
1414
- if (resetPaddingRafRef.current !== void 0) {
1415
- cancelAnimationFrame(resetPaddingRafRef.current);
1523
+ const scrollDelta = scrollOffset - lastScrollOffsetRef.current;
1524
+ if (scrollDelta !== 0) {
1525
+ const target = getScrollAdjustTarget(ctx, contentNodeRef.current);
1526
+ if (target) {
1527
+ const horizontal = !!ctx.state.props.horizontal;
1528
+ const axis = getScrollAdjustAxis(horizontal);
1529
+ const { contentNode, scrollElement: el } = target;
1530
+ const scrollBy = () => scrollAdjustBy(el, axis.x * scrollDelta, axis.y * scrollDelta);
1531
+ contentNodeRef.current = contentNode;
1532
+ if (contentNode) {
1533
+ const prevScroll = horizontal ? el.scrollLeft : el.scrollTop;
1534
+ const totalSize = contentNode[axis.contentSizeKey];
1535
+ const viewportSize = el[axis.viewportSizeKey];
1536
+ const nextScroll = prevScroll + scrollDelta;
1537
+ const needsTemporaryPadding = scrollDelta > 0 && !ctx.state.adjustingFromInitialMount && totalSize < nextScroll + viewportSize;
1538
+ if (needsTemporaryPadding) {
1539
+ const previousPaddingEnd = (_a3 = resetPaddingBaselineRef.current) != null ? _a3 : contentNode.style[axis.paddingEndProp];
1540
+ resetPaddingBaselineRef.current = previousPaddingEnd;
1541
+ const pad = (nextScroll + viewportSize - totalSize) * 2;
1542
+ contentNode.style[axis.paddingEndProp] = `${pad}px`;
1543
+ void contentNode.offsetHeight;
1544
+ scrollBy();
1545
+ if (resetPaddingRafRef.current !== void 0) {
1546
+ cancelAnimationFrame(resetPaddingRafRef.current);
1547
+ }
1548
+ resetPaddingRafRef.current = requestAnimationFrame(() => {
1549
+ resetPaddingRafRef.current = void 0;
1550
+ resetPaddingBaselineRef.current = void 0;
1551
+ contentNode.style[axis.paddingEndProp] = previousPaddingEnd;
1552
+ });
1553
+ } else {
1554
+ scrollBy();
1416
1555
  }
1417
- resetPaddingRafRef.current = requestAnimationFrame(() => {
1418
- resetPaddingRafRef.current = void 0;
1419
- resetPaddingBaselineRef.current = void 0;
1420
- contentNode.style[axis.paddingEndProp] = previousPaddingEnd;
1421
- });
1422
1556
  } else {
1423
1557
  scrollBy();
1424
1558
  }
@@ -1729,10 +1863,9 @@ var initialScrollWatchdog = {
1729
1863
  clear(state) {
1730
1864
  initialScrollWatchdog.set(state, void 0);
1731
1865
  },
1732
- didObserveProgress(newScroll, watchdog) {
1733
- const previousDistance = Math.abs(watchdog.startScroll - watchdog.targetOffset);
1866
+ didReachTarget(newScroll, watchdog) {
1734
1867
  const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
1735
- return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET || nextDistance + INITIAL_SCROLL_MIN_TARGET_OFFSET < previousDistance;
1868
+ return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
1736
1869
  },
1737
1870
  get(state) {
1738
1871
  var _a3, _b;
@@ -1757,19 +1890,19 @@ var initialScrollWatchdog = {
1757
1890
  }
1758
1891
  };
1759
1892
  function setInitialScrollSession(state, options = {}) {
1760
- var _a3, _b, _c;
1893
+ var _a3, _b, _c, _d;
1761
1894
  const existingSession = state.initialScrollSession;
1762
1895
  const kind = (_a3 = options.kind) != null ? _a3 : existingSession == null ? void 0 : existingSession.kind;
1763
1896
  const completion = existingSession == null ? void 0 : existingSession.completion;
1764
- const hasBootstrapOverride = Object.hasOwn(options, "bootstrap");
1765
- const bootstrap = kind === "bootstrap" ? hasBootstrapOverride ? options.bootstrap : (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0 : void 0;
1897
+ const existingBootstrap = (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0;
1898
+ const bootstrap = kind === "bootstrap" ? options.bootstrap === null ? void 0 : (_b = options.bootstrap) != null ? _b : existingBootstrap : void 0;
1766
1899
  if (!kind) {
1767
1900
  return clearInitialScrollSession(state);
1768
1901
  }
1769
1902
  if (!state.initialScroll && !bootstrap && !hasInitialScrollSessionCompletion(completion)) {
1770
1903
  return clearInitialScrollSession(state);
1771
1904
  }
1772
- const previousDataLength = (_c = (_b = options.previousDataLength) != null ? _b : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _c : 0;
1905
+ const previousDataLength = (_d = (_c = options.previousDataLength) != null ? _c : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _d : 0;
1773
1906
  state.initialScrollSession = createInitialScrollSession({
1774
1907
  bootstrap,
1775
1908
  completion,
@@ -2521,7 +2654,7 @@ function advanceMeasuredInitialScroll(ctx, options) {
2521
2654
  const activeInitialTargetOffset = scrollingTo ? (_a3 = scrollingTo.targetOffset) != null ? _a3 : scrollingTo.offset : void 0;
2522
2655
  const didOffsetChange = initialScroll.contentOffset === void 0 || Math.abs(initialScroll.contentOffset - resolvedOffset) > 1;
2523
2656
  const didActiveInitialTargetChange = activeInitialTargetOffset !== void 0 && Math.abs(activeInitialTargetOffset - resolvedOffset) > 1;
2524
- const isAlreadyAtDesiredInitialTarget = activeInitialTargetOffset !== void 0 && Math.abs(state.scroll - activeInitialTargetOffset) <= 1 && Math.abs(state.scrollPending - activeInitialTargetOffset) <= 1;
2657
+ const isAlreadyAtDesiredInitialTarget = activeInitialTargetOffset !== void 0 && Math.abs(state.scroll - resolvedOffset) <= 1 && Math.abs(state.scrollPending - resolvedOffset) <= 1;
2525
2658
  if (!(options == null ? void 0 : options.forceScroll) && !didOffsetChange && isInitialScrollInProgress && !didActiveInitialTargetChange) {
2526
2659
  return false;
2527
2660
  }
@@ -2593,6 +2726,30 @@ function checkAllSizesKnown(state, indices) {
2593
2726
  });
2594
2727
  }
2595
2728
 
2729
+ // src/utils/requestAdjust.ts
2730
+ function requestAdjust(ctx, positionDiff, dataChanged) {
2731
+ const state = ctx.state;
2732
+ if (Math.abs(positionDiff) > 0.1) {
2733
+ const doit = () => {
2734
+ {
2735
+ state.scrollAdjustHandler.requestAdjust(positionDiff);
2736
+ if (state.adjustingFromInitialMount) {
2737
+ state.adjustingFromInitialMount--;
2738
+ }
2739
+ }
2740
+ };
2741
+ state.scroll += positionDiff;
2742
+ state.scrollForNextCalculateItemsInView = void 0;
2743
+ const readyToRender = peek$(ctx, "readyToRender");
2744
+ if (readyToRender) {
2745
+ doit();
2746
+ } else {
2747
+ state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
2748
+ requestAnimationFrame(doit);
2749
+ }
2750
+ }
2751
+ }
2752
+
2596
2753
  // src/core/bootstrapInitialScroll.ts
2597
2754
  var DEFAULT_BOOTSTRAP_REVEAL_EPSILON = 1;
2598
2755
  var DEFAULT_BOOTSTRAP_REVEAL_MAX_FRAMES = 8;
@@ -2686,7 +2843,7 @@ function clearBootstrapInitialScrollSession(state) {
2686
2843
  bootstrapInitialScroll.frameHandle = void 0;
2687
2844
  }
2688
2845
  setInitialScrollSession(state, {
2689
- bootstrap: void 0,
2846
+ bootstrap: null,
2690
2847
  kind: (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind
2691
2848
  });
2692
2849
  }
@@ -2842,15 +2999,18 @@ function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {
2842
2999
  return;
2843
3000
  }
2844
3001
  if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2845
- if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {
2846
- clearPendingInitialScrollFooterLayout(ctx, {
2847
- dataLength: state.props.data.length,
2848
- stylePaddingBottom: (_b = state.props.stylePaddingBottom) != null ? _b : 0,
2849
- target: initialScroll
2850
- });
2851
- return;
3002
+ const shouldKeepEndTargetAlive = isRetargetableBottomAlignedInitialScrollTarget(initialScroll) && peek$(ctx, "isAtEnd");
3003
+ if (!shouldKeepEndTargetAlive) {
3004
+ if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {
3005
+ clearPendingInitialScrollFooterLayout(ctx, {
3006
+ dataLength: state.props.data.length,
3007
+ stylePaddingBottom: (_b = state.props.stylePaddingBottom) != null ? _b : 0,
3008
+ target: initialScroll
3009
+ });
3010
+ } else {
3011
+ clearFinishedViewportRetargetableInitialScroll(state);
3012
+ }
2852
3013
  }
2853
- clearFinishedViewportRetargetableInitialScroll(state);
2854
3014
  }
2855
3015
  }
2856
3016
  function startBootstrapInitialScrollOnMount(ctx, options) {
@@ -2889,7 +3049,7 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
2889
3049
  }
2890
3050
  const shouldResetDidFinish = !!(state.didFinishInitialScroll && previousDataLength === 0 && dataLength > 0 && initialScroll.index !== void 0);
2891
3051
  const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2892
- const shouldClearFinishedResizePreservation = didDataChange && dataLength > 0 && state.didFinishInitialScroll && !bootstrapInitialScroll && !shouldResetDidFinish;
3052
+ const shouldClearFinishedResizePreservation = !initialScrollAtEnd && didDataChange && dataLength > 0 && state.didFinishInitialScroll && !bootstrapInitialScroll && !shouldResetDidFinish;
2893
3053
  if (shouldClearFinishedResizePreservation) {
2894
3054
  clearPreservedInitialScrollTarget(state);
2895
3055
  return;
@@ -2992,27 +3152,46 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2992
3152
  }
2993
3153
  }
2994
3154
  function handleBootstrapInitialScrollLayoutChange(ctx) {
3155
+ var _a3, _b, _c, _d;
2995
3156
  const state = ctx.state;
2996
3157
  const initialScroll = state.initialScroll;
2997
- if (isOffsetInitialScrollSession(state) || state.props.data.length === 0 || !initialScroll) {
2998
- return;
2999
- }
3000
3158
  const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
3001
- if (!bootstrapInitialScroll && initialScroll.viewPosition !== 1) {
3002
- return;
3003
- }
3004
- const didFinishInitialScroll = state.didFinishInitialScroll;
3005
- if (didFinishInitialScroll) {
3006
- setInitialScrollTarget(state, initialScroll, {
3007
- resetDidFinish: true
3008
- });
3009
- state.clearPreservedInitialScrollOnNextFinish = true;
3159
+ if (initialScroll && state.props.data.length > 0 && !isOffsetInitialScrollSession(state) && (bootstrapInitialScroll || initialScroll.viewPosition === 1)) {
3160
+ const resolvedOffset = resolveInitialScrollOffset(ctx, initialScroll);
3161
+ const scrollingTo = ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) ? state.scrollingTo : void 0;
3162
+ if (!bootstrapInitialScroll && (scrollingTo || state.didFinishInitialScroll)) {
3163
+ const currentOffset = scrollingTo ? (_b = scrollingTo.targetOffset) != null ? _b : scrollingTo.offset : getObservedBootstrapInitialScrollOffset(state);
3164
+ const offsetDiff = resolvedOffset - currentOffset;
3165
+ if (Math.abs(offsetDiff) > DEFAULT_BOOTSTRAP_REVEAL_EPSILON) {
3166
+ if (scrollingTo) {
3167
+ const existingWatchdog = initialScrollWatchdog.get(state);
3168
+ scrollingTo.offset = resolvedOffset;
3169
+ scrollingTo.targetOffset = resolvedOffset;
3170
+ state.initialScroll = {
3171
+ ...initialScroll,
3172
+ contentOffset: resolvedOffset
3173
+ };
3174
+ state.hasScrolled = false;
3175
+ initialScrollWatchdog.set(state, {
3176
+ startScroll: (_c = existingWatchdog == null ? void 0 : existingWatchdog.startScroll) != null ? _c : state.scroll,
3177
+ targetOffset: resolvedOffset
3178
+ });
3179
+ }
3180
+ requestAdjust(ctx, offsetDiff);
3181
+ if (state.didFinishInitialScroll) {
3182
+ (_d = state.triggerCalculateItemsInView) == null ? void 0 : _d.call(state, { forceFullItemPositions: true });
3183
+ }
3184
+ }
3185
+ if (state.didFinishInitialScroll) {
3186
+ clearFinishedViewportRetargetableInitialScroll(state);
3187
+ }
3188
+ } else {
3189
+ rearmBootstrapInitialScroll(ctx, {
3190
+ scroll: resolvedOffset,
3191
+ targetIndexSeed: initialScroll.index
3192
+ });
3193
+ }
3010
3194
  }
3011
- rearmBootstrapInitialScroll(ctx, {
3012
- scroll: resolveInitialScrollOffset(ctx, initialScroll),
3013
- seedContentOffset: didFinishInitialScroll && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
3014
- targetIndexSeed: initialScroll.index
3015
- });
3016
3195
  }
3017
3196
  function evaluateBootstrapInitialScroll(ctx) {
3018
3197
  var _a3, _b;
@@ -3219,7 +3398,7 @@ function checkFinishedScrollFallback(ctx) {
3219
3398
  state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, delay);
3220
3399
  };
3221
3400
  const checkHasScrolled = () => {
3222
- var _c;
3401
+ var _c, _d;
3223
3402
  state.timeoutCheckFinishedScrollFallback = void 0;
3224
3403
  const isStillScrollingTo = state.scrollingTo;
3225
3404
  if (isStillScrollingTo) {
@@ -3232,11 +3411,13 @@ function checkFinishedScrollFallback(ctx) {
3232
3411
  isStillScrollingTo
3233
3412
  );
3234
3413
  const completionState = getResolvedScrollCompletionState(ctx, isStillScrollingTo);
3235
- const canFinishAfterSilentNativeDispatch = silentInitialDispatch && completionState.isAtResolvedTarget && numChecks >= 1;
3236
- if (shouldFinishZeroTarget || state.hasScrolled || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
3414
+ const canFinishAfterSilentNativeDispatch = Platform.OS === "android";
3415
+ const shouldFinishAfterObservedScroll = state.hasScrolled && (!isStillScrollingTo.isInitialScroll || completionState.isAtResolvedTarget);
3416
+ const shouldRetryUnalignedInitialScroll = isStillScrollingTo.isInitialScroll && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
3417
+ if (shouldFinishZeroTarget || shouldFinishAfterObservedScroll || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
3237
3418
  finishScrollTo(ctx);
3238
- } else if (isNativeInitialPending && numChecks <= maxChecks) {
3239
- const targetOffset = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : state.scrollPending;
3419
+ } else if ((isNativeInitialPending || shouldRetryUnalignedInitialScroll) && numChecks <= maxChecks) {
3420
+ const targetOffset = (_d = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : isStillScrollingTo.targetOffset) != null ? _d : state.scrollPending;
3240
3421
  scrollToFallbackOffset(ctx, targetOffset);
3241
3422
  scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
3242
3423
  } else {
@@ -3249,6 +3430,15 @@ function checkFinishedScrollFallback(ctx) {
3249
3430
  }
3250
3431
 
3251
3432
  // src/core/initialScrollLifecycle.ts
3433
+ function retargetActiveInitialScrollAtEnd(ctx) {
3434
+ var _a3;
3435
+ const state = ctx.state;
3436
+ const initialScroll = state.initialScroll;
3437
+ if (!initialScroll || state.didFinishInitialScroll || ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" || initialScroll.viewPosition !== 1 || state.props.data.length === 0) {
3438
+ return false;
3439
+ }
3440
+ return advanceCurrentInitialScrollSession(ctx, { forceScroll: true });
3441
+ }
3252
3442
  function handleInitialScrollLayoutReady(ctx) {
3253
3443
  var _a3;
3254
3444
  if (!ctx.state.initialScroll) {
@@ -3322,30 +3512,6 @@ function handleInitialScrollDataChange(ctx, options) {
3322
3512
  advanceCurrentInitialScrollSession(ctx);
3323
3513
  }
3324
3514
 
3325
- // src/utils/requestAdjust.ts
3326
- function requestAdjust(ctx, positionDiff, dataChanged) {
3327
- const state = ctx.state;
3328
- if (Math.abs(positionDiff) > 0.1) {
3329
- const doit = () => {
3330
- {
3331
- state.scrollAdjustHandler.requestAdjust(positionDiff);
3332
- if (state.adjustingFromInitialMount) {
3333
- state.adjustingFromInitialMount--;
3334
- }
3335
- }
3336
- };
3337
- state.scroll += positionDiff;
3338
- state.scrollForNextCalculateItemsInView = void 0;
3339
- const readyToRender = peek$(ctx, "readyToRender");
3340
- if (readyToRender) {
3341
- doit();
3342
- } else {
3343
- state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
3344
- requestAnimationFrame(doit);
3345
- }
3346
- }
3347
- }
3348
-
3349
3515
  // src/core/mvcp.ts
3350
3516
  var MVCP_POSITION_EPSILON = 0.1;
3351
3517
  var MVCP_ANCHOR_LOCK_TTL_MS = 300;
@@ -3619,7 +3785,10 @@ function prepareMVCP(ctx, dataChanged) {
3619
3785
  return;
3620
3786
  }
3621
3787
  if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
3622
- requestAdjust(ctx, positionDiff);
3788
+ const shouldSkipAdjustForMaintainedEnd = state.maintainingScrollAtEnd && peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
3789
+ if (!shouldSkipAdjustForMaintainedEnd) {
3790
+ requestAdjust(ctx, positionDiff);
3791
+ }
3623
3792
  }
3624
3793
  };
3625
3794
  }
@@ -4238,6 +4407,30 @@ function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
4238
4407
  var unstableBatchedUpdates = ReactDOM.unstable_batchedUpdates;
4239
4408
  var batchedUpdates = typeof unstableBatchedUpdates === "function" ? unstableBatchedUpdates : (fn) => fn();
4240
4409
 
4410
+ // src/utils/containerPool.ts
4411
+ var MIN_INITIAL_CONTAINER_POOL_SIZE = 32;
4412
+ var MAX_INITIAL_SPARE_CONTAINERS = 64;
4413
+ function getInitialContainerPoolSize(dataLength, numContainers, initialContainerPoolRatio) {
4414
+ if (dataLength <= 0 || numContainers <= 0) {
4415
+ return 0;
4416
+ }
4417
+ const ratioPoolSize = Math.ceil(numContainers * initialContainerPoolRatio);
4418
+ const cappedSparePoolSize = numContainers + MAX_INITIAL_SPARE_CONTAINERS;
4419
+ const targetPoolSize = Math.max(
4420
+ numContainers,
4421
+ Math.min(ratioPoolSize, cappedSparePoolSize),
4422
+ Math.min(dataLength, MIN_INITIAL_CONTAINER_POOL_SIZE)
4423
+ );
4424
+ const maxUsefulPoolSize = Math.max(dataLength, numContainers);
4425
+ return Math.min(maxUsefulPoolSize, targetPoolSize);
4426
+ }
4427
+ function getExpandedContainerPoolSize(dataLength, numContainers) {
4428
+ if (dataLength <= 0 || numContainers <= 0) {
4429
+ return 0;
4430
+ }
4431
+ return Math.min(Math.max(dataLength, numContainers), Math.max(numContainers, Math.ceil(numContainers * 1.5)));
4432
+ }
4433
+
4241
4434
  // src/utils/findAvailableContainers.ts
4242
4435
  function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers, protectedKeys) {
4243
4436
  const numContainers = peek$(ctx, "numContainers");
@@ -4443,7 +4636,7 @@ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentSt
4443
4636
  function calculateItemsInView(ctx, params = {}) {
4444
4637
  const state = ctx.state;
4445
4638
  batchedUpdates(() => {
4446
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
4639
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q;
4447
4640
  const {
4448
4641
  columns,
4449
4642
  columnSpans,
@@ -4490,12 +4683,22 @@ function calculateItemsInView(ctx, params = {}) {
4490
4683
  // current initial-scroll target instead of transient native adjustments.
4491
4684
  resolveInitialScrollOffset(ctx, initialScroll)
4492
4685
  ) : state.scroll;
4493
- const scrollAdjustPending = (_c = peek$(ctx, "scrollAdjustPending")) != null ? _c : 0;
4494
- const scrollAdjustPad = scrollAdjustPending - topPad;
4495
- let scroll = Math.round(scrollState + scrollExtra + scrollAdjustPad);
4496
- if (scroll + scrollLength > totalSize) {
4497
- scroll = Math.max(0, totalSize - scrollLength);
4498
- }
4686
+ let scrollAdjustPending = 0;
4687
+ let scrollAdjustPad = 0;
4688
+ let scroll = 0;
4689
+ let scrollTopBuffered = 0;
4690
+ let scrollBottom = 0;
4691
+ let scrollBottomBuffered = 0;
4692
+ const updateScroll2 = (nextScrollState) => {
4693
+ var _a4;
4694
+ scrollAdjustPending = (_a4 = peek$(ctx, "scrollAdjustPending")) != null ? _a4 : 0;
4695
+ scrollAdjustPad = scrollAdjustPending - topPad;
4696
+ scroll = Math.round(nextScrollState + scrollExtra + scrollAdjustPad);
4697
+ if (scroll + scrollLength > totalSize) {
4698
+ scroll = Math.max(0, totalSize - scrollLength);
4699
+ }
4700
+ };
4701
+ updateScroll2(scrollState);
4499
4702
  const previousStickyIndex = peek$(ctx, "activeStickyIndex");
4500
4703
  const currentStickyIdx = stickyIndicesArr.length > 0 ? findCurrentStickyIndex(stickyIndicesArr, scroll, state) : -1;
4501
4704
  const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyIndicesArr[currentStickyIdx] : -1;
@@ -4511,9 +4714,12 @@ function calculateItemsInView(ctx, params = {}) {
4511
4714
  scrollBufferTop = drawDistance * 1.5;
4512
4715
  scrollBufferBottom = drawDistance * 0.5;
4513
4716
  }
4514
- const scrollTopBuffered = scroll - scrollBufferTop;
4515
- const scrollBottom = scroll + scrollLength + (scroll < 0 ? -scroll : 0);
4516
- const scrollBottomBuffered = scrollBottom + scrollBufferBottom;
4717
+ const updateScrollRange = () => {
4718
+ scrollTopBuffered = scroll - scrollBufferTop;
4719
+ scrollBottom = scroll + scrollLength + (scroll < 0 ? -scroll : 0);
4720
+ scrollBottomBuffered = scrollBottom + scrollBufferBottom;
4721
+ };
4722
+ updateScrollRange();
4517
4723
  if (!suppressInitialScrollSideEffects && !dataChanged && !forceFullItemPositions && scrollForNextCalculateItemsInView) {
4518
4724
  const { top, bottom } = scrollForNextCalculateItemsInView;
4519
4725
  if (top === null && bottom === null) {
@@ -4532,7 +4738,7 @@ function calculateItemsInView(ctx, params = {}) {
4532
4738
  columns.length = 0;
4533
4739
  columnSpans.length = 0;
4534
4740
  }
4535
- const startIndex = forceFullItemPositions || dataChanged ? 0 : (_d = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _d : 0;
4741
+ const startIndex = forceFullItemPositions || dataChanged ? 0 : (_c = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _c : 0;
4536
4742
  const optimizeForVisibleWindow = !forceFullItemPositions && !dataChanged && numColumns > 1 && minIndexSizeChanged !== void 0;
4537
4743
  updateItemPositions(ctx, dataChanged, {
4538
4744
  doMVCP,
@@ -4557,21 +4763,25 @@ function calculateItemsInView(ctx, params = {}) {
4557
4763
  }
4558
4764
  }
4559
4765
  const scrollBeforeMVCP = state.scroll;
4560
- const scrollAdjustPendingBeforeMVCP = (_e = peek$(ctx, "scrollAdjustPending")) != null ? _e : 0;
4766
+ const scrollAdjustPendingBeforeMVCP = (_d = peek$(ctx, "scrollAdjustPending")) != null ? _d : 0;
4561
4767
  checkMVCP == null ? void 0 : checkMVCP();
4562
- const didMVCPAdjustScroll = !!checkMVCP && (state.scroll !== scrollBeforeMVCP || ((_f = peek$(ctx, "scrollAdjustPending")) != null ? _f : 0) !== scrollAdjustPendingBeforeMVCP);
4768
+ const didMVCPAdjustScroll = !!checkMVCP && (state.scroll !== scrollBeforeMVCP || ((_e = peek$(ctx, "scrollAdjustPending")) != null ? _e : 0) !== scrollAdjustPendingBeforeMVCP);
4769
+ if (didMVCPAdjustScroll && initialScroll) {
4770
+ updateScroll2(state.scroll);
4771
+ updateScrollRange();
4772
+ }
4563
4773
  let startNoBuffer = null;
4564
4774
  let startBuffered = null;
4565
4775
  let startBufferedId = null;
4566
4776
  let endNoBuffer = null;
4567
4777
  let endBuffered = null;
4568
- let loopStart = (_g = suppressInitialScrollSideEffects ? bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.targetIndexSeed : void 0) != null ? _g : !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
4778
+ let loopStart = (_f = suppressInitialScrollSideEffects ? bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.targetIndexSeed : void 0) != null ? _f : !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
4569
4779
  for (let i = loopStart; i >= 0; i--) {
4570
- const id = (_h = idCache[i]) != null ? _h : getId(state, i);
4780
+ const id = (_g = idCache[i]) != null ? _g : getId(state, i);
4571
4781
  const top = positions[i];
4572
- const size = (_i = sizes.get(id)) != null ? _i : getItemSize(ctx, id, i, data[i]);
4782
+ const size = (_h = sizes.get(id)) != null ? _h : getItemSize(ctx, id, i, data[i]);
4573
4783
  const bottom = top + size;
4574
- if (bottom > scroll - scrollBufferTop) {
4784
+ if (bottom > scrollTopBuffered) {
4575
4785
  loopStart = i;
4576
4786
  } else {
4577
4787
  break;
@@ -4600,8 +4810,8 @@ function calculateItemsInView(ctx, params = {}) {
4600
4810
  let firstFullyOnScreenIndex;
4601
4811
  const dataLength = data.length;
4602
4812
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
4603
- const id = (_j = idCache[i]) != null ? _j : getId(state, i);
4604
- const size = (_k = sizes.get(id)) != null ? _k : getItemSize(ctx, id, i, data[i]);
4813
+ const id = (_i = idCache[i]) != null ? _i : getId(state, i);
4814
+ const size = (_j = sizes.get(id)) != null ? _j : getItemSize(ctx, id, i, data[i]);
4605
4815
  const top = positions[i];
4606
4816
  if (!foundEnd) {
4607
4817
  if (startNoBuffer === null && top + size > scroll) {
@@ -4640,7 +4850,7 @@ function calculateItemsInView(ctx, params = {}) {
4640
4850
  const firstVisibleAnchorIndex = firstFullyOnScreenIndex != null ? firstFullyOnScreenIndex : startNoBuffer;
4641
4851
  if (firstVisibleAnchorIndex !== null && firstVisibleAnchorIndex !== void 0 && endNoBuffer !== null) {
4642
4852
  for (let i = firstVisibleAnchorIndex; i <= endNoBuffer; i++) {
4643
- const id = (_l = idCache[i]) != null ? _l : getId(state, i);
4853
+ const id = (_k = idCache[i]) != null ? _k : getId(state, i);
4644
4854
  idsInView.push(id);
4645
4855
  }
4646
4856
  }
@@ -4673,7 +4883,7 @@ function calculateItemsInView(ctx, params = {}) {
4673
4883
  const needNewContainers = [];
4674
4884
  const needNewContainersSet = /* @__PURE__ */ new Set();
4675
4885
  for (let i = startBuffered; i <= endBuffered; i++) {
4676
- const id = (_m = idCache[i]) != null ? _m : getId(state, i);
4886
+ const id = (_l = idCache[i]) != null ? _l : getId(state, i);
4677
4887
  if (!containerItemKeys.has(id)) {
4678
4888
  needNewContainersSet.add(i);
4679
4889
  needNewContainers.push(i);
@@ -4682,7 +4892,7 @@ function calculateItemsInView(ctx, params = {}) {
4682
4892
  if (alwaysRenderArr.length > 0) {
4683
4893
  for (const index of alwaysRenderArr) {
4684
4894
  if (index < 0 || index >= dataLength) continue;
4685
- const id = (_n = idCache[index]) != null ? _n : getId(state, index);
4895
+ const id = (_m = idCache[index]) != null ? _m : getId(state, index);
4686
4896
  if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
4687
4897
  needNewContainersSet.add(index);
4688
4898
  needNewContainers.push(index);
@@ -4721,7 +4931,7 @@ function calculateItemsInView(ctx, params = {}) {
4721
4931
  for (let idx = 0; idx < needNewContainers.length; idx++) {
4722
4932
  const i = needNewContainers[idx];
4723
4933
  const containerIndex = availableContainers[idx];
4724
- const id = (_o = idCache[i]) != null ? _o : getId(state, i);
4934
+ const id = (_n = idCache[i]) != null ? _n : getId(state, i);
4725
4935
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
4726
4936
  if (oldKey && oldKey !== id) {
4727
4937
  containerItemKeys.delete(oldKey);
@@ -4732,7 +4942,7 @@ function calculateItemsInView(ctx, params = {}) {
4732
4942
  state.containerItemTypes.set(containerIndex, requiredItemTypes[idx]);
4733
4943
  }
4734
4944
  containerItemKeys.set(id, containerIndex);
4735
- (_p = state.userScrollAnchorResetKeys) == null ? void 0 : _p.add(id);
4945
+ (_o = state.userScrollAnchorResetKeys) == null ? void 0 : _o.add(id);
4736
4946
  const containerSticky = `containerSticky${containerIndex}`;
4737
4947
  const isSticky = stickyIndicesSet.has(i);
4738
4948
  const isAlwaysRender = alwaysRenderSet.has(i);
@@ -4756,17 +4966,17 @@ function calculateItemsInView(ctx, params = {}) {
4756
4966
  if (numContainers !== prevNumContainers) {
4757
4967
  set$(ctx, "numContainers", numContainers);
4758
4968
  if (numContainers > peek$(ctx, "numContainersPooled")) {
4759
- set$(ctx, "numContainersPooled", Math.ceil(numContainers * 1.5));
4969
+ set$(ctx, "numContainersPooled", getExpandedContainerPoolSize(dataLength, numContainers));
4760
4970
  }
4761
4971
  }
4762
4972
  }
4763
- if (((_q = state.userScrollAnchorResetKeys) == null ? void 0 : _q.size) === 0) {
4973
+ if (((_p = state.userScrollAnchorResetKeys) == null ? void 0 : _p.size) === 0) {
4764
4974
  state.userScrollAnchorResetKeys = void 0;
4765
4975
  }
4766
4976
  if (alwaysRenderArr.length > 0) {
4767
4977
  for (const index of alwaysRenderArr) {
4768
4978
  if (index < 0 || index >= dataLength) continue;
4769
- const id = (_r = idCache[index]) != null ? _r : getId(state, index);
4979
+ const id = (_q = idCache[index]) != null ? _q : getId(state, index);
4770
4980
  const containerIndex = containerItemKeys.get(id);
4771
4981
  if (containerIndex !== void 0) {
4772
4982
  state.stickyContainerPool.add(containerIndex);
@@ -4870,21 +5080,25 @@ function doMaintainScrollAtEnd(ctx) {
4870
5080
  if (contentSize < state.scrollLength) {
4871
5081
  state.scroll = 0;
4872
5082
  }
4873
- requestAnimationFrame(() => {
4874
- var _a3;
4875
- if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
4876
- state.maintainingScrollAtEnd = true;
4877
- (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
4878
- animated: maintainScrollAtEnd.animated
4879
- });
4880
- setTimeout(
4881
- () => {
4882
- state.maintainingScrollAtEnd = false;
4883
- },
4884
- maintainScrollAtEnd.animated ? 500 : 0
4885
- );
4886
- }
4887
- });
5083
+ if (!state.maintainingScrollAtEnd) {
5084
+ state.maintainingScrollAtEnd = true;
5085
+ requestAnimationFrame(() => {
5086
+ var _a3;
5087
+ if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
5088
+ (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
5089
+ animated: maintainScrollAtEnd.animated
5090
+ });
5091
+ setTimeout(
5092
+ () => {
5093
+ state.maintainingScrollAtEnd = false;
5094
+ },
5095
+ maintainScrollAtEnd.animated ? 500 : 0
5096
+ );
5097
+ } else {
5098
+ state.maintainingScrollAtEnd = false;
5099
+ }
5100
+ });
5101
+ }
4888
5102
  return true;
4889
5103
  }
4890
5104
  return false;
@@ -4996,14 +5210,21 @@ function doInitialAllocateContainers(ctx) {
4996
5210
  } else {
4997
5211
  averageItemSize = estimatedItemSize;
4998
5212
  }
4999
- const numContainers = Math.ceil((scrollLength + drawDistance * 2) / averageItemSize * numColumns);
5213
+ const numContainers = Math.max(
5214
+ 1,
5215
+ Math.ceil((scrollLength + drawDistance * 2) / averageItemSize * numColumns)
5216
+ );
5000
5217
  for (let i = 0; i < numContainers; i++) {
5001
5218
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
5002
5219
  set$(ctx, `containerColumn${i}`, -1);
5003
5220
  set$(ctx, `containerSpan${i}`, 1);
5004
5221
  }
5005
5222
  set$(ctx, "numContainers", numContainers);
5006
- set$(ctx, "numContainersPooled", numContainers * state.props.initialContainerPoolRatio);
5223
+ set$(
5224
+ ctx,
5225
+ "numContainersPooled",
5226
+ getInitialContainerPoolSize(data.length, numContainers, state.props.initialContainerPoolRatio)
5227
+ );
5007
5228
  if (state.lastLayout) {
5008
5229
  if (state.initialScroll) {
5009
5230
  requestAnimationFrame(() => {
@@ -5154,8 +5375,8 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
5154
5375
  // src/core/onScroll.ts
5155
5376
  function trackInitialScrollNativeProgress(state, newScroll) {
5156
5377
  const initialNativeScrollWatchdog = initialScrollWatchdog.get(state);
5157
- const didInitialScrollProgress = !!initialNativeScrollWatchdog && initialScrollWatchdog.didObserveProgress(newScroll, initialNativeScrollWatchdog);
5158
- if (didInitialScrollProgress) {
5378
+ const didInitialScrollReachTarget = !!initialNativeScrollWatchdog && initialScrollWatchdog.didReachTarget(newScroll, initialNativeScrollWatchdog);
5379
+ if (didInitialScrollReachTarget) {
5159
5380
  initialScrollWatchdog.clear(state);
5160
5381
  return;
5161
5382
  }
@@ -5293,16 +5514,20 @@ function maybeUpdateAnchoredEndSpace(ctx) {
5293
5514
  let contentBelowAnchor = 0;
5294
5515
  const footerSize = ctx.values.get("footerSize") || 0;
5295
5516
  const stylePaddingBottom = state.props.stylePaddingBottom || 0;
5517
+ let hasUnknownTailSize = false;
5296
5518
  for (let index = anchorIndex; index < data.length; index++) {
5297
5519
  const itemKey = getId(state, index);
5298
5520
  const size = itemKey ? state.sizesKnown.get(itemKey) : void 0;
5299
5521
  const effectiveSize = index === anchorIndex && anchorMaxSize !== void 0 ? Math.min(size || 0, Math.max(0, anchorMaxSize)) : size;
5522
+ if (size === void 0) {
5523
+ hasUnknownTailSize = true;
5524
+ }
5300
5525
  if (effectiveSize !== null && effectiveSize !== void 0 && effectiveSize > 0) {
5301
5526
  contentBelowAnchor += effectiveSize;
5302
5527
  }
5303
5528
  }
5304
5529
  contentBelowAnchor += footerSize + stylePaddingBottom;
5305
- nextSize = Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
5530
+ nextSize = hasUnknownTailSize ? previousSize || 0 : Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
5306
5531
  }
5307
5532
  }
5308
5533
  if (previousSize !== nextSize) {
@@ -5315,6 +5540,22 @@ function maybeUpdateAnchoredEndSpace(ctx) {
5315
5540
  return nextSize;
5316
5541
  }
5317
5542
 
5543
+ // src/core/updateContentInsetEndAdjustment.ts
5544
+ function updateContentInsetEndAdjustment(ctx, previousContentInsetEndAdjustment) {
5545
+ const state = ctx.state;
5546
+ const previousContentInsetEnd = getContentInsetEnd(ctx, previousContentInsetEndAdjustment);
5547
+ const nextContentInsetEnd = getContentInsetEnd(ctx);
5548
+ const insetDiff = nextContentInsetEnd - previousContentInsetEnd;
5549
+ if (insetDiff !== 0) {
5550
+ const wasWithinEndThreshold = !!peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
5551
+ updateScroll(ctx, state.scroll, true, { markHasScrolled: false });
5552
+ const didRetargetInitialScroll = retargetActiveInitialScrollAtEnd(ctx);
5553
+ if (!didRetargetInitialScroll && wasWithinEndThreshold && (insetDiff > 0)) {
5554
+ requestAdjust(ctx, insetDiff);
5555
+ }
5556
+ }
5557
+ }
5558
+
5318
5559
  // src/core/updateItemSize.ts
5319
5560
  function runOrScheduleMVCPRecalculate(ctx) {
5320
5561
  const state = ctx.state;
@@ -5358,15 +5599,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5358
5599
  const {
5359
5600
  didContainersLayout,
5360
5601
  sizesKnown,
5361
- props: {
5362
- getFixedItemSize,
5363
- getItemType,
5364
- horizontal,
5365
- suggestEstimatedItemSize,
5366
- onItemSizeChanged,
5367
- data,
5368
- maintainScrollAtEnd
5369
- }
5602
+ props: { getFixedItemSize, getItemType, horizontal, onItemSizeChanged, data, maintainScrollAtEnd }
5370
5603
  } = state;
5371
5604
  if (!data) return;
5372
5605
  const index = state.indexByKey.get(itemKey);
@@ -5417,18 +5650,6 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5417
5650
  if (minIndexSizeChanged !== void 0) {
5418
5651
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
5419
5652
  }
5420
- if (IS_DEV && suggestEstimatedItemSize && minIndexSizeChanged !== void 0) {
5421
- if (state.timeoutSizeMessage) clearTimeout(state.timeoutSizeMessage);
5422
- state.timeoutSizeMessage = setTimeout(() => {
5423
- var _a4;
5424
- state.timeoutSizeMessage = void 0;
5425
- const num = state.sizesKnown.size;
5426
- const avg = (_a4 = state.averageSizes[""]) == null ? void 0 : _a4.avg;
5427
- console.warn(
5428
- `[legend-list] Based on the ${num} items rendered so far, the optimal estimated size is ${avg}.`
5429
- );
5430
- }, 1e3);
5431
- }
5432
5653
  const cur = peek$(ctx, "otherAxisSize");
5433
5654
  if (!cur || maxOtherAxisSize > cur) {
5434
5655
  set$(ctx, "otherAxisSize", maxOtherAxisSize);
@@ -5533,12 +5754,47 @@ function createColumnWrapperStyle(contentContainerStyle) {
5533
5754
  }
5534
5755
 
5535
5756
  // src/utils/createImperativeHandle.ts
5757
+ var DEFAULT_AVERAGE_ITEM_SIZE_TYPE = "default";
5758
+ function getAverageItemSizes(state) {
5759
+ const averageItemSizes = {};
5760
+ for (const itemType in state.averageSizes) {
5761
+ const averageSize = state.averageSizes[itemType];
5762
+ if (averageSize) {
5763
+ averageItemSizes[itemType || DEFAULT_AVERAGE_ITEM_SIZE_TYPE] = {
5764
+ average: averageSize.avg,
5765
+ count: averageSize.num
5766
+ };
5767
+ }
5768
+ }
5769
+ return averageItemSizes;
5770
+ }
5536
5771
  function createImperativeHandle(ctx) {
5537
5772
  const state = ctx.state;
5538
5773
  const IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS = 800;
5539
5774
  const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
5540
5775
  let imperativeScrollToken = 0;
5541
5776
  const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0;
5777
+ const isScrollToIndexReady = (targetIndex, allowEmpty = false) => {
5778
+ var _a3;
5779
+ const props = state.props;
5780
+ const dataLength = props.data.length;
5781
+ const anchorIndex = (_a3 = props.anchoredEndSpace) == null ? void 0 : _a3.anchorIndex;
5782
+ if (targetIndex < 0) {
5783
+ return allowEmpty;
5784
+ }
5785
+ if (targetIndex >= dataLength) {
5786
+ return false;
5787
+ }
5788
+ if (anchorIndex === void 0 || anchorIndex < 0 || anchorIndex >= dataLength || targetIndex < anchorIndex || props.getFixedItemSize) {
5789
+ return true;
5790
+ }
5791
+ for (let index = anchorIndex; index < dataLength; index++) {
5792
+ if (!state.sizesKnown.has(getId(state, index))) {
5793
+ return false;
5794
+ }
5795
+ }
5796
+ return true;
5797
+ };
5542
5798
  const runWhenReady = (token, run, isReady) => {
5543
5799
  const startedAt = Date.now();
5544
5800
  let stableFrames = 0;
@@ -5560,11 +5816,10 @@ function createImperativeHandle(ctx) {
5560
5816
  };
5561
5817
  requestAnimationFrame(check);
5562
5818
  };
5563
- const runScrollWithPromise = (run, options) => new Promise((resolve) => {
5564
- var _a3, _b;
5819
+ const runScrollWithPromise = (run, isReady = () => true) => new Promise((resolve) => {
5820
+ var _a3;
5565
5821
  const token = ++imperativeScrollToken;
5566
- const isReady = (_a3 = options == null ? void 0 : options.isReady) != null ? _a3 : (() => true);
5567
- (_b = state.pendingScrollResolve) == null ? void 0 : _b.call(state);
5822
+ (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
5568
5823
  state.pendingScrollResolve = resolve;
5569
5824
  const runNow = () => {
5570
5825
  if (token !== imperativeScrollToken) {
@@ -5639,6 +5894,7 @@ function createImperativeHandle(ctx) {
5639
5894
  },
5640
5895
  end: state.endNoBuffer,
5641
5896
  endBuffered: state.endBuffered,
5897
+ getAverageItemSizes: () => getAverageItemSizes(state),
5642
5898
  isAtEnd: peek$(ctx, "isAtEnd"),
5643
5899
  isAtStart: peek$(ctx, "isAtStart"),
5644
5900
  isEndReached: state.isEndReached,
@@ -5662,8 +5918,14 @@ function createImperativeHandle(ctx) {
5662
5918
  startBuffered: state.startBuffered
5663
5919
  }),
5664
5920
  reportContentInset: (inset) => {
5921
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
5922
+ const previousInset = state.contentInsetOverride;
5665
5923
  state.contentInsetOverride = inset != null ? inset : void 0;
5924
+ 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);
5666
5925
  updateScroll(ctx, state.scroll, true, { markHasScrolled: false });
5926
+ if (didChange) {
5927
+ retargetActiveInitialScrollAtEnd(ctx);
5928
+ }
5667
5929
  },
5668
5930
  scrollIndexIntoView: (options) => runScrollWithPromise(() => scrollIndexIntoView(options)),
5669
5931
  scrollItemIntoView: ({ item, ...props }) => runScrollWithPromise(() => {
@@ -5675,40 +5937,34 @@ function createImperativeHandle(ctx) {
5675
5937
  }
5676
5938
  return false;
5677
5939
  }),
5678
- scrollToEnd: (options) => runScrollWithPromise(() => {
5679
- const data = state.props.data;
5680
- const stylePaddingBottom = state.props.stylePaddingBottom;
5681
- const index = data.length - 1;
5682
- if (index !== -1) {
5683
- const paddingBottom = stylePaddingBottom || 0;
5684
- const footerSize = peek$(ctx, "footerSize") || 0;
5685
- scrollToIndex(ctx, {
5686
- ...options,
5687
- index,
5688
- viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
5689
- viewPosition: 1
5690
- });
5691
- return true;
5692
- }
5693
- return false;
5694
- }),
5695
- scrollToIndex: (params) => {
5696
- const shouldWaitForOutOfRangeTarget = params.index >= 0 && params.index >= state.props.data.length;
5697
- const options = shouldWaitForOutOfRangeTarget ? {
5698
- isReady: () => {
5699
- var _a3;
5700
- const props = state.props;
5701
- const anchorIndex = (_a3 = props.anchoredEndSpace) == null ? void 0 : _a3.anchorIndex;
5702
- const lastIndex = props.data.length - 1;
5703
- const isInRange = params.index < props.data.length;
5704
- const shouldWaitForAnchorSize = isInRange && anchorIndex !== void 0 && anchorIndex >= 0 && params.index >= anchorIndex && !props.getFixedItemSize && !state.sizesKnown.has(getId(state, lastIndex));
5705
- return isInRange && !shouldWaitForAnchorSize;
5940
+ scrollToEnd: (options) => runScrollWithPromise(
5941
+ () => {
5942
+ const data = state.props.data;
5943
+ const stylePaddingBottom = state.props.stylePaddingBottom;
5944
+ const index = data.length - 1;
5945
+ if (index !== -1) {
5946
+ const paddingBottom = stylePaddingBottom || 0;
5947
+ const footerSize = peek$(ctx, "footerSize") || 0;
5948
+ scrollToIndex(ctx, {
5949
+ ...options,
5950
+ index,
5951
+ viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
5952
+ viewPosition: 1
5953
+ });
5954
+ return true;
5706
5955
  }
5707
- } : void 0;
5708
- return runScrollWithPromise(() => {
5709
- scrollToIndex(ctx, params);
5710
- return true;
5711
- }, options);
5956
+ return false;
5957
+ },
5958
+ () => isScrollToIndexReady(state.props.data.length - 1, true)
5959
+ ),
5960
+ scrollToIndex: (params) => {
5961
+ return runScrollWithPromise(
5962
+ () => {
5963
+ scrollToIndex(ctx, params);
5964
+ return true;
5965
+ },
5966
+ params.index >= 0 ? () => isScrollToIndexReady(params.index) : void 0
5967
+ );
5712
5968
  },
5713
5969
  scrollToItem: ({ item, ...props }) => runScrollWithPromise(() => {
5714
5970
  const data = state.props.data;
@@ -5964,6 +6220,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5964
6220
  data: dataProp = [],
5965
6221
  dataVersion,
5966
6222
  drawDistance = 250,
6223
+ contentInsetEndAdjustment,
5967
6224
  estimatedItemSize = 100,
5968
6225
  estimatedListSize,
5969
6226
  extraData,
@@ -5971,7 +6228,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5971
6228
  getFixedItemSize,
5972
6229
  getItemType,
5973
6230
  horizontal,
5974
- initialContainerPoolRatio = 2,
6231
+ initialContainerPoolRatio = 3,
5975
6232
  initialScrollAtEnd = false,
5976
6233
  initialScrollIndex: initialScrollIndexProp,
5977
6234
  initialScrollOffset: initialScrollOffsetProp,
@@ -6012,7 +6269,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6012
6269
  stickyIndices: stickyIndicesDeprecated,
6013
6270
  // TODOV3: Remove from v3 release
6014
6271
  style: styleProp,
6015
- suggestEstimatedItemSize,
6016
6272
  useWindowScroll = false,
6017
6273
  viewabilityConfig,
6018
6274
  viewabilityConfigCallbackPairs,
@@ -6073,6 +6329,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6073
6329
  const combinedRef = useCombinedRef(refScroller, refScrollView);
6074
6330
  const keyExtractor = keyExtractorProp != null ? keyExtractorProp : ((_item, index) => index.toString());
6075
6331
  const stickyHeaderIndices = stickyHeaderIndicesProp != null ? stickyHeaderIndicesProp : stickyIndicesDeprecated;
6332
+ const contentInsetEndAdjustmentResolved = contentInsetEndAdjustment ;
6333
+ const previousContentInsetEndAdjustmentRef = useRef(contentInsetEndAdjustmentResolved);
6076
6334
  const alwaysRenderIndices = useMemo(() => {
6077
6335
  const indices = getAlwaysRenderIndices(alwaysRender, dataProp, keyExtractor, anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex);
6078
6336
  return { arr: indices, set: new Set(indices) };
@@ -6151,7 +6409,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6151
6409
  startReachedSnapshotDataChangeEpoch: void 0,
6152
6410
  stickyContainerPool: /* @__PURE__ */ new Set(),
6153
6411
  stickyContainers: /* @__PURE__ */ new Map(),
6154
- timeoutSizeMessage: 0,
6155
6412
  timeouts: /* @__PURE__ */ new Set(),
6156
6413
  totalSize: 0,
6157
6414
  viewabilityConfigCallbackPairs: void 0
@@ -6171,7 +6428,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6171
6428
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
6172
6429
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
6173
6430
  const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
6174
- if (didDataChangeLocal && state.didFinishInitialScroll && ((_f = state.initialScroll) == null ? void 0 : _f.viewPosition) === 1 && state.props.data.length > 0) {
6431
+ if (didDataChangeLocal && !initialScrollAtEnd && state.didFinishInitialScroll && ((_f = state.initialScroll) == null ? void 0 : _f.viewPosition) === 1 && state.props.data.length > 0) {
6175
6432
  clearPreservedInitialScrollTarget(state);
6176
6433
  }
6177
6434
  if (didDataChangeLocal) {
@@ -6192,6 +6449,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6192
6449
  anchoredEndSpace: anchoredEndSpaceResolved,
6193
6450
  animatedProps: animatedPropsInternal,
6194
6451
  contentInset,
6452
+ contentInsetEndAdjustment: contentInsetEndAdjustmentResolved,
6195
6453
  data: dataProp,
6196
6454
  dataVersion,
6197
6455
  drawDistance,
@@ -6225,7 +6483,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6225
6483
  stickyPositionComponentInternal,
6226
6484
  stylePaddingBottom: stylePaddingBottomState,
6227
6485
  stylePaddingTop: stylePaddingTopState,
6228
- suggestEstimatedItemSize: !!suggestEstimatedItemSize,
6229
6486
  useWindowScroll: useWindowScrollResolved
6230
6487
  };
6231
6488
  state.refScroller = refScroller;
@@ -6309,6 +6566,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6309
6566
  didAnchoredEndSpaceAnchorIndexChange,
6310
6567
  numColumnsProp
6311
6568
  ]);
6569
+ useLayoutEffect(() => {
6570
+ const previousContentInsetEndAdjustment = previousContentInsetEndAdjustmentRef.current;
6571
+ previousContentInsetEndAdjustmentRef.current = contentInsetEndAdjustmentResolved;
6572
+ updateContentInsetEndAdjustment(ctx, previousContentInsetEndAdjustment);
6573
+ }, [ctx, contentInsetEndAdjustmentResolved]);
6312
6574
  const onLayoutFooter = useCallback(
6313
6575
  (layout) => {
6314
6576
  if (!usesBootstrapInitialScroll) {