@legendapp/list 3.3.10 → 3.3.11

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
@@ -993,6 +993,7 @@ function supersedeInitialScroll(ctx) {
993
993
  function finishInitialScroll(ctx, options) {
994
994
  var _a3, _b, _c;
995
995
  const state = ctx.state;
996
+ const freshDataTransitionEpoch = state.freshDataTransitionEpoch;
996
997
  if ((options == null ? void 0 : options.resolvedOffset) !== void 0) {
997
998
  syncInitialScrollOffset(state, options.resolvedOffset);
998
999
  } else if ((options == null ? void 0 : options.syncObservedOffset) && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
@@ -1002,9 +1003,13 @@ function finishInitialScroll(ctx, options) {
1002
1003
  }
1003
1004
  }
1004
1005
  const complete = () => {
1005
- var _a4, _b2, _c2, _d, _e;
1006
- const shouldReleaseDeferredPublicOnScroll = Platform.OS === "web" && ((_a4 = state.initialScrollSession) == null ? void 0 : _a4.kind) === "bootstrap";
1007
- const finalScrollOffset = (_d = (_c2 = (_b2 = options == null ? void 0 : options.resolvedOffset) != null ? _b2 : state.scrollPending) != null ? _c2 : state.scroll) != null ? _d : 0;
1006
+ var _a4, _b2, _c2, _d, _e, _f;
1007
+ if (freshDataTransitionEpoch !== state.freshDataTransitionEpoch) {
1008
+ (_a4 = options == null ? void 0 : options.onFinished) == null ? void 0 : _a4.call(options);
1009
+ return;
1010
+ }
1011
+ const shouldReleaseDeferredPublicOnScroll = Platform.OS === "web" && ((_b2 = state.initialScrollSession) == null ? void 0 : _b2.kind) === "bootstrap";
1012
+ const finalScrollOffset = (_e = (_d = (_c2 = options == null ? void 0 : options.resolvedOffset) != null ? _c2 : state.scrollPending) != null ? _d : state.scroll) != null ? _e : 0;
1008
1013
  initialScrollWatchdog.clear(state);
1009
1014
  if ((options == null ? void 0 : options.preserveTarget) && state.initialScroll) {
1010
1015
  state.clearPreservedInitialScrollOnNextFinish = void 0;
@@ -1033,7 +1038,7 @@ function finishInitialScroll(ctx, options) {
1033
1038
  if (shouldReleaseDeferredPublicOnScroll) {
1034
1039
  releaseDeferredPublicOnScroll(ctx, finalScrollOffset);
1035
1040
  }
1036
- (_e = options == null ? void 0 : options.onFinished) == null ? void 0 : _e.call(options);
1041
+ (_f = options == null ? void 0 : options.onFinished) == null ? void 0 : _f.call(options);
1037
1042
  };
1038
1043
  if (options == null ? void 0 : options.waitForCompletionFrame) {
1039
1044
  requestAnimationFrame(complete);
@@ -1317,246 +1322,21 @@ function clampScrollOffset(ctx, offset, scrollTarget) {
1317
1322
  return clampedOffset;
1318
1323
  }
1319
1324
 
1320
- // src/core/finishScrollTo.ts
1321
- function finishScrollTo(ctx) {
1322
- var _a3, _b;
1323
- const state = ctx.state;
1324
- if (state == null ? void 0 : state.scrollingTo) {
1325
- cancelScrollCompletionChecks(state);
1326
- const resolvePendingScroll = state.pendingScrollResolve;
1327
- state.pendingScrollResolve = void 0;
1328
- const scrollingTo = state.scrollingTo;
1329
- state.scrollHistory.length = 0;
1330
- state.scrollingTo = void 0;
1331
- state.scrollTargetPinnedRange = void 0;
1332
- if (state.pendingTotalSize !== void 0) {
1333
- addTotalSize(ctx, null, state.pendingTotalSize);
1334
- }
1335
- if (PlatformAdjustBreaksScroll) {
1336
- state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
1337
- }
1338
- if (scrollingTo.isInitialScroll || state.initialScroll) {
1339
- const isOffsetSession = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
1340
- const shouldPreserveResizeTarget = !!scrollingTo.isInitialScroll && !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_b = state.initialScroll) == null ? void 0 : _b.viewPosition) === 1;
1341
- finishInitialScroll(ctx, {
1342
- onFinished: () => {
1343
- resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1344
- },
1345
- preserveTarget: isOffsetSession && state.props.data.length === 0 || shouldPreserveResizeTarget,
1346
- recalculateItems: true,
1347
- schedulePreservedTargetClear: shouldPreserveResizeTarget,
1348
- syncObservedOffset: isOffsetSession,
1349
- waitForCompletionFrame: !!scrollingTo.waitForInitialScrollCompletionFrame
1350
- });
1351
- return;
1352
- }
1353
- recalculateSettledScroll(ctx);
1354
- resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1355
- }
1356
- }
1357
-
1358
- // src/core/checkFinishedScroll.ts
1359
- var INITIAL_SCROLL_MAX_FALLBACK_CHECKS = 20;
1360
- var INITIAL_SCROLL_COMPLETION_TARGET_EPSILON = 1;
1361
- var INITIAL_SCROLL_ZERO_TARGET_EPSILON = 1;
1362
- var SILENT_INITIAL_SCROLL_RETRY_DELAY_MS = 16;
1363
- var SILENT_INITIAL_SCROLL_TARGET_EPSILON = 1;
1364
- function checkFinishedScroll(ctx, options) {
1365
- const scrollingTo = ctx.state.scrollingTo;
1366
- if (options == null ? void 0 : options.onlyIfAligned) {
1367
- if (!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) || scrollingTo.animated) {
1368
- return;
1369
- }
1370
- if (!getResolvedScrollCompletionState(ctx, scrollingTo).isAtResolvedTarget) {
1371
- return;
1372
- }
1373
- }
1374
- ctx.state.scheduledWork.frame(() => checkFinishedScrollFrame(ctx), "checkFinishedScrollFrame");
1375
- }
1376
- function hasScrollCompletionOwnership(state, options) {
1377
- const { clampedTargetOffset, scrollingTo } = options;
1378
- return !scrollingTo.isInitialScroll || state.hasScrolled || clampedTargetOffset <= INITIAL_SCROLL_COMPLETION_TARGET_EPSILON;
1379
- }
1380
- function isSilentInitialDispatch(state, scrollingTo) {
1381
- return !!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && initialScrollCompletion.didDispatchNativeScroll(state) && !state.hasScrolled;
1382
- }
1383
- function getInitialScrollWatchdogTargetOffset(state) {
1384
- var _a3;
1385
- return (_a3 = initialScrollWatchdog.get(state)) == null ? void 0 : _a3.targetOffset;
1386
- }
1387
- function isNativeInitialNonZeroTarget(state) {
1388
- const targetOffset = getInitialScrollWatchdogTargetOffset(state);
1389
- return !state.didFinishInitialScroll && initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset);
1390
- }
1391
- function shouldFinishInitialScrollWithoutNativeProgress(state, scrollingTo) {
1392
- var _a3, _b;
1393
- if (!scrollingTo.isInitialScroll || scrollingTo.animated || !state.didContainersLayout) {
1394
- return false;
1395
- }
1396
- if (((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap") {
1397
- return false;
1398
- }
1399
- const targetOffset = (_b = scrollingTo.targetOffset) != null ? _b : scrollingTo.offset;
1400
- if (initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset) && initialScrollCompletion.didDispatchNativeScroll(state) && !state.hasScrolled) {
1401
- return false;
1402
- }
1403
- if (initialScrollWatchdog.isAtZeroTargetOffset(targetOffset) || Math.abs(state.scroll - targetOffset) > 1 || Math.abs(state.scrollPending - targetOffset) > 1) {
1404
- return false;
1405
- }
1406
- return !!scrollingTo.waitForInitialScrollCompletionFrame || isNativeInitialNonZeroTarget(state);
1407
- }
1408
- function shouldFinishInitialZeroTargetScroll(ctx) {
1409
- var _a3;
1410
- const { state } = ctx;
1411
- return !!((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) && state.props.data.length > 0 && getContentSize(ctx) <= state.scrollLength && state.scrollPending <= INITIAL_SCROLL_ZERO_TARGET_EPSILON;
1412
- }
1413
- function isEndAlignedLastItemTarget(ctx, scrollingTo) {
1414
- return scrollingTo.index === ctx.state.props.data.length - 1 && scrollingTo.viewPosition === 1;
1415
- }
1416
- function getCurrentTargetOffset(ctx, scrollingTo) {
1417
- var _a3;
1418
- const index = scrollingTo.index;
1419
- const shouldRecomputeEndTarget = isEndAlignedLastItemTarget(ctx, scrollingTo);
1420
- const requestedTargetOffset = shouldRecomputeEndTarget && index !== void 0 ? calculateOffsetWithOffsetPosition(ctx, calculateOffsetForIndex(ctx, index), scrollingTo) : (_a3 = scrollingTo.targetOffset) != null ? _a3 : clampScrollOffset(ctx, scrollingTo.offset - (scrollingTo.viewOffset || 0), scrollingTo);
1421
- return clampScrollOffset(ctx, requestedTargetOffset, scrollingTo);
1422
- }
1423
- function getResolvedScrollCompletionState(ctx, scrollingTo) {
1424
- const { state } = ctx;
1425
- const scroll = state.scrollPending;
1426
- const adjust = state.scrollAdjustHandler.getAdjust();
1427
- const clampedTargetOffset = getCurrentTargetOffset(ctx, scrollingTo);
1428
- const maxOffset = clampScrollOffset(ctx, scroll, scrollingTo);
1429
- const diff1 = Math.abs(scroll - clampedTargetOffset);
1430
- const adjustedTargetOffset = clampedTargetOffset + adjust;
1431
- const diff2 = Math.abs(scroll - adjustedTargetOffset);
1432
- const canUseAdjustedCompletion = !scrollingTo.animated || Platform.OS === "ios";
1433
- return {
1434
- clampedTargetOffset,
1435
- isAtResolvedTarget: Math.abs(scroll - maxOffset) < 1 && (diff1 < 1 || canUseAdjustedCompletion && diff2 < 1)
1436
- };
1437
- }
1438
- function checkFinishedScrollFrame(ctx) {
1439
- const scrollingTo = ctx.state.scrollingTo;
1440
- if (!scrollingTo) {
1441
- return;
1442
- }
1443
- const { state } = ctx;
1444
- const completionState = getResolvedScrollCompletionState(ctx, scrollingTo);
1445
- if (completionState.isAtResolvedTarget && hasScrollCompletionOwnership(state, {
1446
- clampedTargetOffset: completionState.clampedTargetOffset,
1447
- scrollingTo
1448
- })) {
1449
- finishScrollTo(ctx);
1450
- }
1451
- }
1452
- function scrollToFallbackOffset(ctx, offset) {
1453
- var _a3;
1454
- (_a3 = ctx.state.refScroller.current) == null ? void 0 : _a3.scrollTo({
1455
- animated: false,
1456
- x: ctx.state.props.horizontal ? offset : 0,
1457
- y: ctx.state.props.horizontal ? 0 : offset
1458
- });
1459
- }
1460
- function checkFinishedScrollFallback(ctx) {
1461
- const state = ctx.state;
1462
- if (state.scheduledWork.has("checkFinishedScrollFallback")) {
1463
- return;
1464
- }
1465
- const scrollingTo = state.scrollingTo;
1466
- const shouldFinishInitialZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
1467
- const silentInitialDispatch = isSilentInitialDispatch(state, scrollingTo);
1468
- const canFinishInitialWithoutNativeProgress = scrollingTo !== void 0 ? shouldFinishInitialScrollWithoutNativeProgress(state, scrollingTo) : false;
1469
- const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && !shouldFinishInitialZeroTarget && !canFinishInitialWithoutNativeProgress || !state.didContainersLayout;
1470
- const initialDelay = shouldFinishInitialZeroTarget || canFinishInitialWithoutNativeProgress ? 0 : silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : slowTimeout ? 500 : 100;
1471
- let numChecks = 0;
1472
- const scheduleFallbackCheck = (delay) => {
1473
- state.scheduledWork.timeout(checkHasScrolled, delay, "checkFinishedScrollFallback");
1474
- };
1475
- const checkHasScrolled = () => {
1476
- var _a3, _b, _c, _d;
1477
- const isStillScrollingTo = state.scrollingTo;
1478
- if (isStillScrollingTo) {
1479
- numChecks++;
1480
- const isNativeInitialPending = isNativeInitialNonZeroTarget(state) && !state.hasScrolled;
1481
- const maxChecks = silentInitialDispatch ? 5 : isNativeInitialPending ? INITIAL_SCROLL_MAX_FALLBACK_CHECKS : 5;
1482
- const shouldFinishZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
1483
- const canFinishInitialScrollWithoutNativeProgress = shouldFinishInitialScrollWithoutNativeProgress(
1484
- state,
1485
- isStillScrollingTo
1486
- );
1487
- const completionState = getResolvedScrollCompletionState(ctx, isStillScrollingTo);
1488
- const canFinishAfterSilentNativeDispatch = Platform.OS === "android" && silentInitialDispatch && completionState.isAtResolvedTarget && numChecks >= 1;
1489
- const shouldRetrySilentInitialNativeScroll = Platform.OS === "android" && canFinishAfterSilentNativeDispatch && !initialScrollCompletion.didRetrySilentInitialScroll(state);
1490
- const shouldFinishAfterObservedScroll = state.hasScrolled && (!isStillScrollingTo.isInitialScroll || completionState.isAtResolvedTarget);
1491
- const shouldRetryUnalignedInitialScroll = isStillScrollingTo.isInitialScroll && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
1492
- const shouldRetryUnalignedEndScroll = Platform.OS === "ios" && !isStillScrollingTo.isInitialScroll && isEndAlignedLastItemTarget(ctx, isStillScrollingTo) && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
1493
- if (shouldRetrySilentInitialNativeScroll) {
1494
- const targetOffset = (_b = (_a3 = getInitialScrollWatchdogTargetOffset(state)) != null ? _a3 : isStillScrollingTo.targetOffset) != null ? _b : 0;
1495
- const jiggleOffset = targetOffset >= SILENT_INITIAL_SCROLL_TARGET_EPSILON ? targetOffset - SILENT_INITIAL_SCROLL_TARGET_EPSILON : targetOffset + SILENT_INITIAL_SCROLL_TARGET_EPSILON;
1496
- initialScrollCompletion.markSilentInitialScrollRetry(state);
1497
- scrollToFallbackOffset(ctx, jiggleOffset);
1498
- state.scheduledWork.frame(
1499
- () => scrollToFallbackOffset(ctx, targetOffset),
1500
- "checkFinishedScrollRetryFrame"
1501
- );
1502
- scheduleFallbackCheck(SILENT_INITIAL_SCROLL_RETRY_DELAY_MS);
1503
- } else if (shouldRetryUnalignedEndScroll) {
1504
- scrollToFallbackOffset(ctx, completionState.clampedTargetOffset);
1505
- scheduleFallbackCheck(100);
1506
- } else if (shouldFinishZeroTarget || shouldFinishAfterObservedScroll || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
1507
- finishScrollTo(ctx);
1508
- } else if ((isNativeInitialPending || shouldRetryUnalignedInitialScroll) && numChecks <= maxChecks) {
1509
- const targetOffset = (_d = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : isStillScrollingTo.targetOffset) != null ? _d : state.scrollPending;
1510
- scrollToFallbackOffset(ctx, targetOffset);
1511
- scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
1512
- } else {
1513
- scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
1514
- }
1515
- }
1516
- };
1517
- scheduleFallbackCheck(initialDelay);
1518
- }
1519
-
1520
- // src/core/doScrollTo.native.ts
1521
- function doScrollTo(ctx, params) {
1522
- const state = ctx.state;
1523
- const { animated, horizontal, isInitialScroll, offset } = params;
1524
- const isAnimated = !!animated;
1525
- const { refScroller } = state;
1526
- const scroller = refScroller.current;
1527
- if (!scroller) {
1528
- return;
1529
- }
1530
- const isHorizontal = !!horizontal;
1531
- const contentSize = isHorizontal ? getContentSize(ctx) : void 0;
1532
- const nativeOffset = toNativeHorizontalOffset(state, offset, contentSize);
1533
- scroller.scrollTo({
1534
- animated: isAnimated,
1535
- x: isHorizontal ? nativeOffset : 0,
1536
- y: isHorizontal ? 0 : offset
1537
- });
1538
- if (isInitialScroll) {
1539
- initialScrollCompletion.markInitialScrollNativeDispatch(state);
1540
- }
1541
- if (isAnimated && Math.abs(state.scroll - offset) <= 1) {
1542
- checkFinishedScroll(ctx);
1543
- }
1544
- if (!isAnimated) {
1545
- state.scroll = offset;
1546
- checkFinishedScrollFallback(ctx);
1547
- }
1548
- }
1549
-
1550
1325
  // src/core/scrollRequestTracker.ts
1551
1326
  function getScrollRequestTracker(ctx) {
1552
1327
  if (!ctx.scrollRequestTracker) {
1553
1328
  let currentToken = 0;
1554
- const start = (resolve) => {
1329
+ let waitingToken;
1330
+ const start = (resolve, isInternal = false) => {
1555
1331
  const state = ctx.state;
1332
+ if (!isInternal && (state.maintainingScrollAtEnd || state.pendingMaintainScrollAtEnd)) {
1333
+ finishMaintainScrollAtEnd(ctx);
1334
+ }
1556
1335
  state.scheduledWork.cancel("imperativeScrollReady");
1557
1336
  const token = ++currentToken;
1558
1337
  settlePendingImperativeScroll(state);
1559
1338
  state.pendingScrollResolve = resolve;
1339
+ waitingToken = token;
1560
1340
  return token;
1561
1341
  };
1562
1342
  const runNow = (token, resolve, run) => {
@@ -1564,6 +1344,7 @@ function getScrollRequestTracker(ctx) {
1564
1344
  if (token !== currentToken) {
1565
1345
  return;
1566
1346
  }
1347
+ waitingToken = void 0;
1567
1348
  const didStartScroll = run();
1568
1349
  if (!didStartScroll || !state.scrollingTo) {
1569
1350
  if (state.pendingScrollResolve === resolve) {
@@ -1574,6 +1355,7 @@ function getScrollRequestTracker(ctx) {
1574
1355
  };
1575
1356
  ctx.scrollRequestTracker = {
1576
1357
  isCurrent: (token) => token === currentToken,
1358
+ isWaitingToRun: () => waitingToken === currentToken && !!ctx.state.pendingScrollResolve,
1577
1359
  runNow,
1578
1360
  runNowIfIdle: (run) => {
1579
1361
  const state = ctx.state;
@@ -1581,7 +1363,7 @@ function getScrollRequestTracker(ctx) {
1581
1363
  return Promise.resolve();
1582
1364
  }
1583
1365
  return new Promise((resolve) => {
1584
- const token = start(resolve);
1366
+ const token = start(resolve, true);
1585
1367
  runNow(token, resolve, run);
1586
1368
  });
1587
1369
  },
@@ -1645,96 +1427,15 @@ function requestAdjust(ctx, positionDiff, source) {
1645
1427
  }
1646
1428
  }
1647
1429
 
1648
- // src/core/doMaintainScrollAtEnd.ts
1649
- function finishMaintainScrollAtEnd(ctx) {
1650
- var _a3;
1651
- const { state } = ctx;
1652
- const currentPadding = peek$(ctx, "alignItemsAtEndPadding") || 0;
1653
- const nextPadding = getAlignItemsAtEndPadding(ctx);
1654
- state.maintainingScrollAtEnd = void 0;
1655
- state.pendingMaintainScrollAtEnd = false;
1656
- if (currentPadding !== nextPadding) {
1657
- set$(ctx, "alignItemsAtEndPadding", nextPadding);
1658
- state.scrollForNextCalculateItemsInView = void 0;
1659
- (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { forceFullItemPositions: true });
1660
- const nextScroll = clampScrollOffset(ctx, state.scroll + nextPadding - currentPadding);
1661
- requestAdjust(ctx, nextScroll - state.scroll);
1662
- }
1663
- }
1664
- function doMaintainScrollAtEnd(ctx) {
1665
- const state = ctx.state;
1666
- const {
1667
- didContainersLayout,
1668
- didFinishInitialScroll,
1669
- pendingNativeMVCPAdjust,
1670
- props: { maintainScrollAtEnd }
1671
- } = state;
1672
- const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
1673
- const isReplayingPendingMaintain = !!state.pendingMaintainScrollAtEnd;
1674
- const shouldMaintainScrollAtEnd = !!((isWithinMaintainScrollAtEndThreshold || isReplayingPendingMaintain) && maintainScrollAtEnd && didFinishInitialScroll);
1675
- if (shouldMaintainScrollAtEnd && !didContainersLayout) {
1676
- state.pendingMaintainScrollAtEnd = true;
1677
- return false;
1678
- }
1679
- if (pendingNativeMVCPAdjust) {
1680
- state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
1681
- return false;
1682
- }
1683
- if (shouldMaintainScrollAtEnd && didContainersLayout) {
1684
- state.pendingMaintainScrollAtEnd = false;
1685
- const contentSize = getContentSize(ctx);
1686
- if (contentSize < state.scrollLength) {
1687
- state.scroll = 0;
1688
- }
1689
- if (!state.maintainingScrollAtEnd) {
1690
- const pendingState = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
1691
- const activeState = maintainScrollAtEnd.animated ? "animated" : "instant";
1692
- const scrollAtRequest = state.scroll;
1693
- state.maintainingScrollAtEnd = pendingState;
1694
- requestAnimationFrame(() => {
1695
- if (state.maintainingScrollAtEnd !== pendingState) {
1696
- return;
1697
- }
1698
- const isStillWithinThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
1699
- const didScrollSinceRequest = state.scroll !== scrollAtRequest;
1700
- if (isReplayingPendingMaintain || isStillWithinThreshold || !didScrollSinceRequest) {
1701
- state.maintainingScrollAtEnd = activeState;
1702
- const scrollPromise = getScrollRequestTracker(ctx).runNowIfIdle(
1703
- () => ctx.scrollToEnd({ animated: maintainScrollAtEnd.animated })
1704
- );
1705
- void scrollPromise.then(() => {
1706
- if (state.maintainingScrollAtEnd !== activeState) {
1707
- return;
1708
- }
1709
- if (state.pendingMaintainScrollAtEnd) {
1710
- state.maintainingScrollAtEnd = void 0;
1711
- doMaintainScrollAtEnd(ctx);
1712
- } else {
1713
- finishMaintainScrollAtEnd(ctx);
1714
- }
1715
- });
1716
- } else if (state.maintainingScrollAtEnd === pendingState) {
1717
- finishMaintainScrollAtEnd(ctx);
1718
- }
1719
- });
1720
- } else {
1721
- state.pendingMaintainScrollAtEnd = true;
1722
- }
1723
- return true;
1724
- }
1725
- finishMaintainScrollAtEnd(ctx);
1726
- return false;
1727
- }
1728
-
1729
- // src/core/mvcp.ts
1730
- var MVCP_POSITION_EPSILON = 0.1;
1731
- var MVCP_ANCHOR_LOCK_TTL_MS = 300;
1732
- var MVCP_ANCHOR_LOCK_QUIET_PASSES_TO_RELEASE = 2;
1733
- var NATIVE_END_CLAMP_EPSILON = 1;
1734
- function resolveAnchorLock(state, enableMVCPAnchorLock, mvcpData, now) {
1735
- if (!enableMVCPAnchorLock) {
1736
- state.mvcpAnchorLock = void 0;
1737
- return void 0;
1430
+ // src/core/mvcp.ts
1431
+ var MVCP_POSITION_EPSILON = 0.1;
1432
+ var MVCP_ANCHOR_LOCK_TTL_MS = 300;
1433
+ var MVCP_ANCHOR_LOCK_QUIET_PASSES_TO_RELEASE = 2;
1434
+ var NATIVE_END_CLAMP_EPSILON = 1;
1435
+ function resolveAnchorLock(state, enableMVCPAnchorLock, mvcpData, now) {
1436
+ if (!enableMVCPAnchorLock) {
1437
+ state.mvcpAnchorLock = void 0;
1438
+ return void 0;
1738
1439
  }
1739
1440
  const lock = state.mvcpAnchorLock;
1740
1441
  if (!lock) {
@@ -2024,144 +1725,494 @@ function prepareMVCP(ctx, dataChanged, adjustmentSource) {
2024
1725
  };
2025
1726
  }
2026
1727
  }
2027
-
2028
- // src/platform/flushSync.native.ts
2029
- var flushSync = (fn) => {
2030
- fn();
2031
- };
2032
-
2033
- // src/utils/getScrollVelocity.ts
2034
- var MAX_SCROLL_VELOCITY_WINDOW_MS = 1e3;
2035
- var SCROLL_VELOCITY_HALF_LIFE_MS = 200;
2036
- var getScrollVelocity = (state) => {
2037
- const { scrollHistory } = state;
2038
- const newestIndex = scrollHistory.length - 1;
2039
- if (newestIndex < 1) {
2040
- return 0;
2041
- }
2042
- const newest = scrollHistory[newestIndex];
2043
- if (Date.now() - newest.time > MAX_SCROLL_VELOCITY_WINDOW_MS) {
2044
- return 0;
1728
+
1729
+ // src/platform/flushSync.native.ts
1730
+ var flushSync = (fn) => {
1731
+ fn();
1732
+ };
1733
+
1734
+ // src/utils/getScrollVelocity.ts
1735
+ var MAX_SCROLL_VELOCITY_WINDOW_MS = 1e3;
1736
+ var SCROLL_VELOCITY_HALF_LIFE_MS = 200;
1737
+ var getScrollVelocity = (state) => {
1738
+ const { scrollHistory } = state;
1739
+ const newestIndex = scrollHistory.length - 1;
1740
+ if (newestIndex < 1) {
1741
+ return 0;
1742
+ }
1743
+ const newest = scrollHistory[newestIndex];
1744
+ if (Date.now() - newest.time > MAX_SCROLL_VELOCITY_WINDOW_MS) {
1745
+ return 0;
1746
+ }
1747
+ let direction = 0;
1748
+ let weightedVelocity = 0;
1749
+ let totalWeight = 0;
1750
+ for (let i = newestIndex; i > 0; i--) {
1751
+ const current = scrollHistory[i];
1752
+ const previous = scrollHistory[i - 1];
1753
+ const scrollDiff = current.scroll - previous.scroll;
1754
+ const timeDiff = current.time - previous.time;
1755
+ const deltaSign = Math.sign(scrollDiff);
1756
+ if (deltaSign !== 0) {
1757
+ if (direction === 0) {
1758
+ direction = deltaSign;
1759
+ } else if (deltaSign !== direction) {
1760
+ break;
1761
+ }
1762
+ }
1763
+ if (newest.time - previous.time > MAX_SCROLL_VELOCITY_WINDOW_MS) {
1764
+ break;
1765
+ }
1766
+ if (scrollDiff === 0 || timeDiff <= 0) {
1767
+ continue;
1768
+ }
1769
+ const age = newest.time - current.time;
1770
+ const weight = Math.exp(-age / SCROLL_VELOCITY_HALF_LIFE_MS);
1771
+ weightedVelocity += scrollDiff / timeDiff * weight;
1772
+ totalWeight += weight;
1773
+ }
1774
+ return totalWeight > 0 ? weightedVelocity / totalWeight : 0;
1775
+ };
1776
+
1777
+ // src/utils/isInMVCPActiveMode.native.ts
1778
+ function isInMVCPActiveMode(state) {
1779
+ return state.dataChangeNeedsScrollUpdate;
1780
+ }
1781
+
1782
+ // src/core/updateScroll.ts
1783
+ function updateScroll(ctx, newScroll, forceUpdate, options) {
1784
+ var _a3;
1785
+ const state = ctx.state;
1786
+ const { ignoreScrollFromMVCP, lastScrollAdjustForHistory, scrollAdjustHandler, scrollHistory, scrollingTo } = state;
1787
+ const prevScroll = state.scroll;
1788
+ if ((options == null ? void 0 : options.markHasScrolled) !== false) {
1789
+ state.hasScrolled = true;
1790
+ }
1791
+ const currentTime = Date.now();
1792
+ state.lastBatchingAction = currentTime;
1793
+ const adjust = scrollAdjustHandler.getAdjust();
1794
+ const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
1795
+ if (adjustChanged) {
1796
+ scrollHistory.length = 0;
1797
+ }
1798
+ state.lastScrollAdjustForHistory = adjust;
1799
+ if (scrollingTo === void 0 && !(scrollHistory.length === 0 && newScroll === state.scroll)) {
1800
+ if (!adjustChanged) {
1801
+ scrollHistory.push({ scroll: newScroll, time: currentTime });
1802
+ }
1803
+ }
1804
+ if (scrollHistory.length > 5) {
1805
+ scrollHistory.shift();
1806
+ }
1807
+ if (ignoreScrollFromMVCP && !scrollingTo) {
1808
+ const { lt, gt } = ignoreScrollFromMVCP;
1809
+ if (lt && newScroll < lt || gt && newScroll > gt) {
1810
+ state.ignoreScrollFromMVCPIgnored = true;
1811
+ return;
1812
+ }
1813
+ }
1814
+ state.scrollPrev = prevScroll;
1815
+ state.scrollPrevTime = state.scrollTime;
1816
+ state.scroll = newScroll;
1817
+ state.scrollTime = currentTime;
1818
+ const scrollDelta = Math.abs(newScroll - prevScroll);
1819
+ const isUserScrollEvent = !!(options == null ? void 0 : options.fromNativeScrollEvent) && scrollDelta > 0.1 && !adjustChanged && scrollingTo === void 0 && !state.pendingNativeMVCPAdjust;
1820
+ const didCancelMaintainScrollAtEnd = isUserScrollEvent && newScroll < prevScroll && !!(state.maintainingScrollAtEnd || state.pendingMaintainScrollAtEnd);
1821
+ if (didCancelMaintainScrollAtEnd) {
1822
+ finishMaintainScrollAtEnd(ctx);
1823
+ }
1824
+ const allowedEdge = isUserScrollEvent ? beginReachedEdgeUserScroll(ctx, newScroll - prevScroll) : void 0;
1825
+ const didResolvePendingNativeMVCPAdjust = resolvePendingNativeMVCPAdjust(ctx, newScroll);
1826
+ const scrollLength = state.scrollLength;
1827
+ const isLargeUserScrollJump = scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength && !state.pendingNativeMVCPAdjust;
1828
+ const scrollVelocity = getScrollVelocity(state);
1829
+ updateAdaptiveRender(ctx, scrollVelocity, { forceLight: isLargeUserScrollJump });
1830
+ const lastCalculated = state.scrollLastCalculate;
1831
+ const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
1832
+ const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || didCancelMaintainScrollAtEnd || allowedEdge !== void 0 || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
1833
+ if (shouldUpdate) {
1834
+ state.scrollLastCalculate = state.scroll;
1835
+ state.ignoreScrollFromMVCPIgnored = false;
1836
+ state.lastScrollDelta = scrollDelta;
1837
+ const runCalculateItems = () => {
1838
+ var _a4;
1839
+ const calculateItemsParams = {
1840
+ doMVCP: scrollingTo !== void 0,
1841
+ scrollVelocity
1842
+ };
1843
+ if (isLargeUserScrollJump) {
1844
+ calculateItemsParams.drawDistanceMode = "visible-first";
1845
+ }
1846
+ (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, calculateItemsParams);
1847
+ checkThresholds(ctx, allowedEdge);
1848
+ };
1849
+ if (isLargeUserScrollJump) {
1850
+ state.mvcpAnchorLock = void 0;
1851
+ state.pendingNativeMVCPAdjust = void 0;
1852
+ state.userScrollAnchorReset = { keys: /* @__PURE__ */ new Set() };
1853
+ state.scheduledWork.cancel("mvcpRecalculate");
1854
+ flushSync(runCalculateItems);
1855
+ scheduleFullDrawDistancePrewarm(ctx);
1856
+ } else {
1857
+ runCalculateItems();
1858
+ }
1859
+ const shouldMaintainScrollAtEndAfterPendingSettle = !!state.pendingMaintainScrollAtEnd || !!((_a3 = state.props.maintainScrollAtEnd) == null ? void 0 : _a3.onDataChange);
1860
+ if (didResolvePendingNativeMVCPAdjust && shouldMaintainScrollAtEndAfterPendingSettle) {
1861
+ state.pendingMaintainScrollAtEnd = false;
1862
+ doMaintainScrollAtEnd(ctx);
1863
+ }
1864
+ state.dataChangeNeedsScrollUpdate = false;
1865
+ state.lastScrollDelta = 0;
1866
+ }
1867
+ }
1868
+
1869
+ // src/core/doMaintainScrollAtEnd.ts
1870
+ function interruptMaintainScrollAtEnd(ctx) {
1871
+ var _a3;
1872
+ const { state } = ctx;
1873
+ const maintaining = state.maintainingScrollAtEnd;
1874
+ const cancelActiveScroll = !!(maintaining === "animated" || maintaining === "instant" || maintaining && ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isScrollToEnd));
1875
+ if (cancelActiveScroll) {
1876
+ cancelImperativeScroll(state);
1877
+ }
1878
+ if (maintaining || state.pendingMaintainScrollAtEnd) {
1879
+ finishMaintainScrollAtEnd(ctx);
1880
+ }
1881
+ return cancelActiveScroll;
1882
+ }
1883
+ function finishMaintainScrollAtEnd(ctx) {
1884
+ var _a3;
1885
+ const { state } = ctx;
1886
+ const currentPadding = peek$(ctx, "alignItemsAtEndPadding") || 0;
1887
+ const nextPadding = getAlignItemsAtEndPadding(ctx);
1888
+ state.maintainingScrollAtEnd = void 0;
1889
+ state.pendingMaintainScrollAtEnd = false;
1890
+ if (currentPadding !== nextPadding) {
1891
+ set$(ctx, "alignItemsAtEndPadding", nextPadding);
1892
+ state.scrollForNextCalculateItemsInView = void 0;
1893
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { forceFullItemPositions: true });
1894
+ const nextScroll = clampScrollOffset(ctx, state.scroll + nextPadding - currentPadding);
1895
+ requestAdjust(ctx, nextScroll - state.scroll);
1896
+ }
1897
+ }
1898
+ function doMaintainScrollAtEnd(ctx) {
1899
+ var _a3, _b, _c, _d, _e;
1900
+ const state = ctx.state;
1901
+ const {
1902
+ didContainersLayout,
1903
+ didFinishInitialScroll,
1904
+ pendingNativeMVCPAdjust,
1905
+ props: { maintainScrollAtEnd }
1906
+ } = state;
1907
+ const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
1908
+ const isReplayingPendingMaintain = !!state.pendingMaintainScrollAtEnd;
1909
+ const shouldMaintainScrollAtEnd = !!(!((_a3 = ctx.scrollRequestTracker) == null ? void 0 : _a3.isWaitingToRun()) && (!state.scrollingTo || state.scrollingTo.isScrollToEnd) && (isWithinMaintainScrollAtEndThreshold || isReplayingPendingMaintain || ((_b = state.scrollingTo) == null ? void 0 : _b.isScrollToEnd)) && maintainScrollAtEnd && didFinishInitialScroll);
1910
+ if (shouldMaintainScrollAtEnd && !state.scrollingTo) {
1911
+ const scroller = state.refScroller.current;
1912
+ const nativeMax = (_c = scroller == null ? void 0 : scroller.getMaxScrollOffset) == null ? void 0 : _c.call(scroller);
1913
+ const observed = (_d = scroller == null ? void 0 : scroller.getCurrentScrollOffset) == null ? void 0 : _d.call(scroller);
1914
+ if (nativeMax !== void 0 && observed !== void 0 && nativeMax < state.scroll - 1 && Math.abs(observed - nativeMax) <= 1) {
1915
+ state.pendingMaintainScrollAtEnd = true;
1916
+ state.scrollPending = observed;
1917
+ updateScroll(ctx, observed, true, { markHasScrolled: false });
1918
+ }
1919
+ }
1920
+ if (shouldMaintainScrollAtEnd && !didContainersLayout) {
1921
+ state.pendingMaintainScrollAtEnd = true;
1922
+ return false;
1923
+ }
1924
+ if (pendingNativeMVCPAdjust) {
1925
+ state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
1926
+ return false;
1927
+ }
1928
+ if (shouldMaintainScrollAtEnd && (state.scrollingTo || state.pendingScrollResolve)) {
1929
+ state.pendingMaintainScrollAtEnd = true;
1930
+ (_e = state.maintainingScrollAtEnd) != null ? _e : state.maintainingScrollAtEnd = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
1931
+ return false;
1932
+ }
1933
+ if (shouldMaintainScrollAtEnd && didContainersLayout) {
1934
+ state.pendingMaintainScrollAtEnd = false;
1935
+ const contentSize = getContentSize(ctx);
1936
+ if (contentSize < state.scrollLength) {
1937
+ state.scroll = 0;
1938
+ }
1939
+ if (!state.maintainingScrollAtEnd) {
1940
+ const pendingState = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
1941
+ const activeState = maintainScrollAtEnd.animated ? "animated" : "instant";
1942
+ const scrollAtRequest = state.scroll;
1943
+ state.maintainingScrollAtEnd = pendingState;
1944
+ requestAnimationFrame(() => {
1945
+ if (state.maintainingScrollAtEnd !== pendingState) {
1946
+ return;
1947
+ }
1948
+ const isStillWithinThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
1949
+ const didScrollSinceRequest = state.scroll !== scrollAtRequest;
1950
+ if (isReplayingPendingMaintain || isStillWithinThreshold || !didScrollSinceRequest) {
1951
+ if (state.scrollingTo || state.pendingScrollResolve) {
1952
+ state.pendingMaintainScrollAtEnd = true;
1953
+ return;
1954
+ }
1955
+ state.maintainingScrollAtEnd = activeState;
1956
+ const scrollPromise = getScrollRequestTracker(ctx).runNowIfIdle(
1957
+ () => ctx.scrollToEnd({ animated: maintainScrollAtEnd.animated })
1958
+ );
1959
+ void scrollPromise.then(() => {
1960
+ if (state.maintainingScrollAtEnd !== activeState) {
1961
+ return;
1962
+ }
1963
+ if (state.pendingMaintainScrollAtEnd) {
1964
+ state.maintainingScrollAtEnd = void 0;
1965
+ doMaintainScrollAtEnd(ctx);
1966
+ } else {
1967
+ finishMaintainScrollAtEnd(ctx);
1968
+ }
1969
+ });
1970
+ } else if (state.maintainingScrollAtEnd === pendingState) {
1971
+ finishMaintainScrollAtEnd(ctx);
1972
+ }
1973
+ });
1974
+ } else {
1975
+ state.pendingMaintainScrollAtEnd = true;
1976
+ }
1977
+ return true;
1978
+ }
1979
+ finishMaintainScrollAtEnd(ctx);
1980
+ return false;
1981
+ }
1982
+
1983
+ // src/core/finishScrollTo.ts
1984
+ function finishScrollTo(ctx) {
1985
+ var _a3, _b, _c;
1986
+ const state = ctx.state;
1987
+ if (state == null ? void 0 : state.scrollingTo) {
1988
+ cancelScrollCompletionChecks(state);
1989
+ const resolvePendingScroll = ((_a3 = ctx.scrollRequestTracker) == null ? void 0 : _a3.isWaitingToRun()) ? void 0 : state.pendingScrollResolve;
1990
+ if (resolvePendingScroll) {
1991
+ state.pendingScrollResolve = void 0;
1992
+ }
1993
+ const scrollingTo = state.scrollingTo;
1994
+ state.scrollHistory.length = 0;
1995
+ state.scrollingTo = void 0;
1996
+ state.scrollTargetPinnedRange = void 0;
1997
+ if (state.pendingTotalSize !== void 0) {
1998
+ addTotalSize(ctx, null, state.pendingTotalSize);
1999
+ }
2000
+ if (PlatformAdjustBreaksScroll) {
2001
+ state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
2002
+ }
2003
+ if (scrollingTo.isInitialScroll || state.initialScroll) {
2004
+ const isOffsetSession = ((_b = state.initialScrollSession) == null ? void 0 : _b.kind) === "offset";
2005
+ const shouldPreserveResizeTarget = !!scrollingTo.isInitialScroll && !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_c = state.initialScroll) == null ? void 0 : _c.viewPosition) === 1;
2006
+ finishInitialScroll(ctx, {
2007
+ onFinished: () => {
2008
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
2009
+ },
2010
+ preserveTarget: isOffsetSession && state.props.data.length === 0 || shouldPreserveResizeTarget,
2011
+ recalculateItems: true,
2012
+ schedulePreservedTargetClear: shouldPreserveResizeTarget,
2013
+ syncObservedOffset: isOffsetSession,
2014
+ waitForCompletionFrame: !!scrollingTo.waitForInitialScrollCompletionFrame
2015
+ });
2016
+ return;
2017
+ }
2018
+ recalculateSettledScroll(ctx);
2019
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
2020
+ if (state.pendingMaintainScrollAtEnd) {
2021
+ state.maintainingScrollAtEnd = void 0;
2022
+ doMaintainScrollAtEnd(ctx);
2023
+ }
2024
+ }
2025
+ }
2026
+
2027
+ // src/core/checkFinishedScroll.ts
2028
+ var INITIAL_SCROLL_MAX_FALLBACK_CHECKS = 20;
2029
+ var INITIAL_SCROLL_COMPLETION_TARGET_EPSILON = 1;
2030
+ var INITIAL_SCROLL_ZERO_TARGET_EPSILON = 1;
2031
+ var SILENT_INITIAL_SCROLL_RETRY_DELAY_MS = 16;
2032
+ var SILENT_INITIAL_SCROLL_TARGET_EPSILON = 1;
2033
+ function checkFinishedScroll(ctx, options) {
2034
+ const scrollingTo = ctx.state.scrollingTo;
2035
+ if (options == null ? void 0 : options.onlyIfAligned) {
2036
+ if (!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) || scrollingTo.animated) {
2037
+ return;
2038
+ }
2039
+ if (!getResolvedScrollCompletionState(ctx, scrollingTo).isAtResolvedTarget) {
2040
+ return;
2041
+ }
2042
+ }
2043
+ ctx.state.scheduledWork.frame(() => checkFinishedScrollFrame(ctx), "checkFinishedScrollFrame");
2044
+ }
2045
+ function hasScrollCompletionOwnership(state, options) {
2046
+ const { clampedTargetOffset, scrollingTo } = options;
2047
+ return !scrollingTo.isInitialScroll || state.hasScrolled || clampedTargetOffset <= INITIAL_SCROLL_COMPLETION_TARGET_EPSILON;
2048
+ }
2049
+ function isSilentInitialDispatch(state, scrollingTo) {
2050
+ return !!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && initialScrollCompletion.didDispatchNativeScroll(state) && !state.hasScrolled;
2051
+ }
2052
+ function getInitialScrollWatchdogTargetOffset(state) {
2053
+ var _a3;
2054
+ return (_a3 = initialScrollWatchdog.get(state)) == null ? void 0 : _a3.targetOffset;
2055
+ }
2056
+ function isNativeInitialNonZeroTarget(state) {
2057
+ const targetOffset = getInitialScrollWatchdogTargetOffset(state);
2058
+ return !state.didFinishInitialScroll && initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset);
2059
+ }
2060
+ function shouldFinishInitialScrollWithoutNativeProgress(state, scrollingTo) {
2061
+ var _a3, _b;
2062
+ if (!scrollingTo.isInitialScroll || scrollingTo.animated || !state.didContainersLayout) {
2063
+ return false;
2064
+ }
2065
+ if (((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap") {
2066
+ return false;
2067
+ }
2068
+ const targetOffset = (_b = scrollingTo.targetOffset) != null ? _b : scrollingTo.offset;
2069
+ if (initialScrollWatchdog.hasNonZeroTargetOffset(targetOffset) && initialScrollCompletion.didDispatchNativeScroll(state) && !state.hasScrolled) {
2070
+ return false;
2071
+ }
2072
+ if (initialScrollWatchdog.isAtZeroTargetOffset(targetOffset) || Math.abs(state.scroll - targetOffset) > 1 || Math.abs(state.scrollPending - targetOffset) > 1) {
2073
+ return false;
2074
+ }
2075
+ return !!scrollingTo.waitForInitialScrollCompletionFrame || isNativeInitialNonZeroTarget(state);
2076
+ }
2077
+ function shouldFinishInitialZeroTargetScroll(ctx) {
2078
+ var _a3;
2079
+ const { state } = ctx;
2080
+ return !!((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) && state.props.data.length > 0 && getContentSize(ctx) <= state.scrollLength && state.scrollPending <= INITIAL_SCROLL_ZERO_TARGET_EPSILON;
2081
+ }
2082
+ function isEndAlignedLastItemTarget(ctx, scrollingTo) {
2083
+ return scrollingTo.index === ctx.state.props.data.length - 1 && scrollingTo.viewPosition === 1;
2084
+ }
2085
+ function getCurrentTargetOffset(ctx, scrollingTo) {
2086
+ var _a3;
2087
+ const index = scrollingTo.index;
2088
+ const shouldRecomputeEndTarget = isEndAlignedLastItemTarget(ctx, scrollingTo);
2089
+ const requestedTargetOffset = shouldRecomputeEndTarget && index !== void 0 ? calculateOffsetWithOffsetPosition(ctx, calculateOffsetForIndex(ctx, index), scrollingTo) : (_a3 = scrollingTo.targetOffset) != null ? _a3 : clampScrollOffset(ctx, scrollingTo.offset - (scrollingTo.viewOffset || 0), scrollingTo);
2090
+ return clampScrollOffset(ctx, requestedTargetOffset, scrollingTo);
2091
+ }
2092
+ function getResolvedScrollCompletionState(ctx, scrollingTo) {
2093
+ const { state } = ctx;
2094
+ const scroll = state.scrollPending;
2095
+ const adjust = state.scrollAdjustHandler.getAdjust();
2096
+ const clampedTargetOffset = getCurrentTargetOffset(ctx, scrollingTo);
2097
+ const maxOffset = clampScrollOffset(ctx, scroll, scrollingTo);
2098
+ const diff1 = Math.abs(scroll - clampedTargetOffset);
2099
+ const adjustedTargetOffset = clampedTargetOffset + adjust;
2100
+ const diff2 = Math.abs(scroll - adjustedTargetOffset);
2101
+ const canUseAdjustedCompletion = !scrollingTo.animated || Platform.OS === "ios";
2102
+ return {
2103
+ clampedTargetOffset,
2104
+ isAtResolvedTarget: Math.abs(scroll - maxOffset) < 1 && (diff1 < 1 || canUseAdjustedCompletion && diff2 < 1)
2105
+ };
2106
+ }
2107
+ function checkFinishedScrollFrame(ctx) {
2108
+ const scrollingTo = ctx.state.scrollingTo;
2109
+ if (!scrollingTo) {
2110
+ return;
2045
2111
  }
2046
- let direction = 0;
2047
- let weightedVelocity = 0;
2048
- let totalWeight = 0;
2049
- for (let i = newestIndex; i > 0; i--) {
2050
- const current = scrollHistory[i];
2051
- const previous = scrollHistory[i - 1];
2052
- const scrollDiff = current.scroll - previous.scroll;
2053
- const timeDiff = current.time - previous.time;
2054
- const deltaSign = Math.sign(scrollDiff);
2055
- if (deltaSign !== 0) {
2056
- if (direction === 0) {
2057
- direction = deltaSign;
2058
- } else if (deltaSign !== direction) {
2059
- break;
2060
- }
2061
- }
2062
- if (newest.time - previous.time > MAX_SCROLL_VELOCITY_WINDOW_MS) {
2063
- break;
2064
- }
2065
- if (scrollDiff === 0 || timeDiff <= 0) {
2066
- continue;
2067
- }
2068
- const age = newest.time - current.time;
2069
- const weight = Math.exp(-age / SCROLL_VELOCITY_HALF_LIFE_MS);
2070
- weightedVelocity += scrollDiff / timeDiff * weight;
2071
- totalWeight += weight;
2112
+ const { state } = ctx;
2113
+ const completionState = getResolvedScrollCompletionState(ctx, scrollingTo);
2114
+ if (completionState.isAtResolvedTarget && hasScrollCompletionOwnership(state, {
2115
+ clampedTargetOffset: completionState.clampedTargetOffset,
2116
+ scrollingTo
2117
+ })) {
2118
+ finishScrollTo(ctx);
2072
2119
  }
2073
- return totalWeight > 0 ? weightedVelocity / totalWeight : 0;
2074
- };
2075
-
2076
- // src/utils/isInMVCPActiveMode.native.ts
2077
- function isInMVCPActiveMode(state) {
2078
- return state.dataChangeNeedsScrollUpdate;
2079
2120
  }
2080
-
2081
- // src/core/updateScroll.ts
2082
- function updateScroll(ctx, newScroll, forceUpdate, options) {
2121
+ function scrollToFallbackOffset(ctx, offset) {
2083
2122
  var _a3;
2123
+ (_a3 = ctx.state.refScroller.current) == null ? void 0 : _a3.scrollTo({
2124
+ animated: false,
2125
+ x: ctx.state.props.horizontal ? offset : 0,
2126
+ y: ctx.state.props.horizontal ? 0 : offset
2127
+ });
2128
+ }
2129
+ function checkFinishedScrollFallback(ctx) {
2084
2130
  const state = ctx.state;
2085
- const { ignoreScrollFromMVCP, lastScrollAdjustForHistory, scrollAdjustHandler, scrollHistory, scrollingTo } = state;
2086
- const prevScroll = state.scroll;
2087
- if ((options == null ? void 0 : options.markHasScrolled) !== false) {
2088
- state.hasScrolled = true;
2089
- }
2090
- const currentTime = Date.now();
2091
- state.lastBatchingAction = currentTime;
2092
- const adjust = scrollAdjustHandler.getAdjust();
2093
- const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
2094
- if (adjustChanged) {
2095
- scrollHistory.length = 0;
2131
+ if (state.scheduledWork.has("checkFinishedScrollFallback")) {
2132
+ return;
2096
2133
  }
2097
- state.lastScrollAdjustForHistory = adjust;
2098
- if (scrollingTo === void 0 && !(scrollHistory.length === 0 && newScroll === state.scroll)) {
2099
- if (!adjustChanged) {
2100
- scrollHistory.push({ scroll: newScroll, time: currentTime });
2134
+ const scrollingTo = state.scrollingTo;
2135
+ const shouldFinishInitialZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
2136
+ const silentInitialDispatch = isSilentInitialDispatch(state, scrollingTo);
2137
+ const canFinishInitialWithoutNativeProgress = scrollingTo !== void 0 ? shouldFinishInitialScrollWithoutNativeProgress(state, scrollingTo) : false;
2138
+ const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && !shouldFinishInitialZeroTarget && !canFinishInitialWithoutNativeProgress || !state.didContainersLayout;
2139
+ const initialDelay = shouldFinishInitialZeroTarget || canFinishInitialWithoutNativeProgress ? 0 : silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : slowTimeout ? 500 : 100;
2140
+ let numChecks = 0;
2141
+ const scheduleFallbackCheck = (delay) => {
2142
+ state.scheduledWork.timeout(checkHasScrolled, delay, "checkFinishedScrollFallback");
2143
+ };
2144
+ const checkHasScrolled = () => {
2145
+ var _a3, _b, _c, _d;
2146
+ const isStillScrollingTo = state.scrollingTo;
2147
+ if (isStillScrollingTo) {
2148
+ numChecks++;
2149
+ const isNativeInitialPending = isNativeInitialNonZeroTarget(state) && !state.hasScrolled;
2150
+ const maxChecks = silentInitialDispatch ? 5 : isNativeInitialPending ? INITIAL_SCROLL_MAX_FALLBACK_CHECKS : 5;
2151
+ const shouldFinishZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
2152
+ const canFinishInitialScrollWithoutNativeProgress = shouldFinishInitialScrollWithoutNativeProgress(
2153
+ state,
2154
+ isStillScrollingTo
2155
+ );
2156
+ const completionState = getResolvedScrollCompletionState(ctx, isStillScrollingTo);
2157
+ const canFinishAfterSilentNativeDispatch = Platform.OS === "android" && silentInitialDispatch && completionState.isAtResolvedTarget && numChecks >= 1;
2158
+ const shouldRetrySilentInitialNativeScroll = Platform.OS === "android" && canFinishAfterSilentNativeDispatch && !initialScrollCompletion.didRetrySilentInitialScroll(state);
2159
+ const shouldFinishAfterObservedScroll = state.hasScrolled && (!isStillScrollingTo.isInitialScroll || completionState.isAtResolvedTarget);
2160
+ const shouldRetryUnalignedInitialScroll = isStillScrollingTo.isInitialScroll && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
2161
+ const shouldRetryUnalignedEndScroll = Platform.OS !== "web" && !isStillScrollingTo.isInitialScroll && isEndAlignedLastItemTarget(ctx, isStillScrollingTo) && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
2162
+ if (shouldRetrySilentInitialNativeScroll) {
2163
+ const targetOffset = (_b = (_a3 = getInitialScrollWatchdogTargetOffset(state)) != null ? _a3 : isStillScrollingTo.targetOffset) != null ? _b : 0;
2164
+ const jiggleOffset = targetOffset >= SILENT_INITIAL_SCROLL_TARGET_EPSILON ? targetOffset - SILENT_INITIAL_SCROLL_TARGET_EPSILON : targetOffset + SILENT_INITIAL_SCROLL_TARGET_EPSILON;
2165
+ initialScrollCompletion.markSilentInitialScrollRetry(state);
2166
+ scrollToFallbackOffset(ctx, jiggleOffset);
2167
+ state.scheduledWork.frame(
2168
+ () => scrollToFallbackOffset(ctx, targetOffset),
2169
+ "checkFinishedScrollRetryFrame"
2170
+ );
2171
+ scheduleFallbackCheck(SILENT_INITIAL_SCROLL_RETRY_DELAY_MS);
2172
+ } else if (shouldRetryUnalignedEndScroll) {
2173
+ scrollToFallbackOffset(ctx, completionState.clampedTargetOffset);
2174
+ scheduleFallbackCheck(100);
2175
+ } else if (shouldFinishZeroTarget || shouldFinishAfterObservedScroll || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
2176
+ finishScrollTo(ctx);
2177
+ } else if ((isNativeInitialPending || shouldRetryUnalignedInitialScroll) && numChecks <= maxChecks) {
2178
+ const targetOffset = (_d = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : isStillScrollingTo.targetOffset) != null ? _d : state.scrollPending;
2179
+ scrollToFallbackOffset(ctx, targetOffset);
2180
+ scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
2181
+ } else {
2182
+ scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
2183
+ }
2101
2184
  }
2185
+ };
2186
+ scheduleFallbackCheck(initialDelay);
2187
+ }
2188
+
2189
+ // src/core/doScrollTo.native.ts
2190
+ function doScrollTo(ctx, params) {
2191
+ const state = ctx.state;
2192
+ const { animated, horizontal, isInitialScroll, offset } = params;
2193
+ const isAnimated = !!animated;
2194
+ const { refScroller } = state;
2195
+ const scroller = refScroller.current;
2196
+ if (!scroller) {
2197
+ return;
2102
2198
  }
2103
- if (scrollHistory.length > 5) {
2104
- scrollHistory.shift();
2105
- }
2106
- if (ignoreScrollFromMVCP && !scrollingTo) {
2107
- const { lt, gt } = ignoreScrollFromMVCP;
2108
- if (lt && newScroll < lt || gt && newScroll > gt) {
2109
- state.ignoreScrollFromMVCPIgnored = true;
2110
- return;
2111
- }
2199
+ const isHorizontal = !!horizontal;
2200
+ const contentSize = isHorizontal ? getContentSize(ctx) : void 0;
2201
+ const nativeOffset = toNativeHorizontalOffset(state, offset, contentSize);
2202
+ scroller.scrollTo({
2203
+ animated: isAnimated,
2204
+ x: isHorizontal ? nativeOffset : 0,
2205
+ y: isHorizontal ? 0 : offset
2206
+ });
2207
+ if (isInitialScroll) {
2208
+ initialScrollCompletion.markInitialScrollNativeDispatch(state);
2112
2209
  }
2113
- state.scrollPrev = prevScroll;
2114
- state.scrollPrevTime = state.scrollTime;
2115
- state.scroll = newScroll;
2116
- state.scrollTime = currentTime;
2117
- const scrollDelta = Math.abs(newScroll - prevScroll);
2118
- const isUserScrollEvent = !!(options == null ? void 0 : options.fromNativeScrollEvent) && scrollDelta > 0.1 && !adjustChanged && scrollingTo === void 0 && !state.pendingNativeMVCPAdjust;
2119
- const didCancelMaintainScrollAtEnd = isUserScrollEvent && newScroll < prevScroll && !!(state.maintainingScrollAtEnd || state.pendingMaintainScrollAtEnd);
2120
- if (didCancelMaintainScrollAtEnd) {
2121
- finishMaintainScrollAtEnd(ctx);
2210
+ if (isAnimated && Math.abs(state.scroll - offset) <= 1) {
2211
+ checkFinishedScroll(ctx);
2122
2212
  }
2123
- const allowedEdge = isUserScrollEvent ? beginReachedEdgeUserScroll(ctx, newScroll - prevScroll) : void 0;
2124
- const didResolvePendingNativeMVCPAdjust = resolvePendingNativeMVCPAdjust(ctx, newScroll);
2125
- const scrollLength = state.scrollLength;
2126
- const isLargeUserScrollJump = scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength && !state.pendingNativeMVCPAdjust;
2127
- const scrollVelocity = getScrollVelocity(state);
2128
- updateAdaptiveRender(ctx, scrollVelocity, { forceLight: isLargeUserScrollJump });
2129
- const lastCalculated = state.scrollLastCalculate;
2130
- const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
2131
- const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || didCancelMaintainScrollAtEnd || allowedEdge !== void 0 || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
2132
- if (shouldUpdate) {
2133
- state.scrollLastCalculate = state.scroll;
2134
- state.ignoreScrollFromMVCPIgnored = false;
2135
- state.lastScrollDelta = scrollDelta;
2136
- const runCalculateItems = () => {
2137
- var _a4;
2138
- const calculateItemsParams = {
2139
- doMVCP: scrollingTo !== void 0,
2140
- scrollVelocity
2141
- };
2142
- if (isLargeUserScrollJump) {
2143
- calculateItemsParams.drawDistanceMode = "visible-first";
2144
- }
2145
- (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, calculateItemsParams);
2146
- checkThresholds(ctx, allowedEdge);
2147
- };
2148
- if (isLargeUserScrollJump) {
2149
- state.mvcpAnchorLock = void 0;
2150
- state.pendingNativeMVCPAdjust = void 0;
2151
- state.userScrollAnchorReset = { keys: /* @__PURE__ */ new Set() };
2152
- state.scheduledWork.cancel("mvcpRecalculate");
2153
- flushSync(runCalculateItems);
2154
- scheduleFullDrawDistancePrewarm(ctx);
2155
- } else {
2156
- runCalculateItems();
2157
- }
2158
- const shouldMaintainScrollAtEndAfterPendingSettle = !!state.pendingMaintainScrollAtEnd || !!((_a3 = state.props.maintainScrollAtEnd) == null ? void 0 : _a3.onDataChange);
2159
- if (didResolvePendingNativeMVCPAdjust && shouldMaintainScrollAtEndAfterPendingSettle) {
2160
- state.pendingMaintainScrollAtEnd = false;
2161
- doMaintainScrollAtEnd(ctx);
2162
- }
2163
- state.dataChangeNeedsScrollUpdate = false;
2164
- state.lastScrollDelta = 0;
2213
+ if (!isAnimated) {
2214
+ state.scroll = offset;
2215
+ checkFinishedScrollFallback(ctx);
2165
2216
  }
2166
2217
  }
2167
2218
 
@@ -3170,6 +3221,11 @@ function handleInitialScrollDataChange(ctx, options) {
3170
3221
  const state = ctx.state;
3171
3222
  const previousInitialScrollDataLength = (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.previousDataLength) != null ? _b : 0;
3172
3223
  const shouldUseLatestInitialScroll = dataLength > 0 && (!state.hasHadNonEmptyData || didStartFreshData);
3224
+ if (didStartFreshData) {
3225
+ cancelImperativeScroll(state);
3226
+ state.maintainingScrollAtEnd = void 0;
3227
+ state.pendingMaintainScrollAtEnd = false;
3228
+ }
3173
3229
  if (dataLength > 0) {
3174
3230
  state.hasHadNonEmptyData = true;
3175
3231
  }
@@ -4714,6 +4770,7 @@ function maybeUpdateAnchoredEndSpace(ctx) {
4714
4770
  const nextAnchorIndex = anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex;
4715
4771
  let nextAnchorKey;
4716
4772
  let isReady = true;
4773
+ let canUpdateSize = true;
4717
4774
  let nextSize = 0;
4718
4775
  if (anchoredEndSpace) {
4719
4776
  const { anchorIndex, anchorMaxSize, anchorOffset = 0 } = anchoredEndSpace;
@@ -4735,25 +4792,30 @@ function maybeUpdateAnchoredEndSpace(ctx) {
4735
4792
  contentBelowAnchor = Math.max(0, contentBelowAnchor - ctx.scrollAxisGap);
4736
4793
  contentBelowAnchor += (ctx.values.get("footerSize") || 0) + getStylePaddingEnd(state.props);
4737
4794
  isReady = !hasUnknownTailSize;
4738
- nextSize = hasUnknownTailSize ? previousSize || 0 : Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
4795
+ const knownSizeBound = Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
4796
+ nextSize = hasUnknownTailSize ? Math.min(previousSize || 0, knownSizeBound) : knownSizeBound;
4739
4797
  } else if (anchorIndex >= 0) {
4740
4798
  isReady = false;
4799
+ canUpdateSize = false;
4741
4800
  }
4742
4801
  }
4743
4802
  const didSizeChange = previousSize !== nextSize && (previousSize !== void 0 || anchoredEndSpace !== void 0);
4744
4803
  const didEffectiveSizeChange = (previousSize || 0) !== nextSize;
4804
+ const canApplySizeChange = canUpdateSize && (isReady || previousSize !== void 0);
4745
4805
  const didReadyAnchorChange = previousReadyAnchorIndex !== nextAnchorIndex || previousReadyAnchorKey !== nextAnchorKey;
4746
- if (isReady && (didSizeChange || didReadyAnchorChange)) {
4806
+ const didBecomeReady = isReady && state.anchoredEndSpacePendingReady;
4807
+ state.anchoredEndSpacePendingReady = !isReady;
4808
+ if (canApplySizeChange && didSizeChange) {
4809
+ set$(ctx, "anchoredEndSpaceSize", nextSize);
4810
+ (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
4811
+ }
4812
+ if (canApplySizeChange && didEffectiveSizeChange) {
4813
+ updateContentMetricsState(ctx);
4814
+ updateScroll(ctx, state.scroll, true, { markHasScrolled: false });
4815
+ }
4816
+ if (isReady && (didSizeChange || didReadyAnchorChange || didBecomeReady)) {
4747
4817
  state.anchoredEndSpaceReadyAnchorIndex = nextAnchorIndex;
4748
4818
  state.anchoredEndSpaceReadyAnchorKey = nextAnchorKey;
4749
- if (didSizeChange) {
4750
- set$(ctx, "anchoredEndSpaceSize", nextSize);
4751
- (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
4752
- }
4753
- if (didEffectiveSizeChange) {
4754
- updateContentMetricsState(ctx);
4755
- updateScroll(ctx, state.scroll, true, { markHasScrolled: false });
4756
- }
4757
4819
  (_b = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onReady) == null ? void 0 : _b.call(anchoredEndSpace, { anchorIndex: nextAnchorIndex, anchorKey: nextAnchorKey, size: nextSize });
4758
4820
  }
4759
4821
  return nextSize;
@@ -6657,6 +6719,9 @@ function scrollToEnd(ctx, options) {
6657
6719
  viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
6658
6720
  viewPosition: 1
6659
6721
  });
6722
+ if (state.scrollingTo) {
6723
+ state.scrollingTo.isScrollToEnd = true;
6724
+ }
6660
6725
  return true;
6661
6726
  }
6662
6727
 
@@ -7390,6 +7455,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7390
7455
  endReachedSnapshot: void 0,
7391
7456
  firstFullyOnScreenIndex: -1,
7392
7457
  freshDataTransitionEpoch: 0,
7458
+ handledDataChangeEpoch: 0,
7459
+ handledFreshDataTransitionEpoch: 0,
7393
7460
  hasHadNonEmptyData: dataProp.length > 0,
7394
7461
  idCache: [],
7395
7462
  idsInView: [],
@@ -7449,8 +7516,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7449
7516
  refState.current = ctx.state;
7450
7517
  }
7451
7518
  const state = refState.current;
7452
- const handledDataChangeEpochRef = useRef(state.dataChangeEpoch);
7453
- const handledFreshDataTransitionEpochRef = useRef(state.freshDataTransitionEpoch);
7454
7519
  const isFirstLocal = state.isFirst;
7455
7520
  const previousAdaptiveRender = state.props.adaptiveRender;
7456
7521
  const previousHideItemsUntilMeasured = state.props.hideItemsUntilMeasured;
@@ -7605,10 +7670,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7605
7670
  useDevChecks(props);
7606
7671
  }
7607
7672
  useLayoutEffect(() => {
7608
- const didDataChange = handledDataChangeEpochRef.current !== dataChangeEpoch;
7609
- const didStartFreshData = handledFreshDataTransitionEpochRef.current !== freshDataTransitionEpoch;
7610
- handledDataChangeEpochRef.current = dataChangeEpoch;
7611
- handledFreshDataTransitionEpochRef.current = freshDataTransitionEpoch;
7673
+ const didDataChange = state.handledDataChangeEpoch !== dataChangeEpoch;
7674
+ const didStartFreshData = state.handledFreshDataTransitionEpoch !== freshDataTransitionEpoch;
7675
+ state.handledDataChangeEpoch = dataChangeEpoch;
7676
+ state.handledFreshDataTransitionEpoch = freshDataTransitionEpoch;
7612
7677
  if (didStartFreshData) {
7613
7678
  resetInitialRenderState(ctx, {
7614
7679
  resetInitialScroll: !!initialScrollProp,
@@ -7812,13 +7877,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7812
7877
  onScroll: (event) => onScroll(ctx, event),
7813
7878
  onScrollBeginDrag: (event) => {
7814
7879
  var _a4, _b2;
7815
- const maintainingScrollAtEnd = state.maintainingScrollAtEnd;
7816
- if (maintainingScrollAtEnd === "animated" || maintainingScrollAtEnd === "instant") {
7817
- cancelImperativeScroll(state);
7818
- }
7819
- if (maintainingScrollAtEnd) {
7820
- finishMaintainScrollAtEnd(ctx);
7821
- }
7880
+ interruptMaintainScrollAtEnd(ctx);
7822
7881
  prepareReachedEdgeForNextUserScroll(ctx);
7823
7882
  (_b2 = (_a4 = state.props).onScrollBeginDrag) == null ? void 0 : _b2.call(_a4, event);
7824
7883
  },