@legendapp/list 3.0.0-beta.2 → 3.0.0-beta.20

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.native.js CHANGED
@@ -28,30 +28,64 @@ var React2__namespace = /*#__PURE__*/_interopNamespace(React2);
28
28
  reactNative.Animated.View;
29
29
  var View = reactNative.View;
30
30
  var Text = reactNative.Text;
31
+
32
+ // src/state/getContentInsetEnd.ts
33
+ function getContentInsetEnd(state) {
34
+ var _a3;
35
+ const { props } = state;
36
+ const horizontal = props.horizontal;
37
+ let contentInset = props.contentInset;
38
+ if (!contentInset) {
39
+ const animatedInset = (_a3 = props.animatedProps) == null ? void 0 : _a3.contentInset;
40
+ if (animatedInset) {
41
+ if ("get" in animatedInset) {
42
+ contentInset = animatedInset.get();
43
+ } else {
44
+ contentInset = animatedInset;
45
+ }
46
+ }
47
+ }
48
+ return (horizontal ? contentInset == null ? void 0 : contentInset.right : contentInset == null ? void 0 : contentInset.bottom) || 0;
49
+ }
50
+
51
+ // src/state/getContentSize.ts
52
+ function getContentSize(ctx) {
53
+ var _a3;
54
+ const { values, state } = ctx;
55
+ const stylePaddingTop = values.get("stylePaddingTop") || 0;
56
+ const stylePaddingBottom = state.props.stylePaddingBottom || 0;
57
+ const headerSize = values.get("headerSize") || 0;
58
+ const footerSize = values.get("footerSize") || 0;
59
+ const contentInsetBottom = getContentInsetEnd(state);
60
+ const totalSize = (_a3 = state.pendingTotalSize) != null ? _a3 : values.get("totalSize");
61
+ return headerSize + footerSize + totalSize + stylePaddingTop + stylePaddingBottom + (contentInsetBottom || 0);
62
+ }
31
63
  var createAnimatedValue = (value) => new reactNative.Animated.Value(value);
32
64
 
33
65
  // src/state/state.tsx
34
66
  var ContextState = React2__namespace.createContext(null);
67
+ var contextNum = 0;
35
68
  function StateProvider({ children }) {
36
69
  const [value] = React2__namespace.useState(() => ({
37
70
  animatedScrollY: createAnimatedValue(0),
38
71
  columnWrapperStyle: void 0,
39
- internalState: void 0,
72
+ contextNum: contextNum++,
40
73
  listeners: /* @__PURE__ */ new Map(),
41
74
  mapViewabilityAmountCallbacks: /* @__PURE__ */ new Map(),
42
75
  mapViewabilityAmountValues: /* @__PURE__ */ new Map(),
43
76
  mapViewabilityCallbacks: /* @__PURE__ */ new Map(),
44
77
  mapViewabilityConfigStates: /* @__PURE__ */ new Map(),
45
78
  mapViewabilityValues: /* @__PURE__ */ new Map(),
79
+ positionListeners: /* @__PURE__ */ new Map(),
80
+ state: void 0,
46
81
  values: /* @__PURE__ */ new Map([
47
82
  ["alignItemsPaddingTop", 0],
48
83
  ["stylePaddingTop", 0],
49
84
  ["headerSize", 0],
50
85
  ["numContainers", 0],
51
- ["activeStickyIndex", void 0],
86
+ ["activeStickyIndex", -1],
52
87
  ["totalSize", 0],
53
- ["scrollAdjustPending", 0],
54
- ["scrollingTo", void 0]
88
+ ["scrollAdjustPending", 0]
55
89
  ]),
56
90
  viewRefs: /* @__PURE__ */ new Map()
57
91
  }));
@@ -119,15 +153,24 @@ function set$(ctx, signalName, value) {
119
153
  }
120
154
  }
121
155
  }
