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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/react.mjs CHANGED
@@ -13,37 +13,6 @@ var View = forwardRef(function View2(props, ref) {
13
13
  });
14
14
  var Text = View;
15
15
 
16
- // src/state/getContentInsetEnd.ts
17
- function getContentInsetEnd(state) {
18
- var _a3;
19
- const { props } = state;
20
- const horizontal = props.horizontal;
21
- const contentInset = props.contentInset;
22
- const baseInset = contentInset != null ? contentInset : state.nativeContentInset;
23
- const overrideInset = (_a3 = state.contentInsetOverride) != null ? _a3 : void 0;
24
- if (overrideInset) {
25
- const mergedInset = { bottom: 0, right: 0, ...baseInset, ...overrideInset };
26
- return (horizontal ? mergedInset.right : mergedInset.bottom) || 0;
27
- }
28
- if (baseInset) {
29
- return (horizontal ? baseInset.right : baseInset.bottom) || 0;
30
- }
31
- return 0;
32
- }
33
-
34
- // src/state/getContentSize.ts
35
- function getContentSize(ctx) {
36
- var _a3;
37
- const { values, state } = ctx;
38
- const stylePaddingTop = values.get("stylePaddingTop") || 0;
39
- const stylePaddingBottom = state.props.stylePaddingBottom || 0;
40
- const headerSize = values.get("headerSize") || 0;
41
- const footerSize = values.get("footerSize") || 0;
42
- const contentInsetBottom = getContentInsetEnd(state);
43
- const totalSize = (_a3 = state.pendingTotalSize) != null ? _a3 : values.get("totalSize");
44
- return headerSize + footerSize + totalSize + stylePaddingTop + stylePaddingBottom + (contentInsetBottom || 0);
45
- }
46
-
47
16
  // src/platform/Animated.tsx
48
17
  var createAnimatedValue = (value) => value;
49
18
 
@@ -68,6 +37,11 @@ function StateProvider({ children }) {
68
37
  ["headerSize", 0],
69
38
  ["numContainers", 0],
70
39
  ["activeStickyIndex", -1],
40
+ ["isAtEnd", false],
41
+ ["isAtStart", false],
42
+ ["isNearEnd", false],
43
+ ["isNearStart", false],
44
+ ["isWithinMaintainScrollAtEndThreshold", false],
71
45
  ["totalSize", 0],
72
46
  ["scrollAdjustPending", 0]
73
47
  ]),
