@legendapp/list 3.3.9 → 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.
@@ -959,6 +959,7 @@ function supersedeInitialScroll(ctx) {
959
959
  function finishInitialScroll(ctx, options) {
960
960
  var _a3, _b, _c;
961
961
  const state = ctx.state;
962
+ const freshDataTransitionEpoch = state.freshDataTransitionEpoch;
962
963
  if ((options == null ? void 0 : options.resolvedOffset) !== void 0) {
963
964
  syncInitialScrollOffset(state, options.resolvedOffset);
964
965
  } else if ((options == null ? void 0 : options.syncObservedOffset) && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset") {
@@ -968,9 +969,13 @@ function finishInitialScroll(ctx, options) {
968
969
  }
969
970
  }
970
971
  const complete = () => {
971
- var _a4, _b2, _c2, _d, _e;
972
- const shouldReleaseDeferredPublicOnScroll = ((_a4 = state.initialScrollSession) == null ? void 0 : _a4.kind) === "bootstrap";
973
- const finalScrollOffset = (_d = (_c2 = (_b2 = options == null ? void 0 : options.resolvedOffset) != null ? _b2 : state.scrollPending) != null ? _c2 : state.scroll) != null ? _d : 0;
972
+ var _a4, _b2, _c2, _d, _e, _f;
973
+ if (freshDataTransitionEpoch !== state.freshDataTransitionEpoch) {
974
+ (_a4 = options == null ? void 0 : options.onFinished) == null ? void 0 : _a4.call(options);
975
+ return;
976
+ }
977
+ const shouldReleaseDeferredPublicOnScroll = ((_b2 = state.initialScrollSession) == null ? void 0 : _b2.kind) === "bootstrap";
978
+ const finalScrollOffset = (_e = (_d = (_c2 = options == null ? void 0 : options.resolvedOffset) != null ? _c2 : state.scrollPending) != null ? _d : state.scroll) != null ? _e : 0;
974
979
  initialScrollWatchdog.clear(state);
975
980
  if ((options == null ? void 0 : options.preserveTarget) && state.initialScroll) {
976
981
  state.clearPreservedInitialScrollOnNextFinish = void 0;
@@ -999,7 +1004,7 @@ function finishInitialScroll(ctx, options) {
999
1004
  if (shouldReleaseDeferredPublicOnScroll) {
1000
1005
  releaseDeferredPublicOnScroll(ctx, finalScrollOffset);
1001
1006
  }
1002
- (_e = options == null ? void 0 : options.onFinished) == null ? void 0 : _e.call(options);
1007
+ (_f = options == null ? void 0 : options.onFinished) == null ? void 0 : _f.call(options);
1003
1008
  };
1004
1009
  if (options == null ? void 0 : options.waitForCompletionFrame) {
1005
1010
  requestAnimationFrame(complete);
@@ -1281,153 +1286,21 @@ function clampScrollOffset(ctx, offset, scrollTarget) {
1281
1286
  return clampedOffset;
1282
1287
  }
1283
1288
 
1284
- // src/core/finishScrollTo.ts
1285
- function finishScrollTo(ctx) {
1286
- var _a3, _b;
1287
- const state = ctx.state;
1288
- if (state == null ? void 0 : state.scrollingTo) {
1289
- cancelScrollCompletionChecks(state);
1290
- const resolvePendingScroll = state.pendingScrollResolve;
1291
- state.pendingScrollResolve = void 0;
1292
- const scrollingTo = state.scrollingTo;
1293
- state.scrollHistory.length = 0;
1294
- state.scrollingTo = void 0;
1295
- state.scrollTargetPinnedRange = void 0;
1296
- if (state.pendingTotalSize !== void 0) {
1297
- addTotalSize(ctx, null, state.pendingTotalSize);
1298
- }
1299
- {
1300
- state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
1301
- }
1302
- if (scrollingTo.isInitialScroll || state.initialScroll) {
1303
- const isOffsetSession = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
1304
- const shouldPreserveResizeTarget = !!scrollingTo.isInitialScroll && !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_b = state.initialScroll) == null ? void 0 : _b.viewPosition) === 1;
1305
- finishInitialScroll(ctx, {
1306
- onFinished: () => {
1307
- resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1308
- },
1309
- preserveTarget: isOffsetSession && state.props.data.length === 0 || shouldPreserveResizeTarget,
1310
- recalculateItems: true,
1311
- schedulePreservedTargetClear: shouldPreserveResizeTarget,
1312
- syncObservedOffset: isOffsetSession,
1313
- waitForCompletionFrame: !!scrollingTo.waitForInitialScrollCompletionFrame
1314
- });
1315
- return;
1316
- }
1317
- recalculateSettledScroll(ctx);
1318
- resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1319
- }
1320
- }
1321
-
1322
- // src/core/doScrollTo.ts
1323
- var SCROLL_END_IDLE_MS = 80;
1324
- var SCROLL_END_MAX_MS = 1500;
1325
- var SMOOTH_SCROLL_DURATION_MS = 320;
1326
- var SCROLL_END_TARGET_EPSILON = 1;
1327
- function doScrollTo(ctx, params) {
1328
- var _a3, _b;
1329
- const state = ctx.state;
1330
- const { animated, horizontal, offset } = params;
1331
- state.scheduledWork.cancel("platformScrollCompletion");
1332
- const scroller = state.refScroller.current;
1333
- const node = scroller == null ? void 0 : scroller.getScrollableNode();
1334
- if (!scroller || !node) {
1335
- return;
1336
- }
1337
- const isAnimated = !!animated;
1338
- const isHorizontal = !!horizontal;
1339
- const contentSize = isHorizontal ? getContentSize(ctx) : void 0;
1340
- const left = isHorizontal ? toNativeHorizontalOffset(state, offset, contentSize) : 0;
1341
- const top = isHorizontal ? 0 : offset;
1342
- scroller.scrollTo({ animated: isAnimated, x: left, y: top });
1343
- if (isAnimated) {
1344
- const target = (_b = (_a3 = scroller.getScrollEventTarget) == null ? void 0 : _a3.call(scroller)) != null ? _b : null;
1345
- listenForScrollEnd(ctx, {
1346
- readOffset: () => scroller.getCurrentScrollOffset(),
1347
- target,
1348
- targetOffset: offset
1349
- });
1350
- } else {
1351
- state.scroll = offset;
1352
- const targetToken = state.scrollingTo;
1353
- state.scheduledWork.timeout(
1354
- () => {
1355
- if (targetToken === state.scrollingTo) {
1356
- finishScrollTo(ctx);
1357
- }
1358
- },
1359
- 100,
1360
- "platformScrollCompletion"
1361
- );
1362
- }
1363
- }
1364
- function listenForScrollEnd(ctx, params) {
1365
- const { readOffset, target, targetOffset } = params;
1366
- if (!target) {
1367
- finishScrollTo(ctx);
1368
- return;
1369
- }
1370
- const supportsScrollEnd = "onscrollend" in target;
1371
- let idleTimeout;
1372
- let settled = false;
1373
- const { scheduledWork, scrollingTo: targetToken } = ctx.state;
1374
- const maxTimeout = setTimeout(() => finish("max"), SCROLL_END_MAX_MS);
1375
- const cleanup = () => {
1376
- target.removeEventListener("scroll", onScroll2);
1377
- if (supportsScrollEnd) {
1378
- target.removeEventListener("scrollend", onScrollEnd);
1379
- }
1380
- if (idleTimeout !== void 0) {
1381
- clearTimeout(idleTimeout);
1382
- }
1383
- clearTimeout(maxTimeout);
1384
- };
1385
- const cancel = () => {
1386
- if (!settled) {
1387
- settled = true;
1388
- cleanup();
1389
- }
1390
- };
1391
- const finish = (reason) => {
1392
- if (settled) return;
1393
- if (targetToken !== ctx.state.scrollingTo) {
1394
- scheduledWork.cancel("platformScrollCompletion");
1395
- return;
1396
- }
1397
- const currentOffset = readOffset();
1398
- const isNearTarget = Math.abs(currentOffset - targetOffset) <= SCROLL_END_TARGET_EPSILON;
1399
- if (reason === "scrollend" && !isNearTarget) {
1400
- return;
1401
- }
1402
- scheduledWork.cancel("platformScrollCompletion");
1403
- finishScrollTo(ctx);
1404
- };
1405
- const onScroll2 = () => {
1406
- if (idleTimeout !== void 0) {
1407
- clearTimeout(idleTimeout);
1408
- }
1409
- idleTimeout = setTimeout(() => finish("idle"), SCROLL_END_IDLE_MS);
1410
- };
1411
- const onScrollEnd = () => finish("scrollend");
1412
- target.addEventListener("scroll", onScroll2);
1413
- if (supportsScrollEnd) {
1414
- target.addEventListener("scrollend", onScrollEnd);
1415
- } else {
1416
- idleTimeout = setTimeout(() => finish("idle"), SMOOTH_SCROLL_DURATION_MS);
1417
- }
1418
- scheduledWork.register("platformScrollCompletion", cancel);
1419
- }
1420
-
1421
1289
  // src/core/scrollRequestTracker.ts
1422
1290
  function getScrollRequestTracker(ctx) {
1423
1291
  if (!ctx.scrollRequestTracker) {
1424
1292
  let currentToken = 0;
1425
- const start = (resolve) => {
1293
+ let waitingToken;
1294
+ const start = (resolve, isInternal = false) => {
1426
1295
  const state = ctx.state;
1296
+ if (!isInternal && (state.maintainingScrollAtEnd || state.pendingMaintainScrollAtEnd)) {
1297
+ finishMaintainScrollAtEnd(ctx);
1298
+ }
1427
1299
  state.scheduledWork.cancel("imperativeScrollReady");
1428
1300
  const token = ++currentToken;
1429
1301
  settlePendingImperativeScroll(state);
1430
1302
  state.pendingScrollResolve = resolve;
1303
+ waitingToken = token;
1431
1304
  return token;
1432
1305
  };
1433
1306
  const runNow = (token, resolve, run) => {
@@ -1435,6 +1308,7 @@ function getScrollRequestTracker(ctx) {
1435
1308
  if (token !== currentToken) {
1436
1309
  return;
1437
1310
  }
1311
+ waitingToken = void 0;
1438
1312
  const didStartScroll = run();
1439
1313
  if (!didStartScroll || !state.scrollingTo) {
1440
1314
  if (state.pendingScrollResolve === resolve) {
@@ -1445,6 +1319,7 @@ function getScrollRequestTracker(ctx) {
1445
1319
  };
1446
1320
  ctx.scrollRequestTracker = {
1447
1321
  isCurrent: (token) => token === currentToken,
1322
+ isWaitingToRun: () => waitingToken === currentToken && !!ctx.state.pendingScrollResolve,
1448
1323
  runNow,
1449
1324
  runNowIfIdle: (run) => {
1450
1325
  const state = ctx.state;
@@ -1452,7 +1327,7 @@ function getScrollRequestTracker(ctx) {
1452
1327
  return Promise.resolve();
1453
1328
  }
1454
1329
  return new Promise((resolve) => {
1455
- const token = start(resolve);
1330
+ const token = start(resolve, true);
1456
1331
  runNow(token, resolve, run);
1457
1332
  });
1458
1333
  },
@@ -1486,87 +1361,6 @@ function requestAdjust(ctx, positionDiff, source) {
1486
1361
  }
1487
1362
  }
1488
1363
 
1489
- // src/core/doMaintainScrollAtEnd.ts
1490
- function finishMaintainScrollAtEnd(ctx) {
1491
- var _a3;
1492
- const { state } = ctx;
1493
- const currentPadding = peek$(ctx, "alignItemsAtEndPadding") || 0;
1494
- const nextPadding = getAlignItemsAtEndPadding(ctx);
1495
- state.maintainingScrollAtEnd = void 0;
1496
- state.pendingMaintainScrollAtEnd = false;
1497
- if (currentPadding !== nextPadding) {
1498
- set$(ctx, "alignItemsAtEndPadding", nextPadding);
1499
- state.scrollForNextCalculateItemsInView = void 0;
1500
- (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { forceFullItemPositions: true });
1501
- const nextScroll = clampScrollOffset(ctx, state.scroll + nextPadding - currentPadding);
1502
- requestAdjust(ctx, nextScroll - state.scroll);
1503
- }
1504
- }
1505
- function doMaintainScrollAtEnd(ctx) {
1506
- const state = ctx.state;
1507
- const {
1508
- didContainersLayout,
1509
- didFinishInitialScroll,
1510
- pendingNativeMVCPAdjust,
1511
- props: { maintainScrollAtEnd }
1512
- } = state;
1513
- const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
1514
- const isReplayingPendingMaintain = !!state.pendingMaintainScrollAtEnd;
1515
- const shouldMaintainScrollAtEnd = !!((isWithinMaintainScrollAtEndThreshold || isReplayingPendingMaintain) && maintainScrollAtEnd && didFinishInitialScroll);
1516
- if (shouldMaintainScrollAtEnd && !didContainersLayout) {
1517
- state.pendingMaintainScrollAtEnd = true;
1518
- return false;
1519
- }
1520
- if (pendingNativeMVCPAdjust) {
1521
- state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
1522
- return false;
1523
- }
1524
- if (shouldMaintainScrollAtEnd && didContainersLayout) {
1525
- state.pendingMaintainScrollAtEnd = false;
1526
- const contentSize = getContentSize(ctx);
1527
- if (contentSize < state.scrollLength) {
1528
- state.scroll = 0;
1529
- }
1530
- if (!state.maintainingScrollAtEnd) {
1531
- const pendingState = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
1532
- const activeState = maintainScrollAtEnd.animated ? "animated" : "instant";
1533
- const scrollAtRequest = state.scroll;
1534
- state.maintainingScrollAtEnd = pendingState;
1535
- requestAnimationFrame(() => {
1536
- if (state.maintainingScrollAtEnd !== pendingState) {
1537
- return;
1538
- }
1539
- const isStillWithinThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
1540
- const didScrollSinceRequest = state.scroll !== scrollAtRequest;
1541
- if (isReplayingPendingMaintain || isStillWithinThreshold || !didScrollSinceRequest) {
1542
- state.maintainingScrollAtEnd = activeState;
1543
- const scrollPromise = getScrollRequestTracker(ctx).runNowIfIdle(
1544
- () => ctx.scrollToEnd({ animated: maintainScrollAtEnd.animated })
1545
- );
1546
- void scrollPromise.then(() => {
1547
- if (state.maintainingScrollAtEnd !== activeState) {
1548
- return;
1549
- }
1550
- if (state.pendingMaintainScrollAtEnd) {
1551
- state.maintainingScrollAtEnd = void 0;
1552
- doMaintainScrollAtEnd(ctx);
1553
- } else {
1554
- finishMaintainScrollAtEnd(ctx);
1555
- }
1556
- });
1557
- } else if (state.maintainingScrollAtEnd === pendingState) {
1558
- finishMaintainScrollAtEnd(ctx);
1559
- }
1560
- });
1561
- } else {
1562
- state.pendingMaintainScrollAtEnd = true;
1563
- }
1564
- return true;
1565
- }
1566
- finishMaintainScrollAtEnd(ctx);
1567
- return false;
1568
- }
1569
-
1570
1364
  // src/core/mvcp.ts
1571
1365
  var MVCP_POSITION_EPSILON = 0.1;
1572
1366
  var MVCP_ANCHOR_LOCK_TTL_MS = 300;
@@ -2004,6 +1798,271 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2004
1798
  }
2005
1799
  }
2006
1800
 
1801
+ // src/core/doMaintainScrollAtEnd.ts
1802
+ function interruptMaintainScrollAtEnd(ctx) {
1803
+ var _a3;
1804
+ const { state } = ctx;
1805
+ const maintaining = state.maintainingScrollAtEnd;
1806
+ const cancelActiveScroll = !!(maintaining === "animated" || maintaining === "instant" || maintaining && ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isScrollToEnd));
1807
+ if (cancelActiveScroll) {
1808
+ cancelImperativeScroll(state);
1809
+ }
1810
+ if (maintaining || state.pendingMaintainScrollAtEnd) {
1811
+ finishMaintainScrollAtEnd(ctx);
1812
+ }
1813
+ return cancelActiveScroll;
1814
+ }
1815
+ function finishMaintainScrollAtEnd(ctx) {
1816
+ var _a3;
1817
+ const { state } = ctx;
1818
+ const currentPadding = peek$(ctx, "alignItemsAtEndPadding") || 0;
1819
+ const nextPadding = getAlignItemsAtEndPadding(ctx);
1820
+ state.maintainingScrollAtEnd = void 0;
1821
+ state.pendingMaintainScrollAtEnd = false;
1822
+ if (currentPadding !== nextPadding) {
1823
+ set$(ctx, "alignItemsAtEndPadding", nextPadding);
1824
+ state.scrollForNextCalculateItemsInView = void 0;
1825
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { forceFullItemPositions: true });
1826
+ const nextScroll = clampScrollOffset(ctx, state.scroll + nextPadding - currentPadding);
1827
+ requestAdjust(ctx, nextScroll - state.scroll);
1828
+ }
1829
+ }
1830
+ function doMaintainScrollAtEnd(ctx) {
1831
+ var _a3, _b, _c, _d, _e;
1832
+ const state = ctx.state;
1833
+ const {
1834
+ didContainersLayout,
1835
+ didFinishInitialScroll,
1836
+ pendingNativeMVCPAdjust,
1837
+ props: { maintainScrollAtEnd }
1838
+ } = state;
1839
+ const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
1840
+ const isReplayingPendingMaintain = !!state.pendingMaintainScrollAtEnd;
1841
+ 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);
1842
+ if (shouldMaintainScrollAtEnd && !state.scrollingTo) {
1843
+ const scroller = state.refScroller.current;
1844
+ const nativeMax = (_c = scroller == null ? void 0 : scroller.getMaxScrollOffset) == null ? void 0 : _c.call(scroller);
1845
+ const observed = (_d = scroller == null ? void 0 : scroller.getCurrentScrollOffset) == null ? void 0 : _d.call(scroller);
1846
+ if (nativeMax !== void 0 && observed !== void 0 && nativeMax < state.scroll - 1 && Math.abs(observed - nativeMax) <= 1) {
1847
+ state.pendingMaintainScrollAtEnd = true;
1848
+ state.scrollPending = observed;
1849
+ updateScroll(ctx, observed, true, { markHasScrolled: false });
1850
+ }
1851
+ }
1852
+ if (shouldMaintainScrollAtEnd && !didContainersLayout) {
1853
+ state.pendingMaintainScrollAtEnd = true;
1854
+ return false;
1855
+ }
1856
+ if (pendingNativeMVCPAdjust) {
1857
+ state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
1858
+ return false;
1859
+ }
1860
+ if (shouldMaintainScrollAtEnd && (state.scrollingTo || state.pendingScrollResolve)) {
1861
+ state.pendingMaintainScrollAtEnd = true;
1862
+ (_e = state.maintainingScrollAtEnd) != null ? _e : state.maintainingScrollAtEnd = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
1863
+ return false;
1864
+ }
1865
+ if (shouldMaintainScrollAtEnd && didContainersLayout) {
1866
+ state.pendingMaintainScrollAtEnd = false;
1867
+ const contentSize = getContentSize(ctx);
1868
+ if (contentSize < state.scrollLength) {
1869
+ state.scroll = 0;
1870
+ }
1871
+ if (!state.maintainingScrollAtEnd) {
1872
+ const pendingState = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
1873
+ const activeState = maintainScrollAtEnd.animated ? "animated" : "instant";
1874
+ const scrollAtRequest = state.scroll;
1875
+ state.maintainingScrollAtEnd = pendingState;
1876
+ requestAnimationFrame(() => {
1877
+ if (state.maintainingScrollAtEnd !== pendingState) {
1878
+ return;
1879
+ }
1880
+ const isStillWithinThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
1881
+ const didScrollSinceRequest = state.scroll !== scrollAtRequest;
1882
+ if (isReplayingPendingMaintain || isStillWithinThreshold || !didScrollSinceRequest) {
1883
+ if (state.scrollingTo || state.pendingScrollResolve) {
1884
+ state.pendingMaintainScrollAtEnd = true;
1885
+ return;
1886
+ }
1887
+ state.maintainingScrollAtEnd = activeState;
1888
+ const scrollPromise = getScrollRequestTracker(ctx).runNowIfIdle(
1889
+ () => ctx.scrollToEnd({ animated: maintainScrollAtEnd.animated })
1890
+ );
1891
+ void scrollPromise.then(() => {
1892
+ if (state.maintainingScrollAtEnd !== activeState) {
1893
+ return;
1894
+ }
1895
+ if (state.pendingMaintainScrollAtEnd) {
1896
+ state.maintainingScrollAtEnd = void 0;
1897
+ doMaintainScrollAtEnd(ctx);
1898
+ } else {
1899
+ finishMaintainScrollAtEnd(ctx);
1900
+ }
1901
+ });
1902
+ } else if (state.maintainingScrollAtEnd === pendingState) {
1903
+ finishMaintainScrollAtEnd(ctx);
1904
+ }
1905
+ });
1906
+ } else {
1907
+ state.pendingMaintainScrollAtEnd = true;
1908
+ }
1909
+ return true;
1910
+ }
1911
+ finishMaintainScrollAtEnd(ctx);
1912
+ return false;
1913
+ }
1914
+
1915
+ // src/core/finishScrollTo.ts
1916
+ function finishScrollTo(ctx) {
1917
+ var _a3, _b, _c;
1918
+ const state = ctx.state;
1919
+ if (state == null ? void 0 : state.scrollingTo) {
1920
+ cancelScrollCompletionChecks(state);
1921
+ const resolvePendingScroll = ((_a3 = ctx.scrollRequestTracker) == null ? void 0 : _a3.isWaitingToRun()) ? void 0 : state.pendingScrollResolve;
1922
+ if (resolvePendingScroll) {
1923
+ state.pendingScrollResolve = void 0;
1924
+ }
1925
+ const scrollingTo = state.scrollingTo;
1926
+ state.scrollHistory.length = 0;
1927
+ state.scrollingTo = void 0;
1928
+ state.scrollTargetPinnedRange = void 0;
1929
+ if (state.pendingTotalSize !== void 0) {
1930
+ addTotalSize(ctx, null, state.pendingTotalSize);
1931
+ }
1932
+ {
1933
+ state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
1934
+ }
1935
+ if (scrollingTo.isInitialScroll || state.initialScroll) {
1936
+ const isOffsetSession = ((_b = state.initialScrollSession) == null ? void 0 : _b.kind) === "offset";
1937
+ const shouldPreserveResizeTarget = !!scrollingTo.isInitialScroll && !state.clearPreservedInitialScrollOnNextFinish && state.props.data.length > 0 && ((_c = state.initialScroll) == null ? void 0 : _c.viewPosition) === 1;
1938
+ finishInitialScroll(ctx, {
1939
+ onFinished: () => {
1940
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1941
+ },
1942
+ preserveTarget: isOffsetSession && state.props.data.length === 0 || shouldPreserveResizeTarget,
1943
+ recalculateItems: true,
1944
+ schedulePreservedTargetClear: shouldPreserveResizeTarget,
1945
+ syncObservedOffset: isOffsetSession,
1946
+ waitForCompletionFrame: !!scrollingTo.waitForInitialScrollCompletionFrame
1947
+ });
1948
+ return;
1949
+ }
1950
+ recalculateSettledScroll(ctx);
1951
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1952
+ if (state.pendingMaintainScrollAtEnd) {
1953
+ state.maintainingScrollAtEnd = void 0;
1954
+ doMaintainScrollAtEnd(ctx);
1955
+ }
1956
+ }
1957
+ }
1958
+
1959
+ // src/core/doScrollTo.ts
1960
+ var SCROLL_END_IDLE_MS = 80;
1961
+ var SCROLL_END_MAX_MS = 1500;
1962
+ var SMOOTH_SCROLL_DURATION_MS = 320;
1963
+ var SCROLL_END_TARGET_EPSILON = 1;
1964
+ function doScrollTo(ctx, params) {
1965
+ var _a3, _b, _c, _d, _e, _f;
1966
+ const state = ctx.state;
1967
+ const { animated, horizontal, offset } = params;
1968
+ state.scheduledWork.cancel("platformScrollCompletion");
1969
+ const scroller = state.refScroller.current;
1970
+ const node = scroller == null ? void 0 : scroller.getScrollableNode();
1971
+ if (!scroller || !node) {
1972
+ return;
1973
+ }
1974
+ const isAnimated = !!animated;
1975
+ const isHorizontal = !!horizontal;
1976
+ const contentSize = isHorizontal ? getContentSize(ctx) : void 0;
1977
+ const left = isHorizontal ? toNativeHorizontalOffset(state, offset, contentSize) : 0;
1978
+ const top = isHorizontal ? 0 : offset;
1979
+ scroller.scrollTo({ animated: isAnimated, x: left, y: top });
1980
+ if (isAnimated) {
1981
+ const targetOffset = Math.max(0, Math.min(offset, (_b = (_a3 = scroller.getMaxScrollOffset) == null ? void 0 : _a3.call(scroller)) != null ? _b : offset));
1982
+ if (Math.abs(((_d = (_c = scroller.getCurrentScrollOffset) == null ? void 0 : _c.call(scroller)) != null ? _d : Number.NaN) - targetOffset) <= SCROLL_END_TARGET_EPSILON) {
1983
+ const targetToken = state.scrollingTo;
1984
+ state.scheduledWork.frame(() => {
1985
+ if (targetToken === state.scrollingTo) finishScrollTo(ctx);
1986
+ }, "platformScrollCompletion");
1987
+ return;
1988
+ }
1989
+ const target = (_f = (_e = scroller.getScrollEventTarget) == null ? void 0 : _e.call(scroller)) != null ? _f : null;
1990
+ listenForScrollEnd(ctx, {
1991
+ readOffset: () => scroller.getCurrentScrollOffset(),
1992
+ target,
1993
+ targetOffset
1994
+ });
1995
+ } else {
1996
+ state.scroll = offset;
1997
+ const targetToken = state.scrollingTo;
1998
+ state.scheduledWork.timeout(
1999
+ () => {
2000
+ if (targetToken === state.scrollingTo) {
2001
+ finishScrollTo(ctx);
2002
+ }
2003
+ },
2004
+ 100,
2005
+ "platformScrollCompletion"
2006
+ );
2007
+ }
2008
+ }
2009
+ function listenForScrollEnd(ctx, params) {
2010
+ const { readOffset, target, targetOffset } = params;
2011
+ if (!target) {
2012
+ finishScrollTo(ctx);
2013
+ return;
2014
+ }
2015
+ const supportsScrollEnd = "onscrollend" in target;
2016
+ let idleTimeout;
2017
+ let settled = false;
2018
+ const { scheduledWork, scrollingTo: targetToken } = ctx.state;
2019
+ const maxTimeout = setTimeout(() => finish("max"), SCROLL_END_MAX_MS);
2020
+ const cleanup = () => {
2021
+ target.removeEventListener("scroll", onScroll2);
2022
+ if (supportsScrollEnd) {
2023
+ target.removeEventListener("scrollend", onScrollEnd);
2024
+ }
2025
+ if (idleTimeout !== void 0) {
2026
+ clearTimeout(idleTimeout);
2027
+ }
2028
+ clearTimeout(maxTimeout);
2029
+ };
2030
+ const cancel = () => {
2031
+ if (!settled) {
2032
+ settled = true;
2033
+ cleanup();
2034
+ }
2035
+ };
2036
+ const finish = (reason) => {
2037
+ if (settled) return;
2038
+ if (targetToken !== ctx.state.scrollingTo) {
2039
+ scheduledWork.cancel("platformScrollCompletion");
2040
+ return;
2041
+ }
2042
+ const currentOffset = readOffset();
2043
+ const isNearTarget = Math.abs(currentOffset - targetOffset) <= SCROLL_END_TARGET_EPSILON;
2044
+ if (reason === "scrollend" && !isNearTarget) {
2045
+ return;
2046
+ }
2047
+ scheduledWork.cancel("platformScrollCompletion");
2048
+ finishScrollTo(ctx);
2049
+ };
2050
+ const onScroll2 = () => {
2051
+ if (idleTimeout !== void 0) {
2052
+ clearTimeout(idleTimeout);
2053
+ }
2054
+ idleTimeout = setTimeout(() => finish("idle"), SCROLL_END_IDLE_MS);
2055
+ };
2056
+ const onScrollEnd = () => finish("scrollend");
2057
+ target.addEventListener("scroll", onScroll2);
2058
+ if (supportsScrollEnd) {
2059
+ target.addEventListener("scrollend", onScrollEnd);
2060
+ } else {
2061
+ idleTimeout = setTimeout(() => finish("idle"), SMOOTH_SCROLL_DURATION_MS);
2062
+ }
2063
+ scheduledWork.register("platformScrollCompletion", cancel);
2064
+ }
2065
+
2007
2066
  // src/core/scrollTo.ts
2008
2067
  function getAverageSizeSnapshot(state) {
2009
2068
  if (Object.keys(state.averageSizes).length === 0) {
@@ -3199,6 +3258,11 @@ function handleInitialScrollDataChange(ctx, options) {
3199
3258
  const state = ctx.state;
3200
3259
  const previousInitialScrollDataLength = (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.previousDataLength) != null ? _b : 0;
3201
3260
  const shouldUseLatestInitialScroll = dataLength > 0 && (!state.hasHadNonEmptyData || didStartFreshData);
3261
+ if (didStartFreshData) {
3262
+ cancelImperativeScroll(state);
3263
+ state.maintainingScrollAtEnd = void 0;
3264
+ state.pendingMaintainScrollAtEnd = false;
3265
+ }
3202
3266
  if (dataLength > 0) {
3203
3267
  state.hasHadNonEmptyData = true;
3204
3268
  }
@@ -4743,6 +4807,7 @@ function maybeUpdateAnchoredEndSpace(ctx) {
4743
4807
  const nextAnchorIndex = anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex;
4744
4808
  let nextAnchorKey;
4745
4809
  let isReady = true;
4810
+ let canUpdateSize = true;
4746
4811
  let nextSize = 0;
4747
4812
  if (anchoredEndSpace) {
4748
4813
  const { anchorIndex, anchorMaxSize, anchorOffset = 0 } = anchoredEndSpace;
@@ -4764,25 +4829,30 @@ function maybeUpdateAnchoredEndSpace(ctx) {
4764
4829
  contentBelowAnchor = Math.max(0, contentBelowAnchor - ctx.scrollAxisGap);
4765
4830
  contentBelowAnchor += (ctx.values.get("footerSize") || 0) + getStylePaddingEnd(state.props);
4766
4831
  isReady = !hasUnknownTailSize;
4767
- nextSize = hasUnknownTailSize ? previousSize || 0 : Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
4832
+ const knownSizeBound = Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
4833
+ nextSize = hasUnknownTailSize ? Math.min(previousSize || 0, knownSizeBound) : knownSizeBound;
4768
4834
  } else if (anchorIndex >= 0) {
4769
4835
  isReady = false;
4836
+ canUpdateSize = false;
4770
4837
  }
4771
4838
  }
4772
4839
  const didSizeChange = previousSize !== nextSize && (previousSize !== void 0 || anchoredEndSpace !== void 0);
4773
4840
  const didEffectiveSizeChange = (previousSize || 0) !== nextSize;
4841
+ const canApplySizeChange = canUpdateSize && (isReady || previousSize !== void 0);
4774
4842
  const didReadyAnchorChange = previousReadyAnchorIndex !== nextAnchorIndex || previousReadyAnchorKey !== nextAnchorKey;
4775
- if (isReady && (didSizeChange || didReadyAnchorChange)) {
4843
+ const didBecomeReady = isReady && state.anchoredEndSpacePendingReady;
4844
+ state.anchoredEndSpacePendingReady = !isReady;
4845
+ if (canApplySizeChange && didSizeChange) {
4846
+ set$(ctx, "anchoredEndSpaceSize", nextSize);
4847
+ (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
4848
+ }
4849
+ if (canApplySizeChange && didEffectiveSizeChange) {
4850
+ updateContentMetricsState(ctx);
4851
+ updateScroll(ctx, state.scroll, true, { markHasScrolled: false });
4852
+ }
4853
+ if (isReady && (didSizeChange || didReadyAnchorChange || didBecomeReady)) {
4776
4854
  state.anchoredEndSpaceReadyAnchorIndex = nextAnchorIndex;
4777
4855
  state.anchoredEndSpaceReadyAnchorKey = nextAnchorKey;
4778
- if (didSizeChange) {
4779
- set$(ctx, "anchoredEndSpaceSize", nextSize);
4780
- (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
4781
- }
4782
- if (didEffectiveSizeChange) {
4783
- updateContentMetricsState(ctx);
4784
- updateScroll(ctx, state.scroll, true, { markHasScrolled: false });
4785
- }
4786
4856
  (_b = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onReady) == null ? void 0 : _b.call(anchoredEndSpace, { anchorIndex: nextAnchorIndex, anchorKey: nextAnchorKey, size: nextSize });
4787
4857
  }
4788
4858
  return nextSize;
@@ -5782,7 +5852,9 @@ function sortDOMElements(container, indexByElement) {
5782
5852
  break;
5783
5853
  }
5784
5854
  }
5785
- if (nextStableElement) {
5855
+ if ("moveBefore" in container && typeof container.moveBefore === "function" && container.isConnected) {
5856
+ container.moveBefore(element, nextStableElement);
5857
+ } else if (nextStableElement) {
5786
5858
  container.insertBefore(element, nextStableElement);
5787
5859
  } else {
5788
5860
  container.appendChild(element);
@@ -5829,13 +5901,18 @@ function findLIS(arr) {
5829
5901
  // src/hooks/useDOMOrder.ts
5830
5902
  function useDOMOrder(ref) {
5831
5903
  const ctx = useStateContext();
5832
- const debounceRef = useRef(void 0);
5833
5904
  useEffect(() => {
5905
+ let timeoutId;
5906
+ let lastUpdateTime = 0;
5907
+ let delay = 500;
5834
5908
  const unsubscribe = listen$(ctx, "lastPositionUpdate", () => {
5835
- if (debounceRef.current !== void 0) {
5836
- clearTimeout(debounceRef.current);
5837
- }
5838
- debounceRef.current = setTimeout(() => {
5909
+ const container = ref.current;
5910
+ const now = Date.now();
5911
+ delay = container && "moveBefore" in container && typeof container.moveBefore === "function" && container.isConnected && (now - lastUpdateTime >= 1e3 || timeoutId !== void 0 && delay === 0) && now - ctx.state.scrollTime >= 500 ? 0 : 500;
5912
+ lastUpdateTime = now;
5913
+ clearTimeout(timeoutId);
5914
+ timeoutId = setTimeout(() => {
5915
+ timeoutId = void 0;
5839
5916
  const parent = ref.current;
5840
5917
  if (parent) {
5841
5918
  const indexByElement = /* @__PURE__ */ new Map();
@@ -5848,14 +5925,11 @@ function useDOMOrder(ref) {
5848
5925
  }
5849
5926
  sortDOMElements(parent, indexByElement);
5850
5927
  }
5851
- debounceRef.current = void 0;
5852
- }, 500);
5928
+ }, delay);
5853
5929
  });
5854
5930
  return () => {
5855
5931
  unsubscribe();
5856
- if (debounceRef.current !== void 0) {
5857
- clearTimeout(debounceRef.current);
5858
- }
5932
+ clearTimeout(timeoutId);
5859
5933
  };
5860
5934
  }, [ctx]);
5861
5935
  }
@@ -6225,6 +6299,7 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
6225
6299
  return (_a4 = scrollRef.current) == null ? void 0 : _a4.getBoundingClientRect();
6226
6300
  },
6227
6301
  getCurrentScrollOffset,
6302
+ getMaxScrollOffset,
6228
6303
  getScrollableNode: () => resolveScrollableNode(scrollRef.current, isWindowScroll),
6229
6304
  getScrollEventTarget: () => getScrollTarget(),
6230
6305
  getScrollResponder: () => resolveScrollableNode(scrollRef.current, isWindowScroll),
@@ -6330,6 +6405,22 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
6330
6405
  scrollEventCoalescer.cancel();
6331
6406
  };
6332
6407
  }, [emitScrollEnd, getScrollTarget, handleScroll, scrollEventCoalescer]);
6408
+ useEffect(() => {
6409
+ const target = getScrollTarget();
6410
+ if (!target) return;
6411
+ const onWheel = (nativeEvent) => {
6412
+ const event = nativeEvent;
6413
+ const delta = horizontal ? event.deltaX || (event.shiftKey ? event.deltaY : 0) : event.deltaY;
6414
+ if (delta < 0 && !event.ctrlKey && !event.defaultPrevented && (ctx.state.maintainingScrollAtEnd || ctx.state.pendingMaintainScrollAtEnd)) {
6415
+ const offset = getCurrentScrollOffset();
6416
+ if (interruptMaintainScrollAtEnd(ctx)) {
6417
+ scrollToLocalOffset(offset, false);
6418
+ }
6419
+ }
6420
+ };
6421
+ target.addEventListener("wheel", onWheel, { passive: true });
6422
+ return () => target.removeEventListener("wheel", onWheel);
6423
+ }, [ctx, getCurrentScrollOffset, getScrollTarget, horizontal, scrollToLocalOffset]);
6333
6424
  useEffect(() => {
6334
6425
  const doScroll = () => {
6335
6426
  if (contentOffset) {
@@ -6539,8 +6630,11 @@ function ScrollAdjust() {
6539
6630
  window.getComputedStyle(contentNode)[axis.paddingEndProp]
6540
6631
  );
6541
6632
  const temporaryPaddingEnd = `${(currentPaddingEnd || 0) + pad}px`;
6542
- temporaryPaddingRef.current = { baseline: baselinePaddingEnd, value: temporaryPaddingEnd };
6543
6633
  contentNode.style[axis.paddingEndProp] = temporaryPaddingEnd;
6634
+ temporaryPaddingRef.current = {
6635
+ baseline: baselinePaddingEnd,
6636
+ value: contentNode.style[axis.paddingEndProp]
6637
+ };
6544
6638
  void contentNode.offsetHeight;
6545
6639
  scrollBy();
6546
6640
  if (resetPaddingRafRef.current !== void 0) {
@@ -7278,6 +7372,9 @@ function scrollToEnd(ctx, options) {
7278
7372
  viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
7279
7373
  viewPosition: 1
7280
7374
  });
7375
+ if (state.scrollingTo) {
7376
+ state.scrollingTo.isScrollToEnd = true;
7377
+ }
7281
7378
  return true;
7282
7379
  }
7283
7380
 
@@ -7995,6 +8092,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7995
8092
  endReachedSnapshot: void 0,
7996
8093
  firstFullyOnScreenIndex: -1,
7997
8094
  freshDataTransitionEpoch: 0,
8095
+ handledDataChangeEpoch: 0,
8096
+ handledFreshDataTransitionEpoch: 0,
7998
8097
  hasHadNonEmptyData: dataProp.length > 0,
7999
8098
  idCache: [],
8000
8099
  idsInView: [],
@@ -8079,6 +8178,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8079
8178
  if (shouldResetFreshDataLayout) {
8080
8179
  state.freshDataTransitionEpoch += 1;
8081
8180
  }
8181
+ const dataChangeEpoch = state.dataChangeEpoch;
8182
+ const freshDataTransitionEpoch = state.freshDataTransitionEpoch;
8082
8183
  const throttledOnScroll = useThrottledOnScroll(onScrollProp != null ? onScrollProp : noopOnScroll, scrollEventThrottle != null ? scrollEventThrottle : 0);
8083
8184
  const throttleScrollFn = scrollEventThrottle && onScrollProp ? throttledOnScroll : onScrollProp;
8084
8185
  const didAnchoredEndSpaceAnchorIndexChange = !isFirstLocal && !didDataChangeLocal && ((_q = state.props.anchoredEndSpace) == null ? void 0 : _q.anchorIndex) !== (anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex);
@@ -8201,7 +8302,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8201
8302
  useDevChecks(props);
8202
8303
  }
8203
8304
  useLayoutEffect(() => {
8204
- if (shouldResetFreshDataLayout) {
8305
+ const didDataChange = state.handledDataChangeEpoch !== dataChangeEpoch;
8306
+ const didStartFreshData = state.handledFreshDataTransitionEpoch !== freshDataTransitionEpoch;
8307
+ state.handledDataChangeEpoch = dataChangeEpoch;
8308
+ state.handledFreshDataTransitionEpoch = freshDataTransitionEpoch;
8309
+ if (didStartFreshData) {
8205
8310
  resetInitialRenderState(ctx, {
8206
8311
  resetInitialScroll: !!initialScrollProp,
8207
8312
  resetLayout: true
@@ -8209,8 +8314,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8209
8314
  }
8210
8315
  handleInitialScrollDataChange(ctx, {
8211
8316
  dataLength: dataProp.length,
8212
- didDataChange: didDataChangeLocal,
8213
- didStartFreshData: shouldResetFreshDataLayout,
8317
+ didDataChange,
8318
+ didStartFreshData,
8214
8319
  initialScrollAtEnd,
8215
8320
  latestInitialScroll: initialScrollProp,
8216
8321
  latestInitialScrollSessionKind: initialScrollUsesOffsetOnly ? "offset" : "bootstrap",
@@ -8220,8 +8325,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8220
8325
  }, [
8221
8326
  dataProp.length,
8222
8327
  dataKey,
8223
- didDataChangeLocal,
8224
- shouldResetFreshDataLayout,
8328
+ dataChangeEpoch,
8329
+ freshDataTransitionEpoch,
8225
8330
  initialScrollAtEnd,
8226
8331
  stylePaddingEndState,
8227
8332
  usesBootstrapInitialScroll
@@ -8401,13 +8506,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8401
8506
  onScroll: (event) => onScroll(ctx, event),
8402
8507
  onScrollBeginDrag: (event) => {
8403
8508
  var _a4, _b2;
8404
- const maintainingScrollAtEnd = state.maintainingScrollAtEnd;
8405
- if (maintainingScrollAtEnd === "animated" || maintainingScrollAtEnd === "instant") {
8406
- cancelImperativeScroll(state);
8407
- }
8408
- if (maintainingScrollAtEnd) {
8409
- finishMaintainScrollAtEnd(ctx);
8410
- }
8509
+ interruptMaintainScrollAtEnd(ctx);
8411
8510
  prepareReachedEdgeForNextUserScroll(ctx);
8412
8511
  (_b2 = (_a4 = state.props).onScrollBeginDrag) == null ? void 0 : _b2.call(_a4, event);
8413
8512
  },