@legendapp/list 2.0.0-next.8 → 2.0.0

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) {
@@ -725,33 +881,45 @@ function calculateOffsetForIndex(ctx, state, index) {
725
881
  let position = 0;
726
882
  if (index !== void 0) {
727
883
  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;
884
+ const paddingTop = peek$(ctx, "stylePaddingTop");
885
+ if (paddingTop) {
886
+ position += paddingTop;
887
+ }
888
+ const headerSize = peek$(ctx, "headerSize");
889
+ if (headerSize) {
890
+ position += headerSize;
891
+ }
736
892
  }
737
893
  return position;
738
894
  }
739
895
 
740
896
  // src/utils/getItemSize.ts
741
897
  function getItemSize(state, key, index, data, useAverageSize) {
898
+ var _a, _b;
742
899
  const {
743
900
  sizesKnown,
744
901
  sizes,
745
902
  scrollingTo,
746
- props: { estimatedItemSize, getEstimatedItemSize }
903
+ averageSizes,
904
+ props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType }
747
905
  } = state;
748
906
  const sizeKnown = sizesKnown.get(key);
749
907
  if (sizeKnown !== void 0) {
750
908
  return sizeKnown;
751
909
  }
752
910
  let size;
753
- if (useAverageSize !== void 0 && sizeKnown === void 0 && !getEstimatedItemSize && !scrollingTo) {
754
- size = useAverageSize;
911
+ const itemType = getItemType ? (_a = getItemType(data, index)) != null ? _a : "" : "";
912
+ if (getFixedItemSize) {
913
+ size = getFixedItemSize(index, data, itemType);
914
+ if (size !== void 0) {
915
+ sizesKnown.set(key, size);
916
+ }
917
+ }
918
+ if (size === void 0 && useAverageSize && sizeKnown === void 0 && !scrollingTo) {
919
+ const averageSizeForType = (_b = averageSizes[itemType]) == null ? void 0 : _b.avg;
920
+ if (averageSizeForType !== void 0) {
921
+ size = roundSize(averageSizeForType);
922
+ }
755
923
  }
756
924
  if (size === void 0) {
757
925
  size = sizes.get(key);
@@ -760,7 +928,7 @@ function getItemSize(state, key, index, data, useAverageSize) {
760
928
  }
761
929
  }
762
930
  if (size === void 0) {
763
- size = getEstimatedItemSize ? getEstimatedItemSize(index, data) : estimatedItemSize;
931
+ size = getEstimatedItemSize ? getEstimatedItemSize(index, data, itemType) : estimatedItemSize;
764
932
  }
765
933
  sizes.set(key, size);
766
934
  return size;
@@ -779,11 +947,52 @@ function calculateOffsetWithOffsetPosition(state, offsetParam, params) {
779
947
  return offset;
780
948
  }
781
949
 
950
+ // src/core/finishScrollTo.ts
951
+ var finishScrollTo = (state) => {
952
+ if (state) {
953
+ state.scrollingTo = void 0;
954
+ state.scrollHistory.length = 0;
955
+ }
956
+ };
957
+
958
+ // src/core/scrollTo.ts
959
+ function scrollTo(state, params = {}) {
960
+ var _a;
961
+ const { animated, noScrollingTo } = params;
962
+ const {
963
+ refScroller,
964
+ props: { horizontal }
965
+ } = state;
966
+ const offset = calculateOffsetWithOffsetPosition(state, params.offset, params);
967
+ state.scrollHistory.length = 0;
968
+ if (!noScrollingTo) {
969
+ state.scrollingTo = params;
970
+ }
971
+ state.scrollPending = offset;
972
+ (_a = refScroller.current) == null ? void 0 : _a.scrollTo({
973
+ animated: !!animated,
974
+ x: horizontal ? offset : 0,
975
+ y: horizontal ? 0 : offset
976
+ });
977
+ if (!animated) {
978
+ state.scroll = offset;
979
+ setTimeout(() => finishScrollTo(state), 100);
980
+ }
981
+ }
982
+
782
983
  // src/utils/requestAdjust.ts
783
- function requestAdjust(ctx, state, positionDiff) {
984
+ function requestAdjust(ctx, state, positionDiff, dataChanged) {
784
985
  if (Math.abs(positionDiff) > 0.1) {
986
+ const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
785
987
  const doit = () => {
786
- state.scrollAdjustHandler.requestAdjust(positionDiff);
988
+ if (needsScrollWorkaround) {
989
+ scrollTo(state, {
990
+ noScrollingTo: true,
991
+ offset: state.scroll
992
+ });
993
+ } else {
994
+ state.scrollAdjustHandler.requestAdjust(positionDiff);
995
+ }
787
996
  };
788
997
  state.scroll += positionDiff;
789
998
  state.scrollForNextCalculateItemsInView = void 0;
@@ -802,49 +1011,72 @@ function requestAdjust(ctx, state, positionDiff) {
802
1011
  if (state.ignoreScrollFromMVCPTimeout) {
803
1012
  clearTimeout(state.ignoreScrollFromMVCPTimeout);
804
1013
  }
805
- state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
806
- state.ignoreScrollFromMVCP = void 0;
807
- }, 100);
1014
+ state.ignoreScrollFromMVCPTimeout = setTimeout(
1015
+ () => {
1016
+ state.ignoreScrollFromMVCP = void 0;
1017
+ },
1018
+ needsScrollWorkaround ? 250 : 100
1019
+ );
808
1020
  } else {
809
1021
  requestAnimationFrame(doit);
810
1022
  }
811
1023
  }
812
1024
  }
813
1025
 
814
- // src/core/prepareMVCP.ts
815
- function prepareMVCP(ctx, state) {
1026
+ // src/core/mvcp.ts
1027
+ function prepareMVCP(ctx, state, dataChanged) {
816
1028
  const {
1029
+ idsInView,
817
1030
  positions,
818
1031
  scrollingTo,
819
1032
  props: { maintainVisibleContentPosition }
820
1033
  } = state;
821
1034
  let prevPosition;
822
1035
  let targetId;
823
- let targetIndex;
1036
+ const idsInViewWithPositions = [];
824
1037
  const scrollTarget = scrollingTo == null ? void 0 : scrollingTo.index;
825
1038
  if (maintainVisibleContentPosition) {
826
1039
  const indexByKey = state.indexByKey;
827
1040
  if (scrollTarget !== void 0) {
828
1041
  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);
1042
+ } else if (idsInView.length > 0 && peek$(ctx, "containersDidLayout")) {
1043
+ if (dataChanged) {
1044
+ for (let i = 0; i < idsInView.length; i++) {
1045
+ const id = idsInView[i];
1046
+ const index = indexByKey.get(id);
1047
+ if (index !== void 0) {
1048
+ idsInViewWithPositions.push({ id, position: positions.get(id) });
1049
+ }
1050
+ }
1051
+ } else {
1052
+ targetId = state.idsInView.find((id) => indexByKey.get(id) !== void 0);
1053
+ }
833
1054
  }
834
- if (targetId !== void 0 && targetIndex !== void 0) {
1055
+ if (targetId !== void 0) {
835
1056
  prevPosition = positions.get(targetId);
836
1057
  }
837
1058
  }
838
1059
  return () => {
1060
+ let positionDiff;
1061
+ if (dataChanged && targetId === void 0) {
1062
+ for (let i = 0; i < idsInViewWithPositions.length; i++) {
1063
+ const { id, position } = idsInViewWithPositions[i];
1064
+ const newPosition = positions.get(id);
1065
+ if (newPosition !== void 0) {
1066
+ positionDiff = newPosition - position;
1067
+ break;
1068
+ }
1069
+ }
1070
+ }
839
1071
  if (targetId !== void 0 && prevPosition !== void 0) {
840
1072
  const newPosition = positions.get(targetId);
841
1073
  if (newPosition !== void 0) {
842
- const positionDiff = newPosition - prevPosition;
843
- if (Math.abs(positionDiff) > 0.1) {
844
- requestAdjust(ctx, state, positionDiff);
845
- }
1074
+ positionDiff = newPosition - prevPosition;
846
1075
  }
847
1076
  }
1077
+ if (positionDiff !== void 0 && Math.abs(positionDiff) > 0.1) {
1078
+ requestAdjust(ctx, state, positionDiff, dataChanged);
1079
+ }
848
1080
  };
849
1081
  }
850
1082
 
@@ -853,10 +1085,10 @@ function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
853
1085
  if (stylePaddingTop !== void 0) {
854
1086
  const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
855
1087
  if (stylePaddingTop < prevStylePaddingTop) {
856
- let prevTotalSize = peek$(ctx, "totalSize");
1088
+ let prevTotalSize = peek$(ctx, "totalSize") || 0;
857
1089
  set$(ctx, "totalSize", prevTotalSize + prevStylePaddingTop);
858
1090
  state.timeoutSetPaddingTop = setTimeout(() => {
859
- prevTotalSize = peek$(ctx, "totalSize");
1091
+ prevTotalSize = peek$(ctx, "totalSize") || 0;
860
1092
  set$(ctx, "totalSize", prevTotalSize - prevStylePaddingTop);
861
1093
  }, 16);
862
1094
  }
@@ -920,43 +1152,6 @@ function addTotalSize(ctx, state, key, add) {
920
1152
  }
921
1153
  }
922
1154
 
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
1155
  // src/utils/updateSnapToOffsets.ts
961
1156
  function updateSnapToOffsets(ctx, state) {
962
1157
  const {
@@ -973,69 +1168,42 @@ function updateSnapToOffsets(ctx, state) {
973
1168
  }
974
1169
 
975
1170
  // src/core/updateAllPositions.ts
976
- function updateAllPositions(ctx, state, dataChanged) {
977
- var _a, _b, _c, _d, _e;
1171
+ function updateAllPositions(ctx, state, dataChanged, startIndex = 0) {
1172
+ var _a, _b, _c, _d, _e, _f;
978
1173
  const {
979
- averageSizes,
980
1174
  columns,
981
1175
  indexByKey,
982
1176
  positions,
983
- firstFullyOnScreenIndex,
984
1177
  idCache,
985
1178
  sizesKnown,
986
- props: { snapToIndices }
1179
+ props: { getEstimatedItemSize, snapToIndices, enableAverages }
987
1180
  } = state;
988
1181
  const data = state.props.data;
989
1182
  const numColumns = peek$(ctx, "numColumns");
990
1183
  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
- }
1184
+ const useAverageSize = enableAverages && !getEstimatedItemSize;
1030
1185
  let currentRowTop = 0;
1031
1186
  let column = 1;
1032
1187
  let maxSizeInRow = 0;
1033
1188
  const hasColumns = numColumns > 1;
1189
+ if (startIndex > 0) {
1190
+ const prevIndex = startIndex - 1;
1191
+ const prevId = (_a = idCache.get(prevIndex)) != null ? _a : getId(state, prevIndex);
1192
+ const prevPosition = (_b = positions.get(prevId)) != null ? _b : 0;
1193
+ if (hasColumns) {
1194
+ const prevColumn = (_c = columns.get(prevId)) != null ? _c : 1;
1195
+ currentRowTop = prevPosition;
1196
+ column = prevColumn % numColumns + 1;
1197
+ } else {
1198
+ const prevSize = (_d = sizesKnown.get(prevId)) != null ? _d : getItemSize(state, prevId, prevIndex, data[prevIndex], useAverageSize);
1199
+ currentRowTop = prevPosition + prevSize;
1200
+ }
1201
+ }
1034
1202
  const needsIndexByKey = dataChanged || indexByKey.size === 0;
1035
1203
  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);
1204
+ for (let i = startIndex; i < dataLength; i++) {
1205
+ const id = (_e = idCache.get(i)) != null ? _e : getId(state, i);
1206
+ const size = (_f = sizesKnown.get(id)) != null ? _f : getItemSize(state, id, i, data[i], useAverageSize);
1039
1207
  if (__DEV__ && needsIndexByKey) {
1040
1208
  if (indexByKeyForChecking.has(id)) {
1041
1209
  console.error(
@@ -1070,7 +1238,19 @@ function updateAllPositions(ctx, state, dataChanged) {
1070
1238
  }
1071
1239
 
1072
1240
  // src/core/viewability.ts
1073
- var mapViewabilityConfigCallbackPairs = /* @__PURE__ */ new Map();
1241
+ function ensureViewabilityState(ctx, configId) {
1242
+ let map = ctx.mapViewabilityConfigStates;
1243
+ if (!map) {
1244
+ map = /* @__PURE__ */ new Map();
1245
+ ctx.mapViewabilityConfigStates = map;
1246
+ }
1247
+ let state = map.get(configId);
1248
+ if (!state) {
1249
+ state = { end: -1, previousEnd: -1, previousStart: -1, start: -1, viewableItems: [] };
1250
+ map.set(configId, state);
1251
+ }
1252
+ return state;
1253
+ }
1074
1254
  function setupViewability(props) {
1075
1255
  let { viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged } = props;
1076
1256
  if (viewabilityConfig || onViewableItemsChanged) {
@@ -1084,17 +1264,6 @@ function setupViewability(props) {
1084
1264
  }
1085
1265
  ];
1086
1266
  }
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
1267
  return viewabilityConfigCallbackPairs;
1099
1268
  }
1100
1269
  function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollSize, start, end) {
@@ -1103,9 +1272,7 @@ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollS
1103
1272
  props: { data }
1104
1273
  } = state;
1105
1274
  for (const viewabilityConfigCallbackPair of viewabilityConfigCallbackPairs) {
1106
- const viewabilityState = mapViewabilityConfigCallbackPairs.get(
1107
- viewabilityConfigCallbackPair.viewabilityConfig.id
1108
- );
1275
+ const viewabilityState = ensureViewabilityState(ctx, viewabilityConfigCallbackPair.viewabilityConfig.id);
1109
1276
  viewabilityState.start = start;
1110
1277
  viewabilityState.end = end;
1111
1278
  if (viewabilityConfigCallbackPair.viewabilityConfig.minimumViewTime) {
@@ -1122,7 +1289,7 @@ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollS
1122
1289
  function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, state, ctx, scrollSize) {
1123
1290
  const { viewabilityConfig, onViewableItemsChanged } = viewabilityConfigCallbackPair;
1124
1291
  const configId = viewabilityConfig.id;
1125
- const viewabilityState = mapViewabilityConfigCallbackPairs.get(configId);
1292
+ const viewabilityState = ensureViewabilityState(ctx, configId);
1126
1293
  const { viewableItems: previousViewableItems, start, end } = viewabilityState;
1127
1294
  const viewabilityTokens = /* @__PURE__ */ new Map();
1128
1295
  for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
@@ -1201,6 +1368,15 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
1201
1368
  }
1202
1369
  }
1203
1370
  }
1371
+ function shallowEqual(prev, next) {
1372
+ if (!prev) return false;
1373
+ const keys = Object.keys(next);
1374
+ for (let i = 0; i < keys.length; i++) {
1375
+ const k = keys[i];
1376
+ if (prev[k] !== next[k]) return false;
1377
+ }
1378
+ return true;
1379
+ }
1204
1380
  function computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
1205
1381
  const { sizes, positions, scroll: scrollState } = state;
1206
1382
  const topPad = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
@@ -1229,7 +1405,8 @@ function computeViewability(state, ctx, viewabilityConfig, containerId, key, scr
1229
1405
  size,
1230
1406
  sizeVisible
1231
1407
  };
1232
- if (JSON.stringify(value) !== JSON.stringify(ctx.mapViewabilityAmountValues.get(containerId))) {
1408
+ const prev = ctx.mapViewabilityAmountValues.get(containerId);
1409
+ if (!shallowEqual(prev, value)) {
1233
1410
  ctx.mapViewabilityAmountValues.set(containerId, value);
1234
1411
  const cb = ctx.mapViewabilityAmountCallbacks.get(containerId);
1235
1412
  if (cb) {
@@ -1258,6 +1435,7 @@ function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
1258
1435
  const cb = ctx.mapViewabilityCallbacks.get(key);
1259
1436
  cb == null ? void 0 : cb(viewToken);
1260
1437
  }
1438
+ var batchedUpdates = unstable_batchedUpdates || ((callback) => callback());
1261
1439
 
1262
1440
  // src/utils/checkAllSizesKnown.ts
1263
1441
  function checkAllSizesKnown(state) {
@@ -1274,35 +1452,79 @@ function checkAllSizesKnown(state) {
1274
1452
  }
1275
1453
 
1276
1454
  // src/utils/findAvailableContainers.ts
1277
- function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffered, pendingRemoval) {
1455
+ function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers) {
1278
1456
  const numContainers = peek$(ctx, "numContainers");
1457
+ const { stickyContainerPool, containerItemTypes } = state;
1279
1458
  const result = [];
1280
1459
  const availableContainers = [];
1281
- for (let u = 0; u < numContainers; u++) {
1460
+ const stickyIndicesSet = state.props.stickyIndicesSet;
1461
+ const stickyItemIndices = (needNewContainers == null ? void 0 : needNewContainers.filter((index) => stickyIndicesSet.has(index))) || [];
1462
+ const canReuseContainer = (containerIndex, requiredType) => {
1463
+ if (!requiredType) return true;
1464
+ const existingType = containerItemTypes.get(containerIndex);
1465
+ if (!existingType) return true;
1466
+ return existingType === requiredType;
1467
+ };
1468
+ const neededTypes = requiredItemTypes ? [...requiredItemTypes] : [];
1469
+ let typeIndex = 0;
1470
+ for (let i = 0; i < stickyItemIndices.length; i++) {
1471
+ const requiredType = neededTypes[typeIndex];
1472
+ let foundContainer = false;
1473
+ for (const containerIndex of stickyContainerPool) {
1474
+ const key = peek$(ctx, `containerItemKey${containerIndex}`);
1475
+ const isPendingRemoval = pendingRemoval.includes(containerIndex);
1476
+ if ((key === void 0 || isPendingRemoval) && canReuseContainer(containerIndex, requiredType)) {
1477
+ result.push(containerIndex);
1478
+ if (isPendingRemoval) {
1479
+ const index = pendingRemoval.indexOf(containerIndex);
1480
+ pendingRemoval.splice(index, 1);
1481
+ }
1482
+ foundContainer = true;
1483
+ if (requiredItemTypes) typeIndex++;
1484
+ break;
1485
+ }
1486
+ }
1487
+ if (!foundContainer) {
1488
+ const newContainerIndex = numContainers + result.filter((index) => index >= numContainers).length;
1489
+ result.push(newContainerIndex);
1490
+ stickyContainerPool.add(newContainerIndex);
1491
+ if (requiredItemTypes) typeIndex++;
1492
+ }
1493
+ }
1494
+ for (let u = 0; u < numContainers && result.length < numNeeded; u++) {
1495
+ if (stickyContainerPool.has(u)) {
1496
+ continue;
1497
+ }
1282
1498
  const key = peek$(ctx, `containerItemKey${u}`);
1283
1499
  let isOk = key === void 0;
1284
1500
  if (!isOk) {
1285
1501
  const index = pendingRemoval.indexOf(u);
1286
1502
  if (index !== -1) {
1287
1503
  pendingRemoval.splice(index, 1);
1288
- isOk = true;
1504
+ const requiredType = neededTypes[typeIndex];
1505
+ isOk = canReuseContainer(u, requiredType);
1289
1506
  }
1290
1507
  }
1291
1508
  if (isOk) {
1292
1509
  result.push(u);
1293
- if (result.length >= numNeeded) {
1294
- return result;
1510
+ if (requiredItemTypes) {
1511
+ typeIndex++;
1295
1512
  }
1296
1513
  }
1297
1514
  }
1298
- for (let u = 0; u < numContainers; u++) {
1515
+ for (let u = 0; u < numContainers && result.length < numNeeded; u++) {
1516
+ if (stickyContainerPool.has(u)) {
1517
+ continue;
1518
+ }
1299
1519
  const key = peek$(ctx, `containerItemKey${u}`);
1300
1520
  if (key === void 0) continue;
1301
1521
  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 });
1522
+ const isOutOfView = index < startBuffered || index > endBuffered;
1523
+ if (isOutOfView) {
1524
+ const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
1525
+ if (!requiredItemTypes || typeIndex < neededTypes.length && canReuseContainer(u, neededTypes[typeIndex])) {
1526
+ availableContainers.push({ distance, index: u });
1527
+ }
1306
1528
  }
1307
1529
  }
1308
1530
  const remaining = numNeeded - result.length;
@@ -1314,6 +1536,9 @@ function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffer
1314
1536
  }
1315
1537
  for (const container of availableContainers) {
1316
1538
  result.push(container.index);
1539
+ if (requiredItemTypes) {
1540
+ typeIndex++;
1541
+ }
1317
1542
  }
1318
1543
  }
1319
1544
  const stillNeeded = numNeeded - result.length;
@@ -1342,37 +1567,44 @@ function comparatorByDistance(a, b) {
1342
1567
  return b.distance - a.distance;
1343
1568
  }
1344
1569
 
1345
- // src/core/finishScrollTo.ts
1346
- var finishScrollTo = (state) => {
1347
- if (state) {
1348
- state.scrollingTo = void 0;
1349
- state.scrollHistory.length = 0;
1570
+ // src/utils/getScrollVelocity.ts
1571
+ var getScrollVelocity = (state) => {
1572
+ const { scrollHistory } = state;
1573
+ let velocity = 0;
1574
+ if (scrollHistory.length >= 1) {
1575
+ const newest = scrollHistory[scrollHistory.length - 1];
1576
+ let oldest;
1577
+ let start = 0;
1578
+ const now = Date.now();
1579
+ for (let i = 0; i < scrollHistory.length - 1; i++) {
1580
+ const entry = scrollHistory[i];
1581
+ const nextEntry = scrollHistory[i + 1];
1582
+ if (i > 0) {
1583
+ const prevEntry = scrollHistory[i - 1];
1584
+ const prevDirection = entry.scroll - prevEntry.scroll;
1585
+ const currentDirection = nextEntry.scroll - entry.scroll;
1586
+ if (prevDirection > 0 && currentDirection < 0 || prevDirection < 0 && currentDirection > 0) {
1587
+ start = i;
1588
+ break;
1589
+ }
1590
+ }
1591
+ }
1592
+ for (let i = start; i < scrollHistory.length - 1; i++) {
1593
+ const entry = scrollHistory[i];
1594
+ if (now - entry.time <= 1e3) {
1595
+ oldest = entry;
1596
+ break;
1597
+ }
1598
+ }
1599
+ if (oldest && oldest !== newest) {
1600
+ const scrollDiff = newest.scroll - oldest.scroll;
1601
+ const timeDiff = newest.time - oldest.time;
1602
+ velocity = timeDiff > 0 ? scrollDiff / timeDiff : 0;
1603
+ }
1350
1604
  }
1605
+ return velocity;
1351
1606
  };
1352
1607
 
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
1608
  // src/core/scrollToIndex.ts
1377
1609
  function scrollToIndex(ctx, state, { index, viewOffset = 0, animated = true, viewPosition }) {
1378
1610
  if (index >= state.props.data.length) {
@@ -1450,43 +1682,123 @@ function checkAtBottom(ctx, state) {
1450
1682
  );
1451
1683
  }
1452
1684
  }
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 });
1685
+
1686
+ // src/utils/setDidLayout.ts
1687
+ function setDidLayout(ctx, state) {
1688
+ const {
1689
+ loadStartTime,
1690
+ initialScroll,
1691
+ props: { onLoad }
1692
+ } = state;
1693
+ state.queuedInitialLayout = true;
1694
+ checkAtBottom(ctx, state);
1695
+ const setIt = () => {
1696
+ set$(ctx, "containersDidLayout", true);
1697
+ if (onLoad) {
1698
+ onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1699
+ }
1700
+ };
1701
+ if (Platform.OS === "android" || !IsNewArchitecture) {
1702
+ if (initialScroll) {
1703
+ queueMicrotask(() => {
1704
+ scrollToIndex(ctx, state, { ...initialScroll, animated: false });
1705
+ requestAnimationFrame(() => {
1706
+ scrollToIndex(ctx, state, { ...initialScroll, animated: false });
1707
+ setIt();
1708
+ });
1709
+ });
1710
+ } else {
1711
+ queueMicrotask(setIt);
1712
+ }
1713
+ } else {
1714
+ setIt();
1715
+ }
1716
+ }
1717
+
1718
+ // src/core/calculateItemsInView.ts
1719
+ function findCurrentStickyIndex(stickyArray, scroll, state) {
1720
+ var _a;
1721
+ const idCache = state.idCache;
1722
+ const positions = state.positions;
1723
+ for (let i = stickyArray.length - 1; i >= 0; i--) {
1724
+ const stickyId = (_a = idCache.get(stickyArray[i])) != null ? _a : getId(state, stickyArray[i]);
1725
+ const stickyPos = stickyId ? positions.get(stickyId) : void 0;
1726
+ if (stickyPos !== void 0 && scroll >= stickyPos) {
1727
+ return i;
1728
+ }
1729
+ }
1730
+ return -1;
1731
+ }
1732
+ function getActiveStickyIndices(ctx, state, stickyIndices) {
1733
+ return new Set(
1734
+ 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))
1735
+ );
1736
+ }
1737
+ function handleStickyActivation(ctx, state, stickyIndices, stickyArray, scroll, needNewContainers, startBuffered, endBuffered) {
1738
+ var _a;
1739
+ const activeIndices = getActiveStickyIndices(ctx, state, stickyIndices);
1740
+ const currentStickyIdx = findCurrentStickyIndex(stickyArray, scroll, state);
1741
+ for (let offset = 0; offset <= 1; offset++) {
1742
+ const idx = currentStickyIdx - offset;
1743
+ if (idx < 0 || activeIndices.has(stickyArray[idx])) continue;
1744
+ const stickyIndex = stickyArray[idx];
1745
+ const stickyId = (_a = state.idCache.get(stickyIndex)) != null ? _a : getId(state, stickyIndex);
1746
+ if (stickyId && !state.containerItemKeys.has(stickyId) && (stickyIndex < startBuffered || stickyIndex > endBuffered)) {
1747
+ needNewContainers.push(stickyIndex);
1748
+ }
1465
1749
  }
1466
- set$(ctx, "containersDidLayout", true);
1467
- if (onLoad) {
1468
- onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1750
+ }
1751
+ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, pendingRemoval) {
1752
+ var _a, _b, _c;
1753
+ const currentStickyIdx = findCurrentStickyIndex(stickyArray, scroll, state);
1754
+ for (const containerIndex of state.stickyContainerPool) {
1755
+ const itemKey = peek$(ctx, `containerItemKey${containerIndex}`);
1756
+ const itemIndex = itemKey ? state.indexByKey.get(itemKey) : void 0;
1757
+ if (itemIndex === void 0) continue;
1758
+ const arrayIdx = stickyArray.indexOf(itemIndex);
1759
+ if (arrayIdx === -1) continue;
1760
+ const isRecentSticky = arrayIdx >= currentStickyIdx - 1 && arrayIdx <= currentStickyIdx + 1;
1761
+ if (isRecentSticky) continue;
1762
+ const nextIndex = stickyArray[arrayIdx + 1];
1763
+ let shouldRecycle = false;
1764
+ if (nextIndex) {
1765
+ const nextId = (_a = state.idCache.get(nextIndex)) != null ? _a : getId(state, nextIndex);
1766
+ const nextPos = nextId ? state.positions.get(nextId) : void 0;
1767
+ shouldRecycle = nextPos !== void 0 && scroll > nextPos + scrollBuffer * 2;
1768
+ } else {
1769
+ const currentId = (_b = state.idCache.get(itemIndex)) != null ? _b : getId(state, itemIndex);
1770
+ if (currentId) {
1771
+ const currentPos = state.positions.get(currentId);
1772
+ const currentSize = (_c = state.sizes.get(currentId)) != null ? _c : getItemSize(state, currentId, itemIndex, state.props.data[itemIndex]);
1773
+ shouldRecycle = currentPos !== void 0 && scroll > currentPos + currentSize + scrollBuffer * 3;
1774
+ }
1775
+ }
1776
+ if (shouldRecycle) {
1777
+ pendingRemoval.push(containerIndex);
1778
+ }
1469
1779
  }
1470
1780
  }
1471
-
1472
- // src/core/calculateItemsInView.ts
1473
1781
  function calculateItemsInView(ctx, state, params = {}) {
1474
- unstable_batchedUpdates(() => {
1782
+ batchedUpdates(() => {
1475
1783
  var _a, _b, _c, _d, _e, _f, _g, _h;
1476
1784
  const {
1477
- scrollLength,
1478
- startBufferedId: startBufferedIdOrig,
1479
- positions,
1480
1785
  columns,
1481
1786
  containerItemKeys,
1787
+ enableScrollForNextCalculateItemsInView,
1482
1788
  idCache,
1483
- sizes,
1484
1789
  indexByKey,
1790
+ minIndexSizeChanged,
1791
+ positions,
1485
1792
  scrollForNextCalculateItemsInView,
1486
- enableScrollForNextCalculateItemsInView,
1487
- minIndexSizeChanged
1793
+ scrollLength,
1794
+ sizes,
1795
+ startBufferedId: startBufferedIdOrig,
1796
+ viewabilityConfigCallbackPairs,
1797
+ props: { getItemType, initialScroll, itemsAreEqual, keyExtractor, scrollBuffer }
1488
1798
  } = state;
1489
- const data = state.props.data;
1799
+ const { data } = state.props;
1800
+ const stickyIndicesArr = state.props.stickyIndicesArr || [];
1801
+ const stickyIndicesSet = state.props.stickyIndicesSet || /* @__PURE__ */ new Set();
1490
1802
  const prevNumContainers = peek$(ctx, "numContainers");
1491
1803
  if (!data || scrollLength === 0 || !prevNumContainers) {
1492
1804
  return;
@@ -1498,14 +1810,22 @@ function calculateItemsInView(ctx, state, params = {}) {
1498
1810
  const { dataChanged, doMVCP } = params;
1499
1811
  const speed = getScrollVelocity(state);
1500
1812
  if (doMVCP || dataChanged) {
1501
- const checkMVCP = doMVCP ? prepareMVCP(ctx, state) : void 0;
1502
- updateAllPositions(ctx, state, dataChanged);
1813
+ const checkMVCP = doMVCP ? prepareMVCP(ctx, state, dataChanged) : void 0;
1814
+ if (dataChanged) {
1815
+ indexByKey.clear();
1816
+ idCache.clear();
1817
+ positions.clear();
1818
+ }
1819
+ const startIndex = dataChanged ? 0 : minIndexSizeChanged != null ? minIndexSizeChanged : 0;
1820
+ updateAllPositions(ctx, state, dataChanged, startIndex);
1821
+ if (minIndexSizeChanged !== void 0) {
1822
+ state.minIndexSizeChanged = void 0;
1823
+ }
1503
1824
  checkMVCP == null ? void 0 : checkMVCP();
1504
1825
  }
1505
1826
  const scrollExtra = 0;
1506
1827
  const { queuedInitialLayout } = state;
1507
1828
  let { scroll: scrollState } = state;
1508
- const initialScroll = state.props.initialScroll;
1509
1829
  if (!queuedInitialLayout && initialScroll) {
1510
1830
  const updatedOffset = calculateOffsetWithOffsetPosition(
1511
1831
  state,
@@ -1517,16 +1837,15 @@ function calculateItemsInView(ctx, state, params = {}) {
1517
1837
  const scrollAdjustPad = -previousScrollAdjust - topPad;
1518
1838
  let scroll = scrollState + scrollExtra + scrollAdjustPad;
1519
1839
  if (scroll + scrollLength > totalSize) {
1520
- scroll = totalSize - scrollLength;
1840
+ scroll = Math.max(0, totalSize - scrollLength);
1521
1841
  }
1522
1842
  if (ENABLE_DEBUG_VIEW) {
1523
1843
  set$(ctx, "debugRawScroll", scrollState);
1524
1844
  set$(ctx, "debugComputedScroll", scroll);
1525
1845
  }
1526
- const scrollBuffer = state.props.scrollBuffer;
1527
1846
  let scrollBufferTop = scrollBuffer;
1528
1847
  let scrollBufferBottom = scrollBuffer;
1529
- if (speed > 0) {
1848
+ if (speed > 0 || speed === 0 && scroll < Math.max(50, scrollBuffer)) {
1530
1849
  scrollBufferTop = scrollBuffer * 0.5;
1531
1850
  scrollBufferBottom = scrollBuffer * 1.5;
1532
1851
  } else {
@@ -1534,7 +1853,7 @@ function calculateItemsInView(ctx, state, params = {}) {
1534
1853
  scrollBufferBottom = scrollBuffer * 0.5;
1535
1854
  }
1536
1855
  const scrollTopBuffered = scroll - scrollBufferTop;
1537
- const scrollBottom = scroll + scrollLength;
1856
+ const scrollBottom = scroll + scrollLength + (scroll < 0 ? -scroll : 0);
1538
1857
  const scrollBottomBuffered = scrollBottom + scrollBufferBottom;
1539
1858
  if (scrollForNextCalculateItemsInView) {
1540
1859
  const { top, bottom } = scrollForNextCalculateItemsInView;
@@ -1548,10 +1867,6 @@ function calculateItemsInView(ctx, state, params = {}) {
1548
1867
  let endNoBuffer = null;
1549
1868
  let endBuffered = null;
1550
1869
  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
1870
  for (let i = loopStart; i >= 0; i--) {
1556
1871
  const id = (_a = idCache.get(i)) != null ? _a : getId(state, i);
1557
1872
  const top = positions.get(id);
@@ -1634,7 +1949,7 @@ function calculateItemsInView(ctx, state, params = {}) {
1634
1949
  if (dataChanged) {
1635
1950
  for (let i = 0; i < numContainers; i++) {
1636
1951
  const itemKey = peek$(ctx, `containerItemKey${i}`);
1637
- if (!state.props.keyExtractor || itemKey && indexByKey.get(itemKey) === void 0) {
1952
+ if (!keyExtractor || itemKey && indexByKey.get(itemKey) === void 0) {
1638
1953
  pendingRemoval.push(i);
1639
1954
  }
1640
1955
  }
@@ -1648,14 +1963,32 @@ function calculateItemsInView(ctx, state, params = {}) {
1648
1963
  needNewContainers.push(i);
1649
1964
  }
1650
1965
  }
1966
+ if (stickyIndicesArr.length > 0) {
1967
+ handleStickyActivation(
1968
+ ctx,
1969
+ state,
1970
+ stickyIndicesSet,
1971
+ stickyIndicesArr,
1972
+ scroll,
1973
+ needNewContainers,
1974
+ startBuffered,
1975
+ endBuffered
1976
+ );
1977
+ }
1651
1978
  if (needNewContainers.length > 0) {
1979
+ const requiredItemTypes = getItemType ? needNewContainers.map((i) => {
1980
+ const itemType = getItemType(data[i], i);
1981
+ return itemType ? String(itemType) : "";
1982
+ }) : void 0;
1652
1983
  const availableContainers = findAvailableContainers(
1653
1984
  ctx,
1654
1985
  state,
1655
1986
  needNewContainers.length,
1656
1987
  startBuffered,
1657
1988
  endBuffered,
1658
- pendingRemoval
1989
+ pendingRemoval,
1990
+ requiredItemTypes,
1991
+ needNewContainers
1659
1992
  );
1660
1993
  for (let idx = 0; idx < needNewContainers.length; idx++) {
1661
1994
  const i = needNewContainers[idx];
@@ -1667,7 +2000,19 @@ function calculateItemsInView(ctx, state, params = {}) {
1667
2000
  }
1668
2001
  set$(ctx, `containerItemKey${containerIndex}`, id);
1669
2002
  set$(ctx, `containerItemData${containerIndex}`, data[i]);
2003
+ if (requiredItemTypes) {
2004
+ state.containerItemTypes.set(containerIndex, requiredItemTypes[idx]);
2005
+ }
1670
2006
  containerItemKeys.add(id);
2007
+ if (stickyIndicesSet.has(i)) {
2008
+ set$(ctx, `containerSticky${containerIndex}`, true);
2009
+ const topPadding = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
2010
+ set$(ctx, `containerStickyOffset${containerIndex}`, new Animated.Value(topPadding));
2011
+ state.stickyContainerPool.add(containerIndex);
2012
+ } else {
2013
+ set$(ctx, `containerSticky${containerIndex}`, false);
2014
+ state.stickyContainerPool.delete(containerIndex);
2015
+ }
1671
2016
  if (containerIndex >= numContainers2) {
1672
2017
  numContainers2 = containerIndex + 1;
1673
2018
  }
@@ -1680,12 +2025,21 @@ function calculateItemsInView(ctx, state, params = {}) {
1680
2025
  }
1681
2026
  }
1682
2027
  }
2028
+ if (stickyIndicesArr.length > 0) {
2029
+ handleStickyRecycling(ctx, state, stickyIndicesArr, scroll, scrollBuffer, pendingRemoval);
2030
+ }
1683
2031
  for (let i = 0; i < numContainers; i++) {
1684
2032
  const itemKey = peek$(ctx, `containerItemKey${i}`);
1685
2033
  if (pendingRemoval.includes(i)) {
1686
2034
  if (itemKey) {
1687
2035
  containerItemKeys.delete(itemKey);
1688
2036
  }
2037
+ state.containerItemTypes.delete(i);
2038
+ if (state.stickyContainerPool.has(i)) {
2039
+ set$(ctx, `containerSticky${i}`, false);
2040
+ set$(ctx, `containerStickyOffset${i}`, void 0);
2041
+ state.stickyContainerPool.delete(i);
2042
+ }
1689
2043
  set$(ctx, `containerItemKey${i}`, void 0);
1690
2044
  set$(ctx, `containerItemData${i}`, void 0);
1691
2045
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
@@ -1709,7 +2063,7 @@ function calculateItemsInView(ctx, state, params = {}) {
1709
2063
  if (column >= 0 && column !== prevColumn) {
1710
2064
  set$(ctx, `containerColumn${i}`, column);
1711
2065
  }
1712
- if (prevData !== item) {
2066
+ if (prevData !== item && (itemsAreEqual ? !itemsAreEqual(prevData, item, itemIndex, data) : true)) {
1713
2067
  set$(ctx, `containerItemData${i}`, data[itemIndex]);
1714
2068
  }
1715
2069
  }
@@ -1721,42 +2075,55 @@ function calculateItemsInView(ctx, state, params = {}) {
1721
2075
  setDidLayout(ctx, state);
1722
2076
  }
1723
2077
  }
1724
- if (state.props.viewabilityConfigCallbackPairs) {
1725
- updateViewableItems(
1726
- state,
1727
- ctx,
1728
- state.props.viewabilityConfigCallbackPairs,
1729
- scrollLength,
1730
- startNoBuffer,
1731
- endNoBuffer
1732
- );
2078
+ if (viewabilityConfigCallbackPairs) {
2079
+ updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollLength, startNoBuffer, endNoBuffer);
1733
2080
  }
1734
2081
  });
1735
2082
  }
1736
2083
 
1737
2084
  // src/core/doInitialAllocateContainers.ts
1738
2085
  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
- );
2086
+ var _a;
2087
+ const {
2088
+ scrollLength,
2089
+ props: {
2090
+ data,
2091
+ getEstimatedItemSize,
2092
+ getFixedItemSize,
2093
+ getItemType,
2094
+ scrollBuffer,
2095
+ numColumns,
2096
+ estimatedItemSize
2097
+ }
2098
+ } = state;
2099
+ const hasContainers = peek$(ctx, "numContainers");
2100
+ if (scrollLength > 0 && data.length > 0 && !hasContainers) {
2101
+ let averageItemSize;
2102
+ const fn = getFixedItemSize || getEstimatedItemSize;
2103
+ if (fn) {
2104
+ let totalSize = 0;
2105
+ const num = Math.min(20, data.length);
2106
+ for (let i = 0; i < num; i++) {
2107
+ totalSize += fn(0, data[0], getItemType ? (_a = getItemType(data[0], 0)) != null ? _a : "" : "");
2108
+ }
2109
+ averageItemSize = totalSize / num;
2110
+ } else {
2111
+ averageItemSize = estimatedItemSize;
2112
+ }
2113
+ const numContainers = Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize * numColumns);
1747
2114
  for (let i = 0; i < numContainers; i++) {
1748
2115
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
1749
2116
  set$(ctx, `containerColumn${i}`, -1);
1750
2117
  }
1751
2118
  set$(ctx, "numContainers", numContainers);
1752
2119
  set$(ctx, "numContainersPooled", numContainers * state.props.initialContainerPoolRatio);
1753
- if (!IsNewArchitecture) {
2120
+ if (!IsNewArchitecture || state.lastLayout) {
1754
2121
  if (state.props.initialScroll) {
1755
2122
  requestAnimationFrame(() => {
1756
- calculateItemsInView(ctx, state);
2123
+ calculateItemsInView(ctx, state, { dataChanged: true });
1757
2124
  });
1758
2125
  } else {
1759
- calculateItemsInView(ctx, state);
2126
+ calculateItemsInView(ctx, state, { dataChanged: true });
1760
2127
  }
1761
2128
  }
1762
2129
  return true;
@@ -1776,16 +2143,18 @@ function doMaintainScrollAtEnd(ctx, state, animated) {
1776
2143
  }
1777
2144
  requestAnimationFrame(() => {
1778
2145
  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
- );
2146
+ if (state == null ? void 0 : state.isAtEnd) {
2147
+ state.maintainingScrollAtEnd = true;
2148
+ (_a = refScroller.current) == null ? void 0 : _a.scrollToEnd({
2149
+ animated
2150
+ });
2151
+ setTimeout(
2152
+ () => {
2153
+ state.maintainingScrollAtEnd = false;
2154
+ },
2155
+ 0
2156
+ );
2157
+ }
1789
2158
  });
1790
2159
  return true;
1791
2160
  }
@@ -1826,40 +2195,49 @@ function handleLayout(ctx, state, layout, setCanRender) {
1826
2195
  const otherAxisSize = layout[state.props.horizontal ? "height" : "width"];
1827
2196
  const needsCalculate = !state.lastLayout || scrollLength > state.scrollLength || state.lastLayout.x !== layout.x || state.lastLayout.y !== layout.y;
1828
2197
  state.lastLayout = layout;
1829
- const didChange = scrollLength !== state.scrollLength;
1830
2198
  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
- );
2199
+ const didChange = scrollLength !== state.scrollLength || otherAxisSize !== prevOtherAxisSize;
2200
+ if (didChange) {
2201
+ state.scrollLength = scrollLength;
2202
+ state.otherAxisSize = otherAxisSize;
2203
+ state.lastBatchingAction = Date.now();
2204
+ state.scrollForNextCalculateItemsInView = void 0;
2205
+ doInitialAllocateContainers(ctx, state);
2206
+ if (needsCalculate) {
2207
+ calculateItemsInView(ctx, state, { doMVCP: true });
2208
+ }
2209
+ if (didChange || otherAxisSize !== prevOtherAxisSize) {
2210
+ set$(ctx, "scrollSize", { height: layout.height, width: layout.width });
2211
+ }
2212
+ if (maintainScrollAtEnd === true || maintainScrollAtEnd.onLayout) {
2213
+ doMaintainScrollAtEnd(ctx, state, false);
2214
+ }
2215
+ updateAlignItemsPaddingTop(ctx, state);
2216
+ checkAtBottom(ctx, state);
2217
+ checkAtTop(state);
2218
+ if (state) {
2219
+ state.needsOtherAxisSize = otherAxisSize - (state.props.stylePaddingTop || 0) < 10;
2220
+ }
2221
+ if (__DEV__ && scrollLength === 0) {
2222
+ warnDevOnce(
2223
+ "height0",
2224
+ `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.`
2225
+ );
2226
+ }
2227
+ setCanRender(true);
1856
2228
  }
1857
- setCanRender(true);
1858
2229
  }
1859
2230
 
1860
2231
  // src/core/onScroll.ts
1861
2232
  function onScroll(ctx, state, event) {
1862
- var _a, _b, _c, _d, _e;
2233
+ var _a, _b, _c;
2234
+ const {
2235
+ scrollProcessingEnabled,
2236
+ props: { onScroll: onScrollProp }
2237
+ } = state;
2238
+ if (scrollProcessingEnabled === false) {
2239
+ return;
2240
+ }
1863
2241
  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
2242
  return;
1865
2243
  }
@@ -1873,15 +2251,16 @@ function onScroll(ctx, state, event) {
1873
2251
  }
1874
2252
  state.scrollPending = newScroll;
1875
2253
  updateScroll(ctx, state, newScroll);
1876
- (_e = (_d = state.props).onScroll) == null ? void 0 : _e.call(_d, event);
2254
+ onScrollProp == null ? void 0 : onScrollProp(event);
1877
2255
  }
1878
2256
  function updateScroll(ctx, state, newScroll) {
1879
2257
  const scrollingTo = state.scrollingTo;
1880
2258
  state.hasScrolled = true;
1881
2259
  state.lastBatchingAction = Date.now();
1882
- const currentTime = performance.now();
2260
+ const currentTime = Date.now();
1883
2261
  if (scrollingTo === void 0 && !(state.scrollHistory.length === 0 && newScroll === state.scroll)) {
1884
- state.scrollHistory.push({ scroll: newScroll, time: currentTime });
2262
+ const adjust = state.scrollAdjustHandler.getAdjust();
2263
+ state.scrollHistory.push({ scroll: newScroll - adjust, time: currentTime });
1885
2264
  }
1886
2265
  if (state.scrollHistory.length > 5) {
1887
2266
  state.scrollHistory.shift();
@@ -1890,9 +2269,11 @@ function updateScroll(ctx, state, newScroll) {
1890
2269
  state.scrollPrevTime = state.scrollTime;
1891
2270
  state.scroll = newScroll;
1892
2271
  state.scrollTime = currentTime;
1893
- calculateItemsInView(ctx, state);
1894
- checkAtBottom(ctx, state);
1895
- checkAtTop(state);
2272
+ if (Math.abs(state.scroll - state.scrollPrev) > 2) {
2273
+ calculateItemsInView(ctx, state);
2274
+ checkAtBottom(ctx, state);
2275
+ checkAtTop(state);
2276
+ }
1896
2277
  }
1897
2278
 
1898
2279
  // src/core/ScrollAdjustHandler.ts
@@ -1915,13 +2296,19 @@ var ScrollAdjustHandler = class {
1915
2296
  setMounted() {
1916
2297
  this.mounted = true;
1917
2298
  }
2299
+ getAdjust() {
2300
+ return this.appliedAdjust;
2301
+ }
1918
2302
  };
1919
2303
 
1920
2304
  // src/core/updateItemSize.ts
1921
- function updateItemSizes(ctx, state, itemUpdates) {
1922
- var _a;
2305
+ function updateItemSize(ctx, state, itemKey, sizeObj) {
2306
+ var _a, _b;
1923
2307
  const {
2308
+ sizesKnown,
1924
2309
  props: {
2310
+ getFixedItemSize,
2311
+ getItemType,
1925
2312
  horizontal,
1926
2313
  maintainVisibleContentPosition,
1927
2314
  suggestEstimatedItemSize,
@@ -1931,47 +2318,60 @@ function updateItemSizes(ctx, state, itemUpdates) {
1931
2318
  }
1932
2319
  } = state;
1933
2320
  if (!data) return;
2321
+ if (getFixedItemSize) {
2322
+ const index2 = state.indexByKey.get(itemKey);
2323
+ if (index2 === void 0) {
2324
+ return;
2325
+ }
2326
+ const itemData = state.props.data[index2];
2327
+ if (itemData === void 0) {
2328
+ return;
2329
+ }
2330
+ const type = getItemType ? (_a = getItemType(itemData, index2)) != null ? _a : "" : "";
2331
+ const size2 = getFixedItemSize(index2, itemData, type);
2332
+ if (size2 !== void 0 && size2 === sizesKnown.get(itemKey)) {
2333
+ return;
2334
+ }
2335
+ }
1934
2336
  const containersDidLayout = peek$(ctx, "containersDidLayout");
1935
2337
  let needsRecalculate = !containersDidLayout;
1936
2338
  let shouldMaintainScrollAtEnd = false;
1937
2339
  let minIndexSizeChanged;
1938
2340
  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
- }
2341
+ const index = state.indexByKey.get(itemKey);
2342
+ const prevSizeKnown = state.sizesKnown.get(itemKey);
2343
+ const diff = updateOneItemSize(state, itemKey, sizeObj);
2344
+ const size = Math.floor((horizontal ? sizeObj.width : sizeObj.height) * 8) / 8;
2345
+ if (diff !== 0) {
2346
+ minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
2347
+ if (((_b = state.scrollingTo) == null ? void 0 : _b.viewPosition) && maintainVisibleContentPosition && index === state.scrollingTo.index && diff > 0) {
2348
+ requestAdjust(ctx, state, diff * state.scrollingTo.viewPosition);
2349
+ }
2350
+ const { startBuffered, endBuffered } = state;
2351
+ needsRecalculate || (needsRecalculate = index >= startBuffered && index <= endBuffered);
2352
+ if (!needsRecalculate) {
2353
+ const numContainers = ctx.values.get("numContainers");
2354
+ for (let i = 0; i < numContainers; i++) {
2355
+ if (peek$(ctx, `containerItemKey${i}`) === itemKey) {
2356
+ needsRecalculate = true;
2357
+ break;
1958
2358
  }
1959
2359
  }
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
2360
  }
2361
+ if (state.needsOtherAxisSize) {
2362
+ const otherAxisSize = horizontal ? sizeObj.height : sizeObj.width;
2363
+ maxOtherAxisSize = Math.max(maxOtherAxisSize, otherAxisSize);
2364
+ }
2365
+ if (prevSizeKnown !== void 0 && Math.abs(prevSizeKnown - size) > 5) {
2366
+ shouldMaintainScrollAtEnd = true;
2367
+ }
2368
+ onItemSizeChanged == null ? void 0 : onItemSizeChanged({
2369
+ index,
2370
+ itemData: state.props.data[index],
2371
+ itemKey,
2372
+ previous: size - diff,
2373
+ size
2374
+ });
1975
2375
  }
1976
2376
  if (minIndexSizeChanged !== void 0) {
1977
2377
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
@@ -2004,43 +2404,29 @@ function updateItemSizes(ctx, state, itemUpdates) {
2004
2404
  }
2005
2405
  }
2006
2406
  }
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
2407
  function updateOneItemSize(state, itemKey, sizeObj) {
2408
+ var _a;
2025
2409
  const {
2026
2410
  sizes,
2027
2411
  indexByKey,
2028
2412
  sizesKnown,
2029
2413
  averageSizes,
2030
- props: { data, horizontal }
2414
+ props: { data, horizontal, getEstimatedItemSize, getItemType, getFixedItemSize }
2031
2415
  } = state;
2032
2416
  if (!data) return 0;
2033
2417
  const index = indexByKey.get(itemKey);
2034
2418
  const prevSize = getItemSize(state, itemKey, index, data);
2035
2419
  const size = Math.floor((horizontal ? sizeObj.width : sizeObj.height) * 8) / 8;
2036
2420
  sizesKnown.set(itemKey, size);
2037
- const itemType = "";
2038
- let averages = averageSizes[itemType];
2039
- if (!averages) {
2040
- averages = averageSizes[itemType] = { avg: 0, num: 0 };
2421
+ if (!getEstimatedItemSize && !getFixedItemSize && size > 0) {
2422
+ const itemType = getItemType ? (_a = getItemType(data[index], index)) != null ? _a : "" : "";
2423
+ let averages = averageSizes[itemType];
2424
+ if (!averages) {
2425
+ averages = averageSizes[itemType] = { avg: 0, num: 0 };
2426
+ }
2427
+ averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
2428
+ averages.num++;
2041
2429
  }
2042
- averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
2043
- averages.num++;
2044
2430
  if (!prevSize || Math.abs(prevSize - size) > 0.1) {
2045
2431
  sizes.set(itemKey, size);
2046
2432
  return size - prevSize;
@@ -2078,86 +2464,165 @@ function createColumnWrapperStyle(contentContainerStyle) {
2078
2464
  }
2079
2465
  }
2080
2466
  function getRenderedItem(ctx, state, key) {
2467
+ var _a;
2081
2468
  if (!state) {
2082
2469
  return null;
2083
2470
  }
2084
2471
  const {
2085
2472
  indexByKey,
2086
- props: { data, renderItem: renderItem2 }
2473
+ props: { data, getItemType, renderItem }
2087
2474
  } = state;
2088
2475
  const index = indexByKey.get(key);
2089
2476
  if (index === void 0) {
2090
2477
  return null;
2091
2478
  }
2092
2479
  let renderedItem = null;
2093
- if (renderItem2) {
2480
+ if (renderItem && data[index]) {
2094
2481
  const itemProps = {
2482
+ data,
2095
2483
  extraData: peek$(ctx, "extraData"),
2096
2484
  index,
2097
- item: data[index]
2485
+ item: data[index],
2486
+ type: getItemType ? (_a = getItemType(data[index], index)) != null ? _a : "" : ""
2098
2487
  };
2099
- renderedItem = React3__default.createElement(renderItem2, itemProps);
2488
+ renderedItem = isFunction(renderItem) ? renderItem(itemProps) : React3__default.createElement(renderItem, itemProps);
2100
2489
  }
2101
2490
  return { index, item: data[index], renderedItem };
2102
2491
  }
2103
2492
 
2493
+ // src/utils/throttledOnScroll.ts
2494
+ function useThrottledOnScroll(originalHandler, scrollEventThrottle) {
2495
+ const throttle = useThrottleDebounce("throttle");
2496
+ return (event) => throttle(originalHandler, scrollEventThrottle, { nativeEvent: event.nativeEvent });
2497
+ }
2498
+
2499
+ // src/utils/updateAveragesOnDataChange.ts
2500
+ function updateAveragesOnDataChange(state, oldData, newData) {
2501
+ var _a;
2502
+ const {
2503
+ averageSizes,
2504
+ sizesKnown,
2505
+ indexByKey,
2506
+ props: { itemsAreEqual, getItemType, keyExtractor }
2507
+ } = state;
2508
+ if (!itemsAreEqual || !oldData.length || !newData.length) {
2509
+ for (const key in averageSizes) {
2510
+ delete averageSizes[key];
2511
+ }
2512
+ return;
2513
+ }
2514
+ const itemTypesToPreserve = {};
2515
+ const newDataLength = newData.length;
2516
+ const oldDataLength = oldData.length;
2517
+ for (let newIndex = 0; newIndex < newDataLength; newIndex++) {
2518
+ const newItem = newData[newIndex];
2519
+ const id = keyExtractor ? keyExtractor(newItem, newIndex) : String(newIndex);
2520
+ const oldIndex = indexByKey.get(id);
2521
+ if (oldIndex !== void 0 && oldIndex < oldDataLength) {
2522
+ const knownSize = sizesKnown.get(id);
2523
+ if (knownSize === void 0) continue;
2524
+ const oldItem = oldData[oldIndex];
2525
+ const areEqual = itemsAreEqual(oldItem, newItem, newIndex, newData);
2526
+ if (areEqual) {
2527
+ const itemType = getItemType ? (_a = getItemType(newItem, newIndex)) != null ? _a : "" : "";
2528
+ let typeData = itemTypesToPreserve[itemType];
2529
+ if (!typeData) {
2530
+ typeData = itemTypesToPreserve[itemType] = { count: 0, totalSize: 0 };
2531
+ }
2532
+ typeData.totalSize += knownSize;
2533
+ typeData.count++;
2534
+ }
2535
+ }
2536
+ }
2537
+ for (const key in averageSizes) {
2538
+ delete averageSizes[key];
2539
+ }
2540
+ for (const itemType in itemTypesToPreserve) {
2541
+ const { totalSize, count } = itemTypesToPreserve[itemType];
2542
+ if (count > 0) {
2543
+ averageSizes[itemType] = {
2544
+ avg: totalSize / count,
2545
+ num: count
2546
+ };
2547
+ }
2548
+ }
2549
+ }
2550
+
2104
2551
  // src/components/LegendList.tsx
2105
2552
  var DEFAULT_DRAW_DISTANCE = 250;
2106
2553
  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
- });
2554
+ var LegendList = typedMemo(
2555
+ typedForwardRef(function LegendList2(props, forwardedRef) {
2556
+ const { children, data: dataProp, renderItem: renderItemProp, ...restProps } = props;
2557
+ const isChildrenMode = children !== void 0 && dataProp === void 0;
2558
+ const processedProps = isChildrenMode ? {
2559
+ ...restProps,
2560
+ data: (isArray(children) ? children : React3.Children.toArray(children)).flat(1),
2561
+ renderItem: ({ item }) => item
2562
+ } : {
2563
+ ...restProps,
2564
+ data: dataProp || [],
2565
+ renderItem: renderItemProp
2566
+ };
2567
+ return /* @__PURE__ */ React3.createElement(StateProvider, null, /* @__PURE__ */ React3.createElement(LegendListInner, { ...processedProps, ref: forwardedRef }));
2568
+ })
2569
+ );
2110
2570
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
2111
2571
  var _a;
2112
2572
  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
2573
  alignItemsAtEnd = false,
2124
- maintainVisibleContentPosition = false,
2125
- onScroll: onScrollProp,
2126
- onMomentumScrollEnd,
2127
- numColumns: numColumnsProp = 1,
2128
2574
  columnWrapperStyle,
2129
- keyExtractor: keyExtractorProp,
2130
- renderItem: renderItem2,
2131
- estimatedListSize,
2575
+ contentContainerStyle: contentContainerStyleProp,
2576
+ data: dataProp = [],
2577
+ drawDistance = 250,
2578
+ enableAverages = true,
2132
2579
  estimatedItemSize: estimatedItemSizeProp,
2580
+ estimatedListSize,
2581
+ extraData,
2133
2582
  getEstimatedItemSize,
2134
- suggestEstimatedItemSize,
2135
- ListHeaderComponent,
2583
+ getFixedItemSize,
2584
+ getItemType,
2585
+ horizontal,
2586
+ initialContainerPoolRatio = 2,
2587
+ initialScrollIndex: initialScrollIndexProp,
2588
+ initialScrollOffset: initialScrollOffsetProp,
2589
+ itemsAreEqual,
2590
+ keyExtractor: keyExtractorProp,
2136
2591
  ListEmptyComponent,
2592
+ ListHeaderComponent,
2593
+ maintainScrollAtEnd = false,
2594
+ maintainScrollAtEndThreshold = 0.1,
2595
+ maintainVisibleContentPosition = true,
2596
+ numColumns: numColumnsProp = 1,
2597
+ onEndReached,
2598
+ onEndReachedThreshold = 0.5,
2137
2599
  onItemSizeChanged,
2138
- refScrollView,
2139
- waitForInitialLayout = true,
2140
- extraData,
2141
- contentContainerStyle: contentContainerStyleProp,
2142
- style: styleProp,
2143
2600
  onLayout: onLayoutProp,
2601
+ onLoad,
2602
+ onMomentumScrollEnd,
2144
2603
  onRefresh,
2145
- refreshing,
2604
+ onScroll: onScrollProp,
2605
+ onStartReached,
2606
+ onStartReachedThreshold = 0.5,
2607
+ onViewableItemsChanged,
2146
2608
  progressViewOffset,
2609
+ recycleItems = false,
2147
2610
  refreshControl,
2148
- initialContainerPoolRatio = 2,
2611
+ refreshing,
2612
+ refScrollView,
2613
+ renderItem,
2614
+ scrollEventThrottle,
2615
+ snapToIndices,
2616
+ stickyIndices,
2617
+ style: styleProp,
2618
+ suggestEstimatedItemSize,
2149
2619
  viewabilityConfig,
2150
2620
  viewabilityConfigCallbackPairs,
2151
- snapToIndices,
2152
- onViewableItemsChanged,
2153
- onStartReached,
2154
- onEndReached,
2155
- onLoad,
2621
+ waitForInitialLayout = true,
2156
2622
  ...rest
2157
2623
  } = props;
2158
2624
  const [renderNum, setRenderNum] = useState(0);
2159
- const initialScroll = typeof initialScrollIndexProp === "number" ? { index: initialScrollIndexProp } : initialScrollIndexProp;
2160
- const initialScrollIndex = initialScroll == null ? void 0 : initialScroll.index;
2625
+ const initialScroll = initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? { index: initialScrollIndexProp.index || 0, viewOffset: initialScrollIndexProp.viewOffset || 0 } : { index: initialScrollIndexProp || 0, viewOffset: initialScrollOffsetProp || 0 } : void 0;
2161
2626
  const [canRender, setCanRender] = React3.useState(!IsNewArchitecture);
2162
2627
  const contentContainerStyle = { ...StyleSheet.flatten(contentContainerStyleProp) };
2163
2628
  const style = { ...StyleSheet.flatten(styleProp) };
@@ -2174,9 +2639,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2174
2639
  if (!refState.current) {
2175
2640
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : IsNewArchitecture ? { height: 0, width: 0 } : Dimensions.get("window"))[horizontal ? "width" : "height"];
2176
2641
  refState.current = {
2642
+ activeStickyIndex: void 0,
2177
2643
  averageSizes: {},
2178
2644
  columns: /* @__PURE__ */ new Map(),
2179
2645
  containerItemKeys: /* @__PURE__ */ new Set(),
2646
+ containerItemTypes: /* @__PURE__ */ new Map(),
2180
2647
  enableScrollForNextCalculateItemsInView: true,
2181
2648
  endBuffered: -1,
2182
2649
  endNoBuffer: -1,
@@ -2195,11 +2662,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2195
2662
  loadStartTime: Date.now(),
2196
2663
  minIndexSizeChanged: 0,
2197
2664
  nativeMarginTop: 0,
2198
- pendingAdjust: 0,
2199
2665
  positions: /* @__PURE__ */ new Map(),
2200
2666
  props: {},
2201
2667
  queuedCalculateItemsInView: 0,
2202
- queuedItemSizeUpdates: [],
2203
2668
  refScroller: void 0,
2204
2669
  scroll: 0,
2205
2670
  scrollAdjustHandler: new ScrollAdjustHandler(ctx),
@@ -2209,12 +2674,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2209
2674
  scrollPending: 0,
2210
2675
  scrollPrev: 0,
2211
2676
  scrollPrevTime: 0,
2677
+ scrollProcessingEnabled: true,
2212
2678
  scrollTime: 0,
2213
2679
  sizes: /* @__PURE__ */ new Map(),
2214
2680
  sizesKnown: /* @__PURE__ */ new Map(),
2215
2681
  startBuffered: -1,
2216
2682
  startNoBuffer: -1,
2217
2683
  startReachedBlockedByTimer: false,
2684
+ stickyContainerPool: /* @__PURE__ */ new Set(),
2685
+ stickyContainers: /* @__PURE__ */ new Map(),
2218
2686
  timeoutSizeMessage: 0,
2219
2687
  timeouts: /* @__PURE__ */ new Set(),
2220
2688
  totalSize: 0,
@@ -2226,14 +2694,19 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2226
2694
  const state = refState.current;
2227
2695
  const isFirst = !state.props.renderItem;
2228
2696
  const didDataChange = state.props.data !== dataProp;
2697
+ const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
2229
2698
  state.props = {
2230
2699
  alignItemsAtEnd,
2231
2700
  data: dataProp,
2701
+ enableAverages,
2232
2702
  estimatedItemSize,
2233
2703
  getEstimatedItemSize,
2704
+ getFixedItemSize,
2705
+ getItemType,
2234
2706
  horizontal: !!horizontal,
2235
2707
  initialContainerPoolRatio,
2236
2708
  initialScroll,
2709
+ itemsAreEqual,
2237
2710
  keyExtractor,
2238
2711
  maintainScrollAtEnd,
2239
2712
  maintainScrollAtEndThreshold,
@@ -2243,21 +2716,26 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2243
2716
  onEndReachedThreshold,
2244
2717
  onItemSizeChanged,
2245
2718
  onLoad,
2246
- onScroll: onScrollProp,
2719
+ onScroll: throttleScrollFn,
2247
2720
  onStartReached,
2248
2721
  onStartReachedThreshold,
2249
- renderItem: renderItem2,
2722
+ recycleItems: !!recycleItems,
2723
+ renderItem,
2250
2724
  scrollBuffer,
2251
2725
  snapToIndices,
2726
+ stickyIndicesArr: stickyIndices != null ? stickyIndices : [],
2727
+ stickyIndicesSet: useMemo(() => new Set(stickyIndices != null ? stickyIndices : []), [stickyIndices == null ? void 0 : stickyIndices.join(",")]),
2252
2728
  stylePaddingBottom: stylePaddingBottomState,
2253
2729
  stylePaddingTop: stylePaddingTopState,
2254
- suggestEstimatedItemSize: !!suggestEstimatedItemSize,
2255
- viewabilityConfigCallbackPairs: void 0
2730
+ suggestEstimatedItemSize: !!suggestEstimatedItemSize
2256
2731
  };
2257
2732
  state.refScroller = refScroller;
2258
2733
  const checkResetContainers = (isFirst2) => {
2259
2734
  const state2 = refState.current;
2260
2735
  if (state2) {
2736
+ if (!isFirst2 && state2.props.data !== dataProp) {
2737
+ updateAveragesOnDataChange(state2, state2.props.data, dataProp);
2738
+ }
2261
2739
  state2.props.data = dataProp;
2262
2740
  if (!isFirst2) {
2263
2741
  calculateItemsInView(ctx, state2, { dataChanged: true, doMVCP: true });
@@ -2299,12 +2777,19 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2299
2777
  updateAllPositions(ctx, state);
2300
2778
  }
2301
2779
  const initialContentOffset = useMemo(() => {
2302
- const initialContentOffset2 = initialScrollOffset || calculateOffsetForIndex(ctx, state, initialScrollIndex);
2303
- refState.current.isStartReached = initialContentOffset2 < refState.current.scrollLength * onStartReachedThreshold;
2304
- if (initialContentOffset2 > 0) {
2305
- scrollTo(state, { animated: false, index: initialScrollIndex, offset: initialContentOffset2 });
2780
+ if (initialScroll) {
2781
+ const { index, viewOffset } = initialScroll;
2782
+ let initialContentOffset2 = viewOffset || 0;
2783
+ if (index !== void 0) {
2784
+ initialContentOffset2 += calculateOffsetForIndex(ctx, state, index);
2785
+ }
2786
+ refState.current.isStartReached = initialContentOffset2 < refState.current.scrollLength * onStartReachedThreshold;
2787
+ if (initialContentOffset2 > 0) {
2788
+ scrollTo(state, { animated: false, index, offset: initialContentOffset2 });
2789
+ }
2790
+ return initialContentOffset2;
2306
2791
  }
2307
- return initialContentOffset2;
2792
+ return 0;
2308
2793
  }, [renderNum]);
2309
2794
  if (isFirst || didDataChange || numColumnsProp !== peek$(ctx, "numColumns")) {
2310
2795
  refState.current.lastBatchingAction = Date.now();
@@ -2317,27 +2802,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2317
2802
  refState.current.positions.clear();
2318
2803
  }
2319
2804
  }
2320
- useLayoutEffect(() => {
2321
- if (IsNewArchitecture) {
2322
- let measured;
2323
- refScroller.current.measure((x, y, width, height) => {
2324
- measured = { height, width, x, y };
2325
- });
2326
- if (measured) {
2327
- const size = Math.floor(measured[horizontal ? "width" : "height"] * 8) / 8;
2328
- if (size) {
2329
- handleLayout(ctx, state, measured, setCanRender);
2330
- }
2331
- }
2332
- }
2333
- if (!isFirst) {
2334
- calculateItemsInView(ctx, state, { doMVCP: true });
2335
- }
2336
- }, [dataProp]);
2337
2805
  const onLayoutHeader = useCallback((rect, fromLayoutEffect) => {
2338
2806
  const size = rect[horizontal ? "width" : "height"];
2339
2807
  set$(ctx, "headerSize", size);
2340
- if (initialScroll) {
2808
+ if ((initialScroll == null ? void 0 : initialScroll.index) !== void 0) {
2341
2809
  if (IsNewArchitecture && Platform.OS !== "android") {
2342
2810
  if (fromLayoutEffect) {
2343
2811
  setRenderNum((v) => v + 1);
@@ -2355,7 +2823,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2355
2823
  }
2356
2824
  }, [snapToIndices]);
2357
2825
  useLayoutEffect(() => {
2358
- const didAllocateContainers = doInitialAllocateContainersCallback();
2826
+ const didAllocateContainers = dataProp.length > 0 && doInitialAllocateContainersCallback();
2359
2827
  if (!didAllocateContainers) {
2360
2828
  checkResetContainers(
2361
2829
  /*isFirst*/
@@ -2366,6 +2834,20 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2366
2834
  useLayoutEffect(() => {
2367
2835
  set$(ctx, "extraData", extraData);
2368
2836
  }, [extraData]);
2837
+ useLayoutEffect(() => {
2838
+ if (IsNewArchitecture) {
2839
+ let measured;
2840
+ refScroller.current.measure((x, y, width, height) => {
2841
+ measured = { height, width, x, y };
2842
+ });
2843
+ if (measured) {
2844
+ const size = Math.floor(measured[horizontal ? "width" : "height"] * 8) / 8;
2845
+ if (size) {
2846
+ handleLayout(ctx, state, measured, setCanRender);
2847
+ }
2848
+ }
2849
+ }
2850
+ }, []);
2369
2851
  useLayoutEffect(initializeStateVars, [
2370
2852
  memoizedLastItemKeys.join(","),
2371
2853
  numColumnsProp,
@@ -2382,7 +2864,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2382
2864
  viewabilityConfigCallbackPairs
2383
2865
  });
2384
2866
  state.viewabilityConfigCallbackPairs = viewability;
2385
- state.props.viewabilityConfigCallbackPairs = viewability;
2386
2867
  state.enableScrollForNextCalculateItemsInView = !viewability;
2387
2868
  }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
2388
2869
  if (!IsNewArchitecture) {
@@ -2422,10 +2903,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2422
2903
  const state2 = refState.current;
2423
2904
  return state2 ? {
2424
2905
  contentLength: state2.totalSize,
2906
+ data: state2.props.data,
2425
2907
  end: state2.endNoBuffer,
2426
2908
  endBuffered: state2.endBuffered,
2427
2909
  isAtEnd: state2.isAtEnd,
2428
2910
  isAtStart: state2.isAtStart,
2911
+ positionAtIndex: (index) => state2.positions.get(getId(state2, index)),
2429
2912
  positions: state2.positions,
2430
2913
  scroll: state2.scroll,
2431
2914
  scrollLength: state2.scrollLength,
@@ -2452,7 +2935,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2452
2935
  const footerSize = peek$(ctx, "footerSize") || 0;
2453
2936
  scrollToIndex(ctx, state, {
2454
2937
  index,
2455
- viewOffset: -paddingBottom - footerSize,
2938
+ viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
2456
2939
  viewPosition: 1,
2457
2940
  ...options
2458
2941
  });
@@ -2467,6 +2950,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2467
2950
  }
2468
2951
  },
2469
2952
  scrollToOffset: (params) => scrollTo(state, params),
2953
+ setScrollProcessingEnabled: (enabled) => {
2954
+ refState.current.scrollProcessingEnabled = enabled;
2955
+ },
2470
2956
  setVisibleContentAnchorOffset: (value) => {
2471
2957
  const val = typeof value === "function" ? value(peek$(ctx, "scrollAdjustUserOffset") || 0) : value;
2472
2958
  set$(ctx, "scrollAdjustUserOffset", val);
@@ -2488,6 +2974,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2488
2974
  }),
2489
2975
  []
2490
2976
  );
2977
+ const onScrollHandler = useMemo(() => {
2978
+ const onScrollFn = fns.onScroll;
2979
+ if (stickyIndices == null ? void 0 : stickyIndices.length) {
2980
+ const { animatedScrollY } = ctx;
2981
+ return Animated.event([{ nativeEvent: { contentOffset: { [horizontal ? "x" : "y"]: animatedScrollY } } }], {
2982
+ listener: onScrollFn,
2983
+ useNativeDriver: true
2984
+ });
2985
+ }
2986
+ return onScrollFn;
2987
+ }, [stickyIndices == null ? void 0 : stickyIndices.length, horizontal, scrollEventThrottle]);
2491
2988
  return /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(
2492
2989
  ListComponent,
2493
2990
  {
@@ -2504,14 +3001,20 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2504
3001
  onLayout,
2505
3002
  onLayoutHeader,
2506
3003
  onMomentumScrollEnd: (event) => {
2507
- requestAnimationFrame(() => {
2508
- finishScrollTo(refState.current);
2509
- });
3004
+ if (IsNewArchitecture) {
3005
+ requestAnimationFrame(() => {
3006
+ finishScrollTo(refState.current);
3007
+ });
3008
+ } else {
3009
+ setTimeout(() => {
3010
+ finishScrollTo(refState.current);
3011
+ }, 1e3);
3012
+ }
2510
3013
  if (onMomentumScrollEnd) {
2511
3014
  onMomentumScrollEnd(event);
2512
3015
  }
2513
3016
  },
2514
- onScroll: fns.onScroll,
3017
+ onScroll: onScrollHandler,
2515
3018
  recycleItems,
2516
3019
  refreshControl: refreshControl ? stylePaddingTopState > 0 ? React3.cloneElement(refreshControl, {
2517
3020
  progressViewOffset: (refreshControl.props.progressViewOffset || 0) + stylePaddingTopState
@@ -2527,6 +3030,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2527
3030
  scrollAdjustHandler: (_a = refState.current) == null ? void 0 : _a.scrollAdjustHandler,
2528
3031
  scrollEventThrottle: Platform.OS === "web" ? 16 : void 0,
2529
3032
  snapToIndices,
3033
+ stickyIndices,
2530
3034
  style,
2531
3035
  updateItemSize: fns.updateItemSize,
2532
3036
  waitForInitialLayout
@@ -2534,17 +3038,4 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2534
3038
  ), __DEV__ && ENABLE_DEBUG_VIEW && /* @__PURE__ */ React3.createElement(DebugView, { state: refState.current }));
2535
3039
  });
2536
3040
 
2537
- // src/components/LazyLegendList.tsx
2538
- var typedForwardRef2 = forwardRef;
2539
- var renderItem = ({ item }) => item;
2540
- var LazyLegendList = typedForwardRef2(function LazyLegendList2(props, forwardedRef) {
2541
- const { LegendList: LegendListProp, children, ...rest } = props;
2542
- const LegendListComponent = LegendListProp != null ? LegendListProp : LegendList;
2543
- const data = (isArray(children) ? children : React3.Children.toArray(children)).flat(1);
2544
- return (
2545
- // @ts-expect-error TODO: Fix this type
2546
- /* @__PURE__ */ React3.createElement(LegendListComponent, { ...rest, data, ref: forwardedRef, renderItem })
2547
- );
2548
- });
2549
-
2550
- export { LazyLegendList, LegendList, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useViewability, useViewabilityAmount };
3041
+ export { LegendList, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };