@legendapp/list 3.0.6 → 3.1.0

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,30 @@ 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/calculateOffsetForIndex.ts
1221
+ function calculateOffsetForIndex(ctx, index) {
1222
+ const state = ctx.state;
1223
+ return index !== void 0 ? state.positions[index] || 0 : 0;
1224
+ }
1225
+
1226
+ // src/core/getTopOffsetAdjustment.ts
1227
+ function getTopOffsetAdjustment(ctx) {
1228
+ return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "alignItemsAtEndPadding") || 0) + (peek$(ctx, "headerSize") || 0);
1229
+ }
1230
+
1231
+ // src/utils/getId.ts
1232
+ function getId(state, index) {
1233
+ const { data, keyExtractor } = state.props;
1234
+ if (!data) {
1235
+ return "";
1236
+ }
1237
+ const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
1238
+ const id = ret;
1239
+ state.idCache[index] = id;
1240
+ return id;
1241
+ }
1242
+
1243
+ // src/core/updateContentMetricsState.ts
1197
1244
  function getRawContentLength(ctx) {
1198
1245
  var _a3, _b, _c;
1199
1246
  const { state, values } = ctx;
@@ -1204,240 +1251,187 @@ function getAlignItemsAtEndPadding(ctx) {
1204
1251
  const shouldPad = !!state.props.alignItemsAtEndPaddingEnabled && !state.props.horizontal && state.props.data.length > 0 && state.scrollLength > 0;
1205
1252
  return shouldPad ? Math.max(0, state.scrollLength - getRawContentLength(ctx) - getContentInsetEnd(ctx)) : 0;
1206
1253
  }
1207
- function updateContentMetrics(ctx) {
1254
+ function updateContentMetricsState(ctx) {
1255
+ const previousPadding = peek$(ctx, "alignItemsAtEndPadding") || 0;
1208
1256
  const nextPadding = getAlignItemsAtEndPadding(ctx);
1209
- if (peek$(ctx, "alignItemsAtEndPadding") !== nextPadding) {
1257
+ if (previousPadding !== nextPadding) {
1210
1258
  set$(ctx, "alignItemsAtEndPadding", nextPadding);
1211
1259
  }
1212
1260
  }
1213
- function setContentLengthSignal(ctx, signalName, size) {
1214
- if (peek$(ctx, signalName) !== size) {
1215
- set$(ctx, signalName, size);
1216
- updateContentMetrics(ctx);
1261
+
1262
+ // src/core/addTotalSize.ts
1263
+ function addTotalSize(ctx, key, add, notifyTotalSize = true) {
1264
+ const state = ctx.state;
1265
+ const prevTotalSize = state.totalSize;
1266
+ let totalSize = state.totalSize;
1267
+ if (key === null) {
1268
+ totalSize = add;
1269
+ if (state.timeoutSetPaddingTop) {
1270
+ clearTimeout(state.timeoutSetPaddingTop);
1271
+ state.timeoutSetPaddingTop = void 0;
1272
+ }
1273
+ } else {
1274
+ totalSize += add;
1275
+ }
1276
+ if (prevTotalSize !== totalSize) {
1277
+ if (!IsNewArchitecture && state.initialScroll && totalSize < prevTotalSize) {
1278
+ state.pendingTotalSize = totalSize;
1279
+ } else {
1280
+ state.pendingTotalSize = void 0;
1281
+ state.totalSize = totalSize;
1282
+ if (notifyTotalSize) {
1283
+ set$(ctx, "totalSize", totalSize);
1284
+ }
1285
+ updateContentMetricsState(ctx);
1286
+ }
1287
+ } else if (notifyTotalSize && ctx.values.get("totalSize") !== totalSize) {
1288
+ set$(ctx, "totalSize", totalSize);
1217
1289
  }
1218
1290
  }
1219
- function setHeaderSize(ctx, size) {
1220
- setContentLengthSignal(ctx, "headerSize", size);
1291
+
1292
+ // src/core/setSize.ts
1293
+ function setSize(ctx, itemKey, size, notifyTotalSize = true) {
1294
+ const state = ctx.state;
1295
+ const { sizes } = state;
1296
+ const previousSize = sizes.get(itemKey);
1297
+ const diff = previousSize !== void 0 ? size - previousSize : size;
1298
+ if (diff !== 0) {
1299
+ addTotalSize(ctx, itemKey, diff, notifyTotalSize);
1300
+ }
1301
+ sizes.set(itemKey, size);
1221
1302
  }
1222
- function setFooterSize(ctx, size) {
1223
- setContentLengthSignal(ctx, "footerSize", size);
1303
+
1304
+ // src/utils/getItemSize.ts
1305
+ function getKnownOrFixedSize(ctx, key, index, data, resolved) {
1306
+ var _a3, _b;
1307
+ const state = ctx.state;
1308
+ const { getFixedItemSize, getItemType } = state.props;
1309
+ let size = key ? state.sizesKnown.get(key) : void 0;
1310
+ if (size === void 0 && key && getFixedItemSize) {
1311
+ const itemType = (_b = resolved == null ? void 0 : resolved.itemType) != null ? _b : getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1312
+ const fixedSize = (resolved == null ? void 0 : resolved.didResolveFixedItemSize) ? resolved.fixedItemSize : getFixedItemSize(data, index, itemType);
1313
+ if (fixedSize !== void 0) {
1314
+ size = fixedSize + ctx.scrollAxisGap;
1315
+ state.sizesKnown.set(key, size);
1316
+ }
1317
+ }
1318
+ return size;
1224
1319
  }
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);
1320
+ function getKnownOrFixedItemSize(ctx, index) {
1321
+ const key = getId(ctx.state, index);
1322
+ return getKnownOrFixedSize(ctx, key, index, ctx.state.props.data[index]);
1228
1323
  }
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);
1324
+ function areKnownOrFixedItemSizesAvailable(ctx, startIndex, endIndex) {
1325
+ for (let index = startIndex; index <= endIndex; index++) {
1326
+ if (getKnownOrFixedItemSize(ctx, index) === void 0) {
1327
+ return false;
1328
+ }
1237
1329
  }
1238
- return didChange;
1330
+ return true;
1239
1331
  }
1240
- function useLatestRef(value) {
1241
- const ref = React2__namespace.useRef(value);
1242
- ref.current = value;
1243
- return ref;
1332
+ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize, notifyTotalSize, resolved) {
1333
+ var _a3, _b, _c, _d;
1334
+ const state = ctx.state;
1335
+ const {
1336
+ sizes,
1337
+ averageSizes,
1338
+ props: { estimatedItemSize, getItemType },
1339
+ scrollingTo
1340
+ } = state;
1341
+ const sizeKnown = state.sizesKnown.get(key);
1342
+ if (sizeKnown !== void 0) {
1343
+ return sizeKnown;
1344
+ }
1345
+ let size;
1346
+ const renderedSize = sizes.get(key);
1347
+ if (preferCachedSize) {
1348
+ if (renderedSize !== void 0) {
1349
+ return renderedSize;
1350
+ }
1351
+ }
1352
+ size = getKnownOrFixedSize(ctx, key, index, data, resolved);
1353
+ if (size !== void 0) {
1354
+ setSize(ctx, key, size, notifyTotalSize);
1355
+ return size;
1356
+ }
1357
+ const itemType = (_b = resolved == null ? void 0 : resolved.itemType) != null ? _b : getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1358
+ if (useAverageSize && !scrollingTo) {
1359
+ const averageSizeForType = (_c = averageSizes[itemType]) == null ? void 0 : _c.avg;
1360
+ if (averageSizeForType !== void 0) {
1361
+ size = roundSize(averageSizeForType);
1362
+ }
1363
+ }
1364
+ if (size === void 0 && renderedSize !== void 0) {
1365
+ return renderedSize;
1366
+ }
1367
+ if (size === void 0 && useAverageSize && scrollingTo) {
1368
+ const averageSizeForType = (_d = scrollingTo.averageSizeSnapshot) == null ? void 0 : _d[itemType];
1369
+ if (averageSizeForType !== void 0) {
1370
+ size = roundSize(averageSizeForType);
1371
+ }
1372
+ }
1373
+ if (size === void 0) {
1374
+ size = estimatedItemSize + ctx.scrollAxisGap;
1375
+ }
1376
+ setSize(ctx, key, size, notifyTotalSize);
1377
+ return size;
1244
1378
  }
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
- );
1379
+ function getItemSizeAtIndex(ctx, index) {
1380
+ if (index === void 0 || index < 0) {
1381
+ return void 0;
1382
+ }
1383
+ const targetId = getId(ctx.state, index);
1384
+ return getItemSize(ctx, targetId, index, ctx.state.props.data[index]);
1259
1385
  }
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
1386
 
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);
1387
+ // src/core/calculateOffsetWithOffsetPosition.ts
1388
+ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1389
+ var _a3;
1390
+ const state = ctx.state;
1391
+ const { index, viewOffset, viewPosition } = params;
1392
+ let offset = offsetParam;
1393
+ if (viewOffset) {
1394
+ offset -= viewOffset;
1395
+ }
1396
+ if (index !== void 0) {
1397
+ const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
1398
+ if (topOffsetAdjustment) {
1399
+ offset += topOffsetAdjustment;
1313
1400
  }
1314
- if (!ListFooterComponent) {
1315
- setFooterSize(ctx, 0);
1401
+ }
1402
+ if (viewPosition !== void 0 && index !== void 0) {
1403
+ const dataLength = state.props.data.length;
1404
+ if (dataLength === 0) {
1405
+ return offset;
1316
1406
  }
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
- );
1399
- }
1400
- }, [childrenMode, ctx, keyExtractor]);
1401
- React2.useEffect(() => {
1402
- const state = ctx.state;
1403
- 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;
1407
+ const isOutOfBounds = index < 0 || index >= dataLength;
1408
+ const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
1409
+ const itemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1410
+ const trailingInset = getContentInsetEnd(ctx);
1411
+ offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1412
+ if (!isOutOfBounds && index === state.props.data.length - 1) {
1413
+ const footerSize = peek$(ctx, "footerSize") || 0;
1414
+ offset += footerSize;
1407
1415
  }
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]);
1416
+ }
1417
+ return offset;
1437
1418
  }
