@legendapp/list 3.0.0-beta.31 → 3.0.0-beta.32

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/index.js CHANGED
@@ -42,7 +42,7 @@ function getContentInsetEnd(state) {
42
42
  const baseInset = contentInset != null ? contentInset : state.nativeContentInset;
43
43
  const overrideInset = (_a3 = state.contentInsetOverride) != null ? _a3 : void 0;
44
44
  if (overrideInset) {
45
- const mergedInset = { bottom: 0, left: 0, right: 0, top: 0, ...baseInset, ...overrideInset };
45
+ const mergedInset = { bottom: 0, right: 0, ...baseInset, ...overrideInset };
46
46
  return (horizontal ? mergedInset.right : mergedInset.bottom) || 0;
47
47
  }
48
48
  if (baseInset) {
@@ -193,7 +193,7 @@ function useSelector$(signalName, selector) {
193
193
  var DebugRow = ({ children }) => {
194
194
  return /* @__PURE__ */ React3__namespace.createElement(View, { style: { alignItems: "center", flexDirection: "row", justifyContent: "space-between" } }, children);
195
195
  };
196
- var DebugView = React3__namespace.memo(function DebugView2({ state }) {
196
+ React3__namespace.memo(function DebugView2({ state }) {
197
197
  const ctx = useStateContext();
198
198
  const [totalSize = 0, scrollAdjust = 0, rawScroll = 0, scroll = 0, _numContainers = 0, _numContainersPooled = 0] = useArr$([
199
199
  "totalSize",
@@ -358,9 +358,6 @@ var PositionViewSticky = typedMemo(function PositionViewSticky2({
358
358
  return /* @__PURE__ */ React3__namespace.createElement("div", { ref: refView, style: viewStyle, ...rest }, children);
359
359
  });
360
360
  var PositionView = PositionViewState;
361
-
362
- // src/constants-platform.ts
363
- var IsNewArchitecture = true;
364
361
  function useInit(cb) {
365
362
  React3.useState(() => cb());
366
363
  }
@@ -571,7 +568,8 @@ function createResizeObserver(element, callback) {
571
568
  function useOnLayoutSync({
572
569
  ref,
573
570
  onLayoutProp,
574
- onLayoutChange
571
+ onLayoutChange,
572
+ webLayoutResync
575
573
  }, deps) {
576
574
  React3.useLayoutEffect(() => {
577
575
  var _a3, _b;
@@ -595,7 +593,9 @@ function useOnLayoutSync({
595
593
  var _a4;
596
594
  const target = entry.target instanceof HTMLElement ? entry.target : void 0;
597
595
  const rectObserved = (_a4 = entry.contentRect) != null ? _a4 : target == null ? void 0 : target.getBoundingClientRect();
598
- if (rectObserved.width !== prevRect.width || rectObserved.height !== prevRect.height) {
596
+ const didSizeChange = rectObserved.width !== prevRect.width || rectObserved.height !== prevRect.height;
597
+ const shouldResyncLayout = !!(webLayoutResync == null ? void 0 : webLayoutResync());
598
+ if (didSizeChange || shouldResyncLayout) {
599
599
  prevRect = rectObserved;
600
600
  emit(toLayout(rectObserved), false);
601
601
  }
@@ -621,6 +621,15 @@ var Platform = {
621
621
  OS: "web"
622
622
  };
623
623
 
624
+ // src/utils/isInMVCPActiveMode.ts
625
+ function isInMVCPActiveMode(state) {
626
+ const lock = state.mvcpAnchorLock;
627
+ if (lock && Date.now() > lock.expiresAt) {
628
+ state.mvcpAnchorLock = void 0;
629
+ }
630
+ return state.dataChangeNeedsScrollUpdate || !!state.mvcpAnchorLock;
631
+ }
632
+
624
633
  // src/components/Container.tsx
625
634
  var Container = typedMemo(function Container2({
626
635
  id,
@@ -728,7 +737,8 @@ var Container = typedMemo(function Container2({
728
737
  updateItemSizeFn(currentItemKey, layout);
729
738
  itemLayoutRef.current.didLayout = true;
730
739
  };
731
- if (prevSize !== void 0 && size + 1 < prevSize) {
740
+ const shouldDeferWebShrinkLayoutUpdate = !isInMVCPActiveMode(ctx.state) && prevSize !== void 0 && size + 1 < prevSize;
741
+ if (shouldDeferWebShrinkLayoutUpdate) {
732
742
  const token = pendingShrinkToken + 1;
733
743
  itemLayoutRef.current.pendingShrinkToken = token;
734
744
  requestAnimationFrame(() => {
@@ -752,7 +762,8 @@ var Container = typedMemo(function Container2({
752
762
  const { onLayout } = useOnLayoutSync(
753
763
  {
754
764
  onLayoutChange,
755
- ref
765
+ ref,
766
+ webLayoutResync: () => isInMVCPActiveMode(ctx.state)
756
767
  },
757
768
  [itemKey, layoutRenderCount]
758
769
  );
@@ -944,25 +955,6 @@ var Containers = typedMemo(function Containers2({
944
955
  }
945
956
  return /* @__PURE__ */ React3__namespace.createElement(ContainersInner, { horizontal, numColumns, waitForInitialLayout }, containers);
946
957
  });
947
- function DevNumbers() {
948
- return IS_DEV && // biome-ignore lint/nursery/noShadow: const function name shadowing is intentional
949
- React3__namespace.memo(function DevNumbers2() {
950
- return Array.from({ length: 100 }).map((_, index) => /* @__PURE__ */ React3__namespace.createElement(
951
- "div",
952
- {
953
- key: index,
954
- style: {
955
- height: 100,
956
- pointerEvents: "none",
957
- position: "absolute",
958
- top: index * 100,
959
- width: "100%"
960
- }
961
- },
962
- /* @__PURE__ */ React3__namespace.createElement("div", { style: { color: "red" } }, index * 100)
963
- ));
964
- });
965
- }
966
958
 
967
959
  // src/platform/StyleSheet.tsx
968
960
  function flattenStyles(styles) {
@@ -1137,6 +1129,7 @@ function useValueListener$(key, callback) {
1137
1129
  function ScrollAdjust() {
1138
1130
  const ctx = useStateContext();
1139
1131
  const lastScrollOffsetRef = React3__namespace.useRef(0);
1132
+ const resetPaddingRafRef = React3__namespace.useRef(void 0);
1140
1133
  const callback = React3__namespace.useCallback(() => {
1141
1134
  var _a3;
1142
1135
  const scrollAdjust = peek$(ctx, "scrollAdjust");
@@ -1151,13 +1144,17 @@ function ScrollAdjust() {
1151
1144
  const nextScroll = prevScroll + scrollDelta;
1152
1145
  const totalSize = el.scrollHeight;
1153
1146
  if (scrollDelta > 0 && !ctx.state.adjustingFromInitialMount && totalSize < nextScroll + el.clientHeight) {
1147
+ const paddingBottom = ctx.state.props.stylePaddingBottom || 0;
1154
1148
  const child = el.firstElementChild;
1155
1149
  const pad = (nextScroll + el.clientHeight - totalSize) * 2;
1156
1150
  child.style.paddingBottom = `${pad}px`;
1157
1151
  void el.offsetHeight;
1158
1152
  scrollView.scrollBy(0, scrollDelta);
1159
- requestAnimationFrame(() => {
1160
- const paddingBottom = ctx.state.props.stylePaddingBottom;
1153
+ if (resetPaddingRafRef.current !== void 0) {
1154
+ cancelAnimationFrame(resetPaddingRafRef.current);
1155
+ }
1156
+ resetPaddingRafRef.current = requestAnimationFrame(() => {
1157
+ resetPaddingRafRef.current = void 0;
1161
1158
  child.style.paddingBottom = paddingBottom ? `${paddingBottom}px` : "0";
1162
1159
  });
1163
1160
  } else {
@@ -1279,7 +1276,7 @@ var ListComponent = typedMemo(function ListComponent2({
1279
1276
  },
1280
1277
  getComponent(ListFooterComponent)
1281
1278
  ),
1282
- IS_DEV && ENABLE_DEVMODE && /* @__PURE__ */ React3__namespace.createElement(DevNumbers, null)
1279
+ IS_DEV && ENABLE_DEVMODE
1283
1280
  );
1284
1281
  });
1285
1282
 
@@ -1596,6 +1593,7 @@ function finishScrollTo(ctx) {
1596
1593
  var SCROLL_END_IDLE_MS = 80;
1597
1594
  var SCROLL_END_MAX_MS = 1500;
1598
1595
  var SMOOTH_SCROLL_DURATION_MS = 320;
1596
+ var SCROLL_END_TARGET_EPSILON = 1;
1599
1597
  function doScrollTo(ctx, params) {
1600
1598
  const state = ctx.state;
1601
1599
  const { animated, horizontal, offset } = params;
@@ -1606,7 +1604,10 @@ function doScrollTo(ctx, params) {
1606
1604
  const top = horizontal ? 0 : offset;
1607
1605
  node.scrollTo({ behavior: animated ? "smooth" : "auto", left, top });
1608
1606
  if (animated) {
1609
- listenForScrollEnd(ctx, node);
1607
+ listenForScrollEnd(ctx, node, {
1608
+ horizontal: !!horizontal,
1609
+ targetOffset: offset
1610
+ });
1610
1611
  } else {
1611
1612
  state.scroll = offset;
1612
1613
  setTimeout(() => {
@@ -1615,31 +1616,17 @@ function doScrollTo(ctx, params) {
1615
1616
  }
1616
1617
  }
1617
1618
  }
1618
- function listenForScrollEnd(ctx, node) {
1619
+ function listenForScrollEnd(ctx, node, params) {
1620
+ const { horizontal, targetOffset } = params;
1619
1621
  const supportsScrollEnd = "onscrollend" in node;
1620
1622
  let idleTimeout;
1621
1623
  let maxTimeout;
1622
1624
  let settled = false;
1623
1625
  const targetToken = ctx.state.scrollingTo;
1624
- const finish = () => {
1625
- if (settled) return;
1626
- settled = true;
1627
- cleanup();
1628
- if (targetToken === ctx.state.scrollingTo) {
1629
- finishScrollTo(ctx);
1630
- }
1631
- };
1632
- const onScroll2 = () => {
1633
- if (idleTimeout) {
1634
- clearTimeout(idleTimeout);
1635
- }
1636
- idleTimeout = setTimeout(finish, SCROLL_END_IDLE_MS);
1637
- };
1638
1626
  const cleanup = () => {
1627
+ node.removeEventListener("scroll", onScroll2);
1639
1628
  if (supportsScrollEnd) {
1640
- node.removeEventListener("scrollend", finish);
1641
- } else {
1642
- node.removeEventListener("scroll", onScroll2);
1629
+ node.removeEventListener("scrollend", onScrollEnd);
1643
1630
  }
1644
1631
  if (idleTimeout) {
1645
1632
  clearTimeout(idleTimeout);
@@ -1648,14 +1635,37 @@ function listenForScrollEnd(ctx, node) {
1648
1635
  clearTimeout(maxTimeout);
1649
1636
  }
1650
1637
  };
1638
+ const finish = (reason) => {
1639
+ if (settled) return;
1640
+ if (targetToken !== ctx.state.scrollingTo) {
1641
+ settled = true;
1642
+ cleanup();
1643
+ return;
1644
+ }
1645
+ const currentOffset = horizontal ? node.scrollLeft : node.scrollTop;
1646
+ const isNearTarget = Math.abs(currentOffset - targetOffset) <= SCROLL_END_TARGET_EPSILON;
1647
+ if (reason === "scrollend" && !isNearTarget) {
1648
+ return;
1649
+ }
1650
+ settled = true;
1651
+ cleanup();
1652
+ finishScrollTo(ctx);
1653
+ };
1654
+ const onScroll2 = () => {
1655
+ if (idleTimeout) {
1656
+ clearTimeout(idleTimeout);
1657
+ }
1658
+ idleTimeout = setTimeout(() => finish("idle"), SCROLL_END_IDLE_MS);
1659
+ };
1660
+ const onScrollEnd = () => finish("scrollend");
1661
+ node.addEventListener("scroll", onScroll2);
1651
1662
  if (supportsScrollEnd) {
1652
- node.addEventListener("scrollend", finish, { once: true });
1663
+ node.addEventListener("scrollend", onScrollEnd);
1664
+ maxTimeout = setTimeout(() => finish("max"), SCROLL_END_MAX_MS);
1653
1665
  } else {
1654
- node.addEventListener("scroll", onScroll2);
1655
- idleTimeout = setTimeout(finish, SMOOTH_SCROLL_DURATION_MS);
1656
- maxTimeout = setTimeout(finish, SCROLL_END_MAX_MS);
1666
+ idleTimeout = setTimeout(() => finish("idle"), SMOOTH_SCROLL_DURATION_MS);
1667
+ maxTimeout = setTimeout(() => finish("max"), SCROLL_END_MAX_MS);
1657
1668
  }
1658
- return cleanup;
1659
1669
  }
1660
1670
 
1661
1671
  // src/core/scrollTo.ts
@@ -1680,7 +1690,7 @@ function scrollTo(ctx, params) {
1680
1690
  }
1681
1691
  state.scrollPending = offset;
1682
1692
  if (forceScroll || !isInitialScroll || Platform.OS === "android") {
1683
- doScrollTo(ctx, { animated, horizontal, isInitialScroll, offset });
1693
+ doScrollTo(ctx, { animated, horizontal, offset });
1684
1694
  } else {
1685
1695
  state.scroll = offset;
1686
1696
  }
@@ -1723,7 +1733,8 @@ function updateScroll(ctx, newScroll, forceUpdate) {
1723
1733
  const scrollDelta = Math.abs(newScroll - prevScroll);
1724
1734
  const scrollLength = state.scrollLength;
1725
1735
  const lastCalculated = state.scrollLastCalculate;
1726
- const shouldUpdate = forceUpdate || state.dataChangeNeedsScrollUpdate || state.scrollLastCalculate === void 0 || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
1736
+ const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
1737
+ const shouldUpdate = useAggressiveItemRecalculation || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
1727
1738
  if (shouldUpdate) {
1728
1739
  state.scrollLastCalculate = state.scroll;
1729
1740
  state.ignoreScrollFromMVCPIgnored = false;
@@ -1768,13 +1779,58 @@ function requestAdjust(ctx, positionDiff, dataChanged) {
1768
1779
  }
1769
1780
 
1770
1781
  // src/core/mvcp.ts
1782
+ var MVCP_POSITION_EPSILON = 0.1;
1783
+ var MVCP_ANCHOR_LOCK_TTL_MS = 300;
1784
+ var MVCP_ANCHOR_LOCK_QUIET_PASSES_TO_RELEASE = 2;
1785
+ function resolveAnchorLock(state, enableMVCPAnchorLock, mvcpData, now) {
1786
+ if (!enableMVCPAnchorLock) {
1787
+ state.mvcpAnchorLock = void 0;
1788
+ return void 0;
1789
+ }
1790
+ const lock = state.mvcpAnchorLock;
1791
+ if (!lock) {
1792
+ return void 0;
1793
+ }
1794
+ const isExpired = now > lock.expiresAt;
1795
+ const isMissing = state.indexByKey.get(lock.id) === void 0;
1796
+ if (isExpired || isMissing || !mvcpData) {
1797
+ state.mvcpAnchorLock = void 0;
1798
+ return void 0;
1799
+ }
1800
+ return lock;
1801
+ }
1802
+ function updateAnchorLock(state, params) {
1803
+ {
1804
+ const { anchorId, anchorPosition, dataChanged, now, positionDiff } = params;
1805
+ const enableMVCPAnchorLock = !!dataChanged || !!state.mvcpAnchorLock;
1806
+ const mvcpData = state.props.maintainVisibleContentPosition.data;
1807
+ if (!enableMVCPAnchorLock || !mvcpData || state.scrollingTo || !anchorId || anchorPosition === void 0) {
1808
+ return;
1809
+ }
1810
+ const existingLock = state.mvcpAnchorLock;
1811
+ const quietPasses = !dataChanged && Math.abs(positionDiff) <= MVCP_POSITION_EPSILON && (existingLock == null ? void 0 : existingLock.id) === anchorId ? existingLock.quietPasses + 1 : 0;
1812
+ if (!dataChanged && quietPasses >= MVCP_ANCHOR_LOCK_QUIET_PASSES_TO_RELEASE) {
1813
+ state.mvcpAnchorLock = void 0;
1814
+ return;
1815
+ }
1816
+ state.mvcpAnchorLock = {
1817
+ expiresAt: now + MVCP_ANCHOR_LOCK_TTL_MS,
1818
+ id: anchorId,
1819
+ position: anchorPosition,
1820
+ quietPasses
1821
+ };
1822
+ }
1823
+ }
1771
1824
  function prepareMVCP(ctx, dataChanged) {
1772
1825
  const state = ctx.state;
1773
1826
  const { idsInView, positions, props } = state;
1774
1827
  const {
1775
1828
  maintainVisibleContentPosition: { data: mvcpData, size: mvcpScroll, shouldRestorePosition }
1776
1829
  } = props;
1830
+ const now = Date.now();
1831
+ const enableMVCPAnchorLock = (!!dataChanged || !!state.mvcpAnchorLock);
1777
1832
  const scrollingTo = state.scrollingTo;
1833
+ const anchorLock = resolveAnchorLock(state, enableMVCPAnchorLock, mvcpData, now) ;
1778
1834
  let prevPosition;
1779
1835
  let targetId;
1780
1836
  const idsInViewWithPositions = [];
@@ -1783,28 +1839,45 @@ function prepareMVCP(ctx, dataChanged) {
1783
1839
  const shouldMVCP = dataChanged ? mvcpData : mvcpScroll;
1784
1840
  const indexByKey = state.indexByKey;
1785
1841
  if (shouldMVCP) {
1786
- if (scrollTarget !== void 0) {
1842
+ if (anchorLock && scrollTarget === void 0) {
1843
+ targetId = anchorLock.id;
1844
+ prevPosition = anchorLock.position;
1845
+ } else if (scrollTarget !== void 0) {
1787
1846
  targetId = getId(state, scrollTarget);
1788
- } else if (idsInView.length > 0 && state.didContainersLayout) {
1789
- if (dataChanged) {
1790
- for (let i = 0; i < idsInView.length; i++) {
1791
- const id = idsInView[i];
1792
- const index = indexByKey.get(id);
1793
- if (index !== void 0) {
1794
- idsInViewWithPositions.push({ id, position: positions.get(id) });
1795
- }
1847
+ } else if (idsInView.length > 0 && state.didContainersLayout && !dataChanged) {
1848
+ targetId = idsInView.find((id) => indexByKey.get(id) !== void 0);
1849
+ }
1850
+ if (dataChanged && idsInView.length > 0 && state.didContainersLayout) {
1851
+ for (let i = 0; i < idsInView.length; i++) {
1852
+ const id = idsInView[i];
1853
+ const index = indexByKey.get(id);
1854
+ if (index !== void 0) {
1855
+ idsInViewWithPositions.push({ id, position: positions.get(id) });
1796
1856
  }
1797
- } else {
1798
- targetId = idsInView.find((id) => indexByKey.get(id) !== void 0);
1799
1857
  }
1800
1858
  }
1801
- if (targetId !== void 0) {
1859
+ if (targetId !== void 0 && prevPosition === void 0) {
1802
1860
  prevPosition = positions.get(targetId);
1803
1861
  }
1804
1862
  return () => {
1805
1863
  let positionDiff = 0;
1806
- if (dataChanged && targetId === void 0 && mvcpData) {
1807
- const data = state.props.data;
1864
+ let anchorIdForLock = anchorLock == null ? void 0 : anchorLock.id;
1865
+ let anchorPositionForLock;
1866
+ let skipTargetAnchor = false;
1867
+ const data = state.props.data;
1868
+ const shouldValidateLockedAnchor = dataChanged && mvcpData && scrollTarget === void 0 && targetId !== void 0 && (anchorLock == null ? void 0 : anchorLock.id) === targetId && shouldRestorePosition !== void 0;
1869
+ if (shouldValidateLockedAnchor && targetId !== void 0) {
1870
+ const index = indexByKey.get(targetId);
1871
+ if (index !== void 0) {
1872
+ const item = data[index];
1873
+ skipTargetAnchor = item === void 0 || !shouldRestorePosition(item, index, data);
1874
+ if (skipTargetAnchor && (anchorLock == null ? void 0 : anchorLock.id) === targetId) {
1875
+ state.mvcpAnchorLock = void 0;
1876
+ }
1877
+ }
1878
+ }
1879
+ const shouldUseFallbackVisibleAnchor = dataChanged && mvcpData && scrollTarget === void 0 && (targetId === void 0 || positions.get(targetId) === void 0 || skipTargetAnchor);
1880
+ if (shouldUseFallbackVisibleAnchor) {
1808
1881
  for (let i = 0; i < idsInViewWithPositions.length; i++) {
1809
1882
  const { id, position } = idsInViewWithPositions[i];
1810
1883
  const index = indexByKey.get(id);
@@ -1817,11 +1890,13 @@ function prepareMVCP(ctx, dataChanged) {
1817
1890
  const newPosition = positions.get(id);
1818
1891
  if (newPosition !== void 0) {
1819
1892
  positionDiff = newPosition - position;
1893
+ anchorIdForLock = id;
1894
+ anchorPositionForLock = newPosition;
1820
1895
  break;
1821
1896
  }
1822
1897
  }
1823
1898
  }
1824
- if (targetId !== void 0 && prevPosition !== void 0) {
1899
+ if (!skipTargetAnchor && targetId !== void 0 && prevPosition !== void 0) {
1825
1900
  const newPosition = positions.get(targetId);
1826
1901
  if (newPosition !== void 0) {
1827
1902
  const totalSize = getContentSize(ctx);
@@ -1834,20 +1909,29 @@ function prepareMVCP(ctx, dataChanged) {
1834
1909
  }
1835
1910
  }
1836
1911
  positionDiff = diff;
1912
+ anchorIdForLock = targetId;
1913
+ anchorPositionForLock = newPosition;
1837
1914
  }
1838
1915
  }
1839
1916
  if (scrollingToViewPosition && scrollingToViewPosition > 0) {
1840
1917
  const newSize = getItemSize(ctx, targetId, scrollTarget, state.props.data[scrollTarget]);
1841
1918
  const prevSize = scrollingTo == null ? void 0 : scrollingTo.itemSize;
1842
- if (newSize !== void 0 && prevSize !== void 0 && newSize !== (scrollingTo == null ? void 0 : scrollingTo.itemSize)) {
1919
+ if (newSize !== void 0 && prevSize !== void 0 && newSize !== prevSize) {
1843
1920
  const diff = newSize - prevSize;
1844
1921
  if (diff !== 0) {
1845
- positionDiff += (newSize - prevSize) * scrollingToViewPosition;
1922
+ positionDiff += diff * scrollingToViewPosition;
1846
1923
  scrollingTo.itemSize = newSize;
1847
1924
  }
1848
1925
  }
1849
1926
  }
1850
- if (Math.abs(positionDiff) > 0.1) {
1927
+ updateAnchorLock(state, {
1928
+ anchorId: anchorIdForLock,
1929
+ anchorPosition: anchorPositionForLock,
1930
+ dataChanged,
1931
+ now,
1932
+ positionDiff
1933
+ });
1934
+ if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
1851
1935
  requestAdjust(ctx, positionDiff);
1852
1936
  }
1853
1937
  };
@@ -2561,7 +2645,7 @@ function handleStickyActivation(ctx, stickyHeaderIndices, stickyArray, currentSt
2561
2645
  }
2562
2646
  }
2563
2647
  }
2564
- function handleStickyRecycling(ctx, stickyArray, scroll, scrollBuffer, currentStickyIdx, pendingRemoval, alwaysRenderIndicesSet) {
2648
+ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentStickyIdx, pendingRemoval, alwaysRenderIndicesSet) {
2565
2649
  var _a3, _b, _c;
2566
2650
  const state = ctx.state;
2567
2651
  for (const containerIndex of state.stickyContainerPool) {
@@ -2582,13 +2666,13 @@ function handleStickyRecycling(ctx, stickyArray, scroll, scrollBuffer, currentSt
2582
2666
  if (nextIndex) {
2583
2667
  const nextId = (_a3 = state.idCache[nextIndex]) != null ? _a3 : getId(state, nextIndex);
2584
2668
  const nextPos = nextId ? state.positions.get(nextId) : void 0;
2585
- shouldRecycle = nextPos !== void 0 && scroll > nextPos + scrollBuffer * 2;
2669
+ shouldRecycle = nextPos !== void 0 && scroll > nextPos + drawDistance * 2;
2586
2670
  } else {
2587
2671
  const currentId = (_b = state.idCache[itemIndex]) != null ? _b : getId(state, itemIndex);
2588
2672
  if (currentId) {
2589
2673
  const currentPos = state.positions.get(currentId);
2590
2674
  const currentSize = (_c = state.sizes.get(currentId)) != null ? _c : getItemSize(ctx, currentId, itemIndex, state.props.data[itemIndex]);
2591
- shouldRecycle = currentPos !== void 0 && scroll > currentPos + currentSize + scrollBuffer * 3;
2675
+ shouldRecycle = currentPos !== void 0 && scroll > currentPos + currentSize + drawDistance * 3;
2592
2676
  }
2593
2677
  }
2594
2678
  if (shouldRecycle) {
@@ -2613,11 +2697,11 @@ function calculateItemsInView(ctx, params = {}) {
2613
2697
  props: {
2614
2698
  alwaysRenderIndicesArr,
2615
2699
  alwaysRenderIndicesSet,
2700
+ drawDistance,
2616
2701
  getItemType,
2617
2702
  itemsAreEqual,
2618
2703
  keyExtractor,
2619
- onStickyHeaderChange,
2620
- scrollBuffer
2704
+ onStickyHeaderChange
2621
2705
  },
2622
2706
  scrollForNextCalculateItemsInView,
2623
2707
  scrollLength,
@@ -2630,6 +2714,7 @@ function calculateItemsInView(ctx, params = {}) {
2630
2714
  const stickyIndicesSet = state.props.stickyIndicesSet || /* @__PURE__ */ new Set();
2631
2715
  const alwaysRenderArr = alwaysRenderIndicesArr || [];
2632
2716
  const alwaysRenderSet = alwaysRenderIndicesSet || /* @__PURE__ */ new Set();
2717
+ const { dataChanged, doMVCP, forceFullItemPositions } = params;
2633
2718
  const prevNumContainers = peek$(ctx, "numContainers");
2634
2719
  if (!data || scrollLength === 0 || !prevNumContainers) {
2635
2720
  return;
@@ -2637,7 +2722,6 @@ function calculateItemsInView(ctx, params = {}) {
2637
2722
  const totalSize = getContentSize(ctx);
2638
2723
  const topPad = peek$(ctx, "stylePaddingTop") + peek$(ctx, "headerSize");
2639
2724
  const numColumns = peek$(ctx, "numColumns");
2640
- const { dataChanged, doMVCP, forceFullItemPositions } = params;
2641
2725
  const speed = getScrollVelocity(state);
2642
2726
  const scrollExtra = 0;
2643
2727
  const { queuedInitialLayout } = state;
@@ -2656,24 +2740,20 @@ function calculateItemsInView(ctx, params = {}) {
2656
2740
  if (scroll + scrollLength > totalSize) {
2657
2741
  scroll = Math.max(0, totalSize - scrollLength);
2658
2742
  }
2659
- if (ENABLE_DEBUG_VIEW) {
2660
- set$(ctx, "debugRawScroll", scrollState);
2661
- set$(ctx, "debugComputedScroll", scroll);
2662
- }
2663
2743
  const previousStickyIndex = peek$(ctx, "activeStickyIndex");
2664
2744
  const currentStickyIdx = stickyIndicesArr.length > 0 ? findCurrentStickyIndex(stickyIndicesArr, scroll, state) : -1;
2665
2745
  const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyIndicesArr[currentStickyIdx] : -1;
2666
2746
  if (currentStickyIdx >= 0 || previousStickyIndex >= 0) {
2667
2747
  set$(ctx, "activeStickyIndex", nextActiveStickyIndex);
2668
2748
  }
2669
- let scrollBufferTop = scrollBuffer;
2670
- let scrollBufferBottom = scrollBuffer;
2671
- if (speed > 0 || speed === 0 && scroll < Math.max(50, scrollBuffer)) {
2672
- scrollBufferTop = scrollBuffer * 0.5;
2673
- scrollBufferBottom = scrollBuffer * 1.5;
2749
+ let scrollBufferTop = drawDistance;
2750
+ let scrollBufferBottom = drawDistance;
2751
+ if (speed > 0 || speed === 0 && scroll < Math.max(50, drawDistance)) {
2752
+ scrollBufferTop = drawDistance * 0.5;
2753
+ scrollBufferBottom = drawDistance * 1.5;
2674
2754
  } else {
2675
- scrollBufferTop = scrollBuffer * 1.5;
2676
- scrollBufferBottom = scrollBuffer * 0.5;
2755
+ scrollBufferTop = drawDistance * 1.5;
2756
+ scrollBufferBottom = drawDistance * 0.5;
2677
2757
  }
2678
2758
  const scrollTopBuffered = scroll - scrollBufferTop;
2679
2759
  const scrollBottom = scroll + scrollLength + (scroll < 0 ? -scroll : 0);
@@ -2683,7 +2763,9 @@ function calculateItemsInView(ctx, params = {}) {
2683
2763
  if (top === null && bottom === null) {
2684
2764
  state.scrollForNextCalculateItemsInView = void 0;
2685
2765
  } else if ((top === null || scrollTopBuffered > top) && (bottom === null || scrollBottomBuffered < bottom)) {
2686
- return;
2766
+ if (!isInMVCPActiveMode(state)) {
2767
+ return;
2768
+ }
2687
2769
  }
2688
2770
  }
2689
2771
  const checkMVCP = doMVCP ? prepareMVCP(ctx, dataChanged) : void 0;
@@ -2915,7 +2997,7 @@ function calculateItemsInView(ctx, params = {}) {
2915
2997
  ctx,
2916
2998
  stickyIndicesArr,
2917
2999
  scroll,
2918
- scrollBuffer,
3000
+ drawDistance,
2919
3001
  currentStickyIdx,
2920
3002
  pendingRemoval,
2921
3003
  alwaysRenderSet
@@ -3027,7 +3109,8 @@ function checkFinishedScrollFrame(ctx) {
3027
3109
  const diff1 = Math.abs(scroll - clampedTargetOffset);
3028
3110
  const diff2 = Math.abs(diff1 - adjust);
3029
3111
  const isNotOverscrolled = Math.abs(scroll - maxOffset) < 1;
3030
- if (isNotOverscrolled && (diff1 < 1 || diff2 < 1)) {
3112
+ const isAtTarget = diff1 < 1 || !scrollingTo.animated && diff2 < 1;
3113
+ if (isNotOverscrolled && isAtTarget) {
3031
3114
  finishScrollTo(ctx);
3032
3115
  }
3033
3116
  }
@@ -3171,10 +3254,10 @@ function doInitialAllocateContainers(ctx) {
3171
3254
  scrollLength,
3172
3255
  props: {
3173
3256
  data,
3257
+ drawDistance,
3174
3258
  getEstimatedItemSize,
3175
3259
  getFixedItemSize,
3176
3260
  getItemType,
3177
- scrollBuffer,
3178
3261
  numColumns,
3179
3262
  estimatedItemSize
3180
3263
  }
@@ -3196,7 +3279,7 @@ function doInitialAllocateContainers(ctx) {
3196
3279
  } else {
3197
3280
  averageItemSize = estimatedItemSize;
3198
3281
  }
3199
- const numContainers = Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize * numColumns);
3282
+ const numContainers = Math.ceil((scrollLength + drawDistance * 2) / averageItemSize * numColumns);
3200
3283
  for (let i = 0; i < numContainers; i++) {
3201
3284
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
3202
3285
  set$(ctx, `containerColumn${i}`, -1);
@@ -3284,7 +3367,7 @@ function onScroll(ctx, event) {
3284
3367
  }
3285
3368
  }
3286
3369
  let newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
3287
- if (state.scrollingTo) {
3370
+ if (state.scrollingTo && state.scrollingTo.offset >= newScroll) {
3288
3371
  const maxOffset = clampScrollOffset(ctx, newScroll);
3289
3372
  if (newScroll !== maxOffset && Math.abs(newScroll - maxOffset) > 1) {
3290
3373
  newScroll = maxOffset;
@@ -3357,6 +3440,26 @@ var ScrollAdjustHandler = class {
3357
3440
  };
3358
3441
 
3359
3442
  // src/core/updateItemSize.ts
3443
+ function runOrScheduleMVCPRecalculate(ctx) {
3444
+ const state = ctx.state;
3445
+ {
3446
+ if (!state.mvcpAnchorLock) {
3447
+ if (state.queuedMVCPRecalculate !== void 0) {
3448
+ cancelAnimationFrame(state.queuedMVCPRecalculate);
3449
+ state.queuedMVCPRecalculate = void 0;
3450
+ }
3451
+ calculateItemsInView(ctx, { doMVCP: true });
3452
+ return;
3453
+ }
3454
+ if (state.queuedMVCPRecalculate !== void 0) {
3455
+ return;
3456
+ }
3457
+ state.queuedMVCPRecalculate = requestAnimationFrame(() => {
3458
+ state.queuedMVCPRecalculate = void 0;
3459
+ calculateItemsInView(ctx, { doMVCP: true });
3460
+ });
3461
+ }
3462
+ }
3360
3463
  function updateItemSize(ctx, itemKey, sizeObj) {
3361
3464
  var _a3;
3362
3465
  const state = ctx.state;
@@ -3440,7 +3543,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
3440
3543
  if (didContainersLayout || checkAllSizesKnown(state)) {
3441
3544
  if (needsRecalculate) {
3442
3545
  state.scrollForNextCalculateItemsInView = void 0;
3443
- calculateItemsInView(ctx, { doMVCP: true });
3546
+ runOrScheduleMVCPRecalculate(ctx);
3444
3547
  }
3445
3548
  if (shouldMaintainScrollAtEnd) {
3446
3549
  if (maintainScrollAtEnd === true || maintainScrollAtEnd.onItemLayout) {
@@ -3786,8 +3889,6 @@ function useThrottledOnScroll(originalHandler, scrollEventThrottle) {
3786
3889
  }
3787
3890
 
3788
3891
  // src/components/LegendList.tsx
3789
- var DEFAULT_DRAW_DISTANCE = 250;
3790
- var DEFAULT_ITEM_SIZE = 100;
3791
3892
  var LegendList = typedMemo(
3792
3893
  // biome-ignore lint/nursery/noShadow: const function name shadowing is intentional
3793
3894
  typedForwardRef(function LegendList2(props, forwardedRef) {
@@ -3817,7 +3918,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3817
3918
  data: dataProp = [],
3818
3919
  dataVersion,
3819
3920
  drawDistance = 250,
3820
- estimatedItemSize: estimatedItemSizeProp,
3921
+ estimatedItemSize = 100,
3821
3922
  estimatedListSize,
3822
3923
  extraData,
3823
3924
  getEstimatedItemSize,
@@ -3896,14 +3997,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3896
3997
  index: initialScrollIndexProp || 0,
3897
3998
  viewOffset: initialScrollOffsetProp || 0
3898
3999
  } : void 0;
3899
- const [canRender, setCanRender] = React3__namespace.useState(!IsNewArchitecture);
4000
+ const [canRender, setCanRender] = React3__namespace.useState(false);
3900
4001
  const ctx = useStateContext();
3901
4002
  ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
3902
4003
  const refScroller = React3.useRef(null);
3903
4004
  const combinedRef = useCombinedRef(refScroller, refScrollView);
3904
- const estimatedItemSize = estimatedItemSizeProp != null ? estimatedItemSizeProp : DEFAULT_ITEM_SIZE;
3905
- const scrollBuffer = (drawDistance != null ? drawDistance : DEFAULT_DRAW_DISTANCE) || 1;
3906
- const keyExtractor = keyExtractorProp != null ? keyExtractorProp : (_item, index) => index.toString();
4005
+ const keyExtractor = keyExtractorProp != null ? keyExtractorProp : ((_item, index) => index.toString());
3907
4006
  const stickyHeaderIndices = stickyHeaderIndicesProp != null ? stickyHeaderIndicesProp : stickyIndicesDeprecated;
3908
4007
  const alwaysRenderIndices = React3.useMemo(() => {
3909
4008
  const indices = getAlwaysRenderIndices(alwaysRender, dataProp, keyExtractor);
@@ -3932,8 +4031,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3932
4031
  ctx.state = {
3933
4032
  activeStickyIndex: -1,
3934
4033
  averageSizes: {},
3935
- columns: /* @__PURE__ */ new Map(),
3936
4034
  columnSpans: /* @__PURE__ */ new Map(),
4035
+ columns: /* @__PURE__ */ new Map(),
3937
4036
  containerItemKeys: /* @__PURE__ */ new Map(),
3938
4037
  containerItemTypes: /* @__PURE__ */ new Map(),
3939
4038
  contentInsetOverride: void 0,
@@ -4020,6 +4119,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4020
4119
  contentInset,
4021
4120
  data: dataProp,
4022
4121
  dataVersion,
4122
+ drawDistance,
4023
4123
  estimatedItemSize,
4024
4124
  getEstimatedItemSize: useWrapIfItem(getEstimatedItemSize),
4025
4125
  getFixedItemSize: useWrapIfItem(getFixedItemSize),
@@ -4043,7 +4143,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4043
4143
  overrideItemLayout,
4044
4144
  recycleItems: !!recycleItems,
4045
4145
  renderItem,
4046
- scrollBuffer,
4047
4146
  snapToIndices,
4048
4147
  stickyIndicesArr: stickyHeaderIndices != null ? stickyHeaderIndices : [],
4049
4148
  stickyIndicesSet: React3.useMemo(() => new Set(stickyHeaderIndices != null ? stickyHeaderIndices : []), [stickyHeaderIndices == null ? void 0 : stickyHeaderIndices.join(",")]),
@@ -4066,12 +4165,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4066
4165
  setPaddingTop(ctx, { stylePaddingTop: stylePaddingTopState });
4067
4166
  refState.current.props.stylePaddingBottom = stylePaddingBottomState;
4068
4167
  let paddingDiff = stylePaddingTopState - prevPaddingTop;
4069
- if (shouldAdjustPadding && maintainVisibleContentPositionConfig.size && paddingDiff && prevPaddingTop !== void 0 && Platform.OS === "ios") {
4070
- if (state.scroll < 0) {
4071
- paddingDiff += state.scroll;
4072
- }
4073
- requestAdjust(ctx, paddingDiff);
4074
- }
4168
+ if (shouldAdjustPadding && maintainVisibleContentPositionConfig.size && paddingDiff && prevPaddingTop !== void 0 && Platform.OS === "ios") ;
4075
4169
  };
4076
4170
  if (isFirstLocal) {
4077
4171
  initializeStateVars(false);
@@ -4273,7 +4367,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4273
4367
  updateItemSize: fns.updateItemSize,
4274
4368
  waitForInitialLayout
4275
4369
  }
4276
- ), IS_DEV && ENABLE_DEBUG_VIEW && /* @__PURE__ */ React3__namespace.createElement(DebugView, { state: refState.current }));
4370
+ ), IS_DEV && ENABLE_DEBUG_VIEW);
4277
4371
  });
4278
4372
 
4279
4373
  exports.LegendList = LegendList;