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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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
  };
@@ -2035,186 +2437,13 @@ function scrollToIndex(ctx, {
2035
2437
  index,
2036
2438
  isInitialScroll,
2037
2439
  itemSize,
2038
- offset: firstIndexOffset,
2039
- viewOffset,
2040
- viewPosition: viewPosition != null ? viewPosition : 0
2041
- });
2042
- }
2043
-
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
- }
2440
+ offset: firstIndexOffset,
2441
+ viewOffset,
2442
+ viewPosition: viewPosition != null ? viewPosition : 0
2443
+ });
2210
2444
  }
2211
2445
 
2212
2446
  // src/core/initialScroll.ts
2213
- function syncInitialScrollOffset(state, offset) {
2214
- state.scroll = offset;
2215
- state.scrollPending = offset;
2216
- state.scrollPrev = offset;
2217
- }
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) {
@@ -4080,7 +4441,6 @@ function calculateItemsInView(ctx, params = {}) {
4080
4441
  alwaysRenderIndicesSet,
4081
4442
  drawDistance,
4082
4443
  getItemType,
4083
- itemsAreEqual,
4084
4444
  keyExtractor,
4085
4445
  onStickyHeaderChange
4086
4446
  },
@@ -4107,11 +4467,11 @@ function calculateItemsInView(ctx, params = {}) {
4107
4467
  const numColumns = peek$(ctx, "numColumns");
4108
4468
  const speed = getScrollVelocity(state);
4109
4469
  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 ? (
4470
+ const { initialScroll, queuedInitialLayout } = state;
4471
+ const scrollState = suppressInitialScrollSideEffects ? (_b = bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.scroll) != null ? _b : state.scroll : !queuedInitialLayout && hasActiveInitialScroll(state) && initialScroll ? (
4112
4472
  // Before the initial layout settles, keep viewport math anchored to the
4113
4473
  // current initial-scroll target instead of transient native adjustments.
4114
- resolveInitialScrollOffset(ctx, state.initialScroll)
4474
+ resolveInitialScrollOffset(ctx, initialScroll)
4115
4475
  ) : state.scroll;
4116
4476
  const scrollAdjustPending = (_c = peek$(ctx, "scrollAdjustPending")) != null ? _c : 0;
4117
4477
  const scrollAdjustPad = scrollAdjustPending - topPad;
@@ -4156,9 +4516,11 @@ function calculateItemsInView(ctx, params = {}) {
4156
4516
  columnSpans.length = 0;
4157
4517
  }
4158
4518
  const startIndex = forceFullItemPositions || dataChanged ? 0 : (_d = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _d : 0;
4519
+ const optimizeForVisibleWindow = !forceFullItemPositions && !dataChanged && numColumns > 1 && minIndexSizeChanged !== void 0;
4159
4520
  updateItemPositions(ctx, dataChanged, {
4160
4521
  doMVCP,
4161
4522
  forceFullUpdate: !!forceFullItemPositions,
4523
+ optimizeForVisibleWindow,
4162
4524
  scrollBottomBuffered,
4163
4525
  startIndex
4164
4526
  });
@@ -4406,33 +4768,11 @@ function calculateItemsInView(ctx, params = {}) {
4406
4768
  set$(ctx, `containerSpan${i}`, 1);
4407
4769
  } else {
4408
4770
  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
- }
4771
+ if (itemIndex !== void 0) {
4772
+ didChangePositions = syncMountedContainer(ctx, i, itemIndex, {
4773
+ scrollAdjustPending,
4774
+ updateLayout: true
4775
+ }).didChangePosition || didChangePositions;
4436
4776
  }
4437
4777
  }
4438
4778
  }
@@ -4443,7 +4783,10 @@ function calculateItemsInView(ctx, params = {}) {
4443
4783
  evaluateBootstrapInitialScroll(ctx);
4444
4784
  return;
4445
4785
  }
4446
- if (!queuedInitialLayout && endBuffered !== null && checkAllSizesKnown(state)) {
4786
+ const mountedBufferedIndices = getMountedBufferedIndices(state);
4787
+ const mountedNoBufferIndices = getMountedNoBufferIndices(state);
4788
+ const readinessIndices = hasActiveInitialScroll(state) ? mountedBufferedIndices : mountedNoBufferIndices.length > 0 ? mountedNoBufferIndices : mountedBufferedIndices;
4789
+ if (!queuedInitialLayout && readinessIndices.length > 0 && checkAllSizesKnown(state, readinessIndices)) {
4447
4790
  setDidLayout(ctx);
4448
4791
  handleInitialScrollLayoutReady(ctx);
4449
4792
  }
@@ -4454,7 +4797,9 @@ function calculateItemsInView(ctx, params = {}) {
4454
4797
  viewabilityConfigCallbackPairs,
4455
4798
  scrollLength,
4456
4799
  startNoBuffer,
4457
- endNoBuffer
4800
+ endNoBuffer,
4801
+ startBuffered != null ? startBuffered : startNoBuffer,
4802
+ endBuffered != null ? endBuffered : endNoBuffer
4458
4803
  );
4459
4804
  }
4460
4805
  if (onStickyHeaderChange && stickyIndicesArr.length > 0 && nextActiveStickyIndex !== void 0 && nextActiveStickyIndex !== previousStickyIndex) {
@@ -4466,37 +4811,17 @@ function calculateItemsInView(ctx, params = {}) {
4466
4811
  });
4467
4812
  }
4468
4813
 
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
4814
  // src/core/doMaintainScrollAtEnd.ts
4490
4815
  function doMaintainScrollAtEnd(ctx) {
4491
4816
  const state = ctx.state;
4492
4817
  const {
4493
4818
  didContainersLayout,
4494
- isAtEnd,
4495
4819
  pendingNativeMVCPAdjust,
4496
4820
  refScroller,
4497
4821
  props: { maintainScrollAtEnd }
4498
4822
  } = state;
4499
- const shouldMaintainScrollAtEnd = !!(isAtEnd && maintainScrollAtEnd && didContainersLayout);
4823
+ const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
4824
+ const shouldMaintainScrollAtEnd = !!(isWithinMaintainScrollAtEndThreshold && maintainScrollAtEnd && didContainersLayout);
4500
4825
  if (pendingNativeMVCPAdjust) {
4501
4826
  state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
4502
4827
  return false;
@@ -4509,7 +4834,7 @@ function doMaintainScrollAtEnd(ctx) {
4509
4834
  }
4510
4835
  requestAnimationFrame(() => {
4511
4836
  var _a3;
4512
- if (state.isAtEnd) {
4837
+ if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
4513
4838
  state.maintainingScrollAtEnd = true;
4514
4839
  (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
4515
4840
  animated: maintainScrollAtEnd.animated
@@ -4527,68 +4852,22 @@ function doMaintainScrollAtEnd(ctx) {
4527
4852
  return false;
4528
4853
  }
4529
4854
 
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
4855
  // src/core/checkResetContainers.ts
4583
- function checkResetContainers(ctx, dataProp) {
4856
+ function checkResetContainers(ctx, dataProp, { didColumnsChange = false } = {}) {
4584
4857
  const state = ctx.state;
4585
4858
  const { previousData } = state;
4586
- if (previousData) {
4587
- updateAveragesOnDataChange(state, previousData, dataProp);
4588
- }
4589
4859
  const { maintainScrollAtEnd } = state.props;
4860
+ if (didColumnsChange) {
4861
+ state.sizes.clear();
4862
+ state.sizesKnown.clear();
4863
+ for (const key in state.averageSizes) {
4864
+ delete state.averageSizes[key];
4865
+ }
4866
+ state.minIndexSizeChanged = 0;
4867
+ state.scrollForNextCalculateItemsInView = void 0;
4868
+ }
4590
4869
  calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
4591
- const shouldMaintainScrollAtEnd = maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onDataChange;
4870
+ const shouldMaintainScrollAtEnd = !didColumnsChange && (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onDataChange);
4592
4871
  const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx);
4593
4872
  if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
4594
4873
  state.isEndReached = false;
@@ -4599,6 +4878,53 @@ function checkResetContainers(ctx, dataProp) {
4599
4878
  delete state.previousData;
4600
4879
  }
4601
4880
 
4881
+ // src/core/checkStructuralDataChange.ts
4882
+ function checkStructuralDataChange(state, dataProp, previousData) {
4883
+ var _a3;
4884
+ state.pendingDataComparison = void 0;
4885
+ if (!previousData || !dataProp || dataProp.length !== previousData.length) {
4886
+ return true;
4887
+ }
4888
+ const {
4889
+ idCache,
4890
+ props: { itemsAreEqual, keyExtractor }
4891
+ } = state;
4892
+ let byIndex;
4893
+ for (let i = 0; i < dataProp.length; i++) {
4894
+ if (dataProp[i] === previousData[i]) {
4895
+ continue;
4896
+ }
4897
+ if (!keyExtractor) {
4898
+ if (byIndex) {
4899
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4900
+ }
4901
+ return true;
4902
+ }
4903
+ const previousKey = (_a3 = idCache[i]) != null ? _a3 : keyExtractor(previousData[i], i);
4904
+ const nextKey = keyExtractor(dataProp[i], i);
4905
+ if (previousKey !== nextKey) {
4906
+ if (byIndex) {
4907
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4908
+ }
4909
+ return true;
4910
+ }
4911
+ if (!itemsAreEqual) {
4912
+ if (byIndex) {
4913
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4914
+ }
4915
+ return true;
4916
+ }
4917
+ const isEqual = itemsAreEqual(previousData[i], dataProp[i], i, dataProp);
4918
+ byIndex != null ? byIndex : byIndex = [];
4919
+ byIndex[i] = isEqual ? 1 : 2;
4920
+ if (!isEqual) {
4921
+ state.pendingDataComparison = { byIndex, nextData: dataProp, previousData };
4922
+ return true;
4923
+ }
4924
+ }
4925
+ return false;
4926
+ }
4927
+
4602
4928
  // src/core/doInitialAllocateContainers.ts
4603
4929
  function doInitialAllocateContainers(ctx) {
4604
4930
  var _a3, _b, _c;
@@ -4842,6 +5168,7 @@ function onScroll(ctx, event) {
4842
5168
  state.scrollPending = newScroll;
4843
5169
  updateScroll(ctx, newScroll, insetChanged);
4844
5170
  trackInitialScrollNativeProgress(state, newScroll);
5171
+ clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx);
4845
5172
  if (state.scrollingTo) {
4846
5173
  checkFinishedScroll(ctx);
4847
5174
  }
@@ -4905,6 +5232,43 @@ var ScrollAdjustHandler = class {
4905
5232
  }
4906
5233
  };
4907
5234
 
5235
+ // src/core/updateAnchoredEndSpace.ts
5236
+ function maybeUpdateAnchoredEndSpace(ctx) {
5237
+ var _a3;
5238
+ const state = ctx.state;
5239
+ const anchoredEndSpace = state.props.anchoredEndSpace;
5240
+ const previousSize = peek$(ctx, "anchoredEndSpaceSize");
5241
+ let nextSize = 0;
5242
+ if (anchoredEndSpace) {
5243
+ const { anchorIndex, anchorMaxSize, anchorOffset = 0 } = anchoredEndSpace;
5244
+ const { data } = state.props;
5245
+ if (anchorIndex >= 0 && anchorIndex < data.length && state.scrollLength > 0) {
5246
+ let contentBelowAnchor = 0;
5247
+ const footerSize = ctx.values.get("footerSize") || 0;
5248
+ const stylePaddingBottom = state.props.stylePaddingBottom || 0;
5249
+ for (let index = anchorIndex; index < data.length; index++) {
5250
+ const itemKey = getId(state, index);
5251
+ const size = itemKey ? state.sizesKnown.get(itemKey) : void 0;
5252
+ const effectiveSize = index === anchorIndex && anchorMaxSize !== void 0 ? Math.min(size || 0, Math.max(0, anchorMaxSize)) : size;
5253
+ if (effectiveSize !== null && effectiveSize !== void 0 && effectiveSize > 0) {
5254
+ contentBelowAnchor += effectiveSize;
5255
+ }
5256
+ }
5257
+ contentBelowAnchor += footerSize + stylePaddingBottom;
5258
+ nextSize = Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
5259
+ }
5260
+ }
5261
+ if (previousSize === nextSize) {
5262
+ return nextSize;
5263
+ }
5264
+ set$(ctx, "anchoredEndSpaceSize", nextSize);
5265
+ (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
5266
+ if (anchoredEndSpace == null ? void 0 : anchoredEndSpace.includeInEndInset) {
5267
+ updateScroll(ctx, state.scroll, true);
5268
+ }
5269
+ return nextSize;
5270
+ }
5271
+
4908
5272
  // src/core/updateItemSize.ts
4909
5273
  function runOrScheduleMVCPRecalculate(ctx) {
4910
5274
  const state = ctx.state;
@@ -4986,6 +5350,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
4986
5350
  previous: size - diff,
4987
5351
  size
4988
5352
  });
5353
+ maybeUpdateAnchoredEndSpace(ctx);
4989
5354
  }
4990
5355
  if (minIndexSizeChanged !== void 0) {
4991
5356
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
@@ -5109,7 +5474,7 @@ function createImperativeHandle(ctx) {
5109
5474
  const IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS = 800;
5110
5475
  const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
5111
5476
  let imperativeScrollToken = 0;
5112
- const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0 || hasActiveMVCPAnchorLock(state);
5477
+ const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0;
5113
5478
  const runWhenSettled = (token, run) => {
5114
5479
  const startedAt = Date.now();
5115
5480
  let stableFrames = 0;
@@ -5131,9 +5496,10 @@ function createImperativeHandle(ctx) {
5131
5496
  };
5132
5497
  requestAnimationFrame(check);
5133
5498
  };
5134
- const runScrollWithPromise = (run) => new Promise((resolve) => {
5499
+ const runScrollWithPromise = (run, options) => new Promise((resolve) => {
5135
5500
  var _a3;
5136
5501
  const token = ++imperativeScrollToken;
5502
+ const shouldWaitOneFrame = !!(options == null ? void 0 : options.shouldWaitOneFrame);
5137
5503
  (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
5138
5504
  state.pendingScrollResolve = resolve;
5139
5505
  const runNow = () => {
@@ -5148,11 +5514,12 @@ function createImperativeHandle(ctx) {
5148
5514
  resolve();
5149
5515
  }
5150
5516
  };
5517
+ const execute = shouldWaitOneFrame ? () => requestAnimationFrame(runNow) : runNow;
5151
5518
  if (isSettlingAfterDataChange()) {
5152
- runWhenSettled(token, runNow);
5153
- return;
5519
+ runWhenSettled(token, execute);
5520
+ } else {
5521
+ execute();
5154
5522
  }
5155
- runNow();
5156
5523
  });
5157
5524
  const scrollIndexIntoView = (options) => {
5158
5525
  if (state) {
@@ -5209,10 +5576,13 @@ function createImperativeHandle(ctx) {
5209
5576
  },
5210
5577
  end: state.endNoBuffer,
5211
5578
  endBuffered: state.endBuffered,
5212
- isAtEnd: state.isAtEnd,
5213
- isAtStart: state.isAtStart,
5579
+ isAtEnd: peek$(ctx, "isAtEnd"),
5580
+ isAtStart: peek$(ctx, "isAtStart"),
5214
5581
  isEndReached: state.isEndReached,
5582
+ isNearEnd: peek$(ctx, "isNearEnd"),
5583
+ isNearStart: peek$(ctx, "isNearStart"),
5215
5584
  isStartReached: state.isStartReached,
5585
+ isWithinMaintainScrollAtEndThreshold: peek$(ctx, "isWithinMaintainScrollAtEndThreshold"),
5216
5586
  listen: (signalName, cb) => listen$(ctx, signalName, cb),
5217
5587
  listenToPosition: (key, cb) => listenPosition$(ctx, key, cb),
5218
5588
  positionAtIndex: (index) => state.positions[index],
@@ -5259,10 +5629,15 @@ function createImperativeHandle(ctx) {
5259
5629
  }
5260
5630
  return false;
5261
5631
  }),
5262
- scrollToIndex: (params) => runScrollWithPromise(() => {
5263
- scrollToIndex(ctx, params);
5264
- return true;
5265
- }),
5632
+ scrollToIndex: (params) => runScrollWithPromise(
5633
+ () => {
5634
+ scrollToIndex(ctx, params);
5635
+ return true;
5636
+ },
5637
+ {
5638
+ shouldWaitOneFrame: params.index >= 0 && params.index >= state.props.data.length
5639
+ }
5640
+ ),
5266
5641
  scrollToItem: ({ item, ...props }) => runScrollWithPromise(() => {
5267
5642
  const data = state.props.data;
5268
5643
  const index = data.indexOf(item);
@@ -5358,7 +5733,7 @@ function getRenderedItem(ctx, key) {
5358
5733
  item,
5359
5734
  type: getItemType ? (_a3 = getItemType(item, index)) != null ? _a3 : "" : ""
5360
5735
  };
5361
- renderedItem = isFunction(renderItem) ? renderItem(itemProps) : React3__default.createElement(renderItem, itemProps);
5736
+ renderedItem = React3__default.createElement(renderItem, itemProps);
5362
5737
  }
5363
5738
  return { index, item: data[index], renderedItem };
5364
5739
  }
@@ -5492,9 +5867,18 @@ var LegendList = typedMemo(
5492
5867
  })
5493
5868
  );
5494
5869
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
5495
- var _a3, _b, _c, _d, _e, _f, _g;
5870
+ var _a3, _b, _c, _d, _e, _f, _g, _h;
5871
+ const noopOnScroll = useCallback((_event) => {
5872
+ }, []);
5873
+ if (props.recycleItems === void 0) {
5874
+ warnDevOnce(
5875
+ "recycleItems-omitted",
5876
+ "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."
5877
+ );
5878
+ }
5496
5879
  const {
5497
5880
  alignItemsAtEnd = false,
5881
+ anchoredEndSpace,
5498
5882
  alwaysRender,
5499
5883
  columnWrapperStyle,
5500
5884
  contentContainerStyle: contentContainerStyleProp,
@@ -5560,7 +5944,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5560
5944
  const positionComponentInternal = props.positionComponentInternal;
5561
5945
  const stickyPositionComponentInternal = props.stickyPositionComponentInternal;
5562
5946
  const {
5563
- childrenMode,
5564
5947
  positionComponentInternal: _positionComponentInternal,
5565
5948
  stickyPositionComponentInternal: _stickyPositionComponentInternal,
5566
5949
  ...restProps
@@ -5624,18 +6007,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5624
6007
  dataVersion,
5625
6008
  keyExtractor
5626
6009
  ]);
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
6010
  const useWindowScrollResolved = !!useWindowScroll && !renderScrollComponent;
5640
6011
  const refState = useRef(void 0);
5641
6012
  const hasOverrideItemLayout = !!overrideItemLayout;
@@ -5644,7 +6015,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5644
6015
  if (!ctx.state) {
5645
6016
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : { height: 0, width: 0 } )[horizontal ? "width" : "height"];
5646
6017
  ctx.state = {
5647
- activeStickyIndex: -1,
5648
6018
  averageSizes: {},
5649
6019
  columnSpans: [],
5650
6020
  columns: [],
@@ -5668,8 +6038,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5668
6038
  kind: initialScrollUsesOffsetOnly ? "offset" : "bootstrap",
5669
6039
  previousDataLength: dataProp.length
5670
6040
  } : void 0,
5671
- isAtEnd: false,
5672
- isAtStart: false,
5673
6041
  isEndReached: null,
5674
6042
  isFirst: true,
5675
6043
  isStartReached: null,
@@ -5680,6 +6048,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5680
6048
  minIndexSizeChanged: 0,
5681
6049
  nativeContentInset: void 0,
5682
6050
  nativeMarginTop: 0,
6051
+ pendingDataComparison: void 0,
5683
6052
  pendingNativeMVCPAdjust: void 0,
5684
6053
  positions: [],
5685
6054
  props: {},
@@ -5718,22 +6087,29 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5718
6087
  }
5719
6088
  const state = refState.current;
5720
6089
  const isFirstLocal = state.isFirst;
5721
- state.didColumnsChange = numColumnsProp !== state.props.numColumns;
6090
+ const previousNumColumnsProp = state.props.numColumns;
6091
+ state.didColumnsChange = numColumnsProp !== previousNumColumnsProp;
5722
6092
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
5723
6093
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
5724
- const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkActualChange(state, dataProp, state.props.data);
6094
+ const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
6095
+ if (didDataChangeLocal && state.didFinishInitialScroll && ((_f = state.initialScroll) == null ? void 0 : _f.viewPosition) === 1 && state.props.data.length > 0) {
6096
+ clearPreservedInitialScrollTarget(state);
6097
+ }
5725
6098
  if (didDataChangeLocal) {
5726
6099
  state.dataChangeEpoch += 1;
5727
6100
  state.dataChangeNeedsScrollUpdate = true;
5728
6101
  state.didDataChange = true;
5729
6102
  state.previousData = state.props.data;
5730
6103
  }
5731
- const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
6104
+ const throttledOnScroll = useThrottledOnScroll(onScrollProp != null ? onScrollProp : noopOnScroll, scrollEventThrottle != null ? scrollEventThrottle : 0);
6105
+ const throttleScrollFn = scrollEventThrottle && onScrollProp ? throttledOnScroll : onScrollProp;
6106
+ const anchoredEndSpaceResolved = anchoredEndSpace ? { ...anchoredEndSpace, includeInEndInset: true } : anchoredEndSpace;
5732
6107
  state.props = {
5733
6108
  alignItemsAtEnd,
5734
6109
  alwaysRender,
5735
6110
  alwaysRenderIndicesArr: alwaysRenderIndices.arr,
5736
6111
  alwaysRenderIndicesSet: alwaysRenderIndices.set,
6112
+ anchoredEndSpace: anchoredEndSpaceResolved,
5737
6113
  animatedProps: animatedPropsInternal,
5738
6114
  contentInset,
5739
6115
  data: dataProp,
@@ -5818,16 +6194,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5818
6194
  if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
5819
6195
  refState.current.lastBatchingAction = Date.now();
5820
6196
  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
6197
  refState.current.sizes.clear();
5826
6198
  refState.current.positions.length = 0;
5827
6199
  refState.current.totalSize = 0;
5828
6200
  set$(ctx, "totalSize", 0);
5829
6201
  }
5830
6202
  }
6203
+ if (IS_DEV) {
6204
+ useDevChecks(props);
6205
+ }
5831
6206
  useLayoutEffect(() => {
5832
6207
  handleInitialScrollDataChange(ctx, {
5833
6208
  dataLength: dataProp.length,
@@ -5837,6 +6212,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5837
6212
  useBootstrapInitialScroll: usesBootstrapInitialScroll
5838
6213
  });
5839
6214
  }, [dataProp.length, didDataChangeLocal, initialScrollAtEnd, stylePaddingBottomState, usesBootstrapInitialScroll]);
6215
+ useLayoutEffect(() => {
6216
+ maybeUpdateAnchoredEndSpace(ctx);
6217
+ }, [
6218
+ ctx,
6219
+ dataProp,
6220
+ dataVersion,
6221
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex,
6222
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorMaxSize,
6223
+ anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorOffset,
6224
+ numColumnsProp
6225
+ ]);
5840
6226
  const onLayoutFooter = useCallback(
5841
6227
  (layout) => {
5842
6228
  if (!usesBootstrapInitialScroll) {
@@ -5852,14 +6238,21 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5852
6238
  [dataProp.length, initialScrollAtEnd, horizontal, stylePaddingBottomState, usesBootstrapInitialScroll]
5853
6239
  );
5854
6240
  const onLayoutChange = useCallback(
5855
- (layout) => {
6241
+ (layout, fromLayoutEffect) => {
6242
+ const previousScrollLength = state.scrollLength;
6243
+ const previousOtherAxisSize = state.otherAxisSize;
5856
6244
  handleLayout(ctx, layout, setCanRender);
6245
+ maybeUpdateAnchoredEndSpace(ctx);
6246
+ const didLayoutAffectBootstrapTarget = previousScrollLength !== state.scrollLength || previousOtherAxisSize !== state.otherAxisSize;
6247
+ if (usesBootstrapInitialScroll && !fromLayoutEffect && didLayoutAffectBootstrapTarget) {
6248
+ handleBootstrapInitialScrollLayoutChange(ctx);
6249
+ }
5857
6250
  if (usesBootstrapInitialScroll) {
5858
6251
  return;
5859
6252
  }
5860
6253
  advanceCurrentInitialScrollSession(ctx);
5861
6254
  },
5862
- [usesBootstrapInitialScroll]
6255
+ [dataProp.length, initialScrollAtEnd, stylePaddingBottomState, usesBootstrapInitialScroll]
5863
6256
  );
5864
6257
  const { onLayout } = useOnLayoutSync({
5865
6258
  onLayoutChange,
@@ -5872,6 +6265,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5872
6265
  updateSnapToOffsets(ctx);
5873
6266
  }
5874
6267
  }, [snapToIndices]);
6268
+ useLayoutEffect(
6269
+ () => initializeStateVars(true),
6270
+ [dataVersion, memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingBottomState, stylePaddingTopState]
6271
+ );
5875
6272
  useLayoutEffect(() => {
5876
6273
  const {
5877
6274
  didColumnsChange,
@@ -5881,7 +6278,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5881
6278
  } = state;
5882
6279
  const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx);
5883
6280
  if (!didAllocateContainers && !isFirst && (didDataChange || didColumnsChange)) {
5884
- checkResetContainers(ctx, data);
6281
+ checkResetContainers(ctx, data, { didColumnsChange });
6282
+ }
6283
+ if (didDataChange) {
6284
+ state.pendingDataComparison = void 0;
5885
6285
  }
5886
6286
  state.didColumnsChange = false;
5887
6287
  state.didDataChange = false;
@@ -5896,10 +6296,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5896
6296
  (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { forceFullItemPositions: true });
5897
6297
  }
5898
6298
  }, [extraData, hasOverrideItemLayout, numColumnsProp]);
5899
- useLayoutEffect(
5900
- () => initializeStateVars(true),
5901
- [dataVersion, memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingBottomState, stylePaddingTopState]
5902
- );
5903
6299
  useEffect(() => {
5904
6300
  if (!onMetricsChange) {
5905
6301
  return;
@@ -5932,15 +6328,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5932
6328
  state.viewabilityConfigCallbackPairs = viewability;
5933
6329
  state.enableScrollForNextCalculateItemsInView = !viewability;
5934
6330
  }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
6331
+ useInit(() => {
6332
+ });
5935
6333
  useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx), []);
5936
- {
5937
- useEffect(() => {
5938
- if (usesBootstrapInitialScroll) {
5939
- return;
5940
- }
5941
- advanceCurrentInitialScrollSession(ctx);
5942
- }, [usesBootstrapInitialScroll]);
5943
- }
6334
+ useEffect(() => {
6335
+ if (usesBootstrapInitialScroll) {
6336
+ return;
6337
+ }
6338
+ advanceCurrentInitialScrollSession(ctx);
6339
+ }, [ctx, usesBootstrapInitialScroll]);
5944
6340
  const fns = useMemo(
5945
6341
  () => ({
5946
6342
  getRenderedItem: (key) => getRenderedItem(ctx, key),
@@ -5978,7 +6374,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5978
6374
  onScroll: onScrollHandler,
5979
6375
  recycleItems,
5980
6376
  refreshControl: refreshControlElement ? stylePaddingTopState > 0 ? React3.cloneElement(refreshControlElement, {
5981
- progressViewOffset: ((_f = refreshControlElement.props.progressViewOffset) != null ? _f : 0) + stylePaddingTopState
6377
+ progressViewOffset: ((_g = refreshControlElement.props.progressViewOffset) != null ? _g : 0) + stylePaddingTopState
5982
6378
  }) : refreshControlElement : onRefresh && /* @__PURE__ */ React3.createElement(
5983
6379
  RefreshControl,
5984
6380
  {
@@ -5989,7 +6385,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5989
6385
  ),
5990
6386
  refScrollView: combinedRef,
5991
6387
  renderScrollComponent,
5992
- scrollAdjustHandler: (_g = refState.current) == null ? void 0 : _g.scrollAdjustHandler,
6388
+ scrollAdjustHandler: (_h = refState.current) == null ? void 0 : _h.scrollAdjustHandler,
5993
6389
  scrollEventThrottle: 0,
5994
6390
  snapToIndices,
5995
6391
  stickyHeaderIndices,
@@ -6000,13 +6396,40 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6000
6396
  ), IS_DEV && ENABLE_DEBUG_VIEW);
6001
6397
  });
6002
6398
 
6399
+ // src/components/stickyPositionUtils.ts
6400
+ function getStickyPushLimit(state, index, itemKey) {
6401
+ if (!itemKey) {
6402
+ return void 0;
6403
+ }
6404
+ const currentSize = state.sizes.get(itemKey);
6405
+ if (!(currentSize && currentSize > 0)) {
6406
+ return void 0;
6407
+ }
6408
+ const stickyIndexInArray = state.props.stickyIndicesArr.indexOf(index);
6409
+ if (stickyIndexInArray === -1) {
6410
+ return void 0;
6411
+ }
6412
+ const nextStickyIndex = state.props.stickyIndicesArr[stickyIndexInArray + 1];
6413
+ if (nextStickyIndex === void 0) {
6414
+ return void 0;
6415
+ }
6416
+ const nextStickyPosition = state.positions[nextStickyIndex];
6417
+ if (nextStickyPosition === void 0) {
6418
+ return void 0;
6419
+ }
6420
+ return nextStickyPosition - currentSize;
6421
+ }
6422
+
6003
6423
  // src/entrypoints/shared.ts
6004
6424
  var LegendListRuntime = LegendList;
6005
6425
  var internal = {
6006
6426
  getComponent,
6427
+ getStickyPushLimit,
6007
6428
  IsNewArchitecture,
6008
6429
  POSITION_OUT_OF_VIEW,
6009
6430
  peek$,
6431
+ typedForwardRef,
6432
+ typedMemo,
6010
6433
  useArr$,
6011
6434
  useCombinedRef,
6012
6435
  useStateContext
@@ -6016,4 +6439,4 @@ var internal = {
6016
6439
  var LegendList3 = LegendListRuntime;
6017
6440
  var internal2 = internal;
6018
6441
 
6019
- export { LegendList3 as LegendList, internal2 as internal, typedForwardRef, typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };
6442
+ export { LegendList3 as LegendList, internal2 as internal, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };