@legendapp/list 3.0.2 → 3.0.3

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/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 3.0.3
2
+
3
+ - Fix: MVCP was getting batched to improve big jumps, but was making scroll worse
4
+ - Fix: On native, ignore one-physical-pixel layout measurement noise, preventing unnecessary item size updates from Fabric and native onLayout rounding differences.
5
+ - Fix: Average item sizes update correctly when getFixedItemSize returns undefined for only some item types.
6
+
1
7
  ## 3.0.2
2
8
 
3
9
  - Fix: Using viewability was causing scrolling to end to sometimes not update items in view if the JS thread was slammed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@legendapp/list",
3
- "version": "3.0.2",
3
+ "version": "3.0.3",
4
4
  "description": "Legend List is a drop-in replacement for FlatList with much better performance and supporting dynamically sized items.",
5
5
  "sideEffects": false,
6
6
  "private": false,
package/react-native.js CHANGED
@@ -743,6 +743,22 @@ function Separator({ ItemSeparatorComponent, leadingItem }) {
743
743
  const isLastItem = useIsLastItem();
744
744
  return isLastItem ? null : /* @__PURE__ */ React2__namespace.createElement(ItemSeparatorComponent, { leadingItem });
745
745
  }
746
+ var PixelRatio = ReactNative.PixelRatio;
747
+
748
+ // src/utils/layoutMeasurement.ts
749
+ var FLOATING_POINT_SLACK = 0.01;
750
+ var NATIVE_LAYOUT_MEASUREMENT_EPSILON = 1 / PixelRatio.get() + FLOATING_POINT_SLACK;
751
+ function isWithinEpsilon(delta) {
752
+ return Math.abs(delta) <= NATIVE_LAYOUT_MEASUREMENT_EPSILON;
753
+ }
754
+ function isNativeLayoutNoise(delta) {
755
+ return isWithinEpsilon(delta);
756
+ }
757
+ function isNativeLayoutSizeNoise(heightDelta, widthDelta) {
758
+ return isWithinEpsilon(heightDelta) && isWithinEpsilon(widthDelta);
759
+ }
760
+
761
+ // src/hooks/useOnLayoutSync.native.tsx
746
762
  function useOnLayoutSync({
747
763
  ref,
748
764
  onLayoutProp,
@@ -751,11 +767,21 @@ function useOnLayoutSync({
751
767
  const lastLayoutRef = React2.useRef(null);
752
768
  const onLayout = React2.useCallback(
753
769
  (event) => {
754
- var _a3, _b;
755
770
  const { layout } = event.nativeEvent;
756
- if (layout.height !== ((_a3 = lastLayoutRef.current) == null ? void 0 : _a3.height) || layout.width !== ((_b = lastLayoutRef.current) == null ? void 0 : _b.width)) {
771
+ const lastLayout = lastLayoutRef.current;
772
+ const didLayoutSizeChange = lastLayout && (layout.height !== lastLayout.height || layout.width !== lastLayout.width);
773
+ const isMeasuredLayoutNoise = !!didLayoutSizeChange && !!lastLayout.measuredLayout && isNativeLayoutSizeNoise(
774
+ layout.height - lastLayout.measuredLayout.height,
775
+ layout.width - lastLayout.measuredLayout.width
776
+ );
777
+ if (!lastLayout || didLayoutSizeChange && !isMeasuredLayoutNoise) {
757
778
  onLayoutChange(layout, false);
758
- lastLayoutRef.current = layout;
779
+ }
780
+ if (!lastLayout || didLayoutSizeChange) {
781
+ lastLayoutRef.current = {
782
+ ...layout,
783
+ measuredLayout: isMeasuredLayoutNoise ? lastLayout == null ? void 0 : lastLayout.measuredLayout : void 0
784
+ };
759
785
  }
760
786
  onLayoutProp == null ? void 0 : onLayoutProp(event);
761
787
  },
@@ -766,7 +792,7 @@ function useOnLayoutSync({
766
792
  if (ref.current) {
767
793
  ref.current.measure((x, y, width, height) => {
768
794
  const layout = { height, width, x, y };
769
- lastLayoutRef.current = layout;
795
+ lastLayoutRef.current = { ...layout, measuredLayout: layout };
770
796
  onLayoutChange(layout, true);
771
797
  });
772
798
  }
@@ -2595,7 +2621,7 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2595
2621
  if (scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength && !state.pendingNativeMVCPAdjust) {
2596
2622
  state.mvcpAnchorLock = void 0;
2597
2623
  state.pendingNativeMVCPAdjust = void 0;
2598
- state.userScrollAnchorResetKeys = /* @__PURE__ */ new Set();
2624
+ state.userScrollAnchorReset = { keys: /* @__PURE__ */ new Set() };
2599
2625
  if (state.queuedMVCPRecalculate !== void 0) {
2600
2626
  cancelAnimationFrame(state.queuedMVCPRecalculate);
2601
2627
  state.queuedMVCPRecalculate = void 0;
@@ -4413,7 +4439,7 @@ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentSt
4413
4439
  function calculateItemsInView(ctx, params = {}) {
4414
4440
  const state = ctx.state;
4415
4441
  batchedUpdates(() => {
4416
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q;
4442
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
4417
4443
  const {
4418
4444
  columns,
4419
4445
  containerItemKeys,
@@ -4727,7 +4753,7 @@ function calculateItemsInView(ctx, params = {}) {
4727
4753
  state.containerItemTypes.set(containerIndex, requiredItemTypes[idx]);
4728
4754
  }
4729
4755
  containerItemKeys.set(id, containerIndex);
4730
- (_o = state.userScrollAnchorResetKeys) == null ? void 0 : _o.add(id);
4756
+ (_o = state.userScrollAnchorReset) == null ? void 0 : _o.keys.add(id);
4731
4757
  const containerSticky = `containerSticky${containerIndex}`;
4732
4758
  const isSticky = stickyHeaderIndicesSet.has(i);
4733
4759
  const isAlwaysRender = alwaysRenderSet.has(i);
@@ -4755,13 +4781,17 @@ function calculateItemsInView(ctx, params = {}) {
4755
4781
  }
4756
4782
  }
4757
4783
  }
4758
- if (((_p = state.userScrollAnchorResetKeys) == null ? void 0 : _p.size) === 0) {
4759
- state.userScrollAnchorResetKeys = void 0;
4784
+ if (state.userScrollAnchorReset) {
4785
+ if (state.userScrollAnchorReset.keys.size === 0) {
4786
+ state.userScrollAnchorReset = void 0;
4787
+ } else {
4788
+ state.userScrollAnchorReset.batchSize = state.userScrollAnchorReset.keys.size;
4789
+ }
4760
4790
  }
4761
4791
  if (alwaysRenderArr.length > 0) {
4762
4792
  for (const index of alwaysRenderArr) {
4763
4793
  if (index < 0 || index >= dataLength) continue;
4764
- const id = (_q = idCache[index]) != null ? _q : getId(state, index);
4794
+ const id = (_p = idCache[index]) != null ? _p : getId(state, index);
4765
4795
  const containerIndex = containerItemKeys.get(id);
4766
4796
  if (containerIndex !== void 0) {
4767
4797
  state.stickyContainerPool.add(containerIndex);
@@ -5215,37 +5245,42 @@ function updateContentInsetEndAdjustment(ctx, previousContentInsetEndAdjustment)
5215
5245
 
5216
5246
  // src/core/updateItemSize.ts
5217
5247
  function runOrScheduleMVCPRecalculate(ctx) {
5248
+ var _a3, _b;
5218
5249
  const state = ctx.state;
5219
- if (state.userScrollAnchorResetKeys !== void 0) {
5220
- if (state.queuedMVCPRecalculate !== void 0) {
5221
- return;
5222
- }
5223
- state.queuedMVCPRecalculate = requestAnimationFrame(() => {
5224
- var _a3;
5225
- state.queuedMVCPRecalculate = void 0;
5250
+ if (state.userScrollAnchorReset !== void 0) {
5251
+ const replacementBatchSize = (_a3 = state.userScrollAnchorReset.batchSize) != null ? _a3 : state.userScrollAnchorReset.keys.size;
5252
+ const replacementMeasurementBatchThreshold = 3;
5253
+ const shouldBatchReplacementMeasurements = replacementBatchSize > replacementMeasurementBatchThreshold;
5254
+ if (shouldBatchReplacementMeasurements) {
5255
+ if (state.queuedMVCPRecalculate === void 0) {
5256
+ state.queuedMVCPRecalculate = requestAnimationFrame(() => {
5257
+ var _a4;
5258
+ state.queuedMVCPRecalculate = void 0;
5259
+ calculateItemsInView(ctx);
5260
+ if (((_a4 = state.userScrollAnchorReset) == null ? void 0 : _a4.keys.size) === 0) {
5261
+ state.userScrollAnchorReset = void 0;
5262
+ }
5263
+ });
5264
+ }
5265
+ } else {
5226
5266
  calculateItemsInView(ctx);
5227
- if (((_a3 = state.userScrollAnchorResetKeys) == null ? void 0 : _a3.size) === 0) {
5228
- state.userScrollAnchorResetKeys = void 0;
5267
+ if (((_b = state.userScrollAnchorReset) == null ? void 0 : _b.keys.size) === 0) {
5268
+ state.userScrollAnchorReset = void 0;
5229
5269
  }
5230
- });
5231
- return;
5232
- }
5233
- if (Platform.OS === "web") {
5270
+ }
5271
+ } else if (Platform.OS === "web") {
5234
5272
  if (!state.mvcpAnchorLock) {
5235
5273
  if (state.queuedMVCPRecalculate !== void 0) {
5236
5274
  cancelAnimationFrame(state.queuedMVCPRecalculate);
5237
5275
  state.queuedMVCPRecalculate = void 0;
5238
5276
  }
5239
5277
  calculateItemsInView(ctx, { doMVCP: true });
5240
- return;
5241
- }
5242
- if (state.queuedMVCPRecalculate !== void 0) {
5243
- return;
5278
+ } else if (state.queuedMVCPRecalculate === void 0) {
5279
+ state.queuedMVCPRecalculate = requestAnimationFrame(() => {
5280
+ state.queuedMVCPRecalculate = void 0;
5281
+ calculateItemsInView(ctx, { doMVCP: true });
5282
+ });
5244
5283
  }
5245
- state.queuedMVCPRecalculate = requestAnimationFrame(() => {
5246
- state.queuedMVCPRecalculate = void 0;
5247
- calculateItemsInView(ctx, { doMVCP: true });
5248
- });
5249
5284
  } else {
5250
5285
  calculateItemsInView(ctx, { doMVCP: true });
5251
5286
  }
@@ -5263,8 +5298,8 @@ function updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal) {
5263
5298
  function updateItemSize(ctx, itemKey, sizeObj) {
5264
5299
  var _a3;
5265
5300
  const state = ctx.state;
5266
- const userScrollAnchorResetKeys = state.userScrollAnchorResetKeys;
5267
- const didMeasureUserScrollAnchorResetItem = !!(userScrollAnchorResetKeys == null ? void 0 : userScrollAnchorResetKeys.delete(itemKey));
5301
+ const userScrollAnchorReset = state.userScrollAnchorReset;
5302
+ const didMeasureUserScrollAnchorResetItem = !!(userScrollAnchorReset == null ? void 0 : userScrollAnchorReset.keys.delete(itemKey));
5268
5303
  const {
5269
5304
  didContainersLayout,
5270
5305
  sizesKnown,
@@ -5320,8 +5355,8 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5320
5355
  if (needsRecalculate) {
5321
5356
  state.scrollForNextCalculateItemsInView = void 0;
5322
5357
  runOrScheduleMVCPRecalculate(ctx);
5323
- } else if (didMeasureUserScrollAnchorResetItem && (userScrollAnchorResetKeys == null ? void 0 : userScrollAnchorResetKeys.size) === 0) {
5324
- state.userScrollAnchorResetKeys = void 0;
5358
+ } else if (didMeasureUserScrollAnchorResetItem && (userScrollAnchorReset == null ? void 0 : userScrollAnchorReset.keys.size) === 0) {
5359
+ state.userScrollAnchorReset = void 0;
5325
5360
  }
5326
5361
  if (shouldMaintainScrollAtEnd) {
5327
5362
  if (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onItemLayout) {
@@ -5331,7 +5366,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5331
5366
  }
5332
5367
  }
5333
5368
  function updateOneItemSize(ctx, itemKey, sizeObj) {
5334
- var _a3;
5369
+ var _a3, _b;
5335
5370
  const state = ctx.state;
5336
5371
  const {
5337
5372
  indexByKey,
@@ -5341,13 +5376,23 @@ function updateOneItemSize(ctx, itemKey, sizeObj) {
5341
5376
  } = state;
5342
5377
  if (!data) return 0;
5343
5378
  const index = indexByKey.get(itemKey);
5344
- const prevSize = getItemSize(ctx, itemKey, index, data[index]);
5379
+ const itemData = data[index];
5380
+ let itemType;
5381
+ let fixedItemSize;
5382
+ if (getFixedItemSize) {
5383
+ itemType = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
5384
+ fixedItemSize = getFixedItemSize(itemData, index, itemType);
5385
+ }
5386
+ const prevSize = getItemSize(ctx, itemKey, index, itemData);
5345
5387
  const rawSize = horizontal ? sizeObj.width : sizeObj.height;
5346
- const size = Platform.OS === "web" ? Math.round(rawSize) : roundSize(rawSize);
5347
5388
  const prevSizeKnown = sizesKnown.get(itemKey);
5389
+ if (Platform.OS !== "web" && prevSizeKnown !== void 0 && isNativeLayoutNoise(rawSize - prevSizeKnown)) {
5390
+ return 0;
5391
+ }
5392
+ const size = Platform.OS === "web" ? Math.round(rawSize) : roundSize(rawSize);
5348
5393
  sizesKnown.set(itemKey, size);
5349
- if (!getFixedItemSize && size > 0) {
5350
- const itemType = getItemType ? (_a3 = getItemType(data[index], index)) != null ? _a3 : "" : "";
5394
+ if (fixedItemSize === void 0 && size > 0) {
5395
+ itemType != null ? itemType : itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
5351
5396
  let averages = averageSizes[itemType];
5352
5397
  if (!averages) {
5353
5398
  averages = averageSizes[itemType] = { avg: 0, num: 0 };
package/react-native.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as React2 from 'react';
2
2
  import { useReducer, useEffect, createContext, useRef, useState, useMemo, useCallback, useLayoutEffect, useImperativeHandle, useContext } from 'react';
3
3
  import * as ReactNative from 'react-native';
4
- import { Animated, Platform as Platform$1, View as View$1, Text as Text$1, StyleSheet as StyleSheet$1, RefreshControl, Dimensions, I18nManager } from 'react-native';
4
+ import { Animated, Platform as Platform$1, View as View$1, Text as Text$1, PixelRatio as PixelRatio$1, StyleSheet as StyleSheet$1, RefreshControl, Dimensions, I18nManager } from 'react-native';
5
5
  import { useSyncExternalStore } from 'use-sync-external-store/shim';
6
6
 
7
7
  // src/components/LegendList.tsx
@@ -722,6 +722,22 @@ function Separator({ ItemSeparatorComponent, leadingItem }) {
722
722
  const isLastItem = useIsLastItem();
723
723
  return isLastItem ? null : /* @__PURE__ */ React2.createElement(ItemSeparatorComponent, { leadingItem });
724
724
  }
725
+ var PixelRatio = PixelRatio$1;
726
+
727
+ // src/utils/layoutMeasurement.ts
728
+ var FLOATING_POINT_SLACK = 0.01;
729
+ var NATIVE_LAYOUT_MEASUREMENT_EPSILON = 1 / PixelRatio.get() + FLOATING_POINT_SLACK;
730
+ function isWithinEpsilon(delta) {
731
+ return Math.abs(delta) <= NATIVE_LAYOUT_MEASUREMENT_EPSILON;
732
+ }
733
+ function isNativeLayoutNoise(delta) {
734
+ return isWithinEpsilon(delta);
735
+ }
736
+ function isNativeLayoutSizeNoise(heightDelta, widthDelta) {
737
+ return isWithinEpsilon(heightDelta) && isWithinEpsilon(widthDelta);
738
+ }
739
+
740
+ // src/hooks/useOnLayoutSync.native.tsx
725
741
  function useOnLayoutSync({
726
742
  ref,
727
743
  onLayoutProp,
@@ -730,11 +746,21 @@ function useOnLayoutSync({
730
746
  const lastLayoutRef = useRef(null);
731
747
  const onLayout = useCallback(
732
748
  (event) => {
733
- var _a3, _b;
734
749
  const { layout } = event.nativeEvent;
735
- if (layout.height !== ((_a3 = lastLayoutRef.current) == null ? void 0 : _a3.height) || layout.width !== ((_b = lastLayoutRef.current) == null ? void 0 : _b.width)) {
750
+ const lastLayout = lastLayoutRef.current;
751
+ const didLayoutSizeChange = lastLayout && (layout.height !== lastLayout.height || layout.width !== lastLayout.width);
752
+ const isMeasuredLayoutNoise = !!didLayoutSizeChange && !!lastLayout.measuredLayout && isNativeLayoutSizeNoise(
753
+ layout.height - lastLayout.measuredLayout.height,
754
+ layout.width - lastLayout.measuredLayout.width
755
+ );
756
+ if (!lastLayout || didLayoutSizeChange && !isMeasuredLayoutNoise) {
736
757
  onLayoutChange(layout, false);
737
- lastLayoutRef.current = layout;
758
+ }
759
+ if (!lastLayout || didLayoutSizeChange) {
760
+ lastLayoutRef.current = {
761
+ ...layout,
762
+ measuredLayout: isMeasuredLayoutNoise ? lastLayout == null ? void 0 : lastLayout.measuredLayout : void 0
763
+ };
738
764
  }
739
765
  onLayoutProp == null ? void 0 : onLayoutProp(event);
740
766
  },
@@ -745,7 +771,7 @@ function useOnLayoutSync({
745
771
  if (ref.current) {
746
772
  ref.current.measure((x, y, width, height) => {
747
773
  const layout = { height, width, x, y };
748
- lastLayoutRef.current = layout;
774
+ lastLayoutRef.current = { ...layout, measuredLayout: layout };
749
775
  onLayoutChange(layout, true);
750
776
  });
751
777
  }
@@ -2574,7 +2600,7 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2574
2600
  if (scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength && !state.pendingNativeMVCPAdjust) {
2575
2601
  state.mvcpAnchorLock = void 0;
2576
2602
  state.pendingNativeMVCPAdjust = void 0;
2577
- state.userScrollAnchorResetKeys = /* @__PURE__ */ new Set();
2603
+ state.userScrollAnchorReset = { keys: /* @__PURE__ */ new Set() };
2578
2604
  if (state.queuedMVCPRecalculate !== void 0) {
2579
2605
  cancelAnimationFrame(state.queuedMVCPRecalculate);
2580
2606
  state.queuedMVCPRecalculate = void 0;
@@ -4392,7 +4418,7 @@ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentSt
4392
4418
  function calculateItemsInView(ctx, params = {}) {
4393
4419
  const state = ctx.state;
4394
4420
  batchedUpdates(() => {
4395
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q;
4421
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
4396
4422
  const {
4397
4423
  columns,
4398
4424
  containerItemKeys,
@@ -4706,7 +4732,7 @@ function calculateItemsInView(ctx, params = {}) {
4706
4732
  state.containerItemTypes.set(containerIndex, requiredItemTypes[idx]);
4707
4733
  }
4708
4734
  containerItemKeys.set(id, containerIndex);
4709
- (_o = state.userScrollAnchorResetKeys) == null ? void 0 : _o.add(id);
4735
+ (_o = state.userScrollAnchorReset) == null ? void 0 : _o.keys.add(id);
4710
4736
  const containerSticky = `containerSticky${containerIndex}`;
4711
4737
  const isSticky = stickyHeaderIndicesSet.has(i);
4712
4738
  const isAlwaysRender = alwaysRenderSet.has(i);
@@ -4734,13 +4760,17 @@ function calculateItemsInView(ctx, params = {}) {
4734
4760
  }
4735
4761
  }
4736
4762
  }
4737
- if (((_p = state.userScrollAnchorResetKeys) == null ? void 0 : _p.size) === 0) {
4738
- state.userScrollAnchorResetKeys = void 0;
4763
+ if (state.userScrollAnchorReset) {
4764
+ if (state.userScrollAnchorReset.keys.size === 0) {
4765
+ state.userScrollAnchorReset = void 0;
4766
+ } else {
4767
+ state.userScrollAnchorReset.batchSize = state.userScrollAnchorReset.keys.size;
4768
+ }
4739
4769
  }
4740
4770
  if (alwaysRenderArr.length > 0) {
4741
4771
  for (const index of alwaysRenderArr) {
4742
4772
  if (index < 0 || index >= dataLength) continue;
4743
- const id = (_q = idCache[index]) != null ? _q : getId(state, index);
4773
+ const id = (_p = idCache[index]) != null ? _p : getId(state, index);
4744
4774
  const containerIndex = containerItemKeys.get(id);
4745
4775
  if (containerIndex !== void 0) {
4746
4776
  state.stickyContainerPool.add(containerIndex);
@@ -5194,37 +5224,42 @@ function updateContentInsetEndAdjustment(ctx, previousContentInsetEndAdjustment)
5194
5224
 
5195
5225
  // src/core/updateItemSize.ts
5196
5226
  function runOrScheduleMVCPRecalculate(ctx) {
5227
+ var _a3, _b;
5197
5228
  const state = ctx.state;
5198
- if (state.userScrollAnchorResetKeys !== void 0) {
5199
- if (state.queuedMVCPRecalculate !== void 0) {
5200
- return;
5201
- }
5202
- state.queuedMVCPRecalculate = requestAnimationFrame(() => {
5203
- var _a3;
5204
- state.queuedMVCPRecalculate = void 0;
5229
+ if (state.userScrollAnchorReset !== void 0) {
5230
+ const replacementBatchSize = (_a3 = state.userScrollAnchorReset.batchSize) != null ? _a3 : state.userScrollAnchorReset.keys.size;
5231
+ const replacementMeasurementBatchThreshold = 3;
5232
+ const shouldBatchReplacementMeasurements = replacementBatchSize > replacementMeasurementBatchThreshold;
5233
+ if (shouldBatchReplacementMeasurements) {
5234
+ if (state.queuedMVCPRecalculate === void 0) {
5235
+ state.queuedMVCPRecalculate = requestAnimationFrame(() => {
5236
+ var _a4;
5237
+ state.queuedMVCPRecalculate = void 0;
5238
+ calculateItemsInView(ctx);
5239
+ if (((_a4 = state.userScrollAnchorReset) == null ? void 0 : _a4.keys.size) === 0) {
5240
+ state.userScrollAnchorReset = void 0;
5241
+ }
5242
+ });
5243
+ }
5244
+ } else {
5205
5245
  calculateItemsInView(ctx);
5206
- if (((_a3 = state.userScrollAnchorResetKeys) == null ? void 0 : _a3.size) === 0) {
5207
- state.userScrollAnchorResetKeys = void 0;
5246
+ if (((_b = state.userScrollAnchorReset) == null ? void 0 : _b.keys.size) === 0) {
5247
+ state.userScrollAnchorReset = void 0;
5208
5248
  }
5209
- });
5210
- return;
5211
- }
5212
- if (Platform.OS === "web") {
5249
+ }
5250
+ } else if (Platform.OS === "web") {
5213
5251
  if (!state.mvcpAnchorLock) {
5214
5252
  if (state.queuedMVCPRecalculate !== void 0) {
5215
5253
  cancelAnimationFrame(state.queuedMVCPRecalculate);
5216
5254
  state.queuedMVCPRecalculate = void 0;
5217
5255
  }
5218
5256
  calculateItemsInView(ctx, { doMVCP: true });
5219
- return;
5220
- }
5221
- if (state.queuedMVCPRecalculate !== void 0) {
5222
- return;
5257
+ } else if (state.queuedMVCPRecalculate === void 0) {
5258
+ state.queuedMVCPRecalculate = requestAnimationFrame(() => {
5259
+ state.queuedMVCPRecalculate = void 0;
5260
+ calculateItemsInView(ctx, { doMVCP: true });
5261
+ });
5223
5262
  }
5224
- state.queuedMVCPRecalculate = requestAnimationFrame(() => {
5225
- state.queuedMVCPRecalculate = void 0;
5226
- calculateItemsInView(ctx, { doMVCP: true });
5227
- });
5228
5263
  } else {
5229
5264
  calculateItemsInView(ctx, { doMVCP: true });
5230
5265
  }
@@ -5242,8 +5277,8 @@ function updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal) {
5242
5277
  function updateItemSize(ctx, itemKey, sizeObj) {
5243
5278
  var _a3;
5244
5279
  const state = ctx.state;
5245
- const userScrollAnchorResetKeys = state.userScrollAnchorResetKeys;
5246
- const didMeasureUserScrollAnchorResetItem = !!(userScrollAnchorResetKeys == null ? void 0 : userScrollAnchorResetKeys.delete(itemKey));
5280
+ const userScrollAnchorReset = state.userScrollAnchorReset;
5281
+ const didMeasureUserScrollAnchorResetItem = !!(userScrollAnchorReset == null ? void 0 : userScrollAnchorReset.keys.delete(itemKey));
5247
5282
  const {
5248
5283
  didContainersLayout,
5249
5284
  sizesKnown,
@@ -5299,8 +5334,8 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5299
5334
  if (needsRecalculate) {
5300
5335
  state.scrollForNextCalculateItemsInView = void 0;
5301
5336
  runOrScheduleMVCPRecalculate(ctx);
5302
- } else if (didMeasureUserScrollAnchorResetItem && (userScrollAnchorResetKeys == null ? void 0 : userScrollAnchorResetKeys.size) === 0) {
5303
- state.userScrollAnchorResetKeys = void 0;
5337
+ } else if (didMeasureUserScrollAnchorResetItem && (userScrollAnchorReset == null ? void 0 : userScrollAnchorReset.keys.size) === 0) {
5338
+ state.userScrollAnchorReset = void 0;
5304
5339
  }
5305
5340
  if (shouldMaintainScrollAtEnd) {
5306
5341
  if (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onItemLayout) {
@@ -5310,7 +5345,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5310
5345
  }
5311
5346
  }
5312
5347
  function updateOneItemSize(ctx, itemKey, sizeObj) {
5313
- var _a3;
5348
+ var _a3, _b;
5314
5349
  const state = ctx.state;
5315
5350
  const {
5316
5351
  indexByKey,
@@ -5320,13 +5355,23 @@ function updateOneItemSize(ctx, itemKey, sizeObj) {
5320
5355
  } = state;
5321
5356
  if (!data) return 0;
5322
5357
  const index = indexByKey.get(itemKey);
5323
- const prevSize = getItemSize(ctx, itemKey, index, data[index]);
5358
+ const itemData = data[index];
5359
+ let itemType;
5360
+ let fixedItemSize;
5361
+ if (getFixedItemSize) {
5362
+ itemType = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
5363
+ fixedItemSize = getFixedItemSize(itemData, index, itemType);
5364
+ }
5365
+ const prevSize = getItemSize(ctx, itemKey, index, itemData);
5324
5366
  const rawSize = horizontal ? sizeObj.width : sizeObj.height;
5325
- const size = Platform.OS === "web" ? Math.round(rawSize) : roundSize(rawSize);
5326
5367
  const prevSizeKnown = sizesKnown.get(itemKey);
5368
+ if (Platform.OS !== "web" && prevSizeKnown !== void 0 && isNativeLayoutNoise(rawSize - prevSizeKnown)) {
5369
+ return 0;
5370
+ }
5371
+ const size = Platform.OS === "web" ? Math.round(rawSize) : roundSize(rawSize);
5327
5372
  sizesKnown.set(itemKey, size);
5328
- if (!getFixedItemSize && size > 0) {
5329
- const itemType = getItemType ? (_a3 = getItemType(data[index], index)) != null ? _a3 : "" : "";
5373
+ if (fixedItemSize === void 0 && size > 0) {
5374
+ itemType != null ? itemType : itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
5330
5375
  let averages = averageSizes[itemType];
5331
5376
  if (!averages) {
5332
5377
  averages = averageSizes[itemType] = { avg: 0, num: 0 };
@@ -3106,7 +3106,7 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
3106
3106
  if (scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength && !state.pendingNativeMVCPAdjust) {
3107
3107
  state.mvcpAnchorLock = void 0;
3108
3108
  state.pendingNativeMVCPAdjust = void 0;
3109
- state.userScrollAnchorResetKeys = /* @__PURE__ */ new Set();
3109
+ state.userScrollAnchorReset = { keys: /* @__PURE__ */ new Set() };
3110
3110
  if (state.queuedMVCPRecalculate !== void 0) {
3111
3111
  cancelAnimationFrame(state.queuedMVCPRecalculate);
3112
3112
  state.queuedMVCPRecalculate = void 0;
@@ -5069,7 +5069,7 @@ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentSt
5069
5069
  function calculateItemsInView(ctx, params = {}) {
5070
5070
  const state = ctx.state;
5071
5071
  batchedUpdates(() => {
5072
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q;
5072
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
5073
5073
  const {
5074
5074
  columns,
5075
5075
  containerItemKeys,
@@ -5383,7 +5383,7 @@ function calculateItemsInView(ctx, params = {}) {
5383
5383
  state.containerItemTypes.set(containerIndex, requiredItemTypes[idx]);
5384
5384
  }
5385
5385
  containerItemKeys.set(id, containerIndex);
5386
- (_o = state.userScrollAnchorResetKeys) == null ? void 0 : _o.add(id);
5386
+ (_o = state.userScrollAnchorReset) == null ? void 0 : _o.keys.add(id);
5387
5387
  const containerSticky = `containerSticky${containerIndex}`;
5388
5388
  const isSticky = stickyHeaderIndicesSet.has(i);
5389
5389
  const isAlwaysRender = alwaysRenderSet.has(i);
@@ -5411,13 +5411,17 @@ function calculateItemsInView(ctx, params = {}) {
5411
5411
  }
5412
5412
  }
5413
5413
  }
5414
- if (((_p = state.userScrollAnchorResetKeys) == null ? void 0 : _p.size) === 0) {
5415
- state.userScrollAnchorResetKeys = void 0;
5414
+ if (state.userScrollAnchorReset) {
5415
+ if (state.userScrollAnchorReset.keys.size === 0) {
5416
+ state.userScrollAnchorReset = void 0;
5417
+ } else {
5418
+ state.userScrollAnchorReset.batchSize = state.userScrollAnchorReset.keys.size;
5419
+ }
5416
5420
  }
5417
5421
  if (alwaysRenderArr.length > 0) {
5418
5422
  for (const index of alwaysRenderArr) {
5419
5423
  if (index < 0 || index >= dataLength) continue;
5420
- const id = (_q = idCache[index]) != null ? _q : getId(state, index);
5424
+ const id = (_p = idCache[index]) != null ? _p : getId(state, index);
5421
5425
  const containerIndex = containerItemKeys.get(id);
5422
5426
  if (containerIndex !== void 0) {
5423
5427
  state.stickyContainerPool.add(containerIndex);
@@ -5871,37 +5875,42 @@ function updateContentInsetEndAdjustment(ctx, previousContentInsetEndAdjustment)
5871
5875
 
5872
5876
  // src/core/updateItemSize.ts
5873
5877
  function runOrScheduleMVCPRecalculate(ctx) {
5878
+ var _a3, _b;
5874
5879
  const state = ctx.state;
5875
- if (state.userScrollAnchorResetKeys !== void 0) {
5876
- if (state.queuedMVCPRecalculate !== void 0) {
5877
- return;
5878
- }
5879
- state.queuedMVCPRecalculate = requestAnimationFrame(() => {
5880
- var _a3;
5881
- state.queuedMVCPRecalculate = void 0;
5880
+ if (state.userScrollAnchorReset !== void 0) {
5881
+ const replacementBatchSize = (_a3 = state.userScrollAnchorReset.batchSize) != null ? _a3 : state.userScrollAnchorReset.keys.size;
5882
+ const replacementMeasurementBatchThreshold = 3;
5883
+ const shouldBatchReplacementMeasurements = replacementBatchSize > replacementMeasurementBatchThreshold;
5884
+ if (shouldBatchReplacementMeasurements) {
5885
+ if (state.queuedMVCPRecalculate === void 0) {
5886
+ state.queuedMVCPRecalculate = requestAnimationFrame(() => {
5887
+ var _a4;
5888
+ state.queuedMVCPRecalculate = void 0;
5889
+ calculateItemsInView(ctx);
5890
+ if (((_a4 = state.userScrollAnchorReset) == null ? void 0 : _a4.keys.size) === 0) {
5891
+ state.userScrollAnchorReset = void 0;
5892
+ }
5893
+ });
5894
+ }
5895
+ } else {
5882
5896
  calculateItemsInView(ctx);
5883
- if (((_a3 = state.userScrollAnchorResetKeys) == null ? void 0 : _a3.size) === 0) {
5884
- state.userScrollAnchorResetKeys = void 0;
5897
+ if (((_b = state.userScrollAnchorReset) == null ? void 0 : _b.keys.size) === 0) {
5898
+ state.userScrollAnchorReset = void 0;
5885
5899
  }
5886
- });
5887
- return;
5888
- }
5889
- {
5900
+ }
5901
+ } else {
5890
5902
  if (!state.mvcpAnchorLock) {
5891
5903
  if (state.queuedMVCPRecalculate !== void 0) {
5892
5904
  cancelAnimationFrame(state.queuedMVCPRecalculate);
5893
5905
  state.queuedMVCPRecalculate = void 0;
5894
5906
  }
5895
5907
  calculateItemsInView(ctx, { doMVCP: true });
5896
- return;
5897
- }
5898
- if (state.queuedMVCPRecalculate !== void 0) {
5899
- return;
5908
+ } else if (state.queuedMVCPRecalculate === void 0) {
5909
+ state.queuedMVCPRecalculate = requestAnimationFrame(() => {
5910
+ state.queuedMVCPRecalculate = void 0;
5911
+ calculateItemsInView(ctx, { doMVCP: true });
5912
+ });
5900
5913
  }
5901
- state.queuedMVCPRecalculate = requestAnimationFrame(() => {
5902
- state.queuedMVCPRecalculate = void 0;
5903
- calculateItemsInView(ctx, { doMVCP: true });
5904
- });
5905
5914
  }
5906
5915
  }
5907
5916
  function updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal) {
@@ -5917,8 +5926,8 @@ function updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal) {
5917
5926
  function updateItemSize(ctx, itemKey, sizeObj) {
5918
5927
  var _a3;
5919
5928
  const state = ctx.state;
5920
- const userScrollAnchorResetKeys = state.userScrollAnchorResetKeys;
5921
- const didMeasureUserScrollAnchorResetItem = !!(userScrollAnchorResetKeys == null ? void 0 : userScrollAnchorResetKeys.delete(itemKey));
5929
+ const userScrollAnchorReset = state.userScrollAnchorReset;
5930
+ const didMeasureUserScrollAnchorResetItem = !!(userScrollAnchorReset == null ? void 0 : userScrollAnchorReset.keys.delete(itemKey));
5922
5931
  const {
5923
5932
  didContainersLayout,
5924
5933
  sizesKnown,
@@ -5974,8 +5983,8 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5974
5983
  if (needsRecalculate) {
5975
5984
  state.scrollForNextCalculateItemsInView = void 0;
5976
5985
  runOrScheduleMVCPRecalculate(ctx);
5977
- } else if (didMeasureUserScrollAnchorResetItem && (userScrollAnchorResetKeys == null ? void 0 : userScrollAnchorResetKeys.size) === 0) {
5978
- state.userScrollAnchorResetKeys = void 0;
5986
+ } else if (didMeasureUserScrollAnchorResetItem && (userScrollAnchorReset == null ? void 0 : userScrollAnchorReset.keys.size) === 0) {
5987
+ state.userScrollAnchorReset = void 0;
5979
5988
  }
5980
5989
  if (shouldMaintainScrollAtEnd) {
5981
5990
  if (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onItemLayout) {
@@ -5985,7 +5994,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5985
5994
  }
5986
5995
  }
5987
5996
  function updateOneItemSize(ctx, itemKey, sizeObj) {
5988
- var _a3;
5997
+ var _a3, _b;
5989
5998
  const state = ctx.state;
5990
5999
  const {
5991
6000
  indexByKey,
@@ -5995,13 +6004,20 @@ function updateOneItemSize(ctx, itemKey, sizeObj) {
5995
6004
  } = state;
5996
6005
  if (!data) return 0;
5997
6006
  const index = indexByKey.get(itemKey);
5998
- const prevSize = getItemSize(ctx, itemKey, index, data[index]);
6007
+ const itemData = data[index];
6008
+ let itemType;
6009
+ let fixedItemSize;
6010
+ if (getFixedItemSize) {
6011
+ itemType = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
6012
+ fixedItemSize = getFixedItemSize(itemData, index, itemType);
6013
+ }
6014
+ const prevSize = getItemSize(ctx, itemKey, index, itemData);
5999
6015
  const rawSize = horizontal ? sizeObj.width : sizeObj.height;
6000
- const size = Math.round(rawSize) ;
6001
6016
  const prevSizeKnown = sizesKnown.get(itemKey);
6017
+ const size = Math.round(rawSize) ;
6002
6018
  sizesKnown.set(itemKey, size);
6003
- if (!getFixedItemSize && size > 0) {
6004
- const itemType = getItemType ? (_a3 = getItemType(data[index], index)) != null ? _a3 : "" : "";
6019
+ if (fixedItemSize === void 0 && size > 0) {
6020
+ itemType != null ? itemType : itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
6005
6021
  let averages = averageSizes[itemType];
6006
6022
  if (!averages) {
6007
6023
  averages = averageSizes[itemType] = { avg: 0, num: 0 };
@@ -3085,7 +3085,7 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
3085
3085
  if (scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength && !state.pendingNativeMVCPAdjust) {
3086
3086
  state.mvcpAnchorLock = void 0;
3087
3087
  state.pendingNativeMVCPAdjust = void 0;
3088
- state.userScrollAnchorResetKeys = /* @__PURE__ */ new Set();
3088
+ state.userScrollAnchorReset = { keys: /* @__PURE__ */ new Set() };
3089
3089
  if (state.queuedMVCPRecalculate !== void 0) {
3090
3090
  cancelAnimationFrame(state.queuedMVCPRecalculate);
3091
3091
  state.queuedMVCPRecalculate = void 0;
@@ -5048,7 +5048,7 @@ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentSt
5048
5048
  function calculateItemsInView(ctx, params = {}) {
5049
5049
  const state = ctx.state;
5050
5050
  batchedUpdates(() => {
5051
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q;
5051
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
5052
5052
  const {
5053
5053
  columns,
5054
5054
  containerItemKeys,
@@ -5362,7 +5362,7 @@ function calculateItemsInView(ctx, params = {}) {
5362
5362
  state.containerItemTypes.set(containerIndex, requiredItemTypes[idx]);
5363
5363
  }
5364
5364
  containerItemKeys.set(id, containerIndex);
5365
- (_o = state.userScrollAnchorResetKeys) == null ? void 0 : _o.add(id);
5365
+ (_o = state.userScrollAnchorReset) == null ? void 0 : _o.keys.add(id);
5366
5366
  const containerSticky = `containerSticky${containerIndex}`;
5367
5367
  const isSticky = stickyHeaderIndicesSet.has(i);
5368
5368
  const isAlwaysRender = alwaysRenderSet.has(i);
@@ -5390,13 +5390,17 @@ function calculateItemsInView(ctx, params = {}) {
5390
5390
  }
5391
5391
  }
5392
5392
  }
5393
- if (((_p = state.userScrollAnchorResetKeys) == null ? void 0 : _p.size) === 0) {
5394
- state.userScrollAnchorResetKeys = void 0;
5393
+ if (state.userScrollAnchorReset) {
5394
+ if (state.userScrollAnchorReset.keys.size === 0) {
5395
+ state.userScrollAnchorReset = void 0;
5396
+ } else {
5397
+ state.userScrollAnchorReset.batchSize = state.userScrollAnchorReset.keys.size;
5398
+ }
5395
5399
  }
5396
5400
  if (alwaysRenderArr.length > 0) {
5397
5401
  for (const index of alwaysRenderArr) {
5398
5402
  if (index < 0 || index >= dataLength) continue;
5399
- const id = (_q = idCache[index]) != null ? _q : getId(state, index);
5403
+ const id = (_p = idCache[index]) != null ? _p : getId(state, index);
5400
5404
  const containerIndex = containerItemKeys.get(id);
5401
5405
  if (containerIndex !== void 0) {
5402
5406
  state.stickyContainerPool.add(containerIndex);
@@ -5850,37 +5854,42 @@ function updateContentInsetEndAdjustment(ctx, previousContentInsetEndAdjustment)
5850
5854
 
5851
5855
  // src/core/updateItemSize.ts
5852
5856
  function runOrScheduleMVCPRecalculate(ctx) {
5857
+ var _a3, _b;
5853
5858
  const state = ctx.state;
5854
- if (state.userScrollAnchorResetKeys !== void 0) {
5855
- if (state.queuedMVCPRecalculate !== void 0) {
5856
- return;
5857
- }
5858
- state.queuedMVCPRecalculate = requestAnimationFrame(() => {
5859
- var _a3;
5860
- state.queuedMVCPRecalculate = void 0;
5859
+ if (state.userScrollAnchorReset !== void 0) {
5860
+ const replacementBatchSize = (_a3 = state.userScrollAnchorReset.batchSize) != null ? _a3 : state.userScrollAnchorReset.keys.size;
5861
+ const replacementMeasurementBatchThreshold = 3;
5862
+ const shouldBatchReplacementMeasurements = replacementBatchSize > replacementMeasurementBatchThreshold;
5863
+ if (shouldBatchReplacementMeasurements) {
5864
+ if (state.queuedMVCPRecalculate === void 0) {
5865
+ state.queuedMVCPRecalculate = requestAnimationFrame(() => {
5866
+ var _a4;
5867
+ state.queuedMVCPRecalculate = void 0;
5868
+ calculateItemsInView(ctx);
5869
+ if (((_a4 = state.userScrollAnchorReset) == null ? void 0 : _a4.keys.size) === 0) {
5870
+ state.userScrollAnchorReset = void 0;
5871
+ }
5872
+ });
5873
+ }
5874
+ } else {
5861
5875
  calculateItemsInView(ctx);
5862
- if (((_a3 = state.userScrollAnchorResetKeys) == null ? void 0 : _a3.size) === 0) {
5863
- state.userScrollAnchorResetKeys = void 0;
5876
+ if (((_b = state.userScrollAnchorReset) == null ? void 0 : _b.keys.size) === 0) {
5877
+ state.userScrollAnchorReset = void 0;
5864
5878
  }
5865
- });
5866
- return;
5867
- }
5868
- {
5879
+ }
5880
+ } else {
5869
5881
  if (!state.mvcpAnchorLock) {
5870
5882
  if (state.queuedMVCPRecalculate !== void 0) {
5871
5883
  cancelAnimationFrame(state.queuedMVCPRecalculate);
5872
5884
  state.queuedMVCPRecalculate = void 0;
5873
5885
  }
5874
5886
  calculateItemsInView(ctx, { doMVCP: true });
5875
- return;
5876
- }
5877
- if (state.queuedMVCPRecalculate !== void 0) {
5878
- return;
5887
+ } else if (state.queuedMVCPRecalculate === void 0) {
5888
+ state.queuedMVCPRecalculate = requestAnimationFrame(() => {
5889
+ state.queuedMVCPRecalculate = void 0;
5890
+ calculateItemsInView(ctx, { doMVCP: true });
5891
+ });
5879
5892
  }
5880
- state.queuedMVCPRecalculate = requestAnimationFrame(() => {
5881
- state.queuedMVCPRecalculate = void 0;
5882
- calculateItemsInView(ctx, { doMVCP: true });
5883
- });
5884
5893
  }
5885
5894
  }
5886
5895
  function updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal) {
@@ -5896,8 +5905,8 @@ function updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal) {
5896
5905
  function updateItemSize(ctx, itemKey, sizeObj) {
5897
5906
  var _a3;
5898
5907
  const state = ctx.state;
5899
- const userScrollAnchorResetKeys = state.userScrollAnchorResetKeys;
5900
- const didMeasureUserScrollAnchorResetItem = !!(userScrollAnchorResetKeys == null ? void 0 : userScrollAnchorResetKeys.delete(itemKey));
5908
+ const userScrollAnchorReset = state.userScrollAnchorReset;
5909
+ const didMeasureUserScrollAnchorResetItem = !!(userScrollAnchorReset == null ? void 0 : userScrollAnchorReset.keys.delete(itemKey));
5901
5910
  const {
5902
5911
  didContainersLayout,
5903
5912
  sizesKnown,
@@ -5953,8 +5962,8 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5953
5962
  if (needsRecalculate) {
5954
5963
  state.scrollForNextCalculateItemsInView = void 0;
5955
5964
  runOrScheduleMVCPRecalculate(ctx);
5956
- } else if (didMeasureUserScrollAnchorResetItem && (userScrollAnchorResetKeys == null ? void 0 : userScrollAnchorResetKeys.size) === 0) {
5957
- state.userScrollAnchorResetKeys = void 0;
5965
+ } else if (didMeasureUserScrollAnchorResetItem && (userScrollAnchorReset == null ? void 0 : userScrollAnchorReset.keys.size) === 0) {
5966
+ state.userScrollAnchorReset = void 0;
5958
5967
  }
5959
5968
  if (shouldMaintainScrollAtEnd) {
5960
5969
  if (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onItemLayout) {
@@ -5964,7 +5973,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5964
5973
  }
5965
5974
  }
5966
5975
  function updateOneItemSize(ctx, itemKey, sizeObj) {
5967
- var _a3;
5976
+ var _a3, _b;
5968
5977
  const state = ctx.state;
5969
5978
  const {
5970
5979
  indexByKey,
@@ -5974,13 +5983,20 @@ function updateOneItemSize(ctx, itemKey, sizeObj) {
5974
5983
  } = state;
5975
5984
  if (!data) return 0;
5976
5985
  const index = indexByKey.get(itemKey);
5977
- const prevSize = getItemSize(ctx, itemKey, index, data[index]);
5986
+ const itemData = data[index];
5987
+ let itemType;
5988
+ let fixedItemSize;
5989
+ if (getFixedItemSize) {
5990
+ itemType = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
5991
+ fixedItemSize = getFixedItemSize(itemData, index, itemType);
5992
+ }
5993
+ const prevSize = getItemSize(ctx, itemKey, index, itemData);
5978
5994
  const rawSize = horizontal ? sizeObj.width : sizeObj.height;
5979
- const size = Math.round(rawSize) ;
5980
5995
  const prevSizeKnown = sizesKnown.get(itemKey);
5996
+ const size = Math.round(rawSize) ;
5981
5997
  sizesKnown.set(itemKey, size);
5982
- if (!getFixedItemSize && size > 0) {
5983
- const itemType = getItemType ? (_a3 = getItemType(data[index], index)) != null ? _a3 : "" : "";
5998
+ if (fixedItemSize === void 0 && size > 0) {
5999
+ itemType != null ? itemType : itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
5984
6000
  let averages = averageSizes[itemType];
5985
6001
  if (!averages) {
5986
6002
  averages = averageSizes[itemType] = { avg: 0, num: 0 };
package/react.js CHANGED
@@ -3106,7 +3106,7 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
3106
3106
  if (scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength && !state.pendingNativeMVCPAdjust) {
3107
3107
  state.mvcpAnchorLock = void 0;
3108
3108
  state.pendingNativeMVCPAdjust = void 0;
3109
- state.userScrollAnchorResetKeys = /* @__PURE__ */ new Set();
3109
+ state.userScrollAnchorReset = { keys: /* @__PURE__ */ new Set() };
3110
3110
  if (state.queuedMVCPRecalculate !== void 0) {
3111
3111
  cancelAnimationFrame(state.queuedMVCPRecalculate);
3112
3112
  state.queuedMVCPRecalculate = void 0;
@@ -5069,7 +5069,7 @@ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentSt
5069
5069
  function calculateItemsInView(ctx, params = {}) {
5070
5070
  const state = ctx.state;
5071
5071
  batchedUpdates(() => {
5072
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q;
5072
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
5073
5073
  const {
5074
5074
  columns,
5075
5075
  containerItemKeys,
@@ -5383,7 +5383,7 @@ function calculateItemsInView(ctx, params = {}) {
5383
5383
  state.containerItemTypes.set(containerIndex, requiredItemTypes[idx]);
5384
5384
  }
5385
5385
  containerItemKeys.set(id, containerIndex);
5386
- (_o = state.userScrollAnchorResetKeys) == null ? void 0 : _o.add(id);
5386
+ (_o = state.userScrollAnchorReset) == null ? void 0 : _o.keys.add(id);
5387
5387
  const containerSticky = `containerSticky${containerIndex}`;
5388
5388
  const isSticky = stickyHeaderIndicesSet.has(i);
5389
5389
  const isAlwaysRender = alwaysRenderSet.has(i);
@@ -5411,13 +5411,17 @@ function calculateItemsInView(ctx, params = {}) {
5411
5411
  }
5412
5412
  }
5413
5413
  }
5414
- if (((_p = state.userScrollAnchorResetKeys) == null ? void 0 : _p.size) === 0) {
5415
- state.userScrollAnchorResetKeys = void 0;
5414
+ if (state.userScrollAnchorReset) {
5415
+ if (state.userScrollAnchorReset.keys.size === 0) {
5416
+ state.userScrollAnchorReset = void 0;
5417
+ } else {
5418
+ state.userScrollAnchorReset.batchSize = state.userScrollAnchorReset.keys.size;
5419
+ }
5416
5420
  }
5417
5421
  if (alwaysRenderArr.length > 0) {
5418
5422
  for (const index of alwaysRenderArr) {
5419
5423
  if (index < 0 || index >= dataLength) continue;
5420
- const id = (_q = idCache[index]) != null ? _q : getId(state, index);
5424
+ const id = (_p = idCache[index]) != null ? _p : getId(state, index);
5421
5425
  const containerIndex = containerItemKeys.get(id);
5422
5426
  if (containerIndex !== void 0) {
5423
5427
  state.stickyContainerPool.add(containerIndex);
@@ -5871,37 +5875,42 @@ function updateContentInsetEndAdjustment(ctx, previousContentInsetEndAdjustment)
5871
5875
 
5872
5876
  // src/core/updateItemSize.ts
5873
5877
  function runOrScheduleMVCPRecalculate(ctx) {
5878
+ var _a3, _b;
5874
5879
  const state = ctx.state;
5875
- if (state.userScrollAnchorResetKeys !== void 0) {
5876
- if (state.queuedMVCPRecalculate !== void 0) {
5877
- return;
5878
- }
5879
- state.queuedMVCPRecalculate = requestAnimationFrame(() => {
5880
- var _a3;
5881
- state.queuedMVCPRecalculate = void 0;
5880
+ if (state.userScrollAnchorReset !== void 0) {
5881
+ const replacementBatchSize = (_a3 = state.userScrollAnchorReset.batchSize) != null ? _a3 : state.userScrollAnchorReset.keys.size;
5882
+ const replacementMeasurementBatchThreshold = 3;
5883
+ const shouldBatchReplacementMeasurements = replacementBatchSize > replacementMeasurementBatchThreshold;
5884
+ if (shouldBatchReplacementMeasurements) {
5885
+ if (state.queuedMVCPRecalculate === void 0) {
5886
+ state.queuedMVCPRecalculate = requestAnimationFrame(() => {
5887
+ var _a4;
5888
+ state.queuedMVCPRecalculate = void 0;
5889
+ calculateItemsInView(ctx);
5890
+ if (((_a4 = state.userScrollAnchorReset) == null ? void 0 : _a4.keys.size) === 0) {
5891
+ state.userScrollAnchorReset = void 0;
5892
+ }
5893
+ });
5894
+ }
5895
+ } else {
5882
5896
  calculateItemsInView(ctx);
5883
- if (((_a3 = state.userScrollAnchorResetKeys) == null ? void 0 : _a3.size) === 0) {
5884
- state.userScrollAnchorResetKeys = void 0;
5897
+ if (((_b = state.userScrollAnchorReset) == null ? void 0 : _b.keys.size) === 0) {
5898
+ state.userScrollAnchorReset = void 0;
5885
5899
  }
5886
- });
5887
- return;
5888
- }
5889
- {
5900
+ }
5901
+ } else {
5890
5902
  if (!state.mvcpAnchorLock) {
5891
5903
  if (state.queuedMVCPRecalculate !== void 0) {
5892
5904
  cancelAnimationFrame(state.queuedMVCPRecalculate);
5893
5905
  state.queuedMVCPRecalculate = void 0;
5894
5906
  }
5895
5907
  calculateItemsInView(ctx, { doMVCP: true });
5896
- return;
5897
- }
5898
- if (state.queuedMVCPRecalculate !== void 0) {
5899
- return;
5908
+ } else if (state.queuedMVCPRecalculate === void 0) {
5909
+ state.queuedMVCPRecalculate = requestAnimationFrame(() => {
5910
+ state.queuedMVCPRecalculate = void 0;
5911
+ calculateItemsInView(ctx, { doMVCP: true });
5912
+ });
5900
5913
  }
5901
- state.queuedMVCPRecalculate = requestAnimationFrame(() => {
5902
- state.queuedMVCPRecalculate = void 0;
5903
- calculateItemsInView(ctx, { doMVCP: true });
5904
- });
5905
5914
  }
5906
5915
  }
5907
5916
  function updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal) {
@@ -5917,8 +5926,8 @@ function updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal) {
5917
5926
  function updateItemSize(ctx, itemKey, sizeObj) {
5918
5927
  var _a3;
5919
5928
  const state = ctx.state;
5920
- const userScrollAnchorResetKeys = state.userScrollAnchorResetKeys;
5921
- const didMeasureUserScrollAnchorResetItem = !!(userScrollAnchorResetKeys == null ? void 0 : userScrollAnchorResetKeys.delete(itemKey));
5929
+ const userScrollAnchorReset = state.userScrollAnchorReset;
5930
+ const didMeasureUserScrollAnchorResetItem = !!(userScrollAnchorReset == null ? void 0 : userScrollAnchorReset.keys.delete(itemKey));
5922
5931
  const {
5923
5932
  didContainersLayout,
5924
5933
  sizesKnown,
@@ -5974,8 +5983,8 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5974
5983
  if (needsRecalculate) {
5975
5984
  state.scrollForNextCalculateItemsInView = void 0;
5976
5985
  runOrScheduleMVCPRecalculate(ctx);
5977
- } else if (didMeasureUserScrollAnchorResetItem && (userScrollAnchorResetKeys == null ? void 0 : userScrollAnchorResetKeys.size) === 0) {
5978
- state.userScrollAnchorResetKeys = void 0;
5986
+ } else if (didMeasureUserScrollAnchorResetItem && (userScrollAnchorReset == null ? void 0 : userScrollAnchorReset.keys.size) === 0) {
5987
+ state.userScrollAnchorReset = void 0;
5979
5988
  }
5980
5989
  if (shouldMaintainScrollAtEnd) {
5981
5990
  if (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onItemLayout) {
@@ -5985,7 +5994,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5985
5994
  }
5986
5995
  }
5987
5996
  function updateOneItemSize(ctx, itemKey, sizeObj) {
5988
- var _a3;
5997
+ var _a3, _b;
5989
5998
  const state = ctx.state;
5990
5999
  const {
5991
6000
  indexByKey,
@@ -5995,13 +6004,20 @@ function updateOneItemSize(ctx, itemKey, sizeObj) {
5995
6004
  } = state;
5996
6005
  if (!data) return 0;
5997
6006
  const index = indexByKey.get(itemKey);
5998
- const prevSize = getItemSize(ctx, itemKey, index, data[index]);
6007
+ const itemData = data[index];
6008
+ let itemType;
6009
+ let fixedItemSize;
6010
+ if (getFixedItemSize) {
6011
+ itemType = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
6012
+ fixedItemSize = getFixedItemSize(itemData, index, itemType);
6013
+ }
6014
+ const prevSize = getItemSize(ctx, itemKey, index, itemData);
5999
6015
  const rawSize = horizontal ? sizeObj.width : sizeObj.height;
6000
- const size = Math.round(rawSize) ;
6001
6016
  const prevSizeKnown = sizesKnown.get(itemKey);
6017
+ const size = Math.round(rawSize) ;
6002
6018
  sizesKnown.set(itemKey, size);
6003
- if (!getFixedItemSize && size > 0) {
6004
- const itemType = getItemType ? (_a3 = getItemType(data[index], index)) != null ? _a3 : "" : "";
6019
+ if (fixedItemSize === void 0 && size > 0) {
6020
+ itemType != null ? itemType : itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
6005
6021
  let averages = averageSizes[itemType];
6006
6022
  if (!averages) {
6007
6023
  averages = averageSizes[itemType] = { avg: 0, num: 0 };
package/react.mjs CHANGED
@@ -3085,7 +3085,7 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
3085
3085
  if (scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength && !state.pendingNativeMVCPAdjust) {
3086
3086
  state.mvcpAnchorLock = void 0;
3087
3087
  state.pendingNativeMVCPAdjust = void 0;
3088
- state.userScrollAnchorResetKeys = /* @__PURE__ */ new Set();
3088
+ state.userScrollAnchorReset = { keys: /* @__PURE__ */ new Set() };
3089
3089
  if (state.queuedMVCPRecalculate !== void 0) {
3090
3090
  cancelAnimationFrame(state.queuedMVCPRecalculate);
3091
3091
  state.queuedMVCPRecalculate = void 0;
@@ -5048,7 +5048,7 @@ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentSt
5048
5048
  function calculateItemsInView(ctx, params = {}) {
5049
5049
  const state = ctx.state;
5050
5050
  batchedUpdates(() => {
5051
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q;
5051
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
5052
5052
  const {
5053
5053
  columns,
5054
5054
  containerItemKeys,
@@ -5362,7 +5362,7 @@ function calculateItemsInView(ctx, params = {}) {
5362
5362
  state.containerItemTypes.set(containerIndex, requiredItemTypes[idx]);
5363
5363
  }
5364
5364
  containerItemKeys.set(id, containerIndex);
5365
- (_o = state.userScrollAnchorResetKeys) == null ? void 0 : _o.add(id);
5365
+ (_o = state.userScrollAnchorReset) == null ? void 0 : _o.keys.add(id);
5366
5366
  const containerSticky = `containerSticky${containerIndex}`;
5367
5367
  const isSticky = stickyHeaderIndicesSet.has(i);
5368
5368
  const isAlwaysRender = alwaysRenderSet.has(i);
@@ -5390,13 +5390,17 @@ function calculateItemsInView(ctx, params = {}) {
5390
5390
  }
5391
5391
  }
5392
5392
  }
5393
- if (((_p = state.userScrollAnchorResetKeys) == null ? void 0 : _p.size) === 0) {
5394
- state.userScrollAnchorResetKeys = void 0;
5393
+ if (state.userScrollAnchorReset) {
5394
+ if (state.userScrollAnchorReset.keys.size === 0) {
5395
+ state.userScrollAnchorReset = void 0;
5396
+ } else {
5397
+ state.userScrollAnchorReset.batchSize = state.userScrollAnchorReset.keys.size;
5398
+ }
5395
5399
  }
5396
5400
  if (alwaysRenderArr.length > 0) {
5397
5401
  for (const index of alwaysRenderArr) {
5398
5402
  if (index < 0 || index >= dataLength) continue;
5399
- const id = (_q = idCache[index]) != null ? _q : getId(state, index);
5403
+ const id = (_p = idCache[index]) != null ? _p : getId(state, index);
5400
5404
  const containerIndex = containerItemKeys.get(id);
5401
5405
  if (containerIndex !== void 0) {
5402
5406
  state.stickyContainerPool.add(containerIndex);
@@ -5850,37 +5854,42 @@ function updateContentInsetEndAdjustment(ctx, previousContentInsetEndAdjustment)
5850
5854
 
5851
5855
  // src/core/updateItemSize.ts
5852
5856
  function runOrScheduleMVCPRecalculate(ctx) {
5857
+ var _a3, _b;
5853
5858
  const state = ctx.state;
5854
- if (state.userScrollAnchorResetKeys !== void 0) {
5855
- if (state.queuedMVCPRecalculate !== void 0) {
5856
- return;
5857
- }
5858
- state.queuedMVCPRecalculate = requestAnimationFrame(() => {
5859
- var _a3;
5860
- state.queuedMVCPRecalculate = void 0;
5859
+ if (state.userScrollAnchorReset !== void 0) {
5860
+ const replacementBatchSize = (_a3 = state.userScrollAnchorReset.batchSize) != null ? _a3 : state.userScrollAnchorReset.keys.size;
5861
+ const replacementMeasurementBatchThreshold = 3;
5862
+ const shouldBatchReplacementMeasurements = replacementBatchSize > replacementMeasurementBatchThreshold;
5863
+ if (shouldBatchReplacementMeasurements) {
5864
+ if (state.queuedMVCPRecalculate === void 0) {
5865
+ state.queuedMVCPRecalculate = requestAnimationFrame(() => {
5866
+ var _a4;
5867
+ state.queuedMVCPRecalculate = void 0;
5868
+ calculateItemsInView(ctx);
5869
+ if (((_a4 = state.userScrollAnchorReset) == null ? void 0 : _a4.keys.size) === 0) {
5870
+ state.userScrollAnchorReset = void 0;
5871
+ }
5872
+ });
5873
+ }
5874
+ } else {
5861
5875
  calculateItemsInView(ctx);
5862
- if (((_a3 = state.userScrollAnchorResetKeys) == null ? void 0 : _a3.size) === 0) {
5863
- state.userScrollAnchorResetKeys = void 0;
5876
+ if (((_b = state.userScrollAnchorReset) == null ? void 0 : _b.keys.size) === 0) {
5877
+ state.userScrollAnchorReset = void 0;
5864
5878
  }
5865
- });
5866
- return;
5867
- }
5868
- {
5879
+ }
5880
+ } else {
5869
5881
  if (!state.mvcpAnchorLock) {
5870
5882
  if (state.queuedMVCPRecalculate !== void 0) {
5871
5883
  cancelAnimationFrame(state.queuedMVCPRecalculate);
5872
5884
  state.queuedMVCPRecalculate = void 0;
5873
5885
  }
5874
5886
  calculateItemsInView(ctx, { doMVCP: true });
5875
- return;
5876
- }
5877
- if (state.queuedMVCPRecalculate !== void 0) {
5878
- return;
5887
+ } else if (state.queuedMVCPRecalculate === void 0) {
5888
+ state.queuedMVCPRecalculate = requestAnimationFrame(() => {
5889
+ state.queuedMVCPRecalculate = void 0;
5890
+ calculateItemsInView(ctx, { doMVCP: true });
5891
+ });
5879
5892
  }
5880
- state.queuedMVCPRecalculate = requestAnimationFrame(() => {
5881
- state.queuedMVCPRecalculate = void 0;
5882
- calculateItemsInView(ctx, { doMVCP: true });
5883
- });
5884
5893
  }
5885
5894
  }
5886
5895
  function updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal) {
@@ -5896,8 +5905,8 @@ function updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal) {
5896
5905
  function updateItemSize(ctx, itemKey, sizeObj) {
5897
5906
  var _a3;
5898
5907
  const state = ctx.state;
5899
- const userScrollAnchorResetKeys = state.userScrollAnchorResetKeys;
5900
- const didMeasureUserScrollAnchorResetItem = !!(userScrollAnchorResetKeys == null ? void 0 : userScrollAnchorResetKeys.delete(itemKey));
5908
+ const userScrollAnchorReset = state.userScrollAnchorReset;
5909
+ const didMeasureUserScrollAnchorResetItem = !!(userScrollAnchorReset == null ? void 0 : userScrollAnchorReset.keys.delete(itemKey));
5901
5910
  const {
5902
5911
  didContainersLayout,
5903
5912
  sizesKnown,
@@ -5953,8 +5962,8 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5953
5962
  if (needsRecalculate) {
5954
5963
  state.scrollForNextCalculateItemsInView = void 0;
5955
5964
  runOrScheduleMVCPRecalculate(ctx);
5956
- } else if (didMeasureUserScrollAnchorResetItem && (userScrollAnchorResetKeys == null ? void 0 : userScrollAnchorResetKeys.size) === 0) {
5957
- state.userScrollAnchorResetKeys = void 0;
5965
+ } else if (didMeasureUserScrollAnchorResetItem && (userScrollAnchorReset == null ? void 0 : userScrollAnchorReset.keys.size) === 0) {
5966
+ state.userScrollAnchorReset = void 0;
5958
5967
  }
5959
5968
  if (shouldMaintainScrollAtEnd) {
5960
5969
  if (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onItemLayout) {
@@ -5964,7 +5973,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
5964
5973
  }
5965
5974
  }
5966
5975
  function updateOneItemSize(ctx, itemKey, sizeObj) {
5967
- var _a3;
5976
+ var _a3, _b;
5968
5977
  const state = ctx.state;
5969
5978
  const {
5970
5979
  indexByKey,
@@ -5974,13 +5983,20 @@ function updateOneItemSize(ctx, itemKey, sizeObj) {
5974
5983
  } = state;
5975
5984
  if (!data) return 0;
5976
5985
  const index = indexByKey.get(itemKey);
5977
- const prevSize = getItemSize(ctx, itemKey, index, data[index]);
5986
+ const itemData = data[index];
5987
+ let itemType;
5988
+ let fixedItemSize;
5989
+ if (getFixedItemSize) {
5990
+ itemType = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
5991
+ fixedItemSize = getFixedItemSize(itemData, index, itemType);
5992
+ }
5993
+ const prevSize = getItemSize(ctx, itemKey, index, itemData);
5978
5994
  const rawSize = horizontal ? sizeObj.width : sizeObj.height;
5979
- const size = Math.round(rawSize) ;
5980
5995
  const prevSizeKnown = sizesKnown.get(itemKey);
5996
+ const size = Math.round(rawSize) ;
5981
5997
  sizesKnown.set(itemKey, size);
5982
- if (!getFixedItemSize && size > 0) {
5983
- const itemType = getItemType ? (_a3 = getItemType(data[index], index)) != null ? _a3 : "" : "";
5998
+ if (fixedItemSize === void 0 && size > 0) {
5999
+ itemType != null ? itemType : itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
5984
6000
  let averages = averageSizes[itemType];
5985
6001
  if (!averages) {
5986
6002
  averages = averageSizes[itemType] = { avg: 0, num: 0 };