1438
- function useDevChecksNoop(_props) {
1419
+
1420
+ // src/core/clampScrollOffset.ts
1421
+ function clampScrollOffset(ctx, offset, scrollTarget) {
1422
+ const state = ctx.state;
1423
+ const contentSize = getContentSize(ctx);
1424
+ let clampedOffset = offset;
1425
+ if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform.OS !== "android" || state.lastLayout)) {
1426
+ const baseMaxOffset = Math.max(0, contentSize - state.scrollLength);
1427
+ const viewOffset = scrollTarget == null ? void 0 : scrollTarget.viewOffset;
1428
+ const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
1429
+ const maxOffset = baseMaxOffset + extraEndOffset;
1430
+ clampedOffset = Math.min(offset, maxOffset);
1431
+ }
1432
+ clampedOffset = Math.max(0, clampedOffset);
1433
+ return clampedOffset;
1439
1434
  }
1440
- var useDevChecks = IS_DEV ? useDevChecksImpl : useDevChecksNoop;
1441
1435
 
1442
1436
  // src/core/deferredPublicOnScroll.ts
1443
1437
  function withResolvedContentOffset(state, event, resolvedOffset) {
@@ -1750,6 +1744,66 @@ function recalculateSettledScroll(ctx) {
1750
1744
  checkThresholds(ctx);
1751
1745
  }
1752
1746
 
1747
+ // src/core/adaptiveRender.ts
1748
+ var DEFAULT_ENTER_VELOCITY = 4;
1749
+ var DEFAULT_EXIT_VELOCITY = 1;
1750
+ var DEFAULT_EXIT_DELAY = 1e3;
1751
+ function scheduleAdaptiveRenderExit(ctx, exitDelay) {
1752
+ const state = ctx.state;
1753
+ const previousTimeout = state.timeoutAdaptiveRender;
1754
+ if (previousTimeout !== void 0) {
1755
+ clearTimeout(previousTimeout);
1756
+ state.timeouts.delete(previousTimeout);
1757
+ state.timeoutAdaptiveRender = void 0;
1758
+ }
1759
+ if (exitDelay <= 0) {
1760
+ setAdaptiveRender(ctx, "normal");
1761
+ } else {
1762
+ const timeout = setTimeout(() => {
1763
+ state.timeouts.delete(timeout);
1764
+ state.timeoutAdaptiveRender = void 0;
1765
+ setAdaptiveRender(ctx, "normal");
1766
+ }, exitDelay);
1767
+ state.timeoutAdaptiveRender = timeout;
1768
+ state.timeouts.add(timeout);
1769
+ }
1770
+ }
1771
+ function setAdaptiveRender(ctx, mode) {
1772
+ var _a3, _b;
1773
+ const previousMode = peek$(ctx, "adaptiveRender");
1774
+ if (previousMode !== mode) {
1775
+ set$(ctx, "adaptiveRender", mode);
1776
+ (_b = (_a3 = ctx.state.props.adaptiveRender) == null ? void 0 : _a3.onChange) == null ? void 0 : _b.call(_a3, mode);
1777
+ }
1778
+ }
1779
+ function updateAdaptiveRender(ctx, scrollVelocity) {
1780
+ var _a3, _b, _c;
1781
+ const state = ctx.state;
1782
+ const adaptiveRender = state.props.adaptiveRender;
1783
+ const enterVelocity = (_a3 = adaptiveRender == null ? void 0 : adaptiveRender.enterVelocity) != null ? _a3 : DEFAULT_ENTER_VELOCITY;
1784
+ const exitVelocity = (_b = adaptiveRender == null ? void 0 : adaptiveRender.exitVelocity) != null ? _b : DEFAULT_EXIT_VELOCITY;
1785
+ const exitDelay = (_c = adaptiveRender == null ? void 0 : adaptiveRender.exitDelay) != null ? _c : DEFAULT_EXIT_DELAY;
1786
+ const currentMode = peek$(ctx, "adaptiveRender");
1787
+ const threshold = currentMode === "light" ? exitVelocity : enterVelocity;
1788
+ const nextMode = Math.abs(scrollVelocity) > threshold ? "light" : "normal";
1789
+ const previousMode = state.timeoutAdaptiveRender !== void 0 ? "normal" : currentMode;
1790
+ if (nextMode !== previousMode) {
1791
+ if (nextMode === "light") {
1792
+ setAdaptiveRender(ctx, "light");
1793
+ scheduleAdaptiveRenderExit(ctx, exitDelay);
1794
+ } else if (currentMode === "light") {
1795
+ scheduleAdaptiveRenderExit(ctx, exitDelay);
1796
+ }
1797
+ }
1798
+ }
1799
+
1800
+ // src/utils/getEffectiveDrawDistance.ts
1801
+ var INITIAL_DRAW_DISTANCE = 100;
1802
+ function getEffectiveDrawDistance(ctx) {
1803
+ const drawDistance = ctx.state.props.drawDistance;
1804
+ return peek$(ctx, "readyToRender") ? drawDistance : Math.min(drawDistance, INITIAL_DRAW_DISTANCE);
1805
+ }
1806
+
1753
1807
  // src/utils/setInitialRenderState.ts
1754
1808
  function setInitialRenderState(ctx, {
1755
1809
  didLayout,
@@ -1769,6 +1823,13 @@ function setInitialRenderState(ctx, {
1769
1823
  const isReadyToRender = Boolean(state.didContainersLayout && state.didFinishInitialScroll);
1770
1824
  if (isReadyToRender && !peek$(ctx, "readyToRender")) {
1771
1825
  set$(ctx, "readyToRender", true);
1826
+ setAdaptiveRender(ctx, "normal");
1827
+ if (state.props.drawDistance > INITIAL_DRAW_DISTANCE) {
1828
+ requestAnimationFrame(() => {
1829
+ var _a3;
1830
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
1831
+ });
1832
+ }
1772
1833
  if (onLoad) {
1773
1834
  onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1774
1835
  }
@@ -1843,259 +1904,63 @@ function finishInitialScroll(ctx, options) {
1843
1904
  complete();
1844
1905
  }
1845
1906
 
1846
- // src/core/calculateOffsetForIndex.ts
1847
- function calculateOffsetForIndex(ctx, index) {
1907
+ // src/core/finishScrollTo.ts
1908
+ function finishScrollTo(ctx) {
1909
+ var _a3, _b;
1848
1910
  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 "";
1911
+ if (state == null ? void 0 : state.scrollingTo) {
1912
+ const resolvePendingScroll = state.pendingScrollResolve;
1913
+ state.pendingScrollResolve = void 0;
1914
+ const scrollingTo = state.scrollingTo;
1915
+ state.scrollHistory.length = 0;
1916
+ state.scrollingTo = void 0;
1917
+ if (state.pendingTotalSize !== void 0) {
1918
+ addTotalSize(ctx, null, state.pendingTotalSize);
1919
+ }
1920
+ if (PlatformAdjustBreaksScroll) {
1921
+ state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
1922
+ }
1923
+ if (scrollingTo.isInitialScroll || state.initialScroll) {
1924
+ const isOffsetSession = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
1925
+ const shouldPreserveResizeTarget = !!scrollingTo.isInitialScroll && !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_b = state.initialScroll) == null ? void 0 : _b.viewPosition) === 1;
1926
+ finishInitialScroll(ctx, {
1927
+ onFinished: () => {
1928
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1929
+ },
1930
+ preserveTarget: isOffsetSession && state.props.data.length === 0 || shouldPreserveResizeTarget,
1931
+ recalculateItems: true,
1932
+ schedulePreservedTargetClear: shouldPreserveResizeTarget,
1933
+ syncObservedOffset: isOffsetSession,
1934
+ waitForCompletionFrame: !!scrollingTo.waitForInitialScrollCompletionFrame
1935
+ });
1936
+ return;
1937
+ }
1938
+ recalculateSettledScroll(ctx);
1939
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1862
1940
  }
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
1941
  }
1868
1942
 
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;
1943
+ // src/core/checkFinishedScroll.ts
1944
+ var INITIAL_SCROLL_MAX_FALLBACK_CHECKS = 20;
1945
+ var INITIAL_SCROLL_COMPLETION_TARGET_EPSILON = 1;
1946
+ var INITIAL_SCROLL_ZERO_TARGET_EPSILON = 1;
1947
+ var SILENT_INITIAL_SCROLL_RETRY_DELAY_MS = 16;
1948
+ var SILENT_INITIAL_SCROLL_TARGET_EPSILON = 1;
1949
+ function checkFinishedScroll(ctx, options) {
1950
+ const scrollingTo = ctx.state.scrollingTo;
1951
+ if (options == null ? void 0 : options.onlyIfAligned) {
1952
+ if (!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) || scrollingTo.animated) {
1953
+ return;
1879
1954
  }
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);
1955
+ if (!getResolvedScrollCompletionState(ctx, scrollingTo).isAtResolvedTarget) {
1956
+ return;
1893
1957
  }
1894
- } else if (notifyTotalSize && ctx.values.get("totalSize") !== totalSize) {
1895
- set$(ctx, "totalSize", totalSize);
1896
1958
  }
1959
+ ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
1897
1960
  }
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();
2075
- }
2076
- }
2077
-
2078
- // src/core/checkFinishedScroll.ts
2079
- var INITIAL_SCROLL_MAX_FALLBACK_CHECKS = 20;
2080
- var INITIAL_SCROLL_COMPLETION_TARGET_EPSILON = 1;
2081
- var INITIAL_SCROLL_ZERO_TARGET_EPSILON = 1;
2082
- var SILENT_INITIAL_SCROLL_RETRY_DELAY_MS = 16;
2083
- var SILENT_INITIAL_SCROLL_TARGET_EPSILON = 1;
2084
- function checkFinishedScroll(ctx, options) {
2085
- const scrollingTo = ctx.state.scrollingTo;
2086
- if (options == null ? void 0 : options.onlyIfAligned) {
2087
- if (!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) || scrollingTo.animated) {
2088
- return;
2089
- }
2090
- if (!getResolvedScrollCompletionState(ctx, scrollingTo).isAtResolvedTarget) {
2091
- return;
2092
- }
2093
- }
2094
- ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
2095
- }
2096
- function hasScrollCompletionOwnership(state, options) {
2097
- const { clampedTargetOffset, scrollingTo } = options;
2098
- return !scrollingTo.isInitialScroll || state.hasScrolled || clampedTargetOffset <= INITIAL_SCROLL_COMPLETION_TARGET_EPSILON;
1961
+ function hasScrollCompletionOwnership(state, options) {
1962
+ const { clampedTargetOffset, scrollingTo } = options;
1963
+ return !scrollingTo.isInitialScroll || state.hasScrolled || clampedTargetOffset <= INITIAL_SCROLL_COMPLETION_TARGET_EPSILON;
2099
1964
  }
2100
1965
  function isSilentInitialDispatch(state, scrollingTo) {
2101
1966
  return !!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && initialScrollCompletion.didDispatchNativeScroll(state) && !state.hasScrolled;
@@ -2204,65 +2069,374 @@ function checkFinishedScrollFallback(ctx) {
2204
2069
  state,
2205
2070
  isStillScrollingTo
2206
2071
  );
2207
- const completionState = getResolvedScrollCompletionState(ctx, isStillScrollingTo);
2208
- const canFinishAfterSilentNativeDispatch = Platform.OS === "android" && silentInitialDispatch && completionState.isAtResolvedTarget && numChecks >= 1;
2209
- const shouldRetrySilentInitialNativeScroll = Platform.OS === "android" && canFinishAfterSilentNativeDispatch && !initialScrollCompletion.didRetrySilentInitialScroll(state);
2210
- const shouldFinishAfterObservedScroll = state.hasScrolled && (!isStillScrollingTo.isInitialScroll || completionState.isAtResolvedTarget);
2211
- const shouldRetryUnalignedInitialScroll = isStillScrollingTo.isInitialScroll && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
2212
- const shouldRetryUnalignedEndScroll = Platform.OS === "ios" && !isStillScrollingTo.isInitialScroll && isEndAlignedLastItemTarget(ctx, isStillScrollingTo) && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
2213
- if (shouldRetrySilentInitialNativeScroll) {
2214
- const targetOffset = (_b = (_a3 = getInitialScrollWatchdogTargetOffset(state)) != null ? _a3 : isStillScrollingTo.targetOffset) != null ? _b : 0;
2215
- const jiggleOffset = targetOffset >= SILENT_INITIAL_SCROLL_TARGET_EPSILON ? targetOffset - SILENT_INITIAL_SCROLL_TARGET_EPSILON : targetOffset + SILENT_INITIAL_SCROLL_TARGET_EPSILON;
2216
- initialScrollCompletion.markSilentInitialScrollRetry(state);
2217
- scrollToFallbackOffset(ctx, jiggleOffset);
2218
- requestAnimationFrame(() => {
2219
- scrollToFallbackOffset(ctx, targetOffset);
2220
- });
2221
- scheduleFallbackCheck(SILENT_INITIAL_SCROLL_RETRY_DELAY_MS);
2222
- } else if (shouldRetryUnalignedEndScroll) {
2223
- scrollToFallbackOffset(ctx, completionState.clampedTargetOffset);
2224
- scheduleFallbackCheck(100);
2225
- } else if (shouldFinishZeroTarget || shouldFinishAfterObservedScroll || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
2226
- finishScrollTo(ctx);
2227
- } else if ((isNativeInitialPending || shouldRetryUnalignedInitialScroll) && numChecks <= maxChecks) {
2228
- const targetOffset = (_d = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : isStillScrollingTo.targetOffset) != null ? _d : state.scrollPending;
2229
- scrollToFallbackOffset(ctx, targetOffset);
2230
- scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
2231
- } else {
2232
- scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
2233
- }
2072
+ const completionState = getResolvedScrollCompletionState(ctx, isStillScrollingTo);
2073
+ const canFinishAfterSilentNativeDispatch = Platform.OS === "android" && silentInitialDispatch && completionState.isAtResolvedTarget && numChecks >= 1;
2074
+ const shouldRetrySilentInitialNativeScroll = Platform.OS === "android" && canFinishAfterSilentNativeDispatch && !initialScrollCompletion.didRetrySilentInitialScroll(state);
2075
+ const shouldFinishAfterObservedScroll = state.hasScrolled && (!isStillScrollingTo.isInitialScroll || completionState.isAtResolvedTarget);
2076
+ const shouldRetryUnalignedInitialScroll = isStillScrollingTo.isInitialScroll && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
2077
+ const shouldRetryUnalignedEndScroll = Platform.OS === "ios" && !isStillScrollingTo.isInitialScroll && isEndAlignedLastItemTarget(ctx, isStillScrollingTo) && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
2078
+ if (shouldRetrySilentInitialNativeScroll) {
2079
+ const targetOffset = (_b = (_a3 = getInitialScrollWatchdogTargetOffset(state)) != null ? _a3 : isStillScrollingTo.targetOffset) != null ? _b : 0;
2080
+ const jiggleOffset = targetOffset >= SILENT_INITIAL_SCROLL_TARGET_EPSILON ? targetOffset - SILENT_INITIAL_SCROLL_TARGET_EPSILON : targetOffset + SILENT_INITIAL_SCROLL_TARGET_EPSILON;
2081
+ initialScrollCompletion.markSilentInitialScrollRetry(state);
2082
+ scrollToFallbackOffset(ctx, jiggleOffset);
2083
+ requestAnimationFrame(() => {
2084
+ scrollToFallbackOffset(ctx, targetOffset);
2085
+ });
2086
+ scheduleFallbackCheck(SILENT_INITIAL_SCROLL_RETRY_DELAY_MS);
2087
+ } else if (shouldRetryUnalignedEndScroll) {
2088
+ scrollToFallbackOffset(ctx, completionState.clampedTargetOffset);
2089
+ scheduleFallbackCheck(100);
2090
+ } else if (shouldFinishZeroTarget || shouldFinishAfterObservedScroll || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
2091
+ finishScrollTo(ctx);
2092
+ } else if ((isNativeInitialPending || shouldRetryUnalignedInitialScroll) && numChecks <= maxChecks) {
2093
+ const targetOffset = (_d = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : isStillScrollingTo.targetOffset) != null ? _d : state.scrollPending;
2094
+ scrollToFallbackOffset(ctx, targetOffset);
2095
+ scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
2096
+ } else {
2097
+ scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
2098
+ }
2099
+ }
2100
+ };
2101
+ checkHasScrolled();
2102
+ }, initialDelay);
2103
+ }
2104
+
2105
+ // src/core/doScrollTo.native.ts
2106
+ function doScrollTo(ctx, params) {
2107
+ const state = ctx.state;
2108
+ const { animated, horizontal, isInitialScroll, offset } = params;
2109
+ const isAnimated = !!animated;
2110
+ const { refScroller } = state;
2111
+ const scroller = refScroller.current;
2112
+ if (!scroller) {
2113
+ return;
2114
+ }
2115
+ const isHorizontal = !!horizontal;
2116
+ const contentSize = isHorizontal ? getContentSize(ctx) : void 0;
2117
+ const nativeOffset = toNativeHorizontalOffset(state, offset, contentSize);
2118
+ scroller.scrollTo({
2119
+ animated: isAnimated,
2120
+ x: isHorizontal ? nativeOffset : 0,
2121
+ y: isHorizontal ? 0 : offset
2122
+ });
2123
+ if (isInitialScroll) {
2124
+ initialScrollCompletion.markInitialScrollNativeDispatch(state);
2125
+ }
2126
+ if (!isAnimated) {
2127
+ state.scroll = offset;
2128
+ checkFinishedScrollFallback(ctx);
2129
+ }
2130
+ }
2131
+
2132
+ // src/utils/requestAdjust.ts
2133
+ function requestAdjust(ctx, positionDiff, dataChanged) {
2134
+ const state = ctx.state;
2135
+ if (Math.abs(positionDiff) > 0.1) {
2136
+ const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
2137
+ const doit = () => {
2138
+ if (needsScrollWorkaround) {
2139
+ doScrollTo(ctx, { horizontal: state.props.horizontal, offset: state.scroll });
2140
+ } else {
2141
+ state.scrollAdjustHandler.requestAdjust(positionDiff);
2142
+ if (state.adjustingFromInitialMount) {
2143
+ state.adjustingFromInitialMount--;
2144
+ }
2145
+ }
2146
+ };
2147
+ state.scroll += positionDiff;
2148
+ state.scrollForNextCalculateItemsInView = void 0;
2149
+ const readyToRender = peek$(ctx, "readyToRender");
2150
+ if (readyToRender) {
2151
+ doit();
2152
+ if (Platform.OS !== "web") {
2153
+ const threshold = state.scroll - positionDiff / 2;
2154
+ if (!state.ignoreScrollFromMVCP) {
2155
+ state.ignoreScrollFromMVCP = {};
2156
+ }
2157
+ if (positionDiff > 0) {
2158
+ state.ignoreScrollFromMVCP.lt = threshold;
2159
+ } else {
2160
+ state.ignoreScrollFromMVCP.gt = threshold;
2161
+ }
2162
+ if (state.ignoreScrollFromMVCPTimeout) {
2163
+ clearTimeout(state.ignoreScrollFromMVCPTimeout);
2164
+ }
2165
+ const delay = needsScrollWorkaround ? 250 : 100;
2166
+ state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
2167
+ var _a3;
2168
+ state.ignoreScrollFromMVCP = void 0;
2169
+ const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
2170
+ if (shouldForceUpdate) {
2171
+ state.ignoreScrollFromMVCPIgnored = false;
2172
+ state.scrollPending = state.scroll;
2173
+ (_a3 = state.reprocessCurrentScroll) == null ? void 0 : _a3.call(state);
2174
+ }
2175
+ }, delay);
2176
+ }
2177
+ } else {
2178
+ state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
2179
+ requestAnimationFrame(doit);
2180
+ }
2181
+ }
2182
+ }
2183
+
2184
+ // src/core/updateContentMetrics.ts
2185
+ var SCROLL_ADJUST_EPSILON = 0.1;
2186
+ function setContentLengthSignal(ctx, signalName, size) {
2187
+ if (peek$(ctx, signalName) !== size) {
2188
+ set$(ctx, signalName, size);
2189
+ updateContentMetricsState(ctx);
2190
+ }
2191
+ }
2192
+ function shouldAdjustForHeaderSizeChange(ctx, previousHeaderSize, nextHeaderSize) {
2193
+ const { didContainersLayout, didFinishInitialScroll, props, scroll, scrollingTo } = ctx.state;
2194
+ const sizeDiff = nextHeaderSize - previousHeaderSize;
2195
+ const leadingPadding = props.horizontal ? props.stylePaddingLeft : props.stylePaddingTop;
2196
+ const previousHeaderEnd = (leadingPadding || 0) + previousHeaderSize;
2197
+ return Platform.OS === "web" && props.maintainVisibleContentPosition.size && didContainersLayout && didFinishInitialScroll && !scrollingTo && scroll >= previousHeaderEnd - SCROLL_ADJUST_EPSILON && Math.abs(sizeDiff) > SCROLL_ADJUST_EPSILON;
2198
+ }
2199
+ function setHeaderSize(ctx, size) {
2200
+ const { state } = ctx;
2201
+ const previousHeaderSize = peek$(ctx, "headerSize") || 0;
2202
+ const didChange = previousHeaderSize !== size;
2203
+ const hasMeasuredOrEstimatedHeaderBaseline = state.didMeasureHeader || previousHeaderSize > SCROLL_ADJUST_EPSILON;
2204
+ if (didChange) {
2205
+ set$(ctx, "headerSize", size);
2206
+ updateContentMetricsState(ctx);
2207
+ if (hasMeasuredOrEstimatedHeaderBaseline && shouldAdjustForHeaderSizeChange(ctx, previousHeaderSize, size)) {
2208
+ requestAdjust(ctx, size - previousHeaderSize);
2209
+ }
2210
+ }
2211
+ state.didMeasureHeader = true;
2212
+ }
2213
+ function setFooterSize(ctx, size) {
2214
+ setContentLengthSignal(ctx, "footerSize", size);
2215
+ }
2216
+ function areInsetsEqual(left, right) {
2217
+ var _a3, _b, _c, _d, _e, _f, _g, _h;
2218
+ 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);
2219
+ }
2220
+ function setContentInsetOverride(ctx, inset) {
2221
+ const { state } = ctx;
2222
+ const previousInset = state.contentInsetOverride;
2223
+ const nextInset = inset != null ? inset : void 0;
2224
+ const didChange = !areInsetsEqual(previousInset, nextInset);
2225
+ state.contentInsetOverride = nextInset;
2226
+ if (didChange) {
2227
+ updateContentMetricsState(ctx);
2228
+ }
2229
+ return didChange;
2230
+ }
2231
+ function useLatestRef(value) {
2232
+ const ref = React2__namespace.useRef(value);
2233
+ ref.current = value;
2234
+ return ref;
2235
+ }
2236
+
2237
+ // src/hooks/useStableRenderComponent.tsx
2238
+ function useStableRenderComponent(renderComponent, mapProps) {
2239
+ const renderComponentRef = useLatestRef(renderComponent);
2240
+ const mapPropsRef = useLatestRef(mapProps);
2241
+ return React2__namespace.useMemo(
2242
+ () => React2__namespace.forwardRef(
2243
+ (props, ref) => {
2244
+ var _a3, _b;
2245
+ return (_b = (_a3 = renderComponentRef.current) == null ? void 0 : _a3.call(renderComponentRef, mapPropsRef.current(props, ref))) != null ? _b : null;
2246
+ }
2247
+ ),
2248
+ [mapPropsRef, renderComponentRef]
2249
+ );
2250
+ }
2251
+ var LayoutView = ({ onLayoutChange, refView, ...rest }) => {
2252
+ const localRef = React2.useRef(null);
2253
+ const ref = refView != null ? refView : localRef;
2254
+ const { onLayout } = useOnLayoutSync({ onLayoutChange, ref });
2255
+ return /* @__PURE__ */ React2__namespace.createElement(ReactNative.View, { ...rest, onLayout, ref });
2256
+ };
2257
+
2258
+ // src/components/ListComponent.tsx
2259
+ var AlignItemsAtEndSpacer = typedMemo(function AlignItemsAtEndSpacer2({ horizontal }) {
2260
+ const [alignItemsAtEndPadding = 0] = useArr$(["alignItemsAtEndPadding"]);
2261
+ if (alignItemsAtEndPadding <= 0) {
2262
+ return null;
2263
+ }
2264
+ return /* @__PURE__ */ React2__namespace.createElement(
2265
+ View,
2266
+ {
2267
+ style: horizontal ? { flexShrink: 0, width: alignItemsAtEndPadding } : { flexShrink: 0, height: alignItemsAtEndPadding }
2268
+ },
2269
+ null
2270
+ );
2271
+ });
2272
+ var ListComponent = typedMemo(function ListComponent2({
2273
+ canRender,
2274
+ style,
2275
+ contentContainerStyle,
2276
+ horizontal,
2277
+ initialContentOffset,
2278
+ recycleItems,
2279
+ ItemSeparatorComponent,
2280
+ alignItemsAtEnd: _alignItemsAtEnd,
2281
+ onScroll: onScroll2,
2282
+ onLayout,
2283
+ ListHeaderComponent,
2284
+ ListHeaderComponentStyle,
2285
+ ListFooterComponent,
2286
+ ListFooterComponentStyle,
2287
+ ListEmptyComponent,
2288
+ getRenderedItem: getRenderedItem2,
2289
+ updateItemSize: updateItemSize2,
2290
+ refScrollView,
2291
+ renderScrollComponent,
2292
+ onLayoutFooter,
2293
+ scrollAdjustHandler,
2294
+ snapToIndices,
2295
+ stickyHeaderConfig,
2296
+ stickyHeaderIndices,
2297
+ useWindowScroll = false,
2298
+ ...rest
2299
+ }) {
2300
+ const ctx = useStateContext();
2301
+ const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
2302
+ const [otherAxisSize = 0] = useArr$(["otherAxisSize"]);
2303
+ const shouldRenderAlignItemsAtEndSpacer = ctx.state.props.alignItemsAtEndPaddingEnabled;
2304
+ const autoOtherAxisStyle = getAutoOtherAxisStyle({
2305
+ horizontal,
2306
+ needsOtherAxisSize: ctx.state.needsOtherAxisSize,
2307
+ otherAxisSize
2308
+ });
2309
+ const CustomScrollComponent = useStableRenderComponent(
2310
+ renderScrollComponent,
2311
+ (props, ref) => ({ ...props, ref })
2312
+ );
2313
+ const ScrollComponent = renderScrollComponent ? CustomScrollComponent : ListComponentScrollView;
2314
+ const SnapOrScroll = snapToIndices ? SnapWrapper : ScrollComponent;
2315
+ React2.useLayoutEffect(() => {
2316
+ if (!ListHeaderComponent) {
2317
+ setHeaderSize(ctx, 0);
2318
+ }
2319
+ if (!ListFooterComponent) {
2320
+ setFooterSize(ctx, 0);
2321
+ }
2322
+ }, [ListHeaderComponent, ListFooterComponent, ctx]);
2323
+ const onLayoutHeader = React2.useCallback(
2324
+ (rect) => {
2325
+ const size = rect[horizontal ? "width" : "height"];
2326
+ setHeaderSize(ctx, size);
2327
+ },
2328
+ [ctx, horizontal]
2329
+ );
2330
+ const onLayoutFooterInternal = React2.useCallback(
2331
+ (rect, fromLayoutEffect) => {
2332
+ const size = rect[horizontal ? "width" : "height"];
2333
+ setFooterSize(ctx, size);
2334
+ onLayoutFooter == null ? void 0 : onLayoutFooter(rect, fromLayoutEffect);
2335
+ },
2336
+ [ctx, horizontal, onLayoutFooter]
2337
+ );
2338
+ return /* @__PURE__ */ React2__namespace.createElement(
2339
+ SnapOrScroll,
2340
+ {
2341
+ ...rest,
2342
+ ...ScrollComponent === ListComponentScrollView ? { useWindowScroll } : {},
2343
+ contentContainerStyle: [
2344
+ horizontal ? {
2345
+ height: "100%"
2346
+ } : {},
2347
+ contentContainerStyle
2348
+ ],
2349
+ contentOffset: initialContentOffset !== void 0 ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
2350
+ horizontal,
2351
+ maintainVisibleContentPosition: maintainVisibleContentPosition.size || maintainVisibleContentPosition.data ? { minIndexForVisible: 0 } : void 0,
2352
+ onLayout,
2353
+ onScroll: onScroll2,
2354
+ ref: refScrollView,
2355
+ ScrollComponent: snapToIndices ? ScrollComponent : void 0,
2356
+ style: autoOtherAxisStyle ? [autoOtherAxisStyle, style] : style
2357
+ },
2358
+ /* @__PURE__ */ React2__namespace.createElement(ScrollAdjust, null),
2359
+ ListHeaderComponent && /* @__PURE__ */ React2__namespace.createElement(LayoutView, { onLayoutChange: onLayoutHeader, style: ListHeaderComponentStyle }, getComponent(ListHeaderComponent)),
2360
+ ListEmptyComponent && getComponent(ListEmptyComponent),
2361
+ shouldRenderAlignItemsAtEndSpacer && /* @__PURE__ */ React2__namespace.createElement(AlignItemsAtEndSpacer, { horizontal }),
2362
+ canRender && !ListEmptyComponent && /* @__PURE__ */ React2__namespace.createElement(
2363
+ Containers,
2364
+ {
2365
+ getRenderedItem: getRenderedItem2,
2366
+ horizontal,
2367
+ ItemSeparatorComponent,
2368
+ recycleItems,
2369
+ stickyHeaderConfig,
2370
+ updateItemSize: updateItemSize2
2371
+ }
2372
+ ),
2373
+ ListFooterComponent && /* @__PURE__ */ React2__namespace.createElement(LayoutView, { onLayoutChange: onLayoutFooterInternal, style: ListFooterComponentStyle }, getComponent(ListFooterComponent)),
2374
+ Platform.OS === "web" && /* @__PURE__ */ React2__namespace.createElement(WebAnchoredEndSpace, { horizontal }),
2375
+ IS_DEV && ENABLE_DEVMODE
2376
+ );
2377
+ });
2378
+ var WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH = 100;
2379
+ var WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO = 0.9;
2380
+ var WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO = 0.9;
2381
+ function useDevChecksImpl(props) {
2382
+ const ctx = useStateContext();
2383
+ const { childrenMode, keyExtractor, renderScrollComponent, useWindowScroll } = props;
2384
+ React2.useEffect(() => {
2385
+ if (useWindowScroll && renderScrollComponent) {
2386
+ warnDevOnce(
2387
+ "useWindowScrollRenderScrollComponent",
2388
+ "useWindowScroll is not supported when renderScrollComponent is provided."
2389
+ );
2390
+ }
2391
+ }, [renderScrollComponent, useWindowScroll]);
2392
+ React2.useEffect(() => {
2393
+ if (!keyExtractor && !ctx.state.isFirst && ctx.state.didDataChange && !childrenMode) {
2394
+ warnDevOnce(
2395
+ "keyExtractor",
2396
+ "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."
2397
+ );
2398
+ }
2399
+ }, [childrenMode, ctx, keyExtractor]);
2400
+ React2.useEffect(() => {
2401
+ const state = ctx.state;
2402
+ const dataLength = state.props.data.length;
2403
+ const useWindowScrollResolved = state.props.useWindowScroll;
2404
+ if (Platform.OS !== "web" || useWindowScrollResolved || dataLength < WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH) {
2405
+ return;
2406
+ }
2407
+ const warnIfUnboundedOuterSize = () => {
2408
+ const readyToRender = peek$(ctx, "readyToRender");
2409
+ const numContainers = peek$(ctx, "numContainers") || 0;
2410
+ const totalSize = peek$(ctx, "totalSize") || 0;
2411
+ const scrollLength = ctx.state.scrollLength || 0;
2412
+ if (!readyToRender || totalSize <= 0 || scrollLength <= 0) {
2413
+ return;
2414
+ }
2415
+ const rendersAlmostEverything = numContainers >= Math.ceil(dataLength * WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO);
2416
+ const viewportMatchesContent = scrollLength >= totalSize * WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO;
2417
+ if (rendersAlmostEverything && viewportMatchesContent) {
2418
+ warnDevOnce(
2419
+ "webUnboundedOuterSize",
2420
+ "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."
2421
+ );
2234
2422
  }
2235
2423
  };
2236
- checkHasScrolled();
2237
- }, initialDelay);
2424
+ warnIfUnboundedOuterSize();
2425
+ const unsubscribe = [
2426
+ listen$(ctx, "numContainers", warnIfUnboundedOuterSize),
2427
+ listen$(ctx, "readyToRender", warnIfUnboundedOuterSize),
2428
+ listen$(ctx, "totalSize", warnIfUnboundedOuterSize)
2429
+ ];
2430
+ return () => {
2431
+ for (const unsub of unsubscribe) {
2432
+ unsub();
2433
+ }
2434
+ };
2435
+ }, [ctx]);
2238
2436
  }
