@legendapp/list 3.0.0-beta.40 → 3.0.0-beta.42

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
@@ -826,7 +826,7 @@ var Containers = typedMemo(function Containers2({
826
826
  return !((_a3 = ctx.state) == null ? void 0 : _a3.initialScroll) ? !prevValue || value - prevValue > 20 ? 0 : 200 : void 0;
827
827
  }
828
828
  });
829
- const animOpacity = waitForInitialLayout && !IsNewArchitecture ? useValue$("readyToRender", { getValue: (value) => value ? 1 : 0 }) : void 0;
829
+ const animOpacity = waitForInitialLayout ? useValue$("readyToRender", { getValue: (value) => value ? 1 : 0 }) : void 0;
830
830
  const otherAxisSize = useValue$("otherAxisSize", { delay: 0 });
831
831
  const containers = [];
832
832
  for (let i = 0; i < numContainers; i++) {
@@ -1109,6 +1109,7 @@ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
1109
1109
 
1110
1110
  // src/core/calculateOffsetWithOffsetPosition.ts
1111
1111
  function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1112
+ var _a3;
1112
1113
  const state = ctx.state;
1113
1114
  const { index, viewOffset, viewPosition } = params;
1114
1115
  let offset = offsetParam;
@@ -1122,10 +1123,16 @@ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1122
1123
  }
1123
1124
  }
