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