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

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