@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.js CHANGED
@@ -143,6 +143,7 @@ function StateProvider({ children }) {
143
143
  const [value] = React2__namespace.useState(() => ({
144
144
  animatedScrollY: createAnimatedValue(0),
145
145
  columnWrapperStyle: void 0,
146
+ containerLayoutTriggers: /* @__PURE__ */ new Map(),
146
147
  contextNum: contextNum++,
147
148
  listeners: /* @__PURE__ */ new Map(),
148
149
  mapViewabilityAmountCallbacks: /* @__PURE__ */ new Map(),
@@ -561,9 +562,6 @@ function roundSize(size) {
561
562
  function isNullOrUndefined(value) {
562
563
  return value === null || value === void 0;
563
564
  }
564
- function comparatorDefault(a, b) {
565
- return a - b;
566
- }
567
565
  function getPadding(s, type) {
568
566
  var _a3, _b, _c;
569
567
  const axisPadding = type === "Left" || type === "Right" ? s.paddingHorizontal : s.paddingVertical;
@@ -916,18 +914,6 @@ var Container = typedMemo(function Container2({
916
914
  [itemKey, data, extraData]
917
915
  );
918
916
  const { index, renderedItem } = renderedItemInfo || {};
919
- const contextValue = React2.useMemo(() => {
920
- ctx.viewRefs.set(id, ref);
921
- return {
922
- containerId: id,
923
- index,
924
- itemKey,
925
- triggerLayout: () => {
926
- forceLayoutRender((v) => v + 1);
927
- },
928
- value: data
929
- };
930
- }, [id, itemKey, index, data]);
931
917
  const onLayoutChange = React2.useCallback((rectangle) => {
932
918
  var _a3, _b;
933
919
  const {
@@ -977,6 +963,27 @@ var Container = typedMemo(function Container2({
977
963
  });
978
964
  }
979
965
  }, []);
966
+ const triggerLayout = React2.useCallback(() => {
967
+ forceLayoutRender((v) => v + 1);
968
+ }, []);
969
+ const contextValue = React2.useMemo(() => {
970
+ ctx.viewRefs.set(id, ref);
971
+ return {
972
+ containerId: id,
973
+ index,
974
+ itemKey,
975
+ triggerLayout,
976
+ value: data
977
+ };
978
+ }, [id, itemKey, index, data, triggerLayout]);
979
+ React2.useLayoutEffect(() => {
980
+ ctx.containerLayoutTriggers.set(id, triggerLayout);
981
+ return () => {
982
+ if (ctx.containerLayoutTriggers.get(id) === triggerLayout) {
983
+ ctx.containerLayoutTriggers.delete(id);
984
+ }
985
+ };
986
+ }, [ctx, id, triggerLayout]);
980
987
  const { onLayout } = useOnLayoutSync(
981
988
  {
982
989
  onLayoutChange,
@@ -1831,17 +1838,42 @@ function setSize(ctx, itemKey, size, notifyTotalSize = true) {
1831
1838
  }
1832
1839
 
1833
1840
  // src/utils/getItemSize.ts
1834
- function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize, notifyTotalSize) {
1835
- var _a3, _b, _c;
1841
+ function getKnownOrFixedSize(ctx, key, index, data, resolved) {
1842
+ var _a3, _b;
1843
+ const state = ctx.state;
1844
+ const { getFixedItemSize, getItemType } = state.props;
1845
+ let size = key ? state.sizesKnown.get(key) : void 0;
1846
+ if (size === void 0 && key && getFixedItemSize) {
1847
+ const itemType = (_b = resolved == null ? void 0 : resolved.itemType) != null ? _b : getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1848
+ size = (resolved == null ? void 0 : resolved.didResolveFixedItemSize) ? resolved.fixedItemSize : getFixedItemSize(data, index, itemType);
1849
+ if (size !== void 0) {
1850
+ state.sizesKnown.set(key, size);
1851
+ }
1852
+ }
1853
+ return size;
1854
+ }
1855
+ function getKnownOrFixedItemSize(ctx, index) {
1856
+ const key = getId(ctx.state, index);
1857
+ return getKnownOrFixedSize(ctx, key, index, ctx.state.props.data[index]);
1858
+ }
1859
+ function areKnownOrFixedItemSizesAvailable(ctx, startIndex, endIndex) {
1860
+ for (let index = startIndex; index <= endIndex; index++) {
1861
+ if (getKnownOrFixedItemSize(ctx, index) === void 0) {
1862
+ return false;
1863
+ }
1864
+ }
1865
+ return true;
1866
+ }
1867
+ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize, notifyTotalSize, resolved) {
1868
+ var _a3, _b, _c, _d;
1836
1869
  const state = ctx.state;
1837
1870
  const {
1838
- sizesKnown,
1839
1871
  sizes,
1840
1872
  averageSizes,
1841
- props: { estimatedItemSize, getFixedItemSize, getItemType },
1873
+ props: { estimatedItemSize, getItemType },
1842
1874
  scrollingTo
1843
1875
  } = state;
1844
- const sizeKnown = sizesKnown.get(key);
1876
+ const sizeKnown = state.sizesKnown.get(key);
1845
1877
  if (sizeKnown !== void 0) {
1846
1878
  return sizeKnown;
1847
1879
  }
@@ -1852,15 +1884,14 @@ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize, no
1852
1884
  return renderedSize;
1853
1885
  }
1854
1886
  }
1855
- const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1856
- if (getFixedItemSize) {
1857
- size = getFixedItemSize(data, index, itemType);
1858
- if (size !== void 0) {
1859
- sizesKnown.set(key, size);
1860
- }
1887
+ size = getKnownOrFixedSize(ctx, key, index, data, resolved);
1888
+ if (size !== void 0) {
1889
+ setSize(ctx, key, size, notifyTotalSize);
1890
+ return size;
1861
1891
  }
1862
- if (size === void 0 && useAverageSize && sizeKnown === void 0 && !scrollingTo) {
1863
- const averageSizeForType = (_b = averageSizes[itemType]) == null ? void 0 : _b.avg;
1892
+ const itemType = (_b = resolved == null ? void 0 : resolved.itemType) != null ? _b : getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1893
+ if (useAverageSize && !scrollingTo) {
1894
+ const averageSizeForType = (_c = averageSizes[itemType]) == null ? void 0 : _c.avg;
1864
1895
  if (averageSizeForType !== void 0) {
1865
1896
  size = roundSize(averageSizeForType);
1866
1897
  }
@@ -1868,8 +1899,8 @@ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize, no
1868
1899
  if (size === void 0 && renderedSize !== void 0) {
1869
1900
  return renderedSize;
1870
1901
  }
1871
- if (size === void 0 && useAverageSize && sizeKnown === void 0 && scrollingTo) {
1872
- const averageSizeForType = (_c = scrollingTo.averageSizeSnapshot) == null ? void 0 : _c[itemType];
1902
+ if (size === void 0 && useAverageSize && scrollingTo) {
1903
+ const averageSizeForType = (_d = scrollingTo.averageSizeSnapshot) == null ? void 0 : _d[itemType];
1873
1904
  if (averageSizeForType !== void 0) {
1874
1905
  size = roundSize(averageSizeForType);
1875
1906
  }
@@ -2576,8 +2607,8 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2576
2607
  if ((options == null ? void 0 : options.markHasScrolled) !== false) {
2577
2608
  state.hasScrolled = true;
2578
2609
  }
2579
- state.lastBatchingAction = Date.now();
2580
2610
  const currentTime = Date.now();
2611
+ state.lastBatchingAction = currentTime;
2581
2612
  const adjust = scrollAdjustHandler.getAdjust();
2582
2613
  const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
2583
2614
  if (adjustChanged) {
@@ -2886,26 +2917,22 @@ function advanceCurrentInitialScrollSession(ctx, options) {
2886
2917
  }
2887
2918
 
2888
2919
  // src/utils/checkAllSizesKnown.ts
2889
- function isNullOrUndefined2(value) {
2890
- return value === null || value === void 0;
2891
- }
2892
- function getMountedIndicesInRange(state, start, end) {
2893
- if (!isNullOrUndefined2(end) && !isNullOrUndefined2(start) && start >= 0 && end >= 0) {
2894
- 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);
2920
+ function checkAllSizesKnown(state, start, end) {
2921
+ if (start == null || end == null || start < 0 || end < start) {
2922
+ return false;
2895
2923
  }
2896
- return [];
2897
- }
2898
- function getMountedBufferedIndices(state) {
2899
- return getMountedIndicesInRange(state, state.startBuffered, state.endBuffered);
2900
- }
2901
- function getMountedNoBufferIndices(state) {
2902
- return getMountedIndicesInRange(state, state.startNoBuffer, state.endNoBuffer);
2903
- }
2904
- function checkAllSizesKnown(state, indices) {
2905
- return indices.length > 0 && indices.every((index) => {
2906
- const key = getId(state, index);
2907
- return key !== void 0 && state.sizesKnown.has(key);
2908
- });
2924
+ let hasMountedIndex = false;
2925
+ for (const key of state.containerItemKeys.keys()) {
2926
+ const index = state.indexByKey.get(key);
2927
+ if (index !== void 0 && index >= start && index <= end) {
2928
+ hasMountedIndex = true;
2929
+ const id = getId(state, index);
2930
+ if (id === void 0 || !state.sizesKnown.has(id)) {
2931
+ return false;
2932
+ }
2933
+ }
2934
+ }
2935
+ return hasMountedIndex;
2909
2936
  }
2910
2937
 
2911
2938
  // src/core/bootstrapInitialScroll.ts
@@ -3406,8 +3433,7 @@ function evaluateBootstrapInitialScroll(ctx) {
3406
3433
  bootstrapInitialScroll.targetIndexSeed = void 0;
3407
3434
  }
3408
3435
  const resolvedOffset = resolveInitialScrollOffset(ctx, initialScroll);
3409
- const mountedBufferedIndices = getMountedBufferedIndices(state);
3410
- const areMountedBufferedIndicesMeasured = checkAllSizesKnown(state, mountedBufferedIndices);
3436
+ const areMountedBufferedIndicesMeasured = checkAllSizesKnown(state, state.startBuffered, state.endBuffered);
3411
3437
  const didResolvedOffsetChange = Math.abs(bootstrapInitialScroll.scroll - resolvedOffset) > 1;
3412
3438
  const { data } = state.props;
3413
3439
  const visibleIndices = getBootstrapRevealVisibleIndices({
@@ -3838,7 +3864,14 @@ function updateSnapToOffsets(ctx) {
3838
3864
  }
3839
3865
 
3840
3866
  // src/core/updateItemPositions.ts
3841
- function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP, optimizeForVisibleWindow = false } = {
3867
+ function updateItemPositions(ctx, dataChanged, {
3868
+ doMVCP,
3869
+ forceFullUpdate = false,
3870
+ optimizeForVisibleWindow = false,
3871
+ scrollBottomBuffered,
3872
+ scrollVelocity,
3873
+ startIndex
3874
+ } = {
3842
3875
  doMVCP: false,
3843
3876
  forceFullUpdate: false,
3844
3877
  optimizeForVisibleWindow: false,
@@ -3865,7 +3898,7 @@ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffere
3865
3898
  const extraData = peek$(ctx, "extraData");
3866
3899
  const layoutConfig = overrideItemLayout ? { span: 1 } : void 0;
3867
3900
  const lastScrollDelta = state.lastScrollDelta;
3868
- const velocity = getScrollVelocity(state);
3901
+ const velocity = scrollVelocity != null ? scrollVelocity : getScrollVelocity(state);
3869
3902
  const shouldOptimize = !forceFullUpdate && !dataChanged && (optimizeForVisibleWindow || Math.abs(velocity) > 0 || Platform.OS === "web" && state.scrollLength > 0 && lastScrollDelta > state.scrollLength);
3870
3903
  const maxVisibleArea = scrollBottomBuffered + 1e3;
3871
3904
  const useAverageSize = true;
@@ -4045,25 +4078,28 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
4045
4078
  const configId = viewabilityConfig.id;
4046
4079
  const viewabilityState = ensureViewabilityState(ctx, configId);
4047
4080
  const { viewableItems: previousViewableItems, start, end, startBuffered, endBuffered } = viewabilityState;
4048
- const viewabilityTokens = /* @__PURE__ */ new Map();
4081
+ let staleViewabilityAmountIds;
4049
4082
  for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
4050
- viewabilityTokens.set(
4083
+ const nextValue = computeViewability(
4084
+ state,
4085
+ ctx,
4086
+ viewabilityConfig,
4051
4087
  containerId,
4052
- computeViewability(
4053
- state,
4054
- ctx,
4055
- viewabilityConfig,
4056
- containerId,
4057
- value.key,
4058
- scrollSize,
4059
- value.item,
4060
- value.index
4061
- )
4088
+ value.key,
4089
+ scrollSize,
4090
+ value.item,
4091
+ value.index
4062
4092
  );
4093
+ if (nextValue.sizeVisible < 0) {
4094
+ staleViewabilityAmountIds != null ? staleViewabilityAmountIds : staleViewabilityAmountIds = [];
4095
+ staleViewabilityAmountIds.push(containerId);
4096
+ }
4063
4097
  }
4064
4098
  const changed = [];
4099
+ const previousViewableKeys = /* @__PURE__ */ new Set();
4065
4100
  if (previousViewableItems) {
4066
4101
  for (const viewToken of previousViewableItems) {
4102
+ previousViewableKeys.add(viewToken.key);
4067
4103
  const containerId = findContainerId(ctx, viewToken.key);
4068
4104
  if (!checkIsViewable(
4069
4105
  state,
@@ -4095,7 +4131,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
4095
4131
  key
4096
4132
  };
4097
4133
  viewableItems.push(viewToken);
4098
- if (!(previousViewableItems == null ? void 0 : previousViewableItems.find((v) => v.key === viewToken.key))) {
4134
+ if (!previousViewableKeys.has(viewToken.key)) {
4099
4135
  changed.push(viewToken);
4100
4136
  }
4101
4137
  }
@@ -4116,20 +4152,17 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
4116
4152
  onViewableItemsChanged({ changed, end, endBuffered, start, startBuffered, viewableItems });
4117
4153
  }
4118
4154
  }
4119
- for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
4120
- if (value.sizeVisible < 0) {
4121
- ctx.mapViewabilityAmountValues.delete(containerId);
4155
+ if (staleViewabilityAmountIds) {
4156
+ for (const containerId of staleViewabilityAmountIds) {
4157
+ const value = ctx.mapViewabilityAmountValues.get(containerId);
4158
+ if (value && value.sizeVisible < 0) {
4159
+ ctx.mapViewabilityAmountValues.delete(containerId);
4160
+ }
4122
4161
  }
4123
4162
  }
4124
4163
  }
4125
- function shallowEqual(prev, next) {
4126
- if (!prev) return false;
4127
- const keys = Object.keys(next);
4128
- for (let i = 0; i < keys.length; i++) {
4129
- const k = keys[i];
4130
- if (prev[k] !== next[k]) return false;
4131
- }
4132
- return true;
4164
+ function areViewabilityAmountTokensEqual(prev, next) {
4165
+ 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;
4133
4166
  }
4134
4167
  function computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
4135
4168
  const { sizes, scroll: scrollState } = state;
@@ -4154,7 +4187,7 @@ function computeViewability(state, ctx, viewabilityConfig, containerId, key, scr
4154
4187
  sizeVisible: -1
4155
4188
  };
4156
4189
  const prev2 = ctx.mapViewabilityAmountValues.get(containerId);
4157
- if (!shallowEqual(prev2, value2)) {
4190
+ if (!areViewabilityAmountTokensEqual(prev2, value2)) {
4158
4191
  ctx.mapViewabilityAmountValues.set(containerId, value2);
4159
4192
  const cb = ctx.mapViewabilityAmountCallbacks.get(containerId);
4160
4193
  if (cb) {
@@ -4184,7 +4217,7 @@ function computeViewability(state, ctx, viewabilityConfig, containerId, key, scr
4184
4217
  sizeVisible
4185
4218
  };
4186
4219
  const prev = ctx.mapViewabilityAmountValues.get(containerId);
4187
- if (!shallowEqual(prev, value)) {
4220
+ if (!areViewabilityAmountTokensEqual(prev, value)) {
4188
4221
  ctx.mapViewabilityAmountValues.set(containerId, value);
4189
4222
  const cb = ctx.mapViewabilityAmountCallbacks.get(containerId);
4190
4223
  if (cb) {
@@ -4235,126 +4268,152 @@ function getExpandedContainerPoolSize(dataLength, numContainers) {
4235
4268
  }
4236
4269
 
4237
4270
  // src/utils/findAvailableContainers.ts
4238
- function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers, protectedKeys) {
4271
+ function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffered, pendingRemoval, getRequiredItemType, protectedKeys) {
4272
+ const numNeeded = needNewContainers.length;
4273
+ if (numNeeded === 0) {
4274
+ return [];
4275
+ }
4239
4276
  const numContainers = peek$(ctx, "numContainers");
4240
4277
  const state = ctx.state;
4241
4278
  const { stickyContainerPool, containerItemTypes } = state;
4242
4279
  const shouldAvoidAssignedContainerReuse = state.props.recycleItems && !!state.props.positionComponentInternal;
4243
- const result = [];
4244
- const availableContainers = [];
4245
- const pendingRemovalSet = new Set(pendingRemoval);
4280
+ const allocations = [];
4281
+ const pendingRemovalSet = pendingRemoval.length > 0 ? new Set(pendingRemoval) : void 0;
4246
4282
  let pendingRemovalChanged = false;
4283
+ let nextNewContainerIndex = numContainers;
4284
+ const usedContainers = /* @__PURE__ */ new Set();
4285
+ let availableContainers;
4247
4286
  const stickyHeaderIndicesSet = state.props.stickyHeaderIndicesSet;
4248
- const stickyHeaderItemIndices = (needNewContainers == null ? void 0 : needNewContainers.filter((index) => stickyHeaderIndicesSet.has(index))) || [];
4249
4287
  const canReuseContainer = (containerIndex, requiredType) => {
4250
4288
  if (!requiredType) return true;
4251
4289
  const existingType = containerItemTypes.get(containerIndex);
4252
4290
  if (!existingType) return true;
4253
4291
  return existingType === requiredType;
4254
4292
  };
4255
- const neededTypes = requiredItemTypes ? [...requiredItemTypes] : [];
4256
- let typeIndex = 0;
4257
- for (let i = 0; i < stickyHeaderItemIndices.length; i++) {
4258
- const requiredType = neededTypes[typeIndex];
4259
- let foundContainer = false;
4260
- for (const containerIndex of stickyContainerPool) {
4261
- const key = peek$(ctx, `containerItemKey${containerIndex}`);
4262
- const isPendingRemoval = pendingRemovalSet.has(containerIndex);
4263
- if ((key === void 0 || isPendingRemoval) && canReuseContainer(containerIndex, requiredType) && !result.includes(containerIndex)) {
4264
- result.push(containerIndex);
4265
- if (isPendingRemoval && pendingRemovalSet.delete(containerIndex)) {
4266
- pendingRemovalChanged = true;
4267
- }
4268
- foundContainer = true;
4269
- if (requiredItemTypes) typeIndex++;
4270
- break;
4271
- }
4293
+ const pushAllocation = (itemIndex, itemType, containerIndex) => {
4294
+ allocations.push({
4295
+ containerIndex,
4296
+ itemIndex,
4297
+ itemType
4298
+ });
4299
+ usedContainers.add(containerIndex);
4300
+ if (pendingRemovalSet == null ? void 0 : pendingRemovalSet.delete(containerIndex)) {
4301
+ pendingRemovalChanged = true;
4272
4302
  }
4273
- if (!foundContainer) {
4274
- const newContainerIndex = numContainers + result.filter((index) => index >= numContainers).length;
4275
- result.push(newContainerIndex);
4303
+ };
4304
+ const pushNewContainer = (itemIndex, itemType, isSticky) => {
4305
+ const newContainerIndex = nextNewContainerIndex++;
4306
+ pushAllocation(itemIndex, itemType, newContainerIndex);
4307
+ if (isSticky) {
4276
4308
  stickyContainerPool.add(newContainerIndex);
4277
- if (requiredItemTypes) typeIndex++;
4278
- }
4279
- }
4280
- for (let u = 0; u < numContainers && result.length < numNeeded; u++) {
4281
- if (stickyContainerPool.has(u)) {
4282
- continue;
4283
4309
  }
4284
- const key = peek$(ctx, `containerItemKey${u}`);
4285
- const requiredType = neededTypes[typeIndex];
4286
- const isPending = key !== void 0 && pendingRemovalSet.has(u);
4287
- const canUse = key === void 0 || isPending && canReuseContainer(u, requiredType);
4288
- if (canUse) {
4289
- if (isPending) {
4290
- pendingRemovalSet.delete(u);
4291
- pendingRemovalChanged = true;
4292
- }
4293
- result.push(u);
4294
- if (requiredItemTypes) {
4295
- typeIndex++;
4296
- }
4310
+ return newContainerIndex;
4311
+ };
4312
+ const canUseContainer = (containerIndex, itemType) => {
4313
+ if (usedContainers.has(containerIndex) || stickyContainerPool.has(containerIndex)) {
4314
+ return false;
4297
4315
  }
4298
- }
4299
- if (!shouldAvoidAssignedContainerReuse) {
4300
- for (let u = 0; u < numContainers && result.length < numNeeded; u++) {
4301
- if (stickyContainerPool.has(u)) {
4302
- continue;
4303
- }
4304
- const key = peek$(ctx, `containerItemKey${u}`);
4305
- if (key === void 0) continue;
4306
- if ((protectedKeys == null ? void 0 : protectedKeys.has(key)) && state.indexByKey.has(key)) continue;
4307
- const index = state.indexByKey.get(key);
4308
- const isOutOfView = index < startBuffered || index > endBuffered;
4309
- if (isOutOfView) {
4310
- const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
4311
- if (!requiredItemTypes || typeIndex < neededTypes.length && canReuseContainer(u, neededTypes[typeIndex])) {
4312
- availableContainers.push({ distance, index: u });
4316
+ const key = peek$(ctx, `containerItemKey${containerIndex}`);
4317
+ const isPending = !!(pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(containerIndex));
4318
+ return (key === void 0 || isPending) && canReuseContainer(containerIndex, itemType);
4319
+ };
4320
+ const findStickyContainer = (itemType) => {
4321
+ let foundContainer;
4322
+ for (const containerIndex of stickyContainerPool) {
4323
+ if (!usedContainers.has(containerIndex)) {
4324
+ const key = peek$(ctx, `containerItemKey${containerIndex}`);
4325
+ const isPendingRemoval = !!(pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(containerIndex));
4326
+ if ((key === void 0 || isPendingRemoval) && canReuseContainer(containerIndex, itemType)) {
4327
+ foundContainer = containerIndex;
4328
+ break;
4313
4329
  }
4314
4330
  }
4315
4331
  }
4316
- }
4317
- const remaining = numNeeded - result.length;
4318
- if (remaining > 0) {
4319
- if (availableContainers.length > 0) {
4320
- if (availableContainers.length > remaining) {
4321
- availableContainers.sort(comparatorByDistance);
4322
- availableContainers.length = remaining;
4332
+ return foundContainer;
4333
+ };
4334
+ const findUnassignedOrPendingContainer = (itemType) => {
4335
+ let foundContainer;
4336
+ for (let containerIndex = 0; containerIndex < numContainers && foundContainer === void 0; containerIndex++) {
4337
+ if (canUseContainer(containerIndex, itemType)) {
4338
+ foundContainer = containerIndex;
4323
4339
  }
4324
- for (const container of availableContainers) {
4325
- result.push(container.index);
4326
- if (requiredItemTypes) {
4327
- typeIndex++;
4340
+ }
4341
+ return foundContainer;
4342
+ };
4343
+ const getAvailableContainers = () => {
4344
+ if (!availableContainers) {
4345
+ availableContainers = [];
4346
+ if (!shouldAvoidAssignedContainerReuse) {
4347
+ for (let containerIndex = 0; containerIndex < numContainers; containerIndex++) {
4348
+ if (usedContainers.has(containerIndex) || stickyContainerPool.has(containerIndex)) {
4349
+ continue;
4350
+ }
4351
+ const key = peek$(ctx, `containerItemKey${containerIndex}`);
4352
+ if (key === void 0) continue;
4353
+ if ((protectedKeys == null ? void 0 : protectedKeys.has(key)) && state.indexByKey.has(key)) continue;
4354
+ const index = state.indexByKey.get(key);
4355
+ const isOutOfView = index < startBuffered || index > endBuffered;
4356
+ if (isOutOfView) {
4357
+ const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
4358
+ availableContainers.push({ distance, index: containerIndex });
4359
+ }
4328
4360
  }
4361
+ availableContainers.sort(comparatorByDistance);
4329
4362
  }
4330
4363
  }
4331
- const stillNeeded = numNeeded - result.length;
4332
- if (stillNeeded > 0) {
4333
- for (let i = 0; i < stillNeeded; i++) {
4334
- result.push(numContainers + i);
4364
+ return availableContainers;
4365
+ };
4366
+ const findAvailableContainer = (itemType) => {
4367
+ const containers = getAvailableContainers();
4368
+ let matchIndex = -1;
4369
+ for (let i = 0; i < containers.length && matchIndex === -1; i++) {
4370
+ const containerIndex = containers[i].index;
4371
+ if (!usedContainers.has(containerIndex) && canReuseContainer(containerIndex, itemType)) {
4372
+ matchIndex = i;
4335
4373
  }
4336
- if (IS_DEV && numContainers + stillNeeded > peek$(ctx, "numContainersPooled")) {
4337
- console.warn(
4338
- "[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.",
4339
- {
4340
- debugInfo: {
4341
- numContainers,
4342
- numContainersPooled: peek$(ctx, "numContainersPooled"),
4343
- numNeeded,
4344
- stillNeeded
4345
- }
4346
- }
4347
- );
4374
+ }
4375
+ return matchIndex === -1 ? void 0 : containers.splice(matchIndex, 1)[0].index;
4376
+ };
4377
+ for (const itemIndex of needNewContainers) {
4378
+ const itemType = getRequiredItemType == null ? void 0 : getRequiredItemType(itemIndex);
4379
+ const isSticky = stickyHeaderIndicesSet.has(itemIndex);
4380
+ let containerIndex;
4381
+ if (isSticky) {
4382
+ containerIndex = findStickyContainer(itemType);
4383
+ } else {
4384
+ containerIndex = findUnassignedOrPendingContainer(itemType);
4385
+ if (containerIndex === void 0) {
4386
+ containerIndex = findAvailableContainer(itemType);
4348
4387
  }
4349
4388
  }
4389
+ if (containerIndex !== void 0) {
4390
+ pushAllocation(itemIndex, itemType, containerIndex);
4391
+ } else {
4392
+ pushNewContainer(itemIndex, itemType, isSticky);
4393
+ }
4350
4394
  }
4351
4395
  if (pendingRemovalChanged) {
4352
4396
  pendingRemoval.length = 0;
4353
- for (const value of pendingRemovalSet) {
4354
- pendingRemoval.push(value);
4397
+ if (pendingRemovalSet) {
4398
+ for (const value of pendingRemovalSet) {
4399
+ pendingRemoval.push(value);
4400
+ }
4355
4401
  }
4356
4402
  }
4357
- return result.sort(comparatorDefault);
4403
+ if (IS_DEV && nextNewContainerIndex > peek$(ctx, "numContainersPooled")) {
4404
+ console.warn(
4405
+ "[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.",
4406
+ {
4407
+ debugInfo: {
4408
+ numContainers,
4409
+ numContainersPooled: peek$(ctx, "numContainersPooled"),
4410
+ numNeeded,
4411
+ stillNeeded: nextNewContainerIndex - numContainers
4412
+ }
4413
+ }
4414
+ );
4415
+ }
4416
+ return allocations;
4358
4417
  }
4359
4418
  function comparatorByDistance(a, b) {
4360
4419
  return b.distance - a.distance;
@@ -4380,21 +4439,28 @@ function findCurrentStickyIndex(stickyArray, scroll, state) {
4380
4439
  }
4381
4440
  return -1;
4382
4441
  }
4383
- function getActiveStickyIndices(ctx, stickyHeaderIndices) {
4442
+ function isStickyIndexActive(ctx, targetIndex) {
4384
4443
  const state = ctx.state;
4385
- return new Set(
4386
- 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))
4387
- );
4444
+ let isActive = false;
4445
+ for (const containerIndex of state.stickyContainerPool) {
4446
+ const key = peek$(ctx, `containerItemKey${containerIndex}`);
4447
+ const itemIndex = key ? state.indexByKey.get(key) : void 0;
4448
+ if (itemIndex === targetIndex) {
4449
+ isActive = true;
4450
+ break;
4451
+ }
4452
+ }
4453
+ return isActive;
4388
4454
  }
4389
- function handleStickyActivation(ctx, stickyHeaderIndices, stickyArray, currentStickyIdx, needNewContainers, needNewContainersSet, startBuffered, endBuffered) {
4455
+ function handleStickyActivation(ctx, stickyArray, currentStickyIdx, needNewContainers, needNewContainersSet, startBuffered, endBuffered) {
4390
4456
  var _a3;
4391
4457
  const state = ctx.state;
4392
- const activeIndices = getActiveStickyIndices(ctx, stickyHeaderIndices);
4393
4458
  set$(ctx, "activeStickyIndex", currentStickyIdx >= 0 ? stickyArray[currentStickyIdx] : -1);
4394
4459
  for (let offset = 0; offset <= 1; offset++) {
4395
4460
  const idx = currentStickyIdx - offset;
4396
- if (idx < 0 || activeIndices.has(stickyArray[idx])) continue;
4461
+ if (idx < 0) continue;
4397
4462
  const stickyIndex = stickyArray[idx];
4463
+ if (isStickyIndexActive(ctx, stickyIndex)) continue;
4398
4464
  const stickyId = (_a3 = state.idCache[stickyIndex]) != null ? _a3 : getId(state, stickyIndex);
4399
4465
  if (stickyId && !state.containerItemKeys.has(stickyId) && (stickyIndex < startBuffered || stickyIndex > endBuffered) && !needNewContainersSet.has(stickyIndex)) {
4400
4466
  needNewContainersSet.add(stickyIndex);
@@ -4436,10 +4502,86 @@ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentSt
4436
4502
  }
4437
4503
  }
4438
4504
  }
4505
+ function trackVisibleRange(range, i, top, size, scroll, scrollBottom) {
4506
+ let didPassVisibleEnd = false;
4507
+ if (range.startNoBuffer === null && top + size > scroll) {
4508
+ range.startNoBuffer = i;
4509
+ }
4510
+ if (range.firstFullyOnScreenIndex === void 0 && top >= scroll - 10 && top <= scrollBottom) {
4511
+ range.firstFullyOnScreenIndex = i;
4512
+ }
4513
+ if (range.startNoBuffer !== null) {
4514
+ if (top <= scrollBottom) {
4515
+ range.endNoBuffer = i;
4516
+ } else {
4517
+ didPassVisibleEnd = true;
4518
+ }
4519
+ }
4520
+ return didPassVisibleEnd;
4521
+ }
4522
+ function getIdsInVisibleRange(state, range) {
4523
+ var _a3, _b;
4524
+ const idsInView = [];
4525
+ const firstVisibleAnchorIndex = (_a3 = range.firstFullyOnScreenIndex) != null ? _a3 : range.startNoBuffer;
4526
+ if (firstVisibleAnchorIndex !== null && firstVisibleAnchorIndex !== void 0 && range.endNoBuffer !== null) {
4527
+ for (let i = firstVisibleAnchorIndex; i <= range.endNoBuffer; i++) {
4528
+ const id = (_b = state.idCache[i]) != null ? _b : getId(state, i);
4529
+ idsInView.push(id);
4530
+ }
4531
+ }
4532
+ return idsInView;
4533
+ }
4534
+ function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, scrollLength, scroll, scrollBottom) {
4535
+ var _a3, _b;
4536
+ const state = ctx.state;
4537
+ const {
4538
+ endBuffered,
4539
+ idCache,
4540
+ positions,
4541
+ props: { data },
4542
+ sizes,
4543
+ startBuffered
4544
+ } = state;
4545
+ if (startBuffered === null || endBuffered === null || startBuffered < 0 || endBuffered < startBuffered) {
4546
+ return;
4547
+ }
4548
+ const visibleRange = {
4549
+ endNoBuffer: null,
4550
+ firstFullyOnScreenIndex: void 0,
4551
+ startNoBuffer: null
4552
+ };
4553
+ for (let i = startBuffered; i <= endBuffered && i < data.length; i++) {
4554
+ const id = (_a3 = idCache[i]) != null ? _a3 : getId(state, i);
4555
+ const size = (_b = sizes.get(id)) != null ? _b : getItemSize(ctx, id, i, data[i]);
4556
+ const top = positions[i];
4557
+ const didPassVisibleEnd = trackVisibleRange(visibleRange, i, top, size, scroll, scrollBottom);
4558
+ if (didPassVisibleEnd) {
4559
+ break;
4560
+ }
4561
+ }
4562
+ Object.assign(state, {
4563
+ endNoBuffer: visibleRange.endNoBuffer,
4564
+ firstFullyOnScreenIndex: visibleRange.firstFullyOnScreenIndex,
4565
+ idsInView: getIdsInVisibleRange(state, visibleRange),
4566
+ startNoBuffer: visibleRange.startNoBuffer
4567
+ });
4568
+ if (visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null) {
4569
+ updateViewableItems(
4570
+ state,
4571
+ ctx,
4572
+ viewabilityConfigCallbackPairs,
4573
+ scrollLength,
4574
+ visibleRange.startNoBuffer,
4575
+ visibleRange.endNoBuffer,
4576
+ startBuffered,
4577
+ endBuffered
4578
+ );
4579
+ }
4580
+ }
4439
4581
  function calculateItemsInView(ctx, params = {}) {
4440
4582
  const state = ctx.state;
4441
4583
  batchedUpdates(() => {
4442
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
4584
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
4443
4585
  const {
4444
4586
  columns,
4445
4587
  containerItemKeys,
@@ -4540,6 +4682,15 @@ function calculateItemsInView(ctx, params = {}) {
4540
4682
  state.scrollForNextCalculateItemsInView = void 0;
4541
4683
  } else if ((top === null || scrollTopBuffered > top) && (bottom === null || scrollBottomBuffered < bottom)) {
4542
4684
  if (Platform.OS !== "web" || !isInMVCPActiveMode(state)) {
4685
+ if (viewabilityConfigCallbackPairs) {
4686
+ updateViewabilityForCachedRange(
4687
+ ctx,
4688
+ viewabilityConfigCallbackPairs,
4689
+ scrollLength,
4690
+ scroll,
4691
+ scrollBottom
4692
+ );
4693
+ }
4543
4694
  finishCalculateItemsInView == null ? void 0 : finishCalculateItemsInView();
4544
4695
  return;
4545
4696
  }
@@ -4556,6 +4707,7 @@ function calculateItemsInView(ctx, params = {}) {
4556
4707
  forceFullUpdate: !!forceFullItemPositions,
4557
4708
  optimizeForVisibleWindow,
4558
4709
  scrollBottomBuffered,
4710
+ scrollVelocity: speed,
4559
4711
  startIndex
4560
4712
  });
4561
4713
  totalSize = getContentSize(ctx);
@@ -4581,10 +4733,8 @@ function calculateItemsInView(ctx, params = {}) {
4581
4733
  updateScroll2(state.scroll);
4582
4734
  updateScrollRange();
4583
4735
  }
4584
- let startNoBuffer = null;
4585
4736
  let startBuffered = null;
4586
4737
  let startBufferedId = null;
4587
- let endNoBuffer = null;
4588
4738
  let endBuffered = null;
4589
4739
  let loopStart = (_f = suppressInitialScrollSideEffects ? bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.targetIndexSeed : void 0) != null ? _f : !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
4590
4740
  for (let i = loopStart; i >= 0; i--) {
@@ -4618,19 +4768,18 @@ function calculateItemsInView(ctx, params = {}) {
4618
4768
  maxIndexRendered = Math.max(maxIndexRendered, index);
4619
4769
  }
4620
4770
  }
4621
- let firstFullyOnScreenIndex;
4771
+ const visibleRange = {
4772
+ endNoBuffer: null,
4773
+ firstFullyOnScreenIndex: void 0,
4774
+ startNoBuffer: null
4775
+ };
4622
4776
  const dataLength = data.length;
4623
4777
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
4624
4778
  const id = (_i = idCache[i]) != null ? _i : getId(state, i);
4625
4779
  const size = (_j = sizes.get(id)) != null ? _j : getItemSize(ctx, id, i, data[i]);
4626
4780
  const top = positions[i];
4627
4781
  if (!foundEnd) {
4628
- if (startNoBuffer === null && top + size > scroll) {
4629
- startNoBuffer = i;
4630
- }
4631
- if (firstFullyOnScreenIndex === void 0 && top >= scroll - 10 && top <= scrollBottom) {
4632
- firstFullyOnScreenIndex = i;
4633
- }
4782
+ trackVisibleRange(visibleRange, i, top, size, scroll, scrollBottom);
4634
4783
  if (startBuffered === null && top + size > scrollTopBuffered) {
4635
4784
  startBuffered = i;
4636
4785
  startBufferedId = id;
@@ -4640,10 +4789,7 @@ function calculateItemsInView(ctx, params = {}) {
4640
4789
  nextTop = top;
4641
4790
  }
4642
4791
  }
4643
- if (startNoBuffer !== null) {
4644
- if (top <= scrollBottom) {
4645
- endNoBuffer = i;
4646
- }
4792
+ if (visibleRange.startNoBuffer !== null) {
4647
4793
  if (top <= scrollBottomBuffered) {
4648
4794
  endBuffered = i;
4649
4795
  if (scrollBottomBuffered > totalSize) {
@@ -4657,22 +4803,14 @@ function calculateItemsInView(ctx, params = {}) {
4657
4803
  }
4658
4804
  }
4659
4805
  }
4660
- const idsInView = [];
4661
- const firstVisibleAnchorIndex = firstFullyOnScreenIndex != null ? firstFullyOnScreenIndex : startNoBuffer;
4662
- if (firstVisibleAnchorIndex !== null && firstVisibleAnchorIndex !== void 0 && endNoBuffer !== null) {
4663
- for (let i = firstVisibleAnchorIndex; i <= endNoBuffer; i++) {
4664
- const id = (_k = idCache[i]) != null ? _k : getId(state, i);
4665
- idsInView.push(id);
4666
- }
4667
- }
4668
4806
  Object.assign(state, {
4669
4807
  endBuffered,
4670
- endNoBuffer,
4671
- firstFullyOnScreenIndex,
4672
- idsInView,
4808
+ endNoBuffer: visibleRange.endNoBuffer,
4809
+ firstFullyOnScreenIndex: visibleRange.firstFullyOnScreenIndex,
4810
+ idsInView: getIdsInVisibleRange(state, visibleRange),
4673
4811
  startBuffered,
4674
4812
  startBufferedId,
4675
- startNoBuffer
4813
+ startNoBuffer: visibleRange.startNoBuffer
4676
4814
  });
4677
4815
  if (enableScrollForNextCalculateItemsInView && nextTop !== void 0 && nextBottom !== void 0) {
4678
4816
  state.scrollForNextCalculateItemsInView = isNullOrUndefined(nextTop) && isNullOrUndefined(nextBottom) ? void 0 : {
@@ -4694,7 +4832,7 @@ function calculateItemsInView(ctx, params = {}) {
4694
4832
  const needNewContainers = [];
4695
4833
  const needNewContainersSet = /* @__PURE__ */ new Set();
4696
4834
  for (let i = startBuffered; i <= endBuffered; i++) {
4697
- const id = (_l = idCache[i]) != null ? _l : getId(state, i);
4835
+ const id = (_k = idCache[i]) != null ? _k : getId(state, i);
4698
4836
  if (!containerItemKeys.has(id)) {
4699
4837
  needNewContainersSet.add(i);
4700
4838
  needNewContainers.push(i);
@@ -4703,7 +4841,7 @@ function calculateItemsInView(ctx, params = {}) {
4703
4841
  if (alwaysRenderArr.length > 0) {
4704
4842
  for (const index of alwaysRenderArr) {
4705
4843
  if (index < 0 || index >= dataLength) continue;
4706
- const id = (_m = idCache[index]) != null ? _m : getId(state, index);
4844
+ const id = (_l = idCache[index]) != null ? _l : getId(state, index);
4707
4845
  if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
4708
4846
  needNewContainersSet.add(index);
4709
4847
  needNewContainers.push(index);
@@ -4713,7 +4851,6 @@ function calculateItemsInView(ctx, params = {}) {
4713
4851
  if (stickyHeaderIndicesArr.length > 0) {
4714
4852
  handleStickyActivation(
4715
4853
  ctx,
4716
- stickyHeaderIndicesSet,
4717
4854
  stickyHeaderIndicesArr,
4718
4855
  currentStickyIdx,
4719
4856
  needNewContainers,
@@ -4725,35 +4862,34 @@ function calculateItemsInView(ctx, params = {}) {
4725
4862
  set$(ctx, "activeStickyIndex", -1);
4726
4863
  }
4727
4864
  if (needNewContainers.length > 0) {
4728
- const requiredItemTypes = getItemType ? needNewContainers.map((i) => {
4865
+ const getRequiredItemType = getItemType ? (i) => {
4729
4866
  const itemType = getItemType(data[i], i);
4730
4867
  return itemType !== void 0 ? String(itemType) : "";
4731
- }) : void 0;
4732
- const availableContainers = findAvailableContainers(
4868
+ } : void 0;
4869
+ const availableContainerAllocations = findAvailableContainers(
4733
4870
  ctx,
4734
- needNewContainers.length,
4871
+ needNewContainers,
4735
4872
  startBuffered,
4736
4873
  endBuffered,
4737
4874
  pendingRemoval,
4738
- requiredItemTypes,
4739
- needNewContainers,
4875
+ getRequiredItemType,
4740
4876
  protectedContainerKeys
4741
4877
  );
4742
- for (let idx = 0; idx < needNewContainers.length; idx++) {
4743
- const i = needNewContainers[idx];
4744
- const containerIndex = availableContainers[idx];
4745
- const id = (_n = idCache[i]) != null ? _n : getId(state, i);
4878
+ for (const allocation of availableContainerAllocations) {
4879
+ const i = allocation.itemIndex;
4880
+ const containerIndex = allocation.containerIndex;
4881
+ const id = (_m = idCache[i]) != null ? _m : getId(state, i);
4746
4882
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
4747
4883
  if (oldKey && oldKey !== id) {
4748
4884
  containerItemKeys.delete(oldKey);
4749
4885
  }
4750
4886
  set$(ctx, `containerItemKey${containerIndex}`, id);
4751
4887
  set$(ctx, `containerItemData${containerIndex}`, data[i]);
4752
- if (requiredItemTypes) {
4753
- state.containerItemTypes.set(containerIndex, requiredItemTypes[idx]);
4888
+ if (allocation.itemType !== void 0) {
4889
+ state.containerItemTypes.set(containerIndex, allocation.itemType);
4754
4890
  }
4755
4891
  containerItemKeys.set(id, containerIndex);
4756
- (_o = state.userScrollAnchorReset) == null ? void 0 : _o.keys.add(id);
4892
+ (_n = state.userScrollAnchorReset) == null ? void 0 : _n.keys.add(id);
4757
4893
  const containerSticky = `containerSticky${containerIndex}`;
4758
4894
  const isSticky = stickyHeaderIndicesSet.has(i);
4759
4895
  const isAlwaysRender = alwaysRenderSet.has(i);
@@ -4791,7 +4927,7 @@ function calculateItemsInView(ctx, params = {}) {
4791
4927
  if (alwaysRenderArr.length > 0) {
4792
4928
  for (const index of alwaysRenderArr) {
4793
4929
  if (index < 0 || index >= dataLength) continue;
4794
- const id = (_p = idCache[index]) != null ? _p : getId(state, index);
4930
+ const id = (_o = idCache[index]) != null ? _o : getId(state, index);
4795
4931
  const containerIndex = containerItemKeys.get(id);
4796
4932
  if (containerIndex !== void 0) {
4797
4933
  state.stickyContainerPool.add(containerIndex);
@@ -4810,10 +4946,11 @@ function calculateItemsInView(ctx, params = {}) {
4810
4946
  alwaysRenderSet
4811
4947
  );
4812
4948
  }
4949
+ const pendingRemovalSet = pendingRemoval.length > 0 ? new Set(pendingRemoval) : void 0;
4813
4950
  let didChangePositions = false;
4814
4951
  for (let i = 0; i < numContainers; i++) {
4815
4952
  const itemKey = peek$(ctx, `containerItemKey${i}`);
4816
- if (pendingRemoval.includes(i)) {
4953
+ if (pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(i)) {
4817
4954
  if (itemKey !== void 0) {
4818
4955
  containerItemKeys.delete(itemKey);
4819
4956
  }
@@ -4844,24 +4981,24 @@ function calculateItemsInView(ctx, params = {}) {
4844
4981
  evaluateBootstrapInitialScroll(ctx);
4845
4982
  return;
4846
4983
  }
4847
- const mountedBufferedIndices = getMountedBufferedIndices(state);
4848
- const mountedNoBufferIndices = getMountedNoBufferIndices(state);
4849
- const readinessIndices = hasActiveInitialScroll(state) ? mountedBufferedIndices : mountedNoBufferIndices.length > 0 ? mountedNoBufferIndices : mountedBufferedIndices;
4850
- if (!queuedInitialLayout && readinessIndices.length > 0 && checkAllSizesKnown(state, readinessIndices)) {
4851
- setDidLayout(ctx);
4852
- handleInitialScrollLayoutReady(ctx);
4984
+ if (!queuedInitialLayout && !state.didContainersLayout) {
4985
+ const isInitialLayoutReady = hasActiveInitialScroll(state) ? checkAllSizesKnown(state, state.startBuffered, state.endBuffered) : checkAllSizesKnown(state, state.startNoBuffer, state.endNoBuffer) || checkAllSizesKnown(state, state.startBuffered, state.endBuffered);
4986
+ if (isInitialLayoutReady) {
4987
+ setDidLayout(ctx);
4988
+ handleInitialScrollLayoutReady(ctx);
4989
+ }
4853
4990
  }
4854
- if (viewabilityConfigCallbackPairs && startNoBuffer !== null && endNoBuffer !== null) {
4991
+ if (viewabilityConfigCallbackPairs && visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null) {
4855
4992
  if (!didMVCPAdjustScroll) {
4856
4993
  updateViewableItems(
4857
4994
  ctx.state,
4858
4995
  ctx,
4859
4996
  viewabilityConfigCallbackPairs,
4860
4997
  scrollLength,
4861
- startNoBuffer,
4862
- endNoBuffer,
4863
- startBuffered != null ? startBuffered : startNoBuffer,
4864
- endBuffered != null ? endBuffered : endNoBuffer
4998
+ visibleRange.startNoBuffer,
4999
+ visibleRange.endNoBuffer,
5000
+ startBuffered != null ? startBuffered : visibleRange.startNoBuffer,
5001
+ endBuffered != null ? endBuffered : visibleRange.endNoBuffer
4865
5002
  );
4866
5003
  }
4867
5004
  }
@@ -5189,22 +5326,27 @@ var ScrollAdjustHandler = class {
5189
5326
 
5190
5327
  // src/core/updateAnchoredEndSpace.ts
5191
5328
  function maybeUpdateAnchoredEndSpace(ctx) {
5192
- var _a3;
5329
+ var _a3, _b;
5193
5330
  const state = ctx.state;
5194
5331
  const anchoredEndSpace = state.props.anchoredEndSpace;
5195
5332
  const previousSize = peek$(ctx, "anchoredEndSpaceSize");
5333
+ const previousReadyAnchorIndex = state.anchoredEndSpaceReadyAnchorIndex;
5334
+ const previousReadyAnchorKey = state.anchoredEndSpaceReadyAnchorKey;
5335
+ const nextAnchorIndex = anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex;
5336
+ let nextAnchorKey;
5337
+ let isReady = true;
5196
5338
  let nextSize = 0;
5197
5339
  if (anchoredEndSpace) {
5198
5340
  const { anchorIndex, anchorMaxSize, anchorOffset = 0 } = anchoredEndSpace;
5199
5341
  const { data } = state.props;
5200
5342
  if (anchorIndex >= 0 && anchorIndex < data.length && state.scrollLength > 0) {
5343
+ nextAnchorKey = getId(state, anchorIndex);
5201
5344
  let contentBelowAnchor = 0;
5202
5345
  const footerSize = ctx.values.get("footerSize") || 0;
5203
5346
  const stylePaddingBottom = state.props.stylePaddingBottom || 0;
5204
5347
  let hasUnknownTailSize = false;
5205
5348
  for (let index = anchorIndex; index < data.length; index++) {
5206
- const itemKey = getId(state, index);
5207
- const size = itemKey ? state.sizesKnown.get(itemKey) : void 0;
5349
+ const size = getKnownOrFixedItemSize(ctx, index);
5208
5350
  const effectiveSize = index === anchorIndex && anchorMaxSize !== void 0 ? Math.min(size || 0, Math.max(0, anchorMaxSize)) : size;
5209
5351
  if (size === void 0) {
5210
5352
  hasUnknownTailSize = true;
@@ -5214,15 +5356,25 @@ function maybeUpdateAnchoredEndSpace(ctx) {
5214
5356
  }
5215
5357
  }
5216
5358
  contentBelowAnchor += footerSize + stylePaddingBottom;
5359
+ isReady = !hasUnknownTailSize;
5217
5360
  nextSize = hasUnknownTailSize ? previousSize || 0 : Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
5361
+ } else if (anchorIndex >= 0) {
5362
+ isReady = false;
5218
5363
  }
5219
5364
  }
5220
- if (previousSize !== nextSize) {
5221
- set$(ctx, "anchoredEndSpaceSize", nextSize);
5222
- (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
5223
- if (anchoredEndSpace == null ? void 0 : anchoredEndSpace.includeInEndInset) {
5365
+ const didSizeChange = previousSize !== nextSize;
5366
+ const didReadyAnchorChange = previousReadyAnchorIndex !== nextAnchorIndex || previousReadyAnchorKey !== nextAnchorKey;
5367
+ if (isReady && (didSizeChange || didReadyAnchorChange)) {
5368
+ state.anchoredEndSpaceReadyAnchorIndex = nextAnchorIndex;
5369
+ state.anchoredEndSpaceReadyAnchorKey = nextAnchorKey;
5370
+ if (didSizeChange) {
5371
+ set$(ctx, "anchoredEndSpaceSize", nextSize);
5372
+ (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
5373
+ }
5374
+ if (didSizeChange && (anchoredEndSpace == null ? void 0 : anchoredEndSpace.includeInEndInset)) {
5224
5375
  updateScroll(ctx, state.scroll, true);
5225
5376
  }
5377
+ (_b = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onReady) == null ? void 0 : _b.call(anchoredEndSpace, { anchorIndex: nextAnchorIndex, anchorKey: nextAnchorKey, size: nextSize });
5226
5378
  }
5227
5379
  return nextSize;
5228
5380
  }
@@ -5307,6 +5459,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5307
5459
  } = state;
5308
5460
  if (!data) return;
5309
5461
  const index = state.indexByKey.get(itemKey);
5462
+ let resolvedMeasurementItem;
5310
5463
  if (getFixedItemSize) {
5311
5464
  if (index === void 0) {
5312
5465
  return;
@@ -5317,6 +5470,12 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5317
5470
  }
5318
5471
  const type = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
5319
5472
  const size2 = getFixedItemSize(itemData, index, type);
5473
+ resolvedMeasurementItem = {
5474
+ didResolveFixedItemSize: true,
5475
+ fixedItemSize: size2,
5476
+ itemData,
5477
+ itemType: type
5478
+ };
5320
5479
  if (size2 !== void 0 && size2 === sizesKnown.get(itemKey)) {
5321
5480
  updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
5322
5481
  return;
@@ -5326,7 +5485,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5326
5485
  let shouldMaintainScrollAtEnd = false;
5327
5486
  let minIndexSizeChanged;
5328
5487
  const prevSizeKnown = state.sizesKnown.get(itemKey);
5329
- const diff = updateOneItemSize(ctx, itemKey, sizeObj);
5488
+ const diff = updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem);
5330
5489
  const size = roundSize(horizontal ? sizeObj.width : sizeObj.height);
5331
5490
  if (diff !== 0) {
5332
5491
  minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
@@ -5351,7 +5510,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5351
5510
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
5352
5511
  }
5353
5512
  updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
5354
- if (didContainersLayout || checkAllSizesKnown(state, getMountedBufferedIndices(state))) {
5513
+ if (didContainersLayout || checkAllSizesKnown(state, state.startBuffered, state.endBuffered)) {
5355
5514
  if (needsRecalculate) {
5356
5515
  state.scrollForNextCalculateItemsInView = void 0;
5357
5516
  runOrScheduleMVCPRecalculate(ctx);
@@ -5365,8 +5524,8 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5365
5524
  }
5366
5525
  }
5367
5526
  }
5368
- function updateOneItemSize(ctx, itemKey, sizeObj) {
5369
- var _a3, _b;
5527
+ function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
5528
+ var _a3, _b, _c;
5370
5529
  const state = ctx.state;
5371
5530
  const {
5372
5531
  indexByKey,
@@ -5376,14 +5535,19 @@ function updateOneItemSize(ctx, itemKey, sizeObj) {
5376
5535
  } = state;
5377
5536
  if (!data) return 0;
5378
5537
  const index = indexByKey.get(itemKey);
5379
- const itemData = data[index];
5380
- let itemType;
5381
- let fixedItemSize;
5382
- if (getFixedItemSize) {
5383
- itemType = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
5538
+ const itemData = (_a3 = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemData) != null ? _a3 : data[index];
5539
+ let itemType = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemType;
5540
+ let fixedItemSize = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.fixedItemSize;
5541
+ if (getFixedItemSize && !(resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize)) {
5542
+ itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
5384
5543
  fixedItemSize = getFixedItemSize(itemData, index, itemType);
5385
5544
  }
5386
- const prevSize = getItemSize(ctx, itemKey, index, itemData);
5545
+ const resolvedItemSize = (resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize) || itemType !== void 0 || fixedItemSize !== void 0 ? {
5546
+ didResolveFixedItemSize: resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize,
5547
+ fixedItemSize,
5548
+ itemType
5549
+ } : void 0;
5550
+ const prevSize = getItemSize(ctx, itemKey, index, itemData, void 0, void 0, void 0, resolvedItemSize);
5387
5551
  const rawSize = horizontal ? sizeObj.width : sizeObj.height;
5388
5552
  const prevSizeKnown = sizesKnown.get(itemKey);
5389
5553
  if (Platform.OS !== "web" && prevSizeKnown !== void 0 && isNativeLayoutNoise(rawSize - prevSizeKnown)) {
@@ -5392,7 +5556,7 @@ function updateOneItemSize(ctx, itemKey, sizeObj) {
5392
5556
  const size = Platform.OS === "web" ? Math.round(rawSize) : roundSize(rawSize);
5393
5557
  sizesKnown.set(itemKey, size);
5394
5558
  if (fixedItemSize === void 0 && size > 0) {
5395
- itemType != null ? itemType : itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
5559
+ itemType != null ? itemType : itemType = getItemType ? (_c = getItemType(itemData, index)) != null ? _c : "" : "";
5396
5560
  let averages = averageSizes[itemType];
5397
5561
  if (!averages) {
5398
5562
  averages = averageSizes[itemType] = { avg: 0, num: 0 };
@@ -5473,6 +5637,25 @@ function createColumnWrapperStyle(contentContainerStyle) {
5473
5637
  }
5474
5638
  }
5475
5639
 
5640
+ // src/core/scrollToEnd.ts
5641
+ function scrollToEnd(ctx, options) {
5642
+ const state = ctx.state;
5643
+ const data = state.props.data;
5644
+ const index = data.length - 1;
5645
+ if (index === -1) {
5646
+ return false;
5647
+ }
5648
+ const paddingBottom = state.props.stylePaddingBottom || 0;
5649
+ const footerSize = peek$(ctx, "footerSize") || 0;
5650
+ scrollToIndex(ctx, {
5651
+ ...options,
5652
+ index,
5653
+ viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
5654
+ viewPosition: 1
5655
+ });
5656
+ return true;
5657
+ }
5658
+
5476
5659
  // src/utils/createImperativeHandle.ts
5477
5660
  var DEFAULT_AVERAGE_ITEM_SIZE_TYPE = "default";
5478
5661
  function getAverageItemSizes(state) {
@@ -5488,7 +5671,12 @@ function getAverageItemSizes(state) {
5488
5671
  }
5489
5672
  return averageItemSizes;
5490
5673
  }
5491
- function createImperativeHandle(ctx) {
5674
+ function triggerMountedContainerLayouts(ctx) {
5675
+ for (const triggerLayout of ctx.containerLayoutTriggers.values()) {
5676
+ triggerLayout();
5677
+ }
5678
+ }
5679
+ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
5492
5680
  const state = ctx.state;
5493
5681
  const IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS = 800;
5494
5682
  const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
@@ -5505,15 +5693,10 @@ function createImperativeHandle(ctx) {
5505
5693
  if (targetIndex >= dataLength) {
5506
5694
  return false;
5507
5695
  }
5508
- if (anchorIndex === void 0 || anchorIndex < 0 || anchorIndex >= dataLength || targetIndex < anchorIndex || props.getFixedItemSize) {
5696
+ if (anchorIndex === void 0 || anchorIndex < 0 || anchorIndex >= dataLength || targetIndex < anchorIndex) {
5509
5697
  return true;
5510
5698
  }
5511
- for (let index = anchorIndex; index < dataLength; index++) {
5512
- if (!state.sizesKnown.has(getId(state, index))) {
5513
- return false;
5514
- }
5515
- }
5516
- return true;
5699
+ return areKnownOrFixedItemSizesAvailable(ctx, anchorIndex, dataLength - 1);
5517
5700
  };
5518
5701
  const runWhenReady = (token, run, isReady) => {
5519
5702
  const startedAt = Date.now();
@@ -5536,11 +5719,7 @@ function createImperativeHandle(ctx) {
5536
5719
  };
5537
5720
  requestAnimationFrame(check);
5538
5721
  };
5539
- const runScrollWithPromise = (run, isReady = () => true) => new Promise((resolve) => {
5540
- var _a3;
5541
- const token = ++imperativeScrollToken;
5542
- (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
5543
- state.pendingScrollResolve = resolve;
5722
+ const runScrollRequest = (token, resolve, run, isReady = () => true) => {
5544
5723
  const runNow = () => {
5545
5724
  if (token !== imperativeScrollToken) {
5546
5725
  return;
@@ -5558,7 +5737,33 @@ function createImperativeHandle(ctx) {
5558
5737
  } else {
5559
5738
  runNow();
5560
5739
  }
5740
+ };
5741
+ const startImperativeScroll = (resolve) => {
5742
+ var _a3;
5743
+ const token = ++imperativeScrollToken;
5744
+ state.pendingScrollToEnd = void 0;
5745
+ (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
5746
+ state.pendingScrollResolve = resolve;
5747
+ return token;
5748
+ };
5749
+ const runScrollWithPromise = (run, isReady = () => true) => new Promise((resolve) => {
5750
+ const token = startImperativeScroll(resolve);
5751
+ runScrollRequest(token, resolve, run, isReady);
5561
5752
  });
5753
+ state.runPendingScrollToEnd = () => {
5754
+ const pendingScroll = state.pendingScrollToEnd;
5755
+ if (pendingScroll) {
5756
+ state.pendingScrollToEnd = void 0;
5757
+ if (pendingScroll.token === imperativeScrollToken) {
5758
+ runScrollRequest(
5759
+ pendingScroll.token,
5760
+ pendingScroll.resolve,
5761
+ () => scrollToEnd(ctx, pendingScroll.options),
5762
+ () => isScrollToIndexReady(state.props.data.length - 1, true)
5763
+ );
5764
+ }
5765
+ }
5766
+ };
5562
5767
  const scrollIndexIntoView = (options) => {
5563
5768
  if (state) {
5564
5769
  const { index, ...rest } = options;
@@ -5596,6 +5801,7 @@ function createImperativeHandle(ctx) {
5596
5801
  state.columns.length = 0;
5597
5802
  state.columnSpans.length = 0;
5598
5803
  }
5804
+ triggerMountedContainerLayouts(ctx);
5599
5805
  (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
5600
5806
  };
5601
5807
  return {
@@ -5657,26 +5863,20 @@ function createImperativeHandle(ctx) {
5657
5863
  }
5658
5864
  return false;
5659
5865
  }),
5660
- scrollToEnd: (options) => runScrollWithPromise(
5661
- () => {
5662
- const data = state.props.data;
5663
- const stylePaddingBottom = state.props.stylePaddingBottom;
5664
- const index = data.length - 1;
5665
- if (index !== -1) {
5666
- const paddingBottom = stylePaddingBottom || 0;
5667
- const footerSize = peek$(ctx, "footerSize") || 0;
5668
- scrollToIndex(ctx, {
5669
- ...options,
5670
- index,
5671
- viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
5672
- viewPosition: 1
5673
- });
5674
- return true;
5675
- }
5676
- return false;
5677
- },
5678
- () => isScrollToIndexReady(state.props.data.length - 1, true)
5679
- ),
5866
+ scrollToEnd: (options) => new Promise((resolve) => {
5867
+ var _a3;
5868
+ const token = startImperativeScroll(resolve);
5869
+ state.pendingScrollToEnd = {
5870
+ options,
5871
+ resolve,
5872
+ token
5873
+ };
5874
+ if (scheduleImperativeScrollCommit) {
5875
+ scheduleImperativeScrollCommit();
5876
+ } else {
5877
+ (_a3 = state.runPendingScrollToEnd) == null ? void 0 : _a3.call(state);
5878
+ }
5879
+ }),
5680
5880
  scrollToIndex: (params) => {
5681
5881
  return runScrollWithPromise(
5682
5882
  () => {
@@ -6046,6 +6246,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6046
6246
  viewOffset: 0
6047
6247
  } : void 0;
6048
6248
  const [canRender, setCanRender] = React2__namespace.useState(!IsNewArchitecture);
6249
+ const [, scheduleImperativeScrollCommit] = React2__namespace.useReducer((value) => value + 1, 0);
6049
6250
  const ctx = useStateContext();
6050
6251
  ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
6051
6252
  const refScroller = React2.useRef(null);
@@ -6408,7 +6609,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6408
6609
  viewabilityConfigCallbackPairs
6409
6610
  });
6410
6611
  state.viewabilityConfigCallbackPairs = viewability;
6411
- state.enableScrollForNextCalculateItemsInView = !viewability;
6612
+ state.enableScrollForNextCalculateItemsInView = true;
6412
6613
  if (viewability) {
6413
6614
  state.scrollForNextCalculateItemsInView = void 0;
6414
6615
  }
@@ -6418,7 +6619,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6418
6619
  doInitialAllocateContainers(ctx);
6419
6620
  }
6420
6621
  });
6421
- React2.useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx), []);
6622
+ React2.useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx, scheduleImperativeScrollCommit), []);
6623
+ React2.useLayoutEffect(() => {
6624
+ var _a4;
6625
+ (_a4 = state.runPendingScrollToEnd) == null ? void 0 : _a4.call(state);
6626
+ });
6422
6627
  React2.useEffect(() => {
6423
6628
  if (Platform.OS !== "web" || usesBootstrapInitialScroll) {
6424
6629
  return;