@legendapp/list 3.0.6 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/react-native.mjs CHANGED
@@ -131,6 +131,7 @@ function StateProvider({ children }) {
131
131
  mapViewabilityConfigStates: /* @__PURE__ */ new Map(),
132
132
  mapViewabilityValues: /* @__PURE__ */ new Map(),
133
133
  positionListeners: /* @__PURE__ */ new Map(),
134
+ scrollAxisGap: 0,
134
135
  state: void 0,
135
136
  values: /* @__PURE__ */ new Map([
136
137
  ["alignItemsAtEndPadding", 0],
@@ -143,6 +144,7 @@ function StateProvider({ children }) {
143
144
  ["isNearEnd", false],
144
145
  ["isNearStart", false],
145
146
  ["isWithinMaintainScrollAtEndThreshold", false],
147
+ ["adaptiveRender", "light"],
146
148
  ["totalSize", 0],
147
149
  ["scrollAdjustPending", 0]
148
150
  ]),
@@ -588,6 +590,24 @@ var ContextContainer = createContext(null);
588
590
  function useContextContainer() {
589
591
  return useContext(ContextContainer);
590
592
  }
593
+ function useAdaptiveRender() {
594
+ const [mode] = useArr$(["adaptiveRender"]);
595
+ return mode;
596
+ }
597
+ function useAdaptiveRenderChange(callback) {
598
+ const ctx = useStateContext();
599
+ const callbackRef = useRef(callback);
600
+ callbackRef.current = callback;
601
+ useLayoutEffect(() => {
602
+ let mode = peek$(ctx, "adaptiveRender");
603
+ return listen$(ctx, "adaptiveRender", (nextMode) => {
604
+ if (mode !== nextMode) {
605
+ mode = nextMode;
606
+ callbackRef.current(nextMode);
607
+ }
608
+ });
609
+ }, [ctx]);
610
+ }
591
611
  function useViewability(callback, configId) {
592
612
  const ctx = useStateContext();
593
613
  const containerContext = useContextContainer();
@@ -804,6 +824,7 @@ function isInMVCPActiveMode(state) {
804
824
  // src/components/Container.tsx
805
825
  function getContainerPositionStyle({
806
826
  columnWrapperStyle,
827
+ contentContainerAlignItems,
807
828
  horizontal,
808
829
  hasItemSeparator,
809
830
  isHorizontalRTLList,
@@ -829,13 +850,14 @@ function getContainerPositionStyle({
829
850
  }
830
851
  }
831
852
  return horizontal ? {
853
+ bottom: contentContainerAlignItems === "flex-end" && numColumns === 1 ? 0 : void 0,
832
854
  boxSizing: paddingStyles ? "border-box" : void 0,
833
855
  direction: isHorizontalRTLList && Platform.OS === "web" ? "ltr" : void 0,
834
856
  flexDirection: hasItemSeparator ? "row" : void 0,
835
857
  height: otherAxisSize,
836
858
  left: 0,
837
859
  position: "absolute",
838
- top: otherAxisPos,
860
+ top: contentContainerAlignItems === "flex-end" && numColumns === 1 ? void 0 : otherAxisPos,
839
861
  ...paddingStyles || {}
840
862
  } : {
841
863
  boxSizing: paddingStyles ? "border-box" : void 0,
@@ -889,6 +911,7 @@ var Container = typedMemo(function Container2({
889
911
  const style = useMemo(
890
912
  () => getContainerPositionStyle({
891
913
  columnWrapperStyle,
914
+ contentContainerAlignItems: ctx.state.props.contentContainerAlignItems,
892
915
  hasItemSeparator: !!ItemSeparatorComponent,
893
916
  horizontal,
894
917
  isHorizontalRTLList,
@@ -902,6 +925,7 @@ var Container = typedMemo(function Container2({
902
925
  otherAxisPos,
903
926
  otherAxisSize,
904
927
  columnWrapperStyle,
928
+ ctx.state.props.contentContainerAlignItems,
905
929
  numColumns,
906
930
  ItemSeparatorComponent
907
931
  ]
@@ -1172,7 +1196,91 @@ function WebAnchoredEndSpace({ horizontal }) {
1172
1196
  return /* @__PURE__ */ React2.createElement("div", { style }, null);
1173
1197
  }
1174
1198
 
1175
- // src/core/updateContentMetrics.ts
1199
+ // src/core/doMaintainScrollAtEnd.ts
1200
+ function doMaintainScrollAtEnd(ctx) {
1201
+ const state = ctx.state;
1202
+ const {
1203
+ didContainersLayout,
1204
+ pendingNativeMVCPAdjust,
1205
+ refScroller,
1206
+ props: { maintainScrollAtEnd }
1207
+ } = state;
1208
+ const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
1209
+ const shouldMaintainScrollAtEnd = !!(isWithinMaintainScrollAtEndThreshold && maintainScrollAtEnd && didContainersLayout);
1210
+ if (pendingNativeMVCPAdjust) {
1211
+ state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
1212
+ return false;
1213
+ }
1214
+ state.pendingMaintainScrollAtEnd = false;
1215
+ if (shouldMaintainScrollAtEnd) {
1216
+ const contentSize = getContentSize(ctx);
1217
+ if (contentSize < state.scrollLength) {
1218
+ state.scroll = 0;
1219
+ }
1220
+ if (!state.maintainingScrollAtEnd) {
1221
+ const pendingState = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
1222
+ const activeState = maintainScrollAtEnd.animated ? "animated" : "instant";
1223
+ state.maintainingScrollAtEnd = pendingState;
1224
+ requestAnimationFrame(() => {
1225
+ if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
1226
+ state.maintainingScrollAtEnd = activeState;
1227
+ const scroller = refScroller.current;
1228
+ if (state.props.horizontal && isHorizontalRTL(state)) {
1229
+ const currentContentSize = getContentSize(ctx);
1230
+ const logicalEndOffset = getLogicalHorizontalMaxOffset(state, currentContentSize);
1231
+ const nativeOffset = toNativeHorizontalOffset(state, logicalEndOffset, currentContentSize);
1232
+ scroller == null ? void 0 : scroller.scrollTo({
1233
+ animated: maintainScrollAtEnd.animated,
1234
+ x: nativeOffset,
1235
+ y: 0
1236
+ });
1237
+ } else {
1238
+ scroller == null ? void 0 : scroller.scrollToEnd({
1239
+ animated: maintainScrollAtEnd.animated
1240
+ });
1241
+ }
1242
+ setTimeout(
1243
+ () => {
1244
+ if (state.maintainingScrollAtEnd === activeState) {
1245
+ state.maintainingScrollAtEnd = void 0;
1246
+ }
1247
+ },
1248
+ maintainScrollAtEnd.animated ? 500 : 0
1249
+ );
1250
+ } else if (state.maintainingScrollAtEnd === pendingState) {
1251
+ state.maintainingScrollAtEnd = void 0;
1252
+ }
1253
+ });
1254
+ }
1255
+ return true;
1256
+ }
1257
+ return false;
1258
+ }
1259
+
1260
+ // src/core/calculateOffsetForIndex.ts
1261
+ function calculateOffsetForIndex(ctx, index) {
1262
+ const state = ctx.state;
1263
+ return index !== void 0 ? state.positions[index] || 0 : 0;
1264
+ }
1265
+
1266
+ // src/core/getTopOffsetAdjustment.ts
1267
+ function getTopOffsetAdjustment(ctx) {
1268
+ return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "alignItemsAtEndPadding") || 0) + (peek$(ctx, "headerSize") || 0);
1269
+ }
1270
+
1271
+ // src/utils/getId.ts
1272
+ function getId(state, index) {
1273
+ const { data, keyExtractor } = state.props;
1274
+ if (!data) {
1275
+ return "";
1276
+ }
1277
+ const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
1278
+ const id = ret;
1279
+ state.idCache[index] = id;
1280
+ return id;
1281
+ }
1282
+
1283
+ // src/core/updateContentMetricsState.ts
1176
1284
  function getRawContentLength(ctx) {
1177
1285
  var _a3, _b, _c;
1178
1286
  const { state, values } = ctx;
@@ -1183,240 +1291,187 @@ function getAlignItemsAtEndPadding(ctx) {
1183
1291
  const shouldPad = !!state.props.alignItemsAtEndPaddingEnabled && !state.props.horizontal && state.props.data.length > 0 && state.scrollLength > 0;
1184
1292
  return shouldPad ? Math.max(0, state.scrollLength - getRawContentLength(ctx) - getContentInsetEnd(ctx)) : 0;
1185
1293
  }
1186
- function updateContentMetrics(ctx) {
1294
+ function updateContentMetricsState(ctx) {
1295
+ const previousPadding = peek$(ctx, "alignItemsAtEndPadding") || 0;
1187
1296
  const nextPadding = getAlignItemsAtEndPadding(ctx);
1188
- if (peek$(ctx, "alignItemsAtEndPadding") !== nextPadding) {
1297
+ if (previousPadding !== nextPadding) {
1189
1298
  set$(ctx, "alignItemsAtEndPadding", nextPadding);
1190
1299
  }
1191
1300
  }
1192
- function setContentLengthSignal(ctx, signalName, size) {
1193
- if (peek$(ctx, signalName) !== size) {
1194
- set$(ctx, signalName, size);
1195
- updateContentMetrics(ctx);
1301
+
1302
+ // src/core/addTotalSize.ts
1303
+ function addTotalSize(ctx, key, add, notifyTotalSize = true) {
1304
+ const state = ctx.state;
1305
+ const prevTotalSize = state.totalSize;
1306
+ let totalSize = state.totalSize;
1307
+ if (key === null) {
1308
+ totalSize = add;
1309
+ if (state.timeoutSetPaddingTop) {
1310
+ clearTimeout(state.timeoutSetPaddingTop);
1311
+ state.timeoutSetPaddingTop = void 0;
1312
+ }
1313
+ } else {
1314
+ totalSize += add;
1315
+ }
1316
+ if (prevTotalSize !== totalSize) {
1317
+ if (!IsNewArchitecture && state.initialScroll && totalSize < prevTotalSize) {
1318
+ state.pendingTotalSize = totalSize;
1319
+ } else {
1320
+ state.pendingTotalSize = void 0;
1321
+ state.totalSize = totalSize;
1322
+ if (notifyTotalSize) {
1323
+ set$(ctx, "totalSize", totalSize);
1324
+ }
1325
+ updateContentMetricsState(ctx);
1326
+ }
1327
+ } else if (notifyTotalSize && ctx.values.get("totalSize") !== totalSize) {
1328
+ set$(ctx, "totalSize", totalSize);
1196
1329
  }
1197
1330
  }
1198
- function setHeaderSize(ctx, size) {
1199
- setContentLengthSignal(ctx, "headerSize", size);
1331
+
1332
+ // src/core/setSize.ts
1333
+ function setSize(ctx, itemKey, size, notifyTotalSize = true) {
1334
+ const state = ctx.state;
1335
+ const { sizes } = state;
1336
+ const previousSize = sizes.get(itemKey);
1337
+ const diff = previousSize !== void 0 ? size - previousSize : size;
1338
+ if (diff !== 0) {
1339
+ addTotalSize(ctx, itemKey, diff, notifyTotalSize);
1340
+ }
1341
+ sizes.set(itemKey, size);
1200
1342
  }
1201
- function setFooterSize(ctx, size) {
1202
- setContentLengthSignal(ctx, "footerSize", size);
1343
+
1344
+ // src/utils/getItemSize.ts
1345
+ function getKnownOrFixedSize(ctx, key, index, data, resolved) {
1346
+ var _a3, _b;
1347
+ const state = ctx.state;
1348
+ const { getFixedItemSize, getItemType } = state.props;
1349
+ let size = key ? state.sizesKnown.get(key) : void 0;
1350
+ if (size === void 0 && key && getFixedItemSize) {
1351
+ const itemType = (_b = resolved == null ? void 0 : resolved.itemType) != null ? _b : getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1352
+ const fixedSize = (resolved == null ? void 0 : resolved.didResolveFixedItemSize) ? resolved.fixedItemSize : getFixedItemSize(data, index, itemType);
1353
+ if (fixedSize !== void 0) {
1354
+ size = fixedSize + ctx.scrollAxisGap;
1355
+ state.sizesKnown.set(key, size);
1356
+ }
1357
+ }
1358
+ return size;
1203
1359
  }
1204
- function areInsetsEqual(left, right) {
1205
- var _a3, _b, _c, _d, _e, _f, _g, _h;
1206
- 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);
1360
+ function getKnownOrFixedItemSize(ctx, index) {
1361
+ const key = getId(ctx.state, index);
1362
+ return getKnownOrFixedSize(ctx, key, index, ctx.state.props.data[index]);
1207
1363
  }
1208
- function setContentInsetOverride(ctx, inset) {
1209
- const { state } = ctx;
1210
- const previousInset = state.contentInsetOverride;
1211
- const nextInset = inset != null ? inset : void 0;
1212
- const didChange = !areInsetsEqual(previousInset, nextInset);
1213
- state.contentInsetOverride = nextInset;
1214
- if (didChange) {
1215
- updateContentMetrics(ctx);
1364
+ function areKnownOrFixedItemSizesAvailable(ctx, startIndex, endIndex) {
1365
+ for (let index = startIndex; index <= endIndex; index++) {
1366
+ if (getKnownOrFixedItemSize(ctx, index) === void 0) {
1367
+ return false;
1368
+ }
1216
1369
  }
1217
- return didChange;
1370
+ return true;
1218
1371
  }
1219
- function useLatestRef(value) {
1220
- const ref = React2.useRef(value);
1221
- ref.current = value;
1222
- return ref;
1372
+ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize, notifyTotalSize, resolved) {
1373
+ var _a3, _b, _c, _d;
1374
+ const state = ctx.state;
1375
+ const {
1376
+ sizes,
1377
+ averageSizes,
1378
+ props: { estimatedItemSize, getItemType },
1379
+ scrollingTo
1380
+ } = state;
1381
+ const sizeKnown = state.sizesKnown.get(key);
1382
+ if (sizeKnown !== void 0) {
1383
+ return sizeKnown;
1384
+ }
1385
+ let size;
1386
+ const renderedSize = sizes.get(key);
1387
+ if (preferCachedSize) {
1388
+ if (renderedSize !== void 0) {
1389
+ return renderedSize;
1390
+ }
1391
+ }
1392
+ size = getKnownOrFixedSize(ctx, key, index, data, resolved);
1393
+ if (size !== void 0) {
1394
+ setSize(ctx, key, size, notifyTotalSize);
1395
+ return size;
1396
+ }
1397
+ const itemType = (_b = resolved == null ? void 0 : resolved.itemType) != null ? _b : getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1398
+ if (useAverageSize && !scrollingTo) {
1399
+ const averageSizeForType = (_c = averageSizes[itemType]) == null ? void 0 : _c.avg;
1400
+ if (averageSizeForType !== void 0) {
1401
+ size = roundSize(averageSizeForType);
1402
+ }
1403
+ }
1404
+ if (size === void 0 && renderedSize !== void 0) {
1405
+ return renderedSize;
1406
+ }
1407
+ if (size === void 0 && useAverageSize && scrollingTo) {
1408
+ const averageSizeForType = (_d = scrollingTo.averageSizeSnapshot) == null ? void 0 : _d[itemType];
1409
+ if (averageSizeForType !== void 0) {
1410
+ size = roundSize(averageSizeForType);
1411
+ }
1412
+ }
1413
+ if (size === void 0) {
1414
+ size = estimatedItemSize + ctx.scrollAxisGap;
1415
+ }
1416
+ setSize(ctx, key, size, notifyTotalSize);
1417
+ return size;
1223
1418
  }
1224
-
1225
- // src/hooks/useStableRenderComponent.tsx
1226
- function useStableRenderComponent(renderComponent, mapProps) {
1227
- const renderComponentRef = useLatestRef(renderComponent);
1228
- const mapPropsRef = useLatestRef(mapProps);
1229
- return React2.useMemo(
1230
- () => React2.forwardRef(
1231
- (props, ref) => {
1232
- var _a3, _b;
1233
- return (_b = (_a3 = renderComponentRef.current) == null ? void 0 : _a3.call(renderComponentRef, mapPropsRef.current(props, ref))) != null ? _b : null;
1234
- }
1235
- ),
1236
- [mapPropsRef, renderComponentRef]
1237
- );
1419
+ function getItemSizeAtIndex(ctx, index) {
1420
+ if (index === void 0 || index < 0) {
1421
+ return void 0;
1422
+ }
1423
+ const targetId = getId(ctx.state, index);
1424
+ return getItemSize(ctx, targetId, index, ctx.state.props.data[index]);
1238
1425
  }
1239
- var LayoutView = ({ onLayoutChange, refView, ...rest }) => {
1240
- const localRef = useRef(null);
1241
- const ref = refView != null ? refView : localRef;
1242
- const { onLayout } = useOnLayoutSync({ onLayoutChange, ref });
1243
- return /* @__PURE__ */ React2.createElement(View$1, { ...rest, onLayout, ref });
1244
- };
1245
1426
 
1246
- // src/components/ListComponent.tsx
1247
- var ListComponent = typedMemo(function ListComponent2({
1248
- canRender,
1249
- style,
1250
- contentContainerStyle,
1251
- horizontal,
1252
- initialContentOffset,
1253
- recycleItems,
1254
- ItemSeparatorComponent,
1255
- alignItemsAtEnd: _alignItemsAtEnd,
1256
- onScroll: onScroll2,
1257
- onLayout,
1258
- ListHeaderComponent,
1259
- ListHeaderComponentStyle,
1260
- ListFooterComponent,
1261
- ListFooterComponentStyle,
1262
- ListEmptyComponent,
1263
- getRenderedItem: getRenderedItem2,
1264
- updateItemSize: updateItemSize2,
1265
- refScrollView,
1266
- renderScrollComponent,
1267
- onLayoutFooter,
1268
- scrollAdjustHandler,
1269
- snapToIndices,
1270
- stickyHeaderConfig,
1271
- stickyHeaderIndices,
1272
- useWindowScroll = false,
1273
- ...rest
1274
- }) {
1275
- const ctx = useStateContext();
1276
- const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
1277
- const [alignItemsAtEndPadding = 0, otherAxisSize = 0] = useArr$(["alignItemsAtEndPadding", "otherAxisSize"]);
1278
- const autoOtherAxisStyle = getAutoOtherAxisStyle({
1279
- horizontal,
1280
- needsOtherAxisSize: ctx.state.needsOtherAxisSize,
1281
- otherAxisSize
1282
- });
1283
- const CustomScrollComponent = useStableRenderComponent(
1284
- renderScrollComponent,
1285
- (props, ref) => ({ ...props, ref })
1286
- );
1287
- const ScrollComponent = renderScrollComponent ? CustomScrollComponent : ListComponentScrollView;
1288
- const SnapOrScroll = snapToIndices ? SnapWrapper : ScrollComponent;
1289
- useLayoutEffect(() => {
1290
- if (!ListHeaderComponent) {
1291
- setHeaderSize(ctx, 0);
1292
- }
1293
- if (!ListFooterComponent) {
1294
- setFooterSize(ctx, 0);
1295
- }
1296
- }, [ListHeaderComponent, ListFooterComponent, ctx]);
1297
- const onLayoutHeader = useCallback(
1298
- (rect) => {
1299
- const size = rect[horizontal ? "width" : "height"];
1300
- setHeaderSize(ctx, size);
1301
- },
1302
- [ctx, horizontal]
1303
- );
1304
- const onLayoutFooterInternal = useCallback(
1305
- (rect, fromLayoutEffect) => {
1306
- const size = rect[horizontal ? "width" : "height"];
1307
- setFooterSize(ctx, size);
1308
- onLayoutFooter == null ? void 0 : onLayoutFooter(rect, fromLayoutEffect);
1309
- },
1310
- [ctx, horizontal, onLayoutFooter]
1311
- );
1312
- return /* @__PURE__ */ React2.createElement(
1313
- SnapOrScroll,
1314
- {
1315
- ...rest,
1316
- ...ScrollComponent === ListComponentScrollView ? { useWindowScroll } : {},
1317
- contentContainerStyle: [
1318
- horizontal ? {
1319
- height: "100%"
1320
- } : {},
1321
- contentContainerStyle
1322
- ],
1323
- contentOffset: initialContentOffset !== void 0 ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
1324
- horizontal,
1325
- maintainVisibleContentPosition: maintainVisibleContentPosition.size || maintainVisibleContentPosition.data ? { minIndexForVisible: 0 } : void 0,
1326
- onLayout,
1327
- onScroll: onScroll2,
1328
- ref: refScrollView,
1329
- ScrollComponent: snapToIndices ? ScrollComponent : void 0,
1330
- style: autoOtherAxisStyle ? [autoOtherAxisStyle, style] : style
1331
- },
1332
- /* @__PURE__ */ React2.createElement(ScrollAdjust, null),
1333
- ListHeaderComponent && /* @__PURE__ */ React2.createElement(LayoutView, { onLayoutChange: onLayoutHeader, style: ListHeaderComponentStyle }, getComponent(ListHeaderComponent)),
1334
- ListEmptyComponent && getComponent(ListEmptyComponent),
1335
- alignItemsAtEndPadding > 0 && /* @__PURE__ */ React2.createElement(
1336
- View,
1337
- {
1338
- style: horizontal ? { flexShrink: 0, width: alignItemsAtEndPadding } : { flexShrink: 0, height: alignItemsAtEndPadding }
1339
- },
1340
- null
1341
- ),
1342
- canRender && !ListEmptyComponent && /* @__PURE__ */ React2.createElement(
1343
- Containers,
1344
- {
1345
- getRenderedItem: getRenderedItem2,
1346
- horizontal,
1347
- ItemSeparatorComponent,
1348
- recycleItems,
1349
- stickyHeaderConfig,
1350
- updateItemSize: updateItemSize2
1351
- }
1352
- ),
1353
- ListFooterComponent && /* @__PURE__ */ React2.createElement(LayoutView, { onLayoutChange: onLayoutFooterInternal, style: ListFooterComponentStyle }, getComponent(ListFooterComponent)),
1354
- Platform.OS === "web" && /* @__PURE__ */ React2.createElement(WebAnchoredEndSpace, { horizontal }),
1355
- IS_DEV && ENABLE_DEVMODE
1356
- );
1357
- });
1358
- var WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH = 100;
1359
- var WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO = 0.9;
1360
- var WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO = 0.9;
1361
- function useDevChecksImpl(props) {
1362
- const ctx = useStateContext();
1363
- const { childrenMode, keyExtractor, renderScrollComponent, useWindowScroll } = props;
1364
- useEffect(() => {
1365
- if (useWindowScroll && renderScrollComponent) {
1366
- warnDevOnce(
1367
- "useWindowScrollRenderScrollComponent",
1368
- "useWindowScroll is not supported when renderScrollComponent is provided."
1369
- );
1370
- }
1371
- }, [renderScrollComponent, useWindowScroll]);
1372
- useEffect(() => {
1373
- if (!keyExtractor && !ctx.state.isFirst && ctx.state.didDataChange && !childrenMode) {
1374
- warnDevOnce(
1375
- "keyExtractor",
1376
- "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."
1377
- );
1427
+ // src/core/calculateOffsetWithOffsetPosition.ts
1428
+ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1429
+ var _a3;
1430
+ const state = ctx.state;
1431
+ const { index, viewOffset, viewPosition } = params;
1432
+ let offset = offsetParam;
1433
+ if (viewOffset) {
1434
+ offset -= viewOffset;
1435
+ }
1436
+ if (index !== void 0) {
1437
+ const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
1438
+ if (topOffsetAdjustment) {
1439
+ offset += topOffsetAdjustment;
1378
1440
  }
1379
- }, [childrenMode, ctx, keyExtractor]);
1380
- useEffect(() => {
1381
- const state = ctx.state;
1441
+ }
1442
+ if (viewPosition !== void 0 && index !== void 0) {
1382
1443
  const dataLength = state.props.data.length;
1383
- const useWindowScrollResolved = state.props.useWindowScroll;
1384
- if (Platform.OS !== "web" || useWindowScrollResolved || dataLength < WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH) {
1385
- return;
1444
+ if (dataLength === 0) {
1445
+ return offset;
1386
1446
  }
1387
- const warnIfUnboundedOuterSize = () => {
1388
- const readyToRender = peek$(ctx, "readyToRender");
1389
- const numContainers = peek$(ctx, "numContainers") || 0;
1390
- const totalSize = peek$(ctx, "totalSize") || 0;
1391
- const scrollLength = ctx.state.scrollLength || 0;
1392
- if (!readyToRender || totalSize <= 0 || scrollLength <= 0) {
1393
- return;
1394
- }
1395
- const rendersAlmostEverything = numContainers >= Math.ceil(dataLength * WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO);
1396
- const viewportMatchesContent = scrollLength >= totalSize * WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO;
1397
- if (rendersAlmostEverything && viewportMatchesContent) {
1398
- warnDevOnce(
1399
- "webUnboundedOuterSize",
1400
- "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."
1401
- );
1402
- }
1403
- };
1404
- warnIfUnboundedOuterSize();
1405
- const unsubscribe = [
1406
- listen$(ctx, "numContainers", warnIfUnboundedOuterSize),
1407
- listen$(ctx, "readyToRender", warnIfUnboundedOuterSize),
1408
- listen$(ctx, "totalSize", warnIfUnboundedOuterSize)
1409
- ];
1410
- return () => {
1411
- for (const unsub of unsubscribe) {
1412
- unsub();
1413
- }
1414
- };
1415
- }, [ctx]);
1447
+ const isOutOfBounds = index < 0 || index >= dataLength;
1448
+ const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
1449
+ const itemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1450
+ const trailingInset = getContentInsetEnd(ctx);
1451
+ offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1452
+ if (!isOutOfBounds && index === state.props.data.length - 1) {
1453
+ const footerSize = peek$(ctx, "footerSize") || 0;
1454
+ offset += footerSize;
1455
+ }
1456
+ }
1457
+ return offset;
1416
1458
  }
1417
- function useDevChecksNoop(_props) {
1459
+
1460
+ // src/core/clampScrollOffset.ts
1461
+ function clampScrollOffset(ctx, offset, scrollTarget) {
1462
+ const state = ctx.state;
1463
+ const contentSize = getContentSize(ctx);
1464
+ let clampedOffset = offset;
1465
+ if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform.OS !== "android" || state.lastLayout)) {
1466
+ const baseMaxOffset = Math.max(0, contentSize - state.scrollLength);
1467
+ const viewOffset = scrollTarget == null ? void 0 : scrollTarget.viewOffset;
1468
+ const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
1469
+ const maxOffset = baseMaxOffset + extraEndOffset;
1470
+ clampedOffset = Math.min(offset, maxOffset);
1471
+ }
1472
+ clampedOffset = Math.max(0, clampedOffset);
1473
+ return clampedOffset;
1418
1474
  }
1419
- var useDevChecks = IS_DEV ? useDevChecksImpl : useDevChecksNoop;
1420
1475
 
1421
1476
  // src/core/deferredPublicOnScroll.ts
1422
1477
  function withResolvedContentOffset(state, event, resolvedOffset) {
@@ -1729,6 +1784,66 @@ function recalculateSettledScroll(ctx) {
1729
1784
  checkThresholds(ctx);
1730
1785
  }
1731
1786
 
1787
+ // src/core/adaptiveRender.ts
1788
+ var DEFAULT_ENTER_VELOCITY = 4;
1789
+ var DEFAULT_EXIT_VELOCITY = 1;
1790
+ var DEFAULT_EXIT_DELAY = 1e3;
1791
+ function scheduleAdaptiveRenderExit(ctx, exitDelay) {
1792
+ const state = ctx.state;
1793
+ const previousTimeout = state.timeoutAdaptiveRender;
1794
+ if (previousTimeout !== void 0) {
1795
+ clearTimeout(previousTimeout);
1796
+ state.timeouts.delete(previousTimeout);
1797
+ state.timeoutAdaptiveRender = void 0;
1798
+ }
1799
+ if (exitDelay <= 0) {
1800
+ setAdaptiveRender(ctx, "normal");
1801
+ } else {
1802
+ const timeout = setTimeout(() => {
1803
+ state.timeouts.delete(timeout);
1804
+ state.timeoutAdaptiveRender = void 0;
1805
+ setAdaptiveRender(ctx, "normal");
1806
+ }, exitDelay);
1807
+ state.timeoutAdaptiveRender = timeout;
1808
+ state.timeouts.add(timeout);
1809
+ }
1810
+ }
1811
+ function setAdaptiveRender(ctx, mode) {
1812
+ var _a3, _b;
1813
+ const previousMode = peek$(ctx, "adaptiveRender");
1814
+ if (previousMode !== mode) {
1815
+ set$(ctx, "adaptiveRender", mode);
1816
+ (_b = (_a3 = ctx.state.props.adaptiveRender) == null ? void 0 : _a3.onChange) == null ? void 0 : _b.call(_a3, mode);
1817
+ }
1818
+ }
1819
+ function updateAdaptiveRender(ctx, scrollVelocity) {
1820
+ var _a3, _b, _c;
1821
+ const state = ctx.state;
1822
+ const adaptiveRender = state.props.adaptiveRender;
1823
+ const enterVelocity = (_a3 = adaptiveRender == null ? void 0 : adaptiveRender.enterVelocity) != null ? _a3 : DEFAULT_ENTER_VELOCITY;
1824
+ const exitVelocity = (_b = adaptiveRender == null ? void 0 : adaptiveRender.exitVelocity) != null ? _b : DEFAULT_EXIT_VELOCITY;
1825
+ const exitDelay = (_c = adaptiveRender == null ? void 0 : adaptiveRender.exitDelay) != null ? _c : DEFAULT_EXIT_DELAY;
1826
+ const currentMode = peek$(ctx, "adaptiveRender");
1827
+ const threshold = currentMode === "light" ? exitVelocity : enterVelocity;
1828
+ const nextMode = Math.abs(scrollVelocity) > threshold ? "light" : "normal";
1829
+ const previousMode = state.timeoutAdaptiveRender !== void 0 ? "normal" : currentMode;
1830
+ if (nextMode !== previousMode) {
1831
+ if (nextMode === "light") {
1832
+ setAdaptiveRender(ctx, "light");
1833
+ scheduleAdaptiveRenderExit(ctx, exitDelay);
1834
+ } else if (currentMode === "light") {
1835
+ scheduleAdaptiveRenderExit(ctx, exitDelay);
1836
+ }
1837
+ }
1838
+ }
1839
+
1840
+ // src/utils/getEffectiveDrawDistance.ts
1841
+ var INITIAL_DRAW_DISTANCE = 100;
1842
+ function getEffectiveDrawDistance(ctx) {
1843
+ const drawDistance = ctx.state.props.drawDistance;
1844
+ return peek$(ctx, "readyToRender") ? drawDistance : Math.min(drawDistance, INITIAL_DRAW_DISTANCE);
1845
+ }
1846
+
1732
1847
  // src/utils/setInitialRenderState.ts
1733
1848
  function setInitialRenderState(ctx, {
1734
1849
  didLayout,
@@ -1748,6 +1863,13 @@ function setInitialRenderState(ctx, {
1748
1863
  const isReadyToRender = Boolean(state.didContainersLayout && state.didFinishInitialScroll);
1749
1864
  if (isReadyToRender && !peek$(ctx, "readyToRender")) {
1750
1865
  set$(ctx, "readyToRender", true);
1866
+ setAdaptiveRender(ctx, "normal");
1867
+ if (state.props.drawDistance > INITIAL_DRAW_DISTANCE) {
1868
+ requestAnimationFrame(() => {
1869
+ var _a3;
1870
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
1871
+ });
1872
+ }
1751
1873
  if (onLoad) {
1752
1874
  onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1753
1875
  }
@@ -1822,235 +1944,39 @@ function finishInitialScroll(ctx, options) {
1822
1944
  complete();
1823
1945
  }
1824
1946
 
1825
- // src/core/calculateOffsetForIndex.ts
1826
- function calculateOffsetForIndex(ctx, index) {
1947
+ // src/core/finishScrollTo.ts
1948
+ function finishScrollTo(ctx) {
1949
+ var _a3, _b;
1827
1950
  const state = ctx.state;
1828
- return index !== void 0 ? state.positions[index] || 0 : 0;
1829
- }
1830
-
1831
- // src/core/getTopOffsetAdjustment.ts
1832
- function getTopOffsetAdjustment(ctx) {
1833
- return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "alignItemsAtEndPadding") || 0) + (peek$(ctx, "headerSize") || 0);
1834
- }
1835
-
1836
- // src/utils/getId.ts
1837
- function getId(state, index) {
1838
- const { data, keyExtractor } = state.props;
1839
- if (!data) {
1840
- return "";
1841
- }
1842
- const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
1843
- const id = ret;
1844
- state.idCache[index] = id;
1845
- return id;
1846
- }
1847
-
1848
- // src/core/addTotalSize.ts
1849
- function addTotalSize(ctx, key, add, notifyTotalSize = true) {
1850
- const state = ctx.state;
1851
- const prevTotalSize = state.totalSize;
1852
- let totalSize = state.totalSize;
1853
- if (key === null) {
1854
- totalSize = add;
1855
- if (state.timeoutSetPaddingTop) {
1856
- clearTimeout(state.timeoutSetPaddingTop);
1857
- state.timeoutSetPaddingTop = void 0;
1858
- }
1859
- } else {
1860
- totalSize += add;
1861
- }
1862
- if (prevTotalSize !== totalSize) {
1863
- if (!IsNewArchitecture && state.initialScroll && totalSize < prevTotalSize) {
1864
- state.pendingTotalSize = totalSize;
1865
- } else {
1866
- state.pendingTotalSize = void 0;
1867
- state.totalSize = totalSize;
1868
- if (notifyTotalSize) {
1869
- set$(ctx, "totalSize", totalSize);
1870
- }
1871
- updateContentMetrics(ctx);
1872
- }
1873
- } else if (notifyTotalSize && ctx.values.get("totalSize") !== totalSize) {
1874
- set$(ctx, "totalSize", totalSize);
1875
- }
1876
- }
1877
-
1878
- // src/core/setSize.ts
1879
- function setSize(ctx, itemKey, size, notifyTotalSize = true) {
1880
- const state = ctx.state;
1881
- const { sizes } = state;
1882
- const previousSize = sizes.get(itemKey);
1883
- const diff = previousSize !== void 0 ? size - previousSize : size;
1884
- if (diff !== 0) {
1885
- addTotalSize(ctx, itemKey, diff, notifyTotalSize);
1886
- }
1887
- sizes.set(itemKey, size);
1888
- }
1889
-
1890
- // src/utils/getItemSize.ts
1891
- function getKnownOrFixedSize(ctx, key, index, data, resolved) {
1892
- var _a3, _b;
1893
- const state = ctx.state;
1894
- const { getFixedItemSize, getItemType } = state.props;
1895
- let size = key ? state.sizesKnown.get(key) : void 0;
1896
- if (size === void 0 && key && getFixedItemSize) {
1897
- const itemType = (_b = resolved == null ? void 0 : resolved.itemType) != null ? _b : getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1898
- size = (resolved == null ? void 0 : resolved.didResolveFixedItemSize) ? resolved.fixedItemSize : getFixedItemSize(data, index, itemType);
1899
- if (size !== void 0) {
1900
- state.sizesKnown.set(key, size);
1901
- }
1902
- }
1903
- return size;
1904
- }
1905
- function getKnownOrFixedItemSize(ctx, index) {
1906
- const key = getId(ctx.state, index);
1907
- return getKnownOrFixedSize(ctx, key, index, ctx.state.props.data[index]);
1908
- }
1909
- function areKnownOrFixedItemSizesAvailable(ctx, startIndex, endIndex) {
1910
- for (let index = startIndex; index <= endIndex; index++) {
1911
- if (getKnownOrFixedItemSize(ctx, index) === void 0) {
1912
- return false;
1913
- }
1914
- }
1915
- return true;
1916
- }
1917
- function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize, notifyTotalSize, resolved) {
1918
- var _a3, _b, _c, _d;
1919
- const state = ctx.state;
1920
- const {
1921
- sizes,
1922
- averageSizes,
1923
- props: { estimatedItemSize, getItemType },
1924
- scrollingTo
1925
- } = state;
1926
- const sizeKnown = state.sizesKnown.get(key);
1927
- if (sizeKnown !== void 0) {
1928
- return sizeKnown;
1929
- }
1930
- let size;
1931
- const renderedSize = sizes.get(key);
1932
- if (preferCachedSize) {
1933
- if (renderedSize !== void 0) {
1934
- return renderedSize;
1935
- }
1936
- }
1937
- size = getKnownOrFixedSize(ctx, key, index, data, resolved);
1938
- if (size !== void 0) {
1939
- setSize(ctx, key, size, notifyTotalSize);
1940
- return size;
1941
- }
1942
- const itemType = (_b = resolved == null ? void 0 : resolved.itemType) != null ? _b : getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1943
- if (useAverageSize && !scrollingTo) {
1944
- const averageSizeForType = (_c = averageSizes[itemType]) == null ? void 0 : _c.avg;
1945
- if (averageSizeForType !== void 0) {
1946
- size = roundSize(averageSizeForType);
1947
- }
1948
- }
1949
- if (size === void 0 && renderedSize !== void 0) {
1950
- return renderedSize;
1951
- }
1952
- if (size === void 0 && useAverageSize && scrollingTo) {
1953
- const averageSizeForType = (_d = scrollingTo.averageSizeSnapshot) == null ? void 0 : _d[itemType];
1954
- if (averageSizeForType !== void 0) {
1955
- size = roundSize(averageSizeForType);
1956
- }
1957
- }
1958
- if (size === void 0) {
1959
- size = estimatedItemSize;
1960
- }
1961
- setSize(ctx, key, size, notifyTotalSize);
1962
- return size;
1963
- }
1964
- function getItemSizeAtIndex(ctx, index) {
1965
- if (index === void 0 || index < 0) {
1966
- return void 0;
1967
- }
1968
- const targetId = getId(ctx.state, index);
1969
- return getItemSize(ctx, targetId, index, ctx.state.props.data[index]);
1970
- }
1971
-
1972
- // src/core/calculateOffsetWithOffsetPosition.ts
1973
- function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1974
- var _a3;
1975
- const state = ctx.state;
1976
- const { index, viewOffset, viewPosition } = params;
1977
- let offset = offsetParam;
1978
- if (viewOffset) {
1979
- offset -= viewOffset;
1980
- }
1981
- if (index !== void 0) {
1982
- const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
1983
- if (topOffsetAdjustment) {
1984
- offset += topOffsetAdjustment;
1985
- }
1986
- }
1987
- if (viewPosition !== void 0 && index !== void 0) {
1988
- const dataLength = state.props.data.length;
1989
- if (dataLength === 0) {
1990
- return offset;
1991
- }
1992
- const isOutOfBounds = index < 0 || index >= dataLength;
1993
- const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
1994
- const itemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1995
- const trailingInset = getContentInsetEnd(ctx);
1996
- offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1997
- if (!isOutOfBounds && index === state.props.data.length - 1) {
1998
- const footerSize = peek$(ctx, "footerSize") || 0;
1999
- offset += footerSize;
2000
- }
2001
- }
2002
- return offset;
2003
- }
2004
-
2005
- // src/core/clampScrollOffset.ts
2006
- function clampScrollOffset(ctx, offset, scrollTarget) {
2007
- const state = ctx.state;
2008
- const contentSize = getContentSize(ctx);
2009
- let clampedOffset = offset;
2010
- if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform.OS !== "android" || state.lastLayout)) {
2011
- const baseMaxOffset = Math.max(0, contentSize - state.scrollLength);
2012
- const viewOffset = scrollTarget == null ? void 0 : scrollTarget.viewOffset;
2013
- const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
2014
- const maxOffset = baseMaxOffset + extraEndOffset;
2015
- clampedOffset = Math.min(offset, maxOffset);
2016
- }
2017
- clampedOffset = Math.max(0, clampedOffset);
2018
- return clampedOffset;
2019
- }
2020
-
2021
- // src/core/finishScrollTo.ts
2022
- function finishScrollTo(ctx) {
2023
- var _a3, _b;
2024
- const state = ctx.state;
2025
- if (state == null ? void 0 : state.scrollingTo) {
2026
- const resolvePendingScroll = state.pendingScrollResolve;
2027
- state.pendingScrollResolve = void 0;
2028
- const scrollingTo = state.scrollingTo;
2029
- state.scrollHistory.length = 0;
2030
- state.scrollingTo = void 0;
2031
- if (state.pendingTotalSize !== void 0) {
2032
- addTotalSize(ctx, null, state.pendingTotalSize);
2033
- }
2034
- if (PlatformAdjustBreaksScroll) {
2035
- state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
2036
- }
2037
- if (scrollingTo.isInitialScroll || state.initialScroll) {
2038
- const isOffsetSession = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
2039
- const shouldPreserveResizeTarget = !!scrollingTo.isInitialScroll && !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_b = state.initialScroll) == null ? void 0 : _b.viewPosition) === 1;
2040
- finishInitialScroll(ctx, {
2041
- onFinished: () => {
2042
- resolvePendingScroll == null ? void 0 : resolvePendingScroll();
2043
- },
2044
- preserveTarget: isOffsetSession && state.props.data.length === 0 || shouldPreserveResizeTarget,
2045
- recalculateItems: true,
2046
- schedulePreservedTargetClear: shouldPreserveResizeTarget,
2047
- syncObservedOffset: isOffsetSession,
2048
- waitForCompletionFrame: !!scrollingTo.waitForInitialScrollCompletionFrame
2049
- });
2050
- return;
2051
- }
2052
- recalculateSettledScroll(ctx);
2053
- resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1951
+ if (state == null ? void 0 : state.scrollingTo) {
1952
+ const resolvePendingScroll = state.pendingScrollResolve;
1953
+ state.pendingScrollResolve = void 0;
1954
+ const scrollingTo = state.scrollingTo;
1955
+ state.scrollHistory.length = 0;
1956
+ state.scrollingTo = void 0;
1957
+ if (state.pendingTotalSize !== void 0) {
1958
+ addTotalSize(ctx, null, state.pendingTotalSize);
1959
+ }
1960
+ if (PlatformAdjustBreaksScroll) {
1961
+ state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
1962
+ }
1963
+ if (scrollingTo.isInitialScroll || state.initialScroll) {
1964
+ const isOffsetSession = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
1965
+ const shouldPreserveResizeTarget = !!scrollingTo.isInitialScroll && !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_b = state.initialScroll) == null ? void 0 : _b.viewPosition) === 1;
1966
+ finishInitialScroll(ctx, {
1967
+ onFinished: () => {
1968
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1969
+ },
1970
+ preserveTarget: isOffsetSession && state.props.data.length === 0 || shouldPreserveResizeTarget,
1971
+ recalculateItems: true,
1972
+ schedulePreservedTargetClear: shouldPreserveResizeTarget,
1973
+ syncObservedOffset: isOffsetSession,
1974
+ waitForCompletionFrame: !!scrollingTo.waitForInitialScrollCompletionFrame
1975
+ });
1976
+ return;
1977
+ }
1978
+ recalculateSettledScroll(ctx);
1979
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
2054
1980
  }
2055
1981
  }
2056
1982
 
@@ -2243,113 +2169,328 @@ function doScrollTo(ctx, params) {
2243
2169
  }
2244
2170
  }
2245
2171
 
2246
- // src/core/doMaintainScrollAtEnd.ts
2247
- function doMaintainScrollAtEnd(ctx) {
2172
+ // src/utils/requestAdjust.ts
2173
+ function requestAdjust(ctx, positionDiff, dataChanged) {
2248
2174
  const state = ctx.state;
2249
- const {
2250
- didContainersLayout,
2251
- pendingNativeMVCPAdjust,
2252
- refScroller,
2253
- props: { maintainScrollAtEnd }
2254
- } = state;
2255
- const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
2256
- const shouldMaintainScrollAtEnd = !!(isWithinMaintainScrollAtEndThreshold && maintainScrollAtEnd && didContainersLayout);
2257
- if (pendingNativeMVCPAdjust) {
2258
- state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
2259
- return false;
2175
+ if (Math.abs(positionDiff) > 0.1) {
2176
+ const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
2177
+ const doit = () => {
2178
+ if (needsScrollWorkaround) {
2179
+ doScrollTo(ctx, { horizontal: state.props.horizontal, offset: state.scroll });
2180
+ } else {
2181
+ state.scrollAdjustHandler.requestAdjust(positionDiff);
2182
+ if (state.adjustingFromInitialMount) {
2183
+ state.adjustingFromInitialMount--;
2184
+ }
2185
+ }
2186
+ };
2187
+ state.scroll += positionDiff;
2188
+ state.scrollForNextCalculateItemsInView = void 0;
2189
+ const readyToRender = peek$(ctx, "readyToRender");
2190
+ if (readyToRender) {
2191
+ doit();
2192
+ if (Platform.OS !== "web") {
2193
+ const threshold = state.scroll - positionDiff / 2;
2194
+ if (!state.ignoreScrollFromMVCP) {
2195
+ state.ignoreScrollFromMVCP = {};
2196
+ }
2197
+ if (positionDiff > 0) {
2198
+ state.ignoreScrollFromMVCP.lt = threshold;
2199
+ } else {
2200
+ state.ignoreScrollFromMVCP.gt = threshold;
2201
+ }
2202
+ if (state.ignoreScrollFromMVCPTimeout) {
2203
+ clearTimeout(state.ignoreScrollFromMVCPTimeout);
2204
+ }
2205
+ const delay = needsScrollWorkaround ? 250 : 100;
2206
+ state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
2207
+ var _a3;
2208
+ state.ignoreScrollFromMVCP = void 0;
2209
+ const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
2210
+ if (shouldForceUpdate) {
2211
+ state.ignoreScrollFromMVCPIgnored = false;
2212
+ state.scrollPending = state.scroll;
2213
+ (_a3 = state.reprocessCurrentScroll) == null ? void 0 : _a3.call(state);
2214
+ }
2215
+ }, delay);
2216
+ }
2217
+ } else {
2218
+ state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
2219
+ requestAnimationFrame(doit);
2220
+ }
2260
2221
  }
2261
- state.pendingMaintainScrollAtEnd = false;
2262
- if (shouldMaintainScrollAtEnd) {
2263
- const contentSize = getContentSize(ctx);
2264
- if (contentSize < state.scrollLength) {
2265
- state.scroll = 0;
2222
+ }
2223
+
2224
+ // src/core/updateContentMetrics.ts
2225
+ var SCROLL_ADJUST_EPSILON = 0.1;
2226
+ function setContentLengthSignal(ctx, signalName, size) {
2227
+ const didChange = peek$(ctx, signalName) !== size;
2228
+ if (didChange) {
2229
+ set$(ctx, signalName, size);
2230
+ updateContentMetricsState(ctx);
2231
+ }
2232
+ return didChange;
2233
+ }
2234
+ function shouldAdjustForHeaderSizeChange(ctx, previousHeaderSize, nextHeaderSize) {
2235
+ const { didContainersLayout, didFinishInitialScroll, props, scroll, scrollingTo } = ctx.state;
2236
+ const sizeDiff = nextHeaderSize - previousHeaderSize;
2237
+ const leadingPadding = props.horizontal ? props.stylePaddingLeft : props.stylePaddingTop;
2238
+ const previousHeaderEnd = (leadingPadding || 0) + previousHeaderSize;
2239
+ return Platform.OS === "web" && props.maintainVisibleContentPosition.size && didContainersLayout && didFinishInitialScroll && !scrollingTo && scroll >= previousHeaderEnd - SCROLL_ADJUST_EPSILON && Math.abs(sizeDiff) > SCROLL_ADJUST_EPSILON;
2240
+ }
2241
+ function setHeaderSize(ctx, size) {
2242
+ const { state } = ctx;
2243
+ const previousHeaderSize = peek$(ctx, "headerSize") || 0;
2244
+ const didChange = previousHeaderSize !== size;
2245
+ const hasMeasuredOrEstimatedHeaderBaseline = state.didMeasureHeader || previousHeaderSize > SCROLL_ADJUST_EPSILON;
2246
+ if (didChange) {
2247
+ set$(ctx, "headerSize", size);
2248
+ updateContentMetricsState(ctx);
2249
+ if (hasMeasuredOrEstimatedHeaderBaseline && shouldAdjustForHeaderSizeChange(ctx, previousHeaderSize, size)) {
2250
+ requestAdjust(ctx, size - previousHeaderSize);
2251
+ }
2252
+ }
2253
+ state.didMeasureHeader = true;
2254
+ }
2255
+ function setFooterSize(ctx, size) {
2256
+ return setContentLengthSignal(ctx, "footerSize", size);
2257
+ }
2258
+ function areInsetsEqual(left, right) {
2259
+ var _a3, _b, _c, _d, _e, _f, _g, _h;
2260
+ 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);
2261
+ }
2262
+ function setContentInsetOverride(ctx, inset) {
2263
+ const { state } = ctx;
2264
+ const previousInset = state.contentInsetOverride;
2265
+ const nextInset = inset != null ? inset : void 0;
2266
+ const didChange = !areInsetsEqual(previousInset, nextInset);
2267
+ state.contentInsetOverride = nextInset;
2268
+ if (didChange) {
2269
+ updateContentMetricsState(ctx);
2270
+ }
2271
+ return didChange;
2272
+ }
2273
+ function useLatestRef(value) {
2274
+ const ref = React2.useRef(value);
2275
+ ref.current = value;
2276
+ return ref;
2277
+ }
2278
+
2279
+ // src/hooks/useStableRenderComponent.tsx
2280
+ function useStableRenderComponent(renderComponent, mapProps) {
2281
+ const renderComponentRef = useLatestRef(renderComponent);
2282
+ const mapPropsRef = useLatestRef(mapProps);
2283
+ return React2.useMemo(
2284
+ () => React2.forwardRef(
2285
+ (props, ref) => {
2286
+ var _a3, _b;
2287
+ return (_b = (_a3 = renderComponentRef.current) == null ? void 0 : _a3.call(renderComponentRef, mapPropsRef.current(props, ref))) != null ? _b : null;
2288
+ }
2289
+ ),
2290
+ [mapPropsRef, renderComponentRef]
2291
+ );
2292
+ }
2293
+ var LayoutView = ({ onLayoutChange, refView, ...rest }) => {
2294
+ const localRef = useRef(null);
2295
+ const ref = refView != null ? refView : localRef;
2296
+ const { onLayout } = useOnLayoutSync({ onLayoutChange, ref });
2297
+ return /* @__PURE__ */ React2.createElement(View$1, { ...rest, onLayout, ref });
2298
+ };
2299
+
2300
+ // src/components/ListComponent.tsx
2301
+ var AlignItemsAtEndSpacer = typedMemo(function AlignItemsAtEndSpacer2({ horizontal }) {
2302
+ const [alignItemsAtEndPadding = 0] = useArr$(["alignItemsAtEndPadding"]);
2303
+ if (alignItemsAtEndPadding <= 0) {
2304
+ return null;
2305
+ }
2306
+ return /* @__PURE__ */ React2.createElement(
2307
+ View,
2308
+ {
2309
+ style: horizontal ? { flexShrink: 0, width: alignItemsAtEndPadding } : { flexShrink: 0, height: alignItemsAtEndPadding }
2310
+ },
2311
+ null
2312
+ );
2313
+ });
2314
+ var ListComponent = typedMemo(function ListComponent2({
2315
+ canRender,
2316
+ style,
2317
+ contentContainerStyle,
2318
+ horizontal,
2319
+ initialContentOffset,
2320
+ recycleItems,
2321
+ ItemSeparatorComponent,
2322
+ alignItemsAtEnd: _alignItemsAtEnd,
2323
+ onScroll: onScroll2,
2324
+ onLayout,
2325
+ ListHeaderComponent,
2326
+ ListHeaderComponentStyle,
2327
+ ListFooterComponent,
2328
+ ListFooterComponentStyle,
2329
+ ListEmptyComponent,
2330
+ getRenderedItem: getRenderedItem2,
2331
+ updateItemSize: updateItemSize2,
2332
+ refScrollView,
2333
+ renderScrollComponent,
2334
+ onLayoutFooter,
2335
+ scrollAdjustHandler,
2336
+ snapToIndices,
2337
+ stickyHeaderConfig,
2338
+ stickyHeaderIndices,
2339
+ useWindowScroll = false,
2340
+ ...rest
2341
+ }) {
2342
+ const ctx = useStateContext();
2343
+ const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
2344
+ const [otherAxisSize = 0] = useArr$(["otherAxisSize"]);
2345
+ const shouldRenderAlignItemsAtEndSpacer = ctx.state.props.alignItemsAtEndPaddingEnabled;
2346
+ const autoOtherAxisStyle = getAutoOtherAxisStyle({
2347
+ horizontal,
2348
+ needsOtherAxisSize: ctx.state.needsOtherAxisSize,
2349
+ otherAxisSize
2350
+ });
2351
+ const CustomScrollComponent = useStableRenderComponent(
2352
+ renderScrollComponent,
2353
+ (props, ref) => ({ ...props, ref })
2354
+ );
2355
+ const ScrollComponent = renderScrollComponent ? CustomScrollComponent : ListComponentScrollView;
2356
+ const SnapOrScroll = snapToIndices ? SnapWrapper : ScrollComponent;
2357
+ const updateFooterSize = useCallback(
2358
+ (size, afterSizeUpdate) => {
2359
+ var _a3;
2360
+ const didFooterSizeChange = setFooterSize(ctx, size);
2361
+ afterSizeUpdate == null ? void 0 : afterSizeUpdate();
2362
+ if (didFooterSizeChange && ((_a3 = ctx.state.props.maintainScrollAtEnd) == null ? void 0 : _a3.onFooterLayout)) {
2363
+ doMaintainScrollAtEnd(ctx);
2364
+ }
2365
+ },
2366
+ [ctx]
2367
+ );
2368
+ useLayoutEffect(() => {
2369
+ if (!ListHeaderComponent) {
2370
+ setHeaderSize(ctx, 0);
2371
+ }
2372
+ if (!ListFooterComponent) {
2373
+ updateFooterSize(0);
2374
+ }
2375
+ }, [ListHeaderComponent, ListFooterComponent, ctx, updateFooterSize]);
2376
+ const onLayoutHeader = useCallback(
2377
+ (rect) => {
2378
+ const size = rect[horizontal ? "width" : "height"];
2379
+ setHeaderSize(ctx, size);
2380
+ },
2381
+ [ctx, horizontal]
2382
+ );
2383
+ const onLayoutFooterInternal = useCallback(
2384
+ (rect, fromLayoutEffect) => {
2385
+ const size = rect[horizontal ? "width" : "height"];
2386
+ updateFooterSize(size, () => {
2387
+ onLayoutFooter == null ? void 0 : onLayoutFooter(rect, fromLayoutEffect);
2388
+ });
2389
+ },
2390
+ [horizontal, onLayoutFooter, updateFooterSize]
2391
+ );
2392
+ return /* @__PURE__ */ React2.createElement(
2393
+ SnapOrScroll,
2394
+ {
2395
+ ...rest,
2396
+ ...ScrollComponent === ListComponentScrollView ? { useWindowScroll } : {},
2397
+ contentContainerStyle: [
2398
+ horizontal ? {
2399
+ height: "100%"
2400
+ } : {},
2401
+ contentContainerStyle
2402
+ ],
2403
+ contentOffset: initialContentOffset !== void 0 ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
2404
+ horizontal,
2405
+ maintainVisibleContentPosition: maintainVisibleContentPosition.size || maintainVisibleContentPosition.data ? { minIndexForVisible: 0 } : void 0,
2406
+ onLayout,
2407
+ onScroll: onScroll2,
2408
+ ref: refScrollView,
2409
+ ScrollComponent: snapToIndices ? ScrollComponent : void 0,
2410
+ style: autoOtherAxisStyle ? [autoOtherAxisStyle, style] : style
2411
+ },
2412
+ /* @__PURE__ */ React2.createElement(ScrollAdjust, null),
2413
+ ListHeaderComponent && /* @__PURE__ */ React2.createElement(LayoutView, { onLayoutChange: onLayoutHeader, style: ListHeaderComponentStyle }, getComponent(ListHeaderComponent)),
2414
+ ListEmptyComponent && getComponent(ListEmptyComponent),
2415
+ shouldRenderAlignItemsAtEndSpacer && /* @__PURE__ */ React2.createElement(AlignItemsAtEndSpacer, { horizontal }),
2416
+ canRender && !ListEmptyComponent && /* @__PURE__ */ React2.createElement(
2417
+ Containers,
2418
+ {
2419
+ getRenderedItem: getRenderedItem2,
2420
+ horizontal,
2421
+ ItemSeparatorComponent,
2422
+ recycleItems,
2423
+ stickyHeaderConfig,
2424
+ updateItemSize: updateItemSize2
2425
+ }
2426
+ ),
2427
+ ListFooterComponent && /* @__PURE__ */ React2.createElement(LayoutView, { onLayoutChange: onLayoutFooterInternal, style: ListFooterComponentStyle }, getComponent(ListFooterComponent)),
2428
+ Platform.OS === "web" && /* @__PURE__ */ React2.createElement(WebAnchoredEndSpace, { horizontal }),
2429
+ IS_DEV && ENABLE_DEVMODE
2430
+ );
2431
+ });
2432
+ var WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH = 100;
2433
+ var WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO = 0.9;
2434
+ var WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO = 0.9;
2435
+ function useDevChecksImpl(props) {
2436
+ const ctx = useStateContext();
2437
+ const { childrenMode, keyExtractor, renderScrollComponent, useWindowScroll } = props;
2438
+ useEffect(() => {
2439
+ if (useWindowScroll && renderScrollComponent) {
2440
+ warnDevOnce(
2441
+ "useWindowScrollRenderScrollComponent",
2442
+ "useWindowScroll is not supported when renderScrollComponent is provided."
2443
+ );
2266
2444
  }
2267
- if (!state.maintainingScrollAtEnd) {
2268
- state.maintainingScrollAtEnd = true;
2269
- requestAnimationFrame(() => {
2270
- if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
2271
- const scroller = refScroller.current;
2272
- if (state.props.horizontal && isHorizontalRTL(state)) {
2273
- const currentContentSize = getContentSize(ctx);
2274
- const logicalEndOffset = getLogicalHorizontalMaxOffset(state, currentContentSize);
2275
- const nativeOffset = toNativeHorizontalOffset(state, logicalEndOffset, currentContentSize);
2276
- scroller == null ? void 0 : scroller.scrollTo({
2277
- animated: maintainScrollAtEnd.animated,
2278
- x: nativeOffset,
2279
- y: 0
2280
- });
2281
- } else {
2282
- scroller == null ? void 0 : scroller.scrollToEnd({
2283
- animated: maintainScrollAtEnd.animated
2284
- });
2285
- }
2286
- setTimeout(
2287
- () => {
2288
- state.maintainingScrollAtEnd = false;
2289
- },
2290
- maintainScrollAtEnd.animated ? 500 : 0
2291
- );
2292
- } else {
2293
- state.maintainingScrollAtEnd = false;
2294
- }
2295
- });
2445
+ }, [renderScrollComponent, useWindowScroll]);
2446
+ useEffect(() => {
2447
+ if (!keyExtractor && !ctx.state.isFirst && ctx.state.didDataChange && !childrenMode) {
2448
+ warnDevOnce(
2449
+ "keyExtractor",
2450
+ "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."
2451
+ );
2296
2452
  }
2297
- return true;
2298
- }
2299
- return false;
2300
- }
2301
-
2302
- // src/utils/requestAdjust.ts
2303
- function requestAdjust(ctx, positionDiff, dataChanged) {
2304
- const state = ctx.state;
2305
- if (Math.abs(positionDiff) > 0.1) {
2306
- const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
2307
- const doit = () => {
2308
- if (needsScrollWorkaround) {
2309
- doScrollTo(ctx, { horizontal: state.props.horizontal, offset: state.scroll });
2310
- } else {
2311
- state.scrollAdjustHandler.requestAdjust(positionDiff);
2312
- if (state.adjustingFromInitialMount) {
2313
- state.adjustingFromInitialMount--;
2314
- }
2453
+ }, [childrenMode, ctx, keyExtractor]);
2454
+ useEffect(() => {
2455
+ const state = ctx.state;
2456
+ const dataLength = state.props.data.length;
2457
+ const useWindowScrollResolved = state.props.useWindowScroll;
2458
+ if (Platform.OS !== "web" || useWindowScrollResolved || dataLength < WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH) {
2459
+ return;
2460
+ }
2461
+ const warnIfUnboundedOuterSize = () => {
2462
+ const readyToRender = peek$(ctx, "readyToRender");
2463
+ const numContainers = peek$(ctx, "numContainers") || 0;
2464
+ const totalSize = peek$(ctx, "totalSize") || 0;
2465
+ const scrollLength = ctx.state.scrollLength || 0;
2466
+ if (!readyToRender || totalSize <= 0 || scrollLength <= 0) {
2467
+ return;
2468
+ }
2469
+ const rendersAlmostEverything = numContainers >= Math.ceil(dataLength * WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO);
2470
+ const viewportMatchesContent = scrollLength >= totalSize * WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO;
2471
+ if (rendersAlmostEverything && viewportMatchesContent) {
2472
+ warnDevOnce(
2473
+ "webUnboundedOuterSize",
2474
+ "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."
2475
+ );
2315
2476
  }
2316
2477
  };
2317
- state.scroll += positionDiff;
2318
- state.scrollForNextCalculateItemsInView = void 0;
2319
- const readyToRender = peek$(ctx, "readyToRender");
2320
- if (readyToRender) {
2321
- doit();
2322
- if (Platform.OS !== "web") {
2323
- const threshold = state.scroll - positionDiff / 2;
2324
- if (!state.ignoreScrollFromMVCP) {
2325
- state.ignoreScrollFromMVCP = {};
2326
- }
2327
- if (positionDiff > 0) {
2328
- state.ignoreScrollFromMVCP.lt = threshold;
2329
- } else {
2330
- state.ignoreScrollFromMVCP.gt = threshold;
2331
- }
2332
- if (state.ignoreScrollFromMVCPTimeout) {
2333
- clearTimeout(state.ignoreScrollFromMVCPTimeout);
2334
- }
2335
- const delay = needsScrollWorkaround ? 250 : 100;
2336
- state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
2337
- var _a3;
2338
- state.ignoreScrollFromMVCP = void 0;
2339
- const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
2340
- if (shouldForceUpdate) {
2341
- state.ignoreScrollFromMVCPIgnored = false;
2342
- state.scrollPending = state.scroll;
2343
- (_a3 = state.reprocessCurrentScroll) == null ? void 0 : _a3.call(state);
2344
- }
2345
- }, delay);
2478
+ warnIfUnboundedOuterSize();
2479
+ const unsubscribe = [
2480
+ listen$(ctx, "numContainers", warnIfUnboundedOuterSize),
2481
+ listen$(ctx, "readyToRender", warnIfUnboundedOuterSize),
2482
+ listen$(ctx, "totalSize", warnIfUnboundedOuterSize)
2483
+ ];
2484
+ return () => {
2485
+ for (const unsub of unsubscribe) {
2486
+ unsub();
2346
2487
  }
2347
- } else {
2348
- state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
2349
- requestAnimationFrame(doit);
2350
- }
2351
- }
2488
+ };
2489
+ }, [ctx]);
2490
+ }
2491
+ function useDevChecksNoop(_props) {
2352
2492
  }
2493
+ var useDevChecks = IS_DEV ? useDevChecksImpl : useDevChecksNoop;
2353
2494
 
2354
2495
  // src/core/mvcp.ts
2355
2496
  var MVCP_POSITION_EPSILON = 0.1;
@@ -2499,6 +2640,10 @@ function prepareMVCP(ctx, dataChanged) {
2499
2640
  const now = Date.now();
2500
2641
  const enableMVCPAnchorLock = isWeb && (!!dataChanged || !!state.mvcpAnchorLock);
2501
2642
  const scrollingTo = state.scrollingTo;
2643
+ if (isWeb && dataChanged && state.pendingScrollToEnd && scrollingTo === void 0) {
2644
+ state.mvcpAnchorLock = void 0;
2645
+ return void 0;
2646
+ }
2502
2647
  const anchorLock = isWeb ? resolveAnchorLock(state, enableMVCPAnchorLock, mvcpData, now) : void 0;
2503
2648
  let prevPosition;
2504
2649
  let targetId;
@@ -2634,7 +2779,7 @@ function prepareMVCP(ctx, dataChanged) {
2634
2779
  return;
2635
2780
  }
2636
2781
  if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
2637
- const shouldSkipAdjustForMaintainedEnd = state.maintainingScrollAtEnd && peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
2782
+ const shouldSkipAdjustForMaintainedEnd = (state.maintainingScrollAtEnd === "pending-animated" || state.maintainingScrollAtEnd === "animated") && peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
2638
2783
  if (!shouldSkipAdjustForMaintainedEnd) {
2639
2784
  requestAdjust(ctx, positionDiff, dataChanged && mvcpData);
2640
2785
  }
@@ -2648,6 +2793,45 @@ var flushSync = (fn) => {
2648
2793
  fn();
2649
2794
  };
2650
2795
 
2796
+ // src/utils/getScrollVelocity.ts
2797
+ var getScrollVelocity = (state) => {
2798
+ const { scrollHistory } = state;
2799
+ const newestIndex = scrollHistory.length - 1;
2800
+ if (newestIndex < 1) {
2801
+ return 0;
2802
+ }
2803
+ const newest = scrollHistory[newestIndex];
2804
+ const now = Date.now();
2805
+ let direction = 0;
2806
+ for (let i = newestIndex; i > 0; i--) {
2807
+ const delta = scrollHistory[i].scroll - scrollHistory[i - 1].scroll;
2808
+ if (delta !== 0) {
2809
+ direction = Math.sign(delta);
2810
+ break;
2811
+ }
2812
+ }
2813
+ if (direction === 0) {
2814
+ return 0;
2815
+ }
2816
+ let oldest = newest;
2817
+ for (let i = newestIndex - 1; i >= 0; i--) {
2818
+ const current = scrollHistory[i];
2819
+ const next = scrollHistory[i + 1];
2820
+ const delta = next.scroll - current.scroll;
2821
+ const deltaSign = Math.sign(delta);
2822
+ if (deltaSign !== 0 && deltaSign !== direction) {
2823
+ break;
2824
+ }
2825
+ if (now - current.time > 1e3) {
2826
+ break;
2827
+ }
2828
+ oldest = current;
2829
+ }
2830
+ const scrollDiff = newest.scroll - oldest.scroll;
2831
+ const timeDiff = newest.time - oldest.time;
2832
+ return timeDiff > 0 ? scrollDiff / timeDiff : 0;
2833
+ };
2834
+
2651
2835
  // src/core/updateScroll.ts
2652
2836
  function updateScroll(ctx, newScroll, forceUpdate, options) {
2653
2837
  var _a3;
@@ -2673,6 +2857,8 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2673
2857
  if (scrollHistory.length > 5) {
2674
2858
  scrollHistory.shift();
2675
2859
  }
2860
+ const scrollVelocity = getScrollVelocity(state);
2861
+ updateAdaptiveRender(ctx, scrollVelocity);
2676
2862
  if (ignoreScrollFromMVCP && !scrollingTo) {
2677
2863
  const { lt, gt } = ignoreScrollFromMVCP;
2678
2864
  if (lt && newScroll < lt || gt && newScroll > gt) {
@@ -2696,7 +2882,7 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2696
2882
  state.lastScrollDelta = scrollDelta;
2697
2883
  const runCalculateItems = () => {
2698
2884
  var _a4;
2699
- (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { doMVCP: scrollingTo !== void 0 });
2885
+ (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { doMVCP: scrollingTo !== void 0, scrollVelocity });
2700
2886
  checkThresholds(ctx);
2701
2887
  };
2702
2888
  if (scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength && !state.pendingNativeMVCPAdjust) {
@@ -3631,9 +3817,32 @@ function initializeInitialScrollOnMount(ctx, options) {
3631
3817
  }
3632
3818
  function handleInitialScrollDataChange(ctx, options) {
3633
3819
  var _a3, _b, _c;
3634
- const { dataLength, didDataChange, initialScrollAtEnd, stylePaddingBottom, useBootstrapInitialScroll } = options;
3820
+ const {
3821
+ dataLength,
3822
+ didDataChange,
3823
+ initialScrollAtEnd,
3824
+ latestInitialScroll,
3825
+ latestInitialScrollSessionKind,
3826
+ stylePaddingBottom,
3827
+ useBootstrapInitialScroll
3828
+ } = options;
3635
3829
  const state = ctx.state;
3636
3830
  const previousDataLength = (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.previousDataLength) != null ? _b : 0;
3831
+ const isFirstNonEmptyData = !state.hasHadNonEmptyData && dataLength > 0;
3832
+ if (dataLength > 0) {
3833
+ state.hasHadNonEmptyData = true;
3834
+ }
3835
+ if (isFirstNonEmptyData) {
3836
+ if (latestInitialScroll) {
3837
+ setInitialScrollTarget(state, latestInitialScroll);
3838
+ setInitialScrollSession(state, {
3839
+ kind: latestInitialScrollSessionKind,
3840
+ previousDataLength
3841
+ });
3842
+ } else {
3843
+ clearPreservedInitialScrollTarget(state);
3844
+ }
3845
+ }
3637
3846
  if (state.initialScrollSession) {
3638
3847
  state.initialScrollSession.previousDataLength = dataLength;
3639
3848
  }
@@ -3857,45 +4066,6 @@ function updateTotalSize(ctx) {
3857
4066
  }
3858
4067
  }
3859
4068
 
3860
- // src/utils/getScrollVelocity.ts
3861
- var getScrollVelocity = (state) => {
3862
- const { scrollHistory } = state;
3863
- const newestIndex = scrollHistory.length - 1;
3864
- if (newestIndex < 1) {
3865
- return 0;
3866
- }
3867
- const newest = scrollHistory[newestIndex];
3868
- const now = Date.now();
3869
- let direction = 0;
3870
- for (let i = newestIndex; i > 0; i--) {
3871
- const delta = scrollHistory[i].scroll - scrollHistory[i - 1].scroll;
3872
- if (delta !== 0) {
3873
- direction = Math.sign(delta);
3874
- break;
3875
- }
3876
- }
3877
- if (direction === 0) {
3878
- return 0;
3879
- }
3880
- let oldest = newest;
3881
- for (let i = newestIndex - 1; i >= 0; i--) {
3882
- const current = scrollHistory[i];
3883
- const next = scrollHistory[i + 1];
3884
- const delta = next.scroll - current.scroll;
3885
- const deltaSign = Math.sign(delta);
3886
- if (deltaSign !== 0 && deltaSign !== direction) {
3887
- break;
3888
- }
3889
- if (now - current.time > 1e3) {
3890
- break;
3891
- }
3892
- oldest = current;
3893
- }
3894
- const scrollDiff = newest.scroll - oldest.scroll;
3895
- const timeDiff = newest.time - oldest.time;
3896
- return timeDiff > 0 ? scrollDiff / timeDiff : 0;
3897
- };
3898
-
3899
4069
  // src/utils/updateSnapToOffsets.ts
3900
4070
  function updateSnapToOffsets(ctx) {
3901
4071
  const state = ctx.state;
@@ -4631,7 +4801,7 @@ function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, sc
4631
4801
  function calculateItemsInView(ctx, params = {}) {
4632
4802
  const state = ctx.state;
4633
4803
  batchedUpdates(() => {
4634
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
4804
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t;
4635
4805
  const {
4636
4806
  columns,
4637
4807
  containerItemKeys,
@@ -4640,14 +4810,7 @@ function calculateItemsInView(ctx, params = {}) {
4640
4810
  indexByKey,
4641
4811
  minIndexSizeChanged,
4642
4812
  positions,
4643
- props: {
4644
- alwaysRenderIndicesArr,
4645
- alwaysRenderIndicesSet,
4646
- drawDistance,
4647
- getItemType,
4648
- keyExtractor,
4649
- onStickyHeaderChange
4650
- },
4813
+ props: { alwaysRenderIndicesArr, alwaysRenderIndicesSet, getItemType, keyExtractor, onStickyHeaderChange },
4651
4814
  scrollForNextCalculateItemsInView,
4652
4815
  scrollLength,
4653
4816
  sizes,
@@ -4659,6 +4822,7 @@ function calculateItemsInView(ctx, params = {}) {
4659
4822
  const stickyHeaderIndicesSet = state.props.stickyHeaderIndicesSet || /* @__PURE__ */ new Set();
4660
4823
  const alwaysRenderArr = alwaysRenderIndicesArr || [];
4661
4824
  const alwaysRenderSet = alwaysRenderIndicesSet || /* @__PURE__ */ new Set();
4825
+ const drawDistance = getEffectiveDrawDistance(ctx);
4662
4826
  const { dataChanged, doMVCP, forceFullItemPositions } = params;
4663
4827
  const bootstrapInitialScrollState = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" ? state.initialScrollSession.bootstrap : void 0;
4664
4828
  const suppressInitialScrollSideEffects = !!bootstrapInitialScrollState;
@@ -4669,10 +4833,10 @@ function calculateItemsInView(ctx, params = {}) {
4669
4833
  let totalSize = getContentSize(ctx);
4670
4834
  const topPad = peek$(ctx, "stylePaddingTop") + peek$(ctx, "alignItemsAtEndPadding") + peek$(ctx, "headerSize");
4671
4835
  const numColumns = peek$(ctx, "numColumns");
4672
- const speed = getScrollVelocity(state);
4836
+ const speed = (_b = params.scrollVelocity) != null ? _b : getScrollVelocity(state);
4673
4837
  const scrollExtra = 0;
4674
4838
  const { initialScroll, queuedInitialLayout } = state;
4675
- const scrollState = suppressInitialScrollSideEffects ? (_b = bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.scroll) != null ? _b : state.scroll : !queuedInitialLayout && hasActiveInitialScroll(state) && initialScroll ? (
4839
+ const scrollState = suppressInitialScrollSideEffects ? (_c = bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.scroll) != null ? _c : state.scroll : !queuedInitialLayout && hasActiveInitialScroll(state) && initialScroll ? (
4676
4840
  // Before the initial layout settles, keep viewport math anchored to the
4677
4841
  // current initial-scroll target instead of transient native adjustments.
4678
4842
  resolveInitialScrollOffset(ctx, initialScroll)
@@ -4696,19 +4860,25 @@ function calculateItemsInView(ctx, params = {}) {
4696
4860
  };
4697
4861
  updateScroll2(scrollState);
4698
4862
  const previousStickyIndex = peek$(ctx, "activeStickyIndex");
4699
- const currentStickyIdx = stickyHeaderIndicesArr.length > 0 ? findCurrentStickyIndex(stickyHeaderIndicesArr, scroll, state) : -1;
4700
- const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyHeaderIndicesArr[currentStickyIdx] : -1;
4701
- const stickyIndexDidChange = previousStickyIndex !== nextActiveStickyIndex;
4702
- if (currentStickyIdx >= 0 || previousStickyIndex >= 0) {
4703
- set$(ctx, "activeStickyIndex", nextActiveStickyIndex);
4704
- }
4705
- const shouldNotifyStickyHeaderChange = !!onStickyHeaderChange && stickyHeaderIndicesArr.length > 0 && stickyIndexDidChange;
4706
- const finishCalculateItemsInView = shouldNotifyStickyHeaderChange ? () => {
4707
- const item = data[nextActiveStickyIndex];
4708
- if (item !== void 0) {
4709
- onStickyHeaderChange == null ? void 0 : onStickyHeaderChange({ index: nextActiveStickyIndex, item });
4863
+ const resolveStickyState = () => {
4864
+ const currentStickyIdx = stickyHeaderIndicesArr.length > 0 ? findCurrentStickyIndex(stickyHeaderIndicesArr, scroll, state) : -1;
4865
+ const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyHeaderIndicesArr[currentStickyIdx] : -1;
4866
+ const stickyIndexDidChange = previousStickyIndex !== nextActiveStickyIndex;
4867
+ if (currentStickyIdx >= 0 || previousStickyIndex >= 0) {
4868
+ set$(ctx, "activeStickyIndex", nextActiveStickyIndex);
4710
4869
  }
4711
- } : void 0;
4870
+ const shouldNotifyStickyHeaderChange = !!onStickyHeaderChange && stickyHeaderIndicesArr.length > 0 && stickyIndexDidChange;
4871
+ return {
4872
+ currentStickyIdx,
4873
+ finishCalculateItemsInView: shouldNotifyStickyHeaderChange ? () => {
4874
+ const item = data[nextActiveStickyIndex];
4875
+ if (item !== void 0) {
4876
+ onStickyHeaderChange == null ? void 0 : onStickyHeaderChange({ index: nextActiveStickyIndex, item });
4877
+ }
4878
+ } : void 0
4879
+ };
4880
+ };
4881
+ let stickyState = dataChanged ? void 0 : resolveStickyState();
4712
4882
  let scrollBufferTop = drawDistance;
4713
4883
  let scrollBufferBottom = drawDistance;
4714
4884
  if (speed > 0 || speed === 0 && scroll < Math.max(50, drawDistance)) {
@@ -4741,7 +4911,7 @@ function calculateItemsInView(ctx, params = {}) {
4741
4911
  scrollBottom
4742
4912
  );
4743
4913
  }
4744
- finishCalculateItemsInView == null ? void 0 : finishCalculateItemsInView();
4914
+ (_d = stickyState == null ? void 0 : stickyState.finishCalculateItemsInView) == null ? void 0 : _d.call(stickyState);
4745
4915
  return;
4746
4916
  }
4747
4917
  }
@@ -4750,7 +4920,7 @@ function calculateItemsInView(ctx, params = {}) {
4750
4920
  if (dataChanged) {
4751
4921
  resetLayoutCachesForDataChange(state);
4752
4922
  }
4753
- const startIndex = forceFullItemPositions || dataChanged ? 0 : (_c = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _c : 0;
4923
+ const startIndex = forceFullItemPositions || dataChanged ? 0 : (_e = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _e : 0;
4754
4924
  const optimizeForVisibleWindow = !forceFullItemPositions && !dataChanged && numColumns > 1 && minIndexSizeChanged !== void 0;
4755
4925
  updateItemPositions(ctx, dataChanged, {
4756
4926
  doMVCP,
@@ -4776,21 +4946,24 @@ function calculateItemsInView(ctx, params = {}) {
4776
4946
  }
4777
4947
  }
4778
4948
  const scrollBeforeMVCP = state.scroll;
4779
- const scrollAdjustPendingBeforeMVCP = (_d = peek$(ctx, "scrollAdjustPending")) != null ? _d : 0;
4949
+ const scrollAdjustPendingBeforeMVCP = (_f = peek$(ctx, "scrollAdjustPending")) != null ? _f : 0;
4780
4950
  checkMVCP == null ? void 0 : checkMVCP();
4781
- const didMVCPAdjustScroll = !!checkMVCP && (state.scroll !== scrollBeforeMVCP || ((_e = peek$(ctx, "scrollAdjustPending")) != null ? _e : 0) !== scrollAdjustPendingBeforeMVCP);
4951
+ const didMVCPAdjustScroll = !!checkMVCP && (state.scroll !== scrollBeforeMVCP || ((_g = peek$(ctx, "scrollAdjustPending")) != null ? _g : 0) !== scrollAdjustPendingBeforeMVCP);
4782
4952
  if (didMVCPAdjustScroll && initialScroll) {
4783
4953
  updateScroll2(state.scroll);
4784
4954
  updateScrollRange();
4785
4955
  }
4956
+ if (dataChanged) {
4957
+ stickyState = resolveStickyState();
4958
+ }
4786
4959
  let startBuffered = null;
4787
4960
  let startBufferedId = null;
4788
4961
  let endBuffered = null;
4789
- let loopStart = (_f = suppressInitialScrollSideEffects ? bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.targetIndexSeed : void 0) != null ? _f : !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
4962
+ let loopStart = (_h = suppressInitialScrollSideEffects ? bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.targetIndexSeed : void 0) != null ? _h : !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
4790
4963
  for (let i = loopStart; i >= 0; i--) {
4791
- const id = (_g = idCache[i]) != null ? _g : getId(state, i);
4964
+ const id = (_i = idCache[i]) != null ? _i : getId(state, i);
4792
4965
  const top = positions[i];
4793
- const size = (_h = sizes.get(id)) != null ? _h : getItemSize(ctx, id, i, data[i]);
4966
+ const size = (_j = sizes.get(id)) != null ? _j : getItemSize(ctx, id, i, data[i]);
4794
4967
  const bottom = top + size;
4795
4968
  if (bottom > scrollTopBuffered) {
4796
4969
  loopStart = i;
@@ -4825,8 +4998,8 @@ function calculateItemsInView(ctx, params = {}) {
4825
4998
  };
4826
4999
  const dataLength = data.length;
4827
5000
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
4828
- const id = (_i = idCache[i]) != null ? _i : getId(state, i);
4829
- const size = (_j = sizes.get(id)) != null ? _j : getItemSize(ctx, id, i, data[i]);
5001
+ const id = (_k = idCache[i]) != null ? _k : getId(state, i);
5002
+ const size = (_l = sizes.get(id)) != null ? _l : getItemSize(ctx, id, i, data[i]);
4830
5003
  const top = positions[i];
4831
5004
  if (!foundEnd) {
4832
5005
  trackVisibleRange(visibleRange, i, top, size, scroll, scrollBottom);
@@ -4882,7 +5055,7 @@ function calculateItemsInView(ctx, params = {}) {
4882
5055
  const needNewContainers = [];
4883
5056
  const needNewContainersSet = /* @__PURE__ */ new Set();
4884
5057
  for (let i = startBuffered; i <= endBuffered; i++) {
4885
- const id = (_k = idCache[i]) != null ? _k : getId(state, i);
5058
+ const id = (_m = idCache[i]) != null ? _m : getId(state, i);
4886
5059
  if (!containerItemKeys.has(id)) {
4887
5060
  needNewContainersSet.add(i);
4888
5061
  needNewContainers.push(i);
@@ -4891,7 +5064,7 @@ function calculateItemsInView(ctx, params = {}) {
4891
5064
  if (alwaysRenderArr.length > 0) {
4892
5065
  for (const index of alwaysRenderArr) {
4893
5066
  if (index < 0 || index >= dataLength) continue;
4894
- const id = (_l = idCache[index]) != null ? _l : getId(state, index);
5067
+ const id = (_n = idCache[index]) != null ? _n : getId(state, index);
4895
5068
  if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
4896
5069
  needNewContainersSet.add(index);
4897
5070
  needNewContainers.push(index);
@@ -4902,7 +5075,7 @@ function calculateItemsInView(ctx, params = {}) {
4902
5075
  handleStickyActivation(
4903
5076
  ctx,
4904
5077
  stickyHeaderIndicesArr,
4905
- currentStickyIdx,
5078
+ (_o = stickyState == null ? void 0 : stickyState.currentStickyIdx) != null ? _o : -1,
4906
5079
  needNewContainers,
4907
5080
  needNewContainersSet,
4908
5081
  startBuffered,
@@ -4928,7 +5101,7 @@ function calculateItemsInView(ctx, params = {}) {
4928
5101
  for (const allocation of availableContainerAllocations) {
4929
5102
  const i = allocation.itemIndex;
4930
5103
  const containerIndex = allocation.containerIndex;
4931
- const id = (_m = idCache[i]) != null ? _m : getId(state, i);
5104
+ const id = (_p = idCache[i]) != null ? _p : getId(state, i);
4932
5105
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
4933
5106
  if (oldKey && oldKey !== id) {
4934
5107
  containerItemKeys.delete(oldKey);
@@ -4939,7 +5112,7 @@ function calculateItemsInView(ctx, params = {}) {
4939
5112
  state.containerItemTypes.set(containerIndex, allocation.itemType);
4940
5113
  }
4941
5114
  containerItemKeys.set(id, containerIndex);
4942
- (_n = state.userScrollAnchorReset) == null ? void 0 : _n.keys.add(id);
5115
+ (_q = state.userScrollAnchorReset) == null ? void 0 : _q.keys.add(id);
4943
5116
  const containerSticky = `containerSticky${containerIndex}`;
4944
5117
  const isSticky = stickyHeaderIndicesSet.has(i);
4945
5118
  const isAlwaysRender = alwaysRenderSet.has(i);
@@ -4970,14 +5143,12 @@ function calculateItemsInView(ctx, params = {}) {
4970
5143
  if (state.userScrollAnchorReset) {
4971
5144
  if (state.userScrollAnchorReset.keys.size === 0) {
4972
5145
  state.userScrollAnchorReset = void 0;
4973
- } else {
4974
- state.userScrollAnchorReset.batchSize = state.userScrollAnchorReset.keys.size;
4975
5146
  }
4976
5147
  }
4977
5148
  if (alwaysRenderArr.length > 0) {
4978
5149
  for (const index of alwaysRenderArr) {
4979
5150
  if (index < 0 || index >= dataLength) continue;
4980
- const id = (_o = idCache[index]) != null ? _o : getId(state, index);
5151
+ const id = (_r = idCache[index]) != null ? _r : getId(state, index);
4981
5152
  const containerIndex = containerItemKeys.get(id);
4982
5153
  if (containerIndex !== void 0) {
4983
5154
  state.stickyContainerPool.add(containerIndex);
@@ -4991,7 +5162,7 @@ function calculateItemsInView(ctx, params = {}) {
4991
5162
  stickyHeaderIndicesArr,
4992
5163
  scroll,
4993
5164
  drawDistance,
4994
- currentStickyIdx,
5165
+ (_s = stickyState == null ? void 0 : stickyState.currentStickyIdx) != null ? _s : -1,
4995
5166
  pendingRemoval,
4996
5167
  alwaysRenderSet
4997
5168
  );
@@ -5052,7 +5223,7 @@ function calculateItemsInView(ctx, params = {}) {
5052
5223
  );
5053
5224
  }
5054
5225
  }
5055
- finishCalculateItemsInView == null ? void 0 : finishCalculateItemsInView();
5226
+ (_t = stickyState == null ? void 0 : stickyState.finishCalculateItemsInView) == null ? void 0 : _t.call(stickyState);
5056
5227
  });
5057
5228
  }
5058
5229
 
@@ -5135,8 +5306,9 @@ function doInitialAllocateContainers(ctx) {
5135
5306
  const state = ctx.state;
5136
5307
  const {
5137
5308
  scrollLength,
5138
- props: { data, drawDistance, getFixedItemSize, getItemType, numColumns, estimatedItemSize }
5309
+ props: { data, getFixedItemSize, getItemType, numColumns, estimatedItemSize }
5139
5310
  } = state;
5311
+ const drawDistance = getEffectiveDrawDistance(ctx);
5140
5312
  const hasContainers = peek$(ctx, "numContainers");
5141
5313
  if (scrollLength > 0 && data.length > 0 && !hasContainers) {
5142
5314
  let averageItemSize;
@@ -5147,12 +5319,12 @@ function doInitialAllocateContainers(ctx) {
5147
5319
  const item = data[i];
5148
5320
  if (item !== void 0) {
5149
5321
  const itemType = (_a3 = getItemType == null ? void 0 : getItemType(item, i)) != null ? _a3 : "";
5150
- totalSize += (_b = getFixedItemSize(item, i, itemType)) != null ? _b : estimatedItemSize;
5322
+ totalSize += ((_b = getFixedItemSize(item, i, itemType)) != null ? _b : estimatedItemSize) + ctx.scrollAxisGap;
5151
5323
  }
5152
5324
  }
5153
5325
  averageItemSize = totalSize / num;
5154
5326
  } else {
5155
- averageItemSize = estimatedItemSize;
5327
+ averageItemSize = estimatedItemSize + ctx.scrollAxisGap;
5156
5328
  }
5157
5329
  const numContainers = Math.max(
5158
5330
  1,
@@ -5207,7 +5379,7 @@ function handleLayout(ctx, layoutParam, setCanRender) {
5207
5379
  if (didChange) {
5208
5380
  state.scrollLength = scrollLength;
5209
5381
  state.otherAxisSize = otherAxisSize;
5210
- updateContentMetrics(ctx);
5382
+ updateContentMetricsState(ctx);
5211
5383
  state.lastBatchingAction = Date.now();
5212
5384
  state.scrollForNextCalculateItemsInView = void 0;
5213
5385
  if (scrollLength > 0) {
@@ -5448,28 +5620,12 @@ function updateContentInsetEndAdjustment(ctx, previousContentInsetEndAdjustment)
5448
5620
 
5449
5621
  // src/core/updateItemSize.ts
5450
5622
  function runOrScheduleMVCPRecalculate(ctx) {
5451
- var _a3, _b;
5623
+ var _a3;
5452
5624
  const state = ctx.state;
5453
5625
  if (state.userScrollAnchorReset !== void 0) {
5454
- const replacementBatchSize = (_a3 = state.userScrollAnchorReset.batchSize) != null ? _a3 : state.userScrollAnchorReset.keys.size;
5455
- const replacementMeasurementBatchThreshold = 3;
5456
- const shouldBatchReplacementMeasurements = replacementBatchSize > replacementMeasurementBatchThreshold;
5457
- if (shouldBatchReplacementMeasurements) {
5458
- if (state.queuedMVCPRecalculate === void 0) {
5459
- state.queuedMVCPRecalculate = requestAnimationFrame(() => {
5460
- var _a4;
5461
- state.queuedMVCPRecalculate = void 0;
5462
- calculateItemsInView(ctx);
5463
- if (((_a4 = state.userScrollAnchorReset) == null ? void 0 : _a4.keys.size) === 0) {
5464
- state.userScrollAnchorReset = void 0;
5465
- }
5466
- });
5467
- }
5468
- } else {
5469
- calculateItemsInView(ctx);
5470
- if (((_b = state.userScrollAnchorReset) == null ? void 0 : _b.keys.size) === 0) {
5471
- state.userScrollAnchorReset = void 0;
5472
- }
5626
+ calculateItemsInView(ctx);
5627
+ if (((_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys.size) === 0) {
5628
+ state.userScrollAnchorReset = void 0;
5473
5629
  }
5474
5630
  } else if (Platform.OS === "web") {
5475
5631
  if (!state.mvcpAnchorLock) {
@@ -5947,6 +6103,9 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
5947
6103
  scrollTo(ctx, params);
5948
6104
  return true;
5949
6105
  }),
6106
+ setItemSize: (itemKey, size) => {
6107
+ updateItemSize(ctx, itemKey, size);
6108
+ },
5950
6109
  setScrollProcessingEnabled: (enabled) => {
5951
6110
  state.scrollProcessingEnabled = enabled;
5952
6111
  },
@@ -6044,12 +6203,13 @@ function getRenderedItem(ctx, key) {
6044
6203
 
6045
6204
  // src/utils/normalizeMaintainScrollAtEnd.ts
6046
6205
  function normalizeMaintainScrollAtEndOn(on, hasExplicitOn) {
6047
- var _a3, _b, _c;
6206
+ var _a3, _b, _c, _d;
6048
6207
  return {
6049
6208
  animated: false,
6050
6209
  onDataChange: hasExplicitOn ? (_a3 = on == null ? void 0 : on.dataChange) != null ? _a3 : false : true,
6051
- onItemLayout: hasExplicitOn ? (_b = on == null ? void 0 : on.itemLayout) != null ? _b : false : true,
6052
- onLayout: hasExplicitOn ? (_c = on == null ? void 0 : on.layout) != null ? _c : false : true
6210
+ onFooterLayout: hasExplicitOn ? (_b = on == null ? void 0 : on.footerLayout) != null ? _b : false : true,
6211
+ onItemLayout: hasExplicitOn ? (_c = on == null ? void 0 : on.itemLayout) != null ? _c : false : true,
6212
+ onLayout: hasExplicitOn ? (_d = on == null ? void 0 : on.layout) != null ? _d : false : true
6053
6213
  };
6054
6214
  }
6055
6215
  function normalizeMaintainScrollAtEnd(value) {
@@ -6171,7 +6331,7 @@ var LegendList = typedMemo(
6171
6331
  })
6172
6332
  );
6173
6333
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
6174
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i;
6334
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
6175
6335
  const noopOnScroll = useCallback((_event) => {
6176
6336
  }, []);
6177
6337
  if (props.recycleItems === void 0) {
@@ -6202,6 +6362,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6202
6362
  initialScrollAtEnd = false,
6203
6363
  initialScrollIndex: initialScrollIndexProp,
6204
6364
  initialScrollOffset: initialScrollOffsetProp,
6365
+ experimental_adaptiveRender,
6205
6366
  itemsAreEqual,
6206
6367
  keyExtractor: keyExtractorProp,
6207
6368
  ListEmptyComponent,
@@ -6298,6 +6459,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6298
6459
  const [, scheduleImperativeScrollCommit] = React2.useReducer((value) => value + 1, 0);
6299
6460
  const ctx = useStateContext();
6300
6461
  ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
6462
+ 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;
6463
+ const nextScrollAxisGap = typeof scrollAxisGap === "number" && Number.isFinite(scrollAxisGap) ? scrollAxisGap : 0;
6301
6464
  const refScroller = useRef(null);
6302
6465
  const combinedRef = useCombinedRef(refScroller, refScrollView);
6303
6466
  const keyExtractor = keyExtractorProp != null ? keyExtractorProp : ((_item, index) => index.toString());
@@ -6311,8 +6474,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6311
6474
  anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex,
6312
6475
  alwaysRender == null ? void 0 : alwaysRender.top,
6313
6476
  alwaysRender == null ? void 0 : alwaysRender.bottom,
6314
- (_d = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _d.join(","),
6315
- (_e = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _e.join(","),
6477
+ (_j = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _j.join(","),
6478
+ (_k = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _k.join(","),
6316
6479
  dataProp,
6317
6480
  dataVersion,
6318
6481
  keyExtractor
@@ -6340,6 +6503,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6340
6503
  endNoBuffer: -1,
6341
6504
  endReachedSnapshot: void 0,
6342
6505
  firstFullyOnScreenIndex: -1,
6506
+ hasHadNonEmptyData: dataProp.length > 0,
6343
6507
  idCache: [],
6344
6508
  idsInView: [],
6345
6509
  indexByKey: /* @__PURE__ */ new Map(),
@@ -6382,6 +6546,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6382
6546
  startReachedSnapshotDataChangeEpoch: void 0,
6383
6547
  stickyContainerPool: /* @__PURE__ */ new Set(),
6384
6548
  stickyContainers: /* @__PURE__ */ new Map(),
6549
+ timeoutAdaptiveRender: void 0,
6385
6550
  timeouts: /* @__PURE__ */ new Set(),
6386
6551
  totalSize: 0,
6387
6552
  viewabilityConfigCallbackPairs: void 0
@@ -6400,11 +6565,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6400
6565
  const state = refState.current;
6401
6566
  const isFirstLocal = state.isFirst;
6402
6567
  const previousNumColumnsProp = state.props.numColumns;
6403
- state.didColumnsChange = numColumnsProp !== previousNumColumnsProp;
6568
+ const didScrollAxisGapChange = !isFirstLocal && ctx.scrollAxisGap !== nextScrollAxisGap;
6569
+ ctx.scrollAxisGap = nextScrollAxisGap;
6570
+ state.didColumnsChange = numColumnsProp !== previousNumColumnsProp || didScrollAxisGapChange;
6404
6571
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
6405
6572
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
6406
6573
  const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
6407
- if (didDataChangeLocal && !initialScrollAtEnd && state.didFinishInitialScroll && ((_f = state.initialScroll) == null ? void 0 : _f.viewPosition) === 1 && state.props.data.length > 0) {
6574
+ if (didDataChangeLocal && !initialScrollAtEnd && state.didFinishInitialScroll && ((_l = state.initialScroll) == null ? void 0 : _l.viewPosition) === 1 && state.props.data.length > 0) {
6408
6575
  clearPreservedInitialScrollTarget(state);
6409
6576
  }
6410
6577
  if (didDataChangeLocal) {
@@ -6416,8 +6583,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6416
6583
  const throttledOnScroll = useThrottledOnScroll(onScrollProp != null ? onScrollProp : noopOnScroll, scrollEventThrottle != null ? scrollEventThrottle : 0);
6417
6584
  const throttleScrollFn = scrollEventThrottle && onScrollProp ? throttledOnScroll : onScrollProp;
6418
6585
  const anchoredEndSpaceResolved = Platform.OS === "web" && anchoredEndSpace ? { ...anchoredEndSpace, includeInEndInset: true } : anchoredEndSpace;
6419
- const didAnchoredEndSpaceAnchorIndexChange = !isFirstLocal && !didDataChangeLocal && ((_g = state.props.anchoredEndSpace) == null ? void 0 : _g.anchorIndex) !== (anchoredEndSpaceResolved == null ? void 0 : anchoredEndSpaceResolved.anchorIndex);
6586
+ const didAnchoredEndSpaceAnchorIndexChange = !isFirstLocal && !didDataChangeLocal && ((_m = state.props.anchoredEndSpace) == null ? void 0 : _m.anchorIndex) !== (anchoredEndSpaceResolved == null ? void 0 : anchoredEndSpaceResolved.anchorIndex);
6420
6587
  state.props = {
6588
+ adaptiveRender: experimental_adaptiveRender,
6421
6589
  alignItemsAtEnd,
6422
6590
  alignItemsAtEndPaddingEnabled: useAlignItemsAtEndPadding,
6423
6591
  alwaysRender,
@@ -6425,6 +6593,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6425
6593
  alwaysRenderIndicesSet: alwaysRenderIndices.set,
6426
6594
  anchoredEndSpace: anchoredEndSpaceResolved,
6427
6595
  animatedProps: animatedPropsInternal,
6596
+ contentContainerAlignItems: contentContainerStyle.alignItems,
6428
6597
  contentInset,
6429
6598
  contentInsetEndAdjustment: contentInsetEndAdjustmentResolved,
6430
6599
  data: dataProp,
@@ -6477,7 +6646,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6477
6646
  const prevPaddingTop = peek$(ctx, "stylePaddingTop");
6478
6647
  setPaddingTop(ctx, { stylePaddingTop: stylePaddingTopState });
6479
6648
  refState.current.props.stylePaddingBottom = stylePaddingBottomState;
6480
- updateContentMetrics(ctx);
6649
+ updateContentMetricsState(ctx);
6481
6650
  let paddingDiff = stylePaddingTopState - prevPaddingTop;
6482
6651
  if (shouldAdjustPadding && maintainVisibleContentPositionConfig.size && paddingDiff && prevPaddingTop !== void 0 && Platform.OS === "ios") {
6483
6652
  if (state.scroll < 0) {
@@ -6514,7 +6683,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6514
6683
  useBootstrapInitialScroll: usesBootstrapInitialScroll
6515
6684
  });
6516
6685
  }, []);
6517
- if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
6686
+ if (isFirstLocal || didDataChangeLocal || state.didColumnsChange) {
6518
6687
  refState.current.lastBatchingAction = Date.now();
6519
6688
  if (!keyExtractorProp && !isFirstLocal && didDataChangeLocal) {
6520
6689
  refState.current.sizes.clear();
@@ -6531,6 +6700,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6531
6700
  dataLength: dataProp.length,
6532
6701
  didDataChange: didDataChangeLocal,
6533
6702
  initialScrollAtEnd,
6703
+ latestInitialScroll: initialScrollProp,
6704
+ latestInitialScrollSessionKind: initialScrollUsesOffsetOnly ? "offset" : "bootstrap",
6534
6705
  stylePaddingBottom: stylePaddingBottomState,
6535
6706
  useBootstrapInitialScroll: usesBootstrapInitialScroll
6536
6707
  });
@@ -6605,6 +6776,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6605
6776
  dataVersion,
6606
6777
  memoizedLastItemKeys.join(","),
6607
6778
  numColumnsProp,
6779
+ nextScrollAxisGap,
6608
6780
  stylePaddingBottomState,
6609
6781
  stylePaddingTopState,
6610
6782
  useAlignItemsAtEndPadding
@@ -6627,7 +6799,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6627
6799
  state.didColumnsChange = false;
6628
6800
  state.didDataChange = false;
6629
6801
  state.isFirst = false;
6630
- }, [dataProp, dataVersion, numColumnsProp]);
6802
+ }, [dataProp, dataVersion, numColumnsProp, nextScrollAxisGap]);
6631
6803
  useLayoutEffect(() => {
6632
6804
  var _a4;
6633
6805
  set$(ctx, "extraData", extraData);
@@ -6678,6 +6850,14 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6678
6850
  }
6679
6851
  });
6680
6852
  useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx, scheduleImperativeScrollCommit), []);
6853
+ useEffect(() => {
6854
+ return () => {
6855
+ for (const timeout of state.timeouts) {
6856
+ clearTimeout(timeout);
6857
+ }
6858
+ state.timeouts.clear();
6859
+ };
6860
+ }, [state]);
6681
6861
  useLayoutEffect(() => {
6682
6862
  var _a4;
6683
6863
  (_a4 = state.runPendingScrollToEnd) == null ? void 0 : _a4.call(state);
@@ -6725,7 +6905,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6725
6905
  onScroll: onScrollHandler,
6726
6906
  recycleItems,
6727
6907
  refreshControl: refreshControlElement ? stylePaddingTopState > 0 ? React2.cloneElement(refreshControlElement, {
6728
- progressViewOffset: ((_h = refreshControlElement.props.progressViewOffset) != null ? _h : 0) + stylePaddingTopState
6908
+ progressViewOffset: ((_n = refreshControlElement.props.progressViewOffset) != null ? _n : 0) + stylePaddingTopState
6729
6909
  }) : refreshControlElement : onRefresh && /* @__PURE__ */ React2.createElement(
6730
6910
  RefreshControl,
6731
6911
  {
@@ -6736,7 +6916,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6736
6916
  ),
6737
6917
  refScrollView: combinedRef,
6738
6918
  renderScrollComponent,
6739
- scrollAdjustHandler: (_i = refState.current) == null ? void 0 : _i.scrollAdjustHandler,
6919
+ scrollAdjustHandler: (_o = refState.current) == null ? void 0 : _o.scrollAdjustHandler,
6740
6920
  scrollEventThrottle: 0,
6741
6921
  snapToIndices,
6742
6922
  stickyHeaderIndices,
@@ -6768,4 +6948,4 @@ var internal = {
6768
6948
  var LegendList3 = LegendListRuntime;
6769
6949
  var internal2 = internal;
6770
6950
 
6771
- export { LegendList3 as LegendList, internal2 as internal, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };
6951
+ export { LegendList3 as LegendList, internal2 as internal, useAdaptiveRender, useAdaptiveRenderChange, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };