@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/react.mjs CHANGED
@@ -13,37 +13,6 @@ var View = forwardRef(function View2(props, ref) {
13
13
  });
14
14
  var Text = View;
15
15
 
16
- // src/state/getContentInsetEnd.ts
17
- function getContentInsetEnd(state) {
18
- var _a3;
19
- const { props } = state;
20
- const horizontal = props.horizontal;
21
- const contentInset = props.contentInset;
22
- const baseInset = contentInset != null ? contentInset : state.nativeContentInset;
23
- const overrideInset = (_a3 = state.contentInsetOverride) != null ? _a3 : void 0;
24
- if (overrideInset) {
25
- const mergedInset = { bottom: 0, right: 0, ...baseInset, ...overrideInset };
26
- return (horizontal ? mergedInset.right : mergedInset.bottom) || 0;
27
- }
28
- if (baseInset) {
29
- return (horizontal ? baseInset.right : baseInset.bottom) || 0;
30
- }
31
- return 0;
32
- }
33
-
34
- // src/state/getContentSize.ts
35
- function getContentSize(ctx) {
36
- var _a3;
37
- const { values, state } = ctx;
38
- const stylePaddingTop = values.get("stylePaddingTop") || 0;
39
- const stylePaddingBottom = state.props.stylePaddingBottom || 0;
40
- const headerSize = values.get("headerSize") || 0;
41
- const footerSize = values.get("footerSize") || 0;
42
- const contentInsetBottom = getContentInsetEnd(state);
43
- const totalSize = (_a3 = state.pendingTotalSize) != null ? _a3 : values.get("totalSize");
44
- return headerSize + footerSize + totalSize + stylePaddingTop + stylePaddingBottom + (contentInsetBottom || 0);
45
- }
46
-
47
16
  // src/platform/Animated.tsx
48
17
  var createAnimatedValue = (value) => value;
49
18
 
@@ -68,6 +37,11 @@ function StateProvider({ children }) {
68
37
  ["headerSize", 0],
69
38
  ["numContainers", 0],
70
39
  ["activeStickyIndex", -1],
40
+ ["isAtEnd", false],
41
+ ["isAtStart", false],
42
+ ["isNearEnd", false],
43
+ ["isNearStart", false],
44
+ ["isWithinMaintainScrollAtEndThreshold", false],
71
45
  ["totalSize", 0],
72
46
  ["scrollAdjustPending", 0]
73
47
  ]),
