@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.mjs CHANGED
@@ -1,17 +1,19 @@
1
1
  import * as React3 from 'react';
2
- import React3__default, { useReducer, useEffect, createContext, useRef, useState, useMemo, useLayoutEffect, useCallback, useImperativeHandle, useContext, forwardRef, memo } from 'react';
3
- import { View, Text, Platform, Animated, ScrollView, StyleSheet, Dimensions, RefreshControl, unstable_batchedUpdates } from 'react-native';
2
+ import React3__default, { useReducer, useEffect, createContext, useRef, useState, useMemo, useLayoutEffect, useCallback, useImperativeHandle, forwardRef, memo, useContext } from 'react';
3
+ import { View, Text, Platform, Animated, StyleSheet, Dimensions, RefreshControl, unstable_batchedUpdates } from 'react-native';
4
4
  import { useSyncExternalStore } from 'use-sync-external-store/shim';
5
5
 
6
- // src/components/LazyLegendList.tsx
6
+ // src/components/LegendList.tsx
7
7
  var ContextState = React3.createContext(null);
8
8
  function StateProvider({ children }) {
9
9
  const [value] = React3.useState(() => ({
10
+ animatedScrollY: new Animated.Value(0),
10
11
  columnWrapperStyle: void 0,
11
12
  listeners: /* @__PURE__ */ new Map(),
12
13
  mapViewabilityAmountCallbacks: /* @__PURE__ */ new Map(),
13
14
  mapViewabilityAmountValues: /* @__PURE__ */ new Map(),
14
15
  mapViewabilityCallbacks: /* @__PURE__ */ new Map(),
16
+ mapViewabilityConfigStates: /* @__PURE__ */ new Map(),
15
17
  mapViewabilityValues: /* @__PURE__ */ new Map(),
16
18
  values: /* @__PURE__ */ new Map([
17
19
  ["alignItemsPaddingTop", 0],
@@ -164,18 +166,127 @@ var LeanViewComponent = React3.forwardRef((props, ref) => {
164
166
  LeanViewComponent.displayName = "RCTView";
165
167
  var LeanView = Platform.OS === "android" || Platform.OS === "ios" ? LeanViewComponent : View;
166
168
 
167
- // src/components/Separator.tsx
168
- function Separator({ ItemSeparatorComponent, itemKey, leadingItem }) {
169
- const [lastItemKeys] = useArr$(["lastItemKeys"]);
170
- const isALastItem = lastItemKeys.includes(itemKey);
171
- return isALastItem ? null : /* @__PURE__ */ React.createElement(ItemSeparatorComponent, { leadingItem });
172
- }
173
-
174
169
  // src/constants.ts
175
170
  var POSITION_OUT_OF_VIEW = -1e7;
176
171
  var ENABLE_DEVMODE = __DEV__ && false;
177
172
  var ENABLE_DEBUG_VIEW = __DEV__ && false;
178
173
  var IsNewArchitecture = global.nativeFabricUIManager != null;
174
+ var useAnimatedValue = (initialValue) => {
175
+ return useRef(new Animated.Value(initialValue)).current;
176
+ };
177
+
178
+ // src/hooks/useValue$.ts
179
+ function useValue$(key, params) {
180
+ var _a;
181
+ const { getValue, delay } = params || {};
182
+ const ctx = useStateContext();
183
+ const animValue = useAnimatedValue((_a = getValue ? getValue(peek$(ctx, key)) : peek$(ctx, key)) != null ? _a : 0);
184
+ useMemo(() => {
185
+ let newValue;
186
+ let prevValue;
187
+ let didQueueTask = false;
188
+ listen$(ctx, key, (v) => {
189
+ newValue = getValue ? getValue(v) : v;
190
+ if (delay !== void 0) {
191
+ const fn = () => {
192
+ didQueueTask = false;
193
+ if (newValue !== void 0) {
194
+ animValue.setValue(newValue);
195
+ }
196
+ };
197
+ const delayValue = typeof delay === "function" ? delay(newValue, prevValue) : delay;
198
+ prevValue = newValue;
199
+ if (!didQueueTask) {
200
+ didQueueTask = true;
201
+ if (delayValue === 0) {
202
+ queueMicrotask(fn);
203
+ } else {
204
+ setTimeout(fn, delayValue);
205
+ }
206
+ }
207
+ } else {
208
+ animValue.setValue(newValue);
209
+ }
210
+ });
211
+ }, []);
212
+ return animValue;
213
+ }
214
+ var typedForwardRef = forwardRef;
215
+ var typedMemo = memo;
216
+
217
+ // src/components/PositionView.tsx
218
+ var PositionViewState = typedMemo(function PositionView({
219
+ id,
220
+ horizontal,
221
+ style,
222
+ refView,
223
+ ...rest
224
+ }) {
225
+ const [position = POSITION_OUT_OF_VIEW] = useArr$([`containerPosition${id}`]);
226
+ return /* @__PURE__ */ React3.createElement(
227
+ LeanView,
228
+ {
229
+ ref: refView,
230
+ style: [
231
+ style,
232
+ horizontal ? { transform: [{ translateX: position }] } : { transform: [{ translateY: position }] }
233
+ ],
234
+ ...rest
235
+ }
236
+ );
237
+ });
238
+ var PositionViewAnimated = typedMemo(function PositionView2({
239
+ id,
240
+ horizontal,
241
+ style,
242
+ refView,
243
+ ...rest
244
+ }) {
245
+ const position$ = useValue$(`containerPosition${id}`, {
246
+ getValue: (v) => v != null ? v : POSITION_OUT_OF_VIEW
247
+ });
248
+ return /* @__PURE__ */ React3.createElement(
249
+ Animated.View,
250
+ {
251
+ ref: refView,
252
+ style: [
253
+ style,
254
+ horizontal ? { transform: [{ translateX: position$ }] } : { transform: [{ translateY: position$ }] }
255
+ ],
256
+ ...rest
257
+ }
258
+ );
259
+ });
260
+ var PositionViewSticky = typedMemo(function PositionViewSticky2({
261
+ id,
262
+ horizontal,
263
+ style,
264
+ refView,
265
+ animatedScrollY,
266
+ stickyOffset,
267
+ index,
268
+ ...rest
269
+ }) {
270
+ const [position = POSITION_OUT_OF_VIEW, headerSize] = useArr$([`containerPosition${id}`, "headerSize"]);
271
+ const transform = React3.useMemo(() => {
272
+ if (animatedScrollY && stickyOffset) {
273
+ const stickyPosition = animatedScrollY.interpolate({
274
+ extrapolate: "clamp",
275
+ inputRange: [position + headerSize, position + 5e3 + headerSize],
276
+ outputRange: [position, position + 5e3]
277
+ });
278
+ return horizontal ? [{ translateX: stickyPosition }] : [{ translateY: stickyPosition }];
279
+ }
280
+ }, [animatedScrollY, headerSize, horizontal, stickyOffset, position]);
281
+ const viewStyle = React3.useMemo(() => [style, { zIndex: index + 1e3 }, { transform }], [style, transform]);
282
+ return /* @__PURE__ */ React3.createElement(Animated.View, { ref: refView, style: viewStyle, ...rest });
283
+ });
284
+ var PositionView3 = IsNewArchitecture ? PositionViewState : PositionViewAnimated;
285
+ function Separator({ ItemSeparatorComponent, itemKey, leadingItem }) {
286
+ const [lastItemKeys] = useArr$(["lastItemKeys"]);
287
+ const isALastItem = lastItemKeys.includes(itemKey);
288
+ return isALastItem ? null : /* @__PURE__ */ React3.createElement(ItemSeparatorComponent, { leadingItem });
289
+ }
179
290
  var symbolFirst = Symbol();
180
291
  function useInit(cb) {
181
292
  const refValue = useRef(symbolFirst);
@@ -312,8 +423,16 @@ function useListScrollSize() {
312
423
  const [scrollSize] = useArr$(["scrollSize"]);
313
424
  return scrollSize;
314
425
  }
315
- var typedForwardRef = forwardRef;
316
- var typedMemo = memo;
426
+ var noop = () => {
427
+ };
428
+ function useSyncLayout() {
429
+ if (IsNewArchitecture) {
430
+ const { triggerLayout: syncLayout } = useContext(ContextContainer);
431
+ return syncLayout;
432
+ } else {
433
+ return noop;
434
+ }
435
+ }
317
436
 
318
437
  // src/components/Container.tsx
319
438
  var Container = typedMemo(function Container2({
@@ -325,14 +444,15 @@ var Container = typedMemo(function Container2({
325
444
  ItemSeparatorComponent
326
445
  }) {
327
446
  const ctx = useStateContext();
328
- const columnWrapperStyle = ctx.columnWrapperStyle;
329
- const [column = 0, data, itemKey, position = POSITION_OUT_OF_VIEW, numColumns, extraData] = useArr$([
447
+ const { columnWrapperStyle, animatedScrollY } = ctx;
448
+ const [column = 0, data, itemKey, numColumns, extraData, isSticky, stickyOffset] = useArr$([
330
449
  `containerColumn${id}`,
331
450
  `containerItemData${id}`,
332
451
  `containerItemKey${id}`,
333
- `containerPosition${id}`,
334
452
  "numColumns",
335
- "extraData"
453
+ "extraData",
454
+ `containerSticky${id}`,
455
+ `containerStickyOffset${id}`
336
456
  ]);
337
457
  const refLastSize = useRef();
338
458
  const ref = useRef(null);
@@ -340,36 +460,38 @@ var Container = typedMemo(function Container2({
340
460
  const otherAxisPos = numColumns > 1 ? `${(column - 1) / numColumns * 100}%` : 0;
341
461
  const otherAxisSize = numColumns > 1 ? `${1 / numColumns * 100}%` : void 0;
342
462
  let didLayout = false;
343
- let paddingStyles;
344
- if (columnWrapperStyle) {
345
- const { columnGap, rowGap, gap } = columnWrapperStyle;
346
- if (horizontal) {
347
- paddingStyles = {
348
- paddingRight: columnGap || gap || void 0,
349
- paddingVertical: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
350
- };
351
- } else {
352
- paddingStyles = {
353
- paddingBottom: rowGap || gap || void 0,
354
- paddingHorizontal: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
355
- };
463
+ const style = useMemo(() => {
464
+ let paddingStyles;
465
+ if (columnWrapperStyle) {
466
+ const { columnGap, rowGap, gap } = columnWrapperStyle;
467
+ if (horizontal) {
468
+ paddingStyles = {
469
+ paddingRight: columnGap || gap || void 0,
470
+ paddingVertical: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
471
+ };
472
+ } else {
473
+ paddingStyles = {
474
+ paddingBottom: rowGap || gap || void 0,
475
+ paddingHorizontal: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
476
+ };
477
+ }
356
478
  }
357
- }
358
- const style = horizontal ? {
359
- flexDirection: ItemSeparatorComponent ? "row" : void 0,
360
- height: otherAxisSize,
361
- left: position,
362
- position: "absolute",
363
- top: otherAxisPos,
364
- ...paddingStyles || {}
365
- } : {
366
- left: otherAxisPos,
367
- position: "absolute",
368
- right: numColumns > 1 ? null : 0,
369
- top: position,
370
- width: otherAxisSize,
371
- ...paddingStyles || {}
372
- };
479
+ return horizontal ? {
480
+ flexDirection: ItemSeparatorComponent ? "row" : void 0,
481
+ height: otherAxisSize,
482
+ left: 0,
483
+ position: "absolute",
484
+ top: otherAxisPos,
485
+ ...paddingStyles || {}
486
+ } : {
487
+ left: otherAxisPos,
488
+ position: "absolute",
489
+ right: numColumns > 1 ? null : 0,
490
+ top: 0,
491
+ width: otherAxisSize,
492
+ ...paddingStyles || {}
493
+ };
494
+ }, [horizontal, otherAxisPos, otherAxisSize, columnWrapperStyle, numColumns]);
373
495
  const renderedItemInfo = useMemo(
374
496
  () => itemKey !== void 0 ? getRenderedItem2(itemKey) : null,
375
497
  [itemKey, data, extraData]
@@ -434,55 +556,30 @@ var Container = typedMemo(function Container2({
434
556
  }
435
557
  }, [itemKey]);
436
558
  }
437
- return /* @__PURE__ */ React3.createElement(LeanView, { key: recycleItems ? void 0 : itemKey, onLayout, ref, style }, /* @__PURE__ */ React3.createElement(ContextContainer.Provider, { value: contextValue }, renderedItem, renderedItemInfo && ItemSeparatorComponent && /* @__PURE__ */ React3.createElement(
438
- Separator,
559
+ const PositionComponent = isSticky ? PositionViewSticky : PositionView3;
560
+ return /* @__PURE__ */ React3.createElement(
561
+ PositionComponent,
439
562
  {
440
- ItemSeparatorComponent,
441
- itemKey,
442
- leadingItem: renderedItemInfo.item
443
- }
444
- )));
445
- });
446
- var useAnimatedValue = (initialValue) => {
447
- return useRef(new Animated.Value(initialValue)).current;
448
- };
449
-
450
- // src/hooks/useValue$.ts
451
- function useValue$(key, params) {
452
- var _a;
453
- const { getValue, delay } = params || {};
454
- const ctx = useStateContext();
455
- const animValue = useAnimatedValue((_a = getValue ? getValue(peek$(ctx, key)) : peek$(ctx, key)) != null ? _a : 0);
456
- useMemo(() => {
457
- let newValue;
458
- let prevValue;
459
- let didQueueTask = false;
460
- listen$(ctx, key, (v) => {
461
- newValue = getValue ? getValue(v) : v;
462
- if (delay !== void 0) {
463
- const fn = () => {
464
- didQueueTask = false;
465
- if (newValue !== void 0) {
466
- animValue.setValue(newValue);
467
- }
468
- };
469
- const delayValue = typeof delay === "function" ? delay(newValue, prevValue) : delay;
470
- prevValue = newValue;
471
- if (!didQueueTask) {
472
- didQueueTask = true;
473
- if (delayValue === 0) {
474
- queueMicrotask(fn);
475
- } else {
476
- setTimeout(fn, delayValue);
477
- }
478
- }
479
- } else {
480
- animValue.setValue(newValue);
563
+ animatedScrollY: isSticky ? animatedScrollY : void 0,
564
+ horizontal,
565
+ id,
566
+ index,
567
+ key: recycleItems ? void 0 : itemKey,
568
+ onLayout,
569
+ refView: ref,
570
+ stickyOffset: isSticky ? stickyOffset : void 0,
571
+ style
572
+ },
573
+ /* @__PURE__ */ React3.createElement(ContextContainer.Provider, { value: contextValue }, renderedItem, renderedItemInfo && ItemSeparatorComponent && /* @__PURE__ */ React3.createElement(
574
+ Separator,
575
+ {
576
+ ItemSeparatorComponent,
577
+ itemKey,
578
+ leadingItem: renderedItemInfo.item
481
579
  }
482
- });
483
- }, []);
484
- return animValue;
485
- }
580
+ ))
581
+ );
582
+ });
486
583
 
487
584
  // src/components/Containers.tsx
488
585
  var Containers = typedMemo(function Containers2({
@@ -565,7 +662,48 @@ function SnapWrapper({ ScrollComponent, ...props }) {
565
662
  const [snapToOffsets] = useArr$(["snapToOffsets"]);
566
663
  return /* @__PURE__ */ React.createElement(ScrollComponent, { ...props, snapToOffsets });
567
664
  }
568
- function useSyncLayout({
665
+ function useThrottleDebounce(mode) {
666
+ const timeoutRef = useRef(null);
667
+ const lastCallTimeRef = useRef(0);
668
+ const lastArgsRef = useRef(null);
669
+ const clearTimeoutRef = () => {
670
+ if (timeoutRef.current) {
671
+ clearTimeout(timeoutRef.current);
672
+ timeoutRef.current = null;
673
+ }
674
+ };
675
+ const execute = useCallback(
676
+ (callback, delay, ...args) => {
677
+ {
678
+ const now = Date.now();
679
+ lastArgsRef.current = args;
680
+ if (now - lastCallTimeRef.current >= delay) {
681
+ lastCallTimeRef.current = now;
682
+ callback(...args);
683
+ clearTimeoutRef();
684
+ } else {
685
+ clearTimeoutRef();
686
+ timeoutRef.current = setTimeout(
687
+ () => {
688
+ if (lastArgsRef.current) {
689
+ lastCallTimeRef.current = Date.now();
690
+ callback(...lastArgsRef.current);
691
+ timeoutRef.current = null;
692
+ lastArgsRef.current = null;
693
+ }
694
+ },
695
+ delay - (now - lastCallTimeRef.current)
696
+ );
697
+ }
698
+ }
699
+ },
700
+ [mode]
701
+ );
702
+ return execute;
703
+ }
704
+
705
+ // src/hooks/useSyncLayout.tsx
706
+ function useSyncLayout2({
569
707
  onChange
570
708
  }) {
571
709
  const ref = useRef(null);
@@ -642,16 +780,17 @@ var ListComponent = typedMemo(function ListComponent2({
642
780
  scrollAdjustHandler,
643
781
  onLayoutHeader,
644
782
  snapToIndices,
783
+ stickyIndices,
645
784
  ...rest
646
785
  }) {
647
786
  const ctx = useStateContext();
648
- const { onLayout: onLayoutHeaderSync, ref: refHeader } = useSyncLayout({
787
+ const { onLayout: onLayoutHeaderSync, ref: refHeader } = useSyncLayout2({
649
788
  onChange: onLayoutHeader
650
789
  });
651
790
  const ScrollComponent = renderScrollComponent ? useMemo(
652
791
  () => React3.forwardRef((props, ref) => renderScrollComponent({ ...props, ref })),
653
792
  [renderScrollComponent]
654
- ) : ScrollView;
793
+ ) : Animated.ScrollView;
655
794
  React3.useEffect(() => {
656
795
  if (canRender) {
657
796
  setTimeout(() => {
@@ -704,9 +843,26 @@ var ListComponent = typedMemo(function ListComponent2({
704
843
  style: ListFooterComponentStyle
705
844
  },
706
845
  getComponent(ListFooterComponent)
707
- )
846
+ ),
847
+ __DEV__ && ENABLE_DEVMODE && /* @__PURE__ */ React3.createElement(DevNumbers, null)
708
848
  );
709
849
  });
850
+ var DevNumbers = __DEV__ && React3.memo(function DevNumbers2() {
851
+ return Array.from({ length: 100 }).map((_, index) => /* @__PURE__ */ React3.createElement(
852
+ View,
853
+ {
854
+ key: index,
855
+ style: {
856
+ height: 100,
857
+ pointerEvents: "none",
858
+ position: "absolute",
859
+ top: index * 100,
860
+ width: "100%"
861
+ }
862
+ },
863
+ /* @__PURE__ */ React3.createElement(Text, { style: { color: "red" } }, index * 100)
864
+ ));
865
+ });
710
866
 
711
867
  // src/utils/getId.ts
712
868
  function getId(state, index) {
@@ -720,38 +876,33 @@ function getId(state, index) {
720
876
  return id;
721
877
  }
722
878
 
723
- // src/core/calculateOffsetForIndex.ts
724
- function calculateOffsetForIndex(ctx, state, index) {
725
- let position = 0;
726
- if (index !== void 0) {
727
- position = (state == null ? void 0 : state.positions.get(getId(state, index))) || 0;
728
- }
729
- const paddingTop = peek$(ctx, "stylePaddingTop");
730
- if (paddingTop) {
731
- position += paddingTop;
732
- }
733
- const headerSize = peek$(ctx, "headerSize");
734
- if (headerSize) {
735
- position += headerSize;
736
- }
737
- return position;
738
- }
739
-
740
879
  // src/utils/getItemSize.ts
741
880
  function getItemSize(state, key, index, data, useAverageSize) {
881
+ var _a, _b;
742
882
  const {
743
883
  sizesKnown,
744
884
  sizes,
745
885
  scrollingTo,
746
- props: { estimatedItemSize, getEstimatedItemSize }
886
+ averageSizes,
887
+ props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType }
747
888
  } = state;
748
889
  const sizeKnown = sizesKnown.get(key);
749
890
  if (sizeKnown !== void 0) {
750
891
  return sizeKnown;
751
892
  }
752
893
  let size;
753
- if (useAverageSize !== void 0 && sizeKnown === void 0 && !getEstimatedItemSize && !scrollingTo) {
754
- size = useAverageSize;
894
+ const itemType = getItemType ? (_a = getItemType(data, index)) != null ? _a : "" : "";
895
+ if (getFixedItemSize) {
896
+ size = getFixedItemSize(index, data, itemType);
897
+ if (size !== void 0) {
898
+ sizesKnown.set(key, size);
899
+ }
900
+ }
901
+ if (size === void 0 && useAverageSize && sizeKnown === void 0 && !scrollingTo) {
902
+ const averageSizeForType = (_b = averageSizes[itemType]) == null ? void 0 : _b.avg;
903
+ if (averageSizeForType !== void 0) {
904
+ size = roundSize(averageSizeForType);
905
+ }
755
906
  }
756
907
  if (size === void 0) {
757
908
  size = sizes.get(key);
@@ -760,103 +911,21 @@ function getItemSize(state, key, index, data, useAverageSize) {
760
911
  }
761
912
  }
762
913
  if (size === void 0) {
763
- size = getEstimatedItemSize ? getEstimatedItemSize(index, data) : estimatedItemSize;
914
+ size = getEstimatedItemSize ? getEstimatedItemSize(index, data, itemType) : estimatedItemSize;
764
915
  }
765
916
  sizes.set(key, size);
766
917
  return size;
767
918
  }
768
919
 
769
- // src/core/calculateOffsetWithOffsetPosition.ts
770
- function calculateOffsetWithOffsetPosition(state, offsetParam, params) {
771
- const { index, viewOffset, viewPosition } = params;
772
- let offset = offsetParam;
773
- if (viewOffset) {
774
- offset -= viewOffset;
775
- }
776
- if (viewPosition !== void 0 && index !== void 0) {
777
- offset -= viewPosition * (state.scrollLength - getItemSize(state, getId(state, index), index, state.props.data[index]));
778
- }
779
- return offset;
780
- }
781
-
782
- // src/utils/requestAdjust.ts
783
- function requestAdjust(ctx, state, positionDiff) {
784
- if (Math.abs(positionDiff) > 0.1) {
785
- const doit = () => {
786
- state.scrollAdjustHandler.requestAdjust(positionDiff);
787
- };
788
- state.scroll += positionDiff;
789
- state.scrollForNextCalculateItemsInView = void 0;
790
- const didLayout = peek$(ctx, "containersDidLayout");
791
- if (didLayout) {
792
- doit();
793
- const threshold = state.scroll - positionDiff / 2;
794
- if (!state.ignoreScrollFromMVCP) {
795
- state.ignoreScrollFromMVCP = {};
796
- }
797
- if (positionDiff > 0) {
798
- state.ignoreScrollFromMVCP.lt = threshold;
799
- } else {
800
- state.ignoreScrollFromMVCP.gt = threshold;
801
- }
802
- if (state.ignoreScrollFromMVCPTimeout) {
803
- clearTimeout(state.ignoreScrollFromMVCPTimeout);
804
- }
805
- state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
806
- state.ignoreScrollFromMVCP = void 0;
807
- }, 100);
808
- } else {
809
- requestAnimationFrame(doit);
810
- }
811
- }
812
- }
813
-
814
- // src/core/prepareMVCP.ts
815
- function prepareMVCP(ctx, state) {
816
- const {
817
- positions,
818
- scrollingTo,
819
- props: { maintainVisibleContentPosition }
820
- } = state;
821
- let prevPosition;
822
- let targetId;
823
- let targetIndex;
824
- const scrollTarget = scrollingTo == null ? void 0 : scrollingTo.index;
825
- if (maintainVisibleContentPosition) {
826
- const indexByKey = state.indexByKey;
827
- if (scrollTarget !== void 0) {
828
- targetId = getId(state, scrollTarget);
829
- targetIndex = scrollTarget;
830
- } else if (state.idsInView.length > 0 && peek$(ctx, "containersDidLayout")) {
831
- targetId = state.idsInView.find((id) => indexByKey.get(id) !== void 0);
832
- targetIndex = indexByKey.get(targetId);
833
- }
834
- if (targetId !== void 0 && targetIndex !== void 0) {
835
- prevPosition = positions.get(targetId);
836
- }
837
- }
838
- return () => {
839
- if (targetId !== void 0 && prevPosition !== void 0) {
840
- const newPosition = positions.get(targetId);
841
- if (newPosition !== void 0) {
842
- const positionDiff = newPosition - prevPosition;
843
- if (Math.abs(positionDiff) > 0.1) {
844
- requestAdjust(ctx, state, positionDiff);
845
- }
846
- }
847
- }
848
- };
849
- }
850
-
851
920
  // src/utils/setPaddingTop.ts
852
921
  function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
853
922
  if (stylePaddingTop !== void 0) {
854
923
  const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
855
924
  if (stylePaddingTop < prevStylePaddingTop) {
856
- let prevTotalSize = peek$(ctx, "totalSize");
925
+ let prevTotalSize = peek$(ctx, "totalSize") || 0;
857
926
  set$(ctx, "totalSize", prevTotalSize + prevStylePaddingTop);
858
927
  state.timeoutSetPaddingTop = setTimeout(() => {
859
- prevTotalSize = peek$(ctx, "totalSize");
928
+ prevTotalSize = peek$(ctx, "totalSize") || 0;
860
929
  set$(ctx, "totalSize", prevTotalSize - prevStylePaddingTop);
861
930
  }, 16);
862
931
  }
@@ -886,15 +955,15 @@ function updateAlignItemsPaddingTop(ctx, state) {
886
955
  // src/core/updateTotalSize.ts
887
956
  function updateTotalSize(ctx, state) {
888
957
  const {
889
- positions,
890
958
  props: { data }
891
959
  } = state;
892
960
  if (data.length === 0) {
893
961
  addTotalSize(ctx, state, null, 0);
894
962
  } else {
895
- const lastId = getId(state, data.length - 1);
963
+ const lastIndex = data.length - 1;
964
+ const lastId = getId(state, lastIndex);
896
965
  if (lastId !== void 0) {
897
- const lastPosition = positions.get(lastId);
966
+ const lastPosition = getPositionByIndex(ctx, state, lastIndex);
898
967
  if (lastPosition !== void 0) {
899
968
  const lastSize = getItemSize(state, lastId, data.length - 1, data[data.length - 1]);
900
969
  if (lastSize !== void 0) {
@@ -920,122 +989,102 @@ function addTotalSize(ctx, state, key, add) {
920
989
  }
921
990
  }
922
991
 
923
- // src/utils/getScrollVelocity.ts
924
- var getScrollVelocity = (state) => {
925
- const { scrollHistory } = state;
926
- let velocity = 0;
927
- if (scrollHistory.length >= 1) {
928
- const newest = scrollHistory[scrollHistory.length - 1];
929
- let oldest;
930
- let start = 0;
931
- for (let i = 0; i < scrollHistory.length - 1; i++) {
932
- const entry = scrollHistory[i];
933
- const nextEntry = scrollHistory[i + 1];
934
- if (i > 0) {
935
- const prevEntry = scrollHistory[i - 1];
936
- const prevDirection = entry.scroll - prevEntry.scroll;
937
- const currentDirection = nextEntry.scroll - entry.scroll;
938
- if (prevDirection > 0 && currentDirection < 0 || prevDirection < 0 && currentDirection > 0) {
939
- start = i;
940
- break;
941
- }
942
- }
943
- }
944
- for (let i = start; i < scrollHistory.length - 1; i++) {
945
- const entry = scrollHistory[i];
946
- if (newest.time - entry.time <= 1e3) {
947
- oldest = entry;
948
- break;
949
- }
950
- }
951
- if (oldest) {
952
- const scrollDiff = newest.scroll - oldest.scroll;
953
- const timeDiff = newest.time - oldest.time;
954
- velocity = timeDiff > 0 ? scrollDiff / timeDiff : 0;
955
- }
956
- }
957
- return velocity;
958
- };
959
-
960
992
  // src/utils/updateSnapToOffsets.ts
961
993
  function updateSnapToOffsets(ctx, state) {
962
994
  const {
963
- positions,
964
995
  props: { snapToIndices }
965
996
  } = state;
966
997
  const snapToOffsets = Array(snapToIndices.length);
967
998
  for (let i = 0; i < snapToIndices.length; i++) {
968
999
  const idx = snapToIndices[i];
969
- const key = getId(state, idx);
970
- snapToOffsets[i] = positions.get(key);
1000
+ snapToOffsets[i] = getPositionByIndex(ctx, state, idx) || 0;
971
1001
  }
972
1002
  set$(ctx, "snapToOffsets", snapToOffsets);
973
1003
  }
974
1004
 
975
- // src/core/updateAllPositions.ts
976
- function updateAllPositions(ctx, state, dataChanged) {
977
- var _a, _b, _c, _d, _e;
1005
+ // src/core/updateItemPositions.ts
1006
+ function getRequiredRange(_ctx, state) {
1007
+ var _a;
1008
+ const bufferSize = 10;
1009
+ const dataLength = state.props.data.length;
1010
+ let minIndex = 0;
1011
+ let maxIndex = dataLength - 1;
1012
+ if (dataLength < 500) {
1013
+ return { end: maxIndex, start: 0 };
1014
+ }
1015
+ const hasVisibleRange = state.startBuffered >= 0 && state.endBuffered >= 0;
1016
+ if (hasVisibleRange) {
1017
+ minIndex = state.startBuffered;
1018
+ maxIndex = state.endBuffered;
1019
+ }
1020
+ if (((_a = state.scrollingTo) == null ? void 0 : _a.index) !== void 0) {
1021
+ if (hasVisibleRange) {
1022
+ minIndex = Math.min(minIndex, state.scrollingTo.index);
1023
+ maxIndex = Math.max(maxIndex, state.scrollingTo.index);
1024
+ } else {
1025
+ minIndex = state.scrollingTo.index;
1026
+ maxIndex = state.scrollingTo.index;
1027
+ }
1028
+ }
1029
+ minIndex = Math.max(0, minIndex - bufferSize);
1030
+ maxIndex = Math.min(dataLength - 1, maxIndex + bufferSize);
1031
+ return { end: maxIndex, start: minIndex };
1032
+ }
1033
+ function ensurePositionCalculated(ctx, state, index) {
1034
+ if (!state.positionRange) {
1035
+ state.positionRange = { end: -1, start: 0, valid: false };
1036
+ }
1037
+ if (state.positionRange.valid && index >= state.positionRange.start && index <= state.positionRange.end) {
1038
+ return;
1039
+ }
1040
+ const newStart = state.positionRange.valid ? Math.min(state.positionRange.start, index) : 0;
1041
+ const newEnd = Math.min(
1042
+ state.props.data.length - 1,
1043
+ Math.max(state.positionRange.valid ? state.positionRange.end : 0, index + 50)
1044
+ );
1045
+ updateItemPositions(ctx, state, false, newStart, newEnd);
1046
+ }
1047
+ function updateItemPositions(ctx, state, dataChanged, startIndex = 0, endIndex) {
1048
+ var _a, _b, _c, _d, _e, _f, _g;
978
1049
  const {
979
- averageSizes,
980
1050
  columns,
981
1051
  indexByKey,
982
1052
  positions,
983
- firstFullyOnScreenIndex,
984
1053
  idCache,
985
1054
  sizesKnown,
986
- props: { snapToIndices }
1055
+ props: { getEstimatedItemSize, snapToIndices, enableAverages }
987
1056
  } = state;
988
1057
  const data = state.props.data;
989
1058
  const numColumns = peek$(ctx, "numColumns");
990
1059
  const indexByKeyForChecking = __DEV__ ? /* @__PURE__ */ new Map() : void 0;
991
- const scrollVelocity = getScrollVelocity(state);
992
- if (dataChanged) {
993
- indexByKey.clear();
994
- idCache.clear();
995
- }
996
- const itemType = "";
997
- let averageSize = (_a = averageSizes[itemType]) == null ? void 0 : _a.avg;
998
- if (averageSize !== void 0) {
999
- averageSize = roundSize(averageSize);
1000
- }
1001
- const shouldUseBackwards = !dataChanged && scrollVelocity < 0 && firstFullyOnScreenIndex > 5 && firstFullyOnScreenIndex < data.length;
1002
- if (shouldUseBackwards && firstFullyOnScreenIndex !== void 0) {
1003
- const anchorId = getId(state, firstFullyOnScreenIndex);
1004
- const anchorPosition = positions.get(anchorId);
1005
- if (anchorPosition !== void 0) {
1006
- let currentRowTop2 = anchorPosition;
1007
- let maxSizeInRow2 = 0;
1008
- let bailout = false;
1009
- for (let i = firstFullyOnScreenIndex - 1; i >= 0; i--) {
1010
- const id = (_b = idCache.get(i)) != null ? _b : getId(state, i);
1011
- const size = (_c = sizesKnown.get(id)) != null ? _c : getItemSize(state, id, i, data[i], averageSize);
1012
- const itemColumn = columns.get(id);
1013
- maxSizeInRow2 = Math.max(maxSizeInRow2, size);
1014
- if (itemColumn === 1) {
1015
- currentRowTop2 -= maxSizeInRow2;
1016
- maxSizeInRow2 = 0;
1017
- }
1018
- if (currentRowTop2 < -2e3) {
1019
- bailout = true;
1020
- break;
1021
- }
1022
- positions.set(id, currentRowTop2);
1023
- }
1024
- if (!bailout) {
1025
- updateTotalSize(ctx, state);
1026
- return;
1027
- }
1028
- }
1029
- }
1060
+ const useAverageSize = enableAverages && !getEstimatedItemSize;
1030
1061
  let currentRowTop = 0;
1031
1062
  let column = 1;
1032
1063
  let maxSizeInRow = 0;
1033
1064
  const hasColumns = numColumns > 1;
1065
+ if (startIndex > 0) {
1066
+ const prevIndex = startIndex - 1;
1067
+ const prevId = (_a = idCache.get(prevIndex)) != null ? _a : getId(state, prevIndex);
1068
+ const prevPosition = (_b = positions.get(prevId)) != null ? _b : 0;
1069
+ if (hasColumns) {
1070
+ const prevColumn = (_c = columns.get(prevId)) != null ? _c : 1;
1071
+ currentRowTop = prevPosition;
1072
+ column = prevColumn % numColumns + 1;
1073
+ } else {
1074
+ const prevSize = (_d = sizesKnown.get(prevId)) != null ? _d : getItemSize(state, prevId, prevIndex, data[prevIndex], useAverageSize);
1075
+ currentRowTop = prevPosition + prevSize;
1076
+ }
1077
+ }
1034
1078
  const needsIndexByKey = dataChanged || indexByKey.size === 0;
1035
1079
  const dataLength = data.length;
1036
- for (let i = 0; i < dataLength; i++) {
1037
- const id = (_d = idCache.get(i)) != null ? _d : getId(state, i);
1038
- const size = (_e = sizesKnown.get(id)) != null ? _e : getItemSize(state, id, i, data[i], averageSize);
1080
+ const requiredRange = getRequiredRange(ctx, state);
1081
+ const shouldOptimize = dataLength >= 500;
1082
+ const optimizedEndIndex = shouldOptimize ? Math.min(dataLength - 1, requiredRange.end) : dataLength - 1;
1083
+ const actualEndIndex = endIndex !== void 0 ? Math.min(endIndex, dataLength - 1) : optimizedEndIndex;
1084
+ let actualEndReached = startIndex;
1085
+ for (let i = startIndex; i < dataLength; i++) {
1086
+ const id = (_e = idCache.get(i)) != null ? _e : getId(state, i);
1087
+ const size = (_f = sizesKnown.get(id)) != null ? _f : getItemSize(state, id, i, data[i], useAverageSize);
1039
1088
  if (__DEV__ && needsIndexByKey) {
1040
1089
  if (indexByKeyForChecking.has(id)) {
1041
1090
  console.error(
@@ -1062,6 +1111,26 @@ function updateAllPositions(ctx, state, dataChanged) {
1062
1111
  } else {
1063
1112
  currentRowTop += size;
1064
1113
  }
1114
+ actualEndReached = i;
1115
+ if (shouldOptimize && i >= actualEndIndex && (!((_g = state.scrollingTo) == null ? void 0 : _g.index) || i >= state.scrollingTo.index)) {
1116
+ break;
1117
+ }
1118
+ }
1119
+ if (!state.positionRange) {
1120
+ state.positionRange = { end: -1, start: 0, valid: false };
1121
+ }
1122
+ if (dataChanged) {
1123
+ state.positionRange = {
1124
+ end: actualEndReached,
1125
+ start: startIndex,
1126
+ valid: true
1127
+ };
1128
+ } else {
1129
+ state.positionRange = {
1130
+ end: Math.max(state.positionRange.valid ? state.positionRange.end : actualEndReached, actualEndReached),
1131
+ start: Math.min(state.positionRange.valid ? state.positionRange.start : startIndex, startIndex),
1132
+ valid: true
1133
+ };
1065
1134
  }
1066
1135
  updateTotalSize(ctx, state);
1067
1136
  if (snapToIndices) {
@@ -1069,8 +1138,203 @@ function updateAllPositions(ctx, state, dataChanged) {
1069
1138
  }
1070
1139
  }
1071
1140
 
1141
+ // src/utils/getPosition.ts
1142
+ function getPositionByIndex(ctx, state, index) {
1143
+ ensurePositionCalculated(ctx, state, index);
1144
+ const id = getId(state, index);
1145
+ return id ? state.positions.get(id) : void 0;
1146
+ }
1147
+ function getPositionById(ctx, state, id) {
1148
+ const index = state.indexByKey.get(id);
1149
+ if (index === void 0) {
1150
+ return state.positions.get(id);
1151
+ }
1152
+ ensurePositionCalculated(ctx, state, index);
1153
+ return state.positions.get(id);
1154
+ }
1155
+
1156
+ // src/core/calculateOffsetForIndex.ts
1157
+ function calculateOffsetForIndex(ctx, state, index) {
1158
+ let position = 0;
1159
+ if (index !== void 0) {
1160
+ position = getPositionByIndex(ctx, state, index) || 0;
1161
+ const paddingTop = peek$(ctx, "stylePaddingTop");
1162
+ if (paddingTop) {
1163
+ position += paddingTop;
1164
+ }
1165
+ const headerSize = peek$(ctx, "headerSize");
1166
+ if (headerSize) {
1167
+ position += headerSize;
1168
+ }
1169
+ }
1170
+ return position;
1171
+ }
1172
+
1173
+ // src/core/calculateOffsetWithOffsetPosition.ts
1174
+ function calculateOffsetWithOffsetPosition(state, offsetParam, params) {
1175
+ const { index, viewOffset, viewPosition } = params;
1176
+ let offset = offsetParam;
1177
+ if (viewOffset) {
1178
+ offset -= viewOffset;
1179
+ }
1180
+ if (viewPosition !== void 0 && index !== void 0) {
1181
+ offset -= viewPosition * (state.scrollLength - getItemSize(state, getId(state, index), index, state.props.data[index]));
1182
+ }
1183
+ return offset;
1184
+ }
1185
+
1186
+ // src/core/finishScrollTo.ts
1187
+ var finishScrollTo = (state) => {
1188
+ if (state) {
1189
+ state.scrollingTo = void 0;
1190
+ state.scrollHistory.length = 0;
1191
+ }
1192
+ };
1193
+
1194
+ // src/core/scrollTo.ts
1195
+ function scrollTo(state, params = {}) {
1196
+ var _a;
1197
+ const { animated, noScrollingTo } = params;
1198
+ const {
1199
+ refScroller,
1200
+ props: { horizontal }
1201
+ } = state;
1202
+ const offset = calculateOffsetWithOffsetPosition(state, params.offset, params);
1203
+ state.scrollHistory.length = 0;
1204
+ if (!noScrollingTo) {
1205
+ state.scrollingTo = params;
1206
+ }
1207
+ state.scrollPending = offset;
1208
+ (_a = refScroller.current) == null ? void 0 : _a.scrollTo({
1209
+ animated: !!animated,
1210
+ x: horizontal ? offset : 0,
1211
+ y: horizontal ? 0 : offset
1212
+ });
1213
+ if (!animated) {
1214
+ state.scroll = offset;
1215
+ setTimeout(() => finishScrollTo(state), 100);
1216
+ }
1217
+ }
1218
+
1219
+ // src/utils/requestAdjust.ts
1220
+ function requestAdjust(ctx, state, positionDiff, dataChanged) {
1221
+ if (Math.abs(positionDiff) > 0.1) {
1222
+ const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
1223
+ const doit = () => {
1224
+ if (needsScrollWorkaround) {
1225
+ scrollTo(state, {
1226
+ noScrollingTo: true,
1227
+ offset: state.scroll
1228
+ });
1229
+ } else {
1230
+ state.scrollAdjustHandler.requestAdjust(positionDiff);
1231
+ }
1232
+ };
1233
+ state.scroll += positionDiff;
1234
+ state.scrollForNextCalculateItemsInView = void 0;
1235
+ const didLayout = peek$(ctx, "containersDidLayout");
1236
+ if (didLayout) {
1237
+ doit();
1238
+ const threshold = state.scroll - positionDiff / 2;
1239
+ if (!state.ignoreScrollFromMVCP) {
1240
+ state.ignoreScrollFromMVCP = {};
1241
+ }
1242
+ if (positionDiff > 0) {
1243
+ state.ignoreScrollFromMVCP.lt = threshold;
1244
+ } else {
1245
+ state.ignoreScrollFromMVCP.gt = threshold;
1246
+ }
1247
+ if (state.ignoreScrollFromMVCPTimeout) {
1248
+ clearTimeout(state.ignoreScrollFromMVCPTimeout);
1249
+ }
1250
+ state.ignoreScrollFromMVCPTimeout = setTimeout(
1251
+ () => {
1252
+ state.ignoreScrollFromMVCP = void 0;
1253
+ },
1254
+ needsScrollWorkaround ? 250 : 100
1255
+ );
1256
+ } else {
1257
+ requestAnimationFrame(doit);
1258
+ }
1259
+ }
1260
+ }
1261
+
1262
+ // src/core/mvcp.ts
1263
+ function prepareMVCP(ctx, state, dataChanged) {
1264
+ const {
1265
+ idsInView,
1266
+ scrollingTo,
1267
+ props: { maintainVisibleContentPosition }
1268
+ } = state;
1269
+ let prevPosition;
1270
+ let targetId;
1271
+ const idsInViewWithPositions = [];
1272
+ const scrollTarget = scrollingTo == null ? void 0 : scrollingTo.index;
1273
+ if (maintainVisibleContentPosition) {
1274
+ const indexByKey = state.indexByKey;
1275
+ if (scrollTarget !== void 0) {
1276
+ targetId = getId(state, scrollTarget);
1277
+ } else if (idsInView.length > 0 && peek$(ctx, "containersDidLayout")) {
1278
+ if (dataChanged) {
1279
+ for (let i = 0; i < idsInView.length; i++) {
1280
+ const id = idsInView[i];
1281
+ const index = indexByKey.get(id);
1282
+ if (index !== void 0) {
1283
+ const position = getPositionById(ctx, state, id);
1284
+ if (position !== void 0) {
1285
+ idsInViewWithPositions.push({ id, position });
1286
+ }
1287
+ }
1288
+ }
1289
+ } else {
1290
+ targetId = state.idsInView.find((id) => indexByKey.get(id) !== void 0);
1291
+ }
1292
+ }
1293
+ if (targetId !== void 0) {
1294
+ const pos = getPositionById(ctx, state, targetId);
1295
+ if (pos !== void 0) {
1296
+ prevPosition = pos;
1297
+ }
1298
+ }
1299
+ }
1300
+ return () => {
1301
+ let positionDiff;
1302
+ if (dataChanged && targetId === void 0) {
1303
+ for (let i = 0; i < idsInViewWithPositions.length; i++) {
1304
+ const { id, position } = idsInViewWithPositions[i];
1305
+ const newPosition = getPositionById(ctx, state, id);
1306
+ if (newPosition !== void 0) {
1307
+ positionDiff = newPosition - position;
1308
+ break;
1309
+ }
1310
+ }
1311
+ }
1312
+ if (targetId !== void 0 && prevPosition !== void 0) {
1313
+ const newPosition = getPositionById(ctx, state, targetId);
1314
+ if (newPosition !== void 0) {
1315
+ positionDiff = newPosition - prevPosition;
1316
+ }
1317
+ }
1318
+ if (positionDiff !== void 0 && Math.abs(positionDiff) > 0.1) {
1319
+ requestAdjust(ctx, state, positionDiff, dataChanged);
1320
+ }
1321
+ };
1322
+ }
1323
+
1072
1324
  // src/core/viewability.ts
1073
- var mapViewabilityConfigCallbackPairs = /* @__PURE__ */ new Map();
1325
+ function ensureViewabilityState(ctx, configId) {
1326
+ let map = ctx.mapViewabilityConfigStates;
1327
+ if (!map) {
1328
+ map = /* @__PURE__ */ new Map();
1329
+ ctx.mapViewabilityConfigStates = map;
1330
+ }
1331
+ let state = map.get(configId);
1332
+ if (!state) {
1333
+ state = { end: -1, previousEnd: -1, previousStart: -1, start: -1, viewableItems: [] };
1334
+ map.set(configId, state);
1335
+ }
1336
+ return state;
1337
+ }
1074
1338
  function setupViewability(props) {
1075
1339
  let { viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged } = props;
1076
1340
  if (viewabilityConfig || onViewableItemsChanged) {
@@ -1084,17 +1348,6 @@ function setupViewability(props) {
1084
1348
  }
1085
1349
  ];
1086
1350
  }
1087
- if (viewabilityConfigCallbackPairs) {
1088
- for (const pair of viewabilityConfigCallbackPairs) {
1089
- mapViewabilityConfigCallbackPairs.set(pair.viewabilityConfig.id, {
1090
- end: -1,
1091
- previousEnd: -1,
1092
- previousStart: -1,
1093
- start: -1,
1094
- viewableItems: []
1095
- });
1096
- }
1097
- }
1098
1351
  return viewabilityConfigCallbackPairs;
1099
1352
  }
1100
1353
  function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollSize, start, end) {
@@ -1103,9 +1356,7 @@ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollS
1103
1356
  props: { data }
1104
1357
  } = state;
1105
1358
  for (const viewabilityConfigCallbackPair of viewabilityConfigCallbackPairs) {
1106
- const viewabilityState = mapViewabilityConfigCallbackPairs.get(
1107
- viewabilityConfigCallbackPair.viewabilityConfig.id
1108
- );
1359
+ const viewabilityState = ensureViewabilityState(ctx, viewabilityConfigCallbackPair.viewabilityConfig.id);
1109
1360
  viewabilityState.start = start;
1110
1361
  viewabilityState.end = end;
1111
1362
  if (viewabilityConfigCallbackPair.viewabilityConfig.minimumViewTime) {
@@ -1122,7 +1373,7 @@ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollS
1122
1373
  function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, state, ctx, scrollSize) {
1123
1374
  const { viewabilityConfig, onViewableItemsChanged } = viewabilityConfigCallbackPair;
1124
1375
  const configId = viewabilityConfig.id;
1125
- const viewabilityState = mapViewabilityConfigCallbackPairs.get(configId);
1376
+ const viewabilityState = ensureViewabilityState(ctx, configId);
1126
1377
  const { viewableItems: previousViewableItems, start, end } = viewabilityState;
1127
1378
  const viewabilityTokens = /* @__PURE__ */ new Map();
1128
1379
  for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
@@ -1201,6 +1452,15 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
1201
1452
  }
1202
1453
  }
1203
1454
  }
1455
+ function shallowEqual(prev, next) {
1456
+ if (!prev) return false;
1457
+ const keys = Object.keys(next);
1458
+ for (let i = 0; i < keys.length; i++) {
1459
+ const k = keys[i];
1460
+ if (prev[k] !== next[k]) return false;
1461
+ }
1462
+ return true;
1463
+ }
1204
1464
  function computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
1205
1465
  const { sizes, positions, scroll: scrollState } = state;
1206
1466
  const topPad = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
@@ -1229,7 +1489,8 @@ function computeViewability(state, ctx, viewabilityConfig, containerId, key, scr
1229
1489
  size,
1230
1490
  sizeVisible
1231
1491
  };
1232
- if (JSON.stringify(value) !== JSON.stringify(ctx.mapViewabilityAmountValues.get(containerId))) {
1492
+ const prev = ctx.mapViewabilityAmountValues.get(containerId);
1493
+ if (!shallowEqual(prev, value)) {
1233
1494
  ctx.mapViewabilityAmountValues.set(containerId, value);
1234
1495
  const cb = ctx.mapViewabilityAmountCallbacks.get(containerId);
1235
1496
  if (cb) {
@@ -1258,6 +1519,7 @@ function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
1258
1519
  const cb = ctx.mapViewabilityCallbacks.get(key);
1259
1520
  cb == null ? void 0 : cb(viewToken);
1260
1521
  }
1522
+ var batchedUpdates = unstable_batchedUpdates || ((callback) => callback());
1261
1523
 
1262
1524
  // src/utils/checkAllSizesKnown.ts
1263
1525
  function checkAllSizesKnown(state) {
@@ -1274,35 +1536,79 @@ function checkAllSizesKnown(state) {
1274
1536
  }
1275
1537
 
1276
1538
  // src/utils/findAvailableContainers.ts
1277
- function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffered, pendingRemoval) {
1539
+ function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers) {
1278
1540
  const numContainers = peek$(ctx, "numContainers");
1541
+ const { stickyContainerPool, containerItemTypes } = state;
1279
1542
  const result = [];
1280
1543
  const availableContainers = [];
1281
- for (let u = 0; u < numContainers; u++) {
1544
+ const stickyIndicesSet = state.props.stickyIndicesSet;
1545
+ const stickyItemIndices = (needNewContainers == null ? void 0 : needNewContainers.filter((index) => stickyIndicesSet.has(index))) || [];
1546
+ const canReuseContainer = (containerIndex, requiredType) => {
1547
+ if (!requiredType) return true;
1548
+ const existingType = containerItemTypes.get(containerIndex);
1549
+ if (!existingType) return true;
1550
+ return existingType === requiredType;
1551
+ };
1552
+ const neededTypes = requiredItemTypes ? [...requiredItemTypes] : [];
1553
+ let typeIndex = 0;
1554
+ for (let i = 0; i < stickyItemIndices.length; i++) {
1555
+ const requiredType = neededTypes[typeIndex];
1556
+ let foundContainer = false;
1557
+ for (const containerIndex of stickyContainerPool) {
1558
+ const key = peek$(ctx, `containerItemKey${containerIndex}`);
1559
+ const isPendingRemoval = pendingRemoval.includes(containerIndex);
1560
+ if ((key === void 0 || isPendingRemoval) && canReuseContainer(containerIndex, requiredType)) {
1561
+ result.push(containerIndex);
1562
+ if (isPendingRemoval) {
1563
+ const index = pendingRemoval.indexOf(containerIndex);
1564
+ pendingRemoval.splice(index, 1);
1565
+ }
1566
+ foundContainer = true;
1567
+ if (requiredItemTypes) typeIndex++;
1568
+ break;
1569
+ }
1570
+ }
1571
+ if (!foundContainer) {
1572
+ const newContainerIndex = numContainers + result.filter((index) => index >= numContainers).length;
1573
+ result.push(newContainerIndex);
1574
+ stickyContainerPool.add(newContainerIndex);
1575
+ if (requiredItemTypes) typeIndex++;
1576
+ }
1577
+ }
1578
+ for (let u = 0; u < numContainers && result.length < numNeeded; u++) {
1579
+ if (stickyContainerPool.has(u)) {
1580
+ continue;
1581
+ }
1282
1582
  const key = peek$(ctx, `containerItemKey${u}`);
1283
1583
  let isOk = key === void 0;
1284
1584
  if (!isOk) {
1285
1585
  const index = pendingRemoval.indexOf(u);
1286
1586
  if (index !== -1) {
1287
1587
  pendingRemoval.splice(index, 1);
1288
- isOk = true;
1588
+ const requiredType = neededTypes[typeIndex];
1589
+ isOk = canReuseContainer(u, requiredType);
1289
1590
  }
1290
1591
  }
1291
1592
  if (isOk) {
1292
1593
  result.push(u);
1293
- if (result.length >= numNeeded) {
1294
- return result;
1594
+ if (requiredItemTypes) {
1595
+ typeIndex++;
1295
1596
  }
1296
1597
  }
1297
1598
  }
1298
- for (let u = 0; u < numContainers; u++) {
1599
+ for (let u = 0; u < numContainers && result.length < numNeeded; u++) {
1600
+ if (stickyContainerPool.has(u)) {
1601
+ continue;
1602
+ }
1299
1603
  const key = peek$(ctx, `containerItemKey${u}`);
1300
1604
  if (key === void 0) continue;
1301
1605
  const index = state.indexByKey.get(key);
1302
- if (index < startBuffered) {
1303
- availableContainers.push({ distance: startBuffered - index, index: u });
1304
- } else if (index > endBuffered) {
1305
- availableContainers.push({ distance: index - endBuffered, index: u });
1606
+ const isOutOfView = index < startBuffered || index > endBuffered;
1607
+ if (isOutOfView) {
1608
+ const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
1609
+ if (!requiredItemTypes || typeIndex < neededTypes.length && canReuseContainer(u, neededTypes[typeIndex])) {
1610
+ availableContainers.push({ distance, index: u });
1611
+ }
1306
1612
  }
1307
1613
  }
1308
1614
  const remaining = numNeeded - result.length;
@@ -1314,6 +1620,9 @@ function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffer
1314
1620
  }
1315
1621
  for (const container of availableContainers) {
1316
1622
  result.push(container.index);
1623
+ if (requiredItemTypes) {
1624
+ typeIndex++;
1625
+ }
1317
1626
  }
1318
1627
  }
1319
1628
  const stillNeeded = numNeeded - result.length;
@@ -1342,37 +1651,44 @@ function comparatorByDistance(a, b) {
1342
1651
  return b.distance - a.distance;
1343
1652
  }
1344
1653
 
1345
- // src/core/finishScrollTo.ts
1346
- var finishScrollTo = (state) => {
1347
- if (state) {
1348
- state.scrollingTo = void 0;
1349
- state.scrollHistory.length = 0;
1654
+ // src/utils/getScrollVelocity.ts
1655
+ var getScrollVelocity = (state) => {
1656
+ const { scrollHistory } = state;
1657
+ let velocity = 0;
1658
+ if (scrollHistory.length >= 1) {
1659
+ const newest = scrollHistory[scrollHistory.length - 1];
1660
+ let oldest;
1661
+ let start = 0;
1662
+ const now = Date.now();
1663
+ for (let i = 0; i < scrollHistory.length - 1; i++) {
1664
+ const entry = scrollHistory[i];
1665
+ const nextEntry = scrollHistory[i + 1];
1666
+ if (i > 0) {
1667
+ const prevEntry = scrollHistory[i - 1];
1668
+ const prevDirection = entry.scroll - prevEntry.scroll;
1669
+ const currentDirection = nextEntry.scroll - entry.scroll;
1670
+ if (prevDirection > 0 && currentDirection < 0 || prevDirection < 0 && currentDirection > 0) {
1671
+ start = i;
1672
+ break;
1673
+ }
1674
+ }
1675
+ }
1676
+ for (let i = start; i < scrollHistory.length - 1; i++) {
1677
+ const entry = scrollHistory[i];
1678
+ if (now - entry.time <= 1e3) {
1679
+ oldest = entry;
1680
+ break;
1681
+ }
1682
+ }
1683
+ if (oldest && oldest !== newest) {
1684
+ const scrollDiff = newest.scroll - oldest.scroll;
1685
+ const timeDiff = newest.time - oldest.time;
1686
+ velocity = timeDiff > 0 ? scrollDiff / timeDiff : 0;
1687
+ }
1350
1688
  }
1689
+ return velocity;
1351
1690
  };
1352
1691
 
1353
- // src/core/scrollTo.ts
1354
- function scrollTo(state, params = {}) {
1355
- var _a;
1356
- const { animated } = params;
1357
- const {
1358
- refScroller,
1359
- props: { horizontal }
1360
- } = state;
1361
- const offset = calculateOffsetWithOffsetPosition(state, params.offset, params);
1362
- state.scrollHistory.length = 0;
1363
- state.scrollingTo = params;
1364
- state.scrollPending = offset;
1365
- (_a = refScroller.current) == null ? void 0 : _a.scrollTo({
1366
- animated: !!animated,
1367
- x: horizontal ? offset : 0,
1368
- y: horizontal ? 0 : offset
1369
- });
1370
- if (!animated) {
1371
- state.scroll = offset;
1372
- setTimeout(() => finishScrollTo(state), 100);
1373
- }
1374
- }
1375
-
1376
1692
  // src/core/scrollToIndex.ts
1377
1693
  function scrollToIndex(ctx, state, { index, viewOffset = 0, animated = true, viewPosition }) {
1378
1694
  if (index >= state.props.data.length) {
@@ -1450,43 +1766,123 @@ function checkAtBottom(ctx, state) {
1450
1766
  );
1451
1767
  }
1452
1768
  }
1453
-
1454
- // src/utils/setDidLayout.ts
1455
- function setDidLayout(ctx, state) {
1456
- const {
1457
- loadStartTime,
1458
- initialScroll,
1459
- props: { onLoad }
1460
- } = state;
1461
- state.queuedInitialLayout = true;
1462
- checkAtBottom(ctx, state);
1463
- if (!IsNewArchitecture && initialScroll) {
1464
- scrollToIndex(ctx, state, { ...initialScroll, animated: false });
1769
+
1770
+ // src/utils/setDidLayout.ts
1771
+ function setDidLayout(ctx, state) {
1772
+ const {
1773
+ loadStartTime,
1774
+ initialScroll,
1775
+ props: { onLoad }
1776
+ } = state;
1777
+ state.queuedInitialLayout = true;
1778
+ checkAtBottom(ctx, state);
1779
+ const setIt = () => {
1780
+ set$(ctx, "containersDidLayout", true);
1781
+ if (onLoad) {
1782
+ onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1783
+ }
1784
+ };
1785
+ if (Platform.OS === "android" || !IsNewArchitecture) {
1786
+ if (initialScroll) {
1787
+ queueMicrotask(() => {
1788
+ scrollToIndex(ctx, state, { ...initialScroll, animated: false });
1789
+ requestAnimationFrame(() => {
1790
+ scrollToIndex(ctx, state, { ...initialScroll, animated: false });
1791
+ setIt();
1792
+ });
1793
+ });
1794
+ } else {
1795
+ queueMicrotask(setIt);
1796
+ }
1797
+ } else {
1798
+ setIt();
1799
+ }
1800
+ }
1801
+
1802
+ // src/core/calculateItemsInView.ts
1803
+ function findCurrentStickyIndex(stickyArray, scroll, state) {
1804
+ var _a;
1805
+ const idCache = state.idCache;
1806
+ const positions = state.positions;
1807
+ for (let i = stickyArray.length - 1; i >= 0; i--) {
1808
+ const stickyId = (_a = idCache.get(stickyArray[i])) != null ? _a : getId(state, stickyArray[i]);
1809
+ const stickyPos = stickyId ? positions.get(stickyId) : void 0;
1810
+ if (stickyPos !== void 0 && scroll >= stickyPos) {
1811
+ return i;
1812
+ }
1813
+ }
1814
+ return -1;
1815
+ }
1816
+ function getActiveStickyIndices(ctx, state, stickyIndices) {
1817
+ return new Set(
1818
+ 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))
1819
+ );
1820
+ }
1821
+ function handleStickyActivation(ctx, state, stickyIndices, stickyArray, scroll, needNewContainers, startBuffered, endBuffered) {
1822
+ var _a;
1823
+ const activeIndices = getActiveStickyIndices(ctx, state, stickyIndices);
1824
+ const currentStickyIdx = findCurrentStickyIndex(stickyArray, scroll, state);
1825
+ for (let offset = 0; offset <= 1; offset++) {
1826
+ const idx = currentStickyIdx - offset;
1827
+ if (idx < 0 || activeIndices.has(stickyArray[idx])) continue;
1828
+ const stickyIndex = stickyArray[idx];
1829
+ const stickyId = (_a = state.idCache.get(stickyIndex)) != null ? _a : getId(state, stickyIndex);
1830
+ if (stickyId && !state.containerItemKeys.has(stickyId) && (stickyIndex < startBuffered || stickyIndex > endBuffered)) {
1831
+ needNewContainers.push(stickyIndex);
1832
+ }
1465
1833
  }
1466
- set$(ctx, "containersDidLayout", true);
1467
- if (onLoad) {
1468
- onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1834
+ }
1835
+ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, pendingRemoval) {
1836
+ var _a, _b, _c;
1837
+ const currentStickyIdx = findCurrentStickyIndex(stickyArray, scroll, state);
1838
+ for (const containerIndex of state.stickyContainerPool) {
1839
+ const itemKey = peek$(ctx, `containerItemKey${containerIndex}`);
1840
+ const itemIndex = itemKey ? state.indexByKey.get(itemKey) : void 0;
1841
+ if (itemIndex === void 0) continue;
1842
+ const arrayIdx = stickyArray.indexOf(itemIndex);
1843
+ if (arrayIdx === -1) continue;
1844
+ const isRecentSticky = arrayIdx >= currentStickyIdx - 1 && arrayIdx <= currentStickyIdx + 1;
1845
+ if (isRecentSticky) continue;
1846
+ const nextIndex = stickyArray[arrayIdx + 1];
1847
+ let shouldRecycle = false;
1848
+ if (nextIndex) {
1849
+ const nextId = (_a = state.idCache.get(nextIndex)) != null ? _a : getId(state, nextIndex);
1850
+ const nextPos = nextId ? state.positions.get(nextId) : void 0;
1851
+ shouldRecycle = nextPos !== void 0 && scroll > nextPos + scrollBuffer * 2;
1852
+ } else {
1853
+ const currentId = (_b = state.idCache.get(itemIndex)) != null ? _b : getId(state, itemIndex);
1854
+ if (currentId) {
1855
+ const currentPos = state.positions.get(currentId);
1856
+ const currentSize = (_c = state.sizes.get(currentId)) != null ? _c : getItemSize(state, currentId, itemIndex, state.props.data[itemIndex]);
1857
+ shouldRecycle = currentPos !== void 0 && scroll > currentPos + currentSize + scrollBuffer * 3;
1858
+ }
1859
+ }
1860
+ if (shouldRecycle) {
1861
+ pendingRemoval.push(containerIndex);
1862
+ }
1469
1863
  }
1470
1864
  }
1471
-
1472
- // src/core/calculateItemsInView.ts
1473
1865
  function calculateItemsInView(ctx, state, params = {}) {
1474
- unstable_batchedUpdates(() => {
1866
+ batchedUpdates(() => {
1475
1867
  var _a, _b, _c, _d, _e, _f, _g, _h;
1476
1868
  const {
1477
- scrollLength,
1478
- startBufferedId: startBufferedIdOrig,
1479
- positions,
1480
1869
  columns,
1481
1870
  containerItemKeys,
1871
+ enableScrollForNextCalculateItemsInView,
1482
1872
  idCache,
1483
- sizes,
1484
1873
  indexByKey,
1874
+ minIndexSizeChanged,
1875
+ positions,
1485
1876
  scrollForNextCalculateItemsInView,
1486
- enableScrollForNextCalculateItemsInView,
1487
- minIndexSizeChanged
1877
+ scrollLength,
1878
+ sizes,
1879
+ startBufferedId: startBufferedIdOrig,
1880
+ viewabilityConfigCallbackPairs,
1881
+ props: { getItemType, initialScroll, itemsAreEqual, keyExtractor, scrollBuffer }
1488
1882
  } = state;
1489
- const data = state.props.data;
1883
+ const { data } = state.props;
1884
+ const stickyIndicesArr = state.props.stickyIndicesArr || [];
1885
+ const stickyIndicesSet = state.props.stickyIndicesSet || /* @__PURE__ */ new Set();
1490
1886
  const prevNumContainers = peek$(ctx, "numContainers");
1491
1887
  if (!data || scrollLength === 0 || !prevNumContainers) {
1492
1888
  return;
@@ -1498,14 +1894,22 @@ function calculateItemsInView(ctx, state, params = {}) {
1498
1894
  const { dataChanged, doMVCP } = params;
1499
1895
  const speed = getScrollVelocity(state);
1500
1896
  if (doMVCP || dataChanged) {
1501
- const checkMVCP = doMVCP ? prepareMVCP(ctx, state) : void 0;
1502
- updateAllPositions(ctx, state, dataChanged);
1897
+ const checkMVCP = doMVCP ? prepareMVCP(ctx, state, dataChanged) : void 0;
1898
+ if (dataChanged) {
1899
+ indexByKey.clear();
1900
+ idCache.clear();
1901
+ positions.clear();
1902
+ }
1903
+ const startIndex = dataChanged ? 0 : minIndexSizeChanged != null ? minIndexSizeChanged : 0;
1904
+ updateItemPositions(ctx, state, dataChanged, startIndex);
1905
+ if (minIndexSizeChanged !== void 0) {
1906
+ state.minIndexSizeChanged = void 0;
1907
+ }
1503
1908
  checkMVCP == null ? void 0 : checkMVCP();
1504
1909
  }
1505
1910
  const scrollExtra = 0;
1506
1911
  const { queuedInitialLayout } = state;
1507
1912
  let { scroll: scrollState } = state;
1508
- const initialScroll = state.props.initialScroll;
1509
1913
  if (!queuedInitialLayout && initialScroll) {
1510
1914
  const updatedOffset = calculateOffsetWithOffsetPosition(
1511
1915
  state,
@@ -1517,16 +1921,15 @@ function calculateItemsInView(ctx, state, params = {}) {
1517
1921
  const scrollAdjustPad = -previousScrollAdjust - topPad;
1518
1922
  let scroll = scrollState + scrollExtra + scrollAdjustPad;
1519
1923
  if (scroll + scrollLength > totalSize) {
1520
- scroll = totalSize - scrollLength;
1924
+ scroll = Math.max(0, totalSize - scrollLength);
1521
1925
  }
1522
1926
  if (ENABLE_DEBUG_VIEW) {
1523
1927
  set$(ctx, "debugRawScroll", scrollState);
1524
1928
  set$(ctx, "debugComputedScroll", scroll);
1525
1929
  }
1526
- const scrollBuffer = state.props.scrollBuffer;
1527
1930
  let scrollBufferTop = scrollBuffer;
1528
1931
  let scrollBufferBottom = scrollBuffer;
1529
- if (speed > 0) {
1932
+ if (speed > 0 || speed === 0 && scroll < Math.max(50, scrollBuffer)) {
1530
1933
  scrollBufferTop = scrollBuffer * 0.5;
1531
1934
  scrollBufferBottom = scrollBuffer * 1.5;
1532
1935
  } else {
@@ -1534,7 +1937,7 @@ function calculateItemsInView(ctx, state, params = {}) {
1534
1937
  scrollBufferBottom = scrollBuffer * 0.5;
1535
1938
  }
1536
1939
  const scrollTopBuffered = scroll - scrollBufferTop;
1537
- const scrollBottom = scroll + scrollLength;
1940
+ const scrollBottom = scroll + scrollLength + (scroll < 0 ? -scroll : 0);
1538
1941
  const scrollBottomBuffered = scrollBottom + scrollBufferBottom;
1539
1942
  if (scrollForNextCalculateItemsInView) {
1540
1943
  const { top, bottom } = scrollForNextCalculateItemsInView;
@@ -1548,10 +1951,6 @@ function calculateItemsInView(ctx, state, params = {}) {
1548
1951
  let endNoBuffer = null;
1549
1952
  let endBuffered = null;
1550
1953
  let loopStart = startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
1551
- if (minIndexSizeChanged !== void 0) {
1552
- loopStart = Math.min(minIndexSizeChanged, loopStart);
1553
- state.minIndexSizeChanged = void 0;
1554
- }
1555
1954
  for (let i = loopStart; i >= 0; i--) {
1556
1955
  const id = (_a = idCache.get(i)) != null ? _a : getId(state, i);
1557
1956
  const top = positions.get(id);
@@ -1634,7 +2033,7 @@ function calculateItemsInView(ctx, state, params = {}) {
1634
2033
  if (dataChanged) {
1635
2034
  for (let i = 0; i < numContainers; i++) {
1636
2035
  const itemKey = peek$(ctx, `containerItemKey${i}`);
1637
- if (!state.props.keyExtractor || itemKey && indexByKey.get(itemKey) === void 0) {
2036
+ if (!keyExtractor || itemKey && indexByKey.get(itemKey) === void 0) {
1638
2037
  pendingRemoval.push(i);
1639
2038
  }
1640
2039
  }
@@ -1648,14 +2047,32 @@ function calculateItemsInView(ctx, state, params = {}) {
1648
2047
  needNewContainers.push(i);
1649
2048
  }
1650
2049
  }
2050
+ if (stickyIndicesArr.length > 0) {
2051
+ handleStickyActivation(
2052
+ ctx,
2053
+ state,
2054
+ stickyIndicesSet,
2055
+ stickyIndicesArr,
2056
+ scroll,
2057
+ needNewContainers,
2058
+ startBuffered,
2059
+ endBuffered
2060
+ );
2061
+ }
1651
2062
  if (needNewContainers.length > 0) {
2063
+ const requiredItemTypes = getItemType ? needNewContainers.map((i) => {
2064
+ const itemType = getItemType(data[i], i);
2065
+ return itemType ? String(itemType) : "";
2066
+ }) : void 0;
1652
2067
  const availableContainers = findAvailableContainers(
1653
2068
  ctx,
1654
2069
  state,
1655
2070
  needNewContainers.length,
1656
2071
  startBuffered,
1657
2072
  endBuffered,
1658
- pendingRemoval
2073
+ pendingRemoval,
2074
+ requiredItemTypes,
2075
+ needNewContainers
1659
2076
  );
1660
2077
  for (let idx = 0; idx < needNewContainers.length; idx++) {
1661
2078
  const i = needNewContainers[idx];
@@ -1667,7 +2084,19 @@ function calculateItemsInView(ctx, state, params = {}) {
1667
2084
  }
1668
2085
  set$(ctx, `containerItemKey${containerIndex}`, id);
1669
2086
  set$(ctx, `containerItemData${containerIndex}`, data[i]);
2087
+ if (requiredItemTypes) {
2088
+ state.containerItemTypes.set(containerIndex, requiredItemTypes[idx]);
2089
+ }
1670
2090
  containerItemKeys.add(id);
2091
+ if (stickyIndicesSet.has(i)) {
2092
+ set$(ctx, `containerSticky${containerIndex}`, true);
2093
+ const topPadding = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
2094
+ set$(ctx, `containerStickyOffset${containerIndex}`, new Animated.Value(topPadding));
2095
+ state.stickyContainerPool.add(containerIndex);
2096
+ } else {
2097
+ set$(ctx, `containerSticky${containerIndex}`, false);
2098
+ state.stickyContainerPool.delete(containerIndex);
2099
+ }
1671
2100
  if (containerIndex >= numContainers2) {
1672
2101
  numContainers2 = containerIndex + 1;
1673
2102
  }
@@ -1680,12 +2109,21 @@ function calculateItemsInView(ctx, state, params = {}) {
1680
2109
  }
1681
2110
  }
1682
2111
  }
2112
+ if (stickyIndicesArr.length > 0) {
2113
+ handleStickyRecycling(ctx, state, stickyIndicesArr, scroll, scrollBuffer, pendingRemoval);
2114
+ }
1683
2115
  for (let i = 0; i < numContainers; i++) {
1684
2116
  const itemKey = peek$(ctx, `containerItemKey${i}`);
1685
2117
  if (pendingRemoval.includes(i)) {
1686
2118
  if (itemKey) {
1687
2119
  containerItemKeys.delete(itemKey);
1688
2120
  }
2121
+ state.containerItemTypes.delete(i);
2122
+ if (state.stickyContainerPool.has(i)) {
2123
+ set$(ctx, `containerSticky${i}`, false);
2124
+ set$(ctx, `containerStickyOffset${i}`, void 0);
2125
+ state.stickyContainerPool.delete(i);
2126
+ }
1689
2127
  set$(ctx, `containerItemKey${i}`, void 0);
1690
2128
  set$(ctx, `containerItemData${i}`, void 0);
1691
2129
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
@@ -1695,7 +2133,7 @@ function calculateItemsInView(ctx, state, params = {}) {
1695
2133
  const item = data[itemIndex];
1696
2134
  if (item !== void 0) {
1697
2135
  const id = (_h = idCache.get(itemIndex)) != null ? _h : getId(state, itemIndex);
1698
- const position = positions.get(id);
2136
+ const position = getPositionById(ctx, state, id);
1699
2137
  if (position === void 0) {
1700
2138
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
1701
2139
  } else {
@@ -1709,7 +2147,7 @@ function calculateItemsInView(ctx, state, params = {}) {
1709
2147
  if (column >= 0 && column !== prevColumn) {
1710
2148
  set$(ctx, `containerColumn${i}`, column);
1711
2149
  }
1712
- if (prevData !== item) {
2150
+ if (prevData !== item && (itemsAreEqual ? !itemsAreEqual(prevData, item, itemIndex, data) : true)) {
1713
2151
  set$(ctx, `containerItemData${i}`, data[itemIndex]);
1714
2152
  }
1715
2153
  }
@@ -1721,42 +2159,55 @@ function calculateItemsInView(ctx, state, params = {}) {
1721
2159
  setDidLayout(ctx, state);
1722
2160
  }
1723
2161
  }
1724
- if (state.viewabilityConfigCallbackPairs) {
1725
- updateViewableItems(
1726
- state,
1727
- ctx,
1728
- state.viewabilityConfigCallbackPairs,
1729
- scrollLength,
1730
- startNoBuffer,
1731
- endNoBuffer
1732
- );
2162
+ if (viewabilityConfigCallbackPairs) {
2163
+ updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollLength, startNoBuffer, endNoBuffer);
1733
2164
  }
1734
2165
  });
1735
2166
  }
1736
2167
 
1737
2168
  // src/core/doInitialAllocateContainers.ts
1738
2169
  function doInitialAllocateContainers(ctx, state) {
1739
- const { scrollLength } = state;
1740
- const data = state.props.data;
1741
- if (scrollLength > 0 && data.length > 0 && !peek$(ctx, "numContainers")) {
1742
- const averageItemSize = state.props.getEstimatedItemSize ? state.props.getEstimatedItemSize(0, data[0]) : state.props.estimatedItemSize;
1743
- const Extra = 1.5;
1744
- const numContainers = Math.ceil(
1745
- (scrollLength + state.props.scrollBuffer * 2) / averageItemSize * state.props.numColumns * Extra
1746
- );
2170
+ var _a;
2171
+ const {
2172
+ scrollLength,
2173
+ props: {
2174
+ data,
2175
+ getEstimatedItemSize,
2176
+ getFixedItemSize,
2177
+ getItemType,
2178
+ scrollBuffer,
2179
+ numColumns,
2180
+ estimatedItemSize
2181
+ }
2182
+ } = state;
2183
+ const hasContainers = peek$(ctx, "numContainers");
2184
+ if (scrollLength > 0 && data.length > 0 && !hasContainers) {
2185
+ let averageItemSize;
2186
+ const fn = getFixedItemSize || getEstimatedItemSize;
2187
+ if (fn) {
2188
+ let totalSize = 0;
2189
+ const num = Math.min(20, data.length);
2190
+ for (let i = 0; i < num; i++) {
2191
+ totalSize += fn(0, data[0], getItemType ? (_a = getItemType(data[0], 0)) != null ? _a : "" : "");
2192
+ }
2193
+ averageItemSize = totalSize / num;
2194
+ } else {
2195
+ averageItemSize = estimatedItemSize;
2196
+ }
2197
+ const numContainers = Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize * numColumns);
1747
2198
  for (let i = 0; i < numContainers; i++) {
1748
2199
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
1749
2200
  set$(ctx, `containerColumn${i}`, -1);
1750
2201
  }
1751
2202
  set$(ctx, "numContainers", numContainers);
1752
2203
  set$(ctx, "numContainersPooled", numContainers * state.props.initialContainerPoolRatio);
1753
- if (!IsNewArchitecture) {
2204
+ if (!IsNewArchitecture || state.lastLayout) {
1754
2205
  if (state.props.initialScroll) {
1755
2206
  requestAnimationFrame(() => {
1756
- calculateItemsInView(ctx, state);
2207
+ calculateItemsInView(ctx, state, { dataChanged: true });
1757
2208
  });
1758
2209
  } else {
1759
- calculateItemsInView(ctx, state);
2210
+ calculateItemsInView(ctx, state, { dataChanged: true });
1760
2211
  }
1761
2212
  }
1762
2213
  return true;
@@ -1776,16 +2227,18 @@ function doMaintainScrollAtEnd(ctx, state, animated) {
1776
2227
  }
1777
2228
  requestAnimationFrame(() => {
1778
2229
  var _a;
1779
- state.maintainingScrollAtEnd = true;
1780
- (_a = refScroller.current) == null ? void 0 : _a.scrollToEnd({
1781
- animated
1782
- });
1783
- setTimeout(
1784
- () => {
1785
- state.maintainingScrollAtEnd = false;
1786
- },
1787
- 0
1788
- );
2230
+ if (state == null ? void 0 : state.isAtEnd) {
2231
+ state.maintainingScrollAtEnd = true;
2232
+ (_a = refScroller.current) == null ? void 0 : _a.scrollToEnd({
2233
+ animated
2234
+ });
2235
+ setTimeout(
2236
+ () => {
2237
+ state.maintainingScrollAtEnd = false;
2238
+ },
2239
+ 0
2240
+ );
2241
+ }
1789
2242
  });
1790
2243
  return true;
1791
2244
  }
@@ -1826,40 +2279,49 @@ function handleLayout(ctx, state, layout, setCanRender) {
1826
2279
  const otherAxisSize = layout[state.props.horizontal ? "height" : "width"];
1827
2280
  const needsCalculate = !state.lastLayout || scrollLength > state.scrollLength || state.lastLayout.x !== layout.x || state.lastLayout.y !== layout.y;
1828
2281
  state.lastLayout = layout;
1829
- const didChange = scrollLength !== state.scrollLength;
1830
2282
  const prevOtherAxisSize = state.otherAxisSize;
1831
- state.scrollLength = scrollLength;
1832
- state.otherAxisSize = otherAxisSize;
1833
- state.lastBatchingAction = Date.now();
1834
- state.scrollForNextCalculateItemsInView = void 0;
1835
- doInitialAllocateContainers(ctx, state);
1836
- if (needsCalculate) {
1837
- calculateItemsInView(ctx, state, { doMVCP: true });
1838
- }
1839
- if (didChange || otherAxisSize !== prevOtherAxisSize) {
1840
- set$(ctx, "scrollSize", { height: layout.height, width: layout.width });
1841
- }
1842
- if (maintainScrollAtEnd === true || maintainScrollAtEnd.onLayout) {
1843
- doMaintainScrollAtEnd(ctx, state, false);
1844
- }
1845
- updateAlignItemsPaddingTop(ctx, state);
1846
- checkAtBottom(ctx, state);
1847
- checkAtTop(state);
1848
- if (state) {
1849
- state.needsOtherAxisSize = otherAxisSize - (state.props.stylePaddingTop || 0) < 10;
1850
- }
1851
- if (__DEV__ && scrollLength === 0) {
1852
- warnDevOnce(
1853
- "height0",
1854
- `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.`
1855
- );
2283
+ const didChange = scrollLength !== state.scrollLength || otherAxisSize !== prevOtherAxisSize;
2284
+ if (didChange) {
2285
+ state.scrollLength = scrollLength;
2286
+ state.otherAxisSize = otherAxisSize;
2287
+ state.lastBatchingAction = Date.now();
2288
+ state.scrollForNextCalculateItemsInView = void 0;
2289
+ doInitialAllocateContainers(ctx, state);
2290
+ if (needsCalculate) {
2291
+ calculateItemsInView(ctx, state, { doMVCP: true });
2292
+ }
2293
+ if (didChange || otherAxisSize !== prevOtherAxisSize) {
2294
+ set$(ctx, "scrollSize", { height: layout.height, width: layout.width });
2295
+ }
2296
+ if (maintainScrollAtEnd === true || maintainScrollAtEnd.onLayout) {
2297
+ doMaintainScrollAtEnd(ctx, state, false);
2298
+ }
2299
+ updateAlignItemsPaddingTop(ctx, state);
2300
+ checkAtBottom(ctx, state);
2301
+ checkAtTop(state);
2302
+ if (state) {
2303
+ state.needsOtherAxisSize = otherAxisSize - (state.props.stylePaddingTop || 0) < 10;
2304
+ }
2305
+ if (__DEV__ && scrollLength === 0) {
2306
+ warnDevOnce(
2307
+ "height0",
2308
+ `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.`
2309
+ );
2310
+ }
2311
+ setCanRender(true);
1856
2312
  }
1857
- setCanRender(true);
1858
2313
  }
1859
2314
 
1860
2315
  // src/core/onScroll.ts
1861
2316
  function onScroll(ctx, state, event) {
1862
- var _a, _b, _c, _d, _e;
2317
+ var _a, _b, _c;
2318
+ const {
2319
+ scrollProcessingEnabled,
2320
+ props: { onScroll: onScrollProp }
2321
+ } = state;
2322
+ if (scrollProcessingEnabled === false) {
2323
+ return;
2324
+ }
1863
2325
  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) {
1864
2326
  return;
1865
2327
  }
@@ -1873,26 +2335,29 @@ function onScroll(ctx, state, event) {
1873
2335
  }
1874
2336
  state.scrollPending = newScroll;
1875
2337
  updateScroll(ctx, state, newScroll);
1876
- (_e = (_d = state.props).onScroll) == null ? void 0 : _e.call(_d, event);
2338
+ onScrollProp == null ? void 0 : onScrollProp(event);
1877
2339
  }
1878
2340
  function updateScroll(ctx, state, newScroll) {
1879
2341
  const scrollingTo = state.scrollingTo;
1880
2342
  state.hasScrolled = true;
1881
2343
  state.lastBatchingAction = Date.now();
1882
- const currentTime = performance.now();
2344
+ const currentTime = Date.now();
1883
2345
  if (scrollingTo === void 0 && !(state.scrollHistory.length === 0 && newScroll === state.scroll)) {
1884
- state.scrollHistory.push({ scroll: newScroll, time: currentTime });
2346
+ const adjust = state.scrollAdjustHandler.getAdjust();
2347
+ state.scrollHistory.push({ scroll: newScroll - adjust, time: currentTime });
1885
2348
  }
1886
2349
  if (state.scrollHistory.length > 5) {
1887
2350
  state.scrollHistory.shift();
1888
2351
  }
1889
- state.scrollPrev = state.scroll;
1890
- state.scrollPrevTime = state.scrollTime;
1891
2352
  state.scroll = newScroll;
1892
2353
  state.scrollTime = currentTime;
1893
- calculateItemsInView(ctx, state);
1894
- checkAtBottom(ctx, state);
1895
- checkAtTop(state);
2354
+ if (Math.abs(state.scroll - state.scrollPrev) > 2) {
2355
+ calculateItemsInView(ctx, state);
2356
+ checkAtBottom(ctx, state);
2357
+ checkAtTop(state);
2358
+ }
2359
+ state.scrollPrev = state.scroll;
2360
+ state.scrollPrevTime = state.scrollTime;
1896
2361
  }
1897
2362
 
1898
2363
  // src/core/ScrollAdjustHandler.ts
@@ -1915,13 +2380,19 @@ var ScrollAdjustHandler = class {
1915
2380
  setMounted() {
1916
2381
  this.mounted = true;
1917
2382
  }
2383
+ getAdjust() {
2384
+ return this.appliedAdjust;
2385
+ }
1918
2386
  };
1919
2387
 
1920
2388
  // src/core/updateItemSize.ts
1921
- function updateItemSizes(ctx, state, itemUpdates) {
1922
- var _a;
2389
+ function updateItemSize(ctx, state, itemKey, sizeObj) {
2390
+ var _a, _b;
1923
2391
  const {
2392
+ sizesKnown,
1924
2393
  props: {
2394
+ getFixedItemSize,
2395
+ getItemType,
1925
2396
  horizontal,
1926
2397
  maintainVisibleContentPosition,
1927
2398
  suggestEstimatedItemSize,
@@ -1931,47 +2402,60 @@ function updateItemSizes(ctx, state, itemUpdates) {
1931
2402
  }
1932
2403
  } = state;
1933
2404
  if (!data) return;
2405
+ if (getFixedItemSize) {
2406
+ const index2 = state.indexByKey.get(itemKey);
2407
+ if (index2 === void 0) {
2408
+ return;
2409
+ }
2410
+ const itemData = state.props.data[index2];
2411
+ if (itemData === void 0) {
2412
+ return;
2413
+ }
2414
+ const type = getItemType ? (_a = getItemType(itemData, index2)) != null ? _a : "" : "";
2415
+ const size2 = getFixedItemSize(index2, itemData, type);
2416
+ if (size2 !== void 0 && size2 === sizesKnown.get(itemKey)) {
2417
+ return;
2418
+ }
2419
+ }
1934
2420
  const containersDidLayout = peek$(ctx, "containersDidLayout");
1935
2421
  let needsRecalculate = !containersDidLayout;
1936
2422
  let shouldMaintainScrollAtEnd = false;
1937
2423
  let minIndexSizeChanged;
1938
2424
  let maxOtherAxisSize = peek$(ctx, "otherAxisSize") || 0;
1939
- for (const { itemKey, sizeObj } of itemUpdates) {
1940
- const index = state.indexByKey.get(itemKey);
1941
- const prevSizeKnown = state.sizesKnown.get(itemKey);
1942
- const diff = updateOneItemSize(state, itemKey, sizeObj);
1943
- const size = Math.floor((horizontal ? sizeObj.width : sizeObj.height) * 8) / 8;
1944
- if (diff !== 0) {
1945
- minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
1946
- if (((_a = state.scrollingTo) == null ? void 0 : _a.viewPosition) && maintainVisibleContentPosition && index === state.scrollingTo.index && diff > 0) {
1947
- requestAdjust(ctx, state, diff * state.scrollingTo.viewPosition);
1948
- }
1949
- const { startBuffered, endBuffered } = state;
1950
- needsRecalculate || (needsRecalculate = index >= startBuffered && index <= endBuffered);
1951
- if (!needsRecalculate) {
1952
- const numContainers = ctx.values.get("numContainers");
1953
- for (let i = 0; i < numContainers; i++) {
1954
- if (peek$(ctx, `containerItemKey${i}`) === itemKey) {
1955
- needsRecalculate = true;
1956
- break;
1957
- }
2425
+ const index = state.indexByKey.get(itemKey);
2426
+ const prevSizeKnown = state.sizesKnown.get(itemKey);
2427
+ const diff = updateOneItemSize(state, itemKey, sizeObj);
2428
+ const size = Math.floor((horizontal ? sizeObj.width : sizeObj.height) * 8) / 8;
2429
+ if (diff !== 0) {
2430
+ minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
2431
+ if (((_b = state.scrollingTo) == null ? void 0 : _b.viewPosition) && maintainVisibleContentPosition && index === state.scrollingTo.index && diff > 0) {
2432
+ requestAdjust(ctx, state, diff * state.scrollingTo.viewPosition);
2433
+ }
2434
+ const { startBuffered, endBuffered } = state;
2435
+ needsRecalculate || (needsRecalculate = index >= startBuffered && index <= endBuffered);
2436
+ if (!needsRecalculate) {
2437
+ const numContainers = ctx.values.get("numContainers");
2438
+ for (let i = 0; i < numContainers; i++) {
2439
+ if (peek$(ctx, `containerItemKey${i}`) === itemKey) {
2440
+ needsRecalculate = true;
2441
+ break;
1958
2442
  }
1959
2443
  }
1960
- if (state.needsOtherAxisSize) {
1961
- const otherAxisSize = horizontal ? sizeObj.height : sizeObj.width;
1962
- maxOtherAxisSize = Math.max(maxOtherAxisSize, otherAxisSize);
1963
- }
1964
- if (prevSizeKnown !== void 0 && Math.abs(prevSizeKnown - size) > 5) {
1965
- shouldMaintainScrollAtEnd = true;
1966
- }
1967
- onItemSizeChanged == null ? void 0 : onItemSizeChanged({
1968
- index,
1969
- itemData: state.props.data[index],
1970
- itemKey,
1971
- previous: size - diff,
1972
- size
1973
- });
1974
2444
  }
2445
+ if (state.needsOtherAxisSize) {
2446
+ const otherAxisSize = horizontal ? sizeObj.height : sizeObj.width;
2447
+ maxOtherAxisSize = Math.max(maxOtherAxisSize, otherAxisSize);
2448
+ }
2449
+ if (prevSizeKnown !== void 0 && Math.abs(prevSizeKnown - size) > 5) {
2450
+ shouldMaintainScrollAtEnd = true;
2451
+ }
2452
+ onItemSizeChanged == null ? void 0 : onItemSizeChanged({
2453
+ index,
2454
+ itemData: state.props.data[index],
2455
+ itemKey,
2456
+ previous: size - diff,
2457
+ size
2458
+ });
1975
2459
  }
1976
2460
  if (minIndexSizeChanged !== void 0) {
1977
2461
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
@@ -2004,43 +2488,29 @@ function updateItemSizes(ctx, state, itemUpdates) {
2004
2488
  }
2005
2489
  }
2006
2490
  }
2007
- function updateItemSize(ctx, state, itemKey, sizeObj) {
2008
- const { queuedItemSizeUpdates, queuedItemSizeUpdatesWaiting } = state;
2009
- const containersDidLayout = peek$(ctx, "containersDidLayout");
2010
- if (!containersDidLayout || !queuedItemSizeUpdatesWaiting) {
2011
- updateItemSizes(ctx, state, [{ itemKey, sizeObj }]);
2012
- if (containersDidLayout) {
2013
- state.queuedItemSizeUpdatesWaiting = true;
2014
- requestAnimationFrame(() => {
2015
- state.queuedItemSizeUpdatesWaiting = false;
2016
- updateItemSizes(ctx, state, queuedItemSizeUpdates);
2017
- queuedItemSizeUpdates.length = 0;
2018
- });
2019
- }
2020
- } else {
2021
- queuedItemSizeUpdates.push({ itemKey, sizeObj });
2022
- }
2023
- }
2024
2491
  function updateOneItemSize(state, itemKey, sizeObj) {
2492
+ var _a;
2025
2493
  const {
2026
2494
  sizes,
2027
2495
  indexByKey,
2028
2496
  sizesKnown,
2029
2497
  averageSizes,
2030
- props: { data, horizontal }
2498
+ props: { data, horizontal, getEstimatedItemSize, getItemType, getFixedItemSize }
2031
2499
  } = state;
2032
2500
  if (!data) return 0;
2033
2501
  const index = indexByKey.get(itemKey);
2034
2502
  const prevSize = getItemSize(state, itemKey, index, data);
2035
2503
  const size = Math.floor((horizontal ? sizeObj.width : sizeObj.height) * 8) / 8;
2036
2504
  sizesKnown.set(itemKey, size);
2037
- const itemType = "";
2038
- let averages = averageSizes[itemType];
2039
- if (!averages) {
2040
- averages = averageSizes[itemType] = { avg: 0, num: 0 };
2505
+ if (!getEstimatedItemSize && !getFixedItemSize && size > 0) {
2506
+ const itemType = getItemType ? (_a = getItemType(data[index], index)) != null ? _a : "" : "";
2507
+ let averages = averageSizes[itemType];
2508
+ if (!averages) {
2509
+ averages = averageSizes[itemType] = { avg: 0, num: 0 };
2510
+ }
2511
+ averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
2512
+ averages.num++;
2041
2513
  }
2042
- averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
2043
- averages.num++;
2044
2514
  if (!prevSize || Math.abs(prevSize - size) > 0.1) {
2045
2515
  sizes.set(itemKey, size);
2046
2516
  return size - prevSize;
@@ -2078,86 +2548,165 @@ function createColumnWrapperStyle(contentContainerStyle) {
2078
2548
  }
2079
2549
  }
2080
2550
  function getRenderedItem(ctx, state, key) {
2551
+ var _a;
2081
2552
  if (!state) {
2082
2553
  return null;
2083
2554
  }
2084
2555
  const {
2085
2556
  indexByKey,
2086
- props: { data, renderItem: renderItem2 }
2557
+ props: { data, getItemType, renderItem }
2087
2558
  } = state;
2088
2559
  const index = indexByKey.get(key);
2089
2560
  if (index === void 0) {
2090
2561
  return null;
2091
2562
  }
2092
2563
  let renderedItem = null;
2093
- if (renderItem2) {
2564
+ if (renderItem && data[index]) {
2094
2565
  const itemProps = {
2566
+ data,
2095
2567
  extraData: peek$(ctx, "extraData"),
2096
2568
  index,
2097
- item: data[index]
2569
+ item: data[index],
2570
+ type: getItemType ? (_a = getItemType(data[index], index)) != null ? _a : "" : ""
2098
2571
  };
2099
- renderedItem = React3__default.createElement(renderItem2, itemProps);
2572
+ renderedItem = isFunction(renderItem) ? renderItem(itemProps) : React3__default.createElement(renderItem, itemProps);
2100
2573
  }
2101
2574
  return { index, item: data[index], renderedItem };
2102
2575
  }
2103
2576
 
2577
+ // src/utils/throttledOnScroll.ts
2578
+ function useThrottledOnScroll(originalHandler, scrollEventThrottle) {
2579
+ const throttle = useThrottleDebounce("throttle");
2580
+ return (event) => throttle(originalHandler, scrollEventThrottle, { nativeEvent: event.nativeEvent });
2581
+ }
2582
+
2583
+ // src/utils/updateAveragesOnDataChange.ts
2584
+ function updateAveragesOnDataChange(state, oldData, newData) {
2585
+ var _a;
2586
+ const {
2587
+ averageSizes,
2588
+ sizesKnown,
2589
+ indexByKey,
2590
+ props: { itemsAreEqual, getItemType, keyExtractor }
2591
+ } = state;
2592
+ if (!itemsAreEqual || !oldData.length || !newData.length) {
2593
+ for (const key in averageSizes) {
2594
+ delete averageSizes[key];
2595
+ }
2596
+ return;
2597
+ }
2598
+ const itemTypesToPreserve = {};
2599
+ const newDataLength = newData.length;
2600
+ const oldDataLength = oldData.length;
2601
+ for (let newIndex = 0; newIndex < newDataLength; newIndex++) {
2602
+ const newItem = newData[newIndex];
2603
+ const id = keyExtractor ? keyExtractor(newItem, newIndex) : String(newIndex);
2604
+ const oldIndex = indexByKey.get(id);
2605
+ if (oldIndex !== void 0 && oldIndex < oldDataLength) {
2606
+ const knownSize = sizesKnown.get(id);
2607
+ if (knownSize === void 0) continue;
2608
+ const oldItem = oldData[oldIndex];
2609
+ const areEqual = itemsAreEqual(oldItem, newItem, newIndex, newData);
2610
+ if (areEqual) {
2611
+ const itemType = getItemType ? (_a = getItemType(newItem, newIndex)) != null ? _a : "" : "";
2612
+ let typeData = itemTypesToPreserve[itemType];
2613
+ if (!typeData) {
2614
+ typeData = itemTypesToPreserve[itemType] = { count: 0, totalSize: 0 };
2615
+ }
2616
+ typeData.totalSize += knownSize;
2617
+ typeData.count++;
2618
+ }
2619
+ }
2620
+ }
2621
+ for (const key in averageSizes) {
2622
+ delete averageSizes[key];
2623
+ }
2624
+ for (const itemType in itemTypesToPreserve) {
2625
+ const { totalSize, count } = itemTypesToPreserve[itemType];
2626
+ if (count > 0) {
2627
+ averageSizes[itemType] = {
2628
+ avg: totalSize / count,
2629
+ num: count
2630
+ };
2631
+ }
2632
+ }
2633
+ }
2634
+
2104
2635
  // src/components/LegendList.tsx
2105
2636
  var DEFAULT_DRAW_DISTANCE = 250;
2106
2637
  var DEFAULT_ITEM_SIZE = 100;
2107
- var LegendList = typedForwardRef(function LegendList2(props, forwardedRef) {
2108
- return /* @__PURE__ */ React3.createElement(StateProvider, null, /* @__PURE__ */ React3.createElement(LegendListInner, { ...props, ref: forwardedRef }));
2109
- });
2638
+ var LegendList = typedMemo(
2639
+ typedForwardRef(function LegendList2(props, forwardedRef) {
2640
+ const { children, data: dataProp, renderItem: renderItemProp, ...restProps } = props;
2641
+ const isChildrenMode = children !== void 0 && dataProp === void 0;
2642
+ const processedProps = isChildrenMode ? {
2643
+ ...restProps,
2644
+ data: (isArray(children) ? children : React3.Children.toArray(children)).flat(1),
2645
+ renderItem: ({ item }) => item
2646
+ } : {
2647
+ ...restProps,
2648
+ data: dataProp || [],
2649
+ renderItem: renderItemProp
2650
+ };
2651
+ return /* @__PURE__ */ React3.createElement(StateProvider, null, /* @__PURE__ */ React3.createElement(LegendListInner, { ...processedProps, ref: forwardedRef }));
2652
+ })
2653
+ );
2110
2654
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
2111
2655
  var _a;
2112
2656
  const {
2113
- data: dataProp = [],
2114
- initialScrollIndex: initialScrollIndexProp,
2115
- initialScrollOffset,
2116
- horizontal,
2117
- drawDistance = 250,
2118
- recycleItems = false,
2119
- onEndReachedThreshold = 0.5,
2120
- onStartReachedThreshold = 0.5,
2121
- maintainScrollAtEnd = false,
2122
- maintainScrollAtEndThreshold = 0.1,
2123
2657
  alignItemsAtEnd = false,
2124
- maintainVisibleContentPosition = false,
2125
- onScroll: onScrollProp,
2126
- onMomentumScrollEnd,
2127
- numColumns: numColumnsProp = 1,
2128
2658
  columnWrapperStyle,
2129
- keyExtractor: keyExtractorProp,
2130
- renderItem: renderItem2,
2131
- estimatedListSize,
2659
+ contentContainerStyle: contentContainerStyleProp,
2660
+ data: dataProp = [],
2661
+ drawDistance = 250,
2662
+ enableAverages = true,
2132
2663
  estimatedItemSize: estimatedItemSizeProp,
2664
+ estimatedListSize,
2665
+ extraData,
2133
2666
  getEstimatedItemSize,
2134
- suggestEstimatedItemSize,
2135
- ListHeaderComponent,
2667
+ getFixedItemSize,
2668
+ getItemType,
2669
+ horizontal,
2670
+ initialContainerPoolRatio = 2,
2671
+ initialScrollIndex: initialScrollIndexProp,
2672
+ initialScrollOffset: initialScrollOffsetProp,
2673
+ itemsAreEqual,
2674
+ keyExtractor: keyExtractorProp,
2136
2675
  ListEmptyComponent,
2676
+ ListHeaderComponent,
2677
+ maintainScrollAtEnd = false,
2678
+ maintainScrollAtEndThreshold = 0.1,
2679
+ maintainVisibleContentPosition = true,
2680
+ numColumns: numColumnsProp = 1,
2681
+ onEndReached,
2682
+ onEndReachedThreshold = 0.5,
2137
2683
  onItemSizeChanged,
2138
- refScrollView,
2139
- waitForInitialLayout = true,
2140
- extraData,
2141
- contentContainerStyle: contentContainerStyleProp,
2142
- style: styleProp,
2143
2684
  onLayout: onLayoutProp,
2685
+ onLoad,
2686
+ onMomentumScrollEnd,
2144
2687
  onRefresh,
2145
- refreshing,
2688
+ onScroll: onScrollProp,
2689
+ onStartReached,
2690
+ onStartReachedThreshold = 0.5,
2691
+ onViewableItemsChanged,
2146
2692
  progressViewOffset,
2693
+ recycleItems = false,
2147
2694
  refreshControl,
2148
- initialContainerPoolRatio = 2,
2695
+ refreshing,
2696
+ refScrollView,
2697
+ renderItem,
2698
+ scrollEventThrottle,
2699
+ snapToIndices,
2700
+ stickyIndices,
2701
+ style: styleProp,
2702
+ suggestEstimatedItemSize,
2149
2703
  viewabilityConfig,
2150
2704
  viewabilityConfigCallbackPairs,
2151
- snapToIndices,
2152
- onViewableItemsChanged,
2153
- onStartReached,
2154
- onEndReached,
2155
- onLoad,
2705
+ waitForInitialLayout = true,
2156
2706
  ...rest
2157
2707
  } = props;
2158
2708
  const [renderNum, setRenderNum] = useState(0);
2159
- const initialScroll = typeof initialScrollIndexProp === "number" ? { index: initialScrollIndexProp } : initialScrollIndexProp;
2160
- const initialScrollIndex = initialScroll == null ? void 0 : initialScroll.index;
2709
+ const initialScroll = initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? { index: initialScrollIndexProp.index || 0, viewOffset: initialScrollIndexProp.viewOffset || 0 } : { index: initialScrollIndexProp || 0, viewOffset: initialScrollOffsetProp || 0 } : void 0;
2161
2710
  const [canRender, setCanRender] = React3.useState(!IsNewArchitecture);
2162
2711
  const contentContainerStyle = { ...StyleSheet.flatten(contentContainerStyleProp) };
2163
2712
  const style = { ...StyleSheet.flatten(styleProp) };
@@ -2174,9 +2723,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2174
2723
  if (!refState.current) {
2175
2724
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : IsNewArchitecture ? { height: 0, width: 0 } : Dimensions.get("window"))[horizontal ? "width" : "height"];
2176
2725
  refState.current = {
2726
+ activeStickyIndex: void 0,
2177
2727
  averageSizes: {},
2178
2728
  columns: /* @__PURE__ */ new Map(),
2179
2729
  containerItemKeys: /* @__PURE__ */ new Set(),
2730
+ containerItemTypes: /* @__PURE__ */ new Map(),
2180
2731
  enableScrollForNextCalculateItemsInView: true,
2181
2732
  endBuffered: -1,
2182
2733
  endNoBuffer: -1,
@@ -2195,11 +2746,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2195
2746
  loadStartTime: Date.now(),
2196
2747
  minIndexSizeChanged: 0,
2197
2748
  nativeMarginTop: 0,
2198
- pendingAdjust: 0,
2749
+ positionRange: { end: -1, start: -1, valid: false },
2199
2750
  positions: /* @__PURE__ */ new Map(),
2200
2751
  props: {},
2201
2752
  queuedCalculateItemsInView: 0,
2202
- queuedItemSizeUpdates: [],
2203
2753
  refScroller: void 0,
2204
2754
  scroll: 0,
2205
2755
  scrollAdjustHandler: new ScrollAdjustHandler(ctx),
@@ -2209,12 +2759,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2209
2759
  scrollPending: 0,
2210
2760
  scrollPrev: 0,
2211
2761
  scrollPrevTime: 0,
2762
+ scrollProcessingEnabled: true,
2212
2763
  scrollTime: 0,
2213
2764
  sizes: /* @__PURE__ */ new Map(),
2214
2765
  sizesKnown: /* @__PURE__ */ new Map(),
2215
2766
  startBuffered: -1,
2216
2767
  startNoBuffer: -1,
2217
2768
  startReachedBlockedByTimer: false,
2769
+ stickyContainerPool: /* @__PURE__ */ new Set(),
2770
+ stickyContainers: /* @__PURE__ */ new Map(),
2218
2771
  timeoutSizeMessage: 0,
2219
2772
  timeouts: /* @__PURE__ */ new Set(),
2220
2773
  totalSize: 0,
@@ -2226,14 +2779,19 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2226
2779
  const state = refState.current;
2227
2780
  const isFirst = !state.props.renderItem;
2228
2781
  const didDataChange = state.props.data !== dataProp;
2782
+ const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
2229
2783
  state.props = {
2230
2784
  alignItemsAtEnd,
2231
2785
  data: dataProp,
2786
+ enableAverages,
2232
2787
  estimatedItemSize,
2233
2788
  getEstimatedItemSize,
2789
+ getFixedItemSize,
2790
+ getItemType,
2234
2791
  horizontal: !!horizontal,
2235
2792
  initialContainerPoolRatio,
2236
2793
  initialScroll,
2794
+ itemsAreEqual,
2237
2795
  keyExtractor,
2238
2796
  maintainScrollAtEnd,
2239
2797
  maintainScrollAtEndThreshold,
@@ -2243,12 +2801,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2243
2801
  onEndReachedThreshold,
2244
2802
  onItemSizeChanged,
2245
2803
  onLoad,
2246
- onScroll: onScrollProp,
2804
+ onScroll: throttleScrollFn,
2247
2805
  onStartReached,
2248
2806
  onStartReachedThreshold,
2249
- renderItem: renderItem2,
2807
+ recycleItems: !!recycleItems,
2808
+ renderItem,
2250
2809
  scrollBuffer,
2251
2810
  snapToIndices,
2811
+ stickyIndicesArr: stickyIndices != null ? stickyIndices : [],
2812
+ stickyIndicesSet: useMemo(() => new Set(stickyIndices != null ? stickyIndices : []), [stickyIndices == null ? void 0 : stickyIndices.join(",")]),
2252
2813
  stylePaddingBottom: stylePaddingBottomState,
2253
2814
  stylePaddingTop: stylePaddingTopState,
2254
2815
  suggestEstimatedItemSize: !!suggestEstimatedItemSize
@@ -2257,6 +2818,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2257
2818
  const checkResetContainers = (isFirst2) => {
2258
2819
  const state2 = refState.current;
2259
2820
  if (state2) {
2821
+ if (!isFirst2 && state2.props.data !== dataProp) {
2822
+ updateAveragesOnDataChange(state2, state2.props.data, dataProp);
2823
+ }
2260
2824
  state2.props.data = dataProp;
2261
2825
  if (!isFirst2) {
2262
2826
  calculateItemsInView(ctx, state2, { dataChanged: true, doMVCP: true });
@@ -2295,15 +2859,22 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2295
2859
  };
2296
2860
  if (isFirst) {
2297
2861
  initializeStateVars();
2298
- updateAllPositions(ctx, state);
2862
+ updateItemPositions(ctx, state);
2299
2863
  }
2300
2864
  const initialContentOffset = useMemo(() => {
2301
- const initialContentOffset2 = initialScrollOffset || calculateOffsetForIndex(ctx, state, initialScrollIndex);
2302
- refState.current.isStartReached = initialContentOffset2 < refState.current.scrollLength * onStartReachedThreshold;
2303
- if (initialContentOffset2 > 0) {
2304
- scrollTo(state, { animated: false, index: initialScrollIndex, offset: initialContentOffset2 });
2865
+ if (initialScroll) {
2866
+ const { index, viewOffset } = initialScroll;
2867
+ let initialContentOffset2 = viewOffset || 0;
2868
+ if (index !== void 0) {
2869
+ initialContentOffset2 += calculateOffsetForIndex(ctx, state, index);
2870
+ }
2871
+ refState.current.isStartReached = initialContentOffset2 < refState.current.scrollLength * onStartReachedThreshold;
2872
+ if (initialContentOffset2 > 0) {
2873
+ scrollTo(state, { animated: false, index, offset: initialContentOffset2 });
2874
+ }
2875
+ return initialContentOffset2;
2305
2876
  }
2306
- return initialContentOffset2;
2877
+ return 0;
2307
2878
  }, [renderNum]);
2308
2879
  if (isFirst || didDataChange || numColumnsProp !== peek$(ctx, "numColumns")) {
2309
2880
  refState.current.lastBatchingAction = Date.now();
@@ -2316,27 +2887,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2316
2887
  refState.current.positions.clear();
2317
2888
  }
2318
2889
  }
2319
- useLayoutEffect(() => {
2320
- if (IsNewArchitecture) {
2321
- let measured;
2322
- refScroller.current.measure((x, y, width, height) => {
2323
- measured = { height, width, x, y };
2324
- });
2325
- if (measured) {
2326
- const size = Math.floor(measured[horizontal ? "width" : "height"] * 8) / 8;
2327
- if (size) {
2328
- handleLayout(ctx, state, measured, setCanRender);
2329
- }
2330
- }
2331
- }
2332
- if (!isFirst) {
2333
- calculateItemsInView(ctx, state, { doMVCP: true });
2334
- }
2335
- }, [dataProp]);
2336
2890
  const onLayoutHeader = useCallback((rect, fromLayoutEffect) => {
2337
2891
  const size = rect[horizontal ? "width" : "height"];
2338
2892
  set$(ctx, "headerSize", size);
2339
- if (initialScroll) {
2893
+ if ((initialScroll == null ? void 0 : initialScroll.index) !== void 0) {
2340
2894
  if (IsNewArchitecture && Platform.OS !== "android") {
2341
2895
  if (fromLayoutEffect) {
2342
2896
  setRenderNum((v) => v + 1);
@@ -2354,7 +2908,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2354
2908
  }
2355
2909
  }, [snapToIndices]);
2356
2910
  useLayoutEffect(() => {
2357
- const didAllocateContainers = doInitialAllocateContainersCallback();
2911
+ const didAllocateContainers = dataProp.length > 0 && doInitialAllocateContainersCallback();
2358
2912
  if (!didAllocateContainers) {
2359
2913
  checkResetContainers(
2360
2914
  /*isFirst*/
@@ -2365,6 +2919,20 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2365
2919
  useLayoutEffect(() => {
2366
2920
  set$(ctx, "extraData", extraData);
2367
2921
  }, [extraData]);
2922
+ useLayoutEffect(() => {
2923
+ if (IsNewArchitecture) {
2924
+ let measured;
2925
+ refScroller.current.measure((x, y, width, height) => {
2926
+ measured = { height, width, x, y };
2927
+ });
2928
+ if (measured) {
2929
+ const size = Math.floor(measured[horizontal ? "width" : "height"] * 8) / 8;
2930
+ if (size) {
2931
+ handleLayout(ctx, state, measured, setCanRender);
2932
+ }
2933
+ }
2934
+ }
2935
+ }, []);
2368
2936
  useLayoutEffect(initializeStateVars, [
2369
2937
  memoizedLastItemKeys.join(","),
2370
2938
  numColumnsProp,
@@ -2420,10 +2988,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2420
2988
  const state2 = refState.current;
2421
2989
  return state2 ? {
2422
2990
  contentLength: state2.totalSize,
2991
+ data: state2.props.data,
2423
2992
  end: state2.endNoBuffer,
2424
2993
  endBuffered: state2.endBuffered,
2425
2994
  isAtEnd: state2.isAtEnd,
2426
2995
  isAtStart: state2.isAtStart,
2996
+ positionAtIndex: (index) => getPositionByIndex(ctx, state2, index),
2427
2997
  positions: state2.positions,
2428
2998
  scroll: state2.scroll,
2429
2999
  scrollLength: state2.scrollLength,
@@ -2450,7 +3020,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2450
3020
  const footerSize = peek$(ctx, "footerSize") || 0;
2451
3021
  scrollToIndex(ctx, state, {
2452
3022
  index,
2453
- viewOffset: -paddingBottom - footerSize,
3023
+ viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
2454
3024
  viewPosition: 1,
2455
3025
  ...options
2456
3026
  });
@@ -2465,6 +3035,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2465
3035
  }
2466
3036
  },
2467
3037
  scrollToOffset: (params) => scrollTo(state, params),
3038
+ setScrollProcessingEnabled: (enabled) => {
3039
+ refState.current.scrollProcessingEnabled = enabled;
3040
+ },
2468
3041
  setVisibleContentAnchorOffset: (value) => {
2469
3042
  const val = typeof value === "function" ? value(peek$(ctx, "scrollAdjustUserOffset") || 0) : value;
2470
3043
  set$(ctx, "scrollAdjustUserOffset", val);
@@ -2486,6 +3059,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2486
3059
  }),
2487
3060
  []
2488
3061
  );
3062
+ const onScrollHandler = useMemo(() => {
3063
+ const onScrollFn = fns.onScroll;
3064
+ if (stickyIndices == null ? void 0 : stickyIndices.length) {
3065
+ const { animatedScrollY } = ctx;
3066
+ return Animated.event([{ nativeEvent: { contentOffset: { [horizontal ? "x" : "y"]: animatedScrollY } } }], {
3067
+ listener: onScrollFn,
3068
+ useNativeDriver: true
3069
+ });
3070
+ }
3071
+ return onScrollFn;
3072
+ }, [stickyIndices == null ? void 0 : stickyIndices.length, horizontal, scrollEventThrottle]);
2489
3073
  return /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(
2490
3074
  ListComponent,
2491
3075
  {
@@ -2502,14 +3086,20 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2502
3086
  onLayout,
2503
3087
  onLayoutHeader,
2504
3088
  onMomentumScrollEnd: (event) => {
2505
- requestAnimationFrame(() => {
2506
- finishScrollTo(refState.current);
2507
- });
3089
+ if (IsNewArchitecture) {
3090
+ requestAnimationFrame(() => {
3091
+ finishScrollTo(refState.current);
3092
+ });
3093
+ } else {
3094
+ setTimeout(() => {
3095
+ finishScrollTo(refState.current);
3096
+ }, 1e3);
3097
+ }
2508
3098
  if (onMomentumScrollEnd) {
2509
3099
  onMomentumScrollEnd(event);
2510
3100
  }
2511
3101
  },
2512
- onScroll: fns.onScroll,
3102
+ onScroll: onScrollHandler,
2513
3103
  recycleItems,
2514
3104
  refreshControl: refreshControl ? stylePaddingTopState > 0 ? React3.cloneElement(refreshControl, {
2515
3105
  progressViewOffset: (refreshControl.props.progressViewOffset || 0) + stylePaddingTopState
@@ -2525,6 +3115,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2525
3115
  scrollAdjustHandler: (_a = refState.current) == null ? void 0 : _a.scrollAdjustHandler,
2526
3116
  scrollEventThrottle: Platform.OS === "web" ? 16 : void 0,
2527
3117
  snapToIndices,
3118
+ stickyIndices,
2528
3119
  style,
2529
3120
  updateItemSize: fns.updateItemSize,
2530
3121
  waitForInitialLayout
@@ -2532,17 +3123,4 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2532
3123
  ), __DEV__ && ENABLE_DEBUG_VIEW && /* @__PURE__ */ React3.createElement(DebugView, { state: refState.current }));
2533
3124
  });
2534
3125
 
2535
- // src/components/LazyLegendList.tsx
2536
- var typedForwardRef2 = forwardRef;
2537
- var renderItem = ({ item }) => item;
2538
- var LazyLegendList = typedForwardRef2(function LazyLegendList2(props, forwardedRef) {
2539
- const { LegendList: LegendListProp, children, ...rest } = props;
2540
- const LegendListComponent = LegendListProp != null ? LegendListProp : LegendList;
2541
- const data = (isArray(children) ? children : React3.Children.toArray(children)).flat(1);
2542
- return (
2543
- // @ts-expect-error TODO: Fix this type
2544
- /* @__PURE__ */ React3.createElement(LegendListComponent, { ...rest, data, ref: forwardedRef, renderItem })
2545
- );
2546
- });
2547
-
2548
- export { LazyLegendList, LegendList, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useViewability, useViewabilityAmount };
3126
+ export { LegendList, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };