@legendapp/list 3.0.3 → 3.0.5

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.
@@ -1,5 +1,5 @@
1
1
  import * as React3 from 'react';
2
- import { forwardRef, useReducer, useEffect, createContext, useRef, useState, useMemo, useCallback, useImperativeHandle, useLayoutEffect, useContext } from 'react';
2
+ import { forwardRef, useReducer, useEffect, createContext, useRef, useState, useMemo, useCallback, useLayoutEffect, useImperativeHandle, useContext } from 'react';
3
3
  import { useSyncExternalStore } from 'use-sync-external-store/shim';
4
4
  import * as ReactDOM from 'react-dom';
5
5
  import { flushSync } from 'react-dom';
@@ -132,6 +132,7 @@ function StateProvider({ children }) {
132
132
  const [value] = React3.useState(() => ({
133
133
  animatedScrollY: createAnimatedValue(0),
134
134
  columnWrapperStyle: void 0,
135
+ containerLayoutTriggers: /* @__PURE__ */ new Map(),
135
136
  contextNum: contextNum++,
136
137
  listeners: /* @__PURE__ */ new Map(),
137
138
  mapViewabilityAmountCallbacks: /* @__PURE__ */ new Map(),
@@ -399,9 +400,6 @@ function roundSize(size) {
399
400
  function isNullOrUndefined(value) {
400
401
  return value === null || value === void 0;
401
402
  }
402
- function comparatorDefault(a, b) {
403
- return a - b;
404
- }
405
403
  function getPadding(s, type) {
406
404
  var _a3, _b, _c;
407
405
  const axisPadding = type === "Left" || type === "Right" ? s.paddingHorizontal : s.paddingVertical;
@@ -909,18 +907,6 @@ var Container = typedMemo(function Container2({
909
907
  [itemKey, data, extraData]
910
908
  );
911
909
  const { index, renderedItem } = renderedItemInfo || {};
912
- const contextValue = useMemo(() => {
913
- ctx.viewRefs.set(id, ref);
914
- return {
915
- containerId: id,
916
- index,
917
- itemKey,
918
- triggerLayout: () => {
919
- forceLayoutRender((v) => v + 1);
920
- },
921
- value: data
922
- };
923
- }, [id, itemKey, index, data]);
924
910
  const onLayoutChange = useCallback((rectangle) => {
925
911
  const {
926
912
  horizontal: currentHorizontal,
@@ -964,6 +950,27 @@ var Container = typedMemo(function Container2({
964
950
  doUpdate();
965
951
  }
966
952
  }, []);
953
+ const triggerLayout = useCallback(() => {
954
+ forceLayoutRender((v) => v + 1);
955
+ }, []);
956
+ const contextValue = useMemo(() => {
957
+ ctx.viewRefs.set(id, ref);
958
+ return {
959
+ containerId: id,
960
+ index,
961
+ itemKey,
962
+ triggerLayout,
963
+ value: data
964
+ };
965
+ }, [id, itemKey, index, data, triggerLayout]);
966
+ useLayoutEffect(() => {
967
+ ctx.containerLayoutTriggers.set(id, triggerLayout);
968
+ return () => {
969
+ if (ctx.containerLayoutTriggers.get(id) === triggerLayout) {
970
+ ctx.containerLayoutTriggers.delete(id);
971
+ }
972
+ };
973
+ }, [ctx, id, triggerLayout]);
967
974
  const { onLayout } = useOnLayoutSync(
968
975
  {
969
976
  onLayoutChange,
@@ -2441,17 +2448,42 @@ function setSize(ctx, itemKey, size, notifyTotalSize = true) {
2441
2448
  }
2442
2449
 
2443
2450
  // src/utils/getItemSize.ts
2444
- function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize, notifyTotalSize) {
2445
- var _a3, _b, _c;
2451
+ function getKnownOrFixedSize(ctx, key, index, data, resolved) {
2452
+ var _a3, _b;
2453
+ const state = ctx.state;
2454
+ const { getFixedItemSize, getItemType } = state.props;
2455
+ let size = key ? state.sizesKnown.get(key) : void 0;
2456
+ if (size === void 0 && key && getFixedItemSize) {
2457
+ const itemType = (_b = resolved == null ? void 0 : resolved.itemType) != null ? _b : getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
2458
+ size = (resolved == null ? void 0 : resolved.didResolveFixedItemSize) ? resolved.fixedItemSize : getFixedItemSize(data, index, itemType);
2459
+ if (size !== void 0) {
2460
+ state.sizesKnown.set(key, size);
2461
+ }
2462
+ }
2463
+ return size;
2464
+ }
2465
+ function getKnownOrFixedItemSize(ctx, index) {
2466
+ const key = getId(ctx.state, index);
2467
+ return getKnownOrFixedSize(ctx, key, index, ctx.state.props.data[index]);
2468
+ }
2469
+ function areKnownOrFixedItemSizesAvailable(ctx, startIndex, endIndex) {
2470
+ for (let index = startIndex; index <= endIndex; index++) {
2471
+ if (getKnownOrFixedItemSize(ctx, index) === void 0) {
2472
+ return false;
2473
+ }
2474
+ }
2475
+ return true;
2476
+ }
2477
+ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize, notifyTotalSize, resolved) {
2478
+ var _a3, _b, _c, _d;
2446
2479
  const state = ctx.state;
2447
2480
  const {
2448
- sizesKnown,
2449
2481
  sizes,
2450
2482
  averageSizes,
2451
- props: { estimatedItemSize, getFixedItemSize, getItemType },
2483
+ props: { estimatedItemSize, getItemType },
2452
2484
  scrollingTo
2453
2485
  } = state;
2454
- const sizeKnown = sizesKnown.get(key);
2486
+ const sizeKnown = state.sizesKnown.get(key);
2455
2487
  if (sizeKnown !== void 0) {
2456
2488
  return sizeKnown;
2457
2489
  }
@@ -2462,15 +2494,14 @@ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize, no
2462
2494
  return renderedSize;
2463
2495
  }
2464
2496
  }
2465
- const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
2466
- if (getFixedItemSize) {
2467
- size = getFixedItemSize(data, index, itemType);
2468
- if (size !== void 0) {
2469
- sizesKnown.set(key, size);
2470
- }
2497
+ size = getKnownOrFixedSize(ctx, key, index, data, resolved);
2498
+ if (size !== void 0) {
2499
+ setSize(ctx, key, size, notifyTotalSize);
2500
+ return size;
2471
2501
  }
2472
- if (size === void 0 && useAverageSize && sizeKnown === void 0 && !scrollingTo) {
2473
- const averageSizeForType = (_b = averageSizes[itemType]) == null ? void 0 : _b.avg;
2502
+ const itemType = (_b = resolved == null ? void 0 : resolved.itemType) != null ? _b : getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
2503
+ if (useAverageSize && !scrollingTo) {
2504
+ const averageSizeForType = (_c = averageSizes[itemType]) == null ? void 0 : _c.avg;
2474
2505
  if (averageSizeForType !== void 0) {
2475
2506
  size = roundSize(averageSizeForType);
2476
2507
  }
@@ -2478,8 +2509,8 @@ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize, no
2478
2509
  if (size === void 0 && renderedSize !== void 0) {
2479
2510
  return renderedSize;
2480
2511
  }
2481
- if (size === void 0 && useAverageSize && sizeKnown === void 0 && scrollingTo) {
2482
- const averageSizeForType = (_c = scrollingTo.averageSizeSnapshot) == null ? void 0 : _c[itemType];
2512
+ if (size === void 0 && useAverageSize && scrollingTo) {
2513
+ const averageSizeForType = (_d = scrollingTo.averageSizeSnapshot) == null ? void 0 : _d[itemType];
2483
2514
  if (averageSizeForType !== void 0) {
2484
2515
  size = roundSize(averageSizeForType);
2485
2516
  }
@@ -3040,8 +3071,8 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
3040
3071
  if ((options == null ? void 0 : options.markHasScrolled) !== false) {
3041
3072
  state.hasScrolled = true;
3042
3073
  }
3043
- state.lastBatchingAction = Date.now();
3044
3074
  const currentTime = Date.now();
3075
+ state.lastBatchingAction = currentTime;
3045
3076
  const adjust = scrollAdjustHandler.getAdjust();
3046
3077
  const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
3047
3078
  if (adjustChanged) {
@@ -3350,26 +3381,22 @@ function advanceCurrentInitialScrollSession(ctx, options) {
3350
3381
  }
3351
3382
 
3352
3383
  // src/utils/checkAllSizesKnown.ts
3353
- function isNullOrUndefined2(value) {
3354
- return value === null || value === void 0;
3355
- }
3356
- function getMountedIndicesInRange(state, start, end) {
3357
- if (!isNullOrUndefined2(end) && !isNullOrUndefined2(start) && start >= 0 && end >= 0) {
3358
- return Array.from(state.containerItemKeys.keys()).map((key) => state.indexByKey.get(key)).filter((index) => index !== void 0 && index >= start && index <= end).sort((a, b) => a - b);
3384
+ function checkAllSizesKnown(state, start, end) {
3385
+ if (start == null || end == null || start < 0 || end < start) {
3386
+ return false;
3359
3387
  }
3360
- return [];
3361
- }
3362
- function getMountedBufferedIndices(state) {
3363
- return getMountedIndicesInRange(state, state.startBuffered, state.endBuffered);
3364
- }
3365
- function getMountedNoBufferIndices(state) {
3366
- return getMountedIndicesInRange(state, state.startNoBuffer, state.endNoBuffer);
3367
- }
3368
- function checkAllSizesKnown(state, indices) {
3369
- return indices.length > 0 && indices.every((index) => {
3370
- const key = getId(state, index);
3371
- return key !== void 0 && state.sizesKnown.has(key);
3372
- });
3388
+ let hasMountedIndex = false;
3389
+ for (const key of state.containerItemKeys.keys()) {
3390
+ const index = state.indexByKey.get(key);
3391
+ if (index !== void 0 && index >= start && index <= end) {
3392
+ hasMountedIndex = true;
3393
+ const id = getId(state, index);
3394
+ if (id === void 0 || !state.sizesKnown.has(id)) {
3395
+ return false;
3396
+ }
3397
+ }
3398
+ }
3399
+ return hasMountedIndex;
3373
3400
  }
3374
3401
 
3375
3402
  // src/core/bootstrapInitialScroll.ts
@@ -3870,8 +3897,7 @@ function evaluateBootstrapInitialScroll(ctx) {
3870
3897
  bootstrapInitialScroll.targetIndexSeed = void 0;
3871
3898
  }
3872
3899
  const resolvedOffset = resolveInitialScrollOffset(ctx, initialScroll);
3873
- const mountedBufferedIndices = getMountedBufferedIndices(state);
3874
- const areMountedBufferedIndicesMeasured = checkAllSizesKnown(state, mountedBufferedIndices);
3900
+ const areMountedBufferedIndicesMeasured = checkAllSizesKnown(state, state.startBuffered, state.endBuffered);
3875
3901
  const didResolvedOffsetChange = Math.abs(bootstrapInitialScroll.scroll - resolvedOffset) > 1;
3876
3902
  const { data } = state.props;
3877
3903
  const visibleIndices = getBootstrapRevealVisibleIndices({
@@ -4447,7 +4473,14 @@ function updateSnapToOffsets(ctx) {
4447
4473
  }
4448
4474
 
4449
4475
  // src/core/updateItemPositions.ts
4450
- function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP, optimizeForVisibleWindow = false } = {
4476
+ function updateItemPositions(ctx, dataChanged, {
4477
+ doMVCP,
4478
+ forceFullUpdate = false,
4479
+ optimizeForVisibleWindow = false,
4480
+ scrollBottomBuffered,
4481
+ scrollVelocity,
4482
+ startIndex
4483
+ } = {
4451
4484
  doMVCP: false,
4452
4485
  forceFullUpdate: false,
4453
4486
  optimizeForVisibleWindow: false,
@@ -4474,7 +4507,7 @@ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffere
4474
4507
  const extraData = peek$(ctx, "extraData");
4475
4508
  const layoutConfig = overrideItemLayout ? { span: 1 } : void 0;
4476
4509
  const lastScrollDelta = state.lastScrollDelta;
4477
- const velocity = getScrollVelocity(state);
4510
+ const velocity = scrollVelocity != null ? scrollVelocity : getScrollVelocity(state);
4478
4511
  const shouldOptimize = !forceFullUpdate && !dataChanged && (optimizeForVisibleWindow || Math.abs(velocity) > 0 || state.scrollLength > 0 && lastScrollDelta > state.scrollLength);
4479
4512
  const maxVisibleArea = scrollBottomBuffered + 1e3;
4480
4513
  const useAverageSize = true;
@@ -4654,25 +4687,28 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
4654
4687
  const configId = viewabilityConfig.id;
4655
4688
  const viewabilityState = ensureViewabilityState(ctx, configId);
4656
4689
  const { viewableItems: previousViewableItems, start, end, startBuffered, endBuffered } = viewabilityState;
4657
- const viewabilityTokens = /* @__PURE__ */ new Map();
4690
+ let staleViewabilityAmountIds;
4658
4691
  for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
4659
- viewabilityTokens.set(
4692
+ const nextValue = computeViewability(
4693
+ state,
4694
+ ctx,
4695
+ viewabilityConfig,
4660
4696
  containerId,
4661
- computeViewability(
4662
- state,
4663
- ctx,
4664
- viewabilityConfig,
4665
- containerId,
4666
- value.key,
4667
- scrollSize,
4668
- value.item,
4669
- value.index
4670
- )
4697
+ value.key,
4698
+ scrollSize,
4699
+ value.item,
4700
+ value.index
4671
4701
  );
4702
+ if (nextValue.sizeVisible < 0) {
4703
+ staleViewabilityAmountIds != null ? staleViewabilityAmountIds : staleViewabilityAmountIds = [];
4704
+ staleViewabilityAmountIds.push(containerId);
4705
+ }
4672
4706
  }
4673
4707
  const changed = [];
4708
+ const previousViewableKeys = /* @__PURE__ */ new Set();
4674
4709
  if (previousViewableItems) {
4675
4710
  for (const viewToken of previousViewableItems) {
4711
+ previousViewableKeys.add(viewToken.key);
4676
4712
  const containerId = findContainerId(ctx, viewToken.key);
4677
4713
  if (!checkIsViewable(
4678
4714
  state,
@@ -4704,7 +4740,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
4704
4740
  key
4705
4741
  };
4706
4742
  viewableItems.push(viewToken);
4707
- if (!(previousViewableItems == null ? void 0 : previousViewableItems.find((v) => v.key === viewToken.key))) {
4743
+ if (!previousViewableKeys.has(viewToken.key)) {
4708
4744
  changed.push(viewToken);
4709
4745
  }
4710
4746
  }
@@ -4725,20 +4761,17 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
4725
4761
  onViewableItemsChanged({ changed, end, endBuffered, start, startBuffered, viewableItems });
4726
4762
  }
4727
4763
  }
4728
- for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
4729
- if (value.sizeVisible < 0) {
4730
- ctx.mapViewabilityAmountValues.delete(containerId);
4764
+ if (staleViewabilityAmountIds) {
4765
+ for (const containerId of staleViewabilityAmountIds) {
4766
+ const value = ctx.mapViewabilityAmountValues.get(containerId);
4767
+ if (value && value.sizeVisible < 0) {
4768
+ ctx.mapViewabilityAmountValues.delete(containerId);
4769
+ }
4731
4770
  }
4732
4771
  }
4733
4772
  }
4734
- function shallowEqual(prev, next) {
4735
- if (!prev) return false;
4736
- const keys = Object.keys(next);
4737
- for (let i = 0; i < keys.length; i++) {
4738
- const k = keys[i];
4739
- if (prev[k] !== next[k]) return false;
4740
- }
4741
- return true;
4773
+ function areViewabilityAmountTokensEqual(prev, next) {
4774
+ return !!prev && prev.containerId === next.containerId && prev.index === next.index && prev.isViewable === next.isViewable && prev.item === next.item && prev.key === next.key && prev.percentOfScroller === next.percentOfScroller && prev.percentVisible === next.percentVisible && prev.scrollSize === next.scrollSize && prev.size === next.size && prev.sizeVisible === next.sizeVisible;
4742
4775
  }
4743
4776
  function computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
4744
4777
  const { sizes, scroll: scrollState } = state;
@@ -4763,7 +4796,7 @@ function computeViewability(state, ctx, viewabilityConfig, containerId, key, scr
4763
4796
  sizeVisible: -1
4764
4797
  };
4765
4798
  const prev2 = ctx.mapViewabilityAmountValues.get(containerId);
4766
- if (!shallowEqual(prev2, value2)) {
4799
+ if (!areViewabilityAmountTokensEqual(prev2, value2)) {
4767
4800
  ctx.mapViewabilityAmountValues.set(containerId, value2);
4768
4801
  const cb = ctx.mapViewabilityAmountCallbacks.get(containerId);
4769
4802
  if (cb) {
@@ -4793,7 +4826,7 @@ function computeViewability(state, ctx, viewabilityConfig, containerId, key, scr
4793
4826
  sizeVisible
4794
4827
  };
4795
4828
  const prev = ctx.mapViewabilityAmountValues.get(containerId);
4796
- if (!shallowEqual(prev, value)) {
4829
+ if (!areViewabilityAmountTokensEqual(prev, value)) {
4797
4830
  ctx.mapViewabilityAmountValues.set(containerId, value);
4798
4831
  const cb = ctx.mapViewabilityAmountCallbacks.get(containerId);
4799
4832
  if (cb) {
@@ -4844,126 +4877,152 @@ function getExpandedContainerPoolSize(dataLength, numContainers) {
4844
4877
  }
4845
4878
 
4846
4879
  // src/utils/findAvailableContainers.ts
4847
- function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers, protectedKeys) {
4880
+ function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffered, pendingRemoval, getRequiredItemType, protectedKeys) {
4881
+ const numNeeded = needNewContainers.length;
4882
+ if (numNeeded === 0) {
4883
+ return [];
4884
+ }
4848
4885
  const numContainers = peek$(ctx, "numContainers");
4849
4886
  const state = ctx.state;
4850
4887
  const { stickyContainerPool, containerItemTypes } = state;
4851
4888
  const shouldAvoidAssignedContainerReuse = state.props.recycleItems && !!state.props.positionComponentInternal;
4852
- const result = [];
4853
- const availableContainers = [];
4854
- const pendingRemovalSet = new Set(pendingRemoval);
4889
+ const allocations = [];
4890
+ const pendingRemovalSet = pendingRemoval.length > 0 ? new Set(pendingRemoval) : void 0;
4855
4891
  let pendingRemovalChanged = false;
4892
+ let nextNewContainerIndex = numContainers;
4893
+ const usedContainers = /* @__PURE__ */ new Set();
4894
+ let availableContainers;
4856
4895
  const stickyHeaderIndicesSet = state.props.stickyHeaderIndicesSet;
4857
- const stickyHeaderItemIndices = (needNewContainers == null ? void 0 : needNewContainers.filter((index) => stickyHeaderIndicesSet.has(index))) || [];
4858
4896
  const canReuseContainer = (containerIndex, requiredType) => {
4859
4897
  if (!requiredType) return true;
4860
4898
  const existingType = containerItemTypes.get(containerIndex);
4861
4899
  if (!existingType) return true;
4862
4900
  return existingType === requiredType;
4863
4901
  };
4864
- const neededTypes = requiredItemTypes ? [...requiredItemTypes] : [];
4865
- let typeIndex = 0;
4866
- for (let i = 0; i < stickyHeaderItemIndices.length; i++) {
4867
- const requiredType = neededTypes[typeIndex];
4868
- let foundContainer = false;
4869
- for (const containerIndex of stickyContainerPool) {
4870
- const key = peek$(ctx, `containerItemKey${containerIndex}`);
4871
- const isPendingRemoval = pendingRemovalSet.has(containerIndex);
4872
- if ((key === void 0 || isPendingRemoval) && canReuseContainer(containerIndex, requiredType) && !result.includes(containerIndex)) {
4873
- result.push(containerIndex);
4874
- if (isPendingRemoval && pendingRemovalSet.delete(containerIndex)) {
4875
- pendingRemovalChanged = true;
4876
- }
4877
- foundContainer = true;
4878
- if (requiredItemTypes) typeIndex++;
4879
- break;
4880
- }
4902
+ const pushAllocation = (itemIndex, itemType, containerIndex) => {
4903
+ allocations.push({
4904
+ containerIndex,
4905
+ itemIndex,
4906
+ itemType
4907
+ });
4908
+ usedContainers.add(containerIndex);
4909
+ if (pendingRemovalSet == null ? void 0 : pendingRemovalSet.delete(containerIndex)) {
4910
+ pendingRemovalChanged = true;
4881
4911
  }
4882
- if (!foundContainer) {
4883
- const newContainerIndex = numContainers + result.filter((index) => index >= numContainers).length;
4884
- result.push(newContainerIndex);
4912
+ };
4913
+ const pushNewContainer = (itemIndex, itemType, isSticky) => {
4914
+ const newContainerIndex = nextNewContainerIndex++;
4915
+ pushAllocation(itemIndex, itemType, newContainerIndex);
4916
+ if (isSticky) {
4885
4917
  stickyContainerPool.add(newContainerIndex);
4886
- if (requiredItemTypes) typeIndex++;
4887
- }
4888
- }
4889
- for (let u = 0; u < numContainers && result.length < numNeeded; u++) {
4890
- if (stickyContainerPool.has(u)) {
4891
- continue;
4892
4918
  }
4893
- const key = peek$(ctx, `containerItemKey${u}`);
4894
- const requiredType = neededTypes[typeIndex];
4895
- const isPending = key !== void 0 && pendingRemovalSet.has(u);
4896
- const canUse = key === void 0 || isPending && canReuseContainer(u, requiredType);
4897
- if (canUse) {
4898
- if (isPending) {
4899
- pendingRemovalSet.delete(u);
4900
- pendingRemovalChanged = true;
4901
- }
4902
- result.push(u);
4903
- if (requiredItemTypes) {
4904
- typeIndex++;
4905
- }
4919
+ return newContainerIndex;
4920
+ };
4921
+ const canUseContainer = (containerIndex, itemType) => {
4922
+ if (usedContainers.has(containerIndex) || stickyContainerPool.has(containerIndex)) {
4923
+ return false;
4906
4924
  }
4907
- }
4908
- if (!shouldAvoidAssignedContainerReuse) {
4909
- for (let u = 0; u < numContainers && result.length < numNeeded; u++) {
4910
- if (stickyContainerPool.has(u)) {
4911
- continue;
4912
- }
4913
- const key = peek$(ctx, `containerItemKey${u}`);
4914
- if (key === void 0) continue;
4915
- if ((protectedKeys == null ? void 0 : protectedKeys.has(key)) && state.indexByKey.has(key)) continue;
4916
- const index = state.indexByKey.get(key);
4917
- const isOutOfView = index < startBuffered || index > endBuffered;
4918
- if (isOutOfView) {
4919
- const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
4920
- if (!requiredItemTypes || typeIndex < neededTypes.length && canReuseContainer(u, neededTypes[typeIndex])) {
4921
- availableContainers.push({ distance, index: u });
4925
+ const key = peek$(ctx, `containerItemKey${containerIndex}`);
4926
+ const isPending = !!(pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(containerIndex));
4927
+ return (key === void 0 || isPending) && canReuseContainer(containerIndex, itemType);
4928
+ };
4929
+ const findStickyContainer = (itemType) => {
4930
+ let foundContainer;
4931
+ for (const containerIndex of stickyContainerPool) {
4932
+ if (!usedContainers.has(containerIndex)) {
4933
+ const key = peek$(ctx, `containerItemKey${containerIndex}`);
4934
+ const isPendingRemoval = !!(pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(containerIndex));
4935
+ if ((key === void 0 || isPendingRemoval) && canReuseContainer(containerIndex, itemType)) {
4936
+ foundContainer = containerIndex;
4937
+ break;
4922
4938
  }
4923
4939
  }
4924
4940
  }
4925
- }
4926
- const remaining = numNeeded - result.length;
4927
- if (remaining > 0) {
4928
- if (availableContainers.length > 0) {
4929
- if (availableContainers.length > remaining) {
4930
- availableContainers.sort(comparatorByDistance);
4931
- availableContainers.length = remaining;
4941
+ return foundContainer;
4942
+ };
4943
+ const findUnassignedOrPendingContainer = (itemType) => {
4944
+ let foundContainer;
4945
+ for (let containerIndex = 0; containerIndex < numContainers && foundContainer === void 0; containerIndex++) {
4946
+ if (canUseContainer(containerIndex, itemType)) {
4947
+ foundContainer = containerIndex;
4932
4948
  }
4933
- for (const container of availableContainers) {
4934
- result.push(container.index);
4935
- if (requiredItemTypes) {
4936
- typeIndex++;
4949
+ }
4950
+ return foundContainer;
4951
+ };
4952
+ const getAvailableContainers = () => {
4953
+ if (!availableContainers) {
4954
+ availableContainers = [];
4955
+ if (!shouldAvoidAssignedContainerReuse) {
4956
+ for (let containerIndex = 0; containerIndex < numContainers; containerIndex++) {
4957
+ if (usedContainers.has(containerIndex) || stickyContainerPool.has(containerIndex)) {
4958
+ continue;
4959
+ }
4960
+ const key = peek$(ctx, `containerItemKey${containerIndex}`);
4961
+ if (key === void 0) continue;
4962
+ if ((protectedKeys == null ? void 0 : protectedKeys.has(key)) && state.indexByKey.has(key)) continue;
4963
+ const index = state.indexByKey.get(key);
4964
+ const isOutOfView = index < startBuffered || index > endBuffered;
4965
+ if (isOutOfView) {
4966
+ const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
4967
+ availableContainers.push({ distance, index: containerIndex });
4968
+ }
4937
4969
  }
4970
+ availableContainers.sort(comparatorByDistance);
4938
4971
  }
4939
4972
  }
4940
- const stillNeeded = numNeeded - result.length;
4941
- if (stillNeeded > 0) {
4942
- for (let i = 0; i < stillNeeded; i++) {
4943
- result.push(numContainers + i);
4973
+ return availableContainers;
4974
+ };
4975
+ const findAvailableContainer = (itemType) => {
4976
+ const containers = getAvailableContainers();
4977
+ let matchIndex = -1;
4978
+ for (let i = 0; i < containers.length && matchIndex === -1; i++) {
4979
+ const containerIndex = containers[i].index;
4980
+ if (!usedContainers.has(containerIndex) && canReuseContainer(containerIndex, itemType)) {
4981
+ matchIndex = i;
4944
4982
  }
4945
- if (IS_DEV && numContainers + stillNeeded > peek$(ctx, "numContainersPooled")) {
4946
- console.warn(
4947
- "[legend-list] No unused container available, so creating one on demand. This can be a minor performance issue and is likely caused by the estimatedItemSize being too large. Consider decreasing estimatedItemSize.",
4948
- {
4949
- debugInfo: {
4950
- numContainers,
4951
- numContainersPooled: peek$(ctx, "numContainersPooled"),
4952
- numNeeded,
4953
- stillNeeded
4954
- }
4955
- }
4956
- );
4983
+ }
4984
+ return matchIndex === -1 ? void 0 : containers.splice(matchIndex, 1)[0].index;
4985
+ };
4986
+ for (const itemIndex of needNewContainers) {
4987
+ const itemType = getRequiredItemType == null ? void 0 : getRequiredItemType(itemIndex);
4988
+ const isSticky = stickyHeaderIndicesSet.has(itemIndex);
4989
+ let containerIndex;
4990
+ if (isSticky) {
4991
+ containerIndex = findStickyContainer(itemType);
4992
+ } else {
4993
+ containerIndex = findUnassignedOrPendingContainer(itemType);
4994
+ if (containerIndex === void 0) {
4995
+ containerIndex = findAvailableContainer(itemType);
4957
4996
  }
4958
4997
  }
4998
+ if (containerIndex !== void 0) {
4999
+ pushAllocation(itemIndex, itemType, containerIndex);
5000
+ } else {
5001
+ pushNewContainer(itemIndex, itemType, isSticky);
5002
+ }
4959
5003
  }
4960
5004
  if (pendingRemovalChanged) {
4961
5005
  pendingRemoval.length = 0;
4962
- for (const value of pendingRemovalSet) {
4963
- pendingRemoval.push(value);
5006
+ if (pendingRemovalSet) {
5007
+ for (const value of pendingRemovalSet) {
5008
+ pendingRemoval.push(value);
5009
+ }
4964
5010
  }
4965
5011
  }
4966
- return result.sort(comparatorDefault);
5012
+ if (IS_DEV && nextNewContainerIndex > peek$(ctx, "numContainersPooled")) {
5013
+ console.warn(
5014
+ "[legend-list] No unused container available, so creating one on demand. This can be a minor performance issue and is likely caused by the estimatedItemSize being too large. Consider decreasing estimatedItemSize.",
5015
+ {
5016
+ debugInfo: {
5017
+ numContainers,
5018
+ numContainersPooled: peek$(ctx, "numContainersPooled"),
5019
+ numNeeded,
5020
+ stillNeeded: nextNewContainerIndex - numContainers
5021
+ }
5022
+ }
5023
+ );
5024
+ }
5025
+ return allocations;
4967
5026
  }
4968
5027
  function comparatorByDistance(a, b) {
4969
5028
  return b.distance - a.distance;
@@ -4989,21 +5048,28 @@ function findCurrentStickyIndex(stickyArray, scroll, state) {
4989
5048
  }
4990
5049
  return -1;
4991
5050
  }
4992
- function getActiveStickyIndices(ctx, stickyHeaderIndices) {
5051
+ function isStickyIndexActive(ctx, targetIndex) {
4993
5052
  const state = ctx.state;
4994
- return new Set(
4995
- Array.from(state.stickyContainerPool).map((i) => peek$(ctx, `containerItemKey${i}`)).map((key) => key ? state.indexByKey.get(key) : void 0).filter((idx) => idx !== void 0 && stickyHeaderIndices.has(idx))
4996
- );
5053
+ let isActive = false;
5054
+ for (const containerIndex of state.stickyContainerPool) {
5055
+ const key = peek$(ctx, `containerItemKey${containerIndex}`);
5056
+ const itemIndex = key ? state.indexByKey.get(key) : void 0;
5057
+ if (itemIndex === targetIndex) {
5058
+ isActive = true;
5059
+ break;
5060
+ }
5061
+ }
5062
+ return isActive;
4997
5063
  }
4998
- function handleStickyActivation(ctx, stickyHeaderIndices, stickyArray, currentStickyIdx, needNewContainers, needNewContainersSet, startBuffered, endBuffered) {
5064
+ function handleStickyActivation(ctx, stickyArray, currentStickyIdx, needNewContainers, needNewContainersSet, startBuffered, endBuffered) {
4999
5065
  var _a3;
5000
5066
  const state = ctx.state;
5001
- const activeIndices = getActiveStickyIndices(ctx, stickyHeaderIndices);
5002
5067
  set$(ctx, "activeStickyIndex", currentStickyIdx >= 0 ? stickyArray[currentStickyIdx] : -1);
5003
5068
  for (let offset = 0; offset <= 1; offset++) {
5004
5069
  const idx = currentStickyIdx - offset;
5005
- if (idx < 0 || activeIndices.has(stickyArray[idx])) continue;
5070
+ if (idx < 0) continue;
5006
5071
  const stickyIndex = stickyArray[idx];
5072
+ if (isStickyIndexActive(ctx, stickyIndex)) continue;
5007
5073
  const stickyId = (_a3 = state.idCache[stickyIndex]) != null ? _a3 : getId(state, stickyIndex);
5008
5074
  if (stickyId && !state.containerItemKeys.has(stickyId) && (stickyIndex < startBuffered || stickyIndex > endBuffered) && !needNewContainersSet.has(stickyIndex)) {
5009
5075
  needNewContainersSet.add(stickyIndex);
@@ -5045,10 +5111,86 @@ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentSt
5045
5111
  }
5046
5112
  }
5047
5113
  }
5114
+ function trackVisibleRange(range, i, top, size, scroll, scrollBottom) {
5115
+ let didPassVisibleEnd = false;
5116
+ if (range.startNoBuffer === null && top + size > scroll) {
5117
+ range.startNoBuffer = i;
5118
+ }
5119
+ if (range.firstFullyOnScreenIndex === void 0 && top >= scroll - 10 && top <= scrollBottom) {
5120
+ range.firstFullyOnScreenIndex = i;
5121
+ }
5122
+ if (range.startNoBuffer !== null) {
5123
+ if (top <= scrollBottom) {
5124
+ range.endNoBuffer = i;
5125
+ } else {
5126
+ didPassVisibleEnd = true;
5127
+ }
5128
+ }
5129
+ return didPassVisibleEnd;
5130
+ }
5131
+ function getIdsInVisibleRange(state, range) {
5132
+ var _a3, _b;
5133
+ const idsInView = [];
5134
+ const firstVisibleAnchorIndex = (_a3 = range.firstFullyOnScreenIndex) != null ? _a3 : range.startNoBuffer;
5135
+ if (firstVisibleAnchorIndex !== null && firstVisibleAnchorIndex !== void 0 && range.endNoBuffer !== null) {
5136
+ for (let i = firstVisibleAnchorIndex; i <= range.endNoBuffer; i++) {
5137
+ const id = (_b = state.idCache[i]) != null ? _b : getId(state, i);
5138
+ idsInView.push(id);
5139
+ }
5140
+ }
5141
+ return idsInView;
5142
+ }
5143
+ function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, scrollLength, scroll, scrollBottom) {
5144
+ var _a3, _b;
5145
+ const state = ctx.state;
5146
+ const {
5147
+ endBuffered,
5148
+ idCache,
5149
+ positions,
5150
+ props: { data },
5151
+ sizes,
5152
+ startBuffered
5153
+ } = state;
5154
+ if (startBuffered === null || endBuffered === null || startBuffered < 0 || endBuffered < startBuffered) {
5155
+ return;
5156
+ }
5157
+ const visibleRange = {
5158
+ endNoBuffer: null,
5159
+ firstFullyOnScreenIndex: void 0,
5160
+ startNoBuffer: null
5161
+ };
5162
+ for (let i = startBuffered; i <= endBuffered && i < data.length; i++) {
5163
+ const id = (_a3 = idCache[i]) != null ? _a3 : getId(state, i);
5164
+ const size = (_b = sizes.get(id)) != null ? _b : getItemSize(ctx, id, i, data[i]);
5165
+ const top = positions[i];
5166
+ const didPassVisibleEnd = trackVisibleRange(visibleRange, i, top, size, scroll, scrollBottom);
5167
+ if (didPassVisibleEnd) {
5168
+ break;
5169
+ }
5170
+ }
5171
+ Object.assign(state, {
5172
+ endNoBuffer: visibleRange.endNoBuffer,
5173
+ firstFullyOnScreenIndex: visibleRange.firstFullyOnScreenIndex,
5174
+ idsInView: getIdsInVisibleRange(state, visibleRange),
5175
+ startNoBuffer: visibleRange.startNoBuffer
5176
+ });
5177
+ if (visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null) {
5178
+ updateViewableItems(
5179
+ state,
5180
+ ctx,
5181
+ viewabilityConfigCallbackPairs,
5182
+ scrollLength,
5183
+ visibleRange.startNoBuffer,
5184
+ visibleRange.endNoBuffer,
5185
+ startBuffered,
5186
+ endBuffered
5187
+ );
5188
+ }
5189
+ }
5048
5190
  function calculateItemsInView(ctx, params = {}) {
5049
5191
  const state = ctx.state;
5050
5192
  batchedUpdates(() => {
5051
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
5193
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
5052
5194
  const {
5053
5195
  columns,
5054
5196
  containerItemKeys,
@@ -5149,6 +5291,15 @@ function calculateItemsInView(ctx, params = {}) {
5149
5291
  state.scrollForNextCalculateItemsInView = void 0;
5150
5292
  } else if ((top === null || scrollTopBuffered > top) && (bottom === null || scrollBottomBuffered < bottom)) {
5151
5293
  if (!isInMVCPActiveMode(state)) {
5294
+ if (viewabilityConfigCallbackPairs) {
5295
+ updateViewabilityForCachedRange(
5296
+ ctx,
5297
+ viewabilityConfigCallbackPairs,
5298
+ scrollLength,
5299
+ scroll,
5300
+ scrollBottom
5301
+ );
5302
+ }
5152
5303
  finishCalculateItemsInView == null ? void 0 : finishCalculateItemsInView();
5153
5304
  return;
5154
5305
  }
@@ -5165,6 +5316,7 @@ function calculateItemsInView(ctx, params = {}) {
5165
5316
  forceFullUpdate: !!forceFullItemPositions,
5166
5317
  optimizeForVisibleWindow,
5167
5318
  scrollBottomBuffered,
5319
+ scrollVelocity: speed,
5168
5320
  startIndex
5169
5321
  });
5170
5322
  totalSize = getContentSize(ctx);
@@ -5190,10 +5342,8 @@ function calculateItemsInView(ctx, params = {}) {
5190
5342
  updateScroll2(state.scroll);
5191
5343
  updateScrollRange();
5192
5344
  }
5193
- let startNoBuffer = null;
5194
5345
  let startBuffered = null;
5195
5346
  let startBufferedId = null;
5196
- let endNoBuffer = null;
5197
5347
  let endBuffered = null;
5198
5348
  let loopStart = (_f = suppressInitialScrollSideEffects ? bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.targetIndexSeed : void 0) != null ? _f : !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
5199
5349
  for (let i = loopStart; i >= 0; i--) {
@@ -5227,19 +5377,18 @@ function calculateItemsInView(ctx, params = {}) {
5227
5377
  maxIndexRendered = Math.max(maxIndexRendered, index);
5228
5378
  }
5229
5379
  }
5230
- let firstFullyOnScreenIndex;
5380
+ const visibleRange = {
5381
+ endNoBuffer: null,
5382
+ firstFullyOnScreenIndex: void 0,
5383
+ startNoBuffer: null
5384
+ };
5231
5385
  const dataLength = data.length;
5232
5386
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
5233
5387
  const id = (_i = idCache[i]) != null ? _i : getId(state, i);
5234
5388
  const size = (_j = sizes.get(id)) != null ? _j : getItemSize(ctx, id, i, data[i]);
5235
5389
  const top = positions[i];
5236
5390
  if (!foundEnd) {
5237
- if (startNoBuffer === null && top + size > scroll) {
5238
- startNoBuffer = i;
5239
- }
5240
- if (firstFullyOnScreenIndex === void 0 && top >= scroll - 10 && top <= scrollBottom) {
5241
- firstFullyOnScreenIndex = i;
5242
- }
5391
+ trackVisibleRange(visibleRange, i, top, size, scroll, scrollBottom);
5243
5392
  if (startBuffered === null && top + size > scrollTopBuffered) {
5244
5393
  startBuffered = i;
5245
5394
  startBufferedId = id;
@@ -5249,10 +5398,7 @@ function calculateItemsInView(ctx, params = {}) {
5249
5398
  nextTop = top;
5250
5399
  }
5251
5400
  }
5252
- if (startNoBuffer !== null) {
5253
- if (top <= scrollBottom) {
5254
- endNoBuffer = i;
5255
- }
5401
+ if (visibleRange.startNoBuffer !== null) {
5256
5402
  if (top <= scrollBottomBuffered) {
5257
5403
  endBuffered = i;
5258
5404
  if (scrollBottomBuffered > totalSize) {
@@ -5266,22 +5412,14 @@ function calculateItemsInView(ctx, params = {}) {
5266
5412
  }
5267
5413
  }
5268
5414
  }
5269
- const idsInView = [];
5270
- const firstVisibleAnchorIndex = firstFullyOnScreenIndex != null ? firstFullyOnScreenIndex : startNoBuffer;
5271
- if (firstVisibleAnchorIndex !== null && firstVisibleAnchorIndex !== void 0 && endNoBuffer !== null) {
5272
- for (let i = firstVisibleAnchorIndex; i <= endNoBuffer; i++) {
5273
- const id = (_k = idCache[i]) != null ? _k : getId(state, i);
5274
- idsInView.push(id);
5275
- }
5276
- }
5277
5415
  Object.assign(state, {
5278
5416
  endBuffered,
5279
- endNoBuffer,
5280
- firstFullyOnScreenIndex,
5281
- idsInView,
5417
+ endNoBuffer: visibleRange.endNoBuffer,
5418
+ firstFullyOnScreenIndex: visibleRange.firstFullyOnScreenIndex,
5419
+ idsInView: getIdsInVisibleRange(state, visibleRange),
5282
5420
  startBuffered,
5283
5421
  startBufferedId,
5284
- startNoBuffer
5422
+ startNoBuffer: visibleRange.startNoBuffer
5285
5423
  });
5286
5424
  if (enableScrollForNextCalculateItemsInView && nextTop !== void 0 && nextBottom !== void 0) {
5287
5425
  state.scrollForNextCalculateItemsInView = isNullOrUndefined(nextTop) && isNullOrUndefined(nextBottom) ? void 0 : {
@@ -5303,7 +5441,7 @@ function calculateItemsInView(ctx, params = {}) {
5303
5441
  const needNewContainers = [];
5304
5442
  const needNewContainersSet = /* @__PURE__ */ new Set();
5305
5443
  for (let i = startBuffered; i <= endBuffered; i++) {
5306
- const id = (_l = idCache[i]) != null ? _l : getId(state, i);
5444
+ const id = (_k = idCache[i]) != null ? _k : getId(state, i);
5307
5445
  if (!containerItemKeys.has(id)) {
5308
5446
  needNewContainersSet.add(i);
5309
5447
  needNewContainers.push(i);
@@ -5312,7 +5450,7 @@ function calculateItemsInView(ctx, params = {}) {
5312
5450
  if (alwaysRenderArr.length > 0) {
5313
5451
  for (const index of alwaysRenderArr) {
5314
5452
  if (index < 0 || index >= dataLength) continue;
5315
- const id = (_m = idCache[index]) != null ? _m : getId(state, index);
5453
+ const id = (_l = idCache[index]) != null ? _l : getId(state, index);
5316
5454
  if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
5317
5455
  needNewContainersSet.add(index);
5318
5456
  needNewContainers.push(index);
@@ -5322,7 +5460,6 @@ function calculateItemsInView(ctx, params = {}) {
5322
5460
  if (stickyHeaderIndicesArr.length > 0) {
5323
5461
  handleStickyActivation(
5324
5462
  ctx,
5325
- stickyHeaderIndicesSet,
5326
5463
  stickyHeaderIndicesArr,
5327
5464
  currentStickyIdx,
5328
5465
  needNewContainers,
@@ -5334,35 +5471,34 @@ function calculateItemsInView(ctx, params = {}) {
5334
5471
  set$(ctx, "activeStickyIndex", -1);
5335
5472
  }
5336
5473
  if (needNewContainers.length > 0) {
5337
- const requiredItemTypes = getItemType ? needNewContainers.map((i) => {
5474
+ const getRequiredItemType = getItemType ? (i) => {
5338
5475
  const itemType = getItemType(data[i], i);
5339
5476
  return itemType !== void 0 ? String(itemType) : "";
5340
- }) : void 0;
5341
- const availableContainers = findAvailableContainers(
5477
+ } : void 0;
5478
+ const availableContainerAllocations = findAvailableContainers(
5342
5479
  ctx,
5343
- needNewContainers.length,
5480
+ needNewContainers,
5344
5481
  startBuffered,
5345
5482
  endBuffered,
5346
5483
  pendingRemoval,
5347
- requiredItemTypes,
5348
- needNewContainers,
5484
+ getRequiredItemType,
5349
5485
  protectedContainerKeys
5350
5486
  );
5351
- for (let idx = 0; idx < needNewContainers.length; idx++) {
5352
- const i = needNewContainers[idx];
5353
- const containerIndex = availableContainers[idx];
5354
- const id = (_n = idCache[i]) != null ? _n : getId(state, i);
5487
+ for (const allocation of availableContainerAllocations) {
5488
+ const i = allocation.itemIndex;
5489
+ const containerIndex = allocation.containerIndex;
5490
+ const id = (_m = idCache[i]) != null ? _m : getId(state, i);
5355
5491
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
5356
5492
  if (oldKey && oldKey !== id) {
5357
5493
  containerItemKeys.delete(oldKey);
5358
5494
  }
5359
5495
  set$(ctx, `containerItemKey${containerIndex}`, id);
5360
5496
  set$(ctx, `containerItemData${containerIndex}`, data[i]);
5361
- if (requiredItemTypes) {
5362
- state.containerItemTypes.set(containerIndex, requiredItemTypes[idx]);
5497
+ if (allocation.itemType !== void 0) {
5498
+ state.containerItemTypes.set(containerIndex, allocation.itemType);
5363
5499
  }
5364
5500
  containerItemKeys.set(id, containerIndex);
5365
- (_o = state.userScrollAnchorReset) == null ? void 0 : _o.keys.add(id);
5501
+ (_n = state.userScrollAnchorReset) == null ? void 0 : _n.keys.add(id);
5366
5502
  const containerSticky = `containerSticky${containerIndex}`;
5367
5503
  const isSticky = stickyHeaderIndicesSet.has(i);
5368
5504
  const isAlwaysRender = alwaysRenderSet.has(i);
@@ -5400,7 +5536,7 @@ function calculateItemsInView(ctx, params = {}) {
5400
5536
  if (alwaysRenderArr.length > 0) {
5401
5537
  for (const index of alwaysRenderArr) {
5402
5538
  if (index < 0 || index >= dataLength) continue;
5403
- const id = (_p = idCache[index]) != null ? _p : getId(state, index);
5539
+ const id = (_o = idCache[index]) != null ? _o : getId(state, index);
5404
5540
  const containerIndex = containerItemKeys.get(id);
5405
5541
  if (containerIndex !== void 0) {
5406
5542
  state.stickyContainerPool.add(containerIndex);
@@ -5419,10 +5555,11 @@ function calculateItemsInView(ctx, params = {}) {
5419
5555
  alwaysRenderSet
5420
5556
  );
5421
5557
  }
5558
+ const pendingRemovalSet = pendingRemoval.length > 0 ? new Set(pendingRemoval) : void 0;
5422
5559
  let didChangePositions = false;
5423
5560
  for (let i = 0; i < numContainers; i++) {
5424
5561
  const itemKey = peek$(ctx, `containerItemKey${i}`);
5425
- if (pendingRemoval.includes(i)) {
5562
+ if (pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(i)) {
5426
5563
  if (itemKey !== void 0) {
5427
5564
  containerItemKeys.delete(itemKey);
5428
5565
  }
@@ -5453,24 +5590,24 @@ function calculateItemsInView(ctx, params = {}) {
5453
5590
  evaluateBootstrapInitialScroll(ctx);
5454
5591
  return;
5455
5592
  }
5456
- const mountedBufferedIndices = getMountedBufferedIndices(state);
5457
- const mountedNoBufferIndices = getMountedNoBufferIndices(state);
5458
- const readinessIndices = hasActiveInitialScroll(state) ? mountedBufferedIndices : mountedNoBufferIndices.length > 0 ? mountedNoBufferIndices : mountedBufferedIndices;
5459
- if (!queuedInitialLayout && readinessIndices.length > 0 && checkAllSizesKnown(state, readinessIndices)) {
5460
- setDidLayout(ctx);
5461
- handleInitialScrollLayoutReady(ctx);
5593
+ if (!queuedInitialLayout && !state.didContainersLayout) {
5594
+ const isInitialLayoutReady = hasActiveInitialScroll(state) ? checkAllSizesKnown(state, state.startBuffered, state.endBuffered) : checkAllSizesKnown(state, state.startNoBuffer, state.endNoBuffer) || checkAllSizesKnown(state, state.startBuffered, state.endBuffered);
5595
+ if (isInitialLayoutReady) {
5596
+ setDidLayout(ctx);
5597
+ handleInitialScrollLayoutReady(ctx);
5598
+ }
5462
5599
  }
5463
- if (viewabilityConfigCallbackPairs && startNoBuffer !== null && endNoBuffer !== null) {
5600
+ if (viewabilityConfigCallbackPairs && visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null) {
5464
5601
  if (!didMVCPAdjustScroll) {
5465
5602
  updateViewableItems(
5466
5603
  ctx.state,
5467
5604
  ctx,
5468
5605
  viewabilityConfigCallbackPairs,
5469
5606
  scrollLength,
5470
- startNoBuffer,
5471
- endNoBuffer,
5472
- startBuffered != null ? startBuffered : startNoBuffer,
5473
- endBuffered != null ? endBuffered : endNoBuffer
5607
+ visibleRange.startNoBuffer,
5608
+ visibleRange.endNoBuffer,
5609
+ startBuffered != null ? startBuffered : visibleRange.startNoBuffer,
5610
+ endBuffered != null ? endBuffered : visibleRange.endNoBuffer
5474
5611
  );
5475
5612
  }
5476
5613
  }
@@ -5798,22 +5935,27 @@ var ScrollAdjustHandler = class {
5798
5935
 
5799
5936
  // src/core/updateAnchoredEndSpace.ts
5800
5937
  function maybeUpdateAnchoredEndSpace(ctx) {
5801
- var _a3;
5938
+ var _a3, _b;
5802
5939
  const state = ctx.state;
5803
5940
  const anchoredEndSpace = state.props.anchoredEndSpace;
5804
5941
  const previousSize = peek$(ctx, "anchoredEndSpaceSize");
5942
+ const previousReadyAnchorIndex = state.anchoredEndSpaceReadyAnchorIndex;
5943
+ const previousReadyAnchorKey = state.anchoredEndSpaceReadyAnchorKey;
5944
+ const nextAnchorIndex = anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex;
5945
+ let nextAnchorKey;
5946
+ let isReady = true;
5805
5947
  let nextSize = 0;
5806
5948
  if (anchoredEndSpace) {
5807
5949
  const { anchorIndex, anchorMaxSize, anchorOffset = 0 } = anchoredEndSpace;
5808
5950
  const { data } = state.props;
5809
5951
  if (anchorIndex >= 0 && anchorIndex < data.length && state.scrollLength > 0) {
5952
+ nextAnchorKey = getId(state, anchorIndex);
5810
5953
  let contentBelowAnchor = 0;
5811
5954
  const footerSize = ctx.values.get("footerSize") || 0;
5812
5955
  const stylePaddingBottom = state.props.stylePaddingBottom || 0;
5813
5956
  let hasUnknownTailSize = false;
5814
5957
  for (let index = anchorIndex; index < data.length; index++) {
5815
- const itemKey = getId(state, index);
5816
- const size = itemKey ? state.sizesKnown.get(itemKey) : void 0;
5958
+ const size = getKnownOrFixedItemSize(ctx, index);
5817
5959
  const effectiveSize = index === anchorIndex && anchorMaxSize !== void 0 ? Math.min(size || 0, Math.max(0, anchorMaxSize)) : size;
5818
5960
  if (size === void 0) {
5819
5961
  hasUnknownTailSize = true;
@@ -5823,15 +5965,25 @@ function maybeUpdateAnchoredEndSpace(ctx) {
5823
5965
  }
5824
5966
  }
5825
5967
  contentBelowAnchor += footerSize + stylePaddingBottom;
5968
+ isReady = !hasUnknownTailSize;
5826
5969
  nextSize = hasUnknownTailSize ? previousSize || 0 : Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
5970
+ } else if (anchorIndex >= 0) {
5971
+ isReady = false;
5827
5972
  }
5828
5973
  }
5829
- if (previousSize !== nextSize) {
5830
- set$(ctx, "anchoredEndSpaceSize", nextSize);
5831
- (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
5832
- if (anchoredEndSpace == null ? void 0 : anchoredEndSpace.includeInEndInset) {
5974
+ const didSizeChange = previousSize !== nextSize;
5975
+ const didReadyAnchorChange = previousReadyAnchorIndex !== nextAnchorIndex || previousReadyAnchorKey !== nextAnchorKey;
5976
+ if (isReady && (didSizeChange || didReadyAnchorChange)) {
5977
+ state.anchoredEndSpaceReadyAnchorIndex = nextAnchorIndex;
5978
+ state.anchoredEndSpaceReadyAnchorKey = nextAnchorKey;
5979
+ if (didSizeChange) {
5980
+ set$(ctx, "anchoredEndSpaceSize", nextSize);
5981
+ (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
5982
+ }
5983
+ if (didSizeChange && (anchoredEndSpace == null ? void 0 : anchoredEndSpace.includeInEndInset)) {
5833
5984
  updateScroll(ctx, state.scroll, true);
5834
5985
  }
5986
+ (_b = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onReady) == null ? void 0 : _b.call(anchoredEndSpace, { anchorIndex: nextAnchorIndex, anchorKey: nextAnchorKey, size: nextSize });
5835
5987
  }
5836
5988
  return nextSize;
5837
5989
  }
@@ -5914,6 +6066,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5914
6066
  } = state;
5915
6067
  if (!data) return;
5916
6068
  const index = state.indexByKey.get(itemKey);
6069
+ let resolvedMeasurementItem;
5917
6070
  if (getFixedItemSize) {
5918
6071
  if (index === void 0) {
5919
6072
  return;
@@ -5924,6 +6077,12 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5924
6077
  }
5925
6078
  const type = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
5926
6079
  const size2 = getFixedItemSize(itemData, index, type);
6080
+ resolvedMeasurementItem = {
6081
+ didResolveFixedItemSize: true,
6082
+ fixedItemSize: size2,
6083
+ itemData,
6084
+ itemType: type
6085
+ };
5927
6086
  if (size2 !== void 0 && size2 === sizesKnown.get(itemKey)) {
5928
6087
  updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
5929
6088
  return;
@@ -5933,7 +6092,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5933
6092
  let shouldMaintainScrollAtEnd = false;
5934
6093
  let minIndexSizeChanged;
5935
6094
  const prevSizeKnown = state.sizesKnown.get(itemKey);
5936
- const diff = updateOneItemSize(ctx, itemKey, sizeObj);
6095
+ const diff = updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem);
5937
6096
  const size = roundSize(horizontal ? sizeObj.width : sizeObj.height);
5938
6097
  if (diff !== 0) {
5939
6098
  minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
@@ -5958,7 +6117,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5958
6117
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
5959
6118
  }
5960
6119
  updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
5961
- if (didContainersLayout || checkAllSizesKnown(state, getMountedBufferedIndices(state))) {
6120
+ if (didContainersLayout || checkAllSizesKnown(state, state.startBuffered, state.endBuffered)) {
5962
6121
  if (needsRecalculate) {
5963
6122
  state.scrollForNextCalculateItemsInView = void 0;
5964
6123
  runOrScheduleMVCPRecalculate(ctx);
@@ -5972,8 +6131,8 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5972
6131
  }
5973
6132
  }
5974
6133
  }
5975
- function updateOneItemSize(ctx, itemKey, sizeObj) {
5976
- var _a3, _b;
6134
+ function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
6135
+ var _a3, _b, _c;
5977
6136
  const state = ctx.state;
5978
6137
  const {
5979
6138
  indexByKey,
@@ -5983,20 +6142,25 @@ function updateOneItemSize(ctx, itemKey, sizeObj) {
5983
6142
  } = state;
5984
6143
  if (!data) return 0;
5985
6144
  const index = indexByKey.get(itemKey);
5986
- const itemData = data[index];
5987
- let itemType;
5988
- let fixedItemSize;
5989
- if (getFixedItemSize) {
5990
- itemType = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
6145
+ const itemData = (_a3 = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemData) != null ? _a3 : data[index];
6146
+ let itemType = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemType;
6147
+ let fixedItemSize = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.fixedItemSize;
6148
+ if (getFixedItemSize && !(resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize)) {
6149
+ itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
5991
6150
  fixedItemSize = getFixedItemSize(itemData, index, itemType);
5992
6151
  }
5993
- const prevSize = getItemSize(ctx, itemKey, index, itemData);
6152
+ const resolvedItemSize = (resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize) || itemType !== void 0 || fixedItemSize !== void 0 ? {
6153
+ didResolveFixedItemSize: resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize,
6154
+ fixedItemSize,
6155
+ itemType
6156
+ } : void 0;
6157
+ const prevSize = getItemSize(ctx, itemKey, index, itemData, void 0, void 0, void 0, resolvedItemSize);
5994
6158
  const rawSize = horizontal ? sizeObj.width : sizeObj.height;
5995
6159
  const prevSizeKnown = sizesKnown.get(itemKey);
5996
6160
  const size = Math.round(rawSize) ;
5997
6161
  sizesKnown.set(itemKey, size);
5998
6162
  if (fixedItemSize === void 0 && size > 0) {
5999
- itemType != null ? itemType : itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
6163
+ itemType != null ? itemType : itemType = getItemType ? (_c = getItemType(itemData, index)) != null ? _c : "" : "";
6000
6164
  let averages = averageSizes[itemType];
6001
6165
  if (!averages) {
6002
6166
  averages = averageSizes[itemType] = { avg: 0, num: 0 };
@@ -6064,6 +6228,25 @@ function createColumnWrapperStyle(contentContainerStyle) {
6064
6228
  }
6065
6229
  }
6066
6230
 
6231
+ // src/core/scrollToEnd.ts
6232
+ function scrollToEnd(ctx, options) {
6233
+ const state = ctx.state;
6234
+ const data = state.props.data;
6235
+ const index = data.length - 1;
6236
+ if (index === -1) {
6237
+ return false;
6238
+ }
6239
+ const paddingBottom = state.props.stylePaddingBottom || 0;
6240
+ const footerSize = peek$(ctx, "footerSize") || 0;
6241
+ scrollToIndex(ctx, {
6242
+ ...options,
6243
+ index,
6244
+ viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
6245
+ viewPosition: 1
6246
+ });
6247
+ return true;
6248
+ }
6249
+
6067
6250
  // src/utils/createImperativeHandle.ts
6068
6251
  var DEFAULT_AVERAGE_ITEM_SIZE_TYPE = "default";
6069
6252
  function getAverageItemSizes(state) {
@@ -6079,7 +6262,12 @@ function getAverageItemSizes(state) {
6079
6262
  }
6080
6263
  return averageItemSizes;
6081
6264
  }
6082
- function createImperativeHandle(ctx) {
6265
+ function triggerMountedContainerLayouts(ctx) {
6266
+ for (const triggerLayout of ctx.containerLayoutTriggers.values()) {
6267
+ triggerLayout();
6268
+ }
6269
+ }
6270
+ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
6083
6271
  const state = ctx.state;
6084
6272
  const IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS = 800;
6085
6273
  const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
@@ -6096,15 +6284,10 @@ function createImperativeHandle(ctx) {
6096
6284
  if (targetIndex >= dataLength) {
6097
6285
  return false;
6098
6286
  }
6099
- if (anchorIndex === void 0 || anchorIndex < 0 || anchorIndex >= dataLength || targetIndex < anchorIndex || props.getFixedItemSize) {
6287
+ if (anchorIndex === void 0 || anchorIndex < 0 || anchorIndex >= dataLength || targetIndex < anchorIndex) {
6100
6288
  return true;
6101
6289
  }
6102
- for (let index = anchorIndex; index < dataLength; index++) {
6103
- if (!state.sizesKnown.has(getId(state, index))) {
6104
- return false;
6105
- }
6106
- }
6107
- return true;
6290
+ return areKnownOrFixedItemSizesAvailable(ctx, anchorIndex, dataLength - 1);
6108
6291
  };
6109
6292
  const runWhenReady = (token, run, isReady) => {
6110
6293
  const startedAt = Date.now();
@@ -6127,11 +6310,7 @@ function createImperativeHandle(ctx) {
6127
6310
  };
6128
6311
  requestAnimationFrame(check);
6129
6312
  };
6130
- const runScrollWithPromise = (run, isReady = () => true) => new Promise((resolve) => {
6131
- var _a3;
6132
- const token = ++imperativeScrollToken;
6133
- (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
6134
- state.pendingScrollResolve = resolve;
6313
+ const runScrollRequest = (token, resolve, run, isReady = () => true) => {
6135
6314
  const runNow = () => {
6136
6315
  if (token !== imperativeScrollToken) {
6137
6316
  return;
@@ -6149,7 +6328,33 @@ function createImperativeHandle(ctx) {
6149
6328
  } else {
6150
6329
  runNow();
6151
6330
  }
6331
+ };
6332
+ const startImperativeScroll = (resolve) => {
6333
+ var _a3;
6334
+ const token = ++imperativeScrollToken;
6335
+ state.pendingScrollToEnd = void 0;
6336
+ (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
6337
+ state.pendingScrollResolve = resolve;
6338
+ return token;
6339
+ };
6340
+ const runScrollWithPromise = (run, isReady = () => true) => new Promise((resolve) => {
6341
+ const token = startImperativeScroll(resolve);
6342
+ runScrollRequest(token, resolve, run, isReady);
6152
6343
  });
6344
+ state.runPendingScrollToEnd = () => {
6345
+ const pendingScroll = state.pendingScrollToEnd;
6346
+ if (pendingScroll) {
6347
+ state.pendingScrollToEnd = void 0;
6348
+ if (pendingScroll.token === imperativeScrollToken) {
6349
+ runScrollRequest(
6350
+ pendingScroll.token,
6351
+ pendingScroll.resolve,
6352
+ () => scrollToEnd(ctx, pendingScroll.options),
6353
+ () => isScrollToIndexReady(state.props.data.length - 1, true)
6354
+ );
6355
+ }
6356
+ }
6357
+ };
6153
6358
  const scrollIndexIntoView = (options) => {
6154
6359
  if (state) {
6155
6360
  const { index, ...rest } = options;
@@ -6187,6 +6392,7 @@ function createImperativeHandle(ctx) {
6187
6392
  state.columns.length = 0;
6188
6393
  state.columnSpans.length = 0;
6189
6394
  }
6395
+ triggerMountedContainerLayouts(ctx);
6190
6396
  (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
6191
6397
  };
6192
6398
  return {
@@ -6248,26 +6454,20 @@ function createImperativeHandle(ctx) {
6248
6454
  }
6249
6455
  return false;
6250
6456
  }),
6251
- scrollToEnd: (options) => runScrollWithPromise(
6252
- () => {
6253
- const data = state.props.data;
6254
- const stylePaddingBottom = state.props.stylePaddingBottom;
6255
- const index = data.length - 1;
6256
- if (index !== -1) {
6257
- const paddingBottom = stylePaddingBottom || 0;
6258
- const footerSize = peek$(ctx, "footerSize") || 0;
6259
- scrollToIndex(ctx, {
6260
- ...options,
6261
- index,
6262
- viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
6263
- viewPosition: 1
6264
- });
6265
- return true;
6266
- }
6267
- return false;
6268
- },
6269
- () => isScrollToIndexReady(state.props.data.length - 1, true)
6270
- ),
6457
+ scrollToEnd: (options) => new Promise((resolve) => {
6458
+ var _a3;
6459
+ const token = startImperativeScroll(resolve);
6460
+ state.pendingScrollToEnd = {
6461
+ options,
6462
+ resolve,
6463
+ token
6464
+ };
6465
+ if (scheduleImperativeScrollCommit) {
6466
+ scheduleImperativeScrollCommit();
6467
+ } else {
6468
+ (_a3 = state.runPendingScrollToEnd) == null ? void 0 : _a3.call(state);
6469
+ }
6470
+ }),
6271
6471
  scrollToIndex: (params) => {
6272
6472
  return runScrollWithPromise(
6273
6473
  () => {
@@ -6637,6 +6837,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6637
6837
  viewOffset: 0
6638
6838
  } : void 0;
6639
6839
  const [canRender, setCanRender] = React3.useState(!IsNewArchitecture);
6840
+ const [, scheduleImperativeScrollCommit] = React3.useReducer((value) => value + 1, 0);
6640
6841
  const ctx = useStateContext();
6641
6842
  ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
6642
6843
  const refScroller = useRef(null);
@@ -6994,14 +7195,18 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6994
7195
  viewabilityConfigCallbackPairs
6995
7196
  });
6996
7197
  state.viewabilityConfigCallbackPairs = viewability;
6997
- state.enableScrollForNextCalculateItemsInView = !viewability;
7198
+ state.enableScrollForNextCalculateItemsInView = true;
6998
7199
  if (viewability) {
6999
7200
  state.scrollForNextCalculateItemsInView = void 0;
7000
7201
  }
7001
7202
  }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
7002
7203
  useInit(() => {
7003
7204
  });
7004
- useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx), []);
7205
+ useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx, scheduleImperativeScrollCommit), []);
7206
+ useLayoutEffect(() => {
7207
+ var _a4;
7208
+ (_a4 = state.runPendingScrollToEnd) == null ? void 0 : _a4.call(state);
7209
+ });
7005
7210
  useEffect(() => {
7006
7211
  if (usesBootstrapInitialScroll) {
7007
7212
  return;