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