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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/react.js CHANGED
@@ -34,37 +34,6 @@ var View = React3.forwardRef(function View2(props, ref) {
34
34
  });
35
35
  var Text = View;
36
36
 
37
- // src/state/getContentInsetEnd.ts
38
- function getContentInsetEnd(state) {
39
- var _a3;
40
- const { props } = state;
41
- const horizontal = props.horizontal;
42
- const contentInset = props.contentInset;
43
- const baseInset = contentInset != null ? contentInset : state.nativeContentInset;
44
- const overrideInset = (_a3 = state.contentInsetOverride) != null ? _a3 : void 0;
45
- if (overrideInset) {
46
- const mergedInset = { bottom: 0, right: 0, ...baseInset, ...overrideInset };
47
- return (horizontal ? mergedInset.right : mergedInset.bottom) || 0;
48
- }
49
- if (baseInset) {
50
- return (horizontal ? baseInset.right : baseInset.bottom) || 0;
51
- }
52
- return 0;
53
- }
54
-
55
- // src/state/getContentSize.ts
56
- function getContentSize(ctx) {
57
- var _a3;
58
- const { values, state } = ctx;
59
- const stylePaddingTop = values.get("stylePaddingTop") || 0;
60
- const stylePaddingBottom = state.props.stylePaddingBottom || 0;
61
- const headerSize = values.get("headerSize") || 0;
62
- const footerSize = values.get("footerSize") || 0;
63
- const contentInsetBottom = getContentInsetEnd(state);
64
- const totalSize = (_a3 = state.pendingTotalSize) != null ? _a3 : values.get("totalSize");
65
- return headerSize + footerSize + totalSize + stylePaddingTop + stylePaddingBottom + (contentInsetBottom || 0);
66
- }
67
-
68
37
  // src/platform/Animated.tsx
69
38
  var createAnimatedValue = (value) => value;
70
39
 
@@ -89,6 +58,11 @@ function StateProvider({ children }) {
89
58
  ["headerSize", 0],
90
59
  ["numContainers", 0],
91
60
  ["activeStickyIndex", -1],
61
+ ["isAtEnd", false],
62
+ ["isAtStart", false],
63
+ ["isNearEnd", false],
64
+ ["isNearStart", false],
65
+ ["isWithinMaintainScrollAtEndThreshold", false],
92
66
  ["totalSize", 0],
93
67
  ["scrollAdjustPending", 0]
94
68
  ]),
