@legendapp/list 3.3.5 → 3.3.6

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.mjs CHANGED
@@ -55,10 +55,6 @@ function getStylePaddingEnd(props) {
55
55
  }
56
56
  return (isHorizontalRTLProps(props) ? props.stylePaddingLeft : props.stylePaddingRight) || 0;
57
57
  }
58
- function getLogicalHorizontalMaxOffset(state, contentWidth) {
59
- var _a3;
60
- return (_a3 = getHorizontalMaxOffset(state, contentWidth)) != null ? _a3 : 0;
61
- }
62
58
  function getHorizontalInsetEnd(state, inset) {
63
59
  if (!inset) {
64
60
  return 0;
@@ -689,6 +685,12 @@ function checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
689
685
  } = state;
690
686
  const contentSize = getContentSize(ctx);
691
687
  resetSharedEdgeGateIfOutsideHysteresis(ctx);
688
+ if (state.props.data.length === 0 && scrollLength > 0 && contentSize <= scrollLength) {
689
+ set$(ctx, "isAtEnd", true);
690
+ set$(ctx, "isNearEnd", true);
691
+ set$(ctx, "isWithinMaintainScrollAtEndThreshold", true);
692
+ return;
693
+ }
692
694
  if (contentSize > 0 && queuedInitialLayout) {
693
695
  const insetEnd = getContentInsetEnd(ctx);
694
696
  const distanceFromEnd = contentSize - scroll - scrollLength - insetEnd;
@@ -698,7 +700,7 @@ function checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
698
700
  set$(
699
701
  ctx,
700
702
  "isWithinMaintainScrollAtEndThreshold",
701
- isContentLess || distanceFromEnd <= maintainScrollAtEndThreshold * scrollLength
703
+ isContentLess || distanceFromEnd <= maintainScrollAtEndThreshold * scrollLength || state.pendingMaintainScrollAtEnd || maintainingScrollAtEnd === "animated" || maintainingScrollAtEnd === "instant"
702
704
  );
703
705
  const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || maintainingScrollAtEnd;
704
706
  if (!shouldSkipThresholdChecks) {
@@ -1043,10 +1045,22 @@ function getAlignItemsAtEndPadding(ctx) {
1043
1045
  return shouldPad ? Math.max(0, state.scrollLength - getRawContentLength(ctx) - getContentInsetEnd(ctx)) : 0;
1044
1046
  }
1045
1047
  function updateContentMetricsState(ctx) {
1048
+ var _a3;
1049
+ const { state } = ctx;
1046
1050
  const previousPadding = peek$(ctx, "alignItemsAtEndPadding") || 0;
1047
1051
  const nextPadding = getAlignItemsAtEndPadding(ctx);
1048
1052
  if (previousPadding !== nextPadding) {
1049
- set$(ctx, "alignItemsAtEndPadding", nextPadding);
1053
+ const isAnimatedMaintainActive = state.maintainingScrollAtEnd === "pending-animated" || state.maintainingScrollAtEnd === "animated";
1054
+ const isMaintainExpected = state.didContainersLayout && ((_a3 = state.props.maintainScrollAtEnd) == null ? void 0 : _a3.animated) && (isAnimatedMaintainActive || previousPadding > nextPadding && peek$(ctx, "isWithinMaintainScrollAtEndThreshold"));
1055
+ if (isMaintainExpected && !isAnimatedMaintainActive && !state.pendingMaintainScrollAtEnd) {
1056
+ state.scheduledWork.microtask(() => {
1057
+ if (!state.maintainingScrollAtEnd && !state.pendingMaintainScrollAtEnd) {
1058
+ set$(ctx, "alignItemsAtEndPadding", getAlignItemsAtEndPadding(ctx));
1059
+ }
1060
+ }, "alignItemsAtEndPaddingFallback");
1061
+ } else if (!isMaintainExpected) {
1062
+ set$(ctx, "alignItemsAtEndPadding", nextPadding);
1063
+ }
1050
1064
  }
1051
1065
  }
1052
1066
 
@@ -1404,22 +1418,110 @@ function listenForScrollEnd(ctx, params) {
1404
1418
  scheduledWork.register("platformScrollCompletion", cancel);
1405
1419
  }
1406
1420
 
1421
+ // src/core/scrollRequestTracker.ts
1422
+ function getScrollRequestTracker(ctx) {
1423
+ if (!ctx.scrollRequestTracker) {
1424
+ let currentToken = 0;
1425
+ const start = (resolve) => {
1426
+ const state = ctx.state;
1427
+ state.scheduledWork.cancel("imperativeScrollReady");
1428
+ const token = ++currentToken;
1429
+ settlePendingImperativeScroll(state);
1430
+ state.pendingScrollResolve = resolve;
1431
+ return token;
1432
+ };
1433
+ const runNow = (token, resolve, run) => {
1434
+ const state = ctx.state;
1435
+ if (token !== currentToken) {
1436
+ return;
1437
+ }
1438
+ const didStartScroll = run();
1439
+ if (!didStartScroll || !state.scrollingTo) {
1440
+ if (state.pendingScrollResolve === resolve) {
1441
+ state.pendingScrollResolve = void 0;
1442
+ }
1443
+ resolve();
1444
+ }
1445
+ };
1446
+ ctx.scrollRequestTracker = {
1447
+ isCurrent: (token) => token === currentToken,
1448
+ runNow,
1449
+ runNowIfIdle: (run) => {
1450
+ const state = ctx.state;
1451
+ if (state.pendingScrollResolve || state.scrollingTo) {
1452
+ return Promise.resolve();
1453
+ }
1454
+ return new Promise((resolve) => {
1455
+ const token = start(resolve);
1456
+ runNow(token, resolve, run);
1457
+ });
1458
+ },
1459
+ start
1460
+ };
1461
+ }
1462
+ return ctx.scrollRequestTracker;
1463
+ }
1464
+
1465
+ // src/utils/requestAdjust.ts
1466
+ function requestAdjust(ctx, positionDiff, source) {
1467
+ const state = ctx.state;
1468
+ if (Math.abs(positionDiff) > 0.1) {
1469
+ const doit = () => {
1470
+ {
1471
+ state.scrollAdjustHandler.requestAdjust(positionDiff);
1472
+ if (state.adjustingFromInitialMount) {
1473
+ state.adjustingFromInitialMount--;
1474
+ }
1475
+ }
1476
+ };
1477
+ state.scroll += positionDiff;
1478
+ state.scrollForNextCalculateItemsInView = void 0;
1479
+ const readyToRender = peek$(ctx, "readyToRender");
1480
+ if (readyToRender) {
1481
+ doit();
1482
+ } else {
1483
+ state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
1484
+ requestAnimationFrame(doit);
1485
+ }
1486
+ }
1487
+ }
1488
+
1407
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
+ }
1408
1505
  function doMaintainScrollAtEnd(ctx) {
1409
1506
  const state = ctx.state;
1410
1507
  const {
1411
1508
  didContainersLayout,
1509
+ didFinishInitialScroll,
1412
1510
  pendingNativeMVCPAdjust,
1413
- refScroller,
1414
1511
  props: { maintainScrollAtEnd }
1415
1512
  } = state;
1416
1513
  const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
1417
- const shouldMaintainScrollAtEnd = !!(isWithinMaintainScrollAtEndThreshold && maintainScrollAtEnd && didContainersLayout);
1514
+ const isReplayingPendingMaintain = !!state.pendingMaintainScrollAtEnd;
1515
+ const shouldMaintainScrollAtEnd = !!((isWithinMaintainScrollAtEndThreshold || isReplayingPendingMaintain) && maintainScrollAtEnd && didFinishInitialScroll);
1516
+ if (shouldMaintainScrollAtEnd && !didContainersLayout) {
1517
+ state.pendingMaintainScrollAtEnd = true;
1518
+ return false;
1519
+ }
1418
1520
  if (pendingNativeMVCPAdjust) {
1419
1521
  state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
1420
1522
  return false;
1421
1523
  }
1422
- if (shouldMaintainScrollAtEnd) {
1524
+ if (shouldMaintainScrollAtEnd && didContainersLayout) {
1423
1525
  state.pendingMaintainScrollAtEnd = false;
1424
1526
  const contentSize = getContentSize(ctx);
1425
1527
  if (contentSize < state.scrollLength) {
@@ -1431,39 +1533,29 @@ function doMaintainScrollAtEnd(ctx) {
1431
1533
  const scrollAtRequest = state.scroll;
1432
1534
  state.maintainingScrollAtEnd = pendingState;
1433
1535
  requestAnimationFrame(() => {
1536
+ if (state.maintainingScrollAtEnd !== pendingState) {
1537
+ return;
1538
+ }
1434
1539
  const isStillWithinThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
1435
1540
  const didScrollSinceRequest = state.scroll !== scrollAtRequest;
1436
- if (isStillWithinThreshold || !didScrollSinceRequest) {
1541
+ if (isReplayingPendingMaintain || isStillWithinThreshold || !didScrollSinceRequest) {
1437
1542
  state.maintainingScrollAtEnd = activeState;
1438
- const scroller = refScroller.current;
1439
- if (state.props.horizontal && isHorizontalRTL(state)) {
1440
- const currentContentSize = getContentSize(ctx);
1441
- const logicalEndOffset = getLogicalHorizontalMaxOffset(state, currentContentSize);
1442
- const nativeOffset = toNativeHorizontalOffset(state, logicalEndOffset, currentContentSize);
1443
- scroller == null ? void 0 : scroller.scrollTo({
1444
- animated: maintainScrollAtEnd.animated,
1445
- x: nativeOffset,
1446
- y: 0
1447
- });
1448
- } else {
1449
- scroller == null ? void 0 : scroller.scrollToEnd({
1450
- animated: maintainScrollAtEnd.animated
1451
- });
1452
- }
1453
- setTimeout(
1454
- () => {
1455
- if (state.maintainingScrollAtEnd === activeState) {
1456
- state.maintainingScrollAtEnd = void 0;
1457
- if (state.pendingMaintainScrollAtEnd) {
1458
- doMaintainScrollAtEnd(ctx);
1459
- }
1460
- }
1461
- },
1462
- maintainScrollAtEnd.animated ? 500 : 0
1543
+ const scrollPromise = getScrollRequestTracker(ctx).runNowIfIdle(
1544
+ () => ctx.scrollToEnd({ animated: maintainScrollAtEnd.animated })
1463
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
+ });
1464
1557
  } else if (state.maintainingScrollAtEnd === pendingState) {
1465
- state.maintainingScrollAtEnd = void 0;
1466
- state.pendingMaintainScrollAtEnd = false;
1558
+ finishMaintainScrollAtEnd(ctx);
1467
1559
  }
1468
1560
  });
1469
1561
  } else {
@@ -1471,34 +1563,10 @@ function doMaintainScrollAtEnd(ctx) {
1471
1563
  }
1472
1564
  return true;
1473
1565
  }
1474
- state.pendingMaintainScrollAtEnd = false;
1566
+ finishMaintainScrollAtEnd(ctx);
1475
1567
  return false;
1476
1568
  }
1477
1569
 
1478
- // src/utils/requestAdjust.ts
1479
- function requestAdjust(ctx, positionDiff, dataChanged) {
1480
- const state = ctx.state;
1481
- if (Math.abs(positionDiff) > 0.1) {
1482
- const doit = () => {
1483
- {
1484
- state.scrollAdjustHandler.requestAdjust(positionDiff);
1485
- if (state.adjustingFromInitialMount) {
1486
- state.adjustingFromInitialMount--;
1487
- }
1488
- }
1489
- };
1490
- state.scroll += positionDiff;
1491
- state.scrollForNextCalculateItemsInView = void 0;
1492
- const readyToRender = peek$(ctx, "readyToRender");
1493
- if (readyToRender) {
1494
- doit();
1495
- } else {
1496
- state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
1497
- requestAnimationFrame(doit);
1498
- }
1499
- }
1500
- }
1501
-
1502
1570
  // src/core/mvcp.ts
1503
1571
  var MVCP_POSITION_EPSILON = 0.1;
1504
1572
  var MVCP_ANCHOR_LOCK_TTL_MS = 300;
@@ -1635,7 +1703,7 @@ function resolvePendingNativeMVCPAdjust(ctx, newScroll) {
1635
1703
  }
1636
1704
  return false;
1637
1705
  }
1638
- function prepareMVCP(ctx, dataChanged) {
1706
+ function prepareMVCP(ctx, dataChanged, adjustmentSource) {
1639
1707
  const state = ctx.state;
1640
1708
  const { idsInView, positions, props } = state;
1641
1709
  const {
@@ -1887,6 +1955,10 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
1887
1955
  state.scrollTime = currentTime;
1888
1956
  const scrollDelta = Math.abs(newScroll - prevScroll);
1889
1957
  const isUserScrollEvent = !!(options == null ? void 0 : options.fromNativeScrollEvent) && scrollDelta > 0.1 && !adjustChanged && scrollingTo === void 0 && !state.pendingNativeMVCPAdjust;
1958
+ const didCancelMaintainScrollAtEnd = isUserScrollEvent && newScroll < prevScroll && !!(state.maintainingScrollAtEnd || state.pendingMaintainScrollAtEnd);
1959
+ if (didCancelMaintainScrollAtEnd) {
1960
+ finishMaintainScrollAtEnd(ctx);
1961
+ }
1890
1962
  const allowedEdge = isUserScrollEvent ? beginReachedEdgeUserScroll(ctx, newScroll - prevScroll) : void 0;
1891
1963
  const didResolvePendingNativeMVCPAdjust = resolvePendingNativeMVCPAdjust(ctx, newScroll);
1892
1964
  const scrollLength = state.scrollLength;
@@ -1895,7 +1967,7 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
1895
1967
  updateAdaptiveRender(ctx, scrollVelocity, { forceLight: isLargeUserScrollJump });
1896
1968
  const lastCalculated = state.scrollLastCalculate;
1897
1969
  const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
1898
- const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || allowedEdge !== void 0 || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
1970
+ const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || didCancelMaintainScrollAtEnd || allowedEdge !== void 0 || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
1899
1971
  if (shouldUpdate) {
1900
1972
  state.scrollLastCalculate = state.scroll;
1901
1973
  state.ignoreScrollFromMVCPIgnored = false;
@@ -2724,7 +2796,7 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2724
2796
  }
2725
2797
  }
2726
2798
  function handleBootstrapInitialScrollLayoutChange(ctx) {
2727
- var _a3, _b, _c;
2799
+ var _a3, _b;
2728
2800
  const state = ctx.state;
2729
2801
  const initialScroll = state.initialScroll;
2730
2802
  const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
@@ -2738,19 +2810,15 @@ function handleBootstrapInitialScrollLayoutChange(ctx) {
2738
2810
  if (state.didFinishInitialScroll) {
2739
2811
  schedulePreservedEndAnchorCorrection(ctx);
2740
2812
  } else if (scrollingTo) {
2741
- const existingWatchdog = initialScrollWatchdog.get(state);
2742
- scrollingTo.offset = resolvedOffset;
2743
- scrollingTo.targetOffset = resolvedOffset;
2744
2813
  state.initialScroll = {
2745
2814
  ...initialScroll,
2746
2815
  contentOffset: resolvedOffset
2747
2816
  };
2748
- state.hasScrolled = false;
2749
- initialScrollWatchdog.set(state, {
2750
- startScroll: (_c = existingWatchdog == null ? void 0 : existingWatchdog.startScroll) != null ? _c : state.scroll,
2751
- targetOffset: resolvedOffset
2817
+ dispatchInitialScroll(ctx, {
2818
+ forceScroll: true,
2819
+ resolvedOffset,
2820
+ target: state.initialScroll
2752
2821
  });
2753
- requestAdjust(ctx, offsetDiff);
2754
2822
  }
2755
2823
  }
2756
2824
  } else {
@@ -3977,6 +4045,9 @@ function setDidLayout(ctx) {
3977
4045
  state.queuedInitialLayout = true;
3978
4046
  checkAtBottom(ctx);
3979
4047
  setInitialRenderState(ctx, { didLayout: true });
4048
+ if (state.pendingMaintainScrollAtEnd) {
4049
+ doMaintainScrollAtEnd(ctx);
4050
+ }
3980
4051
  }
3981
4052
 
3982
4053
  // src/core/calculateItemsInView.ts
@@ -4213,7 +4284,7 @@ function calculateItemsInView(ctx, params = {}) {
4213
4284
  const stickyHeaderIndicesArr = state.props.stickyHeaderIndicesArr || [];
4214
4285
  const stickyHeaderIndicesSet = state.props.stickyHeaderIndicesSet || /* @__PURE__ */ new Set();
4215
4286
  const drawDistance = getEffectiveDrawDistance(ctx, params.drawDistanceMode);
4216
- const { dataChanged, doMVCP, forceFullItemPositions } = params;
4287
+ const { dataChanged, doMVCP, forceFullItemPositions, mvcpAdjustmentSource } = params;
4217
4288
  const bootstrapInitialScrollState = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" ? state.initialScrollSession.bootstrap : void 0;
4218
4289
  const suppressInitialScrollSideEffects = !!bootstrapInitialScrollState;
4219
4290
  const prevNumContainers = peek$(ctx, "numContainers");
@@ -4717,9 +4788,12 @@ function runOrScheduleMVCPRecalculate(ctx) {
4717
4788
  } else {
4718
4789
  if (!state.mvcpAnchorLock) {
4719
4790
  state.scheduledWork.cancel("mvcpRecalculate");
4720
- calculateItemsInView(ctx, { doMVCP: true });
4791
+ calculateItemsInView(ctx, { doMVCP: true, mvcpAdjustmentSource: "item-size" });
4721
4792
  } else if (!state.scheduledWork.has("mvcpRecalculate")) {
4722
- state.scheduledWork.frame(() => calculateItemsInView(ctx, { doMVCP: true }), "mvcpRecalculate");
4793
+ state.scheduledWork.frame(
4794
+ () => calculateItemsInView(ctx, { doMVCP: true, mvcpAdjustmentSource: "item-size" }),
4795
+ "mvcpRecalculate"
4796
+ );
4723
4797
  }
4724
4798
  }
4725
4799
  }
@@ -5086,6 +5160,30 @@ function useContainerItemSignals(containerContext) {
5086
5160
  itemKey
5087
5161
  };
5088
5162
  }
5163
+ function scheduleViewabilityConfigWarning(ctx, hookName, configId) {
5164
+ let cancelled = false;
5165
+ Promise.resolve().then(() => {
5166
+ var _a3;
5167
+ const viewabilityConfigCallbackPairs = (_a3 = ctx.state) == null ? void 0 : _a3.viewabilityConfigCallbackPairs;
5168
+ if (!cancelled && ctx.state) {
5169
+ if (!(viewabilityConfigCallbackPairs == null ? void 0 : viewabilityConfigCallbackPairs.length)) {
5170
+ warnDevOnce(
5171
+ `${hookName}-missing-viewability-config`,
5172
+ `${hookName} requires viewability to be configured on the owning LegendList. Add viewabilityConfig with a visibility threshold.`
5173
+ );
5174
+ } else if (hookName === "useViewability" && !viewabilityConfigCallbackPairs.some((pair) => pair.viewabilityConfig.id === (configId != null ? configId : ""))) {
5175
+ const configDescription = configId ? ` for configId "${configId}"` : "";
5176
+ warnDevOnce(
5177
+ `${hookName}-missing-viewability-config-${configId != null ? configId : "default"}`,
5178
+ `${hookName}${configDescription} requires a matching viewabilityConfig.id on the owning LegendList. Add an id to viewabilityConfig and pass the same id to useViewability.`
5179
+ );
5180
+ }
5181
+ }
5182
+ });
5183
+ return () => {
5184
+ cancelled = true;
5185
+ };
5186
+ }
5089
5187
  function useAdaptiveRender() {
5090
5188
  const [mode] = useArr$(["adaptiveRender"]);
5091
5189
  return mode;
@@ -5125,7 +5223,9 @@ function useViewability(callback, configId) {
5125
5223
  const { containerId } = containerContext;
5126
5224
  const key = containerId + (configId != null ? configId : "");
5127
5225
  ctx.mapViewabilityCallbacks.set(key, callback);
5226
+ const cancelConfigWarning = IS_DEV ? scheduleViewabilityConfigWarning(ctx, "useViewability", configId) : void 0;
5128
5227
  return () => {
5228
+ cancelConfigWarning == null ? void 0 : cancelConfigWarning();
5129
5229
  ctx.mapViewabilityCallbacks.delete(key);
5130
5230
  };
5131
5231
  }, [ctx, callback, configId, containerContext]);
@@ -5149,7 +5249,9 @@ function useViewabilityAmount(callback) {
5149
5249
  }
5150
5250
  const { containerId } = containerContext;
5151
5251
  ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
5252
+ const cancelConfigWarning = IS_DEV ? scheduleViewabilityConfigWarning(ctx, "useViewabilityAmount") : void 0;
5152
5253
  return () => {
5254
+ cancelConfigWarning == null ? void 0 : cancelConfigWarning();
5153
5255
  ctx.mapViewabilityAmountCallbacks.delete(containerId);
5154
5256
  };
5155
5257
  }, [ctx, callback, containerContext]);
@@ -7022,11 +7124,29 @@ var ScheduledWork = class {
7022
7124
  constructor() {
7023
7125
  this.work = /* @__PURE__ */ new Map();
7024
7126
  }
7127
+ microtask(callback, key) {
7128
+ const pendingWork = this.work.get(key);
7129
+ if (pendingWork == null ? void 0 : pendingWork.callback) {
7130
+ pendingWork.callback = callback;
7131
+ } else {
7132
+ this.cancel(key);
7133
+ const work = { callback, cancel: () => {
7134
+ } };
7135
+ this.work.set(key, work);
7136
+ queueMicrotask(() => {
7137
+ var _a3;
7138
+ if (this.work.get(key) === work) {
7139
+ this.work.delete(key);
7140
+ (_a3 = work.callback) == null ? void 0 : _a3.call(work);
7141
+ }
7142
+ });
7143
+ }
7144
+ }
7025
7145
  timeout(callback, delay, key) {
7026
7146
  if (key) {
7027
7147
  this.cancel(key);
7028
7148
  }
7029
- const work = [void 0, clearTimeout];
7149
+ const work = { cancel: () => clearTimeout(handle) };
7030
7150
  const handle = setTimeout(() => {
7031
7151
  const workKey = key != null ? key : handle;
7032
7152
  if (this.work.get(workKey) === work) {
@@ -7034,14 +7154,13 @@ var ScheduledWork = class {
7034
7154
  callback();
7035
7155
  }
7036
7156
  }, delay);
7037
- work[0] = handle;
7038
7157
  this.work.set(key != null ? key : handle, work);
7039
7158
  }
7040
7159
  frame(callback, key) {
7041
7160
  this.cancel(key);
7042
- const work = [void 0, cancelAnimationFrame];
7161
+ const work = { cancel: () => cancelAnimationFrame(handle) };
7043
7162
  this.work.set(key, work);
7044
- work[0] = requestAnimationFrame(() => {
7163
+ const handle = requestAnimationFrame(() => {
7045
7164
  if (this.work.get(key) === work) {
7046
7165
  this.work.delete(key);
7047
7166
  callback();
@@ -7050,22 +7169,21 @@ var ScheduledWork = class {
7050
7169
  }
7051
7170
  register(key, cancel) {
7052
7171
  this.cancel(key);
7053
- this.work.set(key, [void 0, cancel]);
7172
+ this.work.set(key, { cancel });
7054
7173
  }
7055
7174
  cancel(key) {
7056
7175
  const work = this.work.get(key);
7057
7176
  if (work) {
7058
7177
  this.work.delete(key);
7059
- const [handle, cancel] = work;
7060
- cancel(handle);
7178
+ work.cancel();
7061
7179
  }
7062
7180
  }
7063
7181
  has(key) {
7064
7182
  return this.work.has(key);
7065
7183
  }
7066
7184
  dispose() {
7067
- for (const [handle, cancel] of this.work.values()) {
7068
- cancel(handle);
7185
+ for (const work of this.work.values()) {
7186
+ work.cancel();
7069
7187
  }
7070
7188
  this.work.clear();
7071
7189
  }
@@ -7122,6 +7240,25 @@ var ScrollAdjustHandler = class {
7122
7240
  }
7123
7241
  };
7124
7242
 
7243
+ // src/core/scrollToEnd.ts
7244
+ function scrollToEnd(ctx, options) {
7245
+ const state = ctx.state;
7246
+ const data = state.props.data;
7247
+ const index = data.length - 1;
7248
+ if (index === -1) {
7249
+ return false;
7250
+ }
7251
+ const paddingBottom = state.props.stylePaddingBottom || 0;
7252
+ const footerSize = peek$(ctx, "footerSize") || 0;
7253
+ scrollToIndex(ctx, {
7254
+ ...options,
7255
+ index,
7256
+ viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
7257
+ viewPosition: 1
7258
+ });
7259
+ return true;
7260
+ }
7261
+
7125
7262
  // src/core/updateContentInsetEndAdjustment.ts
7126
7263
  function updateContentInsetEndAdjustment(ctx, previousContentInsetEndAdjustment) {
7127
7264
  const state = ctx.state;
@@ -7184,25 +7321,6 @@ function createColumnWrapperStyle(contentContainerStyle) {
7184
7321
  }
7185
7322
  }
7186
7323
 
7187
- // src/core/scrollToEnd.ts
7188
- function scrollToEnd(ctx, options) {
7189
- const state = ctx.state;
7190
- const data = state.props.data;
7191
- const index = data.length - 1;
7192
- if (index === -1) {
7193
- return false;
7194
- }
7195
- const paddingBottom = state.props.stylePaddingBottom || 0;
7196
- const footerSize = peek$(ctx, "footerSize") || 0;
7197
- scrollToIndex(ctx, {
7198
- ...options,
7199
- index,
7200
- viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
7201
- viewPosition: 1
7202
- });
7203
- return true;
7204
- }
7205
-
7206
7324
  // src/utils/createImperativeHandle.ts
7207
7325
  var DEFAULT_AVERAGE_ITEM_SIZE_TYPE = "default";
7208
7326
  function getAverageItemSizes(state) {
@@ -7225,9 +7343,9 @@ function triggerMountedContainerLayouts(ctx) {
7225
7343
  }
7226
7344
  function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
7227
7345
  const state = ctx.state;
7346
+ const scrollRequestTracker = getScrollRequestTracker(ctx);
7228
7347
  const IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS = 800;
7229
7348
  const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
7230
- let imperativeScrollToken = 0;
7231
7349
  const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.scheduledWork.has("mvcpRecalculate") || state.ignoreScrollFromMVCP !== void 0;
7232
7350
  const isScrollToIndexReady = (targetIndex, allowEmpty = false) => {
7233
7351
  var _a3;
@@ -7249,7 +7367,7 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
7249
7367
  const startedAt = Date.now();
7250
7368
  let stableFrames = 0;
7251
7369
  const check = () => {
7252
- if (token !== imperativeScrollToken) {
7370
+ if (!scrollRequestTracker.isCurrent(token)) {
7253
7371
  return;
7254
7372
  }
7255
7373
  if (isSettlingAfterDataChange() || !isReady()) {
@@ -7267,33 +7385,15 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
7267
7385
  state.scheduledWork.frame(check, "imperativeScrollReady");
7268
7386
  };
7269
7387
  const runScrollRequest = (token, resolve, run, isReady = () => true) => {
7270
- const runNow = () => {
7271
- if (token !== imperativeScrollToken) {
7272
- return;
7273
- }
7274
- const didStartScroll = run();
7275
- if (!didStartScroll || !state.scrollingTo) {
7276
- if (state.pendingScrollResolve === resolve) {
7277
- state.pendingScrollResolve = void 0;
7278
- }
7279
- resolve();
7280
- }
7281
- };
7388
+ const runNow = () => scrollRequestTracker.runNow(token, resolve, run);
7282
7389
  if (isSettlingAfterDataChange() || !isReady()) {
7283
7390
  runWhenReady(token, runNow, isReady);
7284
7391
  } else {
7285
7392
  runNow();
7286
7393
  }
7287
7394
  };
7288
- const startImperativeScroll = (resolve) => {
7289
- state.scheduledWork.cancel("imperativeScrollReady");
7290
- const token = ++imperativeScrollToken;
7291
- settlePendingImperativeScroll(state);
7292
- state.pendingScrollResolve = resolve;
7293
- return token;
7294
- };
7295
7395
  const runScrollWithPromise = (run, isReady = () => true) => new Promise((resolve) => {
7296
- const token = startImperativeScroll(resolve);
7396
+ const token = scrollRequestTracker.start(resolve);
7297
7397
  supersedeInitialScroll(ctx);
7298
7398
  runScrollRequest(token, resolve, run, isReady);
7299
7399
  });
@@ -7301,7 +7401,7 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
7301
7401
  const pendingScroll = state.pendingScrollToEnd;
7302
7402
  if (pendingScroll) {
7303
7403
  state.pendingScrollToEnd = void 0;
7304
- if (pendingScroll.token === imperativeScrollToken) {
7404
+ if (scrollRequestTracker.isCurrent(pendingScroll.token)) {
7305
7405
  runScrollRequest(
7306
7406
  pendingScroll.token,
7307
7407
  pendingScroll.resolve,
@@ -7415,7 +7515,7 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
7415
7515
  }),
7416
7516
  scrollToEnd: (options) => new Promise((resolve) => {
7417
7517
  var _a3;
7418
- const token = startImperativeScroll(resolve);
7518
+ const token = scrollRequestTracker.start(resolve);
7419
7519
  state.pendingScrollToEnd = {
7420
7520
  options,
7421
7521
  resolve,
@@ -7795,6 +7895,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7795
7895
  const shouldInitializeHorizontalRTL = !initialScrollAtEnd && !hasInitialScrollIndex && !hasInitialScrollOffset && isHorizontalRTLProps({ horizontal, rtl });
7796
7896
  const initialScrollUsesOffsetOnly = !initialScrollAtEnd && !hasInitialScrollIndex && (hasInitialScrollOffset || shouldInitializeHorizontalRTL);
7797
7897
  const usesBootstrapInitialScroll = initialScrollAtEnd || hasInitialScrollIndex;
7898
+ const shouldBottomAlignNumericInitialScrollIndex = typeof initialScrollIndexProp === "number" && !hasInitialScrollOffset && dataProp.length > 1 && initialScrollIndexProp >= dataProp.length - 1;
7798
7899
  const initialScrollProp = initialScrollAtEnd ? {
7799
7900
  index: Math.max(0, dataProp.length - 1),
7800
7901
  preserveForBottomPadding: true,
@@ -7807,7 +7908,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7807
7908
  viewPosition: (_d = initialScrollIndexProp.viewPosition) != null ? _d : 0
7808
7909
  } : {
7809
7910
  index: initialScrollIndexProp != null ? initialScrollIndexProp : 0,
7810
- viewOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : 0
7911
+ preserveForBottomPadding: shouldBottomAlignNumericInitialScrollIndex ? true : void 0,
7912
+ viewOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : shouldBottomAlignNumericInitialScrollIndex ? -stylePaddingEndState : 0,
7913
+ viewPosition: shouldBottomAlignNumericInitialScrollIndex ? 1 : void 0
7811
7914
  } : initialScrollUsesOffsetOnly ? {
7812
7915
  contentOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : 0,
7813
7916
  index: 0,
@@ -7913,6 +8016,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7913
8016
  viewabilityConfigCallbackPairs: void 0
7914
8017
  };
7915
8018
  const internalState = ctx.state;
8019
+ ctx.scrollToEnd = (options) => scrollToEnd(ctx, options);
7916
8020
  internalState.triggerCalculateItemsInView = (params) => calculateItemsInView(ctx, params);
7917
8021
  internalState.reprocessCurrentScroll = () => updateScroll(ctx, internalState.scroll, true);
7918
8022
  set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPositionConfig);
@@ -8266,6 +8370,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8266
8370
  onScroll: (event) => onScroll(ctx, event),
8267
8371
  onScrollBeginDrag: (event) => {
8268
8372
  var _a4, _b2;
8373
+ const maintainingScrollAtEnd = state.maintainingScrollAtEnd;
8374
+ if (maintainingScrollAtEnd === "animated" || maintainingScrollAtEnd === "instant") {
8375
+ cancelImperativeScroll(state);
8376
+ }
8377
+ if (maintainingScrollAtEnd) {
8378
+ finishMaintainScrollAtEnd(ctx);
8379
+ }
8269
8380
  prepareReachedEdgeForNextUserScroll(ctx);
8270
8381
  (_b2 = (_a4 = state.props).onScrollBeginDrag) == null ? void 0 : _b2.call(_a4, event);
8271
8382
  },
package/.DS_Store DELETED
Binary file