@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-native.mjs CHANGED
@@ -122,6 +122,7 @@ function StateProvider({ children }) {
122
122
  const [value] = React2.useState(() => ({
123
123
  animatedScrollY: createAnimatedValue(0),
124
124
  columnWrapperStyle: void 0,
125
+ containerLayoutTriggers: /* @__PURE__ */ new Map(),
125
126
  contextNum: contextNum++,
126
127
  listeners: /* @__PURE__ */ new Map(),
127
128
  mapViewabilityAmountCallbacks: /* @__PURE__ */ new Map(),
@@ -540,9 +541,6 @@ function roundSize(size) {
540
541
  function isNullOrUndefined(value) {
541
542
  return value === null || value === void 0;
542
543
  }
543
- function comparatorDefault(a, b) {
544
- return a - b;
545
- }
546
544
  function getPadding(s, type) {
547
545
  var _a3, _b, _c;
548
546
  const axisPadding = type === "Left" || type === "Right" ? s.paddingHorizontal : s.paddingVertical;
@@ -895,18 +893,6 @@ var Container = typedMemo(function Container2({
895
893
  [itemKey, data, extraData]
896
894
  );
897
895
  const { index, renderedItem } = renderedItemInfo || {};
898
- const contextValue = useMemo(() => {
899
- ctx.viewRefs.set(id, ref);
900
- return {
901
- containerId: id,
902
- index,
903
- itemKey,
904
- triggerLayout: () => {
905
- forceLayoutRender((v) => v + 1);
906
- },
907
- value: data
908
- };
909
- }, [id, itemKey, index, data]);
910
896
  const onLayoutChange = useCallback((rectangle) => {
911
897
  var _a3, _b;
912
898
  const {
@@ -956,6 +942,27 @@ var Container = typedMemo(function Container2({
956
942
  });
957
943
  }
958
944
  }, []);
945
+ const triggerLayout = useCallback(() => {
946
+ forceLayoutRender((v) => v + 1);
947
+ }, []);
948
+ const contextValue = useMemo(() => {
949
+ ctx.viewRefs.set(id, ref);
950
+ return {
951
+ containerId: id,
952
+ index,
953
+ itemKey,
954
+ triggerLayout,
955
+ value: data
956
+ };
957
+ }, [id, itemKey, index, data, triggerLayout]);
958
+ useLayoutEffect(() => {
959
+ ctx.containerLayoutTriggers.set(id, triggerLayout);
960
+ return () => {
961
+ if (ctx.containerLayoutTriggers.get(id) === triggerLayout) {
962
+ ctx.containerLayoutTriggers.delete(id);
963
+ }
964
+ };
965
+ }, [ctx, id, triggerLayout]);
959
966
  const { onLayout } = useOnLayoutSync(
960
967
  {
961
968
  onLayoutChange,
@@ -1810,17 +1817,42 @@ function setSize(ctx, itemKey, size, notifyTotalSize = true) {
1810
1817
  }
1811
1818
 
1812
1819
  // src/utils/getItemSize.ts
1813
- function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize, notifyTotalSize) {
1814
- var _a3, _b, _c;
1820
+ function getKnownOrFixedSize(ctx, key, index, data, resolved) {
1821
+ var _a3, _b;
1822
+ const state = ctx.state;
1823
+ const { getFixedItemSize, getItemType } = state.props;
1824
+ let size = key ? state.sizesKnown.get(key) : void 0;
1825
+ if (size === void 0 && key && getFixedItemSize) {
1826
+ const itemType = (_b = resolved == null ? void 0 : resolved.itemType) != null ? _b : getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1827
+ size = (resolved == null ? void 0 : resolved.didResolveFixedItemSize) ? resolved.fixedItemSize : getFixedItemSize(data, index, itemType);
1828
+ if (size !== void 0) {
1829
+ state.sizesKnown.set(key, size);
1830
+ }
1831
+ }
1832
+ return size;
1833
+ }
1834
+ function getKnownOrFixedItemSize(ctx, index) {
1835
+ const key = getId(ctx.state, index);
1836
+ return getKnownOrFixedSize(ctx, key, index, ctx.state.props.data[index]);
1837
+ }
1838
+ function areKnownOrFixedItemSizesAvailable(ctx, startIndex, endIndex) {
1839
+ for (let index = startIndex; index <= endIndex; index++) {
1840
+ if (getKnownOrFixedItemSize(ctx, index) === void 0) {
1841
+ return false;
1842
+ }
1843
+ }
1844
+ return true;
1845
+ }
1846
+ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize, notifyTotalSize, resolved) {
1847
+ var _a3, _b, _c, _d;
1815
1848
  const state = ctx.state;
1816
1849
  const {
1817
- sizesKnown,
1818
1850
  sizes,
1819
1851
  averageSizes,
1820
- props: { estimatedItemSize, getFixedItemSize, getItemType },
1852
+ props: { estimatedItemSize, getItemType },
1821
1853
  scrollingTo
1822
1854
  } = state;
1823
- const sizeKnown = sizesKnown.get(key);
1855
+ const sizeKnown = state.sizesKnown.get(key);
1824
1856
  if (sizeKnown !== void 0) {
1825
1857
  return sizeKnown;
1826
1858
  }
@@ -1831,15 +1863,14 @@ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize, no
1831
1863
  return renderedSize;
1832
1864
  }
1833
1865
  }
1834
- const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1835
- if (getFixedItemSize) {
1836
- size = getFixedItemSize(data, index, itemType);
1837
- if (size !== void 0) {
1838
- sizesKnown.set(key, size);
1839
- }
1866
+ size = getKnownOrFixedSize(ctx, key, index, data, resolved);
1867
+ if (size !== void 0) {
1868
+ setSize(ctx, key, size, notifyTotalSize);
1869
+ return size;
1840
1870
  }
1841
- if (size === void 0 && useAverageSize && sizeKnown === void 0 && !scrollingTo) {
1842
- const averageSizeForType = (_b = averageSizes[itemType]) == null ? void 0 : _b.avg;
1871
+ const itemType = (_b = resolved == null ? void 0 : resolved.itemType) != null ? _b : getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1872
+ if (useAverageSize && !scrollingTo) {
1873
+ const averageSizeForType = (_c = averageSizes[itemType]) == null ? void 0 : _c.avg;
1843
1874
  if (averageSizeForType !== void 0) {
1844
1875
  size = roundSize(averageSizeForType);
1845
1876
  }
@@ -1847,8 +1878,8 @@ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize, no
1847
1878
  if (size === void 0 && renderedSize !== void 0) {
1848
1879
  return renderedSize;
1849
1880
  }
1850
- if (size === void 0 && useAverageSize && sizeKnown === void 0 && scrollingTo) {
1851
- const averageSizeForType = (_c = scrollingTo.averageSizeSnapshot) == null ? void 0 : _c[itemType];
1881
+ if (size === void 0 && useAverageSize && scrollingTo) {
1882
+ const averageSizeForType = (_d = scrollingTo.averageSizeSnapshot) == null ? void 0 : _d[itemType];
1852
1883
  if (averageSizeForType !== void 0) {
1853
1884
  size = roundSize(averageSizeForType);
1854
1885
  }
@@ -2555,8 +2586,8 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2555
2586
  if ((options == null ? void 0 : options.markHasScrolled) !== false) {
2556
2587
  state.hasScrolled = true;
2557
2588
  }
2558
- state.lastBatchingAction = Date.now();
2559
2589
  const currentTime = Date.now();
2590
+ state.lastBatchingAction = currentTime;
2560
2591
  const adjust = scrollAdjustHandler.getAdjust();
2561
2592
  const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
2562
2593
  if (adjustChanged) {
@@ -2865,26 +2896,22 @@ function advanceCurrentInitialScrollSession(ctx, options) {
2865
2896
  }
2866
2897
 
2867
2898
  // src/utils/checkAllSizesKnown.ts
2868
- function isNullOrUndefined2(value) {
2869
- return value === null || value === void 0;
2870
- }
2871
- function getMountedIndicesInRange(state, start, end) {
2872
- if (!isNullOrUndefined2(end) && !isNullOrUndefined2(start) && start >= 0 && end >= 0) {
2873
- 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);
2899
+ function checkAllSizesKnown(state, start, end) {
2900
+ if (start == null || end == null || start < 0 || end < start) {
2901
+ return false;
2874
2902
  }
2875
- return [];
2876
- }
2877
- function getMountedBufferedIndices(state) {
2878
- return getMountedIndicesInRange(state, state.startBuffered, state.endBuffered);
2879
- }
2880
- function getMountedNoBufferIndices(state) {
2881
- return getMountedIndicesInRange(state, state.startNoBuffer, state.endNoBuffer);
2882
- }
2883
- function checkAllSizesKnown(state, indices) {
2884
- return indices.length > 0 && indices.every((index) => {
2885
- const key = getId(state, index);
2886
- return key !== void 0 && state.sizesKnown.has(key);
2887
- });
2903
+ let hasMountedIndex = false;
2904
+ for (const key of state.containerItemKeys.keys()) {
2905
+ const index = state.indexByKey.get(key);
2906
+ if (index !== void 0 && index >= start && index <= end) {
2907
+ hasMountedIndex = true;
2908
+ const id = getId(state, index);
2909
+ if (id === void 0 || !state.sizesKnown.has(id)) {
2910
+ return false;
2911
+ }
2912
+ }
2913
+ }
2914
+ return hasMountedIndex;
2888
2915
  }
2889
2916
 
2890
2917
  // src/core/bootstrapInitialScroll.ts
@@ -3385,8 +3412,7 @@ function evaluateBootstrapInitialScroll(ctx) {
3385
3412
  bootstrapInitialScroll.targetIndexSeed = void 0;
3386
3413
  }
3387
3414
  const resolvedOffset = resolveInitialScrollOffset(ctx, initialScroll);
3388
- const mountedBufferedIndices = getMountedBufferedIndices(state);
3389
- const areMountedBufferedIndicesMeasured = checkAllSizesKnown(state, mountedBufferedIndices);
3415
+ const areMountedBufferedIndicesMeasured = checkAllSizesKnown(state, state.startBuffered, state.endBuffered);
3390
3416
  const didResolvedOffsetChange = Math.abs(bootstrapInitialScroll.scroll - resolvedOffset) > 1;
3391
3417
  const { data } = state.props;
3392
3418
  const visibleIndices = getBootstrapRevealVisibleIndices({
@@ -3817,7 +3843,14 @@ function updateSnapToOffsets(ctx) {
3817
3843
  }
3818
3844
 
3819
3845
  // src/core/updateItemPositions.ts
3820
- function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP, optimizeForVisibleWindow = false } = {
3846
+ function updateItemPositions(ctx, dataChanged, {
3847
+ doMVCP,
3848
+ forceFullUpdate = false,
3849
+ optimizeForVisibleWindow = false,
3850
+ scrollBottomBuffered,
3851
+ scrollVelocity,
3852
+ startIndex
3853
+ } = {
3821
3854
  doMVCP: false,
3822
3855
  forceFullUpdate: false,
3823
3856
  optimizeForVisibleWindow: false,
@@ -3844,7 +3877,7 @@ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffere
3844
3877
  const extraData = peek$(ctx, "extraData");
3845
3878
  const layoutConfig = overrideItemLayout ? { span: 1 } : void 0;
3846
3879
  const lastScrollDelta = state.lastScrollDelta;
3847
- const velocity = getScrollVelocity(state);
3880
+ const velocity = scrollVelocity != null ? scrollVelocity : getScrollVelocity(state);
3848
3881
  const shouldOptimize = !forceFullUpdate && !dataChanged && (optimizeForVisibleWindow || Math.abs(velocity) > 0 || Platform.OS === "web" && state.scrollLength > 0 && lastScrollDelta > state.scrollLength);
3849
3882
  const maxVisibleArea = scrollBottomBuffered + 1e3;
3850
3883
  const useAverageSize = true;
@@ -4024,25 +4057,28 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
4024
4057
  const configId = viewabilityConfig.id;
4025
4058
  const viewabilityState = ensureViewabilityState(ctx, configId);
4026
4059
  const { viewableItems: previousViewableItems, start, end, startBuffered, endBuffered } = viewabilityState;
4027
- const viewabilityTokens = /* @__PURE__ */ new Map();
4060
+ let staleViewabilityAmountIds;
4028
4061
  for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
4029
- viewabilityTokens.set(
4062
+ const nextValue = computeViewability(
4063
+ state,
4064
+ ctx,
4065
+ viewabilityConfig,
4030
4066
  containerId,
4031
- computeViewability(
4032
- state,
4033
- ctx,
4034
- viewabilityConfig,
4035
- containerId,
4036
- value.key,
4037
- scrollSize,
4038
- value.item,
4039
- value.index
4040
- )
4067
+ value.key,
4068
+ scrollSize,
4069
+ value.item,
4070
+ value.index
4041
4071
  );
4072
+ if (nextValue.sizeVisible < 0) {
4073
+ staleViewabilityAmountIds != null ? staleViewabilityAmountIds : staleViewabilityAmountIds = [];
4074
+ staleViewabilityAmountIds.push(containerId);
4075
+ }
4042
4076
  }
4043
4077
  const changed = [];
4078
+ const previousViewableKeys = /* @__PURE__ */ new Set();
4044
4079
  if (previousViewableItems) {
4045
4080
  for (const viewToken of previousViewableItems) {
4081
+ previousViewableKeys.add(viewToken.key);
4046
4082
  const containerId = findContainerId(ctx, viewToken.key);
4047
4083
  if (!checkIsViewable(
4048
4084
  state,
@@ -4074,7 +4110,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
4074
4110
  key
4075
4111
  };
4076
4112
  viewableItems.push(viewToken);
4077
- if (!(previousViewableItems == null ? void 0 : previousViewableItems.find((v) => v.key === viewToken.key))) {
4113
+ if (!previousViewableKeys.has(viewToken.key)) {
4078
4114
  changed.push(viewToken);
4079
4115
  }
4080
4116
  }
@@ -4095,20 +4131,17 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
4095
4131
  onViewableItemsChanged({ changed, end, endBuffered, start, startBuffered, viewableItems });
4096
4132
  }
4097
4133
  }
4098
- for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
4099
- if (value.sizeVisible < 0) {
4100
- ctx.mapViewabilityAmountValues.delete(containerId);
4134
+ if (staleViewabilityAmountIds) {
4135
+ for (const containerId of staleViewabilityAmountIds) {
4136
+ const value = ctx.mapViewabilityAmountValues.get(containerId);
4137
+ if (value && value.sizeVisible < 0) {
4138
+ ctx.mapViewabilityAmountValues.delete(containerId);
4139
+ }
4101
4140
  }
4102
4141
  }
4103
4142
  }
4104
- function shallowEqual(prev, next) {
4105
- if (!prev) return false;
4106
- const keys = Object.keys(next);
4107
- for (let i = 0; i < keys.length; i++) {
4108
- const k = keys[i];
4109
- if (prev[k] !== next[k]) return false;
4110
- }
4111
- return true;
4143
+ function areViewabilityAmountTokensEqual(prev, next) {
4144
+ 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;
4112
4145
  }
4113
4146
  function computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
4114
4147
  const { sizes, scroll: scrollState } = state;
@@ -4133,7 +4166,7 @@ function computeViewability(state, ctx, viewabilityConfig, containerId, key, scr
4133
4166
  sizeVisible: -1
4134
4167
  };
4135
4168
  const prev2 = ctx.mapViewabilityAmountValues.get(containerId);
4136
- if (!shallowEqual(prev2, value2)) {
4169
+ if (!areViewabilityAmountTokensEqual(prev2, value2)) {
4137
4170
  ctx.mapViewabilityAmountValues.set(containerId, value2);
4138
4171
  const cb = ctx.mapViewabilityAmountCallbacks.get(containerId);
4139
4172
  if (cb) {
@@ -4163,7 +4196,7 @@ function computeViewability(state, ctx, viewabilityConfig, containerId, key, scr
4163
4196
  sizeVisible
4164
4197
  };
4165
4198
  const prev = ctx.mapViewabilityAmountValues.get(containerId);
4166
- if (!shallowEqual(prev, value)) {
4199
+ if (!areViewabilityAmountTokensEqual(prev, value)) {
4167
4200
  ctx.mapViewabilityAmountValues.set(containerId, value);
4168
4201
  const cb = ctx.mapViewabilityAmountCallbacks.get(containerId);
4169
4202
  if (cb) {
@@ -4214,126 +4247,152 @@ function getExpandedContainerPoolSize(dataLength, numContainers) {
4214
4247
  }
4215
4248
 
4216
4249
  // src/utils/findAvailableContainers.ts
4217
- function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers, protectedKeys) {
4250
+ function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffered, pendingRemoval, getRequiredItemType, protectedKeys) {
4251
+ const numNeeded = needNewContainers.length;
4252
+ if (numNeeded === 0) {
4253
+ return [];
4254
+ }
4218
4255
  const numContainers = peek$(ctx, "numContainers");
4219
4256
  const state = ctx.state;
4220
4257
  const { stickyContainerPool, containerItemTypes } = state;
4221
4258
  const shouldAvoidAssignedContainerReuse = state.props.recycleItems && !!state.props.positionComponentInternal;
4222
- const result = [];
4223
- const availableContainers = [];
4224
- const pendingRemovalSet = new Set(pendingRemoval);
4259
+ const allocations = [];
4260
+ const pendingRemovalSet = pendingRemoval.length > 0 ? new Set(pendingRemoval) : void 0;
4225
4261
  let pendingRemovalChanged = false;
4262
+ let nextNewContainerIndex = numContainers;
4263
+ const usedContainers = /* @__PURE__ */ new Set();
4264
+ let availableContainers;
4226
4265
  const stickyHeaderIndicesSet = state.props.stickyHeaderIndicesSet;
4227
- const stickyHeaderItemIndices = (needNewContainers == null ? void 0 : needNewContainers.filter((index) => stickyHeaderIndicesSet.has(index))) || [];
4228
4266
  const canReuseContainer = (containerIndex, requiredType) => {
4229
4267
  if (!requiredType) return true;
4230
4268
  const existingType = containerItemTypes.get(containerIndex);
4231
4269
  if (!existingType) return true;
4232
4270
  return existingType === requiredType;
4233
4271
  };
4234
- const neededTypes = requiredItemTypes ? [...requiredItemTypes] : [];
4235
- let typeIndex = 0;
4236
- for (let i = 0; i < stickyHeaderItemIndices.length; i++) {
4237
- const requiredType = neededTypes[typeIndex];
4238
- let foundContainer = false;
4239
- for (const containerIndex of stickyContainerPool) {
4240
- const key = peek$(ctx, `containerItemKey${containerIndex}`);
4241
- const isPendingRemoval = pendingRemovalSet.has(containerIndex);
4242
- if ((key === void 0 || isPendingRemoval) && canReuseContainer(containerIndex, requiredType) && !result.includes(containerIndex)) {
4243
- result.push(containerIndex);
4244
- if (isPendingRemoval && pendingRemovalSet.delete(containerIndex)) {
4245
- pendingRemovalChanged = true;
4246
- }
4247
- foundContainer = true;
4248
- if (requiredItemTypes) typeIndex++;
4249
- break;
4250
- }
4272
+ const pushAllocation = (itemIndex, itemType, containerIndex) => {
4273
+ allocations.push({
4274
+ containerIndex,
4275
+ itemIndex,
4276
+ itemType
4277
+ });
4278
+ usedContainers.add(containerIndex);
4279
+ if (pendingRemovalSet == null ? void 0 : pendingRemovalSet.delete(containerIndex)) {
4280
+ pendingRemovalChanged = true;
4251
4281
  }
4252
- if (!foundContainer) {
4253
- const newContainerIndex = numContainers + result.filter((index) => index >= numContainers).length;
4254
- result.push(newContainerIndex);
4282
+ };
4283
+ const pushNewContainer = (itemIndex, itemType, isSticky) => {
4284
+ const newContainerIndex = nextNewContainerIndex++;
4285
+ pushAllocation(itemIndex, itemType, newContainerIndex);
4286
+ if (isSticky) {
4255
4287
  stickyContainerPool.add(newContainerIndex);
4256
- if (requiredItemTypes) typeIndex++;
4257
- }
4258
- }
4259
- for (let u = 0; u < numContainers && result.length < numNeeded; u++) {
4260
- if (stickyContainerPool.has(u)) {
4261
- continue;
4262
4288
  }
4263
- const key = peek$(ctx, `containerItemKey${u}`);
4264
- const requiredType = neededTypes[typeIndex];
4265
- const isPending = key !== void 0 && pendingRemovalSet.has(u);
4266
- const canUse = key === void 0 || isPending && canReuseContainer(u, requiredType);
4267
- if (canUse) {
4268
- if (isPending) {
4269
- pendingRemovalSet.delete(u);
4270
- pendingRemovalChanged = true;
4271
- }
4272
- result.push(u);
4273
- if (requiredItemTypes) {
4274
- typeIndex++;
4275
- }
4289
+ return newContainerIndex;
4290
+ };
4291
+ const canUseContainer = (containerIndex, itemType) => {
4292
+ if (usedContainers.has(containerIndex) || stickyContainerPool.has(containerIndex)) {
4293
+ return false;
4276
4294
  }
4277
- }
4278
- if (!shouldAvoidAssignedContainerReuse) {
4279
- for (let u = 0; u < numContainers && result.length < numNeeded; u++) {
4280
- if (stickyContainerPool.has(u)) {
4281
- continue;
4282
- }
4283
- const key = peek$(ctx, `containerItemKey${u}`);
4284
- if (key === void 0) continue;
4285
- if ((protectedKeys == null ? void 0 : protectedKeys.has(key)) && state.indexByKey.has(key)) continue;
4286
- const index = state.indexByKey.get(key);
4287
- const isOutOfView = index < startBuffered || index > endBuffered;
4288
- if (isOutOfView) {
4289
- const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
4290
- if (!requiredItemTypes || typeIndex < neededTypes.length && canReuseContainer(u, neededTypes[typeIndex])) {
4291
- availableContainers.push({ distance, index: u });
4295
+ const key = peek$(ctx, `containerItemKey${containerIndex}`);
4296
+ const isPending = !!(pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(containerIndex));
4297
+ return (key === void 0 || isPending) && canReuseContainer(containerIndex, itemType);
4298
+ };
4299
+ const findStickyContainer = (itemType) => {
4300
+ let foundContainer;
4301
+ for (const containerIndex of stickyContainerPool) {
4302
+ if (!usedContainers.has(containerIndex)) {
4303
+ const key = peek$(ctx, `containerItemKey${containerIndex}`);
4304
+ const isPendingRemoval = !!(pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(containerIndex));
4305
+ if ((key === void 0 || isPendingRemoval) && canReuseContainer(containerIndex, itemType)) {
4306
+ foundContainer = containerIndex;
4307
+ break;
4292
4308
  }
4293
4309
  }
4294
4310
  }
4295
- }
4296
- const remaining = numNeeded - result.length;
4297
- if (remaining > 0) {
4298
- if (availableContainers.length > 0) {
4299
- if (availableContainers.length > remaining) {
4300
- availableContainers.sort(comparatorByDistance);
4301
- availableContainers.length = remaining;
4311
+ return foundContainer;
4312
+ };
4313
+ const findUnassignedOrPendingContainer = (itemType) => {
4314
+ let foundContainer;
4315
+ for (let containerIndex = 0; containerIndex < numContainers && foundContainer === void 0; containerIndex++) {
4316
+ if (canUseContainer(containerIndex, itemType)) {
4317
+ foundContainer = containerIndex;
4302
4318
  }
4303
- for (const container of availableContainers) {
4304
- result.push(container.index);
4305
- if (requiredItemTypes) {
4306
- typeIndex++;
4319
+ }
4320
+ return foundContainer;
4321
+ };
4322
+ const getAvailableContainers = () => {
4323
+ if (!availableContainers) {
4324
+ availableContainers = [];
4325
+ if (!shouldAvoidAssignedContainerReuse) {
4326
+ for (let containerIndex = 0; containerIndex < numContainers; containerIndex++) {
4327
+ if (usedContainers.has(containerIndex) || stickyContainerPool.has(containerIndex)) {
4328
+ continue;
4329
+ }
4330
+ const key = peek$(ctx, `containerItemKey${containerIndex}`);
4331
+ if (key === void 0) continue;
4332
+ if ((protectedKeys == null ? void 0 : protectedKeys.has(key)) && state.indexByKey.has(key)) continue;
4333
+ const index = state.indexByKey.get(key);
4334
+ const isOutOfView = index < startBuffered || index > endBuffered;
4335
+ if (isOutOfView) {
4336
+ const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
4337
+ availableContainers.push({ distance, index: containerIndex });
4338
+ }
4307
4339
  }
4340
+ availableContainers.sort(comparatorByDistance);
4308
4341
  }
4309
4342
  }
4310
- const stillNeeded = numNeeded - result.length;
4311
- if (stillNeeded > 0) {
4312
- for (let i = 0; i < stillNeeded; i++) {
4313
- result.push(numContainers + i);
4343
+ return availableContainers;
4344
+ };
4345
+ const findAvailableContainer = (itemType) => {
4346
+ const containers = getAvailableContainers();
4347
+ let matchIndex = -1;
4348
+ for (let i = 0; i < containers.length && matchIndex === -1; i++) {
4349
+ const containerIndex = containers[i].index;
4350
+ if (!usedContainers.has(containerIndex) && canReuseContainer(containerIndex, itemType)) {
4351
+ matchIndex = i;
4314
4352
  }
4315
- if (IS_DEV && numContainers + stillNeeded > peek$(ctx, "numContainersPooled")) {
4316
- console.warn(
4317
- "[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.",
4318
- {
4319
- debugInfo: {
4320
- numContainers,
4321
- numContainersPooled: peek$(ctx, "numContainersPooled"),
4322
- numNeeded,
4323
- stillNeeded
4324
- }
4325
- }
4326
- );
4353
+ }
4354
+ return matchIndex === -1 ? void 0 : containers.splice(matchIndex, 1)[0].index;
4355
+ };
4356
+ for (const itemIndex of needNewContainers) {
4357
+ const itemType = getRequiredItemType == null ? void 0 : getRequiredItemType(itemIndex);
4358
+ const isSticky = stickyHeaderIndicesSet.has(itemIndex);
4359
+ let containerIndex;
4360
+ if (isSticky) {
4361
+ containerIndex = findStickyContainer(itemType);
4362
+ } else {
4363
+ containerIndex = findUnassignedOrPendingContainer(itemType);
4364
+ if (containerIndex === void 0) {
4365
+ containerIndex = findAvailableContainer(itemType);
4327
4366
  }
4328
4367
  }
4368
+ if (containerIndex !== void 0) {
4369
+ pushAllocation(itemIndex, itemType, containerIndex);
4370
+ } else {
4371
+ pushNewContainer(itemIndex, itemType, isSticky);
4372
+ }
4329
4373
  }
4330
4374
  if (pendingRemovalChanged) {
4331
4375
  pendingRemoval.length = 0;
4332
- for (const value of pendingRemovalSet) {
4333
- pendingRemoval.push(value);
4376
+ if (pendingRemovalSet) {
4377
+ for (const value of pendingRemovalSet) {
4378
+ pendingRemoval.push(value);
4379
+ }
4334
4380
  }
4335
4381
  }
4336
- return result.sort(comparatorDefault);
4382
+ if (IS_DEV && nextNewContainerIndex > peek$(ctx, "numContainersPooled")) {
4383
+ console.warn(
4384
+ "[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.",
4385
+ {
4386
+ debugInfo: {
4387
+ numContainers,
4388
+ numContainersPooled: peek$(ctx, "numContainersPooled"),
4389
+ numNeeded,
4390
+ stillNeeded: nextNewContainerIndex - numContainers
4391
+ }
4392
+ }
4393
+ );
4394
+ }
4395
+ return allocations;
4337
4396
  }
4338
4397
  function comparatorByDistance(a, b) {
4339
4398
  return b.distance - a.distance;
@@ -4359,21 +4418,28 @@ function findCurrentStickyIndex(stickyArray, scroll, state) {
4359
4418
  }
4360
4419
  return -1;
4361
4420
  }
4362
- function getActiveStickyIndices(ctx, stickyHeaderIndices) {
4421
+ function isStickyIndexActive(ctx, targetIndex) {
4363
4422
  const state = ctx.state;
4364
- return new Set(
4365
- 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))
4366
- );
4423
+ let isActive = false;
4424
+ for (const containerIndex of state.stickyContainerPool) {
4425
+ const key = peek$(ctx, `containerItemKey${containerIndex}`);
4426
+ const itemIndex = key ? state.indexByKey.get(key) : void 0;
4427
+ if (itemIndex === targetIndex) {
4428
+ isActive = true;
4429
+ break;
4430
+ }
4431
+ }
4432
+ return isActive;
4367
4433
  }
4368
- function handleStickyActivation(ctx, stickyHeaderIndices, stickyArray, currentStickyIdx, needNewContainers, needNewContainersSet, startBuffered, endBuffered) {
4434
+ function handleStickyActivation(ctx, stickyArray, currentStickyIdx, needNewContainers, needNewContainersSet, startBuffered, endBuffered) {
4369
4435
  var _a3;
4370
4436
  const state = ctx.state;
4371
- const activeIndices = getActiveStickyIndices(ctx, stickyHeaderIndices);
4372
4437
  set$(ctx, "activeStickyIndex", currentStickyIdx >= 0 ? stickyArray[currentStickyIdx] : -1);
4373
4438
  for (let offset = 0; offset <= 1; offset++) {
4374
4439
  const idx = currentStickyIdx - offset;
4375
- if (idx < 0 || activeIndices.has(stickyArray[idx])) continue;
4440
+ if (idx < 0) continue;
4376
4441
  const stickyIndex = stickyArray[idx];
4442
+ if (isStickyIndexActive(ctx, stickyIndex)) continue;
4377
4443
  const stickyId = (_a3 = state.idCache[stickyIndex]) != null ? _a3 : getId(state, stickyIndex);
4378
4444
  if (stickyId && !state.containerItemKeys.has(stickyId) && (stickyIndex < startBuffered || stickyIndex > endBuffered) && !needNewContainersSet.has(stickyIndex)) {
4379
4445
  needNewContainersSet.add(stickyIndex);
@@ -4415,10 +4481,86 @@ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentSt
4415
4481
  }
4416
4482
  }
4417
4483
  }
4484
+ function trackVisibleRange(range, i, top, size, scroll, scrollBottom) {
4485
+ let didPassVisibleEnd = false;
4486
+ if (range.startNoBuffer === null && top + size > scroll) {
4487
+ range.startNoBuffer = i;
4488
+ }
4489
+ if (range.firstFullyOnScreenIndex === void 0 && top >= scroll - 10 && top <= scrollBottom) {
4490
+ range.firstFullyOnScreenIndex = i;
4491
+ }
4492
+ if (range.startNoBuffer !== null) {
4493
+ if (top <= scrollBottom) {
4494
+ range.endNoBuffer = i;
4495
+ } else {
4496
+ didPassVisibleEnd = true;
4497
+ }
4498
+ }
4499
+ return didPassVisibleEnd;
4500
+ }
4501
+ function getIdsInVisibleRange(state, range) {
4502
+ var _a3, _b;
4503
+ const idsInView = [];
4504
+ const firstVisibleAnchorIndex = (_a3 = range.firstFullyOnScreenIndex) != null ? _a3 : range.startNoBuffer;
4505
+ if (firstVisibleAnchorIndex !== null && firstVisibleAnchorIndex !== void 0 && range.endNoBuffer !== null) {
4506
+ for (let i = firstVisibleAnchorIndex; i <= range.endNoBuffer; i++) {
4507
+ const id = (_b = state.idCache[i]) != null ? _b : getId(state, i);
4508
+ idsInView.push(id);
4509
+ }
4510
+ }
4511
+ return idsInView;
4512
+ }
4513
+ function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, scrollLength, scroll, scrollBottom) {
4514
+ var _a3, _b;
4515
+ const state = ctx.state;
4516
+ const {
4517
+ endBuffered,
4518
+ idCache,
4519
+ positions,
4520
+ props: { data },
4521
+ sizes,
4522
+ startBuffered
4523
+ } = state;
4524
+ if (startBuffered === null || endBuffered === null || startBuffered < 0 || endBuffered < startBuffered) {
4525
+ return;
4526
+ }
4527
+ const visibleRange = {
4528
+ endNoBuffer: null,
4529
+ firstFullyOnScreenIndex: void 0,
4530
+ startNoBuffer: null
4531
+ };
4532
+ for (let i = startBuffered; i <= endBuffered && i < data.length; i++) {
4533
+ const id = (_a3 = idCache[i]) != null ? _a3 : getId(state, i);
4534
+ const size = (_b = sizes.get(id)) != null ? _b : getItemSize(ctx, id, i, data[i]);
4535
+ const top = positions[i];
4536
+ const didPassVisibleEnd = trackVisibleRange(visibleRange, i, top, size, scroll, scrollBottom);
4537
+ if (didPassVisibleEnd) {
4538
+ break;
4539
+ }
4540
+ }
4541
+ Object.assign(state, {
4542
+ endNoBuffer: visibleRange.endNoBuffer,
4543
+ firstFullyOnScreenIndex: visibleRange.firstFullyOnScreenIndex,
4544
+ idsInView: getIdsInVisibleRange(state, visibleRange),
4545
+ startNoBuffer: visibleRange.startNoBuffer
4546
+ });
4547
+ if (visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null) {
4548
+ updateViewableItems(
4549
+ state,
4550
+ ctx,
4551
+ viewabilityConfigCallbackPairs,
4552
+ scrollLength,
4553
+ visibleRange.startNoBuffer,
4554
+ visibleRange.endNoBuffer,
4555
+ startBuffered,
4556
+ endBuffered
4557
+ );
4558
+ }
4559
+ }
4418
4560
  function calculateItemsInView(ctx, params = {}) {
4419
4561
  const state = ctx.state;
4420
4562
  batchedUpdates(() => {
4421
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
4563
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
4422
4564
  const {
4423
4565
  columns,
4424
4566
  containerItemKeys,
@@ -4519,6 +4661,15 @@ function calculateItemsInView(ctx, params = {}) {
4519
4661
  state.scrollForNextCalculateItemsInView = void 0;
4520
4662
  } else if ((top === null || scrollTopBuffered > top) && (bottom === null || scrollBottomBuffered < bottom)) {
4521
4663
  if (Platform.OS !== "web" || !isInMVCPActiveMode(state)) {
4664
+ if (viewabilityConfigCallbackPairs) {
4665
+ updateViewabilityForCachedRange(
4666
+ ctx,
4667
+ viewabilityConfigCallbackPairs,
4668
+ scrollLength,
4669
+ scroll,
4670
+ scrollBottom
4671
+ );
4672
+ }
4522
4673
  finishCalculateItemsInView == null ? void 0 : finishCalculateItemsInView();
4523
4674
  return;
4524
4675
  }
@@ -4535,6 +4686,7 @@ function calculateItemsInView(ctx, params = {}) {
4535
4686
  forceFullUpdate: !!forceFullItemPositions,
4536
4687
  optimizeForVisibleWindow,
4537
4688
  scrollBottomBuffered,
4689
+ scrollVelocity: speed,
4538
4690
  startIndex
4539
4691
  });
4540
4692
  totalSize = getContentSize(ctx);
@@ -4560,10 +4712,8 @@ function calculateItemsInView(ctx, params = {}) {
4560
4712
  updateScroll2(state.scroll);
4561
4713
  updateScrollRange();
4562
4714
  }
4563
- let startNoBuffer = null;
4564
4715
  let startBuffered = null;
4565
4716
  let startBufferedId = null;
4566
- let endNoBuffer = null;
4567
4717
  let endBuffered = null;
4568
4718
  let loopStart = (_f = suppressInitialScrollSideEffects ? bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.targetIndexSeed : void 0) != null ? _f : !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
4569
4719
  for (let i = loopStart; i >= 0; i--) {
@@ -4597,19 +4747,18 @@ function calculateItemsInView(ctx, params = {}) {
4597
4747
  maxIndexRendered = Math.max(maxIndexRendered, index);
4598
4748
  }
4599
4749
  }
4600
- let firstFullyOnScreenIndex;
4750
+ const visibleRange = {
4751
+ endNoBuffer: null,
4752
+ firstFullyOnScreenIndex: void 0,
4753
+ startNoBuffer: null
4754
+ };
4601
4755
  const dataLength = data.length;
4602
4756
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
4603
4757
  const id = (_i = idCache[i]) != null ? _i : getId(state, i);
4604
4758
  const size = (_j = sizes.get(id)) != null ? _j : getItemSize(ctx, id, i, data[i]);
4605
4759
  const top = positions[i];
4606
4760
  if (!foundEnd) {
4607
- if (startNoBuffer === null && top + size > scroll) {
4608
- startNoBuffer = i;
4609
- }
4610
- if (firstFullyOnScreenIndex === void 0 && top >= scroll - 10 && top <= scrollBottom) {
4611
- firstFullyOnScreenIndex = i;
4612
- }
4761
+ trackVisibleRange(visibleRange, i, top, size, scroll, scrollBottom);
4613
4762
  if (startBuffered === null && top + size > scrollTopBuffered) {
4614
4763
  startBuffered = i;
4615
4764
  startBufferedId = id;
@@ -4619,10 +4768,7 @@ function calculateItemsInView(ctx, params = {}) {
4619
4768
  nextTop = top;
4620
4769
  }
4621
4770
  }
4622
- if (startNoBuffer !== null) {
4623
- if (top <= scrollBottom) {
4624
- endNoBuffer = i;
4625
- }
4771
+ if (visibleRange.startNoBuffer !== null) {
4626
4772
  if (top <= scrollBottomBuffered) {
4627
4773
  endBuffered = i;
4628
4774
  if (scrollBottomBuffered > totalSize) {
@@ -4636,22 +4782,14 @@ function calculateItemsInView(ctx, params = {}) {
4636
4782
  }
4637
4783
  }
4638
4784
  }
4639
- const idsInView = [];
4640
- const firstVisibleAnchorIndex = firstFullyOnScreenIndex != null ? firstFullyOnScreenIndex : startNoBuffer;
4641
- if (firstVisibleAnchorIndex !== null && firstVisibleAnchorIndex !== void 0 && endNoBuffer !== null) {
4642
- for (let i = firstVisibleAnchorIndex; i <= endNoBuffer; i++) {
4643
- const id = (_k = idCache[i]) != null ? _k : getId(state, i);
4644
- idsInView.push(id);
4645
- }
4646
- }
4647
4785
  Object.assign(state, {
4648
4786
  endBuffered,
4649
- endNoBuffer,
4650
- firstFullyOnScreenIndex,
4651
- idsInView,
4787
+ endNoBuffer: visibleRange.endNoBuffer,
4788
+ firstFullyOnScreenIndex: visibleRange.firstFullyOnScreenIndex,
4789
+ idsInView: getIdsInVisibleRange(state, visibleRange),
4652
4790
  startBuffered,
4653
4791
  startBufferedId,
4654
- startNoBuffer
4792
+ startNoBuffer: visibleRange.startNoBuffer
4655
4793
  });
4656
4794
  if (enableScrollForNextCalculateItemsInView && nextTop !== void 0 && nextBottom !== void 0) {
4657
4795
  state.scrollForNextCalculateItemsInView = isNullOrUndefined(nextTop) && isNullOrUndefined(nextBottom) ? void 0 : {
@@ -4673,7 +4811,7 @@ function calculateItemsInView(ctx, params = {}) {
4673
4811
  const needNewContainers = [];
4674
4812
  const needNewContainersSet = /* @__PURE__ */ new Set();
4675
4813
  for (let i = startBuffered; i <= endBuffered; i++) {
4676
- const id = (_l = idCache[i]) != null ? _l : getId(state, i);
4814
+ const id = (_k = idCache[i]) != null ? _k : getId(state, i);
4677
4815
  if (!containerItemKeys.has(id)) {
4678
4816
  needNewContainersSet.add(i);
4679
4817
  needNewContainers.push(i);
@@ -4682,7 +4820,7 @@ function calculateItemsInView(ctx, params = {}) {
4682
4820
  if (alwaysRenderArr.length > 0) {
4683
4821
  for (const index of alwaysRenderArr) {
4684
4822
  if (index < 0 || index >= dataLength) continue;
4685
- const id = (_m = idCache[index]) != null ? _m : getId(state, index);
4823
+ const id = (_l = idCache[index]) != null ? _l : getId(state, index);
4686
4824
  if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
4687
4825
  needNewContainersSet.add(index);
4688
4826
  needNewContainers.push(index);
@@ -4692,7 +4830,6 @@ function calculateItemsInView(ctx, params = {}) {
4692
4830
  if (stickyHeaderIndicesArr.length > 0) {
4693
4831
  handleStickyActivation(
4694
4832
  ctx,
4695
- stickyHeaderIndicesSet,
4696
4833
  stickyHeaderIndicesArr,
4697
4834
  currentStickyIdx,
4698
4835
  needNewContainers,
@@ -4704,35 +4841,34 @@ function calculateItemsInView(ctx, params = {}) {
4704
4841
  set$(ctx, "activeStickyIndex", -1);
4705
4842
  }
4706
4843
  if (needNewContainers.length > 0) {
4707
- const requiredItemTypes = getItemType ? needNewContainers.map((i) => {
4844
+ const getRequiredItemType = getItemType ? (i) => {
4708
4845
  const itemType = getItemType(data[i], i);
4709
4846
  return itemType !== void 0 ? String(itemType) : "";
4710
- }) : void 0;
4711
- const availableContainers = findAvailableContainers(
4847
+ } : void 0;
4848
+ const availableContainerAllocations = findAvailableContainers(
4712
4849
  ctx,
4713
- needNewContainers.length,
4850
+ needNewContainers,
4714
4851
  startBuffered,
4715
4852
  endBuffered,
4716
4853
  pendingRemoval,
4717
- requiredItemTypes,
4718
- needNewContainers,
4854
+ getRequiredItemType,
4719
4855
  protectedContainerKeys
4720
4856
  );
4721
- for (let idx = 0; idx < needNewContainers.length; idx++) {
4722
- const i = needNewContainers[idx];
4723
- const containerIndex = availableContainers[idx];
4724
- const id = (_n = idCache[i]) != null ? _n : getId(state, i);
4857
+ for (const allocation of availableContainerAllocations) {
4858
+ const i = allocation.itemIndex;
4859
+ const containerIndex = allocation.containerIndex;
4860
+ const id = (_m = idCache[i]) != null ? _m : getId(state, i);
4725
4861
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
4726
4862
  if (oldKey && oldKey !== id) {
4727
4863
  containerItemKeys.delete(oldKey);
4728
4864
  }
4729
4865
  set$(ctx, `containerItemKey${containerIndex}`, id);
4730
4866
  set$(ctx, `containerItemData${containerIndex}`, data[i]);
4731
- if (requiredItemTypes) {
4732
- state.containerItemTypes.set(containerIndex, requiredItemTypes[idx]);
4867
+ if (allocation.itemType !== void 0) {
4868
+ state.containerItemTypes.set(containerIndex, allocation.itemType);
4733
4869
  }
4734
4870
  containerItemKeys.set(id, containerIndex);
4735
- (_o = state.userScrollAnchorReset) == null ? void 0 : _o.keys.add(id);
4871
+ (_n = state.userScrollAnchorReset) == null ? void 0 : _n.keys.add(id);
4736
4872
  const containerSticky = `containerSticky${containerIndex}`;
4737
4873
  const isSticky = stickyHeaderIndicesSet.has(i);
4738
4874
  const isAlwaysRender = alwaysRenderSet.has(i);
@@ -4770,7 +4906,7 @@ function calculateItemsInView(ctx, params = {}) {
4770
4906
  if (alwaysRenderArr.length > 0) {
4771
4907
  for (const index of alwaysRenderArr) {
4772
4908
  if (index < 0 || index >= dataLength) continue;
4773
- const id = (_p = idCache[index]) != null ? _p : getId(state, index);
4909
+ const id = (_o = idCache[index]) != null ? _o : getId(state, index);
4774
4910
  const containerIndex = containerItemKeys.get(id);
4775
4911
  if (containerIndex !== void 0) {
4776
4912
  state.stickyContainerPool.add(containerIndex);
@@ -4789,10 +4925,11 @@ function calculateItemsInView(ctx, params = {}) {
4789
4925
  alwaysRenderSet
4790
4926
  );
4791
4927
  }
4928
+ const pendingRemovalSet = pendingRemoval.length > 0 ? new Set(pendingRemoval) : void 0;
4792
4929
  let didChangePositions = false;
4793
4930
  for (let i = 0; i < numContainers; i++) {
4794
4931
  const itemKey = peek$(ctx, `containerItemKey${i}`);
4795
- if (pendingRemoval.includes(i)) {
4932
+ if (pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(i)) {
4796
4933
  if (itemKey !== void 0) {
4797
4934
  containerItemKeys.delete(itemKey);
4798
4935
  }
@@ -4823,24 +4960,24 @@ function calculateItemsInView(ctx, params = {}) {
4823
4960
  evaluateBootstrapInitialScroll(ctx);
4824
4961
  return;
4825
4962
  }
4826
- const mountedBufferedIndices = getMountedBufferedIndices(state);
4827
- const mountedNoBufferIndices = getMountedNoBufferIndices(state);
4828
- const readinessIndices = hasActiveInitialScroll(state) ? mountedBufferedIndices : mountedNoBufferIndices.length > 0 ? mountedNoBufferIndices : mountedBufferedIndices;
4829
- if (!queuedInitialLayout && readinessIndices.length > 0 && checkAllSizesKnown(state, readinessIndices)) {
4830
- setDidLayout(ctx);
4831
- handleInitialScrollLayoutReady(ctx);
4963
+ if (!queuedInitialLayout && !state.didContainersLayout) {
4964
+ const isInitialLayoutReady = hasActiveInitialScroll(state) ? checkAllSizesKnown(state, state.startBuffered, state.endBuffered) : checkAllSizesKnown(state, state.startNoBuffer, state.endNoBuffer) || checkAllSizesKnown(state, state.startBuffered, state.endBuffered);
4965
+ if (isInitialLayoutReady) {
4966
+ setDidLayout(ctx);
4967
+ handleInitialScrollLayoutReady(ctx);
4968
+ }
4832
4969
  }
4833
- if (viewabilityConfigCallbackPairs && startNoBuffer !== null && endNoBuffer !== null) {
4970
+ if (viewabilityConfigCallbackPairs && visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null) {
4834
4971
  if (!didMVCPAdjustScroll) {
4835
4972
  updateViewableItems(
4836
4973
  ctx.state,
4837
4974
  ctx,
4838
4975
  viewabilityConfigCallbackPairs,
4839
4976
  scrollLength,
4840
- startNoBuffer,
4841
- endNoBuffer,
4842
- startBuffered != null ? startBuffered : startNoBuffer,
4843
- endBuffered != null ? endBuffered : endNoBuffer
4977
+ visibleRange.startNoBuffer,
4978
+ visibleRange.endNoBuffer,
4979
+ startBuffered != null ? startBuffered : visibleRange.startNoBuffer,
4980
+ endBuffered != null ? endBuffered : visibleRange.endNoBuffer
4844
4981
  );
4845
4982
  }
4846
4983
  }
@@ -5168,22 +5305,27 @@ var ScrollAdjustHandler = class {
5168
5305
 
5169
5306
  // src/core/updateAnchoredEndSpace.ts
5170
5307
  function maybeUpdateAnchoredEndSpace(ctx) {
5171
- var _a3;
5308
+ var _a3, _b;
5172
5309
  const state = ctx.state;
5173
5310
  const anchoredEndSpace = state.props.anchoredEndSpace;
5174
5311
  const previousSize = peek$(ctx, "anchoredEndSpaceSize");
5312
+ const previousReadyAnchorIndex = state.anchoredEndSpaceReadyAnchorIndex;
5313
+ const previousReadyAnchorKey = state.anchoredEndSpaceReadyAnchorKey;
5314
+ const nextAnchorIndex = anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex;
5315
+ let nextAnchorKey;
5316
+ let isReady = true;
5175
5317
  let nextSize = 0;
5176
5318
  if (anchoredEndSpace) {
5177
5319
  const { anchorIndex, anchorMaxSize, anchorOffset = 0 } = anchoredEndSpace;
5178
5320
  const { data } = state.props;
5179
5321
  if (anchorIndex >= 0 && anchorIndex < data.length && state.scrollLength > 0) {
5322
+ nextAnchorKey = getId(state, anchorIndex);
5180
5323
  let contentBelowAnchor = 0;
5181
5324
  const footerSize = ctx.values.get("footerSize") || 0;
5182
5325
  const stylePaddingBottom = state.props.stylePaddingBottom || 0;
5183
5326
  let hasUnknownTailSize = false;
5184
5327
  for (let index = anchorIndex; index < data.length; index++) {
5185
- const itemKey = getId(state, index);
5186
- const size = itemKey ? state.sizesKnown.get(itemKey) : void 0;
5328
+ const size = getKnownOrFixedItemSize(ctx, index);
5187
5329
  const effectiveSize = index === anchorIndex && anchorMaxSize !== void 0 ? Math.min(size || 0, Math.max(0, anchorMaxSize)) : size;
5188
5330
  if (size === void 0) {
5189
5331
  hasUnknownTailSize = true;
@@ -5193,15 +5335,25 @@ function maybeUpdateAnchoredEndSpace(ctx) {
5193
5335
  }
5194
5336
  }
5195
5337
  contentBelowAnchor += footerSize + stylePaddingBottom;
5338
+ isReady = !hasUnknownTailSize;
5196
5339
  nextSize = hasUnknownTailSize ? previousSize || 0 : Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
5340
+ } else if (anchorIndex >= 0) {
5341
+ isReady = false;
5197
5342
  }
5198
5343
  }
5199
- if (previousSize !== nextSize) {
5200
- set$(ctx, "anchoredEndSpaceSize", nextSize);
5201
- (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
5202
- if (anchoredEndSpace == null ? void 0 : anchoredEndSpace.includeInEndInset) {
5344
+ const didSizeChange = previousSize !== nextSize;
5345
+ const didReadyAnchorChange = previousReadyAnchorIndex !== nextAnchorIndex || previousReadyAnchorKey !== nextAnchorKey;
5346
+ if (isReady && (didSizeChange || didReadyAnchorChange)) {
5347
+ state.anchoredEndSpaceReadyAnchorIndex = nextAnchorIndex;
5348
+ state.anchoredEndSpaceReadyAnchorKey = nextAnchorKey;
5349
+ if (didSizeChange) {
5350
+ set$(ctx, "anchoredEndSpaceSize", nextSize);
5351
+ (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
5352
+ }
5353
+ if (didSizeChange && (anchoredEndSpace == null ? void 0 : anchoredEndSpace.includeInEndInset)) {
5203
5354
  updateScroll(ctx, state.scroll, true);
5204
5355
  }
5356
+ (_b = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onReady) == null ? void 0 : _b.call(anchoredEndSpace, { anchorIndex: nextAnchorIndex, anchorKey: nextAnchorKey, size: nextSize });
5205
5357
  }
5206
5358
  return nextSize;
5207
5359
  }
@@ -5286,6 +5438,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5286
5438
  } = state;
5287
5439
  if (!data) return;
5288
5440
  const index = state.indexByKey.get(itemKey);
5441
+ let resolvedMeasurementItem;
5289
5442
  if (getFixedItemSize) {
5290
5443
  if (index === void 0) {
5291
5444
  return;
@@ -5296,6 +5449,12 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5296
5449
  }
5297
5450
  const type = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
5298
5451
  const size2 = getFixedItemSize(itemData, index, type);
5452
+ resolvedMeasurementItem = {
5453
+ didResolveFixedItemSize: true,
5454
+ fixedItemSize: size2,
5455
+ itemData,
5456
+ itemType: type
5457
+ };
5299
5458
  if (size2 !== void 0 && size2 === sizesKnown.get(itemKey)) {
5300
5459
  updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
5301
5460
  return;
@@ -5305,7 +5464,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5305
5464
  let shouldMaintainScrollAtEnd = false;
5306
5465
  let minIndexSizeChanged;
5307
5466
  const prevSizeKnown = state.sizesKnown.get(itemKey);
5308
- const diff = updateOneItemSize(ctx, itemKey, sizeObj);
5467
+ const diff = updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem);
5309
5468
  const size = roundSize(horizontal ? sizeObj.width : sizeObj.height);
5310
5469
  if (diff !== 0) {
5311
5470
  minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
@@ -5330,7 +5489,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5330
5489
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
5331
5490
  }
5332
5491
  updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
5333
- if (didContainersLayout || checkAllSizesKnown(state, getMountedBufferedIndices(state))) {
5492
+ if (didContainersLayout || checkAllSizesKnown(state, state.startBuffered, state.endBuffered)) {
5334
5493
  if (needsRecalculate) {
5335
5494
  state.scrollForNextCalculateItemsInView = void 0;
5336
5495
  runOrScheduleMVCPRecalculate(ctx);
@@ -5344,8 +5503,8 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5344
5503
  }
5345
5504
  }
5346
5505
  }
5347
- function updateOneItemSize(ctx, itemKey, sizeObj) {
5348
- var _a3, _b;
5506
+ function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
5507
+ var _a3, _b, _c;
5349
5508
  const state = ctx.state;
5350
5509
  const {
5351
5510
  indexByKey,
@@ -5355,14 +5514,19 @@ function updateOneItemSize(ctx, itemKey, sizeObj) {
5355
5514
  } = state;
5356
5515
  if (!data) return 0;
5357
5516
  const index = indexByKey.get(itemKey);
5358
- const itemData = data[index];
5359
- let itemType;
5360
- let fixedItemSize;
5361
- if (getFixedItemSize) {
5362
- itemType = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
5517
+ const itemData = (_a3 = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemData) != null ? _a3 : data[index];
5518
+ let itemType = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemType;
5519
+ let fixedItemSize = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.fixedItemSize;
5520
+ if (getFixedItemSize && !(resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize)) {
5521
+ itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
5363
5522
  fixedItemSize = getFixedItemSize(itemData, index, itemType);
5364
5523
  }
5365
- const prevSize = getItemSize(ctx, itemKey, index, itemData);
5524
+ const resolvedItemSize = (resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize) || itemType !== void 0 || fixedItemSize !== void 0 ? {
5525
+ didResolveFixedItemSize: resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize,
5526
+ fixedItemSize,
5527
+ itemType
5528
+ } : void 0;
5529
+ const prevSize = getItemSize(ctx, itemKey, index, itemData, void 0, void 0, void 0, resolvedItemSize);
5366
5530
  const rawSize = horizontal ? sizeObj.width : sizeObj.height;
5367
5531
  const prevSizeKnown = sizesKnown.get(itemKey);
5368
5532
  if (Platform.OS !== "web" && prevSizeKnown !== void 0 && isNativeLayoutNoise(rawSize - prevSizeKnown)) {
@@ -5371,7 +5535,7 @@ function updateOneItemSize(ctx, itemKey, sizeObj) {
5371
5535
  const size = Platform.OS === "web" ? Math.round(rawSize) : roundSize(rawSize);
5372
5536
  sizesKnown.set(itemKey, size);
5373
5537
  if (fixedItemSize === void 0 && size > 0) {
5374
- itemType != null ? itemType : itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
5538
+ itemType != null ? itemType : itemType = getItemType ? (_c = getItemType(itemData, index)) != null ? _c : "" : "";
5375
5539
  let averages = averageSizes[itemType];
5376
5540
  if (!averages) {
5377
5541
  averages = averageSizes[itemType] = { avg: 0, num: 0 };
@@ -5452,6 +5616,25 @@ function createColumnWrapperStyle(contentContainerStyle) {
5452
5616
  }
5453
5617
  }
5454
5618
 
5619
+ // src/core/scrollToEnd.ts
5620
+ function scrollToEnd(ctx, options) {
5621
+ const state = ctx.state;
5622
+ const data = state.props.data;
5623
+ const index = data.length - 1;
5624
+ if (index === -1) {
5625
+ return false;
5626
+ }
5627
+ const paddingBottom = state.props.stylePaddingBottom || 0;
5628
+ const footerSize = peek$(ctx, "footerSize") || 0;
5629
+ scrollToIndex(ctx, {
5630
+ ...options,
5631
+ index,
5632
+ viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
5633
+ viewPosition: 1
5634
+ });
5635
+ return true;
5636
+ }
5637
+
5455
5638
  // src/utils/createImperativeHandle.ts
5456
5639
  var DEFAULT_AVERAGE_ITEM_SIZE_TYPE = "default";
5457
5640
  function getAverageItemSizes(state) {
@@ -5467,7 +5650,12 @@ function getAverageItemSizes(state) {
5467
5650
  }
5468
5651
  return averageItemSizes;
5469
5652
  }
5470
- function createImperativeHandle(ctx) {
5653
+ function triggerMountedContainerLayouts(ctx) {
5654
+ for (const triggerLayout of ctx.containerLayoutTriggers.values()) {
5655
+ triggerLayout();
5656
+ }
5657
+ }
5658
+ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
5471
5659
  const state = ctx.state;
5472
5660
  const IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS = 800;
5473
5661
  const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
@@ -5484,15 +5672,10 @@ function createImperativeHandle(ctx) {
5484
5672
  if (targetIndex >= dataLength) {
5485
5673
  return false;
5486
5674
  }
5487
- if (anchorIndex === void 0 || anchorIndex < 0 || anchorIndex >= dataLength || targetIndex < anchorIndex || props.getFixedItemSize) {
5675
+ if (anchorIndex === void 0 || anchorIndex < 0 || anchorIndex >= dataLength || targetIndex < anchorIndex) {
5488
5676
  return true;
5489
5677
  }
5490
- for (let index = anchorIndex; index < dataLength; index++) {
5491
- if (!state.sizesKnown.has(getId(state, index))) {
5492
- return false;
5493
- }
5494
- }
5495
- return true;
5678
+ return areKnownOrFixedItemSizesAvailable(ctx, anchorIndex, dataLength - 1);
5496
5679
  };
5497
5680
  const runWhenReady = (token, run, isReady) => {
5498
5681
  const startedAt = Date.now();
@@ -5515,11 +5698,7 @@ function createImperativeHandle(ctx) {
5515
5698
  };
5516
5699
  requestAnimationFrame(check);
5517
5700
  };
5518
- const runScrollWithPromise = (run, isReady = () => true) => new Promise((resolve) => {
5519
- var _a3;
5520
- const token = ++imperativeScrollToken;
5521
- (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
5522
- state.pendingScrollResolve = resolve;
5701
+ const runScrollRequest = (token, resolve, run, isReady = () => true) => {
5523
5702
  const runNow = () => {
5524
5703
  if (token !== imperativeScrollToken) {
5525
5704
  return;
@@ -5537,7 +5716,33 @@ function createImperativeHandle(ctx) {
5537
5716
  } else {
5538
5717
  runNow();
5539
5718
  }
5719
+ };
5720
+ const startImperativeScroll = (resolve) => {
5721
+ var _a3;
5722
+ const token = ++imperativeScrollToken;
5723
+ state.pendingScrollToEnd = void 0;
5724
+ (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
5725
+ state.pendingScrollResolve = resolve;
5726
+ return token;
5727
+ };
5728
+ const runScrollWithPromise = (run, isReady = () => true) => new Promise((resolve) => {
5729
+ const token = startImperativeScroll(resolve);
5730
+ runScrollRequest(token, resolve, run, isReady);
5540
5731
  });
5732
+ state.runPendingScrollToEnd = () => {
5733
+ const pendingScroll = state.pendingScrollToEnd;
5734
+ if (pendingScroll) {
5735
+ state.pendingScrollToEnd = void 0;
5736
+ if (pendingScroll.token === imperativeScrollToken) {
5737
+ runScrollRequest(
5738
+ pendingScroll.token,
5739
+ pendingScroll.resolve,
5740
+ () => scrollToEnd(ctx, pendingScroll.options),
5741
+ () => isScrollToIndexReady(state.props.data.length - 1, true)
5742
+ );
5743
+ }
5744
+ }
5745
+ };
5541
5746
  const scrollIndexIntoView = (options) => {
5542
5747
  if (state) {
5543
5748
  const { index, ...rest } = options;
@@ -5575,6 +5780,7 @@ function createImperativeHandle(ctx) {
5575
5780
  state.columns.length = 0;
5576
5781
  state.columnSpans.length = 0;
5577
5782
  }
5783
+ triggerMountedContainerLayouts(ctx);
5578
5784
  (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
5579
5785
  };
5580
5786
  return {
@@ -5636,26 +5842,20 @@ function createImperativeHandle(ctx) {
5636
5842
  }
5637
5843
  return false;
5638
5844
  }),
5639
- scrollToEnd: (options) => runScrollWithPromise(
5640
- () => {
5641
- const data = state.props.data;
5642
- const stylePaddingBottom = state.props.stylePaddingBottom;
5643
- const index = data.length - 1;
5644
- if (index !== -1) {
5645
- const paddingBottom = stylePaddingBottom || 0;
5646
- const footerSize = peek$(ctx, "footerSize") || 0;
5647
- scrollToIndex(ctx, {
5648
- ...options,
5649
- index,
5650
- viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
5651
- viewPosition: 1
5652
- });
5653
- return true;
5654
- }
5655
- return false;
5656
- },
5657
- () => isScrollToIndexReady(state.props.data.length - 1, true)
5658
- ),
5845
+ scrollToEnd: (options) => new Promise((resolve) => {
5846
+ var _a3;
5847
+ const token = startImperativeScroll(resolve);
5848
+ state.pendingScrollToEnd = {
5849
+ options,
5850
+ resolve,
5851
+ token
5852
+ };
5853
+ if (scheduleImperativeScrollCommit) {
5854
+ scheduleImperativeScrollCommit();
5855
+ } else {
5856
+ (_a3 = state.runPendingScrollToEnd) == null ? void 0 : _a3.call(state);
5857
+ }
5858
+ }),
5659
5859
  scrollToIndex: (params) => {
5660
5860
  return runScrollWithPromise(
5661
5861
  () => {
@@ -6025,6 +6225,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6025
6225
  viewOffset: 0
6026
6226
  } : void 0;
6027
6227
  const [canRender, setCanRender] = React2.useState(!IsNewArchitecture);
6228
+ const [, scheduleImperativeScrollCommit] = React2.useReducer((value) => value + 1, 0);
6028
6229
  const ctx = useStateContext();
6029
6230
  ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
6030
6231
  const refScroller = useRef(null);
@@ -6387,7 +6588,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6387
6588
  viewabilityConfigCallbackPairs
6388
6589
  });
6389
6590
  state.viewabilityConfigCallbackPairs = viewability;
6390
- state.enableScrollForNextCalculateItemsInView = !viewability;
6591
+ state.enableScrollForNextCalculateItemsInView = true;
6391
6592
  if (viewability) {
6392
6593
  state.scrollForNextCalculateItemsInView = void 0;
6393
6594
  }
@@ -6397,7 +6598,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6397
6598
  doInitialAllocateContainers(ctx);
6398
6599
  }
6399
6600
  });
6400
- useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx), []);
6601
+ useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx, scheduleImperativeScrollCommit), []);
6602
+ useLayoutEffect(() => {
6603
+ var _a4;
6604
+ (_a4 = state.runPendingScrollToEnd) == null ? void 0 : _a4.call(state);
6605
+ });
6401
6606
  useEffect(() => {
6402
6607
  if (Platform.OS !== "web" || usesBootstrapInitialScroll) {
6403
6608
  return;