@legendapp/list 3.0.6 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/react-native.js CHANGED
@@ -152,6 +152,7 @@ function StateProvider({ children }) {
152
152
  mapViewabilityConfigStates: /* @__PURE__ */ new Map(),
153
153
  mapViewabilityValues: /* @__PURE__ */ new Map(),
154
154
  positionListeners: /* @__PURE__ */ new Map(),
155
+ scrollAxisGap: 0,
155
156
  state: void 0,
156
157
  values: /* @__PURE__ */ new Map([
157
158
  ["alignItemsAtEndPadding", 0],
@@ -164,6 +165,7 @@ function StateProvider({ children }) {
164
165
  ["isNearEnd", false],
165
166
  ["isNearStart", false],
166
167
  ["isWithinMaintainScrollAtEndThreshold", false],
168
+ ["adaptiveRender", "light"],
167
169
  ["totalSize", 0],
168
170
  ["scrollAdjustPending", 0]
169
171
  ]),
@@ -609,6 +611,24 @@ var ContextContainer = React2.createContext(null);
609
611
  function useContextContainer() {
610
612
  return React2.useContext(ContextContainer);
611
613
  }
614
+ function useAdaptiveRender() {
615
+ const [mode] = useArr$(["adaptiveRender"]);
616
+ return mode;
617
+ }
618
+ function useAdaptiveRenderChange(callback) {
619
+ const ctx = useStateContext();
620
+ const callbackRef = React2.useRef(callback);
621
+ callbackRef.current = callback;
622
+ React2.useLayoutEffect(() => {
623
+ let mode = peek$(ctx, "adaptiveRender");
624
+ return listen$(ctx, "adaptiveRender", (nextMode) => {
625
+ if (mode !== nextMode) {
626
+ mode = nextMode;
627
+ callbackRef.current(nextMode);
628
+ }
629
+ });
630
+ }, [ctx]);
631
+ }
612
632
  function useViewability(callback, configId) {
613
633
  const ctx = useStateContext();
614
634
  const containerContext = useContextContainer();
@@ -825,6 +845,7 @@ function isInMVCPActiveMode(state) {
825
845
  // src/components/Container.tsx
826
846
  function getContainerPositionStyle({
827
847
  columnWrapperStyle,
848
+ contentContainerAlignItems,
828
849
  horizontal,
829
850
  hasItemSeparator,
830
851
  isHorizontalRTLList,
@@ -850,13 +871,14 @@ function getContainerPositionStyle({
850
871
  }
851
872
  }
852
873
  return horizontal ? {
874
+ bottom: contentContainerAlignItems === "flex-end" && numColumns === 1 ? 0 : void 0,
853
875
  boxSizing: paddingStyles ? "border-box" : void 0,
854
876
  direction: isHorizontalRTLList && Platform.OS === "web" ? "ltr" : void 0,
855
877
  flexDirection: hasItemSeparator ? "row" : void 0,
856
878
  height: otherAxisSize,
857
879
  left: 0,
858
880
  position: "absolute",
859
- top: otherAxisPos,
881
+ top: contentContainerAlignItems === "flex-end" && numColumns === 1 ? void 0 : otherAxisPos,
860
882
  ...paddingStyles || {}
861
883
  } : {
862
884
  boxSizing: paddingStyles ? "border-box" : void 0,
@@ -910,6 +932,7 @@ var Container = typedMemo(function Container2({
910
932
  const style = React2.useMemo(
911
933
  () => getContainerPositionStyle({
912
934
  columnWrapperStyle,
935
+ contentContainerAlignItems: ctx.state.props.contentContainerAlignItems,
913
936
  hasItemSeparator: !!ItemSeparatorComponent,
914
937
  horizontal,
915
938
  isHorizontalRTLList,
@@ -923,6 +946,7 @@ var Container = typedMemo(function Container2({
923
946
  otherAxisPos,
924
947
  otherAxisSize,
925
948
  columnWrapperStyle,
949
+ ctx.state.props.contentContainerAlignItems,
926
950
  numColumns,
927
951
  ItemSeparatorComponent
928
952
  ]
@@ -1193,7 +1217,91 @@ function WebAnchoredEndSpace({ horizontal }) {
1193
1217
  return /* @__PURE__ */ React2__namespace.createElement("div", { style }, null);
1194
1218
  }
1195
1219
 
1196
- // src/core/updateContentMetrics.ts
1220
+ // src/core/doMaintainScrollAtEnd.ts
1221
+ function doMaintainScrollAtEnd(ctx) {
1222
+ const state = ctx.state;
1223
+ const {
1224
+ didContainersLayout,
1225
+ pendingNativeMVCPAdjust,
1226
+ refScroller,
1227
+ props: { maintainScrollAtEnd }
1228
+ } = state;
1229
+ const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
1230
+ const shouldMaintainScrollAtEnd = !!(isWithinMaintainScrollAtEndThreshold && maintainScrollAtEnd && didContainersLayout);
1231
+ if (pendingNativeMVCPAdjust) {
1232
+ state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
1233
+ return false;
1234
+ }
1235
+ state.pendingMaintainScrollAtEnd = false;
1236
+ if (shouldMaintainScrollAtEnd) {
1237
+ const contentSize = getContentSize(ctx);
1238
+ if (contentSize < state.scrollLength) {
1239
+ state.scroll = 0;
1240
+ }
1241
+ if (!state.maintainingScrollAtEnd) {
1242
+ const pendingState = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
1243
+ const activeState = maintainScrollAtEnd.animated ? "animated" : "instant";
1244
+ state.maintainingScrollAtEnd = pendingState;
1245
+ requestAnimationFrame(() => {
1246
+ if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
1247
+ state.maintainingScrollAtEnd = activeState;
1248
+ const scroller = refScroller.current;
1249
+ if (state.props.horizontal && isHorizontalRTL(state)) {
1250
+ const currentContentSize = getContentSize(ctx);
1251
+ const logicalEndOffset = getLogicalHorizontalMaxOffset(state, currentContentSize);
1252
+ const nativeOffset = toNativeHorizontalOffset(state, logicalEndOffset, currentContentSize);
1253
+ scroller == null ? void 0 : scroller.scrollTo({
1254
+ animated: maintainScrollAtEnd.animated,
1255
+ x: nativeOffset,
1256
+ y: 0
1257
+ });
1258
+ } else {
1259
+ scroller == null ? void 0 : scroller.scrollToEnd({
1260
+ animated: maintainScrollAtEnd.animated
1261
+ });
1262
+ }
1263
+ setTimeout(
1264
+ () => {
1265
+ if (state.maintainingScrollAtEnd === activeState) {
1266
+ state.maintainingScrollAtEnd = void 0;
1267
+ }
1268
+ },
1269
+ maintainScrollAtEnd.animated ? 500 : 0
1270
+ );
1271
+ } else if (state.maintainingScrollAtEnd === pendingState) {
1272
+ state.maintainingScrollAtEnd = void 0;
1273
+ }
1274
+ });
1275
+ }
1276
+ return true;
1277
+ }
1278
+ return false;
1279
+ }
1280
+
1281
+ // src/core/calculateOffsetForIndex.ts
1282
+ function calculateOffsetForIndex(ctx, index) {
1283
+ const state = ctx.state;
1284
+ return index !== void 0 ? state.positions[index] || 0 : 0;
1285
+ }
1286
+
1287
+ // src/core/getTopOffsetAdjustment.ts
1288
+ function getTopOffsetAdjustment(ctx) {
1289
+ return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "alignItemsAtEndPadding") || 0) + (peek$(ctx, "headerSize") || 0);
1290
+ }
1291
+
1292
+ // src/utils/getId.ts
1293
+ function getId(state, index) {
1294
+ const { data, keyExtractor } = state.props;
1295
+ if (!data) {
1296
+ return "";
1297
+ }
1298
+ const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
1299
+ const id = ret;
1300
+ state.idCache[index] = id;
1301
+ return id;
1302
+ }
1303
+
1304
+ // src/core/updateContentMetricsState.ts
1197
1305
  function getRawContentLength(ctx) {
1198
1306
  var _a3, _b, _c;
1199
1307
  const { state, values } = ctx;
@@ -1204,240 +1312,187 @@ function getAlignItemsAtEndPadding(ctx) {
1204
1312
  const shouldPad = !!state.props.alignItemsAtEndPaddingEnabled && !state.props.horizontal && state.props.data.length > 0 && state.scrollLength > 0;
1205
1313
  return shouldPad ? Math.max(0, state.scrollLength - getRawContentLength(ctx) - getContentInsetEnd(ctx)) : 0;
1206
1314
  }
1207
- function updateContentMetrics(ctx) {
1315
+ function updateContentMetricsState(ctx) {
1316
+ const previousPadding = peek$(ctx, "alignItemsAtEndPadding") || 0;
1208
1317
  const nextPadding = getAlignItemsAtEndPadding(ctx);
1209
- if (peek$(ctx, "alignItemsAtEndPadding") !== nextPadding) {
1318
+ if (previousPadding !== nextPadding) {
1210
1319
  set$(ctx, "alignItemsAtEndPadding", nextPadding);
1211
1320
  }
1212
1321
  }
1213
- function setContentLengthSignal(ctx, signalName, size) {
1214
- if (peek$(ctx, signalName) !== size) {
1215
- set$(ctx, signalName, size);
1216
- updateContentMetrics(ctx);
1322
+
1323
+ // src/core/addTotalSize.ts
1324
+ function addTotalSize(ctx, key, add, notifyTotalSize = true) {
1325
+ const state = ctx.state;
1326
+ const prevTotalSize = state.totalSize;
1327
+ let totalSize = state.totalSize;
1328
+ if (key === null) {
1329
+ totalSize = add;
1330
+ if (state.timeoutSetPaddingTop) {
1331
+ clearTimeout(state.timeoutSetPaddingTop);
1332
+ state.timeoutSetPaddingTop = void 0;
1333
+ }
1334
+ } else {
1335
+ totalSize += add;
1336
+ }
1337
+ if (prevTotalSize !== totalSize) {
1338
+ if (!IsNewArchitecture && state.initialScroll && totalSize < prevTotalSize) {
1339
+ state.pendingTotalSize = totalSize;
1340
+ } else {
1341
+ state.pendingTotalSize = void 0;
1342
+ state.totalSize = totalSize;
1343
+ if (notifyTotalSize) {
1344
+ set$(ctx, "totalSize", totalSize);
1345
+ }
1346
+ updateContentMetricsState(ctx);
1347
+ }
1348
+ } else if (notifyTotalSize && ctx.values.get("totalSize") !== totalSize) {
1349
+ set$(ctx, "totalSize", totalSize);
1217
1350
  }
1218
1351
  }
1219
- function setHeaderSize(ctx, size) {
1220
- setContentLengthSignal(ctx, "headerSize", size);
1352
+
1353
+ // src/core/setSize.ts
1354
+ function setSize(ctx, itemKey, size, notifyTotalSize = true) {
1355
+ const state = ctx.state;
1356
+ const { sizes } = state;
1357
+ const previousSize = sizes.get(itemKey);
1358
+ const diff = previousSize !== void 0 ? size - previousSize : size;
1359
+ if (diff !== 0) {
1360
+ addTotalSize(ctx, itemKey, diff, notifyTotalSize);
1361
+ }
1362
+ sizes.set(itemKey, size);
1221
1363
  }
1222
- function setFooterSize(ctx, size) {
1223
- setContentLengthSignal(ctx, "footerSize", size);
1364
+
1365
+ // src/utils/getItemSize.ts
1366
+ function getKnownOrFixedSize(ctx, key, index, data, resolved) {
1367
+ var _a3, _b;
1368
+ const state = ctx.state;
1369
+ const { getFixedItemSize, getItemType } = state.props;
1370
+ let size = key ? state.sizesKnown.get(key) : void 0;
1371
+ if (size === void 0 && key && getFixedItemSize) {
1372
+ const itemType = (_b = resolved == null ? void 0 : resolved.itemType) != null ? _b : getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1373
+ const fixedSize = (resolved == null ? void 0 : resolved.didResolveFixedItemSize) ? resolved.fixedItemSize : getFixedItemSize(data, index, itemType);
1374
+ if (fixedSize !== void 0) {
1375
+ size = fixedSize + ctx.scrollAxisGap;
1376
+ state.sizesKnown.set(key, size);
1377
+ }
1378
+ }
1379
+ return size;
1224
1380
  }
1225
- function areInsetsEqual(left, right) {
1226
- var _a3, _b, _c, _d, _e, _f, _g, _h;
1227
- return ((_a3 = left == null ? void 0 : left.top) != null ? _a3 : 0) === ((_b = right == null ? void 0 : right.top) != null ? _b : 0) && ((_c = left == null ? void 0 : left.bottom) != null ? _c : 0) === ((_d = right == null ? void 0 : right.bottom) != null ? _d : 0) && ((_e = left == null ? void 0 : left.left) != null ? _e : 0) === ((_f = right == null ? void 0 : right.left) != null ? _f : 0) && ((_g = left == null ? void 0 : left.right) != null ? _g : 0) === ((_h = right == null ? void 0 : right.right) != null ? _h : 0);
1381
+ function getKnownOrFixedItemSize(ctx, index) {
1382
+ const key = getId(ctx.state, index);
1383
+ return getKnownOrFixedSize(ctx, key, index, ctx.state.props.data[index]);
1228
1384
  }
1229
- function setContentInsetOverride(ctx, inset) {
1230
- const { state } = ctx;
1231
- const previousInset = state.contentInsetOverride;
1232
- const nextInset = inset != null ? inset : void 0;
1233
- const didChange = !areInsetsEqual(previousInset, nextInset);
1234
- state.contentInsetOverride = nextInset;
1235
- if (didChange) {
1236
- updateContentMetrics(ctx);
1385
+ function areKnownOrFixedItemSizesAvailable(ctx, startIndex, endIndex) {
1386
+ for (let index = startIndex; index <= endIndex; index++) {
1387
+ if (getKnownOrFixedItemSize(ctx, index) === void 0) {
1388
+ return false;
1389
+ }
1237
1390
  }
1238
- return didChange;
1391
+ return true;
1239
1392
  }
1240
- function useLatestRef(value) {
1241
- const ref = React2__namespace.useRef(value);
1242
- ref.current = value;
1243
- return ref;
1393
+ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize, notifyTotalSize, resolved) {
1394
+ var _a3, _b, _c, _d;
1395
+ const state = ctx.state;
1396
+ const {
1397
+ sizes,
1398
+ averageSizes,
1399
+ props: { estimatedItemSize, getItemType },
1400
+ scrollingTo
1401
+ } = state;
1402
+ const sizeKnown = state.sizesKnown.get(key);
1403
+ if (sizeKnown !== void 0) {
1404
+ return sizeKnown;
1405
+ }
1406
+ let size;
1407
+ const renderedSize = sizes.get(key);
1408
+ if (preferCachedSize) {
1409
+ if (renderedSize !== void 0) {
1410
+ return renderedSize;
1411
+ }
1412
+ }
1413
+ size = getKnownOrFixedSize(ctx, key, index, data, resolved);
1414
+ if (size !== void 0) {
1415
+ setSize(ctx, key, size, notifyTotalSize);
1416
+ return size;
1417
+ }
1418
+ const itemType = (_b = resolved == null ? void 0 : resolved.itemType) != null ? _b : getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1419
+ if (useAverageSize && !scrollingTo) {
1420
+ const averageSizeForType = (_c = averageSizes[itemType]) == null ? void 0 : _c.avg;
1421
+ if (averageSizeForType !== void 0) {
1422
+ size = roundSize(averageSizeForType);
1423
+ }
1424
+ }
1425
+ if (size === void 0 && renderedSize !== void 0) {
1426
+ return renderedSize;
1427
+ }
1428
+ if (size === void 0 && useAverageSize && scrollingTo) {
1429
+ const averageSizeForType = (_d = scrollingTo.averageSizeSnapshot) == null ? void 0 : _d[itemType];
1430
+ if (averageSizeForType !== void 0) {
1431
+ size = roundSize(averageSizeForType);
1432
+ }
1433
+ }
1434
+ if (size === void 0) {
1435
+ size = estimatedItemSize + ctx.scrollAxisGap;
1436
+ }
1437
+ setSize(ctx, key, size, notifyTotalSize);
1438
+ return size;
1244
1439
  }
1245
-
1246
- // src/hooks/useStableRenderComponent.tsx
1247
- function useStableRenderComponent(renderComponent, mapProps) {
1248
- const renderComponentRef = useLatestRef(renderComponent);
1249
- const mapPropsRef = useLatestRef(mapProps);
1250
- return React2__namespace.useMemo(
1251
- () => React2__namespace.forwardRef(
1252
- (props, ref) => {
1253
- var _a3, _b;
1254
- return (_b = (_a3 = renderComponentRef.current) == null ? void 0 : _a3.call(renderComponentRef, mapPropsRef.current(props, ref))) != null ? _b : null;
1255
- }
1256
- ),
1257
- [mapPropsRef, renderComponentRef]
1258
- );
1440
+ function getItemSizeAtIndex(ctx, index) {
1441
+ if (index === void 0 || index < 0) {
1442
+ return void 0;
1443
+ }
1444
+ const targetId = getId(ctx.state, index);
1445
+ return getItemSize(ctx, targetId, index, ctx.state.props.data[index]);
1259
1446
  }
1260
- var LayoutView = ({ onLayoutChange, refView, ...rest }) => {
1261
- const localRef = React2.useRef(null);
1262
- const ref = refView != null ? refView : localRef;
1263
- const { onLayout } = useOnLayoutSync({ onLayoutChange, ref });
1264
- return /* @__PURE__ */ React2__namespace.createElement(ReactNative.View, { ...rest, onLayout, ref });
1265
- };
1266
1447
 
1267
- // src/components/ListComponent.tsx
1268
- var ListComponent = typedMemo(function ListComponent2({
1269
- canRender,
1270
- style,
1271
- contentContainerStyle,
1272
- horizontal,
1273
- initialContentOffset,
1274
- recycleItems,
1275
- ItemSeparatorComponent,
1276
- alignItemsAtEnd: _alignItemsAtEnd,
1277
- onScroll: onScroll2,
1278
- onLayout,
1279
- ListHeaderComponent,
1280
- ListHeaderComponentStyle,
1281
- ListFooterComponent,
1282
- ListFooterComponentStyle,
1283
- ListEmptyComponent,
1284
- getRenderedItem: getRenderedItem2,
1285
- updateItemSize: updateItemSize2,
1286
- refScrollView,
1287
- renderScrollComponent,
1288
- onLayoutFooter,
1289
- scrollAdjustHandler,
1290
- snapToIndices,
1291
- stickyHeaderConfig,
1292
- stickyHeaderIndices,
1293
- useWindowScroll = false,
1294
- ...rest
1295
- }) {
1296
- const ctx = useStateContext();
1297
- const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
1298
- const [alignItemsAtEndPadding = 0, otherAxisSize = 0] = useArr$(["alignItemsAtEndPadding", "otherAxisSize"]);
1299
- const autoOtherAxisStyle = getAutoOtherAxisStyle({
1300
- horizontal,
1301
- needsOtherAxisSize: ctx.state.needsOtherAxisSize,
1302
- otherAxisSize
1303
- });
1304
- const CustomScrollComponent = useStableRenderComponent(
1305
- renderScrollComponent,
1306
- (props, ref) => ({ ...props, ref })
1307
- );
1308
- const ScrollComponent = renderScrollComponent ? CustomScrollComponent : ListComponentScrollView;
1309
- const SnapOrScroll = snapToIndices ? SnapWrapper : ScrollComponent;
1310
- React2.useLayoutEffect(() => {
1311
- if (!ListHeaderComponent) {
1312
- setHeaderSize(ctx, 0);
1313
- }
1314
- if (!ListFooterComponent) {
1315
- setFooterSize(ctx, 0);
1316
- }
1317
- }, [ListHeaderComponent, ListFooterComponent, ctx]);
1318
- const onLayoutHeader = React2.useCallback(
1319
- (rect) => {
1320
- const size = rect[horizontal ? "width" : "height"];
1321
- setHeaderSize(ctx, size);
1322
- },
1323
- [ctx, horizontal]
1324
- );
1325
- const onLayoutFooterInternal = React2.useCallback(
1326
- (rect, fromLayoutEffect) => {
1327
- const size = rect[horizontal ? "width" : "height"];
1328
- setFooterSize(ctx, size);
1329
- onLayoutFooter == null ? void 0 : onLayoutFooter(rect, fromLayoutEffect);
1330
- },
1331
- [ctx, horizontal, onLayoutFooter]
1332
- );
1333
- return /* @__PURE__ */ React2__namespace.createElement(
1334
- SnapOrScroll,
1335
- {
1336
- ...rest,
1337
- ...ScrollComponent === ListComponentScrollView ? { useWindowScroll } : {},
1338
- contentContainerStyle: [
1339
- horizontal ? {
1340
- height: "100%"
1341
- } : {},
1342
- contentContainerStyle
1343
- ],
1344
- contentOffset: initialContentOffset !== void 0 ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
1345
- horizontal,
1346
- maintainVisibleContentPosition: maintainVisibleContentPosition.size || maintainVisibleContentPosition.data ? { minIndexForVisible: 0 } : void 0,
1347
- onLayout,
1348
- onScroll: onScroll2,
1349
- ref: refScrollView,
1350
- ScrollComponent: snapToIndices ? ScrollComponent : void 0,
1351
- style: autoOtherAxisStyle ? [autoOtherAxisStyle, style] : style
1352
- },
1353
- /* @__PURE__ */ React2__namespace.createElement(ScrollAdjust, null),
1354
- ListHeaderComponent && /* @__PURE__ */ React2__namespace.createElement(LayoutView, { onLayoutChange: onLayoutHeader, style: ListHeaderComponentStyle }, getComponent(ListHeaderComponent)),
1355
- ListEmptyComponent && getComponent(ListEmptyComponent),
1356
- alignItemsAtEndPadding > 0 && /* @__PURE__ */ React2__namespace.createElement(
1357
- View,
1358
- {
1359
- style: horizontal ? { flexShrink: 0, width: alignItemsAtEndPadding } : { flexShrink: 0, height: alignItemsAtEndPadding }
1360
- },
1361
- null
1362
- ),
1363
- canRender && !ListEmptyComponent && /* @__PURE__ */ React2__namespace.createElement(
1364
- Containers,
1365
- {
1366
- getRenderedItem: getRenderedItem2,
1367
- horizontal,
1368
- ItemSeparatorComponent,
1369
- recycleItems,
1370
- stickyHeaderConfig,
1371
- updateItemSize: updateItemSize2
1372
- }
1373
- ),
1374
- ListFooterComponent && /* @__PURE__ */ React2__namespace.createElement(LayoutView, { onLayoutChange: onLayoutFooterInternal, style: ListFooterComponentStyle }, getComponent(ListFooterComponent)),
1375
- Platform.OS === "web" && /* @__PURE__ */ React2__namespace.createElement(WebAnchoredEndSpace, { horizontal }),
1376
- IS_DEV && ENABLE_DEVMODE
1377
- );
1378
- });
1379
- var WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH = 100;
1380
- var WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO = 0.9;
1381
- var WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO = 0.9;
1382
- function useDevChecksImpl(props) {
1383
- const ctx = useStateContext();
1384
- const { childrenMode, keyExtractor, renderScrollComponent, useWindowScroll } = props;
1385
- React2.useEffect(() => {
1386
- if (useWindowScroll && renderScrollComponent) {
1387
- warnDevOnce(
1388
- "useWindowScrollRenderScrollComponent",
1389
- "useWindowScroll is not supported when renderScrollComponent is provided."
1390
- );
1391
- }
1392
- }, [renderScrollComponent, useWindowScroll]);
1393
- React2.useEffect(() => {
1394
- if (!keyExtractor && !ctx.state.isFirst && ctx.state.didDataChange && !childrenMode) {
1395
- warnDevOnce(
1396
- "keyExtractor",
1397
- "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."
1398
- );
1448
+ // src/core/calculateOffsetWithOffsetPosition.ts
1449
+ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1450
+ var _a3;
1451
+ const state = ctx.state;
1452
+ const { index, viewOffset, viewPosition } = params;
1453
+ let offset = offsetParam;
1454
+ if (viewOffset) {
1455
+ offset -= viewOffset;
1456
+ }
1457
+ if (index !== void 0) {
1458
+ const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
1459
+ if (topOffsetAdjustment) {
1460
+ offset += topOffsetAdjustment;
1399
1461
  }
1400
- }, [childrenMode, ctx, keyExtractor]);
1401
- React2.useEffect(() => {
1402
- const state = ctx.state;
1462
+ }
1463
+ if (viewPosition !== void 0 && index !== void 0) {
1403
1464
  const dataLength = state.props.data.length;
1404
- const useWindowScrollResolved = state.props.useWindowScroll;
1405
- if (Platform.OS !== "web" || useWindowScrollResolved || dataLength < WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH) {
1406
- return;
1465
+ if (dataLength === 0) {
1466
+ return offset;
1407
1467
  }
1408
- const warnIfUnboundedOuterSize = () => {
1409
- const readyToRender = peek$(ctx, "readyToRender");
1410
- const numContainers = peek$(ctx, "numContainers") || 0;
1411
- const totalSize = peek$(ctx, "totalSize") || 0;
1412
- const scrollLength = ctx.state.scrollLength || 0;
1413
- if (!readyToRender || totalSize <= 0 || scrollLength <= 0) {
1414
- return;
1415
- }
1416
- const rendersAlmostEverything = numContainers >= Math.ceil(dataLength * WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO);
1417
- const viewportMatchesContent = scrollLength >= totalSize * WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO;
1418
- if (rendersAlmostEverything && viewportMatchesContent) {
1419
- warnDevOnce(
1420
- "webUnboundedOuterSize",
1421
- "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."
1422
- );
1423
- }
1424
- };
1425
- warnIfUnboundedOuterSize();
1426
- const unsubscribe = [
1427
- listen$(ctx, "numContainers", warnIfUnboundedOuterSize),
1428
- listen$(ctx, "readyToRender", warnIfUnboundedOuterSize),
1429
- listen$(ctx, "totalSize", warnIfUnboundedOuterSize)
1430
- ];
1431
- return () => {
1432
- for (const unsub of unsubscribe) {
1433
- unsub();
1434
- }
1435
- };
1436
- }, [ctx]);
1468
+ const isOutOfBounds = index < 0 || index >= dataLength;
1469
+ const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
1470
+ const itemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1471
+ const trailingInset = getContentInsetEnd(ctx);
1472
+ offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1473
+ if (!isOutOfBounds && index === state.props.data.length - 1) {
1474
+ const footerSize = peek$(ctx, "footerSize") || 0;
1475
+ offset += footerSize;
1476
+ }
1477
+ }
1478
+ return offset;
1437
1479
  }
1438
- function useDevChecksNoop(_props) {
1480
+
1481
+ // src/core/clampScrollOffset.ts
1482
+ function clampScrollOffset(ctx, offset, scrollTarget) {
1483
+ const state = ctx.state;
1484
+ const contentSize = getContentSize(ctx);
1485
+ let clampedOffset = offset;
1486
+ if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform.OS !== "android" || state.lastLayout)) {
1487
+ const baseMaxOffset = Math.max(0, contentSize - state.scrollLength);
1488
+ const viewOffset = scrollTarget == null ? void 0 : scrollTarget.viewOffset;
1489
+ const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
1490
+ const maxOffset = baseMaxOffset + extraEndOffset;
1491
+ clampedOffset = Math.min(offset, maxOffset);
1492
+ }
1493
+ clampedOffset = Math.max(0, clampedOffset);
1494
+ return clampedOffset;
1439
1495
  }
1440
- var useDevChecks = IS_DEV ? useDevChecksImpl : useDevChecksNoop;
1441
1496
 
1442
1497
  // src/core/deferredPublicOnScroll.ts
1443
1498
  function withResolvedContentOffset(state, event, resolvedOffset) {
@@ -1750,6 +1805,66 @@ function recalculateSettledScroll(ctx) {
1750
1805
  checkThresholds(ctx);
1751
1806
  }
1752
1807
 
1808
+ // src/core/adaptiveRender.ts
1809
+ var DEFAULT_ENTER_VELOCITY = 4;
1810
+ var DEFAULT_EXIT_VELOCITY = 1;
1811
+ var DEFAULT_EXIT_DELAY = 1e3;
1812
+ function scheduleAdaptiveRenderExit(ctx, exitDelay) {
1813
+ const state = ctx.state;
1814
+ const previousTimeout = state.timeoutAdaptiveRender;
1815
+ if (previousTimeout !== void 0) {
1816
+ clearTimeout(previousTimeout);
1817
+ state.timeouts.delete(previousTimeout);
1818
+ state.timeoutAdaptiveRender = void 0;
1819
+ }
1820
+ if (exitDelay <= 0) {
1821
+ setAdaptiveRender(ctx, "normal");
1822
+ } else {
1823
+ const timeout = setTimeout(() => {
1824
+ state.timeouts.delete(timeout);
1825
+ state.timeoutAdaptiveRender = void 0;
1826
+ setAdaptiveRender(ctx, "normal");
1827
+ }, exitDelay);
1828
+ state.timeoutAdaptiveRender = timeout;
1829
+ state.timeouts.add(timeout);
1830
+ }
1831
+ }
1832
+ function setAdaptiveRender(ctx, mode) {
1833
+ var _a3, _b;
1834
+ const previousMode = peek$(ctx, "adaptiveRender");
1835
+ if (previousMode !== mode) {
1836
+ set$(ctx, "adaptiveRender", mode);
1837
+ (_b = (_a3 = ctx.state.props.adaptiveRender) == null ? void 0 : _a3.onChange) == null ? void 0 : _b.call(_a3, mode);
1838
+ }
1839
+ }
1840
+ function updateAdaptiveRender(ctx, scrollVelocity) {
1841
+ var _a3, _b, _c;
1842
+ const state = ctx.state;
1843
+ const adaptiveRender = state.props.adaptiveRender;
1844
+ const enterVelocity = (_a3 = adaptiveRender == null ? void 0 : adaptiveRender.enterVelocity) != null ? _a3 : DEFAULT_ENTER_VELOCITY;
1845
+ const exitVelocity = (_b = adaptiveRender == null ? void 0 : adaptiveRender.exitVelocity) != null ? _b : DEFAULT_EXIT_VELOCITY;
1846
+ const exitDelay = (_c = adaptiveRender == null ? void 0 : adaptiveRender.exitDelay) != null ? _c : DEFAULT_EXIT_DELAY;
1847
+ const currentMode = peek$(ctx, "adaptiveRender");
1848
+ const threshold = currentMode === "light" ? exitVelocity : enterVelocity;
1849
+ const nextMode = Math.abs(scrollVelocity) > threshold ? "light" : "normal";
1850
+ const previousMode = state.timeoutAdaptiveRender !== void 0 ? "normal" : currentMode;
1851
+ if (nextMode !== previousMode) {
1852
+ if (nextMode === "light") {
1853
+ setAdaptiveRender(ctx, "light");
1854
+ scheduleAdaptiveRenderExit(ctx, exitDelay);
1855
+ } else if (currentMode === "light") {
1856
+ scheduleAdaptiveRenderExit(ctx, exitDelay);
1857
+ }
1858
+ }
1859
+ }
1860
+
1861
+ // src/utils/getEffectiveDrawDistance.ts
1862
+ var INITIAL_DRAW_DISTANCE = 100;
1863
+ function getEffectiveDrawDistance(ctx) {
1864
+ const drawDistance = ctx.state.props.drawDistance;
1865
+ return peek$(ctx, "readyToRender") ? drawDistance : Math.min(drawDistance, INITIAL_DRAW_DISTANCE);
1866
+ }
1867
+
1753
1868
  // src/utils/setInitialRenderState.ts
1754
1869
  function setInitialRenderState(ctx, {
1755
1870
  didLayout,
@@ -1769,6 +1884,13 @@ function setInitialRenderState(ctx, {
1769
1884
  const isReadyToRender = Boolean(state.didContainersLayout && state.didFinishInitialScroll);
1770
1885
  if (isReadyToRender && !peek$(ctx, "readyToRender")) {
1771
1886
  set$(ctx, "readyToRender", true);
1887
+ setAdaptiveRender(ctx, "normal");
1888
+ if (state.props.drawDistance > INITIAL_DRAW_DISTANCE) {
1889
+ requestAnimationFrame(() => {
1890
+ var _a3;
1891
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
1892
+ });
1893
+ }
1772
1894
  if (onLoad) {
1773
1895
  onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1774
1896
  }
@@ -1843,235 +1965,39 @@ function finishInitialScroll(ctx, options) {
1843
1965
  complete();
1844
1966
  }
1845
1967
 
1846
- // src/core/calculateOffsetForIndex.ts
1847
- function calculateOffsetForIndex(ctx, index) {
1968
+ // src/core/finishScrollTo.ts
1969
+ function finishScrollTo(ctx) {
1970
+ var _a3, _b;
1848
1971
  const state = ctx.state;
1849
- return index !== void 0 ? state.positions[index] || 0 : 0;
1850
- }
1851
-
1852
- // src/core/getTopOffsetAdjustment.ts
1853
- function getTopOffsetAdjustment(ctx) {
1854
- return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "alignItemsAtEndPadding") || 0) + (peek$(ctx, "headerSize") || 0);
1855
- }
1856
-
1857
- // src/utils/getId.ts
1858
- function getId(state, index) {
1859
- const { data, keyExtractor } = state.props;
1860
- if (!data) {
1861
- return "";
1862
- }
1863
- const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
1864
- const id = ret;
1865
- state.idCache[index] = id;
1866
- return id;
1867
- }
1868
-
1869
- // src/core/addTotalSize.ts
1870
- function addTotalSize(ctx, key, add, notifyTotalSize = true) {
1871
- const state = ctx.state;
1872
- const prevTotalSize = state.totalSize;
1873
- let totalSize = state.totalSize;
1874
- if (key === null) {
1875
- totalSize = add;
1876
- if (state.timeoutSetPaddingTop) {
1877
- clearTimeout(state.timeoutSetPaddingTop);
1878
- state.timeoutSetPaddingTop = void 0;
1879
- }
1880
- } else {
1881
- totalSize += add;
1882
- }
1883
- if (prevTotalSize !== totalSize) {
1884
- if (!IsNewArchitecture && state.initialScroll && totalSize < prevTotalSize) {
1885
- state.pendingTotalSize = totalSize;
1886
- } else {
1887
- state.pendingTotalSize = void 0;
1888
- state.totalSize = totalSize;
1889
- if (notifyTotalSize) {
1890
- set$(ctx, "totalSize", totalSize);
1891
- }
1892
- updateContentMetrics(ctx);
1893
- }
1894
- } else if (notifyTotalSize && ctx.values.get("totalSize") !== totalSize) {
1895
- set$(ctx, "totalSize", totalSize);
1896
- }
1897
- }
1898
-
1899
- // src/core/setSize.ts
1900
- function setSize(ctx, itemKey, size, notifyTotalSize = true) {
1901
- const state = ctx.state;
1902
- const { sizes } = state;
1903
- const previousSize = sizes.get(itemKey);
1904
- const diff = previousSize !== void 0 ? size - previousSize : size;
1905
- if (diff !== 0) {
1906
- addTotalSize(ctx, itemKey, diff, notifyTotalSize);
1907
- }
1908
- sizes.set(itemKey, size);
1909
- }
1910
-
1911
- // src/utils/getItemSize.ts
1912
- function getKnownOrFixedSize(ctx, key, index, data, resolved) {
1913
- var _a3, _b;
1914
- const state = ctx.state;
1915
- const { getFixedItemSize, getItemType } = state.props;
1916
- let size = key ? state.sizesKnown.get(key) : void 0;
1917
- if (size === void 0 && key && getFixedItemSize) {
1918
- const itemType = (_b = resolved == null ? void 0 : resolved.itemType) != null ? _b : getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1919
- size = (resolved == null ? void 0 : resolved.didResolveFixedItemSize) ? resolved.fixedItemSize : getFixedItemSize(data, index, itemType);
1920
- if (size !== void 0) {
1921
- state.sizesKnown.set(key, size);
1922
- }
1923
- }
1924
- return size;
1925
- }
1926
- function getKnownOrFixedItemSize(ctx, index) {
1927
- const key = getId(ctx.state, index);
1928
- return getKnownOrFixedSize(ctx, key, index, ctx.state.props.data[index]);
1929
- }
1930
- function areKnownOrFixedItemSizesAvailable(ctx, startIndex, endIndex) {
1931
- for (let index = startIndex; index <= endIndex; index++) {
1932
- if (getKnownOrFixedItemSize(ctx, index) === void 0) {
1933
- return false;
1934
- }
1935
- }
1936
- return true;
1937
- }
1938
- function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize, notifyTotalSize, resolved) {
1939
- var _a3, _b, _c, _d;
1940
- const state = ctx.state;
1941
- const {
1942
- sizes,
1943
- averageSizes,
1944
- props: { estimatedItemSize, getItemType },
1945
- scrollingTo
1946
- } = state;
1947
- const sizeKnown = state.sizesKnown.get(key);
1948
- if (sizeKnown !== void 0) {
1949
- return sizeKnown;
1950
- }
1951
- let size;
1952
- const renderedSize = sizes.get(key);
1953
- if (preferCachedSize) {
1954
- if (renderedSize !== void 0) {
1955
- return renderedSize;
1956
- }
1957
- }
1958
- size = getKnownOrFixedSize(ctx, key, index, data, resolved);
1959
- if (size !== void 0) {
1960
- setSize(ctx, key, size, notifyTotalSize);
1961
- return size;
1962
- }
1963
- const itemType = (_b = resolved == null ? void 0 : resolved.itemType) != null ? _b : getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1964
- if (useAverageSize && !scrollingTo) {
1965
- const averageSizeForType = (_c = averageSizes[itemType]) == null ? void 0 : _c.avg;
1966
- if (averageSizeForType !== void 0) {
1967
- size = roundSize(averageSizeForType);
1968
- }
1969
- }
1970
- if (size === void 0 && renderedSize !== void 0) {
1971
- return renderedSize;
1972
- }
1973
- if (size === void 0 && useAverageSize && scrollingTo) {
1974
- const averageSizeForType = (_d = scrollingTo.averageSizeSnapshot) == null ? void 0 : _d[itemType];
1975
- if (averageSizeForType !== void 0) {
1976
- size = roundSize(averageSizeForType);
1977
- }
1978
- }
1979
- if (size === void 0) {
1980
- size = estimatedItemSize;
1981
- }
1982
- setSize(ctx, key, size, notifyTotalSize);
1983
- return size;
1984
- }
1985
- function getItemSizeAtIndex(ctx, index) {
1986
- if (index === void 0 || index < 0) {
1987
- return void 0;
1988
- }
1989
- const targetId = getId(ctx.state, index);
1990
- return getItemSize(ctx, targetId, index, ctx.state.props.data[index]);
1991
- }
1992
-
1993
- // src/core/calculateOffsetWithOffsetPosition.ts
1994
- function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1995
- var _a3;
1996
- const state = ctx.state;
1997
- const { index, viewOffset, viewPosition } = params;
1998
- let offset = offsetParam;
1999
- if (viewOffset) {
2000
- offset -= viewOffset;
2001
- }
2002
- if (index !== void 0) {
2003
- const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
2004
- if (topOffsetAdjustment) {
2005
- offset += topOffsetAdjustment;
2006
- }
2007
- }
2008
- if (viewPosition !== void 0 && index !== void 0) {
2009
- const dataLength = state.props.data.length;
2010
- if (dataLength === 0) {
2011
- return offset;
2012
- }
2013
- const isOutOfBounds = index < 0 || index >= dataLength;
2014
- const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
2015
- const itemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
2016
- const trailingInset = getContentInsetEnd(ctx);
2017
- offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
2018
- if (!isOutOfBounds && index === state.props.data.length - 1) {
2019
- const footerSize = peek$(ctx, "footerSize") || 0;
2020
- offset += footerSize;
2021
- }
2022
- }
2023
- return offset;
2024
- }
2025
-
2026
- // src/core/clampScrollOffset.ts
2027
- function clampScrollOffset(ctx, offset, scrollTarget) {
2028
- const state = ctx.state;
2029
- const contentSize = getContentSize(ctx);
2030
- let clampedOffset = offset;
2031
- if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform.OS !== "android" || state.lastLayout)) {
2032
- const baseMaxOffset = Math.max(0, contentSize - state.scrollLength);
2033
- const viewOffset = scrollTarget == null ? void 0 : scrollTarget.viewOffset;
2034
- const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
2035
- const maxOffset = baseMaxOffset + extraEndOffset;
2036
- clampedOffset = Math.min(offset, maxOffset);
2037
- }
2038
- clampedOffset = Math.max(0, clampedOffset);
2039
- return clampedOffset;
2040
- }
2041
-
2042
- // src/core/finishScrollTo.ts
2043
- function finishScrollTo(ctx) {
2044
- var _a3, _b;
2045
- const state = ctx.state;
2046
- if (state == null ? void 0 : state.scrollingTo) {
2047
- const resolvePendingScroll = state.pendingScrollResolve;
2048
- state.pendingScrollResolve = void 0;
2049
- const scrollingTo = state.scrollingTo;
2050
- state.scrollHistory.length = 0;
2051
- state.scrollingTo = void 0;
2052
- if (state.pendingTotalSize !== void 0) {
2053
- addTotalSize(ctx, null, state.pendingTotalSize);
2054
- }
2055
- if (PlatformAdjustBreaksScroll) {
2056
- state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
2057
- }
2058
- if (scrollingTo.isInitialScroll || state.initialScroll) {
2059
- const isOffsetSession = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
2060
- const shouldPreserveResizeTarget = !!scrollingTo.isInitialScroll && !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_b = state.initialScroll) == null ? void 0 : _b.viewPosition) === 1;
2061
- finishInitialScroll(ctx, {
2062
- onFinished: () => {
2063
- resolvePendingScroll == null ? void 0 : resolvePendingScroll();
2064
- },
2065
- preserveTarget: isOffsetSession && state.props.data.length === 0 || shouldPreserveResizeTarget,
2066
- recalculateItems: true,
2067
- schedulePreservedTargetClear: shouldPreserveResizeTarget,
2068
- syncObservedOffset: isOffsetSession,
2069
- waitForCompletionFrame: !!scrollingTo.waitForInitialScrollCompletionFrame
2070
- });
2071
- return;
2072
- }
2073
- recalculateSettledScroll(ctx);
2074
- resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1972
+ if (state == null ? void 0 : state.scrollingTo) {
1973
+ const resolvePendingScroll = state.pendingScrollResolve;
1974
+ state.pendingScrollResolve = void 0;
1975
+ const scrollingTo = state.scrollingTo;
1976
+ state.scrollHistory.length = 0;
1977
+ state.scrollingTo = void 0;
1978
+ if (state.pendingTotalSize !== void 0) {
1979
+ addTotalSize(ctx, null, state.pendingTotalSize);
1980
+ }
1981
+ if (PlatformAdjustBreaksScroll) {
1982
+ state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
1983
+ }
1984
+ if (scrollingTo.isInitialScroll || state.initialScroll) {
1985
+ const isOffsetSession = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
1986
+ const shouldPreserveResizeTarget = !!scrollingTo.isInitialScroll && !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_b = state.initialScroll) == null ? void 0 : _b.viewPosition) === 1;
1987
+ finishInitialScroll(ctx, {
1988
+ onFinished: () => {
1989
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1990
+ },
1991
+ preserveTarget: isOffsetSession && state.props.data.length === 0 || shouldPreserveResizeTarget,
1992
+ recalculateItems: true,
1993
+ schedulePreservedTargetClear: shouldPreserveResizeTarget,
1994
+ syncObservedOffset: isOffsetSession,
1995
+ waitForCompletionFrame: !!scrollingTo.waitForInitialScrollCompletionFrame
1996
+ });
1997
+ return;
1998
+ }
1999
+ recalculateSettledScroll(ctx);
2000
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
2075
2001
  }
2076
2002
  }
2077
2003
 
@@ -2264,113 +2190,328 @@ function doScrollTo(ctx, params) {
2264
2190
  }
2265
2191
  }
2266
2192
 
2267
- // src/core/doMaintainScrollAtEnd.ts
2268
- function doMaintainScrollAtEnd(ctx) {
2193
+ // src/utils/requestAdjust.ts
2194
+ function requestAdjust(ctx, positionDiff, dataChanged) {
2269
2195
  const state = ctx.state;
2270
- const {
2271
- didContainersLayout,
2272
- pendingNativeMVCPAdjust,
2273
- refScroller,
2274
- props: { maintainScrollAtEnd }
2275
- } = state;
2276
- const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
2277
- const shouldMaintainScrollAtEnd = !!(isWithinMaintainScrollAtEndThreshold && maintainScrollAtEnd && didContainersLayout);
2278
- if (pendingNativeMVCPAdjust) {
2279
- state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
2280
- return false;
2196
+ if (Math.abs(positionDiff) > 0.1) {
2197
+ const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
2198
+ const doit = () => {
2199
+ if (needsScrollWorkaround) {
2200
+ doScrollTo(ctx, { horizontal: state.props.horizontal, offset: state.scroll });
2201
+ } else {
2202
+ state.scrollAdjustHandler.requestAdjust(positionDiff);
2203
+ if (state.adjustingFromInitialMount) {
2204
+ state.adjustingFromInitialMount--;
2205
+ }
2206
+ }
2207
+ };
2208
+ state.scroll += positionDiff;
2209
+ state.scrollForNextCalculateItemsInView = void 0;
2210
+ const readyToRender = peek$(ctx, "readyToRender");
2211
+ if (readyToRender) {
2212
+ doit();
2213
+ if (Platform.OS !== "web") {
2214
+ const threshold = state.scroll - positionDiff / 2;
2215
+ if (!state.ignoreScrollFromMVCP) {
2216
+ state.ignoreScrollFromMVCP = {};
2217
+ }
2218
+ if (positionDiff > 0) {
2219
+ state.ignoreScrollFromMVCP.lt = threshold;
2220
+ } else {
2221
+ state.ignoreScrollFromMVCP.gt = threshold;
2222
+ }
2223
+ if (state.ignoreScrollFromMVCPTimeout) {
2224
+ clearTimeout(state.ignoreScrollFromMVCPTimeout);
2225
+ }
2226
+ const delay = needsScrollWorkaround ? 250 : 100;
2227
+ state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
2228
+ var _a3;
2229
+ state.ignoreScrollFromMVCP = void 0;
2230
+ const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
2231
+ if (shouldForceUpdate) {
2232
+ state.ignoreScrollFromMVCPIgnored = false;
2233
+ state.scrollPending = state.scroll;
2234
+ (_a3 = state.reprocessCurrentScroll) == null ? void 0 : _a3.call(state);
2235
+ }
2236
+ }, delay);
2237
+ }
2238
+ } else {
2239
+ state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
2240
+ requestAnimationFrame(doit);
2241
+ }
2281
2242
  }
2282
- state.pendingMaintainScrollAtEnd = false;
2283
- if (shouldMaintainScrollAtEnd) {
2284
- const contentSize = getContentSize(ctx);
2285
- if (contentSize < state.scrollLength) {
2286
- state.scroll = 0;
2243
+ }
2244
+
2245
+ // src/core/updateContentMetrics.ts
2246
+ var SCROLL_ADJUST_EPSILON = 0.1;
2247
+ function setContentLengthSignal(ctx, signalName, size) {
2248
+ const didChange = peek$(ctx, signalName) !== size;
2249
+ if (didChange) {
2250
+ set$(ctx, signalName, size);
2251
+ updateContentMetricsState(ctx);
2252
+ }
2253
+ return didChange;
2254
+ }
2255
+ function shouldAdjustForHeaderSizeChange(ctx, previousHeaderSize, nextHeaderSize) {
2256
+ const { didContainersLayout, didFinishInitialScroll, props, scroll, scrollingTo } = ctx.state;
2257
+ const sizeDiff = nextHeaderSize - previousHeaderSize;
2258
+ const leadingPadding = props.horizontal ? props.stylePaddingLeft : props.stylePaddingTop;
2259
+ const previousHeaderEnd = (leadingPadding || 0) + previousHeaderSize;
2260
+ return Platform.OS === "web" && props.maintainVisibleContentPosition.size && didContainersLayout && didFinishInitialScroll && !scrollingTo && scroll >= previousHeaderEnd - SCROLL_ADJUST_EPSILON && Math.abs(sizeDiff) > SCROLL_ADJUST_EPSILON;
2261
+ }
2262
+ function setHeaderSize(ctx, size) {
2263
+ const { state } = ctx;
2264
+ const previousHeaderSize = peek$(ctx, "headerSize") || 0;
2265
+ const didChange = previousHeaderSize !== size;
2266
+ const hasMeasuredOrEstimatedHeaderBaseline = state.didMeasureHeader || previousHeaderSize > SCROLL_ADJUST_EPSILON;
2267
+ if (didChange) {
2268
+ set$(ctx, "headerSize", size);
2269
+ updateContentMetricsState(ctx);
2270
+ if (hasMeasuredOrEstimatedHeaderBaseline && shouldAdjustForHeaderSizeChange(ctx, previousHeaderSize, size)) {
2271
+ requestAdjust(ctx, size - previousHeaderSize);
2272
+ }
2273
+ }
2274
+ state.didMeasureHeader = true;
2275
+ }
2276
+ function setFooterSize(ctx, size) {
2277
+ return setContentLengthSignal(ctx, "footerSize", size);
2278
+ }
2279
+ function areInsetsEqual(left, right) {
2280
+ var _a3, _b, _c, _d, _e, _f, _g, _h;
2281
+ return ((_a3 = left == null ? void 0 : left.top) != null ? _a3 : 0) === ((_b = right == null ? void 0 : right.top) != null ? _b : 0) && ((_c = left == null ? void 0 : left.bottom) != null ? _c : 0) === ((_d = right == null ? void 0 : right.bottom) != null ? _d : 0) && ((_e = left == null ? void 0 : left.left) != null ? _e : 0) === ((_f = right == null ? void 0 : right.left) != null ? _f : 0) && ((_g = left == null ? void 0 : left.right) != null ? _g : 0) === ((_h = right == null ? void 0 : right.right) != null ? _h : 0);
2282
+ }
2283
+ function setContentInsetOverride(ctx, inset) {
2284
+ const { state } = ctx;
2285
+ const previousInset = state.contentInsetOverride;
2286
+ const nextInset = inset != null ? inset : void 0;
2287
+ const didChange = !areInsetsEqual(previousInset, nextInset);
2288
+ state.contentInsetOverride = nextInset;
2289
+ if (didChange) {
2290
+ updateContentMetricsState(ctx);
2291
+ }
2292
+ return didChange;
2293
+ }
2294
+ function useLatestRef(value) {
2295
+ const ref = React2__namespace.useRef(value);
2296
+ ref.current = value;
2297
+ return ref;
2298
+ }
2299
+
2300
+ // src/hooks/useStableRenderComponent.tsx
2301
+ function useStableRenderComponent(renderComponent, mapProps) {
2302
+ const renderComponentRef = useLatestRef(renderComponent);
2303
+ const mapPropsRef = useLatestRef(mapProps);
2304
+ return React2__namespace.useMemo(
2305
+ () => React2__namespace.forwardRef(
2306
+ (props, ref) => {
2307
+ var _a3, _b;
2308
+ return (_b = (_a3 = renderComponentRef.current) == null ? void 0 : _a3.call(renderComponentRef, mapPropsRef.current(props, ref))) != null ? _b : null;
2309
+ }
2310
+ ),
2311
+ [mapPropsRef, renderComponentRef]
2312
+ );
2313
+ }
2314
+ var LayoutView = ({ onLayoutChange, refView, ...rest }) => {
2315
+ const localRef = React2.useRef(null);
2316
+ const ref = refView != null ? refView : localRef;
2317
+ const { onLayout } = useOnLayoutSync({ onLayoutChange, ref });
2318
+ return /* @__PURE__ */ React2__namespace.createElement(ReactNative.View, { ...rest, onLayout, ref });
2319
+ };
2320
+
2321
+ // src/components/ListComponent.tsx
2322
+ var AlignItemsAtEndSpacer = typedMemo(function AlignItemsAtEndSpacer2({ horizontal }) {
2323
+ const [alignItemsAtEndPadding = 0] = useArr$(["alignItemsAtEndPadding"]);
2324
+ if (alignItemsAtEndPadding <= 0) {
2325
+ return null;
2326
+ }
2327
+ return /* @__PURE__ */ React2__namespace.createElement(
2328
+ View,
2329
+ {
2330
+ style: horizontal ? { flexShrink: 0, width: alignItemsAtEndPadding } : { flexShrink: 0, height: alignItemsAtEndPadding }
2331
+ },
2332
+ null
2333
+ );
2334
+ });
2335
+ var ListComponent = typedMemo(function ListComponent2({
2336
+ canRender,
2337
+ style,
2338
+ contentContainerStyle,
2339
+ horizontal,
2340
+ initialContentOffset,
2341
+ recycleItems,
2342
+ ItemSeparatorComponent,
2343
+ alignItemsAtEnd: _alignItemsAtEnd,
2344
+ onScroll: onScroll2,
2345
+ onLayout,
2346
+ ListHeaderComponent,
2347
+ ListHeaderComponentStyle,
2348
+ ListFooterComponent,
2349
+ ListFooterComponentStyle,
2350
+ ListEmptyComponent,
2351
+ getRenderedItem: getRenderedItem2,
2352
+ updateItemSize: updateItemSize2,
2353
+ refScrollView,
2354
+ renderScrollComponent,
2355
+ onLayoutFooter,
2356
+ scrollAdjustHandler,
2357
+ snapToIndices,
2358
+ stickyHeaderConfig,
2359
+ stickyHeaderIndices,
2360
+ useWindowScroll = false,
2361
+ ...rest
2362
+ }) {
2363
+ const ctx = useStateContext();
2364
+ const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
2365
+ const [otherAxisSize = 0] = useArr$(["otherAxisSize"]);
2366
+ const shouldRenderAlignItemsAtEndSpacer = ctx.state.props.alignItemsAtEndPaddingEnabled;
2367
+ const autoOtherAxisStyle = getAutoOtherAxisStyle({
2368
+ horizontal,
2369
+ needsOtherAxisSize: ctx.state.needsOtherAxisSize,
2370
+ otherAxisSize
2371
+ });
2372
+ const CustomScrollComponent = useStableRenderComponent(
2373
+ renderScrollComponent,
2374
+ (props, ref) => ({ ...props, ref })
2375
+ );
2376
+ const ScrollComponent = renderScrollComponent ? CustomScrollComponent : ListComponentScrollView;
2377
+ const SnapOrScroll = snapToIndices ? SnapWrapper : ScrollComponent;
2378
+ const updateFooterSize = React2.useCallback(
2379
+ (size, afterSizeUpdate) => {
2380
+ var _a3;
2381
+ const didFooterSizeChange = setFooterSize(ctx, size);
2382
+ afterSizeUpdate == null ? void 0 : afterSizeUpdate();
2383
+ if (didFooterSizeChange && ((_a3 = ctx.state.props.maintainScrollAtEnd) == null ? void 0 : _a3.onFooterLayout)) {
2384
+ doMaintainScrollAtEnd(ctx);
2385
+ }
2386
+ },
2387
+ [ctx]
2388
+ );
2389
+ React2.useLayoutEffect(() => {
2390
+ if (!ListHeaderComponent) {
2391
+ setHeaderSize(ctx, 0);
2392
+ }
2393
+ if (!ListFooterComponent) {
2394
+ updateFooterSize(0);
2395
+ }
2396
+ }, [ListHeaderComponent, ListFooterComponent, ctx, updateFooterSize]);
2397
+ const onLayoutHeader = React2.useCallback(
2398
+ (rect) => {
2399
+ const size = rect[horizontal ? "width" : "height"];
2400
+ setHeaderSize(ctx, size);
2401
+ },
2402
+ [ctx, horizontal]
2403
+ );
2404
+ const onLayoutFooterInternal = React2.useCallback(
2405
+ (rect, fromLayoutEffect) => {
2406
+ const size = rect[horizontal ? "width" : "height"];
2407
+ updateFooterSize(size, () => {
2408
+ onLayoutFooter == null ? void 0 : onLayoutFooter(rect, fromLayoutEffect);
2409
+ });
2410
+ },
2411
+ [horizontal, onLayoutFooter, updateFooterSize]
2412
+ );
2413
+ return /* @__PURE__ */ React2__namespace.createElement(
2414
+ SnapOrScroll,
2415
+ {
2416
+ ...rest,
2417
+ ...ScrollComponent === ListComponentScrollView ? { useWindowScroll } : {},
2418
+ contentContainerStyle: [
2419
+ horizontal ? {
2420
+ height: "100%"
2421
+ } : {},
2422
+ contentContainerStyle
2423
+ ],
2424
+ contentOffset: initialContentOffset !== void 0 ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
2425
+ horizontal,
2426
+ maintainVisibleContentPosition: maintainVisibleContentPosition.size || maintainVisibleContentPosition.data ? { minIndexForVisible: 0 } : void 0,
2427
+ onLayout,
2428
+ onScroll: onScroll2,
2429
+ ref: refScrollView,
2430
+ ScrollComponent: snapToIndices ? ScrollComponent : void 0,
2431
+ style: autoOtherAxisStyle ? [autoOtherAxisStyle, style] : style
2432
+ },
2433
+ /* @__PURE__ */ React2__namespace.createElement(ScrollAdjust, null),
2434
+ ListHeaderComponent && /* @__PURE__ */ React2__namespace.createElement(LayoutView, { onLayoutChange: onLayoutHeader, style: ListHeaderComponentStyle }, getComponent(ListHeaderComponent)),
2435
+ ListEmptyComponent && getComponent(ListEmptyComponent),
2436
+ shouldRenderAlignItemsAtEndSpacer && /* @__PURE__ */ React2__namespace.createElement(AlignItemsAtEndSpacer, { horizontal }),
2437
+ canRender && !ListEmptyComponent && /* @__PURE__ */ React2__namespace.createElement(
2438
+ Containers,
2439
+ {
2440
+ getRenderedItem: getRenderedItem2,
2441
+ horizontal,
2442
+ ItemSeparatorComponent,
2443
+ recycleItems,
2444
+ stickyHeaderConfig,
2445
+ updateItemSize: updateItemSize2
2446
+ }
2447
+ ),
2448
+ ListFooterComponent && /* @__PURE__ */ React2__namespace.createElement(LayoutView, { onLayoutChange: onLayoutFooterInternal, style: ListFooterComponentStyle }, getComponent(ListFooterComponent)),
2449
+ Platform.OS === "web" && /* @__PURE__ */ React2__namespace.createElement(WebAnchoredEndSpace, { horizontal }),
2450
+ IS_DEV && ENABLE_DEVMODE
2451
+ );
2452
+ });
2453
+ var WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH = 100;
2454
+ var WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO = 0.9;
2455
+ var WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO = 0.9;
2456
+ function useDevChecksImpl(props) {
2457
+ const ctx = useStateContext();
2458
+ const { childrenMode, keyExtractor, renderScrollComponent, useWindowScroll } = props;
2459
+ React2.useEffect(() => {
2460
+ if (useWindowScroll && renderScrollComponent) {
2461
+ warnDevOnce(
2462
+ "useWindowScrollRenderScrollComponent",
2463
+ "useWindowScroll is not supported when renderScrollComponent is provided."
2464
+ );
2287
2465
  }
2288
- if (!state.maintainingScrollAtEnd) {
2289
- state.maintainingScrollAtEnd = true;
2290
- requestAnimationFrame(() => {
2291
- if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
2292
- const scroller = refScroller.current;
2293
- if (state.props.horizontal && isHorizontalRTL(state)) {
2294
- const currentContentSize = getContentSize(ctx);
2295
- const logicalEndOffset = getLogicalHorizontalMaxOffset(state, currentContentSize);
2296
- const nativeOffset = toNativeHorizontalOffset(state, logicalEndOffset, currentContentSize);
2297
- scroller == null ? void 0 : scroller.scrollTo({
2298
- animated: maintainScrollAtEnd.animated,
2299
- x: nativeOffset,
2300
- y: 0
2301
- });
2302
- } else {
2303
- scroller == null ? void 0 : scroller.scrollToEnd({
2304
- animated: maintainScrollAtEnd.animated
2305
- });
2306
- }
2307
- setTimeout(
2308
- () => {
2309
- state.maintainingScrollAtEnd = false;
2310
- },
2311
- maintainScrollAtEnd.animated ? 500 : 0
2312
- );
2313
- } else {
2314
- state.maintainingScrollAtEnd = false;
2315
- }
2316
- });
2466
+ }, [renderScrollComponent, useWindowScroll]);
2467
+ React2.useEffect(() => {
2468
+ if (!keyExtractor && !ctx.state.isFirst && ctx.state.didDataChange && !childrenMode) {
2469
+ warnDevOnce(
2470
+ "keyExtractor",
2471
+ "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."
2472
+ );
2317
2473
  }
2318
- return true;
2319
- }
2320
- return false;
2321
- }
2322
-
2323
- // src/utils/requestAdjust.ts
2324
- function requestAdjust(ctx, positionDiff, dataChanged) {
2325
- const state = ctx.state;
2326
- if (Math.abs(positionDiff) > 0.1) {
2327
- const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
2328
- const doit = () => {
2329
- if (needsScrollWorkaround) {
2330
- doScrollTo(ctx, { horizontal: state.props.horizontal, offset: state.scroll });
2331
- } else {
2332
- state.scrollAdjustHandler.requestAdjust(positionDiff);
2333
- if (state.adjustingFromInitialMount) {
2334
- state.adjustingFromInitialMount--;
2335
- }
2474
+ }, [childrenMode, ctx, keyExtractor]);
2475
+ React2.useEffect(() => {
2476
+ const state = ctx.state;
2477
+ const dataLength = state.props.data.length;
2478
+ const useWindowScrollResolved = state.props.useWindowScroll;
2479
+ if (Platform.OS !== "web" || useWindowScrollResolved || dataLength < WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH) {
2480
+ return;
2481
+ }
2482
+ const warnIfUnboundedOuterSize = () => {
2483
+ const readyToRender = peek$(ctx, "readyToRender");
2484
+ const numContainers = peek$(ctx, "numContainers") || 0;
2485
+ const totalSize = peek$(ctx, "totalSize") || 0;
2486
+ const scrollLength = ctx.state.scrollLength || 0;
2487
+ if (!readyToRender || totalSize <= 0 || scrollLength <= 0) {
2488
+ return;
2489
+ }
2490
+ const rendersAlmostEverything = numContainers >= Math.ceil(dataLength * WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO);
2491
+ const viewportMatchesContent = scrollLength >= totalSize * WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO;
2492
+ if (rendersAlmostEverything && viewportMatchesContent) {
2493
+ warnDevOnce(
2494
+ "webUnboundedOuterSize",
2495
+ "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."
2496
+ );
2336
2497
  }
2337
2498
  };
2338
- state.scroll += positionDiff;
2339
- state.scrollForNextCalculateItemsInView = void 0;
2340
- const readyToRender = peek$(ctx, "readyToRender");
2341
- if (readyToRender) {
2342
- doit();
2343
- if (Platform.OS !== "web") {
2344
- const threshold = state.scroll - positionDiff / 2;
2345
- if (!state.ignoreScrollFromMVCP) {
2346
- state.ignoreScrollFromMVCP = {};
2347
- }
2348
- if (positionDiff > 0) {
2349
- state.ignoreScrollFromMVCP.lt = threshold;
2350
- } else {
2351
- state.ignoreScrollFromMVCP.gt = threshold;
2352
- }
2353
- if (state.ignoreScrollFromMVCPTimeout) {
2354
- clearTimeout(state.ignoreScrollFromMVCPTimeout);
2355
- }
2356
- const delay = needsScrollWorkaround ? 250 : 100;
2357
- state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
2358
- var _a3;
2359
- state.ignoreScrollFromMVCP = void 0;
2360
- const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
2361
- if (shouldForceUpdate) {
2362
- state.ignoreScrollFromMVCPIgnored = false;
2363
- state.scrollPending = state.scroll;
2364
- (_a3 = state.reprocessCurrentScroll) == null ? void 0 : _a3.call(state);
2365
- }
2366
- }, delay);
2499
+ warnIfUnboundedOuterSize();
2500
+ const unsubscribe = [
2501
+ listen$(ctx, "numContainers", warnIfUnboundedOuterSize),
2502
+ listen$(ctx, "readyToRender", warnIfUnboundedOuterSize),
2503
+ listen$(ctx, "totalSize", warnIfUnboundedOuterSize)
2504
+ ];
2505
+ return () => {
2506
+ for (const unsub of unsubscribe) {
2507
+ unsub();
2367
2508
  }
2368
- } else {
2369
- state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
2370
- requestAnimationFrame(doit);
2371
- }
2372
- }
2509
+ };
2510
+ }, [ctx]);
2511
+ }
2512
+ function useDevChecksNoop(_props) {
2373
2513
  }
2514
+ var useDevChecks = IS_DEV ? useDevChecksImpl : useDevChecksNoop;
2374
2515
 
2375
2516
  // src/core/mvcp.ts
2376
2517
  var MVCP_POSITION_EPSILON = 0.1;
@@ -2520,6 +2661,10 @@ function prepareMVCP(ctx, dataChanged) {
2520
2661
  const now = Date.now();
2521
2662
  const enableMVCPAnchorLock = isWeb && (!!dataChanged || !!state.mvcpAnchorLock);
2522
2663
  const scrollingTo = state.scrollingTo;
2664
+ if (isWeb && dataChanged && state.pendingScrollToEnd && scrollingTo === void 0) {
2665
+ state.mvcpAnchorLock = void 0;
2666
+ return void 0;
2667
+ }
2523
2668
  const anchorLock = isWeb ? resolveAnchorLock(state, enableMVCPAnchorLock, mvcpData, now) : void 0;
2524
2669
  let prevPosition;
2525
2670
  let targetId;
@@ -2655,7 +2800,7 @@ function prepareMVCP(ctx, dataChanged) {
2655
2800
  return;
2656
2801
  }
2657
2802
  if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
2658
- const shouldSkipAdjustForMaintainedEnd = state.maintainingScrollAtEnd && peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
2803
+ const shouldSkipAdjustForMaintainedEnd = (state.maintainingScrollAtEnd === "pending-animated" || state.maintainingScrollAtEnd === "animated") && peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
2659
2804
  if (!shouldSkipAdjustForMaintainedEnd) {
2660
2805
  requestAdjust(ctx, positionDiff, dataChanged && mvcpData);
2661
2806
  }
@@ -2669,6 +2814,45 @@ var flushSync = (fn) => {
2669
2814
  fn();
2670
2815
  };
2671
2816
 
2817
+ // src/utils/getScrollVelocity.ts
2818
+ var getScrollVelocity = (state) => {
2819
+ const { scrollHistory } = state;
2820
+ const newestIndex = scrollHistory.length - 1;
2821
+ if (newestIndex < 1) {
2822
+ return 0;
2823
+ }
2824
+ const newest = scrollHistory[newestIndex];
2825
+ const now = Date.now();
2826
+ let direction = 0;
2827
+ for (let i = newestIndex; i > 0; i--) {
2828
+ const delta = scrollHistory[i].scroll - scrollHistory[i - 1].scroll;
2829
+ if (delta !== 0) {
2830
+ direction = Math.sign(delta);
2831
+ break;
2832
+ }
2833
+ }
2834
+ if (direction === 0) {
2835
+ return 0;
2836
+ }
2837
+ let oldest = newest;
2838
+ for (let i = newestIndex - 1; i >= 0; i--) {
2839
+ const current = scrollHistory[i];
2840
+ const next = scrollHistory[i + 1];
2841
+ const delta = next.scroll - current.scroll;
2842
+ const deltaSign = Math.sign(delta);
2843
+ if (deltaSign !== 0 && deltaSign !== direction) {
2844
+ break;
2845
+ }
2846
+ if (now - current.time > 1e3) {
2847
+ break;
2848
+ }
2849
+ oldest = current;
2850
+ }
2851
+ const scrollDiff = newest.scroll - oldest.scroll;
2852
+ const timeDiff = newest.time - oldest.time;
2853
+ return timeDiff > 0 ? scrollDiff / timeDiff : 0;
2854
+ };
2855
+
2672
2856
  // src/core/updateScroll.ts
2673
2857
  function updateScroll(ctx, newScroll, forceUpdate, options) {
2674
2858
  var _a3;
@@ -2694,6 +2878,8 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2694
2878
  if (scrollHistory.length > 5) {
2695
2879
  scrollHistory.shift();
2696
2880
  }
2881
+ const scrollVelocity = getScrollVelocity(state);
2882
+ updateAdaptiveRender(ctx, scrollVelocity);
2697
2883
  if (ignoreScrollFromMVCP && !scrollingTo) {
2698
2884
  const { lt, gt } = ignoreScrollFromMVCP;
2699
2885
  if (lt && newScroll < lt || gt && newScroll > gt) {
@@ -2717,7 +2903,7 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2717
2903
  state.lastScrollDelta = scrollDelta;
2718
2904
  const runCalculateItems = () => {
2719
2905
  var _a4;
2720
- (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { doMVCP: scrollingTo !== void 0 });
2906
+ (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { doMVCP: scrollingTo !== void 0, scrollVelocity });
2721
2907
  checkThresholds(ctx);
2722
2908
  };
2723
2909
  if (scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength && !state.pendingNativeMVCPAdjust) {
@@ -3652,9 +3838,32 @@ function initializeInitialScrollOnMount(ctx, options) {
3652
3838
  }
3653
3839
  function handleInitialScrollDataChange(ctx, options) {
3654
3840
  var _a3, _b, _c;
3655
- const { dataLength, didDataChange, initialScrollAtEnd, stylePaddingBottom, useBootstrapInitialScroll } = options;
3841
+ const {
3842
+ dataLength,
3843
+ didDataChange,
3844
+ initialScrollAtEnd,
3845
+ latestInitialScroll,
3846
+ latestInitialScrollSessionKind,
3847
+ stylePaddingBottom,
3848
+ useBootstrapInitialScroll
3849
+ } = options;
3656
3850
  const state = ctx.state;
3657
3851
  const previousDataLength = (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.previousDataLength) != null ? _b : 0;
3852
+ const isFirstNonEmptyData = !state.hasHadNonEmptyData && dataLength > 0;
3853
+ if (dataLength > 0) {
3854
+ state.hasHadNonEmptyData = true;
3855
+ }
3856
+ if (isFirstNonEmptyData) {
3857
+ if (latestInitialScroll) {
3858
+ setInitialScrollTarget(state, latestInitialScroll);
3859
+ setInitialScrollSession(state, {
3860
+ kind: latestInitialScrollSessionKind,
3861
+ previousDataLength
3862
+ });
3863
+ } else {
3864
+ clearPreservedInitialScrollTarget(state);
3865
+ }
3866
+ }
3658
3867
  if (state.initialScrollSession) {
3659
3868
  state.initialScrollSession.previousDataLength = dataLength;
3660
3869
  }
@@ -3878,45 +4087,6 @@ function updateTotalSize(ctx) {
3878
4087
  }
3879
4088
  }
3880
4089
 
3881
- // src/utils/getScrollVelocity.ts
3882
- var getScrollVelocity = (state) => {
3883
- const { scrollHistory } = state;
3884
- const newestIndex = scrollHistory.length - 1;
3885
- if (newestIndex < 1) {
3886
- return 0;
3887
- }
3888
- const newest = scrollHistory[newestIndex];
3889
- const now = Date.now();
3890
- let direction = 0;
3891
- for (let i = newestIndex; i > 0; i--) {
3892
- const delta = scrollHistory[i].scroll - scrollHistory[i - 1].scroll;
3893
- if (delta !== 0) {
3894
- direction = Math.sign(delta);
3895
- break;
3896
- }
3897
- }
3898
- if (direction === 0) {
3899
- return 0;
3900
- }
3901
- let oldest = newest;
3902
- for (let i = newestIndex - 1; i >= 0; i--) {
3903
- const current = scrollHistory[i];
3904
- const next = scrollHistory[i + 1];
3905
- const delta = next.scroll - current.scroll;
3906
- const deltaSign = Math.sign(delta);
3907
- if (deltaSign !== 0 && deltaSign !== direction) {
3908
- break;
3909
- }
3910
- if (now - current.time > 1e3) {
3911
- break;
3912
- }
3913
- oldest = current;
3914
- }
3915
- const scrollDiff = newest.scroll - oldest.scroll;
3916
- const timeDiff = newest.time - oldest.time;
3917
- return timeDiff > 0 ? scrollDiff / timeDiff : 0;
3918
- };
3919
-
3920
4090
  // src/utils/updateSnapToOffsets.ts
3921
4091
  function updateSnapToOffsets(ctx) {
3922
4092
  const state = ctx.state;
@@ -4652,7 +4822,7 @@ function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, sc
4652
4822
  function calculateItemsInView(ctx, params = {}) {
4653
4823
  const state = ctx.state;
4654
4824
  batchedUpdates(() => {
4655
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
4825
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t;
4656
4826
  const {
4657
4827
  columns,
4658
4828
  containerItemKeys,
@@ -4661,14 +4831,7 @@ function calculateItemsInView(ctx, params = {}) {
4661
4831
  indexByKey,
4662
4832
  minIndexSizeChanged,
4663
4833
  positions,
4664
- props: {
4665
- alwaysRenderIndicesArr,
4666
- alwaysRenderIndicesSet,
4667
- drawDistance,
4668
- getItemType,
4669
- keyExtractor,
4670
- onStickyHeaderChange
4671
- },
4834
+ props: { alwaysRenderIndicesArr, alwaysRenderIndicesSet, getItemType, keyExtractor, onStickyHeaderChange },
4672
4835
  scrollForNextCalculateItemsInView,
4673
4836
  scrollLength,
4674
4837
  sizes,
@@ -4680,6 +4843,7 @@ function calculateItemsInView(ctx, params = {}) {
4680
4843
  const stickyHeaderIndicesSet = state.props.stickyHeaderIndicesSet || /* @__PURE__ */ new Set();
4681
4844
  const alwaysRenderArr = alwaysRenderIndicesArr || [];
4682
4845
  const alwaysRenderSet = alwaysRenderIndicesSet || /* @__PURE__ */ new Set();
4846
+ const drawDistance = getEffectiveDrawDistance(ctx);
4683
4847
  const { dataChanged, doMVCP, forceFullItemPositions } = params;
4684
4848
  const bootstrapInitialScrollState = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" ? state.initialScrollSession.bootstrap : void 0;
4685
4849
  const suppressInitialScrollSideEffects = !!bootstrapInitialScrollState;
@@ -4690,10 +4854,10 @@ function calculateItemsInView(ctx, params = {}) {
4690
4854
  let totalSize = getContentSize(ctx);
4691
4855
  const topPad = peek$(ctx, "stylePaddingTop") + peek$(ctx, "alignItemsAtEndPadding") + peek$(ctx, "headerSize");
4692
4856
  const numColumns = peek$(ctx, "numColumns");
4693
- const speed = getScrollVelocity(state);
4857
+ const speed = (_b = params.scrollVelocity) != null ? _b : getScrollVelocity(state);
4694
4858
  const scrollExtra = 0;
4695
4859
  const { initialScroll, queuedInitialLayout } = state;
4696
- const scrollState = suppressInitialScrollSideEffects ? (_b = bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.scroll) != null ? _b : state.scroll : !queuedInitialLayout && hasActiveInitialScroll(state) && initialScroll ? (
4860
+ const scrollState = suppressInitialScrollSideEffects ? (_c = bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.scroll) != null ? _c : state.scroll : !queuedInitialLayout && hasActiveInitialScroll(state) && initialScroll ? (
4697
4861
  // Before the initial layout settles, keep viewport math anchored to the
4698
4862
  // current initial-scroll target instead of transient native adjustments.
4699
4863
  resolveInitialScrollOffset(ctx, initialScroll)
@@ -4717,19 +4881,25 @@ function calculateItemsInView(ctx, params = {}) {
4717
4881
  };
4718
4882
  updateScroll2(scrollState);
4719
4883
  const previousStickyIndex = peek$(ctx, "activeStickyIndex");
4720
- const currentStickyIdx = stickyHeaderIndicesArr.length > 0 ? findCurrentStickyIndex(stickyHeaderIndicesArr, scroll, state) : -1;
4721
- const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyHeaderIndicesArr[currentStickyIdx] : -1;
4722
- const stickyIndexDidChange = previousStickyIndex !== nextActiveStickyIndex;
4723
- if (currentStickyIdx >= 0 || previousStickyIndex >= 0) {
4724
- set$(ctx, "activeStickyIndex", nextActiveStickyIndex);
4725
- }
4726
- const shouldNotifyStickyHeaderChange = !!onStickyHeaderChange && stickyHeaderIndicesArr.length > 0 && stickyIndexDidChange;
4727
- const finishCalculateItemsInView = shouldNotifyStickyHeaderChange ? () => {
4728
- const item = data[nextActiveStickyIndex];
4729
- if (item !== void 0) {
4730
- onStickyHeaderChange == null ? void 0 : onStickyHeaderChange({ index: nextActiveStickyIndex, item });
4884
+ const resolveStickyState = () => {
4885
+ const currentStickyIdx = stickyHeaderIndicesArr.length > 0 ? findCurrentStickyIndex(stickyHeaderIndicesArr, scroll, state) : -1;
4886
+ const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyHeaderIndicesArr[currentStickyIdx] : -1;
4887
+ const stickyIndexDidChange = previousStickyIndex !== nextActiveStickyIndex;
4888
+ if (currentStickyIdx >= 0 || previousStickyIndex >= 0) {
4889
+ set$(ctx, "activeStickyIndex", nextActiveStickyIndex);
4731
4890
  }
4732
- } : void 0;
4891
+ const shouldNotifyStickyHeaderChange = !!onStickyHeaderChange && stickyHeaderIndicesArr.length > 0 && stickyIndexDidChange;
4892
+ return {
4893
+ currentStickyIdx,
4894
+ finishCalculateItemsInView: shouldNotifyStickyHeaderChange ? () => {
4895
+ const item = data[nextActiveStickyIndex];
4896
+ if (item !== void 0) {
4897
+ onStickyHeaderChange == null ? void 0 : onStickyHeaderChange({ index: nextActiveStickyIndex, item });
4898
+ }
4899
+ } : void 0
4900
+ };
4901
+ };
4902
+ let stickyState = dataChanged ? void 0 : resolveStickyState();
4733
4903
  let scrollBufferTop = drawDistance;
4734
4904
  let scrollBufferBottom = drawDistance;
4735
4905
  if (speed > 0 || speed === 0 && scroll < Math.max(50, drawDistance)) {
@@ -4762,7 +4932,7 @@ function calculateItemsInView(ctx, params = {}) {
4762
4932
  scrollBottom
4763
4933
  );
4764
4934
  }
4765
- finishCalculateItemsInView == null ? void 0 : finishCalculateItemsInView();
4935
+ (_d = stickyState == null ? void 0 : stickyState.finishCalculateItemsInView) == null ? void 0 : _d.call(stickyState);
4766
4936
  return;
4767
4937
  }
4768
4938
  }
@@ -4771,7 +4941,7 @@ function calculateItemsInView(ctx, params = {}) {
4771
4941
  if (dataChanged) {
4772
4942
  resetLayoutCachesForDataChange(state);
4773
4943
  }
4774
- const startIndex = forceFullItemPositions || dataChanged ? 0 : (_c = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _c : 0;
4944
+ const startIndex = forceFullItemPositions || dataChanged ? 0 : (_e = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _e : 0;
4775
4945
  const optimizeForVisibleWindow = !forceFullItemPositions && !dataChanged && numColumns > 1 && minIndexSizeChanged !== void 0;
4776
4946
  updateItemPositions(ctx, dataChanged, {
4777
4947
  doMVCP,
@@ -4797,21 +4967,24 @@ function calculateItemsInView(ctx, params = {}) {
4797
4967
  }
4798
4968
  }
4799
4969
  const scrollBeforeMVCP = state.scroll;
4800
- const scrollAdjustPendingBeforeMVCP = (_d = peek$(ctx, "scrollAdjustPending")) != null ? _d : 0;
4970
+ const scrollAdjustPendingBeforeMVCP = (_f = peek$(ctx, "scrollAdjustPending")) != null ? _f : 0;
4801
4971
  checkMVCP == null ? void 0 : checkMVCP();
4802
- const didMVCPAdjustScroll = !!checkMVCP && (state.scroll !== scrollBeforeMVCP || ((_e = peek$(ctx, "scrollAdjustPending")) != null ? _e : 0) !== scrollAdjustPendingBeforeMVCP);
4972
+ const didMVCPAdjustScroll = !!checkMVCP && (state.scroll !== scrollBeforeMVCP || ((_g = peek$(ctx, "scrollAdjustPending")) != null ? _g : 0) !== scrollAdjustPendingBeforeMVCP);
4803
4973
  if (didMVCPAdjustScroll && initialScroll) {
4804
4974
  updateScroll2(state.scroll);
4805
4975
  updateScrollRange();
4806
4976
  }
4977
+ if (dataChanged) {
4978
+ stickyState = resolveStickyState();
4979
+ }
4807
4980
  let startBuffered = null;
4808
4981
  let startBufferedId = null;
4809
4982
  let endBuffered = null;
4810
- let loopStart = (_f = suppressInitialScrollSideEffects ? bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.targetIndexSeed : void 0) != null ? _f : !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
4983
+ let loopStart = (_h = suppressInitialScrollSideEffects ? bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.targetIndexSeed : void 0) != null ? _h : !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
4811
4984
  for (let i = loopStart; i >= 0; i--) {
4812
- const id = (_g = idCache[i]) != null ? _g : getId(state, i);
4985
+ const id = (_i = idCache[i]) != null ? _i : getId(state, i);
4813
4986
  const top = positions[i];
4814
- const size = (_h = sizes.get(id)) != null ? _h : getItemSize(ctx, id, i, data[i]);
4987
+ const size = (_j = sizes.get(id)) != null ? _j : getItemSize(ctx, id, i, data[i]);
4815
4988
  const bottom = top + size;
4816
4989
  if (bottom > scrollTopBuffered) {
4817
4990
  loopStart = i;
@@ -4846,8 +5019,8 @@ function calculateItemsInView(ctx, params = {}) {
4846
5019
  };
4847
5020
  const dataLength = data.length;
4848
5021
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
4849
- const id = (_i = idCache[i]) != null ? _i : getId(state, i);
4850
- const size = (_j = sizes.get(id)) != null ? _j : getItemSize(ctx, id, i, data[i]);
5022
+ const id = (_k = idCache[i]) != null ? _k : getId(state, i);
5023
+ const size = (_l = sizes.get(id)) != null ? _l : getItemSize(ctx, id, i, data[i]);
4851
5024
  const top = positions[i];
4852
5025
  if (!foundEnd) {
4853
5026
  trackVisibleRange(visibleRange, i, top, size, scroll, scrollBottom);
@@ -4903,7 +5076,7 @@ function calculateItemsInView(ctx, params = {}) {
4903
5076
  const needNewContainers = [];
4904
5077
  const needNewContainersSet = /* @__PURE__ */ new Set();
4905
5078
  for (let i = startBuffered; i <= endBuffered; i++) {
4906
- const id = (_k = idCache[i]) != null ? _k : getId(state, i);
5079
+ const id = (_m = idCache[i]) != null ? _m : getId(state, i);
4907
5080
  if (!containerItemKeys.has(id)) {
4908
5081
  needNewContainersSet.add(i);
4909
5082
  needNewContainers.push(i);
@@ -4912,7 +5085,7 @@ function calculateItemsInView(ctx, params = {}) {
4912
5085
  if (alwaysRenderArr.length > 0) {
4913
5086
  for (const index of alwaysRenderArr) {
4914
5087
  if (index < 0 || index >= dataLength) continue;
4915
- const id = (_l = idCache[index]) != null ? _l : getId(state, index);
5088
+ const id = (_n = idCache[index]) != null ? _n : getId(state, index);
4916
5089
  if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
4917
5090
  needNewContainersSet.add(index);
4918
5091
  needNewContainers.push(index);
@@ -4923,7 +5096,7 @@ function calculateItemsInView(ctx, params = {}) {
4923
5096
  handleStickyActivation(
4924
5097
  ctx,
4925
5098
  stickyHeaderIndicesArr,
4926
- currentStickyIdx,
5099
+ (_o = stickyState == null ? void 0 : stickyState.currentStickyIdx) != null ? _o : -1,
4927
5100
  needNewContainers,
4928
5101
  needNewContainersSet,
4929
5102
  startBuffered,
@@ -4949,7 +5122,7 @@ function calculateItemsInView(ctx, params = {}) {
4949
5122
  for (const allocation of availableContainerAllocations) {
4950
5123
  const i = allocation.itemIndex;
4951
5124
  const containerIndex = allocation.containerIndex;
4952
- const id = (_m = idCache[i]) != null ? _m : getId(state, i);
5125
+ const id = (_p = idCache[i]) != null ? _p : getId(state, i);
4953
5126
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
4954
5127
  if (oldKey && oldKey !== id) {
4955
5128
  containerItemKeys.delete(oldKey);
@@ -4960,7 +5133,7 @@ function calculateItemsInView(ctx, params = {}) {
4960
5133
  state.containerItemTypes.set(containerIndex, allocation.itemType);
4961
5134
  }
4962
5135
  containerItemKeys.set(id, containerIndex);
4963
- (_n = state.userScrollAnchorReset) == null ? void 0 : _n.keys.add(id);
5136
+ (_q = state.userScrollAnchorReset) == null ? void 0 : _q.keys.add(id);
4964
5137
  const containerSticky = `containerSticky${containerIndex}`;
4965
5138
  const isSticky = stickyHeaderIndicesSet.has(i);
4966
5139
  const isAlwaysRender = alwaysRenderSet.has(i);
@@ -4991,14 +5164,12 @@ function calculateItemsInView(ctx, params = {}) {
4991
5164
  if (state.userScrollAnchorReset) {
4992
5165
  if (state.userScrollAnchorReset.keys.size === 0) {
4993
5166
  state.userScrollAnchorReset = void 0;
4994
- } else {
4995
- state.userScrollAnchorReset.batchSize = state.userScrollAnchorReset.keys.size;
4996
5167
  }
4997
5168
  }
4998
5169
  if (alwaysRenderArr.length > 0) {
4999
5170
  for (const index of alwaysRenderArr) {
5000
5171
  if (index < 0 || index >= dataLength) continue;
5001
- const id = (_o = idCache[index]) != null ? _o : getId(state, index);
5172
+ const id = (_r = idCache[index]) != null ? _r : getId(state, index);
5002
5173
  const containerIndex = containerItemKeys.get(id);
5003
5174
  if (containerIndex !== void 0) {
5004
5175
  state.stickyContainerPool.add(containerIndex);
@@ -5012,7 +5183,7 @@ function calculateItemsInView(ctx, params = {}) {
5012
5183
  stickyHeaderIndicesArr,
5013
5184
  scroll,
5014
5185
  drawDistance,
5015
- currentStickyIdx,
5186
+ (_s = stickyState == null ? void 0 : stickyState.currentStickyIdx) != null ? _s : -1,
5016
5187
  pendingRemoval,
5017
5188
  alwaysRenderSet
5018
5189
  );
@@ -5073,7 +5244,7 @@ function calculateItemsInView(ctx, params = {}) {
5073
5244
  );
5074
5245
  }
5075
5246
  }
5076
- finishCalculateItemsInView == null ? void 0 : finishCalculateItemsInView();
5247
+ (_t = stickyState == null ? void 0 : stickyState.finishCalculateItemsInView) == null ? void 0 : _t.call(stickyState);
5077
5248
  });
5078
5249
  }
5079
5250
 
@@ -5156,8 +5327,9 @@ function doInitialAllocateContainers(ctx) {
5156
5327
  const state = ctx.state;
5157
5328
  const {
5158
5329
  scrollLength,
5159
- props: { data, drawDistance, getFixedItemSize, getItemType, numColumns, estimatedItemSize }
5330
+ props: { data, getFixedItemSize, getItemType, numColumns, estimatedItemSize }
5160
5331
  } = state;
5332
+ const drawDistance = getEffectiveDrawDistance(ctx);
5161
5333
  const hasContainers = peek$(ctx, "numContainers");
5162
5334
  if (scrollLength > 0 && data.length > 0 && !hasContainers) {
5163
5335
  let averageItemSize;
@@ -5168,12 +5340,12 @@ function doInitialAllocateContainers(ctx) {
5168
5340
  const item = data[i];
5169
5341
  if (item !== void 0) {
5170
5342
  const itemType = (_a3 = getItemType == null ? void 0 : getItemType(item, i)) != null ? _a3 : "";
5171
- totalSize += (_b = getFixedItemSize(item, i, itemType)) != null ? _b : estimatedItemSize;
5343
+ totalSize += ((_b = getFixedItemSize(item, i, itemType)) != null ? _b : estimatedItemSize) + ctx.scrollAxisGap;
5172
5344
  }
5173
5345
  }
5174
5346
  averageItemSize = totalSize / num;
5175
5347
  } else {
5176
- averageItemSize = estimatedItemSize;
5348
+ averageItemSize = estimatedItemSize + ctx.scrollAxisGap;
5177
5349
  }
5178
5350
  const numContainers = Math.max(
5179
5351
  1,
@@ -5228,7 +5400,7 @@ function handleLayout(ctx, layoutParam, setCanRender) {
5228
5400
  if (didChange) {
5229
5401
  state.scrollLength = scrollLength;
5230
5402
  state.otherAxisSize = otherAxisSize;
5231
- updateContentMetrics(ctx);
5403
+ updateContentMetricsState(ctx);
5232
5404
  state.lastBatchingAction = Date.now();
5233
5405
  state.scrollForNextCalculateItemsInView = void 0;
5234
5406
  if (scrollLength > 0) {
@@ -5469,28 +5641,12 @@ function updateContentInsetEndAdjustment(ctx, previousContentInsetEndAdjustment)
5469
5641
 
5470
5642
  // src/core/updateItemSize.ts
5471
5643
  function runOrScheduleMVCPRecalculate(ctx) {
5472
- var _a3, _b;
5644
+ var _a3;
5473
5645
  const state = ctx.state;
5474
5646
  if (state.userScrollAnchorReset !== void 0) {
5475
- const replacementBatchSize = (_a3 = state.userScrollAnchorReset.batchSize) != null ? _a3 : state.userScrollAnchorReset.keys.size;
5476
- const replacementMeasurementBatchThreshold = 3;
5477
- const shouldBatchReplacementMeasurements = replacementBatchSize > replacementMeasurementBatchThreshold;
5478
- if (shouldBatchReplacementMeasurements) {
5479
- if (state.queuedMVCPRecalculate === void 0) {
5480
- state.queuedMVCPRecalculate = requestAnimationFrame(() => {
5481
- var _a4;
5482
- state.queuedMVCPRecalculate = void 0;
5483
- calculateItemsInView(ctx);
5484
- if (((_a4 = state.userScrollAnchorReset) == null ? void 0 : _a4.keys.size) === 0) {
5485
- state.userScrollAnchorReset = void 0;
5486
- }
5487
- });
5488
- }
5489
- } else {
5490
- calculateItemsInView(ctx);
5491
- if (((_b = state.userScrollAnchorReset) == null ? void 0 : _b.keys.size) === 0) {
5492
- state.userScrollAnchorReset = void 0;
5493
- }
5647
+ calculateItemsInView(ctx);
5648
+ if (((_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys.size) === 0) {
5649
+ state.userScrollAnchorReset = void 0;
5494
5650
  }
5495
5651
  } else if (Platform.OS === "web") {
5496
5652
  if (!state.mvcpAnchorLock) {
@@ -5968,6 +6124,9 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
5968
6124
  scrollTo(ctx, params);
5969
6125
  return true;
5970
6126
  }),
6127
+ setItemSize: (itemKey, size) => {
6128
+ updateItemSize(ctx, itemKey, size);
6129
+ },
5971
6130
  setScrollProcessingEnabled: (enabled) => {
5972
6131
  state.scrollProcessingEnabled = enabled;
5973
6132
  },
@@ -6065,12 +6224,13 @@ function getRenderedItem(ctx, key) {
6065
6224
 
6066
6225
  // src/utils/normalizeMaintainScrollAtEnd.ts
6067
6226
  function normalizeMaintainScrollAtEndOn(on, hasExplicitOn) {
6068
- var _a3, _b, _c;
6227
+ var _a3, _b, _c, _d;
6069
6228
  return {
6070
6229
  animated: false,
6071
6230
  onDataChange: hasExplicitOn ? (_a3 = on == null ? void 0 : on.dataChange) != null ? _a3 : false : true,
6072
- onItemLayout: hasExplicitOn ? (_b = on == null ? void 0 : on.itemLayout) != null ? _b : false : true,
6073
- onLayout: hasExplicitOn ? (_c = on == null ? void 0 : on.layout) != null ? _c : false : true
6231
+ onFooterLayout: hasExplicitOn ? (_b = on == null ? void 0 : on.footerLayout) != null ? _b : false : true,
6232
+ onItemLayout: hasExplicitOn ? (_c = on == null ? void 0 : on.itemLayout) != null ? _c : false : true,
6233
+ onLayout: hasExplicitOn ? (_d = on == null ? void 0 : on.layout) != null ? _d : false : true
6074
6234
  };
6075
6235
  }
6076
6236
  function normalizeMaintainScrollAtEnd(value) {
@@ -6192,7 +6352,7 @@ var LegendList = typedMemo(
6192
6352
  })
6193
6353
  );
6194
6354
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
6195
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i;
6355
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
6196
6356
  const noopOnScroll = React2.useCallback((_event) => {
6197
6357
  }, []);
6198
6358
  if (props.recycleItems === void 0) {
@@ -6223,6 +6383,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6223
6383
  initialScrollAtEnd = false,
6224
6384
  initialScrollIndex: initialScrollIndexProp,
6225
6385
  initialScrollOffset: initialScrollOffsetProp,
6386
+ experimental_adaptiveRender,
6226
6387
  itemsAreEqual,
6227
6388
  keyExtractor: keyExtractorProp,
6228
6389
  ListEmptyComponent,
@@ -6319,6 +6480,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6319
6480
  const [, scheduleImperativeScrollCommit] = React2__namespace.useReducer((value) => value + 1, 0);
6320
6481
  const ctx = useStateContext();
6321
6482
  ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
6483
+ const scrollAxisGap = horizontal ? (_f = (_d = ctx.columnWrapperStyle) == null ? void 0 : _d.columnGap) != null ? _f : (_e = ctx.columnWrapperStyle) == null ? void 0 : _e.gap : (_i = (_g = ctx.columnWrapperStyle) == null ? void 0 : _g.rowGap) != null ? _i : (_h = ctx.columnWrapperStyle) == null ? void 0 : _h.gap;
6484
+ const nextScrollAxisGap = typeof scrollAxisGap === "number" && Number.isFinite(scrollAxisGap) ? scrollAxisGap : 0;
6322
6485
  const refScroller = React2.useRef(null);
6323
6486
  const combinedRef = useCombinedRef(refScroller, refScrollView);
6324
6487
  const keyExtractor = keyExtractorProp != null ? keyExtractorProp : ((_item, index) => index.toString());
@@ -6332,8 +6495,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6332
6495
  anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex,
6333
6496
  alwaysRender == null ? void 0 : alwaysRender.top,
6334
6497
  alwaysRender == null ? void 0 : alwaysRender.bottom,
6335
- (_d = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _d.join(","),
6336
- (_e = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _e.join(","),
6498
+ (_j = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _j.join(","),
6499
+ (_k = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _k.join(","),
6337
6500
  dataProp,
6338
6501
  dataVersion,
6339
6502
  keyExtractor
@@ -6361,6 +6524,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6361
6524
  endNoBuffer: -1,
6362
6525
  endReachedSnapshot: void 0,
6363
6526
  firstFullyOnScreenIndex: -1,
6527
+ hasHadNonEmptyData: dataProp.length > 0,
6364
6528
  idCache: [],
6365
6529
  idsInView: [],
6366
6530
  indexByKey: /* @__PURE__ */ new Map(),
@@ -6403,6 +6567,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6403
6567
  startReachedSnapshotDataChangeEpoch: void 0,
6404
6568
  stickyContainerPool: /* @__PURE__ */ new Set(),
6405
6569
  stickyContainers: /* @__PURE__ */ new Map(),
6570
+ timeoutAdaptiveRender: void 0,
6406
6571
  timeouts: /* @__PURE__ */ new Set(),
6407
6572
  totalSize: 0,
6408
6573
  viewabilityConfigCallbackPairs: void 0
@@ -6421,11 +6586,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6421
6586
  const state = refState.current;
6422
6587
  const isFirstLocal = state.isFirst;
6423
6588
  const previousNumColumnsProp = state.props.numColumns;
6424
- state.didColumnsChange = numColumnsProp !== previousNumColumnsProp;
6589
+ const didScrollAxisGapChange = !isFirstLocal && ctx.scrollAxisGap !== nextScrollAxisGap;
6590
+ ctx.scrollAxisGap = nextScrollAxisGap;
6591
+ state.didColumnsChange = numColumnsProp !== previousNumColumnsProp || didScrollAxisGapChange;
6425
6592
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
6426
6593
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
6427
6594
  const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
6428
- if (didDataChangeLocal && !initialScrollAtEnd && state.didFinishInitialScroll && ((_f = state.initialScroll) == null ? void 0 : _f.viewPosition) === 1 && state.props.data.length > 0) {
6595
+ if (didDataChangeLocal && !initialScrollAtEnd && state.didFinishInitialScroll && ((_l = state.initialScroll) == null ? void 0 : _l.viewPosition) === 1 && state.props.data.length > 0) {
6429
6596
  clearPreservedInitialScrollTarget(state);
6430
6597
  }
6431
6598
  if (didDataChangeLocal) {
@@ -6437,8 +6604,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6437
6604
  const throttledOnScroll = useThrottledOnScroll(onScrollProp != null ? onScrollProp : noopOnScroll, scrollEventThrottle != null ? scrollEventThrottle : 0);
6438
6605
  const throttleScrollFn = scrollEventThrottle && onScrollProp ? throttledOnScroll : onScrollProp;
6439
6606
  const anchoredEndSpaceResolved = Platform.OS === "web" && anchoredEndSpace ? { ...anchoredEndSpace, includeInEndInset: true } : anchoredEndSpace;
6440
- const didAnchoredEndSpaceAnchorIndexChange = !isFirstLocal && !didDataChangeLocal && ((_g = state.props.anchoredEndSpace) == null ? void 0 : _g.anchorIndex) !== (anchoredEndSpaceResolved == null ? void 0 : anchoredEndSpaceResolved.anchorIndex);
6607
+ const didAnchoredEndSpaceAnchorIndexChange = !isFirstLocal && !didDataChangeLocal && ((_m = state.props.anchoredEndSpace) == null ? void 0 : _m.anchorIndex) !== (anchoredEndSpaceResolved == null ? void 0 : anchoredEndSpaceResolved.anchorIndex);
6441
6608
  state.props = {
6609
+ adaptiveRender: experimental_adaptiveRender,
6442
6610
  alignItemsAtEnd,
6443
6611
  alignItemsAtEndPaddingEnabled: useAlignItemsAtEndPadding,
6444
6612
  alwaysRender,
@@ -6446,6 +6614,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6446
6614
  alwaysRenderIndicesSet: alwaysRenderIndices.set,
6447
6615
  anchoredEndSpace: anchoredEndSpaceResolved,
6448
6616
  animatedProps: animatedPropsInternal,
6617
+ contentContainerAlignItems: contentContainerStyle.alignItems,
6449
6618
  contentInset,
6450
6619
  contentInsetEndAdjustment: contentInsetEndAdjustmentResolved,
6451
6620
  data: dataProp,
@@ -6498,7 +6667,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6498
6667
  const prevPaddingTop = peek$(ctx, "stylePaddingTop");
6499
6668
  setPaddingTop(ctx, { stylePaddingTop: stylePaddingTopState });
6500
6669
  refState.current.props.stylePaddingBottom = stylePaddingBottomState;
6501
- updateContentMetrics(ctx);
6670
+ updateContentMetricsState(ctx);
6502
6671
  let paddingDiff = stylePaddingTopState - prevPaddingTop;
6503
6672
  if (shouldAdjustPadding && maintainVisibleContentPositionConfig.size && paddingDiff && prevPaddingTop !== void 0 && Platform.OS === "ios") {
6504
6673
  if (state.scroll < 0) {
@@ -6535,7 +6704,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6535
6704
  useBootstrapInitialScroll: usesBootstrapInitialScroll
6536
6705
  });
6537
6706
  }, []);
6538
- if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
6707
+ if (isFirstLocal || didDataChangeLocal || state.didColumnsChange) {
6539
6708
  refState.current.lastBatchingAction = Date.now();
6540
6709
  if (!keyExtractorProp && !isFirstLocal && didDataChangeLocal) {
6541
6710
  refState.current.sizes.clear();
@@ -6552,6 +6721,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6552
6721
  dataLength: dataProp.length,
6553
6722
  didDataChange: didDataChangeLocal,
6554
6723
  initialScrollAtEnd,
6724
+ latestInitialScroll: initialScrollProp,
6725
+ latestInitialScrollSessionKind: initialScrollUsesOffsetOnly ? "offset" : "bootstrap",
6555
6726
  stylePaddingBottom: stylePaddingBottomState,
6556
6727
  useBootstrapInitialScroll: usesBootstrapInitialScroll
6557
6728
  });
@@ -6626,6 +6797,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6626
6797
  dataVersion,
6627
6798
  memoizedLastItemKeys.join(","),
6628
6799
  numColumnsProp,
6800
+ nextScrollAxisGap,
6629
6801
  stylePaddingBottomState,
6630
6802
  stylePaddingTopState,
6631
6803
  useAlignItemsAtEndPadding
@@ -6648,7 +6820,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6648
6820
  state.didColumnsChange = false;
6649
6821
  state.didDataChange = false;
6650
6822
  state.isFirst = false;
6651
- }, [dataProp, dataVersion, numColumnsProp]);
6823
+ }, [dataProp, dataVersion, numColumnsProp, nextScrollAxisGap]);
6652
6824
  React2.useLayoutEffect(() => {
6653
6825
  var _a4;
6654
6826
  set$(ctx, "extraData", extraData);
@@ -6699,6 +6871,14 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6699
6871
  }
6700
6872
  });
6701
6873
  React2.useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx, scheduleImperativeScrollCommit), []);
6874
+ React2.useEffect(() => {
6875
+ return () => {
6876
+ for (const timeout of state.timeouts) {
6877
+ clearTimeout(timeout);
6878
+ }
6879
+ state.timeouts.clear();
6880
+ };
6881
+ }, [state]);
6702
6882
  React2.useLayoutEffect(() => {
6703
6883
  var _a4;
6704
6884
  (_a4 = state.runPendingScrollToEnd) == null ? void 0 : _a4.call(state);
@@ -6746,7 +6926,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6746
6926
  onScroll: onScrollHandler,
6747
6927
  recycleItems,
6748
6928
  refreshControl: refreshControlElement ? stylePaddingTopState > 0 ? React2__namespace.cloneElement(refreshControlElement, {
6749
- progressViewOffset: ((_h = refreshControlElement.props.progressViewOffset) != null ? _h : 0) + stylePaddingTopState
6929
+ progressViewOffset: ((_n = refreshControlElement.props.progressViewOffset) != null ? _n : 0) + stylePaddingTopState
6750
6930
  }) : refreshControlElement : onRefresh && /* @__PURE__ */ React2__namespace.createElement(
6751
6931
  ReactNative.RefreshControl,
6752
6932
  {
@@ -6757,7 +6937,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6757
6937
  ),
6758
6938
  refScrollView: combinedRef,
6759
6939
  renderScrollComponent,
6760
- scrollAdjustHandler: (_i = refState.current) == null ? void 0 : _i.scrollAdjustHandler,
6940
+ scrollAdjustHandler: (_o = refState.current) == null ? void 0 : _o.scrollAdjustHandler,
6761
6941
  scrollEventThrottle: 0,
6762
6942
  snapToIndices,
6763
6943
  stickyHeaderIndices,
@@ -6791,6 +6971,8 @@ var internal2 = internal;
6791
6971
 
6792
6972
  exports.LegendList = LegendList3;
6793
6973
  exports.internal = internal2;
6974
+ exports.useAdaptiveRender = useAdaptiveRender;
6975
+ exports.useAdaptiveRenderChange = useAdaptiveRenderChange;
6794
6976
  exports.useIsLastItem = useIsLastItem;
6795
6977
  exports.useListScrollSize = useListScrollSize;
6796
6978
  exports.useRecyclingEffect = useRecyclingEffect;