2239
-
2240
- // src/core/doScrollTo.native.ts
2241
- function doScrollTo(ctx, params) {
2242
- const state = ctx.state;
2243
- const { animated, horizontal, isInitialScroll, offset } = params;
2244
- const isAnimated = !!animated;
2245
- const { refScroller } = state;
2246
- const scroller = refScroller.current;
2247
- if (!scroller) {
2248
- return;
2249
- }
2250
- const isHorizontal = !!horizontal;
2251
- const contentSize = isHorizontal ? getContentSize(ctx) : void 0;
2252
- const nativeOffset = toNativeHorizontalOffset(state, offset, contentSize);
2253
- scroller.scrollTo({
2254
- animated: isAnimated,
2255
- x: isHorizontal ? nativeOffset : 0,
2256
- y: isHorizontal ? 0 : offset
2257
- });
2258
- if (isInitialScroll) {
2259
- initialScrollCompletion.markInitialScrollNativeDispatch(state);
2260
- }
2261
- if (!isAnimated) {
2262
- state.scroll = offset;
2263
- checkFinishedScrollFallback(ctx);
2264
- }
2437
+ function useDevChecksNoop(_props) {
2265
2438
  }
2439
+ var useDevChecks = IS_DEV ? useDevChecksImpl : useDevChecksNoop;
2266
2440
 
2267
2441
  // src/core/doMaintainScrollAtEnd.ts
2268
2442
  function doMaintainScrollAtEnd(ctx) {
@@ -2286,9 +2460,12 @@ function doMaintainScrollAtEnd(ctx) {
2286
2460
  state.scroll = 0;
2287
2461
  }
2288
2462
  if (!state.maintainingScrollAtEnd) {
2289
- state.maintainingScrollAtEnd = true;
2463
+ const pendingState = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
2464
+ const activeState = maintainScrollAtEnd.animated ? "animated" : "instant";
2465
+ state.maintainingScrollAtEnd = pendingState;
2290
2466
  requestAnimationFrame(() => {
2291
2467
  if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
2468
+ state.maintainingScrollAtEnd = activeState;
2292
2469
  const scroller = refScroller.current;
2293
2470
  if (state.props.horizontal && isHorizontalRTL(state)) {
2294
2471
  const currentContentSize = getContentSize(ctx);
@@ -2306,12 +2483,14 @@ function doMaintainScrollAtEnd(ctx) {
2306
2483
  }
2307
2484
  setTimeout(
2308
2485
  () => {
2309
- state.maintainingScrollAtEnd = false;
2486
+ if (state.maintainingScrollAtEnd === activeState) {
2487
+ state.maintainingScrollAtEnd = void 0;
2488
+ }
2310
2489
  },
2311
2490
  maintainScrollAtEnd.animated ? 500 : 0
2312
2491
  );
2313
- } else {
2314
- state.maintainingScrollAtEnd = false;
2492
+ } else if (state.maintainingScrollAtEnd === pendingState) {
2493
+ state.maintainingScrollAtEnd = void 0;
2315
2494
  }
2316
2495
  });
2317
2496
  }
@@ -2320,58 +2499,6 @@ function doMaintainScrollAtEnd(ctx) {
2320
2499
  return false;
2321
2500
  }
2322
2501
 
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
- }
2336
- }
2337
- };
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);
2367
- }
2368
- } else {
2369
- state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
2370
- requestAnimationFrame(doit);
2371
- }
2372
- }
2373
- }
2374
-
2375
2502
  // src/core/mvcp.ts
