@legendapp/list 2.0.0-next.9 → 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.js CHANGED
@@ -24,15 +24,17 @@ function _interopNamespace(e) {
24
24
 
25
25
  var React3__namespace = /*#__PURE__*/_interopNamespace(React3);
26
26
 
27
- // src/components/LazyLegendList.tsx
27
+ // src/components/LegendList.tsx
28
28
  var ContextState = React3__namespace.createContext(null);
29
29
  function StateProvider({ children }) {
30
30
  const [value] = React3__namespace.useState(() => ({
31
+ animatedScrollY: new reactNative.Animated.Value(0),
31
32
  columnWrapperStyle: void 0,
32
33
  listeners: /* @__PURE__ */ new Map(),
33
34
  mapViewabilityAmountCallbacks: /* @__PURE__ */ new Map(),
34
35
  mapViewabilityAmountValues: /* @__PURE__ */ new Map(),
35
36
  mapViewabilityCallbacks: /* @__PURE__ */ new Map(),
37
+ mapViewabilityConfigStates: /* @__PURE__ */ new Map(),
36
38
  mapViewabilityValues: /* @__PURE__ */ new Map(),
37
39
  values: /* @__PURE__ */ new Map([
38
40
  ["alignItemsPaddingTop", 0],
@@ -185,18 +187,127 @@ var LeanViewComponent = React3__namespace.forwardRef((props, ref) => {
185
187
  LeanViewComponent.displayName = "RCTView";
186
188
  var LeanView = reactNative.Platform.OS === "android" || reactNative.Platform.OS === "ios" ? LeanViewComponent : reactNative.View;
187
189
 
188
- // src/components/Separator.tsx
189
- function Separator({ ItemSeparatorComponent, itemKey, leadingItem }) {
190
- const [lastItemKeys] = useArr$(["lastItemKeys"]);
191
- const isALastItem = lastItemKeys.includes(itemKey);
192
- return isALastItem ? null : /* @__PURE__ */ React.createElement(ItemSeparatorComponent, { leadingItem });
193
- }
194
-
195
190
  // src/constants.ts
196
191
  var POSITION_OUT_OF_VIEW = -1e7;
197
192
  var ENABLE_DEVMODE = __DEV__ && false;
198
193
  var ENABLE_DEBUG_VIEW = __DEV__ && false;
199
194
  var IsNewArchitecture = global.nativeFabricUIManager != null;
195
+ var useAnimatedValue = (initialValue) => {
196
+ return React3.useRef(new reactNative.Animated.Value(initialValue)).current;
197
+ };
198
+
199
+ // src/hooks/useValue$.ts
200
+ function useValue$(key, params) {
201
+ var _a;
202
+ const { getValue, delay } = params || {};
203
+ const ctx = useStateContext();
204
+ const animValue = useAnimatedValue((_a = getValue ? getValue(peek$(ctx, key)) : peek$(ctx, key)) != null ? _a : 0);
205
+ React3.useMemo(() => {
206
+ let newValue;
207
+ let prevValue;
208
+ let didQueueTask = false;
209
+ listen$(ctx, key, (v) => {
210
+ newValue = getValue ? getValue(v) : v;
211
+ if (delay !== void 0) {
212
+ const fn = () => {
213
+ didQueueTask = false;
214
+ if (newValue !== void 0) {
215
+ animValue.setValue(newValue);
216
+ }
217
+ };
218
+ const delayValue = typeof delay === "function" ? delay(newValue, prevValue) : delay;
219
+ prevValue = newValue;
220
+ if (!didQueueTask) {
221
+ didQueueTask = true;
222
+ if (delayValue === 0) {
223
+ queueMicrotask(fn);
224
+ } else {
225
+ setTimeout(fn, delayValue);
226
+ }
227
+ }
228
+ } else {
229
+ animValue.setValue(newValue);
230
+ }
231
+ });
232
+ }, []);
233
+ return animValue;
234
+ }
235
+ var typedForwardRef = React3.forwardRef;
236
+ var typedMemo = React3.memo;
237
+
238
+ // src/components/PositionView.tsx
239
+ var PositionViewState = typedMemo(function PositionView({
240
+ id,
241
+ horizontal,
242
+ style,
243
+ refView,
244
+ ...rest
245
+ }) {
246
+ const [position = POSITION_OUT_OF_VIEW] = useArr$([`containerPosition${id}`]);
247
+ return /* @__PURE__ */ React3__namespace.createElement(
248
+ LeanView,
249
+ {
250
+ ref: refView,
251
+ style: [
252
+ style,
253
+ horizontal ? { transform: [{ translateX: position }] } : { transform: [{ translateY: position }] }
254
+ ],
255
+ ...rest
256
+ }
257
+ );
258
+ });
259
+ var PositionViewAnimated = typedMemo(function PositionView2({
260
+ id,
261
+ horizontal,
262
+ style,
263
+ refView,
264
+ ...rest
265
+ }) {
266
+ const position$ = useValue$(`containerPosition${id}`, {
267
+ getValue: (v) => v != null ? v : POSITION_OUT_OF_VIEW
268
+ });
269
+ return /* @__PURE__ */ React3__namespace.createElement(
270
+ reactNative.Animated.View,
271
+ {
272
+ ref: refView,
273
+ style: [
274
+ style,
275
+ horizontal ? { transform: [{ translateX: position$ }] } : { transform: [{ translateY: position$ }] }
276
+ ],
277
+ ...rest
278
+ }
279
+ );
280
+ });
281
+ var PositionViewSticky = typedMemo(function PositionViewSticky2({
282
+ id,
283
+ horizontal,
284
+ style,
285
+ refView,
286
+ animatedScrollY,
287
+ stickyOffset,
288
+ index,
289
+ ...rest
290
+ }) {
291
+ const [position = POSITION_OUT_OF_VIEW, headerSize] = useArr$([`containerPosition${id}`, "headerSize"]);
292
+ const transform = React3__namespace.useMemo(() => {
293
+ if (animatedScrollY && stickyOffset) {
294
+ const stickyPosition = animatedScrollY.interpolate({
295
+ extrapolate: "clamp",
296
+ inputRange: [position + headerSize, position + 5e3 + headerSize],
297
+ outputRange: [position, position + 5e3]
298
+ });
299
+ return horizontal ? [{ translateX: stickyPosition }] : [{ translateY: stickyPosition }];
300
+ }
301
+ }, [animatedScrollY, headerSize, horizontal, stickyOffset, position]);
302
+ const viewStyle = React3__namespace.useMemo(() => [style, { zIndex: index + 1e3 }, { transform }], [style, transform]);
303
+ return /* @__PURE__ */ React3__namespace.createElement(reactNative.Animated.View, { ref: refView, style: viewStyle, ...rest });
304
+ });
305
+ var PositionView3 = IsNewArchitecture ? PositionViewState : PositionViewAnimated;
306
+ function Separator({ ItemSeparatorComponent, itemKey, leadingItem }) {
307
+ const [lastItemKeys] = useArr$(["lastItemKeys"]);
308
+ const isALastItem = lastItemKeys.includes(itemKey);
309
+ return isALastItem ? null : /* @__PURE__ */ React3__namespace.createElement(ItemSeparatorComponent, { leadingItem });
310
+ }
200
311
  var symbolFirst = Symbol();
201
312
  function useInit(cb) {
202
313
  const refValue = React3.useRef(symbolFirst);
@@ -333,8 +444,16 @@ function useListScrollSize() {
333
444
  const [scrollSize] = useArr$(["scrollSize"]);
334
445
  return scrollSize;
335
446
  }
336
- var typedForwardRef = React3.forwardRef;
337
- var typedMemo = React3.memo;
447
+ var noop = () => {
448
+ };
449
+ function useSyncLayout() {
450
+ if (IsNewArchitecture) {
451
+ const { triggerLayout: syncLayout } = React3.useContext(ContextContainer);
452
+ return syncLayout;
453
+ } else {
454
+ return noop;
455
+ }
456
+ }
338
457
 
339
458
  // src/components/Container.tsx
340
459
  var Container = typedMemo(function Container2({
@@ -346,14 +465,15 @@ var Container = typedMemo(function Container2({
346
465
  ItemSeparatorComponent
347
466
  }) {
348
467
  const ctx = useStateContext();
349
- const columnWrapperStyle = ctx.columnWrapperStyle;
350
- const [column = 0, data, itemKey, position = POSITION_OUT_OF_VIEW, numColumns, extraData] = useArr$([
468
+ const { columnWrapperStyle, animatedScrollY } = ctx;
469
+ const [column = 0, data, itemKey, numColumns, extraData, isSticky, stickyOffset] = useArr$([
351
470
  `containerColumn${id}`,
352
471
  `containerItemData${id}`,
353
472
  `containerItemKey${id}`,
354
- `containerPosition${id}`,
355
473
  "numColumns",
356
- "extraData"
474
+ "extraData",
475
+ `containerSticky${id}`,
476
+ `containerStickyOffset${id}`
357
477
  ]);
358
478
  const refLastSize = React3.useRef();
359
479
  const ref = React3.useRef(null);
@@ -361,36 +481,38 @@ var Container = typedMemo(function Container2({
361
481
  const otherAxisPos = numColumns > 1 ? `${(column - 1) / numColumns * 100}%` : 0;
362
482
  const otherAxisSize = numColumns > 1 ? `${1 / numColumns * 100}%` : void 0;
363
483
  let didLayout = false;
364
- let paddingStyles;
365
- if (columnWrapperStyle) {
366
- const { columnGap, rowGap, gap } = columnWrapperStyle;
367
- if (horizontal) {
368
- paddingStyles = {
369
- paddingRight: columnGap || gap || void 0,
370
- paddingVertical: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
371
- };
372
- } else {
373
- paddingStyles = {
374
- paddingBottom: rowGap || gap || void 0,
375
- paddingHorizontal: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
376
- };
484
+ const style = React3.useMemo(() => {
485
+ let paddingStyles;
486
+ if (columnWrapperStyle) {
487
+ const { columnGap, rowGap, gap } = columnWrapperStyle;
488
+ if (horizontal) {
489
+ paddingStyles = {
490
+ paddingRight: columnGap || gap || void 0,
491
+ paddingVertical: numColumns > 1 ? (rowGap || gap || 0) / 2 : void 0
492
+ };
493
+ } else {
494
+ paddingStyles = {
495
+ paddingBottom: rowGap || gap || void 0,
496
+ paddingHorizontal: numColumns > 1 ? (columnGap || gap || 0) / 2 : void 0
497
+ };
498
+ }
377
499
  }
378
- }
379
- const style = horizontal ? {
380
- flexDirection: ItemSeparatorComponent ? "row" : void 0,
381
- height: otherAxisSize,
382
- left: position,
383
- position: "absolute",
384
- top: otherAxisPos,
385
- ...paddingStyles || {}
386
- } : {
387
- left: otherAxisPos,
388
- position: "absolute",
389
- right: numColumns > 1 ? null : 0,
390
- top: position,
391
- width: otherAxisSize,
392
- ...paddingStyles || {}
393
- };
500
+ return horizontal ? {
501
+ flexDirection: ItemSeparatorComponent ? "row" : void 0,
502
+ height: otherAxisSize,
503
+ left: 0,
504
+ position: "absolute",
505
+ top: otherAxisPos,
506
+ ...paddingStyles || {}
507
+ } : {
508
+ left: otherAxisPos,
509
+ position: "absolute",
510
+ right: numColumns > 1 ? null : 0,
511
+ top: 0,
512
+ width: otherAxisSize,
513
+ ...paddingStyles || {}
514
+ };
515
+ }, [horizontal, otherAxisPos, otherAxisSize, columnWrapperStyle, numColumns]);
394
516
  const renderedItemInfo = React3.useMemo(
395
517
  () => itemKey !== void 0 ? getRenderedItem2(itemKey) : null,
396
518
  [itemKey, data, extraData]
@@ -455,55 +577,30 @@ var Container = typedMemo(function Container2({
455
577
  }
456
578
  }, [itemKey]);
457
579
  }
458
- return /* @__PURE__ */ React3__namespace.createElement(LeanView, { key: recycleItems ? void 0 : itemKey, onLayout, ref, style }, /* @__PURE__ */ React3__namespace.createElement(ContextContainer.Provider, { value: contextValue }, renderedItem, renderedItemInfo && ItemSeparatorComponent && /* @__PURE__ */ React3__namespace.createElement(
459
- Separator,
580
+ const PositionComponent = isSticky ? PositionViewSticky : PositionView3;
581
+ return /* @__PURE__ */ React3__namespace.createElement(
582
+ PositionComponent,
460
583
  {
461
- ItemSeparatorComponent,
462
- itemKey,
463
- leadingItem: renderedItemInfo.item
464
- }
465
- )));
466
- });
467
- var useAnimatedValue = (initialValue) => {
468
- return React3.useRef(new reactNative.Animated.Value(initialValue)).current;
469
- };
470
-
471
- // src/hooks/useValue$.ts
472
- function useValue$(key, params) {
473
- var _a;
474
- const { getValue, delay } = params || {};
475
- const ctx = useStateContext();
476
- const animValue = useAnimatedValue((_a = getValue ? getValue(peek$(ctx, key)) : peek$(ctx, key)) != null ? _a : 0);
477
- React3.useMemo(() => {
478
- let newValue;
479
- let prevValue;
480
- let didQueueTask = false;
481
- listen$(ctx, key, (v) => {
482
- newValue = getValue ? getValue(v) : v;
483
- if (delay !== void 0) {
484
- const fn = () => {
485
- didQueueTask = false;
486
- if (newValue !== void 0) {
487
- animValue.setValue(newValue);
488
- }
489
- };
490
- const delayValue = typeof delay === "function" ? delay(newValue, prevValue) : delay;
491
- prevValue = newValue;
492
- if (!didQueueTask) {
493
- didQueueTask = true;
494
- if (delayValue === 0) {
495
- queueMicrotask(fn);
496
- } else {
497
- setTimeout(fn, delayValue);
498
- }
499
- }
500
- } else {
501
- animValue.setValue(newValue);
584
+ animatedScrollY: isSticky ? animatedScrollY : void 0,
585
+ horizontal,
586
+ id,
587
+ index,
588
+ key: recycleItems ? void 0 : itemKey,
589
+ onLayout,
590
+ refView: ref,
591
+ stickyOffset: isSticky ? stickyOffset : void 0,
592
+ style
593
+ },
594
+ /* @__PURE__ */ React3__namespace.createElement(ContextContainer.Provider, { value: contextValue }, renderedItem, renderedItemInfo && ItemSeparatorComponent && /* @__PURE__ */ React3__namespace.createElement(
595
+ Separator,
596
+ {
597
+ ItemSeparatorComponent,
598
+ itemKey,
599
+ leadingItem: renderedItemInfo.item
502
600
  }
503
- });
504
- }, []);
505
- return animValue;
506
- }
601
+ ))
602
+ );
603
+ });
507
604
 
508
605
  // src/components/Containers.tsx
509
606
  var Containers = typedMemo(function Containers2({
@@ -586,7 +683,48 @@ function SnapWrapper({ ScrollComponent, ...props }) {
586
683
  const [snapToOffsets] = useArr$(["snapToOffsets"]);
587
684
  return /* @__PURE__ */ React.createElement(ScrollComponent, { ...props, snapToOffsets });
588
685
  }
589
- function useSyncLayout({
686
+ function useThrottleDebounce(mode) {
687
+ const timeoutRef = React3.useRef(null);
688
+ const lastCallTimeRef = React3.useRef(0);
689
+ const lastArgsRef = React3.useRef(null);
690
+ const clearTimeoutRef = () => {
691
+ if (timeoutRef.current) {
692
+ clearTimeout(timeoutRef.current);
693
+ timeoutRef.current = null;
694
+ }
695
+ };
696
+ const execute = React3.useCallback(
697
+ (callback, delay, ...args) => {
698
+ {
699
+ const now = Date.now();
700
+ lastArgsRef.current = args;
701
+ if (now - lastCallTimeRef.current >= delay) {
702
+ lastCallTimeRef.current = now;
703
+ callback(...args);
704
+ clearTimeoutRef();
705
+ } else {
706
+ clearTimeoutRef();
707
+ timeoutRef.current = setTimeout(
708
+ () => {
709
+ if (lastArgsRef.current) {
710
+ lastCallTimeRef.current = Date.now();
711
+ callback(...lastArgsRef.current);
712
+ timeoutRef.current = null;
713
+ lastArgsRef.current = null;
714
+ }
715
+ },
716
+ delay - (now - lastCallTimeRef.current)
717
+ );
718
+ }
719
+ }
720
+ },
721
+ [mode]
722
+ );
723
+ return execute;
724
+ }
725
+
726
+ // src/hooks/useSyncLayout.tsx
727
+ function useSyncLayout2({
590
728
  onChange
591
729
  }) {
592
730
  const ref = React3.useRef(null);
@@ -663,16 +801,17 @@ var ListComponent = typedMemo(function ListComponent2({
663
801
  scrollAdjustHandler,
664
802
  onLayoutHeader,
665
803
  snapToIndices,
804
+ stickyIndices,
666
805
  ...rest
667
806
  }) {
668
807
  const ctx = useStateContext();
669
- const { onLayout: onLayoutHeaderSync, ref: refHeader } = useSyncLayout({
808
+ const { onLayout: onLayoutHeaderSync, ref: refHeader } = useSyncLayout2({
670
809
  onChange: onLayoutHeader
671
810
  });
672
811
  const ScrollComponent = renderScrollComponent ? React3.useMemo(
673
812
  () => React3__namespace.forwardRef((props, ref) => renderScrollComponent({ ...props, ref })),
674
813
  [renderScrollComponent]
675
- ) : reactNative.ScrollView;
814
+ ) : reactNative.Animated.ScrollView;
676
815
  React3__namespace.useEffect(() => {
677
816
  if (canRender) {
678
817
  setTimeout(() => {
@@ -725,9 +864,26 @@ var ListComponent = typedMemo(function ListComponent2({
725
864
  style: ListFooterComponentStyle
726
865
  },
727
866
  getComponent(ListFooterComponent)
728
- )
867
+ ),
868
+ __DEV__ && ENABLE_DEVMODE && /* @__PURE__ */ React3__namespace.createElement(DevNumbers, null)
729
869
  );
730
870
  });
871
+ var DevNumbers = __DEV__ && React3__namespace.memo(function DevNumbers2() {
872
+ return Array.from({ length: 100 }).map((_, index) => /* @__PURE__ */ React3__namespace.createElement(
873
+ reactNative.View,
874
+ {
875
+ key: index,
876
+ style: {
877
+ height: 100,
878
+ pointerEvents: "none",
879
+ position: "absolute",
880
+ top: index * 100,
881
+ width: "100%"
882
+ }
883
+ },
884
+ /* @__PURE__ */ React3__namespace.createElement(reactNative.Text, { style: { color: "red" } }, index * 100)
885
+ ));
886
+ });
731
887
 
732
888
  // src/utils/getId.ts
733
889
  function getId(state, index) {
@@ -746,33 +902,45 @@ function calculateOffsetForIndex(ctx, state, index) {
746
902
  let position = 0;
747
903
  if (index !== void 0) {
748
904
  position = (state == null ? void 0 : state.positions.get(getId(state, index))) || 0;
749
- }
750
- const paddingTop = peek$(ctx, "stylePaddingTop");
751
- if (paddingTop) {
752
- position += paddingTop;
753
- }
754
- const headerSize = peek$(ctx, "headerSize");
755
- if (headerSize) {
756
- position += headerSize;
905
+ const paddingTop = peek$(ctx, "stylePaddingTop");
906
+ if (paddingTop) {
907
+ position += paddingTop;
908
+ }
909
+ const headerSize = peek$(ctx, "headerSize");
910
+ if (headerSize) {
911
+ position += headerSize;
912
+ }
757
913
  }
758
914
  return position;
759
915
  }
760
916
 
761
917
  // src/utils/getItemSize.ts
762
918
  function getItemSize(state, key, index, data, useAverageSize) {
919
+ var _a, _b;
763
920
  const {
764
921
  sizesKnown,
765
922
  sizes,
766
923
  scrollingTo,
767
- props: { estimatedItemSize, getEstimatedItemSize }
924
+ averageSizes,
925
+ props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType }
768
926
  } = state;
769
927
  const sizeKnown = sizesKnown.get(key);
770
928
  if (sizeKnown !== void 0) {
771
929
  return sizeKnown;
772
930
  }
773
931
  let size;
774
- if (useAverageSize !== void 0 && sizeKnown === void 0 && !getEstimatedItemSize && !scrollingTo) {
775
- size = useAverageSize;
932
+ const itemType = getItemType ? (_a = getItemType(data, index)) != null ? _a : "" : "";
933
+ if (getFixedItemSize) {
934
+ size = getFixedItemSize(index, data, itemType);
935
+ if (size !== void 0) {
936
+ sizesKnown.set(key, size);
937
+ }
938
+ }
939
+ if (size === void 0 && useAverageSize && sizeKnown === void 0 && !scrollingTo) {
940
+ const averageSizeForType = (_b = averageSizes[itemType]) == null ? void 0 : _b.avg;
941
+ if (averageSizeForType !== void 0) {
942
+ size = roundSize(averageSizeForType);
943
+ }
776
944
  }
777
945
  if (size === void 0) {
778
946
  size = sizes.get(key);
@@ -781,7 +949,7 @@ function getItemSize(state, key, index, data, useAverageSize) {
781
949
  }
782
950
  }
783
951
  if (size === void 0) {
784
- size = getEstimatedItemSize ? getEstimatedItemSize(index, data) : estimatedItemSize;
952
+ size = getEstimatedItemSize ? getEstimatedItemSize(index, data, itemType) : estimatedItemSize;
785
953
  }
786
954
  sizes.set(key, size);
787
955
  return size;
@@ -800,11 +968,52 @@ function calculateOffsetWithOffsetPosition(state, offsetParam, params) {
800
968
  return offset;
801
969
  }
802
970
 
971
+ // src/core/finishScrollTo.ts
972
+ var finishScrollTo = (state) => {
973
+ if (state) {
974
+ state.scrollingTo = void 0;
975
+ state.scrollHistory.length = 0;
976
+ }
977
+ };
978
+
979
+ // src/core/scrollTo.ts
980
+ function scrollTo(state, params = {}) {
981
+ var _a;
982
+ const { animated, noScrollingTo } = params;
983
+ const {
984
+ refScroller,
985
+ props: { horizontal }
986
+ } = state;
987
+ const offset = calculateOffsetWithOffsetPosition(state, params.offset, params);
988
+ state.scrollHistory.length = 0;
989
+ if (!noScrollingTo) {
990
+ state.scrollingTo = params;
991
+ }
992
+ state.scrollPending = offset;
993
+ (_a = refScroller.current) == null ? void 0 : _a.scrollTo({
994
+ animated: !!animated,
995
+ x: horizontal ? offset : 0,
996
+ y: horizontal ? 0 : offset
997
+ });
998
+ if (!animated) {
999
+ state.scroll = offset;
1000
+ setTimeout(() => finishScrollTo(state), 100);
1001
+ }
1002
+ }
1003
+
803
1004
  // src/utils/requestAdjust.ts
804
- function requestAdjust(ctx, state, positionDiff) {
1005
+ function requestAdjust(ctx, state, positionDiff, dataChanged) {
805
1006
  if (Math.abs(positionDiff) > 0.1) {
1007
+ const needsScrollWorkaround = reactNative.Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
806
1008
  const doit = () => {
807
- state.scrollAdjustHandler.requestAdjust(positionDiff);
1009
+ if (needsScrollWorkaround) {
1010
+ scrollTo(state, {
1011
+ noScrollingTo: true,
1012
+ offset: state.scroll
1013
+ });
1014
+ } else {
1015
+ state.scrollAdjustHandler.requestAdjust(positionDiff);
1016
+ }
808
1017
  };
809
1018
  state.scroll += positionDiff;
810
1019
  state.scrollForNextCalculateItemsInView = void 0;
@@ -823,49 +1032,72 @@ function requestAdjust(ctx, state, positionDiff) {
823
1032
  if (state.ignoreScrollFromMVCPTimeout) {
824
1033
  clearTimeout(state.ignoreScrollFromMVCPTimeout);
825
1034
  }
826
- state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
827
- state.ignoreScrollFromMVCP = void 0;
828
- }, 100);
1035
+ state.ignoreScrollFromMVCPTimeout = setTimeout(
1036
+ () => {
1037
+ state.ignoreScrollFromMVCP = void 0;
1038
+ },
1039
+ needsScrollWorkaround ? 250 : 100
1040
+ );
829
1041
  } else {
830
1042
  requestAnimationFrame(doit);
831
1043
  }
832
1044
  }
833
1045
  }
834
1046
 
835
- // src/core/prepareMVCP.ts
836
- function prepareMVCP(ctx, state) {
1047
+ // src/core/mvcp.ts
1048
+ function prepareMVCP(ctx, state, dataChanged) {
837
1049
  const {
1050
+ idsInView,
838
1051
  positions,
839
1052
  scrollingTo,
840
1053
  props: { maintainVisibleContentPosition }
841
1054
  } = state;
842
1055
  let prevPosition;
843
1056
  let targetId;
844
- let targetIndex;
1057
+ const idsInViewWithPositions = [];
845
1058
  const scrollTarget = scrollingTo == null ? void 0 : scrollingTo.index;
846
1059
  if (maintainVisibleContentPosition) {
847
1060
  const indexByKey = state.indexByKey;
848
1061
  if (scrollTarget !== void 0) {
849
1062
  targetId = getId(state, scrollTarget);
850
- targetIndex = scrollTarget;
851
- } else if (state.idsInView.length > 0 && peek$(ctx, "containersDidLayout")) {
852
- targetId = state.idsInView.find((id) => indexByKey.get(id) !== void 0);
853
- targetIndex = indexByKey.get(targetId);
1063
+ } else if (idsInView.length > 0 && peek$(ctx, "containersDidLayout")) {
1064
+ if (dataChanged) {
1065
+ for (let i = 0; i < idsInView.length; i++) {
1066
+ const id = idsInView[i];
1067
+ const index = indexByKey.get(id);
1068
+ if (index !== void 0) {
1069
+ idsInViewWithPositions.push({ id, position: positions.get(id) });
1070
+ }
1071
+ }
1072
+ } else {
1073
+ targetId = state.idsInView.find((id) => indexByKey.get(id) !== void 0);
1074
+ }
854
1075
  }
855
- if (targetId !== void 0 && targetIndex !== void 0) {
1076
+ if (targetId !== void 0) {
856
1077
  prevPosition = positions.get(targetId);
857
1078
  }
858
1079
  }
859
1080
  return () => {
1081
+ let positionDiff;
1082
+ if (dataChanged && targetId === void 0) {
1083
+ for (let i = 0; i < idsInViewWithPositions.length; i++) {
1084
+ const { id, position } = idsInViewWithPositions[i];
1085
+ const newPosition = positions.get(id);
1086
+ if (newPosition !== void 0) {
1087
+ positionDiff = newPosition - position;
1088
+ break;
1089
+ }
1090
+ }
1091
+ }
860
1092
  if (targetId !== void 0 && prevPosition !== void 0) {
861
1093
  const newPosition = positions.get(targetId);
862
1094
  if (newPosition !== void 0) {
863
- const positionDiff = newPosition - prevPosition;
864
- if (Math.abs(positionDiff) > 0.1) {
865
- requestAdjust(ctx, state, positionDiff);
866
- }
1095
+ positionDiff = newPosition - prevPosition;
867
1096
  }
868
1097
  }
1098
+ if (positionDiff !== void 0 && Math.abs(positionDiff) > 0.1) {
1099
+ requestAdjust(ctx, state, positionDiff, dataChanged);
1100
+ }
869
1101
  };
870
1102
  }
871
1103
 
@@ -874,10 +1106,10 @@ function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
874
1106
  if (stylePaddingTop !== void 0) {
875
1107
  const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
876
1108
  if (stylePaddingTop < prevStylePaddingTop) {
877
- let prevTotalSize = peek$(ctx, "totalSize");
1109
+ let prevTotalSize = peek$(ctx, "totalSize") || 0;
878
1110
  set$(ctx, "totalSize", prevTotalSize + prevStylePaddingTop);
879
1111
  state.timeoutSetPaddingTop = setTimeout(() => {
880
- prevTotalSize = peek$(ctx, "totalSize");
1112
+ prevTotalSize = peek$(ctx, "totalSize") || 0;
881
1113
  set$(ctx, "totalSize", prevTotalSize - prevStylePaddingTop);
882
1114
  }, 16);
883
1115
  }
@@ -941,43 +1173,6 @@ function addTotalSize(ctx, state, key, add) {
941
1173
  }
942
1174
  }
943
1175
 
944
- // src/utils/getScrollVelocity.ts
945
- var getScrollVelocity = (state) => {
946
- const { scrollHistory } = state;
947
- let velocity = 0;
948
- if (scrollHistory.length >= 1) {
949
- const newest = scrollHistory[scrollHistory.length - 1];
950
- let oldest;
951
- let start = 0;
952
- for (let i = 0; i < scrollHistory.length - 1; i++) {
953
- const entry = scrollHistory[i];
954
- const nextEntry = scrollHistory[i + 1];
955
- if (i > 0) {
956
- const prevEntry = scrollHistory[i - 1];
957
- const prevDirection = entry.scroll - prevEntry.scroll;
958
- const currentDirection = nextEntry.scroll - entry.scroll;
959
- if (prevDirection > 0 && currentDirection < 0 || prevDirection < 0 && currentDirection > 0) {
960
- start = i;
961
- break;
962
- }
963
- }
964
- }
965
- for (let i = start; i < scrollHistory.length - 1; i++) {
966
- const entry = scrollHistory[i];
967
- if (newest.time - entry.time <= 1e3) {
968
- oldest = entry;
969
- break;
970
- }
971
- }
972
- if (oldest) {
973
- const scrollDiff = newest.scroll - oldest.scroll;
974
- const timeDiff = newest.time - oldest.time;
975
- velocity = timeDiff > 0 ? scrollDiff / timeDiff : 0;
976
- }
977
- }
978
- return velocity;
979
- };
980
-
981
1176
  // src/utils/updateSnapToOffsets.ts
982
1177
  function updateSnapToOffsets(ctx, state) {
983
1178
  const {
@@ -994,69 +1189,42 @@ function updateSnapToOffsets(ctx, state) {
994
1189
  }
995
1190
 
996
1191
  // src/core/updateAllPositions.ts
997
- function updateAllPositions(ctx, state, dataChanged) {
998
- var _a, _b, _c, _d, _e;
1192
+ function updateAllPositions(ctx, state, dataChanged, startIndex = 0) {
1193
+ var _a, _b, _c, _d, _e, _f;
999
1194
  const {
1000
- averageSizes,
1001
1195
  columns,
1002
1196
  indexByKey,
1003
1197
  positions,
1004
- firstFullyOnScreenIndex,
1005
1198
  idCache,
1006
1199
  sizesKnown,
1007
- props: { snapToIndices }
1200
+ props: { getEstimatedItemSize, snapToIndices, enableAverages }
1008
1201
  } = state;
1009
1202
  const data = state.props.data;
1010
1203
  const numColumns = peek$(ctx, "numColumns");
1011
1204
  const indexByKeyForChecking = __DEV__ ? /* @__PURE__ */ new Map() : void 0;
1012
- const scrollVelocity = getScrollVelocity(state);
1013
- if (dataChanged) {
1014
- indexByKey.clear();
1015
- idCache.clear();
1016
- }
1017
- const itemType = "";
1018
- let averageSize = (_a = averageSizes[itemType]) == null ? void 0 : _a.avg;
1019
- if (averageSize !== void 0) {
1020
- averageSize = roundSize(averageSize);
1021
- }
1022
- const shouldUseBackwards = !dataChanged && scrollVelocity < 0 && firstFullyOnScreenIndex > 5 && firstFullyOnScreenIndex < data.length;
1023
- if (shouldUseBackwards && firstFullyOnScreenIndex !== void 0) {
1024
- const anchorId = getId(state, firstFullyOnScreenIndex);
1025
- const anchorPosition = positions.get(anchorId);
1026
- if (anchorPosition !== void 0) {
1027
- let currentRowTop2 = anchorPosition;
1028
- let maxSizeInRow2 = 0;
1029
- let bailout = false;
1030
- for (let i = firstFullyOnScreenIndex - 1; i >= 0; i--) {
1031
- const id = (_b = idCache.get(i)) != null ? _b : getId(state, i);
1032
- const size = (_c = sizesKnown.get(id)) != null ? _c : getItemSize(state, id, i, data[i], averageSize);
1033
- const itemColumn = columns.get(id);
1034
- maxSizeInRow2 = Math.max(maxSizeInRow2, size);
1035
- if (itemColumn === 1) {
1036
- currentRowTop2 -= maxSizeInRow2;
1037
- maxSizeInRow2 = 0;
1038
- }
1039
- if (currentRowTop2 < -2e3) {
1040
- bailout = true;
1041
- break;
1042
- }
1043
- positions.set(id, currentRowTop2);
1044
- }
1045
- if (!bailout) {
1046
- updateTotalSize(ctx, state);
1047
- return;
1048
- }
1049
- }
1050
- }
1205
+ const useAverageSize = enableAverages && !getEstimatedItemSize;
1051
1206
  let currentRowTop = 0;
1052
1207
  let column = 1;
1053
1208
  let maxSizeInRow = 0;
1054
1209
  const hasColumns = numColumns > 1;
1210
+ if (startIndex > 0) {
1211
+ const prevIndex = startIndex - 1;
1212
+ const prevId = (_a = idCache.get(prevIndex)) != null ? _a : getId(state, prevIndex);
1213
+ const prevPosition = (_b = positions.get(prevId)) != null ? _b : 0;
1214
+ if (hasColumns) {
1215
+ const prevColumn = (_c = columns.get(prevId)) != null ? _c : 1;
1216
+ currentRowTop = prevPosition;
1217
+ column = prevColumn % numColumns + 1;
1218
+ } else {
1219
+ const prevSize = (_d = sizesKnown.get(prevId)) != null ? _d : getItemSize(state, prevId, prevIndex, data[prevIndex], useAverageSize);
1220
+ currentRowTop = prevPosition + prevSize;
1221
+ }
1222
+ }
1055
1223
  const needsIndexByKey = dataChanged || indexByKey.size === 0;
1056
1224
  const dataLength = data.length;
1057
- for (let i = 0; i < dataLength; i++) {
1058
- const id = (_d = idCache.get(i)) != null ? _d : getId(state, i);
1059
- const size = (_e = sizesKnown.get(id)) != null ? _e : getItemSize(state, id, i, data[i], averageSize);
1225
+ for (let i = startIndex; i < dataLength; i++) {
1226
+ const id = (_e = idCache.get(i)) != null ? _e : getId(state, i);
1227
+ const size = (_f = sizesKnown.get(id)) != null ? _f : getItemSize(state, id, i, data[i], useAverageSize);
1060
1228
  if (__DEV__ && needsIndexByKey) {
1061
1229
  if (indexByKeyForChecking.has(id)) {
1062
1230
  console.error(
@@ -1091,7 +1259,19 @@ function updateAllPositions(ctx, state, dataChanged) {
1091
1259
  }
1092
1260
 
1093
1261
  // src/core/viewability.ts
1094
- var mapViewabilityConfigCallbackPairs = /* @__PURE__ */ new Map();
1262
+ function ensureViewabilityState(ctx, configId) {
1263
+ let map = ctx.mapViewabilityConfigStates;
1264
+ if (!map) {
1265
+ map = /* @__PURE__ */ new Map();
1266
+ ctx.mapViewabilityConfigStates = map;
1267
+ }
1268
+ let state = map.get(configId);
1269
+ if (!state) {
1270
+ state = { end: -1, previousEnd: -1, previousStart: -1, start: -1, viewableItems: [] };
1271
+ map.set(configId, state);
1272
+ }
1273
+ return state;
1274
+ }
1095
1275
  function setupViewability(props) {
1096
1276
  let { viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged } = props;
1097
1277
  if (viewabilityConfig || onViewableItemsChanged) {
@@ -1105,17 +1285,6 @@ function setupViewability(props) {
1105
1285
  }
1106
1286
  ];
1107
1287
  }
1108
- if (viewabilityConfigCallbackPairs) {
1109
- for (const pair of viewabilityConfigCallbackPairs) {
1110
- mapViewabilityConfigCallbackPairs.set(pair.viewabilityConfig.id, {
1111
- end: -1,
1112
- previousEnd: -1,
1113
- previousStart: -1,
1114
- start: -1,
1115
- viewableItems: []
1116
- });
1117
- }
1118
- }
1119
1288
  return viewabilityConfigCallbackPairs;
1120
1289
  }
1121
1290
  function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollSize, start, end) {
@@ -1124,9 +1293,7 @@ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollS
1124
1293
  props: { data }
1125
1294
  } = state;
1126
1295
  for (const viewabilityConfigCallbackPair of viewabilityConfigCallbackPairs) {
1127
- const viewabilityState = mapViewabilityConfigCallbackPairs.get(
1128
- viewabilityConfigCallbackPair.viewabilityConfig.id
1129
- );
1296
+ const viewabilityState = ensureViewabilityState(ctx, viewabilityConfigCallbackPair.viewabilityConfig.id);
1130
1297
  viewabilityState.start = start;
1131
1298
  viewabilityState.end = end;
1132
1299
  if (viewabilityConfigCallbackPair.viewabilityConfig.minimumViewTime) {
@@ -1143,7 +1310,7 @@ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollS
1143
1310
  function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, state, ctx, scrollSize) {
1144
1311
  const { viewabilityConfig, onViewableItemsChanged } = viewabilityConfigCallbackPair;
1145
1312
  const configId = viewabilityConfig.id;
1146
- const viewabilityState = mapViewabilityConfigCallbackPairs.get(configId);
1313
+ const viewabilityState = ensureViewabilityState(ctx, configId);
1147
1314
  const { viewableItems: previousViewableItems, start, end } = viewabilityState;
1148
1315
  const viewabilityTokens = /* @__PURE__ */ new Map();
1149
1316
  for (const [containerId, value] of ctx.mapViewabilityAmountValues) {
@@ -1222,6 +1389,15 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
1222
1389
  }
1223
1390
  }
1224
1391
  }
1392
+ function shallowEqual(prev, next) {
1393
+ if (!prev) return false;
1394
+ const keys = Object.keys(next);
1395
+ for (let i = 0; i < keys.length; i++) {
1396
+ const k = keys[i];
1397
+ if (prev[k] !== next[k]) return false;
1398
+ }
1399
+ return true;
1400
+ }
1225
1401
  function computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
1226
1402
  const { sizes, positions, scroll: scrollState } = state;
1227
1403
  const topPad = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
@@ -1250,7 +1426,8 @@ function computeViewability(state, ctx, viewabilityConfig, containerId, key, scr
1250
1426
  size,
1251
1427
  sizeVisible
1252
1428
  };
1253
- if (JSON.stringify(value) !== JSON.stringify(ctx.mapViewabilityAmountValues.get(containerId))) {
1429
+ const prev = ctx.mapViewabilityAmountValues.get(containerId);
1430
+ if (!shallowEqual(prev, value)) {
1254
1431
  ctx.mapViewabilityAmountValues.set(containerId, value);
1255
1432
  const cb = ctx.mapViewabilityAmountCallbacks.get(containerId);
1256
1433
  if (cb) {
@@ -1279,6 +1456,7 @@ function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
1279
1456
  const cb = ctx.mapViewabilityCallbacks.get(key);
1280
1457
  cb == null ? void 0 : cb(viewToken);
1281
1458
  }
1459
+ var batchedUpdates = reactNative.unstable_batchedUpdates || ((callback) => callback());
1282
1460
 
1283
1461
  // src/utils/checkAllSizesKnown.ts
1284
1462
  function checkAllSizesKnown(state) {
@@ -1295,35 +1473,79 @@ function checkAllSizesKnown(state) {
1295
1473
  }
1296
1474
 
1297
1475
  // src/utils/findAvailableContainers.ts
1298
- function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffered, pendingRemoval) {
1476
+ function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers) {
1299
1477
  const numContainers = peek$(ctx, "numContainers");
1478
+ const { stickyContainerPool, containerItemTypes } = state;
1300
1479
  const result = [];
1301
1480
  const availableContainers = [];
1302
- for (let u = 0; u < numContainers; u++) {
1481
+ const stickyIndicesSet = state.props.stickyIndicesSet;
1482
+ const stickyItemIndices = (needNewContainers == null ? void 0 : needNewContainers.filter((index) => stickyIndicesSet.has(index))) || [];
1483
+ const canReuseContainer = (containerIndex, requiredType) => {
1484
+ if (!requiredType) return true;
1485
+ const existingType = containerItemTypes.get(containerIndex);
1486
+ if (!existingType) return true;
1487
+ return existingType === requiredType;
1488
+ };
1489
+ const neededTypes = requiredItemTypes ? [...requiredItemTypes] : [];
1490
+ let typeIndex = 0;
1491
+ for (let i = 0; i < stickyItemIndices.length; i++) {
1492
+ const requiredType = neededTypes[typeIndex];
1493
+ let foundContainer = false;
1494
+ for (const containerIndex of stickyContainerPool) {
1495
+ const key = peek$(ctx, `containerItemKey${containerIndex}`);
1496
+ const isPendingRemoval = pendingRemoval.includes(containerIndex);
1497
+ if ((key === void 0 || isPendingRemoval) && canReuseContainer(containerIndex, requiredType)) {
1498
+ result.push(containerIndex);
1499
+ if (isPendingRemoval) {
1500
+ const index = pendingRemoval.indexOf(containerIndex);
1501
+ pendingRemoval.splice(index, 1);
1502
+ }
1503
+ foundContainer = true;
1504
+ if (requiredItemTypes) typeIndex++;
1505
+ break;
1506
+ }
1507
+ }
1508
+ if (!foundContainer) {
1509
+ const newContainerIndex = numContainers + result.filter((index) => index >= numContainers).length;
1510
+ result.push(newContainerIndex);
1511
+ stickyContainerPool.add(newContainerIndex);
1512
+ if (requiredItemTypes) typeIndex++;
1513
+ }
1514
+ }
1515
+ for (let u = 0; u < numContainers && result.length < numNeeded; u++) {
1516
+ if (stickyContainerPool.has(u)) {
1517
+ continue;
1518
+ }
1303
1519
  const key = peek$(ctx, `containerItemKey${u}`);
1304
1520
  let isOk = key === void 0;
1305
1521
  if (!isOk) {
1306
1522
  const index = pendingRemoval.indexOf(u);
1307
1523
  if (index !== -1) {
1308
1524
  pendingRemoval.splice(index, 1);
1309
- isOk = true;
1525
+ const requiredType = neededTypes[typeIndex];
1526
+ isOk = canReuseContainer(u, requiredType);
1310
1527
  }
1311
1528
  }
1312
1529
  if (isOk) {
1313
1530
  result.push(u);
1314
- if (result.length >= numNeeded) {
1315
- return result;
1531
+ if (requiredItemTypes) {
1532
+ typeIndex++;
1316
1533
  }
1317
1534
  }
1318
1535
  }
1319
- for (let u = 0; u < numContainers; u++) {
1536
+ for (let u = 0; u < numContainers && result.length < numNeeded; u++) {
1537
+ if (stickyContainerPool.has(u)) {
1538
+ continue;
1539
+ }
1320
1540
  const key = peek$(ctx, `containerItemKey${u}`);
1321
1541
  if (key === void 0) continue;
1322
1542
  const index = state.indexByKey.get(key);
1323
- if (index < startBuffered) {
1324
- availableContainers.push({ distance: startBuffered - index, index: u });
1325
- } else if (index > endBuffered) {
1326
- availableContainers.push({ distance: index - endBuffered, index: u });
1543
+ const isOutOfView = index < startBuffered || index > endBuffered;
1544
+ if (isOutOfView) {
1545
+ const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
1546
+ if (!requiredItemTypes || typeIndex < neededTypes.length && canReuseContainer(u, neededTypes[typeIndex])) {
1547
+ availableContainers.push({ distance, index: u });
1548
+ }
1327
1549
  }
1328
1550
  }
1329
1551
  const remaining = numNeeded - result.length;
@@ -1335,6 +1557,9 @@ function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffer
1335
1557
  }
1336
1558
  for (const container of availableContainers) {
1337
1559
  result.push(container.index);
1560
+ if (requiredItemTypes) {
1561
+ typeIndex++;
1562
+ }
1338
1563
  }
1339
1564
  }
1340
1565
  const stillNeeded = numNeeded - result.length;
@@ -1363,37 +1588,44 @@ function comparatorByDistance(a, b) {
1363
1588
  return b.distance - a.distance;
1364
1589
  }
1365
1590
 
1366
- // src/core/finishScrollTo.ts
1367
- var finishScrollTo = (state) => {
1368
- if (state) {
1369
- state.scrollingTo = void 0;
1370
- state.scrollHistory.length = 0;
1591
+ // src/utils/getScrollVelocity.ts
1592
+ var getScrollVelocity = (state) => {
1593
+ const { scrollHistory } = state;
1594
+ let velocity = 0;
1595
+ if (scrollHistory.length >= 1) {
1596
+ const newest = scrollHistory[scrollHistory.length - 1];
1597
+ let oldest;
1598
+ let start = 0;
1599
+ const now = Date.now();
1600
+ for (let i = 0; i < scrollHistory.length - 1; i++) {
1601
+ const entry = scrollHistory[i];
1602
+ const nextEntry = scrollHistory[i + 1];
1603
+ if (i > 0) {
1604
+ const prevEntry = scrollHistory[i - 1];
1605
+ const prevDirection = entry.scroll - prevEntry.scroll;
1606
+ const currentDirection = nextEntry.scroll - entry.scroll;
1607
+ if (prevDirection > 0 && currentDirection < 0 || prevDirection < 0 && currentDirection > 0) {
1608
+ start = i;
1609
+ break;
1610
+ }
1611
+ }
1612
+ }
1613
+ for (let i = start; i < scrollHistory.length - 1; i++) {
1614
+ const entry = scrollHistory[i];
1615
+ if (now - entry.time <= 1e3) {
1616
+ oldest = entry;
1617
+ break;
1618
+ }
1619
+ }
1620
+ if (oldest && oldest !== newest) {
1621
+ const scrollDiff = newest.scroll - oldest.scroll;
1622
+ const timeDiff = newest.time - oldest.time;
1623
+ velocity = timeDiff > 0 ? scrollDiff / timeDiff : 0;
1624
+ }
1371
1625
  }
1626
+ return velocity;
1372
1627
  };
1373
1628
 
1374
- // src/core/scrollTo.ts
1375
- function scrollTo(state, params = {}) {
1376
- var _a;
1377
- const { animated } = params;
1378
- const {
1379
- refScroller,
1380
- props: { horizontal }
1381
- } = state;
1382
- const offset = calculateOffsetWithOffsetPosition(state, params.offset, params);
1383
- state.scrollHistory.length = 0;
1384
- state.scrollingTo = params;
1385
- state.scrollPending = offset;
1386
- (_a = refScroller.current) == null ? void 0 : _a.scrollTo({
1387
- animated: !!animated,
1388
- x: horizontal ? offset : 0,
1389
- y: horizontal ? 0 : offset
1390
- });
1391
- if (!animated) {
1392
- state.scroll = offset;
1393
- setTimeout(() => finishScrollTo(state), 100);
1394
- }
1395
- }
1396
-
1397
1629
  // src/core/scrollToIndex.ts
1398
1630
  function scrollToIndex(ctx, state, { index, viewOffset = 0, animated = true, viewPosition }) {
1399
1631
  if (index >= state.props.data.length) {
@@ -1472,42 +1704,122 @@ function checkAtBottom(ctx, state) {
1472
1704
  }
1473
1705
  }
1474
1706
 
1475
- // src/utils/setDidLayout.ts
1476
- function setDidLayout(ctx, state) {
1477
- const {
1478
- loadStartTime,
1479
- initialScroll,
1480
- props: { onLoad }
1481
- } = state;
1482
- state.queuedInitialLayout = true;
1483
- checkAtBottom(ctx, state);
1484
- if (!IsNewArchitecture && initialScroll) {
1485
- scrollToIndex(ctx, state, { ...initialScroll, animated: false });
1707
+ // src/utils/setDidLayout.ts
1708
+ function setDidLayout(ctx, state) {
1709
+ const {
1710
+ loadStartTime,
1711
+ initialScroll,
1712
+ props: { onLoad }
1713
+ } = state;
1714
+ state.queuedInitialLayout = true;
1715
+ checkAtBottom(ctx, state);
1716
+ const setIt = () => {
1717
+ set$(ctx, "containersDidLayout", true);
1718
+ if (onLoad) {
1719
+ onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1720
+ }
1721
+ };
1722
+ if (reactNative.Platform.OS === "android" || !IsNewArchitecture) {
1723
+ if (initialScroll) {
1724
+ queueMicrotask(() => {
1725
+ scrollToIndex(ctx, state, { ...initialScroll, animated: false });
1726
+ requestAnimationFrame(() => {
1727
+ scrollToIndex(ctx, state, { ...initialScroll, animated: false });
1728
+ setIt();
1729
+ });
1730
+ });
1731
+ } else {
1732
+ queueMicrotask(setIt);
1733
+ }
1734
+ } else {
1735
+ setIt();
1736
+ }
1737
+ }
1738
+
1739
+ // src/core/calculateItemsInView.ts
1740
+ function findCurrentStickyIndex(stickyArray, scroll, state) {
1741
+ var _a;
1742
+ const idCache = state.idCache;
1743
+ const positions = state.positions;
1744
+ for (let i = stickyArray.length - 1; i >= 0; i--) {
1745
+ const stickyId = (_a = idCache.get(stickyArray[i])) != null ? _a : getId(state, stickyArray[i]);
1746
+ const stickyPos = stickyId ? positions.get(stickyId) : void 0;
1747
+ if (stickyPos !== void 0 && scroll >= stickyPos) {
1748
+ return i;
1749
+ }
1750
+ }
1751
+ return -1;
1752
+ }
1753
+ function getActiveStickyIndices(ctx, state, stickyIndices) {
1754
+ return new Set(
1755
+ 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))
1756
+ );
1757
+ }
1758
+ function handleStickyActivation(ctx, state, stickyIndices, stickyArray, scroll, needNewContainers, startBuffered, endBuffered) {
1759
+ var _a;
1760
+ const activeIndices = getActiveStickyIndices(ctx, state, stickyIndices);
1761
+ const currentStickyIdx = findCurrentStickyIndex(stickyArray, scroll, state);
1762
+ for (let offset = 0; offset <= 1; offset++) {
1763
+ const idx = currentStickyIdx - offset;
1764
+ if (idx < 0 || activeIndices.has(stickyArray[idx])) continue;
1765
+ const stickyIndex = stickyArray[idx];
1766
+ const stickyId = (_a = state.idCache.get(stickyIndex)) != null ? _a : getId(state, stickyIndex);
1767
+ if (stickyId && !state.containerItemKeys.has(stickyId) && (stickyIndex < startBuffered || stickyIndex > endBuffered)) {
1768
+ needNewContainers.push(stickyIndex);
1769
+ }
1486
1770
  }
1487
- set$(ctx, "containersDidLayout", true);
1488
- if (onLoad) {
1489
- onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1771
+ }
1772
+ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, pendingRemoval) {
1773
+ var _a, _b, _c;
1774
+ const currentStickyIdx = findCurrentStickyIndex(stickyArray, scroll, state);
1775
+ for (const containerIndex of state.stickyContainerPool) {
1776
+ const itemKey = peek$(ctx, `containerItemKey${containerIndex}`);
1777
+ const itemIndex = itemKey ? state.indexByKey.get(itemKey) : void 0;
1778
+ if (itemIndex === void 0) continue;
1779
+ const arrayIdx = stickyArray.indexOf(itemIndex);
1780
+ if (arrayIdx === -1) continue;
1781
+ const isRecentSticky = arrayIdx >= currentStickyIdx - 1 && arrayIdx <= currentStickyIdx + 1;
1782
+ if (isRecentSticky) continue;
1783
+ const nextIndex = stickyArray[arrayIdx + 1];
1784
+ let shouldRecycle = false;
1785
+ if (nextIndex) {
1786
+ const nextId = (_a = state.idCache.get(nextIndex)) != null ? _a : getId(state, nextIndex);
1787
+ const nextPos = nextId ? state.positions.get(nextId) : void 0;
1788
+ shouldRecycle = nextPos !== void 0 && scroll > nextPos + scrollBuffer * 2;
1789
+ } else {
1790
+ const currentId = (_b = state.idCache.get(itemIndex)) != null ? _b : getId(state, itemIndex);
1791
+ if (currentId) {
1792
+ const currentPos = state.positions.get(currentId);
1793
+ const currentSize = (_c = state.sizes.get(currentId)) != null ? _c : getItemSize(state, currentId, itemIndex, state.props.data[itemIndex]);
1794
+ shouldRecycle = currentPos !== void 0 && scroll > currentPos + currentSize + scrollBuffer * 3;
1795
+ }
1796
+ }
1797
+ if (shouldRecycle) {
1798
+ pendingRemoval.push(containerIndex);
1799
+ }
1490
1800
  }
1491
1801
  }
1492
-
1493
- // src/core/calculateItemsInView.ts
1494
1802
  function calculateItemsInView(ctx, state, params = {}) {
1495
- reactNative.unstable_batchedUpdates(() => {
1803
+ batchedUpdates(() => {
1496
1804
  var _a, _b, _c, _d, _e, _f, _g, _h;
1497
1805
  const {
1498
- scrollLength,
1499
- startBufferedId: startBufferedIdOrig,
1500
- positions,
1501
1806
  columns,
1502
1807
  containerItemKeys,
1808
+ enableScrollForNextCalculateItemsInView,
1503
1809
  idCache,
1504
- sizes,
1505
1810
  indexByKey,
1811
+ minIndexSizeChanged,
1812
+ positions,
1506
1813
  scrollForNextCalculateItemsInView,
1507
- enableScrollForNextCalculateItemsInView,
1508
- minIndexSizeChanged
1814
+ scrollLength,
1815
+ sizes,
1816
+ startBufferedId: startBufferedIdOrig,
1817
+ viewabilityConfigCallbackPairs,
1818
+ props: { getItemType, initialScroll, itemsAreEqual, keyExtractor, scrollBuffer }
1509
1819
  } = state;
1510
- const data = state.props.data;
1820
+ const { data } = state.props;
1821
+ const stickyIndicesArr = state.props.stickyIndicesArr || [];
1822
+ const stickyIndicesSet = state.props.stickyIndicesSet || /* @__PURE__ */ new Set();
1511
1823
  const prevNumContainers = peek$(ctx, "numContainers");
1512
1824
  if (!data || scrollLength === 0 || !prevNumContainers) {
1513
1825
  return;
@@ -1519,14 +1831,22 @@ function calculateItemsInView(ctx, state, params = {}) {
1519
1831
  const { dataChanged, doMVCP } = params;
1520
1832
  const speed = getScrollVelocity(state);
1521
1833
  if (doMVCP || dataChanged) {
1522
- const checkMVCP = doMVCP ? prepareMVCP(ctx, state) : void 0;
1523
- updateAllPositions(ctx, state, dataChanged);
1834
+ const checkMVCP = doMVCP ? prepareMVCP(ctx, state, dataChanged) : void 0;
1835
+ if (dataChanged) {
1836
+ indexByKey.clear();
1837
+ idCache.clear();
1838
+ positions.clear();
1839
+ }
1840
+ const startIndex = dataChanged ? 0 : minIndexSizeChanged != null ? minIndexSizeChanged : 0;
1841
+ updateAllPositions(ctx, state, dataChanged, startIndex);
1842
+ if (minIndexSizeChanged !== void 0) {
1843
+ state.minIndexSizeChanged = void 0;
1844
+ }
1524
1845
  checkMVCP == null ? void 0 : checkMVCP();
1525
1846
  }
1526
1847
  const scrollExtra = 0;
1527
1848
  const { queuedInitialLayout } = state;
1528
1849
  let { scroll: scrollState } = state;
1529
- const initialScroll = state.props.initialScroll;
1530
1850
  if (!queuedInitialLayout && initialScroll) {
1531
1851
  const updatedOffset = calculateOffsetWithOffsetPosition(
1532
1852
  state,
@@ -1538,16 +1858,15 @@ function calculateItemsInView(ctx, state, params = {}) {
1538
1858
  const scrollAdjustPad = -previousScrollAdjust - topPad;
1539
1859
  let scroll = scrollState + scrollExtra + scrollAdjustPad;
1540
1860
  if (scroll + scrollLength > totalSize) {
1541
- scroll = totalSize - scrollLength;
1861
+ scroll = Math.max(0, totalSize - scrollLength);
1542
1862
  }
1543
1863
  if (ENABLE_DEBUG_VIEW) {
1544
1864
  set$(ctx, "debugRawScroll", scrollState);
1545
1865
  set$(ctx, "debugComputedScroll", scroll);
1546
1866
  }
1547
- const scrollBuffer = state.props.scrollBuffer;
1548
1867
  let scrollBufferTop = scrollBuffer;
1549
1868
  let scrollBufferBottom = scrollBuffer;
1550
- if (speed > 0) {
1869
+ if (speed > 0 || speed === 0 && scroll < Math.max(50, scrollBuffer)) {
1551
1870
  scrollBufferTop = scrollBuffer * 0.5;
1552
1871
  scrollBufferBottom = scrollBuffer * 1.5;
1553
1872
  } else {
@@ -1555,7 +1874,7 @@ function calculateItemsInView(ctx, state, params = {}) {
1555
1874
  scrollBufferBottom = scrollBuffer * 0.5;
1556
1875
  }
1557
1876
  const scrollTopBuffered = scroll - scrollBufferTop;
1558
- const scrollBottom = scroll + scrollLength;
1877
+ const scrollBottom = scroll + scrollLength + (scroll < 0 ? -scroll : 0);
1559
1878
  const scrollBottomBuffered = scrollBottom + scrollBufferBottom;
1560
1879
  if (scrollForNextCalculateItemsInView) {
1561
1880
  const { top, bottom } = scrollForNextCalculateItemsInView;
@@ -1569,10 +1888,6 @@ function calculateItemsInView(ctx, state, params = {}) {
1569
1888
  let endNoBuffer = null;
1570
1889
  let endBuffered = null;
1571
1890
  let loopStart = startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
1572
- if (minIndexSizeChanged !== void 0) {
1573
- loopStart = Math.min(minIndexSizeChanged, loopStart);
1574
- state.minIndexSizeChanged = void 0;
1575
- }
1576
1891
  for (let i = loopStart; i >= 0; i--) {
1577
1892
  const id = (_a = idCache.get(i)) != null ? _a : getId(state, i);
1578
1893
  const top = positions.get(id);
@@ -1655,7 +1970,7 @@ function calculateItemsInView(ctx, state, params = {}) {
1655
1970
  if (dataChanged) {
1656
1971
  for (let i = 0; i < numContainers; i++) {
1657
1972
  const itemKey = peek$(ctx, `containerItemKey${i}`);
1658
- if (!state.props.keyExtractor || itemKey && indexByKey.get(itemKey) === void 0) {
1973
+ if (!keyExtractor || itemKey && indexByKey.get(itemKey) === void 0) {
1659
1974
  pendingRemoval.push(i);
1660
1975
  }
1661
1976
  }
@@ -1669,14 +1984,32 @@ function calculateItemsInView(ctx, state, params = {}) {
1669
1984
  needNewContainers.push(i);
1670
1985
  }
1671
1986
  }
1987
+ if (stickyIndicesArr.length > 0) {
1988
+ handleStickyActivation(
1989
+ ctx,
1990
+ state,
1991
+ stickyIndicesSet,
1992
+ stickyIndicesArr,
1993
+ scroll,
1994
+ needNewContainers,
1995
+ startBuffered,
1996
+ endBuffered
1997
+ );
1998
+ }
1672
1999
  if (needNewContainers.length > 0) {
2000
+ const requiredItemTypes = getItemType ? needNewContainers.map((i) => {
2001
+ const itemType = getItemType(data[i], i);
2002
+ return itemType ? String(itemType) : "";
2003
+ }) : void 0;
1673
2004
  const availableContainers = findAvailableContainers(
1674
2005
  ctx,
1675
2006
  state,
1676
2007
  needNewContainers.length,
1677
2008
  startBuffered,
1678
2009
  endBuffered,
1679
- pendingRemoval
2010
+ pendingRemoval,
2011
+ requiredItemTypes,
2012
+ needNewContainers
1680
2013
  );
1681
2014
  for (let idx = 0; idx < needNewContainers.length; idx++) {
1682
2015
  const i = needNewContainers[idx];
@@ -1688,7 +2021,19 @@ function calculateItemsInView(ctx, state, params = {}) {
1688
2021
  }
1689
2022
  set$(ctx, `containerItemKey${containerIndex}`, id);
1690
2023
  set$(ctx, `containerItemData${containerIndex}`, data[i]);
2024
+ if (requiredItemTypes) {
2025
+ state.containerItemTypes.set(containerIndex, requiredItemTypes[idx]);
2026
+ }
1691
2027
  containerItemKeys.add(id);
2028
+ if (stickyIndicesSet.has(i)) {
2029
+ set$(ctx, `containerSticky${containerIndex}`, true);
2030
+ const topPadding = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
2031
+ set$(ctx, `containerStickyOffset${containerIndex}`, new reactNative.Animated.Value(topPadding));
2032
+ state.stickyContainerPool.add(containerIndex);
2033
+ } else {
2034
+ set$(ctx, `containerSticky${containerIndex}`, false);
2035
+ state.stickyContainerPool.delete(containerIndex);
2036
+ }
1692
2037
  if (containerIndex >= numContainers2) {
1693
2038
  numContainers2 = containerIndex + 1;
1694
2039
  }
@@ -1701,12 +2046,21 @@ function calculateItemsInView(ctx, state, params = {}) {
1701
2046
  }
1702
2047
  }
1703
2048
  }
2049
+ if (stickyIndicesArr.length > 0) {
2050
+ handleStickyRecycling(ctx, state, stickyIndicesArr, scroll, scrollBuffer, pendingRemoval);
2051
+ }
1704
2052
  for (let i = 0; i < numContainers; i++) {
1705
2053
  const itemKey = peek$(ctx, `containerItemKey${i}`);
1706
2054
  if (pendingRemoval.includes(i)) {
1707
2055
  if (itemKey) {
1708
2056
  containerItemKeys.delete(itemKey);
1709
2057
  }
2058
+ state.containerItemTypes.delete(i);
2059
+ if (state.stickyContainerPool.has(i)) {
2060
+ set$(ctx, `containerSticky${i}`, false);
2061
+ set$(ctx, `containerStickyOffset${i}`, void 0);
2062
+ state.stickyContainerPool.delete(i);
2063
+ }
1710
2064
  set$(ctx, `containerItemKey${i}`, void 0);
1711
2065
  set$(ctx, `containerItemData${i}`, void 0);
1712
2066
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
@@ -1730,7 +2084,7 @@ function calculateItemsInView(ctx, state, params = {}) {
1730
2084
  if (column >= 0 && column !== prevColumn) {
1731
2085
  set$(ctx, `containerColumn${i}`, column);
1732
2086
  }
1733
- if (prevData !== item) {
2087
+ if (prevData !== item && (itemsAreEqual ? !itemsAreEqual(prevData, item, itemIndex, data) : true)) {
1734
2088
  set$(ctx, `containerItemData${i}`, data[itemIndex]);
1735
2089
  }
1736
2090
  }
@@ -1742,42 +2096,55 @@ function calculateItemsInView(ctx, state, params = {}) {
1742
2096
  setDidLayout(ctx, state);
1743
2097
  }
1744
2098
  }
1745
- if (state.viewabilityConfigCallbackPairs) {
1746
- updateViewableItems(
1747
- state,
1748
- ctx,
1749
- state.viewabilityConfigCallbackPairs,
1750
- scrollLength,
1751
- startNoBuffer,
1752
- endNoBuffer
1753
- );
2099
+ if (viewabilityConfigCallbackPairs) {
2100
+ updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollLength, startNoBuffer, endNoBuffer);
1754
2101
  }
1755
2102
  });
1756
2103
  }
1757
2104
 
1758
2105
  // src/core/doInitialAllocateContainers.ts
1759
2106
  function doInitialAllocateContainers(ctx, state) {
1760
- const { scrollLength } = state;
1761
- const data = state.props.data;
1762
- if (scrollLength > 0 && data.length > 0 && !peek$(ctx, "numContainers")) {
1763
- const averageItemSize = state.props.getEstimatedItemSize ? state.props.getEstimatedItemSize(0, data[0]) : state.props.estimatedItemSize;
1764
- const Extra = 1.5;
1765
- const numContainers = Math.ceil(
1766
- (scrollLength + state.props.scrollBuffer * 2) / averageItemSize * state.props.numColumns * Extra
1767
- );
2107
+ var _a;
2108
+ const {
2109
+ scrollLength,
2110
+ props: {
2111
+ data,
2112
+ getEstimatedItemSize,
2113
+ getFixedItemSize,
2114
+ getItemType,
2115
+ scrollBuffer,
2116
+ numColumns,
2117
+ estimatedItemSize
2118
+ }
2119
+ } = state;
2120
+ const hasContainers = peek$(ctx, "numContainers");
2121
+ if (scrollLength > 0 && data.length > 0 && !hasContainers) {
2122
+ let averageItemSize;
2123
+ const fn = getFixedItemSize || getEstimatedItemSize;
2124
+ if (fn) {
2125
+ let totalSize = 0;
2126
+ const num = Math.min(20, data.length);
2127
+ for (let i = 0; i < num; i++) {
2128
+ totalSize += fn(0, data[0], getItemType ? (_a = getItemType(data[0], 0)) != null ? _a : "" : "");
2129
+ }
2130
+ averageItemSize = totalSize / num;
2131
+ } else {
2132
+ averageItemSize = estimatedItemSize;
2133
+ }
2134
+ const numContainers = Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize * numColumns);
1768
2135
  for (let i = 0; i < numContainers; i++) {
1769
2136
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
1770
2137
  set$(ctx, `containerColumn${i}`, -1);
1771
2138
  }
1772
2139
  set$(ctx, "numContainers", numContainers);
1773
2140
  set$(ctx, "numContainersPooled", numContainers * state.props.initialContainerPoolRatio);
1774
- if (!IsNewArchitecture) {
2141
+ if (!IsNewArchitecture || state.lastLayout) {
1775
2142
  if (state.props.initialScroll) {
1776
2143
  requestAnimationFrame(() => {
1777
- calculateItemsInView(ctx, state);
2144
+ calculateItemsInView(ctx, state, { dataChanged: true });
1778
2145
  });
1779
2146
  } else {
1780
- calculateItemsInView(ctx, state);
2147
+ calculateItemsInView(ctx, state, { dataChanged: true });
1781
2148
  }
1782
2149
  }
1783
2150
  return true;
@@ -1797,16 +2164,18 @@ function doMaintainScrollAtEnd(ctx, state, animated) {
1797
2164
  }
1798
2165
  requestAnimationFrame(() => {
1799
2166
  var _a;
1800
- state.maintainingScrollAtEnd = true;
1801
- (_a = refScroller.current) == null ? void 0 : _a.scrollToEnd({
1802
- animated
1803
- });
1804
- setTimeout(
1805
- () => {
1806
- state.maintainingScrollAtEnd = false;
1807
- },
1808
- 0
1809
- );
2167
+ if (state == null ? void 0 : state.isAtEnd) {
2168
+ state.maintainingScrollAtEnd = true;
2169
+ (_a = refScroller.current) == null ? void 0 : _a.scrollToEnd({
2170
+ animated
2171
+ });
2172
+ setTimeout(
2173
+ () => {
2174
+ state.maintainingScrollAtEnd = false;
2175
+ },
2176
+ 0
2177
+ );
2178
+ }
1810
2179
  });
1811
2180
  return true;
1812
2181
  }
@@ -1847,40 +2216,49 @@ function handleLayout(ctx, state, layout, setCanRender) {
1847
2216
  const otherAxisSize = layout[state.props.horizontal ? "height" : "width"];
1848
2217
  const needsCalculate = !state.lastLayout || scrollLength > state.scrollLength || state.lastLayout.x !== layout.x || state.lastLayout.y !== layout.y;
1849
2218
  state.lastLayout = layout;
1850
- const didChange = scrollLength !== state.scrollLength;
1851
2219
  const prevOtherAxisSize = state.otherAxisSize;
1852
- state.scrollLength = scrollLength;
1853
- state.otherAxisSize = otherAxisSize;
1854
- state.lastBatchingAction = Date.now();
1855
- state.scrollForNextCalculateItemsInView = void 0;
1856
- doInitialAllocateContainers(ctx, state);
1857
- if (needsCalculate) {
1858
- calculateItemsInView(ctx, state, { doMVCP: true });
1859
- }
1860
- if (didChange || otherAxisSize !== prevOtherAxisSize) {
1861
- set$(ctx, "scrollSize", { height: layout.height, width: layout.width });
1862
- }
1863
- if (maintainScrollAtEnd === true || maintainScrollAtEnd.onLayout) {
1864
- doMaintainScrollAtEnd(ctx, state, false);
1865
- }
1866
- updateAlignItemsPaddingTop(ctx, state);
1867
- checkAtBottom(ctx, state);
1868
- checkAtTop(state);
1869
- if (state) {
1870
- state.needsOtherAxisSize = otherAxisSize - (state.props.stylePaddingTop || 0) < 10;
1871
- }
1872
- if (__DEV__ && scrollLength === 0) {
1873
- warnDevOnce(
1874
- "height0",
1875
- `List ${state.props.horizontal ? "width" : "height"} is 0. You may need to set a style or \`flex: \` for the list, because children are absolutely positioned.`
1876
- );
2220
+ const didChange = scrollLength !== state.scrollLength || otherAxisSize !== prevOtherAxisSize;
2221
+ if (didChange) {
2222
+ state.scrollLength = scrollLength;
2223
+ state.otherAxisSize = otherAxisSize;
2224
+ state.lastBatchingAction = Date.now();
2225
+ state.scrollForNextCalculateItemsInView = void 0;
2226
+ doInitialAllocateContainers(ctx, state);
2227
+ if (needsCalculate) {
2228
+ calculateItemsInView(ctx, state, { doMVCP: true });
2229
+ }
2230
+ if (didChange || otherAxisSize !== prevOtherAxisSize) {
2231
+ set$(ctx, "scrollSize", { height: layout.height, width: layout.width });
2232
+ }
2233
+ if (maintainScrollAtEnd === true || maintainScrollAtEnd.onLayout) {
2234
+ doMaintainScrollAtEnd(ctx, state, false);
2235
+ }
2236
+ updateAlignItemsPaddingTop(ctx, state);
2237
+ checkAtBottom(ctx, state);
2238
+ checkAtTop(state);
2239
+ if (state) {
2240
+ state.needsOtherAxisSize = otherAxisSize - (state.props.stylePaddingTop || 0) < 10;
2241
+ }
2242
+ if (__DEV__ && scrollLength === 0) {
2243
+ warnDevOnce(
2244
+ "height0",
2245
+ `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.`
2246
+ );
2247
+ }
2248
+ setCanRender(true);
1877
2249
  }
1878
- setCanRender(true);
1879
2250
  }
1880
2251
 
1881
2252
  // src/core/onScroll.ts
1882
2253
  function onScroll(ctx, state, event) {
1883
- var _a, _b, _c, _d, _e;
2254
+ var _a, _b, _c;
2255
+ const {
2256
+ scrollProcessingEnabled,
2257
+ props: { onScroll: onScrollProp }
2258
+ } = state;
2259
+ if (scrollProcessingEnabled === false) {
2260
+ return;
2261
+ }
1884
2262
  if (((_b = (_a = event.nativeEvent) == null ? void 0 : _a.contentSize) == null ? void 0 : _b.height) === 0 && ((_c = event.nativeEvent.contentSize) == null ? void 0 : _c.width) === 0) {
1885
2263
  return;
1886
2264
  }
@@ -1894,15 +2272,16 @@ function onScroll(ctx, state, event) {
1894
2272
  }
1895
2273
  state.scrollPending = newScroll;
1896
2274
  updateScroll(ctx, state, newScroll);
1897
- (_e = (_d = state.props).onScroll) == null ? void 0 : _e.call(_d, event);
2275
+ onScrollProp == null ? void 0 : onScrollProp(event);
1898
2276
  }
1899
2277
  function updateScroll(ctx, state, newScroll) {
1900
2278
  const scrollingTo = state.scrollingTo;
1901
2279
  state.hasScrolled = true;
1902
2280
  state.lastBatchingAction = Date.now();
1903
- const currentTime = performance.now();
2281
+ const currentTime = Date.now();
1904
2282
  if (scrollingTo === void 0 && !(state.scrollHistory.length === 0 && newScroll === state.scroll)) {
1905
- state.scrollHistory.push({ scroll: newScroll, time: currentTime });
2283
+ const adjust = state.scrollAdjustHandler.getAdjust();
2284
+ state.scrollHistory.push({ scroll: newScroll - adjust, time: currentTime });
1906
2285
  }
1907
2286
  if (state.scrollHistory.length > 5) {
1908
2287
  state.scrollHistory.shift();
@@ -1911,9 +2290,11 @@ function updateScroll(ctx, state, newScroll) {
1911
2290
  state.scrollPrevTime = state.scrollTime;
1912
2291
  state.scroll = newScroll;
1913
2292
  state.scrollTime = currentTime;
1914
- calculateItemsInView(ctx, state);
1915
- checkAtBottom(ctx, state);
1916
- checkAtTop(state);
2293
+ if (Math.abs(state.scroll - state.scrollPrev) > 2) {
2294
+ calculateItemsInView(ctx, state);
2295
+ checkAtBottom(ctx, state);
2296
+ checkAtTop(state);
2297
+ }
1917
2298
  }
1918
2299
 
1919
2300
  // src/core/ScrollAdjustHandler.ts
@@ -1936,13 +2317,19 @@ var ScrollAdjustHandler = class {
1936
2317
  setMounted() {
1937
2318
  this.mounted = true;
1938
2319
  }
2320
+ getAdjust() {
2321
+ return this.appliedAdjust;
2322
+ }
1939
2323
  };
1940
2324
 
1941
2325
  // src/core/updateItemSize.ts
1942
- function updateItemSizes(ctx, state, itemUpdates) {
1943
- var _a;
2326
+ function updateItemSize(ctx, state, itemKey, sizeObj) {
2327
+ var _a, _b;
1944
2328
  const {
2329
+ sizesKnown,
1945
2330
  props: {
2331
+ getFixedItemSize,
2332
+ getItemType,
1946
2333
  horizontal,
1947
2334
  maintainVisibleContentPosition,
1948
2335
  suggestEstimatedItemSize,
@@ -1952,47 +2339,60 @@ function updateItemSizes(ctx, state, itemUpdates) {
1952
2339
  }
1953
2340
  } = state;
1954
2341
  if (!data) return;
2342
+ if (getFixedItemSize) {
2343
+ const index2 = state.indexByKey.get(itemKey);
2344
+ if (index2 === void 0) {
2345
+ return;
2346
+ }
2347
+ const itemData = state.props.data[index2];
2348
+ if (itemData === void 0) {
2349
+ return;
2350
+ }
2351
+ const type = getItemType ? (_a = getItemType(itemData, index2)) != null ? _a : "" : "";
2352
+ const size2 = getFixedItemSize(index2, itemData, type);
2353
+ if (size2 !== void 0 && size2 === sizesKnown.get(itemKey)) {
2354
+ return;
2355
+ }
2356
+ }
1955
2357
  const containersDidLayout = peek$(ctx, "containersDidLayout");
1956
2358
  let needsRecalculate = !containersDidLayout;
1957
2359
  let shouldMaintainScrollAtEnd = false;
1958
2360
  let minIndexSizeChanged;
1959
2361
  let maxOtherAxisSize = peek$(ctx, "otherAxisSize") || 0;
1960
- for (const { itemKey, sizeObj } of itemUpdates) {
1961
- const index = state.indexByKey.get(itemKey);
1962
- const prevSizeKnown = state.sizesKnown.get(itemKey);
1963
- const diff = updateOneItemSize(state, itemKey, sizeObj);
1964
- const size = Math.floor((horizontal ? sizeObj.width : sizeObj.height) * 8) / 8;
1965
- if (diff !== 0) {
1966
- minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
1967
- if (((_a = state.scrollingTo) == null ? void 0 : _a.viewPosition) && maintainVisibleContentPosition && index === state.scrollingTo.index && diff > 0) {
1968
- requestAdjust(ctx, state, diff * state.scrollingTo.viewPosition);
1969
- }
1970
- const { startBuffered, endBuffered } = state;
1971
- needsRecalculate || (needsRecalculate = index >= startBuffered && index <= endBuffered);
1972
- if (!needsRecalculate) {
1973
- const numContainers = ctx.values.get("numContainers");
1974
- for (let i = 0; i < numContainers; i++) {
1975
- if (peek$(ctx, `containerItemKey${i}`) === itemKey) {
1976
- needsRecalculate = true;
1977
- break;
1978
- }
2362
+ const index = state.indexByKey.get(itemKey);
2363
+ const prevSizeKnown = state.sizesKnown.get(itemKey);
2364
+ const diff = updateOneItemSize(state, itemKey, sizeObj);
2365
+ const size = Math.floor((horizontal ? sizeObj.width : sizeObj.height) * 8) / 8;
2366
+ if (diff !== 0) {
2367
+ minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
2368
+ if (((_b = state.scrollingTo) == null ? void 0 : _b.viewPosition) && maintainVisibleContentPosition && index === state.scrollingTo.index && diff > 0) {
2369
+ requestAdjust(ctx, state, diff * state.scrollingTo.viewPosition);
2370
+ }
2371
+ const { startBuffered, endBuffered } = state;
2372
+ needsRecalculate || (needsRecalculate = index >= startBuffered && index <= endBuffered);
2373
+ if (!needsRecalculate) {
2374
+ const numContainers = ctx.values.get("numContainers");
2375
+ for (let i = 0; i < numContainers; i++) {
2376
+ if (peek$(ctx, `containerItemKey${i}`) === itemKey) {
2377
+ needsRecalculate = true;
2378
+ break;
1979
2379
  }
1980
2380
  }
1981
- if (state.needsOtherAxisSize) {
1982
- const otherAxisSize = horizontal ? sizeObj.height : sizeObj.width;
1983
- maxOtherAxisSize = Math.max(maxOtherAxisSize, otherAxisSize);
1984
- }
1985
- if (prevSizeKnown !== void 0 && Math.abs(prevSizeKnown - size) > 5) {
1986
- shouldMaintainScrollAtEnd = true;
1987
- }
1988
- onItemSizeChanged == null ? void 0 : onItemSizeChanged({
1989
- index,
1990
- itemData: state.props.data[index],
1991
- itemKey,
1992
- previous: size - diff,
1993
- size
1994
- });
1995
2381
  }
2382
+ if (state.needsOtherAxisSize) {
2383
+ const otherAxisSize = horizontal ? sizeObj.height : sizeObj.width;
2384
+ maxOtherAxisSize = Math.max(maxOtherAxisSize, otherAxisSize);
2385
+ }
2386
+ if (prevSizeKnown !== void 0 && Math.abs(prevSizeKnown - size) > 5) {
2387
+ shouldMaintainScrollAtEnd = true;
2388
+ }
2389
+ onItemSizeChanged == null ? void 0 : onItemSizeChanged({
2390
+ index,
2391
+ itemData: state.props.data[index],
2392
+ itemKey,
2393
+ previous: size - diff,
2394
+ size
2395
+ });
1996
2396
  }
1997
2397
  if (minIndexSizeChanged !== void 0) {
1998
2398
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
@@ -2025,43 +2425,29 @@ function updateItemSizes(ctx, state, itemUpdates) {
2025
2425
  }
2026
2426
  }
2027
2427
  }
2028
- function updateItemSize(ctx, state, itemKey, sizeObj) {
2029
- const { queuedItemSizeUpdates, queuedItemSizeUpdatesWaiting } = state;
2030
- const containersDidLayout = peek$(ctx, "containersDidLayout");
2031
- if (!containersDidLayout || !queuedItemSizeUpdatesWaiting) {
2032
- updateItemSizes(ctx, state, [{ itemKey, sizeObj }]);
2033
- if (containersDidLayout) {
2034
- state.queuedItemSizeUpdatesWaiting = true;
2035
- requestAnimationFrame(() => {
2036
- state.queuedItemSizeUpdatesWaiting = false;
2037
- updateItemSizes(ctx, state, queuedItemSizeUpdates);
2038
- queuedItemSizeUpdates.length = 0;
2039
- });
2040
- }
2041
- } else {
2042
- queuedItemSizeUpdates.push({ itemKey, sizeObj });
2043
- }
2044
- }
2045
2428
  function updateOneItemSize(state, itemKey, sizeObj) {
2429
+ var _a;
2046
2430
  const {
2047
2431
  sizes,
2048
2432
  indexByKey,
2049
2433
  sizesKnown,
2050
2434
  averageSizes,
2051
- props: { data, horizontal }
2435
+ props: { data, horizontal, getEstimatedItemSize, getItemType, getFixedItemSize }
2052
2436
  } = state;
2053
2437
  if (!data) return 0;
2054
2438
  const index = indexByKey.get(itemKey);
2055
2439
  const prevSize = getItemSize(state, itemKey, index, data);
2056
2440
  const size = Math.floor((horizontal ? sizeObj.width : sizeObj.height) * 8) / 8;
2057
2441
  sizesKnown.set(itemKey, size);
2058
- const itemType = "";
2059
- let averages = averageSizes[itemType];
2060
- if (!averages) {
2061
- averages = averageSizes[itemType] = { avg: 0, num: 0 };
2442
+ if (!getEstimatedItemSize && !getFixedItemSize && size > 0) {
2443
+ const itemType = getItemType ? (_a = getItemType(data[index], index)) != null ? _a : "" : "";
2444
+ let averages = averageSizes[itemType];
2445
+ if (!averages) {
2446
+ averages = averageSizes[itemType] = { avg: 0, num: 0 };
2447
+ }
2448
+ averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
2449
+ averages.num++;
2062
2450
  }
2063
- averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
2064
- averages.num++;
2065
2451
  if (!prevSize || Math.abs(prevSize - size) > 0.1) {
2066
2452
  sizes.set(itemKey, size);
2067
2453
  return size - prevSize;
@@ -2099,86 +2485,165 @@ function createColumnWrapperStyle(contentContainerStyle) {
2099
2485
  }
2100
2486
  }
2101
2487
  function getRenderedItem(ctx, state, key) {
2488
+ var _a;
2102
2489
  if (!state) {
2103
2490
  return null;
2104
2491
  }
2105
2492
  const {
2106
2493
  indexByKey,
2107
- props: { data, renderItem: renderItem2 }
2494
+ props: { data, getItemType, renderItem }
2108
2495
  } = state;
2109
2496
  const index = indexByKey.get(key);
2110
2497
  if (index === void 0) {
2111
2498
  return null;
2112
2499
  }
2113
2500
  let renderedItem = null;
2114
- if (renderItem2) {
2501
+ if (renderItem && data[index]) {
2115
2502
  const itemProps = {
2503
+ data,
2116
2504
  extraData: peek$(ctx, "extraData"),
2117
2505
  index,
2118
- item: data[index]
2506
+ item: data[index],
2507
+ type: getItemType ? (_a = getItemType(data[index], index)) != null ? _a : "" : ""
2119
2508
  };
2120
- renderedItem = React3__namespace.default.createElement(renderItem2, itemProps);
2509
+ renderedItem = isFunction(renderItem) ? renderItem(itemProps) : React3__namespace.default.createElement(renderItem, itemProps);
2121
2510
  }
2122
2511
  return { index, item: data[index], renderedItem };
2123
2512
  }
2124
2513
 
2514
+ // src/utils/throttledOnScroll.ts
2515
+ function useThrottledOnScroll(originalHandler, scrollEventThrottle) {
2516
+ const throttle = useThrottleDebounce("throttle");
2517
+ return (event) => throttle(originalHandler, scrollEventThrottle, { nativeEvent: event.nativeEvent });
2518
+ }
2519
+
2520
+ // src/utils/updateAveragesOnDataChange.ts
2521
+ function updateAveragesOnDataChange(state, oldData, newData) {
2522
+ var _a;
2523
+ const {
2524
+ averageSizes,
2525
+ sizesKnown,
2526
+ indexByKey,
2527
+ props: { itemsAreEqual, getItemType, keyExtractor }
2528
+ } = state;
2529
+ if (!itemsAreEqual || !oldData.length || !newData.length) {
2530
+ for (const key in averageSizes) {
2531
+ delete averageSizes[key];
2532
+ }
2533
+ return;
2534
+ }
2535
+ const itemTypesToPreserve = {};
2536
+ const newDataLength = newData.length;
2537
+ const oldDataLength = oldData.length;
2538
+ for (let newIndex = 0; newIndex < newDataLength; newIndex++) {
2539
+ const newItem = newData[newIndex];
2540
+ const id = keyExtractor ? keyExtractor(newItem, newIndex) : String(newIndex);
2541
+ const oldIndex = indexByKey.get(id);
2542
+ if (oldIndex !== void 0 && oldIndex < oldDataLength) {
2543
+ const knownSize = sizesKnown.get(id);
2544
+ if (knownSize === void 0) continue;
2545
+ const oldItem = oldData[oldIndex];
2546
+ const areEqual = itemsAreEqual(oldItem, newItem, newIndex, newData);
2547
+ if (areEqual) {
2548
+ const itemType = getItemType ? (_a = getItemType(newItem, newIndex)) != null ? _a : "" : "";
2549
+ let typeData = itemTypesToPreserve[itemType];
2550
+ if (!typeData) {
2551
+ typeData = itemTypesToPreserve[itemType] = { count: 0, totalSize: 0 };
2552
+ }
2553
+ typeData.totalSize += knownSize;
2554
+ typeData.count++;
2555
+ }
2556
+ }
2557
+ }
2558
+ for (const key in averageSizes) {
2559
+ delete averageSizes[key];
2560
+ }
2561
+ for (const itemType in itemTypesToPreserve) {
2562
+ const { totalSize, count } = itemTypesToPreserve[itemType];
2563
+ if (count > 0) {
2564
+ averageSizes[itemType] = {
2565
+ avg: totalSize / count,
2566
+ num: count
2567
+ };
2568
+ }
2569
+ }
2570
+ }
2571
+
2125
2572
  // src/components/LegendList.tsx
2126
2573
  var DEFAULT_DRAW_DISTANCE = 250;
2127
2574
  var DEFAULT_ITEM_SIZE = 100;
2128
- var LegendList = typedForwardRef(function LegendList2(props, forwardedRef) {
2129
- return /* @__PURE__ */ React3__namespace.createElement(StateProvider, null, /* @__PURE__ */ React3__namespace.createElement(LegendListInner, { ...props, ref: forwardedRef }));
2130
- });
2575
+ var LegendList = typedMemo(
2576
+ typedForwardRef(function LegendList2(props, forwardedRef) {
2577
+ const { children, data: dataProp, renderItem: renderItemProp, ...restProps } = props;
2578
+ const isChildrenMode = children !== void 0 && dataProp === void 0;
2579
+ const processedProps = isChildrenMode ? {
2580
+ ...restProps,
2581
+ data: (isArray(children) ? children : React3__namespace.Children.toArray(children)).flat(1),
2582
+ renderItem: ({ item }) => item
2583
+ } : {
2584
+ ...restProps,
2585
+ data: dataProp || [],
2586
+ renderItem: renderItemProp
2587
+ };
2588
+ return /* @__PURE__ */ React3__namespace.createElement(StateProvider, null, /* @__PURE__ */ React3__namespace.createElement(LegendListInner, { ...processedProps, ref: forwardedRef }));
2589
+ })
2590
+ );
2131
2591
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
2132
2592
  var _a;
2133
2593
  const {
2134
- data: dataProp = [],
2135
- initialScrollIndex: initialScrollIndexProp,
2136
- initialScrollOffset,
2137
- horizontal,
2138
- drawDistance = 250,
2139
- recycleItems = false,
2140
- onEndReachedThreshold = 0.5,
2141
- onStartReachedThreshold = 0.5,
2142
- maintainScrollAtEnd = false,
2143
- maintainScrollAtEndThreshold = 0.1,
2144
2594
  alignItemsAtEnd = false,
2145
- maintainVisibleContentPosition = false,
2146
- onScroll: onScrollProp,
2147
- onMomentumScrollEnd,
2148
- numColumns: numColumnsProp = 1,
2149
2595
  columnWrapperStyle,
2150
- keyExtractor: keyExtractorProp,
2151
- renderItem: renderItem2,
2152
- estimatedListSize,
2596
+ contentContainerStyle: contentContainerStyleProp,
2597
+ data: dataProp = [],
2598
+ drawDistance = 250,
2599
+ enableAverages = true,
2153
2600
  estimatedItemSize: estimatedItemSizeProp,
2601
+ estimatedListSize,
2602
+ extraData,
2154
2603
  getEstimatedItemSize,
2155
- suggestEstimatedItemSize,
2156
- ListHeaderComponent,
2604
+ getFixedItemSize,
2605
+ getItemType,
2606
+ horizontal,
2607
+ initialContainerPoolRatio = 2,
2608
+ initialScrollIndex: initialScrollIndexProp,
2609
+ initialScrollOffset: initialScrollOffsetProp,
2610
+ itemsAreEqual,
2611
+ keyExtractor: keyExtractorProp,
2157
2612
  ListEmptyComponent,
2613
+ ListHeaderComponent,
2614
+ maintainScrollAtEnd = false,
2615
+ maintainScrollAtEndThreshold = 0.1,
2616
+ maintainVisibleContentPosition = true,
2617
+ numColumns: numColumnsProp = 1,
2618
+ onEndReached,
2619
+ onEndReachedThreshold = 0.5,
2158
2620
  onItemSizeChanged,
2159
- refScrollView,
2160
- waitForInitialLayout = true,
2161
- extraData,
2162
- contentContainerStyle: contentContainerStyleProp,
2163
- style: styleProp,
2164
2621
  onLayout: onLayoutProp,
2622
+ onLoad,
2623
+ onMomentumScrollEnd,
2165
2624
  onRefresh,
2166
- refreshing,
2625
+ onScroll: onScrollProp,
2626
+ onStartReached,
2627
+ onStartReachedThreshold = 0.5,
2628
+ onViewableItemsChanged,
2167
2629
  progressViewOffset,
2630
+ recycleItems = false,
2168
2631
  refreshControl,
2169
- initialContainerPoolRatio = 2,
2632
+ refreshing,
2633
+ refScrollView,
2634
+ renderItem,
2635
+ scrollEventThrottle,
2636
+ snapToIndices,
2637
+ stickyIndices,
2638
+ style: styleProp,
2639
+ suggestEstimatedItemSize,
2170
2640
  viewabilityConfig,
2171
2641
  viewabilityConfigCallbackPairs,
2172
- snapToIndices,
2173
- onViewableItemsChanged,
2174
- onStartReached,
2175
- onEndReached,
2176
- onLoad,
2642
+ waitForInitialLayout = true,
2177
2643
  ...rest
2178
2644
  } = props;
2179
2645
  const [renderNum, setRenderNum] = React3.useState(0);
2180
- const initialScroll = typeof initialScrollIndexProp === "number" ? { index: initialScrollIndexProp } : initialScrollIndexProp;
2181
- const initialScrollIndex = initialScroll == null ? void 0 : initialScroll.index;
2646
+ const initialScroll = initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? { index: initialScrollIndexProp.index || 0, viewOffset: initialScrollIndexProp.viewOffset || 0 } : { index: initialScrollIndexProp || 0, viewOffset: initialScrollOffsetProp || 0 } : void 0;
2182
2647
  const [canRender, setCanRender] = React3__namespace.useState(!IsNewArchitecture);
2183
2648
  const contentContainerStyle = { ...reactNative.StyleSheet.flatten(contentContainerStyleProp) };
2184
2649
  const style = { ...reactNative.StyleSheet.flatten(styleProp) };
@@ -2195,9 +2660,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2195
2660
  if (!refState.current) {
2196
2661
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : IsNewArchitecture ? { height: 0, width: 0 } : reactNative.Dimensions.get("window"))[horizontal ? "width" : "height"];
2197
2662
  refState.current = {
2663
+ activeStickyIndex: void 0,
2198
2664
  averageSizes: {},
2199
2665
  columns: /* @__PURE__ */ new Map(),
2200
2666
  containerItemKeys: /* @__PURE__ */ new Set(),
2667
+ containerItemTypes: /* @__PURE__ */ new Map(),
2201
2668
  enableScrollForNextCalculateItemsInView: true,
2202
2669
  endBuffered: -1,
2203
2670
  endNoBuffer: -1,
@@ -2216,11 +2683,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2216
2683
  loadStartTime: Date.now(),
2217
2684
  minIndexSizeChanged: 0,
2218
2685
  nativeMarginTop: 0,
2219
- pendingAdjust: 0,
2220
2686
  positions: /* @__PURE__ */ new Map(),
2221
2687
  props: {},
2222
2688
  queuedCalculateItemsInView: 0,
2223
- queuedItemSizeUpdates: [],
2224
2689
  refScroller: void 0,
2225
2690
  scroll: 0,
2226
2691
  scrollAdjustHandler: new ScrollAdjustHandler(ctx),
@@ -2230,12 +2695,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2230
2695
  scrollPending: 0,
2231
2696
  scrollPrev: 0,
2232
2697
  scrollPrevTime: 0,
2698
+ scrollProcessingEnabled: true,
2233
2699
  scrollTime: 0,
2234
2700
  sizes: /* @__PURE__ */ new Map(),
2235
2701
  sizesKnown: /* @__PURE__ */ new Map(),
2236
2702
  startBuffered: -1,
2237
2703
  startNoBuffer: -1,
2238
2704
  startReachedBlockedByTimer: false,
2705
+ stickyContainerPool: /* @__PURE__ */ new Set(),
2706
+ stickyContainers: /* @__PURE__ */ new Map(),
2239
2707
  timeoutSizeMessage: 0,
2240
2708
  timeouts: /* @__PURE__ */ new Set(),
2241
2709
  totalSize: 0,
@@ -2247,14 +2715,19 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2247
2715
  const state = refState.current;
2248
2716
  const isFirst = !state.props.renderItem;
2249
2717
  const didDataChange = state.props.data !== dataProp;
2718
+ const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
2250
2719
  state.props = {
2251
2720
  alignItemsAtEnd,
2252
2721
  data: dataProp,
2722
+ enableAverages,
2253
2723
  estimatedItemSize,
2254
2724
  getEstimatedItemSize,
2725
+ getFixedItemSize,
2726
+ getItemType,
2255
2727
  horizontal: !!horizontal,
2256
2728
  initialContainerPoolRatio,
2257
2729
  initialScroll,
2730
+ itemsAreEqual,
2258
2731
  keyExtractor,
2259
2732
  maintainScrollAtEnd,
2260
2733
  maintainScrollAtEndThreshold,
@@ -2264,12 +2737,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2264
2737
  onEndReachedThreshold,
2265
2738
  onItemSizeChanged,
2266
2739
  onLoad,
2267
- onScroll: onScrollProp,
2740
+ onScroll: throttleScrollFn,
2268
2741
  onStartReached,
2269
2742
  onStartReachedThreshold,
2270
- renderItem: renderItem2,
2743
+ recycleItems: !!recycleItems,
2744
+ renderItem,
2271
2745
  scrollBuffer,
2272
2746
  snapToIndices,
2747
+ stickyIndicesArr: stickyIndices != null ? stickyIndices : [],
2748
+ stickyIndicesSet: React3.useMemo(() => new Set(stickyIndices != null ? stickyIndices : []), [stickyIndices == null ? void 0 : stickyIndices.join(",")]),
2273
2749
  stylePaddingBottom: stylePaddingBottomState,
2274
2750
  stylePaddingTop: stylePaddingTopState,
2275
2751
  suggestEstimatedItemSize: !!suggestEstimatedItemSize
@@ -2278,6 +2754,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2278
2754
  const checkResetContainers = (isFirst2) => {
2279
2755
  const state2 = refState.current;
2280
2756
  if (state2) {
2757
+ if (!isFirst2 && state2.props.data !== dataProp) {
2758
+ updateAveragesOnDataChange(state2, state2.props.data, dataProp);
2759
+ }
2281
2760
  state2.props.data = dataProp;
2282
2761
  if (!isFirst2) {
2283
2762
  calculateItemsInView(ctx, state2, { dataChanged: true, doMVCP: true });
@@ -2319,12 +2798,19 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2319
2798
  updateAllPositions(ctx, state);
2320
2799
  }
2321
2800
  const initialContentOffset = React3.useMemo(() => {
2322
- const initialContentOffset2 = initialScrollOffset || calculateOffsetForIndex(ctx, state, initialScrollIndex);
2323
- refState.current.isStartReached = initialContentOffset2 < refState.current.scrollLength * onStartReachedThreshold;
2324
- if (initialContentOffset2 > 0) {
2325
- scrollTo(state, { animated: false, index: initialScrollIndex, offset: initialContentOffset2 });
2801
+ if (initialScroll) {
2802
+ const { index, viewOffset } = initialScroll;
2803
+ let initialContentOffset2 = viewOffset || 0;
2804
+ if (index !== void 0) {
2805
+ initialContentOffset2 += calculateOffsetForIndex(ctx, state, index);
2806
+ }
2807
+ refState.current.isStartReached = initialContentOffset2 < refState.current.scrollLength * onStartReachedThreshold;
2808
+ if (initialContentOffset2 > 0) {
2809
+ scrollTo(state, { animated: false, index, offset: initialContentOffset2 });
2810
+ }
2811
+ return initialContentOffset2;
2326
2812
  }
2327
- return initialContentOffset2;
2813
+ return 0;
2328
2814
  }, [renderNum]);
2329
2815
  if (isFirst || didDataChange || numColumnsProp !== peek$(ctx, "numColumns")) {
2330
2816
  refState.current.lastBatchingAction = Date.now();
@@ -2337,27 +2823,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2337
2823
  refState.current.positions.clear();
2338
2824
  }
2339
2825
  }
2340
- React3.useLayoutEffect(() => {
2341
- if (IsNewArchitecture) {
2342
- let measured;
2343
- refScroller.current.measure((x, y, width, height) => {
2344
- measured = { height, width, x, y };
2345
- });
2346
- if (measured) {
2347
- const size = Math.floor(measured[horizontal ? "width" : "height"] * 8) / 8;
2348
- if (size) {
2349
- handleLayout(ctx, state, measured, setCanRender);
2350
- }
2351
- }
2352
- }
2353
- if (!isFirst) {
2354
- calculateItemsInView(ctx, state, { doMVCP: true });
2355
- }
2356
- }, [dataProp]);
2357
2826
  const onLayoutHeader = React3.useCallback((rect, fromLayoutEffect) => {
2358
2827
  const size = rect[horizontal ? "width" : "height"];
2359
2828
  set$(ctx, "headerSize", size);
2360
- if (initialScroll) {
2829
+ if ((initialScroll == null ? void 0 : initialScroll.index) !== void 0) {
2361
2830
  if (IsNewArchitecture && reactNative.Platform.OS !== "android") {
2362
2831
  if (fromLayoutEffect) {
2363
2832
  setRenderNum((v) => v + 1);
@@ -2375,7 +2844,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2375
2844
  }
2376
2845
  }, [snapToIndices]);
2377
2846
  React3.useLayoutEffect(() => {
2378
- const didAllocateContainers = doInitialAllocateContainersCallback();
2847
+ const didAllocateContainers = dataProp.length > 0 && doInitialAllocateContainersCallback();
2379
2848
  if (!didAllocateContainers) {
2380
2849
  checkResetContainers(
2381
2850
  /*isFirst*/
@@ -2386,6 +2855,20 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2386
2855
  React3.useLayoutEffect(() => {
2387
2856
  set$(ctx, "extraData", extraData);
2388
2857
  }, [extraData]);
2858
+ React3.useLayoutEffect(() => {
2859
+ if (IsNewArchitecture) {
2860
+ let measured;
2861
+ refScroller.current.measure((x, y, width, height) => {
2862
+ measured = { height, width, x, y };
2863
+ });
2864
+ if (measured) {
2865
+ const size = Math.floor(measured[horizontal ? "width" : "height"] * 8) / 8;
2866
+ if (size) {
2867
+ handleLayout(ctx, state, measured, setCanRender);
2868
+ }
2869
+ }
2870
+ }
2871
+ }, []);
2389
2872
  React3.useLayoutEffect(initializeStateVars, [
2390
2873
  memoizedLastItemKeys.join(","),
2391
2874
  numColumnsProp,
@@ -2441,10 +2924,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2441
2924
  const state2 = refState.current;
2442
2925
  return state2 ? {
2443
2926
  contentLength: state2.totalSize,
2927
+ data: state2.props.data,
2444
2928
  end: state2.endNoBuffer,
2445
2929
  endBuffered: state2.endBuffered,
2446
2930
  isAtEnd: state2.isAtEnd,
2447
2931
  isAtStart: state2.isAtStart,
2932
+ positionAtIndex: (index) => state2.positions.get(getId(state2, index)),
2448
2933
  positions: state2.positions,
2449
2934
  scroll: state2.scroll,
2450
2935
  scrollLength: state2.scrollLength,
@@ -2471,7 +2956,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2471
2956
  const footerSize = peek$(ctx, "footerSize") || 0;
2472
2957
  scrollToIndex(ctx, state, {
2473
2958
  index,
2474
- viewOffset: -paddingBottom - footerSize,
2959
+ viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
2475
2960
  viewPosition: 1,
2476
2961
  ...options
2477
2962
  });
@@ -2486,6 +2971,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2486
2971
  }
2487
2972
  },
2488
2973
  scrollToOffset: (params) => scrollTo(state, params),
2974
+ setScrollProcessingEnabled: (enabled) => {
2975
+ refState.current.scrollProcessingEnabled = enabled;
2976
+ },
2489
2977
  setVisibleContentAnchorOffset: (value) => {
2490
2978
  const val = typeof value === "function" ? value(peek$(ctx, "scrollAdjustUserOffset") || 0) : value;
2491
2979
  set$(ctx, "scrollAdjustUserOffset", val);
@@ -2507,6 +2995,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2507
2995
  }),
2508
2996
  []
2509
2997
  );
2998
+ const onScrollHandler = React3.useMemo(() => {
2999
+ const onScrollFn = fns.onScroll;
3000
+ if (stickyIndices == null ? void 0 : stickyIndices.length) {
3001
+ const { animatedScrollY } = ctx;
3002
+ return reactNative.Animated.event([{ nativeEvent: { contentOffset: { [horizontal ? "x" : "y"]: animatedScrollY } } }], {
3003
+ listener: onScrollFn,
3004
+ useNativeDriver: true
3005
+ });
3006
+ }
3007
+ return onScrollFn;
3008
+ }, [stickyIndices == null ? void 0 : stickyIndices.length, horizontal, scrollEventThrottle]);
2510
3009
  return /* @__PURE__ */ React3__namespace.createElement(React3__namespace.Fragment, null, /* @__PURE__ */ React3__namespace.createElement(
2511
3010
  ListComponent,
2512
3011
  {
@@ -2523,14 +3022,20 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2523
3022
  onLayout,
2524
3023
  onLayoutHeader,
2525
3024
  onMomentumScrollEnd: (event) => {
2526
- requestAnimationFrame(() => {
2527
- finishScrollTo(refState.current);
2528
- });
3025
+ if (IsNewArchitecture) {
3026
+ requestAnimationFrame(() => {
3027
+ finishScrollTo(refState.current);
3028
+ });
3029
+ } else {
3030
+ setTimeout(() => {
3031
+ finishScrollTo(refState.current);
3032
+ }, 1e3);
3033
+ }
2529
3034
  if (onMomentumScrollEnd) {
2530
3035
  onMomentumScrollEnd(event);
2531
3036
  }
2532
3037
  },
2533
- onScroll: fns.onScroll,
3038
+ onScroll: onScrollHandler,
2534
3039
  recycleItems,
2535
3040
  refreshControl: refreshControl ? stylePaddingTopState > 0 ? React3__namespace.cloneElement(refreshControl, {
2536
3041
  progressViewOffset: (refreshControl.props.progressViewOffset || 0) + stylePaddingTopState
@@ -2546,6 +3051,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2546
3051
  scrollAdjustHandler: (_a = refState.current) == null ? void 0 : _a.scrollAdjustHandler,
2547
3052
  scrollEventThrottle: reactNative.Platform.OS === "web" ? 16 : void 0,
2548
3053
  snapToIndices,
3054
+ stickyIndices,
2549
3055
  style,
2550
3056
  updateItemSize: fns.updateItemSize,
2551
3057
  waitForInitialLayout
@@ -2553,24 +3059,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
2553
3059
  ), __DEV__ && ENABLE_DEBUG_VIEW && /* @__PURE__ */ React3__namespace.createElement(DebugView, { state: refState.current }));
2554
3060
  });
2555
3061
 
2556
- // src/components/LazyLegendList.tsx
2557
- var typedForwardRef2 = React3.forwardRef;
2558
- var renderItem = ({ item }) => item;
2559
- var LazyLegendList = typedForwardRef2(function LazyLegendList2(props, forwardedRef) {
2560
- const { LegendList: LegendListProp, children, ...rest } = props;
2561
- const LegendListComponent = LegendListProp != null ? LegendListProp : LegendList;
2562
- const data = (isArray(children) ? children : React3__namespace.Children.toArray(children)).flat(1);
2563
- return (
2564
- // @ts-expect-error TODO: Fix this type
2565
- /* @__PURE__ */ React3__namespace.createElement(LegendListComponent, { ...rest, data, ref: forwardedRef, renderItem })
2566
- );
2567
- });
2568
-
2569
- exports.LazyLegendList = LazyLegendList;
2570
3062
  exports.LegendList = LegendList;
2571
3063
  exports.useIsLastItem = useIsLastItem;
2572
3064
  exports.useListScrollSize = useListScrollSize;
2573
3065
  exports.useRecyclingEffect = useRecyclingEffect;
2574
3066
  exports.useRecyclingState = useRecyclingState;
3067
+ exports.useSyncLayout = useSyncLayout;
2575
3068
  exports.useViewability = useViewability;
2576
3069
  exports.useViewabilityAmount = useViewabilityAmount;