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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.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;
@@ -685,17 +702,20 @@ var Container = typedMemo(function Container2({
685
702
  const { columnGap, rowGap, gap } = columnWrapperStyle;
686
703
  if (horizontal) {
687
704
  paddingStyles = {
705
+ paddingBottom: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0,
688
706
  paddingRight: columnGap || gap || void 0,
689
- paddingVertical: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
707
+ paddingTop: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
690
708
  };
691
709
  } else {
692
710
  paddingStyles = {
693
711
  paddingBottom: rowGap || gap || void 0,
694
- paddingHorizontal: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
712
+ paddingLeft: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0,
713
+ paddingRight: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
695
714
  };
696
715
  }
697
716
  }
698
717
  return horizontal ? {
718
+ boxSizing: paddingStyles ? "border-box" : void 0,
699
719
  flexDirection: ItemSeparatorComponent ? "row" : void 0,
700
720
  height: otherAxisSize,
701
721
  left: 0,
@@ -703,6 +723,7 @@ var Container = typedMemo(function Container2({
703
723
  top: otherAxisPos,
704
724
  ...paddingStyles || {}
705
725
  } : {
726
+ boxSizing: paddingStyles ? "border-box" : void 0,
706
727
  left: otherAxisPos,
707
728
  position: "absolute",
708
729
  right: numColumns > 1 ? null : 0,
@@ -931,9 +952,6 @@ var ContainersInner = typedMemo(function ContainersInner2({ horizontal, numColum
931
952
  style.marginRight = -gapX;
932
953
  }
933
954
  } else {
934
- if (gapX) {
935
- style.marginLeft = style.marginRight = -gapX;
936
- }
937
955
  if (gapY) {
938
956
  style.marginBottom = -gapY;
939
957
  }
@@ -1111,6 +1129,7 @@ function resolveWindowScrollTarget({ clampedOffset, horizontal, listPos, scroll
1111
1129
  var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
1112
1130
  children,
1113
1131
  style,
1132
+ contentContainerClassName,
1114
1133
  contentContainerStyle,
1115
1134
  horizontal = false,
1116
1135
  contentOffset,
@@ -1246,18 +1265,18 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
1246
1265
  const scrollEventCoalescer = useRafCoalescer(emitScroll);
1247
1266
  const handleScroll = useCallback(
1248
1267
  (_event) => {
1249
- var _a3;
1250
1268
  if (!onScroll2) {
1251
1269
  return;
1252
1270
  }
1253
- const scrollingTo = (_a3 = ctx.state) == null ? void 0 : _a3.scrollingTo;
1254
- if (scrollingTo && !scrollingTo.animated) {
1271
+ const state = ctx.state;
1272
+ 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);
1273
+ if (shouldFlushImmediately) {
1255
1274
  scrollEventCoalescer.flush();
1256
1275
  } else {
1257
1276
  scrollEventCoalescer.schedule();
1258
1277
  }
1259
1278
  },
1260
- [onScroll2, scrollEventCoalescer]
1279
+ [ctx.state, onScroll2, scrollEventCoalescer]
1261
1280
  );
1262
1281
  useLayoutEffect(() => {
1263
1282
  const target = getScrollTarget();
@@ -1323,13 +1342,14 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
1323
1342
  ...StyleSheet.flatten(contentContainerStyle)
1324
1343
  };
1325
1344
  const {
1345
+ contentContainerClassName: _contentContainerClassName,
1326
1346
  contentInset: _contentInset,
1327
1347
  scrollEventThrottle: _scrollEventThrottle,
1328
1348
  ScrollComponent: _ScrollComponent,
1329
1349
  useWindowScroll: _useWindowScroll,
1330
1350
  ...webProps
1331
1351
  } = props;
1332
- return /* @__PURE__ */ React3.createElement("div", { ref: scrollRef, ...webProps, style: scrollViewStyle }, refreshControl, /* @__PURE__ */ React3.createElement("div", { ref: contentRef, style: contentStyle }, children));
1352
+ return /* @__PURE__ */ React3.createElement("div", { ref: scrollRef, ...webProps, style: scrollViewStyle }, refreshControl, /* @__PURE__ */ React3.createElement("div", { className: contentContainerClassName, ref: contentRef, style: contentStyle }, children));
1333
1353
  });
1334
1354
  function useValueListener$(key, callback) {
1335
1355
  const ctx = useStateContext();
@@ -1342,6 +1362,21 @@ function useValueListener$(key, callback) {
1342
1362
  }
1343
1363
 
1344
1364
  // src/components/ScrollAdjust.tsx
1365
+ function getScrollAdjustAxis(horizontal) {
1366
+ return horizontal ? {
1367
+ contentSizeKey: "scrollWidth",
1368
+ paddingEndProp: "paddingRight",
1369
+ viewportSizeKey: "clientWidth",
1370
+ x: 1,
1371
+ y: 0
1372
+ } : {
1373
+ contentSizeKey: "scrollHeight",
1374
+ paddingEndProp: "paddingBottom",
1375
+ viewportSizeKey: "clientHeight",
1376
+ x: 0,
1377
+ y: 1
1378
+ };
1379
+ }
1345
1380
  function ScrollAdjust() {
1346
1381
  const ctx = useStateContext();
1347
1382
  const lastScrollOffsetRef = React3.useRef(0);
@@ -1355,32 +1390,34 @@ function ScrollAdjust() {
1355
1390
  if (scrollView && scrollOffset !== lastScrollOffsetRef.current) {
1356
1391
  const scrollDelta = scrollOffset - lastScrollOffsetRef.current;
1357
1392
  if (scrollDelta !== 0) {
1393
+ const axis = getScrollAdjustAxis(!!ctx.state.props.horizontal);
1358
1394
  const contentNode = scrollView.getContentNode();
1359
1395
  const prevScroll = scrollView.getCurrentScrollOffset();
1360
1396
  const el = scrollView.getScrollableNode();
1397
+ const scrollBy = () => scrollView.scrollBy(axis.x * scrollDelta, axis.y * scrollDelta);
1361
1398
  if (!contentNode) {
1362
- scrollView.scrollBy(0, scrollDelta);
1399
+ scrollBy();
1363
1400
  lastScrollOffsetRef.current = scrollOffset;
1364
1401
  return;
1365
1402
  }
1366
- const totalSize = contentNode.scrollHeight;
1367
- const viewportSize = el.clientHeight;
1403
+ const totalSize = contentNode[axis.contentSizeKey];
1404
+ const viewportSize = el[axis.viewportSizeKey];
1368
1405
  const nextScroll = prevScroll + scrollDelta;
1369
1406
  if (scrollDelta > 0 && !ctx.state.adjustingFromInitialMount && totalSize < nextScroll + viewportSize) {
1370
- const paddingBottom = ctx.state.props.stylePaddingBottom || 0;
1407
+ const previousPaddingEnd = contentNode.style[axis.paddingEndProp];
1371
1408
  const pad = (nextScroll + viewportSize - totalSize) * 2;
1372
- contentNode.style.paddingBottom = `${pad}px`;
1409
+ contentNode.style[axis.paddingEndProp] = `${pad}px`;
1373
1410
  void contentNode.offsetHeight;
1374
- scrollView.scrollBy(0, scrollDelta);
1411
+ scrollBy();
1375
1412
  if (resetPaddingRafRef.current !== void 0) {
1376
1413
  cancelAnimationFrame(resetPaddingRafRef.current);
1377
1414
  }
1378
1415
  resetPaddingRafRef.current = requestAnimationFrame(() => {
1379
1416
  resetPaddingRafRef.current = void 0;
1380
- contentNode.style.paddingBottom = paddingBottom ? `${paddingBottom}px` : "0";
1417
+ contentNode.style[axis.paddingEndProp] = previousPaddingEnd;
1381
1418
  });
1382
1419
  } else {
1383
- scrollView.scrollBy(0, scrollDelta);
1420
+ scrollBy();
1384
1421
  }
1385
1422
  }
1386
1423
  lastScrollOffsetRef.current = scrollOffset;
@@ -1394,8 +1431,19 @@ function SnapWrapper({ ScrollComponent, ...props }) {
1394
1431
  const [snapToOffsets] = useArr$(["snapToOffsets"]);
1395
1432
  return /* @__PURE__ */ React3.createElement(ScrollComponent, { ...props, snapToOffsets });
1396
1433
  }
1434
+ function WebAnchoredEndSpace({ horizontal }) {
1435
+ const ctx = useStateContext();
1436
+ const [anchoredEndSpaceSize] = useArr$(["anchoredEndSpaceSize"]);
1437
+ const shouldRenderAnchoredEndSpace = !!ctx.state.props.anchoredEndSpace && (anchoredEndSpaceSize || 0) > 0;
1438
+ if (!shouldRenderAnchoredEndSpace) {
1439
+ return null;
1440
+ }
1441
+ const style = horizontal ? { height: "100%", width: anchoredEndSpaceSize || 0 } : { height: anchoredEndSpaceSize || 0 };
1442
+ return /* @__PURE__ */ React3.createElement("div", { style }, null);
1443
+ }
1397
1444
  var LayoutView = ({ onLayoutChange, refView, children, ...rest }) => {
1398
- const ref = refView != null ? refView : useRef(null);
1445
+ const localRef = useRef(null);
1446
+ const ref = refView != null ? refView : localRef;
1399
1447
  useOnLayoutSync({ onLayoutChange, ref });
1400
1448
  return /* @__PURE__ */ React3.createElement("div", { ...rest, ref }, children);
1401
1449
  };
@@ -1440,12 +1488,14 @@ var ListComponent = typedMemo(function ListComponent2({
1440
1488
  }) {
1441
1489
  const ctx = useStateContext();
1442
1490
  const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
1443
- const ScrollComponent = renderScrollComponent ? useMemo(
1444
- () => React3.forwardRef(
1491
+ const ScrollComponent = useMemo(() => {
1492
+ if (!renderScrollComponent) {
1493
+ return ListComponentScrollView;
1494
+ }
1495
+ return React3.forwardRef(
1445
1496
  (props, ref) => renderScrollComponent({ ...props, ref })
1446
- ),
1447
- [renderScrollComponent]
1448
- ) : ListComponentScrollView;
1497
+ );
1498
+ }, [renderScrollComponent]);
1449
1499
  const SnapOrScroll = snapToIndices ? SnapWrapper : ScrollComponent;
1450
1500
  useLayoutEffect(() => {
1451
1501
  if (!ListHeaderComponent) {
@@ -1505,171 +1555,80 @@ var ListComponent = typedMemo(function ListComponent2({
1505
1555
  }
1506
1556
  ),
1507
1557
  ListFooterComponent && /* @__PURE__ */ React3.createElement(LayoutView, { onLayoutChange: onLayoutFooterInternal, style: ListFooterComponentStyle }, getComponent(ListFooterComponent)),
1558
+ /* @__PURE__ */ React3.createElement(WebAnchoredEndSpace, { horizontal }),
1508
1559
  IS_DEV && ENABLE_DEVMODE
1509
1560
  );
1510
1561
  });
1511
-
1512
- // src/core/calculateOffsetForIndex.ts
1513
- function calculateOffsetForIndex(ctx, index) {
1514
- const state = ctx.state;
1515
- return index !== void 0 ? state.positions[index] || 0 : 0;
1516
- }
1517
-
1518
- // src/core/getTopOffsetAdjustment.ts
1519
- function getTopOffsetAdjustment(ctx) {
1520
- return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
1521
- }
1522
-
1523
- // src/utils/getId.ts
1524
- function getId(state, index) {
1525
- const { data, keyExtractor } = state.props;
1526
- if (!data) {
1527
- return "";
1528
- }
1529
- const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
1530
- const id = ret;
1531
- state.idCache[index] = id;
1532
- return id;
1533
- }
1534
-
1535
- // src/core/addTotalSize.ts
1536
- function addTotalSize(ctx, key, add) {
1537
- const state = ctx.state;
1538
- const prevTotalSize = state.totalSize;
1539
- let totalSize = state.totalSize;
1540
- if (key === null) {
1541
- totalSize = add;
1542
- if (state.timeoutSetPaddingTop) {
1543
- clearTimeout(state.timeoutSetPaddingTop);
1544
- state.timeoutSetPaddingTop = void 0;
1545
- }
1546
- } else {
1547
- totalSize += add;
1548
- }
1549
- if (prevTotalSize !== totalSize) {
1550
- {
1551
- state.pendingTotalSize = void 0;
1552
- state.totalSize = totalSize;
1553
- set$(ctx, "totalSize", totalSize);
1554
- }
1555
- }
1556
- }
1557
-
1558
- // src/core/setSize.ts
1559
- function setSize(ctx, itemKey, size) {
1560
- const state = ctx.state;
1561
- const { sizes } = state;
1562
- const previousSize = sizes.get(itemKey);
1563
- const diff = previousSize !== void 0 ? size - previousSize : size;
1564
- if (diff !== 0) {
1565
- addTotalSize(ctx, itemKey, diff);
1566
- }
1567
- sizes.set(itemKey, size);
1568
- }
1569
-
1570
- // src/utils/getItemSize.ts
1571
- function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
1572
- var _a3, _b;
1573
- const state = ctx.state;
1574
- const {
1575
- sizesKnown,
1576
- sizes,
1577
- averageSizes,
1578
- props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType },
1579
- scrollingTo
1580
- } = state;
1581
- const sizeKnown = sizesKnown.get(key);
1582
- if (sizeKnown !== void 0) {
1583
- return sizeKnown;
1584
- }
1585
- let size;
1586
- if (preferCachedSize) {
1587
- const cachedSize = sizes.get(key);
1588
- if (cachedSize !== void 0) {
1589
- return cachedSize;
1590
- }
1591
- }
1592
- const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1593
- if (getFixedItemSize) {
1594
- size = getFixedItemSize(data, index, itemType);
1595
- if (size !== void 0) {
1596
- sizesKnown.set(key, size);
1597
- }
1598
- }
1599
- if (size === void 0 && useAverageSize && sizeKnown === void 0 && !scrollingTo) {
1600
- const averageSizeForType = (_b = averageSizes[itemType]) == null ? void 0 : _b.avg;
1601
- if (averageSizeForType !== void 0) {
1602
- size = roundSize(averageSizeForType);
1562
+ var WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH = 100;
1563
+ var WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO = 0.9;
1564
+ var WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO = 0.9;
1565
+ function useDevChecksImpl(props) {
1566
+ const ctx = useStateContext();
1567
+ const { childrenMode, keyExtractor, renderScrollComponent, stickyHeaderIndices, stickyIndices, useWindowScroll } = props;
1568
+ useEffect(() => {
1569
+ if (stickyIndices && !stickyHeaderIndices) {
1570
+ warnDevOnce(
1571
+ "stickyIndices",
1572
+ "stickyIndices has been renamed to stickyHeaderIndices. Please update your props to use stickyHeaderIndices."
1573
+ );
1603
1574
  }
1604
- }
1605
- if (size === void 0) {
1606
- size = sizes.get(key);
1607
- if (size !== void 0) {
1608
- return size;
1575
+ }, [stickyHeaderIndices, stickyIndices]);
1576
+ useEffect(() => {
1577
+ if (useWindowScroll && renderScrollComponent) {
1578
+ warnDevOnce(
1579
+ "useWindowScrollRenderScrollComponent",
1580
+ "useWindowScroll is not supported when renderScrollComponent is provided."
1581
+ );
1609
1582
  }
1610
- }
1611
- if (size === void 0) {
1612
- size = getEstimatedItemSize ? getEstimatedItemSize(data, index, itemType) : estimatedItemSize;
1613
- }
1614
- setSize(ctx, key, size);
1615
- return size;
1616
- }
1617
- function getItemSizeAtIndex(ctx, index) {
1618
- if (index === void 0 || index < 0) {
1619
- return void 0;
1620
- }
1621
- const targetId = getId(ctx.state, index);
1622
- return getItemSize(ctx, targetId, index, ctx.state.props.data[index]);
1623
- }
1624
-
1625
- // src/core/calculateOffsetWithOffsetPosition.ts
1626
- function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1627
- var _a3;
1628
- const state = ctx.state;
1629
- const { index, viewOffset, viewPosition } = params;
1630
- let offset = offsetParam;
1631
- if (viewOffset) {
1632
- offset -= viewOffset;
1633
- }
1634
- if (index !== void 0) {
1635
- const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
1636
- if (topOffsetAdjustment) {
1637
- offset += topOffsetAdjustment;
1583
+ }, [renderScrollComponent, useWindowScroll]);
1584
+ useEffect(() => {
1585
+ if (!keyExtractor && !ctx.state.isFirst && ctx.state.didDataChange && !childrenMode) {
1586
+ warnDevOnce(
1587
+ "keyExtractor",
1588
+ "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."
1589
+ );
1638
1590
  }
1639
- }
1640
- if (viewPosition !== void 0 && index !== void 0) {
1591
+ }, [childrenMode, ctx, keyExtractor]);
1592
+ useEffect(() => {
1593
+ const state = ctx.state;
1641
1594
  const dataLength = state.props.data.length;
1642
- if (dataLength === 0) {
1643
- return offset;
1644
- }
1645
- const isOutOfBounds = index < 0 || index >= dataLength;
1646
- const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
1647
- const itemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1648
- const trailingInset = getContentInsetEnd(state);
1649
- offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1650
- if (!isOutOfBounds && index === state.props.data.length - 1) {
1651
- const footerSize = peek$(ctx, "footerSize") || 0;
1652
- offset += footerSize;
1595
+ const useWindowScrollResolved = state.props.useWindowScroll;
1596
+ if (useWindowScrollResolved || dataLength < WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH) {
1597
+ return;
1653
1598
  }
1654
- }
1655
- return offset;
1599
+ const warnIfUnboundedOuterSize = () => {
1600
+ const readyToRender = peek$(ctx, "readyToRender");
1601
+ const numContainers = peek$(ctx, "numContainers") || 0;
1602
+ const totalSize = peek$(ctx, "totalSize") || 0;
1603
+ const scrollLength = ctx.state.scrollLength || 0;
1604
+ if (!readyToRender || totalSize <= 0 || scrollLength <= 0) {
1605
+ return;
1606
+ }
1607
+ const rendersAlmostEverything = numContainers >= Math.ceil(dataLength * WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO);
1608
+ const viewportMatchesContent = scrollLength >= totalSize * WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO;
1609
+ if (rendersAlmostEverything && viewportMatchesContent) {
1610
+ warnDevOnce(
1611
+ "webUnboundedOuterSize",
1612
+ "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."
1613
+ );
1614
+ }
1615
+ };
1616
+ warnIfUnboundedOuterSize();
1617
+ const unsubscribe = [
1618
+ listen$(ctx, "numContainers", warnIfUnboundedOuterSize),
1619
+ listen$(ctx, "readyToRender", warnIfUnboundedOuterSize),
1620
+ listen$(ctx, "totalSize", warnIfUnboundedOuterSize)
1621
+ ];
1622
+ return () => {
1623
+ for (const unsub of unsubscribe) {
1624
+ unsub();
1625
+ }
1626
+ };
1627
+ }, [ctx]);
1656
1628
  }
1657
-
1658
- // src/core/clampScrollOffset.ts
1659
- function clampScrollOffset(ctx, offset, scrollTarget) {
1660
- const state = ctx.state;
1661
- const contentSize = getContentSize(ctx);
1662
- let clampedOffset = offset;
1663
- if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform.OS !== "android")) {
1664
- const baseMaxOffset = Math.max(0, contentSize - state.scrollLength);
1665
- const viewOffset = scrollTarget == null ? void 0 : scrollTarget.viewOffset;
1666
- const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
1667
- const maxOffset = baseMaxOffset + extraEndOffset;
1668
- clampedOffset = Math.min(offset, maxOffset);
1669
- }
1670
- clampedOffset = Math.max(0, clampedOffset);
1671
- return clampedOffset;
1629
+ function useDevChecksNoop(_props) {
1672
1630
  }
1631
+ var useDevChecks = IS_DEV ? useDevChecksImpl : useDevChecksNoop;
1673
1632
 
1674
1633
  // src/core/deferredPublicOnScroll.ts
1675
1634
  function withResolvedContentOffset(state, event, resolvedOffset) {
@@ -1817,33 +1776,462 @@ function setInitialScrollSession(state, options = {}) {
1817
1776
  return state.initialScrollSession;
1818
1777
  }
1819
1778
 
1820
- // src/core/finishScrollTo.ts
1821
- function finishScrollTo(ctx) {
1822
- var _a3, _b;
1823
- const state = ctx.state;
1824
- if (state == null ? void 0 : state.scrollingTo) {
1825
- const resolvePendingScroll = state.pendingScrollResolve;
1826
- state.pendingScrollResolve = void 0;
1827
- const scrollingTo = state.scrollingTo;
1828
- state.scrollHistory.length = 0;
1829
- state.scrollingTo = void 0;
1830
- if (state.pendingTotalSize !== void 0) {
1831
- addTotalSize(ctx, null, state.pendingTotalSize);
1832
- }
1833
- {
1834
- state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
1835
- }
1836
- if (scrollingTo.isInitialScroll || state.initialScroll) {
1837
- const isOffsetSession = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
1838
- finishInitialScroll(ctx, {
1839
- onFinished: resolvePendingScroll,
1840
- preserveTarget: isOffsetSession && state.props.data.length === 0 || !!scrollingTo.isInitialScroll && !!((_b = state.initialScroll) == null ? void 0 : _b.preserveForFooterLayout),
1779
+ // src/utils/checkThreshold.ts
1780
+ var HYSTERESIS_MULTIPLIER = 1.3;
1781
+ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot, allowReentryOnChange) => {
1782
+ const absDistance = Math.abs(distance);
1783
+ const within = atThreshold || threshold > 0 && absDistance <= threshold;
1784
+ const updateSnapshot = () => {
1785
+ setSnapshot({
1786
+ atThreshold,
1787
+ contentSize: context.contentSize,
1788
+ dataLength: context.dataLength,
1789
+ scrollPosition: context.scrollPosition
1790
+ });
1791
+ };
1792
+ if (!wasReached) {
1793
+ if (!within) {
1794
+ return false;
1795
+ }
1796
+ onReached(distance);
1797
+ updateSnapshot();
1798
+ return true;
1799
+ }
1800
+ const reset = !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
1801
+ if (reset) {
1802
+ setSnapshot(void 0);
1803
+ return false;
1804
+ }
1805
+ if (within) {
1806
+ const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
1807
+ if (changed) {
1808
+ if (allowReentryOnChange) {
1809
+ onReached(distance);
1810
+ }
1811
+ updateSnapshot();
1812
+ }
1813
+ }
1814
+ return true;
1815
+ };
1816
+
1817
+ // src/utils/hasActiveInitialScroll.ts
1818
+ function hasActiveInitialScroll(state) {
1819
+ return !!(state == null ? void 0 : state.initialScroll) && !state.didFinishInitialScroll;
1820
+ }
1821
+
1822
+ // src/utils/checkAtBottom.ts
1823
+ function checkAtBottom(ctx) {
1824
+ var _a3;
1825
+ const state = ctx.state;
1826
+ if (!state) {
1827
+ return;
1828
+ }
1829
+ const {
1830
+ queuedInitialLayout,
1831
+ scrollLength,
1832
+ scroll,
1833
+ maintainingScrollAtEnd,
1834
+ props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
1835
+ } = state;
1836
+ const contentSize = getContentSize(ctx);
1837
+ if (contentSize > 0 && queuedInitialLayout) {
1838
+ const insetEnd = getContentInsetEnd(ctx);
1839
+ const distanceFromEnd = contentSize - scroll - scrollLength - insetEnd;
1840
+ const isContentLess = contentSize < scrollLength;
1841
+ set$(ctx, "isAtEnd", isContentLess || distanceFromEnd <= EDGE_POSITION_EPSILON);
1842
+ set$(ctx, "isNearEnd", isContentLess || distanceFromEnd <= onEndReachedThreshold * scrollLength);
1843
+ set$(
1844
+ ctx,
1845
+ "isWithinMaintainScrollAtEndThreshold",
1846
+ isContentLess || distanceFromEnd <= maintainScrollAtEndThreshold * scrollLength
1847
+ );
1848
+ const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || maintainingScrollAtEnd;
1849
+ if (!shouldSkipThresholdChecks) {
1850
+ state.isEndReached = checkThreshold(
1851
+ distanceFromEnd,
1852
+ isContentLess,
1853
+ onEndReachedThreshold * scrollLength,
1854
+ state.isEndReached,
1855
+ state.endReachedSnapshot,
1856
+ {
1857
+ contentSize,
1858
+ dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1859
+ scrollPosition: scroll
1860
+ },
1861
+ (distance) => {
1862
+ var _a4, _b;
1863
+ return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
1864
+ },
1865
+ (snapshot) => {
1866
+ state.endReachedSnapshot = snapshot;
1867
+ },
1868
+ true
1869
+ );
1870
+ }
1871
+ }
1872
+ }
1873
+
1874
+ // src/utils/checkAtTop.ts
1875
+ function checkAtTop(ctx) {
1876
+ const state = ctx == null ? void 0 : ctx.state;
1877
+ if (!state) {
1878
+ return;
1879
+ }
1880
+ const {
1881
+ dataChangeEpoch,
1882
+ isStartReached,
1883
+ props: { data, onStartReachedThreshold },
1884
+ scroll,
1885
+ scrollLength,
1886
+ startReachedSnapshot,
1887
+ startReachedSnapshotDataChangeEpoch,
1888
+ totalSize
1889
+ } = state;
1890
+ const dataLength = data.length;
1891
+ const threshold = onStartReachedThreshold * scrollLength;
1892
+ const dataChanged = startReachedSnapshotDataChangeEpoch !== dataChangeEpoch;
1893
+ const withinThreshold = threshold > 0 && Math.abs(scroll) <= threshold;
1894
+ const allowReentryOnDataChange = !!isStartReached && withinThreshold && !!dataChanged && !isInMVCPActiveMode(state);
1895
+ if (isStartReached && threshold > 0 && scroll > threshold && startReachedSnapshot && (dataChanged || startReachedSnapshot.contentSize !== totalSize || startReachedSnapshot.dataLength !== dataLength)) {
1896
+ state.isStartReached = false;
1897
+ state.startReachedSnapshot = void 0;
1898
+ state.startReachedSnapshotDataChangeEpoch = void 0;
1899
+ }
1900
+ set$(ctx, "isAtStart", scroll <= EDGE_POSITION_EPSILON);
1901
+ set$(ctx, "isNearStart", scroll <= threshold);
1902
+ const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || !!state.scrollingTo;
1903
+ const shouldDeferDataChangeRefire = isStartReached && withinThreshold && dataChanged && !allowReentryOnDataChange;
1904
+ if (!shouldSkipThresholdChecks && !shouldDeferDataChangeRefire) {
1905
+ state.isStartReached = checkThreshold(
1906
+ scroll,
1907
+ false,
1908
+ threshold,
1909
+ state.isStartReached,
1910
+ allowReentryOnDataChange ? void 0 : startReachedSnapshot,
1911
+ {
1912
+ contentSize: totalSize,
1913
+ dataLength,
1914
+ scrollPosition: scroll
1915
+ },
1916
+ (distance) => {
1917
+ var _a3, _b;
1918
+ return (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
1919
+ },
1920
+ (snapshot) => {
1921
+ state.startReachedSnapshot = snapshot;
1922
+ state.startReachedSnapshotDataChangeEpoch = snapshot ? dataChangeEpoch : void 0;
1923
+ },
1924
+ allowReentryOnDataChange
1925
+ );
1926
+ }
1927
+ }
1928
+
1929
+ // src/utils/checkThresholds.ts
1930
+ function checkThresholds(ctx) {
1931
+ checkAtBottom(ctx);
1932
+ checkAtTop(ctx);
1933
+ }
1934
+
1935
+ // src/core/recalculateSettledScroll.ts
1936
+ function recalculateSettledScroll(ctx) {
1937
+ var _a3, _b;
1938
+ const state = ctx.state;
1939
+ if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1940
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1941
+ }
1942
+ checkThresholds(ctx);
1943
+ }
1944
+
1945
+ // src/utils/setInitialRenderState.ts
1946
+ function setInitialRenderState(ctx, {
1947
+ didLayout,
1948
+ didInitialScroll
1949
+ }) {
1950
+ const { state } = ctx;
1951
+ const {
1952
+ loadStartTime,
1953
+ props: { onLoad }
1954
+ } = state;
1955
+ if (didLayout) {
1956
+ state.didContainersLayout = true;
1957
+ }
1958
+ if (didInitialScroll) {
1959
+ state.didFinishInitialScroll = true;
1960
+ }
1961
+ const isReadyToRender = Boolean(state.didContainersLayout && state.didFinishInitialScroll);
1962
+ if (isReadyToRender && !peek$(ctx, "readyToRender")) {
1963
+ set$(ctx, "readyToRender", true);
1964
+ if (onLoad) {
1965
+ onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1966
+ }
1967
+ }
1968
+ }
1969
+
1970
+ // src/core/finishInitialScroll.ts
1971
+ var PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS = 2e3;
1972
+ function syncInitialScrollOffset(state, offset) {
1973
+ state.scroll = offset;
1974
+ state.scrollPending = offset;
1975
+ state.scrollPrev = offset;
1976
+ }
1977
+ function clearPreservedInitialScrollTargetTimeout(state) {
1978
+ if (state.timeoutPreservedInitialScrollClear !== void 0) {
1979
+ clearTimeout(state.timeoutPreservedInitialScrollClear);
1980
+ state.timeoutPreservedInitialScrollClear = void 0;
1981
+ }
1982
+ }
1983
+ function clearPreservedInitialScrollTarget(state) {
1984
+ clearPreservedInitialScrollTargetTimeout(state);
1985
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
1986
+ state.initialScroll = void 0;
1987
+ setInitialScrollSession(state);
1988
+ }
1989
+ function finishInitialScroll(ctx, options) {
1990
+ var _a3, _b, _c;
1991
+ const state = ctx.state;
1992
+ if ((options == null ? void 0 : options.resolvedOffset) !== void 0) {
1993
+ syncInitialScrollOffset(state, options.resolvedOffset);
1994
+ } else if ((options == null ? void 0 : options.syncObservedOffset) && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
1995
+ const observedOffset = (_c = (_b = state.refScroller.current) == null ? void 0 : _b.getCurrentScrollOffset) == null ? void 0 : _c.call(_b);
1996
+ if (typeof observedOffset === "number" && Number.isFinite(observedOffset)) {
1997
+ syncInitialScrollOffset(state, observedOffset);
1998
+ }
1999
+ }
2000
+ const complete = () => {
2001
+ var _a4, _b2, _c2, _d, _e;
2002
+ const shouldReleaseDeferredPublicOnScroll = ((_a4 = state.initialScrollSession) == null ? void 0 : _a4.kind) === "bootstrap";
2003
+ const finalScrollOffset = (_d = (_c2 = (_b2 = options == null ? void 0 : options.resolvedOffset) != null ? _b2 : state.scrollPending) != null ? _c2 : state.scroll) != null ? _d : 0;
2004
+ initialScrollWatchdog.clear(state);
2005
+ if ((options == null ? void 0 : options.preserveTarget) && state.initialScroll) {
2006
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
2007
+ setInitialScrollSession(state);
2008
+ clearPreservedInitialScrollTargetTimeout(state);
2009
+ if (options == null ? void 0 : options.schedulePreservedTargetClear) {
2010
+ state.timeoutPreservedInitialScrollClear = setTimeout(() => {
2011
+ var _a5;
2012
+ state.timeoutPreservedInitialScrollClear = void 0;
2013
+ if (!state.didFinishInitialScroll || ((_a5 = state.scrollingTo) == null ? void 0 : _a5.isInitialScroll) || !state.initialScroll) {
2014
+ return;
2015
+ }
2016
+ clearPreservedInitialScrollTarget(state);
2017
+ }, PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS);
2018
+ }
2019
+ } else {
2020
+ clearPreservedInitialScrollTarget(state);
2021
+ }
2022
+ if (options == null ? void 0 : options.recalculateItems) {
2023
+ recalculateSettledScroll(ctx);
2024
+ }
2025
+ setInitialRenderState(ctx, { didInitialScroll: true });
2026
+ if (shouldReleaseDeferredPublicOnScroll) {
2027
+ releaseDeferredPublicOnScroll(ctx, finalScrollOffset);
2028
+ }
2029
+ (_e = options == null ? void 0 : options.onFinished) == null ? void 0 : _e.call(options);
2030
+ };
2031
+ if (options == null ? void 0 : options.waitForCompletionFrame) {
2032
+ requestAnimationFrame(complete);
2033
+ return;
2034
+ }
2035
+ complete();
2036
+ }
2037
+
2038
+ // src/core/calculateOffsetForIndex.ts
2039
+ function calculateOffsetForIndex(ctx, index) {
2040
+ const state = ctx.state;
2041
+ return index !== void 0 ? state.positions[index] || 0 : 0;
2042
+ }
2043
+
2044
+ // src/core/getTopOffsetAdjustment.ts
2045
+ function getTopOffsetAdjustment(ctx) {
2046
+ return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
2047
+ }
2048
+
2049
+ // src/utils/getId.ts
2050
+ function getId(state, index) {
2051
+ const { data, keyExtractor } = state.props;
2052
+ if (!data) {
2053
+ return "";
2054
+ }
2055
+ const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
2056
+ const id = ret;
2057
+ state.idCache[index] = id;
2058
+ return id;
2059
+ }
2060
+
2061
+ // src/core/addTotalSize.ts
2062
+ function addTotalSize(ctx, key, add) {
2063
+ const state = ctx.state;
2064
+ const prevTotalSize = state.totalSize;
2065
+ let totalSize = state.totalSize;
2066
+ if (key === null) {
2067
+ totalSize = add;
2068
+ if (state.timeoutSetPaddingTop) {
2069
+ clearTimeout(state.timeoutSetPaddingTop);
2070
+ state.timeoutSetPaddingTop = void 0;
2071
+ }
2072
+ } else {
2073
+ totalSize += add;
2074
+ }
2075
+ if (prevTotalSize !== totalSize) {
2076
+ {
2077
+ state.pendingTotalSize = void 0;
2078
+ state.totalSize = totalSize;
2079
+ set$(ctx, "totalSize", totalSize);
2080
+ }
2081
+ }
2082
+ }
2083
+
2084
+ // src/core/setSize.ts
2085
+ function setSize(ctx, itemKey, size) {
2086
+ const state = ctx.state;
2087
+ const { sizes } = state;
2088
+ const previousSize = sizes.get(itemKey);
2089
+ const diff = previousSize !== void 0 ? size - previousSize : size;
2090
+ if (diff !== 0) {
2091
+ addTotalSize(ctx, itemKey, diff);
2092
+ }
2093
+ sizes.set(itemKey, size);
2094
+ }
2095
+
2096
+ // src/utils/getItemSize.ts
2097
+ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
2098
+ var _a3, _b, _c;
2099
+ const state = ctx.state;
2100
+ const {
2101
+ sizesKnown,
2102
+ sizes,
2103
+ averageSizes,
2104
+ props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType },
2105
+ scrollingTo
2106
+ } = state;
2107
+ const sizeKnown = sizesKnown.get(key);
2108
+ if (sizeKnown !== void 0) {
2109
+ return sizeKnown;
2110
+ }
2111
+ let size;
2112
+ const renderedSize = sizes.get(key);
2113
+ if (preferCachedSize) {
2114
+ if (renderedSize !== void 0) {
2115
+ return renderedSize;
2116
+ }
2117
+ }
2118
+ const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
2119
+ if (getFixedItemSize) {
2120
+ size = getFixedItemSize(data, index, itemType);
2121
+ if (size !== void 0) {
2122
+ sizesKnown.set(key, size);
2123
+ }
2124
+ }
2125
+ if (size === void 0 && useAverageSize && sizeKnown === void 0 && !scrollingTo) {
2126
+ const averageSizeForType = (_b = averageSizes[itemType]) == null ? void 0 : _b.avg;
2127
+ if (averageSizeForType !== void 0) {
2128
+ size = roundSize(averageSizeForType);
2129
+ }
2130
+ }
2131
+ if (size === void 0 && renderedSize !== void 0) {
2132
+ return renderedSize;
2133
+ }
2134
+ if (size === void 0 && useAverageSize && sizeKnown === void 0 && scrollingTo) {
2135
+ const averageSizeForType = (_c = scrollingTo.averageSizeSnapshot) == null ? void 0 : _c[itemType];
2136
+ if (averageSizeForType !== void 0) {
2137
+ size = roundSize(averageSizeForType);
2138
+ }
2139
+ }
2140
+ if (size === void 0) {
2141
+ size = getEstimatedItemSize ? getEstimatedItemSize(data, index, itemType) : estimatedItemSize;
2142
+ }
2143
+ setSize(ctx, key, size);
2144
+ return size;
2145
+ }
2146
+ function getItemSizeAtIndex(ctx, index) {
2147
+ if (index === void 0 || index < 0) {
2148
+ return void 0;
2149
+ }
2150
+ const targetId = getId(ctx.state, index);
2151
+ return getItemSize(ctx, targetId, index, ctx.state.props.data[index]);
2152
+ }
2153
+
2154
+ // src/core/calculateOffsetWithOffsetPosition.ts
2155
+ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
2156
+ var _a3;
2157
+ const state = ctx.state;
2158
+ const { index, viewOffset, viewPosition } = params;
2159
+ let offset = offsetParam;
2160
+ if (viewOffset) {
2161
+ offset -= viewOffset;
2162
+ }
2163
+ if (index !== void 0) {
2164
+ const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
2165
+ if (topOffsetAdjustment) {
2166
+ offset += topOffsetAdjustment;
2167
+ }
2168
+ }
2169
+ if (viewPosition !== void 0 && index !== void 0) {
2170
+ const dataLength = state.props.data.length;
2171
+ if (dataLength === 0) {
2172
+ return offset;
2173
+ }
2174
+ const isOutOfBounds = index < 0 || index >= dataLength;
2175
+ const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
2176
+ const itemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
2177
+ const trailingInset = getContentInsetEnd(ctx);
2178
+ offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
2179
+ if (!isOutOfBounds && index === state.props.data.length - 1) {
2180
+ const footerSize = peek$(ctx, "footerSize") || 0;
2181
+ offset += footerSize;
2182
+ }
2183
+ }
2184
+ return offset;
2185
+ }
2186
+
2187
+ // src/core/clampScrollOffset.ts
2188
+ function clampScrollOffset(ctx, offset, scrollTarget) {
2189
+ const state = ctx.state;
2190
+ const contentSize = getContentSize(ctx);
2191
+ let clampedOffset = offset;
2192
+ if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform.OS !== "android")) {
2193
+ const baseMaxOffset = Math.max(0, contentSize - state.scrollLength);
2194
+ const viewOffset = scrollTarget == null ? void 0 : scrollTarget.viewOffset;
2195
+ const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
2196
+ const maxOffset = baseMaxOffset + extraEndOffset;
2197
+ clampedOffset = Math.min(offset, maxOffset);
2198
+ }
2199
+ clampedOffset = Math.max(0, clampedOffset);
2200
+ return clampedOffset;
2201
+ }
2202
+
2203
+ // src/core/finishScrollTo.ts
2204
+ function finishScrollTo(ctx) {
2205
+ var _a3, _b;
2206
+ const state = ctx.state;
2207
+ if (state == null ? void 0 : state.scrollingTo) {
2208
+ const resolvePendingScroll = state.pendingScrollResolve;
2209
+ state.pendingScrollResolve = void 0;
2210
+ const scrollingTo = state.scrollingTo;
2211
+ state.scrollHistory.length = 0;
2212
+ state.scrollingTo = void 0;
2213
+ if (state.pendingTotalSize !== void 0) {
2214
+ addTotalSize(ctx, null, state.pendingTotalSize);
2215
+ }
2216
+ {
2217
+ state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
2218
+ }
2219
+ if (scrollingTo.isInitialScroll || state.initialScroll) {
2220
+ const isOffsetSession = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
2221
+ const shouldPreserveResizeTarget = !!scrollingTo.isInitialScroll && !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_b = state.initialScroll) == null ? void 0 : _b.viewPosition) === 1;
2222
+ finishInitialScroll(ctx, {
2223
+ onFinished: () => {
2224
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
2225
+ },
2226
+ preserveTarget: isOffsetSession && state.props.data.length === 0 || shouldPreserveResizeTarget,
1841
2227
  recalculateItems: true,
2228
+ schedulePreservedTargetClear: shouldPreserveResizeTarget,
1842
2229
  syncObservedOffset: isOffsetSession,
1843
2230
  waitForCompletionFrame: !!scrollingTo.waitForInitialScrollCompletionFrame
1844
2231
  });
1845
2232
  return;
1846
2233
  }
2234
+ recalculateSettledScroll(ctx);
1847
2235
  resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1848
2236
  }
1849
2237
  }
@@ -1854,6 +2242,7 @@ var SCROLL_END_MAX_MS = 1500;
1854
2242
  var SMOOTH_SCROLL_DURATION_MS = 320;
1855
2243
  var SCROLL_END_TARGET_EPSILON = 1;
1856
2244
  function doScrollTo(ctx, params) {
2245
+ var _a3, _b;
1857
2246
  const state = ctx.state;
1858
2247
  const { animated, horizontal, offset } = params;
1859
2248
  const scroller = state.refScroller.current;
@@ -1867,7 +2256,7 @@ function doScrollTo(ctx, params) {
1867
2256
  const top = isHorizontal ? 0 : offset;
1868
2257
  scroller.scrollTo({ animated: isAnimated, x: left, y: top });
1869
2258
  if (isAnimated) {
1870
- const target = scroller.getScrollEventTarget();
2259
+ const target = (_b = (_a3 = scroller.getScrollEventTarget) == null ? void 0 : _a3.call(scroller)) != null ? _b : null;
1871
2260
  listenForScrollEnd(ctx, {
1872
2261
  readOffset: () => scroller.getCurrentScrollOffset(),
1873
2262
  target,
@@ -1933,6 +2322,17 @@ function listenForScrollEnd(ctx, params) {
1933
2322
  }
1934
2323
 
1935
2324
  // src/core/scrollTo.ts
2325
+ function getAverageSizeSnapshot(state) {
2326
+ if (Object.keys(state.averageSizes).length === 0) {
2327
+ return void 0;
2328
+ }
2329
+ const snapshot = {};
2330
+ for (const itemType in state.averageSizes) {
2331
+ const averages = state.averageSizes[itemType];
2332
+ snapshot[itemType] = averages.avg;
2333
+ }
2334
+ return snapshot;
2335
+ }
1936
2336
  function syncInitialScrollNativeWatchdog(state, options) {
1937
2337
  var _a3;
1938
2338
  const { isInitialScroll, requestedOffset, targetOffset } = options;
@@ -1980,8 +2380,10 @@ function scrollTo(ctx, params) {
1980
2380
  if (isInitialScroll) {
1981
2381
  initialScrollCompletion.resetFlags(state);
1982
2382
  }
2383
+ const averageSizeSnapshot = getAverageSizeSnapshot(state);
1983
2384
  state.scrollingTo = {
1984
2385
  ...scrollTarget,
2386
+ ...averageSizeSnapshot ? { averageSizeSnapshot } : {},
1985
2387
  targetOffset,
1986
2388
  waitForInitialScrollCompletionFrame
1987
2389
  };
@@ -2028,190 +2430,17 @@ function scrollToIndex(ctx, {
2028
2430
  state.scrollForNextCalculateItemsInView = void 0;
2029
2431
  scrollTo(ctx, {
2030
2432
  animated,
2031
- forceScroll,
2032
- index,
2033
- isInitialScroll,
2034
- itemSize,
2035
- offset: firstIndexOffset,
2036
- viewOffset,
2037
- viewPosition: viewPosition != null ? viewPosition : 0
2038
- });
2039
- }
2040
-
2041
- // src/utils/checkThreshold.ts
2042
- var HYSTERESIS_MULTIPLIER = 1.3;
2043
- var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot, allowReentryOnChange) => {
2044
- const absDistance = Math.abs(distance);
2045
- const within = atThreshold || threshold > 0 && absDistance <= threshold;
2046
- const updateSnapshot = () => {
2047
- setSnapshot({
2048
- atThreshold,
2049
- contentSize: context.contentSize,
2050
- dataLength: context.dataLength,
2051
- scrollPosition: context.scrollPosition
2052
- });
2053
- };
2054
- if (!wasReached) {
2055
- if (!within) {
2056
- return false;
2057
- }
2058
- onReached(distance);
2059
- updateSnapshot();
2060
- return true;
2061
- }
2062
- const reset = !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
2063
- if (reset) {
2064
- setSnapshot(void 0);
2065
- return false;
2066
- }
2067
- if (within) {
2068
- const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
2069
- if (changed) {
2070
- if (allowReentryOnChange) {
2071
- onReached(distance);
2072
- }
2073
- updateSnapshot();
2074
- }
2075
- }
2076
- return true;
2077
- };
2078
-
2079
- // src/utils/checkAtBottom.ts
2080
- function checkAtBottom(ctx) {
2081
- var _a3;
2082
- const state = ctx.state;
2083
- if (!state || state.initialScroll) {
2084
- return;
2085
- }
2086
- const {
2087
- queuedInitialLayout,
2088
- scrollLength,
2089
- scroll,
2090
- maintainingScrollAtEnd,
2091
- props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
2092
- } = state;
2093
- if (state.initialScroll) {
2094
- return;
2095
- }
2096
- const contentSize = getContentSize(ctx);
2097
- if (contentSize > 0 && queuedInitialLayout && !maintainingScrollAtEnd) {
2098
- const insetEnd = getContentInsetEnd(state);
2099
- const distanceFromEnd = contentSize - scroll - scrollLength - insetEnd;
2100
- const isContentLess = contentSize < scrollLength;
2101
- state.isAtEnd = isContentLess || distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
2102
- state.isEndReached = checkThreshold(
2103
- distanceFromEnd,
2104
- isContentLess,
2105
- onEndReachedThreshold * scrollLength,
2106
- state.isEndReached,
2107
- state.endReachedSnapshot,
2108
- {
2109
- contentSize,
2110
- dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
2111
- scrollPosition: scroll
2112
- },
2113
- (distance) => {
2114
- var _a4, _b;
2115
- return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
2116
- },
2117
- (snapshot) => {
2118
- state.endReachedSnapshot = snapshot;
2119
- },
2120
- true
2121
- );
2122
- }
2123
- }
2124
-
2125
- // src/utils/checkAtTop.ts
2126
- function checkAtTop(ctx) {
2127
- const state = ctx == null ? void 0 : ctx.state;
2128
- if (!state || state.initialScroll || state.scrollingTo) {
2129
- return;
2130
- }
2131
- const {
2132
- dataChangeEpoch,
2133
- isStartReached,
2134
- props: { data, onStartReachedThreshold },
2135
- scroll,
2136
- scrollLength,
2137
- startReachedSnapshot,
2138
- startReachedSnapshotDataChangeEpoch,
2139
- totalSize
2140
- } = state;
2141
- const dataLength = data.length;
2142
- const threshold = onStartReachedThreshold * scrollLength;
2143
- const dataChanged = startReachedSnapshotDataChangeEpoch !== dataChangeEpoch;
2144
- const withinThreshold = threshold > 0 && Math.abs(scroll) <= threshold;
2145
- const allowReentryOnDataChange = !!isStartReached && withinThreshold && !!dataChanged && !isInMVCPActiveMode(state);
2146
- if (isStartReached && threshold > 0 && scroll > threshold && startReachedSnapshot && (dataChanged || startReachedSnapshot.contentSize !== totalSize || startReachedSnapshot.dataLength !== dataLength)) {
2147
- state.isStartReached = false;
2148
- state.startReachedSnapshot = void 0;
2149
- state.startReachedSnapshotDataChangeEpoch = void 0;
2150
- }
2151
- state.isAtStart = scroll <= 0;
2152
- if (isStartReached && withinThreshold && dataChanged && !allowReentryOnDataChange) {
2153
- return;
2154
- }
2155
- state.isStartReached = checkThreshold(
2156
- scroll,
2157
- false,
2158
- threshold,
2159
- state.isStartReached,
2160
- allowReentryOnDataChange ? void 0 : startReachedSnapshot,
2161
- {
2162
- contentSize: totalSize,
2163
- dataLength,
2164
- scrollPosition: scroll
2165
- },
2166
- (distance) => {
2167
- var _a3, _b;
2168
- return (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
2169
- },
2170
- (snapshot) => {
2171
- state.startReachedSnapshot = snapshot;
2172
- state.startReachedSnapshotDataChangeEpoch = snapshot ? dataChangeEpoch : void 0;
2173
- },
2174
- allowReentryOnDataChange
2175
- );
2176
- }
2177
-
2178
- // src/utils/checkThresholds.ts
2179
- function checkThresholds(ctx) {
2180
- checkAtBottom(ctx);
2181
- checkAtTop(ctx);
2182
- }
2183
-
2184
- // src/utils/setInitialRenderState.ts
2185
- function setInitialRenderState(ctx, {
2186
- didLayout,
2187
- didInitialScroll
2188
- }) {
2189
- const { state } = ctx;
2190
- const {
2191
- loadStartTime,
2192
- props: { onLoad }
2193
- } = state;
2194
- if (didLayout) {
2195
- state.didContainersLayout = true;
2196
- }
2197
- if (didInitialScroll) {
2198
- state.didFinishInitialScroll = true;
2199
- }
2200
- const isReadyToRender = Boolean(state.didContainersLayout && state.didFinishInitialScroll);
2201
- if (isReadyToRender && !peek$(ctx, "readyToRender")) {
2202
- set$(ctx, "readyToRender", true);
2203
- if (onLoad) {
2204
- onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
2205
- }
2206
- }
2433
+ forceScroll,
2434
+ index,
2435
+ isInitialScroll,
2436
+ itemSize,
2437
+ offset: firstIndexOffset,
2438
+ viewOffset,
2439
+ viewPosition: viewPosition != null ? viewPosition : 0
2440
+ });
2207
2441
  }
2208
2442
 
2209
2443
  // src/core/initialScroll.ts
2210
- function syncInitialScrollOffset(state, offset) {
2211
- state.scroll = offset;
2212
- state.scrollPending = offset;
2213
- state.scrollPrev = offset;
2214
- }
2215
2444
  function dispatchInitialScroll(ctx, params) {
2216
2445
  const { forceScroll, resolvedOffset, target, waitForCompletionFrame } = params;
2217
2446
  const requestedIndex = target.index;
@@ -2232,6 +2461,11 @@ function dispatchInitialScroll(ctx, params) {
2232
2461
  }
2233
2462
  function setInitialScrollTarget(state, target, options) {
2234
2463
  var _a3;
2464
+ state.clearPreservedInitialScrollOnNextFinish = void 0;
2465
+ if (state.timeoutPreservedInitialScrollClear !== void 0) {
2466
+ clearTimeout(state.timeoutPreservedInitialScrollClear);
2467
+ state.timeoutPreservedInitialScrollClear = void 0;
2468
+ }
2235
2469
  state.initialScroll = target;
2236
2470
  if ((options == null ? void 0 : options.resetDidFinish) && state.didFinishInitialScroll) {
2237
2471
  state.didFinishInitialScroll = false;
@@ -2240,44 +2474,6 @@ function setInitialScrollTarget(state, target, options) {
2240
2474
  kind: ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset" ? "offset" : "bootstrap"
2241
2475
  });
2242
2476
  }
2243
- function finishInitialScroll(ctx, options) {
2244
- var _a3, _b, _c;
2245
- const state = ctx.state;
2246
- if ((options == null ? void 0 : options.resolvedOffset) !== void 0) {
2247
- syncInitialScrollOffset(state, options.resolvedOffset);
2248
- } else if ((options == null ? void 0 : options.syncObservedOffset) && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
2249
- const observedOffset = (_c = (_b = state.refScroller.current) == null ? void 0 : _b.getCurrentScrollOffset) == null ? void 0 : _c.call(_b);
2250
- if (typeof observedOffset === "number" && Number.isFinite(observedOffset)) {
2251
- syncInitialScrollOffset(state, observedOffset);
2252
- }
2253
- }
2254
- const complete = () => {
2255
- var _a4, _b2, _c2, _d, _e, _f, _g;
2256
- const shouldReleaseDeferredPublicOnScroll = ((_a4 = state.initialScrollSession) == null ? void 0 : _a4.kind) === "bootstrap";
2257
- const finalScrollOffset = (_d = (_c2 = (_b2 = options == null ? void 0 : options.resolvedOffset) != null ? _b2 : state.scrollPending) != null ? _c2 : state.scroll) != null ? _d : 0;
2258
- initialScrollWatchdog.clear(state);
2259
- if (!(options == null ? void 0 : options.preserveTarget)) {
2260
- state.initialScroll = void 0;
2261
- }
2262
- setInitialScrollSession(state);
2263
- if ((options == null ? void 0 : options.recalculateItems) && ((_e = state.props) == null ? void 0 : _e.data)) {
2264
- (_f = state.triggerCalculateItemsInView) == null ? void 0 : _f.call(state, { forceFullItemPositions: true });
2265
- }
2266
- if (options == null ? void 0 : options.recalculateItems) {
2267
- checkThresholds(ctx);
2268
- }
2269
- setInitialRenderState(ctx, { didInitialScroll: true });
2270
- if (shouldReleaseDeferredPublicOnScroll) {
2271
- releaseDeferredPublicOnScroll(ctx, finalScrollOffset);
2272
- }
2273
- (_g = options == null ? void 0 : options.onFinished) == null ? void 0 : _g.call(options);
2274
- };
2275
- if (options == null ? void 0 : options.waitForCompletionFrame) {
2276
- requestAnimationFrame(complete);
2277
- return;
2278
- }
2279
- complete();
2280
- }
2281
2477
  function resolveInitialScrollOffset(ctx, initialScroll) {
2282
2478
  var _a3, _b;
2283
2479
  const state = ctx.state;
@@ -2371,13 +2567,18 @@ function advanceCurrentInitialScrollSession(ctx, options) {
2371
2567
  function isNullOrUndefined2(value) {
2372
2568
  return value === null || value === void 0;
2373
2569
  }
2374
- function getMountedBufferedIndices(state) {
2375
- const { startBuffered, endBuffered } = state;
2376
- if (!isNullOrUndefined2(endBuffered) && !isNullOrUndefined2(startBuffered) && startBuffered >= 0 && endBuffered >= 0) {
2377
- 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);
2570
+ function getMountedIndicesInRange(state, start, end) {
2571
+ if (!isNullOrUndefined2(end) && !isNullOrUndefined2(start) && start >= 0 && end >= 0) {
2572
+ 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);
2378
2573
  }
2379
2574
  return [];
2380
2575
  }
2576
+ function getMountedBufferedIndices(state) {
2577
+ return getMountedIndicesInRange(state, state.startBuffered, state.endBuffered);
2578
+ }
2579
+ function getMountedNoBufferIndices(state) {
2580
+ return getMountedIndicesInRange(state, state.startNoBuffer, state.endNoBuffer);
2581
+ }
2381
2582
  function checkAllSizesKnown(state, indices = getMountedBufferedIndices(state)) {
2382
2583
  return indices.length > 0 && indices.every((index) => {
2383
2584
  const key = getId(state, index);
@@ -2596,16 +2797,22 @@ function createRetargetedBottomAlignedInitialScroll(options) {
2596
2797
  function areEquivalentBootstrapInitialScrollTargets(current, next) {
2597
2798
  return current.index === next.index && current.preserveForBottomPadding === next.preserveForBottomPadding && current.preserveForFooterLayout === next.preserveForFooterLayout && current.viewOffset === next.viewOffset && current.viewPosition === next.viewPosition;
2598
2799
  }
2599
- function clearPendingInitialScrollFooterLayout(state, target) {
2800
+ function clearPendingInitialScrollFooterLayout(ctx, options) {
2801
+ const { dataLength, stylePaddingBottom, target } = options;
2802
+ const state = ctx.state;
2600
2803
  if (!shouldPreserveInitialScrollForFooterLayout(target)) {
2601
2804
  return;
2602
2805
  }
2603
- if (state.didFinishInitialScroll && !getBootstrapInitialScrollSession(state)) {
2604
- state.initialScroll = void 0;
2605
- setInitialScrollSession(state);
2606
- return;
2607
- }
2608
- setInitialScrollTarget(state, { ...target, preserveForFooterLayout: void 0 });
2806
+ const clearedFooterTarget = createInitialScrollAtEndTarget({
2807
+ dataLength,
2808
+ footerSize: 0,
2809
+ preserveForFooterLayout: void 0,
2810
+ stylePaddingBottom
2811
+ });
2812
+ setInitialScrollTarget(state, clearedFooterTarget);
2813
+ }
2814
+ function clearFinishedViewportRetargetableInitialScroll(state) {
2815
+ clearPreservedInitialScrollTarget(state);
2609
2816
  }
2610
2817
  function didFinishedInitialScrollMoveAwayFromTarget(ctx, target, epsilon = DEFAULT_BOOTSTRAP_REVEAL_EPSILON) {
2611
2818
  const state = ctx.state;
@@ -2620,6 +2827,25 @@ function getObservedBootstrapInitialScrollOffset(state) {
2620
2827
  const observedOffset = (_b = (_a3 = state.refScroller.current) == null ? void 0 : _a3.getCurrentScrollOffset) == null ? void 0 : _b.call(_a3);
2621
2828
  return typeof observedOffset === "number" && Number.isFinite(observedOffset) ? observedOffset : (_d = (_c = state.scrollPending) != null ? _c : state.scroll) != null ? _d : 0;
2622
2829
  }
2830
+ function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {
2831
+ var _a3, _b;
2832
+ const state = ctx.state;
2833
+ const initialScroll = state.initialScroll;
2834
+ if (!state.didFinishInitialScroll || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) || (initialScroll == null ? void 0 : initialScroll.viewPosition) !== 1) {
2835
+ return;
2836
+ }
2837
+ if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2838
+ if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {
2839
+ clearPendingInitialScrollFooterLayout(ctx, {
2840
+ dataLength: state.props.data.length,
2841
+ stylePaddingBottom: (_b = state.props.stylePaddingBottom) != null ? _b : 0,
2842
+ target: initialScroll
2843
+ });
2844
+ return;
2845
+ }
2846
+ clearFinishedViewportRetargetableInitialScroll(state);
2847
+ }
2848
+ }
2623
2849
  function startBootstrapInitialScrollOnMount(ctx, options) {
2624
2850
  var _a3, _b, _c;
2625
2851
  const { initialScrollAtEnd, target } = options;
@@ -2656,15 +2882,16 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
2656
2882
  }
2657
2883
  const shouldResetDidFinish = !!(state.didFinishInitialScroll && previousDataLength === 0 && dataLength > 0 && initialScroll.index !== void 0);
2658
2884
  const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2885
+ const shouldClearFinishedResizePreservation = didDataChange && dataLength > 0 && state.didFinishInitialScroll && !bootstrapInitialScroll && !shouldResetDidFinish;
2886
+ if (shouldClearFinishedResizePreservation) {
2887
+ clearPreservedInitialScrollTarget(state);
2888
+ return;
2889
+ }
2659
2890
  const shouldRetargetBottomAligned = dataLength > 0 && (initialScrollAtEnd || isRetargetableBottomAlignedInitialScrollTarget(initialScroll));
2660
2891
  if (!didDataChange && !shouldResetDidFinish && !shouldRetargetBottomAligned) {
2661
2892
  return;
2662
2893
  }
2663
2894
  if (shouldRetargetBottomAligned) {
2664
- if (!shouldResetDidFinish && didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2665
- clearPendingInitialScrollFooterLayout(state, initialScroll);
2666
- return;
2667
- }
2668
2895
  const updatedInitialScroll = initialScrollAtEnd ? createInitialScrollAtEndTarget({
2669
2896
  dataLength,
2670
2897
  footerSize: peek$(ctx, "footerSize") || 0,
@@ -2677,6 +2904,14 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
2677
2904
  stylePaddingBottom,
2678
2905
  target: initialScroll
2679
2906
  });
2907
+ if (!shouldResetDidFinish && didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2908
+ clearPendingInitialScrollFooterLayout(ctx, {
2909
+ dataLength,
2910
+ stylePaddingBottom,
2911
+ target: initialScroll
2912
+ });
2913
+ return;
2914
+ }
2680
2915
  if (!areEquivalentBootstrapInitialScrollTargets(initialScroll, updatedInitialScroll) || !!bootstrapInitialScroll || shouldResetDidFinish || didDataChange) {
2681
2916
  setInitialScrollTarget(state, updatedInitialScroll, {
2682
2917
  resetDidFinish: shouldResetDidFinish
@@ -2718,7 +2953,11 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2718
2953
  return;
2719
2954
  }
2720
2955
  if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2721
- clearPendingInitialScrollFooterLayout(state, initialScroll);
2956
+ clearPendingInitialScrollFooterLayout(ctx, {
2957
+ dataLength,
2958
+ stylePaddingBottom,
2959
+ target: initialScroll
2960
+ });
2722
2961
  } else {
2723
2962
  const updatedInitialScroll = createInitialScrollAtEndTarget({
2724
2963
  dataLength,
@@ -2728,10 +2967,15 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2728
2967
  });
2729
2968
  const didTargetChange = initialScroll.index !== updatedInitialScroll.index || initialScroll.viewPosition !== updatedInitialScroll.viewPosition || initialScroll.viewOffset !== updatedInitialScroll.viewOffset;
2730
2969
  if (!didTargetChange) {
2731
- clearPendingInitialScrollFooterLayout(state, initialScroll);
2970
+ clearPendingInitialScrollFooterLayout(ctx, {
2971
+ dataLength,
2972
+ stylePaddingBottom,
2973
+ target: initialScroll
2974
+ });
2732
2975
  } else {
2976
+ const didFinishInitialScroll = !!state.didFinishInitialScroll;
2733
2977
  setInitialScrollTarget(state, updatedInitialScroll, {
2734
- resetDidFinish: !!state.didFinishInitialScroll
2978
+ resetDidFinish: didFinishInitialScroll
2735
2979
  });
2736
2980
  rearmBootstrapInitialScroll(ctx, {
2737
2981
  scroll: resolveInitialScrollOffset(ctx, updatedInitialScroll),
@@ -2740,6 +2984,29 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2740
2984
  }
2741
2985
  }
2742
2986
  }
2987
+ function handleBootstrapInitialScrollLayoutChange(ctx) {
2988
+ const state = ctx.state;
2989
+ const initialScroll = state.initialScroll;
2990
+ if (isOffsetInitialScrollSession(state) || state.props.data.length === 0 || !initialScroll) {
2991
+ return;
2992
+ }
2993
+ const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2994
+ if (!bootstrapInitialScroll && initialScroll.viewPosition !== 1) {
2995
+ return;
2996
+ }
2997
+ const didFinishInitialScroll = state.didFinishInitialScroll;
2998
+ if (didFinishInitialScroll) {
2999
+ setInitialScrollTarget(state, initialScroll, {
3000
+ resetDidFinish: true
3001
+ });
3002
+ state.clearPreservedInitialScrollOnNextFinish = true;
3003
+ }
3004
+ rearmBootstrapInitialScroll(ctx, {
3005
+ scroll: resolveInitialScrollOffset(ctx, initialScroll),
3006
+ seedContentOffset: didFinishInitialScroll && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
3007
+ targetIndexSeed: initialScroll.index
3008
+ });
3009
+ }
2743
3010
  function evaluateBootstrapInitialScroll(ctx) {
2744
3011
  var _a3, _b;
2745
3012
  const state = ctx.state;
@@ -2808,12 +3075,15 @@ function evaluateBootstrapInitialScroll(ctx) {
2808
3075
  }
2809
3076
  }
2810
3077
  function finishBootstrapInitialScrollWithoutScroll(ctx, resolvedOffset) {
3078
+ var _a3;
2811
3079
  const state = ctx.state;
2812
3080
  clearBootstrapInitialScrollSession(state);
3081
+ const shouldPreserveResizeTarget = !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_a3 = state.initialScroll) == null ? void 0 : _a3.viewPosition) === 1;
2813
3082
  finishInitialScroll(ctx, {
2814
- preserveTarget: shouldPreserveInitialScrollForFooterLayout(state.initialScroll),
3083
+ preserveTarget: shouldPreserveResizeTarget,
2815
3084
  recalculateItems: true,
2816
- resolvedOffset
3085
+ resolvedOffset,
3086
+ schedulePreservedTargetClear: shouldPreserveResizeTarget
2817
3087
  });
2818
3088
  }
2819
3089
  function abortBootstrapInitialScroll(ctx) {
@@ -3191,7 +3461,7 @@ function resolvePendingNativeMVCPAdjust(ctx, newScroll) {
3191
3461
  settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
3192
3462
  return true;
3193
3463
  }
3194
- if (state.pendingMaintainScrollAtEnd && state.isAtEnd && progressTowardAmount > MVCP_POSITION_EPSILON) {
3464
+ if (state.pendingMaintainScrollAtEnd && peek$(ctx, "isWithinMaintainScrollAtEndThreshold") && progressTowardAmount > MVCP_POSITION_EPSILON) {
3195
3465
  settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
3196
3466
  return true;
3197
3467
  }
@@ -3348,6 +3618,86 @@ function prepareMVCP(ctx, dataChanged) {
3348
3618
  }
3349
3619
  }
3350
3620
 
3621
+ // src/core/syncMountedContainer.ts
3622
+ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3623
+ var _a3, _b, _c, _d, _e, _f, _g, _h;
3624
+ const state = ctx.state;
3625
+ const {
3626
+ columns,
3627
+ columnSpans,
3628
+ positions,
3629
+ props: { data, itemsAreEqual, keyExtractor }
3630
+ } = state;
3631
+ const item = data[itemIndex];
3632
+ if (item === void 0) {
3633
+ return { didChangePosition: false, didRefreshData: false };
3634
+ }
3635
+ const updateLayout = (_a3 = options == null ? void 0 : options.updateLayout) != null ? _a3 : true;
3636
+ let didChangePosition = false;
3637
+ let didRefreshData = false;
3638
+ if (updateLayout) {
3639
+ const positionValue = positions[itemIndex];
3640
+ if (positionValue === void 0) {
3641
+ set$(ctx, `containerPosition${containerIndex}`, POSITION_OUT_OF_VIEW);
3642
+ return { didChangePosition: false, didRefreshData: false };
3643
+ }
3644
+ const position = (positionValue || 0) - ((_b = options == null ? void 0 : options.scrollAdjustPending) != null ? _b : 0);
3645
+ const column = columns[itemIndex] || 1;
3646
+ const span = columnSpans[itemIndex] || 1;
3647
+ const prevPos = peek$(ctx, `containerPosition${containerIndex}`);
3648
+ const prevColumn = peek$(ctx, `containerColumn${containerIndex}`);
3649
+ const prevSpan = peek$(ctx, `containerSpan${containerIndex}`);
3650
+ if (position > POSITION_OUT_OF_VIEW && position !== prevPos) {
3651
+ set$(ctx, `containerPosition${containerIndex}`, position);
3652
+ didChangePosition = true;
3653
+ }
3654
+ if (column >= 0 && column !== prevColumn) {
3655
+ set$(ctx, `containerColumn${containerIndex}`, column);
3656
+ }
3657
+ if (span !== prevSpan) {
3658
+ set$(ctx, `containerSpan${containerIndex}`, span);
3659
+ }
3660
+ }
3661
+ const prevData = peek$(ctx, `containerItemData${containerIndex}`);
3662
+ if (prevData !== item) {
3663
+ 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;
3664
+ const cachedComparison = (_e = pendingDataComparison == null ? void 0 : pendingDataComparison.byIndex[itemIndex]) != null ? _e : 0;
3665
+ if (cachedComparison === 2) {
3666
+ set$(ctx, `containerItemData${containerIndex}`, item);
3667
+ didRefreshData = true;
3668
+ } else if (cachedComparison !== 1) {
3669
+ const itemKey = (_g = (_f = peek$(ctx, `containerItemKey${containerIndex}`)) != null ? _f : state.idCache[itemIndex]) != null ? _g : getId(state, itemIndex);
3670
+ const prevKey = keyExtractor == null ? void 0 : keyExtractor(prevData, itemIndex);
3671
+ if (prevData === void 0 || !keyExtractor || prevKey !== itemKey) {
3672
+ set$(ctx, `containerItemData${containerIndex}`, item);
3673
+ didRefreshData = true;
3674
+ } else if (!itemsAreEqual) {
3675
+ set$(ctx, `containerItemData${containerIndex}`, item);
3676
+ didRefreshData = true;
3677
+ } else {
3678
+ const isEqual = itemsAreEqual(prevData, item, itemIndex, data);
3679
+ if (!state.pendingDataComparison || state.pendingDataComparison.previousData !== state.previousData || state.pendingDataComparison.nextData !== data) {
3680
+ if (state.previousData) {
3681
+ state.pendingDataComparison = {
3682
+ byIndex: [],
3683
+ nextData: data,
3684
+ previousData: state.previousData
3685
+ };
3686
+ }
3687
+ }
3688
+ if ((_h = state.pendingDataComparison) == null ? void 0 : _h.byIndex) {
3689
+ state.pendingDataComparison.byIndex[itemIndex] = isEqual ? 1 : 2;
3690
+ }
3691
+ if (!isEqual) {
3692
+ set$(ctx, `containerItemData${containerIndex}`, item);
3693
+ didRefreshData = true;
3694
+ }
3695
+ }
3696
+ }
3697
+ }
3698
+ return { didChangePosition, didRefreshData };
3699
+ }
3700
+
3351
3701
  // src/core/prepareColumnStartState.ts
3352
3702
  function prepareColumnStartState(ctx, startIndex, useAverageSize) {
3353
3703
  var _a3;
@@ -3510,9 +3860,10 @@ function updateSnapToOffsets(ctx) {
3510
3860
  }
3511
3861
 
3512
3862
  // src/core/updateItemPositions.ts
3513
- function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP } = {
3863
+ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP, optimizeForVisibleWindow = false } = {
3514
3864
  doMVCP: false,
3515
3865
  forceFullUpdate: false,
3866
+ optimizeForVisibleWindow: false,
3516
3867
  scrollBottomBuffered: -1,
3517
3868
  startIndex: 0
3518
3869
  }) {
@@ -3537,7 +3888,7 @@ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffere
3537
3888
  const layoutConfig = overrideItemLayout ? { span: 1 } : void 0;
3538
3889
  const lastScrollDelta = state.lastScrollDelta;
3539
3890
  const velocity = getScrollVelocity(state);
3540
- const shouldOptimize = !forceFullUpdate && !dataChanged && (Math.abs(velocity) > 0 || state.scrollLength > 0 && lastScrollDelta > state.scrollLength);
3891
+ const shouldOptimize = !forceFullUpdate && !dataChanged && (optimizeForVisibleWindow || Math.abs(velocity) > 0 || state.scrollLength > 0 && lastScrollDelta > state.scrollLength);
3541
3892
  const maxVisibleArea = scrollBottomBuffered + 1e3;
3542
3893
  const useAverageSize = !getEstimatedItemSize;
3543
3894
  const preferCachedSize = !doMVCP || dataChanged || state.scrollAdjustHandler.getAdjust() !== 0 || ((_b = peek$(ctx, "scrollAdjustPending")) != null ? _b : 0) !== 0;
@@ -3652,7 +4003,15 @@ function ensureViewabilityState(ctx, configId) {
3652
4003
  }
3653
4004
  let state = map.get(configId);
3654
4005
  if (!state) {
3655
- state = { end: -1, previousEnd: -1, previousStart: -1, start: -1, viewableItems: [] };
4006
+ state = {
4007
+ end: -1,
4008
+ endBuffered: -1,
4009
+ previousEnd: -1,
4010
+ previousStart: -1,
4011
+ start: -1,
4012
+ startBuffered: -1,
4013
+ viewableItems: []
4014
+ };
3656
4015
  map.set(configId, state);
3657
4016
  }
3658
4017
  return state;
@@ -3672,7 +4031,7 @@ function setupViewability(props) {
3672
4031
  }
3673
4032
  return viewabilityConfigCallbackPairs;
3674
4033
  }
3675
- function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollSize, start, end) {
4034
+ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollSize, start, end, startBuffered = start, endBuffered = end) {
3676
4035
  const {
3677
4036
  timeouts,
3678
4037
  props: { data }
@@ -3681,6 +4040,8 @@ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollS
3681
4040
  const viewabilityState = ensureViewabilityState(ctx, viewabilityConfigCallbackPair.viewabilityConfig.id);
3682
4041
  viewabilityState.start = start;
3683
4042
  viewabilityState.end = end;
4043
+ viewabilityState.startBuffered = startBuffered;
4044
+ viewabilityState.endBuffered = endBuffered;
3684
4045
  if (viewabilityConfigCallbackPair.viewabilityConfig.minimumViewTime) {
3685
4046
  const timer = setTimeout(() => {
3686
4047
  timeouts.delete(timer);
@@ -3696,7 +4057,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
3696
4057
  const { viewabilityConfig, onViewableItemsChanged } = viewabilityConfigCallbackPair;
3697
4058
  const configId = viewabilityConfig.id;
3698
4059
  const viewabilityState = ensureViewabilityState(ctx, configId);
3699
- const { viewableItems: previousViewableItems, start, end } = viewabilityState;
4060
+ const { viewableItems: previousViewableItems, start, end, startBuffered, endBuffered } = viewabilityState;
3700
4061
  const viewabilityTokens = /* @__PURE__ */ new Map();
3701
4062
  for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
3702
4063
  viewabilityTokens.set(
@@ -3765,7 +4126,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
3765
4126
  maybeUpdateViewabilityCallback(ctx, configId, change.containerId, change);
3766
4127
  }
3767
4128
  if (onViewableItemsChanged) {
3768
- onViewableItemsChanged({ changed, viewableItems });
4129
+ onViewableItemsChanged({ changed, end, endBuffered, start, startBuffered, viewableItems });
3769
4130
  }
3770
4131
  }
3771
4132
  for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
@@ -3866,6 +4227,7 @@ function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pen
3866
4227
  const numContainers = peek$(ctx, "numContainers");
3867
4228
  const state = ctx.state;
3868
4229
  const { stickyContainerPool, containerItemTypes } = state;
4230
+ const shouldAvoidAssignedContainerReuse = state.props.recycleItems && !!state.props.positionComponentInternal;
3869
4231
  const result = [];
3870
4232
  const availableContainers = [];
3871
4233
  const pendingRemovalSet = new Set(pendingRemoval);
@@ -3922,18 +4284,20 @@ function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pen
3922
4284
  }
3923
4285
  }
3924
4286
  }
3925
- for (let u = 0; u < numContainers && result.length < numNeeded; u++) {
3926
- if (stickyContainerPool.has(u)) {
3927
- continue;
3928
- }
3929
- const key = peek$(ctx, `containerItemKey${u}`);
3930
- if (key === void 0) continue;
3931
- const index = state.indexByKey.get(key);
3932
- const isOutOfView = index < startBuffered || index > endBuffered;
3933
- if (isOutOfView) {
3934
- const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
3935
- if (!requiredItemTypes || typeIndex < neededTypes.length && canReuseContainer(u, neededTypes[typeIndex])) {
3936
- availableContainers.push({ distance, index: u });
4287
+ if (!shouldAvoidAssignedContainerReuse) {
4288
+ for (let u = 0; u < numContainers && result.length < numNeeded; u++) {
4289
+ if (stickyContainerPool.has(u)) {
4290
+ continue;
4291
+ }
4292
+ const key = peek$(ctx, `containerItemKey${u}`);
4293
+ if (key === void 0) continue;
4294
+ const index = state.indexByKey.get(key);
4295
+ const isOutOfView = index < startBuffered || index > endBuffered;
4296
+ if (isOutOfView) {
4297
+ const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
4298
+ if (!requiredItemTypes || typeIndex < neededTypes.length && canReuseContainer(u, neededTypes[typeIndex])) {
4299
+ availableContainers.push({ distance, index: u });
4300
+ }
3937
4301
  }
3938
4302
  }
3939
4303
  }
@@ -4077,7 +4441,6 @@ function calculateItemsInView(ctx, params = {}) {
4077
4441
  alwaysRenderIndicesSet,
4078
4442
  drawDistance,
4079
4443
  getItemType,
4080
- itemsAreEqual,
4081
4444
  keyExtractor,
4082
4445
  onStickyHeaderChange
4083
4446
  },
@@ -4104,11 +4467,11 @@ function calculateItemsInView(ctx, params = {}) {
4104
4467
  const numColumns = peek$(ctx, "numColumns");
4105
4468
  const speed = getScrollVelocity(state);
4106
4469
  const scrollExtra = 0;
4107
- const { queuedInitialLayout } = state;
4108
- 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 ? (
4109
4472
  // Before the initial layout settles, keep viewport math anchored to the
4110
4473
  // current initial-scroll target instead of transient native adjustments.
4111
- resolveInitialScrollOffset(ctx, state.initialScroll)
4474
+ resolveInitialScrollOffset(ctx, initialScroll)
4112
4475
  ) : state.scroll;
4113
4476
  const scrollAdjustPending = (_c = peek$(ctx, "scrollAdjustPending")) != null ? _c : 0;
4114
4477
  const scrollAdjustPad = scrollAdjustPending - topPad;
@@ -4153,9 +4516,11 @@ function calculateItemsInView(ctx, params = {}) {
4153
4516
  columnSpans.length = 0;
4154
4517
  }
4155
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;
4156
4520
  updateItemPositions(ctx, dataChanged, {
4157
4521
  doMVCP,
4158
4522
  forceFullUpdate: !!forceFullItemPositions,
4523
+ optimizeForVisibleWindow,
4159
4524
  scrollBottomBuffered,
4160
4525
  startIndex
4161
4526
  });
@@ -4403,33 +4768,11 @@ function calculateItemsInView(ctx, params = {}) {
4403
4768
  set$(ctx, `containerSpan${i}`, 1);
4404
4769
  } else {
4405
4770
  const itemIndex = indexByKey.get(itemKey);
4406
- const item = data[itemIndex];
4407
- if (item !== void 0) {
4408
- const positionValue = positions[itemIndex];
4409
- if (positionValue === void 0) {
4410
- set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
4411
- } else {
4412
- const position = (positionValue || 0) - scrollAdjustPending;
4413
- const column = columns[itemIndex] || 1;
4414
- const span = columnSpans[itemIndex] || 1;
4415
- const prevPos = peek$(ctx, `containerPosition${i}`);
4416
- const prevColumn = peek$(ctx, `containerColumn${i}`);
4417
- const prevSpan = peek$(ctx, `containerSpan${i}`);
4418
- const prevData = peek$(ctx, `containerItemData${i}`);
4419
- if (position > POSITION_OUT_OF_VIEW && position !== prevPos) {
4420
- set$(ctx, `containerPosition${i}`, position);
4421
- didChangePositions = true;
4422
- }
4423
- if (column >= 0 && column !== prevColumn) {
4424
- set$(ctx, `containerColumn${i}`, column);
4425
- }
4426
- if (span !== prevSpan) {
4427
- set$(ctx, `containerSpan${i}`, span);
4428
- }
4429
- if (prevData !== item && (itemsAreEqual ? !itemsAreEqual(prevData, item, itemIndex, data) : true)) {
4430
- set$(ctx, `containerItemData${i}`, item);
4431
- }
4432
- }
4771
+ if (itemIndex !== void 0) {
4772
+ didChangePositions = syncMountedContainer(ctx, i, itemIndex, {
4773
+ scrollAdjustPending,
4774
+ updateLayout: true
4775
+ }).didChangePosition || didChangePositions;
4433
4776
  }
4434
4777
  }
4435
4778
  }
@@ -4440,7 +4783,10 @@ function calculateItemsInView(ctx, params = {}) {
4440
4783
  evaluateBootstrapInitialScroll(ctx);
4441
4784
  return;
4442
4785
  }
4443
- 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)) {
4444
4790
  setDidLayout(ctx);
4445
4791
  handleInitialScrollLayoutReady(ctx);
4446
4792
  }
@@ -4451,7 +4797,9 @@ function calculateItemsInView(ctx, params = {}) {
4451
4797
  viewabilityConfigCallbackPairs,
4452
4798
  scrollLength,
4453
4799
  startNoBuffer,
4454
- endNoBuffer
4800
+ endNoBuffer,
4801
+ startBuffered != null ? startBuffered : startNoBuffer,
4802
+ endBuffered != null ? endBuffered : endNoBuffer
4455
4803
  );
4456
4804
  }
4457
4805
  if (onStickyHeaderChange && stickyIndicesArr.length > 0 && nextActiveStickyIndex !== void 0 && nextActiveStickyIndex !== previousStickyIndex) {
@@ -4463,37 +4811,17 @@ function calculateItemsInView(ctx, params = {}) {
4463
4811
  });
4464
4812
  }
4465
4813
 
4466
- // src/core/checkActualChange.ts
4467
- function checkActualChange(state, dataProp, previousData) {
4468
- if (!previousData || !dataProp || dataProp.length !== previousData.length) {
4469
- return true;
4470
- }
4471
- const {
4472
- idCache,
4473
- props: { keyExtractor }
4474
- } = state;
4475
- for (let i = 0; i < dataProp.length; i++) {
4476
- if (dataProp[i] !== previousData[i]) {
4477
- return true;
4478
- }
4479
- if (keyExtractor ? idCache[i] !== keyExtractor(previousData[i], i) : dataProp[i] !== previousData[i]) {
4480
- return true;
4481
- }
4482
- }
4483
- return false;
4484
- }
4485
-
4486
4814
  // src/core/doMaintainScrollAtEnd.ts
4487
4815
  function doMaintainScrollAtEnd(ctx) {
4488
4816
  const state = ctx.state;
4489
4817
  const {
4490
4818
  didContainersLayout,
4491
- isAtEnd,
4492
4819
  pendingNativeMVCPAdjust,
4493
4820
  refScroller,
4494
4821
  props: { maintainScrollAtEnd }
4495
4822
  } = state;
4496
- const shouldMaintainScrollAtEnd = !!(isAtEnd && maintainScrollAtEnd && didContainersLayout);
4823
+ const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
4824
+ const shouldMaintainScrollAtEnd = !!(isWithinMaintainScrollAtEndThreshold && maintainScrollAtEnd && didContainersLayout);
4497
4825
  if (pendingNativeMVCPAdjust) {
4498
4826
  state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
4499
4827
  return false;
@@ -4506,7 +4834,7 @@ function doMaintainScrollAtEnd(ctx) {
4506
4834
  }
4507
4835
  requestAnimationFrame(() => {
4508
4836
  var _a3;
4509
- if (state.isAtEnd) {
4837
+ if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
4510
4838
  state.maintainingScrollAtEnd = true;
4511
4839
  (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
4512
4840
  animated: maintainScrollAtEnd.animated
@@ -4524,68 +4852,22 @@ function doMaintainScrollAtEnd(ctx) {
4524
4852
  return false;
4525
4853
  }
4526
4854
 
4527
- // src/utils/updateAveragesOnDataChange.ts
4528
- function updateAveragesOnDataChange(state, oldData, newData) {
4529
- var _a3;
4530
- const {
4531
- averageSizes,
4532
- sizesKnown,
4533
- indexByKey,
4534
- props: { itemsAreEqual, getItemType, keyExtractor }
4535
- } = state;
4536
- if (!itemsAreEqual || !oldData.length || !newData.length) {
4537
- for (const key in averageSizes) {
4538
- delete averageSizes[key];
4539
- }
4540
- return;
4541
- }
4542
- const itemTypesToPreserve = {};
4543
- const newDataLength = newData.length;
4544
- const oldDataLength = oldData.length;
4545
- for (let newIndex = 0; newIndex < newDataLength; newIndex++) {
4546
- const newItem = newData[newIndex];
4547
- const id = keyExtractor ? keyExtractor(newItem, newIndex) : String(newIndex);
4548
- const oldIndex = indexByKey.get(id);
4549
- if (oldIndex !== void 0 && oldIndex < oldDataLength) {
4550
- const knownSize = sizesKnown.get(id);
4551
- if (knownSize === void 0) continue;
4552
- const oldItem = oldData[oldIndex];
4553
- const areEqual = itemsAreEqual(oldItem, newItem, newIndex, newData);
4554
- if (areEqual) {
4555
- const itemType = getItemType ? (_a3 = getItemType(newItem, newIndex)) != null ? _a3 : "" : "";
4556
- let typeData = itemTypesToPreserve[itemType];
4557
- if (!typeData) {
4558
- typeData = itemTypesToPreserve[itemType] = { count: 0, totalSize: 0 };
4559
- }
4560
- typeData.totalSize += knownSize;
4561
- typeData.count++;
4562
- }
4563
- }
4564
- }
4565
- for (const key in averageSizes) {
4566
- delete averageSizes[key];
4567
- }
4568
- for (const itemType in itemTypesToPreserve) {
4569
- const { totalSize, count } = itemTypesToPreserve[itemType];
4570
- if (count > 0) {
4571
- averageSizes[itemType] = {
4572
- avg: totalSize / count,
4573
- num: count
4574
- };
4575
- }
4576
- }
4577
- }
4578
-
4579
4855
  // src/core/checkResetContainers.ts
4580
- function checkResetContainers(ctx, dataProp) {
4856
+ function checkResetContainers(ctx, dataProp, { didColumnsChange = false } = {}) {
4581
4857
  const state = ctx.state;
4582
4858
  const { previousData } = state;
4583
- if (previousData) {
4584
- updateAveragesOnDataChange(state, previousData, dataProp);
4585
- }
4586
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
+ }
4587
4869
  calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
4588
- const shouldMaintainScrollAtEnd = maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onDataChange;
4870
+ const shouldMaintainScrollAtEnd = !didColumnsChange && (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onDataChange);
4589
4871
  const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx);
4590
4872
  if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
4591
4873
  state.isEndReached = false;
@@ -4596,6 +4878,53 @@ function checkResetContainers(ctx, dataProp) {
4596
4878
  delete state.previousData;
4597
4879
  }
4598
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
+
4599
4928
  // src/core/doInitialAllocateContainers.ts
4600
4929
  function doInitialAllocateContainers(ctx) {
4601
4930
  var _a3, _b, _c;
@@ -4839,6 +5168,7 @@ function onScroll(ctx, event) {
4839
5168
  state.scrollPending = newScroll;
4840
5169
  updateScroll(ctx, newScroll, insetChanged);
4841
5170
  trackInitialScrollNativeProgress(state, newScroll);
5171
+ clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx);
4842
5172
  if (state.scrollingTo) {
4843
5173
  checkFinishedScroll(ctx);
4844
5174
  }
@@ -4902,6 +5232,43 @@ var ScrollAdjustHandler = class {
4902
5232
  }
4903
5233
  };
4904
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
+
4905
5272
  // src/core/updateItemSize.ts
4906
5273
  function runOrScheduleMVCPRecalculate(ctx) {
4907
5274
  const state = ctx.state;
@@ -4983,6 +5350,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
4983
5350
  previous: size - diff,
4984
5351
  size
4985
5352
  });
5353
+ maybeUpdateAnchoredEndSpace(ctx);
4986
5354
  }
4987
5355
  if (minIndexSizeChanged !== void 0) {
4988
5356
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
@@ -5106,7 +5474,7 @@ function createImperativeHandle(ctx) {
5106
5474
  const IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS = 800;
5107
5475
  const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
5108
5476
  let imperativeScrollToken = 0;
5109
- 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;
5110
5478
  const runWhenSettled = (token, run) => {
5111
5479
  const startedAt = Date.now();
5112
5480
  let stableFrames = 0;
@@ -5128,9 +5496,10 @@ function createImperativeHandle(ctx) {
5128
5496
  };
5129
5497
  requestAnimationFrame(check);
5130
5498
  };
5131
- const runScrollWithPromise = (run) => new Promise((resolve) => {
5499
+ const runScrollWithPromise = (run, options) => new Promise((resolve) => {
5132
5500
  var _a3;
5133
5501
  const token = ++imperativeScrollToken;
5502
+ const shouldWaitOneFrame = !!(options == null ? void 0 : options.shouldWaitOneFrame);
5134
5503
  (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
5135
5504
  state.pendingScrollResolve = resolve;
5136
5505
  const runNow = () => {
@@ -5145,11 +5514,12 @@ function createImperativeHandle(ctx) {
5145
5514
  resolve();
5146
5515
  }
5147
5516
  };
5517
+ const execute = shouldWaitOneFrame ? () => requestAnimationFrame(runNow) : runNow;
5148
5518
  if (isSettlingAfterDataChange()) {
5149
- runWhenSettled(token, runNow);
5150
- return;
5519
+ runWhenSettled(token, execute);
5520
+ } else {
5521
+ execute();
5151
5522
  }
5152
- runNow();
5153
5523
  });
5154
5524
  const scrollIndexIntoView = (options) => {
5155
5525
  if (state) {
@@ -5206,10 +5576,13 @@ function createImperativeHandle(ctx) {
5206
5576
  },
5207
5577
  end: state.endNoBuffer,
5208
5578
  endBuffered: state.endBuffered,
5209
- isAtEnd: state.isAtEnd,
5210
- isAtStart: state.isAtStart,
5579
+ isAtEnd: peek$(ctx, "isAtEnd"),
5580
+ isAtStart: peek$(ctx, "isAtStart"),
5211
5581
  isEndReached: state.isEndReached,
5582
+ isNearEnd: peek$(ctx, "isNearEnd"),
5583
+ isNearStart: peek$(ctx, "isNearStart"),
5212
5584
  isStartReached: state.isStartReached,
5585
+ isWithinMaintainScrollAtEndThreshold: peek$(ctx, "isWithinMaintainScrollAtEndThreshold"),
5213
5586
  listen: (signalName, cb) => listen$(ctx, signalName, cb),
5214
5587
  listenToPosition: (key, cb) => listenPosition$(ctx, key, cb),
5215
5588
  positionAtIndex: (index) => state.positions[index],
@@ -5256,10 +5629,15 @@ function createImperativeHandle(ctx) {
5256
5629
  }
5257
5630
  return false;
5258
5631
  }),
5259
- scrollToIndex: (params) => runScrollWithPromise(() => {
5260
- scrollToIndex(ctx, params);
5261
- return true;
5262
- }),
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
+ ),
5263
5641
  scrollToItem: ({ item, ...props }) => runScrollWithPromise(() => {
5264
5642
  const data = state.props.data;
5265
5643
  const index = data.indexOf(item);
@@ -5355,7 +5733,7 @@ function getRenderedItem(ctx, key) {
5355
5733
  item,
5356
5734
  type: getItemType ? (_a3 = getItemType(item, index)) != null ? _a3 : "" : ""
5357
5735
  };
5358
- renderedItem = isFunction(renderItem) ? renderItem(itemProps) : React3__default.createElement(renderItem, itemProps);
5736
+ renderedItem = React3__default.createElement(renderItem, itemProps);
5359
5737
  }
5360
5738
  return { index, item: data[index], renderedItem };
5361
5739
  }
@@ -5489,9 +5867,18 @@ var LegendList = typedMemo(
5489
5867
  })
5490
5868
  );
5491
5869
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
5492
- 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
+ }
5493
5879
  const {
5494
5880
  alignItemsAtEnd = false,
5881
+ anchoredEndSpace,
5495
5882
  alwaysRender,
5496
5883
  columnWrapperStyle,
5497
5884
  contentContainerStyle: contentContainerStyleProp,
@@ -5557,7 +5944,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5557
5944
  const positionComponentInternal = props.positionComponentInternal;
5558
5945
  const stickyPositionComponentInternal = props.stickyPositionComponentInternal;
5559
5946
  const {
5560
- childrenMode,
5561
5947
  positionComponentInternal: _positionComponentInternal,
5562
5948
  stickyPositionComponentInternal: _stickyPositionComponentInternal,
5563
5949
  ...restProps
@@ -5621,18 +6007,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5621
6007
  dataVersion,
5622
6008
  keyExtractor
5623
6009
  ]);
5624
- if (IS_DEV && stickyIndicesDeprecated && !stickyHeaderIndicesProp) {
5625
- warnDevOnce(
5626
- "stickyIndices",
5627
- "stickyIndices has been renamed to stickyHeaderIndices. Please update your props to use stickyHeaderIndices."
5628
- );
5629
- }
5630
- if (IS_DEV && useWindowScroll && renderScrollComponent) {
5631
- warnDevOnce(
5632
- "useWindowScrollRenderScrollComponent",
5633
- "useWindowScroll is not supported when renderScrollComponent is provided."
5634
- );
5635
- }
5636
6010
  const useWindowScrollResolved = !!useWindowScroll && !renderScrollComponent;
5637
6011
  const refState = useRef(void 0);
5638
6012
  const hasOverrideItemLayout = !!overrideItemLayout;
@@ -5641,7 +6015,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5641
6015
  if (!ctx.state) {
5642
6016
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : { height: 0, width: 0 } )[horizontal ? "width" : "height"];
5643
6017
  ctx.state = {
5644
- activeStickyIndex: -1,
5645
6018
  averageSizes: {},
5646
6019
  columnSpans: [],
5647
6020
  columns: [],
@@ -5665,8 +6038,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5665
6038
  kind: initialScrollUsesOffsetOnly ? "offset" : "bootstrap",
5666
6039
  previousDataLength: dataProp.length
5667
6040
  } : void 0,
5668
- isAtEnd: false,
5669
- isAtStart: false,
5670
6041
  isEndReached: null,
5671
6042
  isFirst: true,
5672
6043
  isStartReached: null,
@@ -5677,6 +6048,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5677
6048
  minIndexSizeChanged: 0,
5678
6049
  nativeContentInset: void 0,
5679
6050
  nativeMarginTop: 0,
6051
+ pendingDataComparison: void 0,
5680
6052
  pendingNativeMVCPAdjust: void 0,
5681
6053
  positions: [],
5682
6054
  props: {},
@@ -5715,22 +6087,29 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5715
6087
  }
5716
6088
  const state = refState.current;
5717
6089
  const isFirstLocal = state.isFirst;
5718
- state.didColumnsChange = numColumnsProp !== state.props.numColumns;
6090
+ const previousNumColumnsProp = state.props.numColumns;
6091
+ state.didColumnsChange = numColumnsProp !== previousNumColumnsProp;
5719
6092
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
5720
6093
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
5721
- 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
+ }
5722
6098
  if (didDataChangeLocal) {
5723
6099
  state.dataChangeEpoch += 1;
5724
6100
  state.dataChangeNeedsScrollUpdate = true;
5725
6101
  state.didDataChange = true;
5726
6102
  state.previousData = state.props.data;
5727
6103
  }
5728
- 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;
5729
6107
  state.props = {
5730
6108
  alignItemsAtEnd,
5731
6109
  alwaysRender,
5732
6110
  alwaysRenderIndicesArr: alwaysRenderIndices.arr,
5733
6111
  alwaysRenderIndicesSet: alwaysRenderIndices.set,
6112
+ anchoredEndSpace: anchoredEndSpaceResolved,
5734
6113
  animatedProps: animatedPropsInternal,
5735
6114
  contentInset,
5736
6115
  data: dataProp,
@@ -5815,16 +6194,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5815
6194
  if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
5816
6195
  refState.current.lastBatchingAction = Date.now();
5817
6196
  if (!keyExtractorProp && !isFirstLocal && didDataChangeLocal) {
5818
- IS_DEV && !childrenMode && warnDevOnce(
5819
- "keyExtractor",
5820
- "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."
5821
- );
5822
6197
  refState.current.sizes.clear();
5823
6198
  refState.current.positions.length = 0;
5824
6199
  refState.current.totalSize = 0;
5825
6200
  set$(ctx, "totalSize", 0);
5826
6201
  }
5827
6202
  }
6203
+ if (IS_DEV) {
6204
+ useDevChecks(props);
6205
+ }
5828
6206
  useLayoutEffect(() => {
5829
6207
  handleInitialScrollDataChange(ctx, {
5830
6208
  dataLength: dataProp.length,
@@ -5834,6 +6212,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5834
6212
  useBootstrapInitialScroll: usesBootstrapInitialScroll
5835
6213
  });
5836
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
+ ]);
5837
6226
  const onLayoutFooter = useCallback(
5838
6227
  (layout) => {
5839
6228
  if (!usesBootstrapInitialScroll) {
@@ -5849,14 +6238,21 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5849
6238
  [dataProp.length, initialScrollAtEnd, horizontal, stylePaddingBottomState, usesBootstrapInitialScroll]
5850
6239
  );
5851
6240
  const onLayoutChange = useCallback(
5852
- (layout) => {
6241
+ (layout, fromLayoutEffect) => {
6242
+ const previousScrollLength = state.scrollLength;
6243
+ const previousOtherAxisSize = state.otherAxisSize;
5853
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
+ }
5854
6250
  if (usesBootstrapInitialScroll) {
5855
6251
  return;
5856
6252
  }
5857
6253
  advanceCurrentInitialScrollSession(ctx);
5858
6254
  },
5859
- [usesBootstrapInitialScroll]
6255
+ [dataProp.length, initialScrollAtEnd, stylePaddingBottomState, usesBootstrapInitialScroll]
5860
6256
  );
5861
6257
  const { onLayout } = useOnLayoutSync({
5862
6258
  onLayoutChange,
@@ -5869,6 +6265,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5869
6265
  updateSnapToOffsets(ctx);
5870
6266
  }
5871
6267
  }, [snapToIndices]);
6268
+ useLayoutEffect(
6269
+ () => initializeStateVars(true),
6270
+ [dataVersion, memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingBottomState, stylePaddingTopState]
6271
+ );
5872
6272
  useLayoutEffect(() => {
5873
6273
  const {
5874
6274
  didColumnsChange,
@@ -5878,7 +6278,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5878
6278
  } = state;
5879
6279
  const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx);
5880
6280
  if (!didAllocateContainers && !isFirst && (didDataChange || didColumnsChange)) {
5881
- checkResetContainers(ctx, data);
6281
+ checkResetContainers(ctx, data, { didColumnsChange });
6282
+ }
6283
+ if (didDataChange) {
6284
+ state.pendingDataComparison = void 0;
5882
6285
  }
5883
6286
  state.didColumnsChange = false;
5884
6287
  state.didDataChange = false;
@@ -5893,10 +6296,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5893
6296
  (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { forceFullItemPositions: true });
5894
6297
  }
5895
6298
  }, [extraData, hasOverrideItemLayout, numColumnsProp]);
5896
- useLayoutEffect(
5897
- () => initializeStateVars(true),
5898
- [dataVersion, memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingBottomState, stylePaddingTopState]
5899
- );
5900
6299
  useEffect(() => {
5901
6300
  if (!onMetricsChange) {
5902
6301
  return;
@@ -5929,15 +6328,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5929
6328
  state.viewabilityConfigCallbackPairs = viewability;
5930
6329
  state.enableScrollForNextCalculateItemsInView = !viewability;
5931
6330
  }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
6331
+ useInit(() => {
6332
+ });
5932
6333
  useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx), []);
5933
- {
5934
- useEffect(() => {
5935
- if (usesBootstrapInitialScroll) {
5936
- return;
5937
- }
5938
- advanceCurrentInitialScrollSession(ctx);
5939
- }, [usesBootstrapInitialScroll]);
5940
- }
6334
+ useEffect(() => {
6335
+ if (usesBootstrapInitialScroll) {
6336
+ return;
6337
+ }
6338
+ advanceCurrentInitialScrollSession(ctx);
6339
+ }, [ctx, usesBootstrapInitialScroll]);
5941
6340
  const fns = useMemo(
5942
6341
  () => ({
5943
6342
  getRenderedItem: (key) => getRenderedItem(ctx, key),
@@ -5975,7 +6374,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5975
6374
  onScroll: onScrollHandler,
5976
6375
  recycleItems,
5977
6376
  refreshControl: refreshControlElement ? stylePaddingTopState > 0 ? React3.cloneElement(refreshControlElement, {
5978
- progressViewOffset: ((_f = refreshControlElement.props.progressViewOffset) != null ? _f : 0) + stylePaddingTopState
6377
+ progressViewOffset: ((_g = refreshControlElement.props.progressViewOffset) != null ? _g : 0) + stylePaddingTopState
5979
6378
  }) : refreshControlElement : onRefresh && /* @__PURE__ */ React3.createElement(
5980
6379
  RefreshControl,
5981
6380
  {
@@ -5986,7 +6385,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5986
6385
  ),
5987
6386
  refScrollView: combinedRef,
5988
6387
  renderScrollComponent,
5989
- scrollAdjustHandler: (_g = refState.current) == null ? void 0 : _g.scrollAdjustHandler,
6388
+ scrollAdjustHandler: (_h = refState.current) == null ? void 0 : _h.scrollAdjustHandler,
5990
6389
  scrollEventThrottle: 0,
5991
6390
  snapToIndices,
5992
6391
  stickyHeaderIndices,