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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -34,37 +34,6 @@ var View = React3.forwardRef(function View2(props, ref) {
34
34
  });
35
35
  var Text = View;
36
36
 
37
- // src/state/getContentInsetEnd.ts
38
- function getContentInsetEnd(state) {
39
- var _a3;
40
- const { props } = state;
41
- const horizontal = props.horizontal;
42
- const contentInset = props.contentInset;
43
- const baseInset = contentInset != null ? contentInset : state.nativeContentInset;
44
- const overrideInset = (_a3 = state.contentInsetOverride) != null ? _a3 : void 0;
45
- if (overrideInset) {
46
- const mergedInset = { bottom: 0, right: 0, ...baseInset, ...overrideInset };
47
- return (horizontal ? mergedInset.right : mergedInset.bottom) || 0;
48
- }
49
- if (baseInset) {
50
- return (horizontal ? baseInset.right : baseInset.bottom) || 0;
51
- }
52
- return 0;
53
- }
54
-
55
- // src/state/getContentSize.ts
56
- function getContentSize(ctx) {
57
- var _a3;
58
- const { values, state } = ctx;
59
- const stylePaddingTop = values.get("stylePaddingTop") || 0;
60
- const stylePaddingBottom = state.props.stylePaddingBottom || 0;
61
- const headerSize = values.get("headerSize") || 0;
62
- const footerSize = values.get("footerSize") || 0;
63
- const contentInsetBottom = getContentInsetEnd(state);
64
- const totalSize = (_a3 = state.pendingTotalSize) != null ? _a3 : values.get("totalSize");
65
- return headerSize + footerSize + totalSize + stylePaddingTop + stylePaddingBottom + (contentInsetBottom || 0);
66
- }
67
-
68
37
  // src/platform/Animated.tsx
69
38
  var createAnimatedValue = (value) => value;
70
39
 
@@ -89,6 +58,11 @@ function StateProvider({ children }) {
89
58
  ["headerSize", 0],
90
59
  ["numContainers", 0],
91
60
  ["activeStickyIndex", -1],
61
+ ["isAtEnd", false],
62
+ ["isAtStart", false],
63
+ ["isNearEnd", false],
64
+ ["isNearStart", false],
65
+ ["isWithinMaintainScrollAtEndThreshold", false],
92
66
  ["totalSize", 0],
93
67
  ["scrollAdjustPending", 0]
94
68
  ]),
