@legendapp/list 3.0.0-beta.43 → 3.0.0-beta.45

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.
@@ -34,37 +34,6 @@ var View = React3.forwardRef(function View2(props, ref) {
34
34
  });
35
35
  var Text = View;
36
36
 
37
- // src/state/getContentInsetEnd.ts
38
- function getContentInsetEnd(state) {
39
- var _a3;
40
- const { props } = state;
41
- const horizontal = props.horizontal;
42
- const contentInset = props.contentInset;
43
- const baseInset = contentInset != null ? contentInset : state.nativeContentInset;
44
- const overrideInset = (_a3 = state.contentInsetOverride) != null ? _a3 : void 0;
45
- if (overrideInset) {
46
- const mergedInset = { bottom: 0, right: 0, ...baseInset, ...overrideInset };
47
- return (horizontal ? mergedInset.right : mergedInset.bottom) || 0;
48
- }
49
- if (baseInset) {
50
- return (horizontal ? baseInset.right : baseInset.bottom) || 0;
51
- }
52
- return 0;
53
- }
54
-
55
- // src/state/getContentSize.ts
56
- function getContentSize(ctx) {
57
- var _a3;
58
- const { values, state } = ctx;
59
- const stylePaddingTop = values.get("stylePaddingTop") || 0;
60
- const stylePaddingBottom = state.props.stylePaddingBottom || 0;
61
- const headerSize = values.get("headerSize") || 0;
62
- const footerSize = values.get("footerSize") || 0;
63
- const contentInsetBottom = getContentInsetEnd(state);
64
- const totalSize = (_a3 = state.pendingTotalSize) != null ? _a3 : values.get("totalSize");
65
- return headerSize + footerSize + totalSize + stylePaddingTop + stylePaddingBottom + (contentInsetBottom || 0);
66
- }
67
-
68
37
  // src/platform/Animated.tsx
69
38
  var createAnimatedValue = (value) => value;
70
39
 
@@ -89,6 +58,11 @@ function StateProvider({ children }) {
89
58
  ["headerSize", 0],
90
59
  ["numContainers", 0],
91
60
  ["activeStickyIndex", -1],
61
+ ["isAtEnd", false],
62
+ ["isAtStart", false],
63
+ ["isNearEnd", false],
64
+ ["isNearStart", false],
65
+ ["isWithinMaintainScrollAtEndThreshold", false],
92
66
  ["totalSize", 0],
93
67
  ["scrollAdjustPending", 0]
94
68
  ]),