@@ -180,29 +154,71 @@ function notifyPosition$(ctx, key, value) {
180
154
  function useArr$(signalNames) {
181
155
  const ctx = React3__namespace.useContext(ContextState);
182
156
  const { subscribe, get } = React3__namespace.useMemo(() => createSelectorFunctionsArr(ctx, signalNames), [ctx, signalNames]);
183
- const value = shim.useSyncExternalStore(subscribe, get);
157
+ const value = shim.useSyncExternalStore(subscribe, get, get);
184
158
  return value;
185
159
  }
186
160
  function useSelector$(signalName, selector) {
187
161
  const ctx = React3__namespace.useContext(ContextState);
188
162
  const { subscribe, get } = React3__namespace.useMemo(() => createSelectorFunctionsArr(ctx, [signalName]), [ctx, signalName]);
189
- const value = shim.useSyncExternalStore(subscribe, () => selector(get()[0]));
163
+ const getSelectedValue = React3__namespace.useCallback(() => selector(get()[0]), [get, selector]);
164
+ const value = shim.useSyncExternalStore(subscribe, getSelectedValue, getSelectedValue);
190
165
  return value;
191
166
  }
192
167
 
168
+ // src/state/getContentInsetEnd.ts
169
+ function getContentInsetEnd(ctx) {
170
+ var _a3, _b;
171
+ const state = ctx.state;
172
+ const { props } = state;
173
+ const horizontal = props.horizontal;
174
+ const contentInset = props.contentInset;
175
+ const baseInset = contentInset != null ? contentInset : state.nativeContentInset;
176
+ const baseEndInset = (horizontal ? baseInset == null ? void 0 : baseInset.right : baseInset == null ? void 0 : baseInset.bottom) || 0;
177
+ const anchoredEndSpaceSize = peek$(ctx, "anchoredEndSpaceSize");
178
+ const anchoredEndInset = ((_a3 = props.anchoredEndSpace) == null ? void 0 : _a3.includeInEndInset) && anchoredEndSpaceSize ? anchoredEndSpaceSize : 0;
179
+ const overrideInset = (_b = state.contentInsetOverride) != null ? _b : void 0;
180
+ if (overrideInset) {
181
+ const mergedInset = { bottom: 0, right: 0, ...baseInset, ...overrideInset };
182
+ return Math.max((horizontal ? mergedInset.right : mergedInset.bottom) || 0, anchoredEndInset);
183
+ }
184
+ return Math.max(baseEndInset, anchoredEndInset);
185
+ }
186
+
187
+ // src/state/getContentSize.ts
188
+ function getContentSize(ctx) {
189
+ var _a3;
190
+ const { values, state } = ctx;
191
+ const stylePaddingTop = values.get("stylePaddingTop") || 0;
192
+ const stylePaddingBottom = state.props.stylePaddingBottom || 0;
193
+ const headerSize = values.get("headerSize") || 0;
194
+ const footerSize = values.get("footerSize") || 0;
195
+ const contentInsetBottom = getContentInsetEnd(ctx);
196
+ const totalSize = (_a3 = state.pendingTotalSize) != null ? _a3 : values.get("totalSize");
197
+ return headerSize + footerSize + totalSize + stylePaddingTop + stylePaddingBottom + (contentInsetBottom || 0);
198
+ }
199
+
193
200
  // src/components/DebugView.tsx
194
201
  var DebugRow = ({ children }) => {
195
202
  return /* @__PURE__ */ React3__namespace.createElement(View, { style: { alignItems: "center", flexDirection: "row", justifyContent: "space-between" } }, children);
196
203
  };
197
- React3__namespace.memo(function DebugView2({ state }) {
204
+ React3__namespace.memo(function DebugView2() {
198
205
  const ctx = useStateContext();
199
- const [totalSize = 0, scrollAdjust = 0, rawScroll = 0, scroll = 0, _numContainers = 0, _numContainersPooled = 0] = useArr$([
206
+ const [
207
+ totalSize = 0,
208
+ scrollAdjust = 0,
209
+ rawScroll = 0,
210
+ scroll = 0,
211
+ _numContainers = 0,
212
+ _numContainersPooled = 0,
213
+ isAtEnd = false
214
+ ] = useArr$([
200
215
  "totalSize",
201
216
  "scrollAdjust",
202
217
  "debugRawScroll",
203
218
  "debugComputedScroll",
204
219
  "numContainers",
205
- "numContainersPooled"
220
+ "numContainersPooled",
221
+ "isAtEnd"
206
222
  ]);
207
223
  const contentSize = getContentSize(ctx);
208
224
  const [, forceUpdate] = React3.useReducer((x) => x + 1, 0);
@@ -227,7 +243,7 @@ React3__namespace.memo(function DebugView2({ state }) {
227
243
  },
228
244
  /* @__PURE__ */ React3__namespace.createElement(DebugRow, null, /* @__PURE__ */ React3__namespace.createElement(Text, null, "TotalSize:"), /* @__PURE__ */ React3__namespace.createElement(Text, null, totalSize.toFixed(2))),
229
245
  /* @__PURE__ */ React3__namespace.createElement(DebugRow, null, /* @__PURE__ */ React3__namespace.createElement(Text, null, "ContentSize:"), /* @__PURE__ */ React3__namespace.createElement(Text, null, contentSize.toFixed(2))),
230
- /* @__PURE__ */ React3__namespace.createElement(DebugRow, null, /* @__PURE__ */ React3__namespace.createElement(Text, null, "At end:"), /* @__PURE__ */ React3__namespace.createElement(Text, null, String(state.isAtEnd))),
246
+ /* @__PURE__ */ React3__namespace.createElement(DebugRow, null, /* @__PURE__ */ React3__namespace.createElement(Text, null, "At end:"), /* @__PURE__ */ React3__namespace.createElement(Text, null, String(isAtEnd))),
231
247
  /* @__PURE__ */ React3__namespace.createElement(DebugRow, null, /* @__PURE__ */ React3__namespace.createElement(Text, null, "ScrollAdjust:"), /* @__PURE__ */ React3__namespace.createElement(Text, null, scrollAdjust.toFixed(2))),
232
248
  /* @__PURE__ */ React3__namespace.createElement(DebugRow, null, /* @__PURE__ */ React3__namespace.createElement(Text, null, "RawScroll: "), /* @__PURE__ */ React3__namespace.createElement(Text, null, rawScroll.toFixed(2))),
233
249
  /* @__PURE__ */ React3__namespace.createElement(DebugRow, null, /* @__PURE__ */ React3__namespace.createElement(Text, null, "ComputedScroll: "), /* @__PURE__ */ React3__namespace.createElement(Text, null, scroll.toFixed(2)))
@@ -250,6 +266,7 @@ var IS_DEV = (_a2 = processDev != null ? processDev : metroDev) != null ? _a2 :
250
266
 
251
267
  // src/constants.ts
252
268
  var POSITION_OUT_OF_VIEW = -1e7;
269
+ var EDGE_POSITION_EPSILON = 1;
253
270
  var ENABLE_DEVMODE = IS_DEV && false;
254
271
  var ENABLE_DEBUG_VIEW = IS_DEV && false;
255
272
  var typedForwardRef = React3__namespace.forwardRef;
@@ -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
  };
@@ -2062,180 +2464,7 @@ function scrollToIndex(ctx, {
2062
2464
  });
2063
2465
  }
2064
2466
 
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
- }
2231
- }
2232
-
2233
- // src/core/initialScroll.ts
2234
- function syncInitialScrollOffset(state, offset) {
2235
- state.scroll = offset;
2236
- state.scrollPending = offset;
2237
- state.scrollPrev = offset;
2238
- }
2467
+ // src/core/initialScroll.ts
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) {
@@ -3890,6 +4251,7 @@ function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pen
3890
4251
  const numContainers = peek$(ctx, "numContainers");
3891
4252
  const state = ctx.state;
3892
4253
  const { stickyContainerPool, containerItemTypes } = state;
4254
+ const shouldAvoidAssignedContainerReuse = state.props.recycleItems && !!state.props.positionComponentInternal;
3893
4255
  const result = [];
3894
4256
  const availableContainers = [];
3895
4257
  const pendingRemovalSet = new Set(pendingRemoval);
@@ -3946,18 +4308,20 @@ function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pen
3946
4308
  }
3947
4309
  }
3948
4310
  }
3949
- for (let u = 0; u < numContainers && result.length < numNeeded; u++) {
3950
- if (stickyContainerPool.has(u)) {
3951
- continue;
3952
- }
3953
- const key = peek$(ctx, `containerItemKey${u}`);
3954
- if (key === void 0) continue;
3955
- const index = state.indexByKey.get(key);
3956
- const isOutOfView = index < startBuffered || index > endBuffered;
3957
- if (isOutOfView) {
3958
- const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
3959
- if (!requiredItemTypes || typeIndex < neededTypes.length && canReuseContainer(u, neededTypes[typeIndex])) {
3960
- availableContainers.push({ distance, index: u });
4311
+ if (!shouldAvoidAssignedContainerReuse) {
4312
+ for (let u = 0; u < numContainers && result.length < numNeeded; u++) {
4313
+ if (stickyContainerPool.has(u)) {
4314
+ continue;
4315
+ }
4316
+ const key = peek$(ctx, `containerItemKey${u}`);
4317
+ if (key === void 0) continue;
4318
+ const index = state.indexByKey.get(key);
4319
+ const isOutOfView = index < startBuffered || index > endBuffered;
4320
+ if (isOutOfView) {
4321
+ const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
4322
+ if (!requiredItemTypes || typeIndex < neededTypes.length && canReuseContainer(u, neededTypes[typeIndex])) {
4323
+ availableContainers.push({ distance, index: u });
4324
+ }
3961
4325
  }
3962
4326
  }
3963
4327
  }
@@ -4101,7 +4465,6 @@ function calculateItemsInView(ctx, params = {}) {
4101
4465
  alwaysRenderIndicesSet,
4102
4466
  drawDistance,
4103
4467
  getItemType,
4104
- itemsAreEqual,
4105
4468
  keyExtractor,
4106
4469
  onStickyHeaderChange
4107
4470
  },
@@ -4128,11 +4491,11 @@ function calculateItemsInView(ctx, params = {}) {
4128
4491
  const numColumns = peek$(ctx, "numColumns");
4129
4492
  const speed = getScrollVelocity(state);
4130
4493
  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 ? (
4494
+ const { initialScroll, queuedInitialLayout } = state;
4495
+ const scrollState = suppressInitialScrollSideEffects ? (_b = bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.scroll) != null ? _b : state.scroll : !queuedInitialLayout && hasActiveInitialScroll(state) && initialScroll ? (
4133
4496
  // Before the initial layout settles, keep viewport math anchored to the
4134
4497
  // current initial-scroll target instead of transient native adjustments.
4135
- resolveInitialScrollOffset(ctx, state.initialScroll)
4498
+ resolveInitialScrollOffset(ctx, initialScroll)
4136
4499
  ) : state.scroll;
4137
4500
  const scrollAdjustPending = (_c = peek$(ctx, "scrollAdjustPending")) != null ? _c : 0;
4138
4501
  const scrollAdjustPad = scrollAdjustPending - topPad;
@@ -4177,9 +4540,11 @@ function calculateItemsInView(ctx, params = {}) {
4177
4540
  columnSpans.length = 0;
4178
4541
  }
4179
4542
  const startIndex = forceFullItemPositions || dataChanged ? 0 : (_d = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _d : 0;
4543
+ const optimizeForVisibleWindow = !forceFullItemPositions && !dataChanged && numColumns > 1 && minIndexSizeChanged !== void 0;
4180
4544
  updateItemPositions(ctx, dataChanged, {
4181
4545
  doMVCP,
4182
4546
  forceFullUpdate: !!forceFullItemPositions,
4547
+ optimizeForVisibleWindow,
4183
4548
  scrollBottomBuffered,
4184
4549
  startIndex
4185
4550
  });
@@ -4427,33 +4792,11 @@ function calculateItemsInView(ctx, params = {}) {
4427
4792
  set$(ctx, `containerSpan${i}`, 1);
4428
4793
  } else {
4429
4794
  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
- }
4795
+ if (itemIndex !== void 0) {
4796
+ didChangePositions = syncMountedContainer(ctx, i, itemIndex, {
4797
+ scrollAdjustPending,
4798
+ updateLayout: true
4799
+ }).didChangePosition || didChangePositions;
4457
4800
  }
4458
4801
  }
4459
4802
  }
@@ -4464,7 +4807,10 @@ function calculateItemsInView(ctx, params = {}) {
4464
4807
  evaluateBootstrapInitialScroll(ctx);
4465
4808
  return;
4466
4809
  }
4467
- if (!queuedInitialLayout && endBuffered !== null && checkAllSizesKnown(state)) {
4810
+ const mountedBufferedIndices = getMountedBufferedIndices(state);
4811
+ const mountedNoBufferIndices = getMountedNoBufferIndices(state);
4812
+ const readinessIndices = hasActiveInitialScroll(state) ? mountedBufferedIndices : mountedNoBufferIndices.length > 0 ? mountedNoBufferIndices : mountedBufferedIndices;
4813
+ if (!queuedInitialLayout && readinessIndices.length > 0 && checkAllSizesKnown(state, readinessIndices)) {
4468
4814
  setDidLayout(ctx);
4469
4815
  handleInitialScrollLayoutReady(ctx);
4470
4816
  }
@@ -4475,7 +4821,9 @@ function calculateItemsInView(ctx, params = {}) {
4475
4821
  viewabilityConfigCallbackPairs,
4476
4822
  scrollLength,
4477
4823
  startNoBuffer,
4478
- endNoBuffer
4824
+ endNoBuffer,
4825
+ startBuffered != null ? startBuffered : startNoBuffer,
4826
+ endBuffered != null ? endBuffered : endNoBuffer
4479
4827
  );
4480
4828
  }
4481
4829
  if (onStickyHeaderChange && stickyIndicesArr.length > 0 && nextActiveStickyIndex !== void 0 && nextActiveStickyIndex !== previousStickyIndex) {
@@ -4487,37 +4835,17 @@ function calculateItemsInView(ctx, params = {}) {
4487
4835
  });
4488
4836
  }
4489
4837
 
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
4838
  // src/core/doMaintainScrollAtEnd.ts
4511
4839
  function doMaintainScrollAtEnd(ctx) {
4512
4840
  const state = ctx.state;
4513
4841
  const {
4514
4842
  didContainersLayout,
4515
- isAtEnd,
4516
4843
  pendingNativeMVCPAdjust,
4517
4844
  refScroller,
4518
4845
  props: { maintainScrollAtEnd }
4519
4846
  } = state;
4520
- const shouldMaintainScrollAtEnd = !!(isAtEnd && maintainScrollAtEnd && didContainersLayout);
4847
+ const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
4848
+ const shouldMaintainScrollAtEnd = !!(isWithinMaintainScrollAtEndThreshold && maintainScrollAtEnd && didContainersLayout);
4521
4849
  if (pendingNativeMVCPAdjust) {
4522
4850
  state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
4523
4851
  return false;
@@ -4530,7 +4858,7 @@ function doMaintainScrollAtEnd(ctx) {
4530
4858
  }
4531
4859
  requestAnimationFrame(() => {
4532
4860
  var _a3;
4533
- if (state.isAtEnd) {
4861
+ if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
4534
4862
  state.maintainingScrollAtEnd = true;
4535
4863
  (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
4536
4864
  animated: maintainScrollAtEnd.animated
@@ -4548,68 +4876,22 @@ function doMaintainScrollAtEnd(ctx) {
4548
4876
  return false;
4549
4877
  }
4550
4878
 
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
4879
  // src/core/checkResetContainers.ts
4604
- function checkResetContainers(ctx, dataProp) {
4880
+ function checkResetContainers(ctx, dataProp, { didColumnsChange = false } = {}) {
4605
4881
  const state = ctx.state;
4606
4882
  const { previousData } = state;
4607
- if (previousData) {
4608
- updateAveragesOnDataChange(state, previousData, dataProp);
4609
- }
4610
4883
  const { maintainScrollAtEnd } = state.props;
4884
+ if (didColumnsChange) {
4885
+ state.sizes.clear();
4886
+ state.sizesKnown.clear();
4887
+ for (const key in state.averageSizes) {
4888
+ delete state.averageSizes[key];
4889
+ }
4890
+ state.minIndexSizeChanged = 0;
4891
+ state.scrollForNextCalculateItemsInView = void 0;
4892
+ }
4611
4893
  calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
4612
- const shouldMaintainScrollAtEnd = maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onDataChange;
4894
+ const shouldMaintainScrollAtEnd = !didColumnsChange && (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onDataChange);
4613
4895
  const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx);
4614
4896
  if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
4615
4897
  state.isEndReached = false;
@@ -4620,6 +4902,53 @@ function checkResetContainers(ctx, dataProp) {
4620
4902
  delete state.previousData;
4621
4903
  }
4622
4904
 
4905
+ // src/core/checkStructuralDataChange.ts
4906
+ function checkStructuralDataChange(state, dataProp, previousData) {
4907
+ var _a3;
4908
+ state.pendingDataComparison = void 0;
4909
+ if (!previousData || !dataProp || dataProp.length !== previousData.length) {
4910
+ return true;
4911
+ }
4912
+ const {
4913
+ idCache,
4914
+ props: { itemsAreEqual, keyExtractor }
4915
+ } = state;
4916
+ let byIndex;
4917
+ for (let i = 0; i < dataProp.length; i++) {
4918
+ if (dataProp[i] === previousData[i]) {
4919
+ continue;
4920
+ }
4921
+ if (!keyExtractor) {
4922
+ if (byIndex) {
4923
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4924
+ }
4925
+ return true;
4926
+ }
4927
+ const previousKey = (_a3 = idCache[i]) != null ? _a3 : keyExtractor(previousData[i], i);
4928
+ const nextKey = keyExtractor(dataProp[i], i);
4929
+ if (previousKey !== nextKey) {
4930
+ if (byIndex) {
4931
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4932
+ }
4933
+ return true;
4934
+ }
4935
+ if (!itemsAreEqual) {
4936
+ if (byIndex) {
4937
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4938
+ }
4939
+ return true;
4940
+ }
4941
+ const isEqual = itemsAreEqual(previousData[i], dataProp[i], i, dataProp);
4942
+ byIndex != null ? byIndex : byIndex = [];
4943
+ byIndex[i] = isEqual ? 1 : 2;
4944
+ if (!isEqual) {
4945
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4946
+ return true;
4947
+ }
4948
+ }
4949
+ return false;
4950
+ }
4951
+
4623
4952
  // src/core/doInitialAllocateContainers.ts
4624
4953
  function doInitialAllocateContainers(ctx) {
4625
4954
  var _a3, _b, _c;
@@ -4863,6 +5192,7 @@ function onScroll(ctx, event) {
4863
5192
  state.scrollPending = newScroll;
4864
5193
  updateScroll(ctx, newScroll, insetChanged);
4865
5194
  trackInitialScrollNativeProgress(state, newScroll);
5195
+ clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx);
4866
5196
  if (state.scrollingTo) {
4867
5197
  checkFinishedScroll(ctx);
4868
5198
  }
@@ -4926,6 +5256,43 @@ var ScrollAdjustHandler = class {
4926
5256
  }
4927
5257
  };
4928
5258
 
5259
+ // src/core/updateAnchoredEndSpace.ts
5260
+ function maybeUpdateAnchoredEndSpace(ctx) {
5261
+ var _a3;
5262
+ const state = ctx.state;
5263
+ const anchoredEndSpace = state.props.anchoredEndSpace;
5264
+ const previousSize = peek$(ctx, "anchoredEndSpaceSize");
5265
+ let nextSize = 0;
5266
+ if (anchoredEndSpace) {
5267
+ const { anchorIndex, anchorMaxSize, anchorOffset = 0 } = anchoredEndSpace;
5268
+ const { data } = state.props;
5269
+ if (anchorIndex >= 0 && anchorIndex < data.length && state.scrollLength > 0) {
5270
+ let contentBelowAnchor = 0;
5271
+ const footerSize = ctx.values.get("footerSize") || 0;
5272
+ const stylePaddingBottom = state.props.stylePaddingBottom || 0;
5273
+ for (let index = anchorIndex; index < data.length; index++) {
5274
+ const itemKey = getId(state, index);
5275
+ const size = itemKey ? state.sizesKnown.get(itemKey) : void 0;
5276
+ const effectiveSize = index === anchorIndex && anchorMaxSize !== void 0 ? Math.min(size || 0, Math.max(0, anchorMaxSize)) : size;
5277
+ if (effectiveSize !== null && effectiveSize !== void 0 && effectiveSize > 0) {
5278
+ contentBelowAnchor += effectiveSize;
5279
+ }
5280
+ }
5281
+ contentBelowAnchor += footerSize + stylePaddingBottom;
5282
+ nextSize = Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
5283
+ }
5284
+ }
5285
+ if (previousSize === nextSize) {
5286
+ return nextSize;
5287
+ }
5288
+ set$(ctx, "anchoredEndSpaceSize", nextSize);
5289
+ (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
5290
+ if (anchoredEndSpace == null ? void 0 : anchoredEndSpace.includeInEndInset) {
5291
+ updateScroll(ctx, state.scroll, true);
5292
+ }
5293
+ return nextSize;
5294
+ }
5295
+
4929
5296
  // src/core/updateItemSize.ts
4930
5297
  function runOrScheduleMVCPRecalculate(ctx) {
4931
5298
  const state = ctx.state;
@@ -5007,6 +5374,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5007
5374
  previous: size - diff,
5008
5375
  size
5009
5376
  });
5377
+ maybeUpdateAnchoredEndSpace(ctx);
5010
5378
  }
5011
5379
  if (minIndexSizeChanged !== void 0) {
5012
5380
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
@@ -5130,7 +5498,7 @@ function createImperativeHandle(ctx) {
5130
5498
  const IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS = 800;
5131
5499
  const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
5132
5500
  let imperativeScrollToken = 0;
5133
- const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0 || hasActiveMVCPAnchorLock(state);
5501
+ const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0;
5134
5502
  const runWhenSettled = (token, run) => {
5135
5503
  const startedAt = Date.now();
5136
5504
  let stableFrames = 0;
@@ -5152,9 +5520,10 @@ function createImperativeHandle(ctx) {
5152
5520
  };
5153
5521
  requestAnimationFrame(check);
5154
5522
  };
5155
- const runScrollWithPromise = (run) => new Promise((resolve) => {
5523
+ const runScrollWithPromise = (run, options) => new Promise((resolve) => {
5156
5524
  var _a3;
5157
5525
  const token = ++imperativeScrollToken;
5526
+ const shouldWaitOneFrame = !!(options == null ? void 0 : options.shouldWaitOneFrame);
5158
5527
  (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
5159
5528
  state.pendingScrollResolve = resolve;
5160
5529
  const runNow = () => {
@@ -5169,11 +5538,12 @@ function createImperativeHandle(ctx) {
5169
5538
  resolve();
5170
5539
  }
5171
5540
  };
5541
+ const execute = shouldWaitOneFrame ? () => requestAnimationFrame(runNow) : runNow;
5172
5542
  if (isSettlingAfterDataChange()) {
5173
- runWhenSettled(token, runNow);
5174
- return;
5543
+ runWhenSettled(token, execute);
5544
+ } else {
5545
+ execute();
5175
5546
  }
5176
- runNow();
5177
5547
  });
5178
5548
  const scrollIndexIntoView = (options) => {
5179
5549
  if (state) {
@@ -5230,10 +5600,13 @@ function createImperativeHandle(ctx) {
5230
5600
  },
5231
5601
  end: state.endNoBuffer,
5232
5602
  endBuffered: state.endBuffered,
5233
- isAtEnd: state.isAtEnd,
5234
- isAtStart: state.isAtStart,
5603
+ isAtEnd: peek$(ctx, "isAtEnd"),
5604
+ isAtStart: peek$(ctx, "isAtStart"),
5235
5605
  isEndReached: state.isEndReached,
5606
+ isNearEnd: peek$(ctx, "isNearEnd"),
5607
+ isNearStart: peek$(ctx, "isNearStart"),
5236
5608
  isStartReached: state.isStartReached,
5609
+ isWithinMaintainScrollAtEndThreshold: peek$(ctx, "isWithinMaintainScrollAtEndThreshold"),
5237
5610
  listen: (signalName, cb) => listen$(ctx, signalName, cb),
5238
5611
  listenToPosition: (key, cb) => listenPosition$(ctx, key, cb),
5239
5612
  positionAtIndex: (index) => state.positions[index],
@@ -5280,10 +5653,15 @@ function createImperativeHandle(ctx) {
5280
5653
  }
5281
5654
  return false;
5282
5655
  }),
5283
- scrollToIndex: (params) => runScrollWithPromise(() => {
5284
- scrollToIndex(ctx, params);
5285
- return true;
5286
- }),
5656
+ scrollToIndex: (params) => runScrollWithPromise(
5657
+ () => {
5658
+ scrollToIndex(ctx, params);
5659
+ return true;
5660
+ },
5661
+ {
5662
+ shouldWaitOneFrame: params.index >= 0 && params.index >= state.props.data.length
5663
+ }
5664
+ ),
5287
5665
  scrollToItem: ({ item, ...props }) => runScrollWithPromise(() => {
5288
5666
  const data = state.props.data;
5289
5667
  const index = data.indexOf(item);
@@ -5379,7 +5757,7 @@ function getRenderedItem(ctx, key) {
5379
5757
  item,
5380
5758
  type: getItemType ? (_a3 = getItemType(item, index)) != null ? _a3 : "" : ""
5381
5759
  };
5382
- renderedItem = isFunction(renderItem) ? renderItem(itemProps) : React3__namespace.default.createElement(renderItem, itemProps);
5760
+ renderedItem = React3__namespace.default.createElement(renderItem, itemProps);
5383
5761
  }
5384
5762
  return { index, item: data[index], renderedItem };
5385
5763
  }
@@ -5513,9 +5891,18 @@ var LegendList = typedMemo(
5513
5891
  })
5514
5892
  );
5515
5893
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
5516
- var _a3, _b, _c, _d, _e, _f, _g;
5894
+ var _a3, _b, _c, _d, _e, _f, _g, _h;
5895
+ const noopOnScroll = React3.useCallback((_event) => {
5896
+ }, []);
5897
+ if (props.recycleItems === void 0) {
5898
+ warnDevOnce(
5899
+ "recycleItems-omitted",
5900
+ "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."
5901
+ );
5902
+ }
5517
5903
  const {
5518
5904
  alignItemsAtEnd = false,
5905
+ anchoredEndSpace,
5519
5906
  alwaysRender,
5520
5907
  columnWrapperStyle,
5521
5908
  contentContainerStyle: contentContainerStyleProp,
@@ -5581,7 +5968,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5581
5968
  const positionComponentInternal = props.positionComponentInternal;
5582
5969
  const stickyPositionComponentInternal = props.stickyPositionComponentInternal;
5583
5970
  const {
5584
- childrenMode,
5585
5971
  positionComponentInternal: _positionComponentInternal,
5586
5972
  stickyPositionComponentInternal: _stickyPositionComponentInternal,
5587
5973
  ...restProps
@@ -5645,18 +6031,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5645
6031
  dataVersion,
5646
6032
  keyExtractor
5647
6033
  ]);
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
6034
  const useWindowScrollResolved = !!useWindowScroll && !renderScrollComponent;
5661
6035
  const refState = React3.useRef(void 0);
5662
6036
  const hasOverrideItemLayout = !!overrideItemLayout;
@@ -5665,7 +6039,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5665
6039
  if (!ctx.state) {
5666
6040
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : { height: 0, width: 0 } )[horizontal ? "width" : "height"];
5667
6041
  ctx.state = {
5668
- activeStickyIndex: -1,
5669
6042
  averageSizes: {},
5670
6043
  columnSpans: [],
5671
6044
  columns: [],
@@ -5689,8 +6062,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5689
6062
  kind: initialScrollUsesOffsetOnly ? "offset" : "bootstrap",
5690
6063
  previousDataLength: dataProp.length
5691
6064
  } : void 0,
5692
- isAtEnd: false,
5693
- isAtStart: false,
5694
6065
  isEndReached: null,
5695
6066
  isFirst: true,
5696
6067
  isStartReached: null,
@@ -5701,6 +6072,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5701
6072
  minIndexSizeChanged: 0,
5702
6073
  nativeContentInset: void 0,
5703
6074
  nativeMarginTop: 0,
6075
+ pendingDataComparison: void 0,
5704
6076
  pendingNativeMVCPAdjust: void 0,
5705
6077
  positions: [],
5706
6078
  props: {},
@@ -5739,22 +6111,29 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5739
6111
  }
5740
6112
  const state = refState.current;
5741
6113
  const isFirstLocal = state.isFirst;
5742
- state.didColumnsChange = numColumnsProp !== state.props.numColumns;
6114
+ const previousNumColumnsProp = state.props.numColumns;
6115
+ state.didColumnsChange = numColumnsProp !== previousNumColumnsProp;
5743
6116
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
5744
6117
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
5745
- const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkActualChange(state, dataProp, state.props.data);
6118
+ const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
6119
+ if (didDataChangeLocal && state.didFinishInitialScroll && ((_f = state.initialScroll) == null ? void 0 : _f.viewPosition) === 1 && state.props.data.length > 0) {
6120
+ clearPreservedInitialScrollTarget(state);
6121
+ }
5746
6122
  if (didDataChangeLocal) {
5747
6123
  state.dataChangeEpoch += 1;
5748
6124
  state.dataChangeNeedsScrollUpdate = true;
5749
6125
  state.didDataChange = true;
5750
6126
  state.previousData = state.props.data;
5751
6127
  }
5752
- const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
6128
+ const throttledOnScroll = useThrottledOnScroll(onScrollProp != null ? onScrollProp : noopOnScroll, scrollEventThrottle != null ? scrollEventThrottle : 0);
6129
+ const throttleScrollFn = scrollEventThrottle && onScrollProp ? throttledOnScroll : onScrollProp;
6130
+ const anchoredEndSpaceResolved = anchoredEndSpace ? { ...anchoredEndSpace, includeInEndInset: true } : anchoredEndSpace;
5753
6131
  state.props = {
5754
6132
  alignItemsAtEnd,
5755
6133
  alwaysRender,
5756
6134
  alwaysRenderIndicesArr: alwaysRenderIndices.arr,
5757
6135
  alwaysRenderIndicesSet: alwaysRenderIndices.set,
6136
+ anchoredEndSpace: anchoredEndSpaceResolved,
5758
6137
  animatedProps: animatedPropsInternal,
5759
6138
  contentInset,
5760
6139
  data: dataProp,
@@ -5839,16 +6218,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5839
6218
  if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
5840
6219
  refState.current.lastBatchingAction = Date.now();
5841
6220
  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
6221
  refState.current.sizes.clear();
5847
6222
  refState.current.positions.length = 0;
5848
6223
  refState.current.totalSize = 0;
5849
6224
  set$(ctx, "totalSize", 0);
5850
6225
  }
5851
6226
  }
6227
+ if (IS_DEV) {
6228
+ useDevChecks(props);
6229
+ }
5852
6230
  React3.useLayoutEffect(() => {
5853
6231
  handleInitialScrollDataChange(ctx, {
5854
6232
  dataLength: dataProp.length,
@@ -5858,6 +6236,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5858
6236
  useBootstrapInitialScroll: usesBootstrapInitialScroll
5859
6237
  });
5860
6238
  }, [dataProp.length, didDataChangeLocal, initialScrollAtEnd, stylePaddingBottomState, usesBootstrapInitialScroll]);
6239
+ React3.useLayoutEffect(() => {
6240
+ maybeUpdateAnchoredEndSpace(ctx);
6241
+ }, [
6242
+ ctx,
6243
+ dataProp,
6244
+ dataVersion,
6245
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex,
6246
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorMaxSize,
6247
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorOffset,
6248
+ numColumnsProp
6249
+ ]);
5861
6250
  const onLayoutFooter = React3.useCallback(
5862
6251
  (layout) => {
5863
6252
  if (!usesBootstrapInitialScroll) {
@@ -5873,14 +6262,21 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5873
6262
  [dataProp.length, initialScrollAtEnd, horizontal, stylePaddingBottomState, usesBootstrapInitialScroll]
5874
6263
  );
5875
6264
  const onLayoutChange = React3.useCallback(
5876
- (layout) => {
6265
+ (layout, fromLayoutEffect) => {
6266
+ const previousScrollLength = state.scrollLength;
6267
+ const previousOtherAxisSize = state.otherAxisSize;
5877
6268
  handleLayout(ctx, layout, setCanRender);
6269
+ maybeUpdateAnchoredEndSpace(ctx);
6270
+ const didLayoutAffectBootstrapTarget = previousScrollLength !== state.scrollLength || previousOtherAxisSize !== state.otherAxisSize;
6271
+ if (usesBootstrapInitialScroll && !fromLayoutEffect && didLayoutAffectBootstrapTarget) {
6272
+ handleBootstrapInitialScrollLayoutChange(ctx);
6273
+ }
5878
6274
  if (usesBootstrapInitialScroll) {
5879
6275
  return;
5880
6276
  }
5881
6277
  advanceCurrentInitialScrollSession(ctx);
5882
6278
  },
5883
- [usesBootstrapInitialScroll]
6279
+ [dataProp.length, initialScrollAtEnd, stylePaddingBottomState, usesBootstrapInitialScroll]
5884
6280
  );
5885
6281
  const { onLayout } = useOnLayoutSync({
5886
6282
  onLayoutChange,
@@ -5893,6 +6289,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5893
6289
  updateSnapToOffsets(ctx);
5894
6290
  }
5895
6291
  }, [snapToIndices]);
6292
+ React3.useLayoutEffect(
6293
+ () => initializeStateVars(true),
6294
+ [dataVersion, memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingBottomState, stylePaddingTopState]
6295
+ );
5896
6296
  React3.useLayoutEffect(() => {
5897
6297
  const {
5898
6298
  didColumnsChange,
@@ -5902,7 +6302,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5902
6302
  } = state;
5903
6303
  const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx);
5904
6304
  if (!didAllocateContainers && !isFirst && (didDataChange || didColumnsChange)) {
5905
- checkResetContainers(ctx, data);
6305
+ checkResetContainers(ctx, data, { didColumnsChange });
6306
+ }
6307
+ if (didDataChange) {
6308
+ state.pendingDataComparison = void 0;
5906
6309
  }
5907
6310
  state.didColumnsChange = false;
5908
6311
  state.didDataChange = false;
@@ -5917,10 +6320,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5917
6320
  (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { forceFullItemPositions: true });
5918
6321
  }
5919
6322
  }, [extraData, hasOverrideItemLayout, numColumnsProp]);
5920
- React3.useLayoutEffect(
5921
- () => initializeStateVars(true),
5922
- [dataVersion, memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingBottomState, stylePaddingTopState]
5923
- );
5924
6323
  React3.useEffect(() => {
5925
6324
  if (!onMetricsChange) {
5926
6325
  return;
@@ -5953,15 +6352,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5953
6352
  state.viewabilityConfigCallbackPairs = viewability;
5954
6353
  state.enableScrollForNextCalculateItemsInView = !viewability;
5955
6354
  }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
6355
+ useInit(() => {
6356
+ });
5956
6357
  React3.useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx), []);
5957
- {
5958
- React3.useEffect(() => {
5959
- if (usesBootstrapInitialScroll) {
5960
- return;
5961
- }
5962
- advanceCurrentInitialScrollSession(ctx);
5963
- }, [usesBootstrapInitialScroll]);
5964
- }
6358
+ React3.useEffect(() => {
6359
+ if (usesBootstrapInitialScroll) {
6360
+ return;
6361
+ }
6362
+ advanceCurrentInitialScrollSession(ctx);
6363
+ }, [ctx, usesBootstrapInitialScroll]);
5965
6364
  const fns = React3.useMemo(
5966
6365
  () => ({
5967
6366
  getRenderedItem: (key) => getRenderedItem(ctx, key),
@@ -5999,7 +6398,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5999
6398
  onScroll: onScrollHandler,
6000
6399
  recycleItems,
6001
6400
  refreshControl: refreshControlElement ? stylePaddingTopState > 0 ? React3__namespace.cloneElement(refreshControlElement, {
6002
- progressViewOffset: ((_f = refreshControlElement.props.progressViewOffset) != null ? _f : 0) + stylePaddingTopState
6401
+ progressViewOffset: ((_g = refreshControlElement.props.progressViewOffset) != null ? _g : 0) + stylePaddingTopState
6003
6402
  }) : refreshControlElement : onRefresh && /* @__PURE__ */ React3__namespace.createElement(
6004
6403
  RefreshControl,
6005
6404
  {
@@ -6010,7 +6409,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6010
6409
  ),
6011
6410
  refScrollView: combinedRef,
6012
6411
  renderScrollComponent,
6013
- scrollAdjustHandler: (_g = refState.current) == null ? void 0 : _g.scrollAdjustHandler,
6412
+ scrollAdjustHandler: (_h = refState.current) == null ? void 0 : _h.scrollAdjustHandler,
6014
6413
  scrollEventThrottle: 0,
6015
6414
  snapToIndices,
6016
6415
  stickyHeaderIndices,
@@ -6021,13 +6420,40 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6021
6420
  ), IS_DEV && ENABLE_DEBUG_VIEW);
6022
6421
  });
6023
6422
 
6423
+ // src/components/stickyPositionUtils.ts
6424
+ function getStickyPushLimit(state, index, itemKey) {
6425
+ if (!itemKey) {
6426
+ return void 0;
6427
+ }
6428
+ const currentSize = state.sizes.get(itemKey);
6429
+ if (!(currentSize && currentSize > 0)) {
6430
+ return void 0;
6431
+ }
6432
+ const stickyIndexInArray = state.props.stickyIndicesArr.indexOf(index);
6433
+ if (stickyIndexInArray === -1) {
6434
+ return void 0;
6435
+ }
6436
+ const nextStickyIndex = state.props.stickyIndicesArr[stickyIndexInArray + 1];
6437
+ if (nextStickyIndex === void 0) {
6438
+ return void 0;
6439
+ }
6440
+ const nextStickyPosition = state.positions[nextStickyIndex];
6441
+ if (nextStickyPosition === void 0) {
6442
+ return void 0;
6443
+ }
6444
+ return nextStickyPosition - currentSize;
6445
+ }
6446
+
6024
6447
  // src/entrypoints/shared.ts
6025
6448
  var LegendListRuntime = LegendList;
6026
6449
  var internal = {
6027
6450
  getComponent,
6451
+ getStickyPushLimit,
6028
6452
  IsNewArchitecture,
6029
6453
  POSITION_OUT_OF_VIEW,
6030
6454
  peek$,
6455
+ typedForwardRef,
6456
+ typedMemo,
6031
6457
  useArr$,
6032
6458
  useCombinedRef,
6033
6459
  useStateContext
@@ -6039,8 +6465,6 @@ var internal2 = internal;
6039
6465
 
6040
6466
  exports.LegendList = LegendList3;
6041
6467
  exports.internal = internal2;
6042
- exports.typedForwardRef = typedForwardRef;
6043
- exports.typedMemo = typedMemo;
6044
6468
  exports.useIsLastItem = useIsLastItem;
6045
6469
  exports.useListScrollSize = useListScrollSize;
6046
6470
  exports.useRecyclingEffect = useRecyclingEffect;