@@ -159,29 +133,71 @@ function notifyPosition$(ctx, key, value) {
159
133
  function useArr$(signalNames) {
160
134
  const ctx = React3.useContext(ContextState);
161
135
  const { subscribe, get } = React3.useMemo(() => createSelectorFunctionsArr(ctx, signalNames), [ctx, signalNames]);
162
- const value = useSyncExternalStore(subscribe, get);
136
+ const value = useSyncExternalStore(subscribe, get, get);
163
137
  return value;
164
138
  }
165
139
  function useSelector$(signalName, selector) {
166
140
  const ctx = React3.useContext(ContextState);
167
141
  const { subscribe, get } = React3.useMemo(() => createSelectorFunctionsArr(ctx, [signalName]), [ctx, signalName]);
168
- const value = useSyncExternalStore(subscribe, () => selector(get()[0]));
142
+ const getSelectedValue = React3.useCallback(() => selector(get()[0]), [get, selector]);
143
+ const value = useSyncExternalStore(subscribe, getSelectedValue, getSelectedValue);
169
144
  return value;
170
145
  }
171
146
 
147
+ // src/state/getContentInsetEnd.ts
148
+ function getContentInsetEnd(ctx) {
149
+ var _a3, _b;
150
+ const state = ctx.state;
151
+ const { props } = state;
152
+ const horizontal = props.horizontal;
153
+ const contentInset = props.contentInset;
154
+ const baseInset = contentInset != null ? contentInset : state.nativeContentInset;
155
+ const baseEndInset = (horizontal ? baseInset == null ? void 0 : baseInset.right : baseInset == null ? void 0 : baseInset.bottom) || 0;
156
+ const anchoredEndSpaceSize = peek$(ctx, "anchoredEndSpaceSize");
157
+ const anchoredEndInset = ((_a3 = props.anchoredEndSpace) == null ? void 0 : _a3.includeInEndInset) && anchoredEndSpaceSize ? anchoredEndSpaceSize : 0;
158
+ const overrideInset = (_b = state.contentInsetOverride) != null ? _b : void 0;
159
+ if (overrideInset) {
160
+ const mergedInset = { bottom: 0, right: 0, ...baseInset, ...overrideInset };
161
+ return Math.max((horizontal ? mergedInset.right : mergedInset.bottom) || 0, anchoredEndInset);
162
+ }
163
+ return Math.max(baseEndInset, anchoredEndInset);
164
+ }
165
+
166
+ // src/state/getContentSize.ts
167
+ function getContentSize(ctx) {
168
+ var _a3;
169
+ const { values, state } = ctx;
170
+ const stylePaddingTop = values.get("stylePaddingTop") || 0;
171
+ const stylePaddingBottom = state.props.stylePaddingBottom || 0;
172
+ const headerSize = values.get("headerSize") || 0;
173
+ const footerSize = values.get("footerSize") || 0;
174
+ const contentInsetBottom = getContentInsetEnd(ctx);
175
+ const totalSize = (_a3 = state.pendingTotalSize) != null ? _a3 : values.get("totalSize");
176
+ return headerSize + footerSize + totalSize + stylePaddingTop + stylePaddingBottom + (contentInsetBottom || 0);
177
+ }
178
+
172
179
  // src/components/DebugView.tsx
173
180
  var DebugRow = ({ children }) => {
174
181
  return /* @__PURE__ */ React3.createElement(View, { style: { alignItems: "center", flexDirection: "row", justifyContent: "space-between" } }, children);
175
182
  };
176
- React3.memo(function DebugView2({ state }) {
183
+ React3.memo(function DebugView2() {
177
184
  const ctx = useStateContext();
178
- const [totalSize = 0, scrollAdjust = 0, rawScroll = 0, scroll = 0, _numContainers = 0, _numContainersPooled = 0] = useArr$([
185
+ const [
186
+ totalSize = 0,
187
+ scrollAdjust = 0,
188
+ rawScroll = 0,
189
+ scroll = 0,
190
+ _numContainers = 0,
191
+ _numContainersPooled = 0,
192
+ isAtEnd = false
193
+ ] = useArr$([
179
194
  "totalSize",
180
195
  "scrollAdjust",
181
196
  "debugRawScroll",
182
197
  "debugComputedScroll",
183
198
  "numContainers",
184
- "numContainersPooled"
199
+ "numContainersPooled",
200
+ "isAtEnd"
185
201
  ]);
186
202
  const contentSize = getContentSize(ctx);
187
203
  const [, forceUpdate] = useReducer((x) => x + 1, 0);
@@ -206,7 +222,7 @@ React3.memo(function DebugView2({ state }) {
206
222
  },
207
223
  /* @__PURE__ */ React3.createElement(DebugRow, null, /* @__PURE__ */ React3.createElement(Text, null, "TotalSize:"), /* @__PURE__ */ React3.createElement(Text, null, totalSize.toFixed(2))),
208
224
  /* @__PURE__ */ React3.createElement(DebugRow, null, /* @__PURE__ */ React3.createElement(Text, null, "ContentSize:"), /* @__PURE__ */ React3.createElement(Text, null, contentSize.toFixed(2))),
209
- /* @__PURE__ */ React3.createElement(DebugRow, null, /* @__PURE__ */ React3.createElement(Text, null, "At end:"), /* @__PURE__ */ React3.createElement(Text, null, String(state.isAtEnd))),
225
+ /* @__PURE__ */ React3.createElement(DebugRow, null, /* @__PURE__ */ React3.createElement(Text, null, "At end:"), /* @__PURE__ */ React3.createElement(Text, null, String(isAtEnd))),
210
226
  /* @__PURE__ */ React3.createElement(DebugRow, null, /* @__PURE__ */ React3.createElement(Text, null, "ScrollAdjust:"), /* @__PURE__ */ React3.createElement(Text, null, scrollAdjust.toFixed(2))),
211
227
  /* @__PURE__ */ React3.createElement(DebugRow, null, /* @__PURE__ */ React3.createElement(Text, null, "RawScroll: "), /* @__PURE__ */ React3.createElement(Text, null, rawScroll.toFixed(2))),
212
228
  /* @__PURE__ */ React3.createElement(DebugRow, null, /* @__PURE__ */ React3.createElement(Text, null, "ComputedScroll: "), /* @__PURE__ */ React3.createElement(Text, null, scroll.toFixed(2)))
@@ -229,6 +245,7 @@ var IS_DEV = (_a2 = processDev != null ? processDev : metroDev) != null ? _a2 :
229
245
 
230
246
  // src/constants.ts
231
247
  var POSITION_OUT_OF_VIEW = -1e7;
248
+ var EDGE_POSITION_EPSILON = 1;
232
249
  var ENABLE_DEVMODE = IS_DEV && false;
233
250
  var ENABLE_DEBUG_VIEW = IS_DEV && false;
234
251
  var typedForwardRef = React3.forwardRef;
@@ -688,17 +705,20 @@ var Container = typedMemo(function Container2({
688
705
  const { columnGap, rowGap, gap } = columnWrapperStyle;
689
706
  if (horizontal) {
690
707
  paddingStyles = {
708
+ paddingBottom: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0,
691
709
  paddingRight: columnGap || gap || void 0,
692
- paddingVertical: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
710
+ paddingTop: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
693
711
  };
694
712
  } else {
695
713
  paddingStyles = {
696
714
  paddingBottom: rowGap || gap || void 0,
697
- paddingHorizontal: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
715
+ paddingLeft: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0,
716
+ paddingRight: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
698
717
  };
699
718
  }
700
719
  }
701
720
  return horizontal ? {
721
+ boxSizing: paddingStyles ? "border-box" : void 0,
702
722
  flexDirection: ItemSeparatorComponent ? "row" : void 0,
703
723
  height: otherAxisSize,
704
724
  left: 0,
@@ -706,6 +726,7 @@ var Container = typedMemo(function Container2({
706
726
  top: otherAxisPos,
707
727
  ...paddingStyles || {}
708
728
  } : {
729
+ boxSizing: paddingStyles ? "border-box" : void 0,
709
730
  left: otherAxisPos,
710
731
  position: "absolute",
711
732
  right: numColumns > 1 ? null : 0,
@@ -919,7 +940,7 @@ var ContainersInner = typedMemo(function ContainersInner2({ horizontal, numColum
919
940
  const ref = useRef(null);
920
941
  const ctx = useStateContext();
921
942
  const columnWrapperStyle = ctx.columnWrapperStyle;
922
- const [totalSize, otherAxisSize] = useArr$(["totalSize", "otherAxisSize"]);
943
+ const [otherAxisSize, totalSize] = useArr$(["otherAxisSize", "totalSize"]);
923
944
  useDOMOrder(ref);
924
945
  const style = horizontal ? { minHeight: otherAxisSize, position: "relative", width: totalSize } : { height: totalSize, minWidth: otherAxisSize, position: "relative" };
925
946
  if (columnWrapperStyle && numColumns > 1) {
@@ -934,9 +955,6 @@ var ContainersInner = typedMemo(function ContainersInner2({ horizontal, numColum
934
955
  style.marginRight = -gapX;
935
956
  }
936
957
  } else {
937
- if (gapX) {
938
- style.marginLeft = style.marginRight = -gapX;
939
- }
940
958
  if (gapY) {
941
959
  style.marginBottom = -gapY;
942
960
  }
@@ -948,7 +966,6 @@ var Containers = typedMemo(function Containers2({
948
966
  horizontal,
949
967
  recycleItems,
950
968
  ItemSeparatorComponent,
951
- waitForInitialLayout,
952
969
  updateItemSize: updateItemSize2,
953
970
  getRenderedItem: getRenderedItem2,
954
971
  stickyHeaderConfig
@@ -972,7 +989,7 @@ var Containers = typedMemo(function Containers2({
972
989
  )
973
990
  );
974
991
  }
975
- return /* @__PURE__ */ React3.createElement(ContainersInner, { horizontal, numColumns, waitForInitialLayout }, containers);
992
+ return /* @__PURE__ */ React3.createElement(ContainersInner, { horizontal, numColumns }, containers);
976
993
  });
977
994
 
978
995
  // src/platform/StyleSheet.tsx
@@ -1115,6 +1132,7 @@ function resolveWindowScrollTarget({ clampedOffset, horizontal, listPos, scroll
1115
1132
  var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
1116
1133
  children,
1117
1134
  style,
1135
+ contentContainerClassName,
1118
1136
  contentContainerStyle,
1119
1137
  horizontal = false,
1120
1138
  contentOffset,
@@ -1250,18 +1268,18 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
1250
1268
  const scrollEventCoalescer = useRafCoalescer(emitScroll);
1251
1269
  const handleScroll = useCallback(
1252
1270
  (_event) => {
1253
- var _a3;
1254
1271
  if (!onScroll2) {
1255
1272
  return;
1256
1273
  }
1257
- const scrollingTo = (_a3 = ctx.state) == null ? void 0 : _a3.scrollingTo;
1258
- if (scrollingTo && !scrollingTo.animated) {
1274
+ const state = ctx.state;
1275
+ 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);
1276
+ if (shouldFlushImmediately) {
1259
1277
  scrollEventCoalescer.flush();
1260
1278
  } else {
1261
1279
  scrollEventCoalescer.schedule();
1262
1280
  }
1263
1281
  },
1264
- [onScroll2, scrollEventCoalescer]
1282
+ [ctx.state, onScroll2, scrollEventCoalescer]
1265
1283
  );
1266
1284
  useLayoutEffect(() => {
1267
1285
  const target = getScrollTarget();
@@ -1327,13 +1345,14 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
1327
1345
  ...StyleSheet.flatten(contentContainerStyle)
1328
1346
  };
1329
1347
  const {
1348
+ contentContainerClassName: _contentContainerClassName,
1330
1349
  contentInset: _contentInset,
1331
1350
  scrollEventThrottle: _scrollEventThrottle,
1332
1351
  ScrollComponent: _ScrollComponent,
1333
1352
  useWindowScroll: _useWindowScroll,
1334
1353
  ...webProps
1335
1354
  } = props;
1336
- return /* @__PURE__ */ React3.createElement("div", { ref: scrollRef, ...webProps, style: scrollViewStyle }, refreshControl, /* @__PURE__ */ React3.createElement("div", { ref: contentRef, style: contentStyle }, children));
1355
+ return /* @__PURE__ */ React3.createElement("div", { ref: scrollRef, ...webProps, style: scrollViewStyle }, refreshControl, /* @__PURE__ */ React3.createElement("div", { className: contentContainerClassName, ref: contentRef, style: contentStyle }, children));
1337
1356
  });
1338
1357
  function useValueListener$(key, callback) {
1339
1358
  const ctx = useStateContext();
@@ -1346,6 +1365,21 @@ function useValueListener$(key, callback) {
1346
1365
  }
1347
1366
 
1348
1367
  // src/components/ScrollAdjust.tsx
1368
+ function getScrollAdjustAxis(horizontal) {
1369
+ return horizontal ? {
1370
+ contentSizeKey: "scrollWidth",
1371
+ paddingEndProp: "paddingRight",
1372
+ viewportSizeKey: "clientWidth",
1373
+ x: 1,
1374
+ y: 0
1375
+ } : {
1376
+ contentSizeKey: "scrollHeight",
1377
+ paddingEndProp: "paddingBottom",
1378
+ viewportSizeKey: "clientHeight",
1379
+ x: 0,
1380
+ y: 1
1381
+ };
1382
+ }
1349
1383
  function ScrollAdjust() {
1350
1384
  const ctx = useStateContext();
1351
1385
  const lastScrollOffsetRef = React3.useRef(0);
@@ -1359,32 +1393,34 @@ function ScrollAdjust() {
1359
1393
  if (scrollView && scrollOffset !== lastScrollOffsetRef.current) {
1360
1394
  const scrollDelta = scrollOffset - lastScrollOffsetRef.current;
1361
1395
  if (scrollDelta !== 0) {
1396
+ const axis = getScrollAdjustAxis(!!ctx.state.props.horizontal);
1362
1397
  const contentNode = scrollView.getContentNode();
1363
1398
  const prevScroll = scrollView.getCurrentScrollOffset();
1364
1399
  const el = scrollView.getScrollableNode();
1400
+ const scrollBy = () => scrollView.scrollBy(axis.x * scrollDelta, axis.y * scrollDelta);
1365
1401
  if (!contentNode) {
1366
- scrollView.scrollBy(0, scrollDelta);
1402
+ scrollBy();
1367
1403
  lastScrollOffsetRef.current = scrollOffset;
1368
1404
  return;
1369
1405
  }
1370
- const totalSize = contentNode.scrollHeight;
1371
- const viewportSize = el.clientHeight;
1406
+ const totalSize = contentNode[axis.contentSizeKey];
1407
+ const viewportSize = el[axis.viewportSizeKey];
1372
1408
  const nextScroll = prevScroll + scrollDelta;
1373
1409
  if (scrollDelta > 0 && !ctx.state.adjustingFromInitialMount && totalSize < nextScroll + viewportSize) {
1374
- const paddingBottom = ctx.state.props.stylePaddingBottom || 0;
1410
+ const previousPaddingEnd = contentNode.style[axis.paddingEndProp];
1375
1411
  const pad = (nextScroll + viewportSize - totalSize) * 2;
1376
- contentNode.style.paddingBottom = `${pad}px`;
1412
+ contentNode.style[axis.paddingEndProp] = `${pad}px`;
1377
1413
  void contentNode.offsetHeight;
1378
- scrollView.scrollBy(0, scrollDelta);
1414
+ scrollBy();
1379
1415
  if (resetPaddingRafRef.current !== void 0) {
1380
1416
  cancelAnimationFrame(resetPaddingRafRef.current);
1381
1417
  }
1382
1418
  resetPaddingRafRef.current = requestAnimationFrame(() => {
1383
1419
  resetPaddingRafRef.current = void 0;
1384
- contentNode.style.paddingBottom = paddingBottom ? `${paddingBottom}px` : "0";
1420
+ contentNode.style[axis.paddingEndProp] = previousPaddingEnd;
1385
1421
  });
1386
1422
  } else {
1387
- scrollView.scrollBy(0, scrollDelta);
1423
+ scrollBy();
1388
1424
  }
1389
1425
  }
1390
1426
  lastScrollOffsetRef.current = scrollOffset;
@@ -1398,8 +1434,19 @@ function SnapWrapper({ ScrollComponent, ...props }) {
1398
1434
  const [snapToOffsets] = useArr$(["snapToOffsets"]);
1399
1435
  return /* @__PURE__ */ React3.createElement(ScrollComponent, { ...props, snapToOffsets });
1400
1436
  }
1437
+ function WebAnchoredEndSpace({ horizontal }) {
1438
+ const ctx = useStateContext();
1439
+ const [anchoredEndSpaceSize] = useArr$(["anchoredEndSpaceSize"]);
1440
+ const shouldRenderAnchoredEndSpace = !!ctx.state.props.anchoredEndSpace && (anchoredEndSpaceSize || 0) > 0;
1441
+ if (!shouldRenderAnchoredEndSpace) {
1442
+ return null;
1443
+ }
1444
+ const style = horizontal ? { height: "100%", width: anchoredEndSpaceSize || 0 } : { height: anchoredEndSpaceSize || 0 };
1445
+ return /* @__PURE__ */ React3.createElement("div", { style }, null);
1446
+ }
1401
1447
  var LayoutView = ({ onLayoutChange, refView, children, ...rest }) => {
1402
- const ref = refView != null ? refView : useRef(null);
1448
+ const localRef = useRef(null);
1449
+ const ref = refView != null ? refView : localRef;
1403
1450
  useOnLayoutSync({ onLayoutChange, ref });
1404
1451
  return /* @__PURE__ */ React3.createElement("div", { ...rest, ref }, children);
1405
1452
  };
@@ -1423,7 +1470,6 @@ var ListComponent = typedMemo(function ListComponent2({
1423
1470
  recycleItems,
1424
1471
  ItemSeparatorComponent,
1425
1472
  alignItemsAtEnd: _alignItemsAtEnd,
1426
- waitForInitialLayout,
1427
1473
  onScroll: onScroll2,
1428
1474
  onLayout,
1429
1475
  ListHeaderComponent,
@@ -1445,12 +1491,14 @@ var ListComponent = typedMemo(function ListComponent2({
1445
1491
  }) {
1446
1492
  const ctx = useStateContext();
1447
1493
  const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
1448
- const ScrollComponent = renderScrollComponent ? useMemo(
1449
- () => React3.forwardRef(
1494
+ const ScrollComponent = useMemo(() => {
1495
+ if (!renderScrollComponent) {
1496
+ return ListComponentScrollView;
1497
+ }
1498
+ return React3.forwardRef(
1450
1499
  (props, ref) => renderScrollComponent({ ...props, ref })
1451
- ),
1452
- [renderScrollComponent]
1453
- ) : ListComponentScrollView;
1500
+ );
1501
+ }, [renderScrollComponent]);
1454
1502
  const SnapOrScroll = snapToIndices ? SnapWrapper : ScrollComponent;
1455
1503
  useLayoutEffect(() => {
1456
1504
  if (!ListHeaderComponent) {
@@ -1486,7 +1534,7 @@ var ListComponent = typedMemo(function ListComponent2({
1486
1534
  height: "100%"
1487
1535
  } : {}
1488
1536
  ],
1489
- contentOffset: initialContentOffset ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
1537
+ contentOffset: initialContentOffset !== void 0 ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
1490
1538
  horizontal,
1491
1539
  maintainVisibleContentPosition: maintainVisibleContentPosition.size || maintainVisibleContentPosition.data ? { minIndexForVisible: 0 } : void 0,
1492
1540
  onLayout,
@@ -1506,168 +1554,229 @@ var ListComponent = typedMemo(function ListComponent2({
1506
1554
  ItemSeparatorComponent,
1507
1555
  recycleItems,
1508
1556
  stickyHeaderConfig,
1509
- updateItemSize: updateItemSize2,
1510
- waitForInitialLayout
1557
+ updateItemSize: updateItemSize2
1511
1558
  }
1512
1559
  ),
1513
1560
  ListFooterComponent && /* @__PURE__ */ React3.createElement(LayoutView, { onLayoutChange: onLayoutFooterInternal, style: ListFooterComponentStyle }, getComponent(ListFooterComponent)),
1561
+ /* @__PURE__ */ React3.createElement(WebAnchoredEndSpace, { horizontal }),
1514
1562
  IS_DEV && ENABLE_DEVMODE
1515
1563
  );
1516
1564
  });
1517
-
1518
- // src/core/calculateOffsetForIndex.ts
1519
- function calculateOffsetForIndex(ctx, index) {
1520
- const state = ctx.state;
1521
- return index !== void 0 ? state.positions[index] || 0 : 0;
1522
- }
1523
-
1524
- // src/core/getTopOffsetAdjustment.ts
1525
- function getTopOffsetAdjustment(ctx) {
1526
- return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
1565
+ var WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH = 100;
1566
+ var WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO = 0.9;
1567
+ var WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO = 0.9;
1568
+ function useDevChecksImpl(props) {
1569
+ const ctx = useStateContext();
1570
+ const { childrenMode, keyExtractor, renderScrollComponent, stickyHeaderIndices, stickyIndices, useWindowScroll } = props;
1571
+ useEffect(() => {
1572
+ if (stickyIndices && !stickyHeaderIndices) {
1573
+ warnDevOnce(
1574
+ "stickyIndices",
1575
+ "stickyIndices has been renamed to stickyHeaderIndices. Please update your props to use stickyHeaderIndices."
1576
+ );
1577
+ }
1578
+ }, [stickyHeaderIndices, stickyIndices]);
1579
+ useEffect(() => {
1580
+ if (useWindowScroll && renderScrollComponent) {
1581
+ warnDevOnce(
1582
+ "useWindowScrollRenderScrollComponent",
1583
+ "useWindowScroll is not supported when renderScrollComponent is provided."
1584
+ );
1585
+ }
1586
+ }, [renderScrollComponent, useWindowScroll]);
1587
+ useEffect(() => {
1588
+ if (!keyExtractor && !ctx.state.isFirst && ctx.state.didDataChange && !childrenMode) {
1589
+ warnDevOnce(
1590
+ "keyExtractor",
1591
+ "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."
1592
+ );
1593
+ }
1594
+ }, [childrenMode, ctx, keyExtractor]);
1595
+ useEffect(() => {
1596
+ const state = ctx.state;
1597
+ const dataLength = state.props.data.length;
1598
+ const useWindowScrollResolved = state.props.useWindowScroll;
1599
+ if (useWindowScrollResolved || dataLength < WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH) {
1600
+ return;
1601
+ }
1602
+ const warnIfUnboundedOuterSize = () => {
1603
+ const readyToRender = peek$(ctx, "readyToRender");
1604
+ const numContainers = peek$(ctx, "numContainers") || 0;
1605
+ const totalSize = peek$(ctx, "totalSize") || 0;
1606
+ const scrollLength = ctx.state.scrollLength || 0;
1607
+ if (!readyToRender || totalSize <= 0 || scrollLength <= 0) {
1608
+ return;
1609
+ }
1610
+ const rendersAlmostEverything = numContainers >= Math.ceil(dataLength * WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO);
1611
+ const viewportMatchesContent = scrollLength >= totalSize * WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO;
1612
+ if (rendersAlmostEverything && viewportMatchesContent) {
1613
+ warnDevOnce(
1614
+ "webUnboundedOuterSize",
1615
+ "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."
1616
+ );
1617
+ }
1618
+ };
1619
+ warnIfUnboundedOuterSize();
1620
+ const unsubscribe = [
1621
+ listen$(ctx, "numContainers", warnIfUnboundedOuterSize),
1622
+ listen$(ctx, "readyToRender", warnIfUnboundedOuterSize),
1623
+ listen$(ctx, "totalSize", warnIfUnboundedOuterSize)
1624
+ ];
1625
+ return () => {
1626
+ for (const unsub of unsubscribe) {
1627
+ unsub();
1628
+ }
1629
+ };
1630
+ }, [ctx]);
1527
1631
  }
1528
-
1529
- // src/utils/getId.ts
1530
- function getId(state, index) {
1531
- const { data, keyExtractor } = state.props;
1532
- if (!data) {
1533
- return "";
1534
- }
1535
- const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
1536
- const id = ret;
1537
- state.idCache[index] = id;
1538
- return id;
1632
+ function useDevChecksNoop(_props) {
1539
1633
  }
1634
+ var useDevChecks = IS_DEV ? useDevChecksImpl : useDevChecksNoop;
1540
1635
 
1541
- // src/core/addTotalSize.ts
1542
- function addTotalSize(ctx, key, add) {
1543
- const state = ctx.state;
1544
- const prevTotalSize = state.totalSize;
1545
- let totalSize = state.totalSize;
1546
- if (key === null) {
1547
- totalSize = add;
1548
- if (state.timeoutSetPaddingTop) {
1549
- clearTimeout(state.timeoutSetPaddingTop);
1550
- state.timeoutSetPaddingTop = void 0;
1551
- }
1552
- } else {
1553
- totalSize += add;
1554
- }
1555
- if (prevTotalSize !== totalSize) {
1556
- {
1557
- state.pendingTotalSize = void 0;
1558
- state.totalSize = totalSize;
1559
- set$(ctx, "totalSize", totalSize);
1636
+ // src/core/deferredPublicOnScroll.ts
1637
+ function withResolvedContentOffset(state, event, resolvedOffset) {
1638
+ return {
1639
+ ...event,
1640
+ nativeEvent: {
1641
+ ...event.nativeEvent,
1642
+ contentOffset: state.props.horizontal ? { x: resolvedOffset, y: 0 } : { x: 0, y: resolvedOffset }
1560
1643
  }
1561
- }
1644
+ };
1562
1645
  }
1563
-
1564
- // src/core/setSize.ts
1565
- function setSize(ctx, itemKey, size) {
1646
+ function releaseDeferredPublicOnScroll(ctx, resolvedOffset) {
1647
+ var _a3, _b, _c, _d;
1566
1648
  const state = ctx.state;
1567
- const { sizes } = state;
1568
- const previousSize = sizes.get(itemKey);
1569
- const diff = previousSize !== void 0 ? size - previousSize : size;
1570
- if (diff !== 0) {
1571
- addTotalSize(ctx, itemKey, diff);
1649
+ const deferredEvent = state.deferredPublicOnScrollEvent;
1650
+ state.deferredPublicOnScrollEvent = void 0;
1651
+ if (deferredEvent) {
1652
+ (_d = (_c = state.props).onScroll) == null ? void 0 : _d.call(
1653
+ _c,
1654
+ withResolvedContentOffset(
1655
+ state,
1656
+ deferredEvent,
1657
+ (_b = (_a3 = resolvedOffset != null ? resolvedOffset : state.scrollPending) != null ? _a3 : state.scroll) != null ? _b : 0
1658
+ )
1659
+ );
1572
1660
  }
1573
- sizes.set(itemKey, size);
1574
1661
  }
1575
1662
 
1576
- // src/utils/getItemSize.ts
1577
- function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
1578
- var _a3, _b;
1579
- const state = ctx.state;
1580
- const {
1581
- sizesKnown,
1582
- sizes,
1583
- averageSizes,
1584
- props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType },
1585
- scrollingTo
1586
- } = state;
1587
- const sizeKnown = sizesKnown.get(key);
1588
- if (sizeKnown !== void 0) {
1589
- return sizeKnown;
1590
- }
1591
- let size;
1592
- if (preferCachedSize) {
1593
- const cachedSize = sizes.get(key);
1594
- if (cachedSize !== void 0) {
1595
- return cachedSize;
1596
- }
1597
- }
1598
- const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1599
- if (getFixedItemSize) {
1600
- size = getFixedItemSize(data, index, itemType);
1601
- if (size !== void 0) {
1602
- sizesKnown.set(key, size);
1603
- }
1604
- }
1605
- if (size === void 0 && useAverageSize && sizeKnown === void 0 && !scrollingTo) {
1606
- const averageSizeForType = (_b = averageSizes[itemType]) == null ? void 0 : _b.avg;
1607
- if (averageSizeForType !== void 0) {
1608
- size = roundSize(averageSizeForType);
1609
- }
1610
- }
1611
- if (size === void 0) {
1612
- size = sizes.get(key);
1613
- if (size !== void 0) {
1614
- return size;
1615
- }
1616
- }
1617
- if (size === void 0) {
1618
- size = getEstimatedItemSize ? getEstimatedItemSize(data, index, itemType) : estimatedItemSize;
1619
- }
1620
- setSize(ctx, key, size);
1621
- return size;
1663
+ // src/core/initialScrollSession.ts
1664
+ var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
1665
+ function hasInitialScrollSessionCompletion(completion) {
1666
+ return !!((completion == null ? void 0 : completion.didDispatchNativeScroll) || (completion == null ? void 0 : completion.didRetrySilentInitialScroll) || (completion == null ? void 0 : completion.watchdog));
1667
+ }
1668
+ function clearInitialScrollSession(state) {
1669
+ state.initialScrollSession = void 0;
1670
+ return void 0;
1671
+ }
1672
+ function createInitialScrollSession(options) {
1673
+ const { bootstrap, completion, kind, previousDataLength } = options;
1674
+ return kind === "offset" ? {
1675
+ completion,
1676
+ kind,
1677
+ previousDataLength
1678
+ } : {
1679
+ bootstrap,
1680
+ completion,
1681
+ kind,
1682
+ previousDataLength
1683
+ };
1622
1684
  }
1623
-
1624
- // src/core/calculateOffsetWithOffsetPosition.ts
1625
- function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1626
- var _a3;
1627
- const state = ctx.state;
1628
- const { index, viewOffset, viewPosition } = params;
1629
- let offset = offsetParam;
1630
- if (viewOffset) {
1631
- offset -= viewOffset;
1685
+ function ensureInitialScrollSessionCompletion(state, kind = ((_b) => (_b = ((_a3) => (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind)()) != null ? _b : "bootstrap")()) {
1686
+ var _a4, _b2;
1687
+ if (!state.initialScrollSession) {
1688
+ state.initialScrollSession = createInitialScrollSession({
1689
+ completion: {},
1690
+ kind,
1691
+ previousDataLength: 0
1692
+ });
1693
+ } else if (state.initialScrollSession.kind !== kind) {
1694
+ state.initialScrollSession = createInitialScrollSession({
1695
+ bootstrap: state.initialScrollSession.kind === "bootstrap" ? state.initialScrollSession.bootstrap : void 0,
1696
+ completion: state.initialScrollSession.completion,
1697
+ kind,
1698
+ previousDataLength: state.initialScrollSession.previousDataLength
1699
+ });
1632
1700
  }
1633
- if (index !== void 0) {
1634
- const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
1635
- if (topOffsetAdjustment) {
1636
- offset += topOffsetAdjustment;
1701
+ (_b2 = (_a4 = state.initialScrollSession).completion) != null ? _b2 : _a4.completion = {};
1702
+ return state.initialScrollSession.completion;
1703
+ }
1704
+ var initialScrollCompletion = {
1705
+ didDispatchNativeScroll(state) {
1706
+ var _a3, _b;
1707
+ return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didDispatchNativeScroll);
1708
+ },
1709
+ didRetrySilentInitialScroll(state) {
1710
+ var _a3, _b;
1711
+ return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didRetrySilentInitialScroll);
1712
+ },
1713
+ markInitialScrollNativeDispatch(state) {
1714
+ ensureInitialScrollSessionCompletion(state).didDispatchNativeScroll = true;
1715
+ },
1716
+ markSilentInitialScrollRetry(state) {
1717
+ ensureInitialScrollSessionCompletion(state).didRetrySilentInitialScroll = true;
1718
+ },
1719
+ resetFlags(state) {
1720
+ if (!state.initialScrollSession) {
1721
+ return;
1637
1722
  }
1723
+ const completion = ensureInitialScrollSessionCompletion(state, state.initialScrollSession.kind);
1724
+ completion.didDispatchNativeScroll = void 0;
1725
+ completion.didRetrySilentInitialScroll = void 0;
1638
1726
  }
1639
- if (viewPosition !== void 0 && index !== void 0) {
1640
- const dataLength = state.props.data.length;
1641
- if (dataLength === 0) {
1642
- return offset;
1643
- }
1644
- const isOutOfBounds = index < 0 || index >= dataLength;
1645
- const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
1646
- const itemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1647
- const trailingInset = getContentInsetEnd(state);
1648
- offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1649
- if (!isOutOfBounds && index === state.props.data.length - 1) {
1650
- const footerSize = peek$(ctx, "footerSize") || 0;
1651
- offset += footerSize;
1727
+ };
1728
+ var initialScrollWatchdog = {
1729
+ clear(state) {
1730
+ initialScrollWatchdog.set(state, void 0);
1731
+ },
1732
+ didObserveProgress(newScroll, watchdog) {
1733
+ const previousDistance = Math.abs(watchdog.startScroll - watchdog.targetOffset);
1734
+ const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
1735
+ return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET || nextDistance + INITIAL_SCROLL_MIN_TARGET_OFFSET < previousDistance;
1736
+ },
1737
+ get(state) {
1738
+ var _a3, _b;
1739
+ return (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog;
1740
+ },
1741
+ hasNonZeroTargetOffset(targetOffset) {
1742
+ return targetOffset !== void 0 && targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
1743
+ },
1744
+ isAtZeroTargetOffset(targetOffset) {
1745
+ return targetOffset <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
1746
+ },
1747
+ set(state, watchdog) {
1748
+ var _a3, _b;
1749
+ if (!watchdog && !((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog)) {
1750
+ return;
1652
1751
  }
1752
+ const completion = ensureInitialScrollSessionCompletion(state);
1753
+ completion.watchdog = watchdog ? {
1754
+ startScroll: watchdog.startScroll,
1755
+ targetOffset: watchdog.targetOffset
1756
+ } : void 0;
1653
1757
  }
1654
- return offset;
1655
- }
1656
-
1657
- // src/core/clampScrollOffset.ts
1658
- function clampScrollOffset(ctx, offset, scrollTarget) {
1659
- const state = ctx.state;
1660
- const contentSize = getContentSize(ctx);
1661
- let clampedOffset = offset;
1662
- if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform.OS !== "android")) {
1663
- const baseMaxOffset = Math.max(0, contentSize - state.scrollLength);
1664
- const viewOffset = scrollTarget == null ? void 0 : scrollTarget.viewOffset;
1665
- const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
1666
- const maxOffset = baseMaxOffset + extraEndOffset;
1667
- clampedOffset = Math.min(offset, maxOffset);
1668
- }
1669
- clampedOffset = Math.max(0, clampedOffset);
1670
- return clampedOffset;
1758
+ };
1759
+ function setInitialScrollSession(state, options = {}) {
1760
+ var _a3, _b, _c;
1761
+ const existingSession = state.initialScrollSession;
1762
+ const kind = (_a3 = options.kind) != null ? _a3 : existingSession == null ? void 0 : existingSession.kind;
1763
+ const completion = existingSession == null ? void 0 : existingSession.completion;
1764
+ const hasBootstrapOverride = Object.hasOwn(options, "bootstrap");
1765
+ const bootstrap = kind === "bootstrap" ? hasBootstrapOverride ? options.bootstrap : (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0 : void 0;
1766
+ if (!kind) {
1767
+ return clearInitialScrollSession(state);
1768
+ }
1769
+ if (!state.initialScroll && !bootstrap && !hasInitialScrollSessionCompletion(completion)) {
1770
+ return clearInitialScrollSession(state);
1771
+ }
1772
+ const previousDataLength = (_c = (_b = options.previousDataLength) != null ? _b : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _c : 0;
1773
+ state.initialScrollSession = createInitialScrollSession({
1774
+ bootstrap,
1775
+ completion,
1776
+ kind,
1777
+ previousDataLength
1778
+ });
1779
+ return state.initialScrollSession;
1671
1780
  }
1672
1781
 
1673
1782
  // src/utils/checkThreshold.ts
@@ -1708,11 +1817,16 @@ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, co
1708
1817
  return true;
1709
1818
  };
1710
1819
 
1820
+ // src/utils/hasActiveInitialScroll.ts
1821
+ function hasActiveInitialScroll(state) {
1822
+ return !!(state == null ? void 0 : state.initialScroll) && !state.didFinishInitialScroll;
1823
+ }
1824
+
1711
1825
  // src/utils/checkAtBottom.ts
1712
1826
  function checkAtBottom(ctx) {
1713
1827
  var _a3;
1714
1828
  const state = ctx.state;
1715
- if (!state || state.initialScroll) {
1829
+ if (!state) {
1716
1830
  return;
1717
1831
  }
1718
1832
  const {
@@ -1722,42 +1836,48 @@ function checkAtBottom(ctx) {
1722
1836
  maintainingScrollAtEnd,
1723
1837
  props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
1724
1838
  } = state;
1725
- if (state.initialScroll) {
1726
- return;
1727
- }
1728
1839
  const contentSize = getContentSize(ctx);
1729
- if (contentSize > 0 && queuedInitialLayout && !maintainingScrollAtEnd) {
1730
- const insetEnd = getContentInsetEnd(state);
1840
+ if (contentSize > 0 && queuedInitialLayout) {
1841
+ const insetEnd = getContentInsetEnd(ctx);
1731
1842
  const distanceFromEnd = contentSize - scroll - scrollLength - insetEnd;
1732
1843
  const isContentLess = contentSize < scrollLength;
1733
- state.isAtEnd = isContentLess || distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
1734
- state.isEndReached = checkThreshold(
1735
- distanceFromEnd,
1736
- isContentLess,
1737
- onEndReachedThreshold * scrollLength,
1738
- state.isEndReached,
1739
- state.endReachedSnapshot,
1740
- {
1741
- contentSize,
1742
- dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1743
- scrollPosition: scroll
1744
- },
1745
- (distance) => {
1746
- var _a4, _b;
1747
- return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
1748
- },
1749
- (snapshot) => {
1750
- state.endReachedSnapshot = snapshot;
1751
- },
1752
- true
1844
+ set$(ctx, "isAtEnd", isContentLess || distanceFromEnd <= EDGE_POSITION_EPSILON);
1845
+ set$(ctx, "isNearEnd", isContentLess || distanceFromEnd <= onEndReachedThreshold * scrollLength);
1846
+ set$(
1847
+ ctx,
1848
+ "isWithinMaintainScrollAtEndThreshold",
1849
+ isContentLess || distanceFromEnd <= maintainScrollAtEndThreshold * scrollLength
1753
1850
  );
1851
+ const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || maintainingScrollAtEnd;
1852
+ if (!shouldSkipThresholdChecks) {
1853
+ state.isEndReached = checkThreshold(
1854
+ distanceFromEnd,
1855
+ isContentLess,
1856
+ onEndReachedThreshold * scrollLength,
1857
+ state.isEndReached,
1858
+ state.endReachedSnapshot,
1859
+ {
1860
+ contentSize,
1861
+ dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1862
+ scrollPosition: scroll
1863
+ },
1864
+ (distance) => {
1865
+ var _a4, _b;
1866
+ return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
1867
+ },
1868
+ (snapshot) => {
1869
+ state.endReachedSnapshot = snapshot;
1870
+ },
1871
+ true
1872
+ );
1873
+ }
1754
1874
  }
1755
1875
  }
1756
1876
 
1757
1877
  // src/utils/checkAtTop.ts
1758
1878
  function checkAtTop(ctx) {
1759
1879
  const state = ctx == null ? void 0 : ctx.state;
1760
- if (!state || state.initialScroll || state.scrollingTo) {
1880
+ if (!state) {
1761
1881
  return;
1762
1882
  }
1763
1883
  const {
@@ -1780,31 +1900,33 @@ function checkAtTop(ctx) {
1780
1900
  state.startReachedSnapshot = void 0;
1781
1901
  state.startReachedSnapshotDataChangeEpoch = void 0;
1782
1902
  }
1783
- state.isAtStart = scroll <= 0;
1784
- if (isStartReached && withinThreshold && dataChanged && !allowReentryOnDataChange) {
1785
- return;
1903
+ set$(ctx, "isAtStart", scroll <= EDGE_POSITION_EPSILON);
1904
+ set$(ctx, "isNearStart", scroll <= threshold);
1905
+ const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || !!state.scrollingTo;
1906
+ const shouldDeferDataChangeRefire = isStartReached && withinThreshold && dataChanged && !allowReentryOnDataChange;
1907
+ if (!shouldSkipThresholdChecks && !shouldDeferDataChangeRefire) {
1908
+ state.isStartReached = checkThreshold(
1909
+ scroll,
1910
+ false,
1911
+ threshold,
1912
+ state.isStartReached,
1913
+ allowReentryOnDataChange ? void 0 : startReachedSnapshot,
1914
+ {
1915
+ contentSize: totalSize,
1916
+ dataLength,
1917
+ scrollPosition: scroll
1918
+ },
1919
+ (distance) => {
1920
+ var _a3, _b;
1921
+ return (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
1922
+ },
1923
+ (snapshot) => {
1924
+ state.startReachedSnapshot = snapshot;
1925
+ state.startReachedSnapshotDataChangeEpoch = snapshot ? dataChangeEpoch : void 0;
1926
+ },
1927
+ allowReentryOnDataChange
1928
+ );
1786
1929
  }
1787
- state.isStartReached = checkThreshold(
1788
- scroll,
1789
- false,
1790
- threshold,
1791
- state.isStartReached,
1792
- allowReentryOnDataChange ? void 0 : startReachedSnapshot,
1793
- {
1794
- contentSize: totalSize,
1795
- dataLength,
1796
- scrollPosition: scroll
1797
- },
1798
- (distance) => {
1799
- var _a3, _b;
1800
- return (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
1801
- },
1802
- (snapshot) => {
1803
- state.startReachedSnapshot = snapshot;
1804
- state.startReachedSnapshotDataChangeEpoch = snapshot ? dataChangeEpoch : void 0;
1805
- },
1806
- allowReentryOnDataChange
1807
- );
1808
1930
  }
1809
1931
 
1810
1932
  // src/utils/checkThresholds.ts
@@ -1813,6 +1935,16 @@ function checkThresholds(ctx) {
1813
1935
  checkAtTop(ctx);
1814
1936
  }
1815
1937
 
1938
+ // src/core/recalculateSettledScroll.ts
1939
+ function recalculateSettledScroll(ctx) {
1940
+ var _a3, _b;
1941
+ const state = ctx.state;
1942
+ if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1943
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1944
+ }
1945
+ checkThresholds(ctx);
1946
+ }
1947
+
1816
1948
  // src/utils/setInitialRenderState.ts
1817
1949
  function setInitialRenderState(ctx, {
1818
1950
  didLayout,
@@ -1838,6 +1970,239 @@ function setInitialRenderState(ctx, {
1838
1970
  }
1839
1971
  }
1840
1972
 
1973
+ // src/core/finishInitialScroll.ts
1974
+ var PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS = 2e3;
1975
+ function syncInitialScrollOffset(state, offset) {
1976
+ state.scroll = offset;
1977
+ state.scrollPending = offset;
1978
+ state.scrollPrev = offset;
1979
+ }
1980
+ function clearPreservedInitialScrollTargetTimeout(state) {
1981
+ if (state.timeoutPreservedInitialScrollClear !== void 0) {
1982
+ clearTimeout(state.timeoutPreservedInitialScrollClear);
1983
+ state.timeoutPreservedInitialScrollClear = void 0;
1984
+ }
1985
+ }
1986
+ function clearPreservedInitialScrollTarget(state) {
1987
+ clearPreservedInitialScrollTargetTimeout(state);
1988
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
1989
+ state.initialScroll = void 0;
1990
+ setInitialScrollSession(state);
1991
+ }
1992
+ function finishInitialScroll(ctx, options) {
1993
+ var _a3, _b, _c;
1994
+ const state = ctx.state;
1995
+ if ((options == null ? void 0 : options.resolvedOffset) !== void 0) {
1996
+ syncInitialScrollOffset(state, options.resolvedOffset);
1997
+ } else if ((options == null ? void 0 : options.syncObservedOffset) && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
1998
+ const observedOffset = (_c = (_b = state.refScroller.current) == null ? void 0 : _b.getCurrentScrollOffset) == null ? void 0 : _c.call(_b);
1999
+ if (typeof observedOffset === "number" && Number.isFinite(observedOffset)) {
2000
+ syncInitialScrollOffset(state, observedOffset);
2001
+ }
2002
+ }
2003
+ const complete = () => {
2004
+ var _a4, _b2, _c2, _d, _e;
2005
+ const shouldReleaseDeferredPublicOnScroll = ((_a4 = state.initialScrollSession) == null ? void 0 : _a4.kind) === "bootstrap";
2006
+ const finalScrollOffset = (_d = (_c2 = (_b2 = options == null ? void 0 : options.resolvedOffset) != null ? _b2 : state.scrollPending) != null ? _c2 : state.scroll) != null ? _d : 0;
2007
+ initialScrollWatchdog.clear(state);
2008
+ if ((options == null ? void 0 : options.preserveTarget) && state.initialScroll) {
2009
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
2010
+ setInitialScrollSession(state);
2011
+ clearPreservedInitialScrollTargetTimeout(state);
2012
+ if (options == null ? void 0 : options.schedulePreservedTargetClear) {
2013
+ state.timeoutPreservedInitialScrollClear = setTimeout(() => {
2014
+ var _a5;
2015
+ state.timeoutPreservedInitialScrollClear = void 0;
2016
+ if (!state.didFinishInitialScroll || ((_a5 = state.scrollingTo) == null ? void 0 : _a5.isInitialScroll) || !state.initialScroll) {
2017
+ return;
2018
+ }
2019
+ clearPreservedInitialScrollTarget(state);
2020
+ }, PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS);
2021
+ }
2022
+ } else {
2023
+ clearPreservedInitialScrollTarget(state);
2024
+ }
2025
+ if (options == null ? void 0 : options.recalculateItems) {
2026
+ recalculateSettledScroll(ctx);
2027
+ }
2028
+ setInitialRenderState(ctx, { didInitialScroll: true });
2029
+ if (shouldReleaseDeferredPublicOnScroll) {
2030
+ releaseDeferredPublicOnScroll(ctx, finalScrollOffset);
2031
+ }
2032
+ (_e = options == null ? void 0 : options.onFinished) == null ? void 0 : _e.call(options);
2033
+ };
2034
+ if (options == null ? void 0 : options.waitForCompletionFrame) {
2035
+ requestAnimationFrame(complete);
2036
+ return;
2037
+ }
2038
+ complete();
2039
+ }
2040
+
2041
+ // src/core/calculateOffsetForIndex.ts
2042
+ function calculateOffsetForIndex(ctx, index) {
2043
+ const state = ctx.state;
2044
+ return index !== void 0 ? state.positions[index] || 0 : 0;
2045
+ }
2046
+
2047
+ // src/core/getTopOffsetAdjustment.ts
2048
+ function getTopOffsetAdjustment(ctx) {
2049
+ return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
2050
+ }
2051
+
2052
+ // src/utils/getId.ts
2053
+ function getId(state, index) {
2054
+ const { data, keyExtractor } = state.props;
2055
+ if (!data) {
2056
+ return "";
2057
+ }
2058
+ const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
2059
+ const id = ret;
2060
+ state.idCache[index] = id;
2061
+ return id;
2062
+ }
2063
+
2064
+ // src/core/addTotalSize.ts
2065
+ function addTotalSize(ctx, key, add) {
2066
+ const state = ctx.state;
2067
+ const prevTotalSize = state.totalSize;
2068
+ let totalSize = state.totalSize;
2069
+ if (key === null) {
2070
+ totalSize = add;
2071
+ if (state.timeoutSetPaddingTop) {
2072
+ clearTimeout(state.timeoutSetPaddingTop);
2073
+ state.timeoutSetPaddingTop = void 0;
2074
+ }
2075
+ } else {
2076
+ totalSize += add;
2077
+ }
2078
+ if (prevTotalSize !== totalSize) {
2079
+ {
2080
+ state.pendingTotalSize = void 0;
2081
+ state.totalSize = totalSize;
2082
+ set$(ctx, "totalSize", totalSize);
2083
+ }
2084
+ }
2085
+ }
2086
+
2087
+ // src/core/setSize.ts
2088
+ function setSize(ctx, itemKey, size) {
2089
+ const state = ctx.state;
2090
+ const { sizes } = state;
2091
+ const previousSize = sizes.get(itemKey);
2092
+ const diff = previousSize !== void 0 ? size - previousSize : size;
2093
+ if (diff !== 0) {
2094
+ addTotalSize(ctx, itemKey, diff);
2095
+ }
2096
+ sizes.set(itemKey, size);
2097
+ }
2098
+
2099
+ // src/utils/getItemSize.ts
2100
+ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
2101
+ var _a3, _b, _c;
2102
+ const state = ctx.state;
2103
+ const {
2104
+ sizesKnown,
2105
+ sizes,
2106
+ averageSizes,
2107
+ props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType },
2108
+ scrollingTo
2109
+ } = state;
2110
+ const sizeKnown = sizesKnown.get(key);
2111
+ if (sizeKnown !== void 0) {
2112
+ return sizeKnown;
2113
+ }
2114
+ let size;
2115
+ const renderedSize = sizes.get(key);
2116
+ if (preferCachedSize) {
2117
+ if (renderedSize !== void 0) {
2118
+ return renderedSize;
2119
+ }
2120
+ }
2121
+ const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
2122
+ if (getFixedItemSize) {
2123
+ size = getFixedItemSize(data, index, itemType);
2124
+ if (size !== void 0) {
2125
+ sizesKnown.set(key, size);
2126
+ }
2127
+ }
2128
+ if (size === void 0 && useAverageSize && sizeKnown === void 0 && !scrollingTo) {
2129
+ const averageSizeForType = (_b = averageSizes[itemType]) == null ? void 0 : _b.avg;
2130
+ if (averageSizeForType !== void 0) {
2131
+ size = roundSize(averageSizeForType);
2132
+ }
2133
+ }
2134
+ if (size === void 0 && renderedSize !== void 0) {
2135
+ return renderedSize;
2136
+ }
2137
+ if (size === void 0 && useAverageSize && sizeKnown === void 0 && scrollingTo) {
2138
+ const averageSizeForType = (_c = scrollingTo.averageSizeSnapshot) == null ? void 0 : _c[itemType];
2139
+ if (averageSizeForType !== void 0) {
2140
+ size = roundSize(averageSizeForType);
2141
+ }
2142
+ }
2143
+ if (size === void 0) {
2144
+ size = getEstimatedItemSize ? getEstimatedItemSize(data, index, itemType) : estimatedItemSize;
2145
+ }
2146
+ setSize(ctx, key, size);
2147
+ return size;
2148
+ }
2149
+ function getItemSizeAtIndex(ctx, index) {
2150
+ if (index === void 0 || index < 0) {
2151
+ return void 0;
2152
+ }
2153
+ const targetId = getId(ctx.state, index);
2154
+ return getItemSize(ctx, targetId, index, ctx.state.props.data[index]);
2155
+ }
2156
+
2157
+ // src/core/calculateOffsetWithOffsetPosition.ts
2158
+ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
2159
+ var _a3;
2160
+ const state = ctx.state;
2161
+ const { index, viewOffset, viewPosition } = params;
2162
+ let offset = offsetParam;
2163
+ if (viewOffset) {
2164
+ offset -= viewOffset;
2165
+ }
2166
+ if (index !== void 0) {
2167
+ const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
2168
+ if (topOffsetAdjustment) {
2169
+ offset += topOffsetAdjustment;
2170
+ }
2171
+ }
2172
+ if (viewPosition !== void 0 && index !== void 0) {
2173
+ const dataLength = state.props.data.length;
2174
+ if (dataLength === 0) {
2175
+ return offset;
2176
+ }
2177
+ const isOutOfBounds = index < 0 || index >= dataLength;
2178
+ const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
2179
+ const itemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
2180
+ const trailingInset = getContentInsetEnd(ctx);
2181
+ offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
2182
+ if (!isOutOfBounds && index === state.props.data.length - 1) {
2183
+ const footerSize = peek$(ctx, "footerSize") || 0;
2184
+ offset += footerSize;
2185
+ }
2186
+ }
2187
+ return offset;
2188
+ }
2189
+
2190
+ // src/core/clampScrollOffset.ts
2191
+ function clampScrollOffset(ctx, offset, scrollTarget) {
2192
+ const state = ctx.state;
2193
+ const contentSize = getContentSize(ctx);
2194
+ let clampedOffset = offset;
2195
+ if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform.OS !== "android")) {
2196
+ const baseMaxOffset = Math.max(0, contentSize - state.scrollLength);
2197
+ const viewOffset = scrollTarget == null ? void 0 : scrollTarget.viewOffset;
2198
+ const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
2199
+ const maxOffset = baseMaxOffset + extraEndOffset;
2200
+ clampedOffset = Math.min(offset, maxOffset);
2201
+ }
2202
+ clampedOffset = Math.max(0, clampedOffset);
2203
+ return clampedOffset;
2204
+ }
2205
+
1841
2206
  // src/core/finishScrollTo.ts
1842
2207
  function finishScrollTo(ctx) {
1843
2208
  var _a3, _b;
@@ -1847,193 +2212,1134 @@ function finishScrollTo(ctx) {
1847
2212
  state.pendingScrollResolve = void 0;
1848
2213
  const scrollingTo = state.scrollingTo;
1849
2214
  state.scrollHistory.length = 0;
1850
- state.initialScroll = void 0;
1851
- state.initialScrollUsesOffset = false;
1852
- state.initialAnchor = void 0;
1853
- state.initialNativeScrollWatchdog = void 0;
1854
2215
  state.scrollingTo = void 0;
1855
2216
  if (state.pendingTotalSize !== void 0) {
1856
2217
  addTotalSize(ctx, null, state.pendingTotalSize);
1857
2218
  }
1858
- if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1859
- (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1860
- }
1861
2219
  {
1862
2220
  state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
1863
2221
  }
1864
- setInitialRenderState(ctx, { didInitialScroll: true });
1865
- checkThresholds(ctx);
2222
+ if (scrollingTo.isInitialScroll || state.initialScroll) {
2223
+ const isOffsetSession = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
2224
+ const shouldPreserveResizeTarget = !!scrollingTo.isInitialScroll && !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_b = state.initialScroll) == null ? void 0 : _b.viewPosition) === 1;
2225
+ finishInitialScroll(ctx, {
2226
+ onFinished: () => {
2227
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
2228
+ },
2229
+ preserveTarget: isOffsetSession && state.props.data.length === 0 || shouldPreserveResizeTarget,
2230
+ recalculateItems: true,
2231
+ schedulePreservedTargetClear: shouldPreserveResizeTarget,
2232
+ syncObservedOffset: isOffsetSession,
2233
+ waitForCompletionFrame: !!scrollingTo.waitForInitialScrollCompletionFrame
2234
+ });
2235
+ return;
2236
+ }
2237
+ recalculateSettledScroll(ctx);
1866
2238
  resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1867
2239
  }
1868
2240
  }
1869
-
1870
- // src/core/doScrollTo.ts
1871
- var SCROLL_END_IDLE_MS = 80;
1872
- var SCROLL_END_MAX_MS = 1500;
1873
- var SMOOTH_SCROLL_DURATION_MS = 320;
1874
- var SCROLL_END_TARGET_EPSILON = 1;
1875
- function doScrollTo(ctx, params) {
2241
+
2242
+ // src/core/doScrollTo.ts
2243
+ var SCROLL_END_IDLE_MS = 80;
2244
+ var SCROLL_END_MAX_MS = 1500;
2245
+ var SMOOTH_SCROLL_DURATION_MS = 320;
2246
+ var SCROLL_END_TARGET_EPSILON = 1;
2247
+ function doScrollTo(ctx, params) {
2248
+ var _a3, _b;
2249
+ const state = ctx.state;
2250
+ const { animated, horizontal, offset } = params;
2251
+ const scroller = state.refScroller.current;
2252
+ const node = scroller == null ? void 0 : scroller.getScrollableNode();
2253
+ if (!scroller || !node) {
2254
+ return;
2255
+ }
2256
+ const isAnimated = !!animated;
2257
+ const isHorizontal = !!horizontal;
2258
+ const left = isHorizontal ? offset : 0;
2259
+ const top = isHorizontal ? 0 : offset;
2260
+ scroller.scrollTo({ animated: isAnimated, x: left, y: top });
2261
+ if (isAnimated) {
2262
+ const target = (_b = (_a3 = scroller.getScrollEventTarget) == null ? void 0 : _a3.call(scroller)) != null ? _b : null;
2263
+ listenForScrollEnd(ctx, {
2264
+ readOffset: () => scroller.getCurrentScrollOffset(),
2265
+ target,
2266
+ targetOffset: offset
2267
+ });
2268
+ } else {
2269
+ state.scroll = offset;
2270
+ setTimeout(() => {
2271
+ finishScrollTo(ctx);
2272
+ }, 100);
2273
+ }
2274
+ }
2275
+ function listenForScrollEnd(ctx, params) {
2276
+ const { readOffset, target, targetOffset } = params;
2277
+ if (!target) {
2278
+ finishScrollTo(ctx);
2279
+ return;
2280
+ }
2281
+ const supportsScrollEnd = "onscrollend" in target;
2282
+ let idleTimeout;
2283
+ let settled = false;
2284
+ const targetToken = ctx.state.scrollingTo;
2285
+ const maxTimeout = setTimeout(() => finish("max"), SCROLL_END_MAX_MS);
2286
+ const cleanup = () => {
2287
+ target.removeEventListener("scroll", onScroll2);
2288
+ if (supportsScrollEnd) {
2289
+ target.removeEventListener("scrollend", onScrollEnd);
2290
+ }
2291
+ if (idleTimeout) {
2292
+ clearTimeout(idleTimeout);
2293
+ }
2294
+ clearTimeout(maxTimeout);
2295
+ };
2296
+ const finish = (reason) => {
2297
+ if (settled) return;
2298
+ if (targetToken !== ctx.state.scrollingTo) {
2299
+ settled = true;
2300
+ cleanup();
2301
+ return;
2302
+ }
2303
+ const currentOffset = readOffset();
2304
+ const isNearTarget = Math.abs(currentOffset - targetOffset) <= SCROLL_END_TARGET_EPSILON;
2305
+ if (reason === "scrollend" && !isNearTarget) {
2306
+ return;
2307
+ }
2308
+ settled = true;
2309
+ cleanup();
2310
+ finishScrollTo(ctx);
2311
+ };
2312
+ const onScroll2 = () => {
2313
+ if (idleTimeout) {
2314
+ clearTimeout(idleTimeout);
2315
+ }
2316
+ idleTimeout = setTimeout(() => finish("idle"), SCROLL_END_IDLE_MS);
2317
+ };
2318
+ const onScrollEnd = () => finish("scrollend");
2319
+ target.addEventListener("scroll", onScroll2);
2320
+ if (supportsScrollEnd) {
2321
+ target.addEventListener("scrollend", onScrollEnd);
2322
+ } else {
2323
+ idleTimeout = setTimeout(() => finish("idle"), SMOOTH_SCROLL_DURATION_MS);
2324
+ }
2325
+ }
2326
+
2327
+ // src/core/scrollTo.ts
2328
+ function getAverageSizeSnapshot(state) {
2329
+ if (Object.keys(state.averageSizes).length === 0) {
2330
+ return void 0;
2331
+ }
2332
+ const snapshot = {};
2333
+ for (const itemType in state.averageSizes) {
2334
+ const averages = state.averageSizes[itemType];
2335
+ snapshot[itemType] = averages.avg;
2336
+ }
2337
+ return snapshot;
2338
+ }
2339
+ function syncInitialScrollNativeWatchdog(state, options) {
2340
+ var _a3;
2341
+ const { isInitialScroll, requestedOffset, targetOffset } = options;
2342
+ const existingWatchdog = initialScrollWatchdog.get(state);
2343
+ const shouldWatchInitialNativeScroll = !state.didFinishInitialScroll && (isInitialScroll || !!existingWatchdog) && initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset);
2344
+ const shouldClearInitialNativeScrollWatchdog = !state.didFinishInitialScroll && !!existingWatchdog && initialScrollWatchdog.isAtZeroTargetOffset(requestedOffset);
2345
+ if (shouldWatchInitialNativeScroll) {
2346
+ state.hasScrolled = false;
2347
+ initialScrollWatchdog.set(state, {
2348
+ startScroll: (_a3 = existingWatchdog == null ? void 0 : existingWatchdog.startScroll) != null ? _a3 : state.scroll,
2349
+ targetOffset
2350
+ });
2351
+ return;
2352
+ }
2353
+ if (shouldClearInitialNativeScrollWatchdog) {
2354
+ initialScrollWatchdog.clear(state);
2355
+ }
2356
+ }
2357
+ function scrollTo(ctx, params) {
2358
+ var _a3;
2359
+ const state = ctx.state;
2360
+ const { noScrollingTo, forceScroll, ...scrollTarget } = params;
2361
+ const {
2362
+ animated,
2363
+ isInitialScroll,
2364
+ offset: scrollTargetOffset,
2365
+ precomputedWithViewOffset,
2366
+ waitForInitialScrollCompletionFrame
2367
+ } = scrollTarget;
2368
+ const {
2369
+ props: { horizontal }
2370
+ } = state;
2371
+ if (state.animFrameCheckFinishedScroll) {
2372
+ cancelAnimationFrame(ctx.state.animFrameCheckFinishedScroll);
2373
+ }
2374
+ if (state.timeoutCheckFinishedScrollFallback) {
2375
+ clearTimeout(ctx.state.timeoutCheckFinishedScrollFallback);
2376
+ }
2377
+ const requestedOffset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, scrollTargetOffset, scrollTarget);
2378
+ const shouldPreserveRawInitialOffsetRequest = !!isInitialScroll && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
2379
+ const targetOffset = clampScrollOffset(ctx, requestedOffset, scrollTarget);
2380
+ const offset = shouldPreserveRawInitialOffsetRequest ? requestedOffset : targetOffset;
2381
+ state.scrollHistory.length = 0;
2382
+ if (!noScrollingTo) {
2383
+ if (isInitialScroll) {
2384
+ initialScrollCompletion.resetFlags(state);
2385
+ }
2386
+ const averageSizeSnapshot = getAverageSizeSnapshot(state);
2387
+ state.scrollingTo = {
2388
+ ...scrollTarget,
2389
+ ...averageSizeSnapshot ? { averageSizeSnapshot } : {},
2390
+ targetOffset,
2391
+ waitForInitialScrollCompletionFrame
2392
+ };
2393
+ }
2394
+ state.scrollPending = targetOffset;
2395
+ syncInitialScrollNativeWatchdog(state, { isInitialScroll, requestedOffset: offset, targetOffset });
2396
+ if (forceScroll || !isInitialScroll || Platform.OS === "android") {
2397
+ doScrollTo(ctx, { animated, horizontal, offset });
2398
+ } else {
2399
+ state.scroll = offset;
2400
+ }
2401
+ }
2402
+
2403
+ // src/core/scrollToIndex.ts
2404
+ function clampScrollIndex(index, dataLength) {
2405
+ if (dataLength <= 0) {
2406
+ return -1;
2407
+ }
2408
+ if (index >= dataLength) {
2409
+ return dataLength - 1;
2410
+ }
2411
+ if (index < 0) {
2412
+ return 0;
2413
+ }
2414
+ return index;
2415
+ }
2416
+ function scrollToIndex(ctx, {
2417
+ index,
2418
+ viewOffset = 0,
2419
+ animated = true,
2420
+ forceScroll,
2421
+ isInitialScroll,
2422
+ viewPosition
2423
+ }) {
2424
+ const state = ctx.state;
2425
+ const { data } = state.props;
2426
+ index = clampScrollIndex(index, data.length);
2427
+ const itemSize = getItemSizeAtIndex(ctx, index);
2428
+ const firstIndexOffset = calculateOffsetForIndex(ctx, index);
2429
+ const isLast = index === data.length - 1;
2430
+ if (isLast && viewPosition === void 0) {
2431
+ viewPosition = 1;
2432
+ }
2433
+ state.scrollForNextCalculateItemsInView = void 0;
2434
+ scrollTo(ctx, {
2435
+ animated,
2436
+ forceScroll,
2437
+ index,
2438
+ isInitialScroll,
2439
+ itemSize,
2440
+ offset: firstIndexOffset,
2441
+ viewOffset,
2442
+ viewPosition: viewPosition != null ? viewPosition : 0
2443
+ });
2444
+ }
2445
+
2446
+ // src/core/initialScroll.ts
2447
+ function dispatchInitialScroll(ctx, params) {
2448
+ const { forceScroll, resolvedOffset, target, waitForCompletionFrame } = params;
2449
+ const requestedIndex = target.index;
2450
+ const index = requestedIndex !== void 0 ? clampScrollIndex(requestedIndex, ctx.state.props.data.length) : void 0;
2451
+ const itemSize = getItemSizeAtIndex(ctx, index);
2452
+ scrollTo(ctx, {
2453
+ animated: false,
2454
+ forceScroll,
2455
+ index: index !== void 0 && index >= 0 ? index : void 0,
2456
+ isInitialScroll: true,
2457
+ itemSize,
2458
+ offset: resolvedOffset,
2459
+ precomputedWithViewOffset: true,
2460
+ viewOffset: target.viewOffset,
2461
+ viewPosition: target.viewPosition,
2462
+ waitForInitialScrollCompletionFrame: waitForCompletionFrame
2463
+ });
2464
+ }
2465
+ function setInitialScrollTarget(state, target, options) {
2466
+ var _a3;
2467
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
2468
+ if (state.timeoutPreservedInitialScrollClear !== void 0) {
2469
+ clearTimeout(state.timeoutPreservedInitialScrollClear);
2470
+ state.timeoutPreservedInitialScrollClear = void 0;
2471
+ }
2472
+ state.initialScroll = target;
2473
+ if ((options == null ? void 0 : options.resetDidFinish) && state.didFinishInitialScroll) {
2474
+ state.didFinishInitialScroll = false;
2475
+ }
2476
+ setInitialScrollSession(state, {
2477
+ kind: ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" ? "offset" : "bootstrap"
2478
+ });
2479
+ }
2480
+ function resolveInitialScrollOffset(ctx, initialScroll) {
2481
+ var _a3, _b;
2482
+ const state = ctx.state;
2483
+ if (((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
2484
+ return (_b = initialScroll.contentOffset) != null ? _b : 0;
2485
+ }
2486
+ const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, initialScroll.index) : 0;
2487
+ const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, initialScroll);
2488
+ return clampScrollOffset(ctx, resolvedOffset, initialScroll);
2489
+ }
2490
+ function getAdvanceableInitialScrollState(state, options) {
2491
+ const { didFinishInitialScroll, queuedInitialLayout, scrollingTo } = state;
2492
+ const initialScroll = state.initialScroll;
2493
+ const isInitialScrollInProgress = !!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll);
2494
+ const shouldWaitForInitialLayout = !!(options == null ? void 0 : options.requiresMeasuredLayout) && !queuedInitialLayout && !isInitialScrollInProgress;
2495
+ if (!initialScroll || shouldWaitForInitialLayout || didFinishInitialScroll || scrollingTo && !isInitialScrollInProgress) {
2496
+ return void 0;
2497
+ }
2498
+ return {
2499
+ initialScroll,
2500
+ isInitialScrollInProgress,
2501
+ queuedInitialLayout,
2502
+ scrollingTo
2503
+ };
2504
+ }
2505
+ function advanceMeasuredInitialScroll(ctx, options) {
2506
+ var _a3, _b, _c;
2507
+ const state = ctx.state;
2508
+ const advanceableState = getAdvanceableInitialScrollState(state, {
2509
+ requiresMeasuredLayout: true
2510
+ });
2511
+ if (!advanceableState) {
2512
+ return false;
2513
+ }
2514
+ const { initialScroll, isInitialScrollInProgress, queuedInitialLayout } = advanceableState;
2515
+ const scrollingTo = isInitialScrollInProgress ? advanceableState.scrollingTo : void 0;
2516
+ const resolvedOffset = resolveInitialScrollOffset(ctx, initialScroll);
2517
+ const activeInitialTargetOffset = scrollingTo ? (_a3 = scrollingTo.targetOffset) != null ? _a3 : scrollingTo.offset : void 0;
2518
+ const didOffsetChange = initialScroll.contentOffset === void 0 || Math.abs(initialScroll.contentOffset - resolvedOffset) > 1;
2519
+ const didActiveInitialTargetChange = activeInitialTargetOffset !== void 0 && Math.abs(activeInitialTargetOffset - resolvedOffset) > 1;
2520
+ const isAlreadyAtDesiredInitialTarget = activeInitialTargetOffset !== void 0 && Math.abs(state.scroll - activeInitialTargetOffset) <= 1 && Math.abs(state.scrollPending - activeInitialTargetOffset) <= 1;
2521
+ if (!(options == null ? void 0 : options.forceScroll) && !didOffsetChange && isInitialScrollInProgress && !didActiveInitialTargetChange) {
2522
+ return false;
2523
+ }
2524
+ if ((options == null ? void 0 : options.forceScroll) && isAlreadyAtDesiredInitialTarget) {
2525
+ return false;
2526
+ }
2527
+ if (didOffsetChange && ((_b = state.initialScrollSession) == null ? void 0 : _b.kind) !== "offset") {
2528
+ setInitialScrollTarget(state, { ...initialScroll, contentOffset: resolvedOffset });
2529
+ }
2530
+ const forceScroll = (_c = options == null ? void 0 : options.forceScroll) != null ? _c : !!queuedInitialLayout || isInitialScrollInProgress && didOffsetChange;
2531
+ dispatchInitialScroll(ctx, {
2532
+ forceScroll,
2533
+ resolvedOffset,
2534
+ target: initialScroll
2535
+ });
2536
+ return true;
2537
+ }
2538
+ function advanceOffsetInitialScroll(ctx, options) {
2539
+ var _a3, _b;
2540
+ const state = ctx.state;
2541
+ const advanceableState = getAdvanceableInitialScrollState(state);
2542
+ if (!advanceableState) {
2543
+ return false;
2544
+ }
2545
+ const { initialScroll, queuedInitialLayout } = advanceableState;
2546
+ const resolvedOffset = (_a3 = initialScroll.contentOffset) != null ? _a3 : 0;
2547
+ const isAlreadyAtDesiredInitialTarget = Math.abs(state.scroll - resolvedOffset) <= 1 && Math.abs(state.scrollPending - resolvedOffset) <= 1;
2548
+ if ((options == null ? void 0 : options.forceScroll) && isAlreadyAtDesiredInitialTarget) {
2549
+ return false;
2550
+ }
2551
+ const hasMeasuredScrollLayout = !!state.lastLayout && state.scrollLength > 0;
2552
+ const forceScroll = (_b = options == null ? void 0 : options.forceScroll) != null ? _b : hasMeasuredScrollLayout || !!queuedInitialLayout;
2553
+ dispatchInitialScroll(ctx, {
2554
+ forceScroll,
2555
+ resolvedOffset,
2556
+ target: initialScroll
2557
+ });
2558
+ return true;
2559
+ }
2560
+ function advanceCurrentInitialScrollSession(ctx, options) {
2561
+ var _a3;
2562
+ return ((_a3 = ctx.state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" ? advanceOffsetInitialScroll(ctx, {
2563
+ forceScroll: options == null ? void 0 : options.forceScroll
2564
+ }) : advanceMeasuredInitialScroll(ctx, {
2565
+ forceScroll: options == null ? void 0 : options.forceScroll
2566
+ });
2567
+ }
2568
+
2569
+ // src/utils/checkAllSizesKnown.ts
2570
+ function isNullOrUndefined2(value) {
2571
+ return value === null || value === void 0;
2572
+ }
2573
+ function getMountedIndicesInRange(state, start, end) {
2574
+ if (!isNullOrUndefined2(end) && !isNullOrUndefined2(start) && start >= 0 && end >= 0) {
2575
+ 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);
2576
+ }
2577
+ return [];
2578
+ }
2579
+ function getMountedBufferedIndices(state) {
2580
+ return getMountedIndicesInRange(state, state.startBuffered, state.endBuffered);
2581
+ }
2582
+ function getMountedNoBufferIndices(state) {
2583
+ return getMountedIndicesInRange(state, state.startNoBuffer, state.endNoBuffer);
2584
+ }
2585
+ function checkAllSizesKnown(state, indices = getMountedBufferedIndices(state)) {
2586
+ return indices.length > 0 && indices.every((index) => {
2587
+ const key = getId(state, index);
2588
+ return state.sizesKnown.has(key);
2589
+ });
2590
+ }
2591
+
2592
+ // src/core/bootstrapInitialScroll.ts
2593
+ var DEFAULT_BOOTSTRAP_REVEAL_EPSILON = 1;
2594
+ var DEFAULT_BOOTSTRAP_REVEAL_MAX_FRAMES = 8;
2595
+ var DEFAULT_BOOTSTRAP_REVEAL_MAX_PASSES = 24;
2596
+ var BOOTSTRAP_REVEAL_ABORT_WARNING = "LegendList bootstrap initial scroll aborted after exceeding convergence bounds.";
2597
+ function getBootstrapInitialScrollSession(state) {
2598
+ var _a3;
2599
+ return ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" ? state.initialScrollSession.bootstrap : void 0;
2600
+ }
2601
+ function isOffsetInitialScrollSession(state) {
2602
+ var _a3;
2603
+ return ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
2604
+ }
2605
+ function doVisibleIndicesMatch(previous, next) {
2606
+ if (!previous || previous.length !== next.length) {
2607
+ return false;
2608
+ }
2609
+ for (let i = 0; i < previous.length; i++) {
2610
+ if (previous[i] !== next[i]) {
2611
+ return false;
2612
+ }
2613
+ }
2614
+ return true;
2615
+ }
2616
+ function getBootstrapRevealVisibleIndices(options) {
2617
+ const { dataLength, getSize, offset, positions, scrollLength, startIndex: requestedStartIndex } = options;
2618
+ const endOffset = offset + scrollLength;
2619
+ const visibleIndices = [];
2620
+ let index = requestedStartIndex !== void 0 ? Math.max(0, Math.min(dataLength - 1, requestedStartIndex)) : 0;
2621
+ while (index > 0) {
2622
+ const previousIndex = index - 1;
2623
+ const previousPosition = positions[previousIndex];
2624
+ if (previousPosition === void 0) {
2625
+ index = previousIndex;
2626
+ continue;
2627
+ }
2628
+ const previousSize = getSize(previousIndex);
2629
+ if (previousSize === void 0) {
2630
+ index = previousIndex;
2631
+ continue;
2632
+ }
2633
+ if (previousPosition + previousSize <= offset) {
2634
+ break;
2635
+ }
2636
+ index = previousIndex;
2637
+ }
2638
+ for (; index < dataLength; index++) {
2639
+ const position = positions[index];
2640
+ if (position === void 0) {
2641
+ continue;
2642
+ }
2643
+ const size = getSize(index);
2644
+ if (size === void 0) {
2645
+ continue;
2646
+ }
2647
+ if (position < endOffset && position + size > offset) {
2648
+ visibleIndices.push(index);
2649
+ } else if (visibleIndices.length > 0 && position >= endOffset) {
2650
+ break;
2651
+ }
2652
+ }
2653
+ return visibleIndices;
2654
+ }
2655
+ function shouldAbortBootstrapReveal(options) {
2656
+ const {
2657
+ mountFrameCount,
2658
+ maxFrames = DEFAULT_BOOTSTRAP_REVEAL_MAX_FRAMES,
2659
+ maxPasses = DEFAULT_BOOTSTRAP_REVEAL_MAX_PASSES,
2660
+ passCount
2661
+ } = options;
2662
+ return mountFrameCount >= maxFrames || passCount >= maxPasses;
2663
+ }
2664
+ function abortBootstrapRevealIfNeeded(ctx, options) {
2665
+ if (!shouldAbortBootstrapReveal(options)) {
2666
+ return false;
2667
+ }
2668
+ if (IS_DEV) {
2669
+ console.warn(BOOTSTRAP_REVEAL_ABORT_WARNING);
2670
+ }
2671
+ abortBootstrapInitialScroll(ctx);
2672
+ return true;
2673
+ }
2674
+ function clearBootstrapInitialScrollSession(state) {
2675
+ var _a3;
2676
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2677
+ const frameHandle = bootstrapInitialScroll == null ? void 0 : bootstrapInitialScroll.frameHandle;
2678
+ if (frameHandle !== void 0 && typeof cancelAnimationFrame === "function") {
2679
+ cancelAnimationFrame(frameHandle);
2680
+ }
2681
+ if (bootstrapInitialScroll) {
2682
+ bootstrapInitialScroll.frameHandle = void 0;
2683
+ }
2684
+ setInitialScrollSession(state, {
2685
+ bootstrap: void 0,
2686
+ kind: (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind
2687
+ });
2688
+ }
2689
+ function startBootstrapInitialScrollSession(state, options) {
2690
+ var _a3, _b, _c;
2691
+ const previousBootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2692
+ setInitialScrollSession(state, {
2693
+ bootstrap: {
2694
+ frameHandle: previousBootstrapInitialScroll == null ? void 0 : previousBootstrapInitialScroll.frameHandle,
2695
+ // Re-arming during the initial mount should spend from the same watchdog budget.
2696
+ mountFrameCount: (_a3 = previousBootstrapInitialScroll == null ? void 0 : previousBootstrapInitialScroll.mountFrameCount) != null ? _a3 : 0,
2697
+ passCount: 0,
2698
+ previousResolvedOffset: void 0,
2699
+ scroll: options.scroll,
2700
+ seedContentOffset: (_c = (_b = options.seedContentOffset) != null ? _b : previousBootstrapInitialScroll == null ? void 0 : previousBootstrapInitialScroll.seedContentOffset) != null ? _c : options.scroll,
2701
+ targetIndexSeed: options.targetIndexSeed,
2702
+ visibleIndices: void 0
2703
+ },
2704
+ kind: "bootstrap"
2705
+ });
2706
+ }
2707
+ function resetBootstrapInitialScrollSession(state, options) {
2708
+ var _a3, _b, _c;
2709
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2710
+ if (!bootstrapInitialScroll) {
2711
+ if ((options == null ? void 0 : options.scroll) !== void 0) {
2712
+ startBootstrapInitialScrollSession(state, {
2713
+ scroll: options.scroll,
2714
+ seedContentOffset: options.seedContentOffset,
2715
+ targetIndexSeed: options.targetIndexSeed
2716
+ });
2717
+ }
2718
+ } else {
2719
+ bootstrapInitialScroll.passCount = 0;
2720
+ bootstrapInitialScroll.previousResolvedOffset = void 0;
2721
+ bootstrapInitialScroll.scroll = (_a3 = options == null ? void 0 : options.scroll) != null ? _a3 : bootstrapInitialScroll.scroll;
2722
+ bootstrapInitialScroll.seedContentOffset = (_b = options == null ? void 0 : options.seedContentOffset) != null ? _b : bootstrapInitialScroll.seedContentOffset;
2723
+ bootstrapInitialScroll.targetIndexSeed = (_c = options == null ? void 0 : options.targetIndexSeed) != null ? _c : bootstrapInitialScroll.targetIndexSeed;
2724
+ bootstrapInitialScroll.visibleIndices = void 0;
2725
+ setInitialScrollSession(state, {
2726
+ bootstrap: bootstrapInitialScroll,
2727
+ kind: "bootstrap"
2728
+ });
2729
+ }
2730
+ }
2731
+ function queueBootstrapInitialScrollReevaluation(state) {
2732
+ requestAnimationFrame(() => {
2733
+ var _a3;
2734
+ if (getBootstrapInitialScrollSession(state)) {
2735
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { forceFullItemPositions: true });
2736
+ }
2737
+ });
2738
+ }
2739
+ function ensureBootstrapInitialScrollFrameTicker(ctx) {
2740
+ const state = ctx.state;
2741
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2742
+ if (!bootstrapInitialScroll || bootstrapInitialScroll.frameHandle !== void 0) {
2743
+ return;
2744
+ }
2745
+ const tick = () => {
2746
+ const activeBootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2747
+ if (!activeBootstrapInitialScroll) {
2748
+ return;
2749
+ }
2750
+ activeBootstrapInitialScroll.frameHandle = void 0;
2751
+ activeBootstrapInitialScroll.mountFrameCount += 1;
2752
+ if (abortBootstrapRevealIfNeeded(ctx, {
2753
+ mountFrameCount: activeBootstrapInitialScroll.mountFrameCount,
2754
+ passCount: activeBootstrapInitialScroll.passCount
2755
+ })) {
2756
+ return;
2757
+ }
2758
+ ensureBootstrapInitialScrollFrameTicker(ctx);
2759
+ };
2760
+ bootstrapInitialScroll.frameHandle = requestAnimationFrame(tick);
2761
+ }
2762
+ function rearmBootstrapInitialScroll(ctx, options) {
2763
+ resetBootstrapInitialScrollSession(ctx.state, options);
2764
+ ensureBootstrapInitialScrollFrameTicker(ctx);
2765
+ queueBootstrapInitialScrollReevaluation(ctx.state);
2766
+ }
2767
+ function createInitialScrollAtEndTarget(options) {
2768
+ const { dataLength, footerSize, preserveForFooterLayout, stylePaddingBottom } = options;
2769
+ return {
2770
+ contentOffset: void 0,
2771
+ index: Math.max(0, dataLength - 1),
2772
+ preserveForBottomPadding: true,
2773
+ preserveForFooterLayout,
2774
+ viewOffset: -stylePaddingBottom - footerSize,
2775
+ viewPosition: 1
2776
+ };
2777
+ }
2778
+ function shouldPreserveInitialScrollForBottomPadding(target) {
2779
+ return !!(target == null ? void 0 : target.preserveForBottomPadding);
2780
+ }
2781
+ function shouldPreserveInitialScrollForFooterLayout(target) {
2782
+ return !!(target == null ? void 0 : target.preserveForFooterLayout);
2783
+ }
2784
+ function isRetargetableBottomAlignedInitialScrollTarget(target) {
2785
+ return !!(target && target.viewPosition === 1 && (shouldPreserveInitialScrollForBottomPadding(target) || shouldPreserveInitialScrollForFooterLayout(target)));
2786
+ }
2787
+ function createRetargetedBottomAlignedInitialScroll(options) {
2788
+ const { dataLength, footerSize, initialScrollAtEnd, stylePaddingBottom, target } = options;
2789
+ const preserveForFooterLayout = shouldPreserveInitialScrollForFooterLayout(target);
2790
+ return {
2791
+ ...target,
2792
+ contentOffset: void 0,
2793
+ index: initialScrollAtEnd ? Math.max(0, dataLength - 1) : target.index,
2794
+ preserveForBottomPadding: true,
2795
+ preserveForFooterLayout,
2796
+ viewOffset: -stylePaddingBottom - (preserveForFooterLayout ? footerSize : 0),
2797
+ viewPosition: 1
2798
+ };
2799
+ }
2800
+ function areEquivalentBootstrapInitialScrollTargets(current, next) {
2801
+ return current.index === next.index && current.preserveForBottomPadding === next.preserveForBottomPadding && current.preserveForFooterLayout === next.preserveForFooterLayout && current.viewOffset === next.viewOffset && current.viewPosition === next.viewPosition;
2802
+ }
2803
+ function clearPendingInitialScrollFooterLayout(ctx, options) {
2804
+ const { dataLength, stylePaddingBottom, target } = options;
2805
+ const state = ctx.state;
2806
+ if (!shouldPreserveInitialScrollForFooterLayout(target)) {
2807
+ return;
2808
+ }
2809
+ const clearedFooterTarget = createInitialScrollAtEndTarget({
2810
+ dataLength,
2811
+ footerSize: 0,
2812
+ preserveForFooterLayout: void 0,
2813
+ stylePaddingBottom
2814
+ });
2815
+ setInitialScrollTarget(state, clearedFooterTarget);
2816
+ }
2817
+ function clearFinishedViewportRetargetableInitialScroll(state) {
2818
+ clearPreservedInitialScrollTarget(state);
2819
+ }
2820
+ function didFinishedInitialScrollMoveAwayFromTarget(ctx, target, epsilon = DEFAULT_BOOTSTRAP_REVEAL_EPSILON) {
2821
+ const state = ctx.state;
2822
+ if (!state.didFinishInitialScroll) {
2823
+ return false;
2824
+ }
2825
+ const currentOffset = getObservedBootstrapInitialScrollOffset(state);
2826
+ return Math.abs(currentOffset - resolveInitialScrollOffset(ctx, target)) > epsilon;
2827
+ }
2828
+ function getObservedBootstrapInitialScrollOffset(state) {
2829
+ var _a3, _b, _c, _d;
2830
+ const observedOffset = (_b = (_a3 = state.refScroller.current) == null ? void 0 : _a3.getCurrentScrollOffset) == null ? void 0 : _b.call(_a3);
2831
+ return typeof observedOffset === "number" && Number.isFinite(observedOffset) ? observedOffset : (_d = (_c = state.scrollPending) != null ? _c : state.scroll) != null ? _d : 0;
2832
+ }
2833
+ function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {
2834
+ var _a3, _b;
2835
+ const state = ctx.state;
2836
+ const initialScroll = state.initialScroll;
2837
+ if (!state.didFinishInitialScroll || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) || (initialScroll == null ? void 0 : initialScroll.viewPosition) !== 1) {
2838
+ return;
2839
+ }
2840
+ if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2841
+ if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {
2842
+ clearPendingInitialScrollFooterLayout(ctx, {
2843
+ dataLength: state.props.data.length,
2844
+ stylePaddingBottom: (_b = state.props.stylePaddingBottom) != null ? _b : 0,
2845
+ target: initialScroll
2846
+ });
2847
+ return;
2848
+ }
2849
+ clearFinishedViewportRetargetableInitialScroll(state);
2850
+ }
2851
+ }
2852
+ function startBootstrapInitialScrollOnMount(ctx, options) {
2853
+ var _a3, _b, _c;
2854
+ const { initialScrollAtEnd, target } = options;
2855
+ const state = ctx.state;
2856
+ const offset = resolveInitialScrollOffset(ctx, target);
2857
+ 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);
2858
+ const shouldFinishWithPreservedTarget = state.props.data.length === 0 && target.index !== void 0;
2859
+ if (shouldFinishAtOrigin) {
2860
+ clearBootstrapInitialScrollSession(state);
2861
+ finishInitialScroll(ctx, {
2862
+ resolvedOffset: offset
2863
+ });
2864
+ } else if (shouldFinishWithPreservedTarget) {
2865
+ clearBootstrapInitialScrollSession(state);
2866
+ finishInitialScroll(ctx, {
2867
+ preserveTarget: true,
2868
+ resolvedOffset: offset
2869
+ });
2870
+ } else {
2871
+ startBootstrapInitialScrollSession(state, {
2872
+ scroll: offset,
2873
+ seedContentOffset: 0 ,
2874
+ targetIndexSeed: target.index
2875
+ });
2876
+ ensureBootstrapInitialScrollFrameTicker(ctx);
2877
+ }
2878
+ }
2879
+ function handleBootstrapInitialScrollDataChange(ctx, options) {
2880
+ const { dataLength, didDataChange, initialScrollAtEnd, previousDataLength, stylePaddingBottom } = options;
2881
+ const state = ctx.state;
2882
+ const initialScroll = state.initialScroll;
2883
+ if (isOffsetInitialScrollSession(state) || !initialScroll) {
2884
+ return;
2885
+ }
2886
+ const shouldResetDidFinish = !!(state.didFinishInitialScroll && previousDataLength === 0 && dataLength > 0 && initialScroll.index !== void 0);
2887
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2888
+ const shouldClearFinishedResizePreservation = didDataChange && dataLength > 0 && state.didFinishInitialScroll && !bootstrapInitialScroll && !shouldResetDidFinish;
2889
+ if (shouldClearFinishedResizePreservation) {
2890
+ clearPreservedInitialScrollTarget(state);
2891
+ return;
2892
+ }
2893
+ const shouldRetargetBottomAligned = dataLength > 0 && (initialScrollAtEnd || isRetargetableBottomAlignedInitialScrollTarget(initialScroll));
2894
+ if (!didDataChange && !shouldResetDidFinish && !shouldRetargetBottomAligned) {
2895
+ return;
2896
+ }
2897
+ if (shouldRetargetBottomAligned) {
2898
+ const updatedInitialScroll = initialScrollAtEnd ? createInitialScrollAtEndTarget({
2899
+ dataLength,
2900
+ footerSize: peek$(ctx, "footerSize") || 0,
2901
+ preserveForFooterLayout: shouldPreserveInitialScrollForFooterLayout(initialScroll),
2902
+ stylePaddingBottom
2903
+ }) : createRetargetedBottomAlignedInitialScroll({
2904
+ dataLength,
2905
+ footerSize: peek$(ctx, "footerSize") || 0,
2906
+ initialScrollAtEnd,
2907
+ stylePaddingBottom,
2908
+ target: initialScroll
2909
+ });
2910
+ if (!shouldResetDidFinish && didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2911
+ clearPendingInitialScrollFooterLayout(ctx, {
2912
+ dataLength,
2913
+ stylePaddingBottom,
2914
+ target: initialScroll
2915
+ });
2916
+ return;
2917
+ }
2918
+ if (!areEquivalentBootstrapInitialScrollTargets(initialScroll, updatedInitialScroll) || !!bootstrapInitialScroll || shouldResetDidFinish || didDataChange) {
2919
+ setInitialScrollTarget(state, updatedInitialScroll, {
2920
+ resetDidFinish: shouldResetDidFinish
2921
+ });
2922
+ rearmBootstrapInitialScroll(ctx, {
2923
+ scroll: resolveInitialScrollOffset(ctx, updatedInitialScroll),
2924
+ seedContentOffset: shouldResetDidFinish && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
2925
+ targetIndexSeed: updatedInitialScroll.index
2926
+ });
2927
+ return;
2928
+ }
2929
+ }
2930
+ if (!didDataChange) {
2931
+ return;
2932
+ }
2933
+ if (bootstrapInitialScroll || shouldResetDidFinish) {
2934
+ setInitialScrollTarget(state, initialScroll, {
2935
+ resetDidFinish: shouldResetDidFinish
2936
+ });
2937
+ rearmBootstrapInitialScroll(ctx, {
2938
+ scroll: resolveInitialScrollOffset(ctx, initialScroll),
2939
+ seedContentOffset: shouldResetDidFinish && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
2940
+ targetIndexSeed: initialScroll.index
2941
+ });
2942
+ }
2943
+ }
2944
+ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2945
+ const { dataLength, footerSize, initialScrollAtEnd, stylePaddingBottom } = options;
2946
+ const state = ctx.state;
2947
+ if (!initialScrollAtEnd) {
2948
+ return;
2949
+ }
2950
+ const initialScroll = state.initialScroll;
2951
+ if (isOffsetInitialScrollSession(state) || dataLength === 0 || !initialScroll) {
2952
+ return;
2953
+ }
2954
+ const shouldProcessFooterLayout = !!getBootstrapInitialScrollSession(state) || shouldPreserveInitialScrollForFooterLayout(initialScroll);
2955
+ if (!shouldProcessFooterLayout) {
2956
+ return;
2957
+ }
2958
+ if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2959
+ clearPendingInitialScrollFooterLayout(ctx, {
2960
+ dataLength,
2961
+ stylePaddingBottom,
2962
+ target: initialScroll
2963
+ });
2964
+ } else {
2965
+ const updatedInitialScroll = createInitialScrollAtEndTarget({
2966
+ dataLength,
2967
+ footerSize,
2968
+ preserveForFooterLayout: shouldPreserveInitialScrollForFooterLayout(initialScroll),
2969
+ stylePaddingBottom
2970
+ });
2971
+ const didTargetChange = initialScroll.index !== updatedInitialScroll.index || initialScroll.viewPosition !== updatedInitialScroll.viewPosition || initialScroll.viewOffset !== updatedInitialScroll.viewOffset;
2972
+ if (!didTargetChange) {
2973
+ clearPendingInitialScrollFooterLayout(ctx, {
2974
+ dataLength,
2975
+ stylePaddingBottom,
2976
+ target: initialScroll
2977
+ });
2978
+ } else {
2979
+ const didFinishInitialScroll = !!state.didFinishInitialScroll;
2980
+ setInitialScrollTarget(state, updatedInitialScroll, {
2981
+ resetDidFinish: didFinishInitialScroll
2982
+ });
2983
+ rearmBootstrapInitialScroll(ctx, {
2984
+ scroll: resolveInitialScrollOffset(ctx, updatedInitialScroll),
2985
+ targetIndexSeed: updatedInitialScroll.index
2986
+ });
2987
+ }
2988
+ }
2989
+ }
2990
+ function handleBootstrapInitialScrollLayoutChange(ctx) {
2991
+ const state = ctx.state;
2992
+ const initialScroll = state.initialScroll;
2993
+ if (isOffsetInitialScrollSession(state) || state.props.data.length === 0 || !initialScroll) {
2994
+ return;
2995
+ }
2996
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2997
+ if (!bootstrapInitialScroll && initialScroll.viewPosition !== 1) {
2998
+ return;
2999
+ }
3000
+ const didFinishInitialScroll = state.didFinishInitialScroll;
3001
+ if (didFinishInitialScroll) {
3002
+ setInitialScrollTarget(state, initialScroll, {
3003
+ resetDidFinish: true
3004
+ });
3005
+ state.clearPreservedInitialScrollOnNextFinish = true;
3006
+ }
3007
+ rearmBootstrapInitialScroll(ctx, {
3008
+ scroll: resolveInitialScrollOffset(ctx, initialScroll),
3009
+ seedContentOffset: didFinishInitialScroll && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
3010
+ targetIndexSeed: initialScroll.index
3011
+ });
3012
+ }
3013
+ function evaluateBootstrapInitialScroll(ctx) {
3014
+ var _a3, _b;
3015
+ const state = ctx.state;
3016
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
3017
+ const initialScroll = state.initialScroll;
3018
+ if (!bootstrapInitialScroll || !initialScroll || isOffsetInitialScrollSession(state) || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll)) {
3019
+ return;
3020
+ }
3021
+ bootstrapInitialScroll.passCount += 1;
3022
+ if (abortBootstrapRevealIfNeeded(ctx, {
3023
+ mountFrameCount: bootstrapInitialScroll.mountFrameCount,
3024
+ passCount: bootstrapInitialScroll.passCount
3025
+ })) {
3026
+ return;
3027
+ }
3028
+ if (initialScroll.index !== void 0 && state.startBuffered >= 0 && state.endBuffered >= 0 && initialScroll.index >= state.startBuffered && initialScroll.index <= state.endBuffered) {
3029
+ bootstrapInitialScroll.targetIndexSeed = void 0;
3030
+ }
3031
+ const resolvedOffset = resolveInitialScrollOffset(ctx, initialScroll);
3032
+ const mountedBufferedIndices = getMountedBufferedIndices(state);
3033
+ const areMountedBufferedIndicesMeasured = checkAllSizesKnown(state, mountedBufferedIndices);
3034
+ const didResolvedOffsetChange = Math.abs(bootstrapInitialScroll.scroll - resolvedOffset) > 1;
3035
+ const { data } = state.props;
3036
+ const visibleIndices = getBootstrapRevealVisibleIndices({
3037
+ dataLength: data.length,
3038
+ getSize: (index) => {
3039
+ var _a4, _b2;
3040
+ const id = (_a4 = state.idCache[index]) != null ? _a4 : getId(state, index);
3041
+ return (_b2 = state.sizes.get(id)) != null ? _b2 : getItemSize(ctx, id, index, data[index]);
3042
+ },
3043
+ offset: resolvedOffset,
3044
+ positions: state.positions,
3045
+ scrollLength: state.scrollLength,
3046
+ startIndex: (_b = bootstrapInitialScroll.targetIndexSeed) != null ? _b : state.startBuffered >= 0 ? state.startBuffered : void 0
3047
+ });
3048
+ const areVisibleIndicesMeasured = visibleIndices.length > 0 && visibleIndices.every((index) => {
3049
+ var _a4;
3050
+ const id = (_a4 = state.idCache[index]) != null ? _a4 : getId(state, index);
3051
+ return state.sizesKnown.has(id);
3052
+ });
3053
+ const previousResolvedOffset = bootstrapInitialScroll.previousResolvedOffset;
3054
+ const previousVisibleIndices = bootstrapInitialScroll.visibleIndices;
3055
+ bootstrapInitialScroll.previousResolvedOffset = resolvedOffset;
3056
+ bootstrapInitialScroll.visibleIndices = visibleIndices;
3057
+ if (didResolvedOffsetChange) {
3058
+ bootstrapInitialScroll.scroll = resolvedOffset;
3059
+ queueBootstrapInitialScrollReevaluation(state);
3060
+ return;
3061
+ }
3062
+ if (!areMountedBufferedIndicesMeasured || !areVisibleIndicesMeasured) {
3063
+ return;
3064
+ }
3065
+ const didRevealSettle = previousResolvedOffset !== void 0 && Math.abs(previousResolvedOffset - resolvedOffset) <= DEFAULT_BOOTSTRAP_REVEAL_EPSILON && doVisibleIndicesMatch(previousVisibleIndices, visibleIndices);
3066
+ if (!didRevealSettle) {
3067
+ queueBootstrapInitialScrollReevaluation(state);
3068
+ return;
3069
+ }
3070
+ {
3071
+ clearBootstrapInitialScrollSession(state);
3072
+ dispatchInitialScroll(ctx, {
3073
+ forceScroll: true,
3074
+ resolvedOffset,
3075
+ target: initialScroll,
3076
+ waitForCompletionFrame: Platform.OS === "web"
3077
+ });
3078
+ }
3079
+ }
3080
+ function finishBootstrapInitialScrollWithoutScroll(ctx, resolvedOffset) {
3081
+ var _a3;
3082
+ const state = ctx.state;
3083
+ clearBootstrapInitialScrollSession(state);
3084
+ const shouldPreserveResizeTarget = !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_a3 = state.initialScroll) == null ? void 0 : _a3.viewPosition) === 1;
3085
+ finishInitialScroll(ctx, {
3086
+ preserveTarget: shouldPreserveResizeTarget,
3087
+ recalculateItems: true,
3088
+ resolvedOffset,
3089
+ schedulePreservedTargetClear: shouldPreserveResizeTarget
3090
+ });
3091
+ }
3092
+ function abortBootstrapInitialScroll(ctx) {
3093
+ var _a3, _b, _c, _d;
3094
+ const state = ctx.state;
3095
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
3096
+ const initialScroll = state.initialScroll;
3097
+ if (bootstrapInitialScroll && initialScroll && !isOffsetInitialScrollSession(state) && state.refScroller.current) {
3098
+ clearBootstrapInitialScrollSession(state);
3099
+ dispatchInitialScroll(ctx, {
3100
+ forceScroll: true,
3101
+ resolvedOffset: bootstrapInitialScroll.scroll,
3102
+ target: initialScroll,
3103
+ waitForCompletionFrame: Platform.OS === "web"
3104
+ });
3105
+ } else {
3106
+ finishBootstrapInitialScrollWithoutScroll(
3107
+ ctx,
3108
+ (_d = (_c = (_b = (_a3 = getBootstrapInitialScrollSession(state)) == null ? void 0 : _a3.scroll) != null ? _b : state.scrollPending) != null ? _c : state.scroll) != null ? _d : 0
3109
+ );
3110
+ }
3111
+ }
3112
+
3113
+ // src/core/checkFinishedScroll.ts
3114
+ var INITIAL_SCROLL_MAX_FALLBACK_CHECKS = 20;
3115
+ var INITIAL_SCROLL_COMPLETION_TARGET_EPSILON = 1;
3116
+ var INITIAL_SCROLL_ZERO_TARGET_EPSILON = 1;
3117
+ var SILENT_INITIAL_SCROLL_RETRY_DELAY_MS = 16;
3118
+ function checkFinishedScroll(ctx, options) {
3119
+ const scrollingTo = ctx.state.scrollingTo;
3120
+ if (options == null ? void 0 : options.onlyIfAligned) {
3121
+ if (!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) || scrollingTo.animated) {
3122
+ return;
3123
+ }
3124
+ if (!getResolvedScrollCompletionState(ctx, scrollingTo).isAtResolvedTarget) {
3125
+ return;
3126
+ }
3127
+ }
3128
+ ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
3129
+ }
3130
+ function hasScrollCompletionOwnership(state, options) {
3131
+ const { clampedTargetOffset, scrollingTo } = options;
3132
+ return !scrollingTo.isInitialScroll || state.hasScrolled || clampedTargetOffset <= INITIAL_SCROLL_COMPLETION_TARGET_EPSILON;
3133
+ }
3134
+ function isSilentInitialDispatch(state, scrollingTo) {
3135
+ return !!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && initialScrollCompletion.didDispatchNativeScroll(state) && !state.hasScrolled;
3136
+ }
3137
+ function getInitialScrollWatchdogTargetOffset(state) {
3138
+ var _a3;
3139
+ return (_a3 = initialScrollWatchdog.get(state)) == null ? void 0 : _a3.targetOffset;
3140
+ }
3141
+ function isNativeInitialNonZeroTarget(state) {
3142
+ const targetOffset = getInitialScrollWatchdogTargetOffset(state);
3143
+ return !state.didFinishInitialScroll && initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset);
3144
+ }
3145
+ function shouldFinishInitialScrollWithoutNativeProgress(state, scrollingTo) {
3146
+ var _a3, _b;
3147
+ if (!scrollingTo.isInitialScroll || scrollingTo.animated || !state.didContainersLayout) {
3148
+ return false;
3149
+ }
3150
+ if (((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap") {
3151
+ return false;
3152
+ }
3153
+ const targetOffset = (_b = scrollingTo.targetOffset) != null ? _b : scrollingTo.offset;
3154
+ if (initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset) && initialScrollCompletion.didDispatchNativeScroll(state) && !state.hasScrolled) {
3155
+ return false;
3156
+ }
3157
+ if (initialScrollWatchdog.isAtZeroTargetOffset(targetOffset) || Math.abs(state.scroll - targetOffset) > 1 || Math.abs(state.scrollPending - targetOffset) > 1) {
3158
+ return false;
3159
+ }
3160
+ return !!scrollingTo.waitForInitialScrollCompletionFrame || isNativeInitialNonZeroTarget(state);
3161
+ }
3162
+ function shouldFinishInitialZeroTargetScroll(ctx) {
3163
+ var _a3;
3164
+ const { state } = ctx;
3165
+ 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;
3166
+ }
3167
+ function getResolvedScrollCompletionState(ctx, scrollingTo) {
3168
+ var _a3;
3169
+ const { state } = ctx;
3170
+ const scroll = state.scrollPending;
3171
+ const adjust = state.scrollAdjustHandler.getAdjust();
3172
+ const clampedTargetOffset = (_a3 = scrollingTo.targetOffset) != null ? _a3 : clampScrollOffset(ctx, scrollingTo.offset - (scrollingTo.viewOffset || 0), scrollingTo);
3173
+ const maxOffset = clampScrollOffset(ctx, scroll, scrollingTo);
3174
+ const diff1 = Math.abs(scroll - clampedTargetOffset);
3175
+ const diff2 = Math.abs(diff1 - adjust);
3176
+ return {
3177
+ clampedTargetOffset,
3178
+ isAtResolvedTarget: Math.abs(scroll - maxOffset) < 1 && (diff1 < 1 || !scrollingTo.animated && diff2 < 1)
3179
+ };
3180
+ }
3181
+ function checkFinishedScrollFrame(ctx) {
3182
+ const scrollingTo = ctx.state.scrollingTo;
3183
+ if (!scrollingTo) {
3184
+ return;
3185
+ }
3186
+ const { state } = ctx;
3187
+ state.animFrameCheckFinishedScroll = void 0;
3188
+ const completionState = getResolvedScrollCompletionState(ctx, scrollingTo);
3189
+ if (completionState.isAtResolvedTarget && hasScrollCompletionOwnership(state, {
3190
+ clampedTargetOffset: completionState.clampedTargetOffset,
3191
+ scrollingTo
3192
+ })) {
3193
+ finishScrollTo(ctx);
3194
+ }
3195
+ }
3196
+ function scrollToFallbackOffset(ctx, offset) {
3197
+ var _a3;
3198
+ (_a3 = ctx.state.refScroller.current) == null ? void 0 : _a3.scrollTo({
3199
+ animated: false,
3200
+ x: ctx.state.props.horizontal ? offset : 0,
3201
+ y: ctx.state.props.horizontal ? 0 : offset
3202
+ });
3203
+ }
3204
+ function checkFinishedScrollFallback(ctx) {
1876
3205
  const state = ctx.state;
1877
- const { animated, horizontal, offset } = params;
1878
- const scroller = state.refScroller.current;
1879
- const node = scroller == null ? void 0 : scroller.getScrollableNode();
1880
- if (!scroller || !node) {
3206
+ const scrollingTo = state.scrollingTo;
3207
+ const shouldFinishInitialZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
3208
+ const silentInitialDispatch = isSilentInitialDispatch(state, scrollingTo);
3209
+ const canFinishInitialWithoutNativeProgress = scrollingTo !== void 0 ? shouldFinishInitialScrollWithoutNativeProgress(state, scrollingTo) : false;
3210
+ const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && !shouldFinishInitialZeroTarget && !canFinishInitialWithoutNativeProgress || !state.didContainersLayout;
3211
+ const initialDelay = shouldFinishInitialZeroTarget || canFinishInitialWithoutNativeProgress ? 0 : silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : slowTimeout ? 500 : 100;
3212
+ state.timeoutCheckFinishedScrollFallback = setTimeout(() => {
3213
+ let numChecks = 0;
3214
+ const scheduleFallbackCheck = (delay) => {
3215
+ state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, delay);
3216
+ };
3217
+ const checkHasScrolled = () => {
3218
+ var _c;
3219
+ state.timeoutCheckFinishedScrollFallback = void 0;
3220
+ const isStillScrollingTo = state.scrollingTo;
3221
+ if (isStillScrollingTo) {
3222
+ numChecks++;
3223
+ const isNativeInitialPending = isNativeInitialNonZeroTarget(state) && !state.hasScrolled;
3224
+ const maxChecks = silentInitialDispatch ? 5 : isNativeInitialPending ? INITIAL_SCROLL_MAX_FALLBACK_CHECKS : 5;
3225
+ const shouldFinishZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
3226
+ const canFinishInitialScrollWithoutNativeProgress = shouldFinishInitialScrollWithoutNativeProgress(
3227
+ state,
3228
+ isStillScrollingTo
3229
+ );
3230
+ const completionState = getResolvedScrollCompletionState(ctx, isStillScrollingTo);
3231
+ const canFinishAfterSilentNativeDispatch = silentInitialDispatch && completionState.isAtResolvedTarget && numChecks >= 1;
3232
+ if (shouldFinishZeroTarget || state.hasScrolled || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
3233
+ finishScrollTo(ctx);
3234
+ } else if (isNativeInitialPending && numChecks <= maxChecks) {
3235
+ const targetOffset = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : state.scrollPending;
3236
+ scrollToFallbackOffset(ctx, targetOffset);
3237
+ scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
3238
+ } else {
3239
+ scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
3240
+ }
3241
+ }
3242
+ };
3243
+ checkHasScrolled();
3244
+ }, initialDelay);
3245
+ }
3246
+
3247
+ // src/core/initialScrollLifecycle.ts
3248
+ function handleInitialScrollLayoutReady(ctx) {
3249
+ var _a3;
3250
+ if (!ctx.state.initialScroll) {
1881
3251
  return;
1882
3252
  }
1883
- const isAnimated = !!animated;
1884
- const isHorizontal = !!horizontal;
1885
- const left = isHorizontal ? offset : 0;
1886
- const top = isHorizontal ? 0 : offset;
1887
- scroller.scrollTo({ animated: isAnimated, x: left, y: top });
1888
- if (isAnimated) {
1889
- const target = scroller.getScrollEventTarget();
1890
- listenForScrollEnd(ctx, {
1891
- readOffset: () => scroller.getCurrentScrollOffset(),
1892
- target,
1893
- targetOffset: offset
1894
- });
1895
- } else {
1896
- state.scroll = offset;
1897
- setTimeout(() => {
1898
- finishScrollTo(ctx);
1899
- }, 100);
3253
+ const runScroll = () => advanceCurrentInitialScrollSession(ctx, { forceScroll: true });
3254
+ runScroll();
3255
+ if (((_a3 = ctx.state.initialScrollSession) == null ? void 0 : _a3.kind) !== "offset") {
3256
+ requestAnimationFrame(runScroll);
1900
3257
  }
3258
+ checkFinishedScroll(ctx, { onlyIfAligned: true });
1901
3259
  }
1902
- function listenForScrollEnd(ctx, params) {
1903
- const { readOffset, target, targetOffset } = params;
1904
- if (!target) {
1905
- finishScrollTo(ctx);
3260
+ function initializeInitialScrollOnMount(ctx, options) {
3261
+ var _a3, _b;
3262
+ const { dataLength, hasFooterComponent, initialContentOffset, initialScrollAtEnd, useBootstrapInitialScroll } = options;
3263
+ const state = ctx.state;
3264
+ const initialScroll = state.initialScroll;
3265
+ const resolvedInitialContentOffset = initialContentOffset != null ? initialContentOffset : 0;
3266
+ const preserveForFooterLayout = useBootstrapInitialScroll && initialScrollAtEnd && hasFooterComponent;
3267
+ if (initialScroll && (initialScroll.contentOffset === void 0 || !!initialScroll.preserveForFooterLayout !== preserveForFooterLayout && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) !== "offset")) {
3268
+ setInitialScrollTarget(state, {
3269
+ ...initialScroll,
3270
+ contentOffset: resolvedInitialContentOffset,
3271
+ preserveForFooterLayout
3272
+ });
3273
+ }
3274
+ if (useBootstrapInitialScroll && initialScroll && ((_b = state.initialScrollSession) == null ? void 0 : _b.kind) !== "offset") {
3275
+ startBootstrapInitialScrollOnMount(ctx, {
3276
+ initialScrollAtEnd,
3277
+ target: state.initialScroll
3278
+ });
1906
3279
  return;
1907
3280
  }
1908
- const supportsScrollEnd = "onscrollend" in target;
1909
- let idleTimeout;
1910
- let settled = false;
1911
- const targetToken = ctx.state.scrollingTo;
1912
- const maxTimeout = setTimeout(() => finish("max"), SCROLL_END_MAX_MS);
1913
- const cleanup = () => {
1914
- target.removeEventListener("scroll", onScroll2);
1915
- if (supportsScrollEnd) {
1916
- target.removeEventListener("scrollend", onScrollEnd);
1917
- }
1918
- if (idleTimeout) {
1919
- clearTimeout(idleTimeout);
1920
- }
1921
- clearTimeout(maxTimeout);
1922
- };
1923
- const finish = (reason) => {
1924
- if (settled) return;
1925
- if (targetToken !== ctx.state.scrollingTo) {
1926
- settled = true;
1927
- cleanup();
1928
- return;
1929
- }
1930
- const currentOffset = readOffset();
1931
- const isNearTarget = Math.abs(currentOffset - targetOffset) <= SCROLL_END_TARGET_EPSILON;
1932
- if (reason === "scrollend" && !isNearTarget) {
1933
- return;
1934
- }
1935
- settled = true;
1936
- cleanup();
1937
- finishScrollTo(ctx);
1938
- };
1939
- const onScroll2 = () => {
1940
- if (idleTimeout) {
1941
- clearTimeout(idleTimeout);
3281
+ const hasPendingDataDependentInitialScroll = !!initialScroll && dataLength === 0 && !(resolvedInitialContentOffset === 0 && !initialScrollAtEnd);
3282
+ if (!resolvedInitialContentOffset && !hasPendingDataDependentInitialScroll) {
3283
+ if (initialScroll && !initialScrollAtEnd) {
3284
+ finishInitialScroll(ctx, {
3285
+ resolvedOffset: resolvedInitialContentOffset
3286
+ });
3287
+ } else {
3288
+ setInitialRenderState(ctx, { didInitialScroll: true });
1942
3289
  }
1943
- idleTimeout = setTimeout(() => finish("idle"), SCROLL_END_IDLE_MS);
1944
- };
1945
- const onScrollEnd = () => finish("scrollend");
1946
- target.addEventListener("scroll", onScroll2);
1947
- if (supportsScrollEnd) {
1948
- target.addEventListener("scrollend", onScrollEnd);
1949
- } else {
1950
- idleTimeout = setTimeout(() => finish("idle"), SMOOTH_SCROLL_DURATION_MS);
1951
3290
  }
1952
3291
  }
1953
-
1954
- // src/core/scrollTo.ts
1955
- var WATCHDOG_OFFSET_EPSILON = 1;
1956
- function scrollTo(ctx, params) {
1957
- var _a3, _b;
3292
+ function handleInitialScrollDataChange(ctx, options) {
3293
+ var _a3, _b, _c;
3294
+ const { dataLength, didDataChange, initialScrollAtEnd, stylePaddingBottom, useBootstrapInitialScroll } = options;
1958
3295
  const state = ctx.state;
1959
- const { noScrollingTo, forceScroll, ...scrollTarget } = params;
1960
- const { animated, isInitialScroll, offset: scrollTargetOffset, precomputedWithViewOffset } = scrollTarget;
1961
- const {
1962
- props: { horizontal }
1963
- } = state;
1964
- if (state.animFrameCheckFinishedScroll) {
1965
- cancelAnimationFrame(ctx.state.animFrameCheckFinishedScroll);
1966
- }
1967
- if (state.timeoutCheckFinishedScrollFallback) {
1968
- clearTimeout(ctx.state.timeoutCheckFinishedScrollFallback);
3296
+ const previousDataLength = (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.previousDataLength) != null ? _b : 0;
3297
+ if (state.initialScrollSession) {
3298
+ state.initialScrollSession.previousDataLength = dataLength;
1969
3299
  }
1970
- let offset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, scrollTargetOffset, scrollTarget);
1971
- offset = clampScrollOffset(ctx, offset, scrollTarget);
1972
- state.scrollHistory.length = 0;
1973
- if (!noScrollingTo) {
1974
- state.scrollingTo = {
1975
- ...scrollTarget,
1976
- targetOffset: offset
1977
- };
3300
+ setInitialScrollSession(state);
3301
+ if (useBootstrapInitialScroll) {
3302
+ handleBootstrapInitialScrollDataChange(ctx, {
3303
+ dataLength,
3304
+ didDataChange,
3305
+ initialScrollAtEnd,
3306
+ previousDataLength,
3307
+ stylePaddingBottom
3308
+ });
3309
+ return;
1978
3310
  }
1979
- state.scrollPending = offset;
1980
- const shouldWatchInitialNativeScroll = !state.didFinishInitialScroll && (isInitialScroll || !!state.initialNativeScrollWatchdog) && offset > WATCHDOG_OFFSET_EPSILON;
1981
- const shouldClearInitialNativeScrollWatchdog = !state.didFinishInitialScroll && !!state.initialNativeScrollWatchdog && offset <= WATCHDOG_OFFSET_EPSILON;
1982
- if (shouldWatchInitialNativeScroll) {
1983
- state.hasScrolled = false;
1984
- state.initialNativeScrollWatchdog = {
1985
- startScroll: (_b = (_a3 = state.initialNativeScrollWatchdog) == null ? void 0 : _a3.startScroll) != null ? _b : state.scroll,
1986
- targetOffset: offset
1987
- };
1988
- } else if (shouldClearInitialNativeScrollWatchdog) {
1989
- state.initialNativeScrollWatchdog = void 0;
3311
+ const shouldReplayFinishedOffsetInitialScroll = previousDataLength === 0 && dataLength > 0 && !!state.initialScroll && ((_c = ctx.state.initialScrollSession) == null ? void 0 : _c.kind) === "offset" && !!state.didFinishInitialScroll;
3312
+ if (previousDataLength !== 0 || dataLength === 0 || !state.initialScroll || !state.queuedInitialLayout || state.didFinishInitialScroll && !shouldReplayFinishedOffsetInitialScroll) {
3313
+ return;
1990
3314
  }
1991
- if (forceScroll || !isInitialScroll || Platform.OS === "android") {
1992
- doScrollTo(ctx, { animated, horizontal, offset });
1993
- } else {
1994
- state.scroll = offset;
3315
+ if (shouldReplayFinishedOffsetInitialScroll) {
3316
+ state.didFinishInitialScroll = false;
1995
3317
  }
3318
+ advanceCurrentInitialScrollSession(ctx);
1996
3319
  }
1997
3320
 
1998
- // src/core/doMaintainScrollAtEnd.ts
1999
- function doMaintainScrollAtEnd(ctx) {
3321
+ // src/utils/requestAdjust.ts
3322
+ function requestAdjust(ctx, positionDiff, dataChanged) {
2000
3323
  const state = ctx.state;
2001
- const {
2002
- didContainersLayout,
2003
- isAtEnd,
2004
- pendingNativeMVCPAdjust,
2005
- refScroller,
2006
- props: { maintainScrollAtEnd }
2007
- } = state;
2008
- const shouldMaintainScrollAtEnd = !!(isAtEnd && maintainScrollAtEnd && didContainersLayout);
2009
- if (pendingNativeMVCPAdjust) {
2010
- state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
2011
- return false;
2012
- }
2013
- state.pendingMaintainScrollAtEnd = false;
2014
- if (shouldMaintainScrollAtEnd) {
2015
- const contentSize = getContentSize(ctx);
2016
- if (contentSize < state.scrollLength) {
2017
- state.scroll = 0;
2018
- }
2019
- requestAnimationFrame(() => {
2020
- var _a3;
2021
- if (state.isAtEnd) {
2022
- state.maintainingScrollAtEnd = true;
2023
- (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
2024
- animated: maintainScrollAtEnd.animated
2025
- });
2026
- setTimeout(
2027
- () => {
2028
- state.maintainingScrollAtEnd = false;
2029
- },
2030
- maintainScrollAtEnd.animated ? 500 : 0
2031
- );
3324
+ if (Math.abs(positionDiff) > 0.1) {
3325
+ const doit = () => {
3326
+ {
3327
+ state.scrollAdjustHandler.requestAdjust(positionDiff);
3328
+ if (state.adjustingFromInitialMount) {
3329
+ state.adjustingFromInitialMount--;
3330
+ }
2032
3331
  }
2033
- });
2034
- return true;
3332
+ };
3333
+ state.scroll += positionDiff;
3334
+ state.scrollForNextCalculateItemsInView = void 0;
3335
+ const readyToRender = peek$(ctx, "readyToRender");
3336
+ if (readyToRender) {
3337
+ doit();
3338
+ } else {
3339
+ state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
3340
+ requestAnimationFrame(doit);
3341
+ }
2035
3342
  }
2036
- return false;
2037
3343
  }
2038
3344
 
2039
3345
  // src/core/mvcp.ts
@@ -2158,7 +3464,7 @@ function resolvePendingNativeMVCPAdjust(ctx, newScroll) {
2158
3464
  settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
2159
3465
  return true;
2160
3466
  }
2161
- if (state.pendingMaintainScrollAtEnd && state.isAtEnd && progressTowardAmount > MVCP_POSITION_EPSILON) {
3467
+ if (state.pendingMaintainScrollAtEnd && peek$(ctx, "isWithinMaintainScrollAtEndThreshold") && progressTowardAmount > MVCP_POSITION_EPSILON) {
2162
3468
  settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
2163
3469
  return true;
2164
3470
  }
@@ -2289,118 +3595,110 @@ function prepareMVCP(ctx, dataChanged) {
2289
3595
  positionDiff += diff * scrollingToViewPosition;
2290
3596
  scrollingTo.itemSize = newSize;
2291
3597
  }
2292
- }
2293
- }
2294
- updateAnchorLock(state, {
2295
- anchorId: anchorIdForLock,
2296
- anchorPosition: anchorPositionForLock,
2297
- dataChanged,
2298
- now,
2299
- positionDiff
2300
- });
2301
- if (shouldQueueNativeMVCPAdjust()) {
2302
- state.pendingNativeMVCPAdjust = {
2303
- amount: positionDiff,
2304
- furthestProgressTowardAmount: 0,
2305
- manualApplied: 0,
2306
- startScroll: prevScroll
2307
- };
2308
- maybeApplyPredictedNativeMVCPAdjust(ctx);
2309
- return;
2310
- }
2311
- if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
2312
- requestAdjust(ctx, positionDiff);
2313
- }
2314
- };
2315
- }
2316
- }
2317
-
2318
- // src/core/updateScroll.ts
2319
- function updateScroll(ctx, newScroll, forceUpdate) {
2320
- var _a3;
2321
- const state = ctx.state;
2322
- const { ignoreScrollFromMVCP, lastScrollAdjustForHistory, scrollAdjustHandler, scrollHistory, scrollingTo } = state;
2323
- const prevScroll = state.scroll;
2324
- state.hasScrolled = true;
2325
- state.lastBatchingAction = Date.now();
2326
- const currentTime = Date.now();
2327
- const adjust = scrollAdjustHandler.getAdjust();
2328
- const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
2329
- if (adjustChanged) {
2330
- scrollHistory.length = 0;
2331
- }
2332
- state.lastScrollAdjustForHistory = adjust;
2333
- if (scrollingTo === void 0 && !(scrollHistory.length === 0 && newScroll === state.scroll)) {
2334
- if (!adjustChanged) {
2335
- scrollHistory.push({ scroll: newScroll, time: currentTime });
2336
- }
2337
- }
2338
- if (scrollHistory.length > 5) {
2339
- scrollHistory.shift();
2340
- }
2341
- if (ignoreScrollFromMVCP && !scrollingTo) {
2342
- const { lt, gt } = ignoreScrollFromMVCP;
2343
- if (lt && newScroll < lt || gt && newScroll > gt) {
2344
- state.ignoreScrollFromMVCPIgnored = true;
2345
- return;
2346
- }
2347
- }
2348
- state.scrollPrev = prevScroll;
2349
- state.scrollPrevTime = state.scrollTime;
2350
- state.scroll = newScroll;
2351
- state.scrollTime = currentTime;
2352
- const scrollDelta = Math.abs(newScroll - prevScroll);
2353
- const didResolvePendingNativeMVCPAdjust = resolvePendingNativeMVCPAdjust(ctx, newScroll);
2354
- const scrollLength = state.scrollLength;
2355
- const lastCalculated = state.scrollLastCalculate;
2356
- const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
2357
- const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
2358
- if (shouldUpdate) {
2359
- state.scrollLastCalculate = state.scroll;
2360
- state.ignoreScrollFromMVCPIgnored = false;
2361
- state.lastScrollDelta = scrollDelta;
2362
- const runCalculateItems = () => {
2363
- var _a4;
2364
- (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { doMVCP: scrollingTo !== void 0 });
2365
- checkThresholds(ctx);
3598
+ }
3599
+ }
3600
+ updateAnchorLock(state, {
3601
+ anchorId: anchorIdForLock,
3602
+ anchorPosition: anchorPositionForLock,
3603
+ dataChanged,
3604
+ now,
3605
+ positionDiff
3606
+ });
3607
+ if (shouldQueueNativeMVCPAdjust()) {
3608
+ state.pendingNativeMVCPAdjust = {
3609
+ amount: positionDiff,
3610
+ furthestProgressTowardAmount: 0,
3611
+ manualApplied: 0,
3612
+ startScroll: prevScroll
3613
+ };
3614
+ maybeApplyPredictedNativeMVCPAdjust(ctx);
3615
+ return;
3616
+ }
3617
+ if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
3618
+ requestAdjust(ctx, positionDiff);
3619
+ }
2366
3620
  };
2367
- if (scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength) {
2368
- flushSync(runCalculateItems);
2369
- } else {
2370
- runCalculateItems();
2371
- }
2372
- const shouldMaintainScrollAtEndAfterPendingSettle = !!state.pendingMaintainScrollAtEnd || !!((_a3 = state.props.maintainScrollAtEnd) == null ? void 0 : _a3.onDataChange);
2373
- if (didResolvePendingNativeMVCPAdjust && shouldMaintainScrollAtEndAfterPendingSettle) {
2374
- state.pendingMaintainScrollAtEnd = false;
2375
- doMaintainScrollAtEnd(ctx);
2376
- }
2377
- state.dataChangeNeedsScrollUpdate = false;
2378
- state.lastScrollDelta = 0;
2379
3621
  }
2380
3622
  }
2381
3623
 
2382
- // src/utils/requestAdjust.ts
2383
- function requestAdjust(ctx, positionDiff, dataChanged) {
3624
+ // src/core/syncMountedContainer.ts
3625
+ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3626
+ var _a3, _b, _c, _d, _e, _f, _g, _h;
2384
3627
  const state = ctx.state;
2385
- if (Math.abs(positionDiff) > 0.1) {
2386
- const doit = () => {
2387
- {
2388
- state.scrollAdjustHandler.requestAdjust(positionDiff);
2389
- if (state.adjustingFromInitialMount) {
2390
- state.adjustingFromInitialMount--;
3628
+ const {
3629
+ columns,
3630
+ columnSpans,
3631
+ positions,
3632
+ props: { data, itemsAreEqual, keyExtractor }
3633
+ } = state;
3634
+ const item = data[itemIndex];
3635
+ if (item === void 0) {
3636
+ return { didChangePosition: false, didRefreshData: false };
3637
+ }
3638
+ const updateLayout = (_a3 = options == null ? void 0 : options.updateLayout) != null ? _a3 : true;
3639
+ let didChangePosition = false;
3640
+ let didRefreshData = false;
3641
+ if (updateLayout) {
3642
+ const positionValue = positions[itemIndex];
3643
+ if (positionValue === void 0) {
3644
+ set$(ctx, `containerPosition${containerIndex}`, POSITION_OUT_OF_VIEW);
3645
+ return { didChangePosition: false, didRefreshData: false };
3646
+ }
3647
+ const position = (positionValue || 0) - ((_b = options == null ? void 0 : options.scrollAdjustPending) != null ? _b : 0);
3648
+ const column = columns[itemIndex] || 1;
3649
+ const span = columnSpans[itemIndex] || 1;
3650
+ const prevPos = peek$(ctx, `containerPosition${containerIndex}`);
3651
+ const prevColumn = peek$(ctx, `containerColumn${containerIndex}`);
3652
+ const prevSpan = peek$(ctx, `containerSpan${containerIndex}`);
3653
+ if (position > POSITION_OUT_OF_VIEW && position !== prevPos) {
3654
+ set$(ctx, `containerPosition${containerIndex}`, position);
3655
+ didChangePosition = true;
3656
+ }
3657
+ if (column >= 0 && column !== prevColumn) {
3658
+ set$(ctx, `containerColumn${containerIndex}`, column);
3659
+ }
3660
+ if (span !== prevSpan) {
3661
+ set$(ctx, `containerSpan${containerIndex}`, span);
3662
+ }
3663
+ }
3664
+ const prevData = peek$(ctx, `containerItemData${containerIndex}`);
3665
+ if (prevData !== item) {
3666
+ 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;
3667
+ const cachedComparison = (_e = pendingDataComparison == null ? void 0 : pendingDataComparison.byIndex[itemIndex]) != null ? _e : 0;
3668
+ if (cachedComparison === 2) {
3669
+ set$(ctx, `containerItemData${containerIndex}`, item);
3670
+ didRefreshData = true;
3671
+ } else if (cachedComparison !== 1) {
3672
+ const itemKey = (_g = (_f = peek$(ctx, `containerItemKey${containerIndex}`)) != null ? _f : state.idCache[itemIndex]) != null ? _g : getId(state, itemIndex);
3673
+ const prevKey = keyExtractor == null ? void 0 : keyExtractor(prevData, itemIndex);
3674
+ if (prevData === void 0 || !keyExtractor || prevKey !== itemKey) {
3675
+ set$(ctx, `containerItemData${containerIndex}`, item);
3676
+ didRefreshData = true;
3677
+ } else if (!itemsAreEqual) {
3678
+ set$(ctx, `containerItemData${containerIndex}`, item);
3679
+ didRefreshData = true;
3680
+ } else {
3681
+ const isEqual = itemsAreEqual(prevData, item, itemIndex, data);
3682
+ if (!state.pendingDataComparison || state.pendingDataComparison.previousData !== state.previousData || state.pendingDataComparison.nextData !== data) {
3683
+ if (state.previousData) {
3684
+ state.pendingDataComparison = {
3685
+ byIndex: [],
3686
+ nextData: data,
3687
+ previousData: state.previousData
3688
+ };
3689
+ }
3690
+ }
3691
+ if ((_h = state.pendingDataComparison) == null ? void 0 : _h.byIndex) {
3692
+ state.pendingDataComparison.byIndex[itemIndex] = isEqual ? 1 : 2;
3693
+ }
3694
+ if (!isEqual) {
3695
+ set$(ctx, `containerItemData${containerIndex}`, item);
3696
+ didRefreshData = true;
2391
3697
  }
2392
3698
  }
2393
- };
2394
- state.scroll += positionDiff;
2395
- state.scrollForNextCalculateItemsInView = void 0;
2396
- const readyToRender = peek$(ctx, "readyToRender");
2397
- if (readyToRender) {
2398
- doit();
2399
- } else {
2400
- state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
2401
- requestAnimationFrame(doit);
2402
3699
  }
2403
3700
  }
3701
+ return { didChangePosition, didRefreshData };
2404
3702
  }
2405
3703
 
2406
3704
  // src/core/prepareColumnStartState.ts
@@ -2565,9 +3863,10 @@ function updateSnapToOffsets(ctx) {
2565
3863
  }
2566
3864
 
2567
3865
  // src/core/updateItemPositions.ts
2568
- function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP } = {
3866
+ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP, optimizeForVisibleWindow = false } = {
2569
3867
  doMVCP: false,
2570
3868
  forceFullUpdate: false,
3869
+ optimizeForVisibleWindow: false,
2571
3870
  scrollBottomBuffered: -1,
2572
3871
  startIndex: 0
2573
3872
  }) {
@@ -2592,7 +3891,7 @@ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffere
2592
3891
  const layoutConfig = overrideItemLayout ? { span: 1 } : void 0;
2593
3892
  const lastScrollDelta = state.lastScrollDelta;
2594
3893
  const velocity = getScrollVelocity(state);
2595
- const shouldOptimize = !forceFullUpdate && !dataChanged && (Math.abs(velocity) > 0 || state.scrollLength > 0 && lastScrollDelta > state.scrollLength);
3894
+ const shouldOptimize = !forceFullUpdate && !dataChanged && (optimizeForVisibleWindow || Math.abs(velocity) > 0 || state.scrollLength > 0 && lastScrollDelta > state.scrollLength);
2596
3895
  const maxVisibleArea = scrollBottomBuffered + 1e3;
2597
3896
  const useAverageSize = !getEstimatedItemSize;
2598
3897
  const preferCachedSize = !doMVCP || dataChanged || state.scrollAdjustHandler.getAdjust() !== 0 || ((_b = peek$(ctx, "scrollAdjustPending")) != null ? _b : 0) !== 0;
@@ -2707,7 +4006,15 @@ function ensureViewabilityState(ctx, configId) {
2707
4006
  }
2708
4007
  let state = map.get(configId);
2709
4008
  if (!state) {
2710
- state = { end: -1, previousEnd: -1, previousStart: -1, start: -1, viewableItems: [] };
4009
+ state = {
4010
+ end: -1,
4011
+ endBuffered: -1,
4012
+ previousEnd: -1,
4013
+ previousStart: -1,
4014
+ start: -1,
4015
+ startBuffered: -1,
4016
+ viewableItems: []
4017
+ };
2711
4018
  map.set(configId, state);
2712
4019
  }
2713
4020
  return state;
@@ -2727,7 +4034,7 @@ function setupViewability(props) {
2727
4034
  }
2728
4035
  return viewabilityConfigCallbackPairs;
2729
4036
  }
2730
- function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollSize, start, end) {
4037
+ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollSize, start, end, startBuffered = start, endBuffered = end) {
2731
4038
  const {
2732
4039
  timeouts,
2733
4040
  props: { data }
@@ -2736,6 +4043,8 @@ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollS
2736
4043
  const viewabilityState = ensureViewabilityState(ctx, viewabilityConfigCallbackPair.viewabilityConfig.id);
2737
4044
  viewabilityState.start = start;
2738
4045
  viewabilityState.end = end;
4046
+ viewabilityState.startBuffered = startBuffered;
4047
+ viewabilityState.endBuffered = endBuffered;
2739
4048
  if (viewabilityConfigCallbackPair.viewabilityConfig.minimumViewTime) {
2740
4049
  const timer = setTimeout(() => {
2741
4050
  timeouts.delete(timer);
@@ -2751,7 +4060,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
2751
4060
  const { viewabilityConfig, onViewableItemsChanged } = viewabilityConfigCallbackPair;
2752
4061
  const configId = viewabilityConfig.id;
2753
4062
  const viewabilityState = ensureViewabilityState(ctx, configId);
2754
- const { viewableItems: previousViewableItems, start, end } = viewabilityState;
4063
+ const { viewableItems: previousViewableItems, start, end, startBuffered, endBuffered } = viewabilityState;
2755
4064
  const viewabilityTokens = /* @__PURE__ */ new Map();
2756
4065
  for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
2757
4066
  viewabilityTokens.set(
@@ -2820,7 +4129,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
2820
4129
  maybeUpdateViewabilityCallback(ctx, configId, change.containerId, change);
2821
4130
  }
2822
4131
  if (onViewableItemsChanged) {
2823
- onViewableItemsChanged({ changed, viewableItems });
4132
+ onViewableItemsChanged({ changed, end, endBuffered, start, startBuffered, viewableItems });
2824
4133
  }
2825
4134
  }
2826
4135
  for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
@@ -2916,23 +4225,6 @@ function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
2916
4225
  var unstableBatchedUpdates = ReactDOM.unstable_batchedUpdates;
2917
4226
  var batchedUpdates = typeof unstableBatchedUpdates === "function" ? unstableBatchedUpdates : (fn) => fn();
2918
4227
 
2919
- // src/utils/checkAllSizesKnown.ts
2920
- function isNullOrUndefined2(value) {
2921
- return value === null || value === void 0;
2922
- }
2923
- function checkAllSizesKnown(state) {
2924
- const { startBuffered, endBuffered, sizesKnown } = state;
2925
- if (!isNullOrUndefined2(endBuffered) && !isNullOrUndefined2(startBuffered) && startBuffered >= 0 && endBuffered >= 0) {
2926
- let areAllKnown = true;
2927
- for (let i = startBuffered; areAllKnown && i <= endBuffered; i++) {
2928
- const key = getId(state, i);
2929
- areAllKnown && (areAllKnown = sizesKnown.has(key));
2930
- }
2931
- return areAllKnown;
2932
- }
2933
- return false;
2934
- }
2935
-
2936
4228
  // src/utils/findAvailableContainers.ts
2937
4229
  function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers) {
2938
4230
  const numContainers = peek$(ctx, "numContainers");
@@ -3055,97 +4347,11 @@ function comparatorByDistance(a, b) {
3055
4347
  return b.distance - a.distance;
3056
4348
  }
3057
4349
 
3058
- // src/core/scrollToIndex.ts
3059
- function scrollToIndex(ctx, {
3060
- index,
3061
- viewOffset = 0,
3062
- animated = true,
3063
- forceScroll,
3064
- isInitialScroll,
3065
- viewPosition
3066
- }) {
3067
- const state = ctx.state;
3068
- const { data } = state.props;
3069
- if (index >= data.length) {
3070
- index = data.length - 1;
3071
- } else if (index < 0) {
3072
- index = 0;
3073
- }
3074
- const firstIndexOffset = calculateOffsetForIndex(ctx, index);
3075
- const isLast = index === data.length - 1;
3076
- if (isLast && viewPosition === void 0) {
3077
- viewPosition = 1;
3078
- }
3079
- state.scrollForNextCalculateItemsInView = void 0;
3080
- const targetId = getId(state, index);
3081
- const itemSize = getItemSize(ctx, targetId, index, state.props.data[index]);
3082
- scrollTo(ctx, {
3083
- animated,
3084
- forceScroll,
3085
- index,
3086
- isInitialScroll,
3087
- itemSize,
3088
- offset: firstIndexOffset,
3089
- viewOffset,
3090
- viewPosition: viewPosition != null ? viewPosition : 0
3091
- });
3092
- }
3093
-
3094
- // src/utils/performInitialScroll.ts
3095
- function performInitialScroll(ctx, params) {
3096
- var _a3;
3097
- const { forceScroll, initialScrollUsesOffset, resolvedOffset, target } = params;
3098
- if (initialScrollUsesOffset || resolvedOffset !== void 0) {
3099
- scrollTo(ctx, {
3100
- animated: false,
3101
- forceScroll,
3102
- index: initialScrollUsesOffset ? void 0 : target.index,
3103
- isInitialScroll: true,
3104
- offset: (_a3 = resolvedOffset != null ? resolvedOffset : target.contentOffset) != null ? _a3 : 0,
3105
- precomputedWithViewOffset: resolvedOffset !== void 0
3106
- });
3107
- return;
3108
- }
3109
- if (target.index === void 0) {
3110
- return;
3111
- }
3112
- scrollToIndex(ctx, {
3113
- ...target,
3114
- animated: false,
3115
- forceScroll,
3116
- isInitialScroll: true
3117
- });
3118
- }
3119
-
3120
4350
  // src/utils/setDidLayout.ts
3121
4351
  function setDidLayout(ctx) {
3122
4352
  const state = ctx.state;
3123
- const { initialScroll } = state;
3124
4353
  state.queuedInitialLayout = true;
3125
4354
  checkAtBottom(ctx);
3126
- if (initialScroll) {
3127
- const runScroll = () => {
3128
- var _a3, _b;
3129
- const target = state.initialScroll;
3130
- if (!target) {
3131
- return;
3132
- }
3133
- const activeInitialTargetOffset = ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) ? (_b = state.scrollingTo.targetOffset) != null ? _b : state.scrollingTo.offset : void 0;
3134
- const desiredInitialTargetOffset = state.initialScrollUsesOffset ? target.contentOffset : activeInitialTargetOffset;
3135
- const isAlreadyAtDesiredInitialTarget = desiredInitialTargetOffset !== void 0 && Math.abs(state.scroll - desiredInitialTargetOffset) <= 1 && Math.abs(state.scrollPending - desiredInitialTargetOffset) <= 1;
3136
- if (!isAlreadyAtDesiredInitialTarget) {
3137
- performInitialScroll(ctx, {
3138
- forceScroll: true,
3139
- initialScrollUsesOffset: state.initialScrollUsesOffset,
3140
- // Offset-based initial scrolls do not need item lookup, so they can run even before data exists.
3141
- // Re-run on the next frame to pick up measured viewport size without waiting for index resolution.
3142
- target
3143
- });
3144
- }
3145
- };
3146
- runScroll();
3147
- requestAnimationFrame(runScroll);
3148
- }
3149
4355
  setInitialRenderState(ctx, { didLayout: true });
3150
4356
  }
3151
4357
 
@@ -3220,7 +4426,7 @@ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentSt
3220
4426
  function calculateItemsInView(ctx, params = {}) {
3221
4427
  const state = ctx.state;
3222
4428
  batchedUpdates(() => {
3223
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
4429
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
3224
4430
  const {
3225
4431
  columns,
3226
4432
  columnSpans,
@@ -3228,7 +4434,6 @@ function calculateItemsInView(ctx, params = {}) {
3228
4434
  enableScrollForNextCalculateItemsInView,
3229
4435
  idCache,
3230
4436
  indexByKey,
3231
- initialScroll,
3232
4437
  minIndexSizeChanged,
3233
4438
  positions,
3234
4439
  props: {
@@ -3236,7 +4441,6 @@ function calculateItemsInView(ctx, params = {}) {
3236
4441
  alwaysRenderIndicesSet,
3237
4442
  drawDistance,
3238
4443
  getItemType,
3239
- itemsAreEqual,
3240
4444
  keyExtractor,
3241
4445
  onStickyHeaderChange
3242
4446
  },
@@ -3252,6 +4456,8 @@ function calculateItemsInView(ctx, params = {}) {
3252
4456
  const alwaysRenderArr = alwaysRenderIndicesArr || [];
3253
4457
  const alwaysRenderSet = alwaysRenderIndicesSet || /* @__PURE__ */ new Set();
3254
4458
  const { dataChanged, doMVCP, forceFullItemPositions } = params;
4459
+ const bootstrapInitialScrollState = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" ? state.initialScrollSession.bootstrap : void 0;
4460
+ const suppressInitialScrollSideEffects = !!bootstrapInitialScrollState;
3255
4461
  const prevNumContainers = peek$(ctx, "numContainers");
3256
4462
  if (!data || scrollLength === 0 || !prevNumContainers) {
3257
4463
  return;
@@ -3261,17 +4467,13 @@ function calculateItemsInView(ctx, params = {}) {
3261
4467
  const numColumns = peek$(ctx, "numColumns");
3262
4468
  const speed = getScrollVelocity(state);
3263
4469
  const scrollExtra = 0;
3264
- const { queuedInitialLayout } = state;
3265
- let { scroll: scrollState } = state;
3266
- if (!queuedInitialLayout && initialScroll) {
3267
- const updatedOffset = state.initialScrollUsesOffset ? (_a3 = initialScroll.contentOffset) != null ? _a3 : 0 : calculateOffsetWithOffsetPosition(
3268
- ctx,
3269
- calculateOffsetForIndex(ctx, initialScroll.index),
3270
- initialScroll
3271
- );
3272
- scrollState = updatedOffset;
3273
- }
3274
- const scrollAdjustPending = (_b = peek$(ctx, "scrollAdjustPending")) != null ? _b : 0;
4470
+ const { initialScroll, queuedInitialLayout } = state;
4471
+ const scrollState = suppressInitialScrollSideEffects ? (_b = bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.scroll) != null ? _b : state.scroll : !queuedInitialLayout && hasActiveInitialScroll(state) && initialScroll ? (
4472
+ // Before the initial layout settles, keep viewport math anchored to the
4473
+ // current initial-scroll target instead of transient native adjustments.
4474
+ resolveInitialScrollOffset(ctx, initialScroll)
4475
+ ) : state.scroll;
4476
+ const scrollAdjustPending = (_c = peek$(ctx, "scrollAdjustPending")) != null ? _c : 0;
3275
4477
  const scrollAdjustPad = scrollAdjustPending - topPad;
3276
4478
  let scroll = Math.round(scrollState + scrollExtra + scrollAdjustPad);
3277
4479
  if (scroll + scrollLength > totalSize) {
@@ -3295,7 +4497,7 @@ function calculateItemsInView(ctx, params = {}) {
3295
4497
  const scrollTopBuffered = scroll - scrollBufferTop;
3296
4498
  const scrollBottom = scroll + scrollLength + (scroll < 0 ? -scroll : 0);
3297
4499
  const scrollBottomBuffered = scrollBottom + scrollBufferBottom;
3298
- if (!dataChanged && !forceFullItemPositions && scrollForNextCalculateItemsInView) {
4500
+ if (!suppressInitialScrollSideEffects && !dataChanged && !forceFullItemPositions && scrollForNextCalculateItemsInView) {
3299
4501
  const { top, bottom } = scrollForNextCalculateItemsInView;
3300
4502
  if (top === null && bottom === null) {
3301
4503
  state.scrollForNextCalculateItemsInView = void 0;
@@ -3305,7 +4507,7 @@ function calculateItemsInView(ctx, params = {}) {
3305
4507
  }
3306
4508
  }
3307
4509
  }
3308
- const checkMVCP = doMVCP ? prepareMVCP(ctx, dataChanged) : void 0;
4510
+ const checkMVCP = doMVCP && !suppressInitialScrollSideEffects ? prepareMVCP(ctx, dataChanged) : void 0;
3309
4511
  if (dataChanged) {
3310
4512
  indexByKey.clear();
3311
4513
  idCache.length = 0;
@@ -3313,10 +4515,12 @@ function calculateItemsInView(ctx, params = {}) {
3313
4515
  columns.length = 0;
3314
4516
  columnSpans.length = 0;
3315
4517
  }
3316
- const startIndex = forceFullItemPositions || dataChanged ? 0 : (_c = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _c : 0;
4518
+ const startIndex = forceFullItemPositions || dataChanged ? 0 : (_d = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _d : 0;
4519
+ const optimizeForVisibleWindow = !forceFullItemPositions && !dataChanged && numColumns > 1 && minIndexSizeChanged !== void 0;
3317
4520
  updateItemPositions(ctx, dataChanged, {
3318
4521
  doMVCP,
3319
4522
  forceFullUpdate: !!forceFullItemPositions,
4523
+ optimizeForVisibleWindow,
3320
4524
  scrollBottomBuffered,
3321
4525
  startIndex
3322
4526
  });
@@ -3330,11 +4534,11 @@ function calculateItemsInView(ctx, params = {}) {
3330
4534
  let startBufferedId = null;
3331
4535
  let endNoBuffer = null;
3332
4536
  let endBuffered = null;
3333
- let loopStart = !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
4537
+ let loopStart = (_e = suppressInitialScrollSideEffects ? bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.targetIndexSeed : void 0) != null ? _e : !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
3334
4538
  for (let i = loopStart; i >= 0; i--) {
3335
- const id = (_d = idCache[i]) != null ? _d : getId(state, i);
4539
+ const id = (_f = idCache[i]) != null ? _f : getId(state, i);
3336
4540
  const top = positions[i];
3337
- const size = (_e = sizes.get(id)) != null ? _e : getItemSize(ctx, id, i, data[i]);
4541
+ const size = (_g = sizes.get(id)) != null ? _g : getItemSize(ctx, id, i, data[i]);
3338
4542
  const bottom = top + size;
3339
4543
  if (bottom > scroll - scrollBufferTop) {
3340
4544
  loopStart = i;
@@ -3365,8 +4569,8 @@ function calculateItemsInView(ctx, params = {}) {
3365
4569
  let firstFullyOnScreenIndex;
3366
4570
  const dataLength = data.length;
3367
4571
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
3368
- const id = (_f = idCache[i]) != null ? _f : getId(state, i);
3369
- const size = (_g = sizes.get(id)) != null ? _g : getItemSize(ctx, id, i, data[i]);
4572
+ const id = (_h = idCache[i]) != null ? _h : getId(state, i);
4573
+ const size = (_i = sizes.get(id)) != null ? _i : getItemSize(ctx, id, i, data[i]);
3370
4574
  const top = positions[i];
3371
4575
  if (!foundEnd) {
3372
4576
  if (startNoBuffer === null && top + size > scroll) {
@@ -3405,7 +4609,7 @@ function calculateItemsInView(ctx, params = {}) {
3405
4609
  const firstVisibleAnchorIndex = firstFullyOnScreenIndex != null ? firstFullyOnScreenIndex : startNoBuffer;
3406
4610
  if (firstVisibleAnchorIndex !== null && firstVisibleAnchorIndex !== void 0 && endNoBuffer !== null) {
3407
4611
  for (let i = firstVisibleAnchorIndex; i <= endNoBuffer; i++) {
3408
- const id = (_h = idCache[i]) != null ? _h : getId(state, i);
4612
+ const id = (_j = idCache[i]) != null ? _j : getId(state, i);
3409
4613
  idsInView.push(id);
3410
4614
  }
3411
4615
  }
@@ -3438,7 +4642,7 @@ function calculateItemsInView(ctx, params = {}) {
3438
4642
  const needNewContainers = [];
3439
4643
  const needNewContainersSet = /* @__PURE__ */ new Set();
3440
4644
  for (let i = startBuffered; i <= endBuffered; i++) {
3441
- const id = (_i = idCache[i]) != null ? _i : getId(state, i);
4645
+ const id = (_k = idCache[i]) != null ? _k : getId(state, i);
3442
4646
  if (!containerItemKeys.has(id)) {
3443
4647
  needNewContainersSet.add(i);
3444
4648
  needNewContainers.push(i);
@@ -3447,7 +4651,7 @@ function calculateItemsInView(ctx, params = {}) {
3447
4651
  if (alwaysRenderArr.length > 0) {
3448
4652
  for (const index of alwaysRenderArr) {
3449
4653
  if (index < 0 || index >= dataLength) continue;
3450
- const id = (_j = idCache[index]) != null ? _j : getId(state, index);
4654
+ const id = (_l = idCache[index]) != null ? _l : getId(state, index);
3451
4655
  if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
3452
4656
  needNewContainersSet.add(index);
3453
4657
  needNewContainers.push(index);
@@ -3485,7 +4689,7 @@ function calculateItemsInView(ctx, params = {}) {
3485
4689
  for (let idx = 0; idx < needNewContainers.length; idx++) {
3486
4690
  const i = needNewContainers[idx];
3487
4691
  const containerIndex = availableContainers[idx];
3488
- const id = (_k = idCache[i]) != null ? _k : getId(state, i);
4692
+ const id = (_m = idCache[i]) != null ? _m : getId(state, i);
3489
4693
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
3490
4694
  if (oldKey && oldKey !== id) {
3491
4695
  containerItemKeys.delete(oldKey);
@@ -3526,7 +4730,7 @@ function calculateItemsInView(ctx, params = {}) {
3526
4730
  if (alwaysRenderArr.length > 0) {
3527
4731
  for (const index of alwaysRenderArr) {
3528
4732
  if (index < 0 || index >= dataLength) continue;
3529
- const id = (_l = idCache[index]) != null ? _l : getId(state, index);
4733
+ const id = (_n = idCache[index]) != null ? _n : getId(state, index);
3530
4734
  const containerIndex = containerItemKeys.get(id);
3531
4735
  if (containerIndex !== void 0) {
3532
4736
  state.stickyContainerPool.add(containerIndex);
@@ -3564,46 +4768,39 @@ function calculateItemsInView(ctx, params = {}) {
3564
4768
  set$(ctx, `containerSpan${i}`, 1);
3565
4769
  } else {
3566
4770
  const itemIndex = indexByKey.get(itemKey);
3567
- const item = data[itemIndex];
3568
- if (item !== void 0) {
3569
- const positionValue = positions[itemIndex];
3570
- if (positionValue === void 0) {
3571
- set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
3572
- } else {
3573
- const position = (positionValue || 0) - scrollAdjustPending;
3574
- const column = columns[itemIndex] || 1;
3575
- const span = columnSpans[itemIndex] || 1;
3576
- const prevPos = peek$(ctx, `containerPosition${i}`);
3577
- const prevColumn = peek$(ctx, `containerColumn${i}`);
3578
- const prevSpan = peek$(ctx, `containerSpan${i}`);
3579
- const prevData = peek$(ctx, `containerItemData${i}`);
3580
- if (position > POSITION_OUT_OF_VIEW && position !== prevPos) {
3581
- set$(ctx, `containerPosition${i}`, position);
3582
- didChangePositions = true;
3583
- }
3584
- if (column >= 0 && column !== prevColumn) {
3585
- set$(ctx, `containerColumn${i}`, column);
3586
- }
3587
- if (span !== prevSpan) {
3588
- set$(ctx, `containerSpan${i}`, span);
3589
- }
3590
- if (prevData !== item && (itemsAreEqual ? !itemsAreEqual(prevData, item, itemIndex, data) : true)) {
3591
- set$(ctx, `containerItemData${i}`, item);
3592
- }
3593
- }
4771
+ if (itemIndex !== void 0) {
4772
+ didChangePositions = syncMountedContainer(ctx, i, itemIndex, {
4773
+ scrollAdjustPending,
4774
+ updateLayout: true
4775
+ }).didChangePosition || didChangePositions;
3594
4776
  }
3595
4777
  }
3596
4778
  }
3597
4779
  if (didChangePositions) {
3598
4780
  set$(ctx, "lastPositionUpdate", Date.now());
3599
4781
  }
3600
- if (!queuedInitialLayout && endBuffered !== null) {
3601
- if (checkAllSizesKnown(state)) {
3602
- setDidLayout(ctx);
3603
- }
4782
+ if (suppressInitialScrollSideEffects) {
4783
+ evaluateBootstrapInitialScroll(ctx);
4784
+ return;
4785
+ }
4786
+ const mountedBufferedIndices = getMountedBufferedIndices(state);
4787
+ const mountedNoBufferIndices = getMountedNoBufferIndices(state);
4788
+ const readinessIndices = hasActiveInitialScroll(state) ? mountedBufferedIndices : mountedNoBufferIndices.length > 0 ? mountedNoBufferIndices : mountedBufferedIndices;
4789
+ if (!queuedInitialLayout && readinessIndices.length > 0 && checkAllSizesKnown(state, readinessIndices)) {
4790
+ setDidLayout(ctx);
4791
+ handleInitialScrollLayoutReady(ctx);
3604
4792
  }
3605
- if (viewabilityConfigCallbackPairs) {
3606
- updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollLength, startNoBuffer, endNoBuffer);
4793
+ if (viewabilityConfigCallbackPairs && startNoBuffer !== null && endNoBuffer !== null) {
4794
+ updateViewableItems(
4795
+ ctx.state,
4796
+ ctx,
4797
+ viewabilityConfigCallbackPairs,
4798
+ scrollLength,
4799
+ startNoBuffer,
4800
+ endNoBuffer,
4801
+ startBuffered != null ? startBuffered : startNoBuffer,
4802
+ endBuffered != null ? endBuffered : endNoBuffer
4803
+ );
3607
4804
  }
3608
4805
  if (onStickyHeaderChange && stickyIndicesArr.length > 0 && nextActiveStickyIndex !== void 0 && nextActiveStickyIndex !== previousStickyIndex) {
3609
4806
  const item = data[nextActiveStickyIndex];
@@ -3614,163 +4811,63 @@ function calculateItemsInView(ctx, params = {}) {
3614
4811
  });
3615
4812
  }
3616
4813
 
3617
- // src/core/checkActualChange.ts
3618
- function checkActualChange(state, dataProp, previousData) {
3619
- if (!previousData || !dataProp || dataProp.length !== previousData.length) {
3620
- return true;
3621
- }
3622
- const {
3623
- idCache,
3624
- props: { keyExtractor }
3625
- } = state;
3626
- for (let i = 0; i < dataProp.length; i++) {
3627
- if (dataProp[i] !== previousData[i]) {
3628
- return true;
3629
- }
3630
- if (keyExtractor ? idCache[i] !== keyExtractor(previousData[i], i) : dataProp[i] !== previousData[i]) {
3631
- return true;
3632
- }
3633
- }
3634
- return false;
3635
- }
3636
-
3637
- // src/core/checkFinishedScroll.ts
3638
- var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
3639
- var INITIAL_SCROLL_MAX_FALLBACK_CHECKS = 20;
3640
- var INITIAL_SCROLL_ZERO_TARGET_EPSILON = 1;
3641
- function checkFinishedScroll(ctx) {
3642
- ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
3643
- }
3644
- function checkFinishedScrollFrame(ctx) {
3645
- var _a3;
3646
- const scrollingTo = ctx.state.scrollingTo;
3647
- if (scrollingTo) {
3648
- const { state } = ctx;
3649
- state.animFrameCheckFinishedScroll = void 0;
3650
- const scroll = state.scrollPending;
3651
- const adjust = state.scrollAdjustHandler.getAdjust();
3652
- const clampedTargetOffset = (_a3 = scrollingTo.targetOffset) != null ? _a3 : clampScrollOffset(ctx, scrollingTo.offset - (scrollingTo.viewOffset || 0), scrollingTo);
3653
- const maxOffset = clampScrollOffset(ctx, scroll, scrollingTo);
3654
- const diff1 = Math.abs(scroll - clampedTargetOffset);
3655
- const diff2 = Math.abs(diff1 - adjust);
3656
- const isNotOverscrolled = Math.abs(scroll - maxOffset) < 1;
3657
- const isAtTarget = diff1 < 1 || !scrollingTo.animated && diff2 < 1;
3658
- if (isNotOverscrolled && isAtTarget) {
3659
- finishScrollTo(ctx);
3660
- }
3661
- }
3662
- }
3663
- function checkFinishedScrollFallback(ctx) {
4814
+ // src/core/doMaintainScrollAtEnd.ts
4815
+ function doMaintainScrollAtEnd(ctx) {
3664
4816
  const state = ctx.state;
3665
- const scrollingTo = state.scrollingTo;
3666
- const shouldFinishInitialZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
3667
- const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && !shouldFinishInitialZeroTarget || !state.didContainersLayout;
3668
- state.timeoutCheckFinishedScrollFallback = setTimeout(
3669
- () => {
3670
- let numChecks = 0;
3671
- const checkHasScrolled = () => {
3672
- var _a3, _b;
3673
- state.timeoutCheckFinishedScrollFallback = void 0;
3674
- const isStillScrollingTo = state.scrollingTo;
3675
- if (isStillScrollingTo) {
3676
- numChecks++;
3677
- const isNativeInitialPending = isNativeInitialNonZeroTarget(state) && !state.hasScrolled;
3678
- const maxChecks = isNativeInitialPending ? INITIAL_SCROLL_MAX_FALLBACK_CHECKS : 5;
3679
- const shouldFinishZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
3680
- if (shouldFinishZeroTarget || state.hasScrolled || numChecks > maxChecks) {
3681
- finishScrollTo(ctx);
3682
- } else if (isNativeInitialPending && numChecks <= maxChecks) {
3683
- const targetOffset = (_b = (_a3 = state.initialNativeScrollWatchdog) == null ? void 0 : _a3.targetOffset) != null ? _b : state.scrollPending;
3684
- const scroller = state.refScroller.current;
3685
- if (scroller) {
3686
- scroller.scrollTo({
3687
- animated: false,
3688
- x: state.props.horizontal ? targetOffset : 0,
3689
- y: state.props.horizontal ? 0 : targetOffset
3690
- });
3691
- }
3692
- state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, 100);
3693
- } else {
3694
- state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, 100);
3695
- }
3696
- }
3697
- };
3698
- checkHasScrolled();
3699
- },
3700
- slowTimeout ? 500 : 100
3701
- );
3702
- }
3703
- function isNativeInitialNonZeroTarget(state) {
3704
- return !state.didFinishInitialScroll && !!state.initialNativeScrollWatchdog && state.initialNativeScrollWatchdog.targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
3705
- }
3706
- function shouldFinishInitialZeroTargetScroll(ctx) {
3707
- var _a3;
3708
- const { state } = ctx;
3709
- 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;
3710
- }
3711
-
3712
- // src/utils/updateAveragesOnDataChange.ts
3713
- function updateAveragesOnDataChange(state, oldData, newData) {
3714
- var _a3;
3715
4817
  const {
3716
- averageSizes,
3717
- sizesKnown,
3718
- indexByKey,
3719
- props: { itemsAreEqual, getItemType, keyExtractor }
4818
+ didContainersLayout,
4819
+ pendingNativeMVCPAdjust,
4820
+ refScroller,
4821
+ props: { maintainScrollAtEnd }
3720
4822
  } = state;
3721
- if (!itemsAreEqual || !oldData.length || !newData.length) {
3722
- for (const key in averageSizes) {
3723
- delete averageSizes[key];
3724
- }
3725
- return;
3726
- }
3727
- const itemTypesToPreserve = {};
3728
- const newDataLength = newData.length;
3729
- const oldDataLength = oldData.length;
3730
- for (let newIndex = 0; newIndex < newDataLength; newIndex++) {
3731
- const newItem = newData[newIndex];
3732
- const id = keyExtractor ? keyExtractor(newItem, newIndex) : String(newIndex);
3733
- const oldIndex = indexByKey.get(id);
3734
- if (oldIndex !== void 0 && oldIndex < oldDataLength) {
3735
- const knownSize = sizesKnown.get(id);
3736
- if (knownSize === void 0) continue;
3737
- const oldItem = oldData[oldIndex];
3738
- const areEqual = itemsAreEqual(oldItem, newItem, newIndex, newData);
3739
- if (areEqual) {
3740
- const itemType = getItemType ? (_a3 = getItemType(newItem, newIndex)) != null ? _a3 : "" : "";
3741
- let typeData = itemTypesToPreserve[itemType];
3742
- if (!typeData) {
3743
- typeData = itemTypesToPreserve[itemType] = { count: 0, totalSize: 0 };
3744
- }
3745
- typeData.totalSize += knownSize;
3746
- typeData.count++;
3747
- }
3748
- }
3749
- }
3750
- for (const key in averageSizes) {
3751
- delete averageSizes[key];
4823
+ const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
4824
+ const shouldMaintainScrollAtEnd = !!(isWithinMaintainScrollAtEndThreshold && maintainScrollAtEnd && didContainersLayout);
4825
+ if (pendingNativeMVCPAdjust) {
4826
+ state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
4827
+ return false;
3752
4828
  }
3753
- for (const itemType in itemTypesToPreserve) {
3754
- const { totalSize, count } = itemTypesToPreserve[itemType];
3755
- if (count > 0) {
3756
- averageSizes[itemType] = {
3757
- avg: totalSize / count,
3758
- num: count
3759
- };
4829
+ state.pendingMaintainScrollAtEnd = false;
4830
+ if (shouldMaintainScrollAtEnd) {
4831
+ const contentSize = getContentSize(ctx);
4832
+ if (contentSize < state.scrollLength) {
4833
+ state.scroll = 0;
3760
4834
  }
4835
+ requestAnimationFrame(() => {
4836
+ var _a3;
4837
+ if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
4838
+ state.maintainingScrollAtEnd = true;
4839
+ (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
4840
+ animated: maintainScrollAtEnd.animated
4841
+ });
4842
+ setTimeout(
4843
+ () => {
4844
+ state.maintainingScrollAtEnd = false;
4845
+ },
4846
+ maintainScrollAtEnd.animated ? 500 : 0
4847
+ );
4848
+ }
4849
+ });
4850
+ return true;
3761
4851
  }
4852
+ return false;
3762
4853
  }
3763
4854
 
3764
4855
  // src/core/checkResetContainers.ts
3765
- function checkResetContainers(ctx, dataProp) {
4856
+ function checkResetContainers(ctx, dataProp, { didColumnsChange = false } = {}) {
3766
4857
  const state = ctx.state;
3767
4858
  const { previousData } = state;
3768
- if (previousData) {
3769
- updateAveragesOnDataChange(state, previousData, dataProp);
3770
- }
3771
4859
  const { maintainScrollAtEnd } = state.props;
4860
+ if (didColumnsChange) {
4861
+ state.sizes.clear();
4862
+ state.sizesKnown.clear();
4863
+ for (const key in state.averageSizes) {
4864
+ delete state.averageSizes[key];
4865
+ }
4866
+ state.minIndexSizeChanged = 0;
4867
+ state.scrollForNextCalculateItemsInView = void 0;
4868
+ }
3772
4869
  calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
3773
- const shouldMaintainScrollAtEnd = maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onDataChange;
4870
+ const shouldMaintainScrollAtEnd = !didColumnsChange && (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onDataChange);
3774
4871
  const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx);
3775
4872
  if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
3776
4873
  state.isEndReached = false;
@@ -3778,7 +4875,54 @@ function checkResetContainers(ctx, dataProp) {
3778
4875
  if (!didMaintainScrollAtEnd) {
3779
4876
  checkThresholds(ctx);
3780
4877
  }
3781
- delete state.previousData;
4878
+ delete state.previousData;
4879
+ }
4880
+
4881
+ // src/core/checkStructuralDataChange.ts
4882
+ function checkStructuralDataChange(state, dataProp, previousData) {
4883
+ var _a3;
4884
+ state.pendingDataComparison = void 0;
4885
+ if (!previousData || !dataProp || dataProp.length !== previousData.length) {
4886
+ return true;
4887
+ }
4888
+ const {
4889
+ idCache,
4890
+ props: { itemsAreEqual, keyExtractor }
4891
+ } = state;
4892
+ let byIndex;
4893
+ for (let i = 0; i < dataProp.length; i++) {
4894
+ if (dataProp[i] === previousData[i]) {
4895
+ continue;
4896
+ }
4897
+ if (!keyExtractor) {
4898
+ if (byIndex) {
4899
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4900
+ }
4901
+ return true;
4902
+ }
4903
+ const previousKey = (_a3 = idCache[i]) != null ? _a3 : keyExtractor(previousData[i], i);
4904
+ const nextKey = keyExtractor(dataProp[i], i);
4905
+ if (previousKey !== nextKey) {
4906
+ if (byIndex) {
4907
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4908
+ }
4909
+ return true;
4910
+ }
4911
+ if (!itemsAreEqual) {
4912
+ if (byIndex) {
4913
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4914
+ }
4915
+ return true;
4916
+ }
4917
+ const isEqual = itemsAreEqual(previousData[i], dataProp[i], i, dataProp);
4918
+ byIndex != null ? byIndex : byIndex = [];
4919
+ byIndex[i] = isEqual ? 1 : 2;
4920
+ if (!isEqual) {
4921
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4922
+ return true;
4923
+ }
4924
+ }
4925
+ return false;
3782
4926
  }
3783
4927
 
3784
4928
  // src/core/doInitialAllocateContainers.ts
@@ -3896,20 +5040,102 @@ function handleLayout(ctx, layoutParam, setCanRender) {
3896
5040
  setCanRender(true);
3897
5041
  }
3898
5042
 
5043
+ // src/core/updateScroll.ts
5044
+ function updateScroll(ctx, newScroll, forceUpdate) {
5045
+ var _a3;
5046
+ const state = ctx.state;
5047
+ const { ignoreScrollFromMVCP, lastScrollAdjustForHistory, scrollAdjustHandler, scrollHistory, scrollingTo } = state;
5048
+ const prevScroll = state.scroll;
5049
+ state.hasScrolled = true;
5050
+ state.lastBatchingAction = Date.now();
5051
+ const currentTime = Date.now();
5052
+ const adjust = scrollAdjustHandler.getAdjust();
5053
+ const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
5054
+ if (adjustChanged) {
5055
+ scrollHistory.length = 0;
5056
+ }
5057
+ state.lastScrollAdjustForHistory = adjust;
5058
+ if (scrollingTo === void 0 && !(scrollHistory.length === 0 && newScroll === state.scroll)) {
5059
+ if (!adjustChanged) {
5060
+ scrollHistory.push({ scroll: newScroll, time: currentTime });
5061
+ }
5062
+ }
5063
+ if (scrollHistory.length > 5) {
5064
+ scrollHistory.shift();
5065
+ }
5066
+ if (ignoreScrollFromMVCP && !scrollingTo) {
5067
+ const { lt, gt } = ignoreScrollFromMVCP;
5068
+ if (lt && newScroll < lt || gt && newScroll > gt) {
5069
+ state.ignoreScrollFromMVCPIgnored = true;
5070
+ return;
5071
+ }
5072
+ }
5073
+ state.scrollPrev = prevScroll;
5074
+ state.scrollPrevTime = state.scrollTime;
5075
+ state.scroll = newScroll;
5076
+ state.scrollTime = currentTime;
5077
+ const scrollDelta = Math.abs(newScroll - prevScroll);
5078
+ const didResolvePendingNativeMVCPAdjust = resolvePendingNativeMVCPAdjust(ctx, newScroll);
5079
+ const scrollLength = state.scrollLength;
5080
+ const lastCalculated = state.scrollLastCalculate;
5081
+ const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
5082
+ const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
5083
+ if (shouldUpdate) {
5084
+ state.scrollLastCalculate = state.scroll;
5085
+ state.ignoreScrollFromMVCPIgnored = false;
5086
+ state.lastScrollDelta = scrollDelta;
5087
+ const runCalculateItems = () => {
5088
+ var _a4;
5089
+ (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { doMVCP: scrollingTo !== void 0 });
5090
+ checkThresholds(ctx);
5091
+ };
5092
+ if (scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength) {
5093
+ flushSync(runCalculateItems);
5094
+ } else {
5095
+ runCalculateItems();
5096
+ }
5097
+ const shouldMaintainScrollAtEndAfterPendingSettle = !!state.pendingMaintainScrollAtEnd || !!((_a3 = state.props.maintainScrollAtEnd) == null ? void 0 : _a3.onDataChange);
5098
+ if (didResolvePendingNativeMVCPAdjust && shouldMaintainScrollAtEndAfterPendingSettle) {
5099
+ state.pendingMaintainScrollAtEnd = false;
5100
+ doMaintainScrollAtEnd(ctx);
5101
+ }
5102
+ state.dataChangeNeedsScrollUpdate = false;
5103
+ state.lastScrollDelta = 0;
5104
+ }
5105
+ }
5106
+
3899
5107
  // src/core/onScroll.ts
3900
- var INITIAL_SCROLL_PROGRESS_EPSILON = 1;
3901
- function didObserveInitialScrollProgress(newScroll, watchdog) {
3902
- const previousDistance = Math.abs(watchdog.startScroll - watchdog.targetOffset);
3903
- const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
3904
- return nextDistance <= INITIAL_SCROLL_PROGRESS_EPSILON || nextDistance + INITIAL_SCROLL_PROGRESS_EPSILON < previousDistance;
5108
+ function trackInitialScrollNativeProgress(state, newScroll) {
5109
+ const initialNativeScrollWatchdog = initialScrollWatchdog.get(state);
5110
+ const didInitialScrollProgress = !!initialNativeScrollWatchdog && initialScrollWatchdog.didObserveProgress(newScroll, initialNativeScrollWatchdog);
5111
+ if (didInitialScrollProgress) {
5112
+ initialScrollWatchdog.clear(state);
5113
+ return;
5114
+ }
5115
+ if (initialNativeScrollWatchdog) {
5116
+ state.hasScrolled = false;
5117
+ initialScrollWatchdog.set(state, {
5118
+ startScroll: initialNativeScrollWatchdog.startScroll,
5119
+ targetOffset: initialNativeScrollWatchdog.targetOffset
5120
+ });
5121
+ }
5122
+ }
5123
+ function shouldDeferPublicOnScroll(state) {
5124
+ var _a3;
5125
+ return !!state.initialScroll && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" && !state.didFinishInitialScroll;
5126
+ }
5127
+ function cloneScrollEvent(event) {
5128
+ return {
5129
+ ...event,
5130
+ nativeEvent: {
5131
+ ...event.nativeEvent
5132
+ }
5133
+ };
3905
5134
  }
3906
5135
  function onScroll(ctx, event) {
3907
5136
  var _a3, _b, _c, _d;
3908
5137
  const state = ctx.state;
3909
- const {
3910
- scrollProcessingEnabled,
3911
- props: { onScroll: onScrollProp }
3912
- } = state;
5138
+ const { scrollProcessingEnabled } = state;
3913
5139
  if (scrollProcessingEnabled === false) {
3914
5140
  return;
3915
5141
  }
@@ -3940,20 +5166,19 @@ function onScroll(ctx, event) {
3940
5166
  }
3941
5167
  }
3942
5168
  state.scrollPending = newScroll;
3943
- const initialNativeScrollWatchdog = state.initialNativeScrollWatchdog;
3944
- const didInitialScrollProgress = !!initialNativeScrollWatchdog && didObserveInitialScrollProgress(newScroll, initialNativeScrollWatchdog);
3945
- if (didInitialScrollProgress) {
3946
- state.initialNativeScrollWatchdog = void 0;
3947
- }
3948
5169
  updateScroll(ctx, newScroll, insetChanged);
3949
- if (initialNativeScrollWatchdog && !didInitialScrollProgress) {
3950
- state.hasScrolled = false;
3951
- state.initialNativeScrollWatchdog = initialNativeScrollWatchdog;
3952
- }
5170
+ trackInitialScrollNativeProgress(state, newScroll);
5171
+ clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx);
3953
5172
  if (state.scrollingTo) {
3954
5173
  checkFinishedScroll(ctx);
3955
5174
  }
3956
- onScrollProp == null ? void 0 : onScrollProp(event);
5175
+ if (state.props.onScroll) {
5176
+ if (shouldDeferPublicOnScroll(state)) {
5177
+ state.deferredPublicOnScrollEvent = cloneScrollEvent(event);
5178
+ } else {
5179
+ state.props.onScroll(event);
5180
+ }
5181
+ }
3957
5182
  }
3958
5183
 
3959
5184
  // src/core/ScrollAdjustHandler.ts
@@ -4007,6 +5232,43 @@ var ScrollAdjustHandler = class {
4007
5232
  }
4008
5233
  };
4009
5234
 
5235
+ // src/core/updateAnchoredEndSpace.ts
5236
+ function maybeUpdateAnchoredEndSpace(ctx) {
5237
+ var _a3;
5238
+ const state = ctx.state;
5239
+ const anchoredEndSpace = state.props.anchoredEndSpace;
5240
+ const previousSize = peek$(ctx, "anchoredEndSpaceSize");
5241
+ let nextSize = 0;
5242
+ if (anchoredEndSpace) {
5243
+ const { anchorIndex, anchorMaxSize, anchorOffset = 0 } = anchoredEndSpace;
5244
+ const { data } = state.props;
5245
+ if (anchorIndex >= 0 && anchorIndex < data.length && state.scrollLength > 0) {
5246
+ let contentBelowAnchor = 0;
5247
+ const footerSize = ctx.values.get("footerSize") || 0;
5248
+ const stylePaddingBottom = state.props.stylePaddingBottom || 0;
5249
+ for (let index = anchorIndex; index < data.length; index++) {
5250
+ const itemKey = getId(state, index);
5251
+ const size = itemKey ? state.sizesKnown.get(itemKey) : void 0;
5252
+ const effectiveSize = index === anchorIndex && anchorMaxSize !== void 0 ? Math.min(size || 0, Math.max(0, anchorMaxSize)) : size;
5253
+ if (effectiveSize !== null && effectiveSize !== void 0 && effectiveSize > 0) {
5254
+ contentBelowAnchor += effectiveSize;
5255
+ }
5256
+ }
5257
+ contentBelowAnchor += footerSize + stylePaddingBottom;
5258
+ nextSize = Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
5259
+ }
5260
+ }
5261
+ if (previousSize === nextSize) {
5262
+ return nextSize;
5263
+ }
5264
+ set$(ctx, "anchoredEndSpaceSize", nextSize);
5265
+ (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
5266
+ if (anchoredEndSpace == null ? void 0 : anchoredEndSpace.includeInEndInset) {
5267
+ updateScroll(ctx, state.scroll, true);
5268
+ }
5269
+ return nextSize;
5270
+ }
5271
+
4010
5272
  // src/core/updateItemSize.ts
4011
5273
  function runOrScheduleMVCPRecalculate(ctx) {
4012
5274
  const state = ctx.state;
@@ -4088,6 +5350,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
4088
5350
  previous: size - diff,
4089
5351
  size
4090
5352
  });
5353
+ maybeUpdateAnchoredEndSpace(ctx);
4091
5354
  }
4092
5355
  if (minIndexSizeChanged !== void 0) {
4093
5356
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
@@ -4211,7 +5474,7 @@ function createImperativeHandle(ctx) {
4211
5474
  const IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS = 800;
4212
5475
  const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
4213
5476
  let imperativeScrollToken = 0;
4214
- const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0 || hasActiveMVCPAnchorLock(state);
5477
+ const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0;
4215
5478
  const runWhenSettled = (token, run) => {
4216
5479
  const startedAt = Date.now();
4217
5480
  let stableFrames = 0;
@@ -4233,9 +5496,10 @@ function createImperativeHandle(ctx) {
4233
5496
  };
4234
5497
  requestAnimationFrame(check);
4235
5498
  };
4236
- const runScrollWithPromise = (run) => new Promise((resolve) => {
5499
+ const runScrollWithPromise = (run, options) => new Promise((resolve) => {
4237
5500
  var _a3;
4238
5501
  const token = ++imperativeScrollToken;
5502
+ const shouldWaitOneFrame = !!(options == null ? void 0 : options.shouldWaitOneFrame);
4239
5503
  (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
4240
5504
  state.pendingScrollResolve = resolve;
4241
5505
  const runNow = () => {
@@ -4250,11 +5514,12 @@ function createImperativeHandle(ctx) {
4250
5514
  resolve();
4251
5515
  }
4252
5516
  };
5517
+ const execute = shouldWaitOneFrame ? () => requestAnimationFrame(runNow) : runNow;
4253
5518
  if (isSettlingAfterDataChange()) {
4254
- runWhenSettled(token, runNow);
4255
- return;
5519
+ runWhenSettled(token, execute);
5520
+ } else {
5521
+ execute();
4256
5522
  }
4257
- runNow();
4258
5523
  });
4259
5524
  const scrollIndexIntoView = (options) => {
4260
5525
  if (state) {
@@ -4311,10 +5576,13 @@ function createImperativeHandle(ctx) {
4311
5576
  },
4312
5577
  end: state.endNoBuffer,
4313
5578
  endBuffered: state.endBuffered,
4314
- isAtEnd: state.isAtEnd,
4315
- isAtStart: state.isAtStart,
5579
+ isAtEnd: peek$(ctx, "isAtEnd"),
5580
+ isAtStart: peek$(ctx, "isAtStart"),
4316
5581
  isEndReached: state.isEndReached,
5582
+ isNearEnd: peek$(ctx, "isNearEnd"),
5583
+ isNearStart: peek$(ctx, "isNearStart"),
4317
5584
  isStartReached: state.isStartReached,
5585
+ isWithinMaintainScrollAtEndThreshold: peek$(ctx, "isWithinMaintainScrollAtEndThreshold"),
4318
5586
  listen: (signalName, cb) => listen$(ctx, signalName, cb),
4319
5587
  listenToPosition: (key, cb) => listenPosition$(ctx, key, cb),
4320
5588
  positionAtIndex: (index) => state.positions[index],
@@ -4361,10 +5629,15 @@ function createImperativeHandle(ctx) {
4361
5629
  }
4362
5630
  return false;
4363
5631
  }),
4364
- scrollToIndex: (params) => runScrollWithPromise(() => {
4365
- scrollToIndex(ctx, params);
4366
- return true;
4367
- }),
5632
+ scrollToIndex: (params) => runScrollWithPromise(
5633
+ () => {
5634
+ scrollToIndex(ctx, params);
5635
+ return true;
5636
+ },
5637
+ {
5638
+ shouldWaitOneFrame: params.index >= 0 && params.index >= state.props.data.length
5639
+ }
5640
+ ),
4368
5641
  scrollToItem: ({ item, ...props }) => runScrollWithPromise(() => {
4369
5642
  const data = state.props.data;
4370
5643
  const index = data.indexOf(item);
@@ -4460,7 +5733,7 @@ function getRenderedItem(ctx, key) {
4460
5733
  item,
4461
5734
  type: getItemType ? (_a3 = getItemType(item, index)) != null ? _a3 : "" : ""
4462
5735
  };
4463
- renderedItem = isFunction(renderItem) ? renderItem(itemProps) : React3__default.createElement(renderItem, itemProps);
5736
+ renderedItem = React3__default.createElement(renderItem, itemProps);
4464
5737
  }
4465
5738
  return { index, item: data[index], renderedItem };
4466
5739
  }
@@ -4595,8 +5868,17 @@ var LegendList = typedMemo(
4595
5868
  );
4596
5869
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
4597
5870
  var _a3, _b, _c, _d, _e, _f, _g, _h;
5871
+ const noopOnScroll = useCallback((_event) => {
5872
+ }, []);
5873
+ if (props.recycleItems === void 0) {
5874
+ warnDevOnce(
5875
+ "recycleItems-omitted",
5876
+ "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."
5877
+ );
5878
+ }
4598
5879
  const {
4599
5880
  alignItemsAtEnd = false,
5881
+ anchoredEndSpace,
4600
5882
  alwaysRender,
4601
5883
  columnWrapperStyle,
4602
5884
  contentContainerStyle: contentContainerStyleProp,
@@ -4618,6 +5900,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4618
5900
  itemsAreEqual,
4619
5901
  keyExtractor: keyExtractorProp,
4620
5902
  ListEmptyComponent,
5903
+ ListFooterComponent,
5904
+ ListFooterComponentStyle,
4621
5905
  ListHeaderComponent,
4622
5906
  maintainScrollAtEnd = false,
4623
5907
  maintainScrollAtEndThreshold = 0.1,
@@ -4654,14 +5938,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4654
5938
  useWindowScroll = false,
4655
5939
  viewabilityConfig,
4656
5940
  viewabilityConfigCallbackPairs,
4657
- waitForInitialLayout = true,
4658
5941
  ...rest
4659
5942
  } = props;
4660
5943
  const animatedPropsInternal = props.animatedPropsInternal;
4661
5944
  const positionComponentInternal = props.positionComponentInternal;
4662
5945
  const stickyPositionComponentInternal = props.stickyPositionComponentInternal;
4663
5946
  const {
4664
- childrenMode,
4665
5947
  positionComponentInternal: _positionComponentInternal,
4666
5948
  stickyPositionComponentInternal: _stickyPositionComponentInternal,
4667
5949
  ...restProps
@@ -4687,8 +5969,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4687
5969
  const hasInitialScrollIndex = initialScrollIndexProp !== void 0 && initialScrollIndexProp !== null;
4688
5970
  const hasInitialScrollOffset = initialScrollOffsetProp !== void 0 && initialScrollOffsetProp !== null;
4689
5971
  const initialScrollUsesOffsetOnly = !initialScrollAtEnd && !hasInitialScrollIndex && hasInitialScrollOffset;
4690
- const initialScrollProp = initialScrollAtEnd ? { index: Math.max(0, dataProp.length - 1), viewOffset: -stylePaddingBottomState, viewPosition: 1 } : hasInitialScrollIndex ? typeof initialScrollIndexProp === "object" ? {
5972
+ const usesBootstrapInitialScroll = initialScrollAtEnd || hasInitialScrollIndex;
5973
+ const initialScrollProp = initialScrollAtEnd ? {
5974
+ index: Math.max(0, dataProp.length - 1),
5975
+ preserveForBottomPadding: true,
5976
+ viewOffset: -stylePaddingBottomState,
5977
+ viewPosition: 1
5978
+ } : hasInitialScrollIndex ? typeof initialScrollIndexProp === "object" ? {
4691
5979
  index: (_a3 = initialScrollIndexProp.index) != null ? _a3 : 0,
5980
+ preserveForBottomPadding: initialScrollIndexProp.viewOffset === void 0 && initialScrollIndexProp.viewPosition === 1 ? true : void 0,
4692
5981
  viewOffset: (_b = initialScrollIndexProp.viewOffset) != null ? _b : initialScrollIndexProp.viewPosition === 1 ? -stylePaddingBottomState : 0,
4693
5982
  viewPosition: (_c = initialScrollIndexProp.viewPosition) != null ? _c : 0
4694
5983
  } : {
@@ -4718,18 +6007,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4718
6007
  dataVersion,
4719
6008
  keyExtractor
4720
6009
  ]);
4721
- if (IS_DEV && stickyIndicesDeprecated && !stickyHeaderIndicesProp) {
4722
- warnDevOnce(
4723
- "stickyIndices",
4724
- "stickyIndices has been renamed to stickyHeaderIndices. Please update your props to use stickyHeaderIndices."
4725
- );
4726
- }
4727
- if (IS_DEV && useWindowScroll && renderScrollComponent) {
4728
- warnDevOnce(
4729
- "useWindowScrollRenderScrollComponent",
4730
- "useWindowScroll is not supported when renderScrollComponent is provided."
4731
- );
4732
- }
4733
6010
  const useWindowScrollResolved = !!useWindowScroll && !renderScrollComponent;
4734
6011
  const refState = useRef(void 0);
4735
6012
  const hasOverrideItemLayout = !!overrideItemLayout;
@@ -4738,7 +6015,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4738
6015
  if (!ctx.state) {
4739
6016
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : { height: 0, width: 0 } )[horizontal ? "width" : "height"];
4740
6017
  ctx.state = {
4741
- activeStickyIndex: -1,
4742
6018
  averageSizes: {},
4743
6019
  columnSpans: [],
4744
6020
  columns: [],
@@ -4757,24 +6033,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4757
6033
  idCache: [],
4758
6034
  idsInView: [],
4759
6035
  indexByKey: /* @__PURE__ */ new Map(),
4760
- initialAnchor: !initialScrollUsesOffsetOnly && (initialScrollProp == null ? void 0 : initialScrollProp.index) !== void 0 && (initialScrollProp == null ? void 0 : initialScrollProp.viewPosition) !== void 0 ? {
4761
- attempts: 0,
4762
- index: initialScrollProp.index,
4763
- settledTicks: 0,
4764
- viewOffset: (_f = initialScrollProp.viewOffset) != null ? _f : 0,
4765
- viewPosition: initialScrollProp.viewPosition
4766
- } : void 0,
4767
- initialNativeScrollWatchdog: void 0,
4768
6036
  initialScroll: initialScrollProp,
4769
- initialScrollLastDidFinish: false,
4770
- initialScrollLastTarget: initialScrollProp,
4771
- initialScrollLastTargetUsesOffset: initialScrollUsesOffsetOnly,
4772
- initialScrollPreviousDataLength: dataProp.length,
4773
- initialScrollRetryLastLength: void 0,
4774
- initialScrollRetryWindowUntil: 0,
4775
- initialScrollUsesOffset: initialScrollUsesOffsetOnly,
4776
- isAtEnd: false,
4777
- isAtStart: false,
6037
+ initialScrollSession: initialScrollProp ? {
6038
+ kind: initialScrollUsesOffsetOnly ? "offset" : "bootstrap",
6039
+ previousDataLength: dataProp.length
6040
+ } : void 0,
4778
6041
  isEndReached: null,
4779
6042
  isFirst: true,
4780
6043
  isStartReached: null,
@@ -4785,6 +6048,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4785
6048
  minIndexSizeChanged: 0,
4786
6049
  nativeContentInset: void 0,
4787
6050
  nativeMarginTop: 0,
6051
+ pendingDataComparison: void 0,
4788
6052
  pendingNativeMVCPAdjust: void 0,
4789
6053
  positions: [],
4790
6054
  props: {},
@@ -4815,6 +6079,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4815
6079
  };
4816
6080
  const internalState = ctx.state;
4817
6081
  internalState.triggerCalculateItemsInView = (params) => calculateItemsInView(ctx, params);
6082
+ internalState.reprocessCurrentScroll = () => updateScroll(ctx, internalState.scroll, true);
4818
6083
  set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPositionConfig);
4819
6084
  set$(ctx, "extraData", extraData);
4820
6085
  }
@@ -4822,22 +6087,29 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4822
6087
  }
4823
6088
  const state = refState.current;
4824
6089
  const isFirstLocal = state.isFirst;
4825
- state.didColumnsChange = numColumnsProp !== state.props.numColumns;
6090
+ const previousNumColumnsProp = state.props.numColumns;
6091
+ state.didColumnsChange = numColumnsProp !== previousNumColumnsProp;
4826
6092
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
4827
6093
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
4828
- const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkActualChange(state, dataProp, state.props.data);
6094
+ const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
6095
+ if (didDataChangeLocal && state.didFinishInitialScroll && ((_f = state.initialScroll) == null ? void 0 : _f.viewPosition) === 1 && state.props.data.length > 0) {
6096
+ clearPreservedInitialScrollTarget(state);
6097
+ }
4829
6098
  if (didDataChangeLocal) {
4830
6099
  state.dataChangeEpoch += 1;
4831
6100
  state.dataChangeNeedsScrollUpdate = true;
4832
6101
  state.didDataChange = true;
4833
6102
  state.previousData = state.props.data;
4834
6103
  }
4835
- const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
6104
+ const throttledOnScroll = useThrottledOnScroll(onScrollProp != null ? onScrollProp : noopOnScroll, scrollEventThrottle != null ? scrollEventThrottle : 0);
6105
+ const throttleScrollFn = scrollEventThrottle && onScrollProp ? throttledOnScroll : onScrollProp;
6106
+ const anchoredEndSpaceResolved = anchoredEndSpace ? { ...anchoredEndSpace, includeInEndInset: true } : anchoredEndSpace;
4836
6107
  state.props = {
4837
6108
  alignItemsAtEnd,
4838
6109
  alwaysRender,
4839
6110
  alwaysRenderIndicesArr: alwaysRenderIndices.arr,
4840
6111
  alwaysRenderIndicesSet: alwaysRenderIndices.set,
6112
+ anchoredEndSpace: anchoredEndSpaceResolved,
4841
6113
  animatedProps: animatedPropsInternal,
4842
6114
  contentInset,
4843
6115
  data: dataProp,
@@ -4901,292 +6173,87 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4901
6173
  true
4902
6174
  );
4903
6175
  }
4904
- const resolveInitialScrollOffset = useCallback((initialScroll) => {
4905
- var _a4;
4906
- if (state.initialScrollUsesOffset) {
4907
- return clampScrollOffset(ctx, (_a4 = initialScroll.contentOffset) != null ? _a4 : 0);
4908
- }
4909
- const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, initialScroll.index) : 0;
4910
- const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, initialScroll);
4911
- return clampScrollOffset(ctx, resolvedOffset, initialScroll);
4912
- }, []);
4913
- const finishInitialScrollWithoutScroll = useCallback(() => {
4914
- refState.current.initialAnchor = void 0;
4915
- refState.current.initialScroll = void 0;
4916
- state.initialAnchor = void 0;
4917
- state.initialScroll = void 0;
4918
- state.initialScrollUsesOffset = false;
4919
- state.initialScrollLastTarget = void 0;
4920
- state.initialScrollLastTargetUsesOffset = false;
4921
- setInitialRenderState(ctx, { didInitialScroll: true });
4922
- }, []);
4923
- const setActiveInitialScrollTarget = useCallback(
4924
- (target, options) => {
4925
- const usesOffset = !!(options == null ? void 0 : options.usesOffset);
4926
- state.initialScrollUsesOffset = usesOffset;
4927
- state.initialScrollLastTarget = target;
4928
- state.initialScrollLastTargetUsesOffset = usesOffset;
4929
- refState.current.initialScroll = target;
4930
- state.initialScroll = target;
4931
- if ((options == null ? void 0 : options.resetDidFinish) && state.didFinishInitialScroll) {
4932
- state.didFinishInitialScroll = false;
4933
- }
4934
- if (!(options == null ? void 0 : options.syncAnchor)) {
4935
- return;
4936
- }
4937
- },
4938
- []
4939
- );
4940
- const shouldFinishInitialScrollAtOrigin = useCallback(
4941
- (initialScroll, offset) => {
4942
- var _a4, _b2, _c2;
4943
- if (offset !== 0 || initialScrollAtEnd) {
4944
- return false;
4945
- }
4946
- if (state.initialScrollUsesOffset) {
4947
- return Math.abs((_a4 = initialScroll.contentOffset) != null ? _a4 : 0) <= 1;
4948
- }
4949
- return initialScroll.index === 0 && ((_b2 = initialScroll.viewPosition) != null ? _b2 : 0) === 0 && Math.abs((_c2 = initialScroll.viewOffset) != null ? _c2 : 0) <= 1;
4950
- },
4951
- [initialScrollAtEnd]
4952
- );
4953
- const shouldFinishEmptyInitialScrollAtEnd = useCallback(
4954
- (initialScroll, offset) => {
4955
- return dataProp.length === 0 && initialScrollAtEnd && offset === 0 && initialScroll.viewPosition === 1;
4956
- },
4957
- [dataProp.length, initialScrollAtEnd]
4958
- );
4959
- const shouldRearmFinishedEmptyInitialScrollAtEnd = useCallback(
4960
- (initialScroll) => {
4961
- var _a4;
4962
- return !!(state.didFinishInitialScroll && dataProp.length > 0 && initialScroll && !state.initialScrollUsesOffset && initialScroll.index === 0 && initialScroll.viewPosition === 1 && ((_a4 = initialScroll.contentOffset) != null ? _a4 : 0) === 0);
4963
- },
4964
- [dataProp.length]
4965
- );
4966
6176
  const initialContentOffset = useMemo(() => {
4967
- let value;
4968
- const { initialScroll, initialAnchor } = refState.current;
4969
- if (initialScroll) {
4970
- if (!state.initialScrollUsesOffset && !IsNewArchitecture) ;
4971
- if (initialScroll.contentOffset !== void 0) {
4972
- value = initialScroll.contentOffset;
4973
- } else {
4974
- const clampedOffset = resolveInitialScrollOffset(initialScroll);
4975
- const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
4976
- setActiveInitialScrollTarget(updatedInitialScroll, {
4977
- usesOffset: state.initialScrollUsesOffset
4978
- });
4979
- value = clampedOffset;
4980
- }
4981
- } else {
4982
- refState.current.initialAnchor = void 0;
4983
- value = 0;
4984
- }
4985
- const hasPendingDataDependentInitialScroll = !!initialScroll && dataProp.length === 0 && !shouldFinishInitialScrollAtOrigin(initialScroll, value) && !shouldFinishEmptyInitialScrollAtEnd(initialScroll, value);
4986
- if (!value && !hasPendingDataDependentInitialScroll) {
4987
- if (initialScroll && shouldFinishInitialScrollAtOrigin(initialScroll, value)) {
4988
- finishInitialScrollWithoutScroll();
4989
- } else {
4990
- setInitialRenderState(ctx, { didInitialScroll: true });
4991
- }
6177
+ var _a4, _b2;
6178
+ const initialScroll = state.initialScroll;
6179
+ if (!initialScroll) {
6180
+ return void 0;
4992
6181
  }
4993
- return value;
6182
+ const resolvedOffset = (_a4 = initialScroll.contentOffset) != null ? _a4 : resolveInitialScrollOffset(ctx, initialScroll);
6183
+ return usesBootstrapInitialScroll && ((_b2 = state.initialScrollSession) == null ? void 0 : _b2.kind) === "bootstrap" && Platform.OS === "web" ? void 0 : resolvedOffset;
6184
+ }, [usesBootstrapInitialScroll]);
6185
+ useLayoutEffect(() => {
6186
+ initializeInitialScrollOnMount(ctx, {
6187
+ dataLength: dataProp.length,
6188
+ hasFooterComponent: !!ListFooterComponent,
6189
+ initialContentOffset,
6190
+ initialScrollAtEnd,
6191
+ useBootstrapInitialScroll: usesBootstrapInitialScroll
6192
+ });
4994
6193
  }, []);
4995
6194
  if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
4996
6195
  refState.current.lastBatchingAction = Date.now();
4997
6196
  if (!keyExtractorProp && !isFirstLocal && didDataChangeLocal) {
4998
- IS_DEV && !childrenMode && warnDevOnce(
4999
- "keyExtractor",
5000
- "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."
5001
- );
5002
6197
  refState.current.sizes.clear();
5003
6198
  refState.current.positions.length = 0;
5004
6199
  refState.current.totalSize = 0;
5005
6200
  set$(ctx, "totalSize", 0);
5006
6201
  }
5007
6202
  }
5008
- const doInitialScroll = useCallback((options) => {
5009
- var _a4, _b2;
5010
- const allowPostFinishRetry = !!(options == null ? void 0 : options.allowPostFinishRetry);
5011
- const { didFinishInitialScroll, queuedInitialLayout, scrollingTo } = state;
5012
- const initialScroll = (_a4 = state.initialScroll) != null ? _a4 : allowPostFinishRetry ? state.initialScrollLastTarget : void 0;
5013
- const isInitialScrollInProgress = !!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll);
5014
- const needsContainerLayoutForInitialScroll = !state.initialScrollUsesOffset;
5015
- const shouldWaitForInitialLayout = waitForInitialLayout && needsContainerLayoutForInitialScroll && !queuedInitialLayout && !allowPostFinishRetry && !isInitialScrollInProgress;
5016
- if (!initialScroll || shouldWaitForInitialLayout || didFinishInitialScroll && !allowPostFinishRetry || scrollingTo && !isInitialScrollInProgress) {
5017
- return;
5018
- }
5019
- if (allowPostFinishRetry && state.initialScrollLastTargetUsesOffset) {
5020
- return;
5021
- }
5022
- const didMoveAwayFromInitialTarget = allowPostFinishRetry && initialScroll.contentOffset !== void 0 && Math.abs(state.scroll - initialScroll.contentOffset) > 1;
5023
- if (didMoveAwayFromInitialTarget) {
5024
- state.initialScrollRetryWindowUntil = 0;
5025
- return;
5026
- }
5027
- const offset = resolveInitialScrollOffset(initialScroll);
5028
- const activeInitialTargetOffset = isInitialScrollInProgress ? (_b2 = scrollingTo.targetOffset) != null ? _b2 : scrollingTo.offset : void 0;
5029
- const didOffsetChange = initialScroll.contentOffset === void 0 || Math.abs(initialScroll.contentOffset - offset) > 1;
5030
- const didActiveInitialTargetChange = activeInitialTargetOffset !== void 0 && Math.abs(activeInitialTargetOffset - offset) > 1;
5031
- if (!didOffsetChange && (allowPostFinishRetry || isInitialScrollInProgress && !didActiveInitialTargetChange)) {
5032
- return;
5033
- }
5034
- if (didOffsetChange) {
5035
- const updatedInitialScroll = { ...initialScroll, contentOffset: offset };
5036
- if (!state.initialScrollUsesOffset) {
5037
- state.initialScrollLastTarget = updatedInitialScroll;
5038
- state.initialScrollLastTargetUsesOffset = false;
5039
- if (state.initialScroll) {
5040
- refState.current.initialScroll = updatedInitialScroll;
5041
- state.initialScroll = updatedInitialScroll;
5042
- }
5043
- }
5044
- }
5045
- const hasMeasuredScrollLayout = !!state.lastLayout && state.scrollLength > 0;
5046
- const shouldForceNativeInitialScroll = state.initialScrollUsesOffset && hasMeasuredScrollLayout || allowPostFinishRetry || !!queuedInitialLayout || isInitialScrollInProgress && didOffsetChange;
5047
- performInitialScroll(ctx, {
5048
- forceScroll: shouldForceNativeInitialScroll,
5049
- initialScrollUsesOffset: state.initialScrollUsesOffset,
5050
- resolvedOffset: offset,
5051
- target: initialScroll
5052
- });
5053
- }, []);
5054
- useLayoutEffect(() => {
5055
- var _a4;
5056
- const previousDataLength = state.initialScrollPreviousDataLength;
5057
- state.initialScrollPreviousDataLength = dataProp.length;
5058
- if (previousDataLength !== 0 || dataProp.length === 0 || !state.initialScroll || !state.queuedInitialLayout) {
5059
- return;
5060
- }
5061
- if (initialScrollAtEnd) {
5062
- const lastIndex = Math.max(0, dataProp.length - 1);
5063
- const initialScroll = state.initialScroll;
5064
- const shouldRearm = shouldRearmFinishedEmptyInitialScrollAtEnd(initialScroll);
5065
- if (state.didFinishInitialScroll && !shouldRearm) {
5066
- return;
5067
- }
5068
- if (initialScroll && !state.initialScrollUsesOffset && initialScroll.index === lastIndex && initialScroll.viewPosition === 1 && !shouldRearm) {
5069
- return;
5070
- }
5071
- const updatedInitialScroll = {
5072
- contentOffset: void 0,
5073
- index: lastIndex,
5074
- viewOffset: (_a4 = initialScroll == null ? void 0 : initialScroll.viewOffset) != null ? _a4 : -stylePaddingBottomState,
5075
- viewPosition: 1
5076
- };
5077
- setActiveInitialScrollTarget(updatedInitialScroll, {
5078
- resetDidFinish: shouldRearm,
5079
- syncAnchor: true
5080
- });
5081
- doInitialScroll();
5082
- return;
5083
- }
5084
- if (state.didFinishInitialScroll) {
5085
- return;
5086
- }
5087
- doInitialScroll();
5088
- }, [
5089
- dataProp.length,
5090
- doInitialScroll,
5091
- initialScrollAtEnd,
5092
- shouldRearmFinishedEmptyInitialScrollAtEnd,
5093
- stylePaddingBottomState
5094
- ]);
6203
+ if (IS_DEV) {
6204
+ useDevChecks(props);
6205
+ }
5095
6206
  useLayoutEffect(() => {
5096
- var _a4;
5097
- if (!initialScrollAtEnd) {
5098
- return;
5099
- }
5100
- const lastIndex = Math.max(0, dataProp.length - 1);
5101
- const initialScroll = state.initialScroll;
5102
- const shouldRearm = shouldRearmFinishedEmptyInitialScrollAtEnd(initialScroll);
5103
- if (state.didFinishInitialScroll && !shouldRearm) {
5104
- return;
5105
- }
5106
- if (shouldRearm) {
5107
- state.didFinishInitialScroll = false;
5108
- }
5109
- if (initialScroll && !state.initialScrollUsesOffset && initialScroll.index === lastIndex && initialScroll.viewPosition === 1 && !shouldRearm) {
5110
- return;
5111
- }
5112
- const updatedInitialScroll = {
5113
- contentOffset: void 0,
5114
- index: lastIndex,
5115
- viewOffset: (_a4 = initialScroll == null ? void 0 : initialScroll.viewOffset) != null ? _a4 : -stylePaddingBottomState,
5116
- viewPosition: 1
5117
- };
5118
- setActiveInitialScrollTarget(updatedInitialScroll, {
5119
- resetDidFinish: shouldRearm,
5120
- syncAnchor: true
6207
+ handleInitialScrollDataChange(ctx, {
6208
+ dataLength: dataProp.length,
6209
+ didDataChange: didDataChangeLocal,
6210
+ initialScrollAtEnd,
6211
+ stylePaddingBottom: stylePaddingBottomState,
6212
+ useBootstrapInitialScroll: usesBootstrapInitialScroll
5121
6213
  });
5122
- doInitialScroll();
6214
+ }, [dataProp.length, didDataChangeLocal, initialScrollAtEnd, stylePaddingBottomState, usesBootstrapInitialScroll]);
6215
+ useLayoutEffect(() => {
6216
+ maybeUpdateAnchoredEndSpace(ctx);
5123
6217
  }, [
5124
- dataProp.length,
5125
- doInitialScroll,
5126
- initialScrollAtEnd,
5127
- shouldRearmFinishedEmptyInitialScrollAtEnd,
5128
- stylePaddingBottomState
6218
+ ctx,
6219
+ dataProp,
6220
+ dataVersion,
6221
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex,
6222
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorMaxSize,
6223
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorOffset,
6224
+ numColumnsProp
5129
6225
  ]);
5130
6226
  const onLayoutFooter = useCallback(
5131
6227
  (layout) => {
5132
- var _a4;
5133
- if (!initialScrollAtEnd) {
6228
+ if (!usesBootstrapInitialScroll) {
5134
6229
  return;
5135
6230
  }
5136
- const { initialScroll } = state;
5137
- if (!initialScroll) {
5138
- return;
6231
+ handleBootstrapInitialScrollFooterLayout(ctx, {
6232
+ dataLength: dataProp.length,
6233
+ footerSize: layout[horizontal ? "width" : "height"],
6234
+ initialScrollAtEnd,
6235
+ stylePaddingBottom: stylePaddingBottomState
6236
+ });
6237
+ },
6238
+ [dataProp.length, initialScrollAtEnd, horizontal, stylePaddingBottomState, usesBootstrapInitialScroll]
6239
+ );
6240
+ const onLayoutChange = useCallback(
6241
+ (layout, fromLayoutEffect) => {
6242
+ const previousScrollLength = state.scrollLength;
6243
+ const previousOtherAxisSize = state.otherAxisSize;
6244
+ handleLayout(ctx, layout, setCanRender);
6245
+ maybeUpdateAnchoredEndSpace(ctx);
6246
+ const didLayoutAffectBootstrapTarget = previousScrollLength !== state.scrollLength || previousOtherAxisSize !== state.otherAxisSize;
6247
+ if (usesBootstrapInitialScroll && !fromLayoutEffect && didLayoutAffectBootstrapTarget) {
6248
+ handleBootstrapInitialScrollLayoutChange(ctx);
5139
6249
  }
5140
- const lastIndex = Math.max(0, dataProp.length - 1);
5141
- if (initialScroll.index !== lastIndex || initialScroll.viewPosition !== 1) {
6250
+ if (usesBootstrapInitialScroll) {
5142
6251
  return;
5143
6252
  }
5144
- const footerSize = layout[horizontal ? "width" : "height"];
5145
- const viewOffset = -stylePaddingBottomState - footerSize;
5146
- if (initialScroll.viewOffset !== viewOffset) {
5147
- const previousTargetOffset = (_a4 = initialScroll.contentOffset) != null ? _a4 : resolveInitialScrollOffset(initialScroll);
5148
- const didMoveAwayFromFinishedInitialTarget = state.didFinishInitialScroll && Math.abs(state.scroll - previousTargetOffset) > 1;
5149
- if (didMoveAwayFromFinishedInitialTarget) {
5150
- return;
5151
- }
5152
- const updatedInitialScroll = { ...initialScroll, viewOffset };
5153
- setActiveInitialScrollTarget(updatedInitialScroll, {
5154
- resetDidFinish: true
5155
- });
5156
- doInitialScroll();
5157
- }
6253
+ advanceCurrentInitialScrollSession(ctx);
5158
6254
  },
5159
- [
5160
- dataProp.length,
5161
- doInitialScroll,
5162
- horizontal,
5163
- initialScrollAtEnd,
5164
- resolveInitialScrollOffset,
5165
- stylePaddingBottomState
5166
- ]
6255
+ [dataProp.length, initialScrollAtEnd, stylePaddingBottomState, usesBootstrapInitialScroll]
5167
6256
  );
5168
- const onLayoutChange = useCallback((layout) => {
5169
- var _a4;
5170
- handleLayout(ctx, layout, setCanRender);
5171
- const SCROLL_LENGTH_RETRY_WINDOW_MS = 600;
5172
- const now = Date.now();
5173
- const didFinishInitialScroll = !!state.didFinishInitialScroll;
5174
- if (didFinishInitialScroll && !state.initialScrollLastDidFinish) {
5175
- state.initialScrollRetryWindowUntil = now + SCROLL_LENGTH_RETRY_WINDOW_MS;
5176
- }
5177
- state.initialScrollLastDidFinish = didFinishInitialScroll;
5178
- const previousScrollLength = state.initialScrollRetryLastLength;
5179
- const currentScrollLength = state.scrollLength;
5180
- const didScrollLengthChange = previousScrollLength === void 0 || Math.abs(currentScrollLength - previousScrollLength) > 1;
5181
- if (didScrollLengthChange) {
5182
- state.initialScrollRetryLastLength = currentScrollLength;
5183
- }
5184
- if (didFinishInitialScroll && didScrollLengthChange && now <= state.initialScrollRetryWindowUntil && !state.initialScrollLastTargetUsesOffset && ((_a4 = state.initialScrollLastTarget) == null ? void 0 : _a4.index) !== void 0) {
5185
- doInitialScroll({ allowPostFinishRetry: true });
5186
- return;
5187
- }
5188
- doInitialScroll();
5189
- }, []);
5190
6257
  const { onLayout } = useOnLayoutSync({
5191
6258
  onLayoutChange,
5192
6259
  onLayoutProp,
@@ -5198,6 +6265,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5198
6265
  updateSnapToOffsets(ctx);
5199
6266
  }
5200
6267
  }, [snapToIndices]);
6268
+ useLayoutEffect(
6269
+ () => initializeStateVars(true),
6270
+ [dataVersion, memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingBottomState, stylePaddingTopState]
6271
+ );
5201
6272
  useLayoutEffect(() => {
5202
6273
  const {
5203
6274
  didColumnsChange,
@@ -5207,7 +6278,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5207
6278
  } = state;
5208
6279
  const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx);
5209
6280
  if (!didAllocateContainers && !isFirst && (didDataChange || didColumnsChange)) {
5210
- checkResetContainers(ctx, data);
6281
+ checkResetContainers(ctx, data, { didColumnsChange });
6282
+ }
6283
+ if (didDataChange) {
6284
+ state.pendingDataComparison = void 0;
5211
6285
  }
5212
6286
  state.didColumnsChange = false;
5213
6287
  state.didDataChange = false;
@@ -5222,10 +6296,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5222
6296
  (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { forceFullItemPositions: true });
5223
6297
  }
5224
6298
  }, [extraData, hasOverrideItemLayout, numColumnsProp]);
5225
- useLayoutEffect(
5226
- () => initializeStateVars(true),
5227
- [dataVersion, memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingBottomState, stylePaddingTopState]
5228
- );
5229
6299
  useEffect(() => {
5230
6300
  if (!onMetricsChange) {
5231
6301
  return;
@@ -5258,10 +6328,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5258
6328
  state.viewabilityConfigCallbackPairs = viewability;
5259
6329
  state.enableScrollForNextCalculateItemsInView = !viewability;
5260
6330
  }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
6331
+ useInit(() => {
6332
+ });
5261
6333
  useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx), []);
5262
- {
5263
- useEffect(doInitialScroll, []);
5264
- }
6334
+ useEffect(() => {
6335
+ if (usesBootstrapInitialScroll) {
6336
+ return;
6337
+ }
6338
+ advanceCurrentInitialScrollSession(ctx);
6339
+ }, [ctx, usesBootstrapInitialScroll]);
5265
6340
  const fns = useMemo(
5266
6341
  () => ({
5267
6342
  getRenderedItem: (key) => getRenderedItem(ctx, key),
@@ -5290,6 +6365,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5290
6365
  horizontal,
5291
6366
  initialContentOffset,
5292
6367
  ListEmptyComponent: dataProp.length === 0 ? ListEmptyComponent : void 0,
6368
+ ListFooterComponent,
6369
+ ListFooterComponentStyle,
5293
6370
  ListHeaderComponent,
5294
6371
  onLayout,
5295
6372
  onLayoutFooter,
@@ -5314,19 +6391,45 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5314
6391
  stickyHeaderIndices,
5315
6392
  style,
5316
6393
  updateItemSize: fns.updateItemSize,
5317
- useWindowScroll: useWindowScrollResolved,
5318
- waitForInitialLayout
6394
+ useWindowScroll: useWindowScrollResolved
5319
6395
  }
5320
6396
  ), IS_DEV && ENABLE_DEBUG_VIEW);
5321
6397
  });
5322
6398
 
6399
+ // src/components/stickyPositionUtils.ts
6400
+ function getStickyPushLimit(state, index, itemKey) {
6401
+ if (!itemKey) {
6402
+ return void 0;
6403
+ }
6404
+ const currentSize = state.sizes.get(itemKey);
6405
+ if (!(currentSize && currentSize > 0)) {
6406
+ return void 0;
6407
+ }
6408
+ const stickyIndexInArray = state.props.stickyIndicesArr.indexOf(index);
6409
+ if (stickyIndexInArray === -1) {
6410
+ return void 0;
6411
+ }
6412
+ const nextStickyIndex = state.props.stickyIndicesArr[stickyIndexInArray + 1];
6413
+ if (nextStickyIndex === void 0) {
6414
+ return void 0;
6415
+ }
6416
+ const nextStickyPosition = state.positions[nextStickyIndex];
6417
+ if (nextStickyPosition === void 0) {
6418
+ return void 0;
6419
+ }
6420
+ return nextStickyPosition - currentSize;
6421
+ }
6422
+
5323
6423
  // src/entrypoints/shared.ts
5324
6424
  var LegendListRuntime = LegendList;
5325
6425
  var internal = {
5326
6426
  getComponent,
6427
+ getStickyPushLimit,
5327
6428
  IsNewArchitecture,
5328
6429
  POSITION_OUT_OF_VIEW,
5329
6430
  peek$,
6431
+ typedForwardRef,
6432
+ typedMemo,
5330
6433
  useArr$,
5331
6434
  useCombinedRef,
5332
6435
  useStateContext
@@ -5336,4 +6439,4 @@ var internal = {
5336
6439
  var LegendList3 = LegendListRuntime;
5337
6440
  var internal2 = internal;
5338
6441
 
5339
- export { LegendList3 as LegendList, internal2 as internal, typedForwardRef, typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };
6442
+ export { LegendList3 as LegendList, internal2 as internal, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };