@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.
package/react.js CHANGED
@@ -153,6 +153,7 @@ function StateProvider({ children }) {
153
153
  const [value] = React3__namespace.useState(() => ({
154
154
  animatedScrollY: createAnimatedValue(0),
155
155
  columnWrapperStyle: void 0,
156
+ containerLayoutTriggers: /* @__PURE__ */ new Map(),
156
157
  contextNum: contextNum++,
157
158
  listeners: /* @__PURE__ */ new Map(),
158
159
  mapViewabilityAmountCallbacks: /* @__PURE__ */ new Map(),
@@ -420,9 +421,6 @@ function roundSize(size) {
420
421
  function isNullOrUndefined(value) {
421
422
  return value === null || value === void 0;
422
423
  }
423
- function comparatorDefault(a, b) {
424
- return a - b;
425
- }
426
424
  function getPadding(s, type) {
427
425
  var _a3, _b, _c;
428
426
  const axisPadding = type === "Left" || type === "Right" ? s.paddingHorizontal : s.paddingVertical;
@@ -930,18 +928,6 @@ var Container = typedMemo(function Container2({
930
928
  [itemKey, data, extraData]
931
929
  );
932
930
  const { index, renderedItem } = renderedItemInfo || {};
933
- const contextValue = React3.useMemo(() => {
934
- ctx.viewRefs.set(id, ref);
935
- return {
936
- containerId: id,
937
- index,
938
- itemKey,
939
- triggerLayout: () => {
940
- forceLayoutRender((v) => v + 1);
941
- },
942
- value: data
943
- };
944
- }, [id, itemKey, index, data]);
945
931
  const onLayoutChange = React3.useCallback((rectangle) => {
946
932
  const {
947
933
  horizontal: currentHorizontal,
@@ -985,6 +971,27 @@ var Container = typedMemo(function Container2({
985
971
  doUpdate();
986
972
  }
987
973
  }, []);
974
+ const triggerLayout = React3.useCallback(() => {
975
+ forceLayoutRender((v) => v + 1);
976
+ }, []);
977
+ const contextValue = React3.useMemo(() => {
978
+ ctx.viewRefs.set(id, ref);
979
+ return {
980
+ containerId: id,
981
+ index,
982
+ itemKey,
983
+ triggerLayout,
984
+ value: data
985
+ };
986
+ }, [id, itemKey, index, data, triggerLayout]);
987
+ React3.useLayoutEffect(() => {
988
+ ctx.containerLayoutTriggers.set(id, triggerLayout);
989
+ return () => {
990
+ if (ctx.containerLayoutTriggers.get(id) === triggerLayout) {
991
+ ctx.containerLayoutTriggers.delete(id);
992
+ }
993
+ };
994
+ }, [ctx, id, triggerLayout]);
988
995
  const { onLayout } = useOnLayoutSync(
989
996
  {
990
997
  onLayoutChange,
@@ -2462,17 +2469,42 @@ function setSize(ctx, itemKey, size, notifyTotalSize = true) {
2462
2469
  }
2463
2470
 
2464
2471
  // src/utils/getItemSize.ts
2465
- function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize, notifyTotalSize) {
2466
- var _a3, _b, _c;
2472
+ function getKnownOrFixedSize(ctx, key, index, data, resolved) {
2473
+ var _a3, _b;
2474
+ const state = ctx.state;
2475
+ const { getFixedItemSize, getItemType } = state.props;
2476
+ let size = key ? state.sizesKnown.get(key) : void 0;
2477
+ if (size === void 0 && key && getFixedItemSize) {
2478
+ const itemType = (_b = resolved == null ? void 0 : resolved.itemType) != null ? _b : getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
2479
+ size = (resolved == null ? void 0 : resolved.didResolveFixedItemSize) ? resolved.fixedItemSize : getFixedItemSize(data, index, itemType);
2480
+ if (size !== void 0) {
2481
+ state.sizesKnown.set(key, size);
2482
+ }
2483
+ }
2484
+ return size;
2485
+ }
2486
+ function getKnownOrFixedItemSize(ctx, index) {
2487
+ const key = getId(ctx.state, index);
2488
+ return getKnownOrFixedSize(ctx, key, index, ctx.state.props.data[index]);
2489
+ }
2490
+ function areKnownOrFixedItemSizesAvailable(ctx, startIndex, endIndex) {
2491
+ for (let index = startIndex; index <= endIndex; index++) {
2492
+ if (getKnownOrFixedItemSize(ctx, index) === void 0) {
2493
+ return false;
2494
+ }
2495
+ }
2496
+ return true;
2497
+ }
2498
+ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize, notifyTotalSize, resolved) {
2499
+ var _a3, _b, _c, _d;
2467
2500
  const state = ctx.state;
2468
2501
  const {
2469
- sizesKnown,
2470
2502
  sizes,
2471
2503
  averageSizes,
2472
- props: { estimatedItemSize, getFixedItemSize, getItemType },
2504
+ props: { estimatedItemSize, getItemType },
2473
2505
  scrollingTo
2474
2506
  } = state;
2475
- const sizeKnown = sizesKnown.get(key);
2507
+ const sizeKnown = state.sizesKnown.get(key);
2476
2508
  if (sizeKnown !== void 0) {
2477
2509
  return sizeKnown;
2478
2510
  }
@@ -2483,15 +2515,14 @@ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize, no
2483
2515
  return renderedSize;
2484
2516
  }
2485
2517
  }
2486
- const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
2487
- if (getFixedItemSize) {
2488
- size = getFixedItemSize(data, index, itemType);
2489
- if (size !== void 0) {
2490
- sizesKnown.set(key, size);
2491
- }
2518
+ size = getKnownOrFixedSize(ctx, key, index, data, resolved);
2519
+ if (size !== void 0) {
2520
+ setSize(ctx, key, size, notifyTotalSize);
2521
+ return size;
2492
2522
  }
2493
- if (size === void 0 && useAverageSize && sizeKnown === void 0 && !scrollingTo) {
2494
- const averageSizeForType = (_b = averageSizes[itemType]) == null ? void 0 : _b.avg;
2523
+ const itemType = (_b = resolved == null ? void 0 : resolved.itemType) != null ? _b : getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
2524
+ if (useAverageSize && !scrollingTo) {
2525
+ const averageSizeForType = (_c = averageSizes[itemType]) == null ? void 0 : _c.avg;
2495
2526
  if (averageSizeForType !== void 0) {
2496
2527
  size = roundSize(averageSizeForType);
2497
2528
  }
@@ -2499,8 +2530,8 @@ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize, no
2499
2530
  if (size === void 0 && renderedSize !== void 0) {
2500
2531
  return renderedSize;
2501
2532
  }
2502
- if (size === void 0 && useAverageSize && sizeKnown === void 0 && scrollingTo) {
2503
- const averageSizeForType = (_c = scrollingTo.averageSizeSnapshot) == null ? void 0 : _c[itemType];
2533
+ if (size === void 0 && useAverageSize && scrollingTo) {
2534
+ const averageSizeForType = (_d = scrollingTo.averageSizeSnapshot) == null ? void 0 : _d[itemType];
2504
2535
  if (averageSizeForType !== void 0) {
2505
2536
  size = roundSize(averageSizeForType);
2506
2537
  }
@@ -3061,8 +3092,8 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
3061
3092
  if ((options == null ? void 0 : options.markHasScrolled) !== false) {
3062
3093
  state.hasScrolled = true;
3063
3094
  }
3064
- state.lastBatchingAction = Date.now();
3065
3095
  const currentTime = Date.now();
3096
+ state.lastBatchingAction = currentTime;
3066
3097
  const adjust = scrollAdjustHandler.getAdjust();
3067
3098
  const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
3068
3099
  if (adjustChanged) {
@@ -3371,26 +3402,22 @@ function advanceCurrentInitialScrollSession(ctx, options) {
3371
3402
  }
3372
3403
 
3373
3404
  // src/utils/checkAllSizesKnown.ts
3374
- function isNullOrUndefined2(value) {
3375
- return value === null || value === void 0;
3376
- }
3377
- function getMountedIndicesInRange(state, start, end) {
3378
- if (!isNullOrUndefined2(end) && !isNullOrUndefined2(start) && start >= 0 && end >= 0) {
3379
- 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);
3405
+ function checkAllSizesKnown(state, start, end) {
3406
+ if (start == null || end == null || start < 0 || end < start) {
3407
+ return false;
3380
3408
  }
3381
- return [];
3382
- }
3383
- function getMountedBufferedIndices(state) {
3384
- return getMountedIndicesInRange(state, state.startBuffered, state.endBuffered);
3385
- }
3386
- function getMountedNoBufferIndices(state) {
3387
- return getMountedIndicesInRange(state, state.startNoBuffer, state.endNoBuffer);
3388
- }
3389
- function checkAllSizesKnown(state, indices) {
3390
- return indices.length > 0 && indices.every((index) => {
3391
- const key = getId(state, index);
3392
- return key !== void 0 && state.sizesKnown.has(key);
3393
- });
3409
+ let hasMountedIndex = false;
3410
+ for (const key of state.containerItemKeys.keys()) {
3411
+ const index = state.indexByKey.get(key);
3412
+ if (index !== void 0 && index >= start && index <= end) {
3413
+ hasMountedIndex = true;
3414
+ const id = getId(state, index);
3415
+ if (id === void 0 || !state.sizesKnown.has(id)) {
3416
+ return false;
3417
+ }
3418
+ }
3419
+ }
3420
+ return hasMountedIndex;
3394
3421
  }
3395
3422
 
3396
3423
  // src/core/bootstrapInitialScroll.ts
@@ -3891,8 +3918,7 @@ function evaluateBootstrapInitialScroll(ctx) {
3891
3918
  bootstrapInitialScroll.targetIndexSeed = void 0;
3892
3919
  }
3893
3920
  const resolvedOffset = resolveInitialScrollOffset(ctx, initialScroll);
3894
- const mountedBufferedIndices = getMountedBufferedIndices(state);
3895
- const areMountedBufferedIndicesMeasured = checkAllSizesKnown(state, mountedBufferedIndices);
3921
+ const areMountedBufferedIndicesMeasured = checkAllSizesKnown(state, state.startBuffered, state.endBuffered);
3896
3922
  const didResolvedOffsetChange = Math.abs(bootstrapInitialScroll.scroll - resolvedOffset) > 1;
3897
3923
  const { data } = state.props;
3898
3924
  const visibleIndices = getBootstrapRevealVisibleIndices({
@@ -4468,7 +4494,14 @@ function updateSnapToOffsets(ctx) {
4468
4494
  }
4469
4495
 
4470
4496
  // src/core/updateItemPositions.ts
4471
- function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP, optimizeForVisibleWindow = false } = {
4497
+ function updateItemPositions(ctx, dataChanged, {
4498
+ doMVCP,
4499
+ forceFullUpdate = false,
4500
+ optimizeForVisibleWindow = false,
4501
+ scrollBottomBuffered,
4502
+ scrollVelocity,
4503
+ startIndex
4504
+ } = {
4472
4505
  doMVCP: false,
4473
4506
  forceFullUpdate: false,
4474
4507
  optimizeForVisibleWindow: false,
@@ -4495,7 +4528,7 @@ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffere
4495
4528
  const extraData = peek$(ctx, "extraData");
4496
4529
  const layoutConfig = overrideItemLayout ? { span: 1 } : void 0;
4497
4530
  const lastScrollDelta = state.lastScrollDelta;
4498
- const velocity = getScrollVelocity(state);
4531
+ const velocity = scrollVelocity != null ? scrollVelocity : getScrollVelocity(state);
4499
4532
  const shouldOptimize = !forceFullUpdate && !dataChanged && (optimizeForVisibleWindow || Math.abs(velocity) > 0 || state.scrollLength > 0 && lastScrollDelta > state.scrollLength);
4500
4533
  const maxVisibleArea = scrollBottomBuffered + 1e3;
4501
4534
  const useAverageSize = true;
@@ -4675,25 +4708,28 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
4675
4708
  const configId = viewabilityConfig.id;
4676
4709
  const viewabilityState = ensureViewabilityState(ctx, configId);
4677
4710
  const { viewableItems: previousViewableItems, start, end, startBuffered, endBuffered } = viewabilityState;
4678
- const viewabilityTokens = /* @__PURE__ */ new Map();
4711
+ let staleViewabilityAmountIds;
4679
4712
  for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
4680
- viewabilityTokens.set(
4713
+ const nextValue = computeViewability(
4714
+ state,
4715
+ ctx,
4716
+ viewabilityConfig,
4681
4717
  containerId,
4682
- computeViewability(
4683
- state,
4684
- ctx,
4685
- viewabilityConfig,
4686
- containerId,
4687
- value.key,
4688
- scrollSize,
4689
- value.item,
4690
- value.index
4691
- )
4718
+ value.key,
4719
+ scrollSize,
4720
+ value.item,
4721
+ value.index
4692
4722
  );
4723
+ if (nextValue.sizeVisible < 0) {
4724
+ staleViewabilityAmountIds != null ? staleViewabilityAmountIds : staleViewabilityAmountIds = [];
4725
+ staleViewabilityAmountIds.push(containerId);
4726
+ }
4693
4727
  }
4694
4728
  const changed = [];
4729
+ const previousViewableKeys = /* @__PURE__ */ new Set();
4695
4730
  if (previousViewableItems) {
4696
4731
  for (const viewToken of previousViewableItems) {
4732
+ previousViewableKeys.add(viewToken.key);
4697
4733
  const containerId = findContainerId(ctx, viewToken.key);
4698
4734
  if (!checkIsViewable(
4699
4735
  state,
@@ -4725,7 +4761,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
4725
4761
  key
4726
4762
  };
4727
4763
  viewableItems.push(viewToken);
4728
- if (!(previousViewableItems == null ? void 0 : previousViewableItems.find((v) => v.key === viewToken.key))) {
4764
+ if (!previousViewableKeys.has(viewToken.key)) {
4729
4765
  changed.push(viewToken);
4730
4766
  }
4731
4767
  }
@@ -4746,20 +4782,17 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
4746
4782
  onViewableItemsChanged({ changed, end, endBuffered, start, startBuffered, viewableItems });
4747
4783
  }
4748
4784
  }
4749
- for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
4750
- if (value.sizeVisible < 0) {
4751
- ctx.mapViewabilityAmountValues.delete(containerId);
4785
+ if (staleViewabilityAmountIds) {
4786
+ for (const containerId of staleViewabilityAmountIds) {
4787
+ const value = ctx.mapViewabilityAmountValues.get(containerId);
4788
+ if (value && value.sizeVisible < 0) {
4789
+ ctx.mapViewabilityAmountValues.delete(containerId);
4790
+ }
4752
4791
  }
4753
4792
  }
4754
4793
  }
4755
- function shallowEqual(prev, next) {
4756
- if (!prev) return false;
4757
- const keys = Object.keys(next);
4758
- for (let i = 0; i < keys.length; i++) {
4759
- const k = keys[i];
4760
- if (prev[k] !== next[k]) return false;
4761
- }
4762
- return true;
4794
+ function areViewabilityAmountTokensEqual(prev, next) {
4795
+ 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;
4763
4796
  }
4764
4797
  function computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
4765
4798
  const { sizes, scroll: scrollState } = state;
@@ -4784,7 +4817,7 @@ function computeViewability(state, ctx, viewabilityConfig, containerId, key, scr
4784
4817
  sizeVisible: -1
4785
4818
  };
4786
4819
  const prev2 = ctx.mapViewabilityAmountValues.get(containerId);
4787
- if (!shallowEqual(prev2, value2)) {
4820
+ if (!areViewabilityAmountTokensEqual(prev2, value2)) {
4788
4821
  ctx.mapViewabilityAmountValues.set(containerId, value2);
4789
4822
  const cb = ctx.mapViewabilityAmountCallbacks.get(containerId);
4790
4823
  if (cb) {
@@ -4814,7 +4847,7 @@ function computeViewability(state, ctx, viewabilityConfig, containerId, key, scr
4814
4847
  sizeVisible
4815
4848
  };
4816
4849
  const prev = ctx.mapViewabilityAmountValues.get(containerId);
4817
- if (!shallowEqual(prev, value)) {
4850
+ if (!areViewabilityAmountTokensEqual(prev, value)) {
4818
4851
  ctx.mapViewabilityAmountValues.set(containerId, value);
4819
4852
  const cb = ctx.mapViewabilityAmountCallbacks.get(containerId);
4820
4853
  if (cb) {
@@ -4865,126 +4898,152 @@ function getExpandedContainerPoolSize(dataLength, numContainers) {
4865
4898
  }
4866
4899
 
4867
4900
  // src/utils/findAvailableContainers.ts
4868
- function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers, protectedKeys) {
4901
+ function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffered, pendingRemoval, getRequiredItemType, protectedKeys) {
4902
+ const numNeeded = needNewContainers.length;
4903
+ if (numNeeded === 0) {
4904
+ return [];
4905
+ }
4869
4906
  const numContainers = peek$(ctx, "numContainers");
4870
4907
  const state = ctx.state;
4871
4908
  const { stickyContainerPool, containerItemTypes } = state;
4872
4909
  const shouldAvoidAssignedContainerReuse = state.props.recycleItems && !!state.props.positionComponentInternal;
4873
- const result = [];
4874
- const availableContainers = [];
4875
- const pendingRemovalSet = new Set(pendingRemoval);
4910
+ const allocations = [];
4911
+ const pendingRemovalSet = pendingRemoval.length > 0 ? new Set(pendingRemoval) : void 0;
4876
4912
  let pendingRemovalChanged = false;
4913
+ let nextNewContainerIndex = numContainers;
4914
+ const usedContainers = /* @__PURE__ */ new Set();
4915
+ let availableContainers;
4877
4916
  const stickyHeaderIndicesSet = state.props.stickyHeaderIndicesSet;
4878
- const stickyHeaderItemIndices = (needNewContainers == null ? void 0 : needNewContainers.filter((index) => stickyHeaderIndicesSet.has(index))) || [];
4879
4917
  const canReuseContainer = (containerIndex, requiredType) => {
4880
4918
  if (!requiredType) return true;
4881
4919
  const existingType = containerItemTypes.get(containerIndex);
4882
4920
  if (!existingType) return true;
4883
4921
  return existingType === requiredType;
4884
4922
  };
4885
- const neededTypes = requiredItemTypes ? [...requiredItemTypes] : [];
4886
- let typeIndex = 0;
4887
- for (let i = 0; i < stickyHeaderItemIndices.length; i++) {
4888
- const requiredType = neededTypes[typeIndex];
4889
- let foundContainer = false;
4890
- for (const containerIndex of stickyContainerPool) {
4891
- const key = peek$(ctx, `containerItemKey${containerIndex}`);
4892
- const isPendingRemoval = pendingRemovalSet.has(containerIndex);
4893
- if ((key === void 0 || isPendingRemoval) && canReuseContainer(containerIndex, requiredType) && !result.includes(containerIndex)) {
4894
- result.push(containerIndex);
4895
- if (isPendingRemoval && pendingRemovalSet.delete(containerIndex)) {
4896
- pendingRemovalChanged = true;
4897
- }
4898
- foundContainer = true;
4899
- if (requiredItemTypes) typeIndex++;
4900
- break;
4901
- }
4923
+ const pushAllocation = (itemIndex, itemType, containerIndex) => {
4924
+ allocations.push({
4925
+ containerIndex,
4926
+ itemIndex,
4927
+ itemType
4928
+ });
4929
+ usedContainers.add(containerIndex);
4930
+ if (pendingRemovalSet == null ? void 0 : pendingRemovalSet.delete(containerIndex)) {
4931
+ pendingRemovalChanged = true;
4902
4932
  }
4903
- if (!foundContainer) {
4904
- const newContainerIndex = numContainers + result.filter((index) => index >= numContainers).length;
4905
- result.push(newContainerIndex);
4933
+ };
4934
+ const pushNewContainer = (itemIndex, itemType, isSticky) => {
4935
+ const newContainerIndex = nextNewContainerIndex++;
4936
+ pushAllocation(itemIndex, itemType, newContainerIndex);
4937
+ if (isSticky) {
4906
4938
  stickyContainerPool.add(newContainerIndex);
4907
- if (requiredItemTypes) typeIndex++;
4908
- }
4909
- }
4910
- for (let u = 0; u < numContainers && result.length < numNeeded; u++) {
4911
- if (stickyContainerPool.has(u)) {
4912
- continue;
4913
4939
  }
4914
- const key = peek$(ctx, `containerItemKey${u}`);
4915
- const requiredType = neededTypes[typeIndex];
4916
- const isPending = key !== void 0 && pendingRemovalSet.has(u);
4917
- const canUse = key === void 0 || isPending && canReuseContainer(u, requiredType);
4918
- if (canUse) {
4919
- if (isPending) {
4920
- pendingRemovalSet.delete(u);
4921
- pendingRemovalChanged = true;
4922
- }
4923
- result.push(u);
4924
- if (requiredItemTypes) {
4925
- typeIndex++;
4926
- }
4940
+ return newContainerIndex;
4941
+ };
4942
+ const canUseContainer = (containerIndex, itemType) => {
4943
+ if (usedContainers.has(containerIndex) || stickyContainerPool.has(containerIndex)) {
4944
+ return false;
4927
4945
  }
4928
- }
4929
- if (!shouldAvoidAssignedContainerReuse) {
4930
- for (let u = 0; u < numContainers && result.length < numNeeded; u++) {
4931
- if (stickyContainerPool.has(u)) {
4932
- continue;
4933
- }
4934
- const key = peek$(ctx, `containerItemKey${u}`);
4935
- if (key === void 0) continue;
4936
- if ((protectedKeys == null ? void 0 : protectedKeys.has(key)) && state.indexByKey.has(key)) continue;
4937
- const index = state.indexByKey.get(key);
4938
- const isOutOfView = index < startBuffered || index > endBuffered;
4939
- if (isOutOfView) {
4940
- const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
4941
- if (!requiredItemTypes || typeIndex < neededTypes.length && canReuseContainer(u, neededTypes[typeIndex])) {
4942
- availableContainers.push({ distance, index: u });
4946
+ const key = peek$(ctx, `containerItemKey${containerIndex}`);
4947
+ const isPending = !!(pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(containerIndex));
4948
+ return (key === void 0 || isPending) && canReuseContainer(containerIndex, itemType);
4949
+ };
4950
+ const findStickyContainer = (itemType) => {
4951
+ let foundContainer;
4952
+ for (const containerIndex of stickyContainerPool) {
4953
+ if (!usedContainers.has(containerIndex)) {
4954
+ const key = peek$(ctx, `containerItemKey${containerIndex}`);
4955
+ const isPendingRemoval = !!(pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(containerIndex));
4956
+ if ((key === void 0 || isPendingRemoval) && canReuseContainer(containerIndex, itemType)) {
4957
+ foundContainer = containerIndex;
4958
+ break;
4943
4959
  }
4944
4960
  }
4945
4961
  }
4946
- }
4947
- const remaining = numNeeded - result.length;
4948
- if (remaining > 0) {
4949
- if (availableContainers.length > 0) {
4950
- if (availableContainers.length > remaining) {
4951
- availableContainers.sort(comparatorByDistance);
4952
- availableContainers.length = remaining;
4962
+ return foundContainer;
4963
+ };
4964
+ const findUnassignedOrPendingContainer = (itemType) => {
4965
+ let foundContainer;
4966
+ for (let containerIndex = 0; containerIndex < numContainers && foundContainer === void 0; containerIndex++) {
4967
+ if (canUseContainer(containerIndex, itemType)) {
4968
+ foundContainer = containerIndex;
4953
4969
  }
4954
- for (const container of availableContainers) {
4955
- result.push(container.index);
4956
- if (requiredItemTypes) {
4957
- typeIndex++;
4970
+ }
4971
+ return foundContainer;
4972
+ };
4973
+ const getAvailableContainers = () => {
4974
+ if (!availableContainers) {
4975
+ availableContainers = [];
4976
+ if (!shouldAvoidAssignedContainerReuse) {
4977
+ for (let containerIndex = 0; containerIndex < numContainers; containerIndex++) {
4978
+ if (usedContainers.has(containerIndex) || stickyContainerPool.has(containerIndex)) {
4979
+ continue;
4980
+ }
4981
+ const key = peek$(ctx, `containerItemKey${containerIndex}`);
4982
+ if (key === void 0) continue;
4983
+ if ((protectedKeys == null ? void 0 : protectedKeys.has(key)) && state.indexByKey.has(key)) continue;
4984
+ const index = state.indexByKey.get(key);
4985
+ const isOutOfView = index < startBuffered || index > endBuffered;
4986
+ if (isOutOfView) {
4987
+ const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
4988
+ availableContainers.push({ distance, index: containerIndex });
4989
+ }
4958
4990
  }
4991
+ availableContainers.sort(comparatorByDistance);
4959
4992
  }
4960
4993
  }
4961
- const stillNeeded = numNeeded - result.length;
4962
- if (stillNeeded > 0) {
4963
- for (let i = 0; i < stillNeeded; i++) {
4964
- result.push(numContainers + i);
4994
+ return availableContainers;
4995
+ };
4996
+ const findAvailableContainer = (itemType) => {
4997
+ const containers = getAvailableContainers();
4998
+ let matchIndex = -1;
4999
+ for (let i = 0; i < containers.length && matchIndex === -1; i++) {
5000
+ const containerIndex = containers[i].index;
5001
+ if (!usedContainers.has(containerIndex) && canReuseContainer(containerIndex, itemType)) {
5002
+ matchIndex = i;
4965
5003
  }
4966
- if (IS_DEV && numContainers + stillNeeded > peek$(ctx, "numContainersPooled")) {
4967
- console.warn(
4968
- "[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.",
4969
- {
4970
- debugInfo: {
4971
- numContainers,
4972
- numContainersPooled: peek$(ctx, "numContainersPooled"),
4973
- numNeeded,
4974
- stillNeeded
4975
- }
4976
- }
4977
- );
5004
+ }
5005
+ return matchIndex === -1 ? void 0 : containers.splice(matchIndex, 1)[0].index;
5006
+ };
5007
+ for (const itemIndex of needNewContainers) {
5008
+ const itemType = getRequiredItemType == null ? void 0 : getRequiredItemType(itemIndex);
5009
+ const isSticky = stickyHeaderIndicesSet.has(itemIndex);
5010
+ let containerIndex;
5011
+ if (isSticky) {
5012
+ containerIndex = findStickyContainer(itemType);
5013
+ } else {
5014
+ containerIndex = findUnassignedOrPendingContainer(itemType);
5015
+ if (containerIndex === void 0) {
5016
+ containerIndex = findAvailableContainer(itemType);
4978
5017
  }
4979
5018
  }
5019
+ if (containerIndex !== void 0) {
5020
+ pushAllocation(itemIndex, itemType, containerIndex);
5021
+ } else {
5022
+ pushNewContainer(itemIndex, itemType, isSticky);
5023
+ }
4980
5024
  }
4981
5025
  if (pendingRemovalChanged) {
4982
5026
  pendingRemoval.length = 0;
4983
- for (const value of pendingRemovalSet) {
4984
- pendingRemoval.push(value);
5027
+ if (pendingRemovalSet) {
5028
+ for (const value of pendingRemovalSet) {
5029
+ pendingRemoval.push(value);
5030
+ }
4985
5031
  }
4986
5032
  }
4987
- return result.sort(comparatorDefault);
5033
+ if (IS_DEV && nextNewContainerIndex > peek$(ctx, "numContainersPooled")) {
5034
+ console.warn(
5035
+ "[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.",
5036
+ {
5037
+ debugInfo: {
5038
+ numContainers,
5039
+ numContainersPooled: peek$(ctx, "numContainersPooled"),
5040
+ numNeeded,
5041
+ stillNeeded: nextNewContainerIndex - numContainers
5042
+ }
5043
+ }
5044
+ );
5045
+ }
5046
+ return allocations;
4988
5047
  }
4989
5048
  function comparatorByDistance(a, b) {
4990
5049
  return b.distance - a.distance;
@@ -5010,21 +5069,28 @@ function findCurrentStickyIndex(stickyArray, scroll, state) {
5010
5069
  }
5011
5070
  return -1;
5012
5071
  }
5013
- function getActiveStickyIndices(ctx, stickyHeaderIndices) {
5072
+ function isStickyIndexActive(ctx, targetIndex) {
5014
5073
  const state = ctx.state;
5015
- return new Set(
5016
- 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))
5017
- );
5074
+ let isActive = false;
5075
+ for (const containerIndex of state.stickyContainerPool) {
5076
+ const key = peek$(ctx, `containerItemKey${containerIndex}`);
5077
+ const itemIndex = key ? state.indexByKey.get(key) : void 0;
5078
+ if (itemIndex === targetIndex) {
5079
+ isActive = true;
5080
+ break;
5081
+ }
5082
+ }
5083
+ return isActive;
5018
5084
  }
5019
- function handleStickyActivation(ctx, stickyHeaderIndices, stickyArray, currentStickyIdx, needNewContainers, needNewContainersSet, startBuffered, endBuffered) {
5085
+ function handleStickyActivation(ctx, stickyArray, currentStickyIdx, needNewContainers, needNewContainersSet, startBuffered, endBuffered) {
5020
5086
  var _a3;
5021
5087
  const state = ctx.state;
5022
- const activeIndices = getActiveStickyIndices(ctx, stickyHeaderIndices);
5023
5088
  set$(ctx, "activeStickyIndex", currentStickyIdx >= 0 ? stickyArray[currentStickyIdx] : -1);
5024
5089
  for (let offset = 0; offset <= 1; offset++) {
5025
5090
  const idx = currentStickyIdx - offset;
5026
- if (idx < 0 || activeIndices.has(stickyArray[idx])) continue;
5091
+ if (idx < 0) continue;
5027
5092
  const stickyIndex = stickyArray[idx];
5093
+ if (isStickyIndexActive(ctx, stickyIndex)) continue;
5028
5094
  const stickyId = (_a3 = state.idCache[stickyIndex]) != null ? _a3 : getId(state, stickyIndex);
5029
5095
  if (stickyId && !state.containerItemKeys.has(stickyId) && (stickyIndex < startBuffered || stickyIndex > endBuffered) && !needNewContainersSet.has(stickyIndex)) {
5030
5096
  needNewContainersSet.add(stickyIndex);
@@ -5066,10 +5132,86 @@ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentSt
5066
5132
  }
5067
5133
  }
5068
5134
  }
5135
+ function trackVisibleRange(range, i, top, size, scroll, scrollBottom) {
5136
+ let didPassVisibleEnd = false;
5137
+ if (range.startNoBuffer === null && top + size > scroll) {
5138
+ range.startNoBuffer = i;
5139
+ }
5140
+ if (range.firstFullyOnScreenIndex === void 0 && top >= scroll - 10 && top <= scrollBottom) {
5141
+ range.firstFullyOnScreenIndex = i;
5142
+ }
5143
+ if (range.startNoBuffer !== null) {
5144
+ if (top <= scrollBottom) {
5145
+ range.endNoBuffer = i;
5146
+ } else {
5147
+ didPassVisibleEnd = true;
5148
+ }
5149
+ }
5150
+ return didPassVisibleEnd;
5151
+ }
5152
+ function getIdsInVisibleRange(state, range) {
5153
+ var _a3, _b;
5154
+ const idsInView = [];
5155
+ const firstVisibleAnchorIndex = (_a3 = range.firstFullyOnScreenIndex) != null ? _a3 : range.startNoBuffer;
5156
+ if (firstVisibleAnchorIndex !== null && firstVisibleAnchorIndex !== void 0 && range.endNoBuffer !== null) {
5157
+ for (let i = firstVisibleAnchorIndex; i <= range.endNoBuffer; i++) {
5158
+ const id = (_b = state.idCache[i]) != null ? _b : getId(state, i);
5159
+ idsInView.push(id);
5160
+ }
5161
+ }
5162
+ return idsInView;
5163
+ }
5164
+ function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, scrollLength, scroll, scrollBottom) {
5165
+ var _a3, _b;
5166
+ const state = ctx.state;
5167
+ const {
5168
+ endBuffered,
5169
+ idCache,
5170
+ positions,
5171
+ props: { data },
5172
+ sizes,
5173
+ startBuffered
5174
+ } = state;
5175
+ if (startBuffered === null || endBuffered === null || startBuffered < 0 || endBuffered < startBuffered) {
5176
+ return;
5177
+ }
5178
+ const visibleRange = {
5179
+ endNoBuffer: null,
5180
+ firstFullyOnScreenIndex: void 0,
5181
+ startNoBuffer: null
5182
+ };
5183
+ for (let i = startBuffered; i <= endBuffered && i < data.length; i++) {
5184
+ const id = (_a3 = idCache[i]) != null ? _a3 : getId(state, i);
5185
+ const size = (_b = sizes.get(id)) != null ? _b : getItemSize(ctx, id, i, data[i]);
5186
+ const top = positions[i];
5187
+ const didPassVisibleEnd = trackVisibleRange(visibleRange, i, top, size, scroll, scrollBottom);
5188
+ if (didPassVisibleEnd) {
5189
+ break;
5190
+ }
5191
+ }
5192
+ Object.assign(state, {
5193
+ endNoBuffer: visibleRange.endNoBuffer,
5194
+ firstFullyOnScreenIndex: visibleRange.firstFullyOnScreenIndex,
5195
+ idsInView: getIdsInVisibleRange(state, visibleRange),
5196
+ startNoBuffer: visibleRange.startNoBuffer
5197
+ });
5198
+ if (visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null) {
5199
+ updateViewableItems(
5200
+ state,
5201
+ ctx,
5202
+ viewabilityConfigCallbackPairs,
5203
+ scrollLength,
5204
+ visibleRange.startNoBuffer,
5205
+ visibleRange.endNoBuffer,
5206
+ startBuffered,
5207
+ endBuffered
5208
+ );
5209
+ }
5210
+ }
5069
5211
  function calculateItemsInView(ctx, params = {}) {
5070
5212
  const state = ctx.state;
5071
5213
  batchedUpdates(() => {
5072
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
5214
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
5073
5215
  const {
5074
5216
  columns,
5075
5217
  containerItemKeys,
@@ -5170,6 +5312,15 @@ function calculateItemsInView(ctx, params = {}) {
5170
5312
  state.scrollForNextCalculateItemsInView = void 0;
5171
5313
  } else if ((top === null || scrollTopBuffered > top) && (bottom === null || scrollBottomBuffered < bottom)) {
5172
5314
  if (!isInMVCPActiveMode(state)) {
5315
+ if (viewabilityConfigCallbackPairs) {
5316
+ updateViewabilityForCachedRange(
5317
+ ctx,
5318
+ viewabilityConfigCallbackPairs,
5319
+ scrollLength,
5320
+ scroll,
5321
+ scrollBottom
5322
+ );
5323
+ }
5173
5324
  finishCalculateItemsInView == null ? void 0 : finishCalculateItemsInView();
5174
5325
  return;
5175
5326
  }
@@ -5186,6 +5337,7 @@ function calculateItemsInView(ctx, params = {}) {
5186
5337
  forceFullUpdate: !!forceFullItemPositions,
5187
5338
  optimizeForVisibleWindow,
5188
5339
  scrollBottomBuffered,
5340
+ scrollVelocity: speed,
5189
5341
  startIndex
5190
5342
  });
5191
5343
  totalSize = getContentSize(ctx);
@@ -5211,10 +5363,8 @@ function calculateItemsInView(ctx, params = {}) {
5211
5363
  updateScroll2(state.scroll);
5212
5364
  updateScrollRange();
5213
5365
  }
5214
- let startNoBuffer = null;
5215
5366
  let startBuffered = null;
5216
5367
  let startBufferedId = null;
5217
- let endNoBuffer = null;
5218
5368
  let endBuffered = null;
5219
5369
  let loopStart = (_f = suppressInitialScrollSideEffects ? bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.targetIndexSeed : void 0) != null ? _f : !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
5220
5370
  for (let i = loopStart; i >= 0; i--) {
@@ -5248,19 +5398,18 @@ function calculateItemsInView(ctx, params = {}) {
5248
5398
  maxIndexRendered = Math.max(maxIndexRendered, index);
5249
5399
  }
5250
5400
  }
5251
- let firstFullyOnScreenIndex;
5401
+ const visibleRange = {
5402
+ endNoBuffer: null,
5403
+ firstFullyOnScreenIndex: void 0,
5404
+ startNoBuffer: null
5405
+ };
5252
5406
  const dataLength = data.length;
5253
5407
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
5254
5408
  const id = (_i = idCache[i]) != null ? _i : getId(state, i);
5255
5409
  const size = (_j = sizes.get(id)) != null ? _j : getItemSize(ctx, id, i, data[i]);
5256
5410
  const top = positions[i];
5257
5411
  if (!foundEnd) {
5258
- if (startNoBuffer === null && top + size > scroll) {
5259
- startNoBuffer = i;
5260
- }
5261
- if (firstFullyOnScreenIndex === void 0 && top >= scroll - 10 && top <= scrollBottom) {
5262
- firstFullyOnScreenIndex = i;
5263
- }
5412
+ trackVisibleRange(visibleRange, i, top, size, scroll, scrollBottom);
5264
5413
  if (startBuffered === null && top + size > scrollTopBuffered) {
5265
5414
  startBuffered = i;
5266
5415
  startBufferedId = id;
@@ -5270,10 +5419,7 @@ function calculateItemsInView(ctx, params = {}) {
5270
5419
  nextTop = top;
5271
5420
  }
5272
5421
  }
5273
- if (startNoBuffer !== null) {
5274
- if (top <= scrollBottom) {
5275
- endNoBuffer = i;
5276
- }
5422
+ if (visibleRange.startNoBuffer !== null) {
5277
5423
  if (top <= scrollBottomBuffered) {
5278
5424
  endBuffered = i;
5279
5425
  if (scrollBottomBuffered > totalSize) {
@@ -5287,22 +5433,14 @@ function calculateItemsInView(ctx, params = {}) {
5287
5433
  }
5288
5434
  }
5289
5435
  }
5290
- const idsInView = [];
5291
- const firstVisibleAnchorIndex = firstFullyOnScreenIndex != null ? firstFullyOnScreenIndex : startNoBuffer;
5292
- if (firstVisibleAnchorIndex !== null && firstVisibleAnchorIndex !== void 0 && endNoBuffer !== null) {
5293
- for (let i = firstVisibleAnchorIndex; i <= endNoBuffer; i++) {
5294
- const id = (_k = idCache[i]) != null ? _k : getId(state, i);
5295
- idsInView.push(id);
5296
- }
5297
- }
5298
5436
  Object.assign(state, {
5299
5437
  endBuffered,
5300
- endNoBuffer,
5301
- firstFullyOnScreenIndex,
5302
- idsInView,
5438
+ endNoBuffer: visibleRange.endNoBuffer,
5439
+ firstFullyOnScreenIndex: visibleRange.firstFullyOnScreenIndex,
5440
+ idsInView: getIdsInVisibleRange(state, visibleRange),
5303
5441
  startBuffered,
5304
5442
  startBufferedId,
5305
- startNoBuffer
5443
+ startNoBuffer: visibleRange.startNoBuffer
5306
5444
  });
5307
5445
  if (enableScrollForNextCalculateItemsInView && nextTop !== void 0 && nextBottom !== void 0) {
5308
5446
  state.scrollForNextCalculateItemsInView = isNullOrUndefined(nextTop) && isNullOrUndefined(nextBottom) ? void 0 : {
@@ -5324,7 +5462,7 @@ function calculateItemsInView(ctx, params = {}) {
5324
5462
  const needNewContainers = [];
5325
5463
  const needNewContainersSet = /* @__PURE__ */ new Set();
5326
5464
  for (let i = startBuffered; i <= endBuffered; i++) {
5327
- const id = (_l = idCache[i]) != null ? _l : getId(state, i);
5465
+ const id = (_k = idCache[i]) != null ? _k : getId(state, i);
5328
5466
  if (!containerItemKeys.has(id)) {
5329
5467
  needNewContainersSet.add(i);
5330
5468
  needNewContainers.push(i);
@@ -5333,7 +5471,7 @@ function calculateItemsInView(ctx, params = {}) {
5333
5471
  if (alwaysRenderArr.length > 0) {
5334
5472
  for (const index of alwaysRenderArr) {
5335
5473
  if (index < 0 || index >= dataLength) continue;
5336
- const id = (_m = idCache[index]) != null ? _m : getId(state, index);
5474
+ const id = (_l = idCache[index]) != null ? _l : getId(state, index);
5337
5475
  if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
5338
5476
  needNewContainersSet.add(index);
5339
5477
  needNewContainers.push(index);
@@ -5343,7 +5481,6 @@ function calculateItemsInView(ctx, params = {}) {
5343
5481
  if (stickyHeaderIndicesArr.length > 0) {
5344
5482
  handleStickyActivation(
5345
5483
  ctx,
5346
- stickyHeaderIndicesSet,
5347
5484
  stickyHeaderIndicesArr,
5348
5485
  currentStickyIdx,
5349
5486
  needNewContainers,
@@ -5355,35 +5492,34 @@ function calculateItemsInView(ctx, params = {}) {
5355
5492
  set$(ctx, "activeStickyIndex", -1);
5356
5493
  }
5357
5494
  if (needNewContainers.length > 0) {
5358
- const requiredItemTypes = getItemType ? needNewContainers.map((i) => {
5495
+ const getRequiredItemType = getItemType ? (i) => {
5359
5496
  const itemType = getItemType(data[i], i);
5360
5497
  return itemType !== void 0 ? String(itemType) : "";
5361
- }) : void 0;
5362
- const availableContainers = findAvailableContainers(
5498
+ } : void 0;
5499
+ const availableContainerAllocations = findAvailableContainers(
5363
5500
  ctx,
5364
- needNewContainers.length,
5501
+ needNewContainers,
5365
5502
  startBuffered,
5366
5503
  endBuffered,
5367
5504
  pendingRemoval,
5368
- requiredItemTypes,
5369
- needNewContainers,
5505
+ getRequiredItemType,
5370
5506
  protectedContainerKeys
5371
5507
  );
5372
- for (let idx = 0; idx < needNewContainers.length; idx++) {
5373
- const i = needNewContainers[idx];
5374
- const containerIndex = availableContainers[idx];
5375
- const id = (_n = idCache[i]) != null ? _n : getId(state, i);
5508
+ for (const allocation of availableContainerAllocations) {
5509
+ const i = allocation.itemIndex;
5510
+ const containerIndex = allocation.containerIndex;
5511
+ const id = (_m = idCache[i]) != null ? _m : getId(state, i);
5376
5512
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
5377
5513
  if (oldKey && oldKey !== id) {
5378
5514
  containerItemKeys.delete(oldKey);
5379
5515
  }
5380
5516
  set$(ctx, `containerItemKey${containerIndex}`, id);
5381
5517
  set$(ctx, `containerItemData${containerIndex}`, data[i]);
5382
- if (requiredItemTypes) {
5383
- state.containerItemTypes.set(containerIndex, requiredItemTypes[idx]);
5518
+ if (allocation.itemType !== void 0) {
5519
+ state.containerItemTypes.set(containerIndex, allocation.itemType);
5384
5520
  }
5385
5521
  containerItemKeys.set(id, containerIndex);
5386
- (_o = state.userScrollAnchorReset) == null ? void 0 : _o.keys.add(id);
5522
+ (_n = state.userScrollAnchorReset) == null ? void 0 : _n.keys.add(id);
5387
5523
  const containerSticky = `containerSticky${containerIndex}`;
5388
5524
  const isSticky = stickyHeaderIndicesSet.has(i);
5389
5525
  const isAlwaysRender = alwaysRenderSet.has(i);
@@ -5421,7 +5557,7 @@ function calculateItemsInView(ctx, params = {}) {
5421
5557
  if (alwaysRenderArr.length > 0) {
5422
5558
  for (const index of alwaysRenderArr) {
5423
5559
  if (index < 0 || index >= dataLength) continue;
5424
- const id = (_p = idCache[index]) != null ? _p : getId(state, index);
5560
+ const id = (_o = idCache[index]) != null ? _o : getId(state, index);
5425
5561
  const containerIndex = containerItemKeys.get(id);
5426
5562
  if (containerIndex !== void 0) {
5427
5563
  state.stickyContainerPool.add(containerIndex);
@@ -5440,10 +5576,11 @@ function calculateItemsInView(ctx, params = {}) {
5440
5576
  alwaysRenderSet
5441
5577
  );
5442
5578
  }
5579
+ const pendingRemovalSet = pendingRemoval.length > 0 ? new Set(pendingRemoval) : void 0;
5443
5580
  let didChangePositions = false;
5444
5581
  for (let i = 0; i < numContainers; i++) {
5445
5582
  const itemKey = peek$(ctx, `containerItemKey${i}`);
5446
- if (pendingRemoval.includes(i)) {
5583
+ if (pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(i)) {
5447
5584
  if (itemKey !== void 0) {
5448
5585
  containerItemKeys.delete(itemKey);
5449
5586
  }
@@ -5474,24 +5611,24 @@ function calculateItemsInView(ctx, params = {}) {
5474
5611
  evaluateBootstrapInitialScroll(ctx);
5475
5612
  return;
5476
5613
  }
5477
- const mountedBufferedIndices = getMountedBufferedIndices(state);
5478
- const mountedNoBufferIndices = getMountedNoBufferIndices(state);
5479
- const readinessIndices = hasActiveInitialScroll(state) ? mountedBufferedIndices : mountedNoBufferIndices.length > 0 ? mountedNoBufferIndices : mountedBufferedIndices;
5480
- if (!queuedInitialLayout && readinessIndices.length > 0 && checkAllSizesKnown(state, readinessIndices)) {
5481
- setDidLayout(ctx);
5482
- handleInitialScrollLayoutReady(ctx);
5614
+ if (!queuedInitialLayout && !state.didContainersLayout) {
5615
+ const isInitialLayoutReady = hasActiveInitialScroll(state) ? checkAllSizesKnown(state, state.startBuffered, state.endBuffered) : checkAllSizesKnown(state, state.startNoBuffer, state.endNoBuffer) || checkAllSizesKnown(state, state.startBuffered, state.endBuffered);
5616
+ if (isInitialLayoutReady) {
5617
+ setDidLayout(ctx);
5618
+ handleInitialScrollLayoutReady(ctx);
5619
+ }
5483
5620
  }
5484
- if (viewabilityConfigCallbackPairs && startNoBuffer !== null && endNoBuffer !== null) {
5621
+ if (viewabilityConfigCallbackPairs && visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null) {
5485
5622
  if (!didMVCPAdjustScroll) {
5486
5623
  updateViewableItems(
5487
5624
  ctx.state,
5488
5625
  ctx,
5489
5626
  viewabilityConfigCallbackPairs,
5490
5627
  scrollLength,
5491
- startNoBuffer,
5492
- endNoBuffer,
5493
- startBuffered != null ? startBuffered : startNoBuffer,
5494
- endBuffered != null ? endBuffered : endNoBuffer
5628
+ visibleRange.startNoBuffer,
5629
+ visibleRange.endNoBuffer,
5630
+ startBuffered != null ? startBuffered : visibleRange.startNoBuffer,
5631
+ endBuffered != null ? endBuffered : visibleRange.endNoBuffer
5495
5632
  );
5496
5633
  }
5497
5634
  }
@@ -5819,22 +5956,27 @@ var ScrollAdjustHandler = class {
5819
5956
 
5820
5957
  // src/core/updateAnchoredEndSpace.ts
5821
5958
  function maybeUpdateAnchoredEndSpace(ctx) {
5822
- var _a3;
5959
+ var _a3, _b;
5823
5960
  const state = ctx.state;
5824
5961
  const anchoredEndSpace = state.props.anchoredEndSpace;
5825
5962
  const previousSize = peek$(ctx, "anchoredEndSpaceSize");
5963
+ const previousReadyAnchorIndex = state.anchoredEndSpaceReadyAnchorIndex;
5964
+ const previousReadyAnchorKey = state.anchoredEndSpaceReadyAnchorKey;
5965
+ const nextAnchorIndex = anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex;
5966
+ let nextAnchorKey;
5967
+ let isReady = true;
5826
5968
  let nextSize = 0;
5827
5969
  if (anchoredEndSpace) {
5828
5970
  const { anchorIndex, anchorMaxSize, anchorOffset = 0 } = anchoredEndSpace;
5829
5971
  const { data } = state.props;
5830
5972
  if (anchorIndex >= 0 && anchorIndex < data.length && state.scrollLength > 0) {
5973
+ nextAnchorKey = getId(state, anchorIndex);
5831
5974
  let contentBelowAnchor = 0;
5832
5975
  const footerSize = ctx.values.get("footerSize") || 0;
5833
5976
  const stylePaddingBottom = state.props.stylePaddingBottom || 0;
5834
5977
  let hasUnknownTailSize = false;
5835
5978
  for (let index = anchorIndex; index < data.length; index++) {
5836
- const itemKey = getId(state, index);
5837
- const size = itemKey ? state.sizesKnown.get(itemKey) : void 0;
5979
+ const size = getKnownOrFixedItemSize(ctx, index);
5838
5980
  const effectiveSize = index === anchorIndex && anchorMaxSize !== void 0 ? Math.min(size || 0, Math.max(0, anchorMaxSize)) : size;
5839
5981
  if (size === void 0) {
5840
5982
  hasUnknownTailSize = true;
@@ -5844,15 +5986,25 @@ function maybeUpdateAnchoredEndSpace(ctx) {
5844
5986
  }
5845
5987
  }
5846
5988
  contentBelowAnchor += footerSize + stylePaddingBottom;
5989
+ isReady = !hasUnknownTailSize;
5847
5990
  nextSize = hasUnknownTailSize ? previousSize || 0 : Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
5991
+ } else if (anchorIndex >= 0) {
5992
+ isReady = false;
5848
5993
  }
5849
5994
  }
5850
- if (previousSize !== nextSize) {
5851
- set$(ctx, "anchoredEndSpaceSize", nextSize);
5852
- (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
5853
- if (anchoredEndSpace == null ? void 0 : anchoredEndSpace.includeInEndInset) {
5995
+ const didSizeChange = previousSize !== nextSize;
5996
+ const didReadyAnchorChange = previousReadyAnchorIndex !== nextAnchorIndex || previousReadyAnchorKey !== nextAnchorKey;
5997
+ if (isReady && (didSizeChange || didReadyAnchorChange)) {
5998
+ state.anchoredEndSpaceReadyAnchorIndex = nextAnchorIndex;
5999
+ state.anchoredEndSpaceReadyAnchorKey = nextAnchorKey;
6000
+ if (didSizeChange) {
6001
+ set$(ctx, "anchoredEndSpaceSize", nextSize);
6002
+ (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
6003
+ }
6004
+ if (didSizeChange && (anchoredEndSpace == null ? void 0 : anchoredEndSpace.includeInEndInset)) {
5854
6005
  updateScroll(ctx, state.scroll, true);
5855
6006
  }
6007
+ (_b = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onReady) == null ? void 0 : _b.call(anchoredEndSpace, { anchorIndex: nextAnchorIndex, anchorKey: nextAnchorKey, size: nextSize });
5856
6008
  }
5857
6009
  return nextSize;
5858
6010
  }
@@ -5935,6 +6087,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5935
6087
  } = state;
5936
6088
  if (!data) return;
5937
6089
  const index = state.indexByKey.get(itemKey);
6090
+ let resolvedMeasurementItem;
5938
6091
  if (getFixedItemSize) {
5939
6092
  if (index === void 0) {
5940
6093
  return;
@@ -5945,6 +6098,12 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5945
6098
  }
5946
6099
  const type = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
5947
6100
  const size2 = getFixedItemSize(itemData, index, type);
6101
+ resolvedMeasurementItem = {
6102
+ didResolveFixedItemSize: true,
6103
+ fixedItemSize: size2,
6104
+ itemData,
6105
+ itemType: type
6106
+ };
5948
6107
  if (size2 !== void 0 && size2 === sizesKnown.get(itemKey)) {
5949
6108
  updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
5950
6109
  return;
@@ -5954,7 +6113,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5954
6113
  let shouldMaintainScrollAtEnd = false;
5955
6114
  let minIndexSizeChanged;
5956
6115
  const prevSizeKnown = state.sizesKnown.get(itemKey);
5957
- const diff = updateOneItemSize(ctx, itemKey, sizeObj);
6116
+ const diff = updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem);
5958
6117
  const size = roundSize(horizontal ? sizeObj.width : sizeObj.height);
5959
6118
  if (diff !== 0) {
5960
6119
  minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
@@ -5979,7 +6138,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5979
6138
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
5980
6139
  }
5981
6140
  updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
5982
- if (didContainersLayout || checkAllSizesKnown(state, getMountedBufferedIndices(state))) {
6141
+ if (didContainersLayout || checkAllSizesKnown(state, state.startBuffered, state.endBuffered)) {
5983
6142
  if (needsRecalculate) {
5984
6143
  state.scrollForNextCalculateItemsInView = void 0;
5985
6144
  runOrScheduleMVCPRecalculate(ctx);
@@ -5993,8 +6152,8 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5993
6152
  }
5994
6153
  }
5995
6154
  }
5996
- function updateOneItemSize(ctx, itemKey, sizeObj) {
5997
- var _a3, _b;
6155
+ function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
6156
+ var _a3, _b, _c;
5998
6157
  const state = ctx.state;
5999
6158
  const {
6000
6159
  indexByKey,
@@ -6004,20 +6163,25 @@ function updateOneItemSize(ctx, itemKey, sizeObj) {
6004
6163
  } = state;
6005
6164
  if (!data) return 0;
6006
6165
  const index = indexByKey.get(itemKey);
6007
- const itemData = data[index];
6008
- let itemType;
6009
- let fixedItemSize;
6010
- if (getFixedItemSize) {
6011
- itemType = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
6166
+ const itemData = (_a3 = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemData) != null ? _a3 : data[index];
6167
+ let itemType = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemType;
6168
+ let fixedItemSize = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.fixedItemSize;
6169
+ if (getFixedItemSize && !(resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize)) {
6170
+ itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
6012
6171
  fixedItemSize = getFixedItemSize(itemData, index, itemType);
6013
6172
  }
6014
- const prevSize = getItemSize(ctx, itemKey, index, itemData);
6173
+ const resolvedItemSize = (resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize) || itemType !== void 0 || fixedItemSize !== void 0 ? {
6174
+ didResolveFixedItemSize: resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize,
6175
+ fixedItemSize,
6176
+ itemType
6177
+ } : void 0;
6178
+ const prevSize = getItemSize(ctx, itemKey, index, itemData, void 0, void 0, void 0, resolvedItemSize);
6015
6179
  const rawSize = horizontal ? sizeObj.width : sizeObj.height;
6016
6180
  const prevSizeKnown = sizesKnown.get(itemKey);
6017
6181
  const size = Math.round(rawSize) ;
6018
6182
  sizesKnown.set(itemKey, size);
6019
6183
  if (fixedItemSize === void 0 && size > 0) {
6020
- itemType != null ? itemType : itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
6184
+ itemType != null ? itemType : itemType = getItemType ? (_c = getItemType(itemData, index)) != null ? _c : "" : "";
6021
6185
  let averages = averageSizes[itemType];
6022
6186
  if (!averages) {
6023
6187
  averages = averageSizes[itemType] = { avg: 0, num: 0 };
@@ -6085,6 +6249,25 @@ function createColumnWrapperStyle(contentContainerStyle) {
6085
6249
  }
6086
6250
  }
6087
6251
 
6252
+ // src/core/scrollToEnd.ts
6253
+ function scrollToEnd(ctx, options) {
6254
+ const state = ctx.state;
6255
+ const data = state.props.data;
6256
+ const index = data.length - 1;
6257
+ if (index === -1) {
6258
+ return false;
6259
+ }
6260
+ const paddingBottom = state.props.stylePaddingBottom || 0;
6261
+ const footerSize = peek$(ctx, "footerSize") || 0;
6262
+ scrollToIndex(ctx, {
6263
+ ...options,
6264
+ index,
6265
+ viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
6266
+ viewPosition: 1
6267
+ });
6268
+ return true;
6269
+ }
6270
+
6088
6271
  // src/utils/createImperativeHandle.ts
6089
6272
  var DEFAULT_AVERAGE_ITEM_SIZE_TYPE = "default";
6090
6273
  function getAverageItemSizes(state) {
@@ -6100,7 +6283,12 @@ function getAverageItemSizes(state) {
6100
6283
  }
6101
6284
  return averageItemSizes;
6102
6285
  }
6103
- function createImperativeHandle(ctx) {
6286
+ function triggerMountedContainerLayouts(ctx) {
6287
+ for (const triggerLayout of ctx.containerLayoutTriggers.values()) {
6288
+ triggerLayout();
6289
+ }
6290
+ }
6291
+ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
6104
6292
  const state = ctx.state;
6105
6293
  const IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS = 800;
6106
6294
  const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
@@ -6117,15 +6305,10 @@ function createImperativeHandle(ctx) {
6117
6305
  if (targetIndex >= dataLength) {
6118
6306
  return false;
6119
6307
  }
6120
- if (anchorIndex === void 0 || anchorIndex < 0 || anchorIndex >= dataLength || targetIndex < anchorIndex || props.getFixedItemSize) {
6308
+ if (anchorIndex === void 0 || anchorIndex < 0 || anchorIndex >= dataLength || targetIndex < anchorIndex) {
6121
6309
  return true;
6122
6310
  }
6123
- for (let index = anchorIndex; index < dataLength; index++) {
6124
- if (!state.sizesKnown.has(getId(state, index))) {
6125
- return false;
6126
- }
6127
- }
6128
- return true;
6311
+ return areKnownOrFixedItemSizesAvailable(ctx, anchorIndex, dataLength - 1);
6129
6312
  };
6130
6313
  const runWhenReady = (token, run, isReady) => {
6131
6314
  const startedAt = Date.now();
@@ -6148,11 +6331,7 @@ function createImperativeHandle(ctx) {
6148
6331
  };
6149
6332
  requestAnimationFrame(check);
6150
6333
  };
6151
- const runScrollWithPromise = (run, isReady = () => true) => new Promise((resolve) => {
6152
- var _a3;
6153
- const token = ++imperativeScrollToken;
6154
- (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
6155
- state.pendingScrollResolve = resolve;
6334
+ const runScrollRequest = (token, resolve, run, isReady = () => true) => {
6156
6335
  const runNow = () => {
6157
6336
  if (token !== imperativeScrollToken) {
6158
6337
  return;
@@ -6170,7 +6349,33 @@ function createImperativeHandle(ctx) {
6170
6349
  } else {
6171
6350
  runNow();
6172
6351
  }
6352
+ };
6353
+ const startImperativeScroll = (resolve) => {
6354
+ var _a3;
6355
+ const token = ++imperativeScrollToken;
6356
+ state.pendingScrollToEnd = void 0;
6357
+ (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
6358
+ state.pendingScrollResolve = resolve;
6359
+ return token;
6360
+ };
6361
+ const runScrollWithPromise = (run, isReady = () => true) => new Promise((resolve) => {
6362
+ const token = startImperativeScroll(resolve);
6363
+ runScrollRequest(token, resolve, run, isReady);
6173
6364
  });
6365
+ state.runPendingScrollToEnd = () => {
6366
+ const pendingScroll = state.pendingScrollToEnd;
6367
+ if (pendingScroll) {
6368
+ state.pendingScrollToEnd = void 0;
6369
+ if (pendingScroll.token === imperativeScrollToken) {
6370
+ runScrollRequest(
6371
+ pendingScroll.token,
6372
+ pendingScroll.resolve,
6373
+ () => scrollToEnd(ctx, pendingScroll.options),
6374
+ () => isScrollToIndexReady(state.props.data.length - 1, true)
6375
+ );
6376
+ }
6377
+ }
6378
+ };
6174
6379
  const scrollIndexIntoView = (options) => {
6175
6380
  if (state) {
6176
6381
  const { index, ...rest } = options;
@@ -6208,6 +6413,7 @@ function createImperativeHandle(ctx) {
6208
6413
  state.columns.length = 0;
6209
6414
  state.columnSpans.length = 0;
6210
6415
  }
6416
+ triggerMountedContainerLayouts(ctx);
6211
6417
  (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
6212
6418
  };
6213
6419
  return {
@@ -6269,26 +6475,20 @@ function createImperativeHandle(ctx) {
6269
6475
  }
6270
6476
  return false;
6271
6477
  }),
6272
- scrollToEnd: (options) => runScrollWithPromise(
6273
- () => {
6274
- const data = state.props.data;
6275
- const stylePaddingBottom = state.props.stylePaddingBottom;
6276
- const index = data.length - 1;
6277
- if (index !== -1) {
6278
- const paddingBottom = stylePaddingBottom || 0;
6279
- const footerSize = peek$(ctx, "footerSize") || 0;
6280
- scrollToIndex(ctx, {
6281
- ...options,
6282
- index,
6283
- viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
6284
- viewPosition: 1
6285
- });
6286
- return true;
6287
- }
6288
- return false;
6289
- },
6290
- () => isScrollToIndexReady(state.props.data.length - 1, true)
6291
- ),
6478
+ scrollToEnd: (options) => new Promise((resolve) => {
6479
+ var _a3;
6480
+ const token = startImperativeScroll(resolve);
6481
+ state.pendingScrollToEnd = {
6482
+ options,
6483
+ resolve,
6484
+ token
6485
+ };
6486
+ if (scheduleImperativeScrollCommit) {
6487
+ scheduleImperativeScrollCommit();
6488
+ } else {
6489
+ (_a3 = state.runPendingScrollToEnd) == null ? void 0 : _a3.call(state);
6490
+ }
6491
+ }),
6292
6492
  scrollToIndex: (params) => {
6293
6493
  return runScrollWithPromise(
6294
6494
  () => {
@@ -6658,6 +6858,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6658
6858
  viewOffset: 0
6659
6859
  } : void 0;
6660
6860
  const [canRender, setCanRender] = React3__namespace.useState(!IsNewArchitecture);
6861
+ const [, scheduleImperativeScrollCommit] = React3__namespace.useReducer((value) => value + 1, 0);
6661
6862
  const ctx = useStateContext();
6662
6863
  ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
6663
6864
  const refScroller = React3.useRef(null);
@@ -7015,14 +7216,18 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7015
7216
  viewabilityConfigCallbackPairs
7016
7217
  });
7017
7218
  state.viewabilityConfigCallbackPairs = viewability;
7018
- state.enableScrollForNextCalculateItemsInView = !viewability;
7219
+ state.enableScrollForNextCalculateItemsInView = true;
7019
7220
  if (viewability) {
7020
7221
  state.scrollForNextCalculateItemsInView = void 0;
7021
7222
  }
7022
7223
  }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
7023
7224
  useInit(() => {
7024
7225
  });
7025
- React3.useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx), []);
7226
+ React3.useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx, scheduleImperativeScrollCommit), []);
7227
+ React3.useLayoutEffect(() => {
7228
+ var _a4;
7229
+ (_a4 = state.runPendingScrollToEnd) == null ? void 0 : _a4.call(state);
7230
+ });
7026
7231
  React3.useEffect(() => {
7027
7232
  if (usesBootstrapInitialScroll) {
7028
7233
  return;