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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
  };
@@ -2040,199 +2442,26 @@ function scrollToIndex(ctx, {
2040
2442
  const state = ctx.state;
2041
2443
  const { data } = state.props;
2042
2444
  index = clampScrollIndex(index, data.length);
2043
- const itemSize = getItemSizeAtIndex(ctx, index);
2044
- const firstIndexOffset = calculateOffsetForIndex(ctx, index);
2045
- const isLast = index === data.length - 1;
2046
- if (isLast && viewPosition === void 0) {
2047
- viewPosition = 1;
2048
- }
2049
- state.scrollForNextCalculateItemsInView = void 0;
2050
- scrollTo(ctx, {
2051
- 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
- }
2445
+ const itemSize = getItemSizeAtIndex(ctx, index);
2446
+ const firstIndexOffset = calculateOffsetForIndex(ctx, index);
2447
+ const isLast = index === data.length - 1;
2448
+ if (isLast && viewPosition === void 0) {
2449
+ viewPosition = 1;
2227
2450
  }
2451
+ state.scrollForNextCalculateItemsInView = void 0;
2452
+ scrollTo(ctx, {
2453
+ animated,
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) {
@@ -4098,7 +4459,6 @@ function calculateItemsInView(ctx, params = {}) {
4098
4459
  alwaysRenderIndicesSet,
4099
4460
  drawDistance,
4100
4461
  getItemType,
4101
- itemsAreEqual,
4102
4462
  keyExtractor,
4103
4463
  onStickyHeaderChange
4104
4464
  },
@@ -4125,11 +4485,11 @@ function calculateItemsInView(ctx, params = {}) {
4125
4485
  const numColumns = peek$(ctx, "numColumns");
4126
4486
  const speed = getScrollVelocity(state);
4127
4487
  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 ? (
4488
+ const { initialScroll, queuedInitialLayout } = state;
4489
+ const scrollState = suppressInitialScrollSideEffects ? (_b = bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.scroll) != null ? _b : state.scroll : !queuedInitialLayout && hasActiveInitialScroll(state) && initialScroll ? (
4130
4490
  // Before the initial layout settles, keep viewport math anchored to the
4131
4491
  // current initial-scroll target instead of transient native adjustments.
4132
- resolveInitialScrollOffset(ctx, state.initialScroll)
4492
+ resolveInitialScrollOffset(ctx, initialScroll)
4133
4493
  ) : state.scroll;
4134
4494
  const scrollAdjustPending = (_c = peek$(ctx, "scrollAdjustPending")) != null ? _c : 0;
4135
4495
  const scrollAdjustPad = scrollAdjustPending - topPad;
@@ -4174,9 +4534,11 @@ function calculateItemsInView(ctx, params = {}) {
4174
4534
  columnSpans.length = 0;
4175
4535
  }
4176
4536
  const startIndex = forceFullItemPositions || dataChanged ? 0 : (_d = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _d : 0;
4537
+ const optimizeForVisibleWindow = !forceFullItemPositions && !dataChanged && numColumns > 1 && minIndexSizeChanged !== void 0;
4177
4538
  updateItemPositions(ctx, dataChanged, {
4178
4539
  doMVCP,
4179
4540
  forceFullUpdate: !!forceFullItemPositions,
4541
+ optimizeForVisibleWindow,
4180
4542
  scrollBottomBuffered,
4181
4543
  startIndex
4182
4544
  });
@@ -4424,33 +4786,11 @@ function calculateItemsInView(ctx, params = {}) {
4424
4786
  set$(ctx, `containerSpan${i}`, 1);
4425
4787
  } else {
4426
4788
  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
- }
4789
+ if (itemIndex !== void 0) {
4790
+ didChangePositions = syncMountedContainer(ctx, i, itemIndex, {
4791
+ scrollAdjustPending,
4792
+ updateLayout: true
4793
+ }).didChangePosition || didChangePositions;
4454
4794
  }
4455
4795
  }
4456
4796
  }
@@ -4461,7 +4801,10 @@ function calculateItemsInView(ctx, params = {}) {
4461
4801
  evaluateBootstrapInitialScroll(ctx);
4462
4802
  return;
4463
4803
  }
4464
- if (!queuedInitialLayout && endBuffered !== null && checkAllSizesKnown(state)) {
4804
+ const mountedBufferedIndices = getMountedBufferedIndices(state);
4805
+ const mountedNoBufferIndices = getMountedNoBufferIndices(state);
4806
+ const readinessIndices = hasActiveInitialScroll(state) ? mountedBufferedIndices : mountedNoBufferIndices.length > 0 ? mountedNoBufferIndices : mountedBufferedIndices;
4807
+ if (!queuedInitialLayout && readinessIndices.length > 0 && checkAllSizesKnown(state, readinessIndices)) {
4465
4808
  setDidLayout(ctx);
4466
4809
  handleInitialScrollLayoutReady(ctx);
4467
4810
  }
@@ -4472,7 +4815,9 @@ function calculateItemsInView(ctx, params = {}) {
4472
4815
  viewabilityConfigCallbackPairs,
4473
4816
  scrollLength,
4474
4817
  startNoBuffer,
4475
- endNoBuffer
4818
+ endNoBuffer,
4819
+ startBuffered != null ? startBuffered : startNoBuffer,
4820
+ endBuffered != null ? endBuffered : endNoBuffer
4476
4821
  );
4477
4822
  }
4478
4823
  if (onStickyHeaderChange && stickyIndicesArr.length > 0 && nextActiveStickyIndex !== void 0 && nextActiveStickyIndex !== previousStickyIndex) {
@@ -4484,37 +4829,17 @@ function calculateItemsInView(ctx, params = {}) {
4484
4829
  });
4485
4830
  }
4486
4831
 
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
4832
  // src/core/doMaintainScrollAtEnd.ts
4508
4833
  function doMaintainScrollAtEnd(ctx) {
4509
4834
  const state = ctx.state;
4510
4835
  const {
4511
4836
  didContainersLayout,
4512
- isAtEnd,
4513
4837
  pendingNativeMVCPAdjust,
4514
4838
  refScroller,
4515
4839
  props: { maintainScrollAtEnd }
4516
4840
  } = state;
4517
- const shouldMaintainScrollAtEnd = !!(isAtEnd && maintainScrollAtEnd && didContainersLayout);
4841
+ const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
4842
+ const shouldMaintainScrollAtEnd = !!(isWithinMaintainScrollAtEndThreshold && maintainScrollAtEnd && didContainersLayout);
4518
4843
  if (pendingNativeMVCPAdjust) {
4519
4844
  state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
4520
4845
  return false;
@@ -4527,7 +4852,7 @@ function doMaintainScrollAtEnd(ctx) {
4527
4852
  }
4528
4853
  requestAnimationFrame(() => {
4529
4854
  var _a3;
4530
- if (state.isAtEnd) {
4855
+ if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
4531
4856
  state.maintainingScrollAtEnd = true;
4532
4857
  (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
4533
4858
  animated: maintainScrollAtEnd.animated
@@ -4545,68 +4870,22 @@ function doMaintainScrollAtEnd(ctx) {
4545
4870
  return false;
4546
4871
  }
4547
4872
 
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
4873
  // src/core/checkResetContainers.ts
4601
- function checkResetContainers(ctx, dataProp) {
4874
+ function checkResetContainers(ctx, dataProp, { didColumnsChange = false } = {}) {
4602
4875
  const state = ctx.state;
4603
4876
  const { previousData } = state;
4604
- if (previousData) {
4605
- updateAveragesOnDataChange(state, previousData, dataProp);
4606
- }
4607
4877
  const { maintainScrollAtEnd } = state.props;
4878
+ if (didColumnsChange) {
4879
+ state.sizes.clear();
4880
+ state.sizesKnown.clear();
4881
+ for (const key in state.averageSizes) {
4882
+ delete state.averageSizes[key];
4883
+ }
4884
+ state.minIndexSizeChanged = 0;
4885
+ state.scrollForNextCalculateItemsInView = void 0;
4886
+ }
4608
4887
  calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
4609
- const shouldMaintainScrollAtEnd = maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onDataChange;
4888
+ const shouldMaintainScrollAtEnd = !didColumnsChange && (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onDataChange);
4610
4889
  const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx);
4611
4890
  if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
4612
4891
  state.isEndReached = false;
@@ -4617,6 +4896,53 @@ function checkResetContainers(ctx, dataProp) {
4617
4896
  delete state.previousData;
4618
4897
  }
4619
4898
 
4899
+ // src/core/checkStructuralDataChange.ts
4900
+ function checkStructuralDataChange(state, dataProp, previousData) {
4901
+ var _a3;
4902
+ state.pendingDataComparison = void 0;
4903
+ if (!previousData || !dataProp || dataProp.length !== previousData.length) {
4904
+ return true;
4905
+ }
4906
+ const {
4907
+ idCache,
4908
+ props: { itemsAreEqual, keyExtractor }
4909
+ } = state;
4910
+ let byIndex;
4911
+ for (let i = 0; i < dataProp.length; i++) {
4912
+ if (dataProp[i] === previousData[i]) {
4913
+ continue;
4914
+ }
4915
+ if (!keyExtractor) {
4916
+ if (byIndex) {
4917
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4918
+ }
4919
+ return true;
4920
+ }
4921
+ const previousKey = (_a3 = idCache[i]) != null ? _a3 : keyExtractor(previousData[i], i);
4922
+ const nextKey = keyExtractor(dataProp[i], i);
4923
+ if (previousKey !== nextKey) {
4924
+ if (byIndex) {
4925
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4926
+ }
4927
+ return true;
4928
+ }
4929
+ if (!itemsAreEqual) {
4930
+ if (byIndex) {
4931
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4932
+ }
4933
+ return true;
4934
+ }
4935
+ const isEqual = itemsAreEqual(previousData[i], dataProp[i], i, dataProp);
4936
+ byIndex != null ? byIndex : byIndex = [];
4937
+ byIndex[i] = isEqual ? 1 : 2;
4938
+ if (!isEqual) {
4939
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4940
+ return true;
4941
+ }
4942
+ }
4943
+ return false;
4944
+ }
4945
+
4620
4946
  // src/core/doInitialAllocateContainers.ts
4621
4947
  function doInitialAllocateContainers(ctx) {
4622
4948
  var _a3, _b, _c;
@@ -4860,6 +5186,7 @@ function onScroll(ctx, event) {
4860
5186
  state.scrollPending = newScroll;
4861
5187
  updateScroll(ctx, newScroll, insetChanged);
4862
5188
  trackInitialScrollNativeProgress(state, newScroll);
5189
+ clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx);
4863
5190
  if (state.scrollingTo) {
4864
5191
  checkFinishedScroll(ctx);
4865
5192
  }
@@ -4923,6 +5250,43 @@ var ScrollAdjustHandler = class {
4923
5250
  }
4924
5251
  };
4925
5252
 
5253
+ // src/core/updateAnchoredEndSpace.ts
5254
+ function maybeUpdateAnchoredEndSpace(ctx) {
5255
+ var _a3;
5256
+ const state = ctx.state;
5257
+ const anchoredEndSpace = state.props.anchoredEndSpace;
5258
+ const previousSize = peek$(ctx, "anchoredEndSpaceSize");
5259
+ let nextSize = 0;
5260
+ if (anchoredEndSpace) {
5261
+ const { anchorIndex, anchorMaxSize, anchorOffset = 0 } = anchoredEndSpace;
5262
+ const { data } = state.props;
5263
+ if (anchorIndex >= 0 && anchorIndex < data.length && state.scrollLength > 0) {
5264
+ let contentBelowAnchor = 0;
5265
+ const footerSize = ctx.values.get("footerSize") || 0;
5266
+ const stylePaddingBottom = state.props.stylePaddingBottom || 0;
5267
+ for (let index = anchorIndex; index < data.length; index++) {
5268
+ const itemKey = getId(state, index);
5269
+ const size = itemKey ? state.sizesKnown.get(itemKey) : void 0;
5270
+ const effectiveSize = index === anchorIndex && anchorMaxSize !== void 0 ? Math.min(size || 0, Math.max(0, anchorMaxSize)) : size;
5271
+ if (effectiveSize !== null && effectiveSize !== void 0 && effectiveSize > 0) {
5272
+ contentBelowAnchor += effectiveSize;
5273
+ }
5274
+ }
5275
+ contentBelowAnchor += footerSize + stylePaddingBottom;
5276
+ nextSize = Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
5277
+ }
5278
+ }
5279
+ if (previousSize === nextSize) {
5280
+ return nextSize;
5281
+ }
5282
+ set$(ctx, "anchoredEndSpaceSize", nextSize);
5283
+ (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
5284
+ if (anchoredEndSpace == null ? void 0 : anchoredEndSpace.includeInEndInset) {
5285
+ updateScroll(ctx, state.scroll, true);
5286
+ }
5287
+ return nextSize;
5288
+ }
5289
+
4926
5290
  // src/core/updateItemSize.ts
4927
5291
  function runOrScheduleMVCPRecalculate(ctx) {
4928
5292
  const state = ctx.state;
@@ -5004,6 +5368,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5004
5368
  previous: size - diff,
5005
5369
  size
5006
5370
  });
5371
+ maybeUpdateAnchoredEndSpace(ctx);
5007
5372
  }
5008
5373
  if (minIndexSizeChanged !== void 0) {
5009
5374
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
@@ -5127,7 +5492,7 @@ function createImperativeHandle(ctx) {
5127
5492
  const IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS = 800;
5128
5493
  const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
5129
5494
  let imperativeScrollToken = 0;
5130
- const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0 || hasActiveMVCPAnchorLock(state);
5495
+ const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0;
5131
5496
  const runWhenSettled = (token, run) => {
5132
5497
  const startedAt = Date.now();
5133
5498
  let stableFrames = 0;
@@ -5149,9 +5514,10 @@ function createImperativeHandle(ctx) {
5149
5514
  };
5150
5515
  requestAnimationFrame(check);
5151
5516
  };
5152
- const runScrollWithPromise = (run) => new Promise((resolve) => {
5517
+ const runScrollWithPromise = (run, options) => new Promise((resolve) => {
5153
5518
  var _a3;
5154
5519
  const token = ++imperativeScrollToken;
5520
+ const shouldWaitOneFrame = !!(options == null ? void 0 : options.shouldWaitOneFrame);
5155
5521
  (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
5156
5522
  state.pendingScrollResolve = resolve;
5157
5523
  const runNow = () => {
@@ -5166,11 +5532,12 @@ function createImperativeHandle(ctx) {
5166
5532
  resolve();
5167
5533
  }
5168
5534
  };
5535
+ const execute = shouldWaitOneFrame ? () => requestAnimationFrame(runNow) : runNow;
5169
5536
  if (isSettlingAfterDataChange()) {
5170
- runWhenSettled(token, runNow);
5171
- return;
5537
+ runWhenSettled(token, execute);
5538
+ } else {
5539
+ execute();
5172
5540
  }
5173
- runNow();
5174
5541
  });
5175
5542
  const scrollIndexIntoView = (options) => {
5176
5543
  if (state) {
@@ -5227,10 +5594,13 @@ function createImperativeHandle(ctx) {
5227
5594
  },
5228
5595
  end: state.endNoBuffer,
5229
5596
  endBuffered: state.endBuffered,
5230
- isAtEnd: state.isAtEnd,
5231
- isAtStart: state.isAtStart,
5597
+ isAtEnd: peek$(ctx, "isAtEnd"),
5598
+ isAtStart: peek$(ctx, "isAtStart"),
5232
5599
  isEndReached: state.isEndReached,
5600
+ isNearEnd: peek$(ctx, "isNearEnd"),
5601
+ isNearStart: peek$(ctx, "isNearStart"),
5233
5602
  isStartReached: state.isStartReached,
5603
+ isWithinMaintainScrollAtEndThreshold: peek$(ctx, "isWithinMaintainScrollAtEndThreshold"),
5234
5604
  listen: (signalName, cb) => listen$(ctx, signalName, cb),
5235
5605
  listenToPosition: (key, cb) => listenPosition$(ctx, key, cb),
5236
5606
  positionAtIndex: (index) => state.positions[index],
@@ -5277,10 +5647,15 @@ function createImperativeHandle(ctx) {
5277
5647
  }
5278
5648
  return false;
5279
5649
  }),
5280
- scrollToIndex: (params) => runScrollWithPromise(() => {
5281
- scrollToIndex(ctx, params);
5282
- return true;
5283
- }),
5650
+ scrollToIndex: (params) => runScrollWithPromise(
5651
+ () => {
5652
+ scrollToIndex(ctx, params);
5653
+ return true;
5654
+ },
5655
+ {
5656
+ shouldWaitOneFrame: params.index >= 0 && params.index >= state.props.data.length
5657
+ }
5658
+ ),
5284
5659
  scrollToItem: ({ item, ...props }) => runScrollWithPromise(() => {
5285
5660
  const data = state.props.data;
5286
5661
  const index = data.indexOf(item);
@@ -5376,7 +5751,7 @@ function getRenderedItem(ctx, key) {
5376
5751
  item,
5377
5752
  type: getItemType ? (_a3 = getItemType(item, index)) != null ? _a3 : "" : ""
5378
5753
  };
5379
- renderedItem = isFunction(renderItem) ? renderItem(itemProps) : React3__namespace.default.createElement(renderItem, itemProps);
5754
+ renderedItem = React3__namespace.default.createElement(renderItem, itemProps);
5380
5755
  }
5381
5756
  return { index, item: data[index], renderedItem };
5382
5757
  }
@@ -5510,9 +5885,18 @@ var LegendList = typedMemo(
5510
5885
  })
5511
5886
  );
5512
5887
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
5513
- var _a3, _b, _c, _d, _e, _f, _g;
5888
+ var _a3, _b, _c, _d, _e, _f, _g, _h;
5889
+ const noopOnScroll = React3.useCallback((_event) => {
5890
+ }, []);
5891
+ if (props.recycleItems === void 0) {
5892
+ warnDevOnce(
5893
+ "recycleItems-omitted",
5894
+ "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."
5895
+ );
5896
+ }
5514
5897
  const {
5515
5898
  alignItemsAtEnd = false,
5899
+ anchoredEndSpace,
5516
5900
  alwaysRender,
5517
5901
  columnWrapperStyle,
5518
5902
  contentContainerStyle: contentContainerStyleProp,
@@ -5578,7 +5962,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5578
5962
  const positionComponentInternal = props.positionComponentInternal;
5579
5963
  const stickyPositionComponentInternal = props.stickyPositionComponentInternal;
5580
5964
  const {
5581
- childrenMode,
5582
5965
  positionComponentInternal: _positionComponentInternal,
5583
5966
  stickyPositionComponentInternal: _stickyPositionComponentInternal,
5584
5967
  ...restProps
@@ -5642,18 +6025,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5642
6025
  dataVersion,
5643
6026
  keyExtractor
5644
6027
  ]);
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
6028
  const useWindowScrollResolved = !!useWindowScroll && !renderScrollComponent;
5658
6029
  const refState = React3.useRef(void 0);
5659
6030
  const hasOverrideItemLayout = !!overrideItemLayout;
@@ -5662,7 +6033,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5662
6033
  if (!ctx.state) {
5663
6034
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : { height: 0, width: 0 } )[horizontal ? "width" : "height"];
5664
6035
  ctx.state = {
5665
- activeStickyIndex: -1,
5666
6036
  averageSizes: {},
5667
6037
  columnSpans: [],
5668
6038
  columns: [],
@@ -5686,8 +6056,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5686
6056
  kind: initialScrollUsesOffsetOnly ? "offset" : "bootstrap",
5687
6057
  previousDataLength: dataProp.length
5688
6058
  } : void 0,
5689
- isAtEnd: false,
5690
- isAtStart: false,
5691
6059
  isEndReached: null,
5692
6060
  isFirst: true,
5693
6061
  isStartReached: null,
@@ -5698,6 +6066,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5698
6066
  minIndexSizeChanged: 0,
5699
6067
  nativeContentInset: void 0,
5700
6068
  nativeMarginTop: 0,
6069
+ pendingDataComparison: void 0,
5701
6070
  pendingNativeMVCPAdjust: void 0,
5702
6071
  positions: [],
5703
6072
  props: {},
@@ -5736,22 +6105,29 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5736
6105
  }
5737
6106
  const state = refState.current;
5738
6107
  const isFirstLocal = state.isFirst;
5739
- state.didColumnsChange = numColumnsProp !== state.props.numColumns;
6108
+ const previousNumColumnsProp = state.props.numColumns;
6109
+ state.didColumnsChange = numColumnsProp !== previousNumColumnsProp;
5740
6110
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
5741
6111
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
5742
- const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkActualChange(state, dataProp, state.props.data);
6112
+ const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
6113
+ if (didDataChangeLocal && state.didFinishInitialScroll && ((_f = state.initialScroll) == null ? void 0 : _f.viewPosition) === 1 && state.props.data.length > 0) {
6114
+ clearPreservedInitialScrollTarget(state);
6115
+ }
5743
6116
  if (didDataChangeLocal) {
5744
6117
  state.dataChangeEpoch += 1;
5745
6118
  state.dataChangeNeedsScrollUpdate = true;
5746
6119
  state.didDataChange = true;
5747
6120
  state.previousData = state.props.data;
5748
6121
  }
5749
- const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
6122
+ const throttledOnScroll = useThrottledOnScroll(onScrollProp != null ? onScrollProp : noopOnScroll, scrollEventThrottle != null ? scrollEventThrottle : 0);
6123
+ const throttleScrollFn = scrollEventThrottle && onScrollProp ? throttledOnScroll : onScrollProp;
6124
+ const anchoredEndSpaceResolved = anchoredEndSpace ? { ...anchoredEndSpace, includeInEndInset: true } : anchoredEndSpace;
5750
6125
  state.props = {
5751
6126
  alignItemsAtEnd,
5752
6127
  alwaysRender,
5753
6128
  alwaysRenderIndicesArr: alwaysRenderIndices.arr,
5754
6129
  alwaysRenderIndicesSet: alwaysRenderIndices.set,
6130
+ anchoredEndSpace: anchoredEndSpaceResolved,
5755
6131
  animatedProps: animatedPropsInternal,
5756
6132
  contentInset,
5757
6133
  data: dataProp,
@@ -5836,16 +6212,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5836
6212
  if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
5837
6213
  refState.current.lastBatchingAction = Date.now();
5838
6214
  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
6215
  refState.current.sizes.clear();
5844
6216
  refState.current.positions.length = 0;
5845
6217
  refState.current.totalSize = 0;
5846
6218
  set$(ctx, "totalSize", 0);
5847
6219
  }
5848
6220
  }
6221
+ if (IS_DEV) {
6222
+ useDevChecks(props);
6223
+ }
5849
6224
  React3.useLayoutEffect(() => {
5850
6225
  handleInitialScrollDataChange(ctx, {
5851
6226
  dataLength: dataProp.length,
@@ -5855,6 +6230,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5855
6230
  useBootstrapInitialScroll: usesBootstrapInitialScroll
5856
6231
  });
5857
6232
  }, [dataProp.length, didDataChangeLocal, initialScrollAtEnd, stylePaddingBottomState, usesBootstrapInitialScroll]);
6233
+ React3.useLayoutEffect(() => {
6234
+ maybeUpdateAnchoredEndSpace(ctx);
6235
+ }, [
6236
+ ctx,
6237
+ dataProp,
6238
+ dataVersion,
6239
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex,
6240
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorMaxSize,
6241
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorOffset,
6242
+ numColumnsProp
6243
+ ]);
5858
6244
  const onLayoutFooter = React3.useCallback(
5859
6245
  (layout) => {
5860
6246
  if (!usesBootstrapInitialScroll) {
@@ -5870,14 +6256,21 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5870
6256
  [dataProp.length, initialScrollAtEnd, horizontal, stylePaddingBottomState, usesBootstrapInitialScroll]
5871
6257
  );
5872
6258
  const onLayoutChange = React3.useCallback(
5873
- (layout) => {
6259
+ (layout, fromLayoutEffect) => {
6260
+ const previousScrollLength = state.scrollLength;
6261
+ const previousOtherAxisSize = state.otherAxisSize;
5874
6262
  handleLayout(ctx, layout, setCanRender);
6263
+ maybeUpdateAnchoredEndSpace(ctx);
6264
+ const didLayoutAffectBootstrapTarget = previousScrollLength !== state.scrollLength || previousOtherAxisSize !== state.otherAxisSize;
6265
+ if (usesBootstrapInitialScroll && !fromLayoutEffect && didLayoutAffectBootstrapTarget) {
6266
+ handleBootstrapInitialScrollLayoutChange(ctx);
6267
+ }
5875
6268
  if (usesBootstrapInitialScroll) {
5876
6269
  return;
5877
6270
  }
5878
6271
  advanceCurrentInitialScrollSession(ctx);
5879
6272
  },
5880
- [usesBootstrapInitialScroll]
6273
+ [dataProp.length, initialScrollAtEnd, stylePaddingBottomState, usesBootstrapInitialScroll]
5881
6274
  );
5882
6275
  const { onLayout } = useOnLayoutSync({
5883
6276
  onLayoutChange,
@@ -5890,6 +6283,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5890
6283
  updateSnapToOffsets(ctx);
5891
6284
  }
5892
6285
  }, [snapToIndices]);
6286
+ React3.useLayoutEffect(
6287
+ () => initializeStateVars(true),
6288
+ [dataVersion, memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingBottomState, stylePaddingTopState]
6289
+ );
5893
6290
  React3.useLayoutEffect(() => {
5894
6291
  const {
5895
6292
  didColumnsChange,
@@ -5899,7 +6296,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5899
6296
  } = state;
5900
6297
  const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx);
5901
6298
  if (!didAllocateContainers && !isFirst && (didDataChange || didColumnsChange)) {
5902
- checkResetContainers(ctx, data);
6299
+ checkResetContainers(ctx, data, { didColumnsChange });
6300
+ }
6301
+ if (didDataChange) {
6302
+ state.pendingDataComparison = void 0;
5903
6303
  }
5904
6304
  state.didColumnsChange = false;
5905
6305
  state.didDataChange = false;
@@ -5914,10 +6314,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5914
6314
  (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { forceFullItemPositions: true });
5915
6315
  }
5916
6316
  }, [extraData, hasOverrideItemLayout, numColumnsProp]);
5917
- React3.useLayoutEffect(
5918
- () => initializeStateVars(true),
5919
- [dataVersion, memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingBottomState, stylePaddingTopState]
5920
- );
5921
6317
  React3.useEffect(() => {
5922
6318
  if (!onMetricsChange) {
5923
6319
  return;
@@ -5950,15 +6346,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5950
6346
  state.viewabilityConfigCallbackPairs = viewability;
5951
6347
  state.enableScrollForNextCalculateItemsInView = !viewability;
5952
6348
  }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
6349
+ useInit(() => {
6350
+ });
5953
6351
  React3.useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx), []);
5954
- {
5955
- React3.useEffect(() => {
5956
- if (usesBootstrapInitialScroll) {
5957
- return;
5958
- }
5959
- advanceCurrentInitialScrollSession(ctx);
5960
- }, [usesBootstrapInitialScroll]);
5961
- }
6352
+ React3.useEffect(() => {
6353
+ if (usesBootstrapInitialScroll) {
6354
+ return;
6355
+ }
6356
+ advanceCurrentInitialScrollSession(ctx);
6357
+ }, [ctx, usesBootstrapInitialScroll]);
5962
6358
  const fns = React3.useMemo(
5963
6359
  () => ({
5964
6360
  getRenderedItem: (key) => getRenderedItem(ctx, key),
@@ -5996,7 +6392,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5996
6392
  onScroll: onScrollHandler,
5997
6393
  recycleItems,
5998
6394
  refreshControl: refreshControlElement ? stylePaddingTopState > 0 ? React3__namespace.cloneElement(refreshControlElement, {
5999
- progressViewOffset: ((_f = refreshControlElement.props.progressViewOffset) != null ? _f : 0) + stylePaddingTopState
6395
+ progressViewOffset: ((_g = refreshControlElement.props.progressViewOffset) != null ? _g : 0) + stylePaddingTopState
6000
6396
  }) : refreshControlElement : onRefresh && /* @__PURE__ */ React3__namespace.createElement(
6001
6397
  RefreshControl,
6002
6398
  {
@@ -6007,7 +6403,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6007
6403
  ),
6008
6404
  refScrollView: combinedRef,
6009
6405
  renderScrollComponent,
6010
- scrollAdjustHandler: (_g = refState.current) == null ? void 0 : _g.scrollAdjustHandler,
6406
+ scrollAdjustHandler: (_h = refState.current) == null ? void 0 : _h.scrollAdjustHandler,
6011
6407
  scrollEventThrottle: 0,
6012
6408
  snapToIndices,
6013
6409
  stickyHeaderIndices,