@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.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
@@ -661,8 +671,52 @@ function isInMVCPActiveMode(state) {
661
671
  }
662
672
 
663
673
  // src/components/Container.tsx
674
+ function getContainerPositionStyle({
675
+ columnWrapperStyle,
676
+ horizontal,
677
+ hasItemSeparator,
678
+ numColumns,
679
+ otherAxisPos,
680
+ otherAxisSize
681
+ }) {
682
+ let paddingStyles;
683
+ if (columnWrapperStyle) {
684
+ const { columnGap, rowGap, gap } = columnWrapperStyle;
685
+ if (horizontal) {
686
+ paddingStyles = {
687
+ paddingBottom: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0,
688
+ paddingRight: columnGap || gap || void 0,
689
+ paddingTop: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
690
+ };
691
+ } else {
692
+ paddingStyles = {
693
+ paddingBottom: rowGap || gap || void 0,
694
+ paddingLeft: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0,
695
+ paddingRight: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
696
+ };
697
+ }
698
+ }
699
+ return horizontal ? {
700
+ boxSizing: paddingStyles ? "border-box" : void 0,
701
+ flexDirection: hasItemSeparator ? "row" : void 0,
702
+ height: otherAxisSize,
703
+ left: 0,
704
+ position: "absolute",
705
+ top: otherAxisPos,
706
+ ...paddingStyles || {}
707
+ } : {
708
+ boxSizing: paddingStyles ? "border-box" : void 0,
709
+ left: otherAxisPos,
710
+ position: "absolute",
711
+ right: numColumns > 1 ? null : 0,
712
+ top: 0,
713
+ width: otherAxisSize,
714
+ ...paddingStyles || {}
715
+ };
716
+ }
664
717
  var Container = typedMemo(function Container2({
665
718
  id,
719
+ itemKey,
666
720
  recycleItems,
667
721
  horizontal,
668
722
  getRenderedItem: getRenderedItem2,
@@ -674,11 +728,10 @@ var Container = typedMemo(function Container2({
674
728
  const { columnWrapperStyle, animatedScrollY } = ctx;
675
729
  const positionComponentInternal = ctx.state.props.positionComponentInternal;
676
730
  const stickyPositionComponentInternal = ctx.state.props.stickyPositionComponentInternal;
677
- const [column = 0, span = 1, data, itemKey, numColumns = 1, extraData, isSticky] = useArr$([
731
+ const [column = 0, span = 1, data, numColumns = 1, extraData, isSticky] = useArr$([
678
732
  `containerColumn${id}`,
679
733
  `containerSpan${id}`,
680
734
  `containerItemData${id}`,
681
- `containerItemKey${id}`,
682
735
  "numColumns",
683
736
  "extraData",
684
737
  `containerSticky${id}`
@@ -699,42 +752,17 @@ var Container = typedMemo(function Container2({
699
752
  const resolvedSpan = Math.min(Math.max(span || 1, 1), numColumns);
700
753
  const otherAxisPos = numColumns > 1 ? `${(resolvedColumn - 1) / numColumns * 100}%` : 0;
701
754
  const otherAxisSize = numColumns > 1 ? `${resolvedSpan / numColumns * 100}%` : void 0;
702
- const style = useMemo(() => {
703
- let paddingStyles;
704
- if (columnWrapperStyle) {
705
- const { columnGap, rowGap, gap } = columnWrapperStyle;
706
- if (horizontal) {
707
- paddingStyles = {
708
- paddingBottom: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0,
709
- paddingRight: columnGap || gap || void 0,
710
- paddingTop: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
711
- };
712
- } else {
713
- paddingStyles = {
714
- paddingBottom: rowGap || gap || void 0,
715
- paddingLeft: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0,
716
- paddingRight: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
717
- };
718
- }
719
- }
720
- return horizontal ? {
721
- boxSizing: paddingStyles ? "border-box" : void 0,
722
- flexDirection: ItemSeparatorComponent ? "row" : void 0,
723
- height: otherAxisSize,
724
- left: 0,
725
- position: "absolute",
726
- top: otherAxisPos,
727
- ...paddingStyles || {}
728
- } : {
729
- boxSizing: paddingStyles ? "border-box" : void 0,
730
- left: otherAxisPos,
731
- position: "absolute",
732
- right: numColumns > 1 ? null : 0,
733
- top: 0,
734
- width: otherAxisSize,
735
- ...paddingStyles || {}
736
- };
737
- }, [horizontal, otherAxisPos, otherAxisSize, columnWrapperStyle, numColumns]);
755
+ const style = useMemo(
756
+ () => getContainerPositionStyle({
757
+ columnWrapperStyle,
758
+ hasItemSeparator: !!ItemSeparatorComponent,
759
+ horizontal,
760
+ numColumns,
761
+ otherAxisPos,
762
+ otherAxisSize
763
+ }),
764
+ [horizontal, otherAxisPos, otherAxisSize, columnWrapperStyle, numColumns, ItemSeparatorComponent]
765
+ );
738
766
  const renderedItemInfo = useMemo(
739
767
  () => itemKey !== void 0 ? getRenderedItem2(itemKey) : null,
740
768
  [itemKey, data, extraData]
@@ -821,6 +849,39 @@ var Container = typedMemo(function Container2({
821
849
  );
822
850
  });
823
851
 
852
+ // src/components/ContainerSlot.tsx
853
+ function ContainerSlotBase({
854
+ id,
855
+ horizontal,
856
+ recycleItems,
857
+ ItemSeparatorComponent,
858
+ updateItemSize: updateItemSize2,
859
+ getRenderedItem: getRenderedItem2,
860
+ stickyHeaderConfig,
861
+ ContainerComponent = Container
862
+ }) {
863
+ const [itemKey] = useArr$([`containerItemKey${id}`]);
864
+ if (itemKey === void 0) {
865
+ return null;
866
+ }
867
+ return /* @__PURE__ */ React3.createElement(
868
+ ContainerComponent,
869
+ {
870
+ getRenderedItem: getRenderedItem2,
871
+ horizontal,
872
+ ItemSeparatorComponent,
873
+ id,
874
+ itemKey,
875
+ recycleItems,
876
+ stickyHeaderConfig,
877
+ updateItemSize: updateItemSize2
878
+ }
879
+ );
880
+ }
881
+ var ContainerSlot = typedMemo(function ContainerSlot2(props) {
882
+ return /* @__PURE__ */ React3.createElement(ContainerSlotBase, { ...props });
883
+ });
884
+
824
885
  // src/utils/reordering.ts
825
886
  var mapFn = (element) => {
826
887
  const indexStr = element.getAttribute("data-index");
@@ -940,9 +1001,12 @@ var ContainersInner = typedMemo(function ContainersInner2({ horizontal, numColum
940
1001
  const ref = useRef(null);
941
1002
  const ctx = useStateContext();
942
1003
  const columnWrapperStyle = ctx.columnWrapperStyle;
943
- const [otherAxisSize, totalSize] = useArr$(["otherAxisSize", "totalSize"]);
1004
+ const [otherAxisSize, readyToRender, totalSize] = useArr$(["otherAxisSize", "readyToRender", "totalSize"]);
944
1005
  useDOMOrder(ref);
945
- const style = horizontal ? { minHeight: otherAxisSize, position: "relative", width: totalSize } : { height: totalSize, minWidth: otherAxisSize, position: "relative" };
1006
+ const style = horizontal ? { minHeight: otherAxisSize, opacity: readyToRender ? 1 : 0, position: "relative", width: totalSize } : { height: totalSize, minWidth: otherAxisSize, opacity: readyToRender ? 1 : 0, position: "relative" };
1007
+ if (!readyToRender) {
1008
+ style.pointerEvents = "none";
1009
+ }
946
1010
  if (columnWrapperStyle && numColumns > 1) {
947
1011
  const { columnGap, rowGap, gap } = columnWrapperStyle;
948
1012
  const gapX = columnGap || gap || 0;
@@ -970,12 +1034,12 @@ var Containers = typedMemo(function Containers2({
970
1034
  getRenderedItem: getRenderedItem2,
971
1035
  stickyHeaderConfig
972
1036
  }) {
973
- const [numContainers, numColumns] = useArr$(["numContainersPooled", "numColumns"]);
1037
+ const [numContainersPooled, numColumns] = useArr$(["numContainersPooled", "numColumns"]);
974
1038
  const containers = [];
975
- for (let i = 0; i < numContainers; i++) {
1039
+ for (let i = 0; i < numContainersPooled; i++) {
976
1040
  containers.push(
977
1041
  /* @__PURE__ */ React3.createElement(
978
- Container,
1042
+ ContainerSlot,
979
1043
  {
980
1044
  getRenderedItem: getRenderedItem2,
981
1045
  horizontal,
@@ -1045,6 +1109,11 @@ function useRafCoalescer(callback) {
1045
1109
  return coalescer;
1046
1110
  }
1047
1111
 
1112
+ // src/components/webConstants.ts
1113
+ var LEGEND_LIST_CONTENT_CONTAINER_CLASS = "legend-list-content-container";
1114
+ var LEGEND_LIST_SCROLLBAR_X_HIDDEN_CLASS = "legend-list-scrollbar-x-hidden";
1115
+ var LEGEND_LIST_SCROLLBAR_Y_HIDDEN_CLASS = "legend-list-scrollbar-y-hidden";
1116
+
1048
1117
  // src/components/webScrollUtils.ts
1049
1118
  function getDocumentScrollerNode() {
1050
1119
  if (typeof document === "undefined") {
@@ -1129,6 +1198,22 @@ function resolveWindowScrollTarget({ clampedOffset, horizontal, listPos, scroll
1129
1198
  }
1130
1199
 
1131
1200
  // src/components/ListComponentScrollView.tsx
1201
+ var SCROLLBAR_HIDDEN_STYLE_ID = "legend-list-scrollbar-axis-hidden-style";
1202
+ 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;}`;
1203
+ function ensureScrollbarHiddenStyle() {
1204
+ if (typeof document === "undefined" || document.getElementById(SCROLLBAR_HIDDEN_STYLE_ID)) {
1205
+ return;
1206
+ }
1207
+ const styleElement = document.createElement("style");
1208
+ styleElement.id = SCROLLBAR_HIDDEN_STYLE_ID;
1209
+ styleElement.textContent = SCROLLBAR_HIDDEN_STYLE;
1210
+ document.head.appendChild(styleElement);
1211
+ }
1212
+ function getContentInsetEndAdjustmentEnd2(ctx) {
1213
+ var _a3, _b;
1214
+ const adjustment = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.props) == null ? void 0 : _b.contentInsetEndAdjustment;
1215
+ return Math.max(0, adjustment != null ? adjustment : 0);
1216
+ }
1132
1217
  var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
1133
1218
  children,
1134
1219
  style,
@@ -1146,7 +1231,9 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
1146
1231
  onLayout,
1147
1232
  ...props
1148
1233
  }, ref) {
1234
+ var _a3, _b, _c;
1149
1235
  const ctx = useStateContext();
1236
+ const [anchoredEndSpaceSize] = useArr$(["anchoredEndSpaceSize"]);
1150
1237
  const scrollRef = useRef(null);
1151
1238
  const contentRef = useRef(null);
1152
1239
  const isWindowScroll = useWindowScroll;
@@ -1208,10 +1295,9 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
1208
1295
  useImperativeHandle(ref, () => {
1209
1296
  const api = {
1210
1297
  getBoundingClientRect: () => {
1211
- var _a3;
1212
- return (_a3 = scrollRef.current) == null ? void 0 : _a3.getBoundingClientRect();
1298
+ var _a4;
1299
+ return (_a4 = scrollRef.current) == null ? void 0 : _a4.getBoundingClientRect();
1213
1300
  },
1214
- getContentNode: () => contentRef.current,
1215
1301
  getCurrentScrollOffset,
1216
1302
  getScrollableNode: () => resolveScrollableNode(scrollRef.current, isWindowScroll),
1217
1303
  getScrollEventTarget: () => getScrollTarget(),
@@ -1327,6 +1413,12 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
1327
1413
  }
1328
1414
  };
1329
1415
  }, [isWindowScroll, onLayout]);
1416
+ const hiddenScrollIndicatorClassName = !isWindowScroll && (horizontal ? !showsHorizontalScrollIndicator && LEGEND_LIST_SCROLLBAR_X_HIDDEN_CLASS : !showsVerticalScrollIndicator && LEGEND_LIST_SCROLLBAR_Y_HIDDEN_CLASS);
1417
+ useLayoutEffect(() => {
1418
+ if (hiddenScrollIndicatorClassName) {
1419
+ ensureScrollbarHiddenStyle();
1420
+ }
1421
+ }, [hiddenScrollIndicatorClassName]);
1330
1422
  const scrollViewStyle = {
1331
1423
  ...isWindowScroll ? {} : {
1332
1424
  overflow: "auto",
@@ -1337,6 +1429,10 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
1337
1429
  },
1338
1430
  ...StyleSheet.flatten(style)
1339
1431
  };
1432
+ const contentInsetEndAdjustment = getContentInsetEndAdjustmentEnd2(ctx);
1433
+ 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;
1434
+ const renderedContentInsetEndAdjustment = Math.max(0, contentInsetEndAdjustment - anchoredEndInset);
1435
+ const contentInsetEndAdjustmentSpacerStyle = renderedContentInsetEndAdjustment ? horizontal ? { flexShrink: 0, width: renderedContentInsetEndAdjustment } : { height: renderedContentInsetEndAdjustment } : void 0;
1340
1436
  const contentStyle = {
1341
1437
  display: horizontal ? "flex" : "block",
1342
1438
  flexDirection: horizontal ? "row" : void 0,
@@ -1344,15 +1440,38 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
1344
1440
  minWidth: horizontal ? "100%" : void 0,
1345
1441
  ...StyleSheet.flatten(contentContainerStyle)
1346
1442
  };
1443
+ const className = contentContainerClassName ? `${LEGEND_LIST_CONTENT_CONTAINER_CLASS} ${contentContainerClassName}` : LEGEND_LIST_CONTENT_CONTAINER_CLASS;
1347
1444
  const {
1348
1445
  contentContainerClassName: _contentContainerClassName,
1349
1446
  contentInset: _contentInset,
1350
1447
  scrollEventThrottle: _scrollEventThrottle,
1351
1448
  ScrollComponent: _ScrollComponent,
1352
1449
  useWindowScroll: _useWindowScroll,
1450
+ className: scrollViewClassNameProp,
1353
1451
  ...webProps
1354
1452
  } = props;
1355
- return /* @__PURE__ */ React3.createElement("div", { ref: scrollRef, ...webProps, style: scrollViewStyle }, refreshControl, /* @__PURE__ */ React3.createElement("div", { className: contentContainerClassName, ref: contentRef, style: contentStyle }, children));
1453
+ const scrollViewClassName = hiddenScrollIndicatorClassName ? scrollViewClassNameProp ? `${scrollViewClassNameProp} ${hiddenScrollIndicatorClassName}` : hiddenScrollIndicatorClassName : scrollViewClassNameProp;
1454
+ if (IS_DEV) {
1455
+ if (/(?:^|\s)(?:[a-z0-9_-]+:)*gap(?:-[xy])?-(?:\[[^\]]+\]|[^\s]+)/.test(
1456
+ `${contentContainerClassName != null ? contentContainerClassName : ""} ${scrollViewClassNameProp != null ? scrollViewClassNameProp : ""}`
1457
+ )) {
1458
+ warnDevOnce(
1459
+ "className-gap",
1460
+ "className/contentContainerClassName gap classes are not supported in LegendList because it needs to use exact values internally. Use contentContainerStyle={{ gap: ... }} or columnWrapperStyle instead."
1461
+ );
1462
+ }
1463
+ }
1464
+ return /* @__PURE__ */ React3.createElement(
1465
+ "div",
1466
+ {
1467
+ className: scrollViewClassName,
1468
+ ref: scrollRef,
1469
+ ...webProps,
1470
+ style: scrollViewStyle
1471
+ },
1472
+ refreshControl,
1473
+ /* @__PURE__ */ React3.createElement("div", { className, ref: contentRef, style: contentStyle }, children, contentInsetEndAdjustmentSpacerStyle ? /* @__PURE__ */ React3.createElement("div", { "aria-hidden": true, style: contentInsetEndAdjustmentSpacerStyle }) : null)
1474
+ );
1356
1475
  });
1357
1476
  function useValueListener$(key, callback) {
1358
1477
  const ctx = useStateContext();
@@ -1380,48 +1499,63 @@ function getScrollAdjustAxis(horizontal) {
1380
1499
  y: 1
1381
1500
  };
1382
1501
  }
1502
+ function getScrollAdjustTarget(ctx, contentNode) {
1503
+ var _a3, _b, _c;
1504
+ const scrollView = (_a3 = ctx.state) == null ? void 0 : _a3.refScroller.current;
1505
+ const scrollElement = (_c = (_b = scrollView == null ? void 0 : scrollView.getScrollableNode) == null ? void 0 : _b.call(scrollView)) != null ? _c : null;
1506
+ let resolvedContentNode = null;
1507
+ if (scrollElement) {
1508
+ resolvedContentNode = (contentNode == null ? void 0 : contentNode.isConnected) && contentNode.parentElement === scrollElement ? contentNode : scrollElement.querySelector(`:scope > .${LEGEND_LIST_CONTENT_CONTAINER_CLASS}`);
1509
+ }
1510
+ return scrollElement ? { contentNode: resolvedContentNode, scrollElement } : null;
1511
+ }
1512
+ function scrollAdjustBy(el, left, top) {
1513
+ el.scrollBy({ behavior: "auto", left, top });
1514
+ }
1383
1515
  function ScrollAdjust() {
1384
1516
  const ctx = useStateContext();
1385
1517
  const lastScrollOffsetRef = React3.useRef(0);
1386
1518
  const resetPaddingRafRef = React3.useRef(void 0);
1387
1519
  const resetPaddingBaselineRef = React3.useRef(void 0);
1520
+ const contentNodeRef = React3.useRef(null);
1388
1521
  const callback = React3.useCallback(() => {
1389
- var _a3, _b;
1522
+ var _a3;
1390
1523
  const scrollAdjust = peek$(ctx, "scrollAdjust");
1391
1524
  const scrollAdjustUserOffset = peek$(ctx, "scrollAdjustUserOffset");
1392
1525
  const scrollOffset = (scrollAdjust || 0) + (scrollAdjustUserOffset || 0);
1393
- const scrollView = (_a3 = ctx.state) == null ? void 0 : _a3.refScroller.current;
1394
- if (scrollView && scrollOffset !== lastScrollOffsetRef.current) {
1395
- const scrollDelta = scrollOffset - lastScrollOffsetRef.current;
1396
- if (scrollDelta !== 0) {
1397
- const axis = getScrollAdjustAxis(!!ctx.state.props.horizontal);
1398
- const contentNode = scrollView.getContentNode();
1399
- const prevScroll = scrollView.getCurrentScrollOffset();
1400
- const el = scrollView.getScrollableNode();
1401
- const scrollBy = () => scrollView.scrollBy(axis.x * scrollDelta, axis.y * scrollDelta);
1402
- if (!contentNode) {
1403
- scrollBy();
1404
- lastScrollOffsetRef.current = scrollOffset;
1405
- return;
1406
- }
1407
- const totalSize = contentNode[axis.contentSizeKey];
1408
- const viewportSize = el[axis.viewportSizeKey];
1409
- const nextScroll = prevScroll + scrollDelta;
1410
- if (scrollDelta > 0 && !ctx.state.adjustingFromInitialMount && totalSize < nextScroll + viewportSize) {
1411
- const previousPaddingEnd = (_b = resetPaddingBaselineRef.current) != null ? _b : contentNode.style[axis.paddingEndProp];
1412
- resetPaddingBaselineRef.current = previousPaddingEnd;
1413
- const pad = (nextScroll + viewportSize - totalSize) * 2;
1414
- contentNode.style[axis.paddingEndProp] = `${pad}px`;
1415
- void contentNode.offsetHeight;
1416
- scrollBy();
1417
- if (resetPaddingRafRef.current !== void 0) {
1418
- cancelAnimationFrame(resetPaddingRafRef.current);
1526
+ const scrollDelta = scrollOffset - lastScrollOffsetRef.current;
1527
+ if (scrollDelta !== 0) {
1528
+ const target = getScrollAdjustTarget(ctx, contentNodeRef.current);
1529
+ if (target) {
1530
+ const horizontal = !!ctx.state.props.horizontal;
1531
+ const axis = getScrollAdjustAxis(horizontal);
1532
+ const { contentNode, scrollElement: el } = target;
1533
+ const scrollBy = () => scrollAdjustBy(el, axis.x * scrollDelta, axis.y * scrollDelta);
1534
+ contentNodeRef.current = contentNode;
1535
+ if (contentNode) {
1536
+ const prevScroll = horizontal ? el.scrollLeft : el.scrollTop;
1537
+ const totalSize = contentNode[axis.contentSizeKey];
1538
+ const viewportSize = el[axis.viewportSizeKey];
1539
+ const nextScroll = prevScroll + scrollDelta;
1540
+ const needsTemporaryPadding = scrollDelta > 0 && !ctx.state.adjustingFromInitialMount && totalSize < nextScroll + viewportSize;
1541
+ if (needsTemporaryPadding) {
1542
+ const previousPaddingEnd = (_a3 = resetPaddingBaselineRef.current) != null ? _a3 : contentNode.style[axis.paddingEndProp];
1543
+ resetPaddingBaselineRef.current = previousPaddingEnd;
1544
+ const pad = (nextScroll + viewportSize - totalSize) * 2;
1545
+ contentNode.style[axis.paddingEndProp] = `${pad}px`;
1546
+ void contentNode.offsetHeight;
1547
+ scrollBy();
1548
+ if (resetPaddingRafRef.current !== void 0) {
1549
+ cancelAnimationFrame(resetPaddingRafRef.current);
1550
+ }
1551
+ resetPaddingRafRef.current = requestAnimationFrame(() => {
1552
+ resetPaddingRafRef.current = void 0;
1553
+ resetPaddingBaselineRef.current = void 0;
1554
+ contentNode.style[axis.paddingEndProp] = previousPaddingEnd;
1555
+ });
1556
+ } else {
1557
+ scrollBy();
1419
1558
  }
1420
- resetPaddingRafRef.current = requestAnimationFrame(() => {
1421
- resetPaddingRafRef.current = void 0;
1422
- resetPaddingBaselineRef.current = void 0;
1423
- contentNode.style[axis.paddingEndProp] = previousPaddingEnd;
1424
- });
1425
1559
  } else {
1426
1560
  scrollBy();
1427
1561
  }
@@ -1732,10 +1866,9 @@ var initialScrollWatchdog = {
1732
1866
  clear(state) {
1733
1867
  initialScrollWatchdog.set(state, void 0);
1734
1868
  },
1735
- didObserveProgress(newScroll, watchdog) {
1736
- const previousDistance = Math.abs(watchdog.startScroll - watchdog.targetOffset);
1869
+ didReachTarget(newScroll, watchdog) {
1737
1870
  const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
1738
- return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET || nextDistance + INITIAL_SCROLL_MIN_TARGET_OFFSET < previousDistance;
1871
+ return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
1739
1872
  },
1740
1873
  get(state) {
1741
1874
  var _a3, _b;
@@ -1760,19 +1893,19 @@ var initialScrollWatchdog = {
1760
1893
  }
1761
1894
  };
1762
1895
  function setInitialScrollSession(state, options = {}) {
1763
- var _a3, _b, _c;
1896
+ var _a3, _b, _c, _d;
1764
1897
  const existingSession = state.initialScrollSession;
1765
1898
  const kind = (_a3 = options.kind) != null ? _a3 : existingSession == null ? void 0 : existingSession.kind;
1766
1899
  const completion = existingSession == null ? void 0 : existingSession.completion;
1767
- const hasBootstrapOverride = Object.hasOwn(options, "bootstrap");
1768
- const bootstrap = kind === "bootstrap" ? hasBootstrapOverride ? options.bootstrap : (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0 : void 0;
1900
+ const existingBootstrap = (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0;
1901
+ const bootstrap = kind === "bootstrap" ? options.bootstrap === null ? void 0 : (_b = options.bootstrap) != null ? _b : existingBootstrap : void 0;
1769
1902
  if (!kind) {
1770
1903
  return clearInitialScrollSession(state);
1771
1904
  }
1772
1905
  if (!state.initialScroll && !bootstrap && !hasInitialScrollSessionCompletion(completion)) {
1773
1906
  return clearInitialScrollSession(state);
1774
1907
  }
1775
- const previousDataLength = (_c = (_b = options.previousDataLength) != null ? _b : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _c : 0;
1908
+ const previousDataLength = (_d = (_c = options.previousDataLength) != null ? _c : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _d : 0;
1776
1909
  state.initialScrollSession = createInitialScrollSession({
1777
1910
  bootstrap,
1778
1911
  completion,
@@ -2524,7 +2657,7 @@ function advanceMeasuredInitialScroll(ctx, options) {
2524
2657
  const activeInitialTargetOffset = scrollingTo ? (_a3 = scrollingTo.targetOffset) != null ? _a3 : scrollingTo.offset : void 0;
2525
2658
  const didOffsetChange = initialScroll.contentOffset === void 0 || Math.abs(initialScroll.contentOffset - resolvedOffset) > 1;
2526
2659
  const didActiveInitialTargetChange = activeInitialTargetOffset !== void 0 && Math.abs(activeInitialTargetOffset - resolvedOffset) > 1;
2527
- const isAlreadyAtDesiredInitialTarget = activeInitialTargetOffset !== void 0 && Math.abs(state.scroll - activeInitialTargetOffset) <= 1 && Math.abs(state.scrollPending - activeInitialTargetOffset) <= 1;
2660
+ const isAlreadyAtDesiredInitialTarget = activeInitialTargetOffset !== void 0 && Math.abs(state.scroll - resolvedOffset) <= 1 && Math.abs(state.scrollPending - resolvedOffset) <= 1;
2528
2661
  if (!(options == null ? void 0 : options.forceScroll) && !didOffsetChange && isInitialScrollInProgress && !didActiveInitialTargetChange) {
2529
2662
  return false;
2530
2663
  }
@@ -2596,6 +2729,30 @@ function checkAllSizesKnown(state, indices) {
2596
2729
  });
2597
2730
  }
2598
2731
 
2732
+ // src/utils/requestAdjust.ts
2733
+ function requestAdjust(ctx, positionDiff, dataChanged) {
2734
+ const state = ctx.state;
2735
+ if (Math.abs(positionDiff) > 0.1) {
2736
+ const doit = () => {
2737
+ {
2738
+ state.scrollAdjustHandler.requestAdjust(positionDiff);
2739
+ if (state.adjustingFromInitialMount) {
2740
+ state.adjustingFromInitialMount--;
2741
+ }
2742
+ }
2743
+ };
2744
+ state.scroll += positionDiff;
2745
+ state.scrollForNextCalculateItemsInView = void 0;
2746
+ const readyToRender = peek$(ctx, "readyToRender");
2747
+ if (readyToRender) {
2748
+ doit();
2749
+ } else {
2750
+ state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
2751
+ requestAnimationFrame(doit);
2752
+ }
2753
+ }
2754
+ }
2755
+
2599
2756
  // src/core/bootstrapInitialScroll.ts
2600
2757
  var DEFAULT_BOOTSTRAP_REVEAL_EPSILON = 1;
2601
2758
  var DEFAULT_BOOTSTRAP_REVEAL_MAX_FRAMES = 8;
@@ -2689,7 +2846,7 @@ function clearBootstrapInitialScrollSession(state) {
2689
2846
  bootstrapInitialScroll.frameHandle = void 0;
2690
2847
  }
2691
2848
  setInitialScrollSession(state, {
2692
- bootstrap: void 0,
2849
+ bootstrap: null,
2693
2850
  kind: (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind
2694
2851
  });
2695
2852
  }
@@ -2845,15 +3002,18 @@ function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {
2845
3002
  return;
2846
3003
  }
2847
3004
  if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2848
- if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {
2849
- clearPendingInitialScrollFooterLayout(ctx, {
2850
- dataLength: state.props.data.length,
2851
- stylePaddingBottom: (_b = state.props.stylePaddingBottom) != null ? _b : 0,
2852
- target: initialScroll
2853
- });
2854
- return;
3005
+ const shouldKeepEndTargetAlive = isRetargetableBottomAlignedInitialScrollTarget(initialScroll) && peek$(ctx, "isAtEnd");
3006
+ if (!shouldKeepEndTargetAlive) {
3007
+ if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {
3008
+ clearPendingInitialScrollFooterLayout(ctx, {
3009
+ dataLength: state.props.data.length,
3010
+ stylePaddingBottom: (_b = state.props.stylePaddingBottom) != null ? _b : 0,
3011
+ target: initialScroll
3012
+ });
3013
+ } else {
3014
+ clearFinishedViewportRetargetableInitialScroll(state);
3015
+ }
2855
3016
  }
2856
- clearFinishedViewportRetargetableInitialScroll(state);
2857
3017
  }
2858
3018
  }
2859
3019
  function startBootstrapInitialScrollOnMount(ctx, options) {
@@ -2892,7 +3052,7 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
2892
3052
  }
2893
3053
  const shouldResetDidFinish = !!(state.didFinishInitialScroll && previousDataLength === 0 && dataLength > 0 && initialScroll.index !== void 0);
2894
3054
  const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2895
- const shouldClearFinishedResizePreservation = didDataChange && dataLength > 0 && state.didFinishInitialScroll && !bootstrapInitialScroll && !shouldResetDidFinish;
3055
+ const shouldClearFinishedResizePreservation = !initialScrollAtEnd && didDataChange && dataLength > 0 && state.didFinishInitialScroll && !bootstrapInitialScroll && !shouldResetDidFinish;
2896
3056
  if (shouldClearFinishedResizePreservation) {
2897
3057
  clearPreservedInitialScrollTarget(state);
2898
3058
  return;
@@ -2995,27 +3155,46 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2995
3155
  }
2996
3156
  }
2997
3157
  function handleBootstrapInitialScrollLayoutChange(ctx) {
3158
+ var _a3, _b, _c, _d;
2998
3159
  const state = ctx.state;
2999
3160
  const initialScroll = state.initialScroll;
3000
- if (isOffsetInitialScrollSession(state) || state.props.data.length === 0 || !initialScroll) {
3001
- return;
3002
- }
3003
3161
  const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
3004
- if (!bootstrapInitialScroll && initialScroll.viewPosition !== 1) {
3005
- return;
3006
- }
3007
- const didFinishInitialScroll = state.didFinishInitialScroll;
3008
- if (didFinishInitialScroll) {
3009
- setInitialScrollTarget(state, initialScroll, {
3010
- resetDidFinish: true
3011
- });
3012
- state.clearPreservedInitialScrollOnNextFinish = true;
3162
+ if (initialScroll && state.props.data.length > 0 && !isOffsetInitialScrollSession(state) && (bootstrapInitialScroll || initialScroll.viewPosition === 1)) {
3163
+ const resolvedOffset = resolveInitialScrollOffset(ctx, initialScroll);
3164
+ const scrollingTo = ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) ? state.scrollingTo : void 0;
3165
+ if (!bootstrapInitialScroll && (scrollingTo || state.didFinishInitialScroll)) {
3166
+ const currentOffset = scrollingTo ? (_b = scrollingTo.targetOffset) != null ? _b : scrollingTo.offset : getObservedBootstrapInitialScrollOffset(state);
3167
+ const offsetDiff = resolvedOffset - currentOffset;
3168
+ if (Math.abs(offsetDiff) > DEFAULT_BOOTSTRAP_REVEAL_EPSILON) {
3169
+ if (scrollingTo) {
3170
+ const existingWatchdog = initialScrollWatchdog.get(state);
3171
+ scrollingTo.offset = resolvedOffset;
3172
+ scrollingTo.targetOffset = resolvedOffset;
3173
+ state.initialScroll = {
3174
+ ...initialScroll,
3175
+ contentOffset: resolvedOffset
3176
+ };
3177
+ state.hasScrolled = false;
3178
+ initialScrollWatchdog.set(state, {
3179
+ startScroll: (_c = existingWatchdog == null ? void 0 : existingWatchdog.startScroll) != null ? _c : state.scroll,
3180
+ targetOffset: resolvedOffset
3181
+ });
3182
+ }
3183
+ requestAdjust(ctx, offsetDiff);
3184
+ if (state.didFinishInitialScroll) {
3185
+ (_d = state.triggerCalculateItemsInView) == null ? void 0 : _d.call(state, { forceFullItemPositions: true });
3186
+ }
3187
+ }
3188
+ if (state.didFinishInitialScroll) {
3189
+ clearFinishedViewportRetargetableInitialScroll(state);
3190
+ }
3191
+ } else {
3192
+ rearmBootstrapInitialScroll(ctx, {
3193
+ scroll: resolvedOffset,
3194
+ targetIndexSeed: initialScroll.index
3195
+ });
3196
+ }
3013
3197
  }
3014
- rearmBootstrapInitialScroll(ctx, {
3015
- scroll: resolveInitialScrollOffset(ctx, initialScroll),
3016
- seedContentOffset: didFinishInitialScroll && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
3017
- targetIndexSeed: initialScroll.index
3018
- });
3019
3198
  }
3020
3199
  function evaluateBootstrapInitialScroll(ctx) {
3021
3200
  var _a3, _b;
@@ -3222,7 +3401,7 @@ function checkFinishedScrollFallback(ctx) {
3222
3401
  state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, delay);
3223
3402
  };
3224
3403
  const checkHasScrolled = () => {
3225
- var _c;
3404
+ var _c, _d;
3226
3405
  state.timeoutCheckFinishedScrollFallback = void 0;
3227
3406
  const isStillScrollingTo = state.scrollingTo;
3228
3407
  if (isStillScrollingTo) {
@@ -3235,11 +3414,13 @@ function checkFinishedScrollFallback(ctx) {
3235
3414
  isStillScrollingTo
3236
3415
  );
3237
3416
  const completionState = getResolvedScrollCompletionState(ctx, isStillScrollingTo);
3238
- const canFinishAfterSilentNativeDispatch = silentInitialDispatch && completionState.isAtResolvedTarget && numChecks >= 1;
3239
- if (shouldFinishZeroTarget || state.hasScrolled || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
3417
+ const canFinishAfterSilentNativeDispatch = Platform.OS === "android";
3418
+ const shouldFinishAfterObservedScroll = state.hasScrolled && (!isStillScrollingTo.isInitialScroll || completionState.isAtResolvedTarget);
3419
+ const shouldRetryUnalignedInitialScroll = isStillScrollingTo.isInitialScroll && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
3420
+ if (shouldFinishZeroTarget || shouldFinishAfterObservedScroll || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
3240
3421
  finishScrollTo(ctx);
3241
- } else if (isNativeInitialPending && numChecks <= maxChecks) {
3242
- const targetOffset = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : state.scrollPending;
3422
+ } else if ((isNativeInitialPending || shouldRetryUnalignedInitialScroll) && numChecks <= maxChecks) {
3423
+ const targetOffset = (_d = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : isStillScrollingTo.targetOffset) != null ? _d : state.scrollPending;
3243
3424
  scrollToFallbackOffset(ctx, targetOffset);
3244
3425
  scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
3245
3426
  } else {
@@ -3252,6 +3433,15 @@ function checkFinishedScrollFallback(ctx) {
3252
3433
  }
3253
3434
 
3254
3435
  // src/core/initialScrollLifecycle.ts
3436
+ function retargetActiveInitialScrollAtEnd(ctx) {
3437
+ var _a3;
3438
+ const state = ctx.state;
3439
+ const initialScroll = state.initialScroll;
3440
+ if (!initialScroll || state.didFinishInitialScroll || ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" || initialScroll.viewPosition !== 1 || state.props.data.length === 0) {
3441
+ return false;
3442
+ }
3443
+ return advanceCurrentInitialScrollSession(ctx, { forceScroll: true });
3444
+ }
3255
3445
  function handleInitialScrollLayoutReady(ctx) {
3256
3446
  var _a3;
3257
3447
  if (!ctx.state.initialScroll) {
@@ -3325,30 +3515,6 @@ function handleInitialScrollDataChange(ctx, options) {
3325
3515
  advanceCurrentInitialScrollSession(ctx);
3326
3516
  }
3327
3517
 
3328
- // src/utils/requestAdjust.ts
3329
- function requestAdjust(ctx, positionDiff, dataChanged) {
3330
- const state = ctx.state;
3331
- if (Math.abs(positionDiff) > 0.1) {
3332
- const doit = () => {
3333
- {
3334
- state.scrollAdjustHandler.requestAdjust(positionDiff);
3335
- if (state.adjustingFromInitialMount) {
3336
- state.adjustingFromInitialMount--;
3337
- }
3338
- }
3339
- };
3340
- state.scroll += positionDiff;
3341
- state.scrollForNextCalculateItemsInView = void 0;
3342
- const readyToRender = peek$(ctx, "readyToRender");
3343
- if (readyToRender) {
3344
- doit();
3345
- } else {
3346
- state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
3347
- requestAnimationFrame(doit);
3348
- }
3349
- }
3350
- }
3351
-
3352
3518
  // src/core/mvcp.ts
3353
3519
  var MVCP_POSITION_EPSILON = 0.1;
3354
3520
  var MVCP_ANCHOR_LOCK_TTL_MS = 300;
@@ -3622,7 +3788,10 @@ function prepareMVCP(ctx, dataChanged) {
3622
3788
  return;
3623
3789
  }
3624
3790
  if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
3625
- requestAdjust(ctx, positionDiff);
3791
+ const shouldSkipAdjustForMaintainedEnd = state.maintainingScrollAtEnd && peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
3792
+ if (!shouldSkipAdjustForMaintainedEnd) {
3793
+ requestAdjust(ctx, positionDiff);
3794
+ }
3626
3795
  }
3627
3796
  };
3628
3797
  }
@@ -4241,6 +4410,30 @@ function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
4241
4410
  var unstableBatchedUpdates = ReactDOM.unstable_batchedUpdates;
4242
4411
  var batchedUpdates = typeof unstableBatchedUpdates === "function" ? unstableBatchedUpdates : (fn) => fn();
4243
4412
 
4413
+ // src/utils/containerPool.ts
4414
+ var MIN_INITIAL_CONTAINER_POOL_SIZE = 32;
4415
+ var MAX_INITIAL_SPARE_CONTAINERS = 64;
4416
+ function getInitialContainerPoolSize(dataLength, numContainers, initialContainerPoolRatio) {
4417
+ if (dataLength <= 0 || numContainers <= 0) {
4418
+ return 0;
4419
+ }
4420
+ const ratioPoolSize = Math.ceil(numContainers * initialContainerPoolRatio);
4421
+ const cappedSparePoolSize = numContainers + MAX_INITIAL_SPARE_CONTAINERS;
4422
+ const targetPoolSize = Math.max(
4423
+ numContainers,
4424
+ Math.min(ratioPoolSize, cappedSparePoolSize),
4425
+ Math.min(dataLength, MIN_INITIAL_CONTAINER_POOL_SIZE)
4426
+ );
4427
+ const maxUsefulPoolSize = Math.max(dataLength, numContainers);
4428
+ return Math.min(maxUsefulPoolSize, targetPoolSize);
4429
+ }
4430
+ function getExpandedContainerPoolSize(dataLength, numContainers) {
4431
+ if (dataLength <= 0 || numContainers <= 0) {
4432
+ return 0;
4433
+ }
4434
+ return Math.min(Math.max(dataLength, numContainers), Math.max(numContainers, Math.ceil(numContainers * 1.5)));
4435
+ }
4436
+
4244
4437
  // src/utils/findAvailableContainers.ts
4245
4438
  function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers, protectedKeys) {
4246
4439
  const numContainers = peek$(ctx, "numContainers");
@@ -4446,7 +4639,7 @@ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentSt
4446
4639
  function calculateItemsInView(ctx, params = {}) {
4447
4640
  const state = ctx.state;
4448
4641
  batchedUpdates(() => {
4449
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
4642
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q;
4450
4643
  const {
4451
4644
  columns,
4452
4645
  columnSpans,
@@ -4493,12 +4686,22 @@ function calculateItemsInView(ctx, params = {}) {
4493
4686
  // current initial-scroll target instead of transient native adjustments.
4494
4687
  resolveInitialScrollOffset(ctx, initialScroll)
4495
4688
  ) : state.scroll;
4496
- const scrollAdjustPending = (_c = peek$(ctx, "scrollAdjustPending")) != null ? _c : 0;
4497
- const scrollAdjustPad = scrollAdjustPending - topPad;
4498
- let scroll = Math.round(scrollState + scrollExtra + scrollAdjustPad);
4499
- if (scroll + scrollLength > totalSize) {
4500
- scroll = Math.max(0, totalSize - scrollLength);
4501
- }
4689
+ let scrollAdjustPending = 0;
4690
+ let scrollAdjustPad = 0;
4691
+ let scroll = 0;
4692
+ let scrollTopBuffered = 0;
4693
+ let scrollBottom = 0;
4694
+ let scrollBottomBuffered = 0;
4695
+ const updateScroll2 = (nextScrollState) => {
4696
+ var _a4;
4697
+ scrollAdjustPending = (_a4 = peek$(ctx, "scrollAdjustPending")) != null ? _a4 : 0;
4698
+ scrollAdjustPad = scrollAdjustPending - topPad;
4699
+ scroll = Math.round(nextScrollState + scrollExtra + scrollAdjustPad);
4700
+ if (scroll + scrollLength > totalSize) {
4701
+ scroll = Math.max(0, totalSize - scrollLength);
4702
+ }
4703
+ };
4704
+ updateScroll2(scrollState);
4502
4705
  const previousStickyIndex = peek$(ctx, "activeStickyIndex");
4503
4706
  const currentStickyIdx = stickyIndicesArr.length > 0 ? findCurrentStickyIndex(stickyIndicesArr, scroll, state) : -1;
4504
4707
  const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyIndicesArr[currentStickyIdx] : -1;
@@ -4514,9 +4717,12 @@ function calculateItemsInView(ctx, params = {}) {
4514
4717
  scrollBufferTop = drawDistance * 1.5;
4515
4718
  scrollBufferBottom = drawDistance * 0.5;
4516
4719
  }
4517
- const scrollTopBuffered = scroll - scrollBufferTop;
4518
- const scrollBottom = scroll + scrollLength + (scroll < 0 ? -scroll : 0);
4519
- const scrollBottomBuffered = scrollBottom + scrollBufferBottom;
4720
+ const updateScrollRange = () => {
4721
+ scrollTopBuffered = scroll - scrollBufferTop;
4722
+ scrollBottom = scroll + scrollLength + (scroll < 0 ? -scroll : 0);
4723
+ scrollBottomBuffered = scrollBottom + scrollBufferBottom;
4724
+ };
4725
+ updateScrollRange();
4520
4726
  if (!suppressInitialScrollSideEffects && !dataChanged && !forceFullItemPositions && scrollForNextCalculateItemsInView) {
4521
4727
  const { top, bottom } = scrollForNextCalculateItemsInView;
4522
4728
  if (top === null && bottom === null) {
@@ -4535,7 +4741,7 @@ function calculateItemsInView(ctx, params = {}) {
4535
4741
  columns.length = 0;
4536
4742
  columnSpans.length = 0;
4537
4743
  }
4538
- const startIndex = forceFullItemPositions || dataChanged ? 0 : (_d = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _d : 0;
4744
+ const startIndex = forceFullItemPositions || dataChanged ? 0 : (_c = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _c : 0;
4539
4745
  const optimizeForVisibleWindow = !forceFullItemPositions && !dataChanged && numColumns > 1 && minIndexSizeChanged !== void 0;
4540
4746
  updateItemPositions(ctx, dataChanged, {
4541
4747
  doMVCP,
@@ -4560,21 +4766,25 @@ function calculateItemsInView(ctx, params = {}) {
4560
4766
  }
4561
4767
  }
4562
4768
  const scrollBeforeMVCP = state.scroll;
4563
- const scrollAdjustPendingBeforeMVCP = (_e = peek$(ctx, "scrollAdjustPending")) != null ? _e : 0;
4769
+ const scrollAdjustPendingBeforeMVCP = (_d = peek$(ctx, "scrollAdjustPending")) != null ? _d : 0;
4564
4770
  checkMVCP == null ? void 0 : checkMVCP();
4565
- const didMVCPAdjustScroll = !!checkMVCP && (state.scroll !== scrollBeforeMVCP || ((_f = peek$(ctx, "scrollAdjustPending")) != null ? _f : 0) !== scrollAdjustPendingBeforeMVCP);
4771
+ const didMVCPAdjustScroll = !!checkMVCP && (state.scroll !== scrollBeforeMVCP || ((_e = peek$(ctx, "scrollAdjustPending")) != null ? _e : 0) !== scrollAdjustPendingBeforeMVCP);
4772
+ if (didMVCPAdjustScroll && initialScroll) {
4773
+ updateScroll2(state.scroll);
4774
+ updateScrollRange();
4775
+ }
4566
4776
  let startNoBuffer = null;
4567
4777
  let startBuffered = null;
4568
4778
  let startBufferedId = null;
4569
4779
  let endNoBuffer = null;
4570
4780
  let endBuffered = null;
4571
- let loopStart = (_g = suppressInitialScrollSideEffects ? bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.targetIndexSeed : void 0) != null ? _g : !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
4781
+ let loopStart = (_f = suppressInitialScrollSideEffects ? bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.targetIndexSeed : void 0) != null ? _f : !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
4572
4782
  for (let i = loopStart; i >= 0; i--) {
4573
- const id = (_h = idCache[i]) != null ? _h : getId(state, i);
4783
+ const id = (_g = idCache[i]) != null ? _g : getId(state, i);
4574
4784
  const top = positions[i];
4575
- const size = (_i = sizes.get(id)) != null ? _i : getItemSize(ctx, id, i, data[i]);
4785
+ const size = (_h = sizes.get(id)) != null ? _h : getItemSize(ctx, id, i, data[i]);
4576
4786
  const bottom = top + size;
4577
- if (bottom > scroll - scrollBufferTop) {
4787
+ if (bottom > scrollTopBuffered) {
4578
4788
  loopStart = i;
4579
4789
  } else {
4580
4790
  break;
@@ -4603,8 +4813,8 @@ function calculateItemsInView(ctx, params = {}) {
4603
4813
  let firstFullyOnScreenIndex;
4604
4814
  const dataLength = data.length;
4605
4815
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
4606
- const id = (_j = idCache[i]) != null ? _j : getId(state, i);
4607
- const size = (_k = sizes.get(id)) != null ? _k : getItemSize(ctx, id, i, data[i]);
4816
+ const id = (_i = idCache[i]) != null ? _i : getId(state, i);
4817
+ const size = (_j = sizes.get(id)) != null ? _j : getItemSize(ctx, id, i, data[i]);
4608
4818
  const top = positions[i];
4609
4819
  if (!foundEnd) {
4610
4820
  if (startNoBuffer === null && top + size > scroll) {
@@ -4643,7 +4853,7 @@ function calculateItemsInView(ctx, params = {}) {
4643
4853
  const firstVisibleAnchorIndex = firstFullyOnScreenIndex != null ? firstFullyOnScreenIndex : startNoBuffer;
4644
4854
  if (firstVisibleAnchorIndex !== null && firstVisibleAnchorIndex !== void 0 && endNoBuffer !== null) {
4645
4855
  for (let i = firstVisibleAnchorIndex; i <= endNoBuffer; i++) {
4646
- const id = (_l = idCache[i]) != null ? _l : getId(state, i);
4856
+ const id = (_k = idCache[i]) != null ? _k : getId(state, i);
4647
4857
  idsInView.push(id);
4648
4858
  }
4649
4859
  }
@@ -4676,7 +4886,7 @@ function calculateItemsInView(ctx, params = {}) {
4676
4886
  const needNewContainers = [];
4677
4887
  const needNewContainersSet = /* @__PURE__ */ new Set();
4678
4888
  for (let i = startBuffered; i <= endBuffered; i++) {
4679
- const id = (_m = idCache[i]) != null ? _m : getId(state, i);
4889
+ const id = (_l = idCache[i]) != null ? _l : getId(state, i);
4680
4890
  if (!containerItemKeys.has(id)) {
4681
4891
  needNewContainersSet.add(i);
4682
4892
  needNewContainers.push(i);
@@ -4685,7 +4895,7 @@ function calculateItemsInView(ctx, params = {}) {
4685
4895
  if (alwaysRenderArr.length > 0) {
4686
4896
  for (const index of alwaysRenderArr) {
4687
4897
  if (index < 0 || index >= dataLength) continue;
4688
- const id = (_n = idCache[index]) != null ? _n : getId(state, index);
4898
+ const id = (_m = idCache[index]) != null ? _m : getId(state, index);
4689
4899
  if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
4690
4900
  needNewContainersSet.add(index);
4691
4901
  needNewContainers.push(index);
@@ -4724,7 +4934,7 @@ function calculateItemsInView(ctx, params = {}) {
4724
4934
  for (let idx = 0; idx < needNewContainers.length; idx++) {
4725
4935
  const i = needNewContainers[idx];
4726
4936
  const containerIndex = availableContainers[idx];
4727
- const id = (_o = idCache[i]) != null ? _o : getId(state, i);
4937
+ const id = (_n = idCache[i]) != null ? _n : getId(state, i);
4728
4938
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
4729
4939
  if (oldKey && oldKey !== id) {
4730
4940
  containerItemKeys.delete(oldKey);
@@ -4735,7 +4945,7 @@ function calculateItemsInView(ctx, params = {}) {
4735
4945
  state.containerItemTypes.set(containerIndex, requiredItemTypes[idx]);
4736
4946
  }
4737
4947
  containerItemKeys.set(id, containerIndex);
4738
- (_p = state.userScrollAnchorResetKeys) == null ? void 0 : _p.add(id);
4948
+ (_o = state.userScrollAnchorResetKeys) == null ? void 0 : _o.add(id);
4739
4949
  const containerSticky = `containerSticky${containerIndex}`;
4740
4950
  const isSticky = stickyIndicesSet.has(i);
4741
4951
  const isAlwaysRender = alwaysRenderSet.has(i);
@@ -4759,17 +4969,17 @@ function calculateItemsInView(ctx, params = {}) {
4759
4969
  if (numContainers !== prevNumContainers) {
4760
4970
  set$(ctx, "numContainers", numContainers);
4761
4971
  if (numContainers > peek$(ctx, "numContainersPooled")) {
4762
- set$(ctx, "numContainersPooled", Math.ceil(numContainers * 1.5));
4972
+ set$(ctx, "numContainersPooled", getExpandedContainerPoolSize(dataLength, numContainers));
4763
4973
  }
4764
4974
  }
4765
4975
  }
4766
- if (((_q = state.userScrollAnchorResetKeys) == null ? void 0 : _q.size) === 0) {
4976
+ if (((_p = state.userScrollAnchorResetKeys) == null ? void 0 : _p.size) === 0) {
4767
4977
  state.userScrollAnchorResetKeys = void 0;
4768
4978
  }
4769
4979
  if (alwaysRenderArr.length > 0) {
4770
4980
  for (const index of alwaysRenderArr) {
4771
4981
  if (index < 0 || index >= dataLength) continue;
4772
- const id = (_r = idCache[index]) != null ? _r : getId(state, index);
4982
+ const id = (_q = idCache[index]) != null ? _q : getId(state, index);
4773
4983
  const containerIndex = containerItemKeys.get(id);
4774
4984
  if (containerIndex !== void 0) {
4775
4985
  state.stickyContainerPool.add(containerIndex);
@@ -4873,21 +5083,25 @@ function doMaintainScrollAtEnd(ctx) {
4873
5083
  if (contentSize < state.scrollLength) {
4874
5084
  state.scroll = 0;
4875
5085
  }
4876
- requestAnimationFrame(() => {
4877
- var _a3;
4878
- if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
4879
- state.maintainingScrollAtEnd = true;
4880
- (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
4881
- animated: maintainScrollAtEnd.animated
4882
- });
4883
- setTimeout(
4884
- () => {
4885
- state.maintainingScrollAtEnd = false;
4886
- },
4887
- maintainScrollAtEnd.animated ? 500 : 0
4888
- );
4889
- }
4890
- });
5086
+ if (!state.maintainingScrollAtEnd) {
5087
+ state.maintainingScrollAtEnd = true;
5088
+ requestAnimationFrame(() => {
5089
+ var _a3;
5090
+ if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
5091
+ (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
5092
+ animated: maintainScrollAtEnd.animated
5093
+ });
5094
+ setTimeout(
5095
+ () => {
5096
+ state.maintainingScrollAtEnd = false;
5097
+ },
5098
+ maintainScrollAtEnd.animated ? 500 : 0
5099
+ );
5100
+ } else {
5101
+ state.maintainingScrollAtEnd = false;
5102
+ }
5103
+ });
5104
+ }
4891
5105
  return true;
4892
5106
  }
4893
5107
  return false;
@@ -4999,14 +5213,21 @@ function doInitialAllocateContainers(ctx) {
4999
5213
  } else {
5000
5214
  averageItemSize = estimatedItemSize;
5001
5215
  }
5002
- const numContainers = Math.ceil((scrollLength + drawDistance * 2) / averageItemSize * numColumns);
5216
+ const numContainers = Math.max(
5217
+ 1,
5218
+ Math.ceil((scrollLength + drawDistance * 2) / averageItemSize * numColumns)
5219
+ );
5003
5220
  for (let i = 0; i < numContainers; i++) {
5004
5221
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
5005
5222
  set$(ctx, `containerColumn${i}`, -1);
5006
5223
  set$(ctx, `containerSpan${i}`, 1);
5007
5224
  }
5008
5225
  set$(ctx, "numContainers", numContainers);
5009
- set$(ctx, "numContainersPooled", numContainers * state.props.initialContainerPoolRatio);
5226
+ set$(
5227
+ ctx,
5228
+ "numContainersPooled",
5229
+ getInitialContainerPoolSize(data.length, numContainers, state.props.initialContainerPoolRatio)
5230
+ );
5010
5231
  if (state.lastLayout) {
5011
5232
  if (state.initialScroll) {
5012
5233
  requestAnimationFrame(() => {
@@ -5157,8 +5378,8 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
5157
5378
  // src/core/onScroll.ts
5158
5379
  function trackInitialScrollNativeProgress(state, newScroll) {
5159
5380
  const initialNativeScrollWatchdog = initialScrollWatchdog.get(state);
5160
- const didInitialScrollProgress = !!initialNativeScrollWatchdog && initialScrollWatchdog.didObserveProgress(newScroll, initialNativeScrollWatchdog);
5161
- if (didInitialScrollProgress) {
5381
+ const didInitialScrollReachTarget = !!initialNativeScrollWatchdog && initialScrollWatchdog.didReachTarget(newScroll, initialNativeScrollWatchdog);
5382
+ if (didInitialScrollReachTarget) {
5162
5383
  initialScrollWatchdog.clear(state);
5163
5384
  return;
5164
5385
  }
@@ -5296,16 +5517,20 @@ function maybeUpdateAnchoredEndSpace(ctx) {
5296
5517
  let contentBelowAnchor = 0;
5297
5518
  const footerSize = ctx.values.get("footerSize") || 0;
5298
5519
  const stylePaddingBottom = state.props.stylePaddingBottom || 0;
5520
+ let hasUnknownTailSize = false;
5299
5521
  for (let index = anchorIndex; index < data.length; index++) {
5300
5522
  const itemKey = getId(state, index);
5301
5523
  const size = itemKey ? state.sizesKnown.get(itemKey) : void 0;
5302
5524
  const effectiveSize = index === anchorIndex && anchorMaxSize !== void 0 ? Math.min(size || 0, Math.max(0, anchorMaxSize)) : size;
5525
+ if (size === void 0) {
5526
+ hasUnknownTailSize = true;
5527
+ }
5303
5528
  if (effectiveSize !== null && effectiveSize !== void 0 && effectiveSize > 0) {
5304
5529
  contentBelowAnchor += effectiveSize;
5305
5530
  }
5306
5531
  }
5307
5532
  contentBelowAnchor += footerSize + stylePaddingBottom;
5308
- nextSize = Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
5533
+ nextSize = hasUnknownTailSize ? previousSize || 0 : Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
5309
5534
  }
5310
5535
  }
5311
5536
  if (previousSize !== nextSize) {
@@ -5318,6 +5543,22 @@ function maybeUpdateAnchoredEndSpace(ctx) {
5318
5543
  return nextSize;
5319
5544
  }
5320
5545
 
5546
+ // src/core/updateContentInsetEndAdjustment.ts
5547
+ function updateContentInsetEndAdjustment(ctx, previousContentInsetEndAdjustment) {
5548
+ const state = ctx.state;
5549
+ const previousContentInsetEnd = getContentInsetEnd(ctx, previousContentInsetEndAdjustment);
5550
+ const nextContentInsetEnd = getContentInsetEnd(ctx);
5551
+ const insetDiff = nextContentInsetEnd - previousContentInsetEnd;
5552
+ if (insetDiff !== 0) {
5553
+ const wasWithinEndThreshold = !!peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
5554
+ updateScroll(ctx, state.scroll, true, { markHasScrolled: false });
5555
+ const didRetargetInitialScroll = retargetActiveInitialScrollAtEnd(ctx);
5556
+ if (!didRetargetInitialScroll && wasWithinEndThreshold && (insetDiff > 0)) {
5557
+ requestAdjust(ctx, insetDiff);
5558
+ }
5559
+ }
5560
+ }
5561
+
5321
5562
  // src/core/updateItemSize.ts
5322
5563
  function runOrScheduleMVCPRecalculate(ctx) {
5323
5564
  const state = ctx.state;
@@ -5361,15 +5602,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5361
5602
  const {
5362
5603
  didContainersLayout,
5363
5604
  sizesKnown,
5364
- props: {
5365
- getFixedItemSize,
5366
- getItemType,
5367
- horizontal,
5368
- suggestEstimatedItemSize,
5369
- onItemSizeChanged,
5370
- data,
5371
- maintainScrollAtEnd
5372
- }
5605
+ props: { getFixedItemSize, getItemType, horizontal, onItemSizeChanged, data, maintainScrollAtEnd }
5373
5606
  } = state;
5374
5607
  if (!data) return;
5375
5608
  const index = state.indexByKey.get(itemKey);
@@ -5420,18 +5653,6 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5420
5653
  if (minIndexSizeChanged !== void 0) {
5421
5654
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
5422
5655
  }
5423
- if (IS_DEV && suggestEstimatedItemSize && minIndexSizeChanged !== void 0) {
5424
- if (state.timeoutSizeMessage) clearTimeout(state.timeoutSizeMessage);
5425
- state.timeoutSizeMessage = setTimeout(() => {
5426
- var _a4;
5427
- state.timeoutSizeMessage = void 0;
5428
- const num = state.sizesKnown.size;
5429
- const avg = (_a4 = state.averageSizes[""]) == null ? void 0 : _a4.avg;
5430
- console.warn(
5431
- `[legend-list] Based on the ${num} items rendered so far, the optimal estimated size is ${avg}.`
5432
- );
5433
- }, 1e3);
5434
- }
5435
5656
  const cur = peek$(ctx, "otherAxisSize");
5436
5657
  if (!cur || maxOtherAxisSize > cur) {
5437
5658
  set$(ctx, "otherAxisSize", maxOtherAxisSize);
@@ -5536,12 +5757,47 @@ function createColumnWrapperStyle(contentContainerStyle) {
5536
5757
  }
5537
5758
 
5538
5759
  // src/utils/createImperativeHandle.ts
5760
+ var DEFAULT_AVERAGE_ITEM_SIZE_TYPE = "default";
5761
+ function getAverageItemSizes(state) {
5762
+ const averageItemSizes = {};
5763
+ for (const itemType in state.averageSizes) {
5764
+ const averageSize = state.averageSizes[itemType];
5765
+ if (averageSize) {
5766
+ averageItemSizes[itemType || DEFAULT_AVERAGE_ITEM_SIZE_TYPE] = {
5767
+ average: averageSize.avg,
5768
+ count: averageSize.num
5769
+ };
5770
+ }
5771
+ }
5772
+ return averageItemSizes;
5773
+ }
5539
5774
  function createImperativeHandle(ctx) {
5540
5775
  const state = ctx.state;
5541
5776
  const IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS = 800;
5542
5777
  const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
5543
5778
  let imperativeScrollToken = 0;
5544
5779
  const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0;
5780
+ const isScrollToIndexReady = (targetIndex, allowEmpty = false) => {
5781
+ var _a3;
5782
+ const props = state.props;
5783
+ const dataLength = props.data.length;
5784
+ const anchorIndex = (_a3 = props.anchoredEndSpace) == null ? void 0 : _a3.anchorIndex;
5785
+ if (targetIndex < 0) {
5786
+ return allowEmpty;
5787
+ }
5788
+ if (targetIndex >= dataLength) {
5789
+ return false;
5790
+ }
5791
+ if (anchorIndex === void 0 || anchorIndex < 0 || anchorIndex >= dataLength || targetIndex < anchorIndex || props.getFixedItemSize) {
5792
+ return true;
5793
+ }
5794
+ for (let index = anchorIndex; index < dataLength; index++) {
5795
+ if (!state.sizesKnown.has(getId(state, index))) {
5796
+ return false;
5797
+ }
5798
+ }
5799
+ return true;
5800
+ };
5545
5801
  const runWhenReady = (token, run, isReady) => {
5546
5802
  const startedAt = Date.now();
5547
5803
  let stableFrames = 0;
@@ -5563,11 +5819,10 @@ function createImperativeHandle(ctx) {
5563
5819
  };
5564
5820
  requestAnimationFrame(check);
5565
5821
  };
5566
- const runScrollWithPromise = (run, options) => new Promise((resolve) => {
5567
- var _a3, _b;
5822
+ const runScrollWithPromise = (run, isReady = () => true) => new Promise((resolve) => {
5823
+ var _a3;
5568
5824
  const token = ++imperativeScrollToken;
5569
- const isReady = (_a3 = options == null ? void 0 : options.isReady) != null ? _a3 : (() => true);
5570
- (_b = state.pendingScrollResolve) == null ? void 0 : _b.call(state);
5825
+ (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
5571
5826
  state.pendingScrollResolve = resolve;
5572
5827
  const runNow = () => {
5573
5828
  if (token !== imperativeScrollToken) {
@@ -5642,6 +5897,7 @@ function createImperativeHandle(ctx) {
5642
5897
  },
5643
5898
  end: state.endNoBuffer,
5644
5899
  endBuffered: state.endBuffered,
5900
+ getAverageItemSizes: () => getAverageItemSizes(state),
5645
5901
  isAtEnd: peek$(ctx, "isAtEnd"),
5646
5902
  isAtStart: peek$(ctx, "isAtStart"),
5647
5903
  isEndReached: state.isEndReached,
@@ -5665,8 +5921,14 @@ function createImperativeHandle(ctx) {
5665
5921
  startBuffered: state.startBuffered
5666
5922
  }),
5667
5923
  reportContentInset: (inset) => {
5924
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
5925
+ const previousInset = state.contentInsetOverride;
5668
5926
  state.contentInsetOverride = inset != null ? inset : void 0;
5927
+ 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);
5669
5928
  updateScroll(ctx, state.scroll, true, { markHasScrolled: false });
5929
+ if (didChange) {
5930
+ retargetActiveInitialScrollAtEnd(ctx);
5931
+ }
5670
5932
  },
5671
5933
  scrollIndexIntoView: (options) => runScrollWithPromise(() => scrollIndexIntoView(options)),
5672
5934
  scrollItemIntoView: ({ item, ...props }) => runScrollWithPromise(() => {
@@ -5678,40 +5940,34 @@ function createImperativeHandle(ctx) {
5678
5940
  }
5679
5941
  return false;
5680
5942
  }),
5681
- scrollToEnd: (options) => runScrollWithPromise(() => {
5682
- const data = state.props.data;
5683
- const stylePaddingBottom = state.props.stylePaddingBottom;
5684
- const index = data.length - 1;
5685
- if (index !== -1) {
5686
- const paddingBottom = stylePaddingBottom || 0;
5687
- const footerSize = peek$(ctx, "footerSize") || 0;
5688
- scrollToIndex(ctx, {
5689
- ...options,
5690
- index,
5691
- viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
5692
- viewPosition: 1
5693
- });
5694
- return true;
5695
- }
5696
- return false;
5697
- }),
5698
- scrollToIndex: (params) => {
5699
- const shouldWaitForOutOfRangeTarget = params.index >= 0 && params.index >= state.props.data.length;
5700
- const options = shouldWaitForOutOfRangeTarget ? {
5701
- isReady: () => {
5702
- var _a3;
5703
- const props = state.props;
5704
- const anchorIndex = (_a3 = props.anchoredEndSpace) == null ? void 0 : _a3.anchorIndex;
5705
- const lastIndex = props.data.length - 1;
5706
- const isInRange = params.index < props.data.length;
5707
- const shouldWaitForAnchorSize = isInRange && anchorIndex !== void 0 && anchorIndex >= 0 && params.index >= anchorIndex && !props.getFixedItemSize && !state.sizesKnown.has(getId(state, lastIndex));
5708
- return isInRange && !shouldWaitForAnchorSize;
5943
+ scrollToEnd: (options) => runScrollWithPromise(
5944
+ () => {
5945
+ const data = state.props.data;
5946
+ const stylePaddingBottom = state.props.stylePaddingBottom;
5947
+ const index = data.length - 1;
5948
+ if (index !== -1) {
5949
+ const paddingBottom = stylePaddingBottom || 0;
5950
+ const footerSize = peek$(ctx, "footerSize") || 0;
5951
+ scrollToIndex(ctx, {
5952
+ ...options,
5953
+ index,
5954
+ viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
5955
+ viewPosition: 1
5956
+ });
5957
+ return true;
5709
5958
  }
5710
- } : void 0;
5711
- return runScrollWithPromise(() => {
5712
- scrollToIndex(ctx, params);
5713
- return true;
5714
- }, options);
5959
+ return false;
5960
+ },
5961
+ () => isScrollToIndexReady(state.props.data.length - 1, true)
5962
+ ),
5963
+ scrollToIndex: (params) => {
5964
+ return runScrollWithPromise(
5965
+ () => {
5966
+ scrollToIndex(ctx, params);
5967
+ return true;
5968
+ },
5969
+ params.index >= 0 ? () => isScrollToIndexReady(params.index) : void 0
5970
+ );
5715
5971
  },
5716
5972
  scrollToItem: ({ item, ...props }) => runScrollWithPromise(() => {
5717
5973
  const data = state.props.data;
@@ -5967,6 +6223,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5967
6223
  data: dataProp = [],
5968
6224
  dataVersion,
5969
6225
  drawDistance = 250,
6226
+ contentInsetEndAdjustment,
5970
6227
  estimatedItemSize = 100,
5971
6228
  estimatedListSize,
5972
6229
  extraData,
@@ -5974,7 +6231,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5974
6231
  getFixedItemSize,
5975
6232
  getItemType,
5976
6233
  horizontal,
5977
- initialContainerPoolRatio = 2,
6234
+ initialContainerPoolRatio = 3,
5978
6235
  initialScrollAtEnd = false,
5979
6236
  initialScrollIndex: initialScrollIndexProp,
5980
6237
  initialScrollOffset: initialScrollOffsetProp,
@@ -6015,7 +6272,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6015
6272
  stickyIndices: stickyIndicesDeprecated,
6016
6273
  // TODOV3: Remove from v3 release
6017
6274
  style: styleProp,
6018
- suggestEstimatedItemSize,
6019
6275
  useWindowScroll = false,
6020
6276
  viewabilityConfig,
6021
6277
  viewabilityConfigCallbackPairs,
@@ -6076,6 +6332,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6076
6332
  const combinedRef = useCombinedRef(refScroller, refScrollView);
6077
6333
  const keyExtractor = keyExtractorProp != null ? keyExtractorProp : ((_item, index) => index.toString());
6078
6334
  const stickyHeaderIndices = stickyHeaderIndicesProp != null ? stickyHeaderIndicesProp : stickyIndicesDeprecated;
6335
+ const contentInsetEndAdjustmentResolved = contentInsetEndAdjustment ;
6336
+ const previousContentInsetEndAdjustmentRef = useRef(contentInsetEndAdjustmentResolved);
6079
6337
  const alwaysRenderIndices = useMemo(() => {
6080
6338
  const indices = getAlwaysRenderIndices(alwaysRender, dataProp, keyExtractor, anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex);
6081
6339
  return { arr: indices, set: new Set(indices) };
@@ -6154,7 +6412,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6154
6412
  startReachedSnapshotDataChangeEpoch: void 0,
6155
6413
  stickyContainerPool: /* @__PURE__ */ new Set(),
6156
6414
  stickyContainers: /* @__PURE__ */ new Map(),
6157
- timeoutSizeMessage: 0,
6158
6415
  timeouts: /* @__PURE__ */ new Set(),
6159
6416
  totalSize: 0,
6160
6417
  viewabilityConfigCallbackPairs: void 0
@@ -6174,7 +6431,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6174
6431
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
6175
6432
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
6176
6433
  const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
6177
- if (didDataChangeLocal && state.didFinishInitialScroll && ((_f = state.initialScroll) == null ? void 0 : _f.viewPosition) === 1 && state.props.data.length > 0) {
6434
+ if (didDataChangeLocal && !initialScrollAtEnd && state.didFinishInitialScroll && ((_f = state.initialScroll) == null ? void 0 : _f.viewPosition) === 1 && state.props.data.length > 0) {
6178
6435
  clearPreservedInitialScrollTarget(state);
6179
6436
  }
6180
6437
  if (didDataChangeLocal) {
@@ -6195,6 +6452,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6195
6452
  anchoredEndSpace: anchoredEndSpaceResolved,
6196
6453
  animatedProps: animatedPropsInternal,
6197
6454
  contentInset,
6455
+ contentInsetEndAdjustment: contentInsetEndAdjustmentResolved,
6198
6456
  data: dataProp,
6199
6457
  dataVersion,
6200
6458
  drawDistance,
@@ -6228,7 +6486,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6228
6486
  stickyPositionComponentInternal,
6229
6487
  stylePaddingBottom: stylePaddingBottomState,
6230
6488
  stylePaddingTop: stylePaddingTopState,
6231
- suggestEstimatedItemSize: !!suggestEstimatedItemSize,
6232
6489
  useWindowScroll: useWindowScrollResolved
6233
6490
  };
6234
6491
  state.refScroller = refScroller;
@@ -6312,6 +6569,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6312
6569
  didAnchoredEndSpaceAnchorIndexChange,
6313
6570
  numColumnsProp
6314
6571
  ]);
6572
+ useLayoutEffect(() => {
6573
+ const previousContentInsetEndAdjustment = previousContentInsetEndAdjustmentRef.current;
6574
+ previousContentInsetEndAdjustmentRef.current = contentInsetEndAdjustmentResolved;
6575
+ updateContentInsetEndAdjustment(ctx, previousContentInsetEndAdjustment);
6576
+ }, [ctx, contentInsetEndAdjustmentResolved]);
6315
6577
  const onLayoutFooter = useCallback(
6316
6578
  (layout) => {
6317
6579
  if (!usesBootstrapInitialScroll) {