@@ -180,29 +154,71 @@ function notifyPosition$(ctx, key, value) {
180
154
  function useArr$(signalNames) {
181
155
  const ctx = React3__namespace.useContext(ContextState);
182
156
  const { subscribe, get } = React3__namespace.useMemo(() => createSelectorFunctionsArr(ctx, signalNames), [ctx, signalNames]);
183
- const value = shim.useSyncExternalStore(subscribe, get);
157
+ const value = shim.useSyncExternalStore(subscribe, get, get);
184
158
  return value;
185
159
  }
186
160
  function useSelector$(signalName, selector) {
187
161
  const ctx = React3__namespace.useContext(ContextState);
188
162
  const { subscribe, get } = React3__namespace.useMemo(() => createSelectorFunctionsArr(ctx, [signalName]), [ctx, signalName]);
189
- const value = shim.useSyncExternalStore(subscribe, () => selector(get()[0]));
163
+ const getSelectedValue = React3__namespace.useCallback(() => selector(get()[0]), [get, selector]);
164
+ const value = shim.useSyncExternalStore(subscribe, getSelectedValue, getSelectedValue);
190
165
  return value;
191
166
  }
192
167
 
168
+ // src/state/getContentInsetEnd.ts
169
+ function getContentInsetEnd(ctx) {
170
+ var _a3, _b;
171
+ const state = ctx.state;
172
+ const { props } = state;
173
+ const horizontal = props.horizontal;
174
+ const contentInset = props.contentInset;
175
+ const baseInset = contentInset != null ? contentInset : state.nativeContentInset;
176
+ const baseEndInset = (horizontal ? baseInset == null ? void 0 : baseInset.right : baseInset == null ? void 0 : baseInset.bottom) || 0;
177
+ const anchoredEndSpaceSize = peek$(ctx, "anchoredEndSpaceSize");
178
+ const anchoredEndInset = ((_a3 = props.anchoredEndSpace) == null ? void 0 : _a3.includeInEndInset) && anchoredEndSpaceSize ? anchoredEndSpaceSize : 0;
179
+ const overrideInset = (_b = state.contentInsetOverride) != null ? _b : void 0;
180
+ if (overrideInset) {
181
+ const mergedInset = { bottom: 0, right: 0, ...baseInset, ...overrideInset };
182
+ return Math.max((horizontal ? mergedInset.right : mergedInset.bottom) || 0, anchoredEndInset);
183
+ }
184
+ return Math.max(baseEndInset, anchoredEndInset);
185
+ }
186
+
187
+ // src/state/getContentSize.ts
188
+ function getContentSize(ctx) {
189
+ var _a3;
190
+ const { values, state } = ctx;
191
+ const stylePaddingTop = values.get("stylePaddingTop") || 0;
192
+ const stylePaddingBottom = state.props.stylePaddingBottom || 0;
193
+ const headerSize = values.get("headerSize") || 0;
194
+ const footerSize = values.get("footerSize") || 0;
195
+ const contentInsetBottom = getContentInsetEnd(ctx);
196
+ const totalSize = (_a3 = state.pendingTotalSize) != null ? _a3 : values.get("totalSize");
197
+ return headerSize + footerSize + totalSize + stylePaddingTop + stylePaddingBottom + (contentInsetBottom || 0);
198
+ }
199
+
193
200
  // src/components/DebugView.tsx
194
201
  var DebugRow = ({ children }) => {
195
202
  return /* @__PURE__ */ React3__namespace.createElement(View, { style: { alignItems: "center", flexDirection: "row", justifyContent: "space-between" } }, children);
196
203
  };
197
- React3__namespace.memo(function DebugView2({ state }) {
204
+ React3__namespace.memo(function DebugView2() {
198
205
  const ctx = useStateContext();
199
- const [totalSize = 0, scrollAdjust = 0, rawScroll = 0, scroll = 0, _numContainers = 0, _numContainersPooled = 0] = useArr$([
206
+ const [
207
+ totalSize = 0,
208
+ scrollAdjust = 0,
209
+ rawScroll = 0,
210
+ scroll = 0,
211
+ _numContainers = 0,
212
+ _numContainersPooled = 0,
213
+ isAtEnd = false
214
+ ] = useArr$([
200
215
  "totalSize",
201
216
  "scrollAdjust",
202
217
  "debugRawScroll",
203
218
  "debugComputedScroll",
204
219
  "numContainers",
205
- "numContainersPooled"
220
+ "numContainersPooled",
221
+ "isAtEnd"
206
222
  ]);
207
223
  const contentSize = getContentSize(ctx);
208
224
  const [, forceUpdate] = React3.useReducer((x) => x + 1, 0);
@@ -227,7 +243,7 @@ React3__namespace.memo(function DebugView2({ state }) {
227
243
  },
228
244
  /* @__PURE__ */ React3__namespace.createElement(DebugRow, null, /* @__PURE__ */ React3__namespace.createElement(Text, null, "TotalSize:"), /* @__PURE__ */ React3__namespace.createElement(Text, null, totalSize.toFixed(2))),
229
245
  /* @__PURE__ */ React3__namespace.createElement(DebugRow, null, /* @__PURE__ */ React3__namespace.createElement(Text, null, "ContentSize:"), /* @__PURE__ */ React3__namespace.createElement(Text, null, contentSize.toFixed(2))),
230
- /* @__PURE__ */ React3__namespace.createElement(DebugRow, null, /* @__PURE__ */ React3__namespace.createElement(Text, null, "At end:"), /* @__PURE__ */ React3__namespace.createElement(Text, null, String(state.isAtEnd))),
246
+ /* @__PURE__ */ React3__namespace.createElement(DebugRow, null, /* @__PURE__ */ React3__namespace.createElement(Text, null, "At end:"), /* @__PURE__ */ React3__namespace.createElement(Text, null, String(isAtEnd))),
231
247
  /* @__PURE__ */ React3__namespace.createElement(DebugRow, null, /* @__PURE__ */ React3__namespace.createElement(Text, null, "ScrollAdjust:"), /* @__PURE__ */ React3__namespace.createElement(Text, null, scrollAdjust.toFixed(2))),
232
248
  /* @__PURE__ */ React3__namespace.createElement(DebugRow, null, /* @__PURE__ */ React3__namespace.createElement(Text, null, "RawScroll: "), /* @__PURE__ */ React3__namespace.createElement(Text, null, rawScroll.toFixed(2))),
233
249
  /* @__PURE__ */ React3__namespace.createElement(DebugRow, null, /* @__PURE__ */ React3__namespace.createElement(Text, null, "ComputedScroll: "), /* @__PURE__ */ React3__namespace.createElement(Text, null, scroll.toFixed(2)))
@@ -250,6 +266,7 @@ var IS_DEV = (_a2 = processDev != null ? processDev : metroDev) != null ? _a2 :
250
266
 
251
267
  // src/constants.ts
252
268
  var POSITION_OUT_OF_VIEW = -1e7;
269
+ var EDGE_POSITION_EPSILON = 1;
253
270
  var ENABLE_DEVMODE = IS_DEV && false;
254
271
  var ENABLE_DEBUG_VIEW = IS_DEV && false;
255
272
  var typedForwardRef = React3__namespace.forwardRef;
@@ -709,17 +726,20 @@ var Container = typedMemo(function Container2({
709
726
  const { columnGap, rowGap, gap } = columnWrapperStyle;
710
727
  if (horizontal) {
711
728
  paddingStyles = {
729
+ paddingBottom: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0,
712
730
  paddingRight: columnGap || gap || void 0,
713
- paddingVertical: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
731
+ paddingTop: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
714
732
  };
715
733
  } else {
716
734
  paddingStyles = {
717
735
  paddingBottom: rowGap || gap || void 0,
718
- paddingHorizontal: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
736
+ paddingLeft: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0,
737
+ paddingRight: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
719
738
  };
720
739
  }
721
740
  }
722
741
  return horizontal ? {
742
+ boxSizing: paddingStyles ? "border-box" : void 0,
723
743
  flexDirection: ItemSeparatorComponent ? "row" : void 0,
724
744
  height: otherAxisSize,
725
745
  left: 0,
@@ -727,6 +747,7 @@ var Container = typedMemo(function Container2({
727
747
  top: otherAxisPos,
728
748
  ...paddingStyles || {}
729
749
  } : {
750
+ boxSizing: paddingStyles ? "border-box" : void 0,
730
751
  left: otherAxisPos,
731
752
  position: "absolute",
732
753
  right: numColumns > 1 ? null : 0,
@@ -940,7 +961,7 @@ var ContainersInner = typedMemo(function ContainersInner2({ horizontal, numColum
940
961
  const ref = React3.useRef(null);
941
962
  const ctx = useStateContext();
942
963
  const columnWrapperStyle = ctx.columnWrapperStyle;
943
- const [totalSize, otherAxisSize] = useArr$(["totalSize", "otherAxisSize"]);
964
+ const [otherAxisSize, totalSize] = useArr$(["otherAxisSize", "totalSize"]);
944
965
  useDOMOrder(ref);
945
966
  const style = horizontal ? { minHeight: otherAxisSize, position: "relative", width: totalSize } : { height: totalSize, minWidth: otherAxisSize, position: "relative" };
946
967
  if (columnWrapperStyle && numColumns > 1) {
@@ -955,9 +976,6 @@ var ContainersInner = typedMemo(function ContainersInner2({ horizontal, numColum
955
976
  style.marginRight = -gapX;
956
977
  }
957
978
  } else {
958
- if (gapX) {
959
- style.marginLeft = style.marginRight = -gapX;
960
- }
961
979
  if (gapY) {
962
980
  style.marginBottom = -gapY;
963
981
  }
@@ -969,7 +987,6 @@ var Containers = typedMemo(function Containers2({
969
987
  horizontal,
970
988
  recycleItems,
971
989
  ItemSeparatorComponent,
972
- waitForInitialLayout,
973
990
  updateItemSize: updateItemSize2,
974
991
  getRenderedItem: getRenderedItem2,
975
992
  stickyHeaderConfig
@@ -993,7 +1010,7 @@ var Containers = typedMemo(function Containers2({
993
1010
  )
994
1011
  );
995
1012
  }
996
- return /* @__PURE__ */ React3__namespace.createElement(ContainersInner, { horizontal, numColumns, waitForInitialLayout }, containers);
1013
+ return /* @__PURE__ */ React3__namespace.createElement(ContainersInner, { horizontal, numColumns }, containers);
997
1014
  });
998
1015
 
999
1016
  // src/platform/StyleSheet.tsx
@@ -1136,6 +1153,7 @@ function resolveWindowScrollTarget({ clampedOffset, horizontal, listPos, scroll
1136
1153
  var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView2({
1137
1154
  children,
1138
1155
  style,
1156
+ contentContainerClassName,
1139
1157
  contentContainerStyle,
1140
1158
  horizontal = false,
1141
1159
  contentOffset,
@@ -1271,18 +1289,18 @@ var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView
1271
1289
  const scrollEventCoalescer = useRafCoalescer(emitScroll);
1272
1290
  const handleScroll = React3.useCallback(
1273
1291
  (_event) => {
1274
- var _a3;
1275
1292
  if (!onScroll2) {
1276
1293
  return;
1277
1294
  }
1278
- const scrollingTo = (_a3 = ctx.state) == null ? void 0 : _a3.scrollingTo;
1279
- if (scrollingTo && !scrollingTo.animated) {
1295
+ const state = ctx.state;
1296
+ const shouldFlushImmediately = !!(state == null ? void 0 : state.scrollingTo) || !!(state == null ? void 0 : state.initialScrollSession) && !state.didFinishInitialScroll || !!(state == null ? void 0 : state.initialScroll) && !state.didFinishInitialScroll || !!state && isInMVCPActiveMode(state);
1297
+ if (shouldFlushImmediately) {
1280
1298
  scrollEventCoalescer.flush();
1281
1299
  } else {
1282
1300
  scrollEventCoalescer.schedule();
1283
1301
  }
1284
1302
  },
1285
- [onScroll2, scrollEventCoalescer]
1303
+ [ctx.state, onScroll2, scrollEventCoalescer]
1286
1304
  );
1287
1305
  React3.useLayoutEffect(() => {
1288
1306
  const target = getScrollTarget();
@@ -1348,13 +1366,14 @@ var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView
1348
1366
  ...StyleSheet.flatten(contentContainerStyle)
1349
1367
  };
1350
1368
  const {
1369
+ contentContainerClassName: _contentContainerClassName,
1351
1370
  contentInset: _contentInset,
1352
1371
  scrollEventThrottle: _scrollEventThrottle,
1353
1372
  ScrollComponent: _ScrollComponent,
1354
1373
  useWindowScroll: _useWindowScroll,
1355
1374
  ...webProps
1356
1375
  } = props;
1357
- return /* @__PURE__ */ React3__namespace.createElement("div", { ref: scrollRef, ...webProps, style: scrollViewStyle }, refreshControl, /* @__PURE__ */ React3__namespace.createElement("div", { ref: contentRef, style: contentStyle }, children));
1376
+ return /* @__PURE__ */ React3__namespace.createElement("div", { ref: scrollRef, ...webProps, style: scrollViewStyle }, refreshControl, /* @__PURE__ */ React3__namespace.createElement("div", { className: contentContainerClassName, ref: contentRef, style: contentStyle }, children));
1358
1377
  });
1359
1378
  function useValueListener$(key, callback) {
1360
1379
  const ctx = useStateContext();
@@ -1367,6 +1386,21 @@ function useValueListener$(key, callback) {
1367
1386
  }
1368
1387
 
1369
1388
  // src/components/ScrollAdjust.tsx
1389
+ function getScrollAdjustAxis(horizontal) {
1390
+ return horizontal ? {
1391
+ contentSizeKey: "scrollWidth",
1392
+ paddingEndProp: "paddingRight",
1393
+ viewportSizeKey: "clientWidth",
1394
+ x: 1,
1395
+ y: 0
1396
+ } : {
1397
+ contentSizeKey: "scrollHeight",
1398
+ paddingEndProp: "paddingBottom",
1399
+ viewportSizeKey: "clientHeight",
1400
+ x: 0,
1401
+ y: 1
1402
+ };
1403
+ }
1370
1404
  function ScrollAdjust() {
1371
1405
  const ctx = useStateContext();
1372
1406
  const lastScrollOffsetRef = React3__namespace.useRef(0);
@@ -1380,32 +1414,34 @@ function ScrollAdjust() {
1380
1414
  if (scrollView && scrollOffset !== lastScrollOffsetRef.current) {
1381
1415
  const scrollDelta = scrollOffset - lastScrollOffsetRef.current;
1382
1416
  if (scrollDelta !== 0) {
1417
+ const axis = getScrollAdjustAxis(!!ctx.state.props.horizontal);
1383
1418
  const contentNode = scrollView.getContentNode();
1384
1419
  const prevScroll = scrollView.getCurrentScrollOffset();
1385
1420
  const el = scrollView.getScrollableNode();
1421
+ const scrollBy = () => scrollView.scrollBy(axis.x * scrollDelta, axis.y * scrollDelta);
1386
1422
  if (!contentNode) {
1387
- scrollView.scrollBy(0, scrollDelta);
1423
+ scrollBy();
1388
1424
  lastScrollOffsetRef.current = scrollOffset;
1389
1425
  return;
1390
1426
  }
1391
- const totalSize = contentNode.scrollHeight;
1392
- const viewportSize = el.clientHeight;
1427
+ const totalSize = contentNode[axis.contentSizeKey];
1428
+ const viewportSize = el[axis.viewportSizeKey];
1393
1429
  const nextScroll = prevScroll + scrollDelta;
1394
1430
  if (scrollDelta > 0 && !ctx.state.adjustingFromInitialMount && totalSize < nextScroll + viewportSize) {
1395
- const paddingBottom = ctx.state.props.stylePaddingBottom || 0;
1431
+ const previousPaddingEnd = contentNode.style[axis.paddingEndProp];
1396
1432
  const pad = (nextScroll + viewportSize - totalSize) * 2;
1397
- contentNode.style.paddingBottom = `${pad}px`;
1433
+ contentNode.style[axis.paddingEndProp] = `${pad}px`;
1398
1434
  void contentNode.offsetHeight;
1399
- scrollView.scrollBy(0, scrollDelta);
1435
+ scrollBy();
1400
1436
  if (resetPaddingRafRef.current !== void 0) {
1401
1437
  cancelAnimationFrame(resetPaddingRafRef.current);
1402
1438
  }
1403
1439
  resetPaddingRafRef.current = requestAnimationFrame(() => {
1404
1440
  resetPaddingRafRef.current = void 0;
1405
- contentNode.style.paddingBottom = paddingBottom ? `${paddingBottom}px` : "0";
1441
+ contentNode.style[axis.paddingEndProp] = previousPaddingEnd;
1406
1442
  });
1407
1443
  } else {
1408
- scrollView.scrollBy(0, scrollDelta);
1444
+ scrollBy();
1409
1445
  }
1410
1446
  }
1411
1447
  lastScrollOffsetRef.current = scrollOffset;
@@ -1419,8 +1455,19 @@ function SnapWrapper({ ScrollComponent, ...props }) {
1419
1455
  const [snapToOffsets] = useArr$(["snapToOffsets"]);
1420
1456
  return /* @__PURE__ */ React3__namespace.createElement(ScrollComponent, { ...props, snapToOffsets });
1421
1457
  }
1458
+ function WebAnchoredEndSpace({ horizontal }) {
1459
+ const ctx = useStateContext();
1460
+ const [anchoredEndSpaceSize] = useArr$(["anchoredEndSpaceSize"]);
1461
+ const shouldRenderAnchoredEndSpace = !!ctx.state.props.anchoredEndSpace && (anchoredEndSpaceSize || 0) > 0;
1462
+ if (!shouldRenderAnchoredEndSpace) {
1463
+ return null;
1464
+ }
1465
+ const style = horizontal ? { height: "100%", width: anchoredEndSpaceSize || 0 } : { height: anchoredEndSpaceSize || 0 };
1466
+ return /* @__PURE__ */ React3__namespace.createElement("div", { style }, null);
1467
+ }
1422
1468
  var LayoutView = ({ onLayoutChange, refView, children, ...rest }) => {
1423
- const ref = refView != null ? refView : React3.useRef(null);
1469
+ const localRef = React3.useRef(null);
1470
+ const ref = refView != null ? refView : localRef;
1424
1471
  useOnLayoutSync({ onLayoutChange, ref });
1425
1472
  return /* @__PURE__ */ React3__namespace.createElement("div", { ...rest, ref }, children);
1426
1473
  };
@@ -1444,7 +1491,6 @@ var ListComponent = typedMemo(function ListComponent2({
1444
1491
  recycleItems,
1445
1492
  ItemSeparatorComponent,
1446
1493
  alignItemsAtEnd: _alignItemsAtEnd,
1447
- waitForInitialLayout,
1448
1494
  onScroll: onScroll2,
1449
1495
  onLayout,
1450
1496
  ListHeaderComponent,
@@ -1466,12 +1512,14 @@ var ListComponent = typedMemo(function ListComponent2({
1466
1512
  }) {
1467
1513
  const ctx = useStateContext();
1468
1514
  const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
1469
- const ScrollComponent = renderScrollComponent ? React3.useMemo(
1470
- () => React3__namespace.forwardRef(
1515
+ const ScrollComponent = React3.useMemo(() => {
1516
+ if (!renderScrollComponent) {
1517
+ return ListComponentScrollView;
1518
+ }
1519
+ return React3__namespace.forwardRef(
1471
1520
  (props, ref) => renderScrollComponent({ ...props, ref })
1472
- ),
1473
- [renderScrollComponent]
1474
- ) : ListComponentScrollView;
1521
+ );
1522
+ }, [renderScrollComponent]);
1475
1523
  const SnapOrScroll = snapToIndices ? SnapWrapper : ScrollComponent;
1476
1524
  React3.useLayoutEffect(() => {
1477
1525
  if (!ListHeaderComponent) {
@@ -1507,7 +1555,7 @@ var ListComponent = typedMemo(function ListComponent2({
1507
1555
  height: "100%"
1508
1556
  } : {}
1509
1557
  ],
1510
- contentOffset: initialContentOffset ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
1558
+ contentOffset: initialContentOffset !== void 0 ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
1511
1559
  horizontal,
1512
1560
  maintainVisibleContentPosition: maintainVisibleContentPosition.size || maintainVisibleContentPosition.data ? { minIndexForVisible: 0 } : void 0,
1513
1561
  onLayout,
@@ -1527,168 +1575,229 @@ var ListComponent = typedMemo(function ListComponent2({
1527
1575
  ItemSeparatorComponent,
1528
1576
  recycleItems,
1529
1577
  stickyHeaderConfig,
1530
- updateItemSize: updateItemSize2,
1531
- waitForInitialLayout
1578
+ updateItemSize: updateItemSize2
1532
1579
  }
1533
1580
  ),
1534
1581
  ListFooterComponent && /* @__PURE__ */ React3__namespace.createElement(LayoutView, { onLayoutChange: onLayoutFooterInternal, style: ListFooterComponentStyle }, getComponent(ListFooterComponent)),
1582
+ /* @__PURE__ */ React3__namespace.createElement(WebAnchoredEndSpace, { horizontal }),
1535
1583
  IS_DEV && ENABLE_DEVMODE
1536
1584
  );
1537
1585
  });
1538
-
1539
- // src/core/calculateOffsetForIndex.ts
1540
- function calculateOffsetForIndex(ctx, index) {
1541
- const state = ctx.state;
1542
- return index !== void 0 ? state.positions[index] || 0 : 0;
1543
- }
1544
-
1545
- // src/core/getTopOffsetAdjustment.ts
1546
- function getTopOffsetAdjustment(ctx) {
1547
- return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
1586
+ var WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH = 100;
1587
+ var WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO = 0.9;
1588
+ var WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO = 0.9;
1589
+ function useDevChecksImpl(props) {
1590
+ const ctx = useStateContext();
1591
+ const { childrenMode, keyExtractor, renderScrollComponent, stickyHeaderIndices, stickyIndices, useWindowScroll } = props;
1592
+ React3.useEffect(() => {
1593
+ if (stickyIndices && !stickyHeaderIndices) {
1594
+ warnDevOnce(
1595
+ "stickyIndices",
1596
+ "stickyIndices has been renamed to stickyHeaderIndices. Please update your props to use stickyHeaderIndices."
1597
+ );
1598
+ }
1599
+ }, [stickyHeaderIndices, stickyIndices]);
1600
+ React3.useEffect(() => {
1601
+ if (useWindowScroll && renderScrollComponent) {
1602
+ warnDevOnce(
1603
+ "useWindowScrollRenderScrollComponent",
1604
+ "useWindowScroll is not supported when renderScrollComponent is provided."
1605
+ );
1606
+ }
1607
+ }, [renderScrollComponent, useWindowScroll]);
1608
+ React3.useEffect(() => {
1609
+ if (!keyExtractor && !ctx.state.isFirst && ctx.state.didDataChange && !childrenMode) {
1610
+ warnDevOnce(
1611
+ "keyExtractor",
1612
+ "Changing data without a keyExtractor can cause slow performance and resetting scroll. If your list data can change you should use a keyExtractor with a unique id for best performance and behavior."
1613
+ );
1614
+ }
1615
+ }, [childrenMode, ctx, keyExtractor]);
1616
+ React3.useEffect(() => {
1617
+ const state = ctx.state;
1618
+ const dataLength = state.props.data.length;
1619
+ const useWindowScrollResolved = state.props.useWindowScroll;
1620
+ if (useWindowScrollResolved || dataLength < WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH) {
1621
+ return;
1622
+ }
1623
+ const warnIfUnboundedOuterSize = () => {
1624
+ const readyToRender = peek$(ctx, "readyToRender");
1625
+ const numContainers = peek$(ctx, "numContainers") || 0;
1626
+ const totalSize = peek$(ctx, "totalSize") || 0;
1627
+ const scrollLength = ctx.state.scrollLength || 0;
1628
+ if (!readyToRender || totalSize <= 0 || scrollLength <= 0) {
1629
+ return;
1630
+ }
1631
+ const rendersAlmostEverything = numContainers >= Math.ceil(dataLength * WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO);
1632
+ const viewportMatchesContent = scrollLength >= totalSize * WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO;
1633
+ if (rendersAlmostEverything && viewportMatchesContent) {
1634
+ warnDevOnce(
1635
+ "webUnboundedOuterSize",
1636
+ "LegendList appears to have an unbounded outer height on web, so virtualization is effectively disabled. Set a bounded height or flex: 1 on the list container, or use useWindowScroll."
1637
+ );
1638
+ }
1639
+ };
1640
+ warnIfUnboundedOuterSize();
1641
+ const unsubscribe = [
1642
+ listen$(ctx, "numContainers", warnIfUnboundedOuterSize),
1643
+ listen$(ctx, "readyToRender", warnIfUnboundedOuterSize),
1644
+ listen$(ctx, "totalSize", warnIfUnboundedOuterSize)
1645
+ ];
1646
+ return () => {
1647
+ for (const unsub of unsubscribe) {
1648
+ unsub();
1649
+ }
1650
+ };
1651
+ }, [ctx]);
1548
1652
  }
1549
-
1550
- // src/utils/getId.ts
1551
- function getId(state, index) {
1552
- const { data, keyExtractor } = state.props;
1553
- if (!data) {
1554
- return "";
1555
- }
1556
- const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
1557
- const id = ret;
1558
- state.idCache[index] = id;
1559
- return id;
1653
+ function useDevChecksNoop(_props) {
1560
1654
  }
1655
+ var useDevChecks = IS_DEV ? useDevChecksImpl : useDevChecksNoop;
1561
1656
 
1562
- // src/core/addTotalSize.ts
1563
- function addTotalSize(ctx, key, add) {
1564
- const state = ctx.state;
1565
- const prevTotalSize = state.totalSize;
1566
- let totalSize = state.totalSize;
1567
- if (key === null) {
1568
- totalSize = add;
1569
- if (state.timeoutSetPaddingTop) {
1570
- clearTimeout(state.timeoutSetPaddingTop);
1571
- state.timeoutSetPaddingTop = void 0;
1572
- }
1573
- } else {
1574
- totalSize += add;
1575
- }
1576
- if (prevTotalSize !== totalSize) {
1577
- {
1578
- state.pendingTotalSize = void 0;
1579
- state.totalSize = totalSize;
1580
- set$(ctx, "totalSize", totalSize);
1657
+ // src/core/deferredPublicOnScroll.ts
1658
+ function withResolvedContentOffset(state, event, resolvedOffset) {
1659
+ return {
1660
+ ...event,
1661
+ nativeEvent: {
1662
+ ...event.nativeEvent,
1663
+ contentOffset: state.props.horizontal ? { x: resolvedOffset, y: 0 } : { x: 0, y: resolvedOffset }
1581
1664
  }
1582
- }
1665
+ };
1583
1666
  }
1584
-
1585
- // src/core/setSize.ts
1586
- function setSize(ctx, itemKey, size) {
1667
+ function releaseDeferredPublicOnScroll(ctx, resolvedOffset) {
1668
+ var _a3, _b, _c, _d;
1587
1669
  const state = ctx.state;
1588
- const { sizes } = state;
1589
- const previousSize = sizes.get(itemKey);
1590
- const diff = previousSize !== void 0 ? size - previousSize : size;
1591
- if (diff !== 0) {
1592
- addTotalSize(ctx, itemKey, diff);
1670
+ const deferredEvent = state.deferredPublicOnScrollEvent;
1671
+ state.deferredPublicOnScrollEvent = void 0;
1672
+ if (deferredEvent) {
1673
+ (_d = (_c = state.props).onScroll) == null ? void 0 : _d.call(
1674
+ _c,
1675
+ withResolvedContentOffset(
1676
+ state,
1677
+ deferredEvent,
1678
+ (_b = (_a3 = resolvedOffset != null ? resolvedOffset : state.scrollPending) != null ? _a3 : state.scroll) != null ? _b : 0
1679
+ )
1680
+ );
1593
1681
  }
1594
- sizes.set(itemKey, size);
1595
1682
  }
1596
1683
 
1597
- // src/utils/getItemSize.ts
1598
- function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
1599
- var _a3, _b;
1600
- const state = ctx.state;
1601
- const {
1602
- sizesKnown,
1603
- sizes,
1604
- averageSizes,
1605
- props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType },
1606
- scrollingTo
1607
- } = state;
1608
- const sizeKnown = sizesKnown.get(key);
1609
- if (sizeKnown !== void 0) {
1610
- return sizeKnown;
1611
- }
1612
- let size;
1613
- if (preferCachedSize) {
1614
- const cachedSize = sizes.get(key);
1615
- if (cachedSize !== void 0) {
1616
- return cachedSize;
1617
- }
1618
- }
1619
- const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1620
- if (getFixedItemSize) {
1621
- size = getFixedItemSize(data, index, itemType);
1622
- if (size !== void 0) {
1623
- sizesKnown.set(key, size);
1624
- }
1625
- }
1626
- if (size === void 0 && useAverageSize && sizeKnown === void 0 && !scrollingTo) {
1627
- const averageSizeForType = (_b = averageSizes[itemType]) == null ? void 0 : _b.avg;
1628
- if (averageSizeForType !== void 0) {
1629
- size = roundSize(averageSizeForType);
1630
- }
1631
- }
1632
- if (size === void 0) {
1633
- size = sizes.get(key);
1634
- if (size !== void 0) {
1635
- return size;
1636
- }
1637
- }
1638
- if (size === void 0) {
1639
- size = getEstimatedItemSize ? getEstimatedItemSize(data, index, itemType) : estimatedItemSize;
1640
- }
1641
- setSize(ctx, key, size);
1642
- return size;
1684
+ // src/core/initialScrollSession.ts
1685
+ var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
1686
+ function hasInitialScrollSessionCompletion(completion) {
1687
+ return !!((completion == null ? void 0 : completion.didDispatchNativeScroll) || (completion == null ? void 0 : completion.didRetrySilentInitialScroll) || (completion == null ? void 0 : completion.watchdog));
1688
+ }
1689
+ function clearInitialScrollSession(state) {
1690
+ state.initialScrollSession = void 0;
1691
+ return void 0;
1692
+ }
1693
+ function createInitialScrollSession(options) {
1694
+ const { bootstrap, completion, kind, previousDataLength } = options;
1695
+ return kind === "offset" ? {
1696
+ completion,
1697
+ kind,
1698
+ previousDataLength
1699
+ } : {
1700
+ bootstrap,
1701
+ completion,
1702
+ kind,
1703
+ previousDataLength
1704
+ };
1643
1705
  }
1644
-
1645
- // src/core/calculateOffsetWithOffsetPosition.ts
1646
- function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1647
- var _a3;
1648
- const state = ctx.state;
1649
- const { index, viewOffset, viewPosition } = params;
1650
- let offset = offsetParam;
1651
- if (viewOffset) {
1652
- offset -= viewOffset;
1706
+ function ensureInitialScrollSessionCompletion(state, kind = ((_b) => (_b = ((_a3) => (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind)()) != null ? _b : "bootstrap")()) {
1707
+ var _a4, _b2;
1708
+ if (!state.initialScrollSession) {
1709
+ state.initialScrollSession = createInitialScrollSession({
1710
+ completion: {},
1711
+ kind,
1712
+ previousDataLength: 0
1713
+ });
1714
+ } else if (state.initialScrollSession.kind !== kind) {
1715
+ state.initialScrollSession = createInitialScrollSession({
1716
+ bootstrap: state.initialScrollSession.kind === "bootstrap" ? state.initialScrollSession.bootstrap : void 0,
1717
+ completion: state.initialScrollSession.completion,
1718
+ kind,
1719
+ previousDataLength: state.initialScrollSession.previousDataLength
1720
+ });
1653
1721
  }
1654
- if (index !== void 0) {
1655
- const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
1656
- if (topOffsetAdjustment) {
1657
- offset += topOffsetAdjustment;
1722
+ (_b2 = (_a4 = state.initialScrollSession).completion) != null ? _b2 : _a4.completion = {};
1723
+ return state.initialScrollSession.completion;
1724
+ }
1725
+ var initialScrollCompletion = {
1726
+ didDispatchNativeScroll(state) {
1727
+ var _a3, _b;
1728
+ return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didDispatchNativeScroll);
1729
+ },
1730
+ didRetrySilentInitialScroll(state) {
1731
+ var _a3, _b;
1732
+ return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didRetrySilentInitialScroll);
1733
+ },
1734
+ markInitialScrollNativeDispatch(state) {
1735
+ ensureInitialScrollSessionCompletion(state).didDispatchNativeScroll = true;
1736
+ },
1737
+ markSilentInitialScrollRetry(state) {
1738
+ ensureInitialScrollSessionCompletion(state).didRetrySilentInitialScroll = true;
1739
+ },
1740
+ resetFlags(state) {
1741
+ if (!state.initialScrollSession) {
1742
+ return;
1658
1743
  }
1744
+ const completion = ensureInitialScrollSessionCompletion(state, state.initialScrollSession.kind);
1745
+ completion.didDispatchNativeScroll = void 0;
1746
+ completion.didRetrySilentInitialScroll = void 0;
1659
1747
  }
1660
- if (viewPosition !== void 0 && index !== void 0) {
1661
- const dataLength = state.props.data.length;
1662
- if (dataLength === 0) {
1663
- return offset;
1664
- }
1665
- const isOutOfBounds = index < 0 || index >= dataLength;
1666
- const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
1667
- const itemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1668
- const trailingInset = getContentInsetEnd(state);
1669
- offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1670
- if (!isOutOfBounds && index === state.props.data.length - 1) {
1671
- const footerSize = peek$(ctx, "footerSize") || 0;
1672
- offset += footerSize;
1748
+ };
1749
+ var initialScrollWatchdog = {
1750
+ clear(state) {
1751
+ initialScrollWatchdog.set(state, void 0);
1752
+ },
1753
+ didObserveProgress(newScroll, watchdog) {
1754
+ const previousDistance = Math.abs(watchdog.startScroll - watchdog.targetOffset);
1755
+ const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
1756
+ return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET || nextDistance + INITIAL_SCROLL_MIN_TARGET_OFFSET < previousDistance;
1757
+ },
1758
+ get(state) {
1759
+ var _a3, _b;
1760
+ return (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog;
1761
+ },
1762
+ hasNonZeroTargetOffset(targetOffset) {
1763
+ return targetOffset !== void 0 && targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
1764
+ },
1765
+ isAtZeroTargetOffset(targetOffset) {
1766
+ return targetOffset <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
1767
+ },
1768
+ set(state, watchdog) {
1769
+ var _a3, _b;
1770
+ if (!watchdog && !((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog)) {
1771
+ return;
1673
1772
  }
1773
+ const completion = ensureInitialScrollSessionCompletion(state);
1774
+ completion.watchdog = watchdog ? {
1775
+ startScroll: watchdog.startScroll,
1776
+ targetOffset: watchdog.targetOffset
1777
+ } : void 0;
1674
1778
  }
1675
- return offset;
1676
- }
1677
-
1678
- // src/core/clampScrollOffset.ts
1679
- function clampScrollOffset(ctx, offset, scrollTarget) {
1680
- const state = ctx.state;
1681
- const contentSize = getContentSize(ctx);
1682
- let clampedOffset = offset;
1683
- if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform.OS !== "android")) {
1684
- const baseMaxOffset = Math.max(0, contentSize - state.scrollLength);
1685
- const viewOffset = scrollTarget == null ? void 0 : scrollTarget.viewOffset;
1686
- const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
1687
- const maxOffset = baseMaxOffset + extraEndOffset;
1688
- clampedOffset = Math.min(offset, maxOffset);
1689
- }
1690
- clampedOffset = Math.max(0, clampedOffset);
1691
- return clampedOffset;
1779
+ };
1780
+ function setInitialScrollSession(state, options = {}) {
1781
+ var _a3, _b, _c;
1782
+ const existingSession = state.initialScrollSession;
1783
+ const kind = (_a3 = options.kind) != null ? _a3 : existingSession == null ? void 0 : existingSession.kind;
1784
+ const completion = existingSession == null ? void 0 : existingSession.completion;
1785
+ const hasBootstrapOverride = Object.hasOwn(options, "bootstrap");
1786
+ const bootstrap = kind === "bootstrap" ? hasBootstrapOverride ? options.bootstrap : (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0 : void 0;
1787
+ if (!kind) {
1788
+ return clearInitialScrollSession(state);
1789
+ }
1790
+ if (!state.initialScroll && !bootstrap && !hasInitialScrollSessionCompletion(completion)) {
1791
+ return clearInitialScrollSession(state);
1792
+ }
1793
+ const previousDataLength = (_c = (_b = options.previousDataLength) != null ? _b : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _c : 0;
1794
+ state.initialScrollSession = createInitialScrollSession({
1795
+ bootstrap,
1796
+ completion,
1797
+ kind,
1798
+ previousDataLength
1799
+ });
1800
+ return state.initialScrollSession;
1692
1801
  }
1693
1802
 
1694
1803
  // src/utils/checkThreshold.ts
@@ -1729,11 +1838,16 @@ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, co
1729
1838
  return true;
1730
1839
  };
1731
1840
 
1841
+ // src/utils/hasActiveInitialScroll.ts
1842
+ function hasActiveInitialScroll(state) {
1843
+ return !!(state == null ? void 0 : state.initialScroll) && !state.didFinishInitialScroll;
1844
+ }
1845
+
1732
1846
  // src/utils/checkAtBottom.ts
1733
1847
  function checkAtBottom(ctx) {
1734
1848
  var _a3;
1735
1849
  const state = ctx.state;
1736
- if (!state || state.initialScroll) {
1850
+ if (!state) {
1737
1851
  return;
1738
1852
  }
1739
1853
  const {
@@ -1743,42 +1857,48 @@ function checkAtBottom(ctx) {
1743
1857
  maintainingScrollAtEnd,
1744
1858
  props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
1745
1859
  } = state;
1746
- if (state.initialScroll) {
1747
- return;
1748
- }
1749
1860
  const contentSize = getContentSize(ctx);
1750
- if (contentSize > 0 && queuedInitialLayout && !maintainingScrollAtEnd) {
1751
- const insetEnd = getContentInsetEnd(state);
1861
+ if (contentSize > 0 && queuedInitialLayout) {
1862
+ const insetEnd = getContentInsetEnd(ctx);
1752
1863
  const distanceFromEnd = contentSize - scroll - scrollLength - insetEnd;
1753
1864
  const isContentLess = contentSize < scrollLength;
1754
- state.isAtEnd = isContentLess || distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
1755
- state.isEndReached = checkThreshold(
1756
- distanceFromEnd,
1757
- isContentLess,
1758
- onEndReachedThreshold * scrollLength,
1759
- state.isEndReached,
1760
- state.endReachedSnapshot,
1761
- {
1762
- contentSize,
1763
- dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1764
- scrollPosition: scroll
1765
- },
1766
- (distance) => {
1767
- var _a4, _b;
1768
- return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
1769
- },
1770
- (snapshot) => {
1771
- state.endReachedSnapshot = snapshot;
1772
- },
1773
- true
1865
+ set$(ctx, "isAtEnd", isContentLess || distanceFromEnd <= EDGE_POSITION_EPSILON);
1866
+ set$(ctx, "isNearEnd", isContentLess || distanceFromEnd <= onEndReachedThreshold * scrollLength);
1867
+ set$(
1868
+ ctx,
1869
+ "isWithinMaintainScrollAtEndThreshold",
1870
+ isContentLess || distanceFromEnd <= maintainScrollAtEndThreshold * scrollLength
1774
1871
  );
1872
+ const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || maintainingScrollAtEnd;
1873
+ if (!shouldSkipThresholdChecks) {
1874
+ state.isEndReached = checkThreshold(
1875
+ distanceFromEnd,
1876
+ isContentLess,
1877
+ onEndReachedThreshold * scrollLength,
1878
+ state.isEndReached,
1879
+ state.endReachedSnapshot,
1880
+ {
1881
+ contentSize,
1882
+ dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1883
+ scrollPosition: scroll
1884
+ },
1885
+ (distance) => {
1886
+ var _a4, _b;
1887
+ return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
1888
+ },
1889
+ (snapshot) => {
1890
+ state.endReachedSnapshot = snapshot;
1891
+ },
1892
+ true
1893
+ );
1894
+ }
1775
1895
  }
1776
1896
  }
1777
1897
 
1778
1898
  // src/utils/checkAtTop.ts
1779
1899
  function checkAtTop(ctx) {
1780
1900
  const state = ctx == null ? void 0 : ctx.state;
1781
- if (!state || state.initialScroll || state.scrollingTo) {
1901
+ if (!state) {
1782
1902
  return;
1783
1903
  }
1784
1904
  const {
@@ -1801,31 +1921,33 @@ function checkAtTop(ctx) {
1801
1921
  state.startReachedSnapshot = void 0;
1802
1922
  state.startReachedSnapshotDataChangeEpoch = void 0;
1803
1923
  }
1804
- state.isAtStart = scroll <= 0;
1805
- if (isStartReached && withinThreshold && dataChanged && !allowReentryOnDataChange) {
1806
- return;
1924
+ set$(ctx, "isAtStart", scroll <= EDGE_POSITION_EPSILON);
1925
+ set$(ctx, "isNearStart", scroll <= threshold);
1926
+ const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || !!state.scrollingTo;
1927
+ const shouldDeferDataChangeRefire = isStartReached && withinThreshold && dataChanged && !allowReentryOnDataChange;
1928
+ if (!shouldSkipThresholdChecks && !shouldDeferDataChangeRefire) {
1929
+ state.isStartReached = checkThreshold(
1930
+ scroll,
1931
+ false,
1932
+ threshold,
1933
+ state.isStartReached,
1934
+ allowReentryOnDataChange ? void 0 : startReachedSnapshot,
1935
+ {
1936
+ contentSize: totalSize,
1937
+ dataLength,
1938
+ scrollPosition: scroll
1939
+ },
1940
+ (distance) => {
1941
+ var _a3, _b;
1942
+ return (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
1943
+ },
1944
+ (snapshot) => {
1945
+ state.startReachedSnapshot = snapshot;
1946
+ state.startReachedSnapshotDataChangeEpoch = snapshot ? dataChangeEpoch : void 0;
1947
+ },
1948
+ allowReentryOnDataChange
1949
+ );
1807
1950
  }
1808
- state.isStartReached = checkThreshold(
1809
- scroll,
1810
- false,
1811
- threshold,
1812
- state.isStartReached,
1813
- allowReentryOnDataChange ? void 0 : startReachedSnapshot,
1814
- {
1815
- contentSize: totalSize,
1816
- dataLength,
1817
- scrollPosition: scroll
1818
- },
1819
- (distance) => {
1820
- var _a3, _b;
1821
- return (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
1822
- },
1823
- (snapshot) => {
1824
- state.startReachedSnapshot = snapshot;
1825
- state.startReachedSnapshotDataChangeEpoch = snapshot ? dataChangeEpoch : void 0;
1826
- },
1827
- allowReentryOnDataChange
1828
- );
1829
1951
  }
1830
1952
 
1831
1953
  // src/utils/checkThresholds.ts
@@ -1834,6 +1956,16 @@ function checkThresholds(ctx) {
1834
1956
  checkAtTop(ctx);
1835
1957
  }
1836
1958
 
1959
+ // src/core/recalculateSettledScroll.ts
1960
+ function recalculateSettledScroll(ctx) {
1961
+ var _a3, _b;
1962
+ const state = ctx.state;
1963
+ if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1964
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1965
+ }
1966
+ checkThresholds(ctx);
1967
+ }
1968
+
1837
1969
  // src/utils/setInitialRenderState.ts
1838
1970
  function setInitialRenderState(ctx, {
1839
1971
  didLayout,
@@ -1859,6 +1991,239 @@ function setInitialRenderState(ctx, {
1859
1991
  }
1860
1992
  }
1861
1993
 
1994
+ // src/core/finishInitialScroll.ts
1995
+ var PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS = 2e3;
1996
+ function syncInitialScrollOffset(state, offset) {
1997
+ state.scroll = offset;
1998
+ state.scrollPending = offset;
1999
+ state.scrollPrev = offset;
2000
+ }
2001
+ function clearPreservedInitialScrollTargetTimeout(state) {
2002
+ if (state.timeoutPreservedInitialScrollClear !== void 0) {
2003
+ clearTimeout(state.timeoutPreservedInitialScrollClear);
2004
+ state.timeoutPreservedInitialScrollClear = void 0;
2005
+ }
2006
+ }
2007
+ function clearPreservedInitialScrollTarget(state) {
2008
+ clearPreservedInitialScrollTargetTimeout(state);
2009
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
2010
+ state.initialScroll = void 0;
2011
+ setInitialScrollSession(state);
2012
+ }
2013
+ function finishInitialScroll(ctx, options) {
2014
+ var _a3, _b, _c;
2015
+ const state = ctx.state;
2016
+ if ((options == null ? void 0 : options.resolvedOffset) !== void 0) {
2017
+ syncInitialScrollOffset(state, options.resolvedOffset);
2018
+ } else if ((options == null ? void 0 : options.syncObservedOffset) && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
2019
+ const observedOffset = (_c = (_b = state.refScroller.current) == null ? void 0 : _b.getCurrentScrollOffset) == null ? void 0 : _c.call(_b);
2020
+ if (typeof observedOffset === "number" && Number.isFinite(observedOffset)) {
2021
+ syncInitialScrollOffset(state, observedOffset);
2022
+ }
2023
+ }
2024
+ const complete = () => {
2025
+ var _a4, _b2, _c2, _d, _e;
2026
+ const shouldReleaseDeferredPublicOnScroll = ((_a4 = state.initialScrollSession) == null ? void 0 : _a4.kind) === "bootstrap";
2027
+ const finalScrollOffset = (_d = (_c2 = (_b2 = options == null ? void 0 : options.resolvedOffset) != null ? _b2 : state.scrollPending) != null ? _c2 : state.scroll) != null ? _d : 0;
2028
+ initialScrollWatchdog.clear(state);
2029
+ if ((options == null ? void 0 : options.preserveTarget) && state.initialScroll) {
2030
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
2031
+ setInitialScrollSession(state);
2032
+ clearPreservedInitialScrollTargetTimeout(state);
2033
+ if (options == null ? void 0 : options.schedulePreservedTargetClear) {
2034
+ state.timeoutPreservedInitialScrollClear = setTimeout(() => {
2035
+ var _a5;
2036
+ state.timeoutPreservedInitialScrollClear = void 0;
2037
+ if (!state.didFinishInitialScroll || ((_a5 = state.scrollingTo) == null ? void 0 : _a5.isInitialScroll) || !state.initialScroll) {
2038
+ return;
2039
+ }
2040
+ clearPreservedInitialScrollTarget(state);
2041
+ }, PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS);
2042
+ }
2043
+ } else {
2044
+ clearPreservedInitialScrollTarget(state);
2045
+ }
2046
+ if (options == null ? void 0 : options.recalculateItems) {
2047
+ recalculateSettledScroll(ctx);
2048
+ }
2049
+ setInitialRenderState(ctx, { didInitialScroll: true });
2050
+ if (shouldReleaseDeferredPublicOnScroll) {
2051
+ releaseDeferredPublicOnScroll(ctx, finalScrollOffset);
2052
+ }
2053
+ (_e = options == null ? void 0 : options.onFinished) == null ? void 0 : _e.call(options);
2054
+ };
2055
+ if (options == null ? void 0 : options.waitForCompletionFrame) {
2056
+ requestAnimationFrame(complete);
2057
+ return;
2058
+ }
2059
+ complete();
2060
+ }
2061
+
2062
+ // src/core/calculateOffsetForIndex.ts
2063
+ function calculateOffsetForIndex(ctx, index) {
2064
+ const state = ctx.state;
2065
+ return index !== void 0 ? state.positions[index] || 0 : 0;
2066
+ }
2067
+
2068
+ // src/core/getTopOffsetAdjustment.ts
2069
+ function getTopOffsetAdjustment(ctx) {
2070
+ return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
2071
+ }
2072
+
2073
+ // src/utils/getId.ts
2074
+ function getId(state, index) {
2075
+ const { data, keyExtractor } = state.props;
2076
+ if (!data) {
2077
+ return "";
2078
+ }
2079
+ const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
2080
+ const id = ret;
2081
+ state.idCache[index] = id;
2082
+ return id;
2083
+ }
2084
+
2085
+ // src/core/addTotalSize.ts
2086
+ function addTotalSize(ctx, key, add) {
2087
+ const state = ctx.state;
2088
+ const prevTotalSize = state.totalSize;
2089
+ let totalSize = state.totalSize;
2090
+ if (key === null) {
2091
+ totalSize = add;
2092
+ if (state.timeoutSetPaddingTop) {
2093
+ clearTimeout(state.timeoutSetPaddingTop);
2094
+ state.timeoutSetPaddingTop = void 0;
2095
+ }
2096
+ } else {
2097
+ totalSize += add;
2098
+ }
2099
+ if (prevTotalSize !== totalSize) {
2100
+ {
2101
+ state.pendingTotalSize = void 0;
2102
+ state.totalSize = totalSize;
2103
+ set$(ctx, "totalSize", totalSize);
2104
+ }
2105
+ }
2106
+ }
2107
+
2108
+ // src/core/setSize.ts
2109
+ function setSize(ctx, itemKey, size) {
2110
+ const state = ctx.state;
2111
+ const { sizes } = state;
2112
+ const previousSize = sizes.get(itemKey);
2113
+ const diff = previousSize !== void 0 ? size - previousSize : size;
2114
+ if (diff !== 0) {
2115
+ addTotalSize(ctx, itemKey, diff);
2116
+ }
2117
+ sizes.set(itemKey, size);
2118
+ }
2119
+
2120
+ // src/utils/getItemSize.ts
2121
+ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
2122
+ var _a3, _b, _c;
2123
+ const state = ctx.state;
2124
+ const {
2125
+ sizesKnown,
2126
+ sizes,
2127
+ averageSizes,
2128
+ props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType },
2129
+ scrollingTo
2130
+ } = state;
2131
+ const sizeKnown = sizesKnown.get(key);
2132
+ if (sizeKnown !== void 0) {
2133
+ return sizeKnown;
2134
+ }
2135
+ let size;
2136
+ const renderedSize = sizes.get(key);
2137
+ if (preferCachedSize) {
2138
+ if (renderedSize !== void 0) {
2139
+ return renderedSize;
2140
+ }
2141
+ }
2142
+ const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
2143
+ if (getFixedItemSize) {
2144
+ size = getFixedItemSize(data, index, itemType);
2145
+ if (size !== void 0) {
2146
+ sizesKnown.set(key, size);
2147
+ }
2148
+ }
2149
+ if (size === void 0 && useAverageSize && sizeKnown === void 0 && !scrollingTo) {
2150
+ const averageSizeForType = (_b = averageSizes[itemType]) == null ? void 0 : _b.avg;
2151
+ if (averageSizeForType !== void 0) {
2152
+ size = roundSize(averageSizeForType);
2153
+ }
2154
+ }
2155
+ if (size === void 0 && renderedSize !== void 0) {
2156
+ return renderedSize;
2157
+ }
2158
+ if (size === void 0 && useAverageSize && sizeKnown === void 0 && scrollingTo) {
2159
+ const averageSizeForType = (_c = scrollingTo.averageSizeSnapshot) == null ? void 0 : _c[itemType];
2160
+ if (averageSizeForType !== void 0) {
2161
+ size = roundSize(averageSizeForType);
2162
+ }
2163
+ }
2164
+ if (size === void 0) {
2165
+ size = getEstimatedItemSize ? getEstimatedItemSize(data, index, itemType) : estimatedItemSize;
2166
+ }
2167
+ setSize(ctx, key, size);
2168
+ return size;
2169
+ }
2170
+ function getItemSizeAtIndex(ctx, index) {
2171
+ if (index === void 0 || index < 0) {
2172
+ return void 0;
2173
+ }
2174
+ const targetId = getId(ctx.state, index);
2175
+ return getItemSize(ctx, targetId, index, ctx.state.props.data[index]);
2176
+ }
2177
+
2178
+ // src/core/calculateOffsetWithOffsetPosition.ts
2179
+ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
2180
+ var _a3;
2181
+ const state = ctx.state;
2182
+ const { index, viewOffset, viewPosition } = params;
2183
+ let offset = offsetParam;
2184
+ if (viewOffset) {
2185
+ offset -= viewOffset;
2186
+ }
2187
+ if (index !== void 0) {
2188
+ const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
2189
+ if (topOffsetAdjustment) {
2190
+ offset += topOffsetAdjustment;
2191
+ }
2192
+ }
2193
+ if (viewPosition !== void 0 && index !== void 0) {
2194
+ const dataLength = state.props.data.length;
2195
+ if (dataLength === 0) {
2196
+ return offset;
2197
+ }
2198
+ const isOutOfBounds = index < 0 || index >= dataLength;
2199
+ const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
2200
+ const itemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
2201
+ const trailingInset = getContentInsetEnd(ctx);
2202
+ offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
2203
+ if (!isOutOfBounds && index === state.props.data.length - 1) {
2204
+ const footerSize = peek$(ctx, "footerSize") || 0;
2205
+ offset += footerSize;
2206
+ }
2207
+ }
2208
+ return offset;
2209
+ }
2210
+
2211
+ // src/core/clampScrollOffset.ts
2212
+ function clampScrollOffset(ctx, offset, scrollTarget) {
2213
+ const state = ctx.state;
2214
+ const contentSize = getContentSize(ctx);
2215
+ let clampedOffset = offset;
2216
+ if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform.OS !== "android")) {
2217
+ const baseMaxOffset = Math.max(0, contentSize - state.scrollLength);
2218
+ const viewOffset = scrollTarget == null ? void 0 : scrollTarget.viewOffset;
2219
+ const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
2220
+ const maxOffset = baseMaxOffset + extraEndOffset;
2221
+ clampedOffset = Math.min(offset, maxOffset);
2222
+ }
2223
+ clampedOffset = Math.max(0, clampedOffset);
2224
+ return clampedOffset;
2225
+ }
2226
+
1862
2227
  // src/core/finishScrollTo.ts
1863
2228
  function finishScrollTo(ctx) {
1864
2229
  var _a3, _b;
@@ -1868,193 +2233,1134 @@ function finishScrollTo(ctx) {
1868
2233
  state.pendingScrollResolve = void 0;
1869
2234
  const scrollingTo = state.scrollingTo;
1870
2235
  state.scrollHistory.length = 0;
1871
- state.initialScroll = void 0;
1872
- state.initialScrollUsesOffset = false;
1873
- state.initialAnchor = void 0;
1874
- state.initialNativeScrollWatchdog = void 0;
1875
2236
  state.scrollingTo = void 0;
1876
2237
  if (state.pendingTotalSize !== void 0) {
1877
2238
  addTotalSize(ctx, null, state.pendingTotalSize);
1878
2239
  }
1879
- if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1880
- (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1881
- }
1882
2240
  {
1883
2241
  state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
1884
2242
  }
1885
- setInitialRenderState(ctx, { didInitialScroll: true });
1886
- checkThresholds(ctx);
2243
+ if (scrollingTo.isInitialScroll || state.initialScroll) {
2244
+ const isOffsetSession = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
2245
+ const shouldPreserveResizeTarget = !!scrollingTo.isInitialScroll && !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_b = state.initialScroll) == null ? void 0 : _b.viewPosition) === 1;
2246
+ finishInitialScroll(ctx, {
2247
+ onFinished: () => {
2248
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
2249
+ },
2250
+ preserveTarget: isOffsetSession && state.props.data.length === 0 || shouldPreserveResizeTarget,
2251
+ recalculateItems: true,
2252
+ schedulePreservedTargetClear: shouldPreserveResizeTarget,
2253
+ syncObservedOffset: isOffsetSession,
2254
+ waitForCompletionFrame: !!scrollingTo.waitForInitialScrollCompletionFrame
2255
+ });
2256
+ return;
2257
+ }
2258
+ recalculateSettledScroll(ctx);
1887
2259
  resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1888
2260
  }
1889
2261
  }
1890
-
1891
- // src/core/doScrollTo.ts
1892
- var SCROLL_END_IDLE_MS = 80;
1893
- var SCROLL_END_MAX_MS = 1500;
1894
- var SMOOTH_SCROLL_DURATION_MS = 320;
1895
- var SCROLL_END_TARGET_EPSILON = 1;
1896
- function doScrollTo(ctx, params) {
2262
+
2263
+ // src/core/doScrollTo.ts
2264
+ var SCROLL_END_IDLE_MS = 80;
2265
+ var SCROLL_END_MAX_MS = 1500;
2266
+ var SMOOTH_SCROLL_DURATION_MS = 320;
2267
+ var SCROLL_END_TARGET_EPSILON = 1;
2268
+ function doScrollTo(ctx, params) {
2269
+ var _a3, _b;
2270
+ const state = ctx.state;
2271
+ const { animated, horizontal, offset } = params;
2272
+ const scroller = state.refScroller.current;
2273
+ const node = scroller == null ? void 0 : scroller.getScrollableNode();
2274
+ if (!scroller || !node) {
2275
+ return;
2276
+ }
2277
+ const isAnimated = !!animated;
2278
+ const isHorizontal = !!horizontal;
2279
+ const left = isHorizontal ? offset : 0;
2280
+ const top = isHorizontal ? 0 : offset;
2281
+ scroller.scrollTo({ animated: isAnimated, x: left, y: top });
2282
+ if (isAnimated) {
2283
+ const target = (_b = (_a3 = scroller.getScrollEventTarget) == null ? void 0 : _a3.call(scroller)) != null ? _b : null;
2284
+ listenForScrollEnd(ctx, {
2285
+ readOffset: () => scroller.getCurrentScrollOffset(),
2286
+ target,
2287
+ targetOffset: offset
2288
+ });
2289
+ } else {
2290
+ state.scroll = offset;
2291
+ setTimeout(() => {
2292
+ finishScrollTo(ctx);
2293
+ }, 100);
2294
+ }
2295
+ }
2296
+ function listenForScrollEnd(ctx, params) {
2297
+ const { readOffset, target, targetOffset } = params;
2298
+ if (!target) {
2299
+ finishScrollTo(ctx);
2300
+ return;
2301
+ }
2302
+ const supportsScrollEnd = "onscrollend" in target;
2303
+ let idleTimeout;
2304
+ let settled = false;
2305
+ const targetToken = ctx.state.scrollingTo;
2306
+ const maxTimeout = setTimeout(() => finish("max"), SCROLL_END_MAX_MS);
2307
+ const cleanup = () => {
2308
+ target.removeEventListener("scroll", onScroll2);
2309
+ if (supportsScrollEnd) {
2310
+ target.removeEventListener("scrollend", onScrollEnd);
2311
+ }
2312
+ if (idleTimeout) {
2313
+ clearTimeout(idleTimeout);
2314
+ }
2315
+ clearTimeout(maxTimeout);
2316
+ };
2317
+ const finish = (reason) => {
2318
+ if (settled) return;
2319
+ if (targetToken !== ctx.state.scrollingTo) {
2320
+ settled = true;
2321
+ cleanup();
2322
+ return;
2323
+ }
2324
+ const currentOffset = readOffset();
2325
+ const isNearTarget = Math.abs(currentOffset - targetOffset) <= SCROLL_END_TARGET_EPSILON;
2326
+ if (reason === "scrollend" && !isNearTarget) {
2327
+ return;
2328
+ }
2329
+ settled = true;
2330
+ cleanup();
2331
+ finishScrollTo(ctx);
2332
+ };
2333
+ const onScroll2 = () => {
2334
+ if (idleTimeout) {
2335
+ clearTimeout(idleTimeout);
2336
+ }
2337
+ idleTimeout = setTimeout(() => finish("idle"), SCROLL_END_IDLE_MS);
2338
+ };
2339
+ const onScrollEnd = () => finish("scrollend");
2340
+ target.addEventListener("scroll", onScroll2);
2341
+ if (supportsScrollEnd) {
2342
+ target.addEventListener("scrollend", onScrollEnd);
2343
+ } else {
2344
+ idleTimeout = setTimeout(() => finish("idle"), SMOOTH_SCROLL_DURATION_MS);
2345
+ }
2346
+ }
2347
+
2348
+ // src/core/scrollTo.ts
2349
+ function getAverageSizeSnapshot(state) {
2350
+ if (Object.keys(state.averageSizes).length === 0) {
2351
+ return void 0;
2352
+ }
2353
+ const snapshot = {};
2354
+ for (const itemType in state.averageSizes) {
2355
+ const averages = state.averageSizes[itemType];
2356
+ snapshot[itemType] = averages.avg;
2357
+ }
2358
+ return snapshot;
2359
+ }
2360
+ function syncInitialScrollNativeWatchdog(state, options) {
2361
+ var _a3;
2362
+ const { isInitialScroll, requestedOffset, targetOffset } = options;
2363
+ const existingWatchdog = initialScrollWatchdog.get(state);
2364
+ const shouldWatchInitialNativeScroll = !state.didFinishInitialScroll && (isInitialScroll || !!existingWatchdog) && initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset);
2365
+ const shouldClearInitialNativeScrollWatchdog = !state.didFinishInitialScroll && !!existingWatchdog && initialScrollWatchdog.isAtZeroTargetOffset(requestedOffset);
2366
+ if (shouldWatchInitialNativeScroll) {
2367
+ state.hasScrolled = false;
2368
+ initialScrollWatchdog.set(state, {
2369
+ startScroll: (_a3 = existingWatchdog == null ? void 0 : existingWatchdog.startScroll) != null ? _a3 : state.scroll,
2370
+ targetOffset
2371
+ });
2372
+ return;
2373
+ }
2374
+ if (shouldClearInitialNativeScrollWatchdog) {
2375
+ initialScrollWatchdog.clear(state);
2376
+ }
2377
+ }
2378
+ function scrollTo(ctx, params) {
2379
+ var _a3;
2380
+ const state = ctx.state;
2381
+ const { noScrollingTo, forceScroll, ...scrollTarget } = params;
2382
+ const {
2383
+ animated,
2384
+ isInitialScroll,
2385
+ offset: scrollTargetOffset,
2386
+ precomputedWithViewOffset,
2387
+ waitForInitialScrollCompletionFrame
2388
+ } = scrollTarget;
2389
+ const {
2390
+ props: { horizontal }
2391
+ } = state;
2392
+ if (state.animFrameCheckFinishedScroll) {
2393
+ cancelAnimationFrame(ctx.state.animFrameCheckFinishedScroll);
2394
+ }
2395
+ if (state.timeoutCheckFinishedScrollFallback) {
2396
+ clearTimeout(ctx.state.timeoutCheckFinishedScrollFallback);
2397
+ }
2398
+ const requestedOffset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, scrollTargetOffset, scrollTarget);
2399
+ const shouldPreserveRawInitialOffsetRequest = !!isInitialScroll && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
2400
+ const targetOffset = clampScrollOffset(ctx, requestedOffset, scrollTarget);
2401
+ const offset = shouldPreserveRawInitialOffsetRequest ? requestedOffset : targetOffset;
2402
+ state.scrollHistory.length = 0;
2403
+ if (!noScrollingTo) {
2404
+ if (isInitialScroll) {
2405
+ initialScrollCompletion.resetFlags(state);
2406
+ }
2407
+ const averageSizeSnapshot = getAverageSizeSnapshot(state);
2408
+ state.scrollingTo = {
2409
+ ...scrollTarget,
2410
+ ...averageSizeSnapshot ? { averageSizeSnapshot } : {},
2411
+ targetOffset,
2412
+ waitForInitialScrollCompletionFrame
2413
+ };
2414
+ }
2415
+ state.scrollPending = targetOffset;
2416
+ syncInitialScrollNativeWatchdog(state, { isInitialScroll, requestedOffset: offset, targetOffset });
2417
+ if (forceScroll || !isInitialScroll || Platform.OS === "android") {
2418
+ doScrollTo(ctx, { animated, horizontal, offset });
2419
+ } else {
2420
+ state.scroll = offset;
2421
+ }
2422
+ }
2423
+
2424
+ // src/core/scrollToIndex.ts
2425
+ function clampScrollIndex(index, dataLength) {
2426
+ if (dataLength <= 0) {
2427
+ return -1;
2428
+ }
2429
+ if (index >= dataLength) {
2430
+ return dataLength - 1;
2431
+ }
2432
+ if (index < 0) {
2433
+ return 0;
2434
+ }
2435
+ return index;
2436
+ }
2437
+ function scrollToIndex(ctx, {
2438
+ index,
2439
+ viewOffset = 0,
2440
+ animated = true,
2441
+ forceScroll,
2442
+ isInitialScroll,
2443
+ viewPosition
2444
+ }) {
2445
+ const state = ctx.state;
2446
+ const { data } = state.props;
2447
+ index = clampScrollIndex(index, data.length);
2448
+ const itemSize = getItemSizeAtIndex(ctx, index);
2449
+ const firstIndexOffset = calculateOffsetForIndex(ctx, index);
2450
+ const isLast = index === data.length - 1;
2451
+ if (isLast && viewPosition === void 0) {
2452
+ viewPosition = 1;
2453
+ }
2454
+ state.scrollForNextCalculateItemsInView = void 0;
2455
+ scrollTo(ctx, {
2456
+ animated,
2457
+ forceScroll,
2458
+ index,
2459
+ isInitialScroll,
2460
+ itemSize,
2461
+ offset: firstIndexOffset,
2462
+ viewOffset,
2463
+ viewPosition: viewPosition != null ? viewPosition : 0
2464
+ });
2465
+ }
2466
+
2467
+ // src/core/initialScroll.ts
2468
+ function dispatchInitialScroll(ctx, params) {
2469
+ const { forceScroll, resolvedOffset, target, waitForCompletionFrame } = params;
2470
+ const requestedIndex = target.index;
2471
+ const index = requestedIndex !== void 0 ? clampScrollIndex(requestedIndex, ctx.state.props.data.length) : void 0;
2472
+ const itemSize = getItemSizeAtIndex(ctx, index);
2473
+ scrollTo(ctx, {
2474
+ animated: false,
2475
+ forceScroll,
2476
+ index: index !== void 0 && index >= 0 ? index : void 0,
2477
+ isInitialScroll: true,
2478
+ itemSize,
2479
+ offset: resolvedOffset,
2480
+ precomputedWithViewOffset: true,
2481
+ viewOffset: target.viewOffset,
2482
+ viewPosition: target.viewPosition,
2483
+ waitForInitialScrollCompletionFrame: waitForCompletionFrame
2484
+ });
2485
+ }
2486
+ function setInitialScrollTarget(state, target, options) {
2487
+ var _a3;
2488
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
2489
+ if (state.timeoutPreservedInitialScrollClear !== void 0) {
2490
+ clearTimeout(state.timeoutPreservedInitialScrollClear);
2491
+ state.timeoutPreservedInitialScrollClear = void 0;
2492
+ }
2493
+ state.initialScroll = target;
2494
+ if ((options == null ? void 0 : options.resetDidFinish) && state.didFinishInitialScroll) {
2495
+ state.didFinishInitialScroll = false;
2496
+ }
2497
+ setInitialScrollSession(state, {
2498
+ kind: ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" ? "offset" : "bootstrap"
2499
+ });
2500
+ }
2501
+ function resolveInitialScrollOffset(ctx, initialScroll) {
2502
+ var _a3, _b;
2503
+ const state = ctx.state;
2504
+ if (((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
2505
+ return (_b = initialScroll.contentOffset) != null ? _b : 0;
2506
+ }
2507
+ const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, initialScroll.index) : 0;
2508
+ const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, initialScroll);
2509
+ return clampScrollOffset(ctx, resolvedOffset, initialScroll);
2510
+ }
2511
+ function getAdvanceableInitialScrollState(state, options) {
2512
+ const { didFinishInitialScroll, queuedInitialLayout, scrollingTo } = state;
2513
+ const initialScroll = state.initialScroll;
2514
+ const isInitialScrollInProgress = !!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll);
2515
+ const shouldWaitForInitialLayout = !!(options == null ? void 0 : options.requiresMeasuredLayout) && !queuedInitialLayout && !isInitialScrollInProgress;
2516
+ if (!initialScroll || shouldWaitForInitialLayout || didFinishInitialScroll || scrollingTo && !isInitialScrollInProgress) {
2517
+ return void 0;
2518
+ }
2519
+ return {
2520
+ initialScroll,
2521
+ isInitialScrollInProgress,
2522
+ queuedInitialLayout,
2523
+ scrollingTo
2524
+ };
2525
+ }
2526
+ function advanceMeasuredInitialScroll(ctx, options) {
2527
+ var _a3, _b, _c;
2528
+ const state = ctx.state;
2529
+ const advanceableState = getAdvanceableInitialScrollState(state, {
2530
+ requiresMeasuredLayout: true
2531
+ });
2532
+ if (!advanceableState) {
2533
+ return false;
2534
+ }
2535
+ const { initialScroll, isInitialScrollInProgress, queuedInitialLayout } = advanceableState;
2536
+ const scrollingTo = isInitialScrollInProgress ? advanceableState.scrollingTo : void 0;
2537
+ const resolvedOffset = resolveInitialScrollOffset(ctx, initialScroll);
2538
+ const activeInitialTargetOffset = scrollingTo ? (_a3 = scrollingTo.targetOffset) != null ? _a3 : scrollingTo.offset : void 0;
2539
+ const didOffsetChange = initialScroll.contentOffset === void 0 || Math.abs(initialScroll.contentOffset - resolvedOffset) > 1;
2540
+ const didActiveInitialTargetChange = activeInitialTargetOffset !== void 0 && Math.abs(activeInitialTargetOffset - resolvedOffset) > 1;
2541
+ const isAlreadyAtDesiredInitialTarget = activeInitialTargetOffset !== void 0 && Math.abs(state.scroll - activeInitialTargetOffset) <= 1 && Math.abs(state.scrollPending - activeInitialTargetOffset) <= 1;
2542
+ if (!(options == null ? void 0 : options.forceScroll) && !didOffsetChange && isInitialScrollInProgress && !didActiveInitialTargetChange) {
2543
+ return false;
2544
+ }
2545
+ if ((options == null ? void 0 : options.forceScroll) && isAlreadyAtDesiredInitialTarget) {
2546
+ return false;
2547
+ }
2548
+ if (didOffsetChange && ((_b = state.initialScrollSession) == null ? void 0 : _b.kind) !== "offset") {
2549
+ setInitialScrollTarget(state, { ...initialScroll, contentOffset: resolvedOffset });
2550
+ }
2551
+ const forceScroll = (_c = options == null ? void 0 : options.forceScroll) != null ? _c : !!queuedInitialLayout || isInitialScrollInProgress && didOffsetChange;
2552
+ dispatchInitialScroll(ctx, {
2553
+ forceScroll,
2554
+ resolvedOffset,
2555
+ target: initialScroll
2556
+ });
2557
+ return true;
2558
+ }
2559
+ function advanceOffsetInitialScroll(ctx, options) {
2560
+ var _a3, _b;
2561
+ const state = ctx.state;
2562
+ const advanceableState = getAdvanceableInitialScrollState(state);
2563
+ if (!advanceableState) {
2564
+ return false;
2565
+ }
2566
+ const { initialScroll, queuedInitialLayout } = advanceableState;
2567
+ const resolvedOffset = (_a3 = initialScroll.contentOffset) != null ? _a3 : 0;
2568
+ const isAlreadyAtDesiredInitialTarget = Math.abs(state.scroll - resolvedOffset) <= 1 && Math.abs(state.scrollPending - resolvedOffset) <= 1;
2569
+ if ((options == null ? void 0 : options.forceScroll) && isAlreadyAtDesiredInitialTarget) {
2570
+ return false;
2571
+ }
2572
+ const hasMeasuredScrollLayout = !!state.lastLayout && state.scrollLength > 0;
2573
+ const forceScroll = (_b = options == null ? void 0 : options.forceScroll) != null ? _b : hasMeasuredScrollLayout || !!queuedInitialLayout;
2574
+ dispatchInitialScroll(ctx, {
2575
+ forceScroll,
2576
+ resolvedOffset,
2577
+ target: initialScroll
2578
+ });
2579
+ return true;
2580
+ }
2581
+ function advanceCurrentInitialScrollSession(ctx, options) {
2582
+ var _a3;
2583
+ return ((_a3 = ctx.state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" ? advanceOffsetInitialScroll(ctx, {
2584
+ forceScroll: options == null ? void 0 : options.forceScroll
2585
+ }) : advanceMeasuredInitialScroll(ctx, {
2586
+ forceScroll: options == null ? void 0 : options.forceScroll
2587
+ });
2588
+ }
2589
+
2590
+ // src/utils/checkAllSizesKnown.ts
2591
+ function isNullOrUndefined2(value) {
2592
+ return value === null || value === void 0;
2593
+ }
2594
+ function getMountedIndicesInRange(state, start, end) {
2595
+ if (!isNullOrUndefined2(end) && !isNullOrUndefined2(start) && start >= 0 && end >= 0) {
2596
+ return Array.from(state.containerItemKeys.keys()).map((key) => state.indexByKey.get(key)).filter((index) => index !== void 0 && index >= start && index <= end).sort((a, b) => a - b);
2597
+ }
2598
+ return [];
2599
+ }
2600
+ function getMountedBufferedIndices(state) {
2601
+ return getMountedIndicesInRange(state, state.startBuffered, state.endBuffered);
2602
+ }
2603
+ function getMountedNoBufferIndices(state) {
2604
+ return getMountedIndicesInRange(state, state.startNoBuffer, state.endNoBuffer);
2605
+ }
2606
+ function checkAllSizesKnown(state, indices = getMountedBufferedIndices(state)) {
2607
+ return indices.length > 0 && indices.every((index) => {
2608
+ const key = getId(state, index);
2609
+ return state.sizesKnown.has(key);
2610
+ });
2611
+ }
2612
+
2613
+ // src/core/bootstrapInitialScroll.ts
2614
+ var DEFAULT_BOOTSTRAP_REVEAL_EPSILON = 1;
2615
+ var DEFAULT_BOOTSTRAP_REVEAL_MAX_FRAMES = 8;
2616
+ var DEFAULT_BOOTSTRAP_REVEAL_MAX_PASSES = 24;
2617
+ var BOOTSTRAP_REVEAL_ABORT_WARNING = "LegendList bootstrap initial scroll aborted after exceeding convergence bounds.";
2618
+ function getBootstrapInitialScrollSession(state) {
2619
+ var _a3;
2620
+ return ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" ? state.initialScrollSession.bootstrap : void 0;
2621
+ }
2622
+ function isOffsetInitialScrollSession(state) {
2623
+ var _a3;
2624
+ return ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
2625
+ }
2626
+ function doVisibleIndicesMatch(previous, next) {
2627
+ if (!previous || previous.length !== next.length) {
2628
+ return false;
2629
+ }
2630
+ for (let i = 0; i < previous.length; i++) {
2631
+ if (previous[i] !== next[i]) {
2632
+ return false;
2633
+ }
2634
+ }
2635
+ return true;
2636
+ }
2637
+ function getBootstrapRevealVisibleIndices(options) {
2638
+ const { dataLength, getSize, offset, positions, scrollLength, startIndex: requestedStartIndex } = options;
2639
+ const endOffset = offset + scrollLength;
2640
+ const visibleIndices = [];
2641
+ let index = requestedStartIndex !== void 0 ? Math.max(0, Math.min(dataLength - 1, requestedStartIndex)) : 0;
2642
+ while (index > 0) {
2643
+ const previousIndex = index - 1;
2644
+ const previousPosition = positions[previousIndex];
2645
+ if (previousPosition === void 0) {
2646
+ index = previousIndex;
2647
+ continue;
2648
+ }
2649
+ const previousSize = getSize(previousIndex);
2650
+ if (previousSize === void 0) {
2651
+ index = previousIndex;
2652
+ continue;
2653
+ }
2654
+ if (previousPosition + previousSize <= offset) {
2655
+ break;
2656
+ }
2657
+ index = previousIndex;
2658
+ }
2659
+ for (; index < dataLength; index++) {
2660
+ const position = positions[index];
2661
+ if (position === void 0) {
2662
+ continue;
2663
+ }
2664
+ const size = getSize(index);
2665
+ if (size === void 0) {
2666
+ continue;
2667
+ }
2668
+ if (position < endOffset && position + size > offset) {
2669
+ visibleIndices.push(index);
2670
+ } else if (visibleIndices.length > 0 && position >= endOffset) {
2671
+ break;
2672
+ }
2673
+ }
2674
+ return visibleIndices;
2675
+ }
2676
+ function shouldAbortBootstrapReveal(options) {
2677
+ const {
2678
+ mountFrameCount,
2679
+ maxFrames = DEFAULT_BOOTSTRAP_REVEAL_MAX_FRAMES,
2680
+ maxPasses = DEFAULT_BOOTSTRAP_REVEAL_MAX_PASSES,
2681
+ passCount
2682
+ } = options;
2683
+ return mountFrameCount >= maxFrames || passCount >= maxPasses;
2684
+ }
2685
+ function abortBootstrapRevealIfNeeded(ctx, options) {
2686
+ if (!shouldAbortBootstrapReveal(options)) {
2687
+ return false;
2688
+ }
2689
+ if (IS_DEV) {
2690
+ console.warn(BOOTSTRAP_REVEAL_ABORT_WARNING);
2691
+ }
2692
+ abortBootstrapInitialScroll(ctx);
2693
+ return true;
2694
+ }
2695
+ function clearBootstrapInitialScrollSession(state) {
2696
+ var _a3;
2697
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2698
+ const frameHandle = bootstrapInitialScroll == null ? void 0 : bootstrapInitialScroll.frameHandle;
2699
+ if (frameHandle !== void 0 && typeof cancelAnimationFrame === "function") {
2700
+ cancelAnimationFrame(frameHandle);
2701
+ }
2702
+ if (bootstrapInitialScroll) {
2703
+ bootstrapInitialScroll.frameHandle = void 0;
2704
+ }
2705
+ setInitialScrollSession(state, {
2706
+ bootstrap: void 0,
2707
+ kind: (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind
2708
+ });
2709
+ }
2710
+ function startBootstrapInitialScrollSession(state, options) {
2711
+ var _a3, _b, _c;
2712
+ const previousBootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2713
+ setInitialScrollSession(state, {
2714
+ bootstrap: {
2715
+ frameHandle: previousBootstrapInitialScroll == null ? void 0 : previousBootstrapInitialScroll.frameHandle,
2716
+ // Re-arming during the initial mount should spend from the same watchdog budget.
2717
+ mountFrameCount: (_a3 = previousBootstrapInitialScroll == null ? void 0 : previousBootstrapInitialScroll.mountFrameCount) != null ? _a3 : 0,
2718
+ passCount: 0,
2719
+ previousResolvedOffset: void 0,
2720
+ scroll: options.scroll,
2721
+ seedContentOffset: (_c = (_b = options.seedContentOffset) != null ? _b : previousBootstrapInitialScroll == null ? void 0 : previousBootstrapInitialScroll.seedContentOffset) != null ? _c : options.scroll,
2722
+ targetIndexSeed: options.targetIndexSeed,
2723
+ visibleIndices: void 0
2724
+ },
2725
+ kind: "bootstrap"
2726
+ });
2727
+ }
2728
+ function resetBootstrapInitialScrollSession(state, options) {
2729
+ var _a3, _b, _c;
2730
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2731
+ if (!bootstrapInitialScroll) {
2732
+ if ((options == null ? void 0 : options.scroll) !== void 0) {
2733
+ startBootstrapInitialScrollSession(state, {
2734
+ scroll: options.scroll,
2735
+ seedContentOffset: options.seedContentOffset,
2736
+ targetIndexSeed: options.targetIndexSeed
2737
+ });
2738
+ }
2739
+ } else {
2740
+ bootstrapInitialScroll.passCount = 0;
2741
+ bootstrapInitialScroll.previousResolvedOffset = void 0;
2742
+ bootstrapInitialScroll.scroll = (_a3 = options == null ? void 0 : options.scroll) != null ? _a3 : bootstrapInitialScroll.scroll;
2743
+ bootstrapInitialScroll.seedContentOffset = (_b = options == null ? void 0 : options.seedContentOffset) != null ? _b : bootstrapInitialScroll.seedContentOffset;
2744
+ bootstrapInitialScroll.targetIndexSeed = (_c = options == null ? void 0 : options.targetIndexSeed) != null ? _c : bootstrapInitialScroll.targetIndexSeed;
2745
+ bootstrapInitialScroll.visibleIndices = void 0;
2746
+ setInitialScrollSession(state, {
2747
+ bootstrap: bootstrapInitialScroll,
2748
+ kind: "bootstrap"
2749
+ });
2750
+ }
2751
+ }
2752
+ function queueBootstrapInitialScrollReevaluation(state) {
2753
+ requestAnimationFrame(() => {
2754
+ var _a3;
2755
+ if (getBootstrapInitialScrollSession(state)) {
2756
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { forceFullItemPositions: true });
2757
+ }
2758
+ });
2759
+ }
2760
+ function ensureBootstrapInitialScrollFrameTicker(ctx) {
2761
+ const state = ctx.state;
2762
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2763
+ if (!bootstrapInitialScroll || bootstrapInitialScroll.frameHandle !== void 0) {
2764
+ return;
2765
+ }
2766
+ const tick = () => {
2767
+ const activeBootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2768
+ if (!activeBootstrapInitialScroll) {
2769
+ return;
2770
+ }
2771
+ activeBootstrapInitialScroll.frameHandle = void 0;
2772
+ activeBootstrapInitialScroll.mountFrameCount += 1;
2773
+ if (abortBootstrapRevealIfNeeded(ctx, {
2774
+ mountFrameCount: activeBootstrapInitialScroll.mountFrameCount,
2775
+ passCount: activeBootstrapInitialScroll.passCount
2776
+ })) {
2777
+ return;
2778
+ }
2779
+ ensureBootstrapInitialScrollFrameTicker(ctx);
2780
+ };
2781
+ bootstrapInitialScroll.frameHandle = requestAnimationFrame(tick);
2782
+ }
2783
+ function rearmBootstrapInitialScroll(ctx, options) {
2784
+ resetBootstrapInitialScrollSession(ctx.state, options);
2785
+ ensureBootstrapInitialScrollFrameTicker(ctx);
2786
+ queueBootstrapInitialScrollReevaluation(ctx.state);
2787
+ }
2788
+ function createInitialScrollAtEndTarget(options) {
2789
+ const { dataLength, footerSize, preserveForFooterLayout, stylePaddingBottom } = options;
2790
+ return {
2791
+ contentOffset: void 0,
2792
+ index: Math.max(0, dataLength - 1),
2793
+ preserveForBottomPadding: true,
2794
+ preserveForFooterLayout,
2795
+ viewOffset: -stylePaddingBottom - footerSize,
2796
+ viewPosition: 1
2797
+ };
2798
+ }
2799
+ function shouldPreserveInitialScrollForBottomPadding(target) {
2800
+ return !!(target == null ? void 0 : target.preserveForBottomPadding);
2801
+ }
2802
+ function shouldPreserveInitialScrollForFooterLayout(target) {
2803
+ return !!(target == null ? void 0 : target.preserveForFooterLayout);
2804
+ }
2805
+ function isRetargetableBottomAlignedInitialScrollTarget(target) {
2806
+ return !!(target && target.viewPosition === 1 && (shouldPreserveInitialScrollForBottomPadding(target) || shouldPreserveInitialScrollForFooterLayout(target)));
2807
+ }
2808
+ function createRetargetedBottomAlignedInitialScroll(options) {
2809
+ const { dataLength, footerSize, initialScrollAtEnd, stylePaddingBottom, target } = options;
2810
+ const preserveForFooterLayout = shouldPreserveInitialScrollForFooterLayout(target);
2811
+ return {
2812
+ ...target,
2813
+ contentOffset: void 0,
2814
+ index: initialScrollAtEnd ? Math.max(0, dataLength - 1) : target.index,
2815
+ preserveForBottomPadding: true,
2816
+ preserveForFooterLayout,
2817
+ viewOffset: -stylePaddingBottom - (preserveForFooterLayout ? footerSize : 0),
2818
+ viewPosition: 1
2819
+ };
2820
+ }
2821
+ function areEquivalentBootstrapInitialScrollTargets(current, next) {
2822
+ return current.index === next.index && current.preserveForBottomPadding === next.preserveForBottomPadding && current.preserveForFooterLayout === next.preserveForFooterLayout && current.viewOffset === next.viewOffset && current.viewPosition === next.viewPosition;
2823
+ }
2824
+ function clearPendingInitialScrollFooterLayout(ctx, options) {
2825
+ const { dataLength, stylePaddingBottom, target } = options;
2826
+ const state = ctx.state;
2827
+ if (!shouldPreserveInitialScrollForFooterLayout(target)) {
2828
+ return;
2829
+ }
2830
+ const clearedFooterTarget = createInitialScrollAtEndTarget({
2831
+ dataLength,
2832
+ footerSize: 0,
2833
+ preserveForFooterLayout: void 0,
2834
+ stylePaddingBottom
2835
+ });
2836
+ setInitialScrollTarget(state, clearedFooterTarget);
2837
+ }
2838
+ function clearFinishedViewportRetargetableInitialScroll(state) {
2839
+ clearPreservedInitialScrollTarget(state);
2840
+ }
2841
+ function didFinishedInitialScrollMoveAwayFromTarget(ctx, target, epsilon = DEFAULT_BOOTSTRAP_REVEAL_EPSILON) {
2842
+ const state = ctx.state;
2843
+ if (!state.didFinishInitialScroll) {
2844
+ return false;
2845
+ }
2846
+ const currentOffset = getObservedBootstrapInitialScrollOffset(state);
2847
+ return Math.abs(currentOffset - resolveInitialScrollOffset(ctx, target)) > epsilon;
2848
+ }
2849
+ function getObservedBootstrapInitialScrollOffset(state) {
2850
+ var _a3, _b, _c, _d;
2851
+ const observedOffset = (_b = (_a3 = state.refScroller.current) == null ? void 0 : _a3.getCurrentScrollOffset) == null ? void 0 : _b.call(_a3);
2852
+ return typeof observedOffset === "number" && Number.isFinite(observedOffset) ? observedOffset : (_d = (_c = state.scrollPending) != null ? _c : state.scroll) != null ? _d : 0;
2853
+ }
2854
+ function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {
2855
+ var _a3, _b;
2856
+ const state = ctx.state;
2857
+ const initialScroll = state.initialScroll;
2858
+ if (!state.didFinishInitialScroll || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) || (initialScroll == null ? void 0 : initialScroll.viewPosition) !== 1) {
2859
+ return;
2860
+ }
2861
+ if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2862
+ if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {
2863
+ clearPendingInitialScrollFooterLayout(ctx, {
2864
+ dataLength: state.props.data.length,
2865
+ stylePaddingBottom: (_b = state.props.stylePaddingBottom) != null ? _b : 0,
2866
+ target: initialScroll
2867
+ });
2868
+ return;
2869
+ }
2870
+ clearFinishedViewportRetargetableInitialScroll(state);
2871
+ }
2872
+ }
2873
+ function startBootstrapInitialScrollOnMount(ctx, options) {
2874
+ var _a3, _b, _c;
2875
+ const { initialScrollAtEnd, target } = options;
2876
+ const state = ctx.state;
2877
+ const offset = resolveInitialScrollOffset(ctx, target);
2878
+ const shouldFinishAtOrigin = offset === 0 && !initialScrollAtEnd && (isOffsetInitialScrollSession(state) ? Math.abs((_a3 = target.contentOffset) != null ? _a3 : 0) <= 1 : target.index === 0 && ((_b = target.viewPosition) != null ? _b : 0) === 0 && Math.abs((_c = target.viewOffset) != null ? _c : 0) <= 1);
2879
+ const shouldFinishWithPreservedTarget = state.props.data.length === 0 && target.index !== void 0;
2880
+ if (shouldFinishAtOrigin) {
2881
+ clearBootstrapInitialScrollSession(state);
2882
+ finishInitialScroll(ctx, {
2883
+ resolvedOffset: offset
2884
+ });
2885
+ } else if (shouldFinishWithPreservedTarget) {
2886
+ clearBootstrapInitialScrollSession(state);
2887
+ finishInitialScroll(ctx, {
2888
+ preserveTarget: true,
2889
+ resolvedOffset: offset
2890
+ });
2891
+ } else {
2892
+ startBootstrapInitialScrollSession(state, {
2893
+ scroll: offset,
2894
+ seedContentOffset: 0 ,
2895
+ targetIndexSeed: target.index
2896
+ });
2897
+ ensureBootstrapInitialScrollFrameTicker(ctx);
2898
+ }
2899
+ }
2900
+ function handleBootstrapInitialScrollDataChange(ctx, options) {
2901
+ const { dataLength, didDataChange, initialScrollAtEnd, previousDataLength, stylePaddingBottom } = options;
2902
+ const state = ctx.state;
2903
+ const initialScroll = state.initialScroll;
2904
+ if (isOffsetInitialScrollSession(state) || !initialScroll) {
2905
+ return;
2906
+ }
2907
+ const shouldResetDidFinish = !!(state.didFinishInitialScroll && previousDataLength === 0 && dataLength > 0 && initialScroll.index !== void 0);
2908
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2909
+ const shouldClearFinishedResizePreservation = didDataChange && dataLength > 0 && state.didFinishInitialScroll && !bootstrapInitialScroll && !shouldResetDidFinish;
2910
+ if (shouldClearFinishedResizePreservation) {
2911
+ clearPreservedInitialScrollTarget(state);
2912
+ return;
2913
+ }
2914
+ const shouldRetargetBottomAligned = dataLength > 0 && (initialScrollAtEnd || isRetargetableBottomAlignedInitialScrollTarget(initialScroll));
2915
+ if (!didDataChange && !shouldResetDidFinish && !shouldRetargetBottomAligned) {
2916
+ return;
2917
+ }
2918
+ if (shouldRetargetBottomAligned) {
2919
+ const updatedInitialScroll = initialScrollAtEnd ? createInitialScrollAtEndTarget({
2920
+ dataLength,
2921
+ footerSize: peek$(ctx, "footerSize") || 0,
2922
+ preserveForFooterLayout: shouldPreserveInitialScrollForFooterLayout(initialScroll),
2923
+ stylePaddingBottom
2924
+ }) : createRetargetedBottomAlignedInitialScroll({
2925
+ dataLength,
2926
+ footerSize: peek$(ctx, "footerSize") || 0,
2927
+ initialScrollAtEnd,
2928
+ stylePaddingBottom,
2929
+ target: initialScroll
2930
+ });
2931
+ if (!shouldResetDidFinish && didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2932
+ clearPendingInitialScrollFooterLayout(ctx, {
2933
+ dataLength,
2934
+ stylePaddingBottom,
2935
+ target: initialScroll
2936
+ });
2937
+ return;
2938
+ }
2939
+ if (!areEquivalentBootstrapInitialScrollTargets(initialScroll, updatedInitialScroll) || !!bootstrapInitialScroll || shouldResetDidFinish || didDataChange) {
2940
+ setInitialScrollTarget(state, updatedInitialScroll, {
2941
+ resetDidFinish: shouldResetDidFinish
2942
+ });
2943
+ rearmBootstrapInitialScroll(ctx, {
2944
+ scroll: resolveInitialScrollOffset(ctx, updatedInitialScroll),
2945
+ seedContentOffset: shouldResetDidFinish && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
2946
+ targetIndexSeed: updatedInitialScroll.index
2947
+ });
2948
+ return;
2949
+ }
2950
+ }
2951
+ if (!didDataChange) {
2952
+ return;
2953
+ }
2954
+ if (bootstrapInitialScroll || shouldResetDidFinish) {
2955
+ setInitialScrollTarget(state, initialScroll, {
2956
+ resetDidFinish: shouldResetDidFinish
2957
+ });
2958
+ rearmBootstrapInitialScroll(ctx, {
2959
+ scroll: resolveInitialScrollOffset(ctx, initialScroll),
2960
+ seedContentOffset: shouldResetDidFinish && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
2961
+ targetIndexSeed: initialScroll.index
2962
+ });
2963
+ }
2964
+ }
2965
+ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2966
+ const { dataLength, footerSize, initialScrollAtEnd, stylePaddingBottom } = options;
2967
+ const state = ctx.state;
2968
+ if (!initialScrollAtEnd) {
2969
+ return;
2970
+ }
2971
+ const initialScroll = state.initialScroll;
2972
+ if (isOffsetInitialScrollSession(state) || dataLength === 0 || !initialScroll) {
2973
+ return;
2974
+ }
2975
+ const shouldProcessFooterLayout = !!getBootstrapInitialScrollSession(state) || shouldPreserveInitialScrollForFooterLayout(initialScroll);
2976
+ if (!shouldProcessFooterLayout) {
2977
+ return;
2978
+ }
2979
+ if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2980
+ clearPendingInitialScrollFooterLayout(ctx, {
2981
+ dataLength,
2982
+ stylePaddingBottom,
2983
+ target: initialScroll
2984
+ });
2985
+ } else {
2986
+ const updatedInitialScroll = createInitialScrollAtEndTarget({
2987
+ dataLength,
2988
+ footerSize,
2989
+ preserveForFooterLayout: shouldPreserveInitialScrollForFooterLayout(initialScroll),
2990
+ stylePaddingBottom
2991
+ });
2992
+ const didTargetChange = initialScroll.index !== updatedInitialScroll.index || initialScroll.viewPosition !== updatedInitialScroll.viewPosition || initialScroll.viewOffset !== updatedInitialScroll.viewOffset;
2993
+ if (!didTargetChange) {
2994
+ clearPendingInitialScrollFooterLayout(ctx, {
2995
+ dataLength,
2996
+ stylePaddingBottom,
2997
+ target: initialScroll
2998
+ });
2999
+ } else {
3000
+ const didFinishInitialScroll = !!state.didFinishInitialScroll;
3001
+ setInitialScrollTarget(state, updatedInitialScroll, {
3002
+ resetDidFinish: didFinishInitialScroll
3003
+ });
3004
+ rearmBootstrapInitialScroll(ctx, {
3005
+ scroll: resolveInitialScrollOffset(ctx, updatedInitialScroll),
3006
+ targetIndexSeed: updatedInitialScroll.index
3007
+ });
3008
+ }
3009
+ }
3010
+ }
3011
+ function handleBootstrapInitialScrollLayoutChange(ctx) {
3012
+ const state = ctx.state;
3013
+ const initialScroll = state.initialScroll;
3014
+ if (isOffsetInitialScrollSession(state) || state.props.data.length === 0 || !initialScroll) {
3015
+ return;
3016
+ }
3017
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
3018
+ if (!bootstrapInitialScroll && initialScroll.viewPosition !== 1) {
3019
+ return;
3020
+ }
3021
+ const didFinishInitialScroll = state.didFinishInitialScroll;
3022
+ if (didFinishInitialScroll) {
3023
+ setInitialScrollTarget(state, initialScroll, {
3024
+ resetDidFinish: true
3025
+ });
3026
+ state.clearPreservedInitialScrollOnNextFinish = true;
3027
+ }
3028
+ rearmBootstrapInitialScroll(ctx, {
3029
+ scroll: resolveInitialScrollOffset(ctx, initialScroll),
3030
+ seedContentOffset: didFinishInitialScroll && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
3031
+ targetIndexSeed: initialScroll.index
3032
+ });
3033
+ }
3034
+ function evaluateBootstrapInitialScroll(ctx) {
3035
+ var _a3, _b;
3036
+ const state = ctx.state;
3037
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
3038
+ const initialScroll = state.initialScroll;
3039
+ if (!bootstrapInitialScroll || !initialScroll || isOffsetInitialScrollSession(state) || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll)) {
3040
+ return;
3041
+ }
3042
+ bootstrapInitialScroll.passCount += 1;
3043
+ if (abortBootstrapRevealIfNeeded(ctx, {
3044
+ mountFrameCount: bootstrapInitialScroll.mountFrameCount,
3045
+ passCount: bootstrapInitialScroll.passCount
3046
+ })) {
3047
+ return;
3048
+ }
3049
+ if (initialScroll.index !== void 0 && state.startBuffered >= 0 && state.endBuffered >= 0 && initialScroll.index >= state.startBuffered && initialScroll.index <= state.endBuffered) {
3050
+ bootstrapInitialScroll.targetIndexSeed = void 0;
3051
+ }
3052
+ const resolvedOffset = resolveInitialScrollOffset(ctx, initialScroll);
3053
+ const mountedBufferedIndices = getMountedBufferedIndices(state);
3054
+ const areMountedBufferedIndicesMeasured = checkAllSizesKnown(state, mountedBufferedIndices);
3055
+ const didResolvedOffsetChange = Math.abs(bootstrapInitialScroll.scroll - resolvedOffset) > 1;
3056
+ const { data } = state.props;
3057
+ const visibleIndices = getBootstrapRevealVisibleIndices({
3058
+ dataLength: data.length,
3059
+ getSize: (index) => {
3060
+ var _a4, _b2;
3061
+ const id = (_a4 = state.idCache[index]) != null ? _a4 : getId(state, index);
3062
+ return (_b2 = state.sizes.get(id)) != null ? _b2 : getItemSize(ctx, id, index, data[index]);
3063
+ },
3064
+ offset: resolvedOffset,
3065
+ positions: state.positions,
3066
+ scrollLength: state.scrollLength,
3067
+ startIndex: (_b = bootstrapInitialScroll.targetIndexSeed) != null ? _b : state.startBuffered >= 0 ? state.startBuffered : void 0
3068
+ });
3069
+ const areVisibleIndicesMeasured = visibleIndices.length > 0 && visibleIndices.every((index) => {
3070
+ var _a4;
3071
+ const id = (_a4 = state.idCache[index]) != null ? _a4 : getId(state, index);
3072
+ return state.sizesKnown.has(id);
3073
+ });
3074
+ const previousResolvedOffset = bootstrapInitialScroll.previousResolvedOffset;
3075
+ const previousVisibleIndices = bootstrapInitialScroll.visibleIndices;
3076
+ bootstrapInitialScroll.previousResolvedOffset = resolvedOffset;
3077
+ bootstrapInitialScroll.visibleIndices = visibleIndices;
3078
+ if (didResolvedOffsetChange) {
3079
+ bootstrapInitialScroll.scroll = resolvedOffset;
3080
+ queueBootstrapInitialScrollReevaluation(state);
3081
+ return;
3082
+ }
3083
+ if (!areMountedBufferedIndicesMeasured || !areVisibleIndicesMeasured) {
3084
+ return;
3085
+ }
3086
+ const didRevealSettle = previousResolvedOffset !== void 0 && Math.abs(previousResolvedOffset - resolvedOffset) <= DEFAULT_BOOTSTRAP_REVEAL_EPSILON && doVisibleIndicesMatch(previousVisibleIndices, visibleIndices);
3087
+ if (!didRevealSettle) {
3088
+ queueBootstrapInitialScrollReevaluation(state);
3089
+ return;
3090
+ }
3091
+ {
3092
+ clearBootstrapInitialScrollSession(state);
3093
+ dispatchInitialScroll(ctx, {
3094
+ forceScroll: true,
3095
+ resolvedOffset,
3096
+ target: initialScroll,
3097
+ waitForCompletionFrame: Platform.OS === "web"
3098
+ });
3099
+ }
3100
+ }
3101
+ function finishBootstrapInitialScrollWithoutScroll(ctx, resolvedOffset) {
3102
+ var _a3;
3103
+ const state = ctx.state;
3104
+ clearBootstrapInitialScrollSession(state);
3105
+ const shouldPreserveResizeTarget = !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_a3 = state.initialScroll) == null ? void 0 : _a3.viewPosition) === 1;
3106
+ finishInitialScroll(ctx, {
3107
+ preserveTarget: shouldPreserveResizeTarget,
3108
+ recalculateItems: true,
3109
+ resolvedOffset,
3110
+ schedulePreservedTargetClear: shouldPreserveResizeTarget
3111
+ });
3112
+ }
3113
+ function abortBootstrapInitialScroll(ctx) {
3114
+ var _a3, _b, _c, _d;
3115
+ const state = ctx.state;
3116
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
3117
+ const initialScroll = state.initialScroll;
3118
+ if (bootstrapInitialScroll && initialScroll && !isOffsetInitialScrollSession(state) && state.refScroller.current) {
3119
+ clearBootstrapInitialScrollSession(state);
3120
+ dispatchInitialScroll(ctx, {
3121
+ forceScroll: true,
3122
+ resolvedOffset: bootstrapInitialScroll.scroll,
3123
+ target: initialScroll,
3124
+ waitForCompletionFrame: Platform.OS === "web"
3125
+ });
3126
+ } else {
3127
+ finishBootstrapInitialScrollWithoutScroll(
3128
+ ctx,
3129
+ (_d = (_c = (_b = (_a3 = getBootstrapInitialScrollSession(state)) == null ? void 0 : _a3.scroll) != null ? _b : state.scrollPending) != null ? _c : state.scroll) != null ? _d : 0
3130
+ );
3131
+ }
3132
+ }
3133
+
3134
+ // src/core/checkFinishedScroll.ts
3135
+ var INITIAL_SCROLL_MAX_FALLBACK_CHECKS = 20;
3136
+ var INITIAL_SCROLL_COMPLETION_TARGET_EPSILON = 1;
3137
+ var INITIAL_SCROLL_ZERO_TARGET_EPSILON = 1;
3138
+ var SILENT_INITIAL_SCROLL_RETRY_DELAY_MS = 16;
3139
+ function checkFinishedScroll(ctx, options) {
3140
+ const scrollingTo = ctx.state.scrollingTo;
3141
+ if (options == null ? void 0 : options.onlyIfAligned) {
3142
+ if (!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) || scrollingTo.animated) {
3143
+ return;
3144
+ }
3145
+ if (!getResolvedScrollCompletionState(ctx, scrollingTo).isAtResolvedTarget) {
3146
+ return;
3147
+ }
3148
+ }
3149
+ ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
3150
+ }
3151
+ function hasScrollCompletionOwnership(state, options) {
3152
+ const { clampedTargetOffset, scrollingTo } = options;
3153
+ return !scrollingTo.isInitialScroll || state.hasScrolled || clampedTargetOffset <= INITIAL_SCROLL_COMPLETION_TARGET_EPSILON;
3154
+ }
3155
+ function isSilentInitialDispatch(state, scrollingTo) {
3156
+ return !!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && initialScrollCompletion.didDispatchNativeScroll(state) && !state.hasScrolled;
3157
+ }
3158
+ function getInitialScrollWatchdogTargetOffset(state) {
3159
+ var _a3;
3160
+ return (_a3 = initialScrollWatchdog.get(state)) == null ? void 0 : _a3.targetOffset;
3161
+ }
3162
+ function isNativeInitialNonZeroTarget(state) {
3163
+ const targetOffset = getInitialScrollWatchdogTargetOffset(state);
3164
+ return !state.didFinishInitialScroll && initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset);
3165
+ }
3166
+ function shouldFinishInitialScrollWithoutNativeProgress(state, scrollingTo) {
3167
+ var _a3, _b;
3168
+ if (!scrollingTo.isInitialScroll || scrollingTo.animated || !state.didContainersLayout) {
3169
+ return false;
3170
+ }
3171
+ if (((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap") {
3172
+ return false;
3173
+ }
3174
+ const targetOffset = (_b = scrollingTo.targetOffset) != null ? _b : scrollingTo.offset;
3175
+ if (initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset) && initialScrollCompletion.didDispatchNativeScroll(state) && !state.hasScrolled) {
3176
+ return false;
3177
+ }
3178
+ if (initialScrollWatchdog.isAtZeroTargetOffset(targetOffset) || Math.abs(state.scroll - targetOffset) > 1 || Math.abs(state.scrollPending - targetOffset) > 1) {
3179
+ return false;
3180
+ }
3181
+ return !!scrollingTo.waitForInitialScrollCompletionFrame || isNativeInitialNonZeroTarget(state);
3182
+ }
3183
+ function shouldFinishInitialZeroTargetScroll(ctx) {
3184
+ var _a3;
3185
+ const { state } = ctx;
3186
+ return !!((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) && state.props.data.length > 0 && getContentSize(ctx) <= state.scrollLength && state.scrollPending <= INITIAL_SCROLL_ZERO_TARGET_EPSILON;
3187
+ }
3188
+ function getResolvedScrollCompletionState(ctx, scrollingTo) {
3189
+ var _a3;
3190
+ const { state } = ctx;
3191
+ const scroll = state.scrollPending;
3192
+ const adjust = state.scrollAdjustHandler.getAdjust();
3193
+ const clampedTargetOffset = (_a3 = scrollingTo.targetOffset) != null ? _a3 : clampScrollOffset(ctx, scrollingTo.offset - (scrollingTo.viewOffset || 0), scrollingTo);
3194
+ const maxOffset = clampScrollOffset(ctx, scroll, scrollingTo);
3195
+ const diff1 = Math.abs(scroll - clampedTargetOffset);
3196
+ const diff2 = Math.abs(diff1 - adjust);
3197
+ return {
3198
+ clampedTargetOffset,
3199
+ isAtResolvedTarget: Math.abs(scroll - maxOffset) < 1 && (diff1 < 1 || !scrollingTo.animated && diff2 < 1)
3200
+ };
3201
+ }
3202
+ function checkFinishedScrollFrame(ctx) {
3203
+ const scrollingTo = ctx.state.scrollingTo;
3204
+ if (!scrollingTo) {
3205
+ return;
3206
+ }
3207
+ const { state } = ctx;
3208
+ state.animFrameCheckFinishedScroll = void 0;
3209
+ const completionState = getResolvedScrollCompletionState(ctx, scrollingTo);
3210
+ if (completionState.isAtResolvedTarget && hasScrollCompletionOwnership(state, {
3211
+ clampedTargetOffset: completionState.clampedTargetOffset,
3212
+ scrollingTo
3213
+ })) {
3214
+ finishScrollTo(ctx);
3215
+ }
3216
+ }
3217
+ function scrollToFallbackOffset(ctx, offset) {
3218
+ var _a3;
3219
+ (_a3 = ctx.state.refScroller.current) == null ? void 0 : _a3.scrollTo({
3220
+ animated: false,
3221
+ x: ctx.state.props.horizontal ? offset : 0,
3222
+ y: ctx.state.props.horizontal ? 0 : offset
3223
+ });
3224
+ }
3225
+ function checkFinishedScrollFallback(ctx) {
1897
3226
  const state = ctx.state;
1898
- const { animated, horizontal, offset } = params;
1899
- const scroller = state.refScroller.current;
1900
- const node = scroller == null ? void 0 : scroller.getScrollableNode();
1901
- if (!scroller || !node) {
3227
+ const scrollingTo = state.scrollingTo;
3228
+ const shouldFinishInitialZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
3229
+ const silentInitialDispatch = isSilentInitialDispatch(state, scrollingTo);
3230
+ const canFinishInitialWithoutNativeProgress = scrollingTo !== void 0 ? shouldFinishInitialScrollWithoutNativeProgress(state, scrollingTo) : false;
3231
+ const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && !shouldFinishInitialZeroTarget && !canFinishInitialWithoutNativeProgress || !state.didContainersLayout;
3232
+ const initialDelay = shouldFinishInitialZeroTarget || canFinishInitialWithoutNativeProgress ? 0 : silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : slowTimeout ? 500 : 100;
3233
+ state.timeoutCheckFinishedScrollFallback = setTimeout(() => {
3234
+ let numChecks = 0;
3235
+ const scheduleFallbackCheck = (delay) => {
3236
+ state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, delay);
3237
+ };
3238
+ const checkHasScrolled = () => {
3239
+ var _c;
3240
+ state.timeoutCheckFinishedScrollFallback = void 0;
3241
+ const isStillScrollingTo = state.scrollingTo;
3242
+ if (isStillScrollingTo) {
3243
+ numChecks++;
3244
+ const isNativeInitialPending = isNativeInitialNonZeroTarget(state) && !state.hasScrolled;
3245
+ const maxChecks = silentInitialDispatch ? 5 : isNativeInitialPending ? INITIAL_SCROLL_MAX_FALLBACK_CHECKS : 5;
3246
+ const shouldFinishZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
3247
+ const canFinishInitialScrollWithoutNativeProgress = shouldFinishInitialScrollWithoutNativeProgress(
3248
+ state,
3249
+ isStillScrollingTo
3250
+ );
3251
+ const completionState = getResolvedScrollCompletionState(ctx, isStillScrollingTo);
3252
+ const canFinishAfterSilentNativeDispatch = silentInitialDispatch && completionState.isAtResolvedTarget && numChecks >= 1;
3253
+ if (shouldFinishZeroTarget || state.hasScrolled || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
3254
+ finishScrollTo(ctx);
3255
+ } else if (isNativeInitialPending && numChecks <= maxChecks) {
3256
+ const targetOffset = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : state.scrollPending;
3257
+ scrollToFallbackOffset(ctx, targetOffset);
3258
+ scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
3259
+ } else {
3260
+ scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
3261
+ }
3262
+ }
3263
+ };
3264
+ checkHasScrolled();
3265
+ }, initialDelay);
3266
+ }
3267
+
3268
+ // src/core/initialScrollLifecycle.ts
3269
+ function handleInitialScrollLayoutReady(ctx) {
3270
+ var _a3;
3271
+ if (!ctx.state.initialScroll) {
1902
3272
  return;
1903
3273
  }
1904
- const isAnimated = !!animated;
1905
- const isHorizontal = !!horizontal;
1906
- const left = isHorizontal ? offset : 0;
1907
- const top = isHorizontal ? 0 : offset;
1908
- scroller.scrollTo({ animated: isAnimated, x: left, y: top });
1909
- if (isAnimated) {
1910
- const target = scroller.getScrollEventTarget();
1911
- listenForScrollEnd(ctx, {
1912
- readOffset: () => scroller.getCurrentScrollOffset(),
1913
- target,
1914
- targetOffset: offset
1915
- });
1916
- } else {
1917
- state.scroll = offset;
1918
- setTimeout(() => {
1919
- finishScrollTo(ctx);
1920
- }, 100);
3274
+ const runScroll = () => advanceCurrentInitialScrollSession(ctx, { forceScroll: true });
3275
+ runScroll();
3276
+ if (((_a3 = ctx.state.initialScrollSession) == null ? void 0 : _a3.kind) !== "offset") {
3277
+ requestAnimationFrame(runScroll);
1921
3278
  }
3279
+ checkFinishedScroll(ctx, { onlyIfAligned: true });
1922
3280
  }
1923
- function listenForScrollEnd(ctx, params) {
1924
- const { readOffset, target, targetOffset } = params;
1925
- if (!target) {
1926
- finishScrollTo(ctx);
3281
+ function initializeInitialScrollOnMount(ctx, options) {
3282
+ var _a3, _b;
3283
+ const { dataLength, hasFooterComponent, initialContentOffset, initialScrollAtEnd, useBootstrapInitialScroll } = options;
3284
+ const state = ctx.state;
3285
+ const initialScroll = state.initialScroll;
3286
+ const resolvedInitialContentOffset = initialContentOffset != null ? initialContentOffset : 0;
3287
+ const preserveForFooterLayout = useBootstrapInitialScroll && initialScrollAtEnd && hasFooterComponent;
3288
+ if (initialScroll && (initialScroll.contentOffset === void 0 || !!initialScroll.preserveForFooterLayout !== preserveForFooterLayout && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) !== "offset")) {
3289
+ setInitialScrollTarget(state, {
3290
+ ...initialScroll,
3291
+ contentOffset: resolvedInitialContentOffset,
3292
+ preserveForFooterLayout
3293
+ });
3294
+ }
3295
+ if (useBootstrapInitialScroll && initialScroll && ((_b = state.initialScrollSession) == null ? void 0 : _b.kind) !== "offset") {
3296
+ startBootstrapInitialScrollOnMount(ctx, {
3297
+ initialScrollAtEnd,
3298
+ target: state.initialScroll
3299
+ });
1927
3300
  return;
1928
3301
  }
1929
- const supportsScrollEnd = "onscrollend" in target;
1930
- let idleTimeout;
1931
- let settled = false;
1932
- const targetToken = ctx.state.scrollingTo;
1933
- const maxTimeout = setTimeout(() => finish("max"), SCROLL_END_MAX_MS);
1934
- const cleanup = () => {
1935
- target.removeEventListener("scroll", onScroll2);
1936
- if (supportsScrollEnd) {
1937
- target.removeEventListener("scrollend", onScrollEnd);
1938
- }
1939
- if (idleTimeout) {
1940
- clearTimeout(idleTimeout);
1941
- }
1942
- clearTimeout(maxTimeout);
1943
- };
1944
- const finish = (reason) => {
1945
- if (settled) return;
1946
- if (targetToken !== ctx.state.scrollingTo) {
1947
- settled = true;
1948
- cleanup();
1949
- return;
1950
- }
1951
- const currentOffset = readOffset();
1952
- const isNearTarget = Math.abs(currentOffset - targetOffset) <= SCROLL_END_TARGET_EPSILON;
1953
- if (reason === "scrollend" && !isNearTarget) {
1954
- return;
1955
- }
1956
- settled = true;
1957
- cleanup();
1958
- finishScrollTo(ctx);
1959
- };
1960
- const onScroll2 = () => {
1961
- if (idleTimeout) {
1962
- clearTimeout(idleTimeout);
3302
+ const hasPendingDataDependentInitialScroll = !!initialScroll && dataLength === 0 && !(resolvedInitialContentOffset === 0 && !initialScrollAtEnd);
3303
+ if (!resolvedInitialContentOffset && !hasPendingDataDependentInitialScroll) {
3304
+ if (initialScroll && !initialScrollAtEnd) {
3305
+ finishInitialScroll(ctx, {
3306
+ resolvedOffset: resolvedInitialContentOffset
3307
+ });
3308
+ } else {
3309
+ setInitialRenderState(ctx, { didInitialScroll: true });
1963
3310
  }
1964
- idleTimeout = setTimeout(() => finish("idle"), SCROLL_END_IDLE_MS);
1965
- };
1966
- const onScrollEnd = () => finish("scrollend");
1967
- target.addEventListener("scroll", onScroll2);
1968
- if (supportsScrollEnd) {
1969
- target.addEventListener("scrollend", onScrollEnd);
1970
- } else {
1971
- idleTimeout = setTimeout(() => finish("idle"), SMOOTH_SCROLL_DURATION_MS);
1972
3311
  }
1973
3312
  }
1974
-
1975
- // src/core/scrollTo.ts
1976
- var WATCHDOG_OFFSET_EPSILON = 1;
1977
- function scrollTo(ctx, params) {
1978
- var _a3, _b;
3313
+ function handleInitialScrollDataChange(ctx, options) {
3314
+ var _a3, _b, _c;
3315
+ const { dataLength, didDataChange, initialScrollAtEnd, stylePaddingBottom, useBootstrapInitialScroll } = options;
1979
3316
  const state = ctx.state;
1980
- const { noScrollingTo, forceScroll, ...scrollTarget } = params;
1981
- const { animated, isInitialScroll, offset: scrollTargetOffset, precomputedWithViewOffset } = scrollTarget;
1982
- const {
1983
- props: { horizontal }
1984
- } = state;
1985
- if (state.animFrameCheckFinishedScroll) {
1986
- cancelAnimationFrame(ctx.state.animFrameCheckFinishedScroll);
1987
- }
1988
- if (state.timeoutCheckFinishedScrollFallback) {
1989
- clearTimeout(ctx.state.timeoutCheckFinishedScrollFallback);
3317
+ const previousDataLength = (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.previousDataLength) != null ? _b : 0;
3318
+ if (state.initialScrollSession) {
3319
+ state.initialScrollSession.previousDataLength = dataLength;
1990
3320
  }
1991
- let offset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, scrollTargetOffset, scrollTarget);
1992
- offset = clampScrollOffset(ctx, offset, scrollTarget);
1993
- state.scrollHistory.length = 0;
1994
- if (!noScrollingTo) {
1995
- state.scrollingTo = {
1996
- ...scrollTarget,
1997
- targetOffset: offset
1998
- };
3321
+ setInitialScrollSession(state);
3322
+ if (useBootstrapInitialScroll) {
3323
+ handleBootstrapInitialScrollDataChange(ctx, {
3324
+ dataLength,
3325
+ didDataChange,
3326
+ initialScrollAtEnd,
3327
+ previousDataLength,
3328
+ stylePaddingBottom
3329
+ });
3330
+ return;
1999
3331
  }
2000
- state.scrollPending = offset;
2001
- const shouldWatchInitialNativeScroll = !state.didFinishInitialScroll && (isInitialScroll || !!state.initialNativeScrollWatchdog) && offset > WATCHDOG_OFFSET_EPSILON;
2002
- const shouldClearInitialNativeScrollWatchdog = !state.didFinishInitialScroll && !!state.initialNativeScrollWatchdog && offset <= WATCHDOG_OFFSET_EPSILON;
2003
- if (shouldWatchInitialNativeScroll) {
2004
- state.hasScrolled = false;
2005
- state.initialNativeScrollWatchdog = {
2006
- startScroll: (_b = (_a3 = state.initialNativeScrollWatchdog) == null ? void 0 : _a3.startScroll) != null ? _b : state.scroll,
2007
- targetOffset: offset
2008
- };
2009
- } else if (shouldClearInitialNativeScrollWatchdog) {
2010
- state.initialNativeScrollWatchdog = void 0;
3332
+ const shouldReplayFinishedOffsetInitialScroll = previousDataLength === 0 && dataLength > 0 && !!state.initialScroll && ((_c = ctx.state.initialScrollSession) == null ? void 0 : _c.kind) === "offset" && !!state.didFinishInitialScroll;
3333
+ if (previousDataLength !== 0 || dataLength === 0 || !state.initialScroll || !state.queuedInitialLayout || state.didFinishInitialScroll && !shouldReplayFinishedOffsetInitialScroll) {
3334
+ return;
2011
3335
  }
2012
- if (forceScroll || !isInitialScroll || Platform.OS === "android") {
2013
- doScrollTo(ctx, { animated, horizontal, offset });
2014
- } else {
2015
- state.scroll = offset;
3336
+ if (shouldReplayFinishedOffsetInitialScroll) {
3337
+ state.didFinishInitialScroll = false;
2016
3338
  }
3339
+ advanceCurrentInitialScrollSession(ctx);
2017
3340
  }
2018
3341
 
2019
- // src/core/doMaintainScrollAtEnd.ts
2020
- function doMaintainScrollAtEnd(ctx) {
3342
+ // src/utils/requestAdjust.ts
3343
+ function requestAdjust(ctx, positionDiff, dataChanged) {
2021
3344
  const state = ctx.state;
2022
- const {
2023
- didContainersLayout,
2024
- isAtEnd,
2025
- pendingNativeMVCPAdjust,
2026
- refScroller,
2027
- props: { maintainScrollAtEnd }
2028
- } = state;
2029
- const shouldMaintainScrollAtEnd = !!(isAtEnd && maintainScrollAtEnd && didContainersLayout);
2030
- if (pendingNativeMVCPAdjust) {
2031
- state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
2032
- return false;
2033
- }
2034
- state.pendingMaintainScrollAtEnd = false;
2035
- if (shouldMaintainScrollAtEnd) {
2036
- const contentSize = getContentSize(ctx);
2037
- if (contentSize < state.scrollLength) {
2038
- state.scroll = 0;
2039
- }
2040
- requestAnimationFrame(() => {
2041
- var _a3;
2042
- if (state.isAtEnd) {
2043
- state.maintainingScrollAtEnd = true;
2044
- (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
2045
- animated: maintainScrollAtEnd.animated
2046
- });
2047
- setTimeout(
2048
- () => {
2049
- state.maintainingScrollAtEnd = false;
2050
- },
2051
- maintainScrollAtEnd.animated ? 500 : 0
2052
- );
3345
+ if (Math.abs(positionDiff) > 0.1) {
3346
+ const doit = () => {
3347
+ {
3348
+ state.scrollAdjustHandler.requestAdjust(positionDiff);
3349
+ if (state.adjustingFromInitialMount) {
3350
+ state.adjustingFromInitialMount--;
3351
+ }
2053
3352
  }
2054
- });
2055
- return true;
3353
+ };
3354
+ state.scroll += positionDiff;
3355
+ state.scrollForNextCalculateItemsInView = void 0;
3356
+ const readyToRender = peek$(ctx, "readyToRender");
3357
+ if (readyToRender) {
3358
+ doit();
3359
+ } else {
3360
+ state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
3361
+ requestAnimationFrame(doit);
3362
+ }
2056
3363
  }
2057
- return false;
2058
3364
  }
2059
3365
 
2060
3366
  // src/core/mvcp.ts
@@ -2179,7 +3485,7 @@ function resolvePendingNativeMVCPAdjust(ctx, newScroll) {
2179
3485
  settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
2180
3486
  return true;
2181
3487
  }
2182
- if (state.pendingMaintainScrollAtEnd && state.isAtEnd && progressTowardAmount > MVCP_POSITION_EPSILON) {
3488
+ if (state.pendingMaintainScrollAtEnd && peek$(ctx, "isWithinMaintainScrollAtEndThreshold") && progressTowardAmount > MVCP_POSITION_EPSILON) {
2183
3489
  settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
2184
3490
  return true;
2185
3491
  }
@@ -2310,118 +3616,110 @@ function prepareMVCP(ctx, dataChanged) {
2310
3616
  positionDiff += diff * scrollingToViewPosition;
2311
3617
  scrollingTo.itemSize = newSize;
2312
3618
  }
2313
- }
2314
- }
2315
- updateAnchorLock(state, {
2316
- anchorId: anchorIdForLock,
2317
- anchorPosition: anchorPositionForLock,
2318
- dataChanged,
2319
- now,
2320
- positionDiff
2321
- });
2322
- if (shouldQueueNativeMVCPAdjust()) {
2323
- state.pendingNativeMVCPAdjust = {
2324
- amount: positionDiff,
2325
- furthestProgressTowardAmount: 0,
2326
- manualApplied: 0,
2327
- startScroll: prevScroll
2328
- };
2329
- maybeApplyPredictedNativeMVCPAdjust(ctx);
2330
- return;
2331
- }
2332
- if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
2333
- requestAdjust(ctx, positionDiff);
2334
- }
2335
- };
2336
- }
2337
- }
2338
-
2339
- // src/core/updateScroll.ts
2340
- function updateScroll(ctx, newScroll, forceUpdate) {
2341
- var _a3;
2342
- const state = ctx.state;
2343
- const { ignoreScrollFromMVCP, lastScrollAdjustForHistory, scrollAdjustHandler, scrollHistory, scrollingTo } = state;
2344
- const prevScroll = state.scroll;
2345
- state.hasScrolled = true;
2346
- state.lastBatchingAction = Date.now();
2347
- const currentTime = Date.now();
2348
- const adjust = scrollAdjustHandler.getAdjust();
2349
- const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
2350
- if (adjustChanged) {
2351
- scrollHistory.length = 0;
2352
- }
2353
- state.lastScrollAdjustForHistory = adjust;
2354
- if (scrollingTo === void 0 && !(scrollHistory.length === 0 && newScroll === state.scroll)) {
2355
- if (!adjustChanged) {
2356
- scrollHistory.push({ scroll: newScroll, time: currentTime });
2357
- }
2358
- }
2359
- if (scrollHistory.length > 5) {
2360
- scrollHistory.shift();
2361
- }
2362
- if (ignoreScrollFromMVCP && !scrollingTo) {
2363
- const { lt, gt } = ignoreScrollFromMVCP;
2364
- if (lt && newScroll < lt || gt && newScroll > gt) {
2365
- state.ignoreScrollFromMVCPIgnored = true;
2366
- return;
2367
- }
2368
- }
2369
- state.scrollPrev = prevScroll;
2370
- state.scrollPrevTime = state.scrollTime;
2371
- state.scroll = newScroll;
2372
- state.scrollTime = currentTime;
2373
- const scrollDelta = Math.abs(newScroll - prevScroll);
2374
- const didResolvePendingNativeMVCPAdjust = resolvePendingNativeMVCPAdjust(ctx, newScroll);
2375
- const scrollLength = state.scrollLength;
2376
- const lastCalculated = state.scrollLastCalculate;
2377
- const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
2378
- const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
2379
- if (shouldUpdate) {
2380
- state.scrollLastCalculate = state.scroll;
2381
- state.ignoreScrollFromMVCPIgnored = false;
2382
- state.lastScrollDelta = scrollDelta;
2383
- const runCalculateItems = () => {
2384
- var _a4;
2385
- (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { doMVCP: scrollingTo !== void 0 });
2386
- checkThresholds(ctx);
3619
+ }
3620
+ }
3621
+ updateAnchorLock(state, {
3622
+ anchorId: anchorIdForLock,
3623
+ anchorPosition: anchorPositionForLock,
3624
+ dataChanged,
3625
+ now,
3626
+ positionDiff
3627
+ });
3628
+ if (shouldQueueNativeMVCPAdjust()) {
3629
+ state.pendingNativeMVCPAdjust = {
3630
+ amount: positionDiff,
3631
+ furthestProgressTowardAmount: 0,
3632
+ manualApplied: 0,
3633
+ startScroll: prevScroll
3634
+ };
3635
+ maybeApplyPredictedNativeMVCPAdjust(ctx);
3636
+ return;
3637
+ }
3638
+ if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
3639
+ requestAdjust(ctx, positionDiff);
3640
+ }
2387
3641
  };
2388
- if (scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength) {
2389
- ReactDOM.flushSync(runCalculateItems);
2390
- } else {
2391
- runCalculateItems();
2392
- }
2393
- const shouldMaintainScrollAtEndAfterPendingSettle = !!state.pendingMaintainScrollAtEnd || !!((_a3 = state.props.maintainScrollAtEnd) == null ? void 0 : _a3.onDataChange);
2394
- if (didResolvePendingNativeMVCPAdjust && shouldMaintainScrollAtEndAfterPendingSettle) {
2395
- state.pendingMaintainScrollAtEnd = false;
2396
- doMaintainScrollAtEnd(ctx);
2397
- }
2398
- state.dataChangeNeedsScrollUpdate = false;
2399
- state.lastScrollDelta = 0;
2400
3642
  }
2401
3643
  }
2402
3644
 
2403
- // src/utils/requestAdjust.ts
2404
- function requestAdjust(ctx, positionDiff, dataChanged) {
3645
+ // src/core/syncMountedContainer.ts
3646
+ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3647
+ var _a3, _b, _c, _d, _e, _f, _g, _h;
2405
3648
  const state = ctx.state;
2406
- if (Math.abs(positionDiff) > 0.1) {
2407
- const doit = () => {
2408
- {
2409
- state.scrollAdjustHandler.requestAdjust(positionDiff);
2410
- if (state.adjustingFromInitialMount) {
2411
- state.adjustingFromInitialMount--;
3649
+ const {
3650
+ columns,
3651
+ columnSpans,
3652
+ positions,
3653
+ props: { data, itemsAreEqual, keyExtractor }
3654
+ } = state;
3655
+ const item = data[itemIndex];
3656
+ if (item === void 0) {
3657
+ return { didChangePosition: false, didRefreshData: false };
3658
+ }
3659
+ const updateLayout = (_a3 = options == null ? void 0 : options.updateLayout) != null ? _a3 : true;
3660
+ let didChangePosition = false;
3661
+ let didRefreshData = false;
3662
+ if (updateLayout) {
3663
+ const positionValue = positions[itemIndex];
3664
+ if (positionValue === void 0) {
3665
+ set$(ctx, `containerPosition${containerIndex}`, POSITION_OUT_OF_VIEW);
3666
+ return { didChangePosition: false, didRefreshData: false };
3667
+ }
3668
+ const position = (positionValue || 0) - ((_b = options == null ? void 0 : options.scrollAdjustPending) != null ? _b : 0);
3669
+ const column = columns[itemIndex] || 1;
3670
+ const span = columnSpans[itemIndex] || 1;
3671
+ const prevPos = peek$(ctx, `containerPosition${containerIndex}`);
3672
+ const prevColumn = peek$(ctx, `containerColumn${containerIndex}`);
3673
+ const prevSpan = peek$(ctx, `containerSpan${containerIndex}`);
3674
+ if (position > POSITION_OUT_OF_VIEW && position !== prevPos) {
3675
+ set$(ctx, `containerPosition${containerIndex}`, position);
3676
+ didChangePosition = true;
3677
+ }
3678
+ if (column >= 0 && column !== prevColumn) {
3679
+ set$(ctx, `containerColumn${containerIndex}`, column);
3680
+ }
3681
+ if (span !== prevSpan) {
3682
+ set$(ctx, `containerSpan${containerIndex}`, span);
3683
+ }
3684
+ }
3685
+ const prevData = peek$(ctx, `containerItemData${containerIndex}`);
3686
+ if (prevData !== item) {
3687
+ const pendingDataComparison = ((_c = state.pendingDataComparison) == null ? void 0 : _c.previousData) === state.previousData && ((_d = state.pendingDataComparison) == null ? void 0 : _d.nextData) === data ? state.pendingDataComparison : void 0;
3688
+ const cachedComparison = (_e = pendingDataComparison == null ? void 0 : pendingDataComparison.byIndex[itemIndex]) != null ? _e : 0;
3689
+ if (cachedComparison === 2) {
3690
+ set$(ctx, `containerItemData${containerIndex}`, item);
3691
+ didRefreshData = true;
3692
+ } else if (cachedComparison !== 1) {
3693
+ const itemKey = (_g = (_f = peek$(ctx, `containerItemKey${containerIndex}`)) != null ? _f : state.idCache[itemIndex]) != null ? _g : getId(state, itemIndex);
3694
+ const prevKey = keyExtractor == null ? void 0 : keyExtractor(prevData, itemIndex);
3695
+ if (prevData === void 0 || !keyExtractor || prevKey !== itemKey) {
3696
+ set$(ctx, `containerItemData${containerIndex}`, item);
3697
+ didRefreshData = true;
3698
+ } else if (!itemsAreEqual) {
3699
+ set$(ctx, `containerItemData${containerIndex}`, item);
3700
+ didRefreshData = true;
3701
+ } else {
3702
+ const isEqual = itemsAreEqual(prevData, item, itemIndex, data);
3703
+ if (!state.pendingDataComparison || state.pendingDataComparison.previousData !== state.previousData || state.pendingDataComparison.nextData !== data) {
3704
+ if (state.previousData) {
3705
+ state.pendingDataComparison = {
3706
+ byIndex: [],
3707
+ nextData: data,
3708
+ previousData: state.previousData
3709
+ };
3710
+ }
3711
+ }
3712
+ if ((_h = state.pendingDataComparison) == null ? void 0 : _h.byIndex) {
3713
+ state.pendingDataComparison.byIndex[itemIndex] = isEqual ? 1 : 2;
3714
+ }
3715
+ if (!isEqual) {
3716
+ set$(ctx, `containerItemData${containerIndex}`, item);
3717
+ didRefreshData = true;
2412
3718
  }
2413
3719
  }
2414
- };
2415
- state.scroll += positionDiff;
2416
- state.scrollForNextCalculateItemsInView = void 0;
2417
- const readyToRender = peek$(ctx, "readyToRender");
2418
- if (readyToRender) {
2419
- doit();
2420
- } else {
2421
- state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
2422
- requestAnimationFrame(doit);
2423
3720
  }
2424
3721
  }
3722
+ return { didChangePosition, didRefreshData };
2425
3723
  }
2426
3724
 
2427
3725
  // src/core/prepareColumnStartState.ts
@@ -2586,9 +3884,10 @@ function updateSnapToOffsets(ctx) {
2586
3884
  }
2587
3885
 
2588
3886
  // src/core/updateItemPositions.ts
2589
- function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP } = {
3887
+ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP, optimizeForVisibleWindow = false } = {
2590
3888
  doMVCP: false,
2591
3889
  forceFullUpdate: false,
3890
+ optimizeForVisibleWindow: false,
2592
3891
  scrollBottomBuffered: -1,
2593
3892
  startIndex: 0
2594
3893
  }) {
@@ -2613,7 +3912,7 @@ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffere
2613
3912
  const layoutConfig = overrideItemLayout ? { span: 1 } : void 0;
2614
3913
  const lastScrollDelta = state.lastScrollDelta;
2615
3914
  const velocity = getScrollVelocity(state);
2616
- const shouldOptimize = !forceFullUpdate && !dataChanged && (Math.abs(velocity) > 0 || state.scrollLength > 0 && lastScrollDelta > state.scrollLength);
3915
+ const shouldOptimize = !forceFullUpdate && !dataChanged && (optimizeForVisibleWindow || Math.abs(velocity) > 0 || state.scrollLength > 0 && lastScrollDelta > state.scrollLength);
2617
3916
  const maxVisibleArea = scrollBottomBuffered + 1e3;
2618
3917
  const useAverageSize = !getEstimatedItemSize;
2619
3918
  const preferCachedSize = !doMVCP || dataChanged || state.scrollAdjustHandler.getAdjust() !== 0 || ((_b = peek$(ctx, "scrollAdjustPending")) != null ? _b : 0) !== 0;
@@ -2728,7 +4027,15 @@ function ensureViewabilityState(ctx, configId) {
2728
4027
  }
2729
4028
  let state = map.get(configId);
2730
4029
  if (!state) {
2731
- state = { end: -1, previousEnd: -1, previousStart: -1, start: -1, viewableItems: [] };
4030
+ state = {
4031
+ end: -1,
4032
+ endBuffered: -1,
4033
+ previousEnd: -1,
4034
+ previousStart: -1,
4035
+ start: -1,
4036
+ startBuffered: -1,
4037
+ viewableItems: []
4038
+ };
2732
4039
  map.set(configId, state);
2733
4040
  }
2734
4041
  return state;
@@ -2748,7 +4055,7 @@ function setupViewability(props) {
2748
4055
  }
2749
4056
  return viewabilityConfigCallbackPairs;
2750
4057
  }
2751
- function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollSize, start, end) {
4058
+ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollSize, start, end, startBuffered = start, endBuffered = end) {
2752
4059
  const {
2753
4060
  timeouts,
2754
4061
  props: { data }
@@ -2757,6 +4064,8 @@ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollS
2757
4064
  const viewabilityState = ensureViewabilityState(ctx, viewabilityConfigCallbackPair.viewabilityConfig.id);
2758
4065
  viewabilityState.start = start;
2759
4066
  viewabilityState.end = end;
4067
+ viewabilityState.startBuffered = startBuffered;
4068
+ viewabilityState.endBuffered = endBuffered;
2760
4069
  if (viewabilityConfigCallbackPair.viewabilityConfig.minimumViewTime) {
2761
4070
  const timer = setTimeout(() => {
2762
4071
  timeouts.delete(timer);
@@ -2772,7 +4081,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
2772
4081
  const { viewabilityConfig, onViewableItemsChanged } = viewabilityConfigCallbackPair;
2773
4082
  const configId = viewabilityConfig.id;
2774
4083
  const viewabilityState = ensureViewabilityState(ctx, configId);
2775
- const { viewableItems: previousViewableItems, start, end } = viewabilityState;
4084
+ const { viewableItems: previousViewableItems, start, end, startBuffered, endBuffered } = viewabilityState;
2776
4085
  const viewabilityTokens = /* @__PURE__ */ new Map();
2777
4086
  for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
2778
4087
  viewabilityTokens.set(
@@ -2841,7 +4150,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
2841
4150
  maybeUpdateViewabilityCallback(ctx, configId, change.containerId, change);
2842
4151
  }
2843
4152
  if (onViewableItemsChanged) {
2844
- onViewableItemsChanged({ changed, viewableItems });
4153
+ onViewableItemsChanged({ changed, end, endBuffered, start, startBuffered, viewableItems });
2845
4154
  }
2846
4155
  }
2847
4156
  for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
@@ -2937,23 +4246,6 @@ function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
2937
4246
  var unstableBatchedUpdates = ReactDOM__namespace.unstable_batchedUpdates;
2938
4247
  var batchedUpdates = typeof unstableBatchedUpdates === "function" ? unstableBatchedUpdates : (fn) => fn();
2939
4248
 
2940
- // src/utils/checkAllSizesKnown.ts
2941
- function isNullOrUndefined2(value) {
2942
- return value === null || value === void 0;
2943
- }
2944
- function checkAllSizesKnown(state) {
2945
- const { startBuffered, endBuffered, sizesKnown } = state;
2946
- if (!isNullOrUndefined2(endBuffered) && !isNullOrUndefined2(startBuffered) && startBuffered >= 0 && endBuffered >= 0) {
2947
- let areAllKnown = true;
2948
- for (let i = startBuffered; areAllKnown && i <= endBuffered; i++) {
2949
- const key = getId(state, i);
2950
- areAllKnown && (areAllKnown = sizesKnown.has(key));
2951
- }
2952
- return areAllKnown;
2953
- }
2954
- return false;
2955
- }
2956
-
2957
4249
  // src/utils/findAvailableContainers.ts
2958
4250
  function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers) {
2959
4251
  const numContainers = peek$(ctx, "numContainers");
@@ -3076,97 +4368,11 @@ function comparatorByDistance(a, b) {
3076
4368
  return b.distance - a.distance;
3077
4369
  }
3078
4370
 
3079
- // src/core/scrollToIndex.ts
3080
- function scrollToIndex(ctx, {
3081
- index,
3082
- viewOffset = 0,
3083
- animated = true,
3084
- forceScroll,
3085
- isInitialScroll,
3086
- viewPosition
3087
- }) {
3088
- const state = ctx.state;
3089
- const { data } = state.props;
3090
- if (index >= data.length) {
3091
- index = data.length - 1;
3092
- } else if (index < 0) {
3093
- index = 0;
3094
- }
3095
- const firstIndexOffset = calculateOffsetForIndex(ctx, index);
3096
- const isLast = index === data.length - 1;
3097
- if (isLast && viewPosition === void 0) {
3098
- viewPosition = 1;
3099
- }
3100
- state.scrollForNextCalculateItemsInView = void 0;
3101
- const targetId = getId(state, index);
3102
- const itemSize = getItemSize(ctx, targetId, index, state.props.data[index]);
3103
- scrollTo(ctx, {
3104
- animated,
3105
- forceScroll,
3106
- index,
3107
- isInitialScroll,
3108
- itemSize,
3109
- offset: firstIndexOffset,
3110
- viewOffset,
3111
- viewPosition: viewPosition != null ? viewPosition : 0
3112
- });
3113
- }
3114
-
3115
- // src/utils/performInitialScroll.ts
3116
- function performInitialScroll(ctx, params) {
3117
- var _a3;
3118
- const { forceScroll, initialScrollUsesOffset, resolvedOffset, target } = params;
3119
- if (initialScrollUsesOffset || resolvedOffset !== void 0) {
3120
- scrollTo(ctx, {
3121
- animated: false,
3122
- forceScroll,
3123
- index: initialScrollUsesOffset ? void 0 : target.index,
3124
- isInitialScroll: true,
3125
- offset: (_a3 = resolvedOffset != null ? resolvedOffset : target.contentOffset) != null ? _a3 : 0,
3126
- precomputedWithViewOffset: resolvedOffset !== void 0
3127
- });
3128
- return;
3129
- }
3130
- if (target.index === void 0) {
3131
- return;
3132
- }
3133
- scrollToIndex(ctx, {
3134
- ...target,
3135
- animated: false,
3136
- forceScroll,
3137
- isInitialScroll: true
3138
- });
3139
- }
3140
-
3141
4371
  // src/utils/setDidLayout.ts
3142
4372
  function setDidLayout(ctx) {
3143
4373
  const state = ctx.state;
3144
- const { initialScroll } = state;
3145
4374
  state.queuedInitialLayout = true;
3146
4375
  checkAtBottom(ctx);
3147
- if (initialScroll) {
3148
- const runScroll = () => {
3149
- var _a3, _b;
3150
- const target = state.initialScroll;
3151
- if (!target) {
3152
- return;
3153
- }
3154
- const activeInitialTargetOffset = ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) ? (_b = state.scrollingTo.targetOffset) != null ? _b : state.scrollingTo.offset : void 0;
3155
- const desiredInitialTargetOffset = state.initialScrollUsesOffset ? target.contentOffset : activeInitialTargetOffset;
3156
- const isAlreadyAtDesiredInitialTarget = desiredInitialTargetOffset !== void 0 && Math.abs(state.scroll - desiredInitialTargetOffset) <= 1 && Math.abs(state.scrollPending - desiredInitialTargetOffset) <= 1;
3157
- if (!isAlreadyAtDesiredInitialTarget) {
3158
- performInitialScroll(ctx, {
3159
- forceScroll: true,
3160
- initialScrollUsesOffset: state.initialScrollUsesOffset,
3161
- // Offset-based initial scrolls do not need item lookup, so they can run even before data exists.
3162
- // Re-run on the next frame to pick up measured viewport size without waiting for index resolution.
3163
- target
3164
- });
3165
- }
3166
- };
3167
- runScroll();
3168
- requestAnimationFrame(runScroll);
3169
- }
3170
4376
  setInitialRenderState(ctx, { didLayout: true });
3171
4377
  }
3172
4378
 
@@ -3241,7 +4447,7 @@ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentSt
3241
4447
  function calculateItemsInView(ctx, params = {}) {
3242
4448
  const state = ctx.state;
3243
4449
  batchedUpdates(() => {
3244
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
4450
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
3245
4451
  const {
3246
4452
  columns,
3247
4453
  columnSpans,
@@ -3249,7 +4455,6 @@ function calculateItemsInView(ctx, params = {}) {
3249
4455
  enableScrollForNextCalculateItemsInView,
3250
4456
  idCache,
3251
4457
  indexByKey,
3252
- initialScroll,
3253
4458
  minIndexSizeChanged,
3254
4459
  positions,
3255
4460
  props: {
@@ -3257,7 +4462,6 @@ function calculateItemsInView(ctx, params = {}) {
3257
4462
  alwaysRenderIndicesSet,
3258
4463
  drawDistance,
3259
4464
  getItemType,
3260
- itemsAreEqual,
3261
4465
  keyExtractor,
3262
4466
  onStickyHeaderChange
3263
4467
  },
@@ -3273,6 +4477,8 @@ function calculateItemsInView(ctx, params = {}) {
3273
4477
  const alwaysRenderArr = alwaysRenderIndicesArr || [];
3274
4478
  const alwaysRenderSet = alwaysRenderIndicesSet || /* @__PURE__ */ new Set();
3275
4479
  const { dataChanged, doMVCP, forceFullItemPositions } = params;
4480
+ const bootstrapInitialScrollState = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" ? state.initialScrollSession.bootstrap : void 0;
4481
+ const suppressInitialScrollSideEffects = !!bootstrapInitialScrollState;
3276
4482
  const prevNumContainers = peek$(ctx, "numContainers");
3277
4483
  if (!data || scrollLength === 0 || !prevNumContainers) {
3278
4484
  return;
@@ -3282,17 +4488,13 @@ function calculateItemsInView(ctx, params = {}) {
3282
4488
  const numColumns = peek$(ctx, "numColumns");
3283
4489
  const speed = getScrollVelocity(state);
3284
4490
  const scrollExtra = 0;
3285
- const { queuedInitialLayout } = state;
3286
- let { scroll: scrollState } = state;
3287
- if (!queuedInitialLayout && initialScroll) {
3288
- const updatedOffset = state.initialScrollUsesOffset ? (_a3 = initialScroll.contentOffset) != null ? _a3 : 0 : calculateOffsetWithOffsetPosition(
3289
- ctx,
3290
- calculateOffsetForIndex(ctx, initialScroll.index),
3291
- initialScroll
3292
- );
3293
- scrollState = updatedOffset;
3294
- }
3295
- const scrollAdjustPending = (_b = peek$(ctx, "scrollAdjustPending")) != null ? _b : 0;
4491
+ const { initialScroll, queuedInitialLayout } = state;
4492
+ const scrollState = suppressInitialScrollSideEffects ? (_b = bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.scroll) != null ? _b : state.scroll : !queuedInitialLayout && hasActiveInitialScroll(state) && initialScroll ? (
4493
+ // Before the initial layout settles, keep viewport math anchored to the
4494
+ // current initial-scroll target instead of transient native adjustments.
4495
+ resolveInitialScrollOffset(ctx, initialScroll)
4496
+ ) : state.scroll;
4497
+ const scrollAdjustPending = (_c = peek$(ctx, "scrollAdjustPending")) != null ? _c : 0;
3296
4498
  const scrollAdjustPad = scrollAdjustPending - topPad;
3297
4499
  let scroll = Math.round(scrollState + scrollExtra + scrollAdjustPad);
3298
4500
  if (scroll + scrollLength > totalSize) {
@@ -3316,7 +4518,7 @@ function calculateItemsInView(ctx, params = {}) {
3316
4518
  const scrollTopBuffered = scroll - scrollBufferTop;
3317
4519
  const scrollBottom = scroll + scrollLength + (scroll < 0 ? -scroll : 0);
3318
4520
  const scrollBottomBuffered = scrollBottom + scrollBufferBottom;
3319
- if (!dataChanged && !forceFullItemPositions && scrollForNextCalculateItemsInView) {
4521
+ if (!suppressInitialScrollSideEffects && !dataChanged && !forceFullItemPositions && scrollForNextCalculateItemsInView) {
3320
4522
  const { top, bottom } = scrollForNextCalculateItemsInView;
3321
4523
  if (top === null && bottom === null) {
3322
4524
  state.scrollForNextCalculateItemsInView = void 0;
@@ -3326,7 +4528,7 @@ function calculateItemsInView(ctx, params = {}) {
3326
4528
  }
3327
4529
  }
3328
4530
  }
3329
- const checkMVCP = doMVCP ? prepareMVCP(ctx, dataChanged) : void 0;
4531
+ const checkMVCP = doMVCP && !suppressInitialScrollSideEffects ? prepareMVCP(ctx, dataChanged) : void 0;
3330
4532
  if (dataChanged) {
3331
4533
  indexByKey.clear();
3332
4534
  idCache.length = 0;
@@ -3334,10 +4536,12 @@ function calculateItemsInView(ctx, params = {}) {
3334
4536
  columns.length = 0;
3335
4537
  columnSpans.length = 0;
3336
4538
  }
3337
- const startIndex = forceFullItemPositions || dataChanged ? 0 : (_c = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _c : 0;
4539
+ const startIndex = forceFullItemPositions || dataChanged ? 0 : (_d = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _d : 0;
4540
+ const optimizeForVisibleWindow = !forceFullItemPositions && !dataChanged && numColumns > 1 && minIndexSizeChanged !== void 0;
3338
4541
  updateItemPositions(ctx, dataChanged, {
3339
4542
  doMVCP,
3340
4543
  forceFullUpdate: !!forceFullItemPositions,
4544
+ optimizeForVisibleWindow,
3341
4545
  scrollBottomBuffered,
3342
4546
  startIndex
3343
4547
  });
@@ -3351,11 +4555,11 @@ function calculateItemsInView(ctx, params = {}) {
3351
4555
  let startBufferedId = null;
3352
4556
  let endNoBuffer = null;
3353
4557
  let endBuffered = null;
3354
- let loopStart = !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
4558
+ let loopStart = (_e = suppressInitialScrollSideEffects ? bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.targetIndexSeed : void 0) != null ? _e : !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
3355
4559
  for (let i = loopStart; i >= 0; i--) {
3356
- const id = (_d = idCache[i]) != null ? _d : getId(state, i);
4560
+ const id = (_f = idCache[i]) != null ? _f : getId(state, i);
3357
4561
  const top = positions[i];
3358
- const size = (_e = sizes.get(id)) != null ? _e : getItemSize(ctx, id, i, data[i]);
4562
+ const size = (_g = sizes.get(id)) != null ? _g : getItemSize(ctx, id, i, data[i]);
3359
4563
  const bottom = top + size;
3360
4564
  if (bottom > scroll - scrollBufferTop) {
3361
4565
  loopStart = i;
@@ -3386,8 +4590,8 @@ function calculateItemsInView(ctx, params = {}) {
3386
4590
  let firstFullyOnScreenIndex;
3387
4591
  const dataLength = data.length;
3388
4592
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
3389
- const id = (_f = idCache[i]) != null ? _f : getId(state, i);
3390
- const size = (_g = sizes.get(id)) != null ? _g : getItemSize(ctx, id, i, data[i]);
4593
+ const id = (_h = idCache[i]) != null ? _h : getId(state, i);
4594
+ const size = (_i = sizes.get(id)) != null ? _i : getItemSize(ctx, id, i, data[i]);
3391
4595
  const top = positions[i];
3392
4596
  if (!foundEnd) {
3393
4597
  if (startNoBuffer === null && top + size > scroll) {
@@ -3426,7 +4630,7 @@ function calculateItemsInView(ctx, params = {}) {
3426
4630
  const firstVisibleAnchorIndex = firstFullyOnScreenIndex != null ? firstFullyOnScreenIndex : startNoBuffer;
3427
4631
  if (firstVisibleAnchorIndex !== null && firstVisibleAnchorIndex !== void 0 && endNoBuffer !== null) {
3428
4632
  for (let i = firstVisibleAnchorIndex; i <= endNoBuffer; i++) {
3429
- const id = (_h = idCache[i]) != null ? _h : getId(state, i);
4633
+ const id = (_j = idCache[i]) != null ? _j : getId(state, i);
3430
4634
  idsInView.push(id);
3431
4635
  }
3432
4636
  }
@@ -3459,7 +4663,7 @@ function calculateItemsInView(ctx, params = {}) {
3459
4663
  const needNewContainers = [];
3460
4664
  const needNewContainersSet = /* @__PURE__ */ new Set();
3461
4665
  for (let i = startBuffered; i <= endBuffered; i++) {
3462
- const id = (_i = idCache[i]) != null ? _i : getId(state, i);
4666
+ const id = (_k = idCache[i]) != null ? _k : getId(state, i);
3463
4667
  if (!containerItemKeys.has(id)) {
3464
4668
  needNewContainersSet.add(i);
3465
4669
  needNewContainers.push(i);
@@ -3468,7 +4672,7 @@ function calculateItemsInView(ctx, params = {}) {
3468
4672
  if (alwaysRenderArr.length > 0) {
3469
4673
  for (const index of alwaysRenderArr) {
3470
4674
  if (index < 0 || index >= dataLength) continue;
3471
- const id = (_j = idCache[index]) != null ? _j : getId(state, index);
4675
+ const id = (_l = idCache[index]) != null ? _l : getId(state, index);
3472
4676
  if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
3473
4677
  needNewContainersSet.add(index);
3474
4678
  needNewContainers.push(index);
@@ -3506,7 +4710,7 @@ function calculateItemsInView(ctx, params = {}) {
3506
4710
  for (let idx = 0; idx < needNewContainers.length; idx++) {
3507
4711
  const i = needNewContainers[idx];
3508
4712
  const containerIndex = availableContainers[idx];
3509
- const id = (_k = idCache[i]) != null ? _k : getId(state, i);
4713
+ const id = (_m = idCache[i]) != null ? _m : getId(state, i);
3510
4714
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
3511
4715
  if (oldKey && oldKey !== id) {
3512
4716
  containerItemKeys.delete(oldKey);
@@ -3547,7 +4751,7 @@ function calculateItemsInView(ctx, params = {}) {
3547
4751
  if (alwaysRenderArr.length > 0) {
3548
4752
  for (const index of alwaysRenderArr) {
3549
4753
  if (index < 0 || index >= dataLength) continue;
3550
- const id = (_l = idCache[index]) != null ? _l : getId(state, index);
4754
+ const id = (_n = idCache[index]) != null ? _n : getId(state, index);
3551
4755
  const containerIndex = containerItemKeys.get(id);
3552
4756
  if (containerIndex !== void 0) {
3553
4757
  state.stickyContainerPool.add(containerIndex);
@@ -3585,46 +4789,39 @@ function calculateItemsInView(ctx, params = {}) {
3585
4789
  set$(ctx, `containerSpan${i}`, 1);
3586
4790
  } else {
3587
4791
  const itemIndex = indexByKey.get(itemKey);
3588
- const item = data[itemIndex];
3589
- if (item !== void 0) {
3590
- const positionValue = positions[itemIndex];
3591
- if (positionValue === void 0) {
3592
- set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
3593
- } else {
3594
- const position = (positionValue || 0) - scrollAdjustPending;
3595
- const column = columns[itemIndex] || 1;
3596
- const span = columnSpans[itemIndex] || 1;
3597
- const prevPos = peek$(ctx, `containerPosition${i}`);
3598
- const prevColumn = peek$(ctx, `containerColumn${i}`);
3599
- const prevSpan = peek$(ctx, `containerSpan${i}`);
3600
- const prevData = peek$(ctx, `containerItemData${i}`);
3601
- if (position > POSITION_OUT_OF_VIEW && position !== prevPos) {
3602
- set$(ctx, `containerPosition${i}`, position);
3603
- didChangePositions = true;
3604
- }
3605
- if (column >= 0 && column !== prevColumn) {
3606
- set$(ctx, `containerColumn${i}`, column);
3607
- }
3608
- if (span !== prevSpan) {
3609
- set$(ctx, `containerSpan${i}`, span);
3610
- }
3611
- if (prevData !== item && (itemsAreEqual ? !itemsAreEqual(prevData, item, itemIndex, data) : true)) {
3612
- set$(ctx, `containerItemData${i}`, item);
3613
- }
3614
- }
4792
+ if (itemIndex !== void 0) {
4793
+ didChangePositions = syncMountedContainer(ctx, i, itemIndex, {
4794
+ scrollAdjustPending,
4795
+ updateLayout: true
4796
+ }).didChangePosition || didChangePositions;
3615
4797
  }
3616
4798
  }
3617
4799
  }
3618
4800
  if (didChangePositions) {
3619
4801
  set$(ctx, "lastPositionUpdate", Date.now());
3620
4802
  }
3621
- if (!queuedInitialLayout && endBuffered !== null) {
3622
- if (checkAllSizesKnown(state)) {
3623
- setDidLayout(ctx);
3624
- }
4803
+ if (suppressInitialScrollSideEffects) {
4804
+ evaluateBootstrapInitialScroll(ctx);
4805
+ return;
4806
+ }
4807
+ const mountedBufferedIndices = getMountedBufferedIndices(state);
4808
+ const mountedNoBufferIndices = getMountedNoBufferIndices(state);
4809
+ const readinessIndices = hasActiveInitialScroll(state) ? mountedBufferedIndices : mountedNoBufferIndices.length > 0 ? mountedNoBufferIndices : mountedBufferedIndices;
4810
+ if (!queuedInitialLayout && readinessIndices.length > 0 && checkAllSizesKnown(state, readinessIndices)) {
4811
+ setDidLayout(ctx);
4812
+ handleInitialScrollLayoutReady(ctx);
3625
4813
  }
3626
- if (viewabilityConfigCallbackPairs) {
3627
- updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollLength, startNoBuffer, endNoBuffer);
4814
+ if (viewabilityConfigCallbackPairs && startNoBuffer !== null && endNoBuffer !== null) {
4815
+ updateViewableItems(
4816
+ ctx.state,
4817
+ ctx,
4818
+ viewabilityConfigCallbackPairs,
4819
+ scrollLength,
4820
+ startNoBuffer,
4821
+ endNoBuffer,
4822
+ startBuffered != null ? startBuffered : startNoBuffer,
4823
+ endBuffered != null ? endBuffered : endNoBuffer
4824
+ );
3628
4825
  }
3629
4826
  if (onStickyHeaderChange && stickyIndicesArr.length > 0 && nextActiveStickyIndex !== void 0 && nextActiveStickyIndex !== previousStickyIndex) {
3630
4827
  const item = data[nextActiveStickyIndex];
@@ -3635,163 +4832,63 @@ function calculateItemsInView(ctx, params = {}) {
3635
4832
  });
3636
4833
  }
3637
4834
 
3638
- // src/core/checkActualChange.ts
3639
- function checkActualChange(state, dataProp, previousData) {
3640
- if (!previousData || !dataProp || dataProp.length !== previousData.length) {
3641
- return true;
3642
- }
3643
- const {
3644
- idCache,
3645
- props: { keyExtractor }
3646
- } = state;
3647
- for (let i = 0; i < dataProp.length; i++) {
3648
- if (dataProp[i] !== previousData[i]) {
3649
- return true;
3650
- }
3651
- if (keyExtractor ? idCache[i] !== keyExtractor(previousData[i], i) : dataProp[i] !== previousData[i]) {
3652
- return true;
3653
- }
3654
- }
3655
- return false;
3656
- }
3657
-
3658
- // src/core/checkFinishedScroll.ts
3659
- var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
3660
- var INITIAL_SCROLL_MAX_FALLBACK_CHECKS = 20;
3661
- var INITIAL_SCROLL_ZERO_TARGET_EPSILON = 1;
3662
- function checkFinishedScroll(ctx) {
3663
- ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
3664
- }
3665
- function checkFinishedScrollFrame(ctx) {
3666
- var _a3;
3667
- const scrollingTo = ctx.state.scrollingTo;
3668
- if (scrollingTo) {
3669
- const { state } = ctx;
3670
- state.animFrameCheckFinishedScroll = void 0;
3671
- const scroll = state.scrollPending;
3672
- const adjust = state.scrollAdjustHandler.getAdjust();
3673
- const clampedTargetOffset = (_a3 = scrollingTo.targetOffset) != null ? _a3 : clampScrollOffset(ctx, scrollingTo.offset - (scrollingTo.viewOffset || 0), scrollingTo);
3674
- const maxOffset = clampScrollOffset(ctx, scroll, scrollingTo);
3675
- const diff1 = Math.abs(scroll - clampedTargetOffset);
3676
- const diff2 = Math.abs(diff1 - adjust);
3677
- const isNotOverscrolled = Math.abs(scroll - maxOffset) < 1;
3678
- const isAtTarget = diff1 < 1 || !scrollingTo.animated && diff2 < 1;
3679
- if (isNotOverscrolled && isAtTarget) {
3680
- finishScrollTo(ctx);
3681
- }
3682
- }
3683
- }
3684
- function checkFinishedScrollFallback(ctx) {
4835
+ // src/core/doMaintainScrollAtEnd.ts
4836
+ function doMaintainScrollAtEnd(ctx) {
3685
4837
  const state = ctx.state;
3686
- const scrollingTo = state.scrollingTo;
3687
- const shouldFinishInitialZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
3688
- const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && !shouldFinishInitialZeroTarget || !state.didContainersLayout;
3689
- state.timeoutCheckFinishedScrollFallback = setTimeout(
3690
- () => {
3691
- let numChecks = 0;
3692
- const checkHasScrolled = () => {
3693
- var _a3, _b;
3694
- state.timeoutCheckFinishedScrollFallback = void 0;
3695
- const isStillScrollingTo = state.scrollingTo;
3696
- if (isStillScrollingTo) {
3697
- numChecks++;
3698
- const isNativeInitialPending = isNativeInitialNonZeroTarget(state) && !state.hasScrolled;
3699
- const maxChecks = isNativeInitialPending ? INITIAL_SCROLL_MAX_FALLBACK_CHECKS : 5;
3700
- const shouldFinishZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
3701
- if (shouldFinishZeroTarget || state.hasScrolled || numChecks > maxChecks) {
3702
- finishScrollTo(ctx);
3703
- } else if (isNativeInitialPending && numChecks <= maxChecks) {
3704
- const targetOffset = (_b = (_a3 = state.initialNativeScrollWatchdog) == null ? void 0 : _a3.targetOffset) != null ? _b : state.scrollPending;
3705
- const scroller = state.refScroller.current;
3706
- if (scroller) {
3707
- scroller.scrollTo({
3708
- animated: false,
3709
- x: state.props.horizontal ? targetOffset : 0,
3710
- y: state.props.horizontal ? 0 : targetOffset
3711
- });
3712
- }
3713
- state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, 100);
3714
- } else {
3715
- state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, 100);
3716
- }
3717
- }
3718
- };
3719
- checkHasScrolled();
3720
- },
3721
- slowTimeout ? 500 : 100
3722
- );
3723
- }
3724
- function isNativeInitialNonZeroTarget(state) {
3725
- return !state.didFinishInitialScroll && !!state.initialNativeScrollWatchdog && state.initialNativeScrollWatchdog.targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
3726
- }
3727
- function shouldFinishInitialZeroTargetScroll(ctx) {
3728
- var _a3;
3729
- const { state } = ctx;
3730
- return !!((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) && state.props.data.length > 0 && getContentSize(ctx) <= state.scrollLength && state.scrollPending <= INITIAL_SCROLL_ZERO_TARGET_EPSILON;
3731
- }
3732
-
3733
- // src/utils/updateAveragesOnDataChange.ts
3734
- function updateAveragesOnDataChange(state, oldData, newData) {
3735
- var _a3;
3736
4838
  const {
3737
- averageSizes,
3738
- sizesKnown,
3739
- indexByKey,
3740
- props: { itemsAreEqual, getItemType, keyExtractor }
4839
+ didContainersLayout,
4840
+ pendingNativeMVCPAdjust,
4841
+ refScroller,
4842
+ props: { maintainScrollAtEnd }
3741
4843
  } = state;
3742
- if (!itemsAreEqual || !oldData.length || !newData.length) {
3743
- for (const key in averageSizes) {
3744
- delete averageSizes[key];
3745
- }
3746
- return;
3747
- }
3748
- const itemTypesToPreserve = {};
3749
- const newDataLength = newData.length;
3750
- const oldDataLength = oldData.length;
3751
- for (let newIndex = 0; newIndex < newDataLength; newIndex++) {
3752
- const newItem = newData[newIndex];
3753
- const id = keyExtractor ? keyExtractor(newItem, newIndex) : String(newIndex);
3754
- const oldIndex = indexByKey.get(id);
3755
- if (oldIndex !== void 0 && oldIndex < oldDataLength) {
3756
- const knownSize = sizesKnown.get(id);
3757
- if (knownSize === void 0) continue;
3758
- const oldItem = oldData[oldIndex];
3759
- const areEqual = itemsAreEqual(oldItem, newItem, newIndex, newData);
3760
- if (areEqual) {
3761
- const itemType = getItemType ? (_a3 = getItemType(newItem, newIndex)) != null ? _a3 : "" : "";
3762
- let typeData = itemTypesToPreserve[itemType];
3763
- if (!typeData) {
3764
- typeData = itemTypesToPreserve[itemType] = { count: 0, totalSize: 0 };
3765
- }
3766
- typeData.totalSize += knownSize;
3767
- typeData.count++;
3768
- }
3769
- }
3770
- }
3771
- for (const key in averageSizes) {
3772
- delete averageSizes[key];
4844
+ const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
4845
+ const shouldMaintainScrollAtEnd = !!(isWithinMaintainScrollAtEndThreshold && maintainScrollAtEnd && didContainersLayout);
4846
+ if (pendingNativeMVCPAdjust) {
4847
+ state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
4848
+ return false;
3773
4849
  }
3774
- for (const itemType in itemTypesToPreserve) {
3775
- const { totalSize, count } = itemTypesToPreserve[itemType];
3776
- if (count > 0) {
3777
- averageSizes[itemType] = {
3778
- avg: totalSize / count,
3779
- num: count
3780
- };
4850
+ state.pendingMaintainScrollAtEnd = false;
4851
+ if (shouldMaintainScrollAtEnd) {
4852
+ const contentSize = getContentSize(ctx);
4853
+ if (contentSize < state.scrollLength) {
4854
+ state.scroll = 0;
3781
4855
  }
4856
+ requestAnimationFrame(() => {
4857
+ var _a3;
4858
+ if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
4859
+ state.maintainingScrollAtEnd = true;
4860
+ (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
4861
+ animated: maintainScrollAtEnd.animated
4862
+ });
4863
+ setTimeout(
4864
+ () => {
4865
+ state.maintainingScrollAtEnd = false;
4866
+ },
4867
+ maintainScrollAtEnd.animated ? 500 : 0
4868
+ );
4869
+ }
4870
+ });
4871
+ return true;
3782
4872
  }
4873
+ return false;
3783
4874
  }
3784
4875
 
3785
4876
  // src/core/checkResetContainers.ts
3786
- function checkResetContainers(ctx, dataProp) {
4877
+ function checkResetContainers(ctx, dataProp, { didColumnsChange = false } = {}) {
3787
4878
  const state = ctx.state;
3788
4879
  const { previousData } = state;
3789
- if (previousData) {
3790
- updateAveragesOnDataChange(state, previousData, dataProp);
3791
- }
3792
4880
  const { maintainScrollAtEnd } = state.props;
4881
+ if (didColumnsChange) {
4882
+ state.sizes.clear();
4883
+ state.sizesKnown.clear();
4884
+ for (const key in state.averageSizes) {
4885
+ delete state.averageSizes[key];
4886
+ }
4887
+ state.minIndexSizeChanged = 0;
4888
+ state.scrollForNextCalculateItemsInView = void 0;
4889
+ }
3793
4890
  calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
3794
- const shouldMaintainScrollAtEnd = maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onDataChange;
4891
+ const shouldMaintainScrollAtEnd = !didColumnsChange && (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onDataChange);
3795
4892
  const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx);
3796
4893
  if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
3797
4894
  state.isEndReached = false;
@@ -3799,7 +4896,54 @@ function checkResetContainers(ctx, dataProp) {
3799
4896
  if (!didMaintainScrollAtEnd) {
3800
4897
  checkThresholds(ctx);
3801
4898
  }
3802
- delete state.previousData;
4899
+ delete state.previousData;
4900
+ }
4901
+
4902
+ // src/core/checkStructuralDataChange.ts
4903
+ function checkStructuralDataChange(state, dataProp, previousData) {
4904
+ var _a3;
4905
+ state.pendingDataComparison = void 0;
4906
+ if (!previousData || !dataProp || dataProp.length !== previousData.length) {
4907
+ return true;
4908
+ }
4909
+ const {
4910
+ idCache,
4911
+ props: { itemsAreEqual, keyExtractor }
4912
+ } = state;
4913
+ let byIndex;
4914
+ for (let i = 0; i < dataProp.length; i++) {
4915
+ if (dataProp[i] === previousData[i]) {
4916
+ continue;
4917
+ }
4918
+ if (!keyExtractor) {
4919
+ if (byIndex) {
4920
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4921
+ }
4922
+ return true;
4923
+ }
4924
+ const previousKey = (_a3 = idCache[i]) != null ? _a3 : keyExtractor(previousData[i], i);
4925
+ const nextKey = keyExtractor(dataProp[i], i);
4926
+ if (previousKey !== nextKey) {
4927
+ if (byIndex) {
4928
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4929
+ }
4930
+ return true;
4931
+ }
4932
+ if (!itemsAreEqual) {
4933
+ if (byIndex) {
4934
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4935
+ }
4936
+ return true;
4937
+ }
4938
+ const isEqual = itemsAreEqual(previousData[i], dataProp[i], i, dataProp);
4939
+ byIndex != null ? byIndex : byIndex = [];
4940
+ byIndex[i] = isEqual ? 1 : 2;
4941
+ if (!isEqual) {
4942
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4943
+ return true;
4944
+ }
4945
+ }
4946
+ return false;
3803
4947
  }
3804
4948
 
3805
4949
  // src/core/doInitialAllocateContainers.ts
@@ -3917,20 +5061,102 @@ function handleLayout(ctx, layoutParam, setCanRender) {
3917
5061
  setCanRender(true);
3918
5062
  }
3919
5063
 
5064
+ // src/core/updateScroll.ts
5065
+ function updateScroll(ctx, newScroll, forceUpdate) {
5066
+ var _a3;
5067
+ const state = ctx.state;
5068
+ const { ignoreScrollFromMVCP, lastScrollAdjustForHistory, scrollAdjustHandler, scrollHistory, scrollingTo } = state;
5069
+ const prevScroll = state.scroll;
5070
+ state.hasScrolled = true;
5071
+ state.lastBatchingAction = Date.now();
5072
+ const currentTime = Date.now();
5073
+ const adjust = scrollAdjustHandler.getAdjust();
5074
+ const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
5075
+ if (adjustChanged) {
5076
+ scrollHistory.length = 0;
5077
+ }
5078
+ state.lastScrollAdjustForHistory = adjust;
5079
+ if (scrollingTo === void 0 && !(scrollHistory.length === 0 && newScroll === state.scroll)) {
5080
+ if (!adjustChanged) {
5081
+ scrollHistory.push({ scroll: newScroll, time: currentTime });
5082
+ }
5083
+ }
5084
+ if (scrollHistory.length > 5) {
5085
+ scrollHistory.shift();
5086
+ }
5087
+ if (ignoreScrollFromMVCP && !scrollingTo) {
5088
+ const { lt, gt } = ignoreScrollFromMVCP;
5089
+ if (lt && newScroll < lt || gt && newScroll > gt) {
5090
+ state.ignoreScrollFromMVCPIgnored = true;
5091
+ return;
5092
+ }
5093
+ }
5094
+ state.scrollPrev = prevScroll;
5095
+ state.scrollPrevTime = state.scrollTime;
5096
+ state.scroll = newScroll;
5097
+ state.scrollTime = currentTime;
5098
+ const scrollDelta = Math.abs(newScroll - prevScroll);
5099
+ const didResolvePendingNativeMVCPAdjust = resolvePendingNativeMVCPAdjust(ctx, newScroll);
5100
+ const scrollLength = state.scrollLength;
5101
+ const lastCalculated = state.scrollLastCalculate;
5102
+ const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
5103
+ const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
5104
+ if (shouldUpdate) {
5105
+ state.scrollLastCalculate = state.scroll;
5106
+ state.ignoreScrollFromMVCPIgnored = false;
5107
+ state.lastScrollDelta = scrollDelta;
5108
+ const runCalculateItems = () => {
5109
+ var _a4;
5110
+ (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { doMVCP: scrollingTo !== void 0 });
5111
+ checkThresholds(ctx);
5112
+ };
5113
+ if (scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength) {
5114
+ ReactDOM.flushSync(runCalculateItems);
5115
+ } else {
5116
+ runCalculateItems();
5117
+ }
5118
+ const shouldMaintainScrollAtEndAfterPendingSettle = !!state.pendingMaintainScrollAtEnd || !!((_a3 = state.props.maintainScrollAtEnd) == null ? void 0 : _a3.onDataChange);
5119
+ if (didResolvePendingNativeMVCPAdjust && shouldMaintainScrollAtEndAfterPendingSettle) {
5120
+ state.pendingMaintainScrollAtEnd = false;
5121
+ doMaintainScrollAtEnd(ctx);
5122
+ }
5123
+ state.dataChangeNeedsScrollUpdate = false;
5124
+ state.lastScrollDelta = 0;
5125
+ }
5126
+ }
5127
+
3920
5128
  // src/core/onScroll.ts
3921
- var INITIAL_SCROLL_PROGRESS_EPSILON = 1;
3922
- function didObserveInitialScrollProgress(newScroll, watchdog) {
3923
- const previousDistance = Math.abs(watchdog.startScroll - watchdog.targetOffset);
3924
- const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
3925
- return nextDistance <= INITIAL_SCROLL_PROGRESS_EPSILON || nextDistance + INITIAL_SCROLL_PROGRESS_EPSILON < previousDistance;
5129
+ function trackInitialScrollNativeProgress(state, newScroll) {
5130
+ const initialNativeScrollWatchdog = initialScrollWatchdog.get(state);
5131
+ const didInitialScrollProgress = !!initialNativeScrollWatchdog && initialScrollWatchdog.didObserveProgress(newScroll, initialNativeScrollWatchdog);
5132
+ if (didInitialScrollProgress) {
5133
+ initialScrollWatchdog.clear(state);
5134
+ return;
5135
+ }
5136
+ if (initialNativeScrollWatchdog) {
5137
+ state.hasScrolled = false;
5138
+ initialScrollWatchdog.set(state, {
5139
+ startScroll: initialNativeScrollWatchdog.startScroll,
5140
+ targetOffset: initialNativeScrollWatchdog.targetOffset
5141
+ });
5142
+ }
5143
+ }
5144
+ function shouldDeferPublicOnScroll(state) {
5145
+ var _a3;
5146
+ return !!state.initialScroll && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" && !state.didFinishInitialScroll;
5147
+ }
5148
+ function cloneScrollEvent(event) {
5149
+ return {
5150
+ ...event,
5151
+ nativeEvent: {
5152
+ ...event.nativeEvent
5153
+ }
5154
+ };
3926
5155
  }
3927
5156
  function onScroll(ctx, event) {
3928
5157
  var _a3, _b, _c, _d;
3929
5158
  const state = ctx.state;
3930
- const {
3931
- scrollProcessingEnabled,
3932
- props: { onScroll: onScrollProp }
3933
- } = state;
5159
+ const { scrollProcessingEnabled } = state;
3934
5160
  if (scrollProcessingEnabled === false) {
3935
5161
  return;
3936
5162
  }
@@ -3961,20 +5187,19 @@ function onScroll(ctx, event) {
3961
5187
  }
3962
5188
  }
3963
5189
  state.scrollPending = newScroll;
3964
- const initialNativeScrollWatchdog = state.initialNativeScrollWatchdog;
3965
- const didInitialScrollProgress = !!initialNativeScrollWatchdog && didObserveInitialScrollProgress(newScroll, initialNativeScrollWatchdog);
3966
- if (didInitialScrollProgress) {
3967
- state.initialNativeScrollWatchdog = void 0;
3968
- }
3969
5190
  updateScroll(ctx, newScroll, insetChanged);
3970
- if (initialNativeScrollWatchdog && !didInitialScrollProgress) {
3971
- state.hasScrolled = false;
3972
- state.initialNativeScrollWatchdog = initialNativeScrollWatchdog;
3973
- }
5191
+ trackInitialScrollNativeProgress(state, newScroll);
5192
+ clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx);
3974
5193
  if (state.scrollingTo) {
3975
5194
  checkFinishedScroll(ctx);
3976
5195
  }
3977
- onScrollProp == null ? void 0 : onScrollProp(event);
5196
+ if (state.props.onScroll) {
5197
+ if (shouldDeferPublicOnScroll(state)) {
5198
+ state.deferredPublicOnScrollEvent = cloneScrollEvent(event);
5199
+ } else {
5200
+ state.props.onScroll(event);
5201
+ }
5202
+ }
3978
5203
  }
3979
5204
 
3980
5205
  // src/core/ScrollAdjustHandler.ts
@@ -4028,6 +5253,43 @@ var ScrollAdjustHandler = class {
4028
5253
  }
4029
5254
  };
4030
5255
 
5256
+ // src/core/updateAnchoredEndSpace.ts
5257
+ function maybeUpdateAnchoredEndSpace(ctx) {
5258
+ var _a3;
5259
+ const state = ctx.state;
5260
+ const anchoredEndSpace = state.props.anchoredEndSpace;
5261
+ const previousSize = peek$(ctx, "anchoredEndSpaceSize");
5262
+ let nextSize = 0;
5263
+ if (anchoredEndSpace) {
5264
+ const { anchorIndex, anchorMaxSize, anchorOffset = 0 } = anchoredEndSpace;
5265
+ const { data } = state.props;
5266
+ if (anchorIndex >= 0 && anchorIndex < data.length && state.scrollLength > 0) {
5267
+ let contentBelowAnchor = 0;
5268
+ const footerSize = ctx.values.get("footerSize") || 0;
5269
+ const stylePaddingBottom = state.props.stylePaddingBottom || 0;
5270
+ for (let index = anchorIndex; index < data.length; index++) {
5271
+ const itemKey = getId(state, index);
5272
+ const size = itemKey ? state.sizesKnown.get(itemKey) : void 0;
5273
+ const effectiveSize = index === anchorIndex && anchorMaxSize !== void 0 ? Math.min(size || 0, Math.max(0, anchorMaxSize)) : size;
5274
+ if (effectiveSize !== null && effectiveSize !== void 0 && effectiveSize > 0) {
5275
+ contentBelowAnchor += effectiveSize;
5276
+ }
5277
+ }
5278
+ contentBelowAnchor += footerSize + stylePaddingBottom;
5279
+ nextSize = Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
5280
+ }
5281
+ }
5282
+ if (previousSize === nextSize) {
5283
+ return nextSize;
5284
+ }
5285
+ set$(ctx, "anchoredEndSpaceSize", nextSize);
5286
+ (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
5287
+ if (anchoredEndSpace == null ? void 0 : anchoredEndSpace.includeInEndInset) {
5288
+ updateScroll(ctx, state.scroll, true);
5289
+ }
5290
+ return nextSize;
5291
+ }
5292
+
4031
5293
  // src/core/updateItemSize.ts
4032
5294
  function runOrScheduleMVCPRecalculate(ctx) {
4033
5295
  const state = ctx.state;
@@ -4109,6 +5371,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
4109
5371
  previous: size - diff,
4110
5372
  size
4111
5373
  });
5374
+ maybeUpdateAnchoredEndSpace(ctx);
4112
5375
  }
4113
5376
  if (minIndexSizeChanged !== void 0) {
4114
5377
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
@@ -4232,7 +5495,7 @@ function createImperativeHandle(ctx) {
4232
5495
  const IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS = 800;
4233
5496
  const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
4234
5497
  let imperativeScrollToken = 0;
4235
- const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0 || hasActiveMVCPAnchorLock(state);
5498
+ const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0;
4236
5499
  const runWhenSettled = (token, run) => {
4237
5500
  const startedAt = Date.now();
4238
5501
  let stableFrames = 0;
@@ -4254,9 +5517,10 @@ function createImperativeHandle(ctx) {
4254
5517
  };
4255
5518
  requestAnimationFrame(check);
4256
5519
  };
4257
- const runScrollWithPromise = (run) => new Promise((resolve) => {
5520
+ const runScrollWithPromise = (run, options) => new Promise((resolve) => {
4258
5521
  var _a3;
4259
5522
  const token = ++imperativeScrollToken;
5523
+ const shouldWaitOneFrame = !!(options == null ? void 0 : options.shouldWaitOneFrame);
4260
5524
  (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
4261
5525
  state.pendingScrollResolve = resolve;
4262
5526
  const runNow = () => {
@@ -4271,11 +5535,12 @@ function createImperativeHandle(ctx) {
4271
5535
  resolve();
4272
5536
  }
4273
5537
  };
5538
+ const execute = shouldWaitOneFrame ? () => requestAnimationFrame(runNow) : runNow;
4274
5539
  if (isSettlingAfterDataChange()) {
4275
- runWhenSettled(token, runNow);
4276
- return;
5540
+ runWhenSettled(token, execute);
5541
+ } else {
5542
+ execute();
4277
5543
  }
4278
- runNow();
4279
5544
  });
4280
5545
  const scrollIndexIntoView = (options) => {
4281
5546
  if (state) {
@@ -4332,10 +5597,13 @@ function createImperativeHandle(ctx) {
4332
5597
  },
4333
5598
  end: state.endNoBuffer,
4334
5599
  endBuffered: state.endBuffered,
4335
- isAtEnd: state.isAtEnd,
4336
- isAtStart: state.isAtStart,
5600
+ isAtEnd: peek$(ctx, "isAtEnd"),
5601
+ isAtStart: peek$(ctx, "isAtStart"),
4337
5602
  isEndReached: state.isEndReached,
5603
+ isNearEnd: peek$(ctx, "isNearEnd"),
5604
+ isNearStart: peek$(ctx, "isNearStart"),
4338
5605
  isStartReached: state.isStartReached,
5606
+ isWithinMaintainScrollAtEndThreshold: peek$(ctx, "isWithinMaintainScrollAtEndThreshold"),
4339
5607
  listen: (signalName, cb) => listen$(ctx, signalName, cb),
4340
5608
  listenToPosition: (key, cb) => listenPosition$(ctx, key, cb),
4341
5609
  positionAtIndex: (index) => state.positions[index],
@@ -4382,10 +5650,15 @@ function createImperativeHandle(ctx) {
4382
5650
  }
4383
5651
  return false;
4384
5652
  }),
4385
- scrollToIndex: (params) => runScrollWithPromise(() => {
4386
- scrollToIndex(ctx, params);
4387
- return true;
4388
- }),
5653
+ scrollToIndex: (params) => runScrollWithPromise(
5654
+ () => {
5655
+ scrollToIndex(ctx, params);
5656
+ return true;
5657
+ },
5658
+ {
5659
+ shouldWaitOneFrame: params.index >= 0 && params.index >= state.props.data.length
5660
+ }
5661
+ ),
4389
5662
  scrollToItem: ({ item, ...props }) => runScrollWithPromise(() => {
4390
5663
  const data = state.props.data;
4391
5664
  const index = data.indexOf(item);
@@ -4481,7 +5754,7 @@ function getRenderedItem(ctx, key) {
4481
5754
  item,
4482
5755
  type: getItemType ? (_a3 = getItemType(item, index)) != null ? _a3 : "" : ""
4483
5756
  };
4484
- renderedItem = isFunction(renderItem) ? renderItem(itemProps) : React3__namespace.default.createElement(renderItem, itemProps);
5757
+ renderedItem = React3__namespace.default.createElement(renderItem, itemProps);
4485
5758
  }
4486
5759
  return { index, item: data[index], renderedItem };
4487
5760
  }
@@ -4616,8 +5889,17 @@ var LegendList = typedMemo(
4616
5889
  );
4617
5890
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
4618
5891
  var _a3, _b, _c, _d, _e, _f, _g, _h;
5892
+ const noopOnScroll = React3.useCallback((_event) => {
5893
+ }, []);
5894
+ if (props.recycleItems === void 0) {
5895
+ warnDevOnce(
5896
+ "recycleItems-omitted",
5897
+ "recycleItems was not provided, so it defaults to false. Set recycleItems explicitly to true for better performance with recycling-aware rows, or false to preserve remount-on-reuse behavior."
5898
+ );
5899
+ }
4619
5900
  const {
4620
5901
  alignItemsAtEnd = false,
5902
+ anchoredEndSpace,
4621
5903
  alwaysRender,
4622
5904
  columnWrapperStyle,
4623
5905
  contentContainerStyle: contentContainerStyleProp,
@@ -4639,6 +5921,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4639
5921
  itemsAreEqual,
4640
5922
  keyExtractor: keyExtractorProp,
4641
5923
  ListEmptyComponent,
5924
+ ListFooterComponent,
5925
+ ListFooterComponentStyle,
4642
5926
  ListHeaderComponent,
4643
5927
  maintainScrollAtEnd = false,
4644
5928
  maintainScrollAtEndThreshold = 0.1,
@@ -4675,14 +5959,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4675
5959
  useWindowScroll = false,
4676
5960
  viewabilityConfig,
4677
5961
  viewabilityConfigCallbackPairs,
4678
- waitForInitialLayout = true,
4679
5962
  ...rest
4680
5963
  } = props;
4681
5964
  const animatedPropsInternal = props.animatedPropsInternal;
4682
5965
  const positionComponentInternal = props.positionComponentInternal;
4683
5966
  const stickyPositionComponentInternal = props.stickyPositionComponentInternal;
4684
5967
  const {
4685
- childrenMode,
4686
5968
  positionComponentInternal: _positionComponentInternal,
4687
5969
  stickyPositionComponentInternal: _stickyPositionComponentInternal,
4688
5970
  ...restProps
@@ -4708,8 +5990,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4708
5990
  const hasInitialScrollIndex = initialScrollIndexProp !== void 0 && initialScrollIndexProp !== null;
4709
5991
  const hasInitialScrollOffset = initialScrollOffsetProp !== void 0 && initialScrollOffsetProp !== null;
4710
5992
  const initialScrollUsesOffsetOnly = !initialScrollAtEnd && !hasInitialScrollIndex && hasInitialScrollOffset;
4711
- const initialScrollProp = initialScrollAtEnd ? { index: Math.max(0, dataProp.length - 1), viewOffset: -stylePaddingBottomState, viewPosition: 1 } : hasInitialScrollIndex ? typeof initialScrollIndexProp === "object" ? {
5993
+ const usesBootstrapInitialScroll = initialScrollAtEnd || hasInitialScrollIndex;
5994
+ const initialScrollProp = initialScrollAtEnd ? {
5995
+ index: Math.max(0, dataProp.length - 1),
5996
+ preserveForBottomPadding: true,
5997
+ viewOffset: -stylePaddingBottomState,
5998
+ viewPosition: 1
5999
+ } : hasInitialScrollIndex ? typeof initialScrollIndexProp === "object" ? {
4712
6000
  index: (_a3 = initialScrollIndexProp.index) != null ? _a3 : 0,
6001
+ preserveForBottomPadding: initialScrollIndexProp.viewOffset === void 0 && initialScrollIndexProp.viewPosition === 1 ? true : void 0,
4713
6002
  viewOffset: (_b = initialScrollIndexProp.viewOffset) != null ? _b : initialScrollIndexProp.viewPosition === 1 ? -stylePaddingBottomState : 0,
4714
6003
  viewPosition: (_c = initialScrollIndexProp.viewPosition) != null ? _c : 0
4715
6004
  } : {
@@ -4739,18 +6028,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4739
6028
  dataVersion,
4740
6029
  keyExtractor
4741
6030
  ]);
4742
- if (IS_DEV && stickyIndicesDeprecated && !stickyHeaderIndicesProp) {
4743
- warnDevOnce(
4744
- "stickyIndices",
4745
- "stickyIndices has been renamed to stickyHeaderIndices. Please update your props to use stickyHeaderIndices."
4746
- );
4747
- }
4748
- if (IS_DEV && useWindowScroll && renderScrollComponent) {
4749
- warnDevOnce(
4750
- "useWindowScrollRenderScrollComponent",
4751
- "useWindowScroll is not supported when renderScrollComponent is provided."
4752
- );
4753
- }
4754
6031
  const useWindowScrollResolved = !!useWindowScroll && !renderScrollComponent;
4755
6032
  const refState = React3.useRef(void 0);
4756
6033
  const hasOverrideItemLayout = !!overrideItemLayout;
@@ -4759,7 +6036,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4759
6036
  if (!ctx.state) {
4760
6037
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : { height: 0, width: 0 } )[horizontal ? "width" : "height"];
4761
6038
  ctx.state = {
4762
- activeStickyIndex: -1,
4763
6039
  averageSizes: {},
4764
6040
  columnSpans: [],
4765
6041
  columns: [],
@@ -4778,24 +6054,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4778
6054
  idCache: [],
4779
6055
  idsInView: [],
4780
6056
  indexByKey: /* @__PURE__ */ new Map(),
4781
- initialAnchor: !initialScrollUsesOffsetOnly && (initialScrollProp == null ? void 0 : initialScrollProp.index) !== void 0 && (initialScrollProp == null ? void 0 : initialScrollProp.viewPosition) !== void 0 ? {
4782
- attempts: 0,
4783
- index: initialScrollProp.index,
4784
- settledTicks: 0,
4785
- viewOffset: (_f = initialScrollProp.viewOffset) != null ? _f : 0,
4786
- viewPosition: initialScrollProp.viewPosition
4787
- } : void 0,
4788
- initialNativeScrollWatchdog: void 0,
4789
6057
  initialScroll: initialScrollProp,
4790
- initialScrollLastDidFinish: false,
4791
- initialScrollLastTarget: initialScrollProp,
4792
- initialScrollLastTargetUsesOffset: initialScrollUsesOffsetOnly,
4793
- initialScrollPreviousDataLength: dataProp.length,
4794
- initialScrollRetryLastLength: void 0,
4795
- initialScrollRetryWindowUntil: 0,
4796
- initialScrollUsesOffset: initialScrollUsesOffsetOnly,
4797
- isAtEnd: false,
4798
- isAtStart: false,
6058
+ initialScrollSession: initialScrollProp ? {
6059
+ kind: initialScrollUsesOffsetOnly ? "offset" : "bootstrap",
6060
+ previousDataLength: dataProp.length
6061
+ } : void 0,
4799
6062
  isEndReached: null,
4800
6063
  isFirst: true,
4801
6064
  isStartReached: null,
@@ -4806,6 +6069,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4806
6069
  minIndexSizeChanged: 0,
4807
6070
  nativeContentInset: void 0,
4808
6071
  nativeMarginTop: 0,
6072
+ pendingDataComparison: void 0,
4809
6073
  pendingNativeMVCPAdjust: void 0,
4810
6074
  positions: [],
4811
6075
  props: {},
@@ -4836,6 +6100,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4836
6100
  };
4837
6101
  const internalState = ctx.state;
4838
6102
  internalState.triggerCalculateItemsInView = (params) => calculateItemsInView(ctx, params);
6103
+ internalState.reprocessCurrentScroll = () => updateScroll(ctx, internalState.scroll, true);
4839
6104
  set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPositionConfig);
4840
6105
  set$(ctx, "extraData", extraData);
4841
6106
  }
@@ -4843,22 +6108,29 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4843
6108
  }
4844
6109
  const state = refState.current;
4845
6110
  const isFirstLocal = state.isFirst;
4846
- state.didColumnsChange = numColumnsProp !== state.props.numColumns;
6111
+ const previousNumColumnsProp = state.props.numColumns;
6112
+ state.didColumnsChange = numColumnsProp !== previousNumColumnsProp;
4847
6113
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
4848
6114
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
4849
- const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkActualChange(state, dataProp, state.props.data);
6115
+ const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
6116
+ if (didDataChangeLocal && state.didFinishInitialScroll && ((_f = state.initialScroll) == null ? void 0 : _f.viewPosition) === 1 && state.props.data.length > 0) {
6117
+ clearPreservedInitialScrollTarget(state);
6118
+ }
4850
6119
  if (didDataChangeLocal) {
4851
6120
  state.dataChangeEpoch += 1;
4852
6121
  state.dataChangeNeedsScrollUpdate = true;
4853
6122
  state.didDataChange = true;
4854
6123
  state.previousData = state.props.data;
4855
6124
  }
4856
- const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
6125
+ const throttledOnScroll = useThrottledOnScroll(onScrollProp != null ? onScrollProp : noopOnScroll, scrollEventThrottle != null ? scrollEventThrottle : 0);
6126
+ const throttleScrollFn = scrollEventThrottle && onScrollProp ? throttledOnScroll : onScrollProp;
6127
+ const anchoredEndSpaceResolved = anchoredEndSpace ? { ...anchoredEndSpace, includeInEndInset: true } : anchoredEndSpace;
4857
6128
  state.props = {
4858
6129
  alignItemsAtEnd,
4859
6130
  alwaysRender,
4860
6131
  alwaysRenderIndicesArr: alwaysRenderIndices.arr,
4861
6132
  alwaysRenderIndicesSet: alwaysRenderIndices.set,
6133
+ anchoredEndSpace: anchoredEndSpaceResolved,
4862
6134
  animatedProps: animatedPropsInternal,
4863
6135
  contentInset,
4864
6136
  data: dataProp,
@@ -4922,292 +6194,87 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4922
6194
  true
4923
6195
  );
4924
6196
  }
4925
- const resolveInitialScrollOffset = React3.useCallback((initialScroll) => {
4926
- var _a4;
4927
- if (state.initialScrollUsesOffset) {
4928
- return clampScrollOffset(ctx, (_a4 = initialScroll.contentOffset) != null ? _a4 : 0);
4929
- }
4930
- const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, initialScroll.index) : 0;
4931
- const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, initialScroll);
4932
- return clampScrollOffset(ctx, resolvedOffset, initialScroll);
4933
- }, []);
4934
- const finishInitialScrollWithoutScroll = React3.useCallback(() => {
4935
- refState.current.initialAnchor = void 0;
4936
- refState.current.initialScroll = void 0;
4937
- state.initialAnchor = void 0;
4938
- state.initialScroll = void 0;
4939
- state.initialScrollUsesOffset = false;
4940
- state.initialScrollLastTarget = void 0;
4941
- state.initialScrollLastTargetUsesOffset = false;
4942
- setInitialRenderState(ctx, { didInitialScroll: true });
4943
- }, []);
4944
- const setActiveInitialScrollTarget = React3.useCallback(
4945
- (target, options) => {
4946
- const usesOffset = !!(options == null ? void 0 : options.usesOffset);
4947
- state.initialScrollUsesOffset = usesOffset;
4948
- state.initialScrollLastTarget = target;
4949
- state.initialScrollLastTargetUsesOffset = usesOffset;
4950
- refState.current.initialScroll = target;
4951
- state.initialScroll = target;
4952
- if ((options == null ? void 0 : options.resetDidFinish) && state.didFinishInitialScroll) {
4953
- state.didFinishInitialScroll = false;
4954
- }
4955
- if (!(options == null ? void 0 : options.syncAnchor)) {
4956
- return;
4957
- }
4958
- },
4959
- []
4960
- );
4961
- const shouldFinishInitialScrollAtOrigin = React3.useCallback(
4962
- (initialScroll, offset) => {
4963
- var _a4, _b2, _c2;
4964
- if (offset !== 0 || initialScrollAtEnd) {
4965
- return false;
4966
- }
4967
- if (state.initialScrollUsesOffset) {
4968
- return Math.abs((_a4 = initialScroll.contentOffset) != null ? _a4 : 0) <= 1;
4969
- }
4970
- return initialScroll.index === 0 && ((_b2 = initialScroll.viewPosition) != null ? _b2 : 0) === 0 && Math.abs((_c2 = initialScroll.viewOffset) != null ? _c2 : 0) <= 1;
4971
- },
4972
- [initialScrollAtEnd]
4973
- );
4974
- const shouldFinishEmptyInitialScrollAtEnd = React3.useCallback(
4975
- (initialScroll, offset) => {
4976
- return dataProp.length === 0 && initialScrollAtEnd && offset === 0 && initialScroll.viewPosition === 1;
4977
- },
4978
- [dataProp.length, initialScrollAtEnd]
4979
- );
4980
- const shouldRearmFinishedEmptyInitialScrollAtEnd = React3.useCallback(
4981
- (initialScroll) => {
4982
- var _a4;
4983
- return !!(state.didFinishInitialScroll && dataProp.length > 0 && initialScroll && !state.initialScrollUsesOffset && initialScroll.index === 0 && initialScroll.viewPosition === 1 && ((_a4 = initialScroll.contentOffset) != null ? _a4 : 0) === 0);
4984
- },
4985
- [dataProp.length]
4986
- );
4987
6197
  const initialContentOffset = React3.useMemo(() => {
4988
- let value;
4989
- const { initialScroll, initialAnchor } = refState.current;
4990
- if (initialScroll) {
4991
- if (!state.initialScrollUsesOffset && !IsNewArchitecture) ;
4992
- if (initialScroll.contentOffset !== void 0) {
4993
- value = initialScroll.contentOffset;
4994
- } else {
4995
- const clampedOffset = resolveInitialScrollOffset(initialScroll);
4996
- const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
4997
- setActiveInitialScrollTarget(updatedInitialScroll, {
4998
- usesOffset: state.initialScrollUsesOffset
4999
- });
5000
- value = clampedOffset;
5001
- }
5002
- } else {
5003
- refState.current.initialAnchor = void 0;
5004
- value = 0;
5005
- }
5006
- const hasPendingDataDependentInitialScroll = !!initialScroll && dataProp.length === 0 && !shouldFinishInitialScrollAtOrigin(initialScroll, value) && !shouldFinishEmptyInitialScrollAtEnd(initialScroll, value);
5007
- if (!value && !hasPendingDataDependentInitialScroll) {
5008
- if (initialScroll && shouldFinishInitialScrollAtOrigin(initialScroll, value)) {
5009
- finishInitialScrollWithoutScroll();
5010
- } else {
5011
- setInitialRenderState(ctx, { didInitialScroll: true });
5012
- }
6198
+ var _a4, _b2;
6199
+ const initialScroll = state.initialScroll;
6200
+ if (!initialScroll) {
6201
+ return void 0;
5013
6202
  }
5014
- return value;
6203
+ const resolvedOffset = (_a4 = initialScroll.contentOffset) != null ? _a4 : resolveInitialScrollOffset(ctx, initialScroll);
6204
+ return usesBootstrapInitialScroll && ((_b2 = state.initialScrollSession) == null ? void 0 : _b2.kind) === "bootstrap" && Platform.OS === "web" ? void 0 : resolvedOffset;
6205
+ }, [usesBootstrapInitialScroll]);
6206
+ React3.useLayoutEffect(() => {
6207
+ initializeInitialScrollOnMount(ctx, {
6208
+ dataLength: dataProp.length,
6209
+ hasFooterComponent: !!ListFooterComponent,
6210
+ initialContentOffset,
6211
+ initialScrollAtEnd,
6212
+ useBootstrapInitialScroll: usesBootstrapInitialScroll
6213
+ });
5015
6214
  }, []);
5016
6215
  if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
5017
6216
  refState.current.lastBatchingAction = Date.now();
5018
6217
  if (!keyExtractorProp && !isFirstLocal && didDataChangeLocal) {
5019
- IS_DEV && !childrenMode && warnDevOnce(
5020
- "keyExtractor",
5021
- "Changing data without a keyExtractor can cause slow performance and resetting scroll. If your list data can change you should use a keyExtractor with a unique id for best performance and behavior."
5022
- );
5023
6218
  refState.current.sizes.clear();
5024
6219
  refState.current.positions.length = 0;
5025
6220
  refState.current.totalSize = 0;
5026
6221
  set$(ctx, "totalSize", 0);
5027
6222
  }
5028
6223
  }
5029
- const doInitialScroll = React3.useCallback((options) => {
5030
- var _a4, _b2;
5031
- const allowPostFinishRetry = !!(options == null ? void 0 : options.allowPostFinishRetry);
5032
- const { didFinishInitialScroll, queuedInitialLayout, scrollingTo } = state;
5033
- const initialScroll = (_a4 = state.initialScroll) != null ? _a4 : allowPostFinishRetry ? state.initialScrollLastTarget : void 0;
5034
- const isInitialScrollInProgress = !!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll);
5035
- const needsContainerLayoutForInitialScroll = !state.initialScrollUsesOffset;
5036
- const shouldWaitForInitialLayout = waitForInitialLayout && needsContainerLayoutForInitialScroll && !queuedInitialLayout && !allowPostFinishRetry && !isInitialScrollInProgress;
5037
- if (!initialScroll || shouldWaitForInitialLayout || didFinishInitialScroll && !allowPostFinishRetry || scrollingTo && !isInitialScrollInProgress) {
5038
- return;
5039
- }
5040
- if (allowPostFinishRetry && state.initialScrollLastTargetUsesOffset) {
5041
- return;
5042
- }
5043
- const didMoveAwayFromInitialTarget = allowPostFinishRetry && initialScroll.contentOffset !== void 0 && Math.abs(state.scroll - initialScroll.contentOffset) > 1;
5044
- if (didMoveAwayFromInitialTarget) {
5045
- state.initialScrollRetryWindowUntil = 0;
5046
- return;
5047
- }
5048
- const offset = resolveInitialScrollOffset(initialScroll);
5049
- const activeInitialTargetOffset = isInitialScrollInProgress ? (_b2 = scrollingTo.targetOffset) != null ? _b2 : scrollingTo.offset : void 0;
5050
- const didOffsetChange = initialScroll.contentOffset === void 0 || Math.abs(initialScroll.contentOffset - offset) > 1;
5051
- const didActiveInitialTargetChange = activeInitialTargetOffset !== void 0 && Math.abs(activeInitialTargetOffset - offset) > 1;
5052
- if (!didOffsetChange && (allowPostFinishRetry || isInitialScrollInProgress && !didActiveInitialTargetChange)) {
5053
- return;
5054
- }
5055
- if (didOffsetChange) {
5056
- const updatedInitialScroll = { ...initialScroll, contentOffset: offset };
5057
- if (!state.initialScrollUsesOffset) {
5058
- state.initialScrollLastTarget = updatedInitialScroll;
5059
- state.initialScrollLastTargetUsesOffset = false;
5060
- if (state.initialScroll) {
5061
- refState.current.initialScroll = updatedInitialScroll;
5062
- state.initialScroll = updatedInitialScroll;
5063
- }
5064
- }
5065
- }
5066
- const hasMeasuredScrollLayout = !!state.lastLayout && state.scrollLength > 0;
5067
- const shouldForceNativeInitialScroll = state.initialScrollUsesOffset && hasMeasuredScrollLayout || allowPostFinishRetry || !!queuedInitialLayout || isInitialScrollInProgress && didOffsetChange;
5068
- performInitialScroll(ctx, {
5069
- forceScroll: shouldForceNativeInitialScroll,
5070
- initialScrollUsesOffset: state.initialScrollUsesOffset,
5071
- resolvedOffset: offset,
5072
- target: initialScroll
5073
- });
5074
- }, []);
5075
- React3.useLayoutEffect(() => {
5076
- var _a4;
5077
- const previousDataLength = state.initialScrollPreviousDataLength;
5078
- state.initialScrollPreviousDataLength = dataProp.length;
5079
- if (previousDataLength !== 0 || dataProp.length === 0 || !state.initialScroll || !state.queuedInitialLayout) {
5080
- return;
5081
- }
5082
- if (initialScrollAtEnd) {
5083
- const lastIndex = Math.max(0, dataProp.length - 1);
5084
- const initialScroll = state.initialScroll;
5085
- const shouldRearm = shouldRearmFinishedEmptyInitialScrollAtEnd(initialScroll);
5086
- if (state.didFinishInitialScroll && !shouldRearm) {
5087
- return;
5088
- }
5089
- if (initialScroll && !state.initialScrollUsesOffset && initialScroll.index === lastIndex && initialScroll.viewPosition === 1 && !shouldRearm) {
5090
- return;
5091
- }
5092
- const updatedInitialScroll = {
5093
- contentOffset: void 0,
5094
- index: lastIndex,
5095
- viewOffset: (_a4 = initialScroll == null ? void 0 : initialScroll.viewOffset) != null ? _a4 : -stylePaddingBottomState,
5096
- viewPosition: 1
5097
- };
5098
- setActiveInitialScrollTarget(updatedInitialScroll, {
5099
- resetDidFinish: shouldRearm,
5100
- syncAnchor: true
5101
- });
5102
- doInitialScroll();
5103
- return;
5104
- }
5105
- if (state.didFinishInitialScroll) {
5106
- return;
5107
- }
5108
- doInitialScroll();
5109
- }, [
5110
- dataProp.length,
5111
- doInitialScroll,
5112
- initialScrollAtEnd,
5113
- shouldRearmFinishedEmptyInitialScrollAtEnd,
5114
- stylePaddingBottomState
5115
- ]);
6224
+ if (IS_DEV) {
6225
+ useDevChecks(props);
6226
+ }
5116
6227
  React3.useLayoutEffect(() => {
5117
- var _a4;
5118
- if (!initialScrollAtEnd) {
5119
- return;
5120
- }
5121
- const lastIndex = Math.max(0, dataProp.length - 1);
5122
- const initialScroll = state.initialScroll;
5123
- const shouldRearm = shouldRearmFinishedEmptyInitialScrollAtEnd(initialScroll);
5124
- if (state.didFinishInitialScroll && !shouldRearm) {
5125
- return;
5126
- }
5127
- if (shouldRearm) {
5128
- state.didFinishInitialScroll = false;
5129
- }
5130
- if (initialScroll && !state.initialScrollUsesOffset && initialScroll.index === lastIndex && initialScroll.viewPosition === 1 && !shouldRearm) {
5131
- return;
5132
- }
5133
- const updatedInitialScroll = {
5134
- contentOffset: void 0,
5135
- index: lastIndex,
5136
- viewOffset: (_a4 = initialScroll == null ? void 0 : initialScroll.viewOffset) != null ? _a4 : -stylePaddingBottomState,
5137
- viewPosition: 1
5138
- };
5139
- setActiveInitialScrollTarget(updatedInitialScroll, {
5140
- resetDidFinish: shouldRearm,
5141
- syncAnchor: true
6228
+ handleInitialScrollDataChange(ctx, {
6229
+ dataLength: dataProp.length,
6230
+ didDataChange: didDataChangeLocal,
6231
+ initialScrollAtEnd,
6232
+ stylePaddingBottom: stylePaddingBottomState,
6233
+ useBootstrapInitialScroll: usesBootstrapInitialScroll
5142
6234
  });
5143
- doInitialScroll();
6235
+ }, [dataProp.length, didDataChangeLocal, initialScrollAtEnd, stylePaddingBottomState, usesBootstrapInitialScroll]);
6236
+ React3.useLayoutEffect(() => {
6237
+ maybeUpdateAnchoredEndSpace(ctx);
5144
6238
  }, [
5145
- dataProp.length,
5146
- doInitialScroll,
5147
- initialScrollAtEnd,
5148
- shouldRearmFinishedEmptyInitialScrollAtEnd,
5149
- stylePaddingBottomState
6239
+ ctx,
6240
+ dataProp,
6241
+ dataVersion,
6242
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex,
6243
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorMaxSize,
6244
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorOffset,
6245
+ numColumnsProp
5150
6246
  ]);
5151
6247
  const onLayoutFooter = React3.useCallback(
5152
6248
  (layout) => {
5153
- var _a4;
5154
- if (!initialScrollAtEnd) {
6249
+ if (!usesBootstrapInitialScroll) {
5155
6250
  return;
5156
6251
  }
5157
- const { initialScroll } = state;
5158
- if (!initialScroll) {
5159
- return;
6252
+ handleBootstrapInitialScrollFooterLayout(ctx, {
6253
+ dataLength: dataProp.length,
6254
+ footerSize: layout[horizontal ? "width" : "height"],
6255
+ initialScrollAtEnd,
6256
+ stylePaddingBottom: stylePaddingBottomState
6257
+ });
6258
+ },
6259
+ [dataProp.length, initialScrollAtEnd, horizontal, stylePaddingBottomState, usesBootstrapInitialScroll]
6260
+ );
6261
+ const onLayoutChange = React3.useCallback(
6262
+ (layout, fromLayoutEffect) => {
6263
+ const previousScrollLength = state.scrollLength;
6264
+ const previousOtherAxisSize = state.otherAxisSize;
6265
+ handleLayout(ctx, layout, setCanRender);
6266
+ maybeUpdateAnchoredEndSpace(ctx);
6267
+ const didLayoutAffectBootstrapTarget = previousScrollLength !== state.scrollLength || previousOtherAxisSize !== state.otherAxisSize;
6268
+ if (usesBootstrapInitialScroll && !fromLayoutEffect && didLayoutAffectBootstrapTarget) {
6269
+ handleBootstrapInitialScrollLayoutChange(ctx);
5160
6270
  }
5161
- const lastIndex = Math.max(0, dataProp.length - 1);
5162
- if (initialScroll.index !== lastIndex || initialScroll.viewPosition !== 1) {
6271
+ if (usesBootstrapInitialScroll) {
5163
6272
  return;
5164
6273
  }
5165
- const footerSize = layout[horizontal ? "width" : "height"];
5166
- const viewOffset = -stylePaddingBottomState - footerSize;
5167
- if (initialScroll.viewOffset !== viewOffset) {
5168
- const previousTargetOffset = (_a4 = initialScroll.contentOffset) != null ? _a4 : resolveInitialScrollOffset(initialScroll);
5169
- const didMoveAwayFromFinishedInitialTarget = state.didFinishInitialScroll && Math.abs(state.scroll - previousTargetOffset) > 1;
5170
- if (didMoveAwayFromFinishedInitialTarget) {
5171
- return;
5172
- }
5173
- const updatedInitialScroll = { ...initialScroll, viewOffset };
5174
- setActiveInitialScrollTarget(updatedInitialScroll, {
5175
- resetDidFinish: true
5176
- });
5177
- doInitialScroll();
5178
- }
6274
+ advanceCurrentInitialScrollSession(ctx);
5179
6275
  },
5180
- [
5181
- dataProp.length,
5182
- doInitialScroll,
5183
- horizontal,
5184
- initialScrollAtEnd,
5185
- resolveInitialScrollOffset,
5186
- stylePaddingBottomState
5187
- ]
6276
+ [dataProp.length, initialScrollAtEnd, stylePaddingBottomState, usesBootstrapInitialScroll]
5188
6277
  );
5189
- const onLayoutChange = React3.useCallback((layout) => {
5190
- var _a4;
5191
- handleLayout(ctx, layout, setCanRender);
5192
- const SCROLL_LENGTH_RETRY_WINDOW_MS = 600;
5193
- const now = Date.now();
5194
- const didFinishInitialScroll = !!state.didFinishInitialScroll;
5195
- if (didFinishInitialScroll && !state.initialScrollLastDidFinish) {
5196
- state.initialScrollRetryWindowUntil = now + SCROLL_LENGTH_RETRY_WINDOW_MS;
5197
- }
5198
- state.initialScrollLastDidFinish = didFinishInitialScroll;
5199
- const previousScrollLength = state.initialScrollRetryLastLength;
5200
- const currentScrollLength = state.scrollLength;
5201
- const didScrollLengthChange = previousScrollLength === void 0 || Math.abs(currentScrollLength - previousScrollLength) > 1;
5202
- if (didScrollLengthChange) {
5203
- state.initialScrollRetryLastLength = currentScrollLength;
5204
- }
5205
- if (didFinishInitialScroll && didScrollLengthChange && now <= state.initialScrollRetryWindowUntil && !state.initialScrollLastTargetUsesOffset && ((_a4 = state.initialScrollLastTarget) == null ? void 0 : _a4.index) !== void 0) {
5206
- doInitialScroll({ allowPostFinishRetry: true });
5207
- return;
5208
- }
5209
- doInitialScroll();
5210
- }, []);
5211
6278
  const { onLayout } = useOnLayoutSync({
5212
6279
  onLayoutChange,
5213
6280
  onLayoutProp,
@@ -5219,6 +6286,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5219
6286
  updateSnapToOffsets(ctx);
5220
6287
  }
5221
6288
  }, [snapToIndices]);
6289
+ React3.useLayoutEffect(
6290
+ () => initializeStateVars(true),
6291
+ [dataVersion, memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingBottomState, stylePaddingTopState]
6292
+ );
5222
6293
  React3.useLayoutEffect(() => {
5223
6294
  const {
5224
6295
  didColumnsChange,
@@ -5228,7 +6299,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5228
6299
  } = state;
5229
6300
  const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx);
5230
6301
  if (!didAllocateContainers && !isFirst && (didDataChange || didColumnsChange)) {
5231
- checkResetContainers(ctx, data);
6302
+ checkResetContainers(ctx, data, { didColumnsChange });
6303
+ }
6304
+ if (didDataChange) {
6305
+ state.pendingDataComparison = void 0;
5232
6306
  }
5233
6307
  state.didColumnsChange = false;
5234
6308
  state.didDataChange = false;
@@ -5243,10 +6317,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5243
6317
  (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { forceFullItemPositions: true });
5244
6318
  }
5245
6319
  }, [extraData, hasOverrideItemLayout, numColumnsProp]);
5246
- React3.useLayoutEffect(
5247
- () => initializeStateVars(true),
5248
- [dataVersion, memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingBottomState, stylePaddingTopState]
5249
- );
5250
6320
  React3.useEffect(() => {
5251
6321
  if (!onMetricsChange) {
5252
6322
  return;
@@ -5279,10 +6349,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5279
6349
  state.viewabilityConfigCallbackPairs = viewability;
5280
6350
  state.enableScrollForNextCalculateItemsInView = !viewability;
5281
6351
  }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
6352
+ useInit(() => {
6353
+ });
5282
6354
  React3.useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx), []);
5283
- {
5284
- React3.useEffect(doInitialScroll, []);
5285
- }
6355
+ React3.useEffect(() => {
6356
+ if (usesBootstrapInitialScroll) {
6357
+ return;
6358
+ }
6359
+ advanceCurrentInitialScrollSession(ctx);
6360
+ }, [ctx, usesBootstrapInitialScroll]);
5286
6361
  const fns = React3.useMemo(
5287
6362
  () => ({
5288
6363
  getRenderedItem: (key) => getRenderedItem(ctx, key),
@@ -5311,6 +6386,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5311
6386
  horizontal,
5312
6387
  initialContentOffset,
5313
6388
  ListEmptyComponent: dataProp.length === 0 ? ListEmptyComponent : void 0,
6389
+ ListFooterComponent,
6390
+ ListFooterComponentStyle,
5314
6391
  ListHeaderComponent,
5315
6392
  onLayout,
5316
6393
  onLayoutFooter,
@@ -5335,19 +6412,45 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5335
6412
  stickyHeaderIndices,
5336
6413
  style,
5337
6414
  updateItemSize: fns.updateItemSize,
5338
- useWindowScroll: useWindowScrollResolved,
5339
- waitForInitialLayout
6415
+ useWindowScroll: useWindowScrollResolved
5340
6416
  }
5341
6417
  ), IS_DEV && ENABLE_DEBUG_VIEW);
5342
6418
  });
5343
6419
 
6420
+ // src/components/stickyPositionUtils.ts
6421
+ function getStickyPushLimit(state, index, itemKey) {
6422
+ if (!itemKey) {
6423
+ return void 0;
6424
+ }
6425
+ const currentSize = state.sizes.get(itemKey);
6426
+ if (!(currentSize && currentSize > 0)) {
6427
+ return void 0;
6428
+ }
6429
+ const stickyIndexInArray = state.props.stickyIndicesArr.indexOf(index);
6430
+ if (stickyIndexInArray === -1) {
6431
+ return void 0;
6432
+ }
6433
+ const nextStickyIndex = state.props.stickyIndicesArr[stickyIndexInArray + 1];
6434
+ if (nextStickyIndex === void 0) {
6435
+ return void 0;
6436
+ }
6437
+ const nextStickyPosition = state.positions[nextStickyIndex];
6438
+ if (nextStickyPosition === void 0) {
6439
+ return void 0;
6440
+ }
6441
+ return nextStickyPosition - currentSize;
6442
+ }
6443
+
5344
6444
  // src/entrypoints/shared.ts
5345
6445
  var LegendListRuntime = LegendList;
5346
6446
  var internal = {
5347
6447
  getComponent,
6448
+ getStickyPushLimit,
5348
6449
  IsNewArchitecture,
5349
6450
  POSITION_OUT_OF_VIEW,
5350
6451
  peek$,
6452
+ typedForwardRef,
6453
+ typedMemo,
5351
6454
  useArr$,
5352
6455
  useCombinedRef,
5353
6456
  useStateContext
@@ -5359,8 +6462,6 @@ var internal2 = internal;
5359
6462
 
5360
6463
  exports.LegendList = LegendList3;
5361
6464
  exports.internal = internal2;
5362
- exports.typedForwardRef = typedForwardRef;
5363
- exports.typedMemo = typedMemo;
5364
6465
  exports.useIsLastItem = useIsLastItem;
5365
6466
  exports.useListScrollSize = useListScrollSize;
5366
6467
  exports.useRecyclingEffect = useRecyclingEffect;