2376
2503
  var MVCP_POSITION_EPSILON = 0.1;
2377
2504
  var MVCP_ANCHOR_LOCK_TTL_MS = 300;
@@ -2520,6 +2647,10 @@ function prepareMVCP(ctx, dataChanged) {
2520
2647
  const now = Date.now();
2521
2648
  const enableMVCPAnchorLock = isWeb && (!!dataChanged || !!state.mvcpAnchorLock);
2522
2649
  const scrollingTo = state.scrollingTo;
2650
+ if (isWeb && dataChanged && state.pendingScrollToEnd && scrollingTo === void 0) {
2651
+ state.mvcpAnchorLock = void 0;
2652
+ return void 0;
2653
+ }
2523
2654
  const anchorLock = isWeb ? resolveAnchorLock(state, enableMVCPAnchorLock, mvcpData, now) : void 0;
2524
2655
  let prevPosition;
2525
2656
  let targetId;
@@ -2655,7 +2786,7 @@ function prepareMVCP(ctx, dataChanged) {
2655
2786
  return;
2656
2787
  }
2657
2788
  if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
2658
- const shouldSkipAdjustForMaintainedEnd = state.maintainingScrollAtEnd && peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
2789
+ const shouldSkipAdjustForMaintainedEnd = (state.maintainingScrollAtEnd === "pending-animated" || state.maintainingScrollAtEnd === "animated") && peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
2659
2790
  if (!shouldSkipAdjustForMaintainedEnd) {
2660
2791
  requestAdjust(ctx, positionDiff, dataChanged && mvcpData);
2661
2792
  }
@@ -2669,6 +2800,45 @@ var flushSync = (fn) => {
2669
2800
  fn();
2670
2801
  };
2671
2802
 
2803
+ // src/utils/getScrollVelocity.ts
2804
+ var getScrollVelocity = (state) => {
2805
+ const { scrollHistory } = state;
2806
+ const newestIndex = scrollHistory.length - 1;
2807
+ if (newestIndex < 1) {
2808
+ return 0;
2809
+ }
2810
+ const newest = scrollHistory[newestIndex];
2811
+ const now = Date.now();
2812
+ let direction = 0;
2813
+ for (let i = newestIndex; i > 0; i--) {
2814
+ const delta = scrollHistory[i].scroll - scrollHistory[i - 1].scroll;
2815
+ if (delta !== 0) {
2816
+ direction = Math.sign(delta);
2817
+ break;
2818
+ }
2819
+ }
2820
+ if (direction === 0) {
2821
+ return 0;
2822
+ }
2823
+ let oldest = newest;
2824
+ for (let i = newestIndex - 1; i >= 0; i--) {
2825
+ const current = scrollHistory[i];
2826
+ const next = scrollHistory[i + 1];
2827
+ const delta = next.scroll - current.scroll;
2828
+ const deltaSign = Math.sign(delta);
2829
+ if (deltaSign !== 0 && deltaSign !== direction) {
2830
+ break;
2831
+ }
2832
+ if (now - current.time > 1e3) {
2833
+ break;
2834
+ }
2835
+ oldest = current;
2836
+ }
2837
+ const scrollDiff = newest.scroll - oldest.scroll;
2838
+ const timeDiff = newest.time - oldest.time;
2839
+ return timeDiff > 0 ? scrollDiff / timeDiff : 0;
2840
+ };
2841
+
2672
2842
  // src/core/updateScroll.ts
2673
2843
  function updateScroll(ctx, newScroll, forceUpdate, options) {
2674
2844
  var _a3;
@@ -2694,6 +2864,8 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2694
2864
  if (scrollHistory.length > 5) {
2695
2865
  scrollHistory.shift();
2696
2866
  }
2867
+ const scrollVelocity = getScrollVelocity(state);
2868
+ updateAdaptiveRender(ctx, scrollVelocity);
2697
2869
  if (ignoreScrollFromMVCP && !scrollingTo) {
2698
2870
  const { lt, gt } = ignoreScrollFromMVCP;
2699
2871
  if (lt && newScroll < lt || gt && newScroll > gt) {
@@ -2717,7 +2889,7 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2717
2889
  state.lastScrollDelta = scrollDelta;
2718
2890
  const runCalculateItems = () => {
2719
2891
  var _a4;
2720
- (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { doMVCP: scrollingTo !== void 0 });
2892
+ (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { doMVCP: scrollingTo !== void 0, scrollVelocity });
2721
2893
  checkThresholds(ctx);
2722
2894
  };
2723
2895
  if (scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength && !state.pendingNativeMVCPAdjust) {
@@ -3652,9 +3824,32 @@ function initializeInitialScrollOnMount(ctx, options) {
3652
3824
  }
3653
3825
  function handleInitialScrollDataChange(ctx, options) {
3654
3826
  var _a3, _b, _c;
3655
- const { dataLength, didDataChange, initialScrollAtEnd, stylePaddingBottom, useBootstrapInitialScroll } = options;
3827
+ const {
3828
+ dataLength,
3829
+ didDataChange,
3830
+ initialScrollAtEnd,
3831
+ latestInitialScroll,
3832
+ latestInitialScrollSessionKind,
3833
+ stylePaddingBottom,
3834
+ useBootstrapInitialScroll
3835
+ } = options;
3656
3836
  const state = ctx.state;
3657
3837
  const previousDataLength = (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.previousDataLength) != null ? _b : 0;
3838
+ const isFirstNonEmptyData = !state.hasHadNonEmptyData && dataLength > 0;
3839
+ if (dataLength > 0) {
3840
+ state.hasHadNonEmptyData = true;
3841
+ }
3842
+ if (isFirstNonEmptyData) {
3843
+ if (latestInitialScroll) {
3844
+ setInitialScrollTarget(state, latestInitialScroll);
3845
+ setInitialScrollSession(state, {
3846
+ kind: latestInitialScrollSessionKind,
3847
+ previousDataLength
3848
+ });
3849
+ } else {
3850
+ clearPreservedInitialScrollTarget(state);
3851
+ }
3852
+ }
3658
3853
  if (state.initialScrollSession) {
3659
3854
  state.initialScrollSession.previousDataLength = dataLength;
3660
3855
  }
@@ -3878,45 +4073,6 @@ function updateTotalSize(ctx) {
3878
4073
  }
3879
4074
  }
3880
4075
 
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
4076
  // src/utils/updateSnapToOffsets.ts
3921
4077
  function updateSnapToOffsets(ctx) {
3922
4078
  const state = ctx.state;
@@ -4652,7 +4808,7 @@ function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, sc
4652
4808
  function calculateItemsInView(ctx, params = {}) {
4653
4809
  const state = ctx.state;
4654
4810
  batchedUpdates(() => {
4655
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
4811
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t;
4656
4812
  const {
4657
4813
  columns,
4658
4814
  containerItemKeys,
@@ -4661,14 +4817,7 @@ function calculateItemsInView(ctx, params = {}) {
4661
4817
  indexByKey,
4662
4818
  minIndexSizeChanged,
4663
4819
  positions,
4664
- props: {
4665
- alwaysRenderIndicesArr,
4666
- alwaysRenderIndicesSet,
4667
- drawDistance,
4668
- getItemType,
4669
- keyExtractor,
4670
- onStickyHeaderChange
4671
- },
4820
+ props: { alwaysRenderIndicesArr, alwaysRenderIndicesSet, getItemType, keyExtractor, onStickyHeaderChange },
4672
4821
  scrollForNextCalculateItemsInView,
4673
4822
  scrollLength,
4674
4823
  sizes,
@@ -4680,6 +4829,7 @@ function calculateItemsInView(ctx, params = {}) {
4680
4829
  const stickyHeaderIndicesSet = state.props.stickyHeaderIndicesSet || /* @__PURE__ */ new Set();
4681
4830
  const alwaysRenderArr = alwaysRenderIndicesArr || [];
4682
4831
  const alwaysRenderSet = alwaysRenderIndicesSet || /* @__PURE__ */ new Set();
4832
+ const drawDistance = getEffectiveDrawDistance(ctx);
4683
4833
  const { dataChanged, doMVCP, forceFullItemPositions } = params;
4684
4834
  const bootstrapInitialScrollState = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" ? state.initialScrollSession.bootstrap : void 0;
4685
4835
  const suppressInitialScrollSideEffects = !!bootstrapInitialScrollState;
@@ -4690,10 +4840,10 @@ function calculateItemsInView(ctx, params = {}) {
4690
4840
  let totalSize = getContentSize(ctx);
4691
4841
  const topPad = peek$(ctx, "stylePaddingTop") + peek$(ctx, "alignItemsAtEndPadding") + peek$(ctx, "headerSize");
4692
4842
  const numColumns = peek$(ctx, "numColumns");
4693
- const speed = getScrollVelocity(state);
4843
+ const speed = (_b = params.scrollVelocity) != null ? _b : getScrollVelocity(state);
4694
4844
  const scrollExtra = 0;
4695
4845
  const { initialScroll, queuedInitialLayout } = state;
4696
- const scrollState = suppressInitialScrollSideEffects ? (_b = bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.scroll) != null ? _b : state.scroll : !queuedInitialLayout && hasActiveInitialScroll(state) && initialScroll ? (
4846
+ const scrollState = suppressInitialScrollSideEffects ? (_c = bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.scroll) != null ? _c : state.scroll : !queuedInitialLayout && hasActiveInitialScroll(state) && initialScroll ? (
4697
4847
  // Before the initial layout settles, keep viewport math anchored to the
4698
4848
  // current initial-scroll target instead of transient native adjustments.
4699
4849
  resolveInitialScrollOffset(ctx, initialScroll)
@@ -4717,19 +4867,25 @@ function calculateItemsInView(ctx, params = {}) {
4717
4867
  };
4718
4868
  updateScroll2(scrollState);
4719
4869
  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 });
4870
+ const resolveStickyState = () => {
4871
+ const currentStickyIdx = stickyHeaderIndicesArr.length > 0 ? findCurrentStickyIndex(stickyHeaderIndicesArr, scroll, state) : -1;
4872
+ const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyHeaderIndicesArr[currentStickyIdx] : -1;
4873
+ const stickyIndexDidChange = previousStickyIndex !== nextActiveStickyIndex;
4874
+ if (currentStickyIdx >= 0 || previousStickyIndex >= 0) {
4875
+ set$(ctx, "activeStickyIndex", nextActiveStickyIndex);
4731
4876
  }
4732
- } : void 0;
4877
+ const shouldNotifyStickyHeaderChange = !!onStickyHeaderChange && stickyHeaderIndicesArr.length > 0 && stickyIndexDidChange;
4878
+ return {
4879
+ currentStickyIdx,
4880
+ finishCalculateItemsInView: shouldNotifyStickyHeaderChange ? () => {
4881
+ const item = data[nextActiveStickyIndex];
4882
+ if (item !== void 0) {
4883
+ onStickyHeaderChange == null ? void 0 : onStickyHeaderChange({ index: nextActiveStickyIndex, item });
4884
+ }
4885
+ } : void 0
4886
+ };
4887
+ };
4888
+ let stickyState = dataChanged ? void 0 : resolveStickyState();
4733
4889
  let scrollBufferTop = drawDistance;
4734
4890
  let scrollBufferBottom = drawDistance;
4735
4891
  if (speed > 0 || speed === 0 && scroll < Math.max(50, drawDistance)) {
@@ -4762,7 +4918,7 @@ function calculateItemsInView(ctx, params = {}) {
4762
4918
  scrollBottom
4763
4919
  );
4764
4920
  }
4765
- finishCalculateItemsInView == null ? void 0 : finishCalculateItemsInView();
4921
+ (_d = stickyState == null ? void 0 : stickyState.finishCalculateItemsInView) == null ? void 0 : _d.call(stickyState);
4766
4922
  return;
4767
4923
  }
4768
4924
  }
@@ -4771,7 +4927,7 @@ function calculateItemsInView(ctx, params = {}) {
4771
4927
  if (dataChanged) {
4772
4928
  resetLayoutCachesForDataChange(state);
4773
4929
  }
4774
- const startIndex = forceFullItemPositions || dataChanged ? 0 : (_c = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _c : 0;
4930
+ const startIndex = forceFullItemPositions || dataChanged ? 0 : (_e = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _e : 0;
4775
4931
  const optimizeForVisibleWindow = !forceFullItemPositions && !dataChanged && numColumns > 1 && minIndexSizeChanged !== void 0;
4776
4932
  updateItemPositions(ctx, dataChanged, {
4777
4933
  doMVCP,
@@ -4797,21 +4953,24 @@ function calculateItemsInView(ctx, params = {}) {
4797
4953
  }
4798
4954
  }
4799
4955
  const scrollBeforeMVCP = state.scroll;
4800
- const scrollAdjustPendingBeforeMVCP = (_d = peek$(ctx, "scrollAdjustPending")) != null ? _d : 0;
4956
+ const scrollAdjustPendingBeforeMVCP = (_f = peek$(ctx, "scrollAdjustPending")) != null ? _f : 0;
4801
4957
  checkMVCP == null ? void 0 : checkMVCP();
4802
- const didMVCPAdjustScroll = !!checkMVCP && (state.scroll !== scrollBeforeMVCP || ((_e = peek$(ctx, "scrollAdjustPending")) != null ? _e : 0) !== scrollAdjustPendingBeforeMVCP);
4958
+ const didMVCPAdjustScroll = !!checkMVCP && (state.scroll !== scrollBeforeMVCP || ((_g = peek$(ctx, "scrollAdjustPending")) != null ? _g : 0) !== scrollAdjustPendingBeforeMVCP);
4803
4959
  if (didMVCPAdjustScroll && initialScroll) {
4804
4960
  updateScroll2(state.scroll);
4805
4961
  updateScrollRange();
4806
4962
  }
4963
+ if (dataChanged) {
4964
+ stickyState = resolveStickyState();
4965
+ }
4807
4966
  let startBuffered = null;
4808
4967
  let startBufferedId = null;
4809
4968
  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;
4969
+ let loopStart = (_h = suppressInitialScrollSideEffects ? bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.targetIndexSeed : void 0) != null ? _h : !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
4811
4970
  for (let i = loopStart; i >= 0; i--) {
4812
- const id = (_g = idCache[i]) != null ? _g : getId(state, i);
4971
+ const id = (_i = idCache[i]) != null ? _i : getId(state, i);
4813
4972
  const top = positions[i];
4814
- const size = (_h = sizes.get(id)) != null ? _h : getItemSize(ctx, id, i, data[i]);
4973
+ const size = (_j = sizes.get(id)) != null ? _j : getItemSize(ctx, id, i, data[i]);
4815
4974
  const bottom = top + size;
4816
4975
  if (bottom > scrollTopBuffered) {
4817
4976
  loopStart = i;
@@ -4846,8 +5005,8 @@ function calculateItemsInView(ctx, params = {}) {
4846
5005
  };
4847
5006
  const dataLength = data.length;
4848
5007
  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]);
5008
+ const id = (_k = idCache[i]) != null ? _k : getId(state, i);
5009
+ const size = (_l = sizes.get(id)) != null ? _l : getItemSize(ctx, id, i, data[i]);
4851
5010
  const top = positions[i];
4852
5011
  if (!foundEnd) {
4853
5012
  trackVisibleRange(visibleRange, i, top, size, scroll, scrollBottom);
@@ -4903,7 +5062,7 @@ function calculateItemsInView(ctx, params = {}) {
4903
5062
  const needNewContainers = [];
4904
5063
  const needNewContainersSet = /* @__PURE__ */ new Set();
4905
5064
  for (let i = startBuffered; i <= endBuffered; i++) {
4906
- const id = (_k = idCache[i]) != null ? _k : getId(state, i);
5065
+ const id = (_m = idCache[i]) != null ? _m : getId(state, i);
4907
5066
  if (!containerItemKeys.has(id)) {
4908
5067
  needNewContainersSet.add(i);
4909
5068
  needNewContainers.push(i);
@@ -4912,7 +5071,7 @@ function calculateItemsInView(ctx, params = {}) {
4912
5071
  if (alwaysRenderArr.length > 0) {
4913
5072
  for (const index of alwaysRenderArr) {
4914
5073
  if (index < 0 || index >= dataLength) continue;
4915
- const id = (_l = idCache[index]) != null ? _l : getId(state, index);
5074
+ const id = (_n = idCache[index]) != null ? _n : getId(state, index);
4916
5075
  if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
4917
5076
  needNewContainersSet.add(index);
4918
5077
  needNewContainers.push(index);
@@ -4923,7 +5082,7 @@ function calculateItemsInView(ctx, params = {}) {
4923
5082
  handleStickyActivation(
4924
5083
  ctx,
4925
5084
  stickyHeaderIndicesArr,
4926
- currentStickyIdx,
5085
+ (_o = stickyState == null ? void 0 : stickyState.currentStickyIdx) != null ? _o : -1,
4927
5086
  needNewContainers,
4928
5087
  needNewContainersSet,
4929
5088
  startBuffered,
@@ -4949,7 +5108,7 @@ function calculateItemsInView(ctx, params = {}) {
4949
5108
  for (const allocation of availableContainerAllocations) {
4950
5109
  const i = allocation.itemIndex;
4951
5110
  const containerIndex = allocation.containerIndex;
4952
- const id = (_m = idCache[i]) != null ? _m : getId(state, i);
5111
+ const id = (_p = idCache[i]) != null ? _p : getId(state, i);
4953
5112
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
4954
5113
  if (oldKey && oldKey !== id) {
4955
5114
  containerItemKeys.delete(oldKey);
@@ -4960,7 +5119,7 @@ function calculateItemsInView(ctx, params = {}) {
4960
5119
  state.containerItemTypes.set(containerIndex, allocation.itemType);
4961
5120
  }
4962
5121
  containerItemKeys.set(id, containerIndex);
4963
- (_n = state.userScrollAnchorReset) == null ? void 0 : _n.keys.add(id);
5122
+ (_q = state.userScrollAnchorReset) == null ? void 0 : _q.keys.add(id);
4964
5123
  const containerSticky = `containerSticky${containerIndex}`;
4965
5124
  const isSticky = stickyHeaderIndicesSet.has(i);
4966
5125
  const isAlwaysRender = alwaysRenderSet.has(i);
@@ -4991,14 +5150,12 @@ function calculateItemsInView(ctx, params = {}) {
4991
5150
  if (state.userScrollAnchorReset) {
4992
5151
  if (state.userScrollAnchorReset.keys.size === 0) {
4993
5152
  state.userScrollAnchorReset = void 0;
4994
- } else {
4995
- state.userScrollAnchorReset.batchSize = state.userScrollAnchorReset.keys.size;
4996
5153
  }
4997
5154
  }
4998
5155
  if (alwaysRenderArr.length > 0) {
4999
5156
  for (const index of alwaysRenderArr) {
5000
5157
  if (index < 0 || index >= dataLength) continue;
5001
- const id = (_o = idCache[index]) != null ? _o : getId(state, index);
5158
+ const id = (_r = idCache[index]) != null ? _r : getId(state, index);
5002
5159
  const containerIndex = containerItemKeys.get(id);
5003
5160
  if (containerIndex !== void 0) {
5004
5161
  state.stickyContainerPool.add(containerIndex);
@@ -5012,7 +5169,7 @@ function calculateItemsInView(ctx, params = {}) {
5012
5169
  stickyHeaderIndicesArr,
5013
5170
  scroll,
5014
5171
  drawDistance,
5015
- currentStickyIdx,
5172
+ (_s = stickyState == null ? void 0 : stickyState.currentStickyIdx) != null ? _s : -1,
5016
5173
  pendingRemoval,
5017
5174
  alwaysRenderSet
5018
5175
  );
@@ -5073,7 +5230,7 @@ function calculateItemsInView(ctx, params = {}) {
5073
5230
  );
5074
5231
  }
5075
5232
  }
5076
- finishCalculateItemsInView == null ? void 0 : finishCalculateItemsInView();
5233
+ (_t = stickyState == null ? void 0 : stickyState.finishCalculateItemsInView) == null ? void 0 : _t.call(stickyState);
5077
5234
  });
5078
5235
  }
5079
5236
 
@@ -5156,8 +5313,9 @@ function doInitialAllocateContainers(ctx) {
5156
5313
  const state = ctx.state;
5157
5314
  const {
5158
5315
  scrollLength,
5159
- props: { data, drawDistance, getFixedItemSize, getItemType, numColumns, estimatedItemSize }
5316
+ props: { data, getFixedItemSize, getItemType, numColumns, estimatedItemSize }
5160
5317
  } = state;
5318
+ const drawDistance = getEffectiveDrawDistance(ctx);
5161
5319
  const hasContainers = peek$(ctx, "numContainers");
5162
5320
  if (scrollLength > 0 && data.length > 0 && !hasContainers) {
5163
5321
  let averageItemSize;
@@ -5168,12 +5326,12 @@ function doInitialAllocateContainers(ctx) {
5168
5326
  const item = data[i];
5169
5327
  if (item !== void 0) {
5170
5328
  const itemType = (_a3 = getItemType == null ? void 0 : getItemType(item, i)) != null ? _a3 : "";
5171
- totalSize += (_b = getFixedItemSize(item, i, itemType)) != null ? _b : estimatedItemSize;
5329
+ totalSize += ((_b = getFixedItemSize(item, i, itemType)) != null ? _b : estimatedItemSize) + ctx.scrollAxisGap;
5172
5330
  }
5173
5331
  }
5174
5332
  averageItemSize = totalSize / num;
5175
5333
  } else {
5176
- averageItemSize = estimatedItemSize;
5334
+ averageItemSize = estimatedItemSize + ctx.scrollAxisGap;
5177
5335
  }
5178
5336
  const numContainers = Math.max(
5179
5337
  1,
@@ -5228,7 +5386,7 @@ function handleLayout(ctx, layoutParam, setCanRender) {
5228
5386
  if (didChange) {
5229
5387
  state.scrollLength = scrollLength;
5230
5388
  state.otherAxisSize = otherAxisSize;
5231
- updateContentMetrics(ctx);
5389
+ updateContentMetricsState(ctx);
5232
5390
  state.lastBatchingAction = Date.now();
5233
5391
  state.scrollForNextCalculateItemsInView = void 0;
5234
5392
  if (scrollLength > 0) {
@@ -5469,28 +5627,12 @@ function updateContentInsetEndAdjustment(ctx, previousContentInsetEndAdjustment)
5469
5627
 
5470
5628
  // src/core/updateItemSize.ts
5471
5629
  function runOrScheduleMVCPRecalculate(ctx) {
5472
- var _a3, _b;
5630
+ var _a3;
5473
5631
  const state = ctx.state;
5474
5632
  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
- }
5633
+ calculateItemsInView(ctx);
5634
+ if (((_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys.size) === 0) {
5635
+ state.userScrollAnchorReset = void 0;
5494
5636
  }
5495
5637
  } else if (Platform.OS === "web") {
5496
5638
  if (!state.mvcpAnchorLock) {
@@ -5968,6 +6110,9 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
5968
6110
  scrollTo(ctx, params);
5969
6111
  return true;
5970
6112
  }),
6113
+ setItemSize: (itemKey, size) => {
6114
+ updateItemSize(ctx, itemKey, size);
6115
+ },
5971
6116
  setScrollProcessingEnabled: (enabled) => {
5972
6117
  state.scrollProcessingEnabled = enabled;
5973
6118
  },
@@ -6192,7 +6337,7 @@ var LegendList = typedMemo(
6192
6337
  })
6193
6338
  );
6194
6339
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
6195
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i;
6340
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
6196
6341
  const noopOnScroll = React2.useCallback((_event) => {
6197
6342
  }, []);
6198
6343
  if (props.recycleItems === void 0) {
@@ -6223,6 +6368,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6223
6368
  initialScrollAtEnd = false,
6224
6369
  initialScrollIndex: initialScrollIndexProp,
6225
6370
  initialScrollOffset: initialScrollOffsetProp,
6371
+ experimental_adaptiveRender,
6226
6372
  itemsAreEqual,
6227
6373
  keyExtractor: keyExtractorProp,
6228
6374
  ListEmptyComponent,
@@ -6319,6 +6465,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6319
6465
  const [, scheduleImperativeScrollCommit] = React2__namespace.useReducer((value) => value + 1, 0);
6320
6466
  const ctx = useStateContext();
6321
6467
  ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
6468
+ 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;
6469
+ const nextScrollAxisGap = typeof scrollAxisGap === "number" && Number.isFinite(scrollAxisGap) ? scrollAxisGap : 0;
6322
6470
  const refScroller = React2.useRef(null);
6323
6471
  const combinedRef = useCombinedRef(refScroller, refScrollView);
6324
6472
  const keyExtractor = keyExtractorProp != null ? keyExtractorProp : ((_item, index) => index.toString());
@@ -6332,8 +6480,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6332
6480
  anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex,
6333
6481
  alwaysRender == null ? void 0 : alwaysRender.top,
6334
6482
  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(","),
6483
+ (_j = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _j.join(","),
6484
+ (_k = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _k.join(","),
6337
6485
  dataProp,
6338
6486
  dataVersion,
6339
6487
  keyExtractor
@@ -6361,6 +6509,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6361
6509
  endNoBuffer: -1,
6362
6510
  endReachedSnapshot: void 0,
6363
6511
  firstFullyOnScreenIndex: -1,
6512
+ hasHadNonEmptyData: dataProp.length > 0,
6364
6513
  idCache: [],
6365
6514
  idsInView: [],
6366
6515
  indexByKey: /* @__PURE__ */ new Map(),
@@ -6403,6 +6552,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6403
6552
  startReachedSnapshotDataChangeEpoch: void 0,
6404
6553
  stickyContainerPool: /* @__PURE__ */ new Set(),
6405
6554
  stickyContainers: /* @__PURE__ */ new Map(),
6555
+ timeoutAdaptiveRender: void 0,
6406
6556
  timeouts: /* @__PURE__ */ new Set(),
6407
6557
  totalSize: 0,
6408
6558
  viewabilityConfigCallbackPairs: void 0
@@ -6421,11 +6571,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6421
6571
  const state = refState.current;
6422
6572
  const isFirstLocal = state.isFirst;
6423
6573
  const previousNumColumnsProp = state.props.numColumns;
6424
- state.didColumnsChange = numColumnsProp !== previousNumColumnsProp;
6574
+ const didScrollAxisGapChange = !isFirstLocal && ctx.scrollAxisGap !== nextScrollAxisGap;
6575
+ ctx.scrollAxisGap = nextScrollAxisGap;
6576
+ state.didColumnsChange = numColumnsProp !== previousNumColumnsProp || didScrollAxisGapChange;
6425
6577
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
6426
6578
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
6427
6579
  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) {
6580
+ if (didDataChangeLocal && !initialScrollAtEnd && state.didFinishInitialScroll && ((_l = state.initialScroll) == null ? void 0 : _l.viewPosition) === 1 && state.props.data.length > 0) {
6429
6581
  clearPreservedInitialScrollTarget(state);
6430
6582
  }
6431
6583
  if (didDataChangeLocal) {
@@ -6437,8 +6589,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6437
6589
  const throttledOnScroll = useThrottledOnScroll(onScrollProp != null ? onScrollProp : noopOnScroll, scrollEventThrottle != null ? scrollEventThrottle : 0);
6438
6590
  const throttleScrollFn = scrollEventThrottle && onScrollProp ? throttledOnScroll : onScrollProp;
6439
6591
  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);
6592
+ const didAnchoredEndSpaceAnchorIndexChange = !isFirstLocal && !didDataChangeLocal && ((_m = state.props.anchoredEndSpace) == null ? void 0 : _m.anchorIndex) !== (anchoredEndSpaceResolved == null ? void 0 : anchoredEndSpaceResolved.anchorIndex);
6441
6593
  state.props = {
6594
+ adaptiveRender: experimental_adaptiveRender,
6442
6595
  alignItemsAtEnd,
6443
6596
  alignItemsAtEndPaddingEnabled: useAlignItemsAtEndPadding,
6444
6597
  alwaysRender,
@@ -6446,6 +6599,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6446
6599
  alwaysRenderIndicesSet: alwaysRenderIndices.set,
6447
6600
  anchoredEndSpace: anchoredEndSpaceResolved,
6448
6601
  animatedProps: animatedPropsInternal,
6602
+ contentContainerAlignItems: contentContainerStyle.alignItems,
6449
6603
  contentInset,
6450
6604
  contentInsetEndAdjustment: contentInsetEndAdjustmentResolved,
6451
6605
  data: dataProp,
@@ -6498,7 +6652,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6498
6652
  const prevPaddingTop = peek$(ctx, "stylePaddingTop");
6499
6653
  setPaddingTop(ctx, { stylePaddingTop: stylePaddingTopState });
6500
6654
  refState.current.props.stylePaddingBottom = stylePaddingBottomState;
6501
- updateContentMetrics(ctx);
6655
+ updateContentMetricsState(ctx);
6502
6656
  let paddingDiff = stylePaddingTopState - prevPaddingTop;
6503
6657
  if (shouldAdjustPadding && maintainVisibleContentPositionConfig.size && paddingDiff && prevPaddingTop !== void 0 && Platform.OS === "ios") {
6504
6658
  if (state.scroll < 0) {
@@ -6535,7 +6689,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6535
6689
  useBootstrapInitialScroll: usesBootstrapInitialScroll
6536
6690
  });
6537
6691
  }, []);
6538
- if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
6692
+ if (isFirstLocal || didDataChangeLocal || state.didColumnsChange) {
6539
6693
  refState.current.lastBatchingAction = Date.now();
6540
6694
  if (!keyExtractorProp && !isFirstLocal && didDataChangeLocal) {
6541
6695
  refState.current.sizes.clear();
@@ -6552,6 +6706,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6552
6706
  dataLength: dataProp.length,
6553
6707
  didDataChange: didDataChangeLocal,
6554
6708
  initialScrollAtEnd,
6709
+ latestInitialScroll: initialScrollProp,
6710
+ latestInitialScrollSessionKind: initialScrollUsesOffsetOnly ? "offset" : "bootstrap",
6555
6711
  stylePaddingBottom: stylePaddingBottomState,
6556
6712
  useBootstrapInitialScroll: usesBootstrapInitialScroll
6557
6713
  });
@@ -6626,6 +6782,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6626
6782
  dataVersion,
6627
6783
  memoizedLastItemKeys.join(","),
6628
6784
  numColumnsProp,
6785
+ nextScrollAxisGap,
6629
6786
  stylePaddingBottomState,
6630
6787
  stylePaddingTopState,
6631
6788
  useAlignItemsAtEndPadding
@@ -6648,7 +6805,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6648
6805
  state.didColumnsChange = false;
6649
6806
  state.didDataChange = false;
6650
6807
  state.isFirst = false;
6651
- }, [dataProp, dataVersion, numColumnsProp]);
6808
+ }, [dataProp, dataVersion, numColumnsProp, nextScrollAxisGap]);
6652
6809
  React2.useLayoutEffect(() => {
6653
6810
  var _a4;
6654
6811
  set$(ctx, "extraData", extraData);
@@ -6699,6 +6856,14 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6699
6856
  }
6700
6857
  });
6701
6858
  React2.useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx, scheduleImperativeScrollCommit), []);
6859
+ React2.useEffect(() => {
6860
+ return () => {
6861
+ for (const timeout of state.timeouts) {
6862
+ clearTimeout(timeout);
6863
+ }
6864
+ state.timeouts.clear();
6865
+ };
6866
+ }, [state]);
6702
6867
  React2.useLayoutEffect(() => {
6703
6868
  var _a4;
6704
6869
  (_a4 = state.runPendingScrollToEnd) == null ? void 0 : _a4.call(state);
@@ -6746,7 +6911,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6746
6911
  onScroll: onScrollHandler,
6747
6912
  recycleItems,
6748
6913
  refreshControl: refreshControlElement ? stylePaddingTopState > 0 ? React2__namespace.cloneElement(refreshControlElement, {
6749
- progressViewOffset: ((_h = refreshControlElement.props.progressViewOffset) != null ? _h : 0) + stylePaddingTopState
6914
+ progressViewOffset: ((_n = refreshControlElement.props.progressViewOffset) != null ? _n : 0) + stylePaddingTopState
6750
6915
  }) : refreshControlElement : onRefresh && /* @__PURE__ */ React2__namespace.createElement(
6751
6916
  ReactNative.RefreshControl,
6752
6917
  {
@@ -6757,7 +6922,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6757
6922
  ),
6758
6923
  refScrollView: combinedRef,
6759
6924
  renderScrollComponent,
6760
- scrollAdjustHandler: (_i = refState.current) == null ? void 0 : _i.scrollAdjustHandler,
6925
+ scrollAdjustHandler: (_o = refState.current) == null ? void 0 : _o.scrollAdjustHandler,
6761
6926
  scrollEventThrottle: 0,
6762
6927
  snapToIndices,
6763
6928
  stickyHeaderIndices,
@@ -6791,6 +6956,8 @@ var internal2 = internal;
6791
6956
 
6792
6957
  exports.LegendList = LegendList3;
6793
6958
  exports.internal = internal2;
6959
+ exports.useAdaptiveRender = useAdaptiveRender;
6960
+ exports.useAdaptiveRenderChange = useAdaptiveRenderChange;
6794
6961
  exports.useIsLastItem = useIsLastItem;
6795
6962
  exports.useListScrollSize = useListScrollSize;
6796
6963
  exports.useRecyclingEffect = useRecyclingEffect;