1124
1125
  if (viewPosition !== void 0 && index !== void 0) {
1125
- const itemSize = getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1126
+ const dataLength = state.props.data.length;
1127
+ if (dataLength === 0) {
1128
+ return offset;
1129
+ }
1130
+ const isOutOfBounds = index < 0 || index >= dataLength;
1131
+ const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
1132
+ const itemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1126
1133
  const trailingInset = getContentInsetEnd(state);
1127
1134
  offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1128
- if (index === state.props.data.length - 1) {
1135
+ if (!isOutOfBounds && index === state.props.data.length - 1) {
1129
1136
  const footerSize = peek$(ctx, "footerSize") || 0;
1130
1137
  offset += footerSize;
1131
1138
  }
@@ -1327,7 +1334,9 @@ function finishScrollTo(ctx) {
1327
1334
  const scrollingTo = state.scrollingTo;
1328
1335
  state.scrollHistory.length = 0;
1329
1336
  state.initialScroll = void 0;
1337
+ state.initialScrollUsesOffset = false;
1330
1338
  state.initialAnchor = void 0;
1339
+ state.initialNativeScrollWatchdog = void 0;
1331
1340
  state.scrollingTo = void 0;
1332
1341
  if (state.pendingTotalSize !== void 0) {
1333
1342
  addTotalSize(ctx, null, state.pendingTotalSize);
@@ -1345,21 +1354,21 @@ function finishScrollTo(ctx) {
1345
1354
  }
1346
1355
 
1347
1356
  // src/core/checkFinishedScroll.ts
1357
+ var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
1358
+ var INITIAL_SCROLL_MAX_FALLBACK_CHECKS = 20;
1359
+ var INITIAL_SCROLL_ZERO_TARGET_EPSILON = 1;
1348
1360
  function checkFinishedScroll(ctx) {
1349
1361
  ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
1350
1362
  }
1351
1363
  function checkFinishedScrollFrame(ctx) {
1364
+ var _a3;
1352
1365
  const scrollingTo = ctx.state.scrollingTo;
1353
1366
  if (scrollingTo) {
1354
1367
  const { state } = ctx;
1355
1368
  state.animFrameCheckFinishedScroll = void 0;
1356
1369
  const scroll = state.scrollPending;
1357
1370
  const adjust = state.scrollAdjustHandler.getAdjust();
1358
- const clampedTargetOffset = clampScrollOffset(
1359
- ctx,
1360
- scrollingTo.offset - (scrollingTo.viewOffset || 0),
1361
- scrollingTo
1362
- );
1371
+ const clampedTargetOffset = (_a3 = scrollingTo.targetOffset) != null ? _a3 : clampScrollOffset(ctx, scrollingTo.offset - (scrollingTo.viewOffset || 0), scrollingTo);
1363
1372
  const maxOffset = clampScrollOffset(ctx, scroll, scrollingTo);
1364
1373
  const diff1 = Math.abs(scroll - clampedTargetOffset);
1365
1374
  const diff2 = Math.abs(diff1 - adjust);
@@ -1373,17 +1382,33 @@ function checkFinishedScrollFrame(ctx) {
1373
1382
  function checkFinishedScrollFallback(ctx) {
1374
1383
  const state = ctx.state;
1375
1384
  const scrollingTo = state.scrollingTo;
1376
- const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) || !state.didContainersLayout;
1385
+ const shouldFinishInitialZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
1386
+ const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && !shouldFinishInitialZeroTarget || !state.didContainersLayout;
1377
1387
  state.timeoutCheckFinishedScrollFallback = setTimeout(
1378
1388
  () => {
1379
1389
  let numChecks = 0;
1380
1390
  const checkHasScrolled = () => {
1391
+ var _a3, _b;
1381
1392
  state.timeoutCheckFinishedScrollFallback = void 0;
1382
1393
  const isStillScrollingTo = state.scrollingTo;
1383
1394
  if (isStillScrollingTo) {
1384
1395
  numChecks++;
1385
- if (state.hasScrolled || numChecks > 5) {
1396
+ const isNativeInitialPending = isNativeInitialNonZeroTarget(state) && !state.hasScrolled;
1397
+ const maxChecks = isNativeInitialPending ? INITIAL_SCROLL_MAX_FALLBACK_CHECKS : 5;
1398
+ const shouldFinishZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
1399
+ if (shouldFinishZeroTarget || state.hasScrolled || numChecks > maxChecks) {
1386
1400
  finishScrollTo(ctx);
1401
+ } else if (isNativeInitialPending && numChecks <= maxChecks) {
1402
+ const targetOffset = (_b = (_a3 = state.initialNativeScrollWatchdog) == null ? void 0 : _a3.targetOffset) != null ? _b : state.scrollPending;
1403
+ const scroller = state.refScroller.current;
1404
+ if (scroller) {
1405
+ scroller.scrollTo({
1406
+ animated: false,
1407
+ x: state.props.horizontal ? targetOffset : 0,
1408
+ y: state.props.horizontal ? 0 : targetOffset
1409
+ });
1410
+ }
1411
+ state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, 100);
1387
1412
  } else {
1388
1413
  state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, 100);
1389
1414
  }
@@ -1394,6 +1419,14 @@ function checkFinishedScrollFallback(ctx) {
1394
1419
  slowTimeout ? 500 : 100
1395
1420
  );
1396
1421
  }
1422
+ function isNativeInitialNonZeroTarget(state) {
1423
+ return !state.didFinishInitialScroll && !!state.initialNativeScrollWatchdog && state.initialNativeScrollWatchdog.targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
1424
+ }
1425
+ function shouldFinishInitialZeroTargetScroll(ctx) {
1426
+ var _a3;
1427
+ const { state } = ctx;
1428
+ 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;
1429
+ }
1397
1430
 
1398
1431
  // src/core/doScrollTo.native.ts
1399
1432
  function doScrollTo(ctx, params) {
@@ -1417,7 +1450,9 @@ function doScrollTo(ctx, params) {
1417
1450
  }
1418
1451
 
1419
1452
  // src/core/scrollTo.ts
1453
+ var WATCHDOG_OFFSET_EPSILON = 1;
1420
1454
  function scrollTo(ctx, params) {
1455
+ var _a3, _b;
1421
1456
  const state = ctx.state;
1422
1457
  const { noScrollingTo, forceScroll, ...scrollTarget } = params;
1423
1458
  const { animated, isInitialScroll, offset: scrollTargetOffset, precomputedWithViewOffset } = scrollTarget;
@@ -1434,9 +1469,23 @@ function scrollTo(ctx, params) {
1434
1469
  offset = clampScrollOffset(ctx, offset, scrollTarget);
1435
1470
  state.scrollHistory.length = 0;
1436
1471
  if (!noScrollingTo) {
1437
- state.scrollingTo = scrollTarget;
1472
+ state.scrollingTo = {
1473
+ ...scrollTarget,
1474
+ targetOffset: offset
1475
+ };
1438
1476
  }
1439
1477
  state.scrollPending = offset;
1478
+ const shouldWatchInitialNativeScroll = !state.didFinishInitialScroll && (isInitialScroll || !!state.initialNativeScrollWatchdog) && offset > WATCHDOG_OFFSET_EPSILON;
1479
+ const shouldClearInitialNativeScrollWatchdog = !state.didFinishInitialScroll && !!state.initialNativeScrollWatchdog && offset <= WATCHDOG_OFFSET_EPSILON;
1480
+ if (shouldWatchInitialNativeScroll) {
1481
+ state.hasScrolled = false;
1482
+ state.initialNativeScrollWatchdog = {
1483
+ startScroll: (_b = (_a3 = state.initialNativeScrollWatchdog) == null ? void 0 : _a3.startScroll) != null ? _b : state.scroll,
1484
+ targetOffset: offset
1485
+ };
1486
+ } else if (shouldClearInitialNativeScrollWatchdog) {
1487
+ state.initialNativeScrollWatchdog = void 0;
1488
+ }
1440
1489
  if (forceScroll || !isInitialScroll || Platform2.OS === "android") {
1441
1490
  doScrollTo(ctx, { animated, horizontal, offset });
1442
1491
  } else {
@@ -1444,178 +1493,52 @@ function scrollTo(ctx, params) {
1444
1493
  }
1445
1494
  }
1446
1495
 
1447
- // src/platform/flushSync.native.ts
1448
- var flushSync = (fn) => {
1449
- fn();
1450
- };
1451
-
1452
- // src/core/updateScroll.ts
1453
- function updateScroll(ctx, newScroll, forceUpdate) {
1496
+ // src/core/doMaintainScrollAtEnd.ts
1497
+ function doMaintainScrollAtEnd(ctx) {
1454
1498
  const state = ctx.state;
1455
- const { ignoreScrollFromMVCP, lastScrollAdjustForHistory, scrollAdjustHandler, scrollHistory, scrollingTo } = state;
1456
- const prevScroll = state.scroll;
1457
- state.hasScrolled = true;
1458
- state.lastBatchingAction = Date.now();
1459
- const currentTime = Date.now();
1460
- const adjust = scrollAdjustHandler.getAdjust();
1461
- const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
1462
- if (adjustChanged) {
1463
- scrollHistory.length = 0;
1464
- }
1465
- state.lastScrollAdjustForHistory = adjust;
1466
- if (scrollingTo === void 0 && !(scrollHistory.length === 0 && newScroll === state.scroll)) {
1467
- if (!adjustChanged) {
1468
- scrollHistory.push({ scroll: newScroll, time: currentTime });
1469
- }
1470
- }
1471
- if (scrollHistory.length > 5) {
1472
- scrollHistory.shift();
1499
+ const {
1500
+ didContainersLayout,
1501
+ isAtEnd,
1502
+ pendingNativeMVCPAdjust,
1503
+ refScroller,
1504
+ props: { maintainScrollAtEnd }
1505
+ } = state;
1506
+ const shouldMaintainScrollAtEnd = !!(isAtEnd && maintainScrollAtEnd && didContainersLayout);
1507
+ if (pendingNativeMVCPAdjust) {
1508
+ state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
1509
+ return false;
1473
1510
  }
1474
- if (ignoreScrollFromMVCP && !scrollingTo) {
1475
- const { lt, gt } = ignoreScrollFromMVCP;
1476
- if (lt && newScroll < lt || gt && newScroll > gt) {
1477
- state.ignoreScrollFromMVCPIgnored = true;
1478
- return;
1511
+ state.pendingMaintainScrollAtEnd = false;
1512
+ if (shouldMaintainScrollAtEnd) {
1513
+ const contentSize = getContentSize(ctx);
1514
+ if (contentSize < state.scrollLength) {
1515
+ state.scroll = 0;
1479
1516
  }
1480
- }
1481
- state.scrollPrev = prevScroll;
1482
- state.scrollPrevTime = state.scrollTime;
1483
- state.scroll = newScroll;
1484
- state.scrollTime = currentTime;
1485
- const scrollDelta = Math.abs(newScroll - prevScroll);
1486
- const scrollLength = state.scrollLength;
1487
- const lastCalculated = state.scrollLastCalculate;
1488
- const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
1489
- const shouldUpdate = useAggressiveItemRecalculation || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
1490
- if (shouldUpdate) {
1491
- state.scrollLastCalculate = state.scroll;
1492
- state.ignoreScrollFromMVCPIgnored = false;
1493
- state.lastScrollDelta = scrollDelta;
1494
- const runCalculateItems = () => {
1517
+ requestAnimationFrame(() => {
1495
1518
  var _a3;
1496
- (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { doMVCP: scrollingTo !== void 0 });
1497
- checkThresholds(ctx);
1498
- };
1499
- if (Platform2.OS === "web" && scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength) {
1500
- flushSync(runCalculateItems);
1501
- } else {
1502
- runCalculateItems();
1503
- }
1504
- state.dataChangeNeedsScrollUpdate = false;
1505
- state.lastScrollDelta = 0;
1506
- }
1507
- }
1508
-
1509
- // src/utils/requestAdjust.ts
1510
- function requestAdjust(ctx, positionDiff, dataChanged) {
1511
- const state = ctx.state;
1512
- if (Math.abs(positionDiff) > 0.1) {
1513
- const needsScrollWorkaround = Platform2.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
1514
- const doit = () => {
1515
- if (needsScrollWorkaround) {
1516
- scrollTo(ctx, {
1517
- noScrollingTo: true,
1518
- offset: state.scroll
1519
+ if (state.isAtEnd) {
1520
+ state.maintainingScrollAtEnd = true;
1521
+ (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
1522
+ animated: maintainScrollAtEnd.animated
1519
1523
  });
1520
- } else {
1521
- state.scrollAdjustHandler.requestAdjust(positionDiff);
1522
- if (state.adjustingFromInitialMount) {
1523
- state.adjustingFromInitialMount--;
1524
- }
1525
- }
1526
- };
1527
- state.scroll += positionDiff;
1528
- state.scrollForNextCalculateItemsInView = void 0;
1529
- const readyToRender = peek$(ctx, "readyToRender");
1530
- if (readyToRender) {
1531
- doit();
1532
- if (Platform2.OS !== "web") {
1533
- const threshold = state.scroll - positionDiff / 2;
1534
- if (!state.ignoreScrollFromMVCP) {
1535
- state.ignoreScrollFromMVCP = {};
1536
- }
1537
- if (positionDiff > 0) {
1538
- state.ignoreScrollFromMVCP.lt = threshold;
1539
- } else {
1540
- state.ignoreScrollFromMVCP.gt = threshold;
1541
- }
1542
- if (state.ignoreScrollFromMVCPTimeout) {
1543
- clearTimeout(state.ignoreScrollFromMVCPTimeout);
1544
- }
1545
- const delay = needsScrollWorkaround ? 250 : 100;
1546
- state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
1547
- state.ignoreScrollFromMVCP = void 0;
1548
- const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
1549
- if (shouldForceUpdate) {
1550
- state.ignoreScrollFromMVCPIgnored = false;
1551
- state.scrollPending = state.scroll;
1552
- updateScroll(ctx, state.scroll, true);
1553
- }
1554
- }, delay);
1524
+ setTimeout(
1525
+ () => {
1526
+ state.maintainingScrollAtEnd = false;
1527
+ },
1528
+ maintainScrollAtEnd.animated ? 500 : 0
1529
+ );
1555
1530
  }
1556
- } else {
1557
- state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
1558
- requestAnimationFrame(doit);
1559
- }
1560
- }
1561
- }
1562
-
1563
- // src/core/ensureInitialAnchor.ts
1564
- var INITIAL_ANCHOR_TOLERANCE = 0.5;
1565
- var INITIAL_ANCHOR_MAX_ATTEMPTS = 4;
1566
- var INITIAL_ANCHOR_SETTLED_TICKS = 2;
1567
- function ensureInitialAnchor(ctx) {
1568
- var _a3, _b, _c, _d, _e;
1569
- const state = ctx.state;
1570
- const { initialAnchor, didContainersLayout, scroll, scrollLength } = state;
1571
- const anchor = initialAnchor;
1572
- const item = state.props.data[anchor.index];
1573
- if (!didContainersLayout) {
1574
- return;
1575
- }
1576
- const id = getId(state, anchor.index);
1577
- if (state.positions[anchor.index] === void 0) {
1578
- return;
1579
- }
1580
- const size = getItemSize(ctx, id, anchor.index, item, true, true);
1581
- if (size === void 0) {
1582
- return;
1583
- }
1584
- const availableSpace = Math.max(0, scrollLength - size);
1585
- const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
1586
- const desiredOffset = calculateOffsetForIndex(ctx, anchor.index) + topOffsetAdjustment - ((_a3 = anchor.viewOffset) != null ? _a3 : 0) - ((_b = anchor.viewPosition) != null ? _b : 0) * availableSpace;
1587
- const clampedDesiredOffset = clampScrollOffset(ctx, desiredOffset, anchor);
1588
- const delta = clampedDesiredOffset - scroll;
1589
- if (Math.abs(delta) <= INITIAL_ANCHOR_TOLERANCE) {
1590
- const settledTicks = ((_c = anchor.settledTicks) != null ? _c : 0) + 1;
1591
- if (settledTicks >= INITIAL_ANCHOR_SETTLED_TICKS) {
1592
- state.initialAnchor = void 0;
1593
- } else {
1594
- anchor.settledTicks = settledTicks;
1595
- }
1596
- return;
1597
- }
1598
- if (((_d = anchor.attempts) != null ? _d : 0) >= INITIAL_ANCHOR_MAX_ATTEMPTS) {
1599
- state.initialAnchor = void 0;
1600
- return;
1601
- }
1602
- const lastDelta = anchor.lastDelta;
1603
- if (lastDelta !== void 0 && Math.abs(delta) >= Math.abs(lastDelta)) {
1604
- state.initialAnchor = void 0;
1605
- return;
1531
+ });
1532
+ return true;
1606
1533
  }
1607
- Object.assign(anchor, {
1608
- attempts: ((_e = anchor.attempts) != null ? _e : 0) + 1,
1609
- lastDelta: delta,
1610
- settledTicks: 0
1611
- });
1612
- requestAdjust(ctx, delta);
1534
+ return false;
1613
1535
  }
1614
1536
 
1615
1537
  // src/core/mvcp.ts
1616
1538
  var MVCP_POSITION_EPSILON = 0.1;
1617
1539
  var MVCP_ANCHOR_LOCK_TTL_MS = 300;
1618
1540
  var MVCP_ANCHOR_LOCK_QUIET_PASSES_TO_RELEASE = 2;
1541
+ var NATIVE_END_CLAMP_EPSILON = 1;
1619
1542
  function resolveAnchorLock(state, enableMVCPAnchorLock, mvcpData, now) {
1620
1543
  if (!enableMVCPAnchorLock) {
1621
1544
  state.mvcpAnchorLock = void 0;
@@ -1655,6 +1578,100 @@ function updateAnchorLock(state, params) {
1655
1578
  };
1656
1579
  }
1657
1580
  }
1581
+ function shouldQueueNativeMVCPAdjust(dataChanged, state, positionDiff, prevTotalSize, prevScroll, scrollTarget) {
1582
+ if (!dataChanged || Platform2.OS === "web" || !state.props.maintainVisibleContentPosition.data || scrollTarget !== void 0 || positionDiff >= -MVCP_POSITION_EPSILON) {
1583
+ return false;
1584
+ }
1585
+ const distanceFromEnd = prevTotalSize - prevScroll - state.scrollLength;
1586
+ return distanceFromEnd < Math.abs(positionDiff) - MVCP_POSITION_EPSILON;
1587
+ }
1588
+ function getPredictedNativeClamp(state, unresolvedAmount, totalSize) {
1589
+ if (Math.abs(unresolvedAmount) <= MVCP_POSITION_EPSILON) {
1590
+ return 0;
1591
+ }
1592
+ const maxScroll = Math.max(0, totalSize - state.scrollLength);
1593
+ const clampDelta = maxScroll - state.scroll;
1594
+ if (unresolvedAmount < 0) {
1595
+ return Math.max(unresolvedAmount, Math.min(0, clampDelta));
1596
+ }
1597
+ if (unresolvedAmount > 0) {
1598
+ return Math.min(unresolvedAmount, Math.max(0, clampDelta));
1599
+ }
1600
+ return 0;
1601
+ }
1602
+ function getProgressTowardAmount(targetDelta, nativeDelta) {
1603
+ return targetDelta < 0 ? -nativeDelta : nativeDelta;
1604
+ }
1605
+ function settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta) {
1606
+ const state = ctx.state;
1607
+ state.pendingNativeMVCPAdjust = void 0;
1608
+ const remaining = remainingAfterManual - nativeDelta;
1609
+ if (Math.abs(remaining) > MVCP_POSITION_EPSILON) {
1610
+ requestAdjust(ctx, remaining, true);
1611
+ }
1612
+ }
1613
+ function maybeApplyPredictedNativeMVCPAdjust(ctx) {
1614
+ const state = ctx.state;
1615
+ const pending = state.pendingNativeMVCPAdjust;
1616
+ if (!pending || Math.abs(pending.manualApplied) > MVCP_POSITION_EPSILON) {
1617
+ return;
1618
+ }
1619
+ const totalSize = getContentSize(ctx);
1620
+ const predictedNativeClamp = getPredictedNativeClamp(state, pending.amount, totalSize);
1621
+ if (Math.abs(predictedNativeClamp) <= MVCP_POSITION_EPSILON) {
1622
+ return;
1623
+ }
1624
+ const manualDesired = pending.amount - predictedNativeClamp;
1625
+ if (Math.abs(manualDesired) <= MVCP_POSITION_EPSILON) {
1626
+ return;
1627
+ }
1628
+ pending.manualApplied = manualDesired;
1629
+ requestAdjust(ctx, manualDesired, true);
1630
+ pending.furthestProgressTowardAmount = 0;
1631
+ }
1632
+ function resolvePendingNativeMVCPAdjust(ctx, newScroll) {
1633
+ const state = ctx.state;
1634
+ const pending = state.pendingNativeMVCPAdjust;
1635
+ if (!pending) {
1636
+ return false;
1637
+ }
1638
+ const remainingAfterManual = pending.amount - pending.manualApplied;
1639
+ const nativeDelta = newScroll - (pending.startScroll + pending.manualApplied);
1640
+ const isWrongDirection = remainingAfterManual < 0 && nativeDelta > MVCP_POSITION_EPSILON || remainingAfterManual > 0 && nativeDelta < -MVCP_POSITION_EPSILON;
1641
+ const progressTowardAmount = getProgressTowardAmount(remainingAfterManual, nativeDelta);
1642
+ if (Math.abs(remainingAfterManual) <= MVCP_POSITION_EPSILON) {
1643
+ state.pendingNativeMVCPAdjust = void 0;
1644
+ return true;
1645
+ }
1646
+ if (isWrongDirection) {
1647
+ state.pendingNativeMVCPAdjust = void 0;
1648
+ return false;
1649
+ }
1650
+ if (progressTowardAmount + MVCP_POSITION_EPSILON >= Math.abs(remainingAfterManual)) {
1651
+ settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
1652
+ return true;
1653
+ }
1654
+ const expectedNativeClampScroll = Math.max(0, getContentSize(ctx) - state.scrollLength);
1655
+ const distanceToClamp = Math.abs(newScroll - expectedNativeClampScroll);
1656
+ const isAtExpectedNativeClamp = distanceToClamp <= NATIVE_END_CLAMP_EPSILON;
1657
+ if (isAtExpectedNativeClamp) {
1658
+ settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
1659
+ return true;
1660
+ }
1661
+ if (state.pendingMaintainScrollAtEnd && state.isAtEnd && progressTowardAmount > MVCP_POSITION_EPSILON) {
1662
+ settlePendingNativeMVCPAdjust(ctx, remainingAfterManual, nativeDelta);
1663
+ return true;
1664
+ }
1665
+ if (progressTowardAmount > pending.furthestProgressTowardAmount + MVCP_POSITION_EPSILON) {
1666
+ pending.furthestProgressTowardAmount = progressTowardAmount;
1667
+ return false;
1668
+ }
1669
+ if (pending.furthestProgressTowardAmount > MVCP_POSITION_EPSILON && progressTowardAmount < pending.furthestProgressTowardAmount - MVCP_POSITION_EPSILON) {
1670
+ state.pendingNativeMVCPAdjust = void 0;
1671
+ return false;
1672
+ }
1673
+ return false;
1674
+ }
1658
1675
  function prepareMVCP(ctx, dataChanged) {
1659
1676
  const state = ctx.state;
1660
1677
  const { idsInView, positions, props } = state;
@@ -1674,7 +1691,13 @@ function prepareMVCP(ctx, dataChanged) {
1674
1691
  const isEndAnchoredScrollTarget = scrollTarget !== void 0 && state.props.data.length > 0 && scrollTarget >= state.props.data.length - 1 && (scrollingToViewPosition != null ? scrollingToViewPosition : 0) > 0;
1675
1692
  const shouldMVCP = dataChanged ? mvcpData : mvcpScroll;
1676
1693
  const indexByKey = state.indexByKey;
1694
+ const prevScroll = state.scroll;
1695
+ const prevTotalSize = getContentSize(ctx);
1677
1696
  if (shouldMVCP) {
1697
+ if (!isWeb && state.pendingNativeMVCPAdjust && scrollTarget === void 0) {
1698
+ maybeApplyPredictedNativeMVCPAdjust(ctx);
1699
+ return void 0;
1700
+ }
1678
1701
  if (anchorLock && scrollTarget === void 0) {
1679
1702
  targetId = anchorLock.id;
1680
1703
  prevPosition = anchorLock.position;
@@ -1776,18 +1799,206 @@ function prepareMVCP(ctx, dataChanged) {
1776
1799
  }
1777
1800
  }
1778
1801
  }
1779
- updateAnchorLock(state, {
1780
- anchorId: anchorIdForLock,
1781
- anchorPosition: anchorPositionForLock,
1782
- dataChanged,
1783
- now,
1784
- positionDiff
1785
- });
1786
- if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
1787
- requestAdjust(ctx, positionDiff, dataChanged && mvcpData);
1788
- }
1802
+ updateAnchorLock(state, {
1803
+ anchorId: anchorIdForLock,
1804
+ anchorPosition: anchorPositionForLock,
1805
+ dataChanged,
1806
+ now,
1807
+ positionDiff
1808
+ });
1809
+ if (shouldQueueNativeMVCPAdjust(dataChanged, state, positionDiff, prevTotalSize, prevScroll, scrollTarget)) {
1810
+ state.pendingNativeMVCPAdjust = {
1811
+ amount: positionDiff,
1812
+ furthestProgressTowardAmount: 0,
1813
+ manualApplied: 0,
1814
+ startScroll: prevScroll
1815
+ };
1816
+ maybeApplyPredictedNativeMVCPAdjust(ctx);
1817
+ return;
1818
+ }
1819
+ if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
1820
+ requestAdjust(ctx, positionDiff, dataChanged && mvcpData);
1821
+ }
1822
+ };
1823
+ }
1824
+ }
1825
+
1826
+ // src/platform/flushSync.native.ts
1827
+ var flushSync = (fn) => {
1828
+ fn();
1829
+ };
1830
+
1831
+ // src/core/updateScroll.ts
1832
+ function updateScroll(ctx, newScroll, forceUpdate) {
1833
+ var _a3;
1834
+ const state = ctx.state;
1835
+ const { ignoreScrollFromMVCP, lastScrollAdjustForHistory, scrollAdjustHandler, scrollHistory, scrollingTo } = state;
1836
+ const prevScroll = state.scroll;
1837
+ state.hasScrolled = true;
1838
+ state.lastBatchingAction = Date.now();
1839
+ const currentTime = Date.now();
1840
+ const adjust = scrollAdjustHandler.getAdjust();
1841
+ const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
1842
+ if (adjustChanged) {
1843
+ scrollHistory.length = 0;
1844
+ }
1845
+ state.lastScrollAdjustForHistory = adjust;
1846
+ if (scrollingTo === void 0 && !(scrollHistory.length === 0 && newScroll === state.scroll)) {
1847
+ if (!adjustChanged) {
1848
+ scrollHistory.push({ scroll: newScroll, time: currentTime });
1849
+ }
1850
+ }
1851
+ if (scrollHistory.length > 5) {
1852
+ scrollHistory.shift();
1853
+ }
1854
+ if (ignoreScrollFromMVCP && !scrollingTo) {
1855
+ const { lt, gt } = ignoreScrollFromMVCP;
1856
+ if (lt && newScroll < lt || gt && newScroll > gt) {
1857
+ state.ignoreScrollFromMVCPIgnored = true;
1858
+ return;
1859
+ }
1860
+ }
1861
+ state.scrollPrev = prevScroll;
1862
+ state.scrollPrevTime = state.scrollTime;
1863
+ state.scroll = newScroll;
1864
+ state.scrollTime = currentTime;
1865
+ const scrollDelta = Math.abs(newScroll - prevScroll);
1866
+ const didResolvePendingNativeMVCPAdjust = resolvePendingNativeMVCPAdjust(ctx, newScroll);
1867
+ const scrollLength = state.scrollLength;
1868
+ const lastCalculated = state.scrollLastCalculate;
1869
+ const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
1870
+ const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
1871
+ if (shouldUpdate) {
1872
+ state.scrollLastCalculate = state.scroll;
1873
+ state.ignoreScrollFromMVCPIgnored = false;
1874
+ state.lastScrollDelta = scrollDelta;
1875
+ const runCalculateItems = () => {
1876
+ var _a4;
1877
+ (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { doMVCP: scrollingTo !== void 0 });
1878
+ checkThresholds(ctx);
1879
+ };
1880
+ if (Platform2.OS === "web" && scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength) {
1881
+ flushSync(runCalculateItems);
1882
+ } else {
1883
+ runCalculateItems();
1884
+ }
1885
+ const shouldMaintainScrollAtEndAfterPendingSettle = !!state.pendingMaintainScrollAtEnd || !!((_a3 = state.props.maintainScrollAtEnd) == null ? void 0 : _a3.onDataChange);
1886
+ if (didResolvePendingNativeMVCPAdjust && shouldMaintainScrollAtEndAfterPendingSettle) {
1887
+ state.pendingMaintainScrollAtEnd = false;
1888
+ doMaintainScrollAtEnd(ctx);
1889
+ }
1890
+ state.dataChangeNeedsScrollUpdate = false;
1891
+ state.lastScrollDelta = 0;
1892
+ }
1893
+ }
1894
+
1895
+ // src/utils/requestAdjust.ts
1896
+ function requestAdjust(ctx, positionDiff, dataChanged) {
1897
+ const state = ctx.state;
1898
+ if (Math.abs(positionDiff) > 0.1) {
1899
+ const needsScrollWorkaround = Platform2.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
1900
+ const doit = () => {
1901
+ if (needsScrollWorkaround) {
1902
+ scrollTo(ctx, {
1903
+ noScrollingTo: true,
1904
+ offset: state.scroll
1905
+ });
1906
+ } else {
1907
+ state.scrollAdjustHandler.requestAdjust(positionDiff);
1908
+ if (state.adjustingFromInitialMount) {
1909
+ state.adjustingFromInitialMount--;
1910
+ }
1911
+ }
1789
1912
  };
1913
+ state.scroll += positionDiff;
1914
+ state.scrollForNextCalculateItemsInView = void 0;
1915
+ const readyToRender = peek$(ctx, "readyToRender");
1916
+ if (readyToRender) {
1917
+ doit();
1918
+ if (Platform2.OS !== "web") {
1919
+ const threshold = state.scroll - positionDiff / 2;
1920
+ if (!state.ignoreScrollFromMVCP) {
1921
+ state.ignoreScrollFromMVCP = {};
1922
+ }
1923
+ if (positionDiff > 0) {
1924
+ state.ignoreScrollFromMVCP.lt = threshold;
1925
+ } else {
1926
+ state.ignoreScrollFromMVCP.gt = threshold;
1927
+ }
1928
+ if (state.ignoreScrollFromMVCPTimeout) {
1929
+ clearTimeout(state.ignoreScrollFromMVCPTimeout);
1930
+ }
1931
+ const delay = needsScrollWorkaround ? 250 : 100;
1932
+ state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
1933
+ state.ignoreScrollFromMVCP = void 0;
1934
+ const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
1935
+ if (shouldForceUpdate) {
1936
+ state.ignoreScrollFromMVCPIgnored = false;
1937
+ state.scrollPending = state.scroll;
1938
+ updateScroll(ctx, state.scroll, true);
1939
+ }
1940
+ }, delay);
1941
+ }
1942
+ } else {
1943
+ state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
1944
+ requestAnimationFrame(doit);
1945
+ }
1946
+ }
1947
+ }
1948
+
1949
+ // src/core/ensureInitialAnchor.ts
1950
+ var INITIAL_ANCHOR_TOLERANCE = 0.5;
1951
+ var INITIAL_ANCHOR_MAX_ATTEMPTS = 4;
1952
+ var INITIAL_ANCHOR_SETTLED_TICKS = 2;
1953
+ function ensureInitialAnchor(ctx) {
1954
+ var _a3, _b, _c, _d, _e, _f;
1955
+ const state = ctx.state;
1956
+ const { initialAnchor, didContainersLayout, scroll, scrollLength } = state;
1957
+ const anchor = initialAnchor;
1958
+ if (state.initialScroll || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll)) {
1959
+ return;
1960
+ }
1961
+ const item = state.props.data[anchor.index];
1962
+ if (!didContainersLayout) {
1963
+ return;
1964
+ }
1965
+ const id = getId(state, anchor.index);
1966
+ if (state.positions[anchor.index] === void 0) {
1967
+ return;
1968
+ }
1969
+ const size = getItemSize(ctx, id, anchor.index, item, true, true);
1970
+ if (size === void 0) {
1971
+ return;
1972
+ }
1973
+ const availableSpace = Math.max(0, scrollLength - size);
1974
+ const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
1975
+ const desiredOffset = calculateOffsetForIndex(ctx, anchor.index) + topOffsetAdjustment - ((_b = anchor.viewOffset) != null ? _b : 0) - ((_c = anchor.viewPosition) != null ? _c : 0) * availableSpace;
1976
+ const clampedDesiredOffset = clampScrollOffset(ctx, desiredOffset, anchor);
1977
+ const delta = clampedDesiredOffset - scroll;
1978
+ if (Math.abs(delta) <= INITIAL_ANCHOR_TOLERANCE) {
1979
+ const settledTicks = ((_d = anchor.settledTicks) != null ? _d : 0) + 1;
1980
+ if (settledTicks >= INITIAL_ANCHOR_SETTLED_TICKS) {
1981
+ state.initialAnchor = void 0;
1982
+ } else {
1983
+ anchor.settledTicks = settledTicks;
1984
+ }
1985
+ return;
1986
+ }
1987
+ if (((_e = anchor.attempts) != null ? _e : 0) >= INITIAL_ANCHOR_MAX_ATTEMPTS) {
1988
+ state.initialAnchor = void 0;
1989
+ return;
1990
+ }
1991
+ const lastDelta = anchor.lastDelta;
1992
+ if (lastDelta !== void 0 && Math.abs(delta) >= Math.abs(lastDelta)) {
1993
+ state.initialAnchor = void 0;
1994
+ return;
1790
1995
  }
1996
+ Object.assign(anchor, {
1997
+ attempts: ((_f = anchor.attempts) != null ? _f : 0) + 1,
1998
+ lastDelta: delta,
1999
+ settledTicks: 0
2000
+ });
2001
+ requestAdjust(ctx, delta);
1791
2002
  }
1792
2003
 
1793
2004
  // src/core/prepareColumnStartState.ts
@@ -2443,7 +2654,14 @@ function comparatorByDistance(a, b) {
2443
2654
  }
2444
2655
 
2445
2656
  // src/core/scrollToIndex.ts
2446
- function scrollToIndex(ctx, { index, viewOffset = 0, animated = true, viewPosition }) {
2657
+ function scrollToIndex(ctx, {
2658
+ index,
2659
+ viewOffset = 0,
2660
+ animated = true,
2661
+ forceScroll,
2662
+ isInitialScroll,
2663
+ viewPosition
2664
+ }) {
2447
2665
  const state = ctx.state;
2448
2666
  const { data } = state.props;
2449
2667
  if (index >= data.length) {
@@ -2461,7 +2679,9 @@ function scrollToIndex(ctx, { index, viewOffset = 0, animated = true, viewPositi
2461
2679
  const itemSize = getItemSize(ctx, targetId, index, state.props.data[index]);
2462
2680
  scrollTo(ctx, {
2463
2681
  animated,
2682
+ forceScroll,
2464
2683
  index,
2684
+ isInitialScroll,
2465
2685
  itemSize,
2466
2686
  offset: firstIndexOffset,
2467
2687
  viewOffset,
@@ -2469,15 +2689,58 @@ function scrollToIndex(ctx, { index, viewOffset = 0, animated = true, viewPositi
2469
2689
  });
2470
2690
  }
2471
2691
 
2692
+ // src/utils/performInitialScroll.ts
2693
+ function performInitialScroll(ctx, params) {
2694
+ var _a3;
2695
+ const { forceScroll, initialScrollUsesOffset, resolvedOffset, target } = params;
2696
+ if (initialScrollUsesOffset || resolvedOffset !== void 0) {
2697
+ scrollTo(ctx, {
2698
+ animated: false,
2699
+ forceScroll,
2700
+ index: initialScrollUsesOffset ? void 0 : target.index,
2701
+ isInitialScroll: true,
2702
+ offset: (_a3 = resolvedOffset != null ? resolvedOffset : target.contentOffset) != null ? _a3 : 0,
2703
+ precomputedWithViewOffset: resolvedOffset !== void 0
2704
+ });
2705
+ return;
2706
+ }
2707
+ if (target.index === void 0) {
2708
+ return;
2709
+ }
2710
+ scrollToIndex(ctx, {
2711
+ ...target,
2712
+ animated: false,
2713
+ forceScroll,
2714
+ isInitialScroll: true
2715
+ });
2716
+ }
2717
+
2472
2718
  // src/utils/setDidLayout.ts
2473
2719
  function setDidLayout(ctx) {
2474
2720
  const state = ctx.state;
2475
2721
  const { initialScroll } = state;
2476
2722
  state.queuedInitialLayout = true;
2477
2723
  checkAtBottom(ctx);
2478
- if ((initialScroll == null ? void 0 : initialScroll.index) !== void 0) {
2479
- const target = initialScroll;
2480
- const runScroll = () => scrollToIndex(ctx, { ...target, animated: false });
2724
+ if (initialScroll) {
2725
+ const runScroll = () => {
2726
+ var _a3, _b;
2727
+ const target = state.initialScroll;
2728
+ if (!target) {
2729
+ return;
2730
+ }
2731
+ const activeInitialTargetOffset = ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) ? (_b = state.scrollingTo.targetOffset) != null ? _b : state.scrollingTo.offset : void 0;
2732
+ const desiredInitialTargetOffset = state.initialScrollUsesOffset ? target.contentOffset : activeInitialTargetOffset;
2733
+ const isAlreadyAtDesiredInitialTarget = desiredInitialTargetOffset !== void 0 && Math.abs(state.scroll - desiredInitialTargetOffset) <= 1 && Math.abs(state.scrollPending - desiredInitialTargetOffset) <= 1;
2734
+ if (!isAlreadyAtDesiredInitialTarget) {
2735
+ performInitialScroll(ctx, {
2736
+ forceScroll: true,
2737
+ initialScrollUsesOffset: state.initialScrollUsesOffset,
2738
+ // Offset-based initial scrolls do not need item lookup, so they can run even before data exists.
2739
+ // Re-run on the next frame to pick up measured viewport size without waiting for index resolution.
2740
+ target
2741
+ });
2742
+ }
2743
+ };
2481
2744
  runScroll();
2482
2745
  requestAnimationFrame(runScroll);
2483
2746
  }
@@ -2555,7 +2818,7 @@ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentSt
2555
2818
  function calculateItemsInView(ctx, params = {}) {
2556
2819
  const state = ctx.state;
2557
2820
  batchedUpdates(() => {
2558
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
2821
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
2559
2822
  const {
2560
2823
  columns,
2561
2824
  columnSpans,
@@ -2594,7 +2857,7 @@ function calculateItemsInView(ctx, params = {}) {
2594
2857
  }
2595
2858
  return;
2596
2859
  }
2597
- const totalSize = getContentSize(ctx);
2860
+ let totalSize = getContentSize(ctx);
2598
2861
  const topPad = peek$(ctx, "stylePaddingTop") + peek$(ctx, "headerSize");
2599
2862
  const numColumns = peek$(ctx, "numColumns");
2600
2863
  const speed = getScrollVelocity(state);
@@ -2602,14 +2865,14 @@ function calculateItemsInView(ctx, params = {}) {
2602
2865
  const { queuedInitialLayout } = state;
2603
2866
  let { scroll: scrollState } = state;
2604
2867
  if (!queuedInitialLayout && initialScroll) {
2605
- const updatedOffset = calculateOffsetWithOffsetPosition(
2868
+ const updatedOffset = state.initialScrollUsesOffset ? (_a3 = initialScroll.contentOffset) != null ? _a3 : 0 : calculateOffsetWithOffsetPosition(
2606
2869
  ctx,
2607
2870
  calculateOffsetForIndex(ctx, initialScroll.index),
2608
2871
  initialScroll
2609
2872
  );
2610
2873
  scrollState = updatedOffset;
2611
2874
  }
2612
- const scrollAdjustPending = (_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0;
2875
+ const scrollAdjustPending = (_b = peek$(ctx, "scrollAdjustPending")) != null ? _b : 0;
2613
2876
  const scrollAdjustPad = scrollAdjustPending - topPad;
2614
2877
  let scroll = Math.round(scrollState + scrollExtra + scrollAdjustPad);
2615
2878
  if (scroll + scrollLength > totalSize) {
@@ -2654,13 +2917,14 @@ function calculateItemsInView(ctx, params = {}) {
2654
2917
  columns.length = 0;
2655
2918
  columnSpans.length = 0;
2656
2919
  }
2657
- const startIndex = forceFullItemPositions || dataChanged ? 0 : (_b = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _b : 0;
2920
+ const startIndex = forceFullItemPositions || dataChanged ? 0 : (_c = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _c : 0;
2658
2921
  updateItemPositions(ctx, dataChanged, {
2659
2922
  doMVCP,
2660
2923
  forceFullUpdate: !!forceFullItemPositions,
2661
2924
  scrollBottomBuffered,
2662
2925
  startIndex
2663
2926
  });
2927
+ totalSize = getContentSize(ctx);
2664
2928
  if (minIndexSizeChanged !== void 0) {
2665
2929
  state.minIndexSizeChanged = void 0;
2666
2930
  }
@@ -2672,9 +2936,9 @@ function calculateItemsInView(ctx, params = {}) {
2672
2936
  let endBuffered = null;
2673
2937
  let loopStart = !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
2674
2938
  for (let i = loopStart; i >= 0; i--) {
2675
- const id = (_c = idCache[i]) != null ? _c : getId(state, i);
2939
+ const id = (_d = idCache[i]) != null ? _d : getId(state, i);
2676
2940
  const top = positions[i];
2677
- const size = (_d = sizes.get(id)) != null ? _d : getItemSize(ctx, id, i, data[i]);
2941
+ const size = (_e = sizes.get(id)) != null ? _e : getItemSize(ctx, id, i, data[i]);
2678
2942
  const bottom = top + size;
2679
2943
  if (bottom > scroll - scrollBufferTop) {
2680
2944
  loopStart = i;
@@ -2705,14 +2969,14 @@ function calculateItemsInView(ctx, params = {}) {
2705
2969
  let firstFullyOnScreenIndex;
2706
2970
  const dataLength = data.length;
2707
2971
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
2708
- const id = (_e = idCache[i]) != null ? _e : getId(state, i);
2709
- const size = (_f = sizes.get(id)) != null ? _f : getItemSize(ctx, id, i, data[i]);
2972
+ const id = (_f = idCache[i]) != null ? _f : getId(state, i);
2973
+ const size = (_g = sizes.get(id)) != null ? _g : getItemSize(ctx, id, i, data[i]);
2710
2974
  const top = positions[i];
2711
2975
  if (!foundEnd) {
2712
2976
  if (startNoBuffer === null && top + size > scroll) {
2713
2977
  startNoBuffer = i;
2714
2978
  }
2715
- if (firstFullyOnScreenIndex === void 0 && top >= scroll - 10) {
2979
+ if (firstFullyOnScreenIndex === void 0 && top >= scroll - 10 && top <= scrollBottom) {
2716
2980
  firstFullyOnScreenIndex = i;
2717
2981
  }
2718
2982
  if (startBuffered === null && top + size > scrollTopBuffered) {
@@ -2742,9 +3006,12 @@ function calculateItemsInView(ctx, params = {}) {
2742
3006
  }
2743
3007
  }
2744
3008
  const idsInView = [];
2745
- for (let i = firstFullyOnScreenIndex; i <= endNoBuffer; i++) {
2746
- const id = (_g = idCache[i]) != null ? _g : getId(state, i);
2747
- idsInView.push(id);
3009
+ const firstVisibleAnchorIndex = firstFullyOnScreenIndex != null ? firstFullyOnScreenIndex : startNoBuffer;
3010
+ if (firstVisibleAnchorIndex !== null && firstVisibleAnchorIndex !== void 0 && endNoBuffer !== null) {
3011
+ for (let i = firstVisibleAnchorIndex; i <= endNoBuffer; i++) {
3012
+ const id = (_h = idCache[i]) != null ? _h : getId(state, i);
3013
+ idsInView.push(id);
3014
+ }
2748
3015
  }
2749
3016
  Object.assign(state, {
2750
3017
  endBuffered,
@@ -2775,7 +3042,7 @@ function calculateItemsInView(ctx, params = {}) {
2775
3042
  const needNewContainers = [];
2776
3043
  const needNewContainersSet = /* @__PURE__ */ new Set();
2777
3044
  for (let i = startBuffered; i <= endBuffered; i++) {
2778
- const id = (_h = idCache[i]) != null ? _h : getId(state, i);
3045
+ const id = (_i = idCache[i]) != null ? _i : getId(state, i);
2779
3046
  if (!containerItemKeys.has(id)) {
2780
3047
  needNewContainersSet.add(i);
2781
3048
  needNewContainers.push(i);
@@ -2784,7 +3051,7 @@ function calculateItemsInView(ctx, params = {}) {
2784
3051
  if (alwaysRenderArr.length > 0) {
2785
3052
  for (const index of alwaysRenderArr) {
2786
3053
  if (index < 0 || index >= dataLength) continue;
2787
- const id = (_i = idCache[index]) != null ? _i : getId(state, index);
3054
+ const id = (_j = idCache[index]) != null ? _j : getId(state, index);
2788
3055
  if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
2789
3056
  needNewContainersSet.add(index);
2790
3057
  needNewContainers.push(index);
@@ -2822,7 +3089,7 @@ function calculateItemsInView(ctx, params = {}) {
2822
3089
  for (let idx = 0; idx < needNewContainers.length; idx++) {
2823
3090
  const i = needNewContainers[idx];
2824
3091
  const containerIndex = availableContainers[idx];
2825
- const id = (_j = idCache[i]) != null ? _j : getId(state, i);
3092
+ const id = (_k = idCache[i]) != null ? _k : getId(state, i);
2826
3093
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
2827
3094
  if (oldKey && oldKey !== id) {
2828
3095
  containerItemKeys.delete(oldKey);
@@ -2863,7 +3130,7 @@ function calculateItemsInView(ctx, params = {}) {
2863
3130
  if (alwaysRenderArr.length > 0) {
2864
3131
  for (const index of alwaysRenderArr) {
2865
3132
  if (index < 0 || index >= dataLength) continue;
2866
- const id = (_k = idCache[index]) != null ? _k : getId(state, index);
3133
+ const id = (_l = idCache[index]) != null ? _l : getId(state, index);
2867
3134
  const containerIndex = containerItemKeys.get(id);
2868
3135
  if (containerIndex !== void 0) {
2869
3136
  state.stickyContainerPool.add(containerIndex);
@@ -2974,40 +3241,6 @@ function checkActualChange(state, dataProp, previousData) {
2974
3241
  return false;
2975
3242
  }
2976
3243
 
2977
- // src/core/doMaintainScrollAtEnd.ts
2978
- function doMaintainScrollAtEnd(ctx, animated) {
2979
- const state = ctx.state;
2980
- const {
2981
- didContainersLayout,
2982
- isAtEnd,
2983
- refScroller,
2984
- props: { maintainScrollAtEnd }
2985
- } = state;
2986
- if (isAtEnd && maintainScrollAtEnd && didContainersLayout) {
2987
- const contentSize = getContentSize(ctx);
2988
- if (contentSize < state.scrollLength) {
2989
- state.scroll = 0;
2990
- }
2991
- requestAnimationFrame(() => {
2992
- var _a3;
2993
- if (state.isAtEnd) {
2994
- state.maintainingScrollAtEnd = true;
2995
- (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
2996
- animated
2997
- });
2998
- setTimeout(
2999
- () => {
3000
- state.maintainingScrollAtEnd = false;
3001
- },
3002
- 0
3003
- );
3004
- }
3005
- });
3006
- return true;
3007
- }
3008
- return false;
3009
- }
3010
-
3011
3244
  // src/utils/updateAveragesOnDataChange.ts
3012
3245
  function updateAveragesOnDataChange(state, oldData, newData) {
3013
3246
  var _a3;
@@ -3069,8 +3302,8 @@ function checkResetContainers(ctx, dataProp) {
3069
3302
  }
3070
3303
  const { maintainScrollAtEnd } = state.props;
3071
3304
  calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
3072
- const shouldMaintainScrollAtEnd = maintainScrollAtEnd === true || maintainScrollAtEnd.onDataChange;
3073
- const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, false);
3305
+ const shouldMaintainScrollAtEnd = maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onDataChange;
3306
+ const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx);
3074
3307
  if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
3075
3308
  state.isEndReached = false;
3076
3309
  }
@@ -3174,8 +3407,8 @@ function handleLayout(ctx, layoutParam, setCanRender) {
3174
3407
  if (didChange || otherAxisSize !== prevOtherAxisSize) {
3175
3408
  set$(ctx, "scrollSize", { height: layout.height, width: layout.width });
3176
3409
  }
3177
- if (maintainScrollAtEnd === true || maintainScrollAtEnd.onLayout) {
3178
- doMaintainScrollAtEnd(ctx, false);
3410
+ if (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onLayout) {
3411
+ doMaintainScrollAtEnd(ctx);
3179
3412
  }
3180
3413
  checkThresholds(ctx);
3181
3414
  if (state) {
@@ -3192,6 +3425,12 @@ function handleLayout(ctx, layoutParam, setCanRender) {
3192
3425
  }
3193
3426
 
3194
3427
  // src/core/onScroll.ts
3428
+ var INITIAL_SCROLL_PROGRESS_EPSILON = 1;
3429
+ function didObserveInitialScrollProgress(newScroll, watchdog) {
3430
+ const previousDistance = Math.abs(watchdog.startScroll - watchdog.targetOffset);
3431
+ const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
3432
+ return nextDistance <= INITIAL_SCROLL_PROGRESS_EPSILON || nextDistance + INITIAL_SCROLL_PROGRESS_EPSILON < previousDistance;
3433
+ }
3195
3434
  function onScroll(ctx, event) {
3196
3435
  var _a3, _b, _c, _d;
3197
3436
  const state = ctx.state;
@@ -3229,7 +3468,16 @@ function onScroll(ctx, event) {
3229
3468
  }
3230
3469
  }
3231
3470
  state.scrollPending = newScroll;
3471
+ const initialNativeScrollWatchdog = state.initialNativeScrollWatchdog;
3472
+ const didInitialScrollProgress = !!initialNativeScrollWatchdog && didObserveInitialScrollProgress(newScroll, initialNativeScrollWatchdog);
3473
+ if (didInitialScrollProgress) {
3474
+ state.initialNativeScrollWatchdog = void 0;
3475
+ }
3232
3476
  updateScroll(ctx, newScroll, insetChanged);
3477
+ if (initialNativeScrollWatchdog && !didInitialScrollProgress) {
3478
+ state.hasScrolled = false;
3479
+ state.initialNativeScrollWatchdog = initialNativeScrollWatchdog;
3480
+ }
3233
3481
  if (state.scrollingTo) {
3234
3482
  checkFinishedScroll(ctx);
3235
3483
  }
@@ -3396,8 +3644,8 @@ function updateItemSize(ctx, itemKey, sizeObj) {
3396
3644
  runOrScheduleMVCPRecalculate(ctx);
3397
3645
  }
3398
3646
  if (shouldMaintainScrollAtEnd) {
3399
- if (maintainScrollAtEnd === true || maintainScrollAtEnd.onItemLayout) {
3400
- doMaintainScrollAtEnd(ctx, false);
3647
+ if (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onItemLayout) {
3648
+ doMaintainScrollAtEnd(ctx);
3401
3649
  }
3402
3650
  }
3403
3651
  }
@@ -3773,6 +4021,34 @@ function getRenderedItem(ctx, key) {
3773
4021
  return { index, item: data[index], renderedItem };
3774
4022
  }
3775
4023
 
4024
+ // src/utils/normalizeMaintainScrollAtEnd.ts
4025
+ function normalizeMaintainScrollAtEndOn(on, hasExplicitOn) {
4026
+ var _a3, _b, _c;
4027
+ return {
4028
+ animated: false,
4029
+ onDataChange: hasExplicitOn ? (_a3 = on == null ? void 0 : on.dataChange) != null ? _a3 : false : true,
4030
+ onItemLayout: hasExplicitOn ? (_b = on == null ? void 0 : on.itemLayout) != null ? _b : false : true,
4031
+ onLayout: hasExplicitOn ? (_c = on == null ? void 0 : on.layout) != null ? _c : false : true
4032
+ };
4033
+ }
4034
+ function normalizeMaintainScrollAtEnd(value) {
4035
+ var _a3;
4036
+ if (!value) {
4037
+ return void 0;
4038
+ }
4039
+ if (value === true) {
4040
+ return {
4041
+ ...normalizeMaintainScrollAtEndOn(void 0, false),
4042
+ animated: false
4043
+ };
4044
+ }
4045
+ const normalizedTriggers = normalizeMaintainScrollAtEndOn(value.on, "on" in value);
4046
+ return {
4047
+ ...normalizedTriggers,
4048
+ animated: (_a3 = value.animated) != null ? _a3 : false
4049
+ };
4050
+ }
4051
+
3776
4052
  // src/utils/normalizeMaintainVisibleContentPosition.ts
3777
4053
  function normalizeMaintainVisibleContentPosition(value) {
3778
4054
  var _a3, _b;
@@ -3874,7 +4150,7 @@ var LegendList = typedMemo2(
3874
4150
  })
3875
4151
  );
3876
4152
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
3877
- var _a3, _b, _c, _d, _e;
4153
+ var _a3, _b, _c, _d, _e, _f, _g, _h;
3878
4154
  const {
3879
4155
  alignItemsAtEnd = false,
3880
4156
  alwaysRender,
@@ -3960,16 +4236,24 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3960
4236
  const style = { ...StyleSheet.flatten(styleProp) };
3961
4237
  const stylePaddingTopState = extractPadding(style, contentContainerStyle, "Top");
3962
4238
  const stylePaddingBottomState = extractPadding(style, contentContainerStyle, "Bottom");
4239
+ const maintainScrollAtEndConfig = normalizeMaintainScrollAtEnd(maintainScrollAtEnd);
3963
4240
  const maintainVisibleContentPositionConfig = normalizeMaintainVisibleContentPosition(
3964
4241
  maintainVisibleContentPositionProp
3965
4242
  );
3966
- const initialScrollProp = initialScrollAtEnd ? { index: Math.max(0, dataProp.length - 1), viewOffset: -stylePaddingBottomState, viewPosition: 1 } : initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? {
3967
- index: initialScrollIndexProp.index || 0,
3968
- viewOffset: initialScrollIndexProp.viewOffset || (initialScrollIndexProp.viewPosition === 1 ? -stylePaddingBottomState : 0),
3969
- viewPosition: initialScrollIndexProp.viewPosition || 0
4243
+ const hasInitialScrollIndex = initialScrollIndexProp !== void 0 && initialScrollIndexProp !== null;
4244
+ const hasInitialScrollOffset = initialScrollOffsetProp !== void 0 && initialScrollOffsetProp !== null;
4245
+ const initialScrollUsesOffsetOnly = !initialScrollAtEnd && !hasInitialScrollIndex && hasInitialScrollOffset;
4246
+ const initialScrollProp = initialScrollAtEnd ? { index: Math.max(0, dataProp.length - 1), viewOffset: -stylePaddingBottomState, viewPosition: 1 } : hasInitialScrollIndex ? typeof initialScrollIndexProp === "object" ? {
4247
+ index: (_a3 = initialScrollIndexProp.index) != null ? _a3 : 0,
4248
+ viewOffset: (_b = initialScrollIndexProp.viewOffset) != null ? _b : initialScrollIndexProp.viewPosition === 1 ? -stylePaddingBottomState : 0,
4249
+ viewPosition: (_c = initialScrollIndexProp.viewPosition) != null ? _c : 0
3970
4250
  } : {
3971
- index: initialScrollIndexProp || 0,
3972
- viewOffset: initialScrollOffsetProp || 0
4251
+ index: initialScrollIndexProp != null ? initialScrollIndexProp : 0,
4252
+ viewOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : 0
4253
+ } : initialScrollUsesOffsetOnly ? {
4254
+ contentOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : 0,
4255
+ index: 0,
4256
+ viewOffset: 0
3973
4257
  } : void 0;
3974
4258
  const [canRender, setCanRender] = React2.useState(!IsNewArchitecture);
3975
4259
  const ctx = useStateContext();
@@ -3984,8 +4268,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3984
4268
  }, [
3985
4269
  alwaysRender == null ? void 0 : alwaysRender.top,
3986
4270
  alwaysRender == null ? void 0 : alwaysRender.bottom,
3987
- (_a3 = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _a3.join(","),
3988
- (_b = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _b.join(","),
4271
+ (_d = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _d.join(","),
4272
+ (_e = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _e.join(","),
3989
4273
  dataProp,
3990
4274
  dataVersion,
3991
4275
  keyExtractor
@@ -4029,14 +4313,22 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4029
4313
  idCache: [],
4030
4314
  idsInView: [],
4031
4315
  indexByKey: /* @__PURE__ */ new Map(),
4032
- initialAnchor: (initialScrollProp == null ? void 0 : initialScrollProp.index) !== void 0 && (initialScrollProp == null ? void 0 : initialScrollProp.viewPosition) !== void 0 ? {
4316
+ initialAnchor: !initialScrollUsesOffsetOnly && (initialScrollProp == null ? void 0 : initialScrollProp.index) !== void 0 && (initialScrollProp == null ? void 0 : initialScrollProp.viewPosition) !== void 0 ? {
4033
4317
  attempts: 0,
4034
4318
  index: initialScrollProp.index,
4035
4319
  settledTicks: 0,
4036
- viewOffset: (_c = initialScrollProp.viewOffset) != null ? _c : 0,
4320
+ viewOffset: (_f = initialScrollProp.viewOffset) != null ? _f : 0,
4037
4321
  viewPosition: initialScrollProp.viewPosition
4038
4322
  } : void 0,
4323
+ initialNativeScrollWatchdog: void 0,
4039
4324
  initialScroll: initialScrollProp,
4325
+ initialScrollLastDidFinish: false,
4326
+ initialScrollLastTarget: initialScrollProp,
4327
+ initialScrollLastTargetUsesOffset: initialScrollUsesOffsetOnly,
4328
+ initialScrollPreviousDataLength: dataProp.length,
4329
+ initialScrollRetryLastLength: void 0,
4330
+ initialScrollRetryWindowUntil: 0,
4331
+ initialScrollUsesOffset: initialScrollUsesOffsetOnly,
4040
4332
  isAtEnd: false,
4041
4333
  isAtStart: false,
4042
4334
  isEndReached: null,
@@ -4049,6 +4341,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4049
4341
  minIndexSizeChanged: 0,
4050
4342
  nativeContentInset: void 0,
4051
4343
  nativeMarginTop: 0,
4344
+ pendingNativeMVCPAdjust: void 0,
4052
4345
  positions: [],
4053
4346
  props: {},
4054
4347
  queuedCalculateItemsInView: 0,
@@ -4086,7 +4379,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4086
4379
  const state = refState.current;
4087
4380
  const isFirstLocal = state.isFirst;
4088
4381
  state.didColumnsChange = numColumnsProp !== state.props.numColumns;
4089
- const didDataChangeLocal = state.props.dataVersion !== dataVersion || state.props.data !== dataProp && checkActualChange(state, dataProp, state.props.data);
4382
+ const didDataReferenceChangeLocal = state.props.data !== dataProp;
4383
+ const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
4384
+ const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkActualChange(state, dataProp, state.props.data);
4090
4385
  if (didDataChangeLocal) {
4091
4386
  state.dataChangeEpoch += 1;
4092
4387
  state.dataChangeNeedsScrollUpdate = true;
@@ -4112,7 +4407,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4112
4407
  initialContainerPoolRatio,
4113
4408
  itemsAreEqual,
4114
4409
  keyExtractor: useWrapIfItem(keyExtractor),
4115
- maintainScrollAtEnd,
4410
+ maintainScrollAtEnd: maintainScrollAtEndConfig,
4116
4411
  maintainScrollAtEndThreshold,
4117
4412
  maintainVisibleContentPosition: maintainVisibleContentPositionConfig,
4118
4413
  numColumns: numColumnsProp,
@@ -4168,16 +4463,83 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4168
4463
  );
4169
4464
  }
4170
4465
  const resolveInitialScrollOffset = useCallback((initialScroll) => {
4466
+ var _a4;
4467
+ if (state.initialScrollUsesOffset) {
4468
+ return clampScrollOffset(ctx, (_a4 = initialScroll.contentOffset) != null ? _a4 : 0);
4469
+ }
4171
4470
  const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, initialScroll.index) : 0;
4172
4471
  const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, initialScroll);
4173
4472
  return clampScrollOffset(ctx, resolvedOffset, initialScroll);
4174
4473
  }, []);
4474
+ const finishInitialScrollWithoutScroll = useCallback(() => {
4475
+ refState.current.initialAnchor = void 0;
4476
+ refState.current.initialScroll = void 0;
4477
+ state.initialAnchor = void 0;
4478
+ state.initialScroll = void 0;
4479
+ state.initialScrollUsesOffset = false;
4480
+ state.initialScrollLastTarget = void 0;
4481
+ state.initialScrollLastTargetUsesOffset = false;
4482
+ setInitialRenderState(ctx, { didInitialScroll: true });
4483
+ }, []);
4484
+ const setActiveInitialScrollTarget = useCallback(
4485
+ (target, options) => {
4486
+ var _a4;
4487
+ const usesOffset = !!(options == null ? void 0 : options.usesOffset);
4488
+ state.initialScrollUsesOffset = usesOffset;
4489
+ state.initialScrollLastTarget = target;
4490
+ state.initialScrollLastTargetUsesOffset = usesOffset;
4491
+ refState.current.initialScroll = target;
4492
+ state.initialScroll = target;
4493
+ if ((options == null ? void 0 : options.resetDidFinish) && state.didFinishInitialScroll) {
4494
+ state.didFinishInitialScroll = false;
4495
+ }
4496
+ if (!(options == null ? void 0 : options.syncAnchor)) {
4497
+ return;
4498
+ }
4499
+ if (!IsNewArchitecture && !usesOffset && target.index !== void 0 && target.viewPosition !== void 0) {
4500
+ state.initialAnchor = {
4501
+ attempts: 0,
4502
+ index: target.index,
4503
+ settledTicks: 0,
4504
+ viewOffset: (_a4 = target.viewOffset) != null ? _a4 : 0,
4505
+ viewPosition: target.viewPosition
4506
+ };
4507
+ }
4508
+ },
4509
+ []
4510
+ );
4511
+ const shouldFinishInitialScrollAtOrigin = useCallback(
4512
+ (initialScroll, offset) => {
4513
+ var _a4, _b2, _c2;
4514
+ if (offset !== 0 || initialScrollAtEnd) {
4515
+ return false;
4516
+ }
4517
+ if (state.initialScrollUsesOffset) {
4518
+ return Math.abs((_a4 = initialScroll.contentOffset) != null ? _a4 : 0) <= 1;
4519
+ }
4520
+ return initialScroll.index === 0 && ((_b2 = initialScroll.viewPosition) != null ? _b2 : 0) === 0 && Math.abs((_c2 = initialScroll.viewOffset) != null ? _c2 : 0) <= 1;
4521
+ },
4522
+ [initialScrollAtEnd]
4523
+ );
4524
+ const shouldFinishEmptyInitialScrollAtEnd = useCallback(
4525
+ (initialScroll, offset) => {
4526
+ return dataProp.length === 0 && initialScrollAtEnd && offset === 0 && initialScroll.viewPosition === 1;
4527
+ },
4528
+ [dataProp.length, initialScrollAtEnd]
4529
+ );
4530
+ const shouldRearmFinishedEmptyInitialScrollAtEnd = useCallback(
4531
+ (initialScroll) => {
4532
+ var _a4;
4533
+ return !!(state.didFinishInitialScroll && dataProp.length > 0 && initialScroll && !state.initialScrollUsesOffset && initialScroll.index === 0 && initialScroll.viewPosition === 1 && ((_a4 = initialScroll.contentOffset) != null ? _a4 : 0) === 0);
4534
+ },
4535
+ [dataProp.length]
4536
+ );
4175
4537
  const initialContentOffset = useMemo(() => {
4176
4538
  var _a4;
4177
4539
  let value;
4178
4540
  const { initialScroll, initialAnchor } = refState.current;
4179
4541
  if (initialScroll) {
4180
- if (!IsNewArchitecture && initialScroll.index !== void 0 && (!initialAnchor || (initialAnchor == null ? void 0 : initialAnchor.index) !== initialScroll.index)) {
4542
+ if (!state.initialScrollUsesOffset && !IsNewArchitecture && initialScroll.index !== void 0 && (!initialAnchor || (initialAnchor == null ? void 0 : initialAnchor.index) !== initialScroll.index)) {
4181
4543
  refState.current.initialAnchor = {
4182
4544
  attempts: 0,
4183
4545
  index: initialScroll.index,
@@ -4191,16 +4553,22 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4191
4553
  } else {
4192
4554
  const clampedOffset = resolveInitialScrollOffset(initialScroll);
4193
4555
  const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
4194
- refState.current.initialScroll = updatedInitialScroll;
4195
- state.initialScroll = updatedInitialScroll;
4556
+ setActiveInitialScrollTarget(updatedInitialScroll, {
4557
+ usesOffset: state.initialScrollUsesOffset
4558
+ });
4196
4559
  value = clampedOffset;
4197
4560
  }
4198
4561
  } else {
4199
4562
  refState.current.initialAnchor = void 0;
4200
4563
  value = 0;
4201
4564
  }
4202
- if (!value) {
4203
- setInitialRenderState(ctx, { didInitialScroll: true });
4565
+ const hasPendingDataDependentInitialScroll = !!initialScroll && dataProp.length === 0 && !shouldFinishInitialScrollAtOrigin(initialScroll, value) && !shouldFinishEmptyInitialScrollAtEnd(initialScroll, value);
4566
+ if (!value && !hasPendingDataDependentInitialScroll) {
4567
+ if (initialScroll && shouldFinishInitialScrollAtOrigin(initialScroll, value)) {
4568
+ finishInitialScrollWithoutScroll();
4569
+ } else {
4570
+ setInitialRenderState(ctx, { didInitialScroll: true });
4571
+ }
4204
4572
  }
4205
4573
  return value;
4206
4574
  }, []);
@@ -4217,24 +4585,131 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4217
4585
  set$(ctx, "totalSize", 0);
4218
4586
  }
4219
4587
  }
4220
- const doInitialScroll = useCallback(() => {
4221
- const { initialScroll, didFinishInitialScroll, queuedInitialLayout, scrollingTo } = state;
4222
- if (initialScroll && !queuedInitialLayout && !didFinishInitialScroll && !scrollingTo) {
4223
- const offset = resolveInitialScrollOffset(initialScroll);
4588
+ const doInitialScroll = useCallback((options) => {
4589
+ var _a4, _b2;
4590
+ const allowPostFinishRetry = !!(options == null ? void 0 : options.allowPostFinishRetry);
4591
+ const { didFinishInitialScroll, queuedInitialLayout, scrollingTo } = state;
4592
+ const initialScroll = (_a4 = state.initialScroll) != null ? _a4 : allowPostFinishRetry ? state.initialScrollLastTarget : void 0;
4593
+ const isInitialScrollInProgress = !!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll);
4594
+ const needsContainerLayoutForInitialScroll = !state.initialScrollUsesOffset;
4595
+ const shouldWaitForInitialLayout = waitForInitialLayout && needsContainerLayoutForInitialScroll && !queuedInitialLayout && !allowPostFinishRetry && !isInitialScrollInProgress;
4596
+ if (!initialScroll || shouldWaitForInitialLayout || didFinishInitialScroll && !allowPostFinishRetry || scrollingTo && !isInitialScrollInProgress) {
4597
+ return;
4598
+ }
4599
+ if (allowPostFinishRetry && state.initialScrollLastTargetUsesOffset) {
4600
+ return;
4601
+ }
4602
+ const didMoveAwayFromInitialTarget = allowPostFinishRetry && initialScroll.contentOffset !== void 0 && Math.abs(state.scroll - initialScroll.contentOffset) > 1;
4603
+ if (didMoveAwayFromInitialTarget) {
4604
+ state.initialScrollRetryWindowUntil = 0;
4605
+ return;
4606
+ }
4607
+ const offset = resolveInitialScrollOffset(initialScroll);
4608
+ const activeInitialTargetOffset = isInitialScrollInProgress ? (_b2 = scrollingTo.targetOffset) != null ? _b2 : scrollingTo.offset : void 0;
4609
+ const didOffsetChange = initialScroll.contentOffset === void 0 || Math.abs(initialScroll.contentOffset - offset) > 1;
4610
+ const didActiveInitialTargetChange = activeInitialTargetOffset !== void 0 && Math.abs(activeInitialTargetOffset - offset) > 1;
4611
+ if (!didOffsetChange && (allowPostFinishRetry || isInitialScrollInProgress && !didActiveInitialTargetChange)) {
4612
+ return;
4613
+ }
4614
+ if (didOffsetChange) {
4224
4615
  const updatedInitialScroll = { ...initialScroll, contentOffset: offset };
4225
- refState.current.initialScroll = updatedInitialScroll;
4226
- state.initialScroll = updatedInitialScroll;
4227
- scrollTo(ctx, {
4228
- animated: false,
4229
- index: initialScroll.index,
4230
- isInitialScroll: true,
4231
- offset,
4232
- precomputedWithViewOffset: true
4233
- });
4616
+ if (!state.initialScrollUsesOffset) {
4617
+ state.initialScrollLastTarget = updatedInitialScroll;
4618
+ state.initialScrollLastTargetUsesOffset = false;
4619
+ if (state.initialScroll) {
4620
+ refState.current.initialScroll = updatedInitialScroll;
4621
+ state.initialScroll = updatedInitialScroll;
4622
+ }
4623
+ }
4234
4624
  }
4625
+ const hasMeasuredScrollLayout = !!state.lastLayout && state.scrollLength > 0;
4626
+ const shouldForceNativeInitialScroll = state.initialScrollUsesOffset && hasMeasuredScrollLayout || allowPostFinishRetry || !!queuedInitialLayout || isInitialScrollInProgress && didOffsetChange;
4627
+ performInitialScroll(ctx, {
4628
+ forceScroll: shouldForceNativeInitialScroll,
4629
+ initialScrollUsesOffset: state.initialScrollUsesOffset,
4630
+ resolvedOffset: offset,
4631
+ target: initialScroll
4632
+ });
4235
4633
  }, []);
4634
+ useLayoutEffect(() => {
4635
+ var _a4;
4636
+ const previousDataLength = state.initialScrollPreviousDataLength;
4637
+ state.initialScrollPreviousDataLength = dataProp.length;
4638
+ if (previousDataLength !== 0 || dataProp.length === 0 || !state.initialScroll || !state.queuedInitialLayout) {
4639
+ return;
4640
+ }
4641
+ if (initialScrollAtEnd) {
4642
+ const lastIndex = Math.max(0, dataProp.length - 1);
4643
+ const initialScroll = state.initialScroll;
4644
+ const shouldRearm = shouldRearmFinishedEmptyInitialScrollAtEnd(initialScroll);
4645
+ if (state.didFinishInitialScroll && !shouldRearm) {
4646
+ return;
4647
+ }
4648
+ if (initialScroll && !state.initialScrollUsesOffset && initialScroll.index === lastIndex && initialScroll.viewPosition === 1 && !shouldRearm) {
4649
+ return;
4650
+ }
4651
+ const updatedInitialScroll = {
4652
+ contentOffset: void 0,
4653
+ index: lastIndex,
4654
+ viewOffset: (_a4 = initialScroll == null ? void 0 : initialScroll.viewOffset) != null ? _a4 : -stylePaddingBottomState,
4655
+ viewPosition: 1
4656
+ };
4657
+ setActiveInitialScrollTarget(updatedInitialScroll, {
4658
+ resetDidFinish: shouldRearm,
4659
+ syncAnchor: true
4660
+ });
4661
+ doInitialScroll();
4662
+ return;
4663
+ }
4664
+ if (state.didFinishInitialScroll) {
4665
+ return;
4666
+ }
4667
+ doInitialScroll();
4668
+ }, [
4669
+ dataProp.length,
4670
+ doInitialScroll,
4671
+ initialScrollAtEnd,
4672
+ shouldRearmFinishedEmptyInitialScrollAtEnd,
4673
+ stylePaddingBottomState
4674
+ ]);
4675
+ useLayoutEffect(() => {
4676
+ var _a4;
4677
+ if (!initialScrollAtEnd) {
4678
+ return;
4679
+ }
4680
+ const lastIndex = Math.max(0, dataProp.length - 1);
4681
+ const initialScroll = state.initialScroll;
4682
+ const shouldRearm = shouldRearmFinishedEmptyInitialScrollAtEnd(initialScroll);
4683
+ if (state.didFinishInitialScroll && !shouldRearm) {
4684
+ return;
4685
+ }
4686
+ if (shouldRearm) {
4687
+ state.didFinishInitialScroll = false;
4688
+ }
4689
+ if (initialScroll && !state.initialScrollUsesOffset && initialScroll.index === lastIndex && initialScroll.viewPosition === 1 && !shouldRearm) {
4690
+ return;
4691
+ }
4692
+ const updatedInitialScroll = {
4693
+ contentOffset: void 0,
4694
+ index: lastIndex,
4695
+ viewOffset: (_a4 = initialScroll == null ? void 0 : initialScroll.viewOffset) != null ? _a4 : -stylePaddingBottomState,
4696
+ viewPosition: 1
4697
+ };
4698
+ setActiveInitialScrollTarget(updatedInitialScroll, {
4699
+ resetDidFinish: shouldRearm,
4700
+ syncAnchor: true
4701
+ });
4702
+ doInitialScroll();
4703
+ }, [
4704
+ dataProp.length,
4705
+ doInitialScroll,
4706
+ initialScrollAtEnd,
4707
+ shouldRearmFinishedEmptyInitialScrollAtEnd,
4708
+ stylePaddingBottomState
4709
+ ]);
4236
4710
  const onLayoutFooter = useCallback(
4237
4711
  (layout) => {
4712
+ var _a4;
4238
4713
  if (!initialScrollAtEnd) {
4239
4714
  return;
4240
4715
  }
@@ -4249,16 +4724,48 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4249
4724
  const footerSize = layout[horizontal ? "width" : "height"];
4250
4725
  const viewOffset = -stylePaddingBottomState - footerSize;
4251
4726
  if (initialScroll.viewOffset !== viewOffset) {
4727
+ const previousTargetOffset = (_a4 = initialScroll.contentOffset) != null ? _a4 : resolveInitialScrollOffset(initialScroll);
4728
+ const didMoveAwayFromFinishedInitialTarget = state.didFinishInitialScroll && Math.abs(state.scroll - previousTargetOffset) > 1;
4729
+ if (didMoveAwayFromFinishedInitialTarget) {
4730
+ return;
4731
+ }
4252
4732
  const updatedInitialScroll = { ...initialScroll, viewOffset };
4253
- refState.current.initialScroll = updatedInitialScroll;
4254
- state.initialScroll = updatedInitialScroll;
4733
+ setActiveInitialScrollTarget(updatedInitialScroll, {
4734
+ resetDidFinish: true
4735
+ });
4736
+ doInitialScroll();
4255
4737
  }
4256
4738
  },
4257
- [dataProp.length, horizontal, initialScrollAtEnd, stylePaddingBottomState]
4739
+ [
4740
+ dataProp.length,
4741
+ doInitialScroll,
4742
+ horizontal,
4743
+ initialScrollAtEnd,
4744
+ resolveInitialScrollOffset,
4745
+ stylePaddingBottomState
4746
+ ]
4258
4747
  );
4259
4748
  const onLayoutChange = useCallback((layout) => {
4260
- doInitialScroll();
4749
+ var _a4;
4261
4750
  handleLayout(ctx, layout, setCanRender);
4751
+ const SCROLL_LENGTH_RETRY_WINDOW_MS = 600;
4752
+ const now = Date.now();
4753
+ const didFinishInitialScroll = !!state.didFinishInitialScroll;
4754
+ if (didFinishInitialScroll && !state.initialScrollLastDidFinish) {
4755
+ state.initialScrollRetryWindowUntil = now + SCROLL_LENGTH_RETRY_WINDOW_MS;
4756
+ }
4757
+ state.initialScrollLastDidFinish = didFinishInitialScroll;
4758
+ const previousScrollLength = state.initialScrollRetryLastLength;
4759
+ const currentScrollLength = state.scrollLength;
4760
+ const didScrollLengthChange = previousScrollLength === void 0 || Math.abs(currentScrollLength - previousScrollLength) > 1;
4761
+ if (didScrollLengthChange) {
4762
+ state.initialScrollRetryLastLength = currentScrollLength;
4763
+ }
4764
+ if (didFinishInitialScroll && didScrollLengthChange && now <= state.initialScrollRetryWindowUntil && !state.initialScrollLastTargetUsesOffset && ((_a4 = state.initialScrollLastTarget) == null ? void 0 : _a4.index) !== void 0) {
4765
+ doInitialScroll({ allowPostFinishRetry: true });
4766
+ return;
4767
+ }
4768
+ doInitialScroll();
4262
4769
  }, []);
4263
4770
  const { onLayout } = useOnLayoutSync({
4264
4771
  onLayoutChange,
@@ -4375,7 +4882,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4375
4882
  onScroll: onScrollHandler,
4376
4883
  recycleItems,
4377
4884
  refreshControl: refreshControlElement ? stylePaddingTopState > 0 ? React2.cloneElement(refreshControlElement, {
4378
- progressViewOffset: ((_d = refreshControlElement.props.progressViewOffset) != null ? _d : 0) + stylePaddingTopState
4885
+ progressViewOffset: ((_g = refreshControlElement.props.progressViewOffset) != null ? _g : 0) + stylePaddingTopState
4379
4886
  }) : refreshControlElement : onRefresh && /* @__PURE__ */ React2.createElement(
4380
4887
  RefreshControl,
4381
4888
  {
@@ -4386,7 +4893,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4386
4893
  ),
4387
4894
  refScrollView: combinedRef,
4388
4895
  renderScrollComponent,
4389
- scrollAdjustHandler: (_e = refState.current) == null ? void 0 : _e.scrollAdjustHandler,
4896
+ scrollAdjustHandler: (_h = refState.current) == null ? void 0 : _h.scrollAdjustHandler,
4390
4897
  scrollEventThrottle: 0,
4391
4898
  snapToIndices,
4392
4899
  stickyHeaderIndices,
@@ -4398,8 +4905,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4398
4905
  ), IS_DEV && ENABLE_DEBUG_VIEW);
4399
4906
  });
4400
4907
 
4401
- // src/react-native.ts
4402
- var LegendList3 = LegendList;
4908
+ // src/entrypoints/shared.ts
4909
+ var LegendListRuntime = LegendList;
4403
4910
  var internal = {
4404
4911
  getComponent,
4405
4912
  IsNewArchitecture,
@@ -4410,4 +4917,8 @@ var internal = {
4410
4917
  useStateContext
4411
4918
  };
4412
4919
 
4413
- export { LegendList3 as LegendList, internal, typedForwardRef, typedMemo2 as typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };
4920
+ // src/react-native.ts
4921
+ var LegendList3 = LegendListRuntime;
4922
+ var internal2 = internal;
4923
+
4924
+ export { LegendList3 as LegendList, internal2 as internal, typedForwardRef, typedMemo2 as typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };