@legendapp/list 3.4.0 → 3.5.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.mjs CHANGED
@@ -914,8 +914,14 @@ function scheduleFullDrawDistancePrewarm(ctx) {
914
914
  return;
915
915
  }
916
916
  state.scheduledWork.frame(() => {
917
- var _a3;
918
- return (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
917
+ var _a3, _b, _c;
918
+ if (state.props.hasExternalScroll && !hasActiveInitialScroll(state) && !state.scrollingTo && !state.pendingNativeMVCPAdjust) {
919
+ const offset = (_b = (_a3 = state.refScroller.current) == null ? void 0 : _a3.getRawScrollOffset) == null ? void 0 : _b.call(_a3);
920
+ if (offset !== void 0) {
921
+ state.scroll = state.scrollPending = state.props.horizontal ? toLogicalHorizontalOffset(state, offset, state.totalSize) : offset;
922
+ }
923
+ }
924
+ (_c = state.triggerCalculateItemsInView) == null ? void 0 : _c.call(state);
919
925
  }, "fullDrawDistancePrewarm");
920
926
  }
921
927
 
@@ -928,6 +934,9 @@ function resetInitialRenderState(ctx, {
928
934
  if (resetLayout) {
929
935
  state.didContainersLayout = false;
930
936
  state.queuedInitialLayout = false;
937
+ state.scrollBufferDirection = void 0;
938
+ state.scrollHistory.length = 0;
939
+ state.scheduledWork.cancel("renderRangeProjection");
931
940
  state.edgeReachedGate = void 0;
932
941
  state.isStartReached = null;
933
942
  state.isEndReached = null;
@@ -1156,6 +1165,7 @@ function setSize(ctx, itemKey, size, notifyTotalSize = true) {
1156
1165
  const previousSize = sizes.get(itemKey);
1157
1166
  const diff = previousSize !== void 0 ? size - previousSize : size;
1158
1167
  if (diff !== 0) {
1168
+ state.positionsAreCurrent = false;
1159
1169
  addTotalSize(ctx, itemKey, diff, notifyTotalSize);
1160
1170
  }
1161
1171
  sizes.set(itemKey, size);
@@ -1216,6 +1226,7 @@ function getKnownOrFixedSize(ctx, key, index, data, resolved) {
1216
1226
  const fixedSize = (resolved == null ? void 0 : resolved.didResolveFixedItemSize) ? resolved.fixedItemSize : getFixedItemSize(data, index, itemType);
1217
1227
  if (fixedSize !== void 0) {
1218
1228
  size = fixedSize + ctx.scrollAxisGap;
1229
+ state.positionsAreCurrent = false;
1219
1230
  state.sizesKnown.set(key, size);
1220
1231
  }
1221
1232
  }
@@ -1829,6 +1840,9 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
1829
1840
  state.scrollTime = currentTime;
1830
1841
  const scrollDelta = Math.abs(newScroll - prevScroll);
1831
1842
  const isUserScrollEvent = !!(options == null ? void 0 : options.fromNativeScrollEvent) && scrollDelta > 0.1 && !adjustChanged && scrollingTo === void 0 && !state.pendingNativeMVCPAdjust;
1843
+ if (isUserScrollEvent) {
1844
+ state.scrollBufferDirection = newScroll > prevScroll ? 1 : -1;
1845
+ }
1832
1846
  const didCancelMaintainScrollAtEnd = Platform.OS === "web" && isUserScrollEvent && newScroll < prevScroll && !!(state.maintainingScrollAtEnd || state.pendingMaintainScrollAtEnd);
1833
1847
  if (didCancelMaintainScrollAtEnd) {
1834
1848
  finishMaintainScrollAtEnd(ctx);
@@ -1908,10 +1922,10 @@ function finishMaintainScrollAtEnd(ctx) {
1908
1922
  }
1909
1923
  }
1910
1924
  function doMaintainScrollAtEnd(ctx, options) {
1911
- var _a3, _b, _c, _d, _e, _f;
1925
+ var _a3, _b, _c, _d, _e, _f, _g, _h;
1912
1926
  const state = ctx.state;
1913
1927
  let didMaintain = false;
1914
- if (state.isDragging) {
1928
+ if (state.isDragging || ((_b = (_a3 = state.refScroller.current) == null ? void 0 : _a3.isScrollInRange) == null ? void 0 : _b.call(_a3)) === false) {
1915
1929
  finishMaintainScrollAtEnd(ctx);
1916
1930
  } else {
1917
1931
  const {
@@ -1923,11 +1937,11 @@ function doMaintainScrollAtEnd(ctx, options) {
1923
1937
  const immediate = (options == null ? void 0 : options.immediate) && Platform.OS === "web" && !(maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.animated);
1924
1938
  const isWithinMaintainScrollAtEndThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
1925
1939
  const isReplayingPendingMaintain = !!state.pendingMaintainScrollAtEnd;
1926
- 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);
1940
+ 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);
1927
1941
  if (shouldMaintainScrollAtEnd && !state.scrollingTo) {
1928
1942
  const scroller = state.refScroller.current;
1929
- const nativeMax = (_c = scroller == null ? void 0 : scroller.getMaxScrollOffset) == null ? void 0 : _c.call(scroller);
1930
- const observed = (_d = scroller == null ? void 0 : scroller.getCurrentScrollOffset) == null ? void 0 : _d.call(scroller);
1943
+ const nativeMax = (_e = scroller == null ? void 0 : scroller.getMaxScrollOffset) == null ? void 0 : _e.call(scroller);
1944
+ const observed = (_f = scroller == null ? void 0 : scroller.getCurrentScrollOffset) == null ? void 0 : _f.call(scroller);
1931
1945
  if (nativeMax !== void 0 && observed !== void 0 && nativeMax < state.scroll - 1 && Math.abs(observed - nativeMax) <= 1) {
1932
1946
  state.pendingMaintainScrollAtEnd = true;
1933
1947
  state.scrollPending = observed;
@@ -1939,13 +1953,13 @@ function doMaintainScrollAtEnd(ctx, options) {
1939
1953
  } else if (pendingNativeMVCPAdjust) {
1940
1954
  state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
1941
1955
  } else if (shouldMaintainScrollAtEnd && (state.scrollingTo || state.pendingScrollResolve)) {
1942
- if (immediate && ((_e = state.scrollingTo) == null ? void 0 : _e.isScrollToEnd) && !state.scrollingTo.animated) {
1956
+ if (immediate && ((_g = state.scrollingTo) == null ? void 0 : _g.isScrollToEnd) && !state.scrollingTo.animated) {
1943
1957
  state.pendingMaintainScrollAtEnd = false;
1944
1958
  ctx.scrollToEnd({ animated: false });
1945
1959
  didMaintain = true;
1946
1960
  } else {
1947
1961
  state.pendingMaintainScrollAtEnd = true;
1948
- (_f = state.maintainingScrollAtEnd) != null ? _f : state.maintainingScrollAtEnd = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
1962
+ (_h = state.maintainingScrollAtEnd) != null ? _h : state.maintainingScrollAtEnd = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
1949
1963
  }
1950
1964
  } else if (shouldMaintainScrollAtEnd && didContainersLayout) {
1951
1965
  state.pendingMaintainScrollAtEnd = false;
@@ -1959,8 +1973,9 @@ function doMaintainScrollAtEnd(ctx, options) {
1959
1973
  const scrollAtRequest = state.scroll;
1960
1974
  state.maintainingScrollAtEnd = pendingState;
1961
1975
  const run = () => {
1976
+ var _a4, _b2;
1962
1977
  if (state.maintainingScrollAtEnd === pendingState) {
1963
- if (state.isDragging) {
1978
+ if (state.isDragging || ((_b2 = (_a4 = state.refScroller.current) == null ? void 0 : _a4.isScrollInRange) == null ? void 0 : _b2.call(_a4)) === false) {
1964
1979
  finishMaintainScrollAtEnd(ctx);
1965
1980
  } else {
1966
1981
  const isStillWithinThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
@@ -3298,6 +3313,7 @@ function handleInitialScrollDataChange(ctx, options) {
3298
3313
 
3299
3314
  // src/core/resetLayoutCachesForDataChange.ts
3300
3315
  function resetLayoutCachesForDataChange(state) {
3316
+ state.positionsAreCurrent = false;
3301
3317
  state.indexByKey.clear();
3302
3318
  state.idCache.length = 0;
3303
3319
  state.positions.length = 0;
@@ -3578,7 +3594,7 @@ function updateItemPositions(ctx, dataChanged, {
3578
3594
  scrollBottomBuffered: -1,
3579
3595
  startIndex: 0
3580
3596
  }) {
3581
- var _a3, _b, _c, _d, _e;
3597
+ var _a3, _b, _c, _d, _e, _f;
3582
3598
  const state = ctx.state;
3583
3599
  const hasPositionListeners = ctx.positionListeners.size > 0;
3584
3600
  const {
@@ -3594,6 +3610,11 @@ function updateItemPositions(ctx, dataChanged, {
3594
3610
  const dataLength = data.length;
3595
3611
  const numColumns = (_a3 = peek$(ctx, "numColumns")) != null ? _a3 : 1;
3596
3612
  const hasColumns = numColumns > 1;
3613
+ const canReuseEstimates = !doMVCP || !state.didContainersLayout && Object.keys(state.averageSizes).length === 0;
3614
+ if (state.positionsAreCurrent && !dataChanged && !forceFullUpdate && state.positionRecalculationStartIndex === void 0 && !scrollingTo && !hasColumns && !snapToIndices && positions.length === dataLength && canReuseEstimates) {
3615
+ return;
3616
+ }
3617
+ state.positionsAreCurrent = false;
3597
3618
  const indexByKeyForChecking = IS_DEV ? /* @__PURE__ */ new Map() : void 0;
3598
3619
  const extraData = peek$(ctx, "extraData");
3599
3620
  const layoutConfig = overrideItemLayout ? { span: 1 } : void 0;
@@ -3708,6 +3729,10 @@ function updateItemPositions(ctx, dataChanged, {
3708
3729
  }
3709
3730
  if (!didBreakEarly) {
3710
3731
  updateTotalSize(ctx);
3732
+ state.positionsAreCurrent = !hasColumns;
3733
+ if (startIndex <= ((_f = state.positionRecalculationStartIndex) != null ? _f : -1)) {
3734
+ state.positionRecalculationStartIndex = void 0;
3735
+ }
3711
3736
  }
3712
3737
  if (snapToIndices) {
3713
3738
  updateSnapToOffsets(ctx);
@@ -4270,7 +4295,7 @@ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentSt
4270
4295
  }
4271
4296
  function trackVisibleRange(range, i, top, size, scroll, scrollBottom) {
4272
4297
  let didPassVisibleEnd = false;
4273
- if (range.startNoBuffer === null && top + size > scroll) {
4298
+ if (range.startNoBuffer === null && top + size > scroll && top <= scrollBottom) {
4274
4299
  range.startNoBuffer = i;
4275
4300
  }
4276
4301
  if (range.firstFullyOnScreenIndex === void 0 && top >= scroll - 10 && top <= scrollBottom) {
@@ -4336,7 +4361,7 @@ function findFirstVisibleIndexInCachedRange(ctx, scroll) {
4336
4361
  return null;
4337
4362
  }
4338
4363
  function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, scrollLength, scroll, scrollBottom) {
4339
- var _a3, _b;
4364
+ var _a3, _b, _c, _d;
4340
4365
  const state = ctx.state;
4341
4366
  const {
4342
4367
  endBuffered,
@@ -4370,14 +4395,14 @@ function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, sc
4370
4395
  startNoBuffer: visibleRange.startNoBuffer
4371
4396
  });
4372
4397
  maybeEmitFirstVisibleItemChanged(state, visibleRange.startNoBuffer);
4373
- if (visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null) {
4398
+ if (state.props.hasExternalScroll || visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null) {
4374
4399
  updateViewableItems(
4375
4400
  state,
4376
4401
  ctx,
4377
4402
  viewabilityConfigCallbackPairs,
4378
4403
  scrollLength,
4379
- visibleRange.startNoBuffer,
4380
- visibleRange.endNoBuffer,
4404
+ (_c = visibleRange.startNoBuffer) != null ? _c : 0,
4405
+ (_d = visibleRange.endNoBuffer) != null ? _d : -1,
4381
4406
  startBuffered,
4382
4407
  endBuffered
4383
4408
  );
@@ -4386,7 +4411,7 @@ function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, sc
4386
4411
  function calculateItemsInView(ctx, params = {}) {
4387
4412
  const state = ctx.state;
4388
4413
  batchedUpdates(() => {
4389
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u;
4414
+ 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;
4390
4415
  const {
4391
4416
  columns,
4392
4417
  containerItemKeys,
@@ -4438,7 +4463,7 @@ function calculateItemsInView(ctx, params = {}) {
4438
4463
  scrollAdjustPending = (_a4 = peek$(ctx, "scrollAdjustPending")) != null ? _a4 : 0;
4439
4464
  scrollAdjustPad = scrollAdjustPending - topPad;
4440
4465
  scroll = Math.round(nextScrollState + scrollExtra + scrollAdjustPad);
4441
- if (scroll + scrollLength > totalSize) {
4466
+ if (!state.props.hasExternalScroll && scroll + scrollLength > totalSize) {
4442
4467
  scroll = Math.max(0, totalSize - scrollLength);
4443
4468
  }
4444
4469
  };
@@ -4465,7 +4490,11 @@ function calculateItemsInView(ctx, params = {}) {
4465
4490
  let stickyState = dataChanged ? void 0 : resolveStickyState();
4466
4491
  let scrollBufferTop = drawDistance;
4467
4492
  let scrollBufferBottom = drawDistance;
4468
- if (speed > 0 || speed === 0 && scroll < Math.max(50, drawDistance)) {
4493
+ if (speed !== 0) {
4494
+ state.scrollBufferDirection = speed > 0 ? 1 : -1;
4495
+ }
4496
+ const bufferForward = state.scrollBufferDirection === 1 || state.scrollBufferDirection === void 0 && scroll < Math.max(50, drawDistance);
4497
+ if (bufferForward) {
4469
4498
  scrollBufferTop = drawDistance * 0.5;
4470
4499
  scrollBufferBottom = drawDistance * 1.5;
4471
4500
  } else {
@@ -4475,8 +4504,8 @@ function calculateItemsInView(ctx, params = {}) {
4475
4504
  const shouldProjectRenderRange = !dataChanged && !forceFullItemPositions && !suppressInitialScrollSideEffects && !hasActiveInitialScroll(state) && !state.scrollingTo && !state.pendingNativeMVCPAdjust && !!peek$(ctx, "readyToRender");
4476
4505
  const projectedBufferAdjustment = shouldProjectRenderRange ? getProjectedBufferAdjustment(speed, Math.min(scrollBufferTop, scrollBufferBottom)) : 0;
4477
4506
  const updateScrollRange = () => {
4478
- const scrollStart = Math.max(0, scroll);
4479
- const overscrollBeforeContent = Math.max(0, -nativeScrollState);
4507
+ const scrollStart = state.props.hasExternalScroll ? scroll : Math.max(0, scroll);
4508
+ const overscrollBeforeContent = state.props.hasExternalScroll ? 0 : Math.max(0, -nativeScrollState);
4480
4509
  scrollBottom = Math.max(scrollStart, scroll + scrollLength + overscrollBeforeContent);
4481
4510
  scrollTopBuffered = scrollStart - scrollBufferTop + projectedBufferAdjustment;
4482
4511
  scrollBottomBuffered = scrollBottom + scrollBufferBottom + projectedBufferAdjustment;
@@ -4605,7 +4634,7 @@ function calculateItemsInView(ctx, params = {}) {
4605
4634
  nextTop = top;
4606
4635
  }
4607
4636
  }
4608
- if (visibleRange.startNoBuffer !== null) {
4637
+ if (startBuffered !== null) {
4609
4638
  if (top <= scrollBottomBuffered) {
4610
4639
  endBuffered = i;
4611
4640
  if (scrollBottomBuffered > totalSize) {
@@ -4616,6 +4645,8 @@ function calculateItemsInView(ctx, params = {}) {
4616
4645
  } else {
4617
4646
  foundEnd = true;
4618
4647
  }
4648
+ } else if (top > scrollBottomBuffered) {
4649
+ foundEnd = true;
4619
4650
  }
4620
4651
  }
4621
4652
  }
@@ -4633,6 +4664,12 @@ function calculateItemsInView(ctx, params = {}) {
4633
4664
  bottom: nextBottom,
4634
4665
  top: nextTop
4635
4666
  };
4667
+ } else if (enableScrollForNextCalculateItemsInView && state.props.hasExternalScroll) {
4668
+ if (scrollBottomBuffered < 0) {
4669
+ state.scrollForNextCalculateItemsInView = { bottom: 0, top: null };
4670
+ } else if (scrollTopBuffered > totalSize) {
4671
+ state.scrollForNextCalculateItemsInView = { bottom: null, top: totalSize };
4672
+ }
4636
4673
  }
4637
4674
  let numContainers = prevNumContainers;
4638
4675
  const pendingRemoval = [];
@@ -4656,7 +4693,9 @@ function calculateItemsInView(ctx, params = {}) {
4656
4693
  }
4657
4694
  const hasScrollTargetPinnedRange = scrollTargetPinnedStart <= scrollTargetPinnedEnd;
4658
4695
  const isPinnedRenderIndex = (index) => alwaysRenderIndicesSet.has(index) || hasScrollTargetPinnedRange && index >= scrollTargetPinnedStart && index <= scrollTargetPinnedEnd;
4659
- if (startBuffered !== null && endBuffered !== null) {
4696
+ if (startBuffered !== null && endBuffered !== null || alwaysRenderIndicesArr.length || hasScrollTargetPinnedRange) {
4697
+ const allocationStart = startBuffered != null ? startBuffered : 0;
4698
+ const allocationEnd = endBuffered != null ? endBuffered : -1;
4660
4699
  const needNewContainers = [];
4661
4700
  const needNewContainersSet = /* @__PURE__ */ new Set();
4662
4701
  const addPinnedIndex = (index) => {
@@ -4672,7 +4711,7 @@ function calculateItemsInView(ctx, params = {}) {
4672
4711
  }
4673
4712
  }
4674
4713
  };
4675
- for (let i = startBuffered; i <= endBuffered; i++) {
4714
+ for (let i = allocationStart; i <= allocationEnd; i++) {
4676
4715
  const id = (_m = idCache[i]) != null ? _m : getId(state, i);
4677
4716
  if (!containerItemKeys.has(id)) {
4678
4717
  needNewContainersSet.add(i);
@@ -4694,8 +4733,8 @@ function calculateItemsInView(ctx, params = {}) {
4694
4733
  (_n = stickyState == null ? void 0 : stickyState.currentStickyIdx) != null ? _n : -1,
4695
4734
  needNewContainers,
4696
4735
  needNewContainersSet,
4697
- startBuffered,
4698
- endBuffered
4736
+ allocationStart,
4737
+ allocationEnd
4699
4738
  );
4700
4739
  } else if (previousStickyIndex !== -1) {
4701
4740
  set$(ctx, "activeStickyIndex", -1);
@@ -4704,8 +4743,8 @@ function calculateItemsInView(ctx, params = {}) {
4704
4743
  const availableContainerAllocations = findAvailableContainers(
4705
4744
  ctx,
4706
4745
  needNewContainers,
4707
- startBuffered,
4708
- endBuffered,
4746
+ allocationStart,
4747
+ allocationEnd,
4709
4748
  pendingRemoval,
4710
4749
  protectedContainerKeys,
4711
4750
  // Initial end alignment has no scroll velocity yet. Keep its
@@ -4832,19 +4871,19 @@ function calculateItemsInView(ctx, params = {}) {
4832
4871
  handleInitialScrollLayoutReady(ctx);
4833
4872
  }
4834
4873
  }
4835
- if (viewabilityConfigCallbackPairs && visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null) {
4874
+ if (viewabilityConfigCallbackPairs && (state.props.hasExternalScroll || visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null)) {
4836
4875
  updateViewableItems(
4837
4876
  ctx.state,
4838
4877
  ctx,
4839
4878
  viewabilityConfigCallbackPairs,
4840
4879
  scrollLength,
4841
- visibleRange.startNoBuffer,
4842
- visibleRange.endNoBuffer,
4843
- startBuffered != null ? startBuffered : visibleRange.startNoBuffer,
4844
- endBuffered != null ? endBuffered : visibleRange.endNoBuffer
4880
+ (_u = visibleRange.startNoBuffer) != null ? _u : 0,
4881
+ (_v = visibleRange.endNoBuffer) != null ? _v : -1,
4882
+ (_w = startBuffered != null ? startBuffered : visibleRange.startNoBuffer) != null ? _w : 0,
4883
+ (_x = endBuffered != null ? endBuffered : visibleRange.endNoBuffer) != null ? _x : -1
4845
4884
  );
4846
4885
  }
4847
- (_u = stickyState == null ? void 0 : stickyState.finishCalculateItemsInView) == null ? void 0 : _u.call(stickyState);
4886
+ (_y = stickyState == null ? void 0 : stickyState.finishCalculateItemsInView) == null ? void 0 : _y.call(stickyState);
4848
4887
  });
4849
4888
  }
4850
4889
 
@@ -5139,6 +5178,7 @@ function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem, notif
5139
5178
  return 0;
5140
5179
  }
5141
5180
  const size = Platform.OS === "web" ? Math.round(rawSize) : roundSize(rawSize);
5181
+ if (prevSizeKnown !== size) state.positionsAreCurrent = false;
5142
5182
  sizesKnown.set(itemKey, size);
5143
5183
  if (fixedItemSize === void 0 && size > 0) {
5144
5184
  itemType != null ? itemType : itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
@@ -6239,6 +6279,7 @@ var ListComponent = typedMemo(function ListComponent2({
6239
6279
  stickyHeaderConfig,
6240
6280
  stickyHeaderIndices,
6241
6281
  useWindowScroll = false,
6282
+ scrollElement,
6242
6283
  ...rest
6243
6284
  }) {
6244
6285
  const ctx = useStateContext();
@@ -6304,7 +6345,7 @@ var ListComponent = typedMemo(function ListComponent2({
6304
6345
  SnapOrScroll,
6305
6346
  {
6306
6347
  ...rest,
6307
- ...Platform.OS === "web" ? ScrollComponent === ListComponentScrollView ? { onInternalScrollEnd, useWindowScroll } : {} : { onScrollBeginDrag: onInternalScrollBeginDrag, onScrollEndDrag: onInternalScrollEndDrag },
6348
+ ...Platform.OS === "web" ? ScrollComponent === ListComponentScrollView ? { onInternalScrollEnd, scrollElement, useWindowScroll } : {} : { onScrollBeginDrag: onInternalScrollBeginDrag, onScrollEndDrag: onInternalScrollEndDrag },
6308
6349
  contentContainerStyle: [
6309
6350
  horizontal ? { height: "100%" } : {},
6310
6351
  contentContainerStyle,
@@ -6344,7 +6385,15 @@ var WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO = 0.9;
6344
6385
  var WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO = 0.9;
6345
6386
  function useDevChecksImpl(props) {
6346
6387
  const ctx = useStateContext();
6347
- const { anchoredEndSpace, childrenMode, keyExtractor, numColumns, renderScrollComponent, useWindowScroll } = props;
6388
+ const {
6389
+ anchoredEndSpace,
6390
+ childrenMode,
6391
+ keyExtractor,
6392
+ numColumns,
6393
+ renderScrollComponent,
6394
+ scrollElement,
6395
+ useWindowScroll
6396
+ } = props;
6348
6397
  const hasAnchoredEndSpace = !!anchoredEndSpace;
6349
6398
  useEffect(() => {
6350
6399
  if (hasAnchoredEndSpace && (numColumns != null ? numColumns : 1) > 1) {
@@ -6355,13 +6404,13 @@ function useDevChecksImpl(props) {
6355
6404
  }
6356
6405
  }, [hasAnchoredEndSpace, numColumns]);
6357
6406
  useEffect(() => {
6358
- if (useWindowScroll && renderScrollComponent) {
6407
+ if ((useWindowScroll || scrollElement !== void 0) && renderScrollComponent) {
6359
6408
  warnDevOnce(
6360
6409
  "useWindowScrollRenderScrollComponent",
6361
- "useWindowScroll is not supported when renderScrollComponent is provided."
6410
+ "useWindowScroll and scrollElement are not supported when renderScrollComponent is provided."
6362
6411
  );
6363
6412
  }
6364
- }, [renderScrollComponent, useWindowScroll]);
6413
+ }, [renderScrollComponent, scrollElement, useWindowScroll]);
6365
6414
  useEffect(() => {
6366
6415
  if (!keyExtractor && !ctx.state.isFirst && ctx.state.didDataChange && !childrenMode) {
6367
6416
  warnDevOnce(
@@ -6373,8 +6422,8 @@ function useDevChecksImpl(props) {
6373
6422
  useEffect(() => {
6374
6423
  const state = ctx.state;
6375
6424
  const dataLength = state.props.data.length;
6376
- const useWindowScrollResolved = state.props.useWindowScroll;
6377
- if (Platform.OS !== "web" || useWindowScrollResolved || dataLength < WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH) {
6425
+ const usesExternalScroll = state.props.hasExternalScroll;
6426
+ if (Platform.OS !== "web" || usesExternalScroll || dataLength < WEB_UNBOUNDED_HEIGHT_MIN_DATA_LENGTH) {
6378
6427
  return;
6379
6428
  }
6380
6429
  const warnIfUnboundedOuterSize = () => {
@@ -6538,7 +6587,8 @@ function doInitialAllocateContainers(ctx) {
6538
6587
  calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
6539
6588
  });
6540
6589
  } else {
6541
- calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
6590
+ const reuseInitialPositions = !state.didLoad && state.positionsAreCurrent && !state.didDataChange && !state.didColumnsChange;
6591
+ calculateItemsInView(ctx, { dataChanged: !reuseInitialPositions, doMVCP: true });
6542
6592
  }
6543
6593
  }
6544
6594
  return true;
@@ -6577,10 +6627,8 @@ function handleLayout(ctx, layoutParam, setCanRender) {
6577
6627
  updateContentMetricsState(ctx);
6578
6628
  state.lastBatchingAction = Date.now();
6579
6629
  state.scrollForNextCalculateItemsInView = void 0;
6580
- if (scrollLength > 0) {
6581
- doInitialAllocateContainers(ctx);
6582
- }
6583
- if (needsCalculate) {
6630
+ const didAllocate = scrollLength > 0 && doInitialAllocateContainers(ctx);
6631
+ if (needsCalculate && (!didAllocate || state.initialScroll)) {
6584
6632
  calculateItemsInView(ctx, { doMVCP: true });
6585
6633
  }
6586
6634
  if (didChange || otherAxisSize !== prevOtherAxisSize) {
@@ -6662,7 +6710,7 @@ function onScroll(ctx, event) {
6662
6710
  }
6663
6711
  state.lastNativeScroll = newScroll;
6664
6712
  state.lastNativeScrollTime = Date.now();
6665
- if (state.scrollingTo && state.scrollingTo.offset >= newScroll) {
6713
+ if (!state.props.hasExternalScroll && state.scrollingTo && state.scrollingTo.offset >= newScroll) {
6666
6714
  const maxOffset = clampScrollOffset(ctx, newScroll, state.scrollingTo);
6667
6715
  if (newScroll !== maxOffset && Math.abs(newScroll - maxOffset) > 1) {
6668
6716
  newScroll = maxOffset;
@@ -7445,6 +7493,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7445
7493
  stickyHeaderIndices: stickyHeaderIndicesProp,
7446
7494
  style: styleProp,
7447
7495
  useWindowScroll = false,
7496
+ scrollElement,
7448
7497
  viewabilityConfig,
7449
7498
  viewabilityConfigCallbackPairs,
7450
7499
  ...rest
@@ -7539,7 +7588,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7539
7588
  dataVersion,
7540
7589
  keyExtractor
7541
7590
  ]);
7542
- const useWindowScrollResolved = Platform.OS === "web" && !!useWindowScroll && !renderScrollComponent;
7591
+ const scrollElementResolved = Platform.OS === "web" && !renderScrollComponent ? scrollElement : void 0;
7592
+ const useWindowScrollResolved = Platform.OS === "web" && !!useWindowScroll && scrollElementResolved === void 0 && !renderScrollComponent;
7593
+ const hasExternalScroll = useWindowScrollResolved || scrollElementResolved !== void 0;
7543
7594
  const refState = useRef(void 0);
7544
7595
  const hasOverrideItemLayout = !!overrideItemLayout;
7545
7596
  const prevHasOverrideItemLayout = useRef(hasOverrideItemLayout);
@@ -7677,6 +7728,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7677
7728
  estimatedItemSize,
7678
7729
  getFixedItemSize: useWrapIfItem(getFixedItemSize),
7679
7730
  getItemType: useWrapIfItem(getItemType),
7731
+ hasExternalScroll,
7680
7732
  hideItemsUntilMeasured: experimental_hideItemsUntilMeasured,
7681
7733
  horizontal: !!horizontal,
7682
7734
  itemsAreEqual,
@@ -7873,9 +7925,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7873
7925
  const { onLayout } = useOnLayoutSync({
7874
7926
  onLayoutChange,
7875
7927
  onLayoutProp,
7876
- ref: refScroller
7877
- // the type of ScrollView doesn't include measure?
7878
- });
7928
+ ref: refScroller});
7879
7929
  useLayoutEffect(() => {
7880
7930
  if (snapToIndices) {
7881
7931
  updateSnapToOffsets(ctx);
@@ -8043,6 +8093,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
8043
8093
  refScrollView: combinedRef,
8044
8094
  renderScrollComponent,
8045
8095
  scrollAdjustHandler: (_s = refState.current) == null ? void 0 : _s.scrollAdjustHandler,
8096
+ scrollElement: scrollElementResolved,
8046
8097
  scrollEventThrottle: 0,
8047
8098
  snapToIndices,
8048
8099
  stickyHeaderIndices,
@@ -6,11 +6,14 @@ type ScrollEventTarget = Window | HTMLElement;
6
6
  interface ScrollViewMethods {
7
7
  getBoundingClientRect(): DOMRect | null | undefined;
8
8
  getCurrentScrollOffset(): number;
9
+ getRawScrollOffset(): number;
9
10
  getMaxScrollOffset(): number;
10
- getScrollableNode(): HTMLElement;
11
+ getScrollableNode(): HTMLElement | null;
11
12
  getScrollEventTarget(): ScrollEventTarget | null;
12
13
  getScrollResponder(): HTMLElement | null;
13
14
  isWindowScroll?(): boolean;
15
+ getContentNode?(): HTMLElement | null;
16
+ isScrollInRange?(): boolean;
14
17
  scrollBy(x: number, y: number): void;
15
18
  scrollTo(options: {
16
19
  x?: number;
@@ -836,6 +839,13 @@ type ScrollViewPropsWeb = Omit<LooseScrollViewProps, "style" | "contentContainer
836
839
  onLayout?: (event: LooseLayoutChangeEvent) => void;
837
840
  };
838
841
  type LegendListPropsOverrides<ItemT, TItemType extends string | undefined> = Omit<LegendListPropsBase<ItemT, ScrollViewPropsWeb, TItemType>, "anchoredEndSpace" | "refScrollView" | "renderScrollComponent" | "ListHeaderComponentStyle" | "ListFooterComponentStyle" | "onRefresh" | "progressViewOffset" | "refreshing"> & {
842
+ /**
843
+ * Web only: use an ancestor element's scrollbar instead of creating a scrollable list.
844
+ * Pass a state-backed callback ref value so changes to the element are observed.
845
+ * null waits for the element to mount; undefined uses the list's own scrollbar.
846
+ * Takes precedence over useWindowScroll. Not supported with renderScrollComponent.
847
+ */
848
+ scrollElement?: HTMLElement | null;
839
849
  anchoredEndSpace?: AnchoredEndSpaceConfig;
840
850
  refScrollView?: Ref<HTMLElement | ScrollViewMethods>;
841
851
  ListHeaderComponentStyle?: CSSProperties | undefined;
@@ -845,7 +855,7 @@ type LegendListProps<ItemT = any, TItemType extends string | undefined = string
845
855
  type LegendListRef = Omit<LegendListRef$1, "getAnimatableRef" | "getNativeScrollRef" | "getScrollableNode" | "getScrollResponder"> & {
846
856
  getAnimatableRef(): HTMLElement | ScrollViewMethods;
847
857
  getNativeScrollRef(): HTMLElement | ScrollViewMethods;
848
- getScrollableNode(): HTMLElement;
858
+ getScrollableNode(): HTMLElement | null;
849
859
  getScrollResponder(): HTMLElement | null;
850
860
  };
851
861
  type LegendListState = Omit<LegendListState$1, "elementAtIndex"> & {