@@ -159,29 +133,71 @@ function notifyPosition$(ctx, key, value) {
159
133
  function useArr$(signalNames) {
160
134
  const ctx = React3.useContext(ContextState);
161
135
  const { subscribe, get } = React3.useMemo(() => createSelectorFunctionsArr(ctx, signalNames), [ctx, signalNames]);
162
- const value = useSyncExternalStore(subscribe, get);
136
+ const value = useSyncExternalStore(subscribe, get, get);
163
137
  return value;
164
138
  }
165
139
  function useSelector$(signalName, selector) {
166
140
  const ctx = React3.useContext(ContextState);
167
141
  const { subscribe, get } = React3.useMemo(() => createSelectorFunctionsArr(ctx, [signalName]), [ctx, signalName]);
168
- const value = useSyncExternalStore(subscribe, () => selector(get()[0]));
142
+ const getSelectedValue = React3.useCallback(() => selector(get()[0]), [get, selector]);
143
+ const value = useSyncExternalStore(subscribe, getSelectedValue, getSelectedValue);
169
144
  return value;
170
145
  }
171
146
 
147
+ // src/state/getContentInsetEnd.ts
148
+ function getContentInsetEnd(ctx) {
149
+ var _a3, _b;
150
+ const state = ctx.state;
151
+ const { props } = state;
152
+ const horizontal = props.horizontal;
153
+ const contentInset = props.contentInset;
154
+ const baseInset = contentInset != null ? contentInset : state.nativeContentInset;
155
+ const baseEndInset = (horizontal ? baseInset == null ? void 0 : baseInset.right : baseInset == null ? void 0 : baseInset.bottom) || 0;
156
+ const anchoredEndSpaceSize = peek$(ctx, "anchoredEndSpaceSize");
157
+ const anchoredEndInset = ((_a3 = props.anchoredEndSpace) == null ? void 0 : _a3.includeInEndInset) && anchoredEndSpaceSize ? anchoredEndSpaceSize : 0;
158
+ const overrideInset = (_b = state.contentInsetOverride) != null ? _b : void 0;
159
+ if (overrideInset) {
160
+ const mergedInset = { bottom: 0, right: 0, ...baseInset, ...overrideInset };
161
+ return Math.max((horizontal ? mergedInset.right : mergedInset.bottom) || 0, anchoredEndInset);
162
+ }
163
+ return Math.max(baseEndInset, anchoredEndInset);
164
+ }
165
+
166
+ // src/state/getContentSize.ts
167
+ function getContentSize(ctx) {
168
+ var _a3;
169
+ const { values, state } = ctx;
170
+ const stylePaddingTop = values.get("stylePaddingTop") || 0;
171
+ const stylePaddingBottom = state.props.stylePaddingBottom || 0;
172
+ const headerSize = values.get("headerSize") || 0;
173
+ const footerSize = values.get("footerSize") || 0;
174
+ const contentInsetBottom = getContentInsetEnd(ctx);
175
+ const totalSize = (_a3 = state.pendingTotalSize) != null ? _a3 : values.get("totalSize");
176
+ return headerSize + footerSize + totalSize + stylePaddingTop + stylePaddingBottom + (contentInsetBottom || 0);
177
+ }
178
+
172
179
  // src/components/DebugView.tsx
173
180
  var DebugRow = ({ children }) => {
174
181
  return /* @__PURE__ */ React3.createElement(View, { style: { alignItems: "center", flexDirection: "row", justifyContent: "space-between" } }, children);
175
182
  };
176
- React3.memo(function DebugView2({ state }) {
183
+ React3.memo(function DebugView2() {
177
184
  const ctx = useStateContext();
178
- const [totalSize = 0, scrollAdjust = 0, rawScroll = 0, scroll = 0, _numContainers = 0, _numContainersPooled = 0] = useArr$([
185
+ const [
186
+ totalSize = 0,
187
+ scrollAdjust = 0,
188
+ rawScroll = 0,
189
+ scroll = 0,
190
+ _numContainers = 0,
191
+ _numContainersPooled = 0,
192
+ isAtEnd = false
193
+ ] = useArr$([
179
194
  "totalSize",
180
195
  "scrollAdjust",
181
196
  "debugRawScroll",
182
197
  "debugComputedScroll",
183
198
  "numContainers",
184
- "numContainersPooled"
199
+ "numContainersPooled",
200
+ "isAtEnd"
185
201
  ]);
186
202
  const contentSize = getContentSize(ctx);
187
203
  const [, forceUpdate] = useReducer((x) => x + 1, 0);
@@ -206,7 +222,7 @@ React3.memo(function DebugView2({ state }) {
206
222
  },
207
223
  /* @__PURE__ */ React3.createElement(DebugRow, null, /* @__PURE__ */ React3.createElement(Text, null, "TotalSize:"), /* @__PURE__ */ React3.createElement(Text, null, totalSize.toFixed(2))),
208
224
  /* @__PURE__ */ React3.createElement(DebugRow, null, /* @__PURE__ */ React3.createElement(Text, null, "ContentSize:"), /* @__PURE__ */ React3.createElement(Text, null, contentSize.toFixed(2))),
209
- /* @__PURE__ */ React3.createElement(DebugRow, null, /* @__PURE__ */ React3.createElement(Text, null, "At end:"), /* @__PURE__ */ React3.createElement(Text, null, String(state.isAtEnd))),
225
+ /* @__PURE__ */ React3.createElement(DebugRow, null, /* @__PURE__ */ React3.createElement(Text, null, "At end:"), /* @__PURE__ */ React3.createElement(Text, null, String(isAtEnd))),
210
226
  /* @__PURE__ */ React3.createElement(DebugRow, null, /* @__PURE__ */ React3.createElement(Text, null, "ScrollAdjust:"), /* @__PURE__ */ React3.createElement(Text, null, scrollAdjust.toFixed(2))),
211
227
  /* @__PURE__ */ React3.createElement(DebugRow, null, /* @__PURE__ */ React3.createElement(Text, null, "RawScroll: "), /* @__PURE__ */ React3.createElement(Text, null, rawScroll.toFixed(2))),
212
228
  /* @__PURE__ */ React3.createElement(DebugRow, null, /* @__PURE__ */ React3.createElement(Text, null, "ComputedScroll: "), /* @__PURE__ */ React3.createElement(Text, null, scroll.toFixed(2)))
@@ -229,6 +245,7 @@ var IS_DEV = (_a2 = processDev != null ? processDev : metroDev) != null ? _a2 :
229
245
 
230
246
  // src/constants.ts
231
247
  var POSITION_OUT_OF_VIEW = -1e7;
248
+ var EDGE_POSITION_EPSILON = 1;
232
249
  var ENABLE_DEVMODE = IS_DEV && false;
233
250
  var ENABLE_DEBUG_VIEW = IS_DEV && false;
234
251
  var typedForwardRef = React3.forwardRef;
@@ -688,17 +705,20 @@ var Container = typedMemo(function Container2({
688
705
  const { columnGap, rowGap, gap } = columnWrapperStyle;
689
706
  if (horizontal) {
690
707
  paddingStyles = {
708
+ paddingBottom: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0,
691
709
  paddingRight: columnGap || gap || void 0,
692
- paddingVertical: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
710
+ paddingTop: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
693
711
  };
694
712
  } else {
695
713
  paddingStyles = {
696
714
  paddingBottom: rowGap || gap || void 0,
697
- paddingHorizontal: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
715
+ paddingLeft: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0,
716
+ paddingRight: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
698
717
  };
699
718
  }
700
719
  }
701
720
  return horizontal ? {
721
+ boxSizing: paddingStyles ? "border-box" : void 0,
702
722
  flexDirection: ItemSeparatorComponent ? "row" : void 0,
703
723
  height: otherAxisSize,
704
724
  left: 0,
@@ -706,6 +726,7 @@ var Container = typedMemo(function Container2({
706
726
  top: otherAxisPos,
707
727
  ...paddingStyles || {}
708
728
  } : {
729
+ boxSizing: paddingStyles ? "border-box" : void 0,
709
730
  left: otherAxisPos,
710
731
  position: "absolute",
711
732
  right: numColumns > 1 ? null : 0,
@@ -934,9 +955,6 @@ var ContainersInner = typedMemo(function ContainersInner2({ horizontal, numColum
934
955
  style.marginRight = -gapX;
935
956
  }
936
957
  } else {
937
- if (gapX) {
938
- style.marginLeft = style.marginRight = -gapX;
939
- }
940
958
  if (gapY) {
941
959
  style.marginBottom = -gapY;
942
960
  }
@@ -1114,6 +1132,7 @@ function resolveWindowScrollTarget({ clampedOffset, horizontal, listPos, scroll
1114
1132
  var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
1115
1133
  children,
1116
1134
  style,
1135
+ contentContainerClassName,
1117
1136
  contentContainerStyle,
1118
1137
  horizontal = false,
1119
1138
  contentOffset,
@@ -1249,18 +1268,18 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
1249
1268
  const scrollEventCoalescer = useRafCoalescer(emitScroll);
1250
1269
  const handleScroll = useCallback(
1251
1270
  (_event) => {
1252
- var _a3;
1253
1271
  if (!onScroll2) {
1254
1272
  return;
1255
1273
  }
1256
- const scrollingTo = (_a3 = ctx.state) == null ? void 0 : _a3.scrollingTo;
1257
- if (scrollingTo && !scrollingTo.animated) {
1274
+ const state = ctx.state;
1275
+ 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);
1276
+ if (shouldFlushImmediately) {
1258
1277
  scrollEventCoalescer.flush();
1259
1278
  } else {
1260
1279
  scrollEventCoalescer.schedule();
1261
1280
  }
1262
1281
  },
1263
- [onScroll2, scrollEventCoalescer]
1282
+ [ctx.state, onScroll2, scrollEventCoalescer]
1264
1283
  );
1265
1284
  useLayoutEffect(() => {
1266
1285
  const target = getScrollTarget();
@@ -1326,13 +1345,14 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
1326
1345
  ...StyleSheet.flatten(contentContainerStyle)
1327
1346
  };
1328
1347
  const {
1348
+ contentContainerClassName: _contentContainerClassName,
1329
1349
  contentInset: _contentInset,
1330
1350
  scrollEventThrottle: _scrollEventThrottle,
1331
1351
  ScrollComponent: _ScrollComponent,
1332
1352
  useWindowScroll: _useWindowScroll,
1333
1353
  ...webProps
1334
1354
  } = props;
1335
- return /* @__PURE__ */ React3.createElement("div", { ref: scrollRef, ...webProps, style: scrollViewStyle }, refreshControl, /* @__PURE__ */ React3.createElement("div", { ref: contentRef, style: contentStyle }, children));
1355
+ return /* @__PURE__ */ React3.createElement("div", { ref: scrollRef, ...webProps, style: scrollViewStyle }, refreshControl, /* @__PURE__ */ React3.createElement("div", { className: contentContainerClassName, ref: contentRef, style: contentStyle }, children));
1336
1356
  });
1337
1357
  function useValueListener$(key, callback) {
1338
1358
  const ctx = useStateContext();
@@ -1345,6 +1365,21 @@ function useValueListener$(key, callback) {
1345
1365
  }
1346
1366
 
1347
1367
  // src/components/ScrollAdjust.tsx
1368
+ function getScrollAdjustAxis(horizontal) {
1369
+ return horizontal ? {
1370
+ contentSizeKey: "scrollWidth",
1371
+ paddingEndProp: "paddingRight",
1372
+ viewportSizeKey: "clientWidth",
1373
+ x: 1,
1374
+ y: 0
1375
+ } : {
1376
+ contentSizeKey: "scrollHeight",
1377
+ paddingEndProp: "paddingBottom",
1378
+ viewportSizeKey: "clientHeight",
1379
+ x: 0,
1380
+ y: 1
1381
+ };
1382
+ }
1348
1383
  function ScrollAdjust() {
1349
1384
  const ctx = useStateContext();
1350
1385
  const lastScrollOffsetRef = React3.useRef(0);
@@ -1358,32 +1393,34 @@ function ScrollAdjust() {
1358
1393
  if (scrollView && scrollOffset !== lastScrollOffsetRef.current) {
1359
1394
  const scrollDelta = scrollOffset - lastScrollOffsetRef.current;
1360
1395
  if (scrollDelta !== 0) {
1396
+ const axis = getScrollAdjustAxis(!!ctx.state.props.horizontal);
1361
1397
  const contentNode = scrollView.getContentNode();
1362
1398
  const prevScroll = scrollView.getCurrentScrollOffset();
1363
1399
  const el = scrollView.getScrollableNode();
1400
+ const scrollBy = () => scrollView.scrollBy(axis.x * scrollDelta, axis.y * scrollDelta);
1364
1401
  if (!contentNode) {
1365
- scrollView.scrollBy(0, scrollDelta);
1402
+ scrollBy();
1366
1403
  lastScrollOffsetRef.current = scrollOffset;
1367
1404
  return;
1368
1405
  }
1369
- const totalSize = contentNode.scrollHeight;
1370
- const viewportSize = el.clientHeight;
1406
+ const totalSize = contentNode[axis.contentSizeKey];
1407
+ const viewportSize = el[axis.viewportSizeKey];
1371
1408
  const nextScroll = prevScroll + scrollDelta;
1372
1409
  if (scrollDelta > 0 && !ctx.state.adjustingFromInitialMount && totalSize < nextScroll + viewportSize) {
1373
- const paddingBottom = ctx.state.props.stylePaddingBottom || 0;
1410
+ const previousPaddingEnd = contentNode.style[axis.paddingEndProp];
1374
1411
  const pad = (nextScroll + viewportSize - totalSize) * 2;
1375
- contentNode.style.paddingBottom = `${pad}px`;
1412
+ contentNode.style[axis.paddingEndProp] = `${pad}px`;
1376
1413
  void contentNode.offsetHeight;
1377
- scrollView.scrollBy(0, scrollDelta);
1414
+ scrollBy();
1378
1415
  if (resetPaddingRafRef.current !== void 0) {
1379
1416
  cancelAnimationFrame(resetPaddingRafRef.current);
1380
1417
  }
1381
1418
  resetPaddingRafRef.current = requestAnimationFrame(() => {
1382
1419
  resetPaddingRafRef.current = void 0;
1383
- contentNode.style.paddingBottom = paddingBottom ? `${paddingBottom}px` : "0";
1420
+ contentNode.style[axis.paddingEndProp] = previousPaddingEnd;
1384
1421
  });
1385
1422
  } else {
1386
- scrollView.scrollBy(0, scrollDelta);
1423
+ scrollBy();
1387
1424
  }
1388
1425
  }
1389
1426
  lastScrollOffsetRef.current = scrollOffset;
@@ -1397,8 +1434,19 @@ function SnapWrapper({ ScrollComponent, ...props }) {
1397
1434
  const [snapToOffsets] = useArr$(["snapToOffsets"]);
1398
1435
  return /* @__PURE__ */ React3.createElement(ScrollComponent, { ...props, snapToOffsets });
1399
1436
  }
1437
+ function WebAnchoredEndSpace({ horizontal }) {
1438
+ const ctx = useStateContext();
1439
+ const [anchoredEndSpaceSize] = useArr$(["anchoredEndSpaceSize"]);
1440
+ const shouldRenderAnchoredEndSpace = !!ctx.state.props.anchoredEndSpace && (anchoredEndSpaceSize || 0) > 0;
1441
+ if (!shouldRenderAnchoredEndSpace) {
1442
+ return null;
1443
+ }
1444
+ const style = horizontal ? { height: "100%", width: anchoredEndSpaceSize || 0 } : { height: anchoredEndSpaceSize || 0 };
1445
+ return /* @__PURE__ */ React3.createElement("div", { style }, null);
1446
+ }
1400
1447
  var LayoutView = ({ onLayoutChange, refView, children, ...rest }) => {
1401
- const ref = refView != null ? refView : useRef(null);
1448
+ const localRef = useRef(null);
1449
+ const ref = refView != null ? refView : localRef;
1402
1450
  useOnLayoutSync({ onLayoutChange, ref });
1403
1451
  return /* @__PURE__ */ React3.createElement("div", { ...rest, ref }, children);
1404
1452
  };
@@ -1443,12 +1491,14 @@ var ListComponent = typedMemo(function ListComponent2({
1443
1491
  }) {
1444
1492
  const ctx = useStateContext();
1445
1493
  const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
1446
- const ScrollComponent = renderScrollComponent ? useMemo(
1447
- () => React3.forwardRef(
1494
+ const ScrollComponent = useMemo(() => {
1495
+ if (!renderScrollComponent) {
1496
+ return ListComponentScrollView;
1497
+ }
1498
+ return React3.forwardRef(
1448
1499
  (props, ref) => renderScrollComponent({ ...props, ref })
1449
- ),
1450
- [renderScrollComponent]
1451
- ) : ListComponentScrollView;
1500
+ );
1501
+ }, [renderScrollComponent]);
1452
1502
  const SnapOrScroll = snapToIndices ? SnapWrapper : ScrollComponent;
1453
1503
  useLayoutEffect(() => {
1454
1504
  if (!ListHeaderComponent) {
@@ -1508,171 +1558,80 @@ var ListComponent = typedMemo(function ListComponent2({
1508
1558
  }
1509
1559
  ),
1510
1560
  ListFooterComponent && /* @__PURE__ */ React3.createElement(LayoutView, { onLayoutChange: onLayoutFooterInternal, style: ListFooterComponentStyle }, getComponent(ListFooterComponent)),
1561
+ /* @__PURE__ */ React3.createElement(WebAnchoredEndSpace, { horizontal }),
1511
1562
  IS_DEV && ENABLE_DEVMODE
1512
1563
  );
1513
1564
  });
1514
-
1515
- // src/core/calculateOffsetForIndex.ts
1516
- function calculateOffsetForIndex(ctx, index) {
1517
- const state = ctx.state;
1518
- return index !== void 0 ? state.positions[index] || 0 : 0;
1519
- }
1520
-
1521
- // src/core/getTopOffsetAdjustment.ts
1522
- function getTopOffsetAdjustment(ctx) {
1523
- return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
1524
- }
1525
-
1526
- // src/utils/getId.ts
1527
- function getId(state, index) {
1528
- const { data, keyExtractor } = state.props;
1529
- if (!data) {
1530
- return "";
1531
- }
1532
- const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
1533
- const id = ret;
1534
- state.idCache[index] = id;
1535
- return id;
1536
- }
1537
-
1538
- // src/core/addTotalSize.ts
1539
- function addTotalSize(ctx, key, add) {
1540
- const state = ctx.state;
1541
- const prevTotalSize = state.totalSize;
1542
- let totalSize = state.totalSize;
1543
- if (key === null) {
1544
- totalSize = add;
1545
- if (state.timeoutSetPaddingTop) {
1546
- clearTimeout(state.timeoutSetPaddingTop);
1547
- state.timeoutSetPaddingTop = void 0;
1548
- }
1549
- } else {
1550
- totalSize += add;
1551
- }
1552
- if (prevTotalSize !== totalSize) {
1553
- {
1554
- state.pendingTotalSize = void 0;
1555
- state.totalSize = totalSize;
1556
- set$(ctx, "totalSize", totalSize);
1557
- }
1558
- }
1559
- }
1560
-
1561
- // src/core/setSize.ts
1562
- function setSize(ctx, itemKey, size) {
1563
- const state = ctx.state;
1564
- const { sizes } = state;
1565
- const previousSize = sizes.get(itemKey);
1566
- const diff = previousSize !== void 0 ? size - previousSize : size;
1567
- if (diff !== 0) {
1568
- addTotalSize(ctx, itemKey, diff);
1569
- }
1570
- sizes.set(itemKey, size);
1571
- }
1572
-
1573
- // src/utils/getItemSize.ts
1574
- function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
1575
- var _a3, _b;
1576
- const state = ctx.state;
1577
- const {
1578
- sizesKnown,
1579
- sizes,
1580
- averageSizes,
1581
- props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType },
1582
- scrollingTo
1583
- } = state;
1584
- const sizeKnown = sizesKnown.get(key);
1585
- if (sizeKnown !== void 0) {
1586
- return sizeKnown;
1587
- }
1588
- let size;
1589
- if (preferCachedSize) {
1590
- const cachedSize = sizes.get(key);
1591
- if (cachedSize !== void 0) {
1592
- return cachedSize;
1593
- }
1594
- }
1595
- const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1596
- if (getFixedItemSize) {
1597
- size = getFixedItemSize(data, index, itemType);
1598
- if (size !== void 0) {
1599
- sizesKnown.set(key, size);
1600
- }
1601
- }
1602
- if (size === void 0 && useAverageSize && sizeKnown === void 0 && !scrollingTo) {
1603
- const averageSizeForType = (_b = averageSizes[itemType]) == null ? void 0 : _b.avg;
1604
- if (averageSizeForType !== void 0) {
1605
- size = roundSize(averageSizeForType);
1565
+ var WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH = 100;
1566
+ var WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO = 0.9;
1567
+ var WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO = 0.9;
1568
+ function useDevChecksImpl(props) {
1569
+ const ctx = useStateContext();
1570
+ const { childrenMode, keyExtractor, renderScrollComponent, stickyHeaderIndices, stickyIndices, useWindowScroll } = props;
1571
+ useEffect(() => {
1572
+ if (stickyIndices && !stickyHeaderIndices) {
1573
+ warnDevOnce(
1574
+ "stickyIndices",
1575
+ "stickyIndices has been renamed to stickyHeaderIndices. Please update your props to use stickyHeaderIndices."
1576
+ );
1606
1577
  }
1607
- }
1608
- if (size === void 0) {
1609
- size = sizes.get(key);
1610
- if (size !== void 0) {
1611
- return size;
1578
+ }, [stickyHeaderIndices, stickyIndices]);
1579
+ useEffect(() => {
1580
+ if (useWindowScroll && renderScrollComponent) {
1581
+ warnDevOnce(
1582
+ "useWindowScrollRenderScrollComponent",
1583
+ "useWindowScroll is not supported when renderScrollComponent is provided."
1584
+ );
1612
1585
  }
1613
- }
1614
- if (size === void 0) {
1615
- size = getEstimatedItemSize ? getEstimatedItemSize(data, index, itemType) : estimatedItemSize;
1616
- }
1617
- setSize(ctx, key, size);
1618
- return size;
1619
- }
1620
- function getItemSizeAtIndex(ctx, index) {
1621
- if (index === void 0 || index < 0) {
1622
- return void 0;
1623
- }
1624
- const targetId = getId(ctx.state, index);
1625
- return getItemSize(ctx, targetId, index, ctx.state.props.data[index]);
1626
- }
1627
-
1628
- // src/core/calculateOffsetWithOffsetPosition.ts
1629
- function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1630
- var _a3;
1631
- const state = ctx.state;
1632
- const { index, viewOffset, viewPosition } = params;
1633
- let offset = offsetParam;
1634
- if (viewOffset) {
1635
- offset -= viewOffset;
1636
- }
1637
- if (index !== void 0) {
1638
- const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
1639
- if (topOffsetAdjustment) {
1640
- offset += topOffsetAdjustment;
1586
+ }, [renderScrollComponent, useWindowScroll]);
1587
+ useEffect(() => {
1588
+ if (!keyExtractor && !ctx.state.isFirst && ctx.state.didDataChange && !childrenMode) {
1589
+ warnDevOnce(
1590
+ "keyExtractor",
1591
+ "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."
1592
+ );
1641
1593
  }
1642
- }
1643
- if (viewPosition !== void 0 && index !== void 0) {
1594
+ }, [childrenMode, ctx, keyExtractor]);
1595
+ useEffect(() => {
1596
+ const state = ctx.state;
1644
1597
  const dataLength = state.props.data.length;
1645
- if (dataLength === 0) {
1646
- return offset;
1647
- }
1648
- const isOutOfBounds = index < 0 || index >= dataLength;
1649
- const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
1650
- const itemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1651
- const trailingInset = getContentInsetEnd(state);
1652
- offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1653
- if (!isOutOfBounds && index === state.props.data.length - 1) {
1654
- const footerSize = peek$(ctx, "footerSize") || 0;
1655
- offset += footerSize;
1598
+ const useWindowScrollResolved = state.props.useWindowScroll;
1599
+ if (useWindowScrollResolved || dataLength < WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH) {
1600
+ return;
1656
1601
  }
1657
- }
1658
- return offset;
1602
+ const warnIfUnboundedOuterSize = () => {
1603
+ const readyToRender = peek$(ctx, "readyToRender");
1604
+ const numContainers = peek$(ctx, "numContainers") || 0;
1605
+ const totalSize = peek$(ctx, "totalSize") || 0;
1606
+ const scrollLength = ctx.state.scrollLength || 0;
1607
+ if (!readyToRender || totalSize <= 0 || scrollLength <= 0) {
1608
+ return;
1609
+ }
1610
+ const rendersAlmostEverything = numContainers >= Math.ceil(dataLength * WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO);
1611
+ const viewportMatchesContent = scrollLength >= totalSize * WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO;
1612
+ if (rendersAlmostEverything && viewportMatchesContent) {
1613
+ warnDevOnce(
1614
+ "webUnboundedOuterSize",
1615
+ "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."
1616
+ );
1617
+ }
1618
+ };
1619
+ warnIfUnboundedOuterSize();
1620
+ const unsubscribe = [
1621
+ listen$(ctx, "numContainers", warnIfUnboundedOuterSize),
1622
+ listen$(ctx, "readyToRender", warnIfUnboundedOuterSize),
1623
+ listen$(ctx, "totalSize", warnIfUnboundedOuterSize)
1624
+ ];
1625
+ return () => {
1626
+ for (const unsub of unsubscribe) {
1627
+ unsub();
1628
+ }
1629
+ };
1630
+ }, [ctx]);
1659
1631
  }
1660
-
1661
- // src/core/clampScrollOffset.ts
1662
- function clampScrollOffset(ctx, offset, scrollTarget) {
1663
- const state = ctx.state;
1664
- const contentSize = getContentSize(ctx);
1665
- let clampedOffset = offset;
1666
- if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform.OS !== "android")) {
1667
- const baseMaxOffset = Math.max(0, contentSize - state.scrollLength);
1668
- const viewOffset = scrollTarget == null ? void 0 : scrollTarget.viewOffset;
1669
- const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
1670
- const maxOffset = baseMaxOffset + extraEndOffset;
1671
- clampedOffset = Math.min(offset, maxOffset);
1672
- }
1673
- clampedOffset = Math.max(0, clampedOffset);
1674
- return clampedOffset;
1632
+ function useDevChecksNoop(_props) {
1675
1633
  }
1634
+ var useDevChecks = IS_DEV ? useDevChecksImpl : useDevChecksNoop;
1676
1635
 
1677
1636
  // src/core/deferredPublicOnScroll.ts
1678
1637
  function withResolvedContentOffset(state, event, resolvedOffset) {
@@ -1820,33 +1779,462 @@ function setInitialScrollSession(state, options = {}) {
1820
1779
  return state.initialScrollSession;
1821
1780
  }
1822
1781
 
1823
- // src/core/finishScrollTo.ts
1824
- function finishScrollTo(ctx) {
1825
- var _a3, _b;
1826
- const state = ctx.state;
1827
- if (state == null ? void 0 : state.scrollingTo) {
1828
- const resolvePendingScroll = state.pendingScrollResolve;
1829
- state.pendingScrollResolve = void 0;
1830
- const scrollingTo = state.scrollingTo;
1831
- state.scrollHistory.length = 0;
1832
- state.scrollingTo = void 0;
1833
- if (state.pendingTotalSize !== void 0) {
1834
- addTotalSize(ctx, null, state.pendingTotalSize);
1835
- }
1836
- {
1837
- state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
1838
- }
1839
- if (scrollingTo.isInitialScroll || state.initialScroll) {
1840
- const isOffsetSession = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
1841
- finishInitialScroll(ctx, {
1842
- onFinished: resolvePendingScroll,
1843
- preserveTarget: isOffsetSession && state.props.data.length === 0 || !!scrollingTo.isInitialScroll && !!((_b = state.initialScroll) == null ? void 0 : _b.preserveForFooterLayout),
1782
+ // src/utils/checkThreshold.ts
1783
+ var HYSTERESIS_MULTIPLIER = 1.3;
1784
+ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot, allowReentryOnChange) => {
1785
+ const absDistance = Math.abs(distance);
1786
+ const within = atThreshold || threshold > 0 && absDistance <= threshold;
1787
+ const updateSnapshot = () => {
1788
+ setSnapshot({
1789
+ atThreshold,
1790
+ contentSize: context.contentSize,
1791
+ dataLength: context.dataLength,
1792
+ scrollPosition: context.scrollPosition
1793
+ });
1794
+ };
1795
+ if (!wasReached) {
1796
+ if (!within) {
1797
+ return false;
1798
+ }
1799
+ onReached(distance);
1800
+ updateSnapshot();
1801
+ return true;
1802
+ }
1803
+ const reset = !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
1804
+ if (reset) {
1805
+ setSnapshot(void 0);
1806
+ return false;
1807
+ }
1808
+ if (within) {
1809
+ const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
1810
+ if (changed) {
1811
+ if (allowReentryOnChange) {
1812
+ onReached(distance);
1813
+ }
1814
+ updateSnapshot();
1815
+ }
1816
+ }
1817
+ return true;
1818
+ };
1819
+
1820
+ // src/utils/hasActiveInitialScroll.ts
1821
+ function hasActiveInitialScroll(state) {
1822
+ return !!(state == null ? void 0 : state.initialScroll) && !state.didFinishInitialScroll;
1823
+ }
1824
+
1825
+ // src/utils/checkAtBottom.ts
1826
+ function checkAtBottom(ctx) {
1827
+ var _a3;
1828
+ const state = ctx.state;
1829
+ if (!state) {
1830
+ return;
1831
+ }
1832
+ const {
1833
+ queuedInitialLayout,
1834
+ scrollLength,
1835
+ scroll,
1836
+ maintainingScrollAtEnd,
1837
+ props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
1838
+ } = state;
1839
+ const contentSize = getContentSize(ctx);
1840
+ if (contentSize > 0 && queuedInitialLayout) {
1841
+ const insetEnd = getContentInsetEnd(ctx);
1842
+ const distanceFromEnd = contentSize - scroll - scrollLength - insetEnd;
1843
+ const isContentLess = contentSize < scrollLength;
1844
+ set$(ctx, "isAtEnd", isContentLess || distanceFromEnd <= EDGE_POSITION_EPSILON);
1845
+ set$(ctx, "isNearEnd", isContentLess || distanceFromEnd <= onEndReachedThreshold * scrollLength);
1846
+ set$(
1847
+ ctx,
1848
+ "isWithinMaintainScrollAtEndThreshold",
1849
+ isContentLess || distanceFromEnd <= maintainScrollAtEndThreshold * scrollLength
1850
+ );
1851
+ const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || maintainingScrollAtEnd;
1852
+ if (!shouldSkipThresholdChecks) {
1853
+ state.isEndReached = checkThreshold(
1854
+ distanceFromEnd,
1855
+ isContentLess,
1856
+ onEndReachedThreshold * scrollLength,
1857
+ state.isEndReached,
1858
+ state.endReachedSnapshot,
1859
+ {
1860
+ contentSize,
1861
+ dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1862
+ scrollPosition: scroll
1863
+ },
1864
+ (distance) => {
1865
+ var _a4, _b;
1866
+ return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
1867
+ },
1868
+ (snapshot) => {
1869
+ state.endReachedSnapshot = snapshot;
1870
+ },
1871
+ true
1872
+ );
1873
+ }
1874
+ }
1875
+ }
1876
+
1877
+ // src/utils/checkAtTop.ts
1878
+ function checkAtTop(ctx) {
1879
+ const state = ctx == null ? void 0 : ctx.state;
1880
+ if (!state) {
1881
+ return;
1882
+ }
1883
+ const {
1884
+ dataChangeEpoch,
1885
+ isStartReached,
1886
+ props: { data, onStartReachedThreshold },
1887
+ scroll,
1888
+ scrollLength,
1889
+ startReachedSnapshot,
1890
+ startReachedSnapshotDataChangeEpoch,
1891
+ totalSize
1892
+ } = state;
1893
+ const dataLength = data.length;
1894
+ const threshold = onStartReachedThreshold * scrollLength;
1895
+ const dataChanged = startReachedSnapshotDataChangeEpoch !== dataChangeEpoch;
1896
+ const withinThreshold = threshold > 0 && Math.abs(scroll) <= threshold;
1897
+ const allowReentryOnDataChange = !!isStartReached && withinThreshold && !!dataChanged && !isInMVCPActiveMode(state);
1898
+ if (isStartReached && threshold > 0 && scroll > threshold && startReachedSnapshot && (dataChanged || startReachedSnapshot.contentSize !== totalSize || startReachedSnapshot.dataLength !== dataLength)) {
1899
+ state.isStartReached = false;
1900
+ state.startReachedSnapshot = void 0;
1901
+ state.startReachedSnapshotDataChangeEpoch = void 0;
1902
+ }
1903
+ set$(ctx, "isAtStart", scroll <= EDGE_POSITION_EPSILON);
1904
+ set$(ctx, "isNearStart", scroll <= threshold);
1905
+ const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || !!state.scrollingTo;
1906
+ const shouldDeferDataChangeRefire = isStartReached && withinThreshold && dataChanged && !allowReentryOnDataChange;
1907
+ if (!shouldSkipThresholdChecks && !shouldDeferDataChangeRefire) {
1908
+ state.isStartReached = checkThreshold(
1909
+ scroll,
1910
+ false,
1911
+ threshold,
1912
+ state.isStartReached,
1913
+ allowReentryOnDataChange ? void 0 : startReachedSnapshot,
1914
+ {
1915
+ contentSize: totalSize,
1916
+ dataLength,
1917
+ scrollPosition: scroll
1918
+ },
1919
+ (distance) => {
1920
+ var _a3, _b;
1921
+ return (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
1922
+ },
1923
+ (snapshot) => {
1924
+ state.startReachedSnapshot = snapshot;
1925
+ state.startReachedSnapshotDataChangeEpoch = snapshot ? dataChangeEpoch : void 0;
1926
+ },
1927
+ allowReentryOnDataChange
1928
+ );
1929
+ }
1930
+ }
1931
+
1932
+ // src/utils/checkThresholds.ts
1933
+ function checkThresholds(ctx) {
1934
+ checkAtBottom(ctx);
1935
+ checkAtTop(ctx);
1936
+ }
1937
+
1938
+ // src/core/recalculateSettledScroll.ts
1939
+ function recalculateSettledScroll(ctx) {
1940
+ var _a3, _b;
1941
+ const state = ctx.state;
1942
+ if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1943
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1944
+ }
1945
+ checkThresholds(ctx);
1946
+ }
1947
+
1948
+ // src/utils/setInitialRenderState.ts
1949
+ function setInitialRenderState(ctx, {
1950
+ didLayout,
1951
+ didInitialScroll
1952
+ }) {
1953
+ const { state } = ctx;
1954
+ const {
1955
+ loadStartTime,
1956
+ props: { onLoad }
1957
+ } = state;
1958
+ if (didLayout) {
1959
+ state.didContainersLayout = true;
1960
+ }
1961
+ if (didInitialScroll) {
1962
+ state.didFinishInitialScroll = true;
1963
+ }
1964
+ const isReadyToRender = Boolean(state.didContainersLayout && state.didFinishInitialScroll);
1965
+ if (isReadyToRender && !peek$(ctx, "readyToRender")) {
1966
+ set$(ctx, "readyToRender", true);
1967
+ if (onLoad) {
1968
+ onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1969
+ }
1970
+ }
1971
+ }
1972
+
1973
+ // src/core/finishInitialScroll.ts
1974
+ var PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS = 2e3;
1975
+ function syncInitialScrollOffset(state, offset) {
1976
+ state.scroll = offset;
1977
+ state.scrollPending = offset;
1978
+ state.scrollPrev = offset;
1979
+ }
1980
+ function clearPreservedInitialScrollTargetTimeout(state) {
1981
+ if (state.timeoutPreservedInitialScrollClear !== void 0) {
1982
+ clearTimeout(state.timeoutPreservedInitialScrollClear);
1983
+ state.timeoutPreservedInitialScrollClear = void 0;
1984
+ }
1985
+ }
1986
+ function clearPreservedInitialScrollTarget(state) {
1987
+ clearPreservedInitialScrollTargetTimeout(state);
1988
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
1989
+ state.initialScroll = void 0;
1990
+ setInitialScrollSession(state);
1991
+ }
1992
+ function finishInitialScroll(ctx, options) {
1993
+ var _a3, _b, _c;
1994
+ const state = ctx.state;
1995
+ if ((options == null ? void 0 : options.resolvedOffset) !== void 0) {
1996
+ syncInitialScrollOffset(state, options.resolvedOffset);
1997
+ } else if ((options == null ? void 0 : options.syncObservedOffset) && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
1998
+ const observedOffset = (_c = (_b = state.refScroller.current) == null ? void 0 : _b.getCurrentScrollOffset) == null ? void 0 : _c.call(_b);
1999
+ if (typeof observedOffset === "number" && Number.isFinite(observedOffset)) {
2000
+ syncInitialScrollOffset(state, observedOffset);
2001
+ }
2002
+ }
2003
+ const complete = () => {
2004
+ var _a4, _b2, _c2, _d, _e;
2005
+ const shouldReleaseDeferredPublicOnScroll = ((_a4 = state.initialScrollSession) == null ? void 0 : _a4.kind) === "bootstrap";
2006
+ const finalScrollOffset = (_d = (_c2 = (_b2 = options == null ? void 0 : options.resolvedOffset) != null ? _b2 : state.scrollPending) != null ? _c2 : state.scroll) != null ? _d : 0;
2007
+ initialScrollWatchdog.clear(state);
2008
+ if ((options == null ? void 0 : options.preserveTarget) && state.initialScroll) {
2009
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
2010
+ setInitialScrollSession(state);
2011
+ clearPreservedInitialScrollTargetTimeout(state);
2012
+ if (options == null ? void 0 : options.schedulePreservedTargetClear) {
2013
+ state.timeoutPreservedInitialScrollClear = setTimeout(() => {
2014
+ var _a5;
2015
+ state.timeoutPreservedInitialScrollClear = void 0;
2016
+ if (!state.didFinishInitialScroll || ((_a5 = state.scrollingTo) == null ? void 0 : _a5.isInitialScroll) || !state.initialScroll) {
2017
+ return;
2018
+ }
2019
+ clearPreservedInitialScrollTarget(state);
2020
+ }, PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS);
2021
+ }
2022
+ } else {
2023
+ clearPreservedInitialScrollTarget(state);
2024
+ }
2025
+ if (options == null ? void 0 : options.recalculateItems) {
2026
+ recalculateSettledScroll(ctx);
2027
+ }
2028
+ setInitialRenderState(ctx, { didInitialScroll: true });
2029
+ if (shouldReleaseDeferredPublicOnScroll) {
2030
+ releaseDeferredPublicOnScroll(ctx, finalScrollOffset);
2031
+ }
2032
+ (_e = options == null ? void 0 : options.onFinished) == null ? void 0 : _e.call(options);
2033
+ };
2034
+ if (options == null ? void 0 : options.waitForCompletionFrame) {
2035
+ requestAnimationFrame(complete);
2036
+ return;
2037
+ }
2038
+ complete();
2039
+ }
2040
+
2041
+ // src/core/calculateOffsetForIndex.ts
2042
+ function calculateOffsetForIndex(ctx, index) {
2043
+ const state = ctx.state;
2044
+ return index !== void 0 ? state.positions[index] || 0 : 0;
2045
+ }
2046
+
2047
+ // src/core/getTopOffsetAdjustment.ts
2048
+ function getTopOffsetAdjustment(ctx) {
2049
+ return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
2050
+ }
2051
+
2052
+ // src/utils/getId.ts
2053
+ function getId(state, index) {
2054
+ const { data, keyExtractor } = state.props;
2055
+ if (!data) {
2056
+ return "";
2057
+ }
2058
+ const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
2059
+ const id = ret;
2060
+ state.idCache[index] = id;
2061
+ return id;
2062
+ }
2063
+
2064
+ // src/core/addTotalSize.ts
2065
+ function addTotalSize(ctx, key, add) {
2066
+ const state = ctx.state;
2067
+ const prevTotalSize = state.totalSize;
2068
+ let totalSize = state.totalSize;
2069
+ if (key === null) {
2070
+ totalSize = add;
2071
+ if (state.timeoutSetPaddingTop) {
2072
+ clearTimeout(state.timeoutSetPaddingTop);
2073
+ state.timeoutSetPaddingTop = void 0;
2074
+ }
2075
+ } else {
2076
+ totalSize += add;
2077
+ }
2078
+ if (prevTotalSize !== totalSize) {
2079
+ {
2080
+ state.pendingTotalSize = void 0;
2081
+ state.totalSize = totalSize;
2082
+ set$(ctx, "totalSize", totalSize);
2083
+ }
2084
+ }
2085
+ }
2086
+
2087
+ // src/core/setSize.ts
2088
+ function setSize(ctx, itemKey, size) {
2089
+ const state = ctx.state;
2090
+ const { sizes } = state;
2091
+ const previousSize = sizes.get(itemKey);
2092
+ const diff = previousSize !== void 0 ? size - previousSize : size;
2093
+ if (diff !== 0) {
2094
+ addTotalSize(ctx, itemKey, diff);
2095
+ }
2096
+ sizes.set(itemKey, size);
2097
+ }
2098
+
2099
+ // src/utils/getItemSize.ts
2100
+ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
2101
+ var _a3, _b, _c;
2102
+ const state = ctx.state;
2103
+ const {
2104
+ sizesKnown,
2105
+ sizes,
2106
+ averageSizes,
2107
+ props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType },
2108
+ scrollingTo
2109
+ } = state;
2110
+ const sizeKnown = sizesKnown.get(key);
2111
+ if (sizeKnown !== void 0) {
2112
+ return sizeKnown;
2113
+ }
2114
+ let size;
2115
+ const renderedSize = sizes.get(key);
2116
+ if (preferCachedSize) {
2117
+ if (renderedSize !== void 0) {
2118
+ return renderedSize;
2119
+ }
2120
+ }
2121
+ const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
2122
+ if (getFixedItemSize) {
2123
+ size = getFixedItemSize(data, index, itemType);
2124
+ if (size !== void 0) {
2125
+ sizesKnown.set(key, size);
2126
+ }
2127
+ }
2128
+ if (size === void 0 && useAverageSize && sizeKnown === void 0 && !scrollingTo) {
2129
+ const averageSizeForType = (_b = averageSizes[itemType]) == null ? void 0 : _b.avg;
2130
+ if (averageSizeForType !== void 0) {
2131
+ size = roundSize(averageSizeForType);
2132
+ }
2133
+ }
2134
+ if (size === void 0 && renderedSize !== void 0) {
2135
+ return renderedSize;
2136
+ }
2137
+ if (size === void 0 && useAverageSize && sizeKnown === void 0 && scrollingTo) {
2138
+ const averageSizeForType = (_c = scrollingTo.averageSizeSnapshot) == null ? void 0 : _c[itemType];
2139
+ if (averageSizeForType !== void 0) {
2140
+ size = roundSize(averageSizeForType);
2141
+ }
2142
+ }
2143
+ if (size === void 0) {
2144
+ size = getEstimatedItemSize ? getEstimatedItemSize(data, index, itemType) : estimatedItemSize;
2145
+ }
2146
+ setSize(ctx, key, size);
2147
+ return size;
2148
+ }
2149
+ function getItemSizeAtIndex(ctx, index) {
2150
+ if (index === void 0 || index < 0) {
2151
+ return void 0;
2152
+ }
2153
+ const targetId = getId(ctx.state, index);
2154
+ return getItemSize(ctx, targetId, index, ctx.state.props.data[index]);
2155
+ }
2156
+
2157
+ // src/core/calculateOffsetWithOffsetPosition.ts
2158
+ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
2159
+ var _a3;
2160
+ const state = ctx.state;
2161
+ const { index, viewOffset, viewPosition } = params;
2162
+ let offset = offsetParam;
2163
+ if (viewOffset) {
2164
+ offset -= viewOffset;
2165
+ }
2166
+ if (index !== void 0) {
2167
+ const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
2168
+ if (topOffsetAdjustment) {
2169
+ offset += topOffsetAdjustment;
2170
+ }
2171
+ }
2172
+ if (viewPosition !== void 0 && index !== void 0) {
2173
+ const dataLength = state.props.data.length;
2174
+ if (dataLength === 0) {
2175
+ return offset;
2176
+ }
2177
+ const isOutOfBounds = index < 0 || index >= dataLength;
2178
+ const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
2179
+ const itemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
2180
+ const trailingInset = getContentInsetEnd(ctx);
2181
+ offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
2182
+ if (!isOutOfBounds && index === state.props.data.length - 1) {
2183
+ const footerSize = peek$(ctx, "footerSize") || 0;
2184
+ offset += footerSize;
2185
+ }
2186
+ }
2187
+ return offset;
2188
+ }
2189
+
2190
+ // src/core/clampScrollOffset.ts
2191
+ function clampScrollOffset(ctx, offset, scrollTarget) {
2192
+ const state = ctx.state;
2193
+ const contentSize = getContentSize(ctx);
2194
+ let clampedOffset = offset;
2195
+ if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform.OS !== "android")) {
2196
+ const baseMaxOffset = Math.max(0, contentSize - state.scrollLength);
2197
+ const viewOffset = scrollTarget == null ? void 0 : scrollTarget.viewOffset;
2198
+ const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
2199
+ const maxOffset = baseMaxOffset + extraEndOffset;
2200
+ clampedOffset = Math.min(offset, maxOffset);
2201
+ }
2202
+ clampedOffset = Math.max(0, clampedOffset);
2203
+ return clampedOffset;
2204
+ }
2205
+
2206
+ // src/core/finishScrollTo.ts
2207
+ function finishScrollTo(ctx) {
2208
+ var _a3, _b;
2209
+ const state = ctx.state;
2210
+ if (state == null ? void 0 : state.scrollingTo) {
2211
+ const resolvePendingScroll = state.pendingScrollResolve;
2212
+ state.pendingScrollResolve = void 0;
2213
+ const scrollingTo = state.scrollingTo;
2214
+ state.scrollHistory.length = 0;
2215
+ state.scrollingTo = void 0;
2216
+ if (state.pendingTotalSize !== void 0) {
2217
+ addTotalSize(ctx, null, state.pendingTotalSize);
2218
+ }
2219
+ {
2220
+ state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
2221
+ }
2222
+ if (scrollingTo.isInitialScroll || state.initialScroll) {
2223
+ const isOffsetSession = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
2224
+ const shouldPreserveResizeTarget = !!scrollingTo.isInitialScroll && !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_b = state.initialScroll) == null ? void 0 : _b.viewPosition) === 1;
2225
+ finishInitialScroll(ctx, {
2226
+ onFinished: () => {
2227
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
2228
+ },
2229
+ preserveTarget: isOffsetSession && state.props.data.length === 0 || shouldPreserveResizeTarget,
1844
2230
  recalculateItems: true,
2231
+ schedulePreservedTargetClear: shouldPreserveResizeTarget,
1845
2232
  syncObservedOffset: isOffsetSession,
1846
2233
  waitForCompletionFrame: !!scrollingTo.waitForInitialScrollCompletionFrame
1847
2234
  });
1848
2235
  return;
1849
2236
  }
2237
+ recalculateSettledScroll(ctx);
1850
2238
  resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1851
2239
  }
1852
2240
  }
@@ -1857,6 +2245,7 @@ var SCROLL_END_MAX_MS = 1500;
1857
2245
  var SMOOTH_SCROLL_DURATION_MS = 320;
1858
2246
  var SCROLL_END_TARGET_EPSILON = 1;
1859
2247
  function doScrollTo(ctx, params) {
2248
+ var _a3, _b;
1860
2249
  const state = ctx.state;
1861
2250
  const { animated, horizontal, offset } = params;
1862
2251
  const scroller = state.refScroller.current;
@@ -1870,7 +2259,7 @@ function doScrollTo(ctx, params) {
1870
2259
  const top = isHorizontal ? 0 : offset;
1871
2260
  scroller.scrollTo({ animated: isAnimated, x: left, y: top });
1872
2261
  if (isAnimated) {
1873
- const target = scroller.getScrollEventTarget();
2262
+ const target = (_b = (_a3 = scroller.getScrollEventTarget) == null ? void 0 : _a3.call(scroller)) != null ? _b : null;
1874
2263
  listenForScrollEnd(ctx, {
1875
2264
  readOffset: () => scroller.getCurrentScrollOffset(),
1876
2265
  target,
@@ -1936,6 +2325,17 @@ function listenForScrollEnd(ctx, params) {
1936
2325
  }
1937
2326
 
1938
2327
  // src/core/scrollTo.ts
2328
+ function getAverageSizeSnapshot(state) {
2329
+ if (Object.keys(state.averageSizes).length === 0) {
2330
+ return void 0;
2331
+ }
2332
+ const snapshot = {};
2333
+ for (const itemType in state.averageSizes) {
2334
+ const averages = state.averageSizes[itemType];
2335
+ snapshot[itemType] = averages.avg;
2336
+ }
2337
+ return snapshot;
2338
+ }
1939
2339
  function syncInitialScrollNativeWatchdog(state, options) {
1940
2340
  var _a3;
1941
2341
  const { isInitialScroll, requestedOffset, targetOffset } = options;
@@ -1983,8 +2383,10 @@ function scrollTo(ctx, params) {
1983
2383
  if (isInitialScroll) {
1984
2384
  initialScrollCompletion.resetFlags(state);
1985
2385
  }
2386
+ const averageSizeSnapshot = getAverageSizeSnapshot(state);
1986
2387
  state.scrollingTo = {
1987
2388
  ...scrollTarget,
2389
+ ...averageSizeSnapshot ? { averageSizeSnapshot } : {},
1988
2390
  targetOffset,
1989
2391
  waitForInitialScrollCompletionFrame
1990
2392
  };
@@ -2041,180 +2443,7 @@ function scrollToIndex(ctx, {
2041
2443
  });
2042
2444
  }
2043
2445
 
2044
- // src/utils/checkThreshold.ts
2045
- var HYSTERESIS_MULTIPLIER = 1.3;
2046
- var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot, allowReentryOnChange) => {
2047
- const absDistance = Math.abs(distance);
2048
- const within = atThreshold || threshold > 0 && absDistance <= threshold;
2049
- const updateSnapshot = () => {
2050
- setSnapshot({
2051
- atThreshold,
2052
- contentSize: context.contentSize,
2053
- dataLength: context.dataLength,
2054
- scrollPosition: context.scrollPosition
2055
- });
2056
- };
2057
- if (!wasReached) {
2058
- if (!within) {
2059
- return false;
2060
- }
2061
- onReached(distance);
2062
- updateSnapshot();
2063
- return true;
2064
- }
2065
- const reset = !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
2066
- if (reset) {
2067
- setSnapshot(void 0);
2068
- return false;
2069
- }
2070
- if (within) {
2071
- const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
2072
- if (changed) {
2073
- if (allowReentryOnChange) {
2074
- onReached(distance);
2075
- }
2076
- updateSnapshot();
2077
- }
2078
- }
2079
- return true;
2080
- };
2081
-
2082
- // src/utils/checkAtBottom.ts
2083
- function checkAtBottom(ctx) {
2084
- var _a3;
2085
- const state = ctx.state;
2086
- if (!state || state.initialScroll) {
2087
- return;
2088
- }
2089
- const {
2090
- queuedInitialLayout,
2091
- scrollLength,
2092
- scroll,
2093
- maintainingScrollAtEnd,
2094
- props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
2095
- } = state;
2096
- if (state.initialScroll) {
2097
- return;
2098
- }
2099
- const contentSize = getContentSize(ctx);
2100
- if (contentSize > 0 && queuedInitialLayout && !maintainingScrollAtEnd) {
2101
- const insetEnd = getContentInsetEnd(state);
2102
- const distanceFromEnd = contentSize - scroll - scrollLength - insetEnd;
2103
- const isContentLess = contentSize < scrollLength;
2104
- state.isAtEnd = isContentLess || distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
2105
- state.isEndReached = checkThreshold(
2106
- distanceFromEnd,
2107
- isContentLess,
2108
- onEndReachedThreshold * scrollLength,
2109
- state.isEndReached,
2110
- state.endReachedSnapshot,
2111
- {
2112
- contentSize,
2113
- dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
2114
- scrollPosition: scroll
2115
- },
2116
- (distance) => {
2117
- var _a4, _b;
2118
- return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
2119
- },
2120
- (snapshot) => {
2121
- state.endReachedSnapshot = snapshot;
2122
- },
2123
- true
2124
- );
2125
- }
2126
- }
2127
-
2128
- // src/utils/checkAtTop.ts
2129
- function checkAtTop(ctx) {
2130
- const state = ctx == null ? void 0 : ctx.state;
2131
- if (!state || state.initialScroll || state.scrollingTo) {
2132
- return;
2133
- }
2134
- const {
2135
- dataChangeEpoch,
2136
- isStartReached,
2137
- props: { data, onStartReachedThreshold },
2138
- scroll,
2139
- scrollLength,
2140
- startReachedSnapshot,
2141
- startReachedSnapshotDataChangeEpoch,
2142
- totalSize
2143
- } = state;
2144
- const dataLength = data.length;
2145
- const threshold = onStartReachedThreshold * scrollLength;
2146
- const dataChanged = startReachedSnapshotDataChangeEpoch !== dataChangeEpoch;
2147
- const withinThreshold = threshold > 0 && Math.abs(scroll) <= threshold;
2148
- const allowReentryOnDataChange = !!isStartReached && withinThreshold && !!dataChanged && !isInMVCPActiveMode(state);
2149
- if (isStartReached && threshold > 0 && scroll > threshold && startReachedSnapshot && (dataChanged || startReachedSnapshot.contentSize !== totalSize || startReachedSnapshot.dataLength !== dataLength)) {
2150
- state.isStartReached = false;
2151
- state.startReachedSnapshot = void 0;
2152
- state.startReachedSnapshotDataChangeEpoch = void 0;
2153
- }
2154
- state.isAtStart = scroll <= 0;
2155
- if (isStartReached && withinThreshold && dataChanged && !allowReentryOnDataChange) {
2156
- return;
2157
- }
2158
- state.isStartReached = checkThreshold(
2159
- scroll,
2160
- false,
2161
- threshold,
2162
- state.isStartReached,
2163
- allowReentryOnDataChange ? void 0 : startReachedSnapshot,
2164
- {
2165
- contentSize: totalSize,
2166
- dataLength,
2167
- scrollPosition: scroll
2168
- },
2169
- (distance) => {
2170
- var _a3, _b;
2171
- return (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
2172
- },
2173
- (snapshot) => {
2174
- state.startReachedSnapshot = snapshot;
2175
- state.startReachedSnapshotDataChangeEpoch = snapshot ? dataChangeEpoch : void 0;
2176
- },
2177
- allowReentryOnDataChange
2178
- );
2179
- }
2180
-
2181
- // src/utils/checkThresholds.ts
2182
- function checkThresholds(ctx) {
2183
- checkAtBottom(ctx);
2184
- checkAtTop(ctx);
2185
- }
2186
-
2187
- // src/utils/setInitialRenderState.ts
2188
- function setInitialRenderState(ctx, {
2189
- didLayout,
2190
- didInitialScroll
2191
- }) {
2192
- const { state } = ctx;
2193
- const {
2194
- loadStartTime,
2195
- props: { onLoad }
2196
- } = state;
2197
- if (didLayout) {
2198
- state.didContainersLayout = true;
2199
- }
2200
- if (didInitialScroll) {
2201
- state.didFinishInitialScroll = true;
2202
- }
2203
- const isReadyToRender = Boolean(state.didContainersLayout && state.didFinishInitialScroll);
2204
- if (isReadyToRender && !peek$(ctx, "readyToRender")) {
2205
- set$(ctx, "readyToRender", true);
2206
- if (onLoad) {
2207
- onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
2208
- }
2209
- }
2210
- }
2211
-
2212
- // src/core/initialScroll.ts
2213
- function syncInitialScrollOffset(state, offset) {
2214
- state.scroll = offset;
2215
- state.scrollPending = offset;
2216
- state.scrollPrev = offset;
2217
- }
2446
+ // src/core/initialScroll.ts
2218
2447
  function dispatchInitialScroll(ctx, params) {
2219
2448
  const { forceScroll, resolvedOffset, target, waitForCompletionFrame } = params;
2220
2449
  const requestedIndex = target.index;
@@ -2235,6 +2464,11 @@ function dispatchInitialScroll(ctx, params) {
2235
2464
  }
2236
2465
  function setInitialScrollTarget(state, target, options) {
2237
2466
  var _a3;
2467
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
2468
+ if (state.timeoutPreservedInitialScrollClear !== void 0) {
2469
+ clearTimeout(state.timeoutPreservedInitialScrollClear);
2470
+ state.timeoutPreservedInitialScrollClear = void 0;
2471
+ }
2238
2472
  state.initialScroll = target;
2239
2473
  if ((options == null ? void 0 : options.resetDidFinish) && state.didFinishInitialScroll) {
2240
2474
  state.didFinishInitialScroll = false;
@@ -2243,44 +2477,6 @@ function setInitialScrollTarget(state, target, options) {
2243
2477
  kind: ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" ? "offset" : "bootstrap"
2244
2478
  });
2245
2479
  }
2246
- function finishInitialScroll(ctx, options) {
2247
- var _a3, _b, _c;
2248
- const state = ctx.state;
2249
- if ((options == null ? void 0 : options.resolvedOffset) !== void 0) {
2250
- syncInitialScrollOffset(state, options.resolvedOffset);
2251
- } else if ((options == null ? void 0 : options.syncObservedOffset) && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
2252
- const observedOffset = (_c = (_b = state.refScroller.current) == null ? void 0 : _b.getCurrentScrollOffset) == null ? void 0 : _c.call(_b);
2253
- if (typeof observedOffset === "number" && Number.isFinite(observedOffset)) {
2254
- syncInitialScrollOffset(state, observedOffset);
2255
- }
2256
- }
2257
- const complete = () => {
2258
- var _a4, _b2, _c2, _d, _e, _f, _g;
2259
- const shouldReleaseDeferredPublicOnScroll = ((_a4 = state.initialScrollSession) == null ? void 0 : _a4.kind) === "bootstrap";
2260
- const finalScrollOffset = (_d = (_c2 = (_b2 = options == null ? void 0 : options.resolvedOffset) != null ? _b2 : state.scrollPending) != null ? _c2 : state.scroll) != null ? _d : 0;
2261
- initialScrollWatchdog.clear(state);
2262
- if (!(options == null ? void 0 : options.preserveTarget)) {
2263
- state.initialScroll = void 0;
2264
- }
2265
- setInitialScrollSession(state);
2266
- if ((options == null ? void 0 : options.recalculateItems) && ((_e = state.props) == null ? void 0 : _e.data)) {
2267
- (_f = state.triggerCalculateItemsInView) == null ? void 0 : _f.call(state, { forceFullItemPositions: true });
2268
- }
2269
- if (options == null ? void 0 : options.recalculateItems) {
2270
- checkThresholds(ctx);
2271
- }
2272
- setInitialRenderState(ctx, { didInitialScroll: true });
2273
- if (shouldReleaseDeferredPublicOnScroll) {
2274
- releaseDeferredPublicOnScroll(ctx, finalScrollOffset);
2275
- }
2276
- (_g = options == null ? void 0 : options.onFinished) == null ? void 0 : _g.call(options);
2277
- };
2278
- if (options == null ? void 0 : options.waitForCompletionFrame) {
2279
- requestAnimationFrame(complete);
2280
- return;
2281
- }
2282
- complete();
2283
- }
2284
2480
  function resolveInitialScrollOffset(ctx, initialScroll) {
2285
2481
  var _a3, _b;
2286
2482
  const state = ctx.state;
@@ -2374,13 +2570,18 @@ function advanceCurrentInitialScrollSession(ctx, options) {
2374
2570
  function isNullOrUndefined2(value) {
2375
2571
  return value === null || value === void 0;
2376
2572
  }
2377
- function getMountedBufferedIndices(state) {
2378
- const { startBuffered, endBuffered } = state;
2379
- if (!isNullOrUndefined2(endBuffered) && !isNullOrUndefined2(startBuffered) && startBuffered >= 0 && endBuffered >= 0) {
2380
- 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);
2573
+ function getMountedIndicesInRange(state, start, end) {
2574
+ if (!isNullOrUndefined2(end) && !isNullOrUndefined2(start) && start >= 0 && end >= 0) {
2575
+ 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);
2381
2576
  }
2382
2577
  return [];
2383
2578
  }
2579
+ function getMountedBufferedIndices(state) {
2580
+ return getMountedIndicesInRange(state, state.startBuffered, state.endBuffered);
2581
+ }
2582
+ function getMountedNoBufferIndices(state) {
2583
+ return getMountedIndicesInRange(state, state.startNoBuffer, state.endNoBuffer);
2584
+ }
2384
2585
  function checkAllSizesKnown(state, indices = getMountedBufferedIndices(state)) {
2385
2586
  return indices.length > 0 && indices.every((index) => {
2386
2587
  const key = getId(state, index);
@@ -2599,16 +2800,22 @@ function createRetargetedBottomAlignedInitialScroll(options) {
2599
2800
  function areEquivalentBootstrapInitialScrollTargets(current, next) {
2600
2801
  return current.index === next.index && current.preserveForBottomPadding === next.preserveForBottomPadding && current.preserveForFooterLayout === next.preserveForFooterLayout && current.viewOffset === next.viewOffset && current.viewPosition === next.viewPosition;
2601
2802
  }
2602
- function clearPendingInitialScrollFooterLayout(state, target) {
2803
+ function clearPendingInitialScrollFooterLayout(ctx, options) {
2804
+ const { dataLength, stylePaddingBottom, target } = options;
2805
+ const state = ctx.state;
2603
2806
  if (!shouldPreserveInitialScrollForFooterLayout(target)) {
2604
2807
  return;
2605
2808
  }
2606
- if (state.didFinishInitialScroll && !getBootstrapInitialScrollSession(state)) {
2607
- state.initialScroll = void 0;
2608
- setInitialScrollSession(state);
2609
- return;
2610
- }
2611
- setInitialScrollTarget(state, { ...target, preserveForFooterLayout: void 0 });
2809
+ const clearedFooterTarget = createInitialScrollAtEndTarget({
2810
+ dataLength,
2811
+ footerSize: 0,
2812
+ preserveForFooterLayout: void 0,
2813
+ stylePaddingBottom
2814
+ });
2815
+ setInitialScrollTarget(state, clearedFooterTarget);
2816
+ }
2817
+ function clearFinishedViewportRetargetableInitialScroll(state) {
2818
+ clearPreservedInitialScrollTarget(state);
2612
2819
  }
2613
2820
  function didFinishedInitialScrollMoveAwayFromTarget(ctx, target, epsilon = DEFAULT_BOOTSTRAP_REVEAL_EPSILON) {
2614
2821
  const state = ctx.state;
@@ -2623,6 +2830,25 @@ function getObservedBootstrapInitialScrollOffset(state) {
2623
2830
  const observedOffset = (_b = (_a3 = state.refScroller.current) == null ? void 0 : _a3.getCurrentScrollOffset) == null ? void 0 : _b.call(_a3);
2624
2831
  return typeof observedOffset === "number" && Number.isFinite(observedOffset) ? observedOffset : (_d = (_c = state.scrollPending) != null ? _c : state.scroll) != null ? _d : 0;
2625
2832
  }
2833
+ function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {
2834
+ var _a3, _b;
2835
+ const state = ctx.state;
2836
+ const initialScroll = state.initialScroll;
2837
+ if (!state.didFinishInitialScroll || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) || (initialScroll == null ? void 0 : initialScroll.viewPosition) !== 1) {
2838
+ return;
2839
+ }
2840
+ if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2841
+ if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {
2842
+ clearPendingInitialScrollFooterLayout(ctx, {
2843
+ dataLength: state.props.data.length,
2844
+ stylePaddingBottom: (_b = state.props.stylePaddingBottom) != null ? _b : 0,
2845
+ target: initialScroll
2846
+ });
2847
+ return;
2848
+ }
2849
+ clearFinishedViewportRetargetableInitialScroll(state);
2850
+ }
2851
+ }
2626
2852
  function startBootstrapInitialScrollOnMount(ctx, options) {
2627
2853
  var _a3, _b, _c;
2628
2854
  const { initialScrollAtEnd, target } = options;
@@ -2659,15 +2885,16 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
2659
2885
  }
2660
2886
  const shouldResetDidFinish = !!(state.didFinishInitialScroll && previousDataLength === 0 && dataLength > 0 && initialScroll.index !== void 0);
2661
2887
  const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2888
+ const shouldClearFinishedResizePreservation = didDataChange && dataLength > 0 && state.didFinishInitialScroll && !bootstrapInitialScroll && !shouldResetDidFinish;
2889
+ if (shouldClearFinishedResizePreservation) {
2890
+ clearPreservedInitialScrollTarget(state);
2891
+ return;
2892
+ }
2662
2893
  const shouldRetargetBottomAligned = dataLength > 0 && (initialScrollAtEnd || isRetargetableBottomAlignedInitialScrollTarget(initialScroll));
2663
2894
  if (!didDataChange && !shouldResetDidFinish && !shouldRetargetBottomAligned) {
2664
2895
  return;
2665
2896
  }
2666
2897
  if (shouldRetargetBottomAligned) {
2667
- if (!shouldResetDidFinish && didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2668
- clearPendingInitialScrollFooterLayout(state, initialScroll);
2669
- return;
2670
- }
2671
2898
  const updatedInitialScroll = initialScrollAtEnd ? createInitialScrollAtEndTarget({
2672
2899
  dataLength,
2673
2900
  footerSize: peek$(ctx, "footerSize") || 0,
@@ -2680,6 +2907,14 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
2680
2907
  stylePaddingBottom,
2681
2908
  target: initialScroll
2682
2909
  });
2910
+ if (!shouldResetDidFinish && didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2911
+ clearPendingInitialScrollFooterLayout(ctx, {
2912
+ dataLength,
2913
+ stylePaddingBottom,
2914
+ target: initialScroll
2915
+ });
2916
+ return;
2917
+ }
2683
2918
  if (!areEquivalentBootstrapInitialScrollTargets(initialScroll, updatedInitialScroll) || !!bootstrapInitialScroll || shouldResetDidFinish || didDataChange) {
2684
2919
  setInitialScrollTarget(state, updatedInitialScroll, {
2685
2920
  resetDidFinish: shouldResetDidFinish
@@ -2721,7 +2956,11 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2721
2956
  return;
2722
2957
  }
2723
2958
  if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2724
- clearPendingInitialScrollFooterLayout(state, initialScroll);
2959
+ clearPendingInitialScrollFooterLayout(ctx, {
2960
+ dataLength,
2961
+ stylePaddingBottom,
2962
+ target: initialScroll
2963
+ });
2725
2964
  } else {
2726
2965
  const updatedInitialScroll = createInitialScrollAtEndTarget({
2727
2966
  dataLength,
@@ -2731,10 +2970,15 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2731
2970
  });
2732
2971
  const didTargetChange = initialScroll.index !== updatedInitialScroll.index || initialScroll.viewPosition !== updatedInitialScroll.viewPosition || initialScroll.viewOffset !== updatedInitialScroll.viewOffset;
2733
2972
  if (!didTargetChange) {
2734
- clearPendingInitialScrollFooterLayout(state, initialScroll);
2973
+ clearPendingInitialScrollFooterLayout(ctx, {
2974
+ dataLength,
2975
+ stylePaddingBottom,
2976
+ target: initialScroll
2977
+ });
2735
2978
  } else {
2979
+ const didFinishInitialScroll = !!state.didFinishInitialScroll;
2736
2980
  setInitialScrollTarget(state, updatedInitialScroll, {
2737
- resetDidFinish: !!state.didFinishInitialScroll
2981
+ resetDidFinish: didFinishInitialScroll
2738
2982
  });
2739
2983
  rearmBootstrapInitialScroll(ctx, {
2740
2984
  scroll: resolveInitialScrollOffset(ctx, updatedInitialScroll),
@@ -2743,6 +2987,29 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2743
2987
  }
2744
2988
  }
2745
2989
  }
2990
+ function handleBootstrapInitialScrollLayoutChange(ctx) {
2991
+ const state = ctx.state;
2992
+ const initialScroll = state.initialScroll;
2993
+ if (isOffsetInitialScrollSession(state) || state.props.data.length === 0 || !initialScroll) {
2994
+ return;
2995
+ }
2996
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2997
+ if (!bootstrapInitialScroll && initialScroll.viewPosition !== 1) {
2998
+ return;
2999
+ }
3000
+ const didFinishInitialScroll = state.didFinishInitialScroll;
3001
+ if (didFinishInitialScroll) {
3002
+ setInitialScrollTarget(state, initialScroll, {
3003
+ resetDidFinish: true
3004
+ });
3005
+ state.clearPreservedInitialScrollOnNextFinish = true;
3006
+ }
3007
+ rearmBootstrapInitialScroll(ctx, {
3008
+ scroll: resolveInitialScrollOffset(ctx, initialScroll),
3009
+ seedContentOffset: didFinishInitialScroll && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
3010
+ targetIndexSeed: initialScroll.index
3011
+ });
3012
+ }
2746
3013
  function evaluateBootstrapInitialScroll(ctx) {
2747
3014
  var _a3, _b;
2748
3015
  const state = ctx.state;
@@ -2811,12 +3078,15 @@ function evaluateBootstrapInitialScroll(ctx) {
2811
3078
  }
2812
3079
  }
2813
3080
  function finishBootstrapInitialScrollWithoutScroll(ctx, resolvedOffset) {
3081
+ var _a3;
2814
3082
  const state = ctx.state;
2815
3083
  clearBootstrapInitialScrollSession(state);
3084
+ const shouldPreserveResizeTarget = !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_a3 = state.initialScroll) == null ? void 0 : _a3.viewPosition) === 1;
2816
3085
  finishInitialScroll(ctx, {
2817
- preserveTarget: shouldPreserveInitialScrollForFooterLayout(state.initialScroll),
3086
+ preserveTarget: shouldPreserveResizeTarget,
2818
3087
  recalculateItems: true,
2819
- resolvedOffset
3088
+ resolvedOffset,
3089
+ schedulePreservedTargetClear: shouldPreserveResizeTarget
2820
3090
  });
2821
3091
  }
2822
3092
  function abortBootstrapInitialScroll(ctx) {
@@ -3194,7 +3464,7 @@ function resolvePendingNativeMVCPAdjust(ctx, newScroll) {
3194
3464
  settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
3195
3465
  return true;
3196
3466
  }
3197
- if (state.pendingMaintainScrollAtEnd && state.isAtEnd && progressTowardAmount > MVCP_POSITION_EPSILON) {
3467
+ if (state.pendingMaintainScrollAtEnd && peek$(ctx, "isWithinMaintainScrollAtEndThreshold") && progressTowardAmount > MVCP_POSITION_EPSILON) {
3198
3468
  settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
3199
3469
  return true;
3200
3470
  }
@@ -3351,6 +3621,86 @@ function prepareMVCP(ctx, dataChanged) {
3351
3621
  }
3352
3622
  }
3353
3623
 
3624
+ // src/core/syncMountedContainer.ts
3625
+ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3626
+ var _a3, _b, _c, _d, _e, _f, _g, _h;
3627
+ const state = ctx.state;
3628
+ const {
3629
+ columns,
3630
+ columnSpans,
3631
+ positions,
3632
+ props: { data, itemsAreEqual, keyExtractor }
3633
+ } = state;
3634
+ const item = data[itemIndex];
3635
+ if (item === void 0) {
3636
+ return { didChangePosition: false, didRefreshData: false };
3637
+ }
3638
+ const updateLayout = (_a3 = options == null ? void 0 : options.updateLayout) != null ? _a3 : true;
3639
+ let didChangePosition = false;
3640
+ let didRefreshData = false;
3641
+ if (updateLayout) {
3642
+ const positionValue = positions[itemIndex];
3643
+ if (positionValue === void 0) {
3644
+ set$(ctx, `containerPosition${containerIndex}`, POSITION_OUT_OF_VIEW);
3645
+ return { didChangePosition: false, didRefreshData: false };
3646
+ }
3647
+ const position = (positionValue || 0) - ((_b = options == null ? void 0 : options.scrollAdjustPending) != null ? _b : 0);
3648
+ const column = columns[itemIndex] || 1;
3649
+ const span = columnSpans[itemIndex] || 1;
3650
+ const prevPos = peek$(ctx, `containerPosition${containerIndex}`);
3651
+ const prevColumn = peek$(ctx, `containerColumn${containerIndex}`);
3652
+ const prevSpan = peek$(ctx, `containerSpan${containerIndex}`);
3653
+ if (position > POSITION_OUT_OF_VIEW && position !== prevPos) {
3654
+ set$(ctx, `containerPosition${containerIndex}`, position);
3655
+ didChangePosition = true;
3656
+ }
3657
+ if (column >= 0 && column !== prevColumn) {
3658
+ set$(ctx, `containerColumn${containerIndex}`, column);
3659
+ }
3660
+ if (span !== prevSpan) {
3661
+ set$(ctx, `containerSpan${containerIndex}`, span);
3662
+ }
3663
+ }
3664
+ const prevData = peek$(ctx, `containerItemData${containerIndex}`);
3665
+ if (prevData !== item) {
3666
+ 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;
3667
+ const cachedComparison = (_e = pendingDataComparison == null ? void 0 : pendingDataComparison.byIndex[itemIndex]) != null ? _e : 0;
3668
+ if (cachedComparison === 2) {
3669
+ set$(ctx, `containerItemData${containerIndex}`, item);
3670
+ didRefreshData = true;
3671
+ } else if (cachedComparison !== 1) {
3672
+ const itemKey = (_g = (_f = peek$(ctx, `containerItemKey${containerIndex}`)) != null ? _f : state.idCache[itemIndex]) != null ? _g : getId(state, itemIndex);
3673
+ const prevKey = keyExtractor == null ? void 0 : keyExtractor(prevData, itemIndex);
3674
+ if (prevData === void 0 || !keyExtractor || prevKey !== itemKey) {
3675
+ set$(ctx, `containerItemData${containerIndex}`, item);
3676
+ didRefreshData = true;
3677
+ } else if (!itemsAreEqual) {
3678
+ set$(ctx, `containerItemData${containerIndex}`, item);
3679
+ didRefreshData = true;
3680
+ } else {
3681
+ const isEqual = itemsAreEqual(prevData, item, itemIndex, data);
3682
+ if (!state.pendingDataComparison || state.pendingDataComparison.previousData !== state.previousData || state.pendingDataComparison.nextData !== data) {
3683
+ if (state.previousData) {
3684
+ state.pendingDataComparison = {
3685
+ byIndex: [],
3686
+ nextData: data,
3687
+ previousData: state.previousData
3688
+ };
3689
+ }
3690
+ }
3691
+ if ((_h = state.pendingDataComparison) == null ? void 0 : _h.byIndex) {
3692
+ state.pendingDataComparison.byIndex[itemIndex] = isEqual ? 1 : 2;
3693
+ }
3694
+ if (!isEqual) {
3695
+ set$(ctx, `containerItemData${containerIndex}`, item);
3696
+ didRefreshData = true;
3697
+ }
3698
+ }
3699
+ }
3700
+ }
3701
+ return { didChangePosition, didRefreshData };
3702
+ }
3703
+
3354
3704
  // src/core/prepareColumnStartState.ts
3355
3705
  function prepareColumnStartState(ctx, startIndex, useAverageSize) {
3356
3706
  var _a3;
@@ -3513,9 +3863,10 @@ function updateSnapToOffsets(ctx) {
3513
3863
  }
3514
3864
 
3515
3865
  // src/core/updateItemPositions.ts
3516
- function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP } = {
3866
+ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP, optimizeForVisibleWindow = false } = {
3517
3867
  doMVCP: false,
3518
3868
  forceFullUpdate: false,
3869
+ optimizeForVisibleWindow: false,
3519
3870
  scrollBottomBuffered: -1,
3520
3871
  startIndex: 0
3521
3872
  }) {
@@ -3540,7 +3891,7 @@ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffere
3540
3891
  const layoutConfig = overrideItemLayout ? { span: 1 } : void 0;
3541
3892
  const lastScrollDelta = state.lastScrollDelta;
3542
3893
  const velocity = getScrollVelocity(state);
3543
- const shouldOptimize = !forceFullUpdate && !dataChanged && (Math.abs(velocity) > 0 || state.scrollLength > 0 && lastScrollDelta > state.scrollLength);
3894
+ const shouldOptimize = !forceFullUpdate && !dataChanged && (optimizeForVisibleWindow || Math.abs(velocity) > 0 || state.scrollLength > 0 && lastScrollDelta > state.scrollLength);
3544
3895
  const maxVisibleArea = scrollBottomBuffered + 1e3;
3545
3896
  const useAverageSize = !getEstimatedItemSize;
3546
3897
  const preferCachedSize = !doMVCP || dataChanged || state.scrollAdjustHandler.getAdjust() !== 0 || ((_b = peek$(ctx, "scrollAdjustPending")) != null ? _b : 0) !== 0;
@@ -3655,7 +4006,15 @@ function ensureViewabilityState(ctx, configId) {
3655
4006
  }
3656
4007
  let state = map.get(configId);
3657
4008
  if (!state) {
3658
- state = { end: -1, previousEnd: -1, previousStart: -1, start: -1, viewableItems: [] };
4009
+ state = {
4010
+ end: -1,
4011
+ endBuffered: -1,
4012
+ previousEnd: -1,
4013
+ previousStart: -1,
4014
+ start: -1,
4015
+ startBuffered: -1,
4016
+ viewableItems: []
4017
+ };
3659
4018
  map.set(configId, state);
3660
4019
  }
3661
4020
  return state;
@@ -3675,7 +4034,7 @@ function setupViewability(props) {
3675
4034
  }
3676
4035
  return viewabilityConfigCallbackPairs;
3677
4036
  }
3678
- function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollSize, start, end) {
4037
+ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollSize, start, end, startBuffered = start, endBuffered = end) {
3679
4038
  const {
3680
4039
  timeouts,
3681
4040
  props: { data }
@@ -3684,6 +4043,8 @@ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollS
3684
4043
  const viewabilityState = ensureViewabilityState(ctx, viewabilityConfigCallbackPair.viewabilityConfig.id);
3685
4044
  viewabilityState.start = start;
3686
4045
  viewabilityState.end = end;
4046
+ viewabilityState.startBuffered = startBuffered;
4047
+ viewabilityState.endBuffered = endBuffered;
3687
4048
  if (viewabilityConfigCallbackPair.viewabilityConfig.minimumViewTime) {
3688
4049
  const timer = setTimeout(() => {
3689
4050
  timeouts.delete(timer);
@@ -3699,7 +4060,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
3699
4060
  const { viewabilityConfig, onViewableItemsChanged } = viewabilityConfigCallbackPair;
3700
4061
  const configId = viewabilityConfig.id;
3701
4062
  const viewabilityState = ensureViewabilityState(ctx, configId);
3702
- const { viewableItems: previousViewableItems, start, end } = viewabilityState;
4063
+ const { viewableItems: previousViewableItems, start, end, startBuffered, endBuffered } = viewabilityState;
3703
4064
  const viewabilityTokens = /* @__PURE__ */ new Map();
3704
4065
  for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
3705
4066
  viewabilityTokens.set(
@@ -3768,7 +4129,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
3768
4129
  maybeUpdateViewabilityCallback(ctx, configId, change.containerId, change);
3769
4130
  }
3770
4131
  if (onViewableItemsChanged) {
3771
- onViewableItemsChanged({ changed, viewableItems });
4132
+ onViewableItemsChanged({ changed, end, endBuffered, start, startBuffered, viewableItems });
3772
4133
  }
3773
4134
  }
3774
4135
  for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
@@ -3869,6 +4230,7 @@ function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pen
3869
4230
  const numContainers = peek$(ctx, "numContainers");
3870
4231
  const state = ctx.state;
3871
4232
  const { stickyContainerPool, containerItemTypes } = state;
4233
+ const shouldAvoidAssignedContainerReuse = state.props.recycleItems && !!state.props.positionComponentInternal;
3872
4234
  const result = [];
3873
4235
  const availableContainers = [];
3874
4236
  const pendingRemovalSet = new Set(pendingRemoval);
@@ -3925,18 +4287,20 @@ function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pen
3925
4287
  }
3926
4288
  }
3927
4289
  }
3928
- for (let u = 0; u < numContainers && result.length < numNeeded; u++) {
3929
- if (stickyContainerPool.has(u)) {
3930
- continue;
3931
- }
3932
- const key = peek$(ctx, `containerItemKey${u}`);
3933
- if (key === void 0) continue;
3934
- const index = state.indexByKey.get(key);
3935
- const isOutOfView = index < startBuffered || index > endBuffered;
3936
- if (isOutOfView) {
3937
- const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
3938
- if (!requiredItemTypes || typeIndex < neededTypes.length && canReuseContainer(u, neededTypes[typeIndex])) {
3939
- availableContainers.push({ distance, index: u });
4290
+ if (!shouldAvoidAssignedContainerReuse) {
4291
+ for (let u = 0; u < numContainers && result.length < numNeeded; u++) {
4292
+ if (stickyContainerPool.has(u)) {
4293
+ continue;
4294
+ }
4295
+ const key = peek$(ctx, `containerItemKey${u}`);
4296
+ if (key === void 0) continue;
4297
+ const index = state.indexByKey.get(key);
4298
+ const isOutOfView = index < startBuffered || index > endBuffered;
4299
+ if (isOutOfView) {
4300
+ const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
4301
+ if (!requiredItemTypes || typeIndex < neededTypes.length && canReuseContainer(u, neededTypes[typeIndex])) {
4302
+ availableContainers.push({ distance, index: u });
4303
+ }
3940
4304
  }
3941
4305
  }
3942
4306
  }
@@ -4080,7 +4444,6 @@ function calculateItemsInView(ctx, params = {}) {
4080
4444
  alwaysRenderIndicesSet,
4081
4445
  drawDistance,
4082
4446
  getItemType,
4083
- itemsAreEqual,
4084
4447
  keyExtractor,
4085
4448
  onStickyHeaderChange
4086
4449
  },
@@ -4107,11 +4470,11 @@ function calculateItemsInView(ctx, params = {}) {
4107
4470
  const numColumns = peek$(ctx, "numColumns");
4108
4471
  const speed = getScrollVelocity(state);
4109
4472
  const scrollExtra = 0;
4110
- const { queuedInitialLayout } = state;
4111
- const scrollState = suppressInitialScrollSideEffects ? (_b = bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.scroll) != null ? _b : state.scroll : !queuedInitialLayout && state.initialScroll ? (
4473
+ const { initialScroll, queuedInitialLayout } = state;
4474
+ const scrollState = suppressInitialScrollSideEffects ? (_b = bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.scroll) != null ? _b : state.scroll : !queuedInitialLayout && hasActiveInitialScroll(state) && initialScroll ? (
4112
4475
  // Before the initial layout settles, keep viewport math anchored to the
4113
4476
  // current initial-scroll target instead of transient native adjustments.
4114
- resolveInitialScrollOffset(ctx, state.initialScroll)
4477
+ resolveInitialScrollOffset(ctx, initialScroll)
4115
4478
  ) : state.scroll;
4116
4479
  const scrollAdjustPending = (_c = peek$(ctx, "scrollAdjustPending")) != null ? _c : 0;
4117
4480
  const scrollAdjustPad = scrollAdjustPending - topPad;
@@ -4156,9 +4519,11 @@ function calculateItemsInView(ctx, params = {}) {
4156
4519
  columnSpans.length = 0;
4157
4520
  }
4158
4521
  const startIndex = forceFullItemPositions || dataChanged ? 0 : (_d = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _d : 0;
4522
+ const optimizeForVisibleWindow = !forceFullItemPositions && !dataChanged && numColumns > 1 && minIndexSizeChanged !== void 0;
4159
4523
  updateItemPositions(ctx, dataChanged, {
4160
4524
  doMVCP,
4161
4525
  forceFullUpdate: !!forceFullItemPositions,
4526
+ optimizeForVisibleWindow,
4162
4527
  scrollBottomBuffered,
4163
4528
  startIndex
4164
4529
  });
@@ -4406,33 +4771,11 @@ function calculateItemsInView(ctx, params = {}) {
4406
4771
  set$(ctx, `containerSpan${i}`, 1);
4407
4772
  } else {
4408
4773
  const itemIndex = indexByKey.get(itemKey);
4409
- const item = data[itemIndex];
4410
- if (item !== void 0) {
4411
- const positionValue = positions[itemIndex];
4412
- if (positionValue === void 0) {
4413
- set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
4414
- } else {
4415
- const position = (positionValue || 0) - scrollAdjustPending;
4416
- const column = columns[itemIndex] || 1;
4417
- const span = columnSpans[itemIndex] || 1;
4418
- const prevPos = peek$(ctx, `containerPosition${i}`);
4419
- const prevColumn = peek$(ctx, `containerColumn${i}`);
4420
- const prevSpan = peek$(ctx, `containerSpan${i}`);
4421
- const prevData = peek$(ctx, `containerItemData${i}`);
4422
- if (position > POSITION_OUT_OF_VIEW && position !== prevPos) {
4423
- set$(ctx, `containerPosition${i}`, position);
4424
- didChangePositions = true;
4425
- }
4426
- if (column >= 0 && column !== prevColumn) {
4427
- set$(ctx, `containerColumn${i}`, column);
4428
- }
4429
- if (span !== prevSpan) {
4430
- set$(ctx, `containerSpan${i}`, span);
4431
- }
4432
- if (prevData !== item && (itemsAreEqual ? !itemsAreEqual(prevData, item, itemIndex, data) : true)) {
4433
- set$(ctx, `containerItemData${i}`, item);
4434
- }
4435
- }
4774
+ if (itemIndex !== void 0) {
4775
+ didChangePositions = syncMountedContainer(ctx, i, itemIndex, {
4776
+ scrollAdjustPending,
4777
+ updateLayout: true
4778
+ }).didChangePosition || didChangePositions;
4436
4779
  }
4437
4780
  }
4438
4781
  }
@@ -4443,7 +4786,10 @@ function calculateItemsInView(ctx, params = {}) {
4443
4786
  evaluateBootstrapInitialScroll(ctx);
4444
4787
  return;
4445
4788
  }
4446
- if (!queuedInitialLayout && endBuffered !== null && checkAllSizesKnown(state)) {
4789
+ const mountedBufferedIndices = getMountedBufferedIndices(state);
4790
+ const mountedNoBufferIndices = getMountedNoBufferIndices(state);
4791
+ const readinessIndices = hasActiveInitialScroll(state) ? mountedBufferedIndices : mountedNoBufferIndices.length > 0 ? mountedNoBufferIndices : mountedBufferedIndices;
4792
+ if (!queuedInitialLayout && readinessIndices.length > 0 && checkAllSizesKnown(state, readinessIndices)) {
4447
4793
  setDidLayout(ctx);
4448
4794
  handleInitialScrollLayoutReady(ctx);
4449
4795
  }
@@ -4454,7 +4800,9 @@ function calculateItemsInView(ctx, params = {}) {
4454
4800
  viewabilityConfigCallbackPairs,
4455
4801
  scrollLength,
4456
4802
  startNoBuffer,
4457
- endNoBuffer
4803
+ endNoBuffer,
4804
+ startBuffered != null ? startBuffered : startNoBuffer,
4805
+ endBuffered != null ? endBuffered : endNoBuffer
4458
4806
  );
4459
4807
  }
4460
4808
  if (onStickyHeaderChange && stickyIndicesArr.length > 0 && nextActiveStickyIndex !== void 0 && nextActiveStickyIndex !== previousStickyIndex) {
@@ -4466,37 +4814,17 @@ function calculateItemsInView(ctx, params = {}) {
4466
4814
  });
4467
4815
  }
4468
4816
 
4469
- // src/core/checkActualChange.ts
4470
- function checkActualChange(state, dataProp, previousData) {
4471
- if (!previousData || !dataProp || dataProp.length !== previousData.length) {
4472
- return true;
4473
- }
4474
- const {
4475
- idCache,
4476
- props: { keyExtractor }
4477
- } = state;
4478
- for (let i = 0; i < dataProp.length; i++) {
4479
- if (dataProp[i] !== previousData[i]) {
4480
- return true;
4481
- }
4482
- if (keyExtractor ? idCache[i] !== keyExtractor(previousData[i], i) : dataProp[i] !== previousData[i]) {
4483
- return true;
4484
- }
4485
- }
4486
- return false;
4487
- }
4488
-
4489
4817
  // src/core/doMaintainScrollAtEnd.ts
4490
4818
  function doMaintainScrollAtEnd(ctx) {
4491
4819
  const state = ctx.state;
4492
4820
  const {
4493
4821
  didContainersLayout,
4494
- isAtEnd,
4495
4822
  pendingNativeMVCPAdjust,
4496
4823
  refScroller,
4497
4824
  props: { maintainScrollAtEnd }
4498
4825
  } = state;
4499
- const shouldMaintainScrollAtEnd = !!(isAtEnd && maintainScrollAtEnd && didContainersLayout);
4826
+ const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
4827
+ const shouldMaintainScrollAtEnd = !!(isWithinMaintainScrollAtEndThreshold && maintainScrollAtEnd && didContainersLayout);
4500
4828
  if (pendingNativeMVCPAdjust) {
4501
4829
  state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
4502
4830
  return false;
@@ -4509,7 +4837,7 @@ function doMaintainScrollAtEnd(ctx) {
4509
4837
  }
4510
4838
  requestAnimationFrame(() => {
4511
4839
  var _a3;
4512
- if (state.isAtEnd) {
4840
+ if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
4513
4841
  state.maintainingScrollAtEnd = true;
4514
4842
  (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
4515
4843
  animated: maintainScrollAtEnd.animated
@@ -4527,68 +4855,22 @@ function doMaintainScrollAtEnd(ctx) {
4527
4855
  return false;
4528
4856
  }
4529
4857
 
4530
- // src/utils/updateAveragesOnDataChange.ts
4531
- function updateAveragesOnDataChange(state, oldData, newData) {
4532
- var _a3;
4533
- const {
4534
- averageSizes,
4535
- sizesKnown,
4536
- indexByKey,
4537
- props: { itemsAreEqual, getItemType, keyExtractor }
4538
- } = state;
4539
- if (!itemsAreEqual || !oldData.length || !newData.length) {
4540
- for (const key in averageSizes) {
4541
- delete averageSizes[key];
4542
- }
4543
- return;
4544
- }
4545
- const itemTypesToPreserve = {};
4546
- const newDataLength = newData.length;
4547
- const oldDataLength = oldData.length;
4548
- for (let newIndex = 0; newIndex < newDataLength; newIndex++) {
4549
- const newItem = newData[newIndex];
4550
- const id = keyExtractor ? keyExtractor(newItem, newIndex) : String(newIndex);
4551
- const oldIndex = indexByKey.get(id);
4552
- if (oldIndex !== void 0 && oldIndex < oldDataLength) {
4553
- const knownSize = sizesKnown.get(id);
4554
- if (knownSize === void 0) continue;
4555
- const oldItem = oldData[oldIndex];
4556
- const areEqual = itemsAreEqual(oldItem, newItem, newIndex, newData);
4557
- if (areEqual) {
4558
- const itemType = getItemType ? (_a3 = getItemType(newItem, newIndex)) != null ? _a3 : "" : "";
4559
- let typeData = itemTypesToPreserve[itemType];
4560
- if (!typeData) {
4561
- typeData = itemTypesToPreserve[itemType] = { count: 0, totalSize: 0 };
4562
- }
4563
- typeData.totalSize += knownSize;
4564
- typeData.count++;
4565
- }
4566
- }
4567
- }
4568
- for (const key in averageSizes) {
4569
- delete averageSizes[key];
4570
- }
4571
- for (const itemType in itemTypesToPreserve) {
4572
- const { totalSize, count } = itemTypesToPreserve[itemType];
4573
- if (count > 0) {
4574
- averageSizes[itemType] = {
4575
- avg: totalSize / count,
4576
- num: count
4577
- };
4578
- }
4579
- }
4580
- }
4581
-
4582
4858
  // src/core/checkResetContainers.ts
4583
- function checkResetContainers(ctx, dataProp) {
4859
+ function checkResetContainers(ctx, dataProp, { didColumnsChange = false } = {}) {
4584
4860
  const state = ctx.state;
4585
4861
  const { previousData } = state;
4586
- if (previousData) {
4587
- updateAveragesOnDataChange(state, previousData, dataProp);
4588
- }
4589
4862
  const { maintainScrollAtEnd } = state.props;
4863
+ if (didColumnsChange) {
4864
+ state.sizes.clear();
4865
+ state.sizesKnown.clear();
4866
+ for (const key in state.averageSizes) {
4867
+ delete state.averageSizes[key];
4868
+ }
4869
+ state.minIndexSizeChanged = 0;
4870
+ state.scrollForNextCalculateItemsInView = void 0;
4871
+ }
4590
4872
  calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
4591
- const shouldMaintainScrollAtEnd = maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onDataChange;
4873
+ const shouldMaintainScrollAtEnd = !didColumnsChange && (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onDataChange);
4592
4874
  const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx);
4593
4875
  if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
4594
4876
  state.isEndReached = false;
@@ -4599,6 +4881,53 @@ function checkResetContainers(ctx, dataProp) {
4599
4881
  delete state.previousData;
4600
4882
  }
4601
4883
 
4884
+ // src/core/checkStructuralDataChange.ts
4885
+ function checkStructuralDataChange(state, dataProp, previousData) {
4886
+ var _a3;
4887
+ state.pendingDataComparison = void 0;
4888
+ if (!previousData || !dataProp || dataProp.length !== previousData.length) {
4889
+ return true;
4890
+ }
4891
+ const {
4892
+ idCache,
4893
+ props: { itemsAreEqual, keyExtractor }
4894
+ } = state;
4895
+ let byIndex;
4896
+ for (let i = 0; i < dataProp.length; i++) {
4897
+ if (dataProp[i] === previousData[i]) {
4898
+ continue;
4899
+ }
4900
+ if (!keyExtractor) {
4901
+ if (byIndex) {
4902
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4903
+ }
4904
+ return true;
4905
+ }
4906
+ const previousKey = (_a3 = idCache[i]) != null ? _a3 : keyExtractor(previousData[i], i);
4907
+ const nextKey = keyExtractor(dataProp[i], i);
4908
+ if (previousKey !== nextKey) {
4909
+ if (byIndex) {
4910
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4911
+ }
4912
+ return true;
4913
+ }
4914
+ if (!itemsAreEqual) {
4915
+ if (byIndex) {
4916
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4917
+ }
4918
+ return true;
4919
+ }
4920
+ const isEqual = itemsAreEqual(previousData[i], dataProp[i], i, dataProp);
4921
+ byIndex != null ? byIndex : byIndex = [];
4922
+ byIndex[i] = isEqual ? 1 : 2;
4923
+ if (!isEqual) {
4924
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4925
+ return true;
4926
+ }
4927
+ }
4928
+ return false;
4929
+ }
4930
+
4602
4931
  // src/core/doInitialAllocateContainers.ts
4603
4932
  function doInitialAllocateContainers(ctx) {
4604
4933
  var _a3, _b, _c;
@@ -4842,6 +5171,7 @@ function onScroll(ctx, event) {
4842
5171
  state.scrollPending = newScroll;
4843
5172
  updateScroll(ctx, newScroll, insetChanged);
4844
5173
  trackInitialScrollNativeProgress(state, newScroll);
5174
+ clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx);
4845
5175
  if (state.scrollingTo) {
4846
5176
  checkFinishedScroll(ctx);
4847
5177
  }
@@ -4905,6 +5235,43 @@ var ScrollAdjustHandler = class {
4905
5235
  }
4906
5236
  };
4907
5237
 
5238
+ // src/core/updateAnchoredEndSpace.ts
5239
+ function maybeUpdateAnchoredEndSpace(ctx) {
5240
+ var _a3;
5241
+ const state = ctx.state;
5242
+ const anchoredEndSpace = state.props.anchoredEndSpace;
5243
+ const previousSize = peek$(ctx, "anchoredEndSpaceSize");
5244
+ let nextSize = 0;
5245
+ if (anchoredEndSpace) {
5246
+ const { anchorIndex, anchorMaxSize, anchorOffset = 0 } = anchoredEndSpace;
5247
+ const { data } = state.props;
5248
+ if (anchorIndex >= 0 && anchorIndex < data.length && state.scrollLength > 0) {
5249
+ let contentBelowAnchor = 0;
5250
+ const footerSize = ctx.values.get("footerSize") || 0;
5251
+ const stylePaddingBottom = state.props.stylePaddingBottom || 0;
5252
+ for (let index = anchorIndex; index < data.length; index++) {
5253
+ const itemKey = getId(state, index);
5254
+ const size = itemKey ? state.sizesKnown.get(itemKey) : void 0;
5255
+ const effectiveSize = index === anchorIndex && anchorMaxSize !== void 0 ? Math.min(size || 0, Math.max(0, anchorMaxSize)) : size;
5256
+ if (effectiveSize !== null && effectiveSize !== void 0 && effectiveSize > 0) {
5257
+ contentBelowAnchor += effectiveSize;
5258
+ }
5259
+ }
5260
+ contentBelowAnchor += footerSize + stylePaddingBottom;
5261
+ nextSize = Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
5262
+ }
5263
+ }
5264
+ if (previousSize === nextSize) {
5265
+ return nextSize;
5266
+ }
5267
+ set$(ctx, "anchoredEndSpaceSize", nextSize);
5268
+ (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
5269
+ if (anchoredEndSpace == null ? void 0 : anchoredEndSpace.includeInEndInset) {
5270
+ updateScroll(ctx, state.scroll, true);
5271
+ }
5272
+ return nextSize;
5273
+ }
5274
+
4908
5275
  // src/core/updateItemSize.ts
4909
5276
  function runOrScheduleMVCPRecalculate(ctx) {
4910
5277
  const state = ctx.state;
@@ -4986,6 +5353,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
4986
5353
  previous: size - diff,
4987
5354
  size
4988
5355
  });
5356
+ maybeUpdateAnchoredEndSpace(ctx);
4989
5357
  }
4990
5358
  if (minIndexSizeChanged !== void 0) {
4991
5359
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
@@ -5109,7 +5477,7 @@ function createImperativeHandle(ctx) {
5109
5477
  const IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS = 800;
5110
5478
  const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
5111
5479
  let imperativeScrollToken = 0;
5112
- const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0 || hasActiveMVCPAnchorLock(state);
5480
+ const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0;
5113
5481
  const runWhenSettled = (token, run) => {
5114
5482
  const startedAt = Date.now();
5115
5483
  let stableFrames = 0;
@@ -5131,9 +5499,10 @@ function createImperativeHandle(ctx) {
5131
5499
  };
5132
5500
  requestAnimationFrame(check);
5133
5501
  };
5134
- const runScrollWithPromise = (run) => new Promise((resolve) => {
5502
+ const runScrollWithPromise = (run, options) => new Promise((resolve) => {
5135
5503
  var _a3;
5136
5504
  const token = ++imperativeScrollToken;
5505
+ const shouldWaitOneFrame = !!(options == null ? void 0 : options.shouldWaitOneFrame);
5137
5506
  (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
5138
5507
  state.pendingScrollResolve = resolve;
5139
5508
  const runNow = () => {
@@ -5148,11 +5517,12 @@ function createImperativeHandle(ctx) {
5148
5517
  resolve();
5149
5518
  }
5150
5519
  };
5520
+ const execute = shouldWaitOneFrame ? () => requestAnimationFrame(runNow) : runNow;
5151
5521
  if (isSettlingAfterDataChange()) {
5152
- runWhenSettled(token, runNow);
5153
- return;
5522
+ runWhenSettled(token, execute);
5523
+ } else {
5524
+ execute();
5154
5525
  }
5155
- runNow();
5156
5526
  });
5157
5527
  const scrollIndexIntoView = (options) => {
5158
5528
  if (state) {
@@ -5209,10 +5579,13 @@ function createImperativeHandle(ctx) {
5209
5579
  },
5210
5580
  end: state.endNoBuffer,
5211
5581
  endBuffered: state.endBuffered,
5212
- isAtEnd: state.isAtEnd,
5213
- isAtStart: state.isAtStart,
5582
+ isAtEnd: peek$(ctx, "isAtEnd"),
5583
+ isAtStart: peek$(ctx, "isAtStart"),
5214
5584
  isEndReached: state.isEndReached,
5585
+ isNearEnd: peek$(ctx, "isNearEnd"),
5586
+ isNearStart: peek$(ctx, "isNearStart"),
5215
5587
  isStartReached: state.isStartReached,
5588
+ isWithinMaintainScrollAtEndThreshold: peek$(ctx, "isWithinMaintainScrollAtEndThreshold"),
5216
5589
  listen: (signalName, cb) => listen$(ctx, signalName, cb),
5217
5590
  listenToPosition: (key, cb) => listenPosition$(ctx, key, cb),
5218
5591
  positionAtIndex: (index) => state.positions[index],
@@ -5259,10 +5632,15 @@ function createImperativeHandle(ctx) {
5259
5632
  }
5260
5633
  return false;
5261
5634
  }),
5262
- scrollToIndex: (params) => runScrollWithPromise(() => {
5263
- scrollToIndex(ctx, params);
5264
- return true;
5265
- }),
5635
+ scrollToIndex: (params) => runScrollWithPromise(
5636
+ () => {
5637
+ scrollToIndex(ctx, params);
5638
+ return true;
5639
+ },
5640
+ {
5641
+ shouldWaitOneFrame: params.index >= 0 && params.index >= state.props.data.length
5642
+ }
5643
+ ),
5266
5644
  scrollToItem: ({ item, ...props }) => runScrollWithPromise(() => {
5267
5645
  const data = state.props.data;
5268
5646
  const index = data.indexOf(item);
@@ -5358,7 +5736,7 @@ function getRenderedItem(ctx, key) {
5358
5736
  item,
5359
5737
  type: getItemType ? (_a3 = getItemType(item, index)) != null ? _a3 : "" : ""
5360
5738
  };
5361
- renderedItem = isFunction(renderItem) ? renderItem(itemProps) : React3__default.createElement(renderItem, itemProps);
5739
+ renderedItem = React3__default.createElement(renderItem, itemProps);
5362
5740
  }
5363
5741
  return { index, item: data[index], renderedItem };
5364
5742
  }
@@ -5492,9 +5870,18 @@ var LegendList = typedMemo(
5492
5870
  })
5493
5871
  );
5494
5872
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
5495
- var _a3, _b, _c, _d, _e, _f, _g;
5873
+ var _a3, _b, _c, _d, _e, _f, _g, _h;
5874
+ const noopOnScroll = useCallback((_event) => {
5875
+ }, []);
5876
+ if (props.recycleItems === void 0) {
5877
+ warnDevOnce(
5878
+ "recycleItems-omitted",
5879
+ "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."
5880
+ );
5881
+ }
5496
5882
  const {
5497
5883
  alignItemsAtEnd = false,
5884
+ anchoredEndSpace,
5498
5885
  alwaysRender,
5499
5886
  columnWrapperStyle,
5500
5887
  contentContainerStyle: contentContainerStyleProp,
@@ -5560,7 +5947,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5560
5947
  const positionComponentInternal = props.positionComponentInternal;
5561
5948
  const stickyPositionComponentInternal = props.stickyPositionComponentInternal;
5562
5949
  const {
5563
- childrenMode,
5564
5950
  positionComponentInternal: _positionComponentInternal,
5565
5951
  stickyPositionComponentInternal: _stickyPositionComponentInternal,
5566
5952
  ...restProps
@@ -5624,18 +6010,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5624
6010
  dataVersion,
5625
6011
  keyExtractor
5626
6012
  ]);
5627
- if (IS_DEV && stickyIndicesDeprecated && !stickyHeaderIndicesProp) {
5628
- warnDevOnce(
5629
- "stickyIndices",
5630
- "stickyIndices has been renamed to stickyHeaderIndices. Please update your props to use stickyHeaderIndices."
5631
- );
5632
- }
5633
- if (IS_DEV && useWindowScroll && renderScrollComponent) {
5634
- warnDevOnce(
5635
- "useWindowScrollRenderScrollComponent",
5636
- "useWindowScroll is not supported when renderScrollComponent is provided."
5637
- );
5638
- }
5639
6013
  const useWindowScrollResolved = !!useWindowScroll && !renderScrollComponent;
5640
6014
  const refState = useRef(void 0);
5641
6015
  const hasOverrideItemLayout = !!overrideItemLayout;
@@ -5644,7 +6018,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5644
6018
  if (!ctx.state) {
5645
6019
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : { height: 0, width: 0 } )[horizontal ? "width" : "height"];
5646
6020
  ctx.state = {
5647
- activeStickyIndex: -1,
5648
6021
  averageSizes: {},
5649
6022
  columnSpans: [],
5650
6023
  columns: [],
@@ -5668,8 +6041,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5668
6041
  kind: initialScrollUsesOffsetOnly ? "offset" : "bootstrap",
5669
6042
  previousDataLength: dataProp.length
5670
6043
  } : void 0,
5671
- isAtEnd: false,
5672
- isAtStart: false,
5673
6044
  isEndReached: null,
5674
6045
  isFirst: true,
5675
6046
  isStartReached: null,
@@ -5680,6 +6051,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5680
6051
  minIndexSizeChanged: 0,
5681
6052
  nativeContentInset: void 0,
5682
6053
  nativeMarginTop: 0,
6054
+ pendingDataComparison: void 0,
5683
6055
  pendingNativeMVCPAdjust: void 0,
5684
6056
  positions: [],
5685
6057
  props: {},
@@ -5718,22 +6090,29 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5718
6090
  }
5719
6091
  const state = refState.current;
5720
6092
  const isFirstLocal = state.isFirst;
5721
- state.didColumnsChange = numColumnsProp !== state.props.numColumns;
6093
+ const previousNumColumnsProp = state.props.numColumns;
6094
+ state.didColumnsChange = numColumnsProp !== previousNumColumnsProp;
5722
6095
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
5723
6096
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
5724
- const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkActualChange(state, dataProp, state.props.data);
6097
+ const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
6098
+ if (didDataChangeLocal && state.didFinishInitialScroll && ((_f = state.initialScroll) == null ? void 0 : _f.viewPosition) === 1 && state.props.data.length > 0) {
6099
+ clearPreservedInitialScrollTarget(state);
6100
+ }
5725
6101
  if (didDataChangeLocal) {
5726
6102
  state.dataChangeEpoch += 1;
5727
6103
  state.dataChangeNeedsScrollUpdate = true;
5728
6104
  state.didDataChange = true;
5729
6105
  state.previousData = state.props.data;
5730
6106
  }
5731
- const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
6107
+ const throttledOnScroll = useThrottledOnScroll(onScrollProp != null ? onScrollProp : noopOnScroll, scrollEventThrottle != null ? scrollEventThrottle : 0);
6108
+ const throttleScrollFn = scrollEventThrottle && onScrollProp ? throttledOnScroll : onScrollProp;
6109
+ const anchoredEndSpaceResolved = anchoredEndSpace ? { ...anchoredEndSpace, includeInEndInset: true } : anchoredEndSpace;
5732
6110
  state.props = {
5733
6111
  alignItemsAtEnd,
5734
6112
  alwaysRender,
5735
6113
  alwaysRenderIndicesArr: alwaysRenderIndices.arr,
5736
6114
  alwaysRenderIndicesSet: alwaysRenderIndices.set,
6115
+ anchoredEndSpace: anchoredEndSpaceResolved,
5737
6116
  animatedProps: animatedPropsInternal,
5738
6117
  contentInset,
5739
6118
  data: dataProp,
@@ -5818,16 +6197,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5818
6197
  if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
5819
6198
  refState.current.lastBatchingAction = Date.now();
5820
6199
  if (!keyExtractorProp && !isFirstLocal && didDataChangeLocal) {
5821
- IS_DEV && !childrenMode && warnDevOnce(
5822
- "keyExtractor",
5823
- "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."
5824
- );
5825
6200
  refState.current.sizes.clear();
5826
6201
  refState.current.positions.length = 0;
5827
6202
  refState.current.totalSize = 0;
5828
6203
  set$(ctx, "totalSize", 0);
5829
6204
  }
5830
6205
  }
6206
+ if (IS_DEV) {
6207
+ useDevChecks(props);
6208
+ }
5831
6209
  useLayoutEffect(() => {
5832
6210
  handleInitialScrollDataChange(ctx, {
5833
6211
  dataLength: dataProp.length,
@@ -5837,6 +6215,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5837
6215
  useBootstrapInitialScroll: usesBootstrapInitialScroll
5838
6216
  });
5839
6217
  }, [dataProp.length, didDataChangeLocal, initialScrollAtEnd, stylePaddingBottomState, usesBootstrapInitialScroll]);
6218
+ useLayoutEffect(() => {
6219
+ maybeUpdateAnchoredEndSpace(ctx);
6220
+ }, [
6221
+ ctx,
6222
+ dataProp,
6223
+ dataVersion,
6224
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex,
6225
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorMaxSize,
6226
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorOffset,
6227
+ numColumnsProp
6228
+ ]);
5840
6229
  const onLayoutFooter = useCallback(
5841
6230
  (layout) => {
5842
6231
  if (!usesBootstrapInitialScroll) {
@@ -5852,14 +6241,21 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5852
6241
  [dataProp.length, initialScrollAtEnd, horizontal, stylePaddingBottomState, usesBootstrapInitialScroll]
5853
6242
  );
5854
6243
  const onLayoutChange = useCallback(
5855
- (layout) => {
6244
+ (layout, fromLayoutEffect) => {
6245
+ const previousScrollLength = state.scrollLength;
6246
+ const previousOtherAxisSize = state.otherAxisSize;
5856
6247
  handleLayout(ctx, layout, setCanRender);
6248
+ maybeUpdateAnchoredEndSpace(ctx);
6249
+ const didLayoutAffectBootstrapTarget = previousScrollLength !== state.scrollLength || previousOtherAxisSize !== state.otherAxisSize;
6250
+ if (usesBootstrapInitialScroll && !fromLayoutEffect && didLayoutAffectBootstrapTarget) {
6251
+ handleBootstrapInitialScrollLayoutChange(ctx);
6252
+ }
5857
6253
  if (usesBootstrapInitialScroll) {
5858
6254
  return;
5859
6255
  }
5860
6256
  advanceCurrentInitialScrollSession(ctx);
5861
6257
  },
5862
- [usesBootstrapInitialScroll]
6258
+ [dataProp.length, initialScrollAtEnd, stylePaddingBottomState, usesBootstrapInitialScroll]
5863
6259
  );
5864
6260
  const { onLayout } = useOnLayoutSync({
5865
6261
  onLayoutChange,
@@ -5872,6 +6268,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5872
6268
  updateSnapToOffsets(ctx);
5873
6269
  }
5874
6270
  }, [snapToIndices]);
6271
+ useLayoutEffect(
6272
+ () => initializeStateVars(true),
6273
+ [dataVersion, memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingBottomState, stylePaddingTopState]
6274
+ );
5875
6275
  useLayoutEffect(() => {
5876
6276
  const {
5877
6277
  didColumnsChange,
@@ -5881,7 +6281,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5881
6281
  } = state;
5882
6282
  const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx);
5883
6283
  if (!didAllocateContainers && !isFirst && (didDataChange || didColumnsChange)) {
5884
- checkResetContainers(ctx, data);
6284
+ checkResetContainers(ctx, data, { didColumnsChange });
6285
+ }
6286
+ if (didDataChange) {
6287
+ state.pendingDataComparison = void 0;
5885
6288
  }
5886
6289
  state.didColumnsChange = false;
5887
6290
  state.didDataChange = false;
@@ -5896,10 +6299,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5896
6299
  (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { forceFullItemPositions: true });
5897
6300
  }
5898
6301
  }, [extraData, hasOverrideItemLayout, numColumnsProp]);
5899
- useLayoutEffect(
5900
- () => initializeStateVars(true),
5901
- [dataVersion, memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingBottomState, stylePaddingTopState]
5902
- );
5903
6302
  useEffect(() => {
5904
6303
  if (!onMetricsChange) {
5905
6304
  return;
@@ -5932,15 +6331,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5932
6331
  state.viewabilityConfigCallbackPairs = viewability;
5933
6332
  state.enableScrollForNextCalculateItemsInView = !viewability;
5934
6333
  }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
6334
+ useInit(() => {
6335
+ });
5935
6336
  useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx), []);
5936
- {
5937
- useEffect(() => {
5938
- if (usesBootstrapInitialScroll) {
5939
- return;
5940
- }
5941
- advanceCurrentInitialScrollSession(ctx);
5942
- }, [usesBootstrapInitialScroll]);
5943
- }
6337
+ useEffect(() => {
6338
+ if (usesBootstrapInitialScroll) {
6339
+ return;
6340
+ }
6341
+ advanceCurrentInitialScrollSession(ctx);
6342
+ }, [ctx, usesBootstrapInitialScroll]);
5944
6343
  const fns = useMemo(
5945
6344
  () => ({
5946
6345
  getRenderedItem: (key) => getRenderedItem(ctx, key),
@@ -5978,7 +6377,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5978
6377
  onScroll: onScrollHandler,
5979
6378
  recycleItems,
5980
6379
  refreshControl: refreshControlElement ? stylePaddingTopState > 0 ? React3.cloneElement(refreshControlElement, {
5981
- progressViewOffset: ((_f = refreshControlElement.props.progressViewOffset) != null ? _f : 0) + stylePaddingTopState
6380
+ progressViewOffset: ((_g = refreshControlElement.props.progressViewOffset) != null ? _g : 0) + stylePaddingTopState
5982
6381
  }) : refreshControlElement : onRefresh && /* @__PURE__ */ React3.createElement(
5983
6382
  RefreshControl,
5984
6383
  {
@@ -5989,7 +6388,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5989
6388
  ),
5990
6389
  refScrollView: combinedRef,
5991
6390
  renderScrollComponent,
5992
- scrollAdjustHandler: (_g = refState.current) == null ? void 0 : _g.scrollAdjustHandler,
6391
+ scrollAdjustHandler: (_h = refState.current) == null ? void 0 : _h.scrollAdjustHandler,
5993
6392
  scrollEventThrottle: 0,
5994
6393
  snapToIndices,
5995
6394
  stickyHeaderIndices,
@@ -6000,13 +6399,40 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6000
6399
  ), IS_DEV && ENABLE_DEBUG_VIEW);
6001
6400
  });
6002
6401
 
6402
+ // src/components/stickyPositionUtils.ts
6403
+ function getStickyPushLimit(state, index, itemKey) {
6404
+ if (!itemKey) {
6405
+ return void 0;
6406
+ }
6407
+ const currentSize = state.sizes.get(itemKey);
6408
+ if (!(currentSize && currentSize > 0)) {
6409
+ return void 0;
6410
+ }
6411
+ const stickyIndexInArray = state.props.stickyIndicesArr.indexOf(index);
6412
+ if (stickyIndexInArray === -1) {
6413
+ return void 0;
6414
+ }
6415
+ const nextStickyIndex = state.props.stickyIndicesArr[stickyIndexInArray + 1];
6416
+ if (nextStickyIndex === void 0) {
6417
+ return void 0;
6418
+ }
6419
+ const nextStickyPosition = state.positions[nextStickyIndex];
6420
+ if (nextStickyPosition === void 0) {
6421
+ return void 0;
6422
+ }
6423
+ return nextStickyPosition - currentSize;
6424
+ }
6425
+
6003
6426
  // src/entrypoints/shared.ts
6004
6427
  var LegendListRuntime = LegendList;
6005
6428
  var internal = {
6006
6429
  getComponent,
6430
+ getStickyPushLimit,
6007
6431
  IsNewArchitecture,
6008
6432
  POSITION_OUT_OF_VIEW,
6009
6433
  peek$,
6434
+ typedForwardRef,
6435
+ typedMemo,
6010
6436
  useArr$,
6011
6437
  useCombinedRef,
6012
6438
  useStateContext
@@ -6016,4 +6442,4 @@ var internal = {
6016
6442
  var LegendList3 = LegendListRuntime;
6017
6443
  var internal2 = internal;
6018
6444
 
6019
- export { LegendList3 as LegendList, internal2 as internal, typedForwardRef, typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };
6445
+ export { LegendList3 as LegendList, internal2 as internal, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };