@legendapp/list 3.3.0 → 3.3.2

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
@@ -117,6 +117,7 @@ var createAnimatedValue = (value) => new Animated.Value(value);
117
117
 
118
118
  // src/state/state.tsx
119
119
  var ContextState = React2.createContext(null);
120
+ var SIGNAL_NAMES_SEPARATOR = "\0";
120
121
  var contextNum = 0;
121
122
  function StateProvider({ children }) {
122
123
  const [value] = React2.useState(() => ({
@@ -188,6 +189,12 @@ function createSelectorFunctionsArr(ctx, signalNames) {
188
189
  }
189
190
  };
190
191
  }
192
+ function getSignalNamesKey(signalNames) {
193
+ return signalNames.length === 1 ? signalNames[0] : signalNames.join(SIGNAL_NAMES_SEPARATOR);
194
+ }
195
+ function getSignalNamesFromKey(signalNamesKey) {
196
+ return signalNamesKey.split(SIGNAL_NAMES_SEPARATOR);
197
+ }
191
198
  function listen$(ctx, signalName, cb) {
192
199
  const { listeners } = ctx;
193
200
  let setListeners = listeners.get(signalName);
@@ -235,7 +242,11 @@ function notifyPosition$(ctx, key, value) {
235
242
  }
236
243
  function useArr$(signalNames) {
237
244
  const ctx = React2.useContext(ContextState);
238
- const { subscribe, get } = React2.useMemo(() => createSelectorFunctionsArr(ctx, signalNames), [ctx, signalNames]);
245
+ const signalNamesKey = getSignalNamesKey(signalNames);
246
+ const { subscribe, get } = React2.useMemo(
247
+ () => createSelectorFunctionsArr(ctx, getSignalNamesFromKey(signalNamesKey)),
248
+ [ctx, signalNamesKey]
249
+ );
239
250
  const value = useSyncExternalStore(subscribe, get, get);
240
251
  return value;
241
252
  }
@@ -587,9 +598,22 @@ function findContainerId(ctx, key) {
587
598
 
588
599
  // src/state/ContextContainer.ts
589
600
  var ContextContainer = createContext(null);
601
+ var NO_CONTAINER_ID = -1;
590
602
  function useContextContainer() {
591
603
  return useContext(ContextContainer);
592
604
  }
605
+ function useContainerItemInfo(containerContext) {
606
+ var _a3;
607
+ const containerId = (_a3 = containerContext == null ? void 0 : containerContext.containerId) != null ? _a3 : NO_CONTAINER_ID;
608
+ const [itemInfo] = useArr$([`containerItemInfo${containerId}`]);
609
+ return containerContext ? itemInfo : void 0;
610
+ }
611
+ function useContainerItemKey(containerContext) {
612
+ var _a3;
613
+ const containerId = (_a3 = containerContext == null ? void 0 : containerContext.containerId) != null ? _a3 : NO_CONTAINER_ID;
614
+ const [itemKey] = useArr$([`containerItemKey${containerId}`]);
615
+ return containerContext ? itemKey : void 0;
616
+ }
593
617
  function useAdaptiveRender() {
594
618
  const [mode] = useArr$(["adaptiveRender"]);
595
619
  return mode;
@@ -660,40 +684,35 @@ function useViewabilityAmount(callback) {
660
684
  }
661
685
  function useRecyclingEffect(effect) {
662
686
  const containerContext = useContextContainer();
663
- const prevValues = useRef({
664
- prevIndex: void 0,
665
- prevItem: void 0
666
- });
687
+ const itemInfo = useContainerItemInfo(containerContext);
688
+ const prevInfo = useRef(void 0);
667
689
  useEffect(() => {
668
- if (!containerContext) {
690
+ if (!itemInfo) {
669
691
  return;
670
692
  }
671
- const { index, value } = containerContext;
672
693
  let ret;
673
- if (prevValues.current.prevIndex !== void 0 && prevValues.current.prevItem !== void 0) {
694
+ if (prevInfo.current) {
674
695
  ret = effect({
675
- index,
676
- item: value,
677
- prevIndex: prevValues.current.prevIndex,
678
- prevItem: prevValues.current.prevItem
696
+ index: itemInfo.index,
697
+ item: itemInfo.value,
698
+ prevIndex: prevInfo.current.index,
699
+ prevItem: prevInfo.current.value
679
700
  });
680
701
  }
681
- prevValues.current = {
682
- prevIndex: index,
683
- prevItem: value
684
- };
702
+ prevInfo.current = itemInfo;
685
703
  return ret;
686
- }, [effect, containerContext]);
704
+ }, [effect, itemInfo]);
687
705
  }
688
706
  function useRecyclingState(valueOrFun) {
689
- var _a3, _b;
707
+ var _a3;
690
708
  const containerContext = useContextContainer();
691
- const computeValue = (ctx) => {
709
+ const itemInfo = useContainerItemInfo(containerContext);
710
+ const computeValue = (info) => {
692
711
  if (isFunction(valueOrFun)) {
693
712
  const initializer = valueOrFun;
694
- return ctx ? initializer({
695
- index: ctx.index,
696
- item: ctx.value,
713
+ return info ? initializer({
714
+ index: info.index,
715
+ item: info.value,
697
716
  prevIndex: void 0,
698
717
  prevItem: void 0
699
718
  }) : initializer();
@@ -701,13 +720,12 @@ function useRecyclingState(valueOrFun) {
701
720
  return valueOrFun;
702
721
  };
703
722
  const [stateValue, setStateValue] = useState(() => {
704
- return computeValue(containerContext);
723
+ return computeValue(itemInfo);
705
724
  });
706
- const prevItemKeyRef = useRef((_a3 = containerContext == null ? void 0 : containerContext.itemKey) != null ? _a3 : null);
707
- const currentItemKey = (_b = containerContext == null ? void 0 : containerContext.itemKey) != null ? _b : null;
708
- if (currentItemKey !== null && prevItemKeyRef.current !== currentItemKey) {
709
- prevItemKeyRef.current = currentItemKey;
710
- setStateValue(computeValue(containerContext));
725
+ const prevItemKeyRef = useRef((_a3 = itemInfo == null ? void 0 : itemInfo.itemKey) != null ? _a3 : null);
726
+ if (itemInfo && prevItemKeyRef.current !== itemInfo.itemKey) {
727
+ prevItemKeyRef.current = itemInfo.itemKey;
728
+ setStateValue(computeValue(itemInfo));
711
729
  }
712
730
  const triggerLayout = containerContext == null ? void 0 : containerContext.triggerLayout;
713
731
  const setState = useCallback(
@@ -726,12 +744,10 @@ function useRecyclingState(valueOrFun) {
726
744
  }
727
745
  function useIsLastItem() {
728
746
  const containerContext = useContextContainer();
747
+ const itemKey = useContainerItemKey(containerContext);
729
748
  const isLast = useSelector$("lastItemKeys", (lastItemKeys) => {
730
- if (containerContext) {
731
- const { itemKey } = containerContext;
732
- if (!isNullOrUndefined(itemKey)) {
733
- return (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false;
734
- }
749
+ if (containerContext && !isNullOrUndefined(itemKey)) {
750
+ return (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false;
735
751
  }
736
752
  return false;
737
753
  });
@@ -745,12 +761,7 @@ var noop = () => {
745
761
  };
746
762
  function useSyncLayout() {
747
763
  const containerContext = useContextContainer();
748
- if (IsNewArchitecture && containerContext) {
749
- const { triggerLayout: syncLayout } = containerContext;
750
- return syncLayout;
751
- } else {
752
- return noop;
753
- }
764
+ return IsNewArchitecture && containerContext ? containerContext.triggerLayout : noop;
754
765
  }
755
766
 
756
767
  // src/components/Separator.tsx
@@ -1513,6 +1524,7 @@ function finishScrollTo(ctx) {
1513
1524
  const scrollingTo = state.scrollingTo;
1514
1525
  state.scrollHistory.length = 0;
1515
1526
  state.scrollingTo = void 0;
1527
+ state.scrollTargetPinnedRange = void 0;
1516
1528
  if (state.pendingTotalSize !== void 0) {
1517
1529
  addTotalSize(ctx, null, state.pendingTotalSize);
1518
1530
  }
@@ -2152,6 +2164,8 @@ var flushSync = (fn) => {
2152
2164
  };
2153
2165
 
2154
2166
  // src/utils/getScrollVelocity.ts
2167
+ var MAX_SCROLL_VELOCITY_WINDOW_MS = 1e3;
2168
+ var SCROLL_VELOCITY_HALF_LIFE_MS = 200;
2155
2169
  var getScrollVelocity = (state) => {
2156
2170
  const { scrollHistory } = state;
2157
2171
  const newestIndex = scrollHistory.length - 1;
@@ -2159,35 +2173,37 @@ var getScrollVelocity = (state) => {
2159
2173
  return 0;
2160
2174
  }
2161
2175
  const newest = scrollHistory[newestIndex];
2162
- const now = Date.now();
2163
- let direction = 0;
2164
- for (let i = newestIndex; i > 0; i--) {
2165
- const delta = scrollHistory[i].scroll - scrollHistory[i - 1].scroll;
2166
- if (delta !== 0) {
2167
- direction = Math.sign(delta);
2168
- break;
2169
- }
2170
- }
2171
- if (direction === 0) {
2176
+ if (Date.now() - newest.time > MAX_SCROLL_VELOCITY_WINDOW_MS) {
2172
2177
  return 0;
2173
2178
  }
2174
- let oldest = newest;
2175
- for (let i = newestIndex - 1; i >= 0; i--) {
2179
+ let direction = 0;
2180
+ let weightedVelocity = 0;
2181
+ let totalWeight = 0;
2182
+ for (let i = newestIndex; i > 0; i--) {
2176
2183
  const current = scrollHistory[i];
2177
- const next = scrollHistory[i + 1];
2178
- const delta = next.scroll - current.scroll;
2179
- const deltaSign = Math.sign(delta);
2180
- if (deltaSign !== 0 && deltaSign !== direction) {
2181
- break;
2184
+ const previous = scrollHistory[i - 1];
2185
+ const scrollDiff = current.scroll - previous.scroll;
2186
+ const timeDiff = current.time - previous.time;
2187
+ const deltaSign = Math.sign(scrollDiff);
2188
+ if (deltaSign !== 0) {
2189
+ if (direction === 0) {
2190
+ direction = deltaSign;
2191
+ } else if (deltaSign !== direction) {
2192
+ break;
2193
+ }
2182
2194
  }
2183
- if (now - current.time > 1e3) {
2195
+ if (newest.time - previous.time > MAX_SCROLL_VELOCITY_WINDOW_MS) {
2184
2196
  break;
2185
2197
  }
2186
- oldest = current;
2198
+ if (scrollDiff === 0 || timeDiff <= 0) {
2199
+ continue;
2200
+ }
2201
+ const age = newest.time - current.time;
2202
+ const weight = Math.exp(-age / SCROLL_VELOCITY_HALF_LIFE_MS);
2203
+ weightedVelocity += scrollDiff / timeDiff * weight;
2204
+ totalWeight += weight;
2187
2205
  }
2188
- const scrollDiff = newest.scroll - oldest.scroll;
2189
- const timeDiff = newest.time - oldest.time;
2190
- return timeDiff > 0 ? scrollDiff / timeDiff : 0;
2206
+ return totalWeight > 0 ? weightedVelocity / totalWeight : 0;
2191
2207
  };
2192
2208
 
2193
2209
  // src/core/updateScroll.ts
@@ -2304,8 +2320,93 @@ function syncInitialScrollNativeWatchdog(state, options) {
2304
2320
  initialScrollWatchdog.clear(state);
2305
2321
  }
2306
2322
  }
2307
- function scrollTo(ctx, params) {
2323
+ function findPositionIndexAtOrBeforeOffset(ctx, offset) {
2324
+ const state = ctx.state;
2325
+ const dataLength = state.props.data.length;
2326
+ let low = 0;
2327
+ let high = dataLength - 1;
2328
+ let match;
2329
+ while (low <= high) {
2330
+ const mid = Math.floor((low + high) / 2);
2331
+ const top = state.positions[mid];
2332
+ if (top === void 0) {
2333
+ high = mid - 1;
2334
+ } else {
2335
+ if (top <= offset) {
2336
+ match = mid;
2337
+ low = mid + 1;
2338
+ } else {
2339
+ high = mid - 1;
2340
+ }
2341
+ }
2342
+ }
2343
+ return match;
2344
+ }
2345
+ function getItemBottom(ctx, index) {
2308
2346
  var _a3;
2347
+ const top = ctx.state.positions[index];
2348
+ if (top === void 0) {
2349
+ return void 0;
2350
+ }
2351
+ const itemSize = (_a3 = getItemSizeAtIndex(ctx, index)) != null ? _a3 : 0;
2352
+ return top + (Number.isFinite(itemSize) ? itemSize : 0);
2353
+ }
2354
+ function getTargetViewportRenderRange(ctx, targetOffset, targetIndex) {
2355
+ const state = ctx.state;
2356
+ const dataLength = state.props.data.length;
2357
+ if (dataLength === 0) {
2358
+ return void 0;
2359
+ }
2360
+ const viewportStart = Math.max(0, targetOffset);
2361
+ const viewportEnd = Math.max(viewportStart, targetOffset + state.scrollLength);
2362
+ let start = targetIndex !== void 0 ? Math.max(0, Math.min(dataLength - 1, targetIndex)) : findPositionIndexAtOrBeforeOffset(ctx, viewportStart);
2363
+ if (start === void 0) {
2364
+ return void 0;
2365
+ }
2366
+ if (targetIndex !== void 0 && state.positions[start] === void 0) {
2367
+ return { end: start, start };
2368
+ }
2369
+ if (targetIndex === void 0) {
2370
+ const startBottom = getItemBottom(ctx, start);
2371
+ if (startBottom === void 0 || startBottom <= viewportStart) {
2372
+ return void 0;
2373
+ }
2374
+ }
2375
+ while (start > 0) {
2376
+ const top = state.positions[start];
2377
+ if (top === void 0 || top <= viewportStart || state.positions[start - 1] === void 0) {
2378
+ break;
2379
+ }
2380
+ start--;
2381
+ }
2382
+ while (start > 0) {
2383
+ const previousBottom = getItemBottom(ctx, start - 1);
2384
+ if (previousBottom === void 0 || previousBottom <= viewportStart) {
2385
+ break;
2386
+ }
2387
+ start--;
2388
+ }
2389
+ let end = start;
2390
+ while (end + 1 < dataLength) {
2391
+ const nextTop = state.positions[end + 1];
2392
+ if (nextTop === void 0 || nextTop > viewportEnd) {
2393
+ break;
2394
+ }
2395
+ end++;
2396
+ }
2397
+ return { end, start };
2398
+ }
2399
+ function pinScrollTargetRenderRange(ctx, targetOffset, targetIndex) {
2400
+ const range = getTargetViewportRenderRange(ctx, targetOffset, targetIndex);
2401
+ if (range) {
2402
+ ctx.state.scrollTargetPinnedRange = range;
2403
+ ctx.state.scrollForNextCalculateItemsInView = void 0;
2404
+ } else {
2405
+ ctx.state.scrollTargetPinnedRange = void 0;
2406
+ }
2407
+ }
2408
+ function scrollTo(ctx, params) {
2409
+ var _a3, _b;
2309
2410
  const state = ctx.state;
2310
2411
  const { noScrollingTo, forceScroll, ...scrollTarget } = params;
2311
2412
  const {
@@ -2340,11 +2441,20 @@ function scrollTo(ctx, params) {
2340
2441
  targetOffset,
2341
2442
  waitForInitialScrollCompletionFrame
2342
2443
  };
2444
+ if (!isInitialScroll) {
2445
+ pinScrollTargetRenderRange(ctx, targetOffset, scrollTarget.index);
2446
+ }
2343
2447
  }
2344
2448
  state.scrollPending = targetOffset;
2345
2449
  syncInitialScrollNativeWatchdog(state, { isInitialScroll, requestedOffset: offset, targetOffset });
2346
- if (!animated && !isInitialScroll && !noScrollingTo && Math.abs(state.scroll - targetOffset) > 1) {
2347
- updateScroll(ctx, targetOffset, false, { markHasScrolled: false });
2450
+ if (!isInitialScroll && !noScrollingTo && Math.abs(state.scroll - targetOffset) > 1) {
2451
+ if (animated) {
2452
+ if (state.scrollTargetPinnedRange) {
2453
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state);
2454
+ }
2455
+ } else {
2456
+ updateScroll(ctx, targetOffset, true, { markHasScrolled: false });
2457
+ }
2348
2458
  }
2349
2459
  if (forceScroll || !isInitialScroll || Platform.OS === "android") {
2350
2460
  doScrollTo(ctx, { animated, horizontal, isInitialScroll, offset });
@@ -3287,22 +3397,28 @@ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3287
3397
  set$(ctx, `containerSpan${containerIndex}`, span);
3288
3398
  }
3289
3399
  }
3400
+ const prevItemInfo = peek$(ctx, `containerItemInfo${containerIndex}`);
3290
3401
  const prevData = peek$(ctx, `containerItemData${containerIndex}`);
3402
+ let itemInfoValue = prevData;
3403
+ let didChangeItemInfo = (prevItemInfo == null ? void 0 : prevItemInfo.itemKey) !== itemKey || (prevItemInfo == null ? void 0 : prevItemInfo.index) !== itemIndex || (prevItemInfo == null ? void 0 : prevItemInfo.value) !== prevData;
3404
+ const updateData = () => {
3405
+ set$(ctx, `containerItemData${containerIndex}`, item);
3406
+ itemInfoValue = item;
3407
+ didChangeItemInfo = true;
3408
+ didRefreshData = true;
3409
+ };
3291
3410
  if (prevData !== item) {
3292
3411
  const pendingDataComparison = ((_e = state.pendingDataComparison) == null ? void 0 : _e.previousData) === state.previousData && ((_f = state.pendingDataComparison) == null ? void 0 : _f.nextData) === data ? state.pendingDataComparison : void 0;
3293
3412
  const cachedComparison = (_g = pendingDataComparison == null ? void 0 : pendingDataComparison.byIndex[itemIndex]) != null ? _g : 0;
3294
3413
  if (cachedComparison === 2) {
3295
- set$(ctx, `containerItemData${containerIndex}`, item);
3296
- didRefreshData = true;
3414
+ updateData();
3297
3415
  } else if (cachedComparison !== 1) {
3298
3416
  const nextItemKey = (_h = peek$(ctx, `containerItemKey${containerIndex}`)) != null ? _h : itemKey;
3299
3417
  const prevKey = keyExtractor == null ? void 0 : keyExtractor(prevData, itemIndex);
3300
3418
  if (prevData === void 0 || !keyExtractor || prevKey !== nextItemKey) {
3301
- set$(ctx, `containerItemData${containerIndex}`, item);
3302
- didRefreshData = true;
3419
+ updateData();
3303
3420
  } else if (!itemsAreEqual) {
3304
- set$(ctx, `containerItemData${containerIndex}`, item);
3305
- didRefreshData = true;
3421
+ updateData();
3306
3422
  } else {
3307
3423
  const isEqual = itemsAreEqual(prevData, item, itemIndex, data);
3308
3424
  if (!state.pendingDataComparison || state.pendingDataComparison.previousData !== state.previousData || state.pendingDataComparison.nextData !== data) {
@@ -3318,12 +3434,18 @@ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3318
3434
  state.pendingDataComparison.byIndex[itemIndex] = isEqual ? 1 : 2;
3319
3435
  }
3320
3436
  if (!isEqual) {
3321
- set$(ctx, `containerItemData${containerIndex}`, item);
3322
- didRefreshData = true;
3437
+ updateData();
3323
3438
  }
3324
3439
  }
3325
3440
  }
3326
3441
  }
3442
+ if (didChangeItemInfo) {
3443
+ set$(ctx, `containerItemInfo${containerIndex}`, {
3444
+ index: itemIndex,
3445
+ itemKey,
3446
+ value: itemInfoValue
3447
+ });
3448
+ }
3327
3449
  return { didChangePosition, didRefreshData };
3328
3450
  }
3329
3451
 
@@ -4016,6 +4138,32 @@ function setDidLayout(ctx) {
4016
4138
  }
4017
4139
 
4018
4140
  // src/core/calculateItemsInView.ts
4141
+ var RENDER_RANGE_PROJECTION_FULL_VELOCITY = 4;
4142
+ var RENDER_RANGE_PROJECTION_SETTLE_DELAY = 100;
4143
+ function getProjectedBufferAdjustment(scrollVelocity, trailingBuffer) {
4144
+ if (trailingBuffer <= 0) {
4145
+ return 0;
4146
+ }
4147
+ const velocityProgress = Math.min(1, Math.abs(scrollVelocity) / RENDER_RANGE_PROJECTION_FULL_VELOCITY);
4148
+ return Math.sign(scrollVelocity) * trailingBuffer * velocityProgress;
4149
+ }
4150
+ function scheduleRenderRangeProjectionSettle(ctx) {
4151
+ const state = ctx.state;
4152
+ const previousTimeout = state.timeoutRenderRangeProjectionSettle;
4153
+ if (previousTimeout !== void 0) {
4154
+ clearTimeout(previousTimeout);
4155
+ state.timeouts.delete(previousTimeout);
4156
+ }
4157
+ const timeout = setTimeout(() => {
4158
+ var _a3;
4159
+ state.timeoutRenderRangeProjectionSettle = void 0;
4160
+ state.timeouts.delete(timeout);
4161
+ state.scrollHistory.length = 0;
4162
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
4163
+ }, RENDER_RANGE_PROJECTION_SETTLE_DELAY);
4164
+ state.timeoutRenderRangeProjectionSettle = timeout;
4165
+ state.timeouts.add(timeout);
4166
+ }
4019
4167
  function findCurrentStickyIndex(stickyArray, scroll, state) {
4020
4168
  const positions = state.positions;
4021
4169
  for (let i = stickyArray.length - 1; i >= 0; i--) {
@@ -4056,14 +4204,14 @@ function handleStickyActivation(ctx, stickyArray, currentStickyIdx, needNewConta
4056
4204
  }
4057
4205
  }
4058
4206
  }
4059
- function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentStickyIdx, pendingRemoval, alwaysRenderIndicesSet) {
4207
+ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentStickyIdx, pendingRemoval, isPinnedRenderIndex) {
4060
4208
  var _a3, _b;
4061
4209
  const state = ctx.state;
4062
4210
  for (const containerIndex of state.stickyContainerPool) {
4063
4211
  const itemKey = peek$(ctx, `containerItemKey${containerIndex}`);
4064
4212
  const itemIndex = itemKey ? state.indexByKey.get(itemKey) : void 0;
4065
4213
  if (itemIndex === void 0) continue;
4066
- if (alwaysRenderIndicesSet.has(itemIndex)) continue;
4214
+ if (isPinnedRenderIndex(itemIndex)) continue;
4067
4215
  const arrayIdx = stickyArray.indexOf(itemIndex);
4068
4216
  if (arrayIdx === -1) {
4069
4217
  state.stickyContainerPool.delete(containerIndex);
@@ -4227,8 +4375,6 @@ function calculateItemsInView(ctx, params = {}) {
4227
4375
  const { data } = state.props;
4228
4376
  const stickyHeaderIndicesArr = state.props.stickyHeaderIndicesArr || [];
4229
4377
  const stickyHeaderIndicesSet = state.props.stickyHeaderIndicesSet || /* @__PURE__ */ new Set();
4230
- const alwaysRenderArr = alwaysRenderIndicesArr || [];
4231
- const alwaysRenderSet = alwaysRenderIndicesSet || /* @__PURE__ */ new Set();
4232
4378
  const drawDistance = getEffectiveDrawDistance(ctx, params.drawDistanceMode);
4233
4379
  const { dataChanged, doMVCP, forceFullItemPositions } = params;
4234
4380
  const bootstrapInitialScrollState = ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "bootstrap" ? state.initialScrollSession.bootstrap : void 0;
@@ -4295,14 +4441,19 @@ function calculateItemsInView(ctx, params = {}) {
4295
4441
  scrollBufferTop = drawDistance * 1.5;
4296
4442
  scrollBufferBottom = drawDistance * 0.5;
4297
4443
  }
4444
+ const shouldProjectRenderRange = !dataChanged && !forceFullItemPositions && !suppressInitialScrollSideEffects && !hasActiveInitialScroll(state) && !state.scrollingTo && !state.pendingNativeMVCPAdjust && !!peek$(ctx, "readyToRender");
4445
+ const projectedBufferAdjustment = shouldProjectRenderRange ? getProjectedBufferAdjustment(speed, Math.min(scrollBufferTop, scrollBufferBottom)) : 0;
4298
4446
  const updateScrollRange = () => {
4299
4447
  const scrollStart = Math.max(0, scroll);
4300
4448
  const overscrollBeforeContent = Math.max(0, -nativeScrollState);
4301
- scrollTopBuffered = scrollStart - scrollBufferTop;
4302
4449
  scrollBottom = Math.max(scrollStart, scroll + scrollLength + overscrollBeforeContent);
4303
- scrollBottomBuffered = scrollBottom + scrollBufferBottom;
4450
+ scrollTopBuffered = scrollStart - scrollBufferTop + projectedBufferAdjustment;
4451
+ scrollBottomBuffered = scrollBottom + scrollBufferBottom + projectedBufferAdjustment;
4304
4452
  };
4305
4453
  updateScrollRange();
4454
+ if (projectedBufferAdjustment !== 0) {
4455
+ scheduleRenderRangeProjectionSettle(ctx);
4456
+ }
4306
4457
  if (enableScrollForNextCalculateItemsInView && !suppressInitialScrollSideEffects && !dataChanged && !forceFullItemPositions && scrollForNextCalculateItemsInView) {
4307
4458
  const { top, bottom } = scrollForNextCalculateItemsInView;
4308
4459
  if (top === null && bottom === null) {
@@ -4460,9 +4611,34 @@ function calculateItemsInView(ctx, params = {}) {
4460
4611
  }
4461
4612
  }
4462
4613
  }
4614
+ const scrollTargetPinnedRange = state.scrollTargetPinnedRange;
4615
+ let scrollTargetPinnedStart = 0;
4616
+ let scrollTargetPinnedEnd = -1;
4617
+ if (scrollTargetPinnedRange) {
4618
+ scrollTargetPinnedStart = Math.max(0, Math.min(scrollTargetPinnedRange.start, scrollTargetPinnedRange.end));
4619
+ scrollTargetPinnedEnd = Math.min(
4620
+ dataLength - 1,
4621
+ Math.max(scrollTargetPinnedRange.start, scrollTargetPinnedRange.end)
4622
+ );
4623
+ }
4624
+ const hasScrollTargetPinnedRange = scrollTargetPinnedStart <= scrollTargetPinnedEnd;
4625
+ const isPinnedRenderIndex = (index) => alwaysRenderIndicesSet.has(index) || hasScrollTargetPinnedRange && index >= scrollTargetPinnedStart && index <= scrollTargetPinnedEnd;
4463
4626
  if (startBuffered !== null && endBuffered !== null) {
4464
4627
  const needNewContainers = [];
4465
4628
  const needNewContainersSet = /* @__PURE__ */ new Set();
4629
+ const addPinnedIndex = (index) => {
4630
+ var _a4;
4631
+ if (index >= 0 && index < dataLength) {
4632
+ const id = (_a4 = idCache[index]) != null ? _a4 : getId(state, index);
4633
+ const containerIndex = containerItemKeys.get(id);
4634
+ if (containerIndex !== void 0) {
4635
+ state.stickyContainerPool.add(containerIndex);
4636
+ } else if (!isNullOrUndefined(id) && !needNewContainersSet.has(index)) {
4637
+ needNewContainersSet.add(index);
4638
+ needNewContainers.push(index);
4639
+ }
4640
+ }
4641
+ };
4466
4642
  for (let i = startBuffered; i <= endBuffered; i++) {
4467
4643
  const id = (_m = idCache[i]) != null ? _m : getId(state, i);
4468
4644
  if (!containerItemKeys.has(id)) {
@@ -4470,21 +4646,19 @@ function calculateItemsInView(ctx, params = {}) {
4470
4646
  needNewContainers.push(i);
4471
4647
  }
4472
4648
  }
4473
- if (alwaysRenderArr.length > 0) {
4474
- for (const index of alwaysRenderArr) {
4475
- if (index < 0 || index >= dataLength) continue;
4476
- const id = (_n = idCache[index]) != null ? _n : getId(state, index);
4477
- if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
4478
- needNewContainersSet.add(index);
4479
- needNewContainers.push(index);
4480
- }
4649
+ for (const index of alwaysRenderIndicesArr) {
4650
+ addPinnedIndex(index);
4651
+ }
4652
+ if (hasScrollTargetPinnedRange) {
4653
+ for (let index = scrollTargetPinnedStart; index <= scrollTargetPinnedEnd; index++) {
4654
+ addPinnedIndex(index);
4481
4655
  }
4482
4656
  }
4483
4657
  if (stickyHeaderIndicesArr.length > 0) {
4484
4658
  handleStickyActivation(
4485
4659
  ctx,
4486
4660
  stickyHeaderIndicesArr,
4487
- (_o = stickyState == null ? void 0 : stickyState.currentStickyIdx) != null ? _o : -1,
4661
+ (_n = stickyState == null ? void 0 : stickyState.currentStickyIdx) != null ? _n : -1,
4488
4662
  needNewContainers,
4489
4663
  needNewContainersSet,
4490
4664
  startBuffered,
@@ -4510,21 +4684,30 @@ function calculateItemsInView(ctx, params = {}) {
4510
4684
  for (const allocation of availableContainerAllocations) {
4511
4685
  const i = allocation.itemIndex;
4512
4686
  const containerIndex = allocation.containerIndex;
4513
- const id = (_p = idCache[i]) != null ? _p : getId(state, i);
4687
+ const id = (_o = idCache[i]) != null ? _o : getId(state, i);
4514
4688
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
4515
4689
  if (oldKey && oldKey !== id) {
4516
4690
  containerItemKeys.delete(oldKey);
4517
4691
  }
4518
4692
  set$(ctx, `containerItemKey${containerIndex}`, id);
4519
4693
  set$(ctx, `containerItemData${containerIndex}`, data[i]);
4694
+ set$(ctx, `containerItemInfo${containerIndex}`, {
4695
+ index: i,
4696
+ itemKey: id,
4697
+ value: data[i]
4698
+ });
4520
4699
  if (allocation.itemType !== void 0) {
4521
4700
  state.containerItemTypes.set(containerIndex, allocation.itemType);
4522
4701
  }
4523
4702
  containerItemKeys.set(id, containerIndex);
4524
- (_q = state.userScrollAnchorReset) == null ? void 0 : _q.keys.add(id);
4703
+ (_p = state.userScrollAnchorReset) == null ? void 0 : _p.keys.add(id);
4704
+ if (IsNewArchitecture) {
4705
+ (_q = state.pendingLayoutEffectMeasurements) != null ? _q : state.pendingLayoutEffectMeasurements = /* @__PURE__ */ new Set();
4706
+ state.pendingLayoutEffectMeasurements.add(id);
4707
+ }
4525
4708
  const containerSticky = `containerSticky${containerIndex}`;
4526
4709
  const isSticky = stickyHeaderIndicesSet.has(i);
4527
- const isAlwaysRender = alwaysRenderSet.has(i);
4710
+ const isPinnedRender = isPinnedRenderIndex(i);
4528
4711
  if (isSticky) {
4529
4712
  set$(ctx, containerSticky, true);
4530
4713
  state.stickyContainerPool.add(containerIndex);
@@ -4532,9 +4715,9 @@ function calculateItemsInView(ctx, params = {}) {
4532
4715
  if (peek$(ctx, containerSticky)) {
4533
4716
  set$(ctx, containerSticky, false);
4534
4717
  }
4535
- if (isAlwaysRender) {
4718
+ if (isPinnedRender) {
4536
4719
  state.stickyContainerPool.add(containerIndex);
4537
- } else if (state.stickyContainerPool.has(containerIndex)) {
4720
+ } else {
4538
4721
  state.stickyContainerPool.delete(containerIndex);
4539
4722
  }
4540
4723
  }
@@ -4549,20 +4732,8 @@ function calculateItemsInView(ctx, params = {}) {
4549
4732
  }
4550
4733
  }
4551
4734
  }
4552
- if (state.userScrollAnchorReset) {
4553
- if (state.userScrollAnchorReset.keys.size === 0) {
4554
- state.userScrollAnchorReset = void 0;
4555
- }
4556
- }
4557
- if (alwaysRenderArr.length > 0) {
4558
- for (const index of alwaysRenderArr) {
4559
- if (index < 0 || index >= dataLength) continue;
4560
- const id = (_r = idCache[index]) != null ? _r : getId(state, index);
4561
- const containerIndex = containerItemKeys.get(id);
4562
- if (containerIndex !== void 0) {
4563
- state.stickyContainerPool.add(containerIndex);
4564
- }
4565
- }
4735
+ if (((_r = state.userScrollAnchorReset) == null ? void 0 : _r.keys.size) === 0) {
4736
+ state.userScrollAnchorReset = void 0;
4566
4737
  }
4567
4738
  }
4568
4739
  if (state.stickyContainerPool.size > 0) {
@@ -4573,7 +4744,7 @@ function calculateItemsInView(ctx, params = {}) {
4573
4744
  drawDistance,
4574
4745
  (_s = stickyState == null ? void 0 : stickyState.currentStickyIdx) != null ? _s : -1,
4575
4746
  pendingRemoval,
4576
- alwaysRenderSet
4747
+ isPinnedRenderIndex
4577
4748
  );
4578
4749
  }
4579
4750
  const pendingRemovalSet = pendingRemoval.length > 0 ? new Set(pendingRemoval) : void 0;
@@ -4591,6 +4762,7 @@ function calculateItemsInView(ctx, params = {}) {
4591
4762
  }
4592
4763
  set$(ctx, `containerItemKey${i}`, void 0);
4593
4764
  set$(ctx, `containerItemData${i}`, void 0);
4765
+ set$(ctx, `containerItemInfo${i}`, void 0);
4594
4766
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
4595
4767
  set$(ctx, `containerColumn${i}`, -1);
4596
4768
  set$(ctx, `containerSpan${i}`, 1);
@@ -4745,6 +4917,36 @@ function mergeItemSizeUpdateResult(result, next) {
4745
4917
  result.needsRecalculate || (result.needsRecalculate = next.needsRecalculate);
4746
4918
  result.shouldMaintainScrollAtEnd || (result.shouldMaintainScrollAtEnd = next.shouldMaintainScrollAtEnd);
4747
4919
  }
4920
+ var batchedItemSizeRecalculates = /* @__PURE__ */ new WeakMap();
4921
+ function flushBatchedItemSizeRecalculate(ctx, didFallback = false, expectedResult) {
4922
+ const result = batchedItemSizeRecalculates.get(ctx);
4923
+ if (!result || expectedResult && result !== expectedResult) {
4924
+ return;
4925
+ }
4926
+ batchedItemSizeRecalculates.delete(ctx);
4927
+ if (didFallback) {
4928
+ ctx.state.pendingLayoutEffectMeasurements = void 0;
4929
+ }
4930
+ flushItemSizeUpdates(ctx, result);
4931
+ }
4932
+ function queueItemSizeRecalculate(ctx, result) {
4933
+ var _a3, _b;
4934
+ const batch = batchedItemSizeRecalculates.get(ctx);
4935
+ if (batch) {
4936
+ mergeItemSizeUpdateResult(batch, result);
4937
+ } else {
4938
+ const nextBatch = { ...result };
4939
+ batchedItemSizeRecalculates.set(ctx, nextBatch);
4940
+ if ((_a3 = ctx.state.pendingLayoutEffectMeasurements) == null ? void 0 : _a3.size) {
4941
+ requestAnimationFrame(() => {
4942
+ flushBatchedItemSizeRecalculate(ctx, true, nextBatch);
4943
+ });
4944
+ }
4945
+ }
4946
+ if (!((_b = ctx.state.pendingLayoutEffectMeasurements) == null ? void 0 : _b.size)) {
4947
+ flushBatchedItemSizeRecalculate(ctx);
4948
+ }
4949
+ }
4748
4950
  function flushItemSizeUpdates(ctx, result) {
4749
4951
  var _a3;
4750
4952
  const state = ctx.state;
@@ -4761,12 +4963,22 @@ function flushItemSizeUpdates(ctx, result) {
4761
4963
  function updateItemSizes(ctx, measurement) {
4762
4964
  var _a3, _b, _c, _d;
4763
4965
  const state = ctx.state;
4966
+ let didDrainLayoutEffectMeasurements = false;
4967
+ if (IsNewArchitecture && measurement.fromLayoutEffect) {
4968
+ const pendingLayoutEffectMeasurements = state.pendingLayoutEffectMeasurements;
4969
+ if ((pendingLayoutEffectMeasurements == null ? void 0 : pendingLayoutEffectMeasurements.delete(measurement.itemKey)) && pendingLayoutEffectMeasurements.size === 0) {
4970
+ state.pendingLayoutEffectMeasurements = void 0;
4971
+ didDrainLayoutEffectMeasurements = true;
4972
+ }
4973
+ }
4764
4974
  const pendingKeys = (_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys;
4765
4975
  const shouldBatchPendingMeasurements = IsNewArchitecture && measurement.fromLayoutEffect && !!(pendingKeys == null ? void 0 : pendingKeys.size);
4976
+ const shouldQueueRecalculate = IsNewArchitecture && !!measurement.fromLayoutEffect;
4977
+ let result;
4766
4978
  if (!shouldBatchPendingMeasurements) {
4767
- flushItemSizeUpdates(ctx, applyItemSize(ctx, measurement.itemKey, measurement.size));
4979
+ result = applyItemSize(ctx, measurement.itemKey, measurement.size);
4768
4980
  } else {
4769
- const result = {};
4981
+ result = {};
4770
4982
  const updateContainerItemSize = (itemKey, containerId, size) => {
4771
4983
  if (containerId !== void 0 && peek$(ctx, `containerItemKey${containerId}`) === itemKey) {
4772
4984
  mergeItemSizeUpdateResult(result, applyItemSize(ctx, itemKey, size));
@@ -4784,8 +4996,15 @@ function updateItemSizes(ctx, measurement) {
4784
4996
  });
4785
4997
  }
4786
4998
  }
4999
+ }
5000
+ if (shouldQueueRecalculate && result.needsRecalculate) {
5001
+ queueItemSizeRecalculate(ctx, result);
5002
+ } else {
4787
5003
  flushItemSizeUpdates(ctx, result);
4788
5004
  }
5005
+ if (didDrainLayoutEffectMeasurements) {
5006
+ flushBatchedItemSizeRecalculate(ctx);
5007
+ }
4789
5008
  }
4790
5009
  function applyItemSize(ctx, itemKey, sizeObj) {
4791
5010
  var _a3;
@@ -5113,12 +5332,9 @@ var Container = typedMemo(function Container2({
5113
5332
  ctx.viewRefs.set(id, ref);
5114
5333
  return {
5115
5334
  containerId: id,
5116
- index,
5117
- itemKey,
5118
- triggerLayout,
5119
- value: data
5335
+ triggerLayout
5120
5336
  };
5121
- }, [id, itemKey, index, data, triggerLayout]);
5337
+ }, [id, triggerLayout]);
5122
5338
  useLayoutEffect(() => {
5123
5339
  ctx.containerLayoutTriggers.set(id, triggerLayout);
5124
5340
  return () => {