122
- function getContentSize(ctx) {
123
- var _a3, _b;
124
- const { values, internalState } = ctx;
125
- const stylePaddingTop = values.get("stylePaddingTop") || 0;
126
- const stylePaddingBottom = (internalState == null ? void 0 : internalState.props.stylePaddingBottom) || 0;
127
- const headerSize = values.get("headerSize") || 0;
128
- const footerSize = values.get("footerSize") || 0;
129
- const totalSize = (_b = (_a3 = ctx.internalState) == null ? void 0 : _a3.pendingTotalSize) != null ? _b : values.get("totalSize");
130
- return headerSize + footerSize + totalSize + stylePaddingTop + stylePaddingBottom;
156
+ function listenPosition$(ctx, key, cb) {
157
+ const { positionListeners } = ctx;
158
+ let setListeners = positionListeners.get(key);
159
+ if (!setListeners) {
160
+ setListeners = /* @__PURE__ */ new Set();
161
+ positionListeners.set(key, setListeners);
162
+ }
163
+ setListeners.add(cb);
164
+ return () => setListeners.delete(cb);
165
+ }
166
+ function notifyPosition$(ctx, key, value) {
167
+ const { positionListeners } = ctx;
168
+ const setListeners = positionListeners.get(key);
169
+ if (setListeners) {
170
+ for (const listener of setListeners) {
171
+ listener(value);
172
+ }
173
+ }
131
174
  }
132
175
  function useArr$(signalNames) {
133
176
  const ctx = React2__namespace.useContext(ContextState);
@@ -208,7 +251,8 @@ var ENABLE_DEBUG_VIEW = IS_DEV && false;
208
251
  // src/constants-platform.native.ts
209
252
  var IsNewArchitecture = global.nativeFabricUIManager != null;
210
253
  var useAnimatedValue = (initialValue) => {
211
- return React2.useRef(new reactNative.Animated.Value(initialValue)).current;
254
+ const [animAnimatedValue] = React2.useState(() => new reactNative.Animated.Value(initialValue));
255
+ return animAnimatedValue;
212
256
  };
213
257
 
214
258
  // src/utils/helpers.ts
@@ -242,6 +286,11 @@ function extractPadding(style, contentContainerStyle, type) {
242
286
  return getPadding(style, type) + getPadding(contentContainerStyle, type);
243
287
  }
244
288
  function findContainerId(ctx, key) {
289
+ var _a3, _b;
290
+ const directMatch = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.containerItemKeys) == null ? void 0 : _b.get(key);
291
+ if (directMatch !== void 0) {
292
+ return directMatch;
293
+ }
245
294
  const numContainers = peek$(ctx, "numContainers");
246
295
  for (let i = 0; i < numContainers; i++) {
247
296
  const itemKey = peek$(ctx, `containerItemKey${i}`);
@@ -295,9 +344,18 @@ function useValue$(key, params) {
295
344
  }
296
345
  var typedForwardRef = React2.forwardRef;
297
346
  var typedMemo = React2.memo;
347
+ var getComponent = (Component) => {
348
+ if (React2__namespace.isValidElement(Component)) {
349
+ return Component;
350
+ }
351
+ if (Component) {
352
+ return /* @__PURE__ */ React2__namespace.createElement(Component, null);
353
+ }
354
+ return null;
355
+ };
298
356
 
299
357
  // src/components/PositionView.native.tsx
300
- var PositionViewState = typedMemo(function PositionView({
358
+ var PositionViewState = typedMemo(function PositionViewState2({
301
359
  id,
302
360
  horizontal,
303
361
  style,
@@ -317,7 +375,7 @@ var PositionViewState = typedMemo(function PositionView({
317
375
  }
318
376
  );
319
377
  });
320
- var PositionViewAnimated = typedMemo(function PositionView2({
378
+ var PositionViewAnimated = typedMemo(function PositionViewAnimated2({
321
379
  id,
322
380
  horizontal,
323
381
  style,
@@ -343,77 +401,117 @@ var PositionViewSticky = typedMemo(function PositionViewSticky2({
343
401
  animatedScrollY,
344
402
  stickyOffset,
345
403
  index,
404
+ stickyHeaderConfig,
405
+ children,
346
406
  ...rest
347
407
  }) {
348
408
  const [position = POSITION_OUT_OF_VIEW, headerSize] = useArr$([`containerPosition${id}`, "headerSize"]);
349
409
  const transform = React2__namespace.useMemo(() => {
410
+ var _a3;
350
411
  if (animatedScrollY && stickyOffset !== void 0) {
412
+ const stickyConfigOffset = (_a3 = stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset) != null ? _a3 : 0;
351
413
  const stickyPosition = animatedScrollY.interpolate({
352
414
  extrapolateLeft: "clamp",
353
415
  extrapolateRight: "extend",
354
- inputRange: [position + headerSize, position + 5e3 + headerSize],
416
+ inputRange: [
417
+ position + headerSize - stickyConfigOffset - stickyOffset,
418
+ position + 5e3 + headerSize - stickyConfigOffset - stickyOffset
419
+ ],
355
420
  outputRange: [position, position + 5e3]
356
421
  });
357
422
  return horizontal ? [{ translateX: stickyPosition }] : [{ translateY: stickyPosition }];
358
423
  }
359
- }, [animatedScrollY, headerSize, horizontal, stickyOffset, position]);
424
+ }, [animatedScrollY, headerSize, horizontal, stickyOffset, position, stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset]);
360
425
  const viewStyle = React2__namespace.useMemo(() => [style, { zIndex: index + 1e3 }, { transform }], [style, transform]);
361
- return /* @__PURE__ */ React2__namespace.createElement(reactNative.Animated.View, { ref: refView, style: viewStyle, ...rest });
426
+ const renderStickyHeaderBackdrop = React2__namespace.useMemo(() => {
427
+ if (!(stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent)) {
428
+ return null;
429
+ }
430
+ return /* @__PURE__ */ React2__namespace.createElement(
431
+ reactNative.View,
432
+ {
433
+ style: {
434
+ inset: 0,
435
+ pointerEvents: "none",
436
+ position: "absolute"
437
+ }
438
+ },
439
+ getComponent(stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent)
440
+ );
441
+ }, [stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent]);
442
+ return /* @__PURE__ */ React2__namespace.createElement(reactNative.Animated.View, { ref: refView, style: viewStyle, ...rest }, renderStickyHeaderBackdrop, children);
362
443
  });
363
- var PositionView3 = IsNewArchitecture ? PositionViewState : PositionViewAnimated;
364
- var symbolFirst = Symbol();
444
+ var PositionView = IsNewArchitecture ? PositionViewState : PositionViewAnimated;
365
445
  function useInit(cb) {
366
- const refValue = React2.useRef(symbolFirst);
367
- if (refValue.current === symbolFirst) {
368
- refValue.current = cb();
369
- }
370
- return refValue.current;
446
+ React2.useState(() => cb());
371
447
  }
372
448
 
373
449
  // src/state/ContextContainer.ts
374
450
  var ContextContainer = React2.createContext(null);
451
+ function useContextContainer() {
452
+ return React2.useContext(ContextContainer);
453
+ }
375
454
  function useViewability(callback, configId) {
376
455
  const ctx = useStateContext();
377
- const { containerId } = React2.useContext(ContextContainer);
378
- const key = containerId + (configId != null ? configId : "");
456
+ const containerContext = useContextContainer();
379
457
  useInit(() => {
458
+ if (!containerContext) {
459
+ return;
460
+ }
461
+ const { containerId } = containerContext;
462
+ const key = containerId + (configId != null ? configId : "");
380
463
  const value = ctx.mapViewabilityValues.get(key);
381
464
  if (value) {
382
465
  callback(value);
383
466
  }
384
467
  });
385
- ctx.mapViewabilityCallbacks.set(key, callback);
386
- React2.useEffect(
387
- () => () => {
468
+ React2.useEffect(() => {
469
+ if (!containerContext) {
470
+ return;
471
+ }
472
+ const { containerId } = containerContext;
473
+ const key = containerId + (configId != null ? configId : "");
474
+ ctx.mapViewabilityCallbacks.set(key, callback);
475
+ return () => {
388
476
  ctx.mapViewabilityCallbacks.delete(key);
389
- },
390
- []
391
- );
477
+ };
478
+ }, [ctx, callback, configId, containerContext]);
392
479
  }
393
480
  function useViewabilityAmount(callback) {
394
481
  const ctx = useStateContext();
395
- const { containerId } = React2.useContext(ContextContainer);
482
+ const containerContext = useContextContainer();
396
483
  useInit(() => {
484
+ if (!containerContext) {
485
+ return;
486
+ }
487
+ const { containerId } = containerContext;
397
488
  const value = ctx.mapViewabilityAmountValues.get(containerId);
398
489
  if (value) {
399
490
  callback(value);
400
491
  }
401
492
  });
402
- ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
403
- React2.useEffect(
404
- () => () => {
493
+ React2.useEffect(() => {
494
+ if (!containerContext) {
495
+ return;
496
+ }
497
+ const { containerId } = containerContext;
498
+ ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
499
+ return () => {
405
500
  ctx.mapViewabilityAmountCallbacks.delete(containerId);
406
- },
407
- []
408
- );
501
+ };
502
+ }, [ctx, callback, containerContext]);
409
503
  }
410
504
  function useRecyclingEffect(effect) {
411
- const { index, value } = React2.useContext(ContextContainer);
505
+ const containerContext = useContextContainer();
412
506
  const prevValues = React2.useRef({
413
507
  prevIndex: void 0,
414
508
  prevItem: void 0
415
509
  });
416
510
  React2.useEffect(() => {
511
+ if (!containerContext) {
512
+ return;
513
+ }
514
+ const { index, value } = containerContext;
417
515
  let ret;
418
516
  if (prevValues.current.prevIndex !== void 0 && prevValues.current.prevItem !== void 0) {
419
517
  ret = effect({
@@ -428,38 +526,58 @@ function useRecyclingEffect(effect) {
428
526
  prevItem: value
429
527
  };
430
528
  return ret;
431
- }, [index, value, effect]);
529
+ }, [effect, containerContext]);
432
530
  }
433
531
  function useRecyclingState(valueOrFun) {
434
- const { index, value, itemKey, triggerLayout } = React2.useContext(ContextContainer);
435
- const refState = React2.useRef({
436
- itemKey: null,
437
- value: null
532
+ var _a3, _b;
533
+ const containerContext = useContextContainer();
534
+ const computeValue = (ctx) => {
535
+ if (isFunction(valueOrFun)) {
536
+ const initializer = valueOrFun;
537
+ return ctx ? initializer({
538
+ index: ctx.index,
539
+ item: ctx.value,
540
+ prevIndex: void 0,
541
+ prevItem: void 0
542
+ }) : initializer();
543
+ }
544
+ return valueOrFun;
545
+ };
546
+ const [stateValue, setStateValue] = React2.useState(() => {
547
+ return computeValue(containerContext);
438
548
  });
439
- const [_, setRenderNum] = React2.useState(0);
440
- const state = refState.current;
441
- if (state.itemKey !== itemKey) {
442
- state.itemKey = itemKey;
443
- state.value = isFunction(valueOrFun) ? valueOrFun({
444
- index,
445
- item: value,
446
- prevIndex: void 0,
447
- prevItem: void 0
448
- }) : valueOrFun;
549
+ const prevItemKeyRef = React2.useRef((_a3 = containerContext == null ? void 0 : containerContext.itemKey) != null ? _a3 : null);
550
+ const currentItemKey = (_b = containerContext == null ? void 0 : containerContext.itemKey) != null ? _b : null;
551
+ if (currentItemKey !== null && prevItemKeyRef.current !== currentItemKey) {
552
+ prevItemKeyRef.current = currentItemKey;
553
+ setStateValue(computeValue(containerContext));
449
554
  }
555
+ const triggerLayout = containerContext == null ? void 0 : containerContext.triggerLayout;
450
556
  const setState = React2.useCallback(
451
557
  (newState) => {
452
- state.value = isFunction(newState) ? newState(state.value) : newState;
453
- setRenderNum((v) => v + 1);
558
+ if (!triggerLayout) {
559
+ return;
560
+ }
561
+ setStateValue((prevValue) => {
562
+ return isFunction(newState) ? newState(prevValue) : newState;
563
+ });
454
564
  triggerLayout();
455
565
  },
456
- [triggerLayout, state]
566
+ [triggerLayout]
457
567
  );
458
- return [state.value, setState];
568
+ return [stateValue, setState];
459
569
  }
460
570
  function useIsLastItem() {
461
- const { itemKey } = React2.useContext(ContextContainer);
462
- const isLast = useSelector$("lastItemKeys", (lastItemKeys) => (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false);
571
+ const containerContext = useContextContainer();
572
+ const isLast = useSelector$("lastItemKeys", (lastItemKeys) => {
573
+ if (containerContext) {
574
+ const { itemKey } = containerContext;
575
+ if (!isNullOrUndefined(itemKey)) {
576
+ return (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false;
577
+ }
578
+ }
579
+ return false;
580
+ });
463
581
  return isLast;
464
582
  }
465
583
  function useListScrollSize() {
@@ -469,8 +587,9 @@ function useListScrollSize() {
469
587
  var noop = () => {
470
588
  };
471
589
  function useSyncLayout() {
472
- if (IsNewArchitecture) {
473
- const { triggerLayout: syncLayout } = React2.useContext(ContextContainer);
590
+ const containerContext = useContextContainer();
591
+ if (IsNewArchitecture && containerContext) {
592
+ const { triggerLayout: syncLayout } = containerContext;
474
593
  return syncLayout;
475
594
  } else {
476
595
  return noop;
@@ -521,7 +640,8 @@ var Container = typedMemo(function Container2({
521
640
  horizontal,
522
641
  getRenderedItem: getRenderedItem2,
523
642
  updateItemSize: updateItemSize2,
524
- ItemSeparatorComponent
643
+ ItemSeparatorComponent,
644
+ stickyHeaderConfig
525
645
  }) {
526
646
  const ctx = useStateContext();
527
647
  const { columnWrapperStyle, animatedScrollY } = ctx;
@@ -633,6 +753,7 @@ var Container = typedMemo(function Container2({
633
753
  if (!IsNewArchitecture) {
634
754
  React2.useEffect(() => {
635
755
  if (!isNullOrUndefined(itemKey)) {
756
+ didLayoutRef.current = false;
636
757
  const timeout = setTimeout(() => {
637
758
  if (!didLayoutRef.current) {
638
759
  const {
@@ -652,7 +773,7 @@ var Container = typedMemo(function Container2({
652
773
  }
653
774
  }, [itemKey]);
654
775
  }
655
- const PositionComponent = isSticky ? PositionViewSticky : PositionView3;
776
+ const PositionComponent = isSticky ? PositionViewSticky : PositionView;
656
777
  return /* @__PURE__ */ React2__namespace.createElement(
657
778
  PositionComponent,
658
779
  {
@@ -663,6 +784,7 @@ var Container = typedMemo(function Container2({
663
784
  key: recycleItems ? void 0 : itemKey,
664
785
  onLayout,
665
786
  refView: ref,
787
+ stickyHeaderConfig,
666
788
  stickyOffset: isSticky ? stickyOffset : void 0,
667
789
  style
668
790
  },
@@ -687,10 +809,10 @@ var Containers = typedMemo(function Containers2({
687
809
  // If this is the initial scroll, we don't want to delay because we want to update the size immediately
688
810
  delay: (value, prevValue) => {
689
811
  var _a3;
690
- return !((_a3 = ctx.internalState) == null ? void 0 : _a3.initialScroll) ? !prevValue || value - prevValue > 20 ? 0 : 200 : void 0;
812
+ return !((_a3 = ctx.state) == null ? void 0 : _a3.initialScroll) ? !prevValue || value - prevValue > 20 ? 0 : 200 : void 0;
691
813
  }
692
814
  });
693
- const animOpacity = waitForInitialLayout && !IsNewArchitecture ? useValue$("containersDidLayout", { getValue: (value) => value ? 1 : 0 }) : void 0;
815
+ const animOpacity = waitForInitialLayout && !IsNewArchitecture ? useValue$("readyToRender", { getValue: (value) => value ? 1 : 0 }) : void 0;
694
816
  const otherAxisSize = useValue$("otherAxisSize", { delay: 0 });
695
817
  const containers = [];
696
818
  for (let i = 0; i < numContainers; i++) {
@@ -733,7 +855,8 @@ var Containers = typedMemo(function Containers2({
733
855
  return /* @__PURE__ */ React2__namespace.createElement(reactNative.Animated.View, { style }, containers);
734
856
  });
735
857
  function DevNumbers() {
736
- return IS_DEV && React2__namespace.memo(function DevNumbers2() {
858
+ return IS_DEV && // biome-ignore lint/nursery/noShadow: const function name shadowing is intentional
859
+ React2__namespace.memo(function DevNumbers2() {
737
860
  return Array.from({ length: 100 }).map((_, index) => /* @__PURE__ */ React2__namespace.createElement(
738
861
  reactNative.View,
739
862
  {
@@ -799,15 +922,6 @@ var LayoutView = ({ onLayoutChange, refView, ...rest }) => {
799
922
  };
800
923
 
801
924
  // src/components/ListComponent.tsx
802
- var getComponent = (Component) => {
803
- if (React2__namespace.isValidElement(Component)) {
804
- return Component;
805
- }
806
- if (Component) {
807
- return /* @__PURE__ */ React2__namespace.createElement(Component, null);
808
- }
809
- return null;
810
- };
811
925
  var ListComponent = typedMemo(function ListComponent2({
812
926
  canRender,
813
927
  style,
@@ -828,26 +942,20 @@ var ListComponent = typedMemo(function ListComponent2({
828
942
  getRenderedItem: getRenderedItem2,
829
943
  updateItemSize: updateItemSize2,
830
944
  refScrollView,
831
- maintainVisibleContentPosition,
832
945
  renderScrollComponent,
833
946
  scrollAdjustHandler,
834
947
  onLayoutHeader,
835
948
  snapToIndices,
949
+ stickyHeaderConfig,
836
950
  stickyHeaderIndices,
837
951
  ...rest
838
952
  }) {
839
953
  const ctx = useStateContext();
954
+ const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
840
955
  const ScrollComponent = renderScrollComponent ? React2.useMemo(
841
956
  () => React2__namespace.forwardRef((props, ref) => renderScrollComponent({ ...props, ref })),
842
957
  [renderScrollComponent]
843
958
  ) : ListComponentScrollView;
844
- React2__namespace.useEffect(() => {
845
- if (canRender) {
846
- setTimeout(() => {
847
- scrollAdjustHandler.setMounted();
848
- }, 0);
849
- }
850
- }, [canRender]);
851
959
  const SnapOrScroll = snapToIndices ? SnapWrapper : ScrollComponent;
852
960
  return /* @__PURE__ */ React2__namespace.createElement(
853
961
  SnapOrScroll,
@@ -861,7 +969,7 @@ var ListComponent = typedMemo(function ListComponent2({
861
969
  ],
862
970
  contentOffset: initialContentOffset ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
863
971
  horizontal,
864
- maintainVisibleContentPosition: maintainVisibleContentPosition ? { minIndexForVisible: 0 } : void 0,
972
+ maintainVisibleContentPosition: maintainVisibleContentPosition.size || maintainVisibleContentPosition.data ? { minIndexForVisible: 0 } : void 0,
865
973
  onLayout,
866
974
  onScroll: onScroll2,
867
975
  ref: refScrollView,
@@ -879,6 +987,7 @@ var ListComponent = typedMemo(function ListComponent2({
879
987
  horizontal,
880
988
  ItemSeparatorComponent,
881
989
  recycleItems,
990
+ stickyHeaderConfig,
882
991
  updateItemSize: updateItemSize2,
883
992
  waitForInitialLayout
884
993
  }
@@ -911,10 +1020,11 @@ function getId(state, index) {
911
1020
  }
912
1021
 
913
1022
  // src/core/calculateOffsetForIndex.ts
914
- function calculateOffsetForIndex(ctx, state, index) {
1023
+ function calculateOffsetForIndex(ctx, index) {
1024
+ const state = ctx.state;
915
1025
  let position = 0;
916
1026
  if (index !== void 0) {
917
- position = (state == null ? void 0 : state.positions.get(getId(state, index))) || 0;
1027
+ position = state.positions.get(getId(state, index)) || 0;
918
1028
  const paddingTop = peek$(ctx, "stylePaddingTop");
919
1029
  if (paddingTop) {
920
1030
  position += paddingTop;
@@ -928,7 +1038,8 @@ function calculateOffsetForIndex(ctx, state, index) {
928
1038
  }
929
1039
 
930
1040
  // src/utils/setPaddingTop.ts
931
- function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
1041
+ function setPaddingTop(ctx, { stylePaddingTop, alignItemsPaddingTop }) {
1042
+ const state = ctx.state;
932
1043
  if (stylePaddingTop !== void 0) {
933
1044
  const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
934
1045
  if (stylePaddingTop < prevStylePaddingTop) {
@@ -947,7 +1058,8 @@ function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
947
1058
  }
948
1059
 
949
1060
  // src/utils/updateAlignItemsPaddingTop.ts
950
- function updateAlignItemsPaddingTop(ctx, state) {
1061
+ function updateAlignItemsPaddingTop(ctx) {
1062
+ const state = ctx.state;
951
1063
  const {
952
1064
  scrollLength,
953
1065
  props: { alignItemsAtEnd, data }
@@ -958,12 +1070,13 @@ function updateAlignItemsPaddingTop(ctx, state) {
958
1070
  const contentSize = getContentSize(ctx);
959
1071
  alignItemsPaddingTop = Math.max(0, Math.floor(scrollLength - contentSize));
960
1072
  }
961
- setPaddingTop(ctx, state, { alignItemsPaddingTop });
1073
+ setPaddingTop(ctx, { alignItemsPaddingTop });
962
1074
  }
963
1075
  }
964
1076
 
965
1077
  // src/core/addTotalSize.ts
966
- function addTotalSize(ctx, state, key, add) {
1078
+ function addTotalSize(ctx, key, add) {
1079
+ const state = ctx.state;
967
1080
  const { alignItemsAtEnd } = state.props;
968
1081
  const prevTotalSize = state.totalSize;
969
1082
  let totalSize = state.totalSize;
@@ -984,31 +1097,34 @@ function addTotalSize(ctx, state, key, add) {
984
1097
  state.totalSize = totalSize;
985
1098
  set$(ctx, "totalSize", totalSize);
986
1099
  if (alignItemsAtEnd) {
987
- updateAlignItemsPaddingTop(ctx, state);
1100
+ updateAlignItemsPaddingTop(ctx);
988
1101
  }
989
1102
  }
990
1103
  }
991
1104
  }
992
1105
 
993
1106
  // src/core/setSize.ts
994
- function setSize(ctx, state, itemKey, size) {
1107
+ function setSize(ctx, itemKey, size) {
1108
+ const state = ctx.state;
995
1109
  const { sizes } = state;
996
1110
  const previousSize = sizes.get(itemKey);
997
1111
  const diff = previousSize !== void 0 ? size - previousSize : size;
998
1112
  if (diff !== 0) {
999
- addTotalSize(ctx, state, itemKey, diff);
1113
+ addTotalSize(ctx, itemKey, diff);
1000
1114
  }
1001
1115
  sizes.set(itemKey, size);
1002
1116
  }
1003
1117
 
1004
1118
  // src/utils/getItemSize.ts
1005
- function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedSize) {
1119
+ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
1006
1120
  var _a3, _b;
1121
+ const state = ctx.state;
1007
1122
  const {
1008
1123
  sizesKnown,
1009
1124
  sizes,
1010
1125
  averageSizes,
1011
- props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType }
1126
+ props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType },
1127
+ scrollingTo
1012
1128
  } = state;
1013
1129
  const sizeKnown = sizesKnown.get(key);
1014
1130
  if (sizeKnown !== void 0) {
@@ -1016,7 +1132,6 @@ function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedS
1016
1132
  }
1017
1133
  let size;
1018
1134
  const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1019
- const scrollingTo = peek$(ctx, "scrollingTo");
1020
1135
  if (preferCachedSize) {
1021
1136
  const cachedSize = sizes.get(key);
1022
1137
  if (cachedSize !== void 0) {
@@ -1024,7 +1139,7 @@ function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedS
1024
1139
  }
1025
1140
  }
1026
1141
  if (getFixedItemSize) {
1027
- size = getFixedItemSize(index, data, itemType);
1142
+ size = getFixedItemSize(data, index, itemType);
1028
1143
  if (size !== void 0) {
1029
1144
  sizesKnown.set(key, size);
1030
1145
  }
@@ -1042,96 +1157,192 @@ function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedS
1042
1157
  }
1043
1158
  }
1044
1159
  if (size === void 0) {
1045
- size = getEstimatedItemSize ? getEstimatedItemSize(index, data, itemType) : estimatedItemSize;
1160
+ size = getEstimatedItemSize ? getEstimatedItemSize(data, index, itemType) : estimatedItemSize;
1046
1161
  }
1047
- setSize(ctx, state, key, size);
1162
+ setSize(ctx, key, size);
1048
1163
  return size;
1049
1164
  }
1050
1165
 
1051
1166
  // src/core/calculateOffsetWithOffsetPosition.ts
1052
- function calculateOffsetWithOffsetPosition(ctx, state, offsetParam, params) {
1167
+ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1168
+ const state = ctx.state;
1053
1169
  const { index, viewOffset, viewPosition } = params;
1054
1170
  let offset = offsetParam;
1055
1171
  if (viewOffset) {
1056
1172
  offset -= viewOffset;
1057
1173
  }
1058
1174
  if (viewPosition !== void 0 && index !== void 0) {
1059
- offset -= viewPosition * (state.scrollLength - getItemSize(ctx, state, getId(state, index), index, state.props.data[index]));
1175
+ const itemSize = getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1176
+ const trailingInset = getContentInsetEnd(state);
1177
+ offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1060
1178
  }
1061
1179
  return offset;
1062
1180
  }
1181
+ var Platform2 = reactNative.Platform;
1182
+ var PlatformAdjustBreaksScroll = Platform2.OS === "android";
1183
+
1184
+ // src/core/clampScrollOffset.ts
1185
+ function clampScrollOffset(ctx, offset) {
1186
+ const state = ctx.state;
1187
+ const contentSize = getContentSize(ctx);
1188
+ let clampedOffset = offset;
1189
+ if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform2.OS !== "android" || state.lastLayout)) {
1190
+ const maxOffset = Math.max(0, contentSize - state.scrollLength);
1191
+ clampedOffset = Math.min(offset, maxOffset);
1192
+ }
1193
+ clampedOffset = Math.max(0, clampedOffset);
1194
+ return clampedOffset;
1195
+ }
1196
+
1197
+ // src/utils/setInitialRenderState.ts
1198
+ function setInitialRenderState(ctx, {
1199
+ didLayout,
1200
+ didInitialScroll
1201
+ }) {
1202
+ const { state } = ctx;
1203
+ if (didLayout) {
1204
+ state.didContainersLayout = true;
1205
+ }
1206
+ if (didInitialScroll) {
1207
+ state.didFinishInitialScroll = true;
1208
+ }
1209
+ if (state.didContainersLayout && state.didFinishInitialScroll) {
1210
+ set$(ctx, "readyToRender", true);
1211
+ }
1212
+ }
1063
1213
 
1064
1214
  // src/core/finishScrollTo.ts
1065
- function finishScrollTo(ctx, state) {
1215
+ function finishScrollTo(ctx) {
1066
1216
  var _a3, _b;
1067
- if (state) {
1217
+ const state = ctx.state;
1218
+ if (state == null ? void 0 : state.scrollingTo) {
1219
+ const scrollingTo = state.scrollingTo;
1068
1220
  state.scrollHistory.length = 0;
1069
1221
  state.initialScroll = void 0;
1070
1222
  state.initialAnchor = void 0;
1071
- set$(ctx, "scrollingTo", void 0);
1223
+ state.scrollingTo = void 0;
1072
1224
  if (state.pendingTotalSize !== void 0) {
1073
- addTotalSize(ctx, state, null, state.pendingTotalSize);
1225
+ addTotalSize(ctx, null, state.pendingTotalSize);
1074
1226
  }
1075
1227
  if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1076
1228
  (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1077
1229
  }
1230
+ if (PlatformAdjustBreaksScroll) {
1231
+ state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
1232
+ }
1233
+ setInitialRenderState(ctx, { didInitialScroll: true });
1078
1234
  }
1079
1235
  }
1080
- var Platform2 = reactNative.Platform;
1081
1236
 
1082
- // src/core/scrollTo.ts
1083
- function scrollTo(ctx, state, params) {
1237
+ // src/core/checkFinishedScroll.ts
1238
+ function checkFinishedScroll(ctx) {
1239
+ ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
1240
+ }
1241
+ function checkFinishedScrollFrame(ctx) {
1242
+ const scrollingTo = ctx.state.scrollingTo;
1243
+ if (scrollingTo) {
1244
+ const { state } = ctx;
1245
+ state.animFrameCheckFinishedScroll = void 0;
1246
+ const scroll = state.scrollPending;
1247
+ const adjust = state.scrollAdjustHandler.getAdjust();
1248
+ const clampedTargetOffset = clampScrollOffset(ctx, scrollingTo.offset - (scrollingTo.viewOffset || 0));
1249
+ const maxOffset = clampScrollOffset(ctx, scroll);
1250
+ const diff1 = Math.abs(scroll - clampedTargetOffset);
1251
+ const diff2 = Math.abs(diff1 - adjust);
1252
+ const isNotOverscrolled = Math.abs(scroll - maxOffset) < 1;
1253
+ if (isNotOverscrolled && (diff1 < 1 || diff2 < 1)) {
1254
+ finishScrollTo(ctx);
1255
+ }
1256
+ }
1257
+ }
1258
+ function checkFinishedScrollFallback(ctx) {
1259
+ const state = ctx.state;
1260
+ const scrollingTo = state.scrollingTo;
1261
+ const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) || !state.didContainersLayout;
1262
+ state.timeoutCheckFinishedScrollFallback = setTimeout(
1263
+ () => {
1264
+ let numChecks = 0;
1265
+ const checkHasScrolled = () => {
1266
+ state.timeoutCheckFinishedScrollFallback = void 0;
1267
+ const isStillScrollingTo = state.scrollingTo;
1268
+ if (isStillScrollingTo) {
1269
+ numChecks++;
1270
+ if (state.hasScrolled || numChecks > 5) {
1271
+ finishScrollTo(ctx);
1272
+ } else {
1273
+ state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, 100);
1274
+ }
1275
+ }
1276
+ };
1277
+ checkHasScrolled();
1278
+ },
1279
+ slowTimeout ? 500 : 100
1280
+ );
1281
+ }
1282
+
1283
+ // src/core/doScrollTo.native.ts
1284
+ function doScrollTo(ctx, params) {
1084
1285
  var _a3;
1085
- const { noScrollingTo, ...scrollTarget } = params;
1286
+ const state = ctx.state;
1287
+ const { animated, horizontal, offset } = params;
1288
+ const { refScroller } = state;
1289
+ (_a3 = refScroller.current) == null ? void 0 : _a3.scrollTo({
1290
+ animated: !!animated,
1291
+ x: horizontal ? offset : 0,
1292
+ y: horizontal ? 0 : offset
1293
+ });
1294
+ if (!animated) {
1295
+ state.scroll = offset;
1296
+ checkFinishedScrollFallback(ctx);
1297
+ }
1298
+ }
1299
+
1300
+ // src/core/scrollTo.ts
1301
+ function scrollTo(ctx, params) {
1302
+ const state = ctx.state;
1303
+ const { noScrollingTo, forceScroll, ...scrollTarget } = params;
1086
1304
  const { animated, isInitialScroll, offset: scrollTargetOffset, precomputedWithViewOffset } = scrollTarget;
1087
1305
  const {
1088
- refScroller,
1089
1306
  props: { horizontal }
1090
1307
  } = state;
1091
- let offset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, state, scrollTargetOffset, scrollTarget);
1092
- if (Number.isFinite(state.scrollLength) && Number.isFinite(state.totalSize)) {
1093
- const maxOffset = Math.max(0, getContentSize(ctx) - state.scrollLength);
1094
- offset = Math.min(offset, maxOffset);
1308
+ if (state.animFrameCheckFinishedScroll) {
1309
+ cancelAnimationFrame(ctx.state.animFrameCheckFinishedScroll);
1310
+ }
1311
+ if (state.timeoutCheckFinishedScrollFallback) {
1312
+ clearTimeout(ctx.state.timeoutCheckFinishedScrollFallback);
1095
1313
  }
1314
+ let offset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, scrollTargetOffset, scrollTarget);
1315
+ offset = clampScrollOffset(ctx, offset);
1096
1316
  state.scrollHistory.length = 0;
1097
1317
  if (!noScrollingTo) {
1098
- set$(ctx, "scrollingTo", scrollTarget);
1318
+ state.scrollingTo = scrollTarget;
1099
1319
  }
1100
1320
  state.scrollPending = offset;
1101
- if (!isInitialScroll || Platform2.OS === "android") {
1102
- (_a3 = refScroller.current) == null ? void 0 : _a3.scrollTo({
1103
- animated: !!animated,
1104
- x: horizontal ? offset : 0,
1105
- y: horizontal ? 0 : offset
1106
- });
1107
- }
1108
- if (!animated) {
1321
+ if (forceScroll || !isInitialScroll || Platform2.OS === "android") {
1322
+ doScrollTo(ctx, { animated, horizontal, isInitialScroll, offset });
1323
+ } else {
1109
1324
  state.scroll = offset;
1110
- if (Platform2.OS === "web") {
1111
- const unlisten = listen$(ctx, "containersDidLayout", (value) => {
1112
- if (value && peek$(ctx, "scrollingTo")) {
1113
- finishScrollTo(ctx, state);
1114
- unlisten();
1115
- }
1116
- });
1117
- } else {
1118
- setTimeout(() => finishScrollTo(ctx, state), 100);
1119
- }
1120
- if (isInitialScroll) {
1121
- setTimeout(() => {
1122
- state.initialScroll = void 0;
1123
- }, 500);
1124
- }
1125
1325
  }
1126
1326
  }
1127
1327
 
1328
+ // src/platform/flushSync.native.ts
1329
+ var flushSync = (fn) => {
1330
+ fn();
1331
+ };
1332
+
1128
1333
  // src/utils/checkThreshold.ts
1129
1334
  var HYSTERESIS_MULTIPLIER = 1.3;
1130
- var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot) => {
1335
+ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot, allowReentryOnChange) => {
1131
1336
  const absDistance = Math.abs(distance);
1132
1337
  const within = atThreshold || threshold > 0 && absDistance <= threshold;
1338
+ if (wasReached === null) {
1339
+ if (!within && distance >= 0) {
1340
+ return false;
1341
+ }
1342
+ return null;
1343
+ }
1133
1344
  const updateSnapshot = () => {
1134
- setSnapshot == null ? void 0 : setSnapshot({
1345
+ setSnapshot({
1135
1346
  atThreshold,
1136
1347
  contentSize: context.contentSize,
1137
1348
  dataLength: context.dataLength,
@@ -1142,19 +1353,21 @@ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, co
1142
1353
  if (!within) {
1143
1354
  return false;
1144
1355
  }
1145
- onReached == null ? void 0 : onReached(distance);
1356
+ onReached(distance);
1146
1357
  updateSnapshot();
1147
1358
  return true;
1148
1359
  }
1149
1360
  const reset = !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
1150
1361
  if (reset) {
1151
- setSnapshot == null ? void 0 : setSnapshot(void 0);
1362
+ setSnapshot(void 0);
1152
1363
  return false;
1153
1364
  }
1154
1365
  if (within) {
1155
1366
  const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
1156
1367
  if (changed) {
1157
- onReached == null ? void 0 : onReached(distance);
1368
+ if (allowReentryOnChange) {
1369
+ onReached(distance);
1370
+ }
1158
1371
  updateSnapshot();
1159
1372
  }
1160
1373
  }
@@ -1162,8 +1375,9 @@ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, co
1162
1375
  };
1163
1376
 
1164
1377
  // src/utils/checkAtBottom.ts
1165
- function checkAtBottom(ctx, state) {
1378
+ function checkAtBottom(ctx) {
1166
1379
  var _a3;
1380
+ const state = ctx.state;
1167
1381
  if (!state) {
1168
1382
  return;
1169
1383
  }
@@ -1196,7 +1410,8 @@ function checkAtBottom(ctx, state) {
1196
1410
  },
1197
1411
  (snapshot) => {
1198
1412
  state.endReachedSnapshot = snapshot;
1199
- }
1413
+ },
1414
+ true
1200
1415
  );
1201
1416
  }
1202
1417
  }
@@ -1231,20 +1446,21 @@ function checkAtTop(state) {
1231
1446
  },
1232
1447
  (snapshot) => {
1233
1448
  state.startReachedSnapshot = snapshot;
1234
- }
1449
+ },
1450
+ false
1235
1451
  );
1236
1452
  }
1237
1453
 
1238
1454
  // src/core/updateScroll.ts
1239
- function updateScroll(ctx, state, newScroll, forceUpdate) {
1240
- var _a3;
1241
- const scrollingTo = peek$(ctx, "scrollingTo");
1455
+ function updateScroll(ctx, newScroll, forceUpdate) {
1456
+ const state = ctx.state;
1457
+ const { scrollingTo, scrollAdjustHandler, lastScrollAdjustForHistory } = state;
1458
+ const prevScroll = state.scroll;
1242
1459
  state.hasScrolled = true;
1243
1460
  state.lastBatchingAction = Date.now();
1244
1461
  const currentTime = Date.now();
1245
- const adjust = state.scrollAdjustHandler.getAdjust();
1246
- const lastHistoryAdjust = state.lastScrollAdjustForHistory;
1247
- const adjustChanged = lastHistoryAdjust !== void 0 && Math.abs(adjust - lastHistoryAdjust) > 0.1;
1462
+ const adjust = scrollAdjustHandler.getAdjust();
1463
+ const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
1248
1464
  if (adjustChanged) {
1249
1465
  state.scrollHistory.length = 0;
1250
1466
  }
@@ -1257,7 +1473,7 @@ function updateScroll(ctx, state, newScroll, forceUpdate) {
1257
1473
  if (state.scrollHistory.length > 5) {
1258
1474
  state.scrollHistory.shift();
1259
1475
  }
1260
- state.scrollPrev = state.scroll;
1476
+ state.scrollPrev = prevScroll;
1261
1477
  state.scrollPrevTime = state.scrollTime;
1262
1478
  state.scroll = newScroll;
1263
1479
  state.scrollTime = currentTime;
@@ -1269,22 +1485,38 @@ function updateScroll(ctx, state, newScroll, forceUpdate) {
1269
1485
  return;
1270
1486
  }
1271
1487
  }
1272
- if (forceUpdate || state.dataChangeNeedsScrollUpdate || Math.abs(state.scroll - state.scrollPrev) > 2) {
1488
+ const scrollDelta = Math.abs(newScroll - prevScroll);
1489
+ const scrollLength = state.scrollLength;
1490
+ const lastCalculated = state.scrollLastCalculate;
1491
+ const shouldUpdate = forceUpdate || state.dataChangeNeedsScrollUpdate || state.scrollLastCalculate === void 0 || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
1492
+ if (shouldUpdate) {
1493
+ state.scrollLastCalculate = state.scroll;
1273
1494
  state.ignoreScrollFromMVCPIgnored = false;
1274
- (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { doMVCP: scrollingTo !== void 0 });
1275
- checkAtBottom(ctx, state);
1276
- checkAtTop(state);
1495
+ state.lastScrollDelta = scrollDelta;
1496
+ const runCalculateItems = () => {
1497
+ var _a3;
1498
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { doMVCP: scrollingTo !== void 0 });
1499
+ checkAtBottom(ctx);
1500
+ checkAtTop(state);
1501
+ };
1502
+ if (Platform2.OS === "web" && scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength) {
1503
+ flushSync(runCalculateItems);
1504
+ } else {
1505
+ runCalculateItems();
1506
+ }
1277
1507
  state.dataChangeNeedsScrollUpdate = false;
1508
+ state.lastScrollDelta = 0;
1278
1509
  }
1279
1510
  }
1280
1511
 
1281
1512
  // src/utils/requestAdjust.ts
1282
- function requestAdjust(ctx, state, positionDiff, dataChanged) {
1513
+ function requestAdjust(ctx, positionDiff, dataChanged) {
1514
+ const state = ctx.state;
1283
1515
  if (Math.abs(positionDiff) > 0.1) {
1284
1516
  const needsScrollWorkaround = Platform2.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
1285
1517
  const doit = () => {
1286
1518
  if (needsScrollWorkaround) {
1287
- scrollTo(ctx, state, {
1519
+ scrollTo(ctx, {
1288
1520
  noScrollingTo: true,
1289
1521
  offset: state.scroll
1290
1522
  });
@@ -1297,8 +1529,8 @@ function requestAdjust(ctx, state, positionDiff, dataChanged) {
1297
1529
  };
1298
1530
  state.scroll += positionDiff;
1299
1531
  state.scrollForNextCalculateItemsInView = void 0;
1300
- const didLayout = peek$(ctx, "containersDidLayout");
1301
- if (didLayout) {
1532
+ const readyToRender = peek$(ctx, "readyToRender");
1533
+ if (readyToRender) {
1302
1534
  doit();
1303
1535
  if (Platform2.OS !== "web") {
1304
1536
  const threshold = state.scroll - positionDiff / 2;
@@ -1320,7 +1552,7 @@ function requestAdjust(ctx, state, positionDiff, dataChanged) {
1320
1552
  if (shouldForceUpdate) {
1321
1553
  state.ignoreScrollFromMVCPIgnored = false;
1322
1554
  state.scrollPending = state.scroll;
1323
- updateScroll(ctx, state, state.scroll, true);
1555
+ updateScroll(ctx, state.scroll, true);
1324
1556
  }
1325
1557
  }, delay);
1326
1558
  }
@@ -1335,28 +1567,27 @@ function requestAdjust(ctx, state, positionDiff, dataChanged) {
1335
1567
  var INITIAL_ANCHOR_TOLERANCE = 0.5;
1336
1568
  var INITIAL_ANCHOR_MAX_ATTEMPTS = 4;
1337
1569
  var INITIAL_ANCHOR_SETTLED_TICKS = 2;
1338
- function ensureInitialAnchor(ctx, state) {
1570
+ function ensureInitialAnchor(ctx) {
1339
1571
  var _a3, _b, _c, _d, _e;
1340
- const anchor = state.initialAnchor;
1572
+ const state = ctx.state;
1573
+ const { initialAnchor, didContainersLayout, positions, scroll, scrollLength } = state;
1574
+ const anchor = initialAnchor;
1341
1575
  const item = state.props.data[anchor.index];
1342
- const containersDidLayout = peek$(ctx, "containersDidLayout");
1343
- if (!containersDidLayout) {
1576
+ if (!didContainersLayout) {
1344
1577
  return;
1345
1578
  }
1346
1579
  const id = getId(state, anchor.index);
1347
- if (state.positions.get(id) === void 0) {
1580
+ if (positions.get(id) === void 0) {
1348
1581
  return;
1349
1582
  }
1350
- const size = getItemSize(ctx, state, id, anchor.index, item, true, true);
1583
+ const size = getItemSize(ctx, id, anchor.index, item, true, true);
1351
1584
  if (size === void 0) {
1352
1585
  return;
1353
1586
  }
1354
- const availableSpace = Math.max(0, state.scrollLength - size);
1355
- const desiredOffset = calculateOffsetForIndex(ctx, state, anchor.index) - ((_a3 = anchor.viewOffset) != null ? _a3 : 0) - ((_b = anchor.viewPosition) != null ? _b : 0) * availableSpace;
1356
- const contentSize = getContentSize(ctx);
1357
- const maxOffset = Math.max(0, contentSize - state.scrollLength);
1358
- const clampedDesiredOffset = Math.max(0, Math.min(desiredOffset, maxOffset));
1359
- const delta = clampedDesiredOffset - state.scroll;
1587
+ const availableSpace = Math.max(0, scrollLength - size);
1588
+ const desiredOffset = calculateOffsetForIndex(ctx, anchor.index) - ((_a3 = anchor.viewOffset) != null ? _a3 : 0) - ((_b = anchor.viewPosition) != null ? _b : 0) * availableSpace;
1589
+ const clampedDesiredOffset = clampScrollOffset(ctx, desiredOffset);
1590
+ const delta = clampedDesiredOffset - scroll;
1360
1591
  if (Math.abs(delta) <= INITIAL_ANCHOR_TOLERANCE) {
1361
1592
  const settledTicks = ((_c = anchor.settledTicks) != null ? _c : 0) + 1;
1362
1593
  if (settledTicks >= INITIAL_ANCHOR_SETTLED_TICKS) {
@@ -1380,19 +1611,24 @@ function ensureInitialAnchor(ctx, state) {
1380
1611
  lastDelta: delta,
1381
1612
  settledTicks: 0
1382
1613
  });
1383
- requestAdjust(ctx, state, delta);
1614
+ requestAdjust(ctx, delta);
1615
+ requestAnimationFrame(() => finishScrollTo(ctx));
1384
1616
  }
1385
1617
 
1386
1618
  // src/core/mvcp.ts
1387
- function prepareMVCP(ctx, state, dataChanged) {
1619
+ function prepareMVCP(ctx, dataChanged) {
1620
+ const state = ctx.state;
1388
1621
  const { idsInView, positions, props } = state;
1389
- const { maintainVisibleContentPosition } = props;
1390
- const scrollingTo = peek$(ctx, "scrollingTo");
1622
+ const {
1623
+ maintainVisibleContentPosition: { data: mvcpData, size: mvcpScroll, shouldRestorePosition }
1624
+ } = props;
1625
+ const scrollingTo = state.scrollingTo;
1391
1626
  let prevPosition;
1392
1627
  let targetId;
1393
1628
  const idsInViewWithPositions = [];
1394
1629
  const scrollTarget = scrollingTo == null ? void 0 : scrollingTo.index;
1395
- const shouldMVCP = !dataChanged || maintainVisibleContentPosition;
1630
+ const scrollingToViewPosition = scrollingTo == null ? void 0 : scrollingTo.viewPosition;
1631
+ const shouldMVCP = dataChanged ? mvcpData : mvcpScroll;
1396
1632
  const indexByKey = state.indexByKey;
1397
1633
  if (shouldMVCP) {
1398
1634
  if (scrollTarget !== void 0) {
@@ -1400,7 +1636,7 @@ function prepareMVCP(ctx, state, dataChanged) {
1400
1636
  return void 0;
1401
1637
  }
1402
1638
  targetId = getId(state, scrollTarget);
1403
- } else if (idsInView.length > 0 && peek$(ctx, "containersDidLayout")) {
1639
+ } else if (idsInView.length > 0 && state.didContainersLayout) {
1404
1640
  if (dataChanged) {
1405
1641
  for (let i = 0; i < idsInView.length; i++) {
1406
1642
  const id = idsInView[i];
@@ -1417,10 +1653,18 @@ function prepareMVCP(ctx, state, dataChanged) {
1417
1653
  prevPosition = positions.get(targetId);
1418
1654
  }
1419
1655
  return () => {
1420
- let positionDiff;
1421
- if (dataChanged && targetId === void 0 && maintainVisibleContentPosition) {
1656
+ let positionDiff = 0;
1657
+ if (dataChanged && targetId === void 0 && mvcpData) {
1658
+ const data = state.props.data;
1422
1659
  for (let i = 0; i < idsInViewWithPositions.length; i++) {
1423
1660
  const { id, position } = idsInViewWithPositions[i];
1661
+ const index = indexByKey.get(id);
1662
+ if (index !== void 0 && shouldRestorePosition) {
1663
+ const item = data[index];
1664
+ if (item === void 0 || !shouldRestorePosition(item, index, data)) {
1665
+ continue;
1666
+ }
1667
+ }
1424
1668
  const newPosition = positions.get(id);
1425
1669
  if (newPosition !== void 0) {
1426
1670
  positionDiff = newPosition - position;
@@ -1443,16 +1687,28 @@ function prepareMVCP(ctx, state, dataChanged) {
1443
1687
  positionDiff = diff;
1444
1688
  }
1445
1689
  }
1446
- if (positionDiff !== void 0 && Math.abs(positionDiff) > 0.1) {
1447
- requestAdjust(ctx, state, positionDiff, dataChanged && maintainVisibleContentPosition);
1690
+ if (scrollingToViewPosition && scrollingToViewPosition > 0) {
1691
+ const newSize = getItemSize(ctx, targetId, scrollTarget, state.props.data[scrollTarget]);
1692
+ const prevSize = scrollingTo == null ? void 0 : scrollingTo.itemSize;
1693
+ if (newSize !== void 0 && prevSize !== void 0 && newSize !== (scrollingTo == null ? void 0 : scrollingTo.itemSize)) {
1694
+ const diff = newSize - prevSize;
1695
+ if (diff !== 0) {
1696
+ positionDiff += (newSize - prevSize) * scrollingToViewPosition;
1697
+ scrollingTo.itemSize = newSize;
1698
+ }
1699
+ }
1700
+ }
1701
+ if (Math.abs(positionDiff) > 0.1) {
1702
+ requestAdjust(ctx, positionDiff, dataChanged && mvcpData);
1448
1703
  }
1449
1704
  };
1450
1705
  }
1451
1706
  }
1452
1707
 
1453
1708
  // src/core/prepareColumnStartState.ts
1454
- function prepareColumnStartState(ctx, state, startIndex, useAverageSize) {
1709
+ function prepareColumnStartState(ctx, startIndex, useAverageSize) {
1455
1710
  var _a3;
1711
+ const state = ctx.state;
1456
1712
  const numColumns = peek$(ctx, "numColumns");
1457
1713
  let rowStartIndex = startIndex;
1458
1714
  const columnAtStart = state.columns.get(state.idCache[startIndex]);
@@ -1467,7 +1723,7 @@ function prepareColumnStartState(ctx, state, startIndex, useAverageSize) {
1467
1723
  const prevId = state.idCache[prevIndex];
1468
1724
  const prevPosition = (_a3 = state.positions.get(prevId)) != null ? _a3 : 0;
1469
1725
  const prevRowStart = findRowStartIndex(state, numColumns, prevIndex);
1470
- const prevRowHeight = calculateRowMaxSize(ctx, state, prevRowStart, prevIndex, useAverageSize);
1726
+ const prevRowHeight = calculateRowMaxSize(ctx, prevRowStart, prevIndex, useAverageSize);
1471
1727
  currentRowTop = prevPosition + prevRowHeight;
1472
1728
  }
1473
1729
  return {
@@ -1490,7 +1746,8 @@ function findRowStartIndex(state, numColumns, index) {
1490
1746
  }
1491
1747
  return rowStart;
1492
1748
  }
1493
- function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1749
+ function calculateRowMaxSize(ctx, startIndex, endIndex, useAverageSize) {
1750
+ const state = ctx.state;
1494
1751
  if (endIndex < startIndex) {
1495
1752
  return 0;
1496
1753
  }
@@ -1504,7 +1761,7 @@ function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1504
1761
  continue;
1505
1762
  }
1506
1763
  const id = state.idCache[i];
1507
- const size = getItemSize(ctx, state, id, i, data[i], useAverageSize);
1764
+ const size = getItemSize(ctx, id, i, data[i], useAverageSize);
1508
1765
  if (size > maxSize) {
1509
1766
  maxSize = size;
1510
1767
  }
@@ -1513,22 +1770,23 @@ function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1513
1770
  }
1514
1771
 
1515
1772
  // src/core/updateTotalSize.ts
1516
- function updateTotalSize(ctx, state) {
1773
+ function updateTotalSize(ctx) {
1774
+ const state = ctx.state;
1517
1775
  const {
1518
1776
  positions,
1519
1777
  props: { data }
1520
1778
  } = state;
1521
1779
  if (data.length === 0) {
1522
- addTotalSize(ctx, state, null, 0);
1780
+ addTotalSize(ctx, null, 0);
1523
1781
  } else {
1524
1782
  const lastId = getId(state, data.length - 1);
1525
1783
  if (lastId !== void 0) {
1526
1784
  const lastPosition = positions.get(lastId);
1527
1785
  if (lastPosition !== void 0) {
1528
- const lastSize = getItemSize(ctx, state, lastId, data.length - 1, data[data.length - 1]);
1786
+ const lastSize = getItemSize(ctx, lastId, data.length - 1, data[data.length - 1]);
1529
1787
  if (lastSize !== void 0) {
1530
1788
  const totalSize = lastPosition + lastSize;
1531
- addTotalSize(ctx, state, null, totalSize);
1789
+ addTotalSize(ctx, null, totalSize);
1532
1790
  }
1533
1791
  }
1534
1792
  }
@@ -1538,43 +1796,45 @@ function updateTotalSize(ctx, state) {
1538
1796
  // src/utils/getScrollVelocity.ts
1539
1797
  var getScrollVelocity = (state) => {
1540
1798
  const { scrollHistory } = state;
1541
- let velocity = 0;
1542
- if (scrollHistory.length >= 1) {
1543
- const newest = scrollHistory[scrollHistory.length - 1];
1544
- let oldest;
1545
- let start = 0;
1546
- const now = Date.now();
1547
- for (let i = 0; i < scrollHistory.length - 1; i++) {
1548
- const entry = scrollHistory[i];
1549
- const nextEntry = scrollHistory[i + 1];
1550
- if (i > 0) {
1551
- const prevEntry = scrollHistory[i - 1];
1552
- const prevDirection = entry.scroll - prevEntry.scroll;
1553
- const currentDirection = nextEntry.scroll - entry.scroll;
1554
- if (prevDirection > 0 && currentDirection < 0 || prevDirection < 0 && currentDirection > 0) {
1555
- start = i;
1556
- break;
1557
- }
1558
- }
1799
+ const newestIndex = scrollHistory.length - 1;
1800
+ if (newestIndex < 1) {
1801
+ return 0;
1802
+ }
1803
+ const newest = scrollHistory[newestIndex];
1804
+ const now = Date.now();
1805
+ let direction = 0;
1806
+ for (let i = newestIndex; i > 0; i--) {
1807
+ const delta = scrollHistory[i].scroll - scrollHistory[i - 1].scroll;
1808
+ if (delta !== 0) {
1809
+ direction = Math.sign(delta);
1810
+ break;
1559
1811
  }
1560
- for (let i = start; i < scrollHistory.length - 1; i++) {
1561
- const entry = scrollHistory[i];
1562
- if (now - entry.time <= 1e3) {
1563
- oldest = entry;
1564
- break;
1565
- }
1812
+ }
1813
+ if (direction === 0) {
1814
+ return 0;
1815
+ }
1816
+ let oldest = newest;
1817
+ for (let i = newestIndex - 1; i >= 0; i--) {
1818
+ const current = scrollHistory[i];
1819
+ const next = scrollHistory[i + 1];
1820
+ const delta = next.scroll - current.scroll;
1821
+ const deltaSign = Math.sign(delta);
1822
+ if (deltaSign !== 0 && deltaSign !== direction) {
1823
+ break;
1566
1824
  }
1567
- if (oldest && oldest !== newest) {
1568
- const scrollDiff = newest.scroll - oldest.scroll;
1569
- const timeDiff = newest.time - oldest.time;
1570
- velocity = timeDiff > 0 ? scrollDiff / timeDiff : 0;
1825
+ if (now - current.time > 1e3) {
1826
+ break;
1571
1827
  }
1828
+ oldest = current;
1572
1829
  }
1573
- return velocity;
1830
+ const scrollDiff = newest.scroll - oldest.scroll;
1831
+ const timeDiff = newest.time - oldest.time;
1832
+ return timeDiff > 0 ? scrollDiff / timeDiff : 0;
1574
1833
  };
1575
1834
 
1576
1835
  // src/utils/updateSnapToOffsets.ts
1577
- function updateSnapToOffsets(ctx, state) {
1836
+ function updateSnapToOffsets(ctx) {
1837
+ const state = ctx.state;
1578
1838
  const {
1579
1839
  positions,
1580
1840
  props: { snapToIndices }
@@ -1589,30 +1849,32 @@ function updateSnapToOffsets(ctx, state) {
1589
1849
  }
1590
1850
 
1591
1851
  // src/core/updateItemPositions.ts
1592
- function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP } = {
1852
+ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP } = {
1593
1853
  doMVCP: false,
1594
1854
  forceFullUpdate: false,
1595
1855
  scrollBottomBuffered: -1,
1596
1856
  startIndex: 0
1597
1857
  }) {
1598
1858
  var _a3, _b, _c, _d, _e;
1859
+ const state = ctx.state;
1599
1860
  const {
1600
1861
  columns,
1601
1862
  indexByKey,
1602
1863
  positions,
1603
1864
  idCache,
1604
1865
  sizesKnown,
1605
- props: { getEstimatedItemSize, snapToIndices, enableAverages }
1866
+ props: { data, getEstimatedItemSize, snapToIndices },
1867
+ scrollingTo
1606
1868
  } = state;
1607
- const data = state.props.data;
1608
1869
  const dataLength = data.length;
1609
1870
  const numColumns = peek$(ctx, "numColumns");
1610
- const scrollingTo = peek$(ctx, "scrollingTo");
1611
1871
  const hasColumns = numColumns > 1;
1612
1872
  const indexByKeyForChecking = IS_DEV ? /* @__PURE__ */ new Map() : void 0;
1613
- const shouldOptimize = !forceFullUpdate && !dataChanged && Math.abs(getScrollVelocity(state)) > 0;
1873
+ const lastScrollDelta = state.lastScrollDelta;
1874
+ const velocity = getScrollVelocity(state);
1875
+ const shouldOptimize = !forceFullUpdate && !dataChanged && (Math.abs(velocity) > 0 || Platform2.OS === "web" && state.scrollLength > 0 && lastScrollDelta > state.scrollLength);
1614
1876
  const maxVisibleArea = scrollBottomBuffered + 1e3;
1615
- const useAverageSize = enableAverages && !getEstimatedItemSize;
1877
+ const useAverageSize = !getEstimatedItemSize;
1616
1878
  const preferCachedSize = !doMVCP || dataChanged || state.scrollAdjustHandler.getAdjust() !== 0 || ((_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0) !== 0;
1617
1879
  let currentRowTop = 0;
1618
1880
  let column = 1;
@@ -1621,7 +1883,6 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1621
1883
  if (hasColumns) {
1622
1884
  const { startIndex: processedStartIndex, currentRowTop: initialRowTop } = prepareColumnStartState(
1623
1885
  ctx,
1624
- state,
1625
1886
  startIndex,
1626
1887
  useAverageSize
1627
1888
  );
@@ -1631,7 +1892,7 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1631
1892
  const prevIndex = startIndex - 1;
1632
1893
  const prevId = getId(state, prevIndex);
1633
1894
  const prevPosition = (_b = positions.get(prevId)) != null ? _b : 0;
1634
- const prevSize = (_c = sizesKnown.get(prevId)) != null ? _c : getItemSize(ctx, state, prevId, prevIndex, data[prevIndex], useAverageSize, preferCachedSize);
1895
+ const prevSize = (_c = sizesKnown.get(prevId)) != null ? _c : getItemSize(ctx, prevId, prevIndex, data[prevIndex], useAverageSize, preferCachedSize);
1635
1896
  currentRowTop = prevPosition + prevSize;
1636
1897
  }
1637
1898
  }
@@ -1648,7 +1909,7 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1648
1909
  breakAt = i + itemsPerRow + 10;
1649
1910
  }
1650
1911
  const id = (_d = idCache[i]) != null ? _d : getId(state, i);
1651
- const size = (_e = sizesKnown.get(id)) != null ? _e : getItemSize(ctx, state, id, i, data[i], useAverageSize, preferCachedSize);
1912
+ const size = (_e = sizesKnown.get(id)) != null ? _e : getItemSize(ctx, id, i, data[i], useAverageSize, preferCachedSize);
1652
1913
  if (IS_DEV && needsIndexByKey) {
1653
1914
  if (indexByKeyForChecking.has(id)) {
1654
1915
  console.error(
@@ -1657,7 +1918,10 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1657
1918
  }
1658
1919
  indexByKeyForChecking.set(id, i);
1659
1920
  }
1660
- positions.set(id, currentRowTop);
1921
+ if (currentRowTop !== positions.get(id)) {
1922
+ positions.set(id, currentRowTop);
1923
+ notifyPosition$(ctx, id, currentRowTop);
1924
+ }
1661
1925
  if (needsIndexByKey) {
1662
1926
  indexByKey.set(id, i);
1663
1927
  }
@@ -1677,10 +1941,10 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1677
1941
  }
1678
1942
  }
1679
1943
  if (!didBreakEarly) {
1680
- updateTotalSize(ctx, state);
1944
+ updateTotalSize(ctx);
1681
1945
  }
1682
1946
  if (snapToIndices) {
1683
- updateSnapToOffsets(ctx, state);
1947
+ updateSnapToOffsets(ctx);
1684
1948
  }
1685
1949
  }
1686
1950
 
@@ -1758,7 +2022,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
1758
2022
  if (previousViewableItems) {
1759
2023
  for (const viewToken of previousViewableItems) {
1760
2024
  const containerId = findContainerId(ctx, viewToken.key);
1761
- if (!isViewable(
2025
+ if (!checkIsViewable(
1762
2026
  state,
1763
2027
  ctx,
1764
2028
  viewabilityConfig,
@@ -1779,7 +2043,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
1779
2043
  if (item) {
1780
2044
  const key = getId(state, i);
1781
2045
  const containerId = findContainerId(ctx, key);
1782
- if (isViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, i)) {
2046
+ if (checkIsViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, i)) {
1783
2047
  const viewToken = {
1784
2048
  containerId,
1785
2049
  index: i,
@@ -1839,11 +2103,11 @@ function computeViewability(state, ctx, viewabilityConfig, containerId, key, scr
1839
2103
  const percentVisible = size ? isEntirelyVisible ? 100 : 100 * (sizeVisible / size) : 0;
1840
2104
  const percentOfScroller = size ? 100 * (sizeVisible / scrollSize) : 0;
1841
2105
  const percent = isEntirelyVisible ? 100 : viewAreaMode ? percentOfScroller : percentVisible;
1842
- const isViewable2 = percent >= viewablePercentThreshold;
2106
+ const isViewable = percent >= viewablePercentThreshold;
1843
2107
  const value = {
1844
2108
  containerId,
1845
2109
  index,
1846
- isViewable: isViewable2,
2110
+ isViewable,
1847
2111
  item,
1848
2112
  key,
1849
2113
  percentOfScroller,
@@ -1862,8 +2126,11 @@ function computeViewability(state, ctx, viewabilityConfig, containerId, key, scr
1862
2126
  }
1863
2127
  return value;
1864
2128
  }
1865
- function isViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
1866
- const value = ctx.mapViewabilityAmountValues.get(containerId) || computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index);
2129
+ function checkIsViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
2130
+ let value = ctx.mapViewabilityAmountValues.get(containerId);
2131
+ if (!value || value.key !== key) {
2132
+ value = computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index);
2133
+ }
1867
2134
  return value.isViewable;
1868
2135
  }
1869
2136
  function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
@@ -1891,8 +2158,9 @@ function checkAllSizesKnown(state) {
1891
2158
  }
1892
2159
 
1893
2160
  // src/utils/findAvailableContainers.ts
1894
- function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers) {
2161
+ function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers) {
1895
2162
  const numContainers = peek$(ctx, "numContainers");
2163
+ const state = ctx.state;
1896
2164
  const { stickyContainerPool, containerItemTypes } = state;
1897
2165
  const result = [];
1898
2166
  const availableContainers = [];
@@ -2012,21 +2280,26 @@ function comparatorByDistance(a, b) {
2012
2280
  }
2013
2281
 
2014
2282
  // src/core/scrollToIndex.ts
2015
- function scrollToIndex(ctx, state, { index, viewOffset = 0, animated = true, viewPosition }) {
2016
- if (index >= state.props.data.length) {
2017
- index = state.props.data.length - 1;
2283
+ function scrollToIndex(ctx, { index, viewOffset = 0, animated = true, viewPosition }) {
2284
+ const state = ctx.state;
2285
+ const { data } = state.props;
2286
+ if (index >= data.length) {
2287
+ index = data.length - 1;
2018
2288
  } else if (index < 0) {
2019
2289
  index = 0;
2020
2290
  }
2021
- const firstIndexOffset = calculateOffsetForIndex(ctx, state, index);
2022
- const isLast = index === state.props.data.length - 1;
2291
+ const firstIndexOffset = calculateOffsetForIndex(ctx, index);
2292
+ const isLast = index === data.length - 1;
2023
2293
  if (isLast && viewPosition === void 0) {
2024
2294
  viewPosition = 1;
2025
2295
  }
2026
2296
  state.scrollForNextCalculateItemsInView = void 0;
2027
- scrollTo(ctx, state, {
2297
+ const targetId = getId(state, index);
2298
+ const itemSize = getItemSize(ctx, targetId, index, state.props.data[index]);
2299
+ scrollTo(ctx, {
2028
2300
  animated,
2029
2301
  index,
2302
+ itemSize,
2030
2303
  offset: firstIndexOffset,
2031
2304
  viewOffset,
2032
2305
  viewPosition: viewPosition != null ? viewPosition : 0
@@ -2034,34 +2307,28 @@ function scrollToIndex(ctx, state, { index, viewOffset = 0, animated = true, vie
2034
2307
  }
2035
2308
 
2036
2309
  // src/utils/setDidLayout.ts
2037
- function setDidLayout(ctx, state) {
2310
+ function setDidLayout(ctx) {
2311
+ const state = ctx.state;
2038
2312
  const {
2039
2313
  loadStartTime,
2040
2314
  initialScroll,
2041
2315
  props: { onLoad }
2042
2316
  } = state;
2043
2317
  state.queuedInitialLayout = true;
2044
- checkAtBottom(ctx, state);
2318
+ checkAtBottom(ctx);
2045
2319
  const setIt = () => {
2046
- set$(ctx, "containersDidLayout", true);
2320
+ setInitialRenderState(ctx, { didLayout: true });
2047
2321
  if (onLoad) {
2048
2322
  onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
2049
2323
  }
2050
2324
  };
2051
- if (Platform2.OS === "android" && initialScroll) {
2052
- if (IsNewArchitecture) {
2053
- scrollToIndex(ctx, state, { ...initialScroll, animated: false });
2054
- requestAnimationFrame(() => {
2055
- scrollToIndex(ctx, state, { ...initialScroll, animated: false });
2056
- setIt();
2057
- });
2058
- } else {
2059
- scrollToIndex(ctx, state, { ...initialScroll, animated: false });
2060
- setIt();
2061
- }
2062
- } else {
2063
- setIt();
2325
+ if ((initialScroll == null ? void 0 : initialScroll.index) !== void 0) {
2326
+ const target = initialScroll;
2327
+ const runScroll = () => scrollToIndex(ctx, { ...target, animated: false });
2328
+ runScroll();
2329
+ requestAnimationFrame(runScroll);
2064
2330
  }
2331
+ setIt();
2065
2332
  }
2066
2333
 
2067
2334
  // src/core/calculateItemsInView.ts
@@ -2079,15 +2346,17 @@ function findCurrentStickyIndex(stickyArray, scroll, state) {
2079
2346
  }
2080
2347
  return -1;
2081
2348
  }
2082
- function getActiveStickyIndices(ctx, state, stickyHeaderIndices) {
2349
+ function getActiveStickyIndices(ctx, stickyHeaderIndices) {
2350
+ const state = ctx.state;
2083
2351
  return new Set(
2084
2352
  Array.from(state.stickyContainerPool).map((i) => peek$(ctx, `containerItemKey${i}`)).map((key) => key ? state.indexByKey.get(key) : void 0).filter((idx) => idx !== void 0 && stickyHeaderIndices.has(idx))
2085
2353
  );
2086
2354
  }
2087
- function handleStickyActivation(ctx, state, stickyHeaderIndices, stickyArray, currentStickyIdx, needNewContainers, startBuffered, endBuffered) {
2355
+ function handleStickyActivation(ctx, stickyHeaderIndices, stickyArray, currentStickyIdx, needNewContainers, startBuffered, endBuffered) {
2088
2356
  var _a3;
2089
- const activeIndices = getActiveStickyIndices(ctx, state, stickyHeaderIndices);
2090
- state.activeStickyIndex = currentStickyIdx >= 0 ? stickyArray[currentStickyIdx] : void 0;
2357
+ const state = ctx.state;
2358
+ const activeIndices = getActiveStickyIndices(ctx, stickyHeaderIndices);
2359
+ set$(ctx, "activeStickyIndex", currentStickyIdx >= 0 ? stickyArray[currentStickyIdx] : -1);
2091
2360
  for (let offset = 0; offset <= 1; offset++) {
2092
2361
  const idx = currentStickyIdx - offset;
2093
2362
  if (idx < 0 || activeIndices.has(stickyArray[idx])) continue;
@@ -2098,8 +2367,9 @@ function handleStickyActivation(ctx, state, stickyHeaderIndices, stickyArray, cu
2098
2367
  }
2099
2368
  }
2100
2369
  }
2101
- function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, currentStickyIdx, pendingRemoval) {
2370
+ function handleStickyRecycling(ctx, stickyArray, scroll, scrollBuffer, currentStickyIdx, pendingRemoval) {
2102
2371
  var _a3, _b, _c;
2372
+ const state = ctx.state;
2103
2373
  for (const containerIndex of state.stickyContainerPool) {
2104
2374
  const itemKey = peek$(ctx, `containerItemKey${containerIndex}`);
2105
2375
  const itemIndex = itemKey ? state.indexByKey.get(itemKey) : void 0;
@@ -2123,7 +2393,7 @@ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, cu
2123
2393
  const currentId = (_b = state.idCache[itemIndex]) != null ? _b : getId(state, itemIndex);
2124
2394
  if (currentId) {
2125
2395
  const currentPos = state.positions.get(currentId);
2126
- const currentSize = (_c = state.sizes.get(currentId)) != null ? _c : getItemSize(ctx, state, currentId, itemIndex, state.props.data[itemIndex]);
2396
+ const currentSize = (_c = state.sizes.get(currentId)) != null ? _c : getItemSize(ctx, currentId, itemIndex, state.props.data[itemIndex]);
2127
2397
  shouldRecycle = currentPos !== void 0 && scroll > currentPos + currentSize + scrollBuffer * 3;
2128
2398
  }
2129
2399
  }
@@ -2132,7 +2402,8 @@ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, cu
2132
2402
  }
2133
2403
  }
2134
2404
  }
2135
- function calculateItemsInView(ctx, state, params = {}) {
2405
+ function calculateItemsInView(ctx, params = {}) {
2406
+ const state = ctx.state;
2136
2407
  reactNative.unstable_batchedUpdates(() => {
2137
2408
  var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j;
2138
2409
  const {
@@ -2156,8 +2427,8 @@ function calculateItemsInView(ctx, state, params = {}) {
2156
2427
  const stickyIndicesSet = state.props.stickyIndicesSet || /* @__PURE__ */ new Set();
2157
2428
  const prevNumContainers = peek$(ctx, "numContainers");
2158
2429
  if (!data || scrollLength === 0 || !prevNumContainers) {
2159
- if (state.initialAnchor) {
2160
- ensureInitialAnchor(ctx, state);
2430
+ if (!IsNewArchitecture && state.initialAnchor) {
2431
+ ensureInitialAnchor(ctx);
2161
2432
  }
2162
2433
  return;
2163
2434
  }
@@ -2172,15 +2443,14 @@ function calculateItemsInView(ctx, state, params = {}) {
2172
2443
  if (!queuedInitialLayout && initialScroll) {
2173
2444
  const updatedOffset = calculateOffsetWithOffsetPosition(
2174
2445
  ctx,
2175
- state,
2176
- calculateOffsetForIndex(ctx, state, initialScroll.index),
2446
+ calculateOffsetForIndex(ctx, initialScroll.index),
2177
2447
  initialScroll
2178
2448
  );
2179
2449
  scrollState = updatedOffset;
2180
2450
  }
2181
2451
  const scrollAdjustPending = (_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0;
2182
2452
  const scrollAdjustPad = scrollAdjustPending - topPad;
2183
- let scroll = scrollState + scrollExtra + scrollAdjustPad;
2453
+ let scroll = Math.round(scrollState + scrollExtra + scrollAdjustPad);
2184
2454
  if (scroll + scrollLength > totalSize) {
2185
2455
  scroll = Math.max(0, totalSize - scrollLength);
2186
2456
  }
@@ -2188,11 +2458,12 @@ function calculateItemsInView(ctx, state, params = {}) {
2188
2458
  set$(ctx, "debugRawScroll", scrollState);
2189
2459
  set$(ctx, "debugComputedScroll", scroll);
2190
2460
  }
2191
- const previousStickyIndex = state.activeStickyIndex;
2461
+ const previousStickyIndex = peek$(ctx, "activeStickyIndex");
2192
2462
  const currentStickyIdx = stickyIndicesArr.length > 0 ? findCurrentStickyIndex(stickyIndicesArr, scroll, state) : -1;
2193
- const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyIndicesArr[currentStickyIdx] : void 0;
2194
- state.activeStickyIndex = nextActiveStickyIndex;
2195
- set$(ctx, "activeStickyIndex", nextActiveStickyIndex);
2463
+ const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyIndicesArr[currentStickyIdx] : -1;
2464
+ if (currentStickyIdx >= 0 || previousStickyIndex >= 0) {
2465
+ set$(ctx, "activeStickyIndex", nextActiveStickyIndex);
2466
+ }
2196
2467
  let scrollBufferTop = scrollBuffer;
2197
2468
  let scrollBufferBottom = scrollBuffer;
2198
2469
  if (speed > 0 || speed === 0 && scroll < Math.max(50, scrollBuffer)) {
@@ -2205,23 +2476,25 @@ function calculateItemsInView(ctx, state, params = {}) {
2205
2476
  const scrollTopBuffered = scroll - scrollBufferTop;
2206
2477
  const scrollBottom = scroll + scrollLength + (scroll < 0 ? -scroll : 0);
2207
2478
  const scrollBottomBuffered = scrollBottom + scrollBufferBottom;
2208
- if (!dataChanged && scrollForNextCalculateItemsInView) {
2479
+ if (!dataChanged && !forceFullItemPositions && scrollForNextCalculateItemsInView) {
2209
2480
  const { top, bottom } = scrollForNextCalculateItemsInView;
2210
- if (scrollTopBuffered > top && scrollBottomBuffered < bottom) {
2211
- if (state.initialAnchor) {
2212
- ensureInitialAnchor(ctx, state);
2481
+ if (top === null && bottom === null) {
2482
+ state.scrollForNextCalculateItemsInView = void 0;
2483
+ } else if ((top === null || scrollTopBuffered > top) && (bottom === null || scrollBottomBuffered < bottom)) {
2484
+ if (!IsNewArchitecture && state.initialAnchor) {
2485
+ ensureInitialAnchor(ctx);
2213
2486
  }
2214
2487
  return;
2215
2488
  }
2216
2489
  }
2217
- const checkMVCP = doMVCP ? prepareMVCP(ctx, state, dataChanged) : void 0;
2490
+ const checkMVCP = doMVCP ? prepareMVCP(ctx, dataChanged) : void 0;
2218
2491
  if (dataChanged) {
2219
2492
  indexByKey.clear();
2220
2493
  idCache.length = 0;
2221
2494
  positions.clear();
2222
2495
  }
2223
- const startIndex = dataChanged ? 0 : (_b = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _b : 0;
2224
- updateItemPositions(ctx, state, dataChanged, {
2496
+ const startIndex = forceFullItemPositions || dataChanged ? 0 : (_b = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _b : 0;
2497
+ updateItemPositions(ctx, dataChanged, {
2225
2498
  doMVCP,
2226
2499
  forceFullUpdate: !!forceFullItemPositions,
2227
2500
  scrollBottomBuffered,
@@ -2240,9 +2513,9 @@ function calculateItemsInView(ctx, state, params = {}) {
2240
2513
  for (let i = loopStart; i >= 0; i--) {
2241
2514
  const id = (_c = idCache[i]) != null ? _c : getId(state, i);
2242
2515
  const top = positions.get(id);
2243
- const size = (_d = sizes.get(id)) != null ? _d : getItemSize(ctx, state, id, i, data[i]);
2516
+ const size = (_d = sizes.get(id)) != null ? _d : getItemSize(ctx, id, i, data[i]);
2244
2517
  const bottom = top + size;
2245
- if (bottom > scroll - scrollBuffer) {
2518
+ if (bottom > scroll - scrollBufferTop) {
2246
2519
  loopStart = i;
2247
2520
  } else {
2248
2521
  break;
@@ -2267,7 +2540,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2267
2540
  const dataLength = data.length;
2268
2541
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
2269
2542
  const id = (_e = idCache[i]) != null ? _e : getId(state, i);
2270
- const size = (_f = sizes.get(id)) != null ? _f : getItemSize(ctx, state, id, i, data[i]);
2543
+ const size = (_f = sizes.get(id)) != null ? _f : getItemSize(ctx, id, i, data[i]);
2271
2544
  const top = positions.get(id);
2272
2545
  if (!foundEnd) {
2273
2546
  if (startNoBuffer === null && top + size > scroll) {
@@ -2279,7 +2552,11 @@ function calculateItemsInView(ctx, state, params = {}) {
2279
2552
  if (startBuffered === null && top + size > scrollTopBuffered) {
2280
2553
  startBuffered = i;
2281
2554
  startBufferedId = id;
2282
- nextTop = top;
2555
+ if (scrollTopBuffered < 0) {
2556
+ nextTop = null;
2557
+ } else {
2558
+ nextTop = top;
2559
+ }
2283
2560
  }
2284
2561
  if (startNoBuffer !== null) {
2285
2562
  if (top <= scrollBottom) {
@@ -2287,7 +2564,11 @@ function calculateItemsInView(ctx, state, params = {}) {
2287
2564
  }
2288
2565
  if (top <= scrollBottomBuffered) {
2289
2566
  endBuffered = i;
2290
- nextBottom = top + size;
2567
+ if (scrollBottomBuffered > totalSize) {
2568
+ nextBottom = null;
2569
+ } else {
2570
+ nextBottom = top + size;
2571
+ }
2291
2572
  } else {
2292
2573
  foundEnd = true;
2293
2574
  }
@@ -2309,12 +2590,12 @@ function calculateItemsInView(ctx, state, params = {}) {
2309
2590
  startNoBuffer
2310
2591
  });
2311
2592
  if (enableScrollForNextCalculateItemsInView && nextTop !== void 0 && nextBottom !== void 0) {
2312
- state.scrollForNextCalculateItemsInView = nextTop !== void 0 && nextBottom !== void 0 ? {
2593
+ state.scrollForNextCalculateItemsInView = isNullOrUndefined(nextTop) && isNullOrUndefined(nextBottom) ? void 0 : {
2313
2594
  bottom: nextBottom,
2314
2595
  top: nextTop
2315
- } : void 0;
2596
+ };
2316
2597
  }
2317
- const numContainers = peek$(ctx, "numContainers");
2598
+ let numContainers = prevNumContainers;
2318
2599
  const pendingRemoval = [];
2319
2600
  if (dataChanged) {
2320
2601
  for (let i = 0; i < numContainers; i++) {
@@ -2325,7 +2606,6 @@ function calculateItemsInView(ctx, state, params = {}) {
2325
2606
  }
2326
2607
  }
2327
2608
  if (startBuffered !== null && endBuffered !== null) {
2328
- let numContainers2 = prevNumContainers;
2329
2609
  const needNewContainers = [];
2330
2610
  for (let i = startBuffered; i <= endBuffered; i++) {
2331
2611
  const id = (_h = idCache[i]) != null ? _h : getId(state, i);
@@ -2336,7 +2616,6 @@ function calculateItemsInView(ctx, state, params = {}) {
2336
2616
  if (stickyIndicesArr.length > 0) {
2337
2617
  handleStickyActivation(
2338
2618
  ctx,
2339
- state,
2340
2619
  stickyIndicesSet,
2341
2620
  stickyIndicesArr,
2342
2621
  currentStickyIdx,
@@ -2344,18 +2623,16 @@ function calculateItemsInView(ctx, state, params = {}) {
2344
2623
  startBuffered,
2345
2624
  endBuffered
2346
2625
  );
2347
- } else {
2348
- state.activeStickyIndex = void 0;
2349
- set$(ctx, "activeStickyIndex", void 0);
2626
+ } else if (previousStickyIndex !== -1) {
2627
+ set$(ctx, "activeStickyIndex", -1);
2350
2628
  }
2351
2629
  if (needNewContainers.length > 0) {
2352
2630
  const requiredItemTypes = getItemType ? needNewContainers.map((i) => {
2353
2631
  const itemType = getItemType(data[i], i);
2354
- return itemType ? String(itemType) : "";
2632
+ return itemType !== void 0 ? String(itemType) : "";
2355
2633
  }) : void 0;
2356
2634
  const availableContainers = findAvailableContainers(
2357
2635
  ctx,
2358
- state,
2359
2636
  needNewContainers.length,
2360
2637
  startBuffered,
2361
2638
  endBuffered,
@@ -2376,30 +2653,31 @@ function calculateItemsInView(ctx, state, params = {}) {
2376
2653
  if (requiredItemTypes) {
2377
2654
  state.containerItemTypes.set(containerIndex, requiredItemTypes[idx]);
2378
2655
  }
2379
- containerItemKeys.add(id);
2656
+ containerItemKeys.set(id, containerIndex);
2657
+ const containerSticky = `containerSticky${containerIndex}`;
2380
2658
  if (stickyIndicesSet.has(i)) {
2381
- set$(ctx, `containerSticky${containerIndex}`, true);
2659
+ set$(ctx, containerSticky, true);
2382
2660
  const topPadding = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
2383
2661
  set$(ctx, `containerStickyOffset${containerIndex}`, topPadding);
2384
2662
  state.stickyContainerPool.add(containerIndex);
2385
- } else {
2386
- set$(ctx, `containerSticky${containerIndex}`, false);
2663
+ } else if (peek$(ctx, containerSticky)) {
2664
+ set$(ctx, containerSticky, false);
2387
2665
  state.stickyContainerPool.delete(containerIndex);
2388
2666
  }
2389
- if (containerIndex >= numContainers2) {
2390
- numContainers2 = containerIndex + 1;
2667
+ if (containerIndex >= numContainers) {
2668
+ numContainers = containerIndex + 1;
2391
2669
  }
2392
2670
  }
2393
- if (numContainers2 !== prevNumContainers) {
2394
- set$(ctx, "numContainers", numContainers2);
2395
- if (numContainers2 > peek$(ctx, "numContainersPooled")) {
2396
- set$(ctx, "numContainersPooled", Math.ceil(numContainers2 * 1.5));
2671
+ if (numContainers !== prevNumContainers) {
2672
+ set$(ctx, "numContainers", numContainers);
2673
+ if (numContainers > peek$(ctx, "numContainersPooled")) {
2674
+ set$(ctx, "numContainersPooled", Math.ceil(numContainers * 1.5));
2397
2675
  }
2398
2676
  }
2399
2677
  }
2400
2678
  }
2401
2679
  if (stickyIndicesArr.length > 0) {
2402
- handleStickyRecycling(ctx, state, stickyIndicesArr, scroll, scrollBuffer, currentStickyIdx, pendingRemoval);
2680
+ handleStickyRecycling(ctx, stickyIndicesArr, scroll, scrollBuffer, currentStickyIdx, pendingRemoval);
2403
2681
  }
2404
2682
  let didChangePositions = false;
2405
2683
  for (let i = 0; i < numContainers; i++) {
@@ -2451,7 +2729,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2451
2729
  }
2452
2730
  if (!queuedInitialLayout && endBuffered !== null) {
2453
2731
  if (checkAllSizesKnown(state)) {
2454
- setDidLayout(ctx, state);
2732
+ setDidLayout(ctx);
2455
2733
  }
2456
2734
  }
2457
2735
  if (viewabilityConfigCallbackPairs) {
@@ -2464,8 +2742,8 @@ function calculateItemsInView(ctx, state, params = {}) {
2464
2742
  }
2465
2743
  }
2466
2744
  });
2467
- if (state.initialAnchor) {
2468
- ensureInitialAnchor(ctx, state);
2745
+ if (!IsNewArchitecture && state.initialAnchor) {
2746
+ ensureInitialAnchor(ctx);
2469
2747
  }
2470
2748
  }
2471
2749
 
@@ -2490,19 +2768,22 @@ function checkActualChange(state, dataProp, previousData) {
2490
2768
  }
2491
2769
 
2492
2770
  // src/core/doMaintainScrollAtEnd.ts
2493
- function doMaintainScrollAtEnd(ctx, state, animated) {
2771
+ function doMaintainScrollAtEnd(ctx, animated) {
2772
+ const state = ctx.state;
2494
2773
  const {
2774
+ didContainersLayout,
2775
+ isAtEnd,
2495
2776
  refScroller,
2496
2777
  props: { maintainScrollAtEnd }
2497
2778
  } = state;
2498
- if ((state == null ? void 0 : state.isAtEnd) && maintainScrollAtEnd && peek$(ctx, "containersDidLayout")) {
2779
+ if (isAtEnd && maintainScrollAtEnd && didContainersLayout) {
2499
2780
  const paddingTop = peek$(ctx, "alignItemsPaddingTop");
2500
2781
  if (paddingTop > 0) {
2501
2782
  state.scroll = 0;
2502
2783
  }
2503
2784
  requestAnimationFrame(() => {
2504
2785
  var _a3;
2505
- if (state == null ? void 0 : state.isAtEnd) {
2786
+ if (state.isAtEnd) {
2506
2787
  state.maintainingScrollAtEnd = true;
2507
2788
  (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
2508
2789
  animated
@@ -2573,28 +2854,30 @@ function updateAveragesOnDataChange(state, oldData, newData) {
2573
2854
  }
2574
2855
 
2575
2856
  // src/core/checkResetContainers.ts
2576
- function checkResetContainers(ctx, state, dataProp) {
2857
+ function checkResetContainers(ctx, dataProp) {
2858
+ const state = ctx.state;
2577
2859
  const { previousData } = state;
2578
2860
  if (previousData) {
2579
2861
  updateAveragesOnDataChange(state, previousData, dataProp);
2580
2862
  }
2581
2863
  const { maintainScrollAtEnd } = state.props;
2582
- calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
2864
+ calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
2583
2865
  const shouldMaintainScrollAtEnd = maintainScrollAtEnd === true || maintainScrollAtEnd.onDataChange;
2584
- const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, state, false);
2866
+ const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, false);
2585
2867
  if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
2586
2868
  state.isEndReached = false;
2587
2869
  }
2588
2870
  if (!didMaintainScrollAtEnd) {
2589
2871
  checkAtTop(state);
2590
- checkAtBottom(ctx, state);
2872
+ checkAtBottom(ctx);
2591
2873
  }
2592
2874
  delete state.previousData;
2593
2875
  }
2594
2876
 
2595
2877
  // src/core/doInitialAllocateContainers.ts
2596
- function doInitialAllocateContainers(ctx, state) {
2878
+ function doInitialAllocateContainers(ctx) {
2597
2879
  var _a3, _b, _c;
2880
+ const state = ctx.state;
2598
2881
  const {
2599
2882
  scrollLength,
2600
2883
  props: {
@@ -2615,8 +2898,10 @@ function doInitialAllocateContainers(ctx, state) {
2615
2898
  const num = Math.min(20, data.length);
2616
2899
  for (let i = 0; i < num; i++) {
2617
2900
  const item = data[i];
2618
- const itemType = getItemType ? (_a3 = getItemType(item, i)) != null ? _a3 : "" : "";
2619
- totalSize += (_c = (_b = getFixedItemSize == null ? void 0 : getFixedItemSize(i, item, itemType)) != null ? _b : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(i, item, itemType)) != null ? _c : estimatedItemSize;
2901
+ if (item !== void 0) {
2902
+ const itemType = (_a3 = getItemType == null ? void 0 : getItemType(item, i)) != null ? _a3 : "";
2903
+ totalSize += (_c = (_b = getFixedItemSize == null ? void 0 : getFixedItemSize(item, i, itemType)) != null ? _b : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(item, i, itemType)) != null ? _c : estimatedItemSize;
2904
+ }
2620
2905
  }
2621
2906
  averageItemSize = totalSize / num;
2622
2907
  } else {
@@ -2632,10 +2917,10 @@ function doInitialAllocateContainers(ctx, state) {
2632
2917
  if (!IsNewArchitecture || state.lastLayout) {
2633
2918
  if (state.initialScroll) {
2634
2919
  requestAnimationFrame(() => {
2635
- calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
2920
+ calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
2636
2921
  });
2637
2922
  } else {
2638
- calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
2923
+ calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
2639
2924
  }
2640
2925
  }
2641
2926
  return true;
@@ -2643,7 +2928,8 @@ function doInitialAllocateContainers(ctx, state) {
2643
2928
  }
2644
2929
 
2645
2930
  // src/core/handleLayout.ts
2646
- function handleLayout(ctx, state, layout, setCanRender) {
2931
+ function handleLayout(ctx, layout, setCanRender) {
2932
+ const state = ctx.state;
2647
2933
  const { maintainScrollAtEnd } = state.props;
2648
2934
  const measuredLength = layout[state.props.horizontal ? "width" : "height"];
2649
2935
  const previousLength = state.scrollLength;
@@ -2659,19 +2945,19 @@ function handleLayout(ctx, state, layout, setCanRender) {
2659
2945
  state.lastBatchingAction = Date.now();
2660
2946
  state.scrollForNextCalculateItemsInView = void 0;
2661
2947
  if (scrollLength > 0) {
2662
- doInitialAllocateContainers(ctx, state);
2948
+ doInitialAllocateContainers(ctx);
2663
2949
  }
2664
2950
  if (needsCalculate) {
2665
- calculateItemsInView(ctx, state, { doMVCP: true });
2951
+ calculateItemsInView(ctx, { doMVCP: true });
2666
2952
  }
2667
2953
  if (didChange || otherAxisSize !== prevOtherAxisSize) {
2668
2954
  set$(ctx, "scrollSize", { height: layout.height, width: layout.width });
2669
2955
  }
2670
2956
  if (maintainScrollAtEnd === true || maintainScrollAtEnd.onLayout) {
2671
- doMaintainScrollAtEnd(ctx, state, false);
2957
+ doMaintainScrollAtEnd(ctx, false);
2672
2958
  }
2673
- updateAlignItemsPaddingTop(ctx, state);
2674
- checkAtBottom(ctx, state);
2959
+ updateAlignItemsPaddingTop(ctx);
2960
+ checkAtBottom(ctx);
2675
2961
  checkAtTop(state);
2676
2962
  if (state) {
2677
2963
  state.needsOtherAxisSize = otherAxisSize - (state.props.stylePaddingTop || 0) < 10;
@@ -2687,8 +2973,9 @@ function handleLayout(ctx, state, layout, setCanRender) {
2687
2973
  }
2688
2974
 
2689
2975
  // src/core/onScroll.ts
2690
- function onScroll(ctx, state, event) {
2976
+ function onScroll(ctx, event) {
2691
2977
  var _a3, _b, _c;
2978
+ const state = ctx.state;
2692
2979
  const {
2693
2980
  scrollProcessingEnabled,
2694
2981
  props: { onScroll: onScrollProp }
@@ -2699,9 +2986,25 @@ function onScroll(ctx, state, event) {
2699
2986
  if (((_b = (_a3 = event.nativeEvent) == null ? void 0 : _a3.contentSize) == null ? void 0 : _b.height) === 0 && ((_c = event.nativeEvent.contentSize) == null ? void 0 : _c.width) === 0) {
2700
2987
  return;
2701
2988
  }
2702
- const newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
2989
+ let newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
2990
+ if (state.scrollingTo) {
2991
+ const maxOffset = clampScrollOffset(ctx, newScroll);
2992
+ if (newScroll !== maxOffset && Math.abs(newScroll - maxOffset) > 1) {
2993
+ newScroll = maxOffset;
2994
+ scrollTo(ctx, {
2995
+ forceScroll: true,
2996
+ isInitialScroll: true,
2997
+ noScrollingTo: true,
2998
+ offset: newScroll
2999
+ });
3000
+ return;
3001
+ }
3002
+ }
2703
3003
  state.scrollPending = newScroll;
2704
- updateScroll(ctx, state, newScroll);
3004
+ updateScroll(ctx, newScroll);
3005
+ if (state.scrollingTo) {
3006
+ checkFinishedScroll(ctx);
3007
+ }
2705
3008
  onScrollProp == null ? void 0 : onScrollProp(event);
2706
3009
  }
2707
3010
 
@@ -2710,51 +3013,58 @@ var ScrollAdjustHandler = class {
2710
3013
  constructor(ctx) {
2711
3014
  this.appliedAdjust = 0;
2712
3015
  this.pendingAdjust = 0;
2713
- this.mounted = false;
2714
- this.context = ctx;
2715
- if (Platform2.OS === "web") {
2716
- const commitPendingAdjust = () => {
2717
- const state = this.context.internalState;
2718
- const pending = this.pendingAdjust;
2719
- if (pending !== 0) {
2720
- this.pendingAdjust = 0;
2721
- this.appliedAdjust += pending;
2722
- state.scroll += pending;
2723
- state.scrollForNextCalculateItemsInView = void 0;
2724
- set$(this.context, "scrollAdjustPending", 0);
2725
- set$(this.context, "scrollAdjust", this.appliedAdjust);
2726
- calculateItemsInView(this.context, this.context.internalState);
2727
- }
2728
- };
2729
- listen$(this.context, "scrollingTo", (value) => {
2730
- if (value === void 0) {
2731
- commitPendingAdjust();
2732
- }
2733
- });
2734
- }
3016
+ this.ctx = ctx;
2735
3017
  }
2736
3018
  requestAdjust(add) {
2737
- const scrollingTo = peek$(this.context, "scrollingTo");
2738
- if (Platform2.OS === "web" && (scrollingTo == null ? void 0 : scrollingTo.animated) && !scrollingTo.isInitialScroll) {
3019
+ const scrollingTo = this.ctx.state.scrollingTo;
3020
+ if (PlatformAdjustBreaksScroll && (scrollingTo == null ? void 0 : scrollingTo.animated) && !scrollingTo.isInitialScroll) {
2739
3021
  this.pendingAdjust += add;
2740
- set$(this.context, "scrollAdjustPending", this.pendingAdjust);
3022
+ set$(this.ctx, "scrollAdjustPending", this.pendingAdjust);
2741
3023
  } else {
2742
3024
  this.appliedAdjust += add;
2743
- set$(this.context, "scrollAdjust", this.appliedAdjust);
3025
+ set$(this.ctx, "scrollAdjust", this.appliedAdjust);
3026
+ }
3027
+ if (this.ctx.state.scrollingTo) {
3028
+ checkFinishedScroll(this.ctx);
2744
3029
  }
2745
- }
2746
- setMounted() {
2747
- this.mounted = true;
2748
3030
  }
2749
3031
  getAdjust() {
2750
3032
  return this.appliedAdjust;
2751
3033
  }
3034
+ commitPendingAdjust(scrollTarget) {
3035
+ if (PlatformAdjustBreaksScroll) {
3036
+ const state = this.ctx.state;
3037
+ const pending = this.pendingAdjust;
3038
+ this.pendingAdjust = 0;
3039
+ if (pending !== 0) {
3040
+ let targetScroll;
3041
+ if ((scrollTarget == null ? void 0 : scrollTarget.index) !== void 0) {
3042
+ const currentOffset = calculateOffsetForIndex(this.ctx, scrollTarget.index);
3043
+ targetScroll = calculateOffsetWithOffsetPosition(this.ctx, currentOffset, scrollTarget);
3044
+ targetScroll = clampScrollOffset(this.ctx, targetScroll);
3045
+ } else {
3046
+ targetScroll = clampScrollOffset(this.ctx, state.scroll + pending);
3047
+ }
3048
+ const adjustment = targetScroll - state.scroll;
3049
+ if (Math.abs(adjustment) > 0.1 || Math.abs(pending) > 0.1) {
3050
+ this.appliedAdjust += adjustment;
3051
+ state.scroll = targetScroll;
3052
+ state.scrollForNextCalculateItemsInView = void 0;
3053
+ set$(this.ctx, "scrollAdjust", this.appliedAdjust);
3054
+ }
3055
+ set$(this.ctx, "scrollAdjustPending", 0);
3056
+ calculateItemsInView(this.ctx);
3057
+ }
3058
+ }
3059
+ }
2752
3060
  };
2753
3061
 
2754
3062
  // src/core/updateItemSize.ts
2755
- function updateItemSize(ctx, state, itemKey, sizeObj) {
3063
+ function updateItemSize(ctx, itemKey, sizeObj) {
2756
3064
  var _a3;
3065
+ const state = ctx.state;
2757
3066
  const {
3067
+ didContainersLayout,
2758
3068
  sizesKnown,
2759
3069
  props: {
2760
3070
  getFixedItemSize,
@@ -2777,31 +3087,24 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
2777
3087
  return;
2778
3088
  }
2779
3089
  const type = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
2780
- const size2 = getFixedItemSize(index, itemData, type);
3090
+ const size2 = getFixedItemSize(itemData, index, type);
2781
3091
  if (size2 !== void 0 && size2 === sizesKnown.get(itemKey)) {
2782
3092
  return;
2783
3093
  }
2784
3094
  }
2785
- const containersDidLayout = peek$(ctx, "containersDidLayout");
2786
- let needsRecalculate = !containersDidLayout;
3095
+ let needsRecalculate = !didContainersLayout;
2787
3096
  let shouldMaintainScrollAtEnd = false;
2788
3097
  let minIndexSizeChanged;
2789
3098
  let maxOtherAxisSize = peek$(ctx, "otherAxisSize") || 0;
2790
3099
  const prevSizeKnown = state.sizesKnown.get(itemKey);
2791
- const diff = updateOneItemSize(ctx, state, itemKey, sizeObj);
3100
+ const diff = updateOneItemSize(ctx, itemKey, sizeObj);
2792
3101
  const size = roundSize(horizontal ? sizeObj.width : sizeObj.height);
2793
3102
  if (diff !== 0) {
2794
3103
  minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
2795
3104
  const { startBuffered, endBuffered } = state;
2796
3105
  needsRecalculate || (needsRecalculate = index >= startBuffered && index <= endBuffered);
2797
- if (!needsRecalculate) {
2798
- const numContainers = ctx.values.get("numContainers");
2799
- for (let i = 0; i < numContainers; i++) {
2800
- if (peek$(ctx, `containerItemKey${i}`) === itemKey) {
2801
- needsRecalculate = true;
2802
- break;
2803
- }
2804
- }
3106
+ if (!needsRecalculate && state.containerItemKeys.has(itemKey)) {
3107
+ needsRecalculate = true;
2805
3108
  }
2806
3109
  if (state.needsOtherAxisSize) {
2807
3110
  const otherAxisSize = horizontal ? sizeObj.height : sizeObj.width;
@@ -2837,22 +3140,22 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
2837
3140
  if (!cur || maxOtherAxisSize > cur) {
2838
3141
  set$(ctx, "otherAxisSize", maxOtherAxisSize);
2839
3142
  }
2840
- if (containersDidLayout || checkAllSizesKnown(state)) {
3143
+ if (didContainersLayout || checkAllSizesKnown(state)) {
2841
3144
  if (needsRecalculate) {
2842
3145
  state.scrollForNextCalculateItemsInView = void 0;
2843
- calculateItemsInView(ctx, state, { doMVCP: true });
3146
+ calculateItemsInView(ctx, { doMVCP: true });
2844
3147
  }
2845
3148
  if (shouldMaintainScrollAtEnd) {
2846
3149
  if (maintainScrollAtEnd === true || maintainScrollAtEnd.onItemLayout) {
2847
- doMaintainScrollAtEnd(ctx, state, false);
3150
+ doMaintainScrollAtEnd(ctx, false);
2848
3151
  }
2849
3152
  }
2850
3153
  }
2851
3154
  }
2852
- function updateOneItemSize(ctx, state, itemKey, sizeObj) {
3155
+ function updateOneItemSize(ctx, itemKey, sizeObj) {
2853
3156
  var _a3;
3157
+ const state = ctx.state;
2854
3158
  const {
2855
- sizes,
2856
3159
  indexByKey,
2857
3160
  sizesKnown,
2858
3161
  averageSizes,
@@ -2860,9 +3163,10 @@ function updateOneItemSize(ctx, state, itemKey, sizeObj) {
2860
3163
  } = state;
2861
3164
  if (!data) return 0;
2862
3165
  const index = indexByKey.get(itemKey);
2863
- const prevSize = getItemSize(ctx, state, itemKey, index, data[index]);
3166
+ const prevSize = getItemSize(ctx, itemKey, index, data[index]);
2864
3167
  const rawSize = horizontal ? sizeObj.width : sizeObj.height;
2865
3168
  const size = Platform2.OS === "web" ? Math.round(rawSize) : roundSize(rawSize);
3169
+ const prevSizeKnown = sizesKnown.get(itemKey);
2866
3170
  sizesKnown.set(itemKey, size);
2867
3171
  if (!getEstimatedItemSize && !getFixedItemSize && size > 0) {
2868
3172
  const itemType = getItemType ? (_a3 = getItemType(data[index], index)) != null ? _a3 : "" : "";
@@ -2870,15 +3174,25 @@ function updateOneItemSize(ctx, state, itemKey, sizeObj) {
2870
3174
  if (!averages) {
2871
3175
  averages = averageSizes[itemType] = { avg: 0, num: 0 };
2872
3176
  }
2873
- averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
2874
- averages.num++;
3177
+ if (prevSizeKnown !== void 0 && prevSizeKnown > 0) {
3178
+ averages.avg += (size - prevSizeKnown) / averages.num;
3179
+ } else {
3180
+ averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
3181
+ averages.num++;
3182
+ }
2875
3183
  }
2876
3184
  if (!prevSize || Math.abs(prevSize - size) > 0.1) {
2877
- setSize(ctx, state, itemKey, size);
3185
+ setSize(ctx, itemKey, size);
2878
3186
  return size - prevSize;
2879
3187
  }
2880
3188
  return 0;
2881
3189
  }
3190
+ function useWrapIfItem(fn) {
3191
+ return React2.useMemo(
3192
+ () => fn ? (arg1, arg2, arg3) => arg1 !== void 0 && arg2 !== void 0 ? fn(arg1, arg2, arg3) : void 0 : void 0,
3193
+ [fn]
3194
+ );
3195
+ }
2882
3196
  var useCombinedRef = (...refs) => {
2883
3197
  const callback = React2.useCallback((element) => {
2884
3198
  for (const ref of refs) {
@@ -2940,14 +3254,15 @@ function createColumnWrapperStyle(contentContainerStyle) {
2940
3254
  }
2941
3255
 
2942
3256
  // src/utils/createImperativeHandle.ts
2943
- function createImperativeHandle(ctx, state) {
3257
+ function createImperativeHandle(ctx) {
3258
+ const state = ctx.state;
2944
3259
  const scrollIndexIntoView = (options) => {
2945
3260
  if (state) {
2946
3261
  const { index, ...rest } = options;
2947
3262
  const { startNoBuffer, endNoBuffer } = state;
2948
3263
  if (index < startNoBuffer || index > endNoBuffer) {
2949
3264
  const viewPosition = index < startNoBuffer ? 0 : 1;
2950
- scrollToIndex(ctx, state, {
3265
+ scrollToIndex(ctx, {
2951
3266
  ...rest,
2952
3267
  index,
2953
3268
  viewPosition
@@ -2962,7 +3277,7 @@ function createImperativeHandle(ctx, state) {
2962
3277
  getScrollableNode: () => refScroller.current.getScrollableNode(),
2963
3278
  getScrollResponder: () => refScroller.current.getScrollResponder(),
2964
3279
  getState: () => ({
2965
- activeStickyIndex: state.activeStickyIndex,
3280
+ activeStickyIndex: peek$(ctx, "activeStickyIndex"),
2966
3281
  contentLength: state.totalSize,
2967
3282
  data: state.props.data,
2968
3283
  elementAtIndex: (index) => {
@@ -2973,6 +3288,8 @@ function createImperativeHandle(ctx, state) {
2973
3288
  endBuffered: state.endBuffered,
2974
3289
  isAtEnd: state.isAtEnd,
2975
3290
  isAtStart: state.isAtStart,
3291
+ listen: (signalName, cb) => listen$(ctx, signalName, cb),
3292
+ listenToPosition: (key, cb) => listenPosition$(ctx, key, cb),
2976
3293
  positionAtIndex: (index) => state.positions.get(getId(state, index)),
2977
3294
  positions: state.positions,
2978
3295
  scroll: state.scroll,
@@ -2997,23 +3314,23 @@ function createImperativeHandle(ctx, state) {
2997
3314
  if (index !== -1) {
2998
3315
  const paddingBottom = stylePaddingBottom || 0;
2999
3316
  const footerSize = peek$(ctx, "footerSize") || 0;
3000
- scrollToIndex(ctx, state, {
3317
+ scrollToIndex(ctx, {
3318
+ ...options,
3001
3319
  index,
3002
3320
  viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
3003
- viewPosition: 1,
3004
- ...options
3321
+ viewPosition: 1
3005
3322
  });
3006
3323
  }
3007
3324
  },
3008
- scrollToIndex: (params) => scrollToIndex(ctx, state, params),
3325
+ scrollToIndex: (params) => scrollToIndex(ctx, params),
3009
3326
  scrollToItem: ({ item, ...props }) => {
3010
3327
  const data = state.props.data;
3011
3328
  const index = data.indexOf(item);
3012
3329
  if (index !== -1) {
3013
- scrollToIndex(ctx, state, { index, ...props });
3330
+ scrollToIndex(ctx, { index, ...props });
3014
3331
  }
3015
3332
  },
3016
- scrollToOffset: (params) => scrollTo(ctx, state, params),
3333
+ scrollToOffset: (params) => scrollTo(ctx, params),
3017
3334
  setScrollProcessingEnabled: (enabled) => {
3018
3335
  state.scrollProcessingEnabled = enabled;
3019
3336
  },
@@ -3023,8 +3340,9 @@ function createImperativeHandle(ctx, state) {
3023
3340
  }
3024
3341
  };
3025
3342
  }
3026
- function getRenderedItem(ctx, state, key) {
3343
+ function getRenderedItem(ctx, key) {
3027
3344
  var _a3;
3345
+ const state = ctx.state;
3028
3346
  if (!state) {
3029
3347
  return null;
3030
3348
  }
@@ -3051,6 +3369,25 @@ function getRenderedItem(ctx, state, key) {
3051
3369
  }
3052
3370
  return { index, item: data[index], renderedItem };
3053
3371
  }
3372
+
3373
+ // src/utils/normalizeMaintainVisibleContentPosition.ts
3374
+ function normalizeMaintainVisibleContentPosition(value) {
3375
+ var _a3, _b;
3376
+ if (value === true) {
3377
+ return { data: true, size: true };
3378
+ }
3379
+ if (value && typeof value === "object") {
3380
+ return {
3381
+ data: (_a3 = value.data) != null ? _a3 : false,
3382
+ size: (_b = value.size) != null ? _b : true,
3383
+ shouldRestorePosition: value.shouldRestorePosition
3384
+ };
3385
+ }
3386
+ if (value === false) {
3387
+ return { data: false, size: false };
3388
+ }
3389
+ return { data: false, size: true };
3390
+ }
3054
3391
  function useThrottleDebounce(mode) {
3055
3392
  const timeoutRef = React2.useRef(null);
3056
3393
  const lastCallTimeRef = React2.useRef(0);
@@ -3101,6 +3438,7 @@ function useThrottledOnScroll(originalHandler, scrollEventThrottle) {
3101
3438
  var DEFAULT_DRAW_DISTANCE = 250;
3102
3439
  var DEFAULT_ITEM_SIZE = 100;
3103
3440
  var LegendList = typedMemo(
3441
+ // biome-ignore lint/nursery/noShadow: const function name shadowing is intentional
3104
3442
  typedForwardRef(function LegendList2(props, forwardedRef) {
3105
3443
  const { children, data: dataProp, renderItem: renderItemProp, ...restProps } = props;
3106
3444
  const isChildrenMode = children !== void 0 && dataProp === void 0;
@@ -3123,10 +3461,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3123
3461
  alignItemsAtEnd = false,
3124
3462
  columnWrapperStyle,
3125
3463
  contentContainerStyle: contentContainerStyleProp,
3464
+ contentInset,
3126
3465
  data: dataProp = [],
3127
3466
  dataVersion,
3128
3467
  drawDistance = 250,
3129
- enableAverages = true,
3130
3468
  estimatedItemSize: estimatedItemSizeProp,
3131
3469
  estimatedListSize,
3132
3470
  extraData,
@@ -3144,7 +3482,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3144
3482
  ListHeaderComponent,
3145
3483
  maintainScrollAtEnd = false,
3146
3484
  maintainScrollAtEndThreshold = 0.1,
3147
- maintainVisibleContentPosition = false,
3485
+ maintainVisibleContentPosition: maintainVisibleContentPositionProp,
3148
3486
  numColumns: numColumnsProp = 1,
3149
3487
  onEndReached,
3150
3488
  onEndReachedThreshold = 0.5,
@@ -3168,20 +3506,26 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3168
3506
  snapToIndices,
3169
3507
  stickyHeaderIndices: stickyHeaderIndicesProp,
3170
3508
  stickyIndices: stickyIndicesDeprecated,
3509
+ // TODOV3: Remove from v3 release
3171
3510
  style: styleProp,
3172
3511
  suggestEstimatedItemSize,
3173
3512
  viewabilityConfig,
3174
3513
  viewabilityConfigCallbackPairs,
3175
3514
  waitForInitialLayout = true,
3515
+ stickyHeaderConfig,
3176
3516
  ...rest
3177
3517
  } = props;
3518
+ const animatedPropsInternal = props.animatedPropsInternal;
3178
3519
  const { childrenMode } = rest;
3179
3520
  const contentContainerStyle = { ...StyleSheet.flatten(contentContainerStyleProp) };
3180
3521
  const style = { ...StyleSheet.flatten(styleProp) };
3181
3522
  const stylePaddingTopState = extractPadding(style, contentContainerStyle, "Top");
3182
3523
  const stylePaddingBottomState = extractPadding(style, contentContainerStyle, "Bottom");
3524
+ const maintainVisibleContentPositionConfig = normalizeMaintainVisibleContentPosition(
3525
+ maintainVisibleContentPositionProp
3526
+ );
3183
3527
  const [renderNum, setRenderNum] = React2.useState(0);
3184
- const initialScrollProp = initialScrollAtEnd ? { index: Math.max(0, dataProp.length - 1), viewOffset: -stylePaddingBottomState } : initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? { index: initialScrollIndexProp.index || 0, viewOffset: initialScrollIndexProp.viewOffset || 0 } : { index: initialScrollIndexProp || 0, viewOffset: initialScrollOffsetProp || 0 } : void 0;
3528
+ const initialScrollProp = initialScrollAtEnd ? { index: Math.max(0, dataProp.length - 1), viewOffset: -stylePaddingBottomState, viewPosition: 1 } : initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? { index: initialScrollIndexProp.index || 0, viewOffset: initialScrollIndexProp.viewOffset || 0 } : { index: initialScrollIndexProp || 0, viewOffset: initialScrollOffsetProp || 0 } : void 0;
3185
3529
  const [canRender, setCanRender] = React2__namespace.useState(!IsNewArchitecture);
3186
3530
  const ctx = useStateContext();
3187
3531
  ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
@@ -3199,13 +3543,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3199
3543
  }
3200
3544
  const refState = React2.useRef();
3201
3545
  if (!refState.current) {
3202
- if (!ctx.internalState) {
3546
+ if (!ctx.state) {
3203
3547
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : IsNewArchitecture ? { height: 0, width: 0 } : getWindowSize())[horizontal ? "width" : "height"];
3204
- ctx.internalState = {
3205
- activeStickyIndex: void 0,
3548
+ ctx.state = {
3549
+ activeStickyIndex: -1,
3206
3550
  averageSizes: {},
3207
3551
  columns: /* @__PURE__ */ new Map(),
3208
- containerItemKeys: /* @__PURE__ */ new Set(),
3552
+ containerItemKeys: /* @__PURE__ */ new Map(),
3209
3553
  containerItemTypes: /* @__PURE__ */ new Map(),
3210
3554
  dataChangeNeedsScrollUpdate: false,
3211
3555
  didColumnsChange: false,
@@ -3228,11 +3572,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3228
3572
  initialScroll: initialScrollProp,
3229
3573
  isAtEnd: false,
3230
3574
  isAtStart: false,
3231
- isEndReached: false,
3575
+ isEndReached: null,
3232
3576
  isFirst: true,
3233
- isStartReached: false,
3577
+ isStartReached: null,
3234
3578
  lastBatchingAction: Date.now(),
3235
3579
  lastLayout: void 0,
3580
+ lastScrollDelta: 0,
3236
3581
  loadStartTime: Date.now(),
3237
3582
  minIndexSizeChanged: 0,
3238
3583
  nativeMarginTop: 0,
@@ -3262,12 +3607,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3262
3607
  totalSize: 0,
3263
3608
  viewabilityConfigCallbackPairs: void 0
3264
3609
  };
3265
- const internalState = ctx.internalState;
3266
- internalState.triggerCalculateItemsInView = (params) => calculateItemsInView(ctx, internalState, params);
3267
- set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPosition);
3610
+ const internalState = ctx.state;
3611
+ internalState.triggerCalculateItemsInView = (params) => calculateItemsInView(ctx, params);
3612
+ set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPositionConfig);
3268
3613
  set$(ctx, "extraData", extraData);
3269
3614
  }
3270
- refState.current = ctx.internalState;
3615
+ refState.current = ctx.state;
3271
3616
  }
3272
3617
  const state = refState.current;
3273
3618
  const isFirstLocal = state.isFirst;
@@ -3281,20 +3626,21 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3281
3626
  const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
3282
3627
  state.props = {
3283
3628
  alignItemsAtEnd,
3629
+ animatedProps: animatedPropsInternal,
3630
+ contentInset,
3284
3631
  data: dataProp,
3285
3632
  dataVersion,
3286
- enableAverages,
3287
3633
  estimatedItemSize,
3288
- getEstimatedItemSize,
3289
- getFixedItemSize,
3290
- getItemType,
3634
+ getEstimatedItemSize: useWrapIfItem(getEstimatedItemSize),
3635
+ getFixedItemSize: useWrapIfItem(getFixedItemSize),
3636
+ getItemType: useWrapIfItem(getItemType),
3291
3637
  horizontal: !!horizontal,
3292
3638
  initialContainerPoolRatio,
3293
3639
  itemsAreEqual,
3294
- keyExtractor,
3640
+ keyExtractor: useWrapIfItem(keyExtractor),
3295
3641
  maintainScrollAtEnd,
3296
3642
  maintainScrollAtEndThreshold,
3297
- maintainVisibleContentPosition,
3643
+ maintainVisibleContentPosition: maintainVisibleContentPositionConfig,
3298
3644
  numColumns: numColumnsProp,
3299
3645
  onEndReached,
3300
3646
  onEndReachedThreshold,
@@ -3326,57 +3672,57 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3326
3672
  set$(ctx, "lastItemKeys", memoizedLastItemKeys);
3327
3673
  set$(ctx, "numColumns", numColumnsProp);
3328
3674
  const prevPaddingTop = peek$(ctx, "stylePaddingTop");
3329
- setPaddingTop(ctx, state, { stylePaddingTop: stylePaddingTopState });
3675
+ setPaddingTop(ctx, { stylePaddingTop: stylePaddingTopState });
3330
3676
  refState.current.props.stylePaddingBottom = stylePaddingBottomState;
3331
3677
  let paddingDiff = stylePaddingTopState - prevPaddingTop;
3332
- if (paddingDiff && prevPaddingTop !== void 0 && Platform2.OS === "ios") {
3678
+ if (maintainVisibleContentPositionConfig.size && paddingDiff && prevPaddingTop !== void 0 && Platform2.OS === "ios") {
3333
3679
  if (state.scroll < 0) {
3334
3680
  paddingDiff += state.scroll;
3335
3681
  }
3336
- requestAdjust(ctx, state, paddingDiff);
3682
+ requestAdjust(ctx, paddingDiff);
3337
3683
  }
3338
3684
  };
3339
3685
  if (isFirstLocal) {
3340
3686
  initializeStateVars();
3341
3687
  updateItemPositions(
3342
3688
  ctx,
3343
- state,
3344
3689
  /*dataChanged*/
3345
3690
  true
3346
3691
  );
3347
3692
  }
3348
3693
  const initialContentOffset = React2.useMemo(() => {
3349
- var _a4, _b2;
3350
- const { initialScroll } = refState.current;
3351
- if (!initialScroll) {
3694
+ var _a4;
3695
+ let value;
3696
+ const { initialScroll, initialAnchor } = refState.current;
3697
+ if (initialScroll) {
3698
+ if (!IsNewArchitecture && initialScroll.index !== void 0 && (!initialAnchor || (initialAnchor == null ? void 0 : initialAnchor.index) !== initialScroll.index)) {
3699
+ refState.current.initialAnchor = {
3700
+ attempts: 0,
3701
+ index: initialScroll.index,
3702
+ settledTicks: 0,
3703
+ viewOffset: (_a4 = initialScroll.viewOffset) != null ? _a4 : 0,
3704
+ viewPosition: initialScroll.viewPosition
3705
+ };
3706
+ }
3707
+ if (initialScroll.contentOffset !== void 0) {
3708
+ value = initialScroll.contentOffset;
3709
+ } else {
3710
+ const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, initialScroll.index) : 0;
3711
+ const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, initialScroll);
3712
+ const clampedOffset = clampScrollOffset(ctx, resolvedOffset);
3713
+ const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
3714
+ refState.current.initialScroll = updatedInitialScroll;
3715
+ state.initialScroll = updatedInitialScroll;
3716
+ value = clampedOffset;
3717
+ }
3718
+ } else {
3352
3719
  refState.current.initialAnchor = void 0;
3353
- return 0;
3354
- }
3355
- if (initialScroll.index !== void 0 && (!refState.current.initialAnchor || ((_a4 = refState.current.initialAnchor) == null ? void 0 : _a4.index) !== initialScroll.index)) {
3356
- refState.current.initialAnchor = {
3357
- attempts: 0,
3358
- index: initialScroll.index,
3359
- settledTicks: 0,
3360
- viewOffset: (_b2 = initialScroll.viewOffset) != null ? _b2 : 0,
3361
- viewPosition: initialScroll.viewPosition
3362
- };
3720
+ value = 0;
3721
+ }
3722
+ if (!value) {
3723
+ setInitialRenderState(ctx, { didInitialScroll: true });
3363
3724
  }
3364
- if (initialScroll.contentOffset !== void 0) {
3365
- return initialScroll.contentOffset;
3366
- }
3367
- const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, state, initialScroll.index) : 0;
3368
- const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, state, baseOffset, initialScroll);
3369
- let clampedOffset = resolvedOffset;
3370
- if (Number.isFinite(state.scrollLength) && Number.isFinite(state.totalSize)) {
3371
- const maxOffset = Math.max(0, state.totalSize - state.scrollLength);
3372
- clampedOffset = Math.min(clampedOffset, maxOffset);
3373
- }
3374
- clampedOffset = Math.max(0, clampedOffset);
3375
- const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
3376
- refState.current.initialScroll = updatedInitialScroll;
3377
- state.initialScroll = updatedInitialScroll;
3378
- refState.current.isStartReached = clampedOffset < refState.current.scrollLength * onStartReachedThreshold;
3379
- return clampedOffset;
3725
+ return value;
3380
3726
  }, [renderNum]);
3381
3727
  if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
3382
3728
  refState.current.lastBatchingAction = Date.now();
@@ -3406,12 +3752,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3406
3752
  }
3407
3753
  }, []);
3408
3754
  const doInitialScroll = React2.useCallback(() => {
3409
- var _a4;
3410
- const initialScroll = state.initialScroll;
3411
- if (initialScroll) {
3412
- scrollTo(ctx, state, {
3755
+ const { initialScroll, didFinishInitialScroll, queuedInitialLayout, scrollingTo } = state;
3756
+ if (initialScroll && !queuedInitialLayout && !didFinishInitialScroll && !scrollingTo) {
3757
+ scrollTo(ctx, {
3413
3758
  animated: false,
3414
- index: (_a4 = state.initialScroll) == null ? void 0 : _a4.index,
3759
+ index: initialScroll == null ? void 0 : initialScroll.index,
3415
3760
  isInitialScroll: true,
3416
3761
  offset: initialContentOffset,
3417
3762
  precomputedWithViewOffset: true
@@ -3420,7 +3765,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3420
3765
  }, [initialContentOffset]);
3421
3766
  const onLayoutChange = React2.useCallback((layout) => {
3422
3767
  doInitialScroll();
3423
- handleLayout(ctx, state, layout, setCanRender);
3768
+ handleLayout(ctx, layout, setCanRender);
3424
3769
  }, []);
3425
3770
  const { onLayout } = useOnLayoutSync({
3426
3771
  onLayoutChange,
@@ -3430,7 +3775,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3430
3775
  });
3431
3776
  React2.useLayoutEffect(() => {
3432
3777
  if (snapToIndices) {
3433
- updateSnapToOffsets(ctx, state);
3778
+ updateSnapToOffsets(ctx);
3434
3779
  }
3435
3780
  }, [snapToIndices]);
3436
3781
  React2.useLayoutEffect(() => {
@@ -3440,9 +3785,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3440
3785
  isFirst,
3441
3786
  props: { data }
3442
3787
  } = state;
3443
- const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx, state);
3788
+ const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx);
3444
3789
  if (!didAllocateContainers && !isFirst && (didDataChange || didColumnsChange)) {
3445
- checkResetContainers(ctx, state, data);
3790
+ checkResetContainers(ctx, data);
3446
3791
  }
3447
3792
  state.didColumnsChange = false;
3448
3793
  state.didDataChange = false;
@@ -3469,18 +3814,24 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3469
3814
  }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
3470
3815
  if (!IsNewArchitecture) {
3471
3816
  useInit(() => {
3472
- doInitialAllocateContainers(ctx, state);
3817
+ doInitialAllocateContainers(ctx);
3473
3818
  });
3474
3819
  }
3475
- React2.useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx, state), []);
3820
+ React2.useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx), []);
3476
3821
  if (Platform2.OS === "web") {
3477
3822
  React2.useEffect(doInitialScroll, []);
3478
3823
  }
3479
3824
  const fns = React2.useMemo(
3480
3825
  () => ({
3481
- getRenderedItem: (key) => getRenderedItem(ctx, state, key),
3482
- onScroll: (event) => onScroll(ctx, state, event),
3483
- updateItemSize: (itemKey, sizeObj) => updateItemSize(ctx, state, itemKey, sizeObj)
3826
+ getRenderedItem: (key) => getRenderedItem(ctx, key),
3827
+ onMomentumScrollEnd: (event) => {
3828
+ checkFinishedScrollFallback(ctx);
3829
+ if (onMomentumScrollEnd) {
3830
+ onMomentumScrollEnd(event);
3831
+ }
3832
+ },
3833
+ onScroll: (event) => onScroll(ctx, event),
3834
+ updateItemSize: (itemKey, sizeObj) => updateItemSize(ctx, itemKey, sizeObj)
3484
3835
  }),
3485
3836
  []
3486
3837
  );
@@ -3492,28 +3843,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3492
3843
  alignItemsAtEnd,
3493
3844
  canRender,
3494
3845
  contentContainerStyle,
3846
+ contentInset,
3495
3847
  getRenderedItem: fns.getRenderedItem,
3496
3848
  horizontal,
3497
3849
  initialContentOffset,
3498
3850
  ListEmptyComponent: dataProp.length === 0 ? ListEmptyComponent : void 0,
3499
3851
  ListHeaderComponent,
3500
- maintainVisibleContentPosition,
3501
3852
  onLayout,
3502
3853
  onLayoutHeader,
3503
- onMomentumScrollEnd: (event) => {
3504
- if (IsNewArchitecture) {
3505
- requestAnimationFrame(() => {
3506
- finishScrollTo(ctx, refState.current);
3507
- });
3508
- } else {
3509
- setTimeout(() => {
3510
- finishScrollTo(ctx, refState.current);
3511
- }, 1e3);
3512
- }
3513
- if (onMomentumScrollEnd) {
3514
- onMomentumScrollEnd(event);
3515
- }
3516
- },
3854
+ onMomentumScrollEnd: fns.onMomentumScrollEnd,
3517
3855
  onScroll: onScrollHandler,
3518
3856
  recycleItems,
3519
3857
  refreshControl: refreshControl ? stylePaddingTopState > 0 ? React2__namespace.cloneElement(refreshControl, {
@@ -3528,8 +3866,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3528
3866
  ),
3529
3867
  refScrollView: combinedRef,
3530
3868
  scrollAdjustHandler: (_b = refState.current) == null ? void 0 : _b.scrollAdjustHandler,
3531
- scrollEventThrottle: Platform2.OS === "web" ? 16 : void 0,
3869
+ scrollEventThrottle: 0,
3532
3870
  snapToIndices,
3871
+ stickyHeaderConfig,
3533
3872
  stickyHeaderIndices,
3534
3873
  style,
3535
3874
  updateItemSize: fns.updateItemSize,
@@ -3539,6 +3878,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3539
3878
  });
3540
3879
 
3541
3880
  exports.LegendList = LegendList;
3881
+ exports.typedForwardRef = typedForwardRef;
3882
+ exports.typedMemo = typedMemo;
3542
3883
  exports.useIsLastItem = useIsLastItem;
3543
3884
  exports.useListScrollSize = useListScrollSize;
3544
3885
  exports.useRecyclingEffect = useRecyclingEffect;