@@ -180,29 +154,71 @@ function notifyPosition$(ctx, key, value) {
180
154
  function useArr$(signalNames) {
181
155
  const ctx = React3__namespace.useContext(ContextState);
182
156
  const { subscribe, get } = React3__namespace.useMemo(() => createSelectorFunctionsArr(ctx, signalNames), [ctx, signalNames]);
183
- const value = shim.useSyncExternalStore(subscribe, get);
157
+ const value = shim.useSyncExternalStore(subscribe, get, get);
184
158
  return value;
185
159
  }
186
160
  function useSelector$(signalName, selector) {
187
161
  const ctx = React3__namespace.useContext(ContextState);
188
162
  const { subscribe, get } = React3__namespace.useMemo(() => createSelectorFunctionsArr(ctx, [signalName]), [ctx, signalName]);
189
- const value = shim.useSyncExternalStore(subscribe, () => selector(get()[0]));
163
+ const getSelectedValue = React3__namespace.useCallback(() => selector(get()[0]), [get, selector]);
164
+ const value = shim.useSyncExternalStore(subscribe, getSelectedValue, getSelectedValue);
190
165
  return value;
191
166
  }
192
167
 
168
+ // src/state/getContentInsetEnd.ts
169
+ function getContentInsetEnd(ctx) {
170
+ var _a3, _b;
171
+ const state = ctx.state;
172
+ const { props } = state;
173
+ const horizontal = props.horizontal;
174
+ const contentInset = props.contentInset;
175
+ const baseInset = contentInset != null ? contentInset : state.nativeContentInset;
176
+ const baseEndInset = (horizontal ? baseInset == null ? void 0 : baseInset.right : baseInset == null ? void 0 : baseInset.bottom) || 0;
177
+ const anchoredEndSpaceSize = peek$(ctx, "anchoredEndSpaceSize");
178
+ const anchoredEndInset = ((_a3 = props.anchoredEndSpace) == null ? void 0 : _a3.includeInEndInset) && anchoredEndSpaceSize ? anchoredEndSpaceSize : 0;
179
+ const overrideInset = (_b = state.contentInsetOverride) != null ? _b : void 0;
180
+ if (overrideInset) {
181
+ const mergedInset = { bottom: 0, right: 0, ...baseInset, ...overrideInset };
182
+ return Math.max((horizontal ? mergedInset.right : mergedInset.bottom) || 0, anchoredEndInset);
183
+ }
184
+ return Math.max(baseEndInset, anchoredEndInset);
185
+ }
186
+
187
+ // src/state/getContentSize.ts
188
+ function getContentSize(ctx) {
189
+ var _a3;
190
+ const { values, state } = ctx;
191
+ const stylePaddingTop = values.get("stylePaddingTop") || 0;
192
+ const stylePaddingBottom = state.props.stylePaddingBottom || 0;
193
+ const headerSize = values.get("headerSize") || 0;
194
+ const footerSize = values.get("footerSize") || 0;
195
+ const contentInsetBottom = getContentInsetEnd(ctx);
196
+ const totalSize = (_a3 = state.pendingTotalSize) != null ? _a3 : values.get("totalSize");
197
+ return headerSize + footerSize + totalSize + stylePaddingTop + stylePaddingBottom + (contentInsetBottom || 0);
198
+ }
199
+
193
200
  // src/components/DebugView.tsx
194
201
  var DebugRow = ({ children }) => {
195
202
  return /* @__PURE__ */ React3__namespace.createElement(View, { style: { alignItems: "center", flexDirection: "row", justifyContent: "space-between" } }, children);
196
203
  };
197
- React3__namespace.memo(function DebugView2({ state }) {
204
+ React3__namespace.memo(function DebugView2() {
198
205
  const ctx = useStateContext();
199
- const [totalSize = 0, scrollAdjust = 0, rawScroll = 0, scroll = 0, _numContainers = 0, _numContainersPooled = 0] = useArr$([
206
+ const [
207
+ totalSize = 0,
208
+ scrollAdjust = 0,
209
+ rawScroll = 0,
210
+ scroll = 0,
211
+ _numContainers = 0,
212
+ _numContainersPooled = 0,
213
+ isAtEnd = false
214
+ ] = useArr$([
200
215
  "totalSize",
201
216
  "scrollAdjust",
202
217
  "debugRawScroll",
203
218
  "debugComputedScroll",
204
219
  "numContainers",
205
- "numContainersPooled"
220
+ "numContainersPooled",
221
+ "isAtEnd"
206
222
  ]);
207
223
  const contentSize = getContentSize(ctx);
208
224
  const [, forceUpdate] = React3.useReducer((x) => x + 1, 0);
@@ -227,7 +243,7 @@ React3__namespace.memo(function DebugView2({ state }) {
227
243
  },
228
244
  /* @__PURE__ */ React3__namespace.createElement(DebugRow, null, /* @__PURE__ */ React3__namespace.createElement(Text, null, "TotalSize:"), /* @__PURE__ */ React3__namespace.createElement(Text, null, totalSize.toFixed(2))),
229
245
  /* @__PURE__ */ React3__namespace.createElement(DebugRow, null, /* @__PURE__ */ React3__namespace.createElement(Text, null, "ContentSize:"), /* @__PURE__ */ React3__namespace.createElement(Text, null, contentSize.toFixed(2))),
230
- /* @__PURE__ */ React3__namespace.createElement(DebugRow, null, /* @__PURE__ */ React3__namespace.createElement(Text, null, "At end:"), /* @__PURE__ */ React3__namespace.createElement(Text, null, String(state.isAtEnd))),
246
+ /* @__PURE__ */ React3__namespace.createElement(DebugRow, null, /* @__PURE__ */ React3__namespace.createElement(Text, null, "At end:"), /* @__PURE__ */ React3__namespace.createElement(Text, null, String(isAtEnd))),
231
247
  /* @__PURE__ */ React3__namespace.createElement(DebugRow, null, /* @__PURE__ */ React3__namespace.createElement(Text, null, "ScrollAdjust:"), /* @__PURE__ */ React3__namespace.createElement(Text, null, scrollAdjust.toFixed(2))),
232
248
  /* @__PURE__ */ React3__namespace.createElement(DebugRow, null, /* @__PURE__ */ React3__namespace.createElement(Text, null, "RawScroll: "), /* @__PURE__ */ React3__namespace.createElement(Text, null, rawScroll.toFixed(2))),
233
249
  /* @__PURE__ */ React3__namespace.createElement(DebugRow, null, /* @__PURE__ */ React3__namespace.createElement(Text, null, "ComputedScroll: "), /* @__PURE__ */ React3__namespace.createElement(Text, null, scroll.toFixed(2)))
@@ -250,6 +266,7 @@ var IS_DEV = (_a2 = processDev != null ? processDev : metroDev) != null ? _a2 :
250
266
 
251
267
  // src/constants.ts
252
268
  var POSITION_OUT_OF_VIEW = -1e7;
269
+ var EDGE_POSITION_EPSILON = 1;
253
270
  var ENABLE_DEVMODE = IS_DEV && false;
254
271
  var ENABLE_DEBUG_VIEW = IS_DEV && false;
255
272
  var typedForwardRef = React3__namespace.forwardRef;
@@ -709,17 +726,20 @@ var Container = typedMemo(function Container2({
709
726
  const { columnGap, rowGap, gap } = columnWrapperStyle;
710
727
  if (horizontal) {
711
728
  paddingStyles = {
729
+ paddingBottom: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0,
712
730
  paddingRight: columnGap || gap || void 0,
713
- paddingVertical: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
731
+ paddingTop: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
714
732
  };
715
733
  } else {
716
734
  paddingStyles = {
717
735
  paddingBottom: rowGap || gap || void 0,
718
- paddingHorizontal: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
736
+ paddingLeft: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0,
737
+ paddingRight: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
719
738
  };
720
739
  }
721
740
  }
722
741
  return horizontal ? {
742
+ boxSizing: paddingStyles ? "border-box" : void 0,
723
743
  flexDirection: ItemSeparatorComponent ? "row" : void 0,
724
744
  height: otherAxisSize,
725
745
  left: 0,
@@ -727,6 +747,7 @@ var Container = typedMemo(function Container2({
727
747
  top: otherAxisPos,
728
748
  ...paddingStyles || {}
729
749
  } : {
750
+ boxSizing: paddingStyles ? "border-box" : void 0,
730
751
  left: otherAxisPos,
731
752
  position: "absolute",
732
753
  right: numColumns > 1 ? null : 0,
@@ -955,9 +976,6 @@ var ContainersInner = typedMemo(function ContainersInner2({ horizontal, numColum
955
976
  style.marginRight = -gapX;
956
977
  }
957
978
  } else {
958
- if (gapX) {
959
- style.marginLeft = style.marginRight = -gapX;
960
- }
961
979
  if (gapY) {
962
980
  style.marginBottom = -gapY;
963
981
  }
@@ -1135,6 +1153,7 @@ function resolveWindowScrollTarget({ clampedOffset, horizontal, listPos, scroll
1135
1153
  var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView2({
1136
1154
  children,
1137
1155
  style,
1156
+ contentContainerClassName,
1138
1157
  contentContainerStyle,
1139
1158
  horizontal = false,
1140
1159
  contentOffset,
@@ -1270,18 +1289,18 @@ var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView
1270
1289
  const scrollEventCoalescer = useRafCoalescer(emitScroll);
1271
1290
  const handleScroll = React3.useCallback(
1272
1291
  (_event) => {
1273
- var _a3;
1274
1292
  if (!onScroll2) {
1275
1293
  return;
1276
1294
  }
1277
- const scrollingTo = (_a3 = ctx.state) == null ? void 0 : _a3.scrollingTo;
1278
- if (scrollingTo && !scrollingTo.animated) {
1295
+ const state = ctx.state;
1296
+ const shouldFlushImmediately = !!(state == null ? void 0 : state.scrollingTo) || !!(state == null ? void 0 : state.initialScrollSession) && !state.didFinishInitialScroll || !!(state == null ? void 0 : state.initialScroll) && !state.didFinishInitialScroll || !!state && isInMVCPActiveMode(state);
1297
+ if (shouldFlushImmediately) {
1279
1298
  scrollEventCoalescer.flush();
1280
1299
  } else {
1281
1300
  scrollEventCoalescer.schedule();
1282
1301
  }
1283
1302
  },
1284
- [onScroll2, scrollEventCoalescer]
1303
+ [ctx.state, onScroll2, scrollEventCoalescer]
1285
1304
  );
1286
1305
  React3.useLayoutEffect(() => {
1287
1306
  const target = getScrollTarget();
@@ -1347,13 +1366,14 @@ var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView
1347
1366
  ...StyleSheet.flatten(contentContainerStyle)
1348
1367
  };
1349
1368
  const {
1369
+ contentContainerClassName: _contentContainerClassName,
1350
1370
  contentInset: _contentInset,
1351
1371
  scrollEventThrottle: _scrollEventThrottle,
1352
1372
  ScrollComponent: _ScrollComponent,
1353
1373
  useWindowScroll: _useWindowScroll,
1354
1374
  ...webProps
1355
1375
  } = props;
1356
- return /* @__PURE__ */ React3__namespace.createElement("div", { ref: scrollRef, ...webProps, style: scrollViewStyle }, refreshControl, /* @__PURE__ */ React3__namespace.createElement("div", { ref: contentRef, style: contentStyle }, children));
1376
+ return /* @__PURE__ */ React3__namespace.createElement("div", { ref: scrollRef, ...webProps, style: scrollViewStyle }, refreshControl, /* @__PURE__ */ React3__namespace.createElement("div", { className: contentContainerClassName, ref: contentRef, style: contentStyle }, children));
1357
1377
  });
1358
1378
  function useValueListener$(key, callback) {
1359
1379
  const ctx = useStateContext();
@@ -1366,6 +1386,21 @@ function useValueListener$(key, callback) {
1366
1386
  }
1367
1387
 
1368
1388
  // src/components/ScrollAdjust.tsx
1389
+ function getScrollAdjustAxis(horizontal) {
1390
+ return horizontal ? {
1391
+ contentSizeKey: "scrollWidth",
1392
+ paddingEndProp: "paddingRight",
1393
+ viewportSizeKey: "clientWidth",
1394
+ x: 1,
1395
+ y: 0
1396
+ } : {
1397
+ contentSizeKey: "scrollHeight",
1398
+ paddingEndProp: "paddingBottom",
1399
+ viewportSizeKey: "clientHeight",
1400
+ x: 0,
1401
+ y: 1
1402
+ };
1403
+ }
1369
1404
  function ScrollAdjust() {
1370
1405
  const ctx = useStateContext();
1371
1406
  const lastScrollOffsetRef = React3__namespace.useRef(0);
@@ -1379,32 +1414,34 @@ function ScrollAdjust() {
1379
1414
  if (scrollView && scrollOffset !== lastScrollOffsetRef.current) {
1380
1415
  const scrollDelta = scrollOffset - lastScrollOffsetRef.current;
1381
1416
  if (scrollDelta !== 0) {
1417
+ const axis = getScrollAdjustAxis(!!ctx.state.props.horizontal);
1382
1418
  const contentNode = scrollView.getContentNode();
1383
1419
  const prevScroll = scrollView.getCurrentScrollOffset();
1384
1420
  const el = scrollView.getScrollableNode();
1421
+ const scrollBy = () => scrollView.scrollBy(axis.x * scrollDelta, axis.y * scrollDelta);
1385
1422
  if (!contentNode) {
1386
- scrollView.scrollBy(0, scrollDelta);
1423
+ scrollBy();
1387
1424
  lastScrollOffsetRef.current = scrollOffset;
1388
1425
  return;
1389
1426
  }
1390
- const totalSize = contentNode.scrollHeight;
1391
- const viewportSize = el.clientHeight;
1427
+ const totalSize = contentNode[axis.contentSizeKey];
1428
+ const viewportSize = el[axis.viewportSizeKey];
1392
1429
  const nextScroll = prevScroll + scrollDelta;
1393
1430
  if (scrollDelta > 0 && !ctx.state.adjustingFromInitialMount && totalSize < nextScroll + viewportSize) {
1394
- const paddingBottom = ctx.state.props.stylePaddingBottom || 0;
1431
+ const previousPaddingEnd = contentNode.style[axis.paddingEndProp];
1395
1432
  const pad = (nextScroll + viewportSize - totalSize) * 2;
1396
- contentNode.style.paddingBottom = `${pad}px`;
1433
+ contentNode.style[axis.paddingEndProp] = `${pad}px`;
1397
1434
  void contentNode.offsetHeight;
1398
- scrollView.scrollBy(0, scrollDelta);
1435
+ scrollBy();
1399
1436
  if (resetPaddingRafRef.current !== void 0) {
1400
1437
  cancelAnimationFrame(resetPaddingRafRef.current);
1401
1438
  }
1402
1439
  resetPaddingRafRef.current = requestAnimationFrame(() => {
1403
1440
  resetPaddingRafRef.current = void 0;
1404
- contentNode.style.paddingBottom = paddingBottom ? `${paddingBottom}px` : "0";
1441
+ contentNode.style[axis.paddingEndProp] = previousPaddingEnd;
1405
1442
  });
1406
1443
  } else {
1407
- scrollView.scrollBy(0, scrollDelta);
1444
+ scrollBy();
1408
1445
  }
1409
1446
  }
1410
1447
  lastScrollOffsetRef.current = scrollOffset;
@@ -1418,8 +1455,19 @@ function SnapWrapper({ ScrollComponent, ...props }) {
1418
1455
  const [snapToOffsets] = useArr$(["snapToOffsets"]);
1419
1456
  return /* @__PURE__ */ React3__namespace.createElement(ScrollComponent, { ...props, snapToOffsets });
1420
1457
  }
1458
+ function WebAnchoredEndSpace({ horizontal }) {
1459
+ const ctx = useStateContext();
1460
+ const [anchoredEndSpaceSize] = useArr$(["anchoredEndSpaceSize"]);
1461
+ const shouldRenderAnchoredEndSpace = !!ctx.state.props.anchoredEndSpace && (anchoredEndSpaceSize || 0) > 0;
1462
+ if (!shouldRenderAnchoredEndSpace) {
1463
+ return null;
1464
+ }
1465
+ const style = horizontal ? { height: "100%", width: anchoredEndSpaceSize || 0 } : { height: anchoredEndSpaceSize || 0 };
1466
+ return /* @__PURE__ */ React3__namespace.createElement("div", { style }, null);
1467
+ }
1421
1468
  var LayoutView = ({ onLayoutChange, refView, children, ...rest }) => {
1422
- const ref = refView != null ? refView : React3.useRef(null);
1469
+ const localRef = React3.useRef(null);
1470
+ const ref = refView != null ? refView : localRef;
1423
1471
  useOnLayoutSync({ onLayoutChange, ref });
1424
1472
  return /* @__PURE__ */ React3__namespace.createElement("div", { ...rest, ref }, children);
1425
1473
  };
@@ -1464,12 +1512,14 @@ var ListComponent = typedMemo(function ListComponent2({
1464
1512
  }) {
1465
1513
  const ctx = useStateContext();
1466
1514
  const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
1467
- const ScrollComponent = renderScrollComponent ? React3.useMemo(
1468
- () => React3__namespace.forwardRef(
1515
+ const ScrollComponent = React3.useMemo(() => {
1516
+ if (!renderScrollComponent) {
1517
+ return ListComponentScrollView;
1518
+ }
1519
+ return React3__namespace.forwardRef(
1469
1520
  (props, ref) => renderScrollComponent({ ...props, ref })
1470
- ),
1471
- [renderScrollComponent]
1472
- ) : ListComponentScrollView;
1521
+ );
1522
+ }, [renderScrollComponent]);
1473
1523
  const SnapOrScroll = snapToIndices ? SnapWrapper : ScrollComponent;
1474
1524
  React3.useLayoutEffect(() => {
1475
1525
  if (!ListHeaderComponent) {
@@ -1529,171 +1579,80 @@ var ListComponent = typedMemo(function ListComponent2({
1529
1579
  }
1530
1580
  ),
1531
1581
  ListFooterComponent && /* @__PURE__ */ React3__namespace.createElement(LayoutView, { onLayoutChange: onLayoutFooterInternal, style: ListFooterComponentStyle }, getComponent(ListFooterComponent)),
1582
+ /* @__PURE__ */ React3__namespace.createElement(WebAnchoredEndSpace, { horizontal }),
1532
1583
  IS_DEV && ENABLE_DEVMODE
1533
1584
  );
1534
1585
  });
1535
-
1536
- // src/core/calculateOffsetForIndex.ts
1537
- function calculateOffsetForIndex(ctx, index) {
1538
- const state = ctx.state;
1539
- return index !== void 0 ? state.positions[index] || 0 : 0;
1540
- }
1541
-
1542
- // src/core/getTopOffsetAdjustment.ts
1543
- function getTopOffsetAdjustment(ctx) {
1544
- return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
1545
- }
1546
-
1547
- // src/utils/getId.ts
1548
- function getId(state, index) {
1549
- const { data, keyExtractor } = state.props;
1550
- if (!data) {
1551
- return "";
1552
- }
1553
- const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
1554
- const id = ret;
1555
- state.idCache[index] = id;
1556
- return id;
1557
- }
1558
-
1559
- // src/core/addTotalSize.ts
1560
- function addTotalSize(ctx, key, add) {
1561
- const state = ctx.state;
1562
- const prevTotalSize = state.totalSize;
1563
- let totalSize = state.totalSize;
1564
- if (key === null) {
1565
- totalSize = add;
1566
- if (state.timeoutSetPaddingTop) {
1567
- clearTimeout(state.timeoutSetPaddingTop);
1568
- state.timeoutSetPaddingTop = void 0;
1569
- }
1570
- } else {
1571
- totalSize += add;
1572
- }
1573
- if (prevTotalSize !== totalSize) {
1574
- {
1575
- state.pendingTotalSize = void 0;
1576
- state.totalSize = totalSize;
1577
- set$(ctx, "totalSize", totalSize);
1578
- }
1579
- }
1580
- }
1581
-
1582
- // src/core/setSize.ts
1583
- function setSize(ctx, itemKey, size) {
1584
- const state = ctx.state;
1585
- const { sizes } = state;
1586
- const previousSize = sizes.get(itemKey);
1587
- const diff = previousSize !== void 0 ? size - previousSize : size;
1588
- if (diff !== 0) {
1589
- addTotalSize(ctx, itemKey, diff);
1590
- }
1591
- sizes.set(itemKey, size);
1592
- }
1593
-
1594
- // src/utils/getItemSize.ts
1595
- function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
1596
- var _a3, _b;
1597
- const state = ctx.state;
1598
- const {
1599
- sizesKnown,
1600
- sizes,
1601
- averageSizes,
1602
- props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType },
1603
- scrollingTo
1604
- } = state;
1605
- const sizeKnown = sizesKnown.get(key);
1606
- if (sizeKnown !== void 0) {
1607
- return sizeKnown;
1608
- }
1609
- let size;
1610
- if (preferCachedSize) {
1611
- const cachedSize = sizes.get(key);
1612
- if (cachedSize !== void 0) {
1613
- return cachedSize;
1614
- }
1615
- }
1616
- const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1617
- if (getFixedItemSize) {
1618
- size = getFixedItemSize(data, index, itemType);
1619
- if (size !== void 0) {
1620
- sizesKnown.set(key, size);
1621
- }
1622
- }
1623
- if (size === void 0 && useAverageSize && sizeKnown === void 0 && !scrollingTo) {
1624
- const averageSizeForType = (_b = averageSizes[itemType]) == null ? void 0 : _b.avg;
1625
- if (averageSizeForType !== void 0) {
1626
- size = roundSize(averageSizeForType);
1586
+ var WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH = 100;
1587
+ var WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO = 0.9;
1588
+ var WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO = 0.9;
1589
+ function useDevChecksImpl(props) {
1590
+ const ctx = useStateContext();
1591
+ const { childrenMode, keyExtractor, renderScrollComponent, stickyHeaderIndices, stickyIndices, useWindowScroll } = props;
1592
+ React3.useEffect(() => {
1593
+ if (stickyIndices && !stickyHeaderIndices) {
1594
+ warnDevOnce(
1595
+ "stickyIndices",
1596
+ "stickyIndices has been renamed to stickyHeaderIndices. Please update your props to use stickyHeaderIndices."
1597
+ );
1627
1598
  }
1628
- }
1629
- if (size === void 0) {
1630
- size = sizes.get(key);
1631
- if (size !== void 0) {
1632
- return size;
1599
+ }, [stickyHeaderIndices, stickyIndices]);
1600
+ React3.useEffect(() => {
1601
+ if (useWindowScroll && renderScrollComponent) {
1602
+ warnDevOnce(
1603
+ "useWindowScrollRenderScrollComponent",
1604
+ "useWindowScroll is not supported when renderScrollComponent is provided."
1605
+ );
1633
1606
  }
1634
- }
1635
- if (size === void 0) {
1636
- size = getEstimatedItemSize ? getEstimatedItemSize(data, index, itemType) : estimatedItemSize;
1637
- }
1638
- setSize(ctx, key, size);
1639
- return size;
1640
- }
1641
- function getItemSizeAtIndex(ctx, index) {
1642
- if (index === void 0 || index < 0) {
1643
- return void 0;
1644
- }
1645
- const targetId = getId(ctx.state, index);
1646
- return getItemSize(ctx, targetId, index, ctx.state.props.data[index]);
1647
- }
1648
-
1649
- // src/core/calculateOffsetWithOffsetPosition.ts
1650
- function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1651
- var _a3;
1652
- const state = ctx.state;
1653
- const { index, viewOffset, viewPosition } = params;
1654
- let offset = offsetParam;
1655
- if (viewOffset) {
1656
- offset -= viewOffset;
1657
- }
1658
- if (index !== void 0) {
1659
- const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
1660
- if (topOffsetAdjustment) {
1661
- offset += topOffsetAdjustment;
1607
+ }, [renderScrollComponent, useWindowScroll]);
1608
+ React3.useEffect(() => {
1609
+ if (!keyExtractor && !ctx.state.isFirst && ctx.state.didDataChange && !childrenMode) {
1610
+ warnDevOnce(
1611
+ "keyExtractor",
1612
+ "Changing data without a keyExtractor can cause slow performance and resetting scroll. If your list data can change you should use a keyExtractor with a unique id for best performance and behavior."
1613
+ );
1662
1614
  }
1663
- }
1664
- if (viewPosition !== void 0 && index !== void 0) {
1615
+ }, [childrenMode, ctx, keyExtractor]);
1616
+ React3.useEffect(() => {
1617
+ const state = ctx.state;
1665
1618
  const dataLength = state.props.data.length;
1666
- if (dataLength === 0) {
1667
- return offset;
1668
- }
1669
- const isOutOfBounds = index < 0 || index >= dataLength;
1670
- const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
1671
- const itemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1672
- const trailingInset = getContentInsetEnd(state);
1673
- offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1674
- if (!isOutOfBounds && index === state.props.data.length - 1) {
1675
- const footerSize = peek$(ctx, "footerSize") || 0;
1676
- offset += footerSize;
1619
+ const useWindowScrollResolved = state.props.useWindowScroll;
1620
+ if (useWindowScrollResolved || dataLength < WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH) {
1621
+ return;
1677
1622
  }
1678
- }
1679
- return offset;
1623
+ const warnIfUnboundedOuterSize = () => {
1624
+ const readyToRender = peek$(ctx, "readyToRender");
1625
+ const numContainers = peek$(ctx, "numContainers") || 0;
1626
+ const totalSize = peek$(ctx, "totalSize") || 0;
1627
+ const scrollLength = ctx.state.scrollLength || 0;
1628
+ if (!readyToRender || totalSize <= 0 || scrollLength <= 0) {
1629
+ return;
1630
+ }
1631
+ const rendersAlmostEverything = numContainers >= Math.ceil(dataLength * WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO);
1632
+ const viewportMatchesContent = scrollLength >= totalSize * WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO;
1633
+ if (rendersAlmostEverything && viewportMatchesContent) {
1634
+ warnDevOnce(
1635
+ "webUnboundedOuterSize",
1636
+ "LegendList appears to have an unbounded outer height on web, so virtualization is effectively disabled. Set a bounded height or flex: 1 on the list container, or use useWindowScroll."
1637
+ );
1638
+ }
1639
+ };
1640
+ warnIfUnboundedOuterSize();
1641
+ const unsubscribe = [
1642
+ listen$(ctx, "numContainers", warnIfUnboundedOuterSize),
1643
+ listen$(ctx, "readyToRender", warnIfUnboundedOuterSize),
1644
+ listen$(ctx, "totalSize", warnIfUnboundedOuterSize)
1645
+ ];
1646
+ return () => {
1647
+ for (const unsub of unsubscribe) {
1648
+ unsub();
1649
+ }
1650
+ };
1651
+ }, [ctx]);
1680
1652
  }
1681
-
1682
- // src/core/clampScrollOffset.ts
1683
- function clampScrollOffset(ctx, offset, scrollTarget) {
1684
- const state = ctx.state;
1685
- const contentSize = getContentSize(ctx);
1686
- let clampedOffset = offset;
1687
- if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform.OS !== "android")) {
1688
- const baseMaxOffset = Math.max(0, contentSize - state.scrollLength);
1689
- const viewOffset = scrollTarget == null ? void 0 : scrollTarget.viewOffset;
1690
- const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
1691
- const maxOffset = baseMaxOffset + extraEndOffset;
1692
- clampedOffset = Math.min(offset, maxOffset);
1693
- }
1694
- clampedOffset = Math.max(0, clampedOffset);
1695
- return clampedOffset;
1653
+ function useDevChecksNoop(_props) {
1696
1654
  }
1655
+ var useDevChecks = IS_DEV ? useDevChecksImpl : useDevChecksNoop;
1697
1656
 
1698
1657
  // src/core/deferredPublicOnScroll.ts
1699
1658
  function withResolvedContentOffset(state, event, resolvedOffset) {
@@ -1841,33 +1800,462 @@ function setInitialScrollSession(state, options = {}) {
1841
1800
  return state.initialScrollSession;
1842
1801
  }
1843
1802
 
1844
- // src/core/finishScrollTo.ts
1845
- function finishScrollTo(ctx) {
1846
- var _a3, _b;
1847
- const state = ctx.state;
1848
- if (state == null ? void 0 : state.scrollingTo) {
1849
- const resolvePendingScroll = state.pendingScrollResolve;
1850
- state.pendingScrollResolve = void 0;
1851
- const scrollingTo = state.scrollingTo;
1852
- state.scrollHistory.length = 0;
1853
- state.scrollingTo = void 0;
1854
- if (state.pendingTotalSize !== void 0) {
1855
- addTotalSize(ctx, null, state.pendingTotalSize);
1856
- }
1857
- {
1858
- state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
1859
- }
1860
- if (scrollingTo.isInitialScroll || state.initialScroll) {
1861
- const isOffsetSession = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
1862
- finishInitialScroll(ctx, {
1863
- onFinished: resolvePendingScroll,
1864
- preserveTarget: isOffsetSession && state.props.data.length === 0 || !!scrollingTo.isInitialScroll && !!((_b = state.initialScroll) == null ? void 0 : _b.preserveForFooterLayout),
1803
+ // src/utils/checkThreshold.ts
1804
+ var HYSTERESIS_MULTIPLIER = 1.3;
1805
+ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot, allowReentryOnChange) => {
1806
+ const absDistance = Math.abs(distance);
1807
+ const within = atThreshold || threshold > 0 && absDistance <= threshold;
1808
+ const updateSnapshot = () => {
1809
+ setSnapshot({
1810
+ atThreshold,
1811
+ contentSize: context.contentSize,
1812
+ dataLength: context.dataLength,
1813
+ scrollPosition: context.scrollPosition
1814
+ });
1815
+ };
1816
+ if (!wasReached) {
1817
+ if (!within) {
1818
+ return false;
1819
+ }
1820
+ onReached(distance);
1821
+ updateSnapshot();
1822
+ return true;
1823
+ }
1824
+ const reset = !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
1825
+ if (reset) {
1826
+ setSnapshot(void 0);
1827
+ return false;
1828
+ }
1829
+ if (within) {
1830
+ const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
1831
+ if (changed) {
1832
+ if (allowReentryOnChange) {
1833
+ onReached(distance);
1834
+ }
1835
+ updateSnapshot();
1836
+ }
1837
+ }
1838
+ return true;
1839
+ };
1840
+
1841
+ // src/utils/hasActiveInitialScroll.ts
1842
+ function hasActiveInitialScroll(state) {
1843
+ return !!(state == null ? void 0 : state.initialScroll) && !state.didFinishInitialScroll;
1844
+ }
1845
+
1846
+ // src/utils/checkAtBottom.ts
1847
+ function checkAtBottom(ctx) {
1848
+ var _a3;
1849
+ const state = ctx.state;
1850
+ if (!state) {
1851
+ return;
1852
+ }
1853
+ const {
1854
+ queuedInitialLayout,
1855
+ scrollLength,
1856
+ scroll,
1857
+ maintainingScrollAtEnd,
1858
+ props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
1859
+ } = state;
1860
+ const contentSize = getContentSize(ctx);
1861
+ if (contentSize > 0 && queuedInitialLayout) {
1862
+ const insetEnd = getContentInsetEnd(ctx);
1863
+ const distanceFromEnd = contentSize - scroll - scrollLength - insetEnd;
1864
+ const isContentLess = contentSize < scrollLength;
1865
+ set$(ctx, "isAtEnd", isContentLess || distanceFromEnd <= EDGE_POSITION_EPSILON);
1866
+ set$(ctx, "isNearEnd", isContentLess || distanceFromEnd <= onEndReachedThreshold * scrollLength);
1867
+ set$(
1868
+ ctx,
1869
+ "isWithinMaintainScrollAtEndThreshold",
1870
+ isContentLess || distanceFromEnd <= maintainScrollAtEndThreshold * scrollLength
1871
+ );
1872
+ const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || maintainingScrollAtEnd;
1873
+ if (!shouldSkipThresholdChecks) {
1874
+ state.isEndReached = checkThreshold(
1875
+ distanceFromEnd,
1876
+ isContentLess,
1877
+ onEndReachedThreshold * scrollLength,
1878
+ state.isEndReached,
1879
+ state.endReachedSnapshot,
1880
+ {
1881
+ contentSize,
1882
+ dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1883
+ scrollPosition: scroll
1884
+ },
1885
+ (distance) => {
1886
+ var _a4, _b;
1887
+ return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
1888
+ },
1889
+ (snapshot) => {
1890
+ state.endReachedSnapshot = snapshot;
1891
+ },
1892
+ true
1893
+ );
1894
+ }
1895
+ }
1896
+ }
1897
+
1898
+ // src/utils/checkAtTop.ts
1899
+ function checkAtTop(ctx) {
1900
+ const state = ctx == null ? void 0 : ctx.state;
1901
+ if (!state) {
1902
+ return;
1903
+ }
1904
+ const {
1905
+ dataChangeEpoch,
1906
+ isStartReached,
1907
+ props: { data, onStartReachedThreshold },
1908
+ scroll,
1909
+ scrollLength,
1910
+ startReachedSnapshot,
1911
+ startReachedSnapshotDataChangeEpoch,
1912
+ totalSize
1913
+ } = state;
1914
+ const dataLength = data.length;
1915
+ const threshold = onStartReachedThreshold * scrollLength;
1916
+ const dataChanged = startReachedSnapshotDataChangeEpoch !== dataChangeEpoch;
1917
+ const withinThreshold = threshold > 0 && Math.abs(scroll) <= threshold;
1918
+ const allowReentryOnDataChange = !!isStartReached && withinThreshold && !!dataChanged && !isInMVCPActiveMode(state);
1919
+ if (isStartReached && threshold > 0 && scroll > threshold && startReachedSnapshot && (dataChanged || startReachedSnapshot.contentSize !== totalSize || startReachedSnapshot.dataLength !== dataLength)) {
1920
+ state.isStartReached = false;
1921
+ state.startReachedSnapshot = void 0;
1922
+ state.startReachedSnapshotDataChangeEpoch = void 0;
1923
+ }
1924
+ set$(ctx, "isAtStart", scroll <= EDGE_POSITION_EPSILON);
1925
+ set$(ctx, "isNearStart", scroll <= threshold);
1926
+ const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || !!state.scrollingTo;
1927
+ const shouldDeferDataChangeRefire = isStartReached && withinThreshold && dataChanged && !allowReentryOnDataChange;
1928
+ if (!shouldSkipThresholdChecks && !shouldDeferDataChangeRefire) {
1929
+ state.isStartReached = checkThreshold(
1930
+ scroll,
1931
+ false,
1932
+ threshold,
1933
+ state.isStartReached,
1934
+ allowReentryOnDataChange ? void 0 : startReachedSnapshot,
1935
+ {
1936
+ contentSize: totalSize,
1937
+ dataLength,
1938
+ scrollPosition: scroll
1939
+ },
1940
+ (distance) => {
1941
+ var _a3, _b;
1942
+ return (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
1943
+ },
1944
+ (snapshot) => {
1945
+ state.startReachedSnapshot = snapshot;
1946
+ state.startReachedSnapshotDataChangeEpoch = snapshot ? dataChangeEpoch : void 0;
1947
+ },
1948
+ allowReentryOnDataChange
1949
+ );
1950
+ }
1951
+ }
1952
+
1953
+ // src/utils/checkThresholds.ts
1954
+ function checkThresholds(ctx) {
1955
+ checkAtBottom(ctx);
1956
+ checkAtTop(ctx);
1957
+ }
1958
+
1959
+ // src/core/recalculateSettledScroll.ts
1960
+ function recalculateSettledScroll(ctx) {
1961
+ var _a3, _b;
1962
+ const state = ctx.state;
1963
+ if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1964
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1965
+ }
1966
+ checkThresholds(ctx);
1967
+ }
1968
+
1969
+ // src/utils/setInitialRenderState.ts
1970
+ function setInitialRenderState(ctx, {
1971
+ didLayout,
1972
+ didInitialScroll
1973
+ }) {
1974
+ const { state } = ctx;
1975
+ const {
1976
+ loadStartTime,
1977
+ props: { onLoad }
1978
+ } = state;
1979
+ if (didLayout) {
1980
+ state.didContainersLayout = true;
1981
+ }
1982
+ if (didInitialScroll) {
1983
+ state.didFinishInitialScroll = true;
1984
+ }
1985
+ const isReadyToRender = Boolean(state.didContainersLayout && state.didFinishInitialScroll);
1986
+ if (isReadyToRender && !peek$(ctx, "readyToRender")) {
1987
+ set$(ctx, "readyToRender", true);
1988
+ if (onLoad) {
1989
+ onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1990
+ }
1991
+ }
1992
+ }
1993
+
1994
+ // src/core/finishInitialScroll.ts
1995
+ var PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS = 2e3;
1996
+ function syncInitialScrollOffset(state, offset) {
1997
+ state.scroll = offset;
1998
+ state.scrollPending = offset;
1999
+ state.scrollPrev = offset;
2000
+ }
2001
+ function clearPreservedInitialScrollTargetTimeout(state) {
2002
+ if (state.timeoutPreservedInitialScrollClear !== void 0) {
2003
+ clearTimeout(state.timeoutPreservedInitialScrollClear);
2004
+ state.timeoutPreservedInitialScrollClear = void 0;
2005
+ }
2006
+ }
2007
+ function clearPreservedInitialScrollTarget(state) {
2008
+ clearPreservedInitialScrollTargetTimeout(state);
2009
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
2010
+ state.initialScroll = void 0;
2011
+ setInitialScrollSession(state);
2012
+ }
2013
+ function finishInitialScroll(ctx, options) {
2014
+ var _a3, _b, _c;
2015
+ const state = ctx.state;
2016
+ if ((options == null ? void 0 : options.resolvedOffset) !== void 0) {
2017
+ syncInitialScrollOffset(state, options.resolvedOffset);
2018
+ } else if ((options == null ? void 0 : options.syncObservedOffset) && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
2019
+ const observedOffset = (_c = (_b = state.refScroller.current) == null ? void 0 : _b.getCurrentScrollOffset) == null ? void 0 : _c.call(_b);
2020
+ if (typeof observedOffset === "number" && Number.isFinite(observedOffset)) {
2021
+ syncInitialScrollOffset(state, observedOffset);
2022
+ }
2023
+ }
2024
+ const complete = () => {
2025
+ var _a4, _b2, _c2, _d, _e;
2026
+ const shouldReleaseDeferredPublicOnScroll = ((_a4 = state.initialScrollSession) == null ? void 0 : _a4.kind) === "bootstrap";
2027
+ const finalScrollOffset = (_d = (_c2 = (_b2 = options == null ? void 0 : options.resolvedOffset) != null ? _b2 : state.scrollPending) != null ? _c2 : state.scroll) != null ? _d : 0;
2028
+ initialScrollWatchdog.clear(state);
2029
+ if ((options == null ? void 0 : options.preserveTarget) && state.initialScroll) {
2030
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
2031
+ setInitialScrollSession(state);
2032
+ clearPreservedInitialScrollTargetTimeout(state);
2033
+ if (options == null ? void 0 : options.schedulePreservedTargetClear) {
2034
+ state.timeoutPreservedInitialScrollClear = setTimeout(() => {
2035
+ var _a5;
2036
+ state.timeoutPreservedInitialScrollClear = void 0;
2037
+ if (!state.didFinishInitialScroll || ((_a5 = state.scrollingTo) == null ? void 0 : _a5.isInitialScroll) || !state.initialScroll) {
2038
+ return;
2039
+ }
2040
+ clearPreservedInitialScrollTarget(state);
2041
+ }, PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS);
2042
+ }
2043
+ } else {
2044
+ clearPreservedInitialScrollTarget(state);
2045
+ }
2046
+ if (options == null ? void 0 : options.recalculateItems) {
2047
+ recalculateSettledScroll(ctx);
2048
+ }
2049
+ setInitialRenderState(ctx, { didInitialScroll: true });
2050
+ if (shouldReleaseDeferredPublicOnScroll) {
2051
+ releaseDeferredPublicOnScroll(ctx, finalScrollOffset);
2052
+ }
2053
+ (_e = options == null ? void 0 : options.onFinished) == null ? void 0 : _e.call(options);
2054
+ };
2055
+ if (options == null ? void 0 : options.waitForCompletionFrame) {
2056
+ requestAnimationFrame(complete);
2057
+ return;
2058
+ }
2059
+ complete();
2060
+ }
2061
+
2062
+ // src/core/calculateOffsetForIndex.ts
2063
+ function calculateOffsetForIndex(ctx, index) {
2064
+ const state = ctx.state;
2065
+ return index !== void 0 ? state.positions[index] || 0 : 0;
2066
+ }
2067
+
2068
+ // src/core/getTopOffsetAdjustment.ts
2069
+ function getTopOffsetAdjustment(ctx) {
2070
+ return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
2071
+ }
2072
+
2073
+ // src/utils/getId.ts
2074
+ function getId(state, index) {
2075
+ const { data, keyExtractor } = state.props;
2076
+ if (!data) {
2077
+ return "";
2078
+ }
2079
+ const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
2080
+ const id = ret;
2081
+ state.idCache[index] = id;
2082
+ return id;
2083
+ }
2084
+
2085
+ // src/core/addTotalSize.ts
2086
+ function addTotalSize(ctx, key, add) {
2087
+ const state = ctx.state;
2088
+ const prevTotalSize = state.totalSize;
2089
+ let totalSize = state.totalSize;
2090
+ if (key === null) {
2091
+ totalSize = add;
2092
+ if (state.timeoutSetPaddingTop) {
2093
+ clearTimeout(state.timeoutSetPaddingTop);
2094
+ state.timeoutSetPaddingTop = void 0;
2095
+ }
2096
+ } else {
2097
+ totalSize += add;
2098
+ }
2099
+ if (prevTotalSize !== totalSize) {
2100
+ {
2101
+ state.pendingTotalSize = void 0;
2102
+ state.totalSize = totalSize;
2103
+ set$(ctx, "totalSize", totalSize);
2104
+ }
2105
+ }
2106
+ }
2107
+
2108
+ // src/core/setSize.ts
2109
+ function setSize(ctx, itemKey, size) {
2110
+ const state = ctx.state;
2111
+ const { sizes } = state;
2112
+ const previousSize = sizes.get(itemKey);
2113
+ const diff = previousSize !== void 0 ? size - previousSize : size;
2114
+ if (diff !== 0) {
2115
+ addTotalSize(ctx, itemKey, diff);
2116
+ }
2117
+ sizes.set(itemKey, size);
2118
+ }
2119
+
2120
+ // src/utils/getItemSize.ts
2121
+ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
2122
+ var _a3, _b, _c;
2123
+ const state = ctx.state;
2124
+ const {
2125
+ sizesKnown,
2126
+ sizes,
2127
+ averageSizes,
2128
+ props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType },
2129
+ scrollingTo
2130
+ } = state;
2131
+ const sizeKnown = sizesKnown.get(key);
2132
+ if (sizeKnown !== void 0) {
2133
+ return sizeKnown;
2134
+ }
2135
+ let size;
2136
+ const renderedSize = sizes.get(key);
2137
+ if (preferCachedSize) {
2138
+ if (renderedSize !== void 0) {
2139
+ return renderedSize;
2140
+ }
2141
+ }
2142
+ const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
2143
+ if (getFixedItemSize) {
2144
+ size = getFixedItemSize(data, index, itemType);
2145
+ if (size !== void 0) {
2146
+ sizesKnown.set(key, size);
2147
+ }
2148
+ }
2149
+ if (size === void 0 && useAverageSize && sizeKnown === void 0 && !scrollingTo) {
2150
+ const averageSizeForType = (_b = averageSizes[itemType]) == null ? void 0 : _b.avg;
2151
+ if (averageSizeForType !== void 0) {
2152
+ size = roundSize(averageSizeForType);
2153
+ }
2154
+ }
2155
+ if (size === void 0 && renderedSize !== void 0) {
2156
+ return renderedSize;
2157
+ }
2158
+ if (size === void 0 && useAverageSize && sizeKnown === void 0 && scrollingTo) {
2159
+ const averageSizeForType = (_c = scrollingTo.averageSizeSnapshot) == null ? void 0 : _c[itemType];
2160
+ if (averageSizeForType !== void 0) {
2161
+ size = roundSize(averageSizeForType);
2162
+ }
2163
+ }
2164
+ if (size === void 0) {
2165
+ size = getEstimatedItemSize ? getEstimatedItemSize(data, index, itemType) : estimatedItemSize;
2166
+ }
2167
+ setSize(ctx, key, size);
2168
+ return size;
2169
+ }
2170
+ function getItemSizeAtIndex(ctx, index) {
2171
+ if (index === void 0 || index < 0) {
2172
+ return void 0;
2173
+ }
2174
+ const targetId = getId(ctx.state, index);
2175
+ return getItemSize(ctx, targetId, index, ctx.state.props.data[index]);
2176
+ }
2177
+
2178
+ // src/core/calculateOffsetWithOffsetPosition.ts
2179
+ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
2180
+ var _a3;
2181
+ const state = ctx.state;
2182
+ const { index, viewOffset, viewPosition } = params;
2183
+ let offset = offsetParam;
2184
+ if (viewOffset) {
2185
+ offset -= viewOffset;
2186
+ }
2187
+ if (index !== void 0) {
2188
+ const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
2189
+ if (topOffsetAdjustment) {
2190
+ offset += topOffsetAdjustment;
2191
+ }
2192
+ }
2193
+ if (viewPosition !== void 0 && index !== void 0) {
2194
+ const dataLength = state.props.data.length;
2195
+ if (dataLength === 0) {
2196
+ return offset;
2197
+ }
2198
+ const isOutOfBounds = index < 0 || index >= dataLength;
2199
+ const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
2200
+ const itemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
2201
+ const trailingInset = getContentInsetEnd(ctx);
2202
+ offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
2203
+ if (!isOutOfBounds && index === state.props.data.length - 1) {
2204
+ const footerSize = peek$(ctx, "footerSize") || 0;
2205
+ offset += footerSize;
2206
+ }
2207
+ }
2208
+ return offset;
2209
+ }
2210
+
2211
+ // src/core/clampScrollOffset.ts
2212
+ function clampScrollOffset(ctx, offset, scrollTarget) {
2213
+ const state = ctx.state;
2214
+ const contentSize = getContentSize(ctx);
2215
+ let clampedOffset = offset;
2216
+ if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform.OS !== "android")) {
2217
+ const baseMaxOffset = Math.max(0, contentSize - state.scrollLength);
2218
+ const viewOffset = scrollTarget == null ? void 0 : scrollTarget.viewOffset;
2219
+ const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
2220
+ const maxOffset = baseMaxOffset + extraEndOffset;
2221
+ clampedOffset = Math.min(offset, maxOffset);
2222
+ }
2223
+ clampedOffset = Math.max(0, clampedOffset);
2224
+ return clampedOffset;
2225
+ }
2226
+
2227
+ // src/core/finishScrollTo.ts
2228
+ function finishScrollTo(ctx) {
2229
+ var _a3, _b;
2230
+ const state = ctx.state;
2231
+ if (state == null ? void 0 : state.scrollingTo) {
2232
+ const resolvePendingScroll = state.pendingScrollResolve;
2233
+ state.pendingScrollResolve = void 0;
2234
+ const scrollingTo = state.scrollingTo;
2235
+ state.scrollHistory.length = 0;
2236
+ state.scrollingTo = void 0;
2237
+ if (state.pendingTotalSize !== void 0) {
2238
+ addTotalSize(ctx, null, state.pendingTotalSize);
2239
+ }
2240
+ {
2241
+ state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
2242
+ }
2243
+ if (scrollingTo.isInitialScroll || state.initialScroll) {
2244
+ const isOffsetSession = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
2245
+ const shouldPreserveResizeTarget = !!scrollingTo.isInitialScroll && !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_b = state.initialScroll) == null ? void 0 : _b.viewPosition) === 1;
2246
+ finishInitialScroll(ctx, {
2247
+ onFinished: () => {
2248
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
2249
+ },
2250
+ preserveTarget: isOffsetSession && state.props.data.length === 0 || shouldPreserveResizeTarget,
1865
2251
  recalculateItems: true,
2252
+ schedulePreservedTargetClear: shouldPreserveResizeTarget,
1866
2253
  syncObservedOffset: isOffsetSession,
1867
2254
  waitForCompletionFrame: !!scrollingTo.waitForInitialScrollCompletionFrame
1868
2255
  });
1869
2256
  return;
1870
2257
  }
2258
+ recalculateSettledScroll(ctx);
1871
2259
  resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1872
2260
  }
1873
2261
  }
@@ -1878,6 +2266,7 @@ var SCROLL_END_MAX_MS = 1500;
1878
2266
  var SMOOTH_SCROLL_DURATION_MS = 320;
1879
2267
  var SCROLL_END_TARGET_EPSILON = 1;
1880
2268
  function doScrollTo(ctx, params) {
2269
+ var _a3, _b;
1881
2270
  const state = ctx.state;
1882
2271
  const { animated, horizontal, offset } = params;
1883
2272
  const scroller = state.refScroller.current;
@@ -1891,7 +2280,7 @@ function doScrollTo(ctx, params) {
1891
2280
  const top = isHorizontal ? 0 : offset;
1892
2281
  scroller.scrollTo({ animated: isAnimated, x: left, y: top });
1893
2282
  if (isAnimated) {
1894
- const target = scroller.getScrollEventTarget();
2283
+ const target = (_b = (_a3 = scroller.getScrollEventTarget) == null ? void 0 : _a3.call(scroller)) != null ? _b : null;
1895
2284
  listenForScrollEnd(ctx, {
1896
2285
  readOffset: () => scroller.getCurrentScrollOffset(),
1897
2286
  target,
@@ -1957,6 +2346,17 @@ function listenForScrollEnd(ctx, params) {
1957
2346
  }
1958
2347
 
1959
2348
  // src/core/scrollTo.ts
2349
+ function getAverageSizeSnapshot(state) {
2350
+ if (Object.keys(state.averageSizes).length === 0) {
2351
+ return void 0;
2352
+ }
2353
+ const snapshot = {};
2354
+ for (const itemType in state.averageSizes) {
2355
+ const averages = state.averageSizes[itemType];
2356
+ snapshot[itemType] = averages.avg;
2357
+ }
2358
+ return snapshot;
2359
+ }
1960
2360
  function syncInitialScrollNativeWatchdog(state, options) {
1961
2361
  var _a3;
1962
2362
  const { isInitialScroll, requestedOffset, targetOffset } = options;
@@ -2004,8 +2404,10 @@ function scrollTo(ctx, params) {
2004
2404
  if (isInitialScroll) {
2005
2405
  initialScrollCompletion.resetFlags(state);
2006
2406
  }
2407
+ const averageSizeSnapshot = getAverageSizeSnapshot(state);
2007
2408
  state.scrollingTo = {
2008
2409
  ...scrollTarget,
2410
+ ...averageSizeSnapshot ? { averageSizeSnapshot } : {},
2009
2411
  targetOffset,
2010
2412
  waitForInitialScrollCompletionFrame
2011
2413
  };
@@ -2056,186 +2458,13 @@ function scrollToIndex(ctx, {
2056
2458
  index,
2057
2459
  isInitialScroll,
2058
2460
  itemSize,
2059
- offset: firstIndexOffset,
2060
- viewOffset,
2061
- viewPosition: viewPosition != null ? viewPosition : 0
2062
- });
2063
- }
2064
-
2065
- // src/utils/checkThreshold.ts
2066
- var HYSTERESIS_MULTIPLIER = 1.3;
2067
- var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot, allowReentryOnChange) => {
2068
- const absDistance = Math.abs(distance);
2069
- const within = atThreshold || threshold > 0 && absDistance <= threshold;
2070
- const updateSnapshot = () => {
2071
- setSnapshot({
2072
- atThreshold,
2073
- contentSize: context.contentSize,
2074
- dataLength: context.dataLength,
2075
- scrollPosition: context.scrollPosition
2076
- });
2077
- };
2078
- if (!wasReached) {
2079
- if (!within) {
2080
- return false;
2081
- }
2082
- onReached(distance);
2083
- updateSnapshot();
2084
- return true;
2085
- }
2086
- const reset = !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
2087
- if (reset) {
2088
- setSnapshot(void 0);
2089
- return false;
2090
- }
2091
- if (within) {
2092
- const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
2093
- if (changed) {
2094
- if (allowReentryOnChange) {
2095
- onReached(distance);
2096
- }
2097
- updateSnapshot();
2098
- }
2099
- }
2100
- return true;
2101
- };
2102
-
2103
- // src/utils/checkAtBottom.ts
2104
- function checkAtBottom(ctx) {
2105
- var _a3;
2106
- const state = ctx.state;
2107
- if (!state || state.initialScroll) {
2108
- return;
2109
- }
2110
- const {
2111
- queuedInitialLayout,
2112
- scrollLength,
2113
- scroll,
2114
- maintainingScrollAtEnd,
2115
- props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
2116
- } = state;
2117
- if (state.initialScroll) {
2118
- return;
2119
- }
2120
- const contentSize = getContentSize(ctx);
2121
- if (contentSize > 0 && queuedInitialLayout && !maintainingScrollAtEnd) {
2122
- const insetEnd = getContentInsetEnd(state);
2123
- const distanceFromEnd = contentSize - scroll - scrollLength - insetEnd;
2124
- const isContentLess = contentSize < scrollLength;
2125
- state.isAtEnd = isContentLess || distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
2126
- state.isEndReached = checkThreshold(
2127
- distanceFromEnd,
2128
- isContentLess,
2129
- onEndReachedThreshold * scrollLength,
2130
- state.isEndReached,
2131
- state.endReachedSnapshot,
2132
- {
2133
- contentSize,
2134
- dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
2135
- scrollPosition: scroll
2136
- },
2137
- (distance) => {
2138
- var _a4, _b;
2139
- return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
2140
- },
2141
- (snapshot) => {
2142
- state.endReachedSnapshot = snapshot;
2143
- },
2144
- true
2145
- );
2146
- }
2147
- }
2148
-
2149
- // src/utils/checkAtTop.ts
2150
- function checkAtTop(ctx) {
2151
- const state = ctx == null ? void 0 : ctx.state;
2152
- if (!state || state.initialScroll || state.scrollingTo) {
2153
- return;
2154
- }
2155
- const {
2156
- dataChangeEpoch,
2157
- isStartReached,
2158
- props: { data, onStartReachedThreshold },
2159
- scroll,
2160
- scrollLength,
2161
- startReachedSnapshot,
2162
- startReachedSnapshotDataChangeEpoch,
2163
- totalSize
2164
- } = state;
2165
- const dataLength = data.length;
2166
- const threshold = onStartReachedThreshold * scrollLength;
2167
- const dataChanged = startReachedSnapshotDataChangeEpoch !== dataChangeEpoch;
2168
- const withinThreshold = threshold > 0 && Math.abs(scroll) <= threshold;
2169
- const allowReentryOnDataChange = !!isStartReached && withinThreshold && !!dataChanged && !isInMVCPActiveMode(state);
2170
- if (isStartReached && threshold > 0 && scroll > threshold && startReachedSnapshot && (dataChanged || startReachedSnapshot.contentSize !== totalSize || startReachedSnapshot.dataLength !== dataLength)) {
2171
- state.isStartReached = false;
2172
- state.startReachedSnapshot = void 0;
2173
- state.startReachedSnapshotDataChangeEpoch = void 0;
2174
- }
2175
- state.isAtStart = scroll <= 0;
2176
- if (isStartReached && withinThreshold && dataChanged && !allowReentryOnDataChange) {
2177
- return;
2178
- }
2179
- state.isStartReached = checkThreshold(
2180
- scroll,
2181
- false,
2182
- threshold,
2183
- state.isStartReached,
2184
- allowReentryOnDataChange ? void 0 : startReachedSnapshot,
2185
- {
2186
- contentSize: totalSize,
2187
- dataLength,
2188
- scrollPosition: scroll
2189
- },
2190
- (distance) => {
2191
- var _a3, _b;
2192
- return (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
2193
- },
2194
- (snapshot) => {
2195
- state.startReachedSnapshot = snapshot;
2196
- state.startReachedSnapshotDataChangeEpoch = snapshot ? dataChangeEpoch : void 0;
2197
- },
2198
- allowReentryOnDataChange
2199
- );
2200
- }
2201
-
2202
- // src/utils/checkThresholds.ts
2203
- function checkThresholds(ctx) {
2204
- checkAtBottom(ctx);
2205
- checkAtTop(ctx);
2206
- }
2207
-
2208
- // src/utils/setInitialRenderState.ts
2209
- function setInitialRenderState(ctx, {
2210
- didLayout,
2211
- didInitialScroll
2212
- }) {
2213
- const { state } = ctx;
2214
- const {
2215
- loadStartTime,
2216
- props: { onLoad }
2217
- } = state;
2218
- if (didLayout) {
2219
- state.didContainersLayout = true;
2220
- }
2221
- if (didInitialScroll) {
2222
- state.didFinishInitialScroll = true;
2223
- }
2224
- const isReadyToRender = Boolean(state.didContainersLayout && state.didFinishInitialScroll);
2225
- if (isReadyToRender && !peek$(ctx, "readyToRender")) {
2226
- set$(ctx, "readyToRender", true);
2227
- if (onLoad) {
2228
- onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
2229
- }
2230
- }
2461
+ offset: firstIndexOffset,
2462
+ viewOffset,
2463
+ viewPosition: viewPosition != null ? viewPosition : 0
2464
+ });
2231
2465
  }
2232
2466
 
2233
2467
  // src/core/initialScroll.ts
2234
- function syncInitialScrollOffset(state, offset) {
2235
- state.scroll = offset;
2236
- state.scrollPending = offset;
2237
- state.scrollPrev = offset;
2238
- }
2239
2468
  function dispatchInitialScroll(ctx, params) {
2240
2469
  const { forceScroll, resolvedOffset, target, waitForCompletionFrame } = params;
2241
2470
  const requestedIndex = target.index;
@@ -2256,6 +2485,11 @@ function dispatchInitialScroll(ctx, params) {
2256
2485
  }
2257
2486
  function setInitialScrollTarget(state, target, options) {
2258
2487
  var _a3;
2488
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
2489
+ if (state.timeoutPreservedInitialScrollClear !== void 0) {
2490
+ clearTimeout(state.timeoutPreservedInitialScrollClear);
2491
+ state.timeoutPreservedInitialScrollClear = void 0;
2492
+ }
2259
2493
  state.initialScroll = target;
2260
2494
  if ((options == null ? void 0 : options.resetDidFinish) && state.didFinishInitialScroll) {
2261
2495
  state.didFinishInitialScroll = false;
@@ -2264,44 +2498,6 @@ function setInitialScrollTarget(state, target, options) {
2264
2498
  kind: ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" ? "offset" : "bootstrap"
2265
2499
  });
2266
2500
  }
2267
- function finishInitialScroll(ctx, options) {
2268
- var _a3, _b, _c;
2269
- const state = ctx.state;
2270
- if ((options == null ? void 0 : options.resolvedOffset) !== void 0) {
2271
- syncInitialScrollOffset(state, options.resolvedOffset);
2272
- } else if ((options == null ? void 0 : options.syncObservedOffset) && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
2273
- const observedOffset = (_c = (_b = state.refScroller.current) == null ? void 0 : _b.getCurrentScrollOffset) == null ? void 0 : _c.call(_b);
2274
- if (typeof observedOffset === "number" && Number.isFinite(observedOffset)) {
2275
- syncInitialScrollOffset(state, observedOffset);
2276
- }
2277
- }
2278
- const complete = () => {
2279
- var _a4, _b2, _c2, _d, _e, _f, _g;
2280
- const shouldReleaseDeferredPublicOnScroll = ((_a4 = state.initialScrollSession) == null ? void 0 : _a4.kind) === "bootstrap";
2281
- const finalScrollOffset = (_d = (_c2 = (_b2 = options == null ? void 0 : options.resolvedOffset) != null ? _b2 : state.scrollPending) != null ? _c2 : state.scroll) != null ? _d : 0;
2282
- initialScrollWatchdog.clear(state);
2283
- if (!(options == null ? void 0 : options.preserveTarget)) {
2284
- state.initialScroll = void 0;
2285
- }
2286
- setInitialScrollSession(state);
2287
- if ((options == null ? void 0 : options.recalculateItems) && ((_e = state.props) == null ? void 0 : _e.data)) {
2288
- (_f = state.triggerCalculateItemsInView) == null ? void 0 : _f.call(state, { forceFullItemPositions: true });
2289
- }
2290
- if (options == null ? void 0 : options.recalculateItems) {
2291
- checkThresholds(ctx);
2292
- }
2293
- setInitialRenderState(ctx, { didInitialScroll: true });
2294
- if (shouldReleaseDeferredPublicOnScroll) {
2295
- releaseDeferredPublicOnScroll(ctx, finalScrollOffset);
2296
- }
2297
- (_g = options == null ? void 0 : options.onFinished) == null ? void 0 : _g.call(options);
2298
- };
2299
- if (options == null ? void 0 : options.waitForCompletionFrame) {
2300
- requestAnimationFrame(complete);
2301
- return;
2302
- }
2303
- complete();
2304
- }
2305
2501
  function resolveInitialScrollOffset(ctx, initialScroll) {
2306
2502
  var _a3, _b;
2307
2503
  const state = ctx.state;
@@ -2395,13 +2591,18 @@ function advanceCurrentInitialScrollSession(ctx, options) {
2395
2591
  function isNullOrUndefined2(value) {
2396
2592
  return value === null || value === void 0;
2397
2593
  }
2398
- function getMountedBufferedIndices(state) {
2399
- const { startBuffered, endBuffered } = state;
2400
- if (!isNullOrUndefined2(endBuffered) && !isNullOrUndefined2(startBuffered) && startBuffered >= 0 && endBuffered >= 0) {
2401
- return Array.from(state.containerItemKeys.keys()).map((key) => state.indexByKey.get(key)).filter((index) => index !== void 0 && index >= startBuffered && index <= endBuffered).sort((a, b) => a - b);
2594
+ function getMountedIndicesInRange(state, start, end) {
2595
+ if (!isNullOrUndefined2(end) && !isNullOrUndefined2(start) && start >= 0 && end >= 0) {
2596
+ return Array.from(state.containerItemKeys.keys()).map((key) => state.indexByKey.get(key)).filter((index) => index !== void 0 && index >= start && index <= end).sort((a, b) => a - b);
2402
2597
  }
2403
2598
  return [];
2404
2599
  }
2600
+ function getMountedBufferedIndices(state) {
2601
+ return getMountedIndicesInRange(state, state.startBuffered, state.endBuffered);
2602
+ }
2603
+ function getMountedNoBufferIndices(state) {
2604
+ return getMountedIndicesInRange(state, state.startNoBuffer, state.endNoBuffer);
2605
+ }
2405
2606
  function checkAllSizesKnown(state, indices = getMountedBufferedIndices(state)) {
2406
2607
  return indices.length > 0 && indices.every((index) => {
2407
2608
  const key = getId(state, index);
@@ -2620,16 +2821,22 @@ function createRetargetedBottomAlignedInitialScroll(options) {
2620
2821
  function areEquivalentBootstrapInitialScrollTargets(current, next) {
2621
2822
  return current.index === next.index && current.preserveForBottomPadding === next.preserveForBottomPadding && current.preserveForFooterLayout === next.preserveForFooterLayout && current.viewOffset === next.viewOffset && current.viewPosition === next.viewPosition;
2622
2823
  }
2623
- function clearPendingInitialScrollFooterLayout(state, target) {
2824
+ function clearPendingInitialScrollFooterLayout(ctx, options) {
2825
+ const { dataLength, stylePaddingBottom, target } = options;
2826
+ const state = ctx.state;
2624
2827
  if (!shouldPreserveInitialScrollForFooterLayout(target)) {
2625
2828
  return;
2626
2829
  }
2627
- if (state.didFinishInitialScroll && !getBootstrapInitialScrollSession(state)) {
2628
- state.initialScroll = void 0;
2629
- setInitialScrollSession(state);
2630
- return;
2631
- }
2632
- setInitialScrollTarget(state, { ...target, preserveForFooterLayout: void 0 });
2830
+ const clearedFooterTarget = createInitialScrollAtEndTarget({
2831
+ dataLength,
2832
+ footerSize: 0,
2833
+ preserveForFooterLayout: void 0,
2834
+ stylePaddingBottom
2835
+ });
2836
+ setInitialScrollTarget(state, clearedFooterTarget);
2837
+ }
2838
+ function clearFinishedViewportRetargetableInitialScroll(state) {
2839
+ clearPreservedInitialScrollTarget(state);
2633
2840
  }
2634
2841
  function didFinishedInitialScrollMoveAwayFromTarget(ctx, target, epsilon = DEFAULT_BOOTSTRAP_REVEAL_EPSILON) {
2635
2842
  const state = ctx.state;
@@ -2644,6 +2851,25 @@ function getObservedBootstrapInitialScrollOffset(state) {
2644
2851
  const observedOffset = (_b = (_a3 = state.refScroller.current) == null ? void 0 : _a3.getCurrentScrollOffset) == null ? void 0 : _b.call(_a3);
2645
2852
  return typeof observedOffset === "number" && Number.isFinite(observedOffset) ? observedOffset : (_d = (_c = state.scrollPending) != null ? _c : state.scroll) != null ? _d : 0;
2646
2853
  }
2854
+ function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {
2855
+ var _a3, _b;
2856
+ const state = ctx.state;
2857
+ const initialScroll = state.initialScroll;
2858
+ if (!state.didFinishInitialScroll || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) || (initialScroll == null ? void 0 : initialScroll.viewPosition) !== 1) {
2859
+ return;
2860
+ }
2861
+ if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2862
+ if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {
2863
+ clearPendingInitialScrollFooterLayout(ctx, {
2864
+ dataLength: state.props.data.length,
2865
+ stylePaddingBottom: (_b = state.props.stylePaddingBottom) != null ? _b : 0,
2866
+ target: initialScroll
2867
+ });
2868
+ return;
2869
+ }
2870
+ clearFinishedViewportRetargetableInitialScroll(state);
2871
+ }
2872
+ }
2647
2873
  function startBootstrapInitialScrollOnMount(ctx, options) {
2648
2874
  var _a3, _b, _c;
2649
2875
  const { initialScrollAtEnd, target } = options;
@@ -2680,15 +2906,16 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
2680
2906
  }
2681
2907
  const shouldResetDidFinish = !!(state.didFinishInitialScroll && previousDataLength === 0 && dataLength > 0 && initialScroll.index !== void 0);
2682
2908
  const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2909
+ const shouldClearFinishedResizePreservation = didDataChange && dataLength > 0 && state.didFinishInitialScroll && !bootstrapInitialScroll && !shouldResetDidFinish;
2910
+ if (shouldClearFinishedResizePreservation) {
2911
+ clearPreservedInitialScrollTarget(state);
2912
+ return;
2913
+ }
2683
2914
  const shouldRetargetBottomAligned = dataLength > 0 && (initialScrollAtEnd || isRetargetableBottomAlignedInitialScrollTarget(initialScroll));
2684
2915
  if (!didDataChange && !shouldResetDidFinish && !shouldRetargetBottomAligned) {
2685
2916
  return;
2686
2917
  }
2687
2918
  if (shouldRetargetBottomAligned) {
2688
- if (!shouldResetDidFinish && didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2689
- clearPendingInitialScrollFooterLayout(state, initialScroll);
2690
- return;
2691
- }
2692
2919
  const updatedInitialScroll = initialScrollAtEnd ? createInitialScrollAtEndTarget({
2693
2920
  dataLength,
2694
2921
  footerSize: peek$(ctx, "footerSize") || 0,
@@ -2701,6 +2928,14 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
2701
2928
  stylePaddingBottom,
2702
2929
  target: initialScroll
2703
2930
  });
2931
+ if (!shouldResetDidFinish && didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2932
+ clearPendingInitialScrollFooterLayout(ctx, {
2933
+ dataLength,
2934
+ stylePaddingBottom,
2935
+ target: initialScroll
2936
+ });
2937
+ return;
2938
+ }
2704
2939
  if (!areEquivalentBootstrapInitialScrollTargets(initialScroll, updatedInitialScroll) || !!bootstrapInitialScroll || shouldResetDidFinish || didDataChange) {
2705
2940
  setInitialScrollTarget(state, updatedInitialScroll, {
2706
2941
  resetDidFinish: shouldResetDidFinish
@@ -2742,7 +2977,11 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2742
2977
  return;
2743
2978
  }
2744
2979
  if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2745
- clearPendingInitialScrollFooterLayout(state, initialScroll);
2980
+ clearPendingInitialScrollFooterLayout(ctx, {
2981
+ dataLength,
2982
+ stylePaddingBottom,
2983
+ target: initialScroll
2984
+ });
2746
2985
  } else {
2747
2986
  const updatedInitialScroll = createInitialScrollAtEndTarget({
2748
2987
  dataLength,
@@ -2752,10 +2991,15 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2752
2991
  });
2753
2992
  const didTargetChange = initialScroll.index !== updatedInitialScroll.index || initialScroll.viewPosition !== updatedInitialScroll.viewPosition || initialScroll.viewOffset !== updatedInitialScroll.viewOffset;
2754
2993
  if (!didTargetChange) {
2755
- clearPendingInitialScrollFooterLayout(state, initialScroll);
2994
+ clearPendingInitialScrollFooterLayout(ctx, {
2995
+ dataLength,
2996
+ stylePaddingBottom,
2997
+ target: initialScroll
2998
+ });
2756
2999
  } else {
3000
+ const didFinishInitialScroll = !!state.didFinishInitialScroll;
2757
3001
  setInitialScrollTarget(state, updatedInitialScroll, {
2758
- resetDidFinish: !!state.didFinishInitialScroll
3002
+ resetDidFinish: didFinishInitialScroll
2759
3003
  });
2760
3004
  rearmBootstrapInitialScroll(ctx, {
2761
3005
  scroll: resolveInitialScrollOffset(ctx, updatedInitialScroll),
@@ -2764,6 +3008,29 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2764
3008
  }
2765
3009
  }
2766
3010
  }
3011
+ function handleBootstrapInitialScrollLayoutChange(ctx) {
3012
+ const state = ctx.state;
3013
+ const initialScroll = state.initialScroll;
3014
+ if (isOffsetInitialScrollSession(state) || state.props.data.length === 0 || !initialScroll) {
3015
+ return;
3016
+ }
3017
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
3018
+ if (!bootstrapInitialScroll && initialScroll.viewPosition !== 1) {
3019
+ return;
3020
+ }
3021
+ const didFinishInitialScroll = state.didFinishInitialScroll;
3022
+ if (didFinishInitialScroll) {
3023
+ setInitialScrollTarget(state, initialScroll, {
3024
+ resetDidFinish: true
3025
+ });
3026
+ state.clearPreservedInitialScrollOnNextFinish = true;
3027
+ }
3028
+ rearmBootstrapInitialScroll(ctx, {
3029
+ scroll: resolveInitialScrollOffset(ctx, initialScroll),
3030
+ seedContentOffset: didFinishInitialScroll && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
3031
+ targetIndexSeed: initialScroll.index
3032
+ });
3033
+ }
2767
3034
  function evaluateBootstrapInitialScroll(ctx) {
2768
3035
  var _a3, _b;
2769
3036
  const state = ctx.state;
@@ -2832,12 +3099,15 @@ function evaluateBootstrapInitialScroll(ctx) {
2832
3099
  }
2833
3100
  }
2834
3101
  function finishBootstrapInitialScrollWithoutScroll(ctx, resolvedOffset) {
3102
+ var _a3;
2835
3103
  const state = ctx.state;
2836
3104
  clearBootstrapInitialScrollSession(state);
3105
+ const shouldPreserveResizeTarget = !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_a3 = state.initialScroll) == null ? void 0 : _a3.viewPosition) === 1;
2837
3106
  finishInitialScroll(ctx, {
2838
- preserveTarget: shouldPreserveInitialScrollForFooterLayout(state.initialScroll),
3107
+ preserveTarget: shouldPreserveResizeTarget,
2839
3108
  recalculateItems: true,
2840
- resolvedOffset
3109
+ resolvedOffset,
3110
+ schedulePreservedTargetClear: shouldPreserveResizeTarget
2841
3111
  });
2842
3112
  }
2843
3113
  function abortBootstrapInitialScroll(ctx) {
@@ -3215,7 +3485,7 @@ function resolvePendingNativeMVCPAdjust(ctx, newScroll) {
3215
3485
  settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
3216
3486
  return true;
3217
3487
  }
3218
- if (state.pendingMaintainScrollAtEnd && state.isAtEnd && progressTowardAmount > MVCP_POSITION_EPSILON) {
3488
+ if (state.pendingMaintainScrollAtEnd && peek$(ctx, "isWithinMaintainScrollAtEndThreshold") && progressTowardAmount > MVCP_POSITION_EPSILON) {
3219
3489
  settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
3220
3490
  return true;
3221
3491
  }
@@ -3372,6 +3642,86 @@ function prepareMVCP(ctx, dataChanged) {
3372
3642
  }
3373
3643
  }
3374
3644
 
3645
+ // src/core/syncMountedContainer.ts
3646
+ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3647
+ var _a3, _b, _c, _d, _e, _f, _g, _h;
3648
+ const state = ctx.state;
3649
+ const {
3650
+ columns,
3651
+ columnSpans,
3652
+ positions,
3653
+ props: { data, itemsAreEqual, keyExtractor }
3654
+ } = state;
3655
+ const item = data[itemIndex];
3656
+ if (item === void 0) {
3657
+ return { didChangePosition: false, didRefreshData: false };
3658
+ }
3659
+ const updateLayout = (_a3 = options == null ? void 0 : options.updateLayout) != null ? _a3 : true;
3660
+ let didChangePosition = false;
3661
+ let didRefreshData = false;
3662
+ if (updateLayout) {
3663
+ const positionValue = positions[itemIndex];
3664
+ if (positionValue === void 0) {
3665
+ set$(ctx, `containerPosition${containerIndex}`, POSITION_OUT_OF_VIEW);
3666
+ return { didChangePosition: false, didRefreshData: false };
3667
+ }
3668
+ const position = (positionValue || 0) - ((_b = options == null ? void 0 : options.scrollAdjustPending) != null ? _b : 0);
3669
+ const column = columns[itemIndex] || 1;
3670
+ const span = columnSpans[itemIndex] || 1;
3671
+ const prevPos = peek$(ctx, `containerPosition${containerIndex}`);
3672
+ const prevColumn = peek$(ctx, `containerColumn${containerIndex}`);
3673
+ const prevSpan = peek$(ctx, `containerSpan${containerIndex}`);
3674
+ if (position > POSITION_OUT_OF_VIEW && position !== prevPos) {
3675
+ set$(ctx, `containerPosition${containerIndex}`, position);
3676
+ didChangePosition = true;
3677
+ }
3678
+ if (column >= 0 && column !== prevColumn) {
3679
+ set$(ctx, `containerColumn${containerIndex}`, column);
3680
+ }
3681
+ if (span !== prevSpan) {
3682
+ set$(ctx, `containerSpan${containerIndex}`, span);
3683
+ }
3684
+ }
3685
+ const prevData = peek$(ctx, `containerItemData${containerIndex}`);
3686
+ if (prevData !== item) {
3687
+ const pendingDataComparison = ((_c = state.pendingDataComparison) == null ? void 0 : _c.previousData) === state.previousData && ((_d = state.pendingDataComparison) == null ? void 0 : _d.nextData) === data ? state.pendingDataComparison : void 0;
3688
+ const cachedComparison = (_e = pendingDataComparison == null ? void 0 : pendingDataComparison.byIndex[itemIndex]) != null ? _e : 0;
3689
+ if (cachedComparison === 2) {
3690
+ set$(ctx, `containerItemData${containerIndex}`, item);
3691
+ didRefreshData = true;
3692
+ } else if (cachedComparison !== 1) {
3693
+ const itemKey = (_g = (_f = peek$(ctx, `containerItemKey${containerIndex}`)) != null ? _f : state.idCache[itemIndex]) != null ? _g : getId(state, itemIndex);
3694
+ const prevKey = keyExtractor == null ? void 0 : keyExtractor(prevData, itemIndex);
3695
+ if (prevData === void 0 || !keyExtractor || prevKey !== itemKey) {
3696
+ set$(ctx, `containerItemData${containerIndex}`, item);
3697
+ didRefreshData = true;
3698
+ } else if (!itemsAreEqual) {
3699
+ set$(ctx, `containerItemData${containerIndex}`, item);
3700
+ didRefreshData = true;
3701
+ } else {
3702
+ const isEqual = itemsAreEqual(prevData, item, itemIndex, data);
3703
+ if (!state.pendingDataComparison || state.pendingDataComparison.previousData !== state.previousData || state.pendingDataComparison.nextData !== data) {
3704
+ if (state.previousData) {
3705
+ state.pendingDataComparison = {
3706
+ byIndex: [],
3707
+ nextData: data,
3708
+ previousData: state.previousData
3709
+ };
3710
+ }
3711
+ }
3712
+ if ((_h = state.pendingDataComparison) == null ? void 0 : _h.byIndex) {
3713
+ state.pendingDataComparison.byIndex[itemIndex] = isEqual ? 1 : 2;
3714
+ }
3715
+ if (!isEqual) {
3716
+ set$(ctx, `containerItemData${containerIndex}`, item);
3717
+ didRefreshData = true;
3718
+ }
3719
+ }
3720
+ }
3721
+ }
3722
+ return { didChangePosition, didRefreshData };
3723
+ }
3724
+
3375
3725
  // src/core/prepareColumnStartState.ts
3376
3726
  function prepareColumnStartState(ctx, startIndex, useAverageSize) {
3377
3727
  var _a3;
@@ -3534,9 +3884,10 @@ function updateSnapToOffsets(ctx) {
3534
3884
  }
3535
3885
 
3536
3886
  // src/core/updateItemPositions.ts
3537
- function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP } = {
3887
+ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP, optimizeForVisibleWindow = false } = {
3538
3888
  doMVCP: false,
3539
3889
  forceFullUpdate: false,
3890
+ optimizeForVisibleWindow: false,
3540
3891
  scrollBottomBuffered: -1,
3541
3892
  startIndex: 0
3542
3893
  }) {
@@ -3561,7 +3912,7 @@ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffere
3561
3912
  const layoutConfig = overrideItemLayout ? { span: 1 } : void 0;
3562
3913
  const lastScrollDelta = state.lastScrollDelta;
3563
3914
  const velocity = getScrollVelocity(state);
3564
- const shouldOptimize = !forceFullUpdate && !dataChanged && (Math.abs(velocity) > 0 || state.scrollLength > 0 && lastScrollDelta > state.scrollLength);
3915
+ const shouldOptimize = !forceFullUpdate && !dataChanged && (optimizeForVisibleWindow || Math.abs(velocity) > 0 || state.scrollLength > 0 && lastScrollDelta > state.scrollLength);
3565
3916
  const maxVisibleArea = scrollBottomBuffered + 1e3;
3566
3917
  const useAverageSize = !getEstimatedItemSize;
3567
3918
  const preferCachedSize = !doMVCP || dataChanged || state.scrollAdjustHandler.getAdjust() !== 0 || ((_b = peek$(ctx, "scrollAdjustPending")) != null ? _b : 0) !== 0;
@@ -3676,7 +4027,15 @@ function ensureViewabilityState(ctx, configId) {
3676
4027
  }
3677
4028
  let state = map.get(configId);
3678
4029
  if (!state) {
3679
- state = { end: -1, previousEnd: -1, previousStart: -1, start: -1, viewableItems: [] };
4030
+ state = {
4031
+ end: -1,
4032
+ endBuffered: -1,
4033
+ previousEnd: -1,
4034
+ previousStart: -1,
4035
+ start: -1,
4036
+ startBuffered: -1,
4037
+ viewableItems: []
4038
+ };
3680
4039
  map.set(configId, state);
3681
4040
  }
3682
4041
  return state;
@@ -3696,7 +4055,7 @@ function setupViewability(props) {
3696
4055
  }
3697
4056
  return viewabilityConfigCallbackPairs;
3698
4057
  }
3699
- function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollSize, start, end) {
4058
+ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollSize, start, end, startBuffered = start, endBuffered = end) {
3700
4059
  const {
3701
4060
  timeouts,
3702
4061
  props: { data }
@@ -3705,6 +4064,8 @@ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollS
3705
4064
  const viewabilityState = ensureViewabilityState(ctx, viewabilityConfigCallbackPair.viewabilityConfig.id);
3706
4065
  viewabilityState.start = start;
3707
4066
  viewabilityState.end = end;
4067
+ viewabilityState.startBuffered = startBuffered;
4068
+ viewabilityState.endBuffered = endBuffered;
3708
4069
  if (viewabilityConfigCallbackPair.viewabilityConfig.minimumViewTime) {
3709
4070
  const timer = setTimeout(() => {
3710
4071
  timeouts.delete(timer);
@@ -3720,7 +4081,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
3720
4081
  const { viewabilityConfig, onViewableItemsChanged } = viewabilityConfigCallbackPair;
3721
4082
  const configId = viewabilityConfig.id;
3722
4083
  const viewabilityState = ensureViewabilityState(ctx, configId);
3723
- const { viewableItems: previousViewableItems, start, end } = viewabilityState;
4084
+ const { viewableItems: previousViewableItems, start, end, startBuffered, endBuffered } = viewabilityState;
3724
4085
  const viewabilityTokens = /* @__PURE__ */ new Map();
3725
4086
  for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
3726
4087
  viewabilityTokens.set(
@@ -3789,7 +4150,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
3789
4150
  maybeUpdateViewabilityCallback(ctx, configId, change.containerId, change);
3790
4151
  }
3791
4152
  if (onViewableItemsChanged) {
3792
- onViewableItemsChanged({ changed, viewableItems });
4153
+ onViewableItemsChanged({ changed, end, endBuffered, start, startBuffered, viewableItems });
3793
4154
  }
3794
4155
  }
3795
4156
  for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
@@ -4101,7 +4462,6 @@ function calculateItemsInView(ctx, params = {}) {
4101
4462
  alwaysRenderIndicesSet,
4102
4463
  drawDistance,
4103
4464
  getItemType,
4104
- itemsAreEqual,
4105
4465
  keyExtractor,
4106
4466
  onStickyHeaderChange
4107
4467
  },
@@ -4128,11 +4488,11 @@ function calculateItemsInView(ctx, params = {}) {
4128
4488
  const numColumns = peek$(ctx, "numColumns");
4129
4489
  const speed = getScrollVelocity(state);
4130
4490
  const scrollExtra = 0;
4131
- const { queuedInitialLayout } = state;
4132
- const scrollState = suppressInitialScrollSideEffects ? (_b = bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.scroll) != null ? _b : state.scroll : !queuedInitialLayout && state.initialScroll ? (
4491
+ const { initialScroll, queuedInitialLayout } = state;
4492
+ const scrollState = suppressInitialScrollSideEffects ? (_b = bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.scroll) != null ? _b : state.scroll : !queuedInitialLayout && hasActiveInitialScroll(state) && initialScroll ? (
4133
4493
  // Before the initial layout settles, keep viewport math anchored to the
4134
4494
  // current initial-scroll target instead of transient native adjustments.
4135
- resolveInitialScrollOffset(ctx, state.initialScroll)
4495
+ resolveInitialScrollOffset(ctx, initialScroll)
4136
4496
  ) : state.scroll;
4137
4497
  const scrollAdjustPending = (_c = peek$(ctx, "scrollAdjustPending")) != null ? _c : 0;
4138
4498
  const scrollAdjustPad = scrollAdjustPending - topPad;
@@ -4177,9 +4537,11 @@ function calculateItemsInView(ctx, params = {}) {
4177
4537
  columnSpans.length = 0;
4178
4538
  }
4179
4539
  const startIndex = forceFullItemPositions || dataChanged ? 0 : (_d = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _d : 0;
4540
+ const optimizeForVisibleWindow = !forceFullItemPositions && !dataChanged && numColumns > 1 && minIndexSizeChanged !== void 0;
4180
4541
  updateItemPositions(ctx, dataChanged, {
4181
4542
  doMVCP,
4182
4543
  forceFullUpdate: !!forceFullItemPositions,
4544
+ optimizeForVisibleWindow,
4183
4545
  scrollBottomBuffered,
4184
4546
  startIndex
4185
4547
  });
@@ -4427,33 +4789,11 @@ function calculateItemsInView(ctx, params = {}) {
4427
4789
  set$(ctx, `containerSpan${i}`, 1);
4428
4790
  } else {
4429
4791
  const itemIndex = indexByKey.get(itemKey);
4430
- const item = data[itemIndex];
4431
- if (item !== void 0) {
4432
- const positionValue = positions[itemIndex];
4433
- if (positionValue === void 0) {
4434
- set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
4435
- } else {
4436
- const position = (positionValue || 0) - scrollAdjustPending;
4437
- const column = columns[itemIndex] || 1;
4438
- const span = columnSpans[itemIndex] || 1;
4439
- const prevPos = peek$(ctx, `containerPosition${i}`);
4440
- const prevColumn = peek$(ctx, `containerColumn${i}`);
4441
- const prevSpan = peek$(ctx, `containerSpan${i}`);
4442
- const prevData = peek$(ctx, `containerItemData${i}`);
4443
- if (position > POSITION_OUT_OF_VIEW && position !== prevPos) {
4444
- set$(ctx, `containerPosition${i}`, position);
4445
- didChangePositions = true;
4446
- }
4447
- if (column >= 0 && column !== prevColumn) {
4448
- set$(ctx, `containerColumn${i}`, column);
4449
- }
4450
- if (span !== prevSpan) {
4451
- set$(ctx, `containerSpan${i}`, span);
4452
- }
4453
- if (prevData !== item && (itemsAreEqual ? !itemsAreEqual(prevData, item, itemIndex, data) : true)) {
4454
- set$(ctx, `containerItemData${i}`, item);
4455
- }
4456
- }
4792
+ if (itemIndex !== void 0) {
4793
+ didChangePositions = syncMountedContainer(ctx, i, itemIndex, {
4794
+ scrollAdjustPending,
4795
+ updateLayout: true
4796
+ }).didChangePosition || didChangePositions;
4457
4797
  }
4458
4798
  }
4459
4799
  }
@@ -4464,7 +4804,10 @@ function calculateItemsInView(ctx, params = {}) {
4464
4804
  evaluateBootstrapInitialScroll(ctx);
4465
4805
  return;
4466
4806
  }
4467
- if (!queuedInitialLayout && endBuffered !== null && checkAllSizesKnown(state)) {
4807
+ const mountedBufferedIndices = getMountedBufferedIndices(state);
4808
+ const mountedNoBufferIndices = getMountedNoBufferIndices(state);
4809
+ const readinessIndices = hasActiveInitialScroll(state) ? mountedBufferedIndices : mountedNoBufferIndices.length > 0 ? mountedNoBufferIndices : mountedBufferedIndices;
4810
+ if (!queuedInitialLayout && readinessIndices.length > 0 && checkAllSizesKnown(state, readinessIndices)) {
4468
4811
  setDidLayout(ctx);
4469
4812
  handleInitialScrollLayoutReady(ctx);
4470
4813
  }
@@ -4475,7 +4818,9 @@ function calculateItemsInView(ctx, params = {}) {
4475
4818
  viewabilityConfigCallbackPairs,
4476
4819
  scrollLength,
4477
4820
  startNoBuffer,
4478
- endNoBuffer
4821
+ endNoBuffer,
4822
+ startBuffered != null ? startBuffered : startNoBuffer,
4823
+ endBuffered != null ? endBuffered : endNoBuffer
4479
4824
  );
4480
4825
  }
4481
4826
  if (onStickyHeaderChange && stickyIndicesArr.length > 0 && nextActiveStickyIndex !== void 0 && nextActiveStickyIndex !== previousStickyIndex) {
@@ -4487,37 +4832,17 @@ function calculateItemsInView(ctx, params = {}) {
4487
4832
  });
4488
4833
  }
4489
4834
 
4490
- // src/core/checkActualChange.ts
4491
- function checkActualChange(state, dataProp, previousData) {
4492
- if (!previousData || !dataProp || dataProp.length !== previousData.length) {
4493
- return true;
4494
- }
4495
- const {
4496
- idCache,
4497
- props: { keyExtractor }
4498
- } = state;
4499
- for (let i = 0; i < dataProp.length; i++) {
4500
- if (dataProp[i] !== previousData[i]) {
4501
- return true;
4502
- }
4503
- if (keyExtractor ? idCache[i] !== keyExtractor(previousData[i], i) : dataProp[i] !== previousData[i]) {
4504
- return true;
4505
- }
4506
- }
4507
- return false;
4508
- }
4509
-
4510
4835
  // src/core/doMaintainScrollAtEnd.ts
4511
4836
  function doMaintainScrollAtEnd(ctx) {
4512
4837
  const state = ctx.state;
4513
4838
  const {
4514
4839
  didContainersLayout,
4515
- isAtEnd,
4516
4840
  pendingNativeMVCPAdjust,
4517
4841
  refScroller,
4518
4842
  props: { maintainScrollAtEnd }
4519
4843
  } = state;
4520
- const shouldMaintainScrollAtEnd = !!(isAtEnd && maintainScrollAtEnd && didContainersLayout);
4844
+ const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
4845
+ const shouldMaintainScrollAtEnd = !!(isWithinMaintainScrollAtEndThreshold && maintainScrollAtEnd && didContainersLayout);
4521
4846
  if (pendingNativeMVCPAdjust) {
4522
4847
  state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
4523
4848
  return false;
@@ -4530,7 +4855,7 @@ function doMaintainScrollAtEnd(ctx) {
4530
4855
  }
4531
4856
  requestAnimationFrame(() => {
4532
4857
  var _a3;
4533
- if (state.isAtEnd) {
4858
+ if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
4534
4859
  state.maintainingScrollAtEnd = true;
4535
4860
  (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
4536
4861
  animated: maintainScrollAtEnd.animated
@@ -4548,68 +4873,22 @@ function doMaintainScrollAtEnd(ctx) {
4548
4873
  return false;
4549
4874
  }
4550
4875
 
4551
- // src/utils/updateAveragesOnDataChange.ts
4552
- function updateAveragesOnDataChange(state, oldData, newData) {
4553
- var _a3;
4554
- const {
4555
- averageSizes,
4556
- sizesKnown,
4557
- indexByKey,
4558
- props: { itemsAreEqual, getItemType, keyExtractor }
4559
- } = state;
4560
- if (!itemsAreEqual || !oldData.length || !newData.length) {
4561
- for (const key in averageSizes) {
4562
- delete averageSizes[key];
4563
- }
4564
- return;
4565
- }
4566
- const itemTypesToPreserve = {};
4567
- const newDataLength = newData.length;
4568
- const oldDataLength = oldData.length;
4569
- for (let newIndex = 0; newIndex < newDataLength; newIndex++) {
4570
- const newItem = newData[newIndex];
4571
- const id = keyExtractor ? keyExtractor(newItem, newIndex) : String(newIndex);
4572
- const oldIndex = indexByKey.get(id);
4573
- if (oldIndex !== void 0 && oldIndex < oldDataLength) {
4574
- const knownSize = sizesKnown.get(id);
4575
- if (knownSize === void 0) continue;
4576
- const oldItem = oldData[oldIndex];
4577
- const areEqual = itemsAreEqual(oldItem, newItem, newIndex, newData);
4578
- if (areEqual) {
4579
- const itemType = getItemType ? (_a3 = getItemType(newItem, newIndex)) != null ? _a3 : "" : "";
4580
- let typeData = itemTypesToPreserve[itemType];
4581
- if (!typeData) {
4582
- typeData = itemTypesToPreserve[itemType] = { count: 0, totalSize: 0 };
4583
- }
4584
- typeData.totalSize += knownSize;
4585
- typeData.count++;
4586
- }
4587
- }
4588
- }
4589
- for (const key in averageSizes) {
4590
- delete averageSizes[key];
4591
- }
4592
- for (const itemType in itemTypesToPreserve) {
4593
- const { totalSize, count } = itemTypesToPreserve[itemType];
4594
- if (count > 0) {
4595
- averageSizes[itemType] = {
4596
- avg: totalSize / count,
4597
- num: count
4598
- };
4599
- }
4600
- }
4601
- }
4602
-
4603
4876
  // src/core/checkResetContainers.ts
4604
- function checkResetContainers(ctx, dataProp) {
4877
+ function checkResetContainers(ctx, dataProp, { didColumnsChange = false } = {}) {
4605
4878
  const state = ctx.state;
4606
4879
  const { previousData } = state;
4607
- if (previousData) {
4608
- updateAveragesOnDataChange(state, previousData, dataProp);
4609
- }
4610
4880
  const { maintainScrollAtEnd } = state.props;
4881
+ if (didColumnsChange) {
4882
+ state.sizes.clear();
4883
+ state.sizesKnown.clear();
4884
+ for (const key in state.averageSizes) {
4885
+ delete state.averageSizes[key];
4886
+ }
4887
+ state.minIndexSizeChanged = 0;
4888
+ state.scrollForNextCalculateItemsInView = void 0;
4889
+ }
4611
4890
  calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
4612
- const shouldMaintainScrollAtEnd = maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onDataChange;
4891
+ const shouldMaintainScrollAtEnd = !didColumnsChange && (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onDataChange);
4613
4892
  const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx);
4614
4893
  if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
4615
4894
  state.isEndReached = false;
@@ -4620,6 +4899,53 @@ function checkResetContainers(ctx, dataProp) {
4620
4899
  delete state.previousData;
4621
4900
  }
4622
4901
 
4902
+ // src/core/checkStructuralDataChange.ts
4903
+ function checkStructuralDataChange(state, dataProp, previousData) {
4904
+ var _a3;
4905
+ state.pendingDataComparison = void 0;
4906
+ if (!previousData || !dataProp || dataProp.length !== previousData.length) {
4907
+ return true;
4908
+ }
4909
+ const {
4910
+ idCache,
4911
+ props: { itemsAreEqual, keyExtractor }
4912
+ } = state;
4913
+ let byIndex;
4914
+ for (let i = 0; i < dataProp.length; i++) {
4915
+ if (dataProp[i] === previousData[i]) {
4916
+ continue;
4917
+ }
4918
+ if (!keyExtractor) {
4919
+ if (byIndex) {
4920
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4921
+ }
4922
+ return true;
4923
+ }
4924
+ const previousKey = (_a3 = idCache[i]) != null ? _a3 : keyExtractor(previousData[i], i);
4925
+ const nextKey = keyExtractor(dataProp[i], i);
4926
+ if (previousKey !== nextKey) {
4927
+ if (byIndex) {
4928
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4929
+ }
4930
+ return true;
4931
+ }
4932
+ if (!itemsAreEqual) {
4933
+ if (byIndex) {
4934
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4935
+ }
4936
+ return true;
4937
+ }
4938
+ const isEqual = itemsAreEqual(previousData[i], dataProp[i], i, dataProp);
4939
+ byIndex != null ? byIndex : byIndex = [];
4940
+ byIndex[i] = isEqual ? 1 : 2;
4941
+ if (!isEqual) {
4942
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4943
+ return true;
4944
+ }
4945
+ }
4946
+ return false;
4947
+ }
4948
+
4623
4949
  // src/core/doInitialAllocateContainers.ts
4624
4950
  function doInitialAllocateContainers(ctx) {
4625
4951
  var _a3, _b, _c;
@@ -4863,6 +5189,7 @@ function onScroll(ctx, event) {
4863
5189
  state.scrollPending = newScroll;
4864
5190
  updateScroll(ctx, newScroll, insetChanged);
4865
5191
  trackInitialScrollNativeProgress(state, newScroll);
5192
+ clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx);
4866
5193
  if (state.scrollingTo) {
4867
5194
  checkFinishedScroll(ctx);
4868
5195
  }
@@ -4926,6 +5253,43 @@ var ScrollAdjustHandler = class {
4926
5253
  }
4927
5254
  };
4928
5255
 
5256
+ // src/core/updateAnchoredEndSpace.ts
5257
+ function maybeUpdateAnchoredEndSpace(ctx) {
5258
+ var _a3;
5259
+ const state = ctx.state;
5260
+ const anchoredEndSpace = state.props.anchoredEndSpace;
5261
+ const previousSize = peek$(ctx, "anchoredEndSpaceSize");
5262
+ let nextSize = 0;
5263
+ if (anchoredEndSpace) {
5264
+ const { anchorIndex, anchorMaxSize, anchorOffset = 0 } = anchoredEndSpace;
5265
+ const { data } = state.props;
5266
+ if (anchorIndex >= 0 && anchorIndex < data.length && state.scrollLength > 0) {
5267
+ let contentBelowAnchor = 0;
5268
+ const footerSize = ctx.values.get("footerSize") || 0;
5269
+ const stylePaddingBottom = state.props.stylePaddingBottom || 0;
5270
+ for (let index = anchorIndex; index < data.length; index++) {
5271
+ const itemKey = getId(state, index);
5272
+ const size = itemKey ? state.sizesKnown.get(itemKey) : void 0;
5273
+ const effectiveSize = index === anchorIndex && anchorMaxSize !== void 0 ? Math.min(size || 0, Math.max(0, anchorMaxSize)) : size;
5274
+ if (effectiveSize !== null && effectiveSize !== void 0 && effectiveSize > 0) {
5275
+ contentBelowAnchor += effectiveSize;
5276
+ }
5277
+ }
5278
+ contentBelowAnchor += footerSize + stylePaddingBottom;
5279
+ nextSize = Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
5280
+ }
5281
+ }
5282
+ if (previousSize === nextSize) {
5283
+ return nextSize;
5284
+ }
5285
+ set$(ctx, "anchoredEndSpaceSize", nextSize);
5286
+ (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
5287
+ if (anchoredEndSpace == null ? void 0 : anchoredEndSpace.includeInEndInset) {
5288
+ updateScroll(ctx, state.scroll, true);
5289
+ }
5290
+ return nextSize;
5291
+ }
5292
+
4929
5293
  // src/core/updateItemSize.ts
4930
5294
  function runOrScheduleMVCPRecalculate(ctx) {
4931
5295
  const state = ctx.state;
@@ -5007,6 +5371,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5007
5371
  previous: size - diff,
5008
5372
  size
5009
5373
  });
5374
+ maybeUpdateAnchoredEndSpace(ctx);
5010
5375
  }
5011
5376
  if (minIndexSizeChanged !== void 0) {
5012
5377
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
@@ -5130,7 +5495,7 @@ function createImperativeHandle(ctx) {
5130
5495
  const IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS = 800;
5131
5496
  const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
5132
5497
  let imperativeScrollToken = 0;
5133
- const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0 || hasActiveMVCPAnchorLock(state);
5498
+ const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0;
5134
5499
  const runWhenSettled = (token, run) => {
5135
5500
  const startedAt = Date.now();
5136
5501
  let stableFrames = 0;
@@ -5152,9 +5517,10 @@ function createImperativeHandle(ctx) {
5152
5517
  };
5153
5518
  requestAnimationFrame(check);
5154
5519
  };
5155
- const runScrollWithPromise = (run) => new Promise((resolve) => {
5520
+ const runScrollWithPromise = (run, options) => new Promise((resolve) => {
5156
5521
  var _a3;
5157
5522
  const token = ++imperativeScrollToken;
5523
+ const shouldWaitOneFrame = !!(options == null ? void 0 : options.shouldWaitOneFrame);
5158
5524
  (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
5159
5525
  state.pendingScrollResolve = resolve;
5160
5526
  const runNow = () => {
@@ -5169,11 +5535,12 @@ function createImperativeHandle(ctx) {
5169
5535
  resolve();
5170
5536
  }
5171
5537
  };
5538
+ const execute = shouldWaitOneFrame ? () => requestAnimationFrame(runNow) : runNow;
5172
5539
  if (isSettlingAfterDataChange()) {
5173
- runWhenSettled(token, runNow);
5174
- return;
5540
+ runWhenSettled(token, execute);
5541
+ } else {
5542
+ execute();
5175
5543
  }
5176
- runNow();
5177
5544
  });
5178
5545
  const scrollIndexIntoView = (options) => {
5179
5546
  if (state) {
@@ -5230,10 +5597,13 @@ function createImperativeHandle(ctx) {
5230
5597
  },
5231
5598
  end: state.endNoBuffer,
5232
5599
  endBuffered: state.endBuffered,
5233
- isAtEnd: state.isAtEnd,
5234
- isAtStart: state.isAtStart,
5600
+ isAtEnd: peek$(ctx, "isAtEnd"),
5601
+ isAtStart: peek$(ctx, "isAtStart"),
5235
5602
  isEndReached: state.isEndReached,
5603
+ isNearEnd: peek$(ctx, "isNearEnd"),
5604
+ isNearStart: peek$(ctx, "isNearStart"),
5236
5605
  isStartReached: state.isStartReached,
5606
+ isWithinMaintainScrollAtEndThreshold: peek$(ctx, "isWithinMaintainScrollAtEndThreshold"),
5237
5607
  listen: (signalName, cb) => listen$(ctx, signalName, cb),
5238
5608
  listenToPosition: (key, cb) => listenPosition$(ctx, key, cb),
5239
5609
  positionAtIndex: (index) => state.positions[index],
@@ -5280,10 +5650,15 @@ function createImperativeHandle(ctx) {
5280
5650
  }
5281
5651
  return false;
5282
5652
  }),
5283
- scrollToIndex: (params) => runScrollWithPromise(() => {
5284
- scrollToIndex(ctx, params);
5285
- return true;
5286
- }),
5653
+ scrollToIndex: (params) => runScrollWithPromise(
5654
+ () => {
5655
+ scrollToIndex(ctx, params);
5656
+ return true;
5657
+ },
5658
+ {
5659
+ shouldWaitOneFrame: params.index >= 0 && params.index >= state.props.data.length
5660
+ }
5661
+ ),
5287
5662
  scrollToItem: ({ item, ...props }) => runScrollWithPromise(() => {
5288
5663
  const data = state.props.data;
5289
5664
  const index = data.indexOf(item);
@@ -5379,7 +5754,7 @@ function getRenderedItem(ctx, key) {
5379
5754
  item,
5380
5755
  type: getItemType ? (_a3 = getItemType(item, index)) != null ? _a3 : "" : ""
5381
5756
  };
5382
- renderedItem = isFunction(renderItem) ? renderItem(itemProps) : React3__namespace.default.createElement(renderItem, itemProps);
5757
+ renderedItem = React3__namespace.default.createElement(renderItem, itemProps);
5383
5758
  }
5384
5759
  return { index, item: data[index], renderedItem };
5385
5760
  }
@@ -5513,9 +5888,18 @@ var LegendList = typedMemo(
5513
5888
  })
5514
5889
  );
5515
5890
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
5516
- var _a3, _b, _c, _d, _e, _f, _g;
5891
+ var _a3, _b, _c, _d, _e, _f, _g, _h;
5892
+ const noopOnScroll = React3.useCallback((_event) => {
5893
+ }, []);
5894
+ if (props.recycleItems === void 0) {
5895
+ warnDevOnce(
5896
+ "recycleItems-omitted",
5897
+ "recycleItems was not provided, so it defaults to false. Set recycleItems explicitly to true for better performance with recycling-aware rows, or false to preserve remount-on-reuse behavior."
5898
+ );
5899
+ }
5517
5900
  const {
5518
5901
  alignItemsAtEnd = false,
5902
+ anchoredEndSpace,
5519
5903
  alwaysRender,
5520
5904
  columnWrapperStyle,
5521
5905
  contentContainerStyle: contentContainerStyleProp,
@@ -5581,7 +5965,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5581
5965
  const positionComponentInternal = props.positionComponentInternal;
5582
5966
  const stickyPositionComponentInternal = props.stickyPositionComponentInternal;
5583
5967
  const {
5584
- childrenMode,
5585
5968
  positionComponentInternal: _positionComponentInternal,
5586
5969
  stickyPositionComponentInternal: _stickyPositionComponentInternal,
5587
5970
  ...restProps
@@ -5645,18 +6028,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5645
6028
  dataVersion,
5646
6029
  keyExtractor
5647
6030
  ]);
5648
- if (IS_DEV && stickyIndicesDeprecated && !stickyHeaderIndicesProp) {
5649
- warnDevOnce(
5650
- "stickyIndices",
5651
- "stickyIndices has been renamed to stickyHeaderIndices. Please update your props to use stickyHeaderIndices."
5652
- );
5653
- }
5654
- if (IS_DEV && useWindowScroll && renderScrollComponent) {
5655
- warnDevOnce(
5656
- "useWindowScrollRenderScrollComponent",
5657
- "useWindowScroll is not supported when renderScrollComponent is provided."
5658
- );
5659
- }
5660
6031
  const useWindowScrollResolved = !!useWindowScroll && !renderScrollComponent;
5661
6032
  const refState = React3.useRef(void 0);
5662
6033
  const hasOverrideItemLayout = !!overrideItemLayout;
@@ -5665,7 +6036,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5665
6036
  if (!ctx.state) {
5666
6037
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : { height: 0, width: 0 } )[horizontal ? "width" : "height"];
5667
6038
  ctx.state = {
5668
- activeStickyIndex: -1,
5669
6039
  averageSizes: {},
5670
6040
  columnSpans: [],
5671
6041
  columns: [],
@@ -5689,8 +6059,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5689
6059
  kind: initialScrollUsesOffsetOnly ? "offset" : "bootstrap",
5690
6060
  previousDataLength: dataProp.length
5691
6061
  } : void 0,
5692
- isAtEnd: false,
5693
- isAtStart: false,
5694
6062
  isEndReached: null,
5695
6063
  isFirst: true,
5696
6064
  isStartReached: null,
@@ -5701,6 +6069,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5701
6069
  minIndexSizeChanged: 0,
5702
6070
  nativeContentInset: void 0,
5703
6071
  nativeMarginTop: 0,
6072
+ pendingDataComparison: void 0,
5704
6073
  pendingNativeMVCPAdjust: void 0,
5705
6074
  positions: [],
5706
6075
  props: {},
@@ -5739,22 +6108,29 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5739
6108
  }
5740
6109
  const state = refState.current;
5741
6110
  const isFirstLocal = state.isFirst;
5742
- state.didColumnsChange = numColumnsProp !== state.props.numColumns;
6111
+ const previousNumColumnsProp = state.props.numColumns;
6112
+ state.didColumnsChange = numColumnsProp !== previousNumColumnsProp;
5743
6113
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
5744
6114
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
5745
- const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkActualChange(state, dataProp, state.props.data);
6115
+ const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
6116
+ if (didDataChangeLocal && state.didFinishInitialScroll && ((_f = state.initialScroll) == null ? void 0 : _f.viewPosition) === 1 && state.props.data.length > 0) {
6117
+ clearPreservedInitialScrollTarget(state);
6118
+ }
5746
6119
  if (didDataChangeLocal) {
5747
6120
  state.dataChangeEpoch += 1;
5748
6121
  state.dataChangeNeedsScrollUpdate = true;
5749
6122
  state.didDataChange = true;
5750
6123
  state.previousData = state.props.data;
5751
6124
  }
5752
- const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
6125
+ const throttledOnScroll = useThrottledOnScroll(onScrollProp != null ? onScrollProp : noopOnScroll, scrollEventThrottle != null ? scrollEventThrottle : 0);
6126
+ const throttleScrollFn = scrollEventThrottle && onScrollProp ? throttledOnScroll : onScrollProp;
6127
+ const anchoredEndSpaceResolved = anchoredEndSpace ? { ...anchoredEndSpace, includeInEndInset: true } : anchoredEndSpace;
5753
6128
  state.props = {
5754
6129
  alignItemsAtEnd,
5755
6130
  alwaysRender,
5756
6131
  alwaysRenderIndicesArr: alwaysRenderIndices.arr,
5757
6132
  alwaysRenderIndicesSet: alwaysRenderIndices.set,
6133
+ anchoredEndSpace: anchoredEndSpaceResolved,
5758
6134
  animatedProps: animatedPropsInternal,
5759
6135
  contentInset,
5760
6136
  data: dataProp,
@@ -5839,16 +6215,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5839
6215
  if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
5840
6216
  refState.current.lastBatchingAction = Date.now();
5841
6217
  if (!keyExtractorProp && !isFirstLocal && didDataChangeLocal) {
5842
- IS_DEV && !childrenMode && warnDevOnce(
5843
- "keyExtractor",
5844
- "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."
5845
- );
5846
6218
  refState.current.sizes.clear();
5847
6219
  refState.current.positions.length = 0;
5848
6220
  refState.current.totalSize = 0;
5849
6221
  set$(ctx, "totalSize", 0);
5850
6222
  }
5851
6223
  }
6224
+ if (IS_DEV) {
6225
+ useDevChecks(props);
6226
+ }
5852
6227
  React3.useLayoutEffect(() => {
5853
6228
  handleInitialScrollDataChange(ctx, {
5854
6229
  dataLength: dataProp.length,
@@ -5858,6 +6233,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5858
6233
  useBootstrapInitialScroll: usesBootstrapInitialScroll
5859
6234
  });
5860
6235
  }, [dataProp.length, didDataChangeLocal, initialScrollAtEnd, stylePaddingBottomState, usesBootstrapInitialScroll]);
6236
+ React3.useLayoutEffect(() => {
6237
+ maybeUpdateAnchoredEndSpace(ctx);
6238
+ }, [
6239
+ ctx,
6240
+ dataProp,
6241
+ dataVersion,
6242
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex,
6243
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorMaxSize,
6244
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorOffset,
6245
+ numColumnsProp
6246
+ ]);
5861
6247
  const onLayoutFooter = React3.useCallback(
5862
6248
  (layout) => {
5863
6249
  if (!usesBootstrapInitialScroll) {
@@ -5873,14 +6259,21 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5873
6259
  [dataProp.length, initialScrollAtEnd, horizontal, stylePaddingBottomState, usesBootstrapInitialScroll]
5874
6260
  );
5875
6261
  const onLayoutChange = React3.useCallback(
5876
- (layout) => {
6262
+ (layout, fromLayoutEffect) => {
6263
+ const previousScrollLength = state.scrollLength;
6264
+ const previousOtherAxisSize = state.otherAxisSize;
5877
6265
  handleLayout(ctx, layout, setCanRender);
6266
+ maybeUpdateAnchoredEndSpace(ctx);
6267
+ const didLayoutAffectBootstrapTarget = previousScrollLength !== state.scrollLength || previousOtherAxisSize !== state.otherAxisSize;
6268
+ if (usesBootstrapInitialScroll && !fromLayoutEffect && didLayoutAffectBootstrapTarget) {
6269
+ handleBootstrapInitialScrollLayoutChange(ctx);
6270
+ }
5878
6271
  if (usesBootstrapInitialScroll) {
5879
6272
  return;
5880
6273
  }
5881
6274
  advanceCurrentInitialScrollSession(ctx);
5882
6275
  },
5883
- [usesBootstrapInitialScroll]
6276
+ [dataProp.length, initialScrollAtEnd, stylePaddingBottomState, usesBootstrapInitialScroll]
5884
6277
  );
5885
6278
  const { onLayout } = useOnLayoutSync({
5886
6279
  onLayoutChange,
@@ -5893,6 +6286,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5893
6286
  updateSnapToOffsets(ctx);
5894
6287
  }
5895
6288
  }, [snapToIndices]);
6289
+ React3.useLayoutEffect(
6290
+ () => initializeStateVars(true),
6291
+ [dataVersion, memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingBottomState, stylePaddingTopState]
6292
+ );
5896
6293
  React3.useLayoutEffect(() => {
5897
6294
  const {
5898
6295
  didColumnsChange,
@@ -5902,7 +6299,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5902
6299
  } = state;
5903
6300
  const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx);
5904
6301
  if (!didAllocateContainers && !isFirst && (didDataChange || didColumnsChange)) {
5905
- checkResetContainers(ctx, data);
6302
+ checkResetContainers(ctx, data, { didColumnsChange });
6303
+ }
6304
+ if (didDataChange) {
6305
+ state.pendingDataComparison = void 0;
5906
6306
  }
5907
6307
  state.didColumnsChange = false;
5908
6308
  state.didDataChange = false;
@@ -5917,10 +6317,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5917
6317
  (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { forceFullItemPositions: true });
5918
6318
  }
5919
6319
  }, [extraData, hasOverrideItemLayout, numColumnsProp]);
5920
- React3.useLayoutEffect(
5921
- () => initializeStateVars(true),
5922
- [dataVersion, memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingBottomState, stylePaddingTopState]
5923
- );
5924
6320
  React3.useEffect(() => {
5925
6321
  if (!onMetricsChange) {
5926
6322
  return;
@@ -5953,15 +6349,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5953
6349
  state.viewabilityConfigCallbackPairs = viewability;
5954
6350
  state.enableScrollForNextCalculateItemsInView = !viewability;
5955
6351
  }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
6352
+ useInit(() => {
6353
+ });
5956
6354
  React3.useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx), []);
5957
- {
5958
- React3.useEffect(() => {
5959
- if (usesBootstrapInitialScroll) {
5960
- return;
5961
- }
5962
- advanceCurrentInitialScrollSession(ctx);
5963
- }, [usesBootstrapInitialScroll]);
5964
- }
6355
+ React3.useEffect(() => {
6356
+ if (usesBootstrapInitialScroll) {
6357
+ return;
6358
+ }
6359
+ advanceCurrentInitialScrollSession(ctx);
6360
+ }, [ctx, usesBootstrapInitialScroll]);
5965
6361
  const fns = React3.useMemo(
5966
6362
  () => ({
5967
6363
  getRenderedItem: (key) => getRenderedItem(ctx, key),
@@ -5999,7 +6395,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5999
6395
  onScroll: onScrollHandler,
6000
6396
  recycleItems,
6001
6397
  refreshControl: refreshControlElement ? stylePaddingTopState > 0 ? React3__namespace.cloneElement(refreshControlElement, {
6002
- progressViewOffset: ((_f = refreshControlElement.props.progressViewOffset) != null ? _f : 0) + stylePaddingTopState
6398
+ progressViewOffset: ((_g = refreshControlElement.props.progressViewOffset) != null ? _g : 0) + stylePaddingTopState
6003
6399
  }) : refreshControlElement : onRefresh && /* @__PURE__ */ React3__namespace.createElement(
6004
6400
  RefreshControl,
6005
6401
  {
@@ -6010,7 +6406,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6010
6406
  ),
6011
6407
  refScrollView: combinedRef,
6012
6408
  renderScrollComponent,
6013
- scrollAdjustHandler: (_g = refState.current) == null ? void 0 : _g.scrollAdjustHandler,
6409
+ scrollAdjustHandler: (_h = refState.current) == null ? void 0 : _h.scrollAdjustHandler,
6014
6410
  scrollEventThrottle: 0,
6015
6411
  snapToIndices,
6016
6412
  stickyHeaderIndices,
@@ -6021,13 +6417,40 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6021
6417
  ), IS_DEV && ENABLE_DEBUG_VIEW);
6022
6418
  });
6023
6419
 
6420
+ // src/components/stickyPositionUtils.ts
6421
+ function getStickyPushLimit(state, index, itemKey) {
6422
+ if (!itemKey) {
6423
+ return void 0;
6424
+ }
6425
+ const currentSize = state.sizes.get(itemKey);
6426
+ if (!(currentSize && currentSize > 0)) {
6427
+ return void 0;
6428
+ }
6429
+ const stickyIndexInArray = state.props.stickyIndicesArr.indexOf(index);
6430
+ if (stickyIndexInArray === -1) {
6431
+ return void 0;
6432
+ }
6433
+ const nextStickyIndex = state.props.stickyIndicesArr[stickyIndexInArray + 1];
6434
+ if (nextStickyIndex === void 0) {
6435
+ return void 0;
6436
+ }
6437
+ const nextStickyPosition = state.positions[nextStickyIndex];
6438
+ if (nextStickyPosition === void 0) {
6439
+ return void 0;
6440
+ }
6441
+ return nextStickyPosition - currentSize;
6442
+ }
6443
+
6024
6444
  // src/entrypoints/shared.ts
6025
6445
  var LegendListRuntime = LegendList;
6026
6446
  var internal = {
6027
6447
  getComponent,
6448
+ getStickyPushLimit,
6028
6449
  IsNewArchitecture,
6029
6450
  POSITION_OUT_OF_VIEW,
6030
6451
  peek$,
6452
+ typedForwardRef,
6453
+ typedMemo,
6031
6454
  useArr$,
6032
6455
  useCombinedRef,
6033
6456
  useStateContext
@@ -6039,8 +6462,6 @@ var internal2 = internal;
6039
6462
 
6040
6463
  exports.LegendList = LegendList3;
6041
6464
  exports.internal = internal2;
6042
- exports.typedForwardRef = typedForwardRef;
6043
- exports.typedMemo = typedMemo;
6044
6465
  exports.useIsLastItem = useIsLastItem;
6045
6466
  exports.useListScrollSize = useListScrollSize;
6046
6467
  exports.useRecyclingEffect = useRecyclingEffect;