@legendapp/list 3.4.0 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/react-native.js CHANGED
@@ -935,8 +935,14 @@ function scheduleFullDrawDistancePrewarm(ctx) {
935
935
  return;
936
936
  }
937
937
  state.scheduledWork.frame(() => {
938
- var _a3;
939
- return (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
938
+ var _a3, _b, _c;
939
+ if (state.props.hasExternalScroll && !hasActiveInitialScroll(state) && !state.scrollingTo && !state.pendingNativeMVCPAdjust) {
940
+ const offset = (_b = (_a3 = state.refScroller.current) == null ? void 0 : _a3.getRawScrollOffset) == null ? void 0 : _b.call(_a3);
941
+ if (offset !== void 0) {
942
+ state.scroll = state.scrollPending = state.props.horizontal ? toLogicalHorizontalOffset(state, offset, state.totalSize) : offset;
943
+ }
944
+ }
945
+ (_c = state.triggerCalculateItemsInView) == null ? void 0 : _c.call(state);
940
946
  }, "fullDrawDistancePrewarm");
941
947
  }
942
948
 
@@ -949,6 +955,9 @@ function resetInitialRenderState(ctx, {
949
955
  if (resetLayout) {
950
956
  state.didContainersLayout = false;
951
957
  state.queuedInitialLayout = false;
958
+ state.scrollBufferDirection = void 0;
959
+ state.scrollHistory.length = 0;
960
+ state.scheduledWork.cancel("renderRangeProjection");
952
961
  state.edgeReachedGate = void 0;
953
962
  state.isStartReached = null;
954
963
  state.isEndReached = null;
@@ -1177,6 +1186,7 @@ function setSize(ctx, itemKey, size, notifyTotalSize = true) {
1177
1186
  const previousSize = sizes.get(itemKey);
1178
1187
  const diff = previousSize !== void 0 ? size - previousSize : size;
1179
1188
  if (diff !== 0) {
1189
+ state.positionsAreCurrent = false;
1180
1190
  addTotalSize(ctx, itemKey, diff, notifyTotalSize);
1181
1191
  }
1182
1192
  sizes.set(itemKey, size);
@@ -1237,6 +1247,7 @@ function getKnownOrFixedSize(ctx, key, index, data, resolved) {
1237
1247
  const fixedSize = (resolved == null ? void 0 : resolved.didResolveFixedItemSize) ? resolved.fixedItemSize : getFixedItemSize(data, index, itemType);
1238
1248
  if (fixedSize !== void 0) {
1239
1249
  size = fixedSize + ctx.scrollAxisGap;
1250
+ state.positionsAreCurrent = false;
1240
1251
  state.sizesKnown.set(key, size);
1241
1252
  }
1242
1253
  }
@@ -1310,10 +1321,17 @@ function getItemSizeAtIndex(ctx, index) {
1310
1321
  }
1311
1322
 
1312
1323
  // src/core/calculateOffsetWithOffsetPosition.ts
1324
+ function getViewPositionOffset(availableSpace, viewPosition, viewPositionFallback) {
1325
+ let alignment = viewPosition;
1326
+ if (availableSpace < 0 && viewPositionFallback !== void 0) {
1327
+ alignment = viewPositionFallback === "start" ? 0 : 1;
1328
+ }
1329
+ return -alignment * availableSpace;
1330
+ }
1313
1331
  function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1314
1332
  var _a3;
1315
1333
  const state = ctx.state;
1316
- const { index, viewOffset, viewPosition } = params;
1334
+ const { index, viewOffset, viewPosition, viewPositionFallback } = params;
1317
1335
  let offset = offsetParam;
1318
1336
  if (viewOffset) {
1319
1337
  offset -= viewOffset;
@@ -1334,7 +1352,8 @@ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1334
1352
  const measuredItemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1335
1353
  const itemSize = Math.max(0, measuredItemSize - (isOutOfBounds ? 0 : ctx.scrollAxisGap));
1336
1354
  const trailingInset = getContentInsetEnd(ctx);
1337
- offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1355
+ const availableSpace = state.scrollLength - trailingInset - itemSize;
1356
+ offset += getViewPositionOffset(availableSpace, viewPosition, viewPositionFallback);
1338
1357
  }
1339
1358
  return offset;
1340
1359
  }
@@ -1721,15 +1740,27 @@ function prepareMVCP(ctx, dataChanged, adjustmentSource) {
1721
1740
  anchorPositionForLock = newPosition;
1722
1741
  }
1723
1742
  }
1724
- if (scrollingToViewPosition && scrollingToViewPosition > 0) {
1743
+ if (scrollingTo && scrollingToViewPosition !== void 0 && (scrollingToViewPosition > 0 || scrollingTo.viewPositionFallback)) {
1725
1744
  const newSize = getItemSize(ctx, targetId, scrollTarget, state.props.data[scrollTarget]);
1726
- const prevSize = scrollingTo == null ? void 0 : scrollingTo.itemSize;
1745
+ const prevSize = scrollingTo.itemSize;
1727
1746
  if (newSize !== void 0 && prevSize !== void 0 && newSize !== prevSize) {
1728
- const diff = newSize - prevSize;
1729
- if (diff !== 0) {
1730
- positionDiff += diff * scrollingToViewPosition;
1731
- scrollingTo.itemSize = newSize;
1747
+ if (scrollingTo.viewPositionFallback) {
1748
+ const viewport = state.scrollLength - getContentInsetEnd(ctx);
1749
+ const previousSpace = viewport - Math.max(0, prevSize - ctx.scrollAxisGap);
1750
+ const nextSpace = viewport - Math.max(0, newSize - ctx.scrollAxisGap);
1751
+ positionDiff += getViewPositionOffset(
1752
+ nextSpace,
1753
+ scrollingToViewPosition,
1754
+ scrollingTo.viewPositionFallback
1755
+ ) - getViewPositionOffset(
1756
+ previousSpace,
1757
+ scrollingToViewPosition,
1758
+ scrollingTo.viewPositionFallback
1759
+ );
1760
+ } else {
1761
+ positionDiff += (newSize - prevSize) * scrollingToViewPosition;
1732
1762
  }
1763
+ scrollingTo.itemSize = newSize;
1733
1764
  }
1734
1765
  }
1735
1766
  updateAnchorLock(state, {
@@ -1850,6 +1881,9 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
1850
1881
  state.scrollTime = currentTime;
1851
1882
  const scrollDelta = Math.abs(newScroll - prevScroll);
1852
1883
  const isUserScrollEvent = !!(options == null ? void 0 : options.fromNativeScrollEvent) && scrollDelta > 0.1 && !adjustChanged && scrollingTo === void 0 && !state.pendingNativeMVCPAdjust;
1884
+ if (isUserScrollEvent) {
1885
+ state.scrollBufferDirection = newScroll > prevScroll ? 1 : -1;
1886
+ }
1853
1887
  const didCancelMaintainScrollAtEnd = Platform.OS === "web" && isUserScrollEvent && newScroll < prevScroll && !!(state.maintainingScrollAtEnd || state.pendingMaintainScrollAtEnd);
1854
1888
  if (didCancelMaintainScrollAtEnd) {
1855
1889
  finishMaintainScrollAtEnd(ctx);
@@ -1929,10 +1963,10 @@ function finishMaintainScrollAtEnd(ctx) {
1929
1963
  }
1930
1964
  }
1931
1965
  function doMaintainScrollAtEnd(ctx, options) {
1932
- var _a3, _b, _c, _d, _e, _f;
1966
+ var _a3, _b, _c, _d, _e, _f, _g, _h;
1933
1967
  const state = ctx.state;
1934
1968
  let didMaintain = false;
1935
- if (state.isDragging) {
1969
+ if (state.isDragging || ((_b = (_a3 = state.refScroller.current) == null ? void 0 : _a3.isScrollInRange) == null ? void 0 : _b.call(_a3)) === false) {
1936
1970
  finishMaintainScrollAtEnd(ctx);
1937
1971
  } else {
1938
1972
  const {
@@ -1944,11 +1978,11 @@ function doMaintainScrollAtEnd(ctx, options) {
1944
1978
  const immediate = (options == null ? void 0 : options.immediate) && Platform.OS === "web" && !(maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.animated);
1945
1979
  const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
1946
1980
  const isReplayingPendingMaintain = !!state.pendingMaintainScrollAtEnd;
1947
- 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);
1981
+ const shouldMaintainScrollAtEnd = !!(!((_c = ctx.scrollRequestTracker) == null ? void 0 : _c.isWaitingToRun()) && (!state.scrollingTo || state.scrollingTo.isScrollToEnd) && (isWithinMaintainScrollAtEndThreshold || isReplayingPendingMaintain || ((_d = state.scrollingTo) == null ? void 0 : _d.isScrollToEnd)) && maintainScrollAtEnd && didFinishInitialScroll);
1948
1982
  if (shouldMaintainScrollAtEnd && !state.scrollingTo) {
1949
1983
  const scroller = state.refScroller.current;
1950
- const nativeMax = (_c = scroller == null ? void 0 : scroller.getMaxScrollOffset) == null ? void 0 : _c.call(scroller);
1951
- const observed = (_d = scroller == null ? void 0 : scroller.getCurrentScrollOffset) == null ? void 0 : _d.call(scroller);
1984
+ const nativeMax = (_e = scroller == null ? void 0 : scroller.getMaxScrollOffset) == null ? void 0 : _e.call(scroller);
1985
+ const observed = (_f = scroller == null ? void 0 : scroller.getCurrentScrollOffset) == null ? void 0 : _f.call(scroller);
1952
1986
  if (nativeMax !== void 0 && observed !== void 0 && nativeMax < state.scroll - 1 && Math.abs(observed - nativeMax) <= 1) {
1953
1987
  state.pendingMaintainScrollAtEnd = true;
1954
1988
  state.scrollPending = observed;
@@ -1960,13 +1994,13 @@ function doMaintainScrollAtEnd(ctx, options) {
1960
1994
  } else if (pendingNativeMVCPAdjust) {
1961
1995
  state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
1962
1996
  } else if (shouldMaintainScrollAtEnd && (state.scrollingTo || state.pendingScrollResolve)) {
1963
- if (immediate && ((_e = state.scrollingTo) == null ? void 0 : _e.isScrollToEnd) && !state.scrollingTo.animated) {
1997
+ if (immediate && ((_g = state.scrollingTo) == null ? void 0 : _g.isScrollToEnd) && !state.scrollingTo.animated) {
1964
1998
  state.pendingMaintainScrollAtEnd = false;
1965
1999
  ctx.scrollToEnd({ animated: false });
1966
2000
  didMaintain = true;
1967
2001
  } else {
1968
2002
  state.pendingMaintainScrollAtEnd = true;
1969
- (_f = state.maintainingScrollAtEnd) != null ? _f : state.maintainingScrollAtEnd = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
2003
+ (_h = state.maintainingScrollAtEnd) != null ? _h : state.maintainingScrollAtEnd = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
1970
2004
  }
1971
2005
  } else if (shouldMaintainScrollAtEnd && didContainersLayout) {
1972
2006
  state.pendingMaintainScrollAtEnd = false;
@@ -1980,8 +2014,9 @@ function doMaintainScrollAtEnd(ctx, options) {
1980
2014
  const scrollAtRequest = state.scroll;
1981
2015
  state.maintainingScrollAtEnd = pendingState;
1982
2016
  const run = () => {
2017
+ var _a4, _b2;
1983
2018
  if (state.maintainingScrollAtEnd === pendingState) {
1984
- if (state.isDragging) {
2019
+ if (state.isDragging || ((_b2 = (_a4 = state.refScroller.current) == null ? void 0 : _a4.isScrollInRange) == null ? void 0 : _b2.call(_a4)) === false) {
1985
2020
  finishMaintainScrollAtEnd(ctx);
1986
2021
  } else {
1987
2022
  const isStillWithinThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
@@ -2452,7 +2487,8 @@ function scrollToIndex(ctx, {
2452
2487
  animated = true,
2453
2488
  forceScroll,
2454
2489
  isInitialScroll,
2455
- viewPosition
2490
+ viewPosition,
2491
+ viewPositionFallback
2456
2492
  }) {
2457
2493
  const state = ctx.state;
2458
2494
  const { data } = state.props;
@@ -2472,7 +2508,8 @@ function scrollToIndex(ctx, {
2472
2508
  itemSize,
2473
2509
  offset: firstIndexOffset,
2474
2510
  viewOffset,
2475
- viewPosition: viewPosition != null ? viewPosition : 0
2511
+ viewPosition: viewPosition != null ? viewPosition : 0,
2512
+ viewPositionFallback
2476
2513
  });
2477
2514
  }
2478
2515
 
@@ -2492,6 +2529,7 @@ function dispatchInitialScroll(ctx, params) {
2492
2529
  precomputedWithViewOffset: true,
2493
2530
  viewOffset: target.viewOffset,
2494
2531
  viewPosition: target.viewPosition,
2532
+ viewPositionFallback: target.viewPositionFallback,
2495
2533
  waitForInitialScrollCompletionFrame: waitForCompletionFrame
2496
2534
  });
2497
2535
  }
@@ -2826,7 +2864,7 @@ function createRetargetedBottomAlignedInitialScroll(options) {
2826
2864
  };
2827
2865
  }
2828
2866
  function areEquivalentBootstrapInitialScrollTargets(current, next) {
2829
- return current.index === next.index && current.preserveForBottomPadding === next.preserveForBottomPadding && current.preserveForFooterLayout === next.preserveForFooterLayout && current.viewOffset === next.viewOffset && current.viewPosition === next.viewPosition;
2867
+ return current.index === next.index && current.preserveForBottomPadding === next.preserveForBottomPadding && current.preserveForFooterLayout === next.preserveForFooterLayout && current.viewOffset === next.viewOffset && current.viewPosition === next.viewPosition && current.viewPositionFallback === next.viewPositionFallback;
2830
2868
  }
2831
2869
  function clearPendingInitialScrollFooterLayout(ctx, options) {
2832
2870
  const { dataLength, stylePaddingEnd, target } = options;
@@ -3041,7 +3079,7 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
3041
3079
  preserveForFooterLayout: shouldPreserveInitialScrollForFooterLayout(initialScroll),
3042
3080
  stylePaddingEnd
3043
3081
  });
3044
- const didTargetChange = initialScroll.index !== updatedInitialScroll.index || initialScroll.viewPosition !== updatedInitialScroll.viewPosition || initialScroll.viewOffset !== updatedInitialScroll.viewOffset;
3082
+ const didTargetChange = initialScroll.index !== updatedInitialScroll.index || initialScroll.viewPosition !== updatedInitialScroll.viewPosition || initialScroll.viewPositionFallback !== updatedInitialScroll.viewPositionFallback || initialScroll.viewOffset !== updatedInitialScroll.viewOffset;
3045
3083
  if (!didTargetChange) {
3046
3084
  clearPendingInitialScrollFooterLayout(ctx, {
3047
3085
  dataLength,
@@ -3319,6 +3357,7 @@ function handleInitialScrollDataChange(ctx, options) {
3319
3357
 
3320
3358
  // src/core/resetLayoutCachesForDataChange.ts
3321
3359
  function resetLayoutCachesForDataChange(state) {
3360
+ state.positionsAreCurrent = false;
3322
3361
  state.indexByKey.clear();
3323
3362
  state.idCache.length = 0;
3324
3363
  state.positions.length = 0;
@@ -3599,7 +3638,7 @@ function updateItemPositions(ctx, dataChanged, {
3599
3638
  scrollBottomBuffered: -1,
3600
3639
  startIndex: 0
3601
3640
  }) {
3602
- var _a3, _b, _c, _d, _e;
3641
+ var _a3, _b, _c, _d, _e, _f;
3603
3642
  const state = ctx.state;
3604
3643
  const hasPositionListeners = ctx.positionListeners.size > 0;
3605
3644
  const {
@@ -3615,6 +3654,11 @@ function updateItemPositions(ctx, dataChanged, {
3615
3654
  const dataLength = data.length;
3616
3655
  const numColumns = (_a3 = peek$(ctx, "numColumns")) != null ? _a3 : 1;
3617
3656
  const hasColumns = numColumns > 1;
3657
+ const canReuseEstimates = !doMVCP || !state.didContainersLayout && Object.keys(state.averageSizes).length === 0;
3658
+ if (state.positionsAreCurrent && !dataChanged && !forceFullUpdate && state.positionRecalculationStartIndex === void 0 && !scrollingTo && !hasColumns && !snapToIndices && positions.length === dataLength && canReuseEstimates) {
3659
+ return;
3660
+ }
3661
+ state.positionsAreCurrent = false;
3618
3662
  const indexByKeyForChecking = IS_DEV ? /* @__PURE__ */ new Map() : void 0;
3619
3663
  const extraData = peek$(ctx, "extraData");
3620
3664
  const layoutConfig = overrideItemLayout ? { span: 1 } : void 0;
@@ -3729,6 +3773,10 @@ function updateItemPositions(ctx, dataChanged, {
3729
3773
  }
3730
3774
  if (!didBreakEarly) {
3731
3775
  updateTotalSize(ctx);
3776
+ state.positionsAreCurrent = !hasColumns;
3777
+ if (startIndex <= ((_f = state.positionRecalculationStartIndex) != null ? _f : -1)) {
3778
+ state.positionRecalculationStartIndex = void 0;
3779
+ }
3732
3780
  }
3733
3781
  if (snapToIndices) {
3734
3782
  updateSnapToOffsets(ctx);
@@ -4291,7 +4339,7 @@ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentSt
4291
4339
  }
4292
4340
  function trackVisibleRange(range, i, top, size, scroll, scrollBottom) {
4293
4341
  let didPassVisibleEnd = false;
4294
- if (range.startNoBuffer === null && top + size > scroll) {
4342
+ if (range.startNoBuffer === null && top + size > scroll && top <= scrollBottom) {
4295
4343
  range.startNoBuffer = i;
4296
4344
  }
4297
4345
  if (range.firstFullyOnScreenIndex === void 0 && top >= scroll - 10 && top <= scrollBottom) {
@@ -4357,7 +4405,7 @@ function findFirstVisibleIndexInCachedRange(ctx, scroll) {
4357
4405
  return null;
4358
4406
  }
4359
4407
  function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, scrollLength, scroll, scrollBottom) {
4360
- var _a3, _b;
4408
+ var _a3, _b, _c, _d;
4361
4409
  const state = ctx.state;
4362
4410
  const {
4363
4411
  endBuffered,
@@ -4391,14 +4439,14 @@ function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, sc
4391
4439
  startNoBuffer: visibleRange.startNoBuffer
4392
4440
  });
4393
4441
  maybeEmitFirstVisibleItemChanged(state, visibleRange.startNoBuffer);
4394
- if (visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null) {
4442
+ if (state.props.hasExternalScroll || visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null) {
4395
4443
  updateViewableItems(
4396
4444
  state,
4397
4445
  ctx,
4398
4446
  viewabilityConfigCallbackPairs,
4399
4447
  scrollLength,
4400
- visibleRange.startNoBuffer,
4401
- visibleRange.endNoBuffer,
4448
+ (_c = visibleRange.startNoBuffer) != null ? _c : 0,
4449
+ (_d = visibleRange.endNoBuffer) != null ? _d : -1,
4402
4450
  startBuffered,
4403
4451
  endBuffered
4404
4452
  );
@@ -4407,7 +4455,7 @@ function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, sc
4407
4455
  function calculateItemsInView(ctx, params = {}) {
4408
4456
  const state = ctx.state;
4409
4457
  batchedUpdates(() => {
4410
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u;
4458
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y;
4411
4459
  const {
4412
4460
  columns,
4413
4461
  containerItemKeys,
@@ -4459,7 +4507,7 @@ function calculateItemsInView(ctx, params = {}) {
4459
4507
  scrollAdjustPending = (_a4 = peek$(ctx, "scrollAdjustPending")) != null ? _a4 : 0;
4460
4508
  scrollAdjustPad = scrollAdjustPending - topPad;
4461
4509
  scroll = Math.round(nextScrollState + scrollExtra + scrollAdjustPad);
4462
- if (scroll + scrollLength > totalSize) {
4510
+ if (!state.props.hasExternalScroll && scroll + scrollLength > totalSize) {
4463
4511
  scroll = Math.max(0, totalSize - scrollLength);
4464
4512
  }
4465
4513
  };
@@ -4486,7 +4534,11 @@ function calculateItemsInView(ctx, params = {}) {
4486
4534
  let stickyState = dataChanged ? void 0 : resolveStickyState();
4487
4535
  let scrollBufferTop = drawDistance;
4488
4536
  let scrollBufferBottom = drawDistance;
4489
- if (speed > 0 || speed === 0 && scroll < Math.max(50, drawDistance)) {
4537
+ if (speed !== 0) {
4538
+ state.scrollBufferDirection = speed > 0 ? 1 : -1;
4539
+ }
4540
+ const bufferForward = state.scrollBufferDirection === 1 || state.scrollBufferDirection === void 0 && scroll < Math.max(50, drawDistance);
4541
+ if (bufferForward) {
4490
4542
  scrollBufferTop = drawDistance * 0.5;
4491
4543
  scrollBufferBottom = drawDistance * 1.5;
4492
4544
  } else {
@@ -4496,8 +4548,8 @@ function calculateItemsInView(ctx, params = {}) {
4496
4548
  const shouldProjectRenderRange = !dataChanged && !forceFullItemPositions && !suppressInitialScrollSideEffects && !hasActiveInitialScroll(state) && !state.scrollingTo && !state.pendingNativeMVCPAdjust && !!peek$(ctx, "readyToRender");
4497
4549
  const projectedBufferAdjustment = shouldProjectRenderRange ? getProjectedBufferAdjustment(speed, Math.min(scrollBufferTop, scrollBufferBottom)) : 0;
4498
4550
  const updateScrollRange = () => {
4499
- const scrollStart = Math.max(0, scroll);
4500
- const overscrollBeforeContent = Math.max(0, -nativeScrollState);
4551
+ const scrollStart = state.props.hasExternalScroll ? scroll : Math.max(0, scroll);
4552
+ const overscrollBeforeContent = state.props.hasExternalScroll ? 0 : Math.max(0, -nativeScrollState);
4501
4553
  scrollBottom = Math.max(scrollStart, scroll + scrollLength + overscrollBeforeContent);
4502
4554
  scrollTopBuffered = scrollStart - scrollBufferTop + projectedBufferAdjustment;
4503
4555
  scrollBottomBuffered = scrollBottom + scrollBufferBottom + projectedBufferAdjustment;
@@ -4626,7 +4678,7 @@ function calculateItemsInView(ctx, params = {}) {
4626
4678
  nextTop = top;
4627
4679
  }
4628
4680
  }
4629
- if (visibleRange.startNoBuffer !== null) {
4681
+ if (startBuffered !== null) {
4630
4682
  if (top <= scrollBottomBuffered) {
4631
4683
  endBuffered = i;
4632
4684
  if (scrollBottomBuffered > totalSize) {
@@ -4637,6 +4689,8 @@ function calculateItemsInView(ctx, params = {}) {
4637
4689
  } else {
4638
4690
  foundEnd = true;
4639
4691
  }
4692
+ } else if (top > scrollBottomBuffered) {
4693
+ foundEnd = true;
4640
4694
  }
4641
4695
  }
4642
4696
  }
@@ -4654,6 +4708,12 @@ function calculateItemsInView(ctx, params = {}) {
4654
4708
  bottom: nextBottom,
4655
4709
  top: nextTop
4656
4710
  };
4711
+ } else if (enableScrollForNextCalculateItemsInView && state.props.hasExternalScroll) {
4712
+ if (scrollBottomBuffered < 0) {
4713
+ state.scrollForNextCalculateItemsInView = { bottom: 0, top: null };
4714
+ } else if (scrollTopBuffered > totalSize) {
4715
+ state.scrollForNextCalculateItemsInView = { bottom: null, top: totalSize };
4716
+ }
4657
4717
  }
4658
4718
  let numContainers = prevNumContainers;
4659
4719
  const pendingRemoval = [];
@@ -4677,7 +4737,9 @@ function calculateItemsInView(ctx, params = {}) {
4677
4737
  }
4678
4738
  const hasScrollTargetPinnedRange = scrollTargetPinnedStart <= scrollTargetPinnedEnd;
4679
4739
  const isPinnedRenderIndex = (index) => alwaysRenderIndicesSet.has(index) || hasScrollTargetPinnedRange && index >= scrollTargetPinnedStart && index <= scrollTargetPinnedEnd;
4680
- if (startBuffered !== null && endBuffered !== null) {
4740
+ if (startBuffered !== null && endBuffered !== null || alwaysRenderIndicesArr.length || hasScrollTargetPinnedRange) {
4741
+ const allocationStart = startBuffered != null ? startBuffered : 0;
4742
+ const allocationEnd = endBuffered != null ? endBuffered : -1;
4681
4743
  const needNewContainers = [];
4682
4744
  const needNewContainersSet = /* @__PURE__ */ new Set();
4683
4745
  const addPinnedIndex = (index) => {
@@ -4693,7 +4755,7 @@ function calculateItemsInView(ctx, params = {}) {
4693
4755
  }
4694
4756
  }
4695
4757
  };
4696
- for (let i = startBuffered; i <= endBuffered; i++) {
4758
+ for (let i = allocationStart; i <= allocationEnd; i++) {
4697
4759
  const id = (_m = idCache[i]) != null ? _m : getId(state, i);
4698
4760
  if (!containerItemKeys.has(id)) {
4699
4761
  needNewContainersSet.add(i);
@@ -4715,8 +4777,8 @@ function calculateItemsInView(ctx, params = {}) {
4715
4777
  (_n = stickyState == null ? void 0 : stickyState.currentStickyIdx) != null ? _n : -1,
4716
4778
  needNewContainers,
4717
4779
  needNewContainersSet,
4718
- startBuffered,
4719
- endBuffered
4780
+ allocationStart,
4781
+ allocationEnd
4720
4782
  );
4721
4783
  } else if (previousStickyIndex !== -1) {
4722
4784
  set$(ctx, "activeStickyIndex", -1);
@@ -4725,8 +4787,8 @@ function calculateItemsInView(ctx, params = {}) {
4725
4787
  const availableContainerAllocations = findAvailableContainers(
4726
4788
  ctx,
4727
4789
  needNewContainers,
4728
- startBuffered,
4729
- endBuffered,
4790
+ allocationStart,
4791
+ allocationEnd,
4730
4792
  pendingRemoval,
4731
4793
  protectedContainerKeys,
4732
4794
  // Initial end alignment has no scroll velocity yet. Keep its
@@ -4853,19 +4915,19 @@ function calculateItemsInView(ctx, params = {}) {
4853
4915
  handleInitialScrollLayoutReady(ctx);
4854
4916
  }
4855
4917
  }
4856
- if (viewabilityConfigCallbackPairs && visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null) {
4918
+ if (viewabilityConfigCallbackPairs && (state.props.hasExternalScroll || visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null)) {
4857
4919
  updateViewableItems(
4858
4920
  ctx.state,
4859
4921
  ctx,
4860
4922
  viewabilityConfigCallbackPairs,
4861
4923
  scrollLength,
4862
- visibleRange.startNoBuffer,
4863
- visibleRange.endNoBuffer,
4864
- startBuffered != null ? startBuffered : visibleRange.startNoBuffer,
4865
- endBuffered != null ? endBuffered : visibleRange.endNoBuffer
4924
+ (_u = visibleRange.startNoBuffer) != null ? _u : 0,
4925
+ (_v = visibleRange.endNoBuffer) != null ? _v : -1,
4926
+ (_w = startBuffered != null ? startBuffered : visibleRange.startNoBuffer) != null ? _w : 0,
4927
+ (_x = endBuffered != null ? endBuffered : visibleRange.endNoBuffer) != null ? _x : -1
4866
4928
  );
4867
4929
  }
4868
- (_u = stickyState == null ? void 0 : stickyState.finishCalculateItemsInView) == null ? void 0 : _u.call(stickyState);
4930
+ (_y = stickyState == null ? void 0 : stickyState.finishCalculateItemsInView) == null ? void 0 : _y.call(stickyState);
4869
4931
  });
4870
4932
  }
4871
4933
 
@@ -5160,6 +5222,7 @@ function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem, notif
5160
5222
  return 0;
5161
5223
  }
5162
5224
  const size = Platform.OS === "web" ? Math.round(rawSize) : roundSize(rawSize);
5225
+ if (prevSizeKnown !== size) state.positionsAreCurrent = false;
5163
5226
  sizesKnown.set(itemKey, size);
5164
5227
  if (fixedItemSize === void 0 && size > 0) {
5165
5228
  itemType != null ? itemType : itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
@@ -6260,6 +6323,7 @@ var ListComponent = typedMemo(function ListComponent2({
6260
6323
  stickyHeaderConfig,
6261
6324
  stickyHeaderIndices,
6262
6325
  useWindowScroll = false,
6326
+ scrollElement,
6263
6327
  ...rest
6264
6328
  }) {
6265
6329
  const ctx = useStateContext();
@@ -6325,7 +6389,7 @@ var ListComponent = typedMemo(function ListComponent2({
6325
6389
  SnapOrScroll,
6326
6390
  {
6327
6391
  ...rest,
6328
- ...Platform.OS === "web" ? ScrollComponent === ListComponentScrollView ? { onInternalScrollEnd, useWindowScroll } : {} : { onScrollBeginDrag: onInternalScrollBeginDrag, onScrollEndDrag: onInternalScrollEndDrag },
6392
+ ...Platform.OS === "web" ? ScrollComponent === ListComponentScrollView ? { onInternalScrollEnd, scrollElement, useWindowScroll } : {} : { onScrollBeginDrag: onInternalScrollBeginDrag, onScrollEndDrag: onInternalScrollEndDrag },
6329
6393
  contentContainerStyle: [
6330
6394
  horizontal ? { height: "100%" } : {},
6331
6395
  contentContainerStyle,
@@ -6365,7 +6429,15 @@ var WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO = 0.9;
6365
6429
  var WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO = 0.9;
6366
6430
  function useDevChecksImpl(props) {
6367
6431
  const ctx = useStateContext();
6368
- const { anchoredEndSpace, childrenMode, keyExtractor, numColumns, renderScrollComponent, useWindowScroll } = props;
6432
+ const {
6433
+ anchoredEndSpace,
6434
+ childrenMode,
6435
+ keyExtractor,
6436
+ numColumns,
6437
+ renderScrollComponent,
6438
+ scrollElement,
6439
+ useWindowScroll
6440
+ } = props;
6369
6441
  const hasAnchoredEndSpace = !!anchoredEndSpace;
6370
6442
  React2.useEffect(() => {
6371
6443
  if (hasAnchoredEndSpace && (numColumns != null ? numColumns : 1) > 1) {
@@ -6376,13 +6448,13 @@ function useDevChecksImpl(props) {
6376
6448
  }
6377
6449
  }, [hasAnchoredEndSpace, numColumns]);
6378
6450
  React2.useEffect(() => {
6379
- if (useWindowScroll && renderScrollComponent) {
6451
+ if ((useWindowScroll || scrollElement !== void 0) && renderScrollComponent) {
6380
6452
  warnDevOnce(
6381
6453
  "useWindowScrollRenderScrollComponent",
6382
- "useWindowScroll is not supported when renderScrollComponent is provided."
6454
+ "useWindowScroll and scrollElement are not supported when renderScrollComponent is provided."
6383
6455
  );
6384
6456
  }
6385
- }, [renderScrollComponent, useWindowScroll]);
6457
+ }, [renderScrollComponent, scrollElement, useWindowScroll]);
6386
6458
  React2.useEffect(() => {
6387
6459
  if (!keyExtractor && !ctx.state.isFirst && ctx.state.didDataChange && !childrenMode) {
6388
6460
  warnDevOnce(
@@ -6394,8 +6466,8 @@ function useDevChecksImpl(props) {
6394
6466
  React2.useEffect(() => {
6395
6467
  const state = ctx.state;
6396
6468
  const dataLength = state.props.data.length;
6397
- const useWindowScrollResolved = state.props.useWindowScroll;
6398
- if (Platform.OS !== "web" || useWindowScrollResolved || dataLength < WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH) {
6469
+ const usesExternalScroll = state.props.hasExternalScroll;
6470
+ if (Platform.OS !== "web" || usesExternalScroll || dataLength < WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH) {
6399
6471
  return;
6400
6472
  }
6401
6473
  const warnIfUnboundedOuterSize = () => {
@@ -6559,7 +6631,8 @@ function doInitialAllocateContainers(ctx) {
6559
6631
  calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
6560
6632
  });
6561
6633
  } else {
6562
- calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
6634
+ const reuseInitialPositions = !state.didLoad && state.positionsAreCurrent && !state.didDataChange && !state.didColumnsChange;
6635
+ calculateItemsInView(ctx, { dataChanged: !reuseInitialPositions, doMVCP: true });
6563
6636
  }
6564
6637
  }
6565
6638
  return true;
@@ -6598,12 +6671,14 @@ function handleLayout(ctx, layoutParam, setCanRender) {
6598
6671
  updateContentMetricsState(ctx);
6599
6672
  state.lastBatchingAction = Date.now();
6600
6673
  state.scrollForNextCalculateItemsInView = void 0;
6601
- if (scrollLength > 0) {
6602
- doInitialAllocateContainers(ctx);
6603
- }
6604
- if (needsCalculate) {
6674
+ const didAllocate = scrollLength > 0 && doInitialAllocateContainers(ctx);
6675
+ if (needsCalculate && (!didAllocate || state.initialScroll)) {
6605
6676
  calculateItemsInView(ctx, { doMVCP: true });
6606
6677
  }
6678
+ const scrollTarget = state.scrollingTo;
6679
+ if (scrollLength !== previousLength && (scrollTarget == null ? void 0 : scrollTarget.viewPositionFallback) && !scrollTarget.isInitialScroll && scrollTarget.index !== void 0) {
6680
+ scrollToIndex(ctx, { ...scrollTarget, forceScroll: true, index: scrollTarget.index });
6681
+ }
6607
6682
  if (didChange || otherAxisSize !== prevOtherAxisSize) {
6608
6683
  set$(ctx, "scrollSize", { height: layout.height, width: layout.width });
6609
6684
  }
@@ -6683,7 +6758,7 @@ function onScroll(ctx, event) {
6683
6758
  }
6684
6759
  state.lastNativeScroll = newScroll;
6685
6760
  state.lastNativeScrollTime = Date.now();
6686
- if (state.scrollingTo && state.scrollingTo.offset >= newScroll) {
6761
+ if (!state.props.hasExternalScroll && state.scrollingTo && state.scrollingTo.offset >= newScroll) {
6687
6762
  const maxOffset = clampScrollOffset(ctx, newScroll, state.scrollingTo);
6688
6763
  if (newScroll !== maxOffset && Math.abs(newScroll - maxOffset) > 1) {
6689
6764
  newScroll = maxOffset;
@@ -7466,6 +7541,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7466
7541
  stickyHeaderIndices: stickyHeaderIndicesProp,
7467
7542
  style: styleProp,
7468
7543
  useWindowScroll = false,
7544
+ scrollElement,
7469
7545
  viewabilityConfig,
7470
7546
  viewabilityConfigCallbackPairs,
7471
7547
  ...rest
@@ -7521,9 +7597,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7521
7597
  viewPosition: 1
7522
7598
  } : hasInitialScrollIndex ? typeof initialScrollIndexProp === "object" ? {
7523
7599
  index: (_b = initialScrollIndexProp.index) != null ? _b : 0,
7524
- preserveForBottomPadding: initialScrollIndexProp.viewOffset === void 0 && initialScrollIndexProp.viewPosition === 1 ? true : void 0,
7525
- viewOffset: (_c = initialScrollIndexProp.viewOffset) != null ? _c : initialScrollIndexProp.viewPosition === 1 ? getEndAlignedViewOffset(stylePaddingEndState) : 0,
7526
- viewPosition: (_d = initialScrollIndexProp.viewPosition) != null ? _d : 0
7600
+ preserveForBottomPadding: initialScrollIndexProp.viewOffset === void 0 && initialScrollIndexProp.viewPosition === 1 && initialScrollIndexProp.viewPositionFallback === void 0 ? true : void 0,
7601
+ viewOffset: (_c = initialScrollIndexProp.viewOffset) != null ? _c : initialScrollIndexProp.viewPosition === 1 && initialScrollIndexProp.viewPositionFallback === void 0 ? getEndAlignedViewOffset(stylePaddingEndState) : 0,
7602
+ viewPosition: (_d = initialScrollIndexProp.viewPosition) != null ? _d : 0,
7603
+ viewPositionFallback: initialScrollIndexProp.viewPositionFallback
7527
7604
  } : {
7528
7605
  index: initialScrollIndexProp != null ? initialScrollIndexProp : 0,
7529
7606
  preserveForBottomPadding: shouldBottomAlignNumericInitialScrollIndex ? true : void 0,
@@ -7560,7 +7637,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7560
7637
  dataVersion,
7561
7638
  keyExtractor
7562
7639
  ]);
7563
- const useWindowScrollResolved = Platform.OS === "web" && !!useWindowScroll && !renderScrollComponent;
7640
+ const scrollElementResolved = Platform.OS === "web" && !renderScrollComponent ? scrollElement : void 0;
7641
+ const useWindowScrollResolved = Platform.OS === "web" && !!useWindowScroll && scrollElementResolved === void 0 && !renderScrollComponent;
7642
+ const hasExternalScroll = useWindowScrollResolved || scrollElementResolved !== void 0;
7564
7643
  const refState = React2.useRef(void 0);
7565
7644
  const hasOverrideItemLayout = !!overrideItemLayout;
7566
7645
  const prevHasOverrideItemLayout = React2.useRef(hasOverrideItemLayout);
@@ -7698,6 +7777,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7698
7777
  estimatedItemSize,
7699
7778
  getFixedItemSize: useWrapIfItem(getFixedItemSize),
7700
7779
  getItemType: useWrapIfItem(getItemType),
7780
+ hasExternalScroll,
7701
7781
  hideItemsUntilMeasured: experimental_hideItemsUntilMeasured,
7702
7782
  horizontal: !!horizontal,
7703
7783
  itemsAreEqual,
@@ -7894,9 +7974,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7894
7974
  const { onLayout } = useOnLayoutSync({
7895
7975
  onLayoutChange,
7896
7976
  onLayoutProp,
7897
- ref: refScroller
7898
- // the type of ScrollView doesn't include measure?
7899
- });
7977
+ ref: refScroller});
7900
7978
  React2.useLayoutEffect(() => {
7901
7979
  if (snapToIndices) {
7902
7980
  updateSnapToOffsets(ctx);
@@ -8064,6 +8142,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8064
8142
  refScrollView: combinedRef,
8065
8143
  renderScrollComponent,
8066
8144
  scrollAdjustHandler: (_s = refState.current) == null ? void 0 : _s.scrollAdjustHandler,
8145
+ scrollElement: scrollElementResolved,
8067
8146
  scrollEventThrottle: 0,
8068
8147
  snapToIndices,
8069
8148
  stickyHeaderIndices,