@legendapp/list 2.0.0-next.9 → 2.0.1

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/index.js CHANGED
@@ -24,15 +24,17 @@ function _interopNamespace(e) {
24
24
 
25
25
  var React3__namespace = /*#__PURE__*/_interopNamespace(React3);
26
26
 
27
- // src/components/LazyLegendList.tsx
27
+ // src/components/LegendList.tsx
28
28
  var ContextState = React3__namespace.createContext(null);
29
29
  function StateProvider({ children }) {
30
30
  const [value] = React3__namespace.useState(() => ({
31
+ animatedScrollY: new reactNative.Animated.Value(0),
31
32
  columnWrapperStyle: void 0,
32
33
  listeners: /* @__PURE__ */ new Map(),
33
34
  mapViewabilityAmountCallbacks: /* @__PURE__ */ new Map(),
34
35
  mapViewabilityAmountValues: /* @__PURE__ */ new Map(),
35
36
  mapViewabilityCallbacks: /* @__PURE__ */ new Map(),
37
+ mapViewabilityConfigStates: /* @__PURE__ */ new Map(),
36
38
  mapViewabilityValues: /* @__PURE__ */ new Map(),
37
39
  values: /* @__PURE__ */ new Map([
38
40
  ["alignItemsPaddingTop", 0],
@@ -185,18 +187,127 @@ var LeanViewComponent = React3__namespace.forwardRef((props, ref) => {
185
187
  LeanViewComponent.displayName = "RCTView";
186
188
  var LeanView = reactNative.Platform.OS === "android" || reactNative.Platform.OS === "ios" ? LeanViewComponent : reactNative.View;
187
189
 
188
- // src/components/Separator.tsx
189
- function Separator({ ItemSeparatorComponent, itemKey, leadingItem }) {
190
- const [lastItemKeys] = useArr$(["lastItemKeys"]);
191
- const isALastItem = lastItemKeys.includes(itemKey);
192
- return isALastItem ? null : /* @__PURE__ */ React.createElement(ItemSeparatorComponent, { leadingItem });
193
- }
194
-
195
190
  // src/constants.ts
196
191
  var POSITION_OUT_OF_VIEW = -1e7;
197
192
  var ENABLE_DEVMODE = __DEV__ && false;
198
193
  var ENABLE_DEBUG_VIEW = __DEV__ && false;
199
194
  var IsNewArchitecture = global.nativeFabricUIManager != null;
195
+ var useAnimatedValue = (initialValue) => {
196
+ return React3.useRef(new reactNative.Animated.Value(initialValue)).current;
197
+ };
198
+
199
+ // src/hooks/useValue$.ts
200
+ function useValue$(key, params) {
201
+ var _a;
202
+ const { getValue, delay } = params || {};
203
+ const ctx = useStateContext();
204
+ const animValue = useAnimatedValue((_a = getValue ? getValue(peek$(ctx, key)) : peek$(ctx, key)) != null ? _a : 0);
205
+ React3.useMemo(() => {
206
+ let newValue;
207
+ let prevValue;
208
+ let didQueueTask = false;
209
+ listen$(ctx, key, (v) => {
210
+ newValue = getValue ? getValue(v) : v;
211
+ if (delay !== void 0) {
212
+ const fn = () => {
213
+ didQueueTask = false;
214
+ if (newValue !== void 0) {
215
+ animValue.setValue(newValue);
216
+ }
217
+ };
218
+ const delayValue = typeof delay === "function" ? delay(newValue, prevValue) : delay;
219
+ prevValue = newValue;
220
+ if (!didQueueTask) {
221
+ didQueueTask = true;
222
+ if (delayValue === 0) {
223
+ queueMicrotask(fn);
224
+ } else {
225
+ setTimeout(fn, delayValue);
226
+ }
227
+ }
228
+ } else {
229
+ animValue.setValue(newValue);
230
+ }
231
+ });
232
+ }, []);
233
+ return animValue;
234
+ }
235
+ var typedForwardRef = React3.forwardRef;
236
+ var typedMemo = React3.memo;
237
+
238
+ // src/components/PositionView.tsx
239
+ var PositionViewState = typedMemo(function PositionView({
240
+ id,
241
+ horizontal,
242
+ style,
243
+ refView,
244
+ ...rest
245
+ }) {
246
+ const [position = POSITION_OUT_OF_VIEW] = useArr$([`containerPosition${id}`]);
247
+ return /* @__PURE__ */ React3__namespace.createElement(
248
+ LeanView,
249
+ {
250
+ ref: refView,
251
+ style: [
252
+ style,
253
+ horizontal ? { transform: [{ translateX: position }] } : { transform: [{ translateY: position }] }
254
+ ],
255
+ ...rest
256
+ }
257
+ );
258
+ });
259
+ var PositionViewAnimated = typedMemo(function PositionView2({
260
+ id,
261
+ horizontal,
262
+ style,
263
+ refView,
264
+ ...rest
265
+ }) {
266
+ const position$ = useValue$(`containerPosition${id}`, {
267
+ getValue: (v) => v != null ? v : POSITION_OUT_OF_VIEW
268
+ });
269
+ return /* @__PURE__ */ React3__namespace.createElement(
270
+ reactNative.Animated.View,
271
+ {
272
+ ref: refView,
273
+ style: [
274
+ style,
275
+ horizontal ? { transform: [{ translateX: position$ }] } : { transform: [{ translateY: position$ }] }
276
+ ],
277
+ ...rest
278
+ }
279
+ );
280
+ });
281
+ var PositionViewSticky = typedMemo(function PositionViewSticky2({
282
+ id,
283
+ horizontal,
284
+ style,
285
+ refView,
286
+ animatedScrollY,
287
+ stickyOffset,
288
+ index,
289
+ ...rest
290
+ }) {
291
+ const [position = POSITION_OUT_OF_VIEW, headerSize] = useArr$([`containerPosition${id}`, "headerSize"]);
292
+ const transform = React3__namespace.useMemo(() => {
293
+ if (animatedScrollY && stickyOffset) {
294
+ const stickyPosition = animatedScrollY.interpolate({
295
+ extrapolate: "clamp",
296
+ inputRange: [position + headerSize, position + 5e3 + headerSize],
297
+ outputRange: [position, position + 5e3]
298
+ });
299
+ return horizontal ? [{ translateX: stickyPosition }] : [{ translateY: stickyPosition }];
300
+ }
301
+ }, [animatedScrollY, headerSize, horizontal, stickyOffset, position]);
302
+ const viewStyle = React3__namespace.useMemo(() => [style, { zIndex: index + 1e3 }, { transform }], [style, transform]);
303
+ return /* @__PURE__ */ React3__namespace.createElement(reactNative.Animated.View, { ref: refView, style: viewStyle, ...rest });
304
+ });
305
+ var PositionView3 = IsNewArchitecture ? PositionViewState : PositionViewAnimated;
306
+ function Separator({ ItemSeparatorComponent, itemKey, leadingItem }) {
307
+ const [lastItemKeys] = useArr$(["lastItemKeys"]);
308
+ const isALastItem = lastItemKeys.includes(itemKey);
309
+ return isALastItem ? null : /* @__PURE__ */ React3__namespace.createElement(ItemSeparatorComponent, { leadingItem });
310
+ }
200
311
  var symbolFirst = Symbol();
201
312
  function useInit(cb) {
202
313
  const refValue = React3.useRef(symbolFirst);
@@ -333,8 +444,16 @@ function useListScrollSize() {
333
444
  const [scrollSize] = useArr$(["scrollSize"]);
334
445
  return scrollSize;
335
446
  }
336
- var typedForwardRef = React3.forwardRef;
337
- var typedMemo = React3.memo;
447
+ var noop = () => {
448
+ };
449
+ function useSyncLayout() {
450
+ if (IsNewArchitecture) {
451
+ const { triggerLayout: syncLayout } = React3.useContext(ContextContainer);
452
+ return syncLayout;
453
+ } else {
454
+ return noop;
455
+ }
456
+ }
338
457
 
339
458
  // src/components/Container.tsx
340
459
  var Container = typedMemo(function Container2({
@@ -346,14 +465,15 @@ var Container = typedMemo(function Container2({
346
465
  ItemSeparatorComponent
347
466
  }) {
348
467
  const ctx = useStateContext();
349
- const columnWrapperStyle = ctx.columnWrapperStyle;
350
- const [column = 0, data, itemKey, position = POSITION_OUT_OF_VIEW, numColumns, extraData] = useArr$([
468
+ const { columnWrapperStyle, animatedScrollY } = ctx;
469
+ const [column = 0, data, itemKey, numColumns, extraData, isSticky, stickyOffset] = useArr$([
351
470
  `containerColumn${id}`,
352
471
  `containerItemData${id}`,
353
472
  `containerItemKey${id}`,
354
- `containerPosition${id}`,
355
473
  "numColumns",
356
- "extraData"
474
+ "extraData",
475
+ `containerSticky${id}`,
476
+ `containerStickyOffset${id}`
357
477
  ]);
358
478
  const refLastSize = React3.useRef();
359
479
  const ref = React3.useRef(null);
@@ -361,36 +481,38 @@ var Container = typedMemo(function Container2({
361
481
  const otherAxisPos = numColumns > 1 ? `${(column - 1) / numColumns * 100}%` : 0;
362
482
  const otherAxisSize = numColumns > 1 ? `${1 / numColumns * 100}%` : void 0;
363
483
  let didLayout = false;
364
- let paddingStyles;
365
- if (columnWrapperStyle) {
366
- const { columnGap, rowGap, gap } = columnWrapperStyle;
367
- if (horizontal) {
368
- paddingStyles = {
369
- paddingRight: columnGap || gap || void 0,
370
- paddingVertical: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
371
- };
372
- } else {
373
- paddingStyles = {
374
- paddingBottom: rowGap || gap || void 0,
375
- paddingHorizontal: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
376
- };
484
+ const style = React3.useMemo(() => {
485
+ let paddingStyles;
486
+ if (columnWrapperStyle) {
487
+ const { columnGap, rowGap, gap } = columnWrapperStyle;
488
+ if (horizontal) {
489
+ paddingStyles = {
490
+ paddingRight: columnGap || gap || void 0,
491
+ paddingVertical: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
492
+ };
493
+ } else {
494
+ paddingStyles = {
495
+ paddingBottom: rowGap || gap || void 0,
496
+ paddingHorizontal: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
497
+ };
498
+ }
377
499
  }
378
- }
379
- const style = horizontal ? {
380
- flexDirection: ItemSeparatorComponent ? "row" : void 0,
381
- height: otherAxisSize,
382
- left: position,
383
- position: "absolute",
384
- top: otherAxisPos,
385
- ...paddingStyles || {}
386
- } : {
387
- left: otherAxisPos,
388
- position: "absolute",
389
- right: numColumns > 1 ? null : 0,
390
- top: position,
391
- width: otherAxisSize,
392
- ...paddingStyles || {}
393
- };
500
+ return horizontal ? {
501
+ flexDirection: ItemSeparatorComponent ? "row" : void 0,
502
+ height: otherAxisSize,
503
+ left: 0,
504
+ position: "absolute",
505
+ top: otherAxisPos,
506
+ ...paddingStyles || {}
507
+ } : {
508
+ left: otherAxisPos,
509
+ position: "absolute",
510
+ right: numColumns > 1 ? null : 0,
511
+ top: 0,
512
+ width: otherAxisSize,
513
+ ...paddingStyles || {}
514
+ };
515
+ }, [horizontal, otherAxisPos, otherAxisSize, columnWrapperStyle, numColumns]);
394
516
  const renderedItemInfo = React3.useMemo(
395
517
  () => itemKey !== void 0 ? getRenderedItem2(itemKey) : null,
396
518
  [itemKey, data, extraData]
@@ -455,55 +577,30 @@ var Container = typedMemo(function Container2({
455
577
  }
456
578
  }, [itemKey]);
457
579
  }
458
- return /* @__PURE__ */ React3__namespace.createElement(LeanView, { key: recycleItems ? void 0 : itemKey, onLayout, ref, style }, /* @__PURE__ */ React3__namespace.createElement(ContextContainer.Provider, { value: contextValue }, renderedItem, renderedItemInfo && ItemSeparatorComponent && /* @__PURE__ */ React3__namespace.createElement(
459
- Separator,
580
+ const PositionComponent = isSticky ? PositionViewSticky : PositionView3;
581
+ return /* @__PURE__ */ React3__namespace.createElement(
582
+ PositionComponent,
460
583
  {
461
- ItemSeparatorComponent,
462
- itemKey,
463
- leadingItem: renderedItemInfo.item
464
- }
465
- )));
466
- });
467
- var useAnimatedValue = (initialValue) => {
468
- return React3.useRef(new reactNative.Animated.Value(initialValue)).current;
469
- };
470
-
471
- // src/hooks/useValue$.ts
472
- function useValue$(key, params) {
473
- var _a;
474
- const { getValue, delay } = params || {};
475
- const ctx = useStateContext();
476
- const animValue = useAnimatedValue((_a = getValue ? getValue(peek$(ctx, key)) : peek$(ctx, key)) != null ? _a : 0);
477
- React3.useMemo(() => {
478
- let newValue;
479
- let prevValue;
480
- let didQueueTask = false;
481
- listen$(ctx, key, (v) => {
482
- newValue = getValue ? getValue(v) : v;
483
- if (delay !== void 0) {
484
- const fn = () => {
485
- didQueueTask = false;
486
- if (newValue !== void 0) {
487
- animValue.setValue(newValue);
488
- }
489
- };
490
- const delayValue = typeof delay === "function" ? delay(newValue, prevValue) : delay;
491
- prevValue = newValue;
492
- if (!didQueueTask) {
493
- didQueueTask = true;
494
- if (delayValue === 0) {
495
- queueMicrotask(fn);
496
- } else {
497
- setTimeout(fn, delayValue);
498
- }
499
- }
500
- } else {
501
- animValue.setValue(newValue);
584
+ animatedScrollY: isSticky ? animatedScrollY : void 0,
585
+ horizontal,
586
+ id,
587
+ index,
588
+ key: recycleItems ? void 0 : itemKey,
589
+ onLayout,
590
+ refView: ref,
591
+ stickyOffset: isSticky ? stickyOffset : void 0,
592
+ style
593
+ },
594
+ /* @__PURE__ */ React3__namespace.createElement(ContextContainer.Provider, { value: contextValue }, renderedItem, renderedItemInfo && ItemSeparatorComponent && /* @__PURE__ */ React3__namespace.createElement(
595
+ Separator,
596
+ {
597
+ ItemSeparatorComponent,
598
+ itemKey,
599
+ leadingItem: renderedItemInfo.item
502
600
  }
503
- });
504
- }, []);
505
- return animValue;
506
- }
601
+ ))
602
+ );
603
+ });
507
604
 
508
605
  // src/components/Containers.tsx
509
606
  var Containers = typedMemo(function Containers2({
@@ -586,7 +683,48 @@ function SnapWrapper({ ScrollComponent, ...props }) {
586
683
  const [snapToOffsets] = useArr$(["snapToOffsets"]);
587
684
  return /* @__PURE__ */ React.createElement(ScrollComponent, { ...props, snapToOffsets });
588
685
  }
589
- function useSyncLayout({
686
+ function useThrottleDebounce(mode) {
687
+ const timeoutRef = React3.useRef(null);
688
+ const lastCallTimeRef = React3.useRef(0);
689
+ const lastArgsRef = React3.useRef(null);
690
+ const clearTimeoutRef = () => {
691
+ if (timeoutRef.current) {
692
+ clearTimeout(timeoutRef.current);
693
+ timeoutRef.current = null;
694
+ }
695
+ };
696
+ const execute = React3.useCallback(
697
+ (callback, delay, ...args) => {
698
+ {
699
+ const now = Date.now();
700
+ lastArgsRef.current = args;
701
+ if (now - lastCallTimeRef.current >= delay) {
702
+ lastCallTimeRef.current = now;
703
+ callback(...args);
704
+ clearTimeoutRef();
705
+ } else {
706
+ clearTimeoutRef();
707
+ timeoutRef.current = setTimeout(
708
+ () => {
709
+ if (lastArgsRef.current) {
710
+ lastCallTimeRef.current = Date.now();
711
+ callback(...lastArgsRef.current);
712
+ timeoutRef.current = null;
713
+ lastArgsRef.current = null;
714
+ }
715
+ },
716
+ delay - (now - lastCallTimeRef.current)
717
+ );
718
+ }
719
+ }
720
+ },
721
+ [mode]
722
+ );
723
+ return execute;
724
+ }
725
+
726
+ // src/hooks/useSyncLayout.tsx
727
+ function useSyncLayout2({
590
728
  onChange
591
729
  }) {
592
730
  const ref = React3.useRef(null);
@@ -663,16 +801,17 @@ var ListComponent = typedMemo(function ListComponent2({
663
801
  scrollAdjustHandler,
664
802
  onLayoutHeader,
665
803
  snapToIndices,
804
+ stickyIndices,
666
805
  ...rest
667
806
  }) {
668
807
  const ctx = useStateContext();
669
- const { onLayout: onLayoutHeaderSync, ref: refHeader } = useSyncLayout({
808
+ const { onLayout: onLayoutHeaderSync, ref: refHeader } = useSyncLayout2({
670
809
  onChange: onLayoutHeader
671
810
  });
672
811
  const ScrollComponent = renderScrollComponent ? React3.useMemo(
673
812
  () => React3__namespace.forwardRef((props, ref) => renderScrollComponent({ ...props, ref })),
674
813
  [renderScrollComponent]
675
- ) : reactNative.ScrollView;
814
+ ) : reactNative.Animated.ScrollView;
676
815
  React3__namespace.useEffect(() => {
677
816
  if (canRender) {
678
817
  setTimeout(() => {
@@ -725,9 +864,26 @@ var ListComponent = typedMemo(function ListComponent2({
725
864
  style: ListFooterComponentStyle
726
865
  },
727
866
  getComponent(ListFooterComponent)
728
- )
867
+ ),
868
+ __DEV__ && ENABLE_DEVMODE && /* @__PURE__ */ React3__namespace.createElement(DevNumbers, null)
729
869
  );
730
870
  });
871
+ var DevNumbers = __DEV__ && React3__namespace.memo(function DevNumbers2() {
872
+ return Array.from({ length: 100 }).map((_, index) => /* @__PURE__ */ React3__namespace.createElement(
873
+ reactNative.View,
874
+ {
875
+ key: index,
876
+ style: {
877
+ height: 100,
878
+ pointerEvents: "none",
879
+ position: "absolute",
880
+ top: index * 100,
881
+ width: "100%"
882
+ }
883
+ },
884
+ /* @__PURE__ */ React3__namespace.createElement(reactNative.Text, { style: { color: "red" } }, index * 100)
885
+ ));
886
+ });
731
887
 
732
888
  // src/utils/getId.ts
733
889
  function getId(state, index) {
@@ -741,38 +897,33 @@ function getId(state, index) {
741
897
  return id;
742
898
  }
743
899
 
744
- // src/core/calculateOffsetForIndex.ts
745
- function calculateOffsetForIndex(ctx, state, index) {
746
- let position = 0;
747
- if (index !== void 0) {
748
- position = (state == null ? void 0 : state.positions.get(getId(state, index))) || 0;
749
- }
750
- const paddingTop = peek$(ctx, "stylePaddingTop");
751
- if (paddingTop) {
752
- position += paddingTop;
753
- }
754
- const headerSize = peek$(ctx, "headerSize");
755
- if (headerSize) {
756
- position += headerSize;
757
- }
758
- return position;
759
- }
760
-
761
900
  // src/utils/getItemSize.ts
762
901
  function getItemSize(state, key, index, data, useAverageSize) {
902
+ var _a, _b;
763
903
  const {
764
904
  sizesKnown,
765
905
  sizes,
766
906
  scrollingTo,
767
- props: { estimatedItemSize, getEstimatedItemSize }
907
+ averageSizes,
908
+ props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType }
768
909
  } = state;
769
910
  const sizeKnown = sizesKnown.get(key);
770
911
  if (sizeKnown !== void 0) {
771
912
  return sizeKnown;
772
913
  }
773
914
  let size;
774
- if (useAverageSize !== void 0 && sizeKnown === void 0 && !getEstimatedItemSize && !scrollingTo) {
775
- size = useAverageSize;
915
+ const itemType = getItemType ? (_a = getItemType(data, index)) != null ? _a : "" : "";
916
+ if (getFixedItemSize) {
917
+ size = getFixedItemSize(index, data, itemType);
918
+ if (size !== void 0) {
919
+ sizesKnown.set(key, size);
920
+ }
921
+ }
922
+ if (size === void 0 && useAverageSize && sizeKnown === void 0 && !scrollingTo) {
923
+ const averageSizeForType = (_b = averageSizes[itemType]) == null ? void 0 : _b.avg;
924
+ if (averageSizeForType !== void 0) {
925
+ size = roundSize(averageSizeForType);
926
+ }
776
927
  }
777
928
  if (size === void 0) {
778
929
  size = sizes.get(key);
@@ -781,103 +932,21 @@ function getItemSize(state, key, index, data, useAverageSize) {
781
932
  }
782
933
  }
783
934
  if (size === void 0) {
784
- size = getEstimatedItemSize ? getEstimatedItemSize(index, data) : estimatedItemSize;
935
+ size = getEstimatedItemSize ? getEstimatedItemSize(index, data, itemType) : estimatedItemSize;
785
936
  }
786
937
  sizes.set(key, size);
787
938
  return size;
788
939
  }
789
940
 
790
- // src/core/calculateOffsetWithOffsetPosition.ts
791
- function calculateOffsetWithOffsetPosition(state, offsetParam, params) {
792
- const { index, viewOffset, viewPosition } = params;
793
- let offset = offsetParam;
794
- if (viewOffset) {
795
- offset -= viewOffset;
796
- }
797
- if (viewPosition !== void 0 && index !== void 0) {
798
- offset -= viewPosition * (state.scrollLength - getItemSize(state, getId(state, index), index, state.props.data[index]));
799
- }
800
- return offset;
801
- }
802
-
803
- // src/utils/requestAdjust.ts
804
- function requestAdjust(ctx, state, positionDiff) {
805
- if (Math.abs(positionDiff) > 0.1) {
806
- const doit = () => {
807
- state.scrollAdjustHandler.requestAdjust(positionDiff);
808
- };
809
- state.scroll += positionDiff;
810
- state.scrollForNextCalculateItemsInView = void 0;
811
- const didLayout = peek$(ctx, "containersDidLayout");
812
- if (didLayout) {
813
- doit();
814
- const threshold = state.scroll - positionDiff / 2;
815
- if (!state.ignoreScrollFromMVCP) {
816
- state.ignoreScrollFromMVCP = {};
817
- }
818
- if (positionDiff > 0) {
819
- state.ignoreScrollFromMVCP.lt = threshold;
820
- } else {
821
- state.ignoreScrollFromMVCP.gt = threshold;
822
- }
823
- if (state.ignoreScrollFromMVCPTimeout) {
824
- clearTimeout(state.ignoreScrollFromMVCPTimeout);
825
- }
826
- state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
827
- state.ignoreScrollFromMVCP = void 0;
828
- }, 100);
829
- } else {
830
- requestAnimationFrame(doit);
831
- }
832
- }
833
- }
834
-
835
- // src/core/prepareMVCP.ts
836
- function prepareMVCP(ctx, state) {
837
- const {
838
- positions,
839
- scrollingTo,
840
- props: { maintainVisibleContentPosition }
841
- } = state;
842
- let prevPosition;
843
- let targetId;
844
- let targetIndex;
845
- const scrollTarget = scrollingTo == null ? void 0 : scrollingTo.index;
846
- if (maintainVisibleContentPosition) {
847
- const indexByKey = state.indexByKey;
848
- if (scrollTarget !== void 0) {
849
- targetId = getId(state, scrollTarget);
850
- targetIndex = scrollTarget;
851
- } else if (state.idsInView.length > 0 && peek$(ctx, "containersDidLayout")) {
852
- targetId = state.idsInView.find((id) => indexByKey.get(id) !== void 0);
853
- targetIndex = indexByKey.get(targetId);
854
- }
855
- if (targetId !== void 0 && targetIndex !== void 0) {
856
- prevPosition = positions.get(targetId);
857
- }
858
- }
859
- return () => {
860
- if (targetId !== void 0 && prevPosition !== void 0) {
861
- const newPosition = positions.get(targetId);
862
- if (newPosition !== void 0) {
863
- const positionDiff = newPosition - prevPosition;
864
- if (Math.abs(positionDiff) > 0.1) {
865
- requestAdjust(ctx, state, positionDiff);
866
- }
867
- }
868
- }
869
- };
870
- }
871
-
872
941
  // src/utils/setPaddingTop.ts
873
942
  function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
874
943
  if (stylePaddingTop !== void 0) {
875
944
  const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
876
945
  if (stylePaddingTop < prevStylePaddingTop) {
877
- let prevTotalSize = peek$(ctx, "totalSize");
946
+ let prevTotalSize = peek$(ctx, "totalSize") || 0;
878
947
  set$(ctx, "totalSize", prevTotalSize + prevStylePaddingTop);
879
948
  state.timeoutSetPaddingTop = setTimeout(() => {
880
- prevTotalSize = peek$(ctx, "totalSize");
949
+ prevTotalSize = peek$(ctx, "totalSize") || 0;
881
950
  set$(ctx, "totalSize", prevTotalSize - prevStylePaddingTop);
882
951
  }, 16);
883
952
  }
@@ -907,15 +976,15 @@ function updateAlignItemsPaddingTop(ctx, state) {
907
976
  // src/core/updateTotalSize.ts
908
977
  function updateTotalSize(ctx, state) {
909
978
  const {
910
- positions,
911
979
  props: { data }
912
980
  } = state;
913
981
  if (data.length === 0) {
914
982
  addTotalSize(ctx, state, null, 0);
915
983
  } else {
916
- const lastId = getId(state, data.length - 1);
984
+ const lastIndex = data.length - 1;
985
+ const lastId = getId(state, lastIndex);
917
986
  if (lastId !== void 0) {
918
- const lastPosition = positions.get(lastId);
987
+ const lastPosition = getPositionByIndex(ctx, state, lastIndex);
919
988
  if (lastPosition !== void 0) {
920
989
  const lastSize = getItemSize(state, lastId, data.length - 1, data[data.length - 1]);
921
990
  if (lastSize !== void 0) {
@@ -941,122 +1010,102 @@ function addTotalSize(ctx, state, key, add) {
941
1010
  }
942
1011
  }
943
1012
 
944
- // src/utils/getScrollVelocity.ts
945
- var getScrollVelocity = (state) => {
946
- const { scrollHistory } = state;
947
- let velocity = 0;
948
- if (scrollHistory.length >= 1) {
949
- const newest = scrollHistory[scrollHistory.length - 1];
950
- let oldest;
951
- let start = 0;
952
- for (let i = 0; i < scrollHistory.length - 1; i++) {
953
- const entry = scrollHistory[i];
954
- const nextEntry = scrollHistory[i + 1];
955
- if (i > 0) {
956
- const prevEntry = scrollHistory[i - 1];
957
- const prevDirection = entry.scroll - prevEntry.scroll;
958
- const currentDirection = nextEntry.scroll - entry.scroll;
959
- if (prevDirection > 0 && currentDirection < 0 || prevDirection < 0 && currentDirection > 0) {
960
- start = i;
961
- break;
962
- }
963
- }
964
- }
965
- for (let i = start; i < scrollHistory.length - 1; i++) {
966
- const entry = scrollHistory[i];
967
- if (newest.time - entry.time <= 1e3) {
968
- oldest = entry;
969
- break;
970
- }
971
- }
972
- if (oldest) {
973
- const scrollDiff = newest.scroll - oldest.scroll;
974
- const timeDiff = newest.time - oldest.time;
975
- velocity = timeDiff > 0 ? scrollDiff / timeDiff : 0;
976
- }
977
- }
978
- return velocity;
979
- };
980
-
981
1013
  // src/utils/updateSnapToOffsets.ts
982
1014
  function updateSnapToOffsets(ctx, state) {
983
1015
  const {
984
- positions,
985
1016
  props: { snapToIndices }
986
1017
  } = state;
987
1018
  const snapToOffsets = Array(snapToIndices.length);
988
1019
  for (let i = 0; i < snapToIndices.length; i++) {
989
1020
  const idx = snapToIndices[i];
990
- const key = getId(state, idx);
991
- snapToOffsets[i] = positions.get(key);
1021
+ snapToOffsets[i] = getPositionByIndex(ctx, state, idx) || 0;
992
1022
  }
993
1023
  set$(ctx, "snapToOffsets", snapToOffsets);
994
1024
  }
995
1025
 
996
- // src/core/updateAllPositions.ts
997
- function updateAllPositions(ctx, state, dataChanged) {
998
- var _a, _b, _c, _d, _e;
1026
+ // src/core/updateItemPositions.ts
1027
+ function getRequiredRange(_ctx, state) {
1028
+ var _a;
1029
+ const bufferSize = 10;
1030
+ const dataLength = state.props.data.length;
1031
+ let minIndex = 0;
1032
+ let maxIndex = dataLength - 1;
1033
+ if (dataLength < 500) {
1034
+ return { end: maxIndex, start: 0 };
1035
+ }
1036
+ const hasVisibleRange = state.startBuffered >= 0 && state.endBuffered >= 0;
1037
+ if (hasVisibleRange) {
1038
+ minIndex = state.startBuffered;
1039
+ maxIndex = state.endBuffered;
1040
+ }
1041
+ if (((_a = state.scrollingTo) == null ? void 0 : _a.index) !== void 0) {
1042
+ if (hasVisibleRange) {
1043
+ minIndex = Math.min(minIndex, state.scrollingTo.index);
1044
+ maxIndex = Math.max(maxIndex, state.scrollingTo.index);
1045
+ } else {
1046
+ minIndex = state.scrollingTo.index;
1047
+ maxIndex = state.scrollingTo.index;
1048
+ }
1049
+ }
1050
+ minIndex = Math.max(0, minIndex - bufferSize);
1051
+ maxIndex = Math.min(dataLength - 1, maxIndex + bufferSize);
1052
+ return { end: maxIndex, start: minIndex };
1053
+ }
1054
+ function ensurePositionCalculated(ctx, state, index) {
1055
+ if (!state.positionRange) {
1056
+ state.positionRange = { end: -1, start: 0, valid: false };
1057
+ }
1058
+ if (state.positionRange.valid && index >= state.positionRange.start && index <= state.positionRange.end) {
1059
+ return;
1060
+ }
1061
+ const newStart = state.positionRange.valid ? Math.min(state.positionRange.start, index) : 0;
1062
+ const newEnd = Math.min(
1063
+ state.props.data.length - 1,
1064
+ Math.max(state.positionRange.valid ? state.positionRange.end : 0, index + 50)
1065
+ );
1066
+ updateItemPositions(ctx, state, false, newStart, newEnd);
1067
+ }
1068
+ function updateItemPositions(ctx, state, dataChanged, startIndex = 0, endIndex) {
1069
+ var _a, _b, _c, _d, _e, _f, _g;
999
1070
  const {
1000
- averageSizes,
1001
1071
  columns,
1002
1072
  indexByKey,
1003
1073
  positions,
1004
- firstFullyOnScreenIndex,
1005
1074
  idCache,
1006
1075
  sizesKnown,
1007
- props: { snapToIndices }
1076
+ props: { getEstimatedItemSize, snapToIndices, enableAverages }
1008
1077
  } = state;
1009
1078
  const data = state.props.data;
1010
1079
  const numColumns = peek$(ctx, "numColumns");
1011
1080
  const indexByKeyForChecking = __DEV__ ? /* @__PURE__ */ new Map() : void 0;
1012
- const scrollVelocity = getScrollVelocity(state);
1013
- if (dataChanged) {
1014
- indexByKey.clear();
1015
- idCache.clear();
1016
- }
1017
- const itemType = "";
1018
- let averageSize = (_a = averageSizes[itemType]) == null ? void 0 : _a.avg;
1019
- if (averageSize !== void 0) {
1020
- averageSize = roundSize(averageSize);
1021
- }
1022
- const shouldUseBackwards = !dataChanged && scrollVelocity < 0 && firstFullyOnScreenIndex > 5 && firstFullyOnScreenIndex < data.length;
1023
- if (shouldUseBackwards && firstFullyOnScreenIndex !== void 0) {
1024
- const anchorId = getId(state, firstFullyOnScreenIndex);
1025
- const anchorPosition = positions.get(anchorId);
1026
- if (anchorPosition !== void 0) {
1027
- let currentRowTop2 = anchorPosition;
1028
- let maxSizeInRow2 = 0;
1029
- let bailout = false;
1030
- for (let i = firstFullyOnScreenIndex - 1; i >= 0; i--) {
1031
- const id = (_b = idCache.get(i)) != null ? _b : getId(state, i);
1032
- const size = (_c = sizesKnown.get(id)) != null ? _c : getItemSize(state, id, i, data[i], averageSize);
1033
- const itemColumn = columns.get(id);
1034
- maxSizeInRow2 = Math.max(maxSizeInRow2, size);
1035
- if (itemColumn === 1) {
1036
- currentRowTop2 -= maxSizeInRow2;
1037
- maxSizeInRow2 = 0;
1038
- }
1039
- if (currentRowTop2 < -2e3) {
1040
- bailout = true;
1041
- break;
1042
- }
1043
- positions.set(id, currentRowTop2);
1044
- }
1045
- if (!bailout) {
1046
- updateTotalSize(ctx, state);
1047
- return;
1048
- }
1049
- }
1050
- }
1081
+ const useAverageSize = enableAverages && !getEstimatedItemSize;
1051
1082
  let currentRowTop = 0;
1052
1083
  let column = 1;
1053
1084
  let maxSizeInRow = 0;
1054
1085
  const hasColumns = numColumns > 1;
1086
+ if (startIndex > 0) {
1087
+ const prevIndex = startIndex - 1;
1088
+ const prevId = (_a = idCache.get(prevIndex)) != null ? _a : getId(state, prevIndex);
1089
+ const prevPosition = (_b = positions.get(prevId)) != null ? _b : 0;
1090
+ if (hasColumns) {
1091
+ const prevColumn = (_c = columns.get(prevId)) != null ? _c : 1;
1092
+ currentRowTop = prevPosition;
1093
+ column = prevColumn % numColumns + 1;
1094
+ } else {
1095
+ const prevSize = (_d = sizesKnown.get(prevId)) != null ? _d : getItemSize(state, prevId, prevIndex, data[prevIndex], useAverageSize);
1096
+ currentRowTop = prevPosition + prevSize;
1097
+ }
1098
+ }
1055
1099
  const needsIndexByKey = dataChanged || indexByKey.size === 0;
1056
1100
  const dataLength = data.length;
1057
- for (let i = 0; i < dataLength; i++) {
1058
- const id = (_d = idCache.get(i)) != null ? _d : getId(state, i);
1059
- const size = (_e = sizesKnown.get(id)) != null ? _e : getItemSize(state, id, i, data[i], averageSize);
1101
+ const requiredRange = getRequiredRange(ctx, state);
1102
+ const shouldOptimize = dataLength >= 500;
1103
+ const optimizedEndIndex = shouldOptimize ? Math.min(dataLength - 1, requiredRange.end) : dataLength - 1;
1104
+ const actualEndIndex = endIndex !== void 0 ? Math.min(endIndex, dataLength - 1) : optimizedEndIndex;
1105
+ let actualEndReached = startIndex;
1106
+ for (let i = startIndex; i < dataLength; i++) {
1107
+ const id = (_e = idCache.get(i)) != null ? _e : getId(state, i);
1108
+ const size = (_f = sizesKnown.get(id)) != null ? _f : getItemSize(state, id, i, data[i], useAverageSize);
1060
1109
  if (__DEV__ && needsIndexByKey) {
1061
1110
  if (indexByKeyForChecking.has(id)) {
1062
1111
  console.error(
@@ -1083,6 +1132,26 @@ function updateAllPositions(ctx, state, dataChanged) {
1083
1132
  } else {
1084
1133
  currentRowTop += size;
1085
1134
  }
1135
+ actualEndReached = i;
1136
+ if (shouldOptimize && i >= actualEndIndex && (!((_g = state.scrollingTo) == null ? void 0 : _g.index) || i >= state.scrollingTo.index)) {
1137
+ break;
1138
+ }
1139
+ }
1140
+ if (!state.positionRange) {
1141
+ state.positionRange = { end: -1, start: 0, valid: false };
1142
+ }
1143
+ if (dataChanged) {
1144
+ state.positionRange = {
1145
+ end: actualEndReached,
1146
+ start: startIndex,
1147
+ valid: true
1148
+ };
1149
+ } else {
1150
+ state.positionRange = {
1151
+ end: Math.max(state.positionRange.valid ? state.positionRange.end : actualEndReached, actualEndReached),
1152
+ start: Math.min(state.positionRange.valid ? state.positionRange.start : startIndex, startIndex),
1153
+ valid: true
1154
+ };
1086
1155
  }
1087
1156
  updateTotalSize(ctx, state);
1088
1157
  if (snapToIndices) {
@@ -1090,8 +1159,203 @@ function updateAllPositions(ctx, state, dataChanged) {
1090
1159
  }
1091
1160
  }
1092
1161
 
1162
+ // src/utils/getPosition.ts
1163
+ function getPositionByIndex(ctx, state, index) {
1164
+ ensurePositionCalculated(ctx, state, index);
1165
+ const id = getId(state, index);
1166
+ return id ? state.positions.get(id) : void 0;
1167
+ }
1168
+ function getPositionById(ctx, state, id) {
1169
+ const index = state.indexByKey.get(id);
1170
+ if (index === void 0) {
1171
+ return state.positions.get(id);
1172
+ }
1173
+ ensurePositionCalculated(ctx, state, index);
1174
+ return state.positions.get(id);
1175
+ }
1176
+
1177
+ // src/core/calculateOffsetForIndex.ts
1178
+ function calculateOffsetForIndex(ctx, state, index) {
1179
+ let position = 0;
1180
+ if (index !== void 0) {
1181
+ position = getPositionByIndex(ctx, state, index) || 0;
1182
+ const paddingTop = peek$(ctx, "stylePaddingTop");
1183
+ if (paddingTop) {
1184
+ position += paddingTop;
1185
+ }
1186
+ const headerSize = peek$(ctx, "headerSize");
1187
+ if (headerSize) {
1188
+ position += headerSize;
1189
+ }
1190
+ }
1191
+ return position;
1192
+ }
1193
+
1194
+ // src/core/calculateOffsetWithOffsetPosition.ts
1195
+ function calculateOffsetWithOffsetPosition(state, offsetParam, params) {
1196
+ const { index, viewOffset, viewPosition } = params;
1197
+ let offset = offsetParam;
1198
+ if (viewOffset) {
1199
+ offset -= viewOffset;
1200
+ }
1201
+ if (viewPosition !== void 0 && index !== void 0) {
1202
+ offset -= viewPosition * (state.scrollLength - getItemSize(state, getId(state, index), index, state.props.data[index]));
1203
+ }
1204
+ return offset;
1205
+ }
1206
+
1207
+ // src/core/finishScrollTo.ts
1208
+ var finishScrollTo = (state) => {
1209
+ if (state) {
1210
+ state.scrollingTo = void 0;
1211
+ state.scrollHistory.length = 0;
1212
+ }
1213
+ };
1214
+
1215
+ // src/core/scrollTo.ts
1216
+ function scrollTo(state, params = {}) {
1217
+ var _a;
1218
+ const { animated, noScrollingTo } = params;
1219
+ const {
1220
+ refScroller,
1221
+ props: { horizontal }
1222
+ } = state;
1223
+ const offset = calculateOffsetWithOffsetPosition(state, params.offset, params);
1224
+ state.scrollHistory.length = 0;
1225
+ if (!noScrollingTo) {
1226
+ state.scrollingTo = params;
1227
+ }
1228
+ state.scrollPending = offset;
1229
+ (_a = refScroller.current) == null ? void 0 : _a.scrollTo({
1230
+ animated: !!animated,
1231
+ x: horizontal ? offset : 0,
1232
+ y: horizontal ? 0 : offset
1233
+ });
1234
+ if (!animated) {
1235
+ state.scroll = offset;
1236
+ setTimeout(() => finishScrollTo(state), 100);
1237
+ }
1238
+ }
1239
+
1240
+ // src/utils/requestAdjust.ts
1241
+ function requestAdjust(ctx, state, positionDiff, dataChanged) {
1242
+ if (Math.abs(positionDiff) > 0.1) {
1243
+ const needsScrollWorkaround = reactNative.Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
1244
+ const doit = () => {
1245
+ if (needsScrollWorkaround) {
1246
+ scrollTo(state, {
1247
+ noScrollingTo: true,
1248
+ offset: state.scroll
1249
+ });
1250
+ } else {
1251
+ state.scrollAdjustHandler.requestAdjust(positionDiff);
1252
+ }
1253
+ };
1254
+ state.scroll += positionDiff;
1255
+ state.scrollForNextCalculateItemsInView = void 0;
1256
+ const didLayout = peek$(ctx, "containersDidLayout");
1257
+ if (didLayout) {
1258
+ doit();
1259
+ const threshold = state.scroll - positionDiff / 2;
1260
+ if (!state.ignoreScrollFromMVCP) {
1261
+ state.ignoreScrollFromMVCP = {};
1262
+ }
1263
+ if (positionDiff > 0) {
1264
+ state.ignoreScrollFromMVCP.lt = threshold;
1265
+ } else {
1266
+ state.ignoreScrollFromMVCP.gt = threshold;
1267
+ }
1268
+ if (state.ignoreScrollFromMVCPTimeout) {
1269
+ clearTimeout(state.ignoreScrollFromMVCPTimeout);
1270
+ }
1271
+ state.ignoreScrollFromMVCPTimeout = setTimeout(
1272
+ () => {
1273
+ state.ignoreScrollFromMVCP = void 0;
1274
+ },
1275
+ needsScrollWorkaround ? 250 : 100
1276
+ );
1277
+ } else {
1278
+ requestAnimationFrame(doit);
1279
+ }
1280
+ }
1281
+ }
1282
+
1283
+ // src/core/mvcp.ts
1284
+ function prepareMVCP(ctx, state, dataChanged) {
1285
+ const {
1286
+ idsInView,
1287
+ scrollingTo,
1288
+ props: { maintainVisibleContentPosition }
1289
+ } = state;
1290
+ let prevPosition;
1291
+ let targetId;
1292
+ const idsInViewWithPositions = [];
1293
+ const scrollTarget = scrollingTo == null ? void 0 : scrollingTo.index;
1294
+ if (maintainVisibleContentPosition) {
1295
+ const indexByKey = state.indexByKey;
1296
+ if (scrollTarget !== void 0) {
1297
+ targetId = getId(state, scrollTarget);
1298
+ } else if (idsInView.length > 0 && peek$(ctx, "containersDidLayout")) {
1299
+ if (dataChanged) {
1300
+ for (let i = 0; i < idsInView.length; i++) {
1301
+ const id = idsInView[i];
1302
+ const index = indexByKey.get(id);
1303
+ if (index !== void 0) {
1304
+ const position = getPositionById(ctx, state, id);
1305
+ if (position !== void 0) {
1306
+ idsInViewWithPositions.push({ id, position });
1307
+ }
1308
+ }
1309
+ }
1310
+ } else {
1311
+ targetId = state.idsInView.find((id) => indexByKey.get(id) !== void 0);
1312
+ }
1313
+ }
1314
+ if (targetId !== void 0) {
1315
+ const pos = getPositionById(ctx, state, targetId);
1316
+ if (pos !== void 0) {
1317
+ prevPosition = pos;
1318
+ }
1319
+ }
1320
+ }
1321
+ return () => {
1322
+ let positionDiff;
1323
+ if (dataChanged && targetId === void 0) {
1324
+ for (let i = 0; i < idsInViewWithPositions.length; i++) {
1325
+ const { id, position } = idsInViewWithPositions[i];
1326
+ const newPosition = getPositionById(ctx, state, id);
1327
+ if (newPosition !== void 0) {
1328
+ positionDiff = newPosition - position;
1329
+ break;
1330
+ }
1331
+ }
1332
+ }
1333
+ if (targetId !== void 0 && prevPosition !== void 0) {
1334
+ const newPosition = getPositionById(ctx, state, targetId);
1335
+ if (newPosition !== void 0) {
1336
+ positionDiff = newPosition - prevPosition;
1337
+ }
1338
+ }
1339
+ if (positionDiff !== void 0 && Math.abs(positionDiff) > 0.1) {
1340
+ requestAdjust(ctx, state, positionDiff, dataChanged);
1341
+ }
1342
+ };
1343
+ }
1344
+
1093
1345
  // src/core/viewability.ts
1094
- var mapViewabilityConfigCallbackPairs = /* @__PURE__ */ new Map();
1346
+ function ensureViewabilityState(ctx, configId) {
1347
+ let map = ctx.mapViewabilityConfigStates;
1348
+ if (!map) {
1349
+ map = /* @__PURE__ */ new Map();
1350
+ ctx.mapViewabilityConfigStates = map;
1351
+ }
1352
+ let state = map.get(configId);
1353
+ if (!state) {
1354
+ state = { end: -1, previousEnd: -1, previousStart: -1, start: -1, viewableItems: [] };
1355
+ map.set(configId, state);
1356
+ }
1357
+ return state;
1358
+ }
1095
1359
  function setupViewability(props) {
1096
1360
  let { viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged } = props;
1097
1361
  if (viewabilityConfig || onViewableItemsChanged) {
@@ -1105,17 +1369,6 @@ function setupViewability(props) {
1105
1369
  }
1106
1370
  ];
1107
1371
  }
1108
- if (viewabilityConfigCallbackPairs) {
1109
- for (const pair of viewabilityConfigCallbackPairs) {
1110
- mapViewabilityConfigCallbackPairs.set(pair.viewabilityConfig.id, {
1111
- end: -1,
1112
- previousEnd: -1,
1113
- previousStart: -1,
1114
- start: -1,
1115
- viewableItems: []
1116
- });
1117
- }
1118
- }
1119
1372
  return viewabilityConfigCallbackPairs;
1120
1373
  }
1121
1374
  function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollSize, start, end) {
@@ -1124,9 +1377,7 @@ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollS
1124
1377
  props: { data }
1125
1378
  } = state;
1126
1379
  for (const viewabilityConfigCallbackPair of viewabilityConfigCallbackPairs) {
1127
- const viewabilityState = mapViewabilityConfigCallbackPairs.get(
1128
- viewabilityConfigCallbackPair.viewabilityConfig.id
1129
- );
1380
+ const viewabilityState = ensureViewabilityState(ctx, viewabilityConfigCallbackPair.viewabilityConfig.id);
1130
1381
  viewabilityState.start = start;
1131
1382
  viewabilityState.end = end;
1132
1383
  if (viewabilityConfigCallbackPair.viewabilityConfig.minimumViewTime) {
@@ -1143,7 +1394,7 @@ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollS
1143
1394
  function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, state, ctx, scrollSize) {
1144
1395
  const { viewabilityConfig, onViewableItemsChanged } = viewabilityConfigCallbackPair;
1145
1396
  const configId = viewabilityConfig.id;
1146
- const viewabilityState = mapViewabilityConfigCallbackPairs.get(configId);
1397
+ const viewabilityState = ensureViewabilityState(ctx, configId);
1147
1398
  const { viewableItems: previousViewableItems, start, end } = viewabilityState;
1148
1399
  const viewabilityTokens = /* @__PURE__ */ new Map();
1149
1400
  for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
@@ -1222,6 +1473,15 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
1222
1473
  }
1223
1474
  }
1224
1475
  }
1476
+ function shallowEqual(prev, next) {
1477
+ if (!prev) return false;
1478
+ const keys = Object.keys(next);
1479
+ for (let i = 0; i < keys.length; i++) {
1480
+ const k = keys[i];
1481
+ if (prev[k] !== next[k]) return false;
1482
+ }
1483
+ return true;
1484
+ }
1225
1485
  function computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
1226
1486
  const { sizes, positions, scroll: scrollState } = state;
1227
1487
  const topPad = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
@@ -1250,7 +1510,8 @@ function computeViewability(state, ctx, viewabilityConfig, containerId, key, scr
1250
1510
  size,
1251
1511
  sizeVisible
1252
1512
  };
1253
- if (JSON.stringify(value) !== JSON.stringify(ctx.mapViewabilityAmountValues.get(containerId))) {
1513
+ const prev = ctx.mapViewabilityAmountValues.get(containerId);
1514
+ if (!shallowEqual(prev, value)) {
1254
1515
  ctx.mapViewabilityAmountValues.set(containerId, value);
1255
1516
  const cb = ctx.mapViewabilityAmountCallbacks.get(containerId);
1256
1517
  if (cb) {
@@ -1279,6 +1540,7 @@ function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
1279
1540
  const cb = ctx.mapViewabilityCallbacks.get(key);
1280
1541
  cb == null ? void 0 : cb(viewToken);
1281
1542
  }
1543
+ var batchedUpdates = reactNative.unstable_batchedUpdates || ((callback) => callback());
1282
1544
 
1283
1545
  // src/utils/checkAllSizesKnown.ts
1284
1546
  function checkAllSizesKnown(state) {
@@ -1295,35 +1557,79 @@ function checkAllSizesKnown(state) {
1295
1557
  }
1296
1558
 
1297
1559
  // src/utils/findAvailableContainers.ts
1298
- function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffered, pendingRemoval) {
1560
+ function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers) {
1299
1561
  const numContainers = peek$(ctx, "numContainers");
1562
+ const { stickyContainerPool, containerItemTypes } = state;
1300
1563
  const result = [];
1301
1564
  const availableContainers = [];
1302
- for (let u = 0; u < numContainers; u++) {
1565
+ const stickyIndicesSet = state.props.stickyIndicesSet;
1566
+ const stickyItemIndices = (needNewContainers == null ? void 0 : needNewContainers.filter((index) => stickyIndicesSet.has(index))) || [];
1567
+ const canReuseContainer = (containerIndex, requiredType) => {
1568
+ if (!requiredType) return true;
1569
+ const existingType = containerItemTypes.get(containerIndex);
1570
+ if (!existingType) return true;
1571
+ return existingType === requiredType;
1572
+ };
1573
+ const neededTypes = requiredItemTypes ? [...requiredItemTypes] : [];
1574
+ let typeIndex = 0;
1575
+ for (let i = 0; i < stickyItemIndices.length; i++) {
1576
+ const requiredType = neededTypes[typeIndex];
1577
+ let foundContainer = false;
1578
+ for (const containerIndex of stickyContainerPool) {
1579
+ const key = peek$(ctx, `containerItemKey${containerIndex}`);
1580
+ const isPendingRemoval = pendingRemoval.includes(containerIndex);
1581
+ if ((key === void 0 || isPendingRemoval) && canReuseContainer(containerIndex, requiredType)) {
1582
+ result.push(containerIndex);
1583
+ if (isPendingRemoval) {
1584
+ const index = pendingRemoval.indexOf(containerIndex);
1585
+ pendingRemoval.splice(index, 1);
1586
+ }
1587
+ foundContainer = true;
1588
+ if (requiredItemTypes) typeIndex++;
1589
+ break;
1590
+ }
1591
+ }
1592
+ if (!foundContainer) {
1593
+ const newContainerIndex = numContainers + result.filter((index) => index >= numContainers).length;
1594
+ result.push(newContainerIndex);
1595
+ stickyContainerPool.add(newContainerIndex);
1596
+ if (requiredItemTypes) typeIndex++;
1597
+ }
1598
+ }
1599
+ for (let u = 0; u < numContainers && result.length < numNeeded; u++) {
1600
+ if (stickyContainerPool.has(u)) {
1601
+ continue;
1602
+ }
1303
1603
  const key = peek$(ctx, `containerItemKey${u}`);
1304
1604
  let isOk = key === void 0;
1305
1605
  if (!isOk) {
1306
1606
  const index = pendingRemoval.indexOf(u);
1307
1607
  if (index !== -1) {
1308
1608
  pendingRemoval.splice(index, 1);
1309
- isOk = true;
1609
+ const requiredType = neededTypes[typeIndex];
1610
+ isOk = canReuseContainer(u, requiredType);
1310
1611
  }
1311
1612
  }
1312
1613
  if (isOk) {
1313
1614
  result.push(u);
1314
- if (result.length >= numNeeded) {
1315
- return result;
1615
+ if (requiredItemTypes) {
1616
+ typeIndex++;
1316
1617
  }
1317
1618
  }
1318
1619
  }
1319
- for (let u = 0; u < numContainers; u++) {
1620
+ for (let u = 0; u < numContainers && result.length < numNeeded; u++) {
1621
+ if (stickyContainerPool.has(u)) {
1622
+ continue;
1623
+ }
1320
1624
  const key = peek$(ctx, `containerItemKey${u}`);
1321
1625
  if (key === void 0) continue;
1322
1626
  const index = state.indexByKey.get(key);
1323
- if (index < startBuffered) {
1324
- availableContainers.push({ distance: startBuffered - index, index: u });
1325
- } else if (index > endBuffered) {
1326
- availableContainers.push({ distance: index - endBuffered, index: u });
1627
+ const isOutOfView = index < startBuffered || index > endBuffered;
1628
+ if (isOutOfView) {
1629
+ const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
1630
+ if (!requiredItemTypes || typeIndex < neededTypes.length && canReuseContainer(u, neededTypes[typeIndex])) {
1631
+ availableContainers.push({ distance, index: u });
1632
+ }
1327
1633
  }
1328
1634
  }
1329
1635
  const remaining = numNeeded - result.length;
@@ -1335,6 +1641,9 @@ function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffer
1335
1641
  }
1336
1642
  for (const container of availableContainers) {
1337
1643
  result.push(container.index);
1644
+ if (requiredItemTypes) {
1645
+ typeIndex++;
1646
+ }
1338
1647
  }
1339
1648
  }
1340
1649
  const stillNeeded = numNeeded - result.length;
@@ -1363,37 +1672,44 @@ function comparatorByDistance(a, b) {
1363
1672
  return b.distance - a.distance;
1364
1673
  }
1365
1674
 
1366
- // src/core/finishScrollTo.ts
1367
- var finishScrollTo = (state) => {
1368
- if (state) {
1369
- state.scrollingTo = void 0;
1370
- state.scrollHistory.length = 0;
1675
+ // src/utils/getScrollVelocity.ts
1676
+ var getScrollVelocity = (state) => {
1677
+ const { scrollHistory } = state;
1678
+ let velocity = 0;
1679
+ if (scrollHistory.length >= 1) {
1680
+ const newest = scrollHistory[scrollHistory.length - 1];
1681
+ let oldest;
1682
+ let start = 0;
1683
+ const now = Date.now();
1684
+ for (let i = 0; i < scrollHistory.length - 1; i++) {
1685
+ const entry = scrollHistory[i];
1686
+ const nextEntry = scrollHistory[i + 1];
1687
+ if (i > 0) {
1688
+ const prevEntry = scrollHistory[i - 1];
1689
+ const prevDirection = entry.scroll - prevEntry.scroll;
1690
+ const currentDirection = nextEntry.scroll - entry.scroll;
1691
+ if (prevDirection > 0 && currentDirection < 0 || prevDirection < 0 && currentDirection > 0) {
1692
+ start = i;
1693
+ break;
1694
+ }
1695
+ }
1696
+ }
1697
+ for (let i = start; i < scrollHistory.length - 1; i++) {
1698
+ const entry = scrollHistory[i];
1699
+ if (now - entry.time <= 1e3) {
1700
+ oldest = entry;
1701
+ break;
1702
+ }
1703
+ }
1704
+ if (oldest && oldest !== newest) {
1705
+ const scrollDiff = newest.scroll - oldest.scroll;
1706
+ const timeDiff = newest.time - oldest.time;
1707
+ velocity = timeDiff > 0 ? scrollDiff / timeDiff : 0;
1708
+ }
1371
1709
  }
1710
+ return velocity;
1372
1711
  };
1373
1712
 
1374
- // src/core/scrollTo.ts
1375
- function scrollTo(state, params = {}) {
1376
- var _a;
1377
- const { animated } = params;
1378
- const {
1379
- refScroller,
1380
- props: { horizontal }
1381
- } = state;
1382
- const offset = calculateOffsetWithOffsetPosition(state, params.offset, params);
1383
- state.scrollHistory.length = 0;
1384
- state.scrollingTo = params;
1385
- state.scrollPending = offset;
1386
- (_a = refScroller.current) == null ? void 0 : _a.scrollTo({
1387
- animated: !!animated,
1388
- x: horizontal ? offset : 0,
1389
- y: horizontal ? 0 : offset
1390
- });
1391
- if (!animated) {
1392
- state.scroll = offset;
1393
- setTimeout(() => finishScrollTo(state), 100);
1394
- }
1395
- }
1396
-
1397
1713
  // src/core/scrollToIndex.ts
1398
1714
  function scrollToIndex(ctx, state, { index, viewOffset = 0, animated = true, viewPosition }) {
1399
1715
  if (index >= state.props.data.length) {
@@ -1471,43 +1787,123 @@ function checkAtBottom(ctx, state) {
1471
1787
  );
1472
1788
  }
1473
1789
  }
1474
-
1475
- // src/utils/setDidLayout.ts
1476
- function setDidLayout(ctx, state) {
1477
- const {
1478
- loadStartTime,
1479
- initialScroll,
1480
- props: { onLoad }
1481
- } = state;
1482
- state.queuedInitialLayout = true;
1483
- checkAtBottom(ctx, state);
1484
- if (!IsNewArchitecture && initialScroll) {
1485
- scrollToIndex(ctx, state, { ...initialScroll, animated: false });
1790
+
1791
+ // src/utils/setDidLayout.ts
1792
+ function setDidLayout(ctx, state) {
1793
+ const {
1794
+ loadStartTime,
1795
+ initialScroll,
1796
+ props: { onLoad }
1797
+ } = state;
1798
+ state.queuedInitialLayout = true;
1799
+ checkAtBottom(ctx, state);
1800
+ const setIt = () => {
1801
+ set$(ctx, "containersDidLayout", true);
1802
+ if (onLoad) {
1803
+ onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1804
+ }
1805
+ };
1806
+ if (reactNative.Platform.OS === "android" || !IsNewArchitecture) {
1807
+ if (initialScroll) {
1808
+ queueMicrotask(() => {
1809
+ scrollToIndex(ctx, state, { ...initialScroll, animated: false });
1810
+ requestAnimationFrame(() => {
1811
+ scrollToIndex(ctx, state, { ...initialScroll, animated: false });
1812
+ setIt();
1813
+ });
1814
+ });
1815
+ } else {
1816
+ queueMicrotask(setIt);
1817
+ }
1818
+ } else {
1819
+ setIt();
1820
+ }
1821
+ }
1822
+
1823
+ // src/core/calculateItemsInView.ts
1824
+ function findCurrentStickyIndex(stickyArray, scroll, state) {
1825
+ var _a;
1826
+ const idCache = state.idCache;
1827
+ const positions = state.positions;
1828
+ for (let i = stickyArray.length - 1; i >= 0; i--) {
1829
+ const stickyId = (_a = idCache.get(stickyArray[i])) != null ? _a : getId(state, stickyArray[i]);
1830
+ const stickyPos = stickyId ? positions.get(stickyId) : void 0;
1831
+ if (stickyPos !== void 0 && scroll >= stickyPos) {
1832
+ return i;
1833
+ }
1834
+ }
1835
+ return -1;
1836
+ }
1837
+ function getActiveStickyIndices(ctx, state, stickyIndices) {
1838
+ return new Set(
1839
+ Array.from(state.stickyContainerPool).map((i) => peek$(ctx, `containerItemKey${i}`)).map((key) => key ? state.indexByKey.get(key) : void 0).filter((idx) => idx !== void 0 && stickyIndices.has(idx))
1840
+ );
1841
+ }
1842
+ function handleStickyActivation(ctx, state, stickyIndices, stickyArray, scroll, needNewContainers, startBuffered, endBuffered) {
1843
+ var _a;
1844
+ const activeIndices = getActiveStickyIndices(ctx, state, stickyIndices);
1845
+ const currentStickyIdx = findCurrentStickyIndex(stickyArray, scroll, state);
1846
+ for (let offset = 0; offset <= 1; offset++) {
1847
+ const idx = currentStickyIdx - offset;
1848
+ if (idx < 0 || activeIndices.has(stickyArray[idx])) continue;
1849
+ const stickyIndex = stickyArray[idx];
1850
+ const stickyId = (_a = state.idCache.get(stickyIndex)) != null ? _a : getId(state, stickyIndex);
1851
+ if (stickyId && !state.containerItemKeys.has(stickyId) && (stickyIndex < startBuffered || stickyIndex > endBuffered)) {
1852
+ needNewContainers.push(stickyIndex);
1853
+ }
1486
1854
  }
1487
- set$(ctx, "containersDidLayout", true);
1488
- if (onLoad) {
1489
- onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1855
+ }
1856
+ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, pendingRemoval) {
1857
+ var _a, _b, _c;
1858
+ const currentStickyIdx = findCurrentStickyIndex(stickyArray, scroll, state);
1859
+ for (const containerIndex of state.stickyContainerPool) {
1860
+ const itemKey = peek$(ctx, `containerItemKey${containerIndex}`);
1861
+ const itemIndex = itemKey ? state.indexByKey.get(itemKey) : void 0;
1862
+ if (itemIndex === void 0) continue;
1863
+ const arrayIdx = stickyArray.indexOf(itemIndex);
1864
+ if (arrayIdx === -1) continue;
1865
+ const isRecentSticky = arrayIdx >= currentStickyIdx - 1 && arrayIdx <= currentStickyIdx + 1;
1866
+ if (isRecentSticky) continue;
1867
+ const nextIndex = stickyArray[arrayIdx + 1];
1868
+ let shouldRecycle = false;
1869
+ if (nextIndex) {
1870
+ const nextId = (_a = state.idCache.get(nextIndex)) != null ? _a : getId(state, nextIndex);
1871
+ const nextPos = nextId ? state.positions.get(nextId) : void 0;
1872
+ shouldRecycle = nextPos !== void 0 && scroll > nextPos + scrollBuffer * 2;
1873
+ } else {
1874
+ const currentId = (_b = state.idCache.get(itemIndex)) != null ? _b : getId(state, itemIndex);
1875
+ if (currentId) {
1876
+ const currentPos = state.positions.get(currentId);
1877
+ const currentSize = (_c = state.sizes.get(currentId)) != null ? _c : getItemSize(state, currentId, itemIndex, state.props.data[itemIndex]);
1878
+ shouldRecycle = currentPos !== void 0 && scroll > currentPos + currentSize + scrollBuffer * 3;
1879
+ }
1880
+ }
1881
+ if (shouldRecycle) {
1882
+ pendingRemoval.push(containerIndex);
1883
+ }
1490
1884
  }
1491
1885
  }
1492
-
1493
- // src/core/calculateItemsInView.ts
1494
1886
  function calculateItemsInView(ctx, state, params = {}) {
1495
- reactNative.unstable_batchedUpdates(() => {
1887
+ batchedUpdates(() => {
1496
1888
  var _a, _b, _c, _d, _e, _f, _g, _h;
1497
1889
  const {
1498
- scrollLength,
1499
- startBufferedId: startBufferedIdOrig,
1500
- positions,
1501
1890
  columns,
1502
1891
  containerItemKeys,
1892
+ enableScrollForNextCalculateItemsInView,
1503
1893
  idCache,
1504
- sizes,
1505
1894
  indexByKey,
1895
+ minIndexSizeChanged,
1896
+ positions,
1506
1897
  scrollForNextCalculateItemsInView,
1507
- enableScrollForNextCalculateItemsInView,
1508
- minIndexSizeChanged
1898
+ scrollLength,
1899
+ sizes,
1900
+ startBufferedId: startBufferedIdOrig,
1901
+ viewabilityConfigCallbackPairs,
1902
+ props: { getItemType, initialScroll, itemsAreEqual, keyExtractor, scrollBuffer }
1509
1903
  } = state;
1510
- const data = state.props.data;
1904
+ const { data } = state.props;
1905
+ const stickyIndicesArr = state.props.stickyIndicesArr || [];
1906
+ const stickyIndicesSet = state.props.stickyIndicesSet || /* @__PURE__ */ new Set();
1511
1907
  const prevNumContainers = peek$(ctx, "numContainers");
1512
1908
  if (!data || scrollLength === 0 || !prevNumContainers) {
1513
1909
  return;
@@ -1519,14 +1915,22 @@ function calculateItemsInView(ctx, state, params = {}) {
1519
1915
  const { dataChanged, doMVCP } = params;
1520
1916
  const speed = getScrollVelocity(state);
1521
1917
  if (doMVCP || dataChanged) {
1522
- const checkMVCP = doMVCP ? prepareMVCP(ctx, state) : void 0;
1523
- updateAllPositions(ctx, state, dataChanged);
1918
+ const checkMVCP = doMVCP ? prepareMVCP(ctx, state, dataChanged) : void 0;
1919
+ if (dataChanged) {
1920
+ indexByKey.clear();
1921
+ idCache.clear();
1922
+ positions.clear();
1923
+ }
1924
+ const startIndex = dataChanged ? 0 : minIndexSizeChanged != null ? minIndexSizeChanged : 0;
1925
+ updateItemPositions(ctx, state, dataChanged, startIndex);
1926
+ if (minIndexSizeChanged !== void 0) {
1927
+ state.minIndexSizeChanged = void 0;
1928
+ }
1524
1929
  checkMVCP == null ? void 0 : checkMVCP();
1525
1930
  }
1526
1931
  const scrollExtra = 0;
1527
1932
  const { queuedInitialLayout } = state;
1528
1933
  let { scroll: scrollState } = state;
1529
- const initialScroll = state.props.initialScroll;
1530
1934
  if (!queuedInitialLayout && initialScroll) {
1531
1935
  const updatedOffset = calculateOffsetWithOffsetPosition(
1532
1936
  state,
@@ -1538,16 +1942,15 @@ function calculateItemsInView(ctx, state, params = {}) {
1538
1942
  const scrollAdjustPad = -previousScrollAdjust - topPad;
1539
1943
  let scroll = scrollState + scrollExtra + scrollAdjustPad;
1540
1944
  if (scroll + scrollLength > totalSize) {
1541
- scroll = totalSize - scrollLength;
1945
+ scroll = Math.max(0, totalSize - scrollLength);
1542
1946
  }
1543
1947
  if (ENABLE_DEBUG_VIEW) {
1544
1948
  set$(ctx, "debugRawScroll", scrollState);
1545
1949
  set$(ctx, "debugComputedScroll", scroll);
1546
1950
  }
1547
- const scrollBuffer = state.props.scrollBuffer;
1548
1951
  let scrollBufferTop = scrollBuffer;
1549
1952
  let scrollBufferBottom = scrollBuffer;
1550
- if (speed > 0) {
1953
+ if (speed > 0 || speed === 0 && scroll < Math.max(50, scrollBuffer)) {
1551
1954
  scrollBufferTop = scrollBuffer * 0.5;
1552
1955
  scrollBufferBottom = scrollBuffer * 1.5;
1553
1956
  } else {
@@ -1555,7 +1958,7 @@ function calculateItemsInView(ctx, state, params = {}) {
1555
1958
  scrollBufferBottom = scrollBuffer * 0.5;
1556
1959
  }
1557
1960
  const scrollTopBuffered = scroll - scrollBufferTop;
1558
- const scrollBottom = scroll + scrollLength;
1961
+ const scrollBottom = scroll + scrollLength + (scroll < 0 ? -scroll : 0);
1559
1962
  const scrollBottomBuffered = scrollBottom + scrollBufferBottom;
1560
1963
  if (scrollForNextCalculateItemsInView) {
1561
1964
  const { top, bottom } = scrollForNextCalculateItemsInView;
@@ -1569,10 +1972,6 @@ function calculateItemsInView(ctx, state, params = {}) {
1569
1972
  let endNoBuffer = null;
1570
1973
  let endBuffered = null;
1571
1974
  let loopStart = startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
1572
- if (minIndexSizeChanged !== void 0) {
1573
- loopStart = Math.min(minIndexSizeChanged, loopStart);
1574
- state.minIndexSizeChanged = void 0;
1575
- }
1576
1975
  for (let i = loopStart; i >= 0; i--) {
1577
1976
  const id = (_a = idCache.get(i)) != null ? _a : getId(state, i);
1578
1977
  const top = positions.get(id);
@@ -1655,7 +2054,7 @@ function calculateItemsInView(ctx, state, params = {}) {
1655
2054
  if (dataChanged) {
1656
2055
  for (let i = 0; i < numContainers; i++) {
1657
2056
  const itemKey = peek$(ctx, `containerItemKey${i}`);
1658
- if (!state.props.keyExtractor || itemKey && indexByKey.get(itemKey) === void 0) {
2057
+ if (!keyExtractor || itemKey && indexByKey.get(itemKey) === void 0) {
1659
2058
  pendingRemoval.push(i);
1660
2059
  }
1661
2060
  }
@@ -1669,14 +2068,32 @@ function calculateItemsInView(ctx, state, params = {}) {
1669
2068
  needNewContainers.push(i);
1670
2069
  }
1671
2070
  }
2071
+ if (stickyIndicesArr.length > 0) {
2072
+ handleStickyActivation(
2073
+ ctx,
2074
+ state,
2075
+ stickyIndicesSet,
2076
+ stickyIndicesArr,
2077
+ scroll,
2078
+ needNewContainers,
2079
+ startBuffered,
2080
+ endBuffered
2081
+ );
2082
+ }
1672
2083
  if (needNewContainers.length > 0) {
2084
+ const requiredItemTypes = getItemType ? needNewContainers.map((i) => {
2085
+ const itemType = getItemType(data[i], i);
2086
+ return itemType ? String(itemType) : "";
2087
+ }) : void 0;
1673
2088
  const availableContainers = findAvailableContainers(
1674
2089
  ctx,
1675
2090
  state,
1676
2091
  needNewContainers.length,
1677
2092
  startBuffered,
1678
2093
  endBuffered,
1679
- pendingRemoval
2094
+ pendingRemoval,
2095
+ requiredItemTypes,
2096
+ needNewContainers
1680
2097
  );
1681
2098
  for (let idx = 0; idx < needNewContainers.length; idx++) {
1682
2099
  const i = needNewContainers[idx];
@@ -1688,7 +2105,19 @@ function calculateItemsInView(ctx, state, params = {}) {
1688
2105
  }
1689
2106
  set$(ctx, `containerItemKey${containerIndex}`, id);
1690
2107
  set$(ctx, `containerItemData${containerIndex}`, data[i]);
2108
+ if (requiredItemTypes) {
2109
+ state.containerItemTypes.set(containerIndex, requiredItemTypes[idx]);
2110
+ }
1691
2111
  containerItemKeys.add(id);
2112
+ if (stickyIndicesSet.has(i)) {
2113
+ set$(ctx, `containerSticky${containerIndex}`, true);
2114
+ const topPadding = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
2115
+ set$(ctx, `containerStickyOffset${containerIndex}`, new reactNative.Animated.Value(topPadding));
2116
+ state.stickyContainerPool.add(containerIndex);
2117
+ } else {
2118
+ set$(ctx, `containerSticky${containerIndex}`, false);
2119
+ state.stickyContainerPool.delete(containerIndex);
2120
+ }
1692
2121
  if (containerIndex >= numContainers2) {
1693
2122
  numContainers2 = containerIndex + 1;
1694
2123
  }
@@ -1701,12 +2130,21 @@ function calculateItemsInView(ctx, state, params = {}) {
1701
2130
  }
1702
2131
  }
1703
2132
  }
2133
+ if (stickyIndicesArr.length > 0) {
2134
+ handleStickyRecycling(ctx, state, stickyIndicesArr, scroll, scrollBuffer, pendingRemoval);
2135
+ }
1704
2136
  for (let i = 0; i < numContainers; i++) {
1705
2137
  const itemKey = peek$(ctx, `containerItemKey${i}`);
1706
2138
  if (pendingRemoval.includes(i)) {
1707
2139
  if (itemKey) {
1708
2140
  containerItemKeys.delete(itemKey);
1709
2141
  }
2142
+ state.containerItemTypes.delete(i);
2143
+ if (state.stickyContainerPool.has(i)) {
2144
+ set$(ctx, `containerSticky${i}`, false);
2145
+ set$(ctx, `containerStickyOffset${i}`, void 0);
2146
+ state.stickyContainerPool.delete(i);
2147
+ }
1710
2148
  set$(ctx, `containerItemKey${i}`, void 0);
1711
2149
  set$(ctx, `containerItemData${i}`, void 0);
1712
2150
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
@@ -1716,7 +2154,7 @@ function calculateItemsInView(ctx, state, params = {}) {
1716
2154
  const item = data[itemIndex];
1717
2155
  if (item !== void 0) {
1718
2156
  const id = (_h = idCache.get(itemIndex)) != null ? _h : getId(state, itemIndex);
1719
- const position = positions.get(id);
2157
+ const position = getPositionById(ctx, state, id);
1720
2158
  if (position === void 0) {
1721
2159
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
1722
2160
  } else {
@@ -1730,7 +2168,7 @@ function calculateItemsInView(ctx, state, params = {}) {
1730
2168
  if (column >= 0 && column !== prevColumn) {
1731
2169
  set$(ctx, `containerColumn${i}`, column);
1732
2170
  }
1733
- if (prevData !== item) {
2171
+ if (prevData !== item && (itemsAreEqual ? !itemsAreEqual(prevData, item, itemIndex, data) : true)) {
1734
2172
  set$(ctx, `containerItemData${i}`, data[itemIndex]);
1735
2173
  }
1736
2174
  }
@@ -1742,42 +2180,55 @@ function calculateItemsInView(ctx, state, params = {}) {
1742
2180
  setDidLayout(ctx, state);
1743
2181
  }
1744
2182
  }
1745
- if (state.viewabilityConfigCallbackPairs) {
1746
- updateViewableItems(
1747
- state,
1748
- ctx,
1749
- state.viewabilityConfigCallbackPairs,
1750
- scrollLength,
1751
- startNoBuffer,
1752
- endNoBuffer
1753
- );
2183
+ if (viewabilityConfigCallbackPairs) {
2184
+ updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollLength, startNoBuffer, endNoBuffer);
1754
2185
  }
1755
2186
  });
1756
2187
  }
1757
2188
 
1758
2189
  // src/core/doInitialAllocateContainers.ts
1759
2190
  function doInitialAllocateContainers(ctx, state) {
1760
- const { scrollLength } = state;
1761
- const data = state.props.data;
1762
- if (scrollLength > 0 && data.length > 0 && !peek$(ctx, "numContainers")) {
1763
- const averageItemSize = state.props.getEstimatedItemSize ? state.props.getEstimatedItemSize(0, data[0]) : state.props.estimatedItemSize;
1764
- const Extra = 1.5;
1765
- const numContainers = Math.ceil(
1766
- (scrollLength + state.props.scrollBuffer * 2) / averageItemSize * state.props.numColumns * Extra
1767
- );
2191
+ var _a;
2192
+ const {
2193
+ scrollLength,
2194
+ props: {
2195
+ data,
2196
+ getEstimatedItemSize,
2197
+ getFixedItemSize,
2198
+ getItemType,
2199
+ scrollBuffer,
2200
+ numColumns,
2201
+ estimatedItemSize
2202
+ }
2203
+ } = state;
2204
+ const hasContainers = peek$(ctx, "numContainers");
2205
+ if (scrollLength > 0 && data.length > 0 && !hasContainers) {
2206
+ let averageItemSize;
2207
+ const fn = getFixedItemSize || getEstimatedItemSize;
2208
+ if (fn) {
2209
+ let totalSize = 0;
2210
+ const num = Math.min(20, data.length);
2211
+ for (let i = 0; i < num; i++) {
2212
+ totalSize += fn(0, data[0], getItemType ? (_a = getItemType(data[0], 0)) != null ? _a : "" : "");
2213
+ }
2214
+ averageItemSize = totalSize / num;
2215
+ } else {
2216
+ averageItemSize = estimatedItemSize;
2217
+ }
2218
+ const numContainers = Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize * numColumns);
1768
2219
  for (let i = 0; i < numContainers; i++) {
1769
2220
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
1770
2221
  set$(ctx, `containerColumn${i}`, -1);
1771
2222
  }
1772
2223
  set$(ctx, "numContainers", numContainers);
1773
2224
  set$(ctx, "numContainersPooled", numContainers * state.props.initialContainerPoolRatio);
1774
- if (!IsNewArchitecture) {
2225
+ if (!IsNewArchitecture || state.lastLayout) {
1775
2226
  if (state.props.initialScroll) {
1776
2227
  requestAnimationFrame(() => {
1777
- calculateItemsInView(ctx, state);
2228
+ calculateItemsInView(ctx, state, { dataChanged: true });
1778
2229
  });
1779
2230
  } else {
1780
- calculateItemsInView(ctx, state);
2231
+ calculateItemsInView(ctx, state, { dataChanged: true });
1781
2232
  }
1782
2233
  }
1783
2234
  return true;
@@ -1797,16 +2248,18 @@ function doMaintainScrollAtEnd(ctx, state, animated) {
1797
2248
  }
1798
2249
  requestAnimationFrame(() => {
1799
2250
  var _a;
1800
- state.maintainingScrollAtEnd = true;
1801
- (_a = refScroller.current) == null ? void 0 : _a.scrollToEnd({
1802
- animated
1803
- });
1804
- setTimeout(
1805
- () => {
1806
- state.maintainingScrollAtEnd = false;
1807
- },
1808
- 0
1809
- );
2251
+ if (state == null ? void 0 : state.isAtEnd) {
2252
+ state.maintainingScrollAtEnd = true;
2253
+ (_a = refScroller.current) == null ? void 0 : _a.scrollToEnd({
2254
+ animated
2255
+ });
2256
+ setTimeout(
2257
+ () => {
2258
+ state.maintainingScrollAtEnd = false;
2259
+ },
2260
+ 0
2261
+ );
2262
+ }
1810
2263
  });
1811
2264
  return true;
1812
2265
  }
@@ -1847,40 +2300,49 @@ function handleLayout(ctx, state, layout, setCanRender) {
1847
2300
  const otherAxisSize = layout[state.props.horizontal ? "height" : "width"];
1848
2301
  const needsCalculate = !state.lastLayout || scrollLength > state.scrollLength || state.lastLayout.x !== layout.x || state.lastLayout.y !== layout.y;
1849
2302
  state.lastLayout = layout;
1850
- const didChange = scrollLength !== state.scrollLength;
1851
2303
  const prevOtherAxisSize = state.otherAxisSize;
1852
- state.scrollLength = scrollLength;
1853
- state.otherAxisSize = otherAxisSize;
1854
- state.lastBatchingAction = Date.now();
1855
- state.scrollForNextCalculateItemsInView = void 0;
1856
- doInitialAllocateContainers(ctx, state);
1857
- if (needsCalculate) {
1858
- calculateItemsInView(ctx, state, { doMVCP: true });
1859
- }
1860
- if (didChange || otherAxisSize !== prevOtherAxisSize) {
1861
- set$(ctx, "scrollSize", { height: layout.height, width: layout.width });
1862
- }
1863
- if (maintainScrollAtEnd === true || maintainScrollAtEnd.onLayout) {
1864
- doMaintainScrollAtEnd(ctx, state, false);
1865
- }
1866
- updateAlignItemsPaddingTop(ctx, state);
1867
- checkAtBottom(ctx, state);
1868
- checkAtTop(state);
1869
- if (state) {
1870
- state.needsOtherAxisSize = otherAxisSize - (state.props.stylePaddingTop || 0) < 10;
1871
- }
1872
- if (__DEV__ && scrollLength === 0) {
1873
- warnDevOnce(
1874
- "height0",
1875
- `List ${state.props.horizontal ? "width" : "height"} is 0. You may need to set a style or \`flex: \` for the list, because children are absolutely positioned.`
1876
- );
2304
+ const didChange = scrollLength !== state.scrollLength || otherAxisSize !== prevOtherAxisSize;
2305
+ if (didChange) {
2306
+ state.scrollLength = scrollLength;
2307
+ state.otherAxisSize = otherAxisSize;
2308
+ state.lastBatchingAction = Date.now();
2309
+ state.scrollForNextCalculateItemsInView = void 0;
2310
+ doInitialAllocateContainers(ctx, state);
2311
+ if (needsCalculate) {
2312
+ calculateItemsInView(ctx, state, { doMVCP: true });
2313
+ }
2314
+ if (didChange || otherAxisSize !== prevOtherAxisSize) {
2315
+ set$(ctx, "scrollSize", { height: layout.height, width: layout.width });
2316
+ }
2317
+ if (maintainScrollAtEnd === true || maintainScrollAtEnd.onLayout) {
2318
+ doMaintainScrollAtEnd(ctx, state, false);
2319
+ }
2320
+ updateAlignItemsPaddingTop(ctx, state);
2321
+ checkAtBottom(ctx, state);
2322
+ checkAtTop(state);
2323
+ if (state) {
2324
+ state.needsOtherAxisSize = otherAxisSize - (state.props.stylePaddingTop || 0) < 10;
2325
+ }
2326
+ if (__DEV__ && scrollLength === 0) {
2327
+ warnDevOnce(
2328
+ "height0",
2329
+ `List ${state.props.horizontal ? "width" : "height"} is 0. You may need to set a style or \`flex: \` for the list, because children are absolutely positioned.`
2330
+ );
2331
+ }
2332
+ setCanRender(true);
1877
2333
  }
1878
- setCanRender(true);
1879
2334
  }
1880
2335
 
1881
2336
  // src/core/onScroll.ts
1882
2337
  function onScroll(ctx, state, event) {
1883
- var _a, _b, _c, _d, _e;
2338
+ var _a, _b, _c;
2339
+ const {
2340
+ scrollProcessingEnabled,
2341
+ props: { onScroll: onScrollProp }
2342
+ } = state;
2343
+ if (scrollProcessingEnabled === false) {
2344
+ return;
2345
+ }
1884
2346
  if (((_b = (_a = event.nativeEvent) == null ? void 0 : _a.contentSize) == null ? void 0 : _b.height) === 0 && ((_c = event.nativeEvent.contentSize) == null ? void 0 : _c.width) === 0) {
1885
2347
  return;
1886
2348
  }
@@ -1894,26 +2356,29 @@ function onScroll(ctx, state, event) {
1894
2356
  }
1895
2357
  state.scrollPending = newScroll;
1896
2358
  updateScroll(ctx, state, newScroll);
1897
- (_e = (_d = state.props).onScroll) == null ? void 0 : _e.call(_d, event);
2359
+ onScrollProp == null ? void 0 : onScrollProp(event);
1898
2360
  }
1899
2361
  function updateScroll(ctx, state, newScroll) {
1900
2362
  const scrollingTo = state.scrollingTo;
1901
2363
  state.hasScrolled = true;
1902
2364
  state.lastBatchingAction = Date.now();
1903
- const currentTime = performance.now();
2365
+ const currentTime = Date.now();
1904
2366
  if (scrollingTo === void 0 && !(state.scrollHistory.length === 0 && newScroll === state.scroll)) {
1905
- state.scrollHistory.push({ scroll: newScroll, time: currentTime });
2367
+ const adjust = state.scrollAdjustHandler.getAdjust();
2368
+ state.scrollHistory.push({ scroll: newScroll - adjust, time: currentTime });
1906
2369
  }
1907
2370
  if (state.scrollHistory.length > 5) {
1908
2371
  state.scrollHistory.shift();
1909
2372
  }
1910
- state.scrollPrev = state.scroll;
1911
- state.scrollPrevTime = state.scrollTime;
1912
2373
  state.scroll = newScroll;
1913
2374
  state.scrollTime = currentTime;
1914
- calculateItemsInView(ctx, state);
1915
- checkAtBottom(ctx, state);
1916
- checkAtTop(state);
2375
+ if (Math.abs(state.scroll - state.scrollPrev) > 2) {
2376
+ calculateItemsInView(ctx, state);
2377
+ checkAtBottom(ctx, state);
2378
+ checkAtTop(state);
2379
+ }
2380
+ state.scrollPrev = state.scroll;
2381
+ state.scrollPrevTime = state.scrollTime;
1917
2382
  }
1918
2383
 
1919
2384
  // src/core/ScrollAdjustHandler.ts
@@ -1936,13 +2401,19 @@ var ScrollAdjustHandler = class {
1936
2401
  setMounted() {
1937
2402
  this.mounted = true;
1938
2403
  }
2404
+ getAdjust() {
2405
+ return this.appliedAdjust;
2406
+ }
1939
2407
  };
1940
2408
 
1941
2409
  // src/core/updateItemSize.ts
1942
- function updateItemSizes(ctx, state, itemUpdates) {
1943
- var _a;
2410
+ function updateItemSize(ctx, state, itemKey, sizeObj) {
2411
+ var _a, _b;
1944
2412
  const {
2413
+ sizesKnown,
1945
2414
  props: {
2415
+ getFixedItemSize,
2416
+ getItemType,
1946
2417
  horizontal,
1947
2418
  maintainVisibleContentPosition,
1948
2419
  suggestEstimatedItemSize,
@@ -1952,47 +2423,60 @@ function updateItemSizes(ctx, state, itemUpdates) {
1952
2423
  }
1953
2424
  } = state;
1954
2425
  if (!data) return;
2426
+ if (getFixedItemSize) {
2427
+ const index2 = state.indexByKey.get(itemKey);
2428
+ if (index2 === void 0) {
2429
+ return;
2430
+ }
2431
+ const itemData = state.props.data[index2];
2432
+ if (itemData === void 0) {
2433
+ return;
2434
+ }
2435
+ const type = getItemType ? (_a = getItemType(itemData, index2)) != null ? _a : "" : "";
2436
+ const size2 = getFixedItemSize(index2, itemData, type);
2437
+ if (size2 !== void 0 && size2 === sizesKnown.get(itemKey)) {
2438
+ return;
2439
+ }
2440
+ }
1955
2441
  const containersDidLayout = peek$(ctx, "containersDidLayout");
1956
2442
  let needsRecalculate = !containersDidLayout;
1957
2443
  let shouldMaintainScrollAtEnd = false;
1958
2444
  let minIndexSizeChanged;
1959
2445
  let maxOtherAxisSize = peek$(ctx, "otherAxisSize") || 0;
1960
- for (const { itemKey, sizeObj } of itemUpdates) {
1961
- const index = state.indexByKey.get(itemKey);
1962
- const prevSizeKnown = state.sizesKnown.get(itemKey);
1963
- const diff = updateOneItemSize(state, itemKey, sizeObj);
1964
- const size = Math.floor((horizontal ? sizeObj.width : sizeObj.height) * 8) / 8;
1965
- if (diff !== 0) {
1966
- minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
1967
- if (((_a = state.scrollingTo) == null ? void 0 : _a.viewPosition) && maintainVisibleContentPosition && index === state.scrollingTo.index && diff > 0) {
1968
- requestAdjust(ctx, state, diff * state.scrollingTo.viewPosition);
1969
- }
1970
- const { startBuffered, endBuffered } = state;
1971
- needsRecalculate || (needsRecalculate = index >= startBuffered && index <= endBuffered);
1972
- if (!needsRecalculate) {
1973
- const numContainers = ctx.values.get("numContainers");
1974
- for (let i = 0; i < numContainers; i++) {
1975
- if (peek$(ctx, `containerItemKey${i}`) === itemKey) {
1976
- needsRecalculate = true;
1977
- break;
1978
- }
2446
+ const index = state.indexByKey.get(itemKey);
2447
+ const prevSizeKnown = state.sizesKnown.get(itemKey);
2448
+ const diff = updateOneItemSize(state, itemKey, sizeObj);
2449
+ const size = Math.floor((horizontal ? sizeObj.width : sizeObj.height) * 8) / 8;
2450
+ if (diff !== 0) {
2451
+ minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
2452
+ if (((_b = state.scrollingTo) == null ? void 0 : _b.viewPosition) && maintainVisibleContentPosition && index === state.scrollingTo.index && diff > 0) {
2453
+ requestAdjust(ctx, state, diff * state.scrollingTo.viewPosition);
2454
+ }
2455
+ const { startBuffered, endBuffered } = state;
2456
+ needsRecalculate || (needsRecalculate = index >= startBuffered && index <= endBuffered);
2457
+ if (!needsRecalculate) {
2458
+ const numContainers = ctx.values.get("numContainers");
2459
+ for (let i = 0; i < numContainers; i++) {
2460
+ if (peek$(ctx, `containerItemKey${i}`) === itemKey) {
2461
+ needsRecalculate = true;
2462
+ break;
1979
2463
  }
1980
2464
  }
1981
- if (state.needsOtherAxisSize) {
1982
- const otherAxisSize = horizontal ? sizeObj.height : sizeObj.width;
1983
- maxOtherAxisSize = Math.max(maxOtherAxisSize, otherAxisSize);
1984
- }
1985
- if (prevSizeKnown !== void 0 && Math.abs(prevSizeKnown - size) > 5) {
1986
- shouldMaintainScrollAtEnd = true;
1987
- }
1988
- onItemSizeChanged == null ? void 0 : onItemSizeChanged({
1989
- index,
1990
- itemData: state.props.data[index],
1991
- itemKey,
1992
- previous: size - diff,
1993
- size
1994
- });
1995
2465
  }
2466
+ if (state.needsOtherAxisSize) {
2467
+ const otherAxisSize = horizontal ? sizeObj.height : sizeObj.width;
2468
+ maxOtherAxisSize = Math.max(maxOtherAxisSize, otherAxisSize);
2469
+ }
2470
+ if (prevSizeKnown !== void 0 && Math.abs(prevSizeKnown - size) > 5) {
2471
+ shouldMaintainScrollAtEnd = true;
2472
+ }
2473
+ onItemSizeChanged == null ? void 0 : onItemSizeChanged({
2474
+ index,
2475
+ itemData: state.props.data[index],
2476
+ itemKey,
2477
+ previous: size - diff,
2478
+ size
2479
+ });
1996
2480
  }
1997
2481
  if (minIndexSizeChanged !== void 0) {
1998
2482
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
@@ -2025,43 +2509,29 @@ function updateItemSizes(ctx, state, itemUpdates) {
2025
2509
  }
2026
2510
  }
2027
2511
  }
2028
- function updateItemSize(ctx, state, itemKey, sizeObj) {
2029
- const { queuedItemSizeUpdates, queuedItemSizeUpdatesWaiting } = state;
2030
- const containersDidLayout = peek$(ctx, "containersDidLayout");
2031
- if (!containersDidLayout || !queuedItemSizeUpdatesWaiting) {
2032
- updateItemSizes(ctx, state, [{ itemKey, sizeObj }]);
2033
- if (containersDidLayout) {
2034
- state.queuedItemSizeUpdatesWaiting = true;
2035
- requestAnimationFrame(() => {
2036
- state.queuedItemSizeUpdatesWaiting = false;
2037
- updateItemSizes(ctx, state, queuedItemSizeUpdates);
2038
- queuedItemSizeUpdates.length = 0;
2039
- });
2040
- }
2041
- } else {
2042
- queuedItemSizeUpdates.push({ itemKey, sizeObj });
2043
- }
2044
- }
2045
2512
  function updateOneItemSize(state, itemKey, sizeObj) {
2513
+ var _a;
2046
2514
  const {
2047
2515
  sizes,
2048
2516
  indexByKey,
2049
2517
  sizesKnown,
2050
2518
  averageSizes,
2051
- props: { data, horizontal }
2519
+ props: { data, horizontal, getEstimatedItemSize, getItemType, getFixedItemSize }
2052
2520
  } = state;
2053
2521
  if (!data) return 0;
2054
2522
  const index = indexByKey.get(itemKey);
2055
2523
  const prevSize = getItemSize(state, itemKey, index, data);
2056
2524
  const size = Math.floor((horizontal ? sizeObj.width : sizeObj.height) * 8) / 8;
2057
2525
  sizesKnown.set(itemKey, size);
2058
- const itemType = "";
2059
- let averages = averageSizes[itemType];
2060
- if (!averages) {
2061
- averages = averageSizes[itemType] = { avg: 0, num: 0 };
2526
+ if (!getEstimatedItemSize && !getFixedItemSize && size > 0) {
2527
+ const itemType = getItemType ? (_a = getItemType(data[index], index)) != null ? _a : "" : "";
2528
+ let averages = averageSizes[itemType];
2529
+ if (!averages) {
2530
+ averages = averageSizes[itemType] = { avg: 0, num: 0 };
2531
+ }
2532
+ averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
2533
+ averages.num++;
2062
2534
  }
2063
- averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
2064
- averages.num++;
2065
2535
  if (!prevSize || Math.abs(prevSize - size) > 0.1) {
2066
2536
  sizes.set(itemKey, size);
2067
2537
  return size - prevSize;
@@ -2099,86 +2569,165 @@ function createColumnWrapperStyle(contentContainerStyle) {
2099
2569
  }
2100
2570
  }
2101
2571
  function getRenderedItem(ctx, state, key) {
2572
+ var _a;
2102
2573
  if (!state) {
2103
2574
  return null;
2104
2575
  }
2105
2576
  const {
2106
2577
  indexByKey,
2107
- props: { data, renderItem: renderItem2 }
2578
+ props: { data, getItemType, renderItem }
2108
2579
  } = state;
2109
2580
  const index = indexByKey.get(key);
2110
2581
  if (index === void 0) {
2111
2582
  return null;
2112
2583
  }
2113
2584
  let renderedItem = null;
2114
- if (renderItem2) {
2585
+ if (renderItem && data[index]) {
2115
2586
  const itemProps = {
2587
+ data,
2116
2588
  extraData: peek$(ctx, "extraData"),
2117
2589
  index,
2118
- item: data[index]
2590
+ item: data[index],
2591
+ type: getItemType ? (_a = getItemType(data[index], index)) != null ? _a : "" : ""
2119
2592
  };
2120
- renderedItem = React3__namespace.default.createElement(renderItem2, itemProps);
2593
+ renderedItem = isFunction(renderItem) ? renderItem(itemProps) : React3__namespace.default.createElement(renderItem, itemProps);
2121
2594
  }
2122
2595
  return { index, item: data[index], renderedItem };
2123
2596
  }
2124
2597
 
2598
+ // src/utils/throttledOnScroll.ts
2599
+ function useThrottledOnScroll(originalHandler, scrollEventThrottle) {
2600
+ const throttle = useThrottleDebounce("throttle");
2601
+ return (event) => throttle(originalHandler, scrollEventThrottle, { nativeEvent: event.nativeEvent });
2602
+ }
2603
+
2604
+ // src/utils/updateAveragesOnDataChange.ts
2605
+ function updateAveragesOnDataChange(state, oldData, newData) {
2606
+ var _a;
2607
+ const {
2608
+ averageSizes,
2609
+ sizesKnown,
2610
+ indexByKey,
2611
+ props: { itemsAreEqual, getItemType, keyExtractor }
2612
+ } = state;
2613
+ if (!itemsAreEqual || !oldData.length || !newData.length) {
2614
+ for (const key in averageSizes) {
2615
+ delete averageSizes[key];
2616
+ }
2617
+ return;
2618
+ }
2619
+ const itemTypesToPreserve = {};
2620
+ const newDataLength = newData.length;
2621
+ const oldDataLength = oldData.length;
2622
+ for (let newIndex = 0; newIndex < newDataLength; newIndex++) {
2623
+ const newItem = newData[newIndex];
2624
+ const id = keyExtractor ? keyExtractor(newItem, newIndex) : String(newIndex);
2625
+ const oldIndex = indexByKey.get(id);
2626
+ if (oldIndex !== void 0 && oldIndex < oldDataLength) {
2627
+ const knownSize = sizesKnown.get(id);
2628
+ if (knownSize === void 0) continue;
2629
+ const oldItem = oldData[oldIndex];
2630
+ const areEqual = itemsAreEqual(oldItem, newItem, newIndex, newData);
2631
+ if (areEqual) {
2632
+ const itemType = getItemType ? (_a = getItemType(newItem, newIndex)) != null ? _a : "" : "";
2633
+ let typeData = itemTypesToPreserve[itemType];
2634
+ if (!typeData) {
2635
+ typeData = itemTypesToPreserve[itemType] = { count: 0, totalSize: 0 };
2636
+ }
2637
+ typeData.totalSize += knownSize;
2638
+ typeData.count++;
2639
+ }
2640
+ }
2641
+ }
2642
+ for (const key in averageSizes) {
2643
+ delete averageSizes[key];
2644
+ }
2645
+ for (const itemType in itemTypesToPreserve) {
2646
+ const { totalSize, count } = itemTypesToPreserve[itemType];
2647
+ if (count > 0) {
2648
+ averageSizes[itemType] = {
2649
+ avg: totalSize / count,
2650
+ num: count
2651
+ };
2652
+ }
2653
+ }
2654
+ }
2655
+
2125
2656
  // src/components/LegendList.tsx
2126
2657
  var DEFAULT_DRAW_DISTANCE = 250;
2127
2658
  var DEFAULT_ITEM_SIZE = 100;
2128
- var LegendList = typedForwardRef(function LegendList2(props, forwardedRef) {
2129
- return /* @__PURE__ */ React3__namespace.createElement(StateProvider, null, /* @__PURE__ */ React3__namespace.createElement(LegendListInner, { ...props, ref: forwardedRef }));
2130
- });
2659
+ var LegendList = typedMemo(
2660
+ typedForwardRef(function LegendList2(props, forwardedRef) {
2661
+ const { children, data: dataProp, renderItem: renderItemProp, ...restProps } = props;
2662
+ const isChildrenMode = children !== void 0 && dataProp === void 0;
2663
+ const processedProps = isChildrenMode ? {
2664
+ ...restProps,
2665
+ data: (isArray(children) ? children : React3__namespace.Children.toArray(children)).flat(1),
2666
+ renderItem: ({ item }) => item
2667
+ } : {
2668
+ ...restProps,
2669
+ data: dataProp || [],
2670
+ renderItem: renderItemProp
2671
+ };
2672
+ return /* @__PURE__ */ React3__namespace.createElement(StateProvider, null, /* @__PURE__ */ React3__namespace.createElement(LegendListInner, { ...processedProps, ref: forwardedRef }));
2673
+ })
2674
+ );
2131
2675
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
2132
2676
  var _a;
2133
2677
  const {
2134
- data: dataProp = [],
2135
- initialScrollIndex: initialScrollIndexProp,
2136
- initialScrollOffset,
2137
- horizontal,
2138
- drawDistance = 250,
2139
- recycleItems = false,
2140
- onEndReachedThreshold = 0.5,
2141
- onStartReachedThreshold = 0.5,
2142
- maintainScrollAtEnd = false,
2143
- maintainScrollAtEndThreshold = 0.1,
2144
2678
  alignItemsAtEnd = false,
2145
- maintainVisibleContentPosition = false,
2146
- onScroll: onScrollProp,
2147
- onMomentumScrollEnd,
2148
- numColumns: numColumnsProp = 1,
2149
2679
  columnWrapperStyle,
2150
- keyExtractor: keyExtractorProp,
2151
- renderItem: renderItem2,
2152
- estimatedListSize,
2680
+ contentContainerStyle: contentContainerStyleProp,
2681
+ data: dataProp = [],
2682
+ drawDistance = 250,
2683
+ enableAverages = true,
2153
2684
  estimatedItemSize: estimatedItemSizeProp,
2685
+ estimatedListSize,
2686
+ extraData,
2154
2687
  getEstimatedItemSize,
2155
- suggestEstimatedItemSize,
2156
- ListHeaderComponent,
2688
+ getFixedItemSize,
2689
+ getItemType,
2690
+ horizontal,
2691
+ initialContainerPoolRatio = 2,
2692
+ initialScrollIndex: initialScrollIndexProp,
2693
+ initialScrollOffset: initialScrollOffsetProp,
2694
+ itemsAreEqual,
2695
+ keyExtractor: keyExtractorProp,
2157
2696
  ListEmptyComponent,
2697
+ ListHeaderComponent,
2698
+ maintainScrollAtEnd = false,
2699
+ maintainScrollAtEndThreshold = 0.1,
2700
+ maintainVisibleContentPosition = true,
2701
+ numColumns: numColumnsProp = 1,
2702
+ onEndReached,
2703
+ onEndReachedThreshold = 0.5,
2158
2704
  onItemSizeChanged,
2159
- refScrollView,
2160
- waitForInitialLayout = true,
2161
- extraData,
2162
- contentContainerStyle: contentContainerStyleProp,
2163
- style: styleProp,
2164
2705
  onLayout: onLayoutProp,
2706
+ onLoad,
2707
+ onMomentumScrollEnd,
2165
2708
  onRefresh,
2166
- refreshing,
2709
+ onScroll: onScrollProp,
2710
+ onStartReached,
2711
+ onStartReachedThreshold = 0.5,
2712
+ onViewableItemsChanged,
2167
2713
  progressViewOffset,
2714
+ recycleItems = false,
2168
2715
  refreshControl,
2169
- initialContainerPoolRatio = 2,
2716
+ refreshing,
2717
+ refScrollView,
2718
+ renderItem,
2719
+ scrollEventThrottle,
2720
+ snapToIndices,
2721
+ stickyIndices,
2722
+ style: styleProp,
2723
+ suggestEstimatedItemSize,
2170
2724
  viewabilityConfig,
2171
2725
  viewabilityConfigCallbackPairs,
2172
- snapToIndices,
2173
- onViewableItemsChanged,
2174
- onStartReached,
2175
- onEndReached,
2176
- onLoad,
2726
+ waitForInitialLayout = true,
2177
2727
  ...rest
2178
2728
  } = props;
2179
2729
  const [renderNum, setRenderNum] = React3.useState(0);
2180
- const initialScroll = typeof initialScrollIndexProp === "number" ? { index: initialScrollIndexProp } : initialScrollIndexProp;
2181
- const initialScrollIndex = initialScroll == null ? void 0 : initialScroll.index;
2730
+ const initialScroll = initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? { index: initialScrollIndexProp.index || 0, viewOffset: initialScrollIndexProp.viewOffset || 0 } : { index: initialScrollIndexProp || 0, viewOffset: initialScrollOffsetProp || 0 } : void 0;
2182
2731
  const [canRender, setCanRender] = React3__namespace.useState(!IsNewArchitecture);
2183
2732
  const contentContainerStyle = { ...reactNative.StyleSheet.flatten(contentContainerStyleProp) };
2184
2733
  const style = { ...reactNative.StyleSheet.flatten(styleProp) };
@@ -2195,9 +2744,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2195
2744
  if (!refState.current) {
2196
2745
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : IsNewArchitecture ? { height: 0, width: 0 } : reactNative.Dimensions.get("window"))[horizontal ? "width" : "height"];
2197
2746
  refState.current = {
2747
+ activeStickyIndex: void 0,
2198
2748
  averageSizes: {},
2199
2749
  columns: /* @__PURE__ */ new Map(),
2200
2750
  containerItemKeys: /* @__PURE__ */ new Set(),
2751
+ containerItemTypes: /* @__PURE__ */ new Map(),
2201
2752
  enableScrollForNextCalculateItemsInView: true,
2202
2753
  endBuffered: -1,
2203
2754
  endNoBuffer: -1,
@@ -2216,11 +2767,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2216
2767
  loadStartTime: Date.now(),
2217
2768
  minIndexSizeChanged: 0,
2218
2769
  nativeMarginTop: 0,
2219
- pendingAdjust: 0,
2770
+ positionRange: { end: -1, start: -1, valid: false },
2220
2771
  positions: /* @__PURE__ */ new Map(),
2221
2772
  props: {},
2222
2773
  queuedCalculateItemsInView: 0,
2223
- queuedItemSizeUpdates: [],
2224
2774
  refScroller: void 0,
2225
2775
  scroll: 0,
2226
2776
  scrollAdjustHandler: new ScrollAdjustHandler(ctx),
@@ -2230,12 +2780,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2230
2780
  scrollPending: 0,
2231
2781
  scrollPrev: 0,
2232
2782
  scrollPrevTime: 0,
2783
+ scrollProcessingEnabled: true,
2233
2784
  scrollTime: 0,
2234
2785
  sizes: /* @__PURE__ */ new Map(),
2235
2786
  sizesKnown: /* @__PURE__ */ new Map(),
2236
2787
  startBuffered: -1,
2237
2788
  startNoBuffer: -1,
2238
2789
  startReachedBlockedByTimer: false,
2790
+ stickyContainerPool: /* @__PURE__ */ new Set(),
2791
+ stickyContainers: /* @__PURE__ */ new Map(),
2239
2792
  timeoutSizeMessage: 0,
2240
2793
  timeouts: /* @__PURE__ */ new Set(),
2241
2794
  totalSize: 0,
@@ -2247,14 +2800,19 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2247
2800
  const state = refState.current;
2248
2801
  const isFirst = !state.props.renderItem;
2249
2802
  const didDataChange = state.props.data !== dataProp;
2803
+ const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
2250
2804
  state.props = {
2251
2805
  alignItemsAtEnd,
2252
2806
  data: dataProp,
2807
+ enableAverages,
2253
2808
  estimatedItemSize,
2254
2809
  getEstimatedItemSize,
2810
+ getFixedItemSize,
2811
+ getItemType,
2255
2812
  horizontal: !!horizontal,
2256
2813
  initialContainerPoolRatio,
2257
2814
  initialScroll,
2815
+ itemsAreEqual,
2258
2816
  keyExtractor,
2259
2817
  maintainScrollAtEnd,
2260
2818
  maintainScrollAtEndThreshold,
@@ -2264,12 +2822,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2264
2822
  onEndReachedThreshold,
2265
2823
  onItemSizeChanged,
2266
2824
  onLoad,
2267
- onScroll: onScrollProp,
2825
+ onScroll: throttleScrollFn,
2268
2826
  onStartReached,
2269
2827
  onStartReachedThreshold,
2270
- renderItem: renderItem2,
2828
+ recycleItems: !!recycleItems,
2829
+ renderItem,
2271
2830
  scrollBuffer,
2272
2831
  snapToIndices,
2832
+ stickyIndicesArr: stickyIndices != null ? stickyIndices : [],
2833
+ stickyIndicesSet: React3.useMemo(() => new Set(stickyIndices != null ? stickyIndices : []), [stickyIndices == null ? void 0 : stickyIndices.join(",")]),
2273
2834
  stylePaddingBottom: stylePaddingBottomState,
2274
2835
  stylePaddingTop: stylePaddingTopState,
2275
2836
  suggestEstimatedItemSize: !!suggestEstimatedItemSize
@@ -2278,6 +2839,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2278
2839
  const checkResetContainers = (isFirst2) => {
2279
2840
  const state2 = refState.current;
2280
2841
  if (state2) {
2842
+ if (!isFirst2 && state2.props.data !== dataProp) {
2843
+ updateAveragesOnDataChange(state2, state2.props.data, dataProp);
2844
+ }
2281
2845
  state2.props.data = dataProp;
2282
2846
  if (!isFirst2) {
2283
2847
  calculateItemsInView(ctx, state2, { dataChanged: true, doMVCP: true });
@@ -2316,15 +2880,22 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2316
2880
  };
2317
2881
  if (isFirst) {
2318
2882
  initializeStateVars();
2319
- updateAllPositions(ctx, state);
2883
+ updateItemPositions(ctx, state);
2320
2884
  }
2321
2885
  const initialContentOffset = React3.useMemo(() => {
2322
- const initialContentOffset2 = initialScrollOffset || calculateOffsetForIndex(ctx, state, initialScrollIndex);
2323
- refState.current.isStartReached = initialContentOffset2 < refState.current.scrollLength * onStartReachedThreshold;
2324
- if (initialContentOffset2 > 0) {
2325
- scrollTo(state, { animated: false, index: initialScrollIndex, offset: initialContentOffset2 });
2886
+ if (initialScroll) {
2887
+ const { index, viewOffset } = initialScroll;
2888
+ let initialContentOffset2 = viewOffset || 0;
2889
+ if (index !== void 0) {
2890
+ initialContentOffset2 += calculateOffsetForIndex(ctx, state, index);
2891
+ }
2892
+ refState.current.isStartReached = initialContentOffset2 < refState.current.scrollLength * onStartReachedThreshold;
2893
+ if (initialContentOffset2 > 0) {
2894
+ scrollTo(state, { animated: false, index, offset: initialContentOffset2 });
2895
+ }
2896
+ return initialContentOffset2;
2326
2897
  }
2327
- return initialContentOffset2;
2898
+ return 0;
2328
2899
  }, [renderNum]);
2329
2900
  if (isFirst || didDataChange || numColumnsProp !== peek$(ctx, "numColumns")) {
2330
2901
  refState.current.lastBatchingAction = Date.now();
@@ -2337,27 +2908,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2337
2908
  refState.current.positions.clear();
2338
2909
  }
2339
2910
  }
2340
- React3.useLayoutEffect(() => {
2341
- if (IsNewArchitecture) {
2342
- let measured;
2343
- refScroller.current.measure((x, y, width, height) => {
2344
- measured = { height, width, x, y };
2345
- });
2346
- if (measured) {
2347
- const size = Math.floor(measured[horizontal ? "width" : "height"] * 8) / 8;
2348
- if (size) {
2349
- handleLayout(ctx, state, measured, setCanRender);
2350
- }
2351
- }
2352
- }
2353
- if (!isFirst) {
2354
- calculateItemsInView(ctx, state, { doMVCP: true });
2355
- }
2356
- }, [dataProp]);
2357
2911
  const onLayoutHeader = React3.useCallback((rect, fromLayoutEffect) => {
2358
2912
  const size = rect[horizontal ? "width" : "height"];
2359
2913
  set$(ctx, "headerSize", size);
2360
- if (initialScroll) {
2914
+ if ((initialScroll == null ? void 0 : initialScroll.index) !== void 0) {
2361
2915
  if (IsNewArchitecture && reactNative.Platform.OS !== "android") {
2362
2916
  if (fromLayoutEffect) {
2363
2917
  setRenderNum((v) => v + 1);
@@ -2375,7 +2929,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2375
2929
  }
2376
2930
  }, [snapToIndices]);
2377
2931
  React3.useLayoutEffect(() => {
2378
- const didAllocateContainers = doInitialAllocateContainersCallback();
2932
+ const didAllocateContainers = dataProp.length > 0 && doInitialAllocateContainersCallback();
2379
2933
  if (!didAllocateContainers) {
2380
2934
  checkResetContainers(
2381
2935
  /*isFirst*/
@@ -2386,6 +2940,20 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2386
2940
  React3.useLayoutEffect(() => {
2387
2941
  set$(ctx, "extraData", extraData);
2388
2942
  }, [extraData]);
2943
+ React3.useLayoutEffect(() => {
2944
+ if (IsNewArchitecture) {
2945
+ let measured;
2946
+ refScroller.current.measure((x, y, width, height) => {
2947
+ measured = { height, width, x, y };
2948
+ });
2949
+ if (measured) {
2950
+ const size = Math.floor(measured[horizontal ? "width" : "height"] * 8) / 8;
2951
+ if (size) {
2952
+ handleLayout(ctx, state, measured, setCanRender);
2953
+ }
2954
+ }
2955
+ }
2956
+ }, []);
2389
2957
  React3.useLayoutEffect(initializeStateVars, [
2390
2958
  memoizedLastItemKeys.join(","),
2391
2959
  numColumnsProp,
@@ -2441,10 +3009,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2441
3009
  const state2 = refState.current;
2442
3010
  return state2 ? {
2443
3011
  contentLength: state2.totalSize,
3012
+ data: state2.props.data,
2444
3013
  end: state2.endNoBuffer,
2445
3014
  endBuffered: state2.endBuffered,
2446
3015
  isAtEnd: state2.isAtEnd,
2447
3016
  isAtStart: state2.isAtStart,
3017
+ positionAtIndex: (index) => getPositionByIndex(ctx, state2, index),
2448
3018
  positions: state2.positions,
2449
3019
  scroll: state2.scroll,
2450
3020
  scrollLength: state2.scrollLength,
@@ -2471,7 +3041,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2471
3041
  const footerSize = peek$(ctx, "footerSize") || 0;
2472
3042
  scrollToIndex(ctx, state, {
2473
3043
  index,
2474
- viewOffset: -paddingBottom - footerSize,
3044
+ viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
2475
3045
  viewPosition: 1,
2476
3046
  ...options
2477
3047
  });
@@ -2486,6 +3056,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2486
3056
  }
2487
3057
  },
2488
3058
  scrollToOffset: (params) => scrollTo(state, params),
3059
+ setScrollProcessingEnabled: (enabled) => {
3060
+ refState.current.scrollProcessingEnabled = enabled;
3061
+ },
2489
3062
  setVisibleContentAnchorOffset: (value) => {
2490
3063
  const val = typeof value === "function" ? value(peek$(ctx, "scrollAdjustUserOffset") || 0) : value;
2491
3064
  set$(ctx, "scrollAdjustUserOffset", val);
@@ -2507,6 +3080,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2507
3080
  }),
2508
3081
  []
2509
3082
  );
3083
+ const onScrollHandler = React3.useMemo(() => {
3084
+ const onScrollFn = fns.onScroll;
3085
+ if (stickyIndices == null ? void 0 : stickyIndices.length) {
3086
+ const { animatedScrollY } = ctx;
3087
+ return reactNative.Animated.event([{ nativeEvent: { contentOffset: { [horizontal ? "x" : "y"]: animatedScrollY } } }], {
3088
+ listener: onScrollFn,
3089
+ useNativeDriver: true
3090
+ });
3091
+ }
3092
+ return onScrollFn;
3093
+ }, [stickyIndices == null ? void 0 : stickyIndices.length, horizontal, scrollEventThrottle]);
2510
3094
  return /* @__PURE__ */ React3__namespace.createElement(React3__namespace.Fragment, null, /* @__PURE__ */ React3__namespace.createElement(
2511
3095
  ListComponent,
2512
3096
  {
@@ -2523,14 +3107,20 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2523
3107
  onLayout,
2524
3108
  onLayoutHeader,
2525
3109
  onMomentumScrollEnd: (event) => {
2526
- requestAnimationFrame(() => {
2527
- finishScrollTo(refState.current);
2528
- });
3110
+ if (IsNewArchitecture) {
3111
+ requestAnimationFrame(() => {
3112
+ finishScrollTo(refState.current);
3113
+ });
3114
+ } else {
3115
+ setTimeout(() => {
3116
+ finishScrollTo(refState.current);
3117
+ }, 1e3);
3118
+ }
2529
3119
  if (onMomentumScrollEnd) {
2530
3120
  onMomentumScrollEnd(event);
2531
3121
  }
2532
3122
  },
2533
- onScroll: fns.onScroll,
3123
+ onScroll: onScrollHandler,
2534
3124
  recycleItems,
2535
3125
  refreshControl: refreshControl ? stylePaddingTopState > 0 ? React3__namespace.cloneElement(refreshControl, {
2536
3126
  progressViewOffset: (refreshControl.props.progressViewOffset || 0) + stylePaddingTopState
@@ -2546,6 +3136,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2546
3136
  scrollAdjustHandler: (_a = refState.current) == null ? void 0 : _a.scrollAdjustHandler,
2547
3137
  scrollEventThrottle: reactNative.Platform.OS === "web" ? 16 : void 0,
2548
3138
  snapToIndices,
3139
+ stickyIndices,
2549
3140
  style,
2550
3141
  updateItemSize: fns.updateItemSize,
2551
3142
  waitForInitialLayout
@@ -2553,24 +3144,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2553
3144
  ), __DEV__ && ENABLE_DEBUG_VIEW && /* @__PURE__ */ React3__namespace.createElement(DebugView, { state: refState.current }));
2554
3145
  });
2555
3146
 
2556
- // src/components/LazyLegendList.tsx
2557
- var typedForwardRef2 = React3.forwardRef;
2558
- var renderItem = ({ item }) => item;
2559
- var LazyLegendList = typedForwardRef2(function LazyLegendList2(props, forwardedRef) {
2560
- const { LegendList: LegendListProp, children, ...rest } = props;
2561
- const LegendListComponent = LegendListProp != null ? LegendListProp : LegendList;
2562
- const data = (isArray(children) ? children : React3__namespace.Children.toArray(children)).flat(1);
2563
- return (
2564
- // @ts-expect-error TODO: Fix this type
2565
- /* @__PURE__ */ React3__namespace.createElement(LegendListComponent, { ...rest, data, ref: forwardedRef, renderItem })
2566
- );
2567
- });
2568
-
2569
- exports.LazyLegendList = LazyLegendList;
2570
3147
  exports.LegendList = LegendList;
2571
3148
  exports.useIsLastItem = useIsLastItem;
2572
3149
  exports.useListScrollSize = useListScrollSize;
2573
3150
  exports.useRecyclingEffect = useRecyclingEffect;
2574
3151
  exports.useRecyclingState = useRecyclingState;
3152
+ exports.useSyncLayout = useSyncLayout;
2575
3153
  exports.useViewability = useViewability;
2576
3154
  exports.useViewabilityAmount = useViewabilityAmount;