@legendapp/list 3.0.0-beta.53 → 3.0.0-beta.55

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.mjs CHANGED
@@ -1,13 +1,118 @@
1
1
  import * as React2 from 'react';
2
2
  import React2__default, { useReducer, useEffect, createContext, useRef, useState, useMemo, useCallback, useLayoutEffect, useImperativeHandle, useContext } from 'react';
3
3
  import * as ReactNative from 'react-native';
4
- import { Animated, View as View$1, Text as Text$1, Platform as Platform$1, StyleSheet as StyleSheet$1, RefreshControl, Dimensions } from 'react-native';
4
+ import { Animated, Platform as Platform$1, View as View$1, Text as Text$1, StyleSheet as StyleSheet$1, RefreshControl, Dimensions, I18nManager } from 'react-native';
5
5
  import { useSyncExternalStore } from 'use-sync-external-store/shim';
6
6
 
7
7
  // src/components/LegendList.tsx
8
8
  Animated.View;
9
9
  var View = View$1;
10
10
  var Text = Text$1;
11
+ var Platform = Platform$1;
12
+ var PlatformAdjustBreaksScroll = Platform.OS === "android";
13
+
14
+ // src/utils/rtl.ts
15
+ function clampHorizontalOffset(offset, maxOffset) {
16
+ if (maxOffset === void 0) {
17
+ return offset;
18
+ }
19
+ return Math.max(0, Math.min(maxOffset, offset));
20
+ }
21
+ function getHorizontalMaxOffset(state, contentWidth) {
22
+ if (contentWidth === void 0 || !Number.isFinite(contentWidth) || !Number.isFinite(state.scrollLength) || contentWidth <= state.scrollLength) {
23
+ return contentWidth !== void 0 && Number.isFinite(contentWidth) && Number.isFinite(state.scrollLength) ? 0 : void 0;
24
+ }
25
+ return Math.max(0, contentWidth - state.scrollLength);
26
+ }
27
+ function getDefaultHorizontalRTLScrollType() {
28
+ return Platform.OS === "web" ? "normal" : "inverted";
29
+ }
30
+ function getNativeHorizontalRTLScrollType(state) {
31
+ var _a3;
32
+ return (_a3 = state == null ? void 0 : state.horizontalRTLScrollType) != null ? _a3 : getDefaultHorizontalRTLScrollType();
33
+ }
34
+ function isRTLProps(props) {
35
+ var _a3;
36
+ return (_a3 = props == null ? void 0 : props.rtl) != null ? _a3 : !!I18nManager.isRTL;
37
+ }
38
+ function isHorizontalRTL(state) {
39
+ return isHorizontalRTLProps(state == null ? void 0 : state.props);
40
+ }
41
+ function isHorizontalRTLProps(props) {
42
+ return !!(props == null ? void 0 : props.horizontal) && isRTLProps(props);
43
+ }
44
+ function getLogicalHorizontalMaxOffset(state, contentWidth) {
45
+ var _a3;
46
+ return (_a3 = getHorizontalMaxOffset(state, contentWidth)) != null ? _a3 : 0;
47
+ }
48
+ function getHorizontalInsetEnd(state, inset) {
49
+ if (!inset) {
50
+ return 0;
51
+ }
52
+ return (isHorizontalRTL(state) ? inset.left : inset.right) || 0;
53
+ }
54
+ function toPhysicalHorizontalItemPosition(state, logicalPosition, itemSize, listSize) {
55
+ if (!isHorizontalRTL(state) || listSize === void 0 || !Number.isFinite(listSize)) {
56
+ return logicalPosition;
57
+ }
58
+ return Math.max(0, listSize - logicalPosition - itemSize);
59
+ }
60
+ function toNativeHorizontalOffset(state, logicalOffset, contentWidth) {
61
+ if (!state || !isHorizontalRTL(state)) {
62
+ return logicalOffset;
63
+ }
64
+ const maxOffset = getHorizontalMaxOffset(state, contentWidth);
65
+ const clampedLogicalOffset = clampHorizontalOffset(logicalOffset, maxOffset);
66
+ const mode = getNativeHorizontalRTLScrollType(state);
67
+ if (mode === "negative") {
68
+ return clampedLogicalOffset === 0 ? 0 : -clampedLogicalOffset;
69
+ }
70
+ if (mode === "inverted") {
71
+ if (maxOffset === void 0) {
72
+ return clampedLogicalOffset;
73
+ }
74
+ return clampHorizontalOffset(maxOffset - clampedLogicalOffset, maxOffset);
75
+ }
76
+ return clampedLogicalOffset;
77
+ }
78
+ function toLogicalHorizontalOffset(state, rawOffset, contentWidth) {
79
+ if (!isHorizontalRTL(state)) {
80
+ state.horizontalRTLScrollType = void 0;
81
+ return rawOffset;
82
+ }
83
+ const maxOffset = getHorizontalMaxOffset(state, contentWidth);
84
+ if (rawOffset < 0) {
85
+ state.horizontalRTLScrollType = "negative";
86
+ return clampHorizontalOffset(-rawOffset, maxOffset);
87
+ }
88
+ if (maxOffset === void 0) {
89
+ return rawOffset;
90
+ }
91
+ const normalOffset = rawOffset;
92
+ const invertedOffset = maxOffset - rawOffset;
93
+ if (!Number.isFinite(invertedOffset)) {
94
+ state.horizontalRTLScrollType = "normal";
95
+ return normalOffset;
96
+ }
97
+ const previousMode = state.horizontalRTLScrollType;
98
+ if (previousMode === "inverted") {
99
+ return clampHorizontalOffset(invertedOffset, maxOffset);
100
+ }
101
+ if (previousMode === "normal") {
102
+ return clampHorizontalOffset(normalOffset, maxOffset);
103
+ }
104
+ if (!state.hasScrolled) {
105
+ const defaultMode = getDefaultHorizontalRTLScrollType();
106
+ state.horizontalRTLScrollType = defaultMode;
107
+ return clampHorizontalOffset(defaultMode === "inverted" ? invertedOffset : normalOffset, maxOffset);
108
+ }
109
+ const referenceScroll = state.scroll;
110
+ const distanceNormal = Math.abs(normalOffset - referenceScroll);
111
+ const distanceInverted = Math.abs(invertedOffset - referenceScroll);
112
+ const useInverted = distanceInverted + 0.5 < distanceNormal;
113
+ state.horizontalRTLScrollType = useInverted ? "inverted" : "normal";
114
+ return clampHorizontalOffset(useInverted ? invertedOffset : normalOffset, maxOffset);
115
+ }
11
116
  var createAnimatedValue = (value) => new Animated.Value(value);
12
117
 
13
118
  // src/state/state.tsx
@@ -149,7 +254,7 @@ function getContentInsetEnd(ctx, contentInsetEndAdjustmentOverride) {
149
254
  const horizontal = props.horizontal;
150
255
  const contentInset = props.contentInset;
151
256
  const baseInset = contentInset != null ? contentInset : state.nativeContentInset;
152
- const baseEndInset = (horizontal ? baseInset == null ? void 0 : baseInset.right : baseInset == null ? void 0 : baseInset.bottom) || 0;
257
+ const baseEndInset = (horizontal ? getHorizontalInsetEnd(state, baseInset) : baseInset == null ? void 0 : baseInset.bottom) || 0;
153
258
  const contentInsetEndAdjustment = getContentInsetEndAdjustmentEnd(
154
259
  contentInsetEndAdjustmentOverride != null ? contentInsetEndAdjustmentOverride : props.contentInsetEndAdjustment
155
260
  );
@@ -158,9 +263,9 @@ function getContentInsetEnd(ctx, contentInsetEndAdjustmentOverride) {
158
263
  const overrideInset = (_b = state.contentInsetOverride) != null ? _b : void 0;
159
264
  const adjustedBaseEndInset = baseEndInset + contentInsetEndAdjustment;
160
265
  if (overrideInset) {
161
- const mergedInset = { bottom: 0, right: 0, ...baseInset, ...overrideInset };
266
+ const mergedInset = { bottom: 0, left: 0, right: 0, ...baseInset, ...overrideInset };
162
267
  return Math.max(
163
- ((horizontal ? mergedInset.right : mergedInset.bottom) || 0) + contentInsetEndAdjustment,
268
+ ((horizontal ? getHorizontalInsetEnd(state, mergedInset) : mergedInset.bottom) || 0) + contentInsetEndAdjustment,
164
269
  anchoredEndInset
165
270
  );
166
271
  }
@@ -440,7 +545,8 @@ function comparatorDefault(a, b) {
440
545
  }
441
546
  function getPadding(s, type) {
442
547
  var _a3, _b, _c;
443
- return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : s.paddingVertical) != null ? _b : s.padding) != null ? _c : 0;
548
+ const axisPadding = type === "Left" || type === "Right" ? s.paddingHorizontal : s.paddingVertical;
549
+ return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : axisPadding) != null ? _b : s.padding) != null ? _c : 0;
444
550
  }
445
551
  function extractPadding(style, contentContainerStyle, type) {
446
552
  return getPadding(style, type) + getPadding(contentContainerStyle, type);
@@ -647,8 +753,6 @@ function useOnLayoutSync({
647
753
  }
648
754
  return { onLayout };
649
755
  }
650
- var Platform = Platform$1;
651
- var PlatformAdjustBreaksScroll = Platform.OS === "android";
652
756
 
653
757
  // src/utils/isInMVCPActiveMode.native.ts
654
758
  function isInMVCPActiveMode(state) {
@@ -660,6 +764,7 @@ function getContainerPositionStyle({
660
764
  columnWrapperStyle,
661
765
  horizontal,
662
766
  hasItemSeparator,
767
+ isHorizontalRTLList,
663
768
  numColumns,
664
769
  otherAxisPos,
665
770
  otherAxisSize
@@ -683,6 +788,7 @@ function getContainerPositionStyle({
683
788
  }
684
789
  return horizontal ? {
685
790
  boxSizing: paddingStyles ? "border-box" : void 0,
791
+ direction: isHorizontalRTLList && Platform.OS === "web" ? "ltr" : void 0,
686
792
  flexDirection: hasItemSeparator ? "row" : void 0,
687
793
  height: otherAxisSize,
688
794
  left: 0,
@@ -701,6 +807,7 @@ function getContainerPositionStyle({
701
807
  }
702
808
  var Container = typedMemo(function Container2({
703
809
  id,
810
+ itemKey,
704
811
  recycleItems,
705
812
  horizontal,
706
813
  getRenderedItem: getRenderedItem2,
@@ -710,13 +817,13 @@ var Container = typedMemo(function Container2({
710
817
  }) {
711
818
  const ctx = useStateContext();
712
819
  const { columnWrapperStyle, animatedScrollY } = ctx;
820
+ const isHorizontalRTLList = isHorizontalRTL(ctx.state);
713
821
  const positionComponentInternal = ctx.state.props.positionComponentInternal;
714
822
  const stickyPositionComponentInternal = ctx.state.props.stickyPositionComponentInternal;
715
- const [column = 0, span = 1, data, itemKey, numColumns = 1, extraData, isSticky] = useArr$([
823
+ const [column = 0, span = 1, data, numColumns = 1, extraData, isSticky] = useArr$([
716
824
  `containerColumn${id}`,
717
825
  `containerSpan${id}`,
718
826
  `containerItemData${id}`,
719
- `containerItemKey${id}`,
720
827
  "numColumns",
721
828
  "extraData",
722
829
  `containerSticky${id}`
@@ -742,11 +849,20 @@ var Container = typedMemo(function Container2({
742
849
  columnWrapperStyle,
743
850
  hasItemSeparator: !!ItemSeparatorComponent,
744
851
  horizontal,
852
+ isHorizontalRTLList,
745
853
  numColumns,
746
854
  otherAxisPos,
747
855
  otherAxisSize
748
856
  }),
749
- [horizontal, otherAxisPos, otherAxisSize, columnWrapperStyle, numColumns, ItemSeparatorComponent]
857
+ [
858
+ horizontal,
859
+ isHorizontalRTLList,
860
+ otherAxisPos,
861
+ otherAxisSize,
862
+ columnWrapperStyle,
863
+ numColumns,
864
+ ItemSeparatorComponent
865
+ ]
750
866
  );
751
867
  const renderedItemInfo = useMemo(
752
868
  () => itemKey !== void 0 ? getRenderedItem2(itemKey) : null,
@@ -861,14 +977,51 @@ var Container = typedMemo(function Container2({
861
977
  );
862
978
  });
863
979
 
980
+ // src/components/ContainerSlot.tsx
981
+ function ContainerSlotBase({
982
+ id,
983
+ horizontal,
984
+ recycleItems,
985
+ ItemSeparatorComponent,
986
+ updateItemSize: updateItemSize2,
987
+ getRenderedItem: getRenderedItem2,
988
+ stickyHeaderConfig,
989
+ ContainerComponent = Container
990
+ }) {
991
+ const [itemKey] = useArr$([`containerItemKey${id}`]);
992
+ if (itemKey === void 0) {
993
+ return null;
994
+ }
995
+ return /* @__PURE__ */ React2.createElement(
996
+ ContainerComponent,
997
+ {
998
+ getRenderedItem: getRenderedItem2,
999
+ horizontal,
1000
+ ItemSeparatorComponent,
1001
+ id,
1002
+ itemKey,
1003
+ recycleItems,
1004
+ stickyHeaderConfig,
1005
+ updateItemSize: updateItemSize2
1006
+ }
1007
+ );
1008
+ }
1009
+ var ContainerSlot = typedMemo(function ContainerSlot2(props) {
1010
+ return /* @__PURE__ */ React2.createElement(ContainerSlotBase, { ...props });
1011
+ });
1012
+
864
1013
  // src/components/Containers.native.tsx
865
1014
  var ContainersLayer = typedMemo(function ContainersLayer2({ children, horizontal }) {
866
1015
  const ctx = useStateContext();
867
1016
  const columnWrapperStyle = ctx.columnWrapperStyle;
868
1017
  const animSize = useValue$("totalSize");
869
- const otherAxisSize = useValue$("otherAxisSize");
870
- const [readyToRender, numColumns] = useArr$(["readyToRender", "numColumns"]);
871
- const style = horizontal ? { minHeight: otherAxisSize, opacity: readyToRender ? 1 : 0, width: animSize } : { height: animSize, minWidth: otherAxisSize, opacity: readyToRender ? 1 : 0 };
1018
+ const [readyToRender, numColumns, otherAxisSize = 0] = useArr$(["readyToRender", "numColumns", "otherAxisSize"]);
1019
+ const style = horizontal ? {
1020
+ height: otherAxisSize || "100%",
1021
+ minHeight: otherAxisSize,
1022
+ opacity: readyToRender ? 1 : 0,
1023
+ width: animSize
1024
+ } : { height: animSize, minWidth: otherAxisSize, opacity: readyToRender ? 1 : 0 };
872
1025
  if (columnWrapperStyle) {
873
1026
  const { columnGap, rowGap, gap } = columnWrapperStyle;
874
1027
  const gapX = columnGap || gap || 0;
@@ -899,12 +1052,12 @@ var Containers = typedMemo(function Containers2({
899
1052
  updateItemSize: updateItemSize2,
900
1053
  getRenderedItem: getRenderedItem2
901
1054
  }) {
902
- const [numContainers] = useArr$(["numContainersPooled"]);
1055
+ const [numContainersPooled] = useArr$(["numContainersPooled"]);
903
1056
  const containers = [];
904
- for (let i = 0; i < numContainers; i++) {
1057
+ for (let i = 0; i < numContainersPooled; i++) {
905
1058
  containers.push(
906
1059
  /* @__PURE__ */ React2.createElement(
907
- Container,
1060
+ ContainerSlot,
908
1061
  {
909
1062
  getRenderedItem: getRenderedItem2,
910
1063
  horizontal,
@@ -921,6 +1074,18 @@ var Containers = typedMemo(function Containers2({
921
1074
  return /* @__PURE__ */ React2.createElement(ContainersLayer, { horizontal }, containers);
922
1075
  });
923
1076
  var ListComponentScrollView = Animated.ScrollView;
1077
+
1078
+ // src/components/listComponentStyles.ts
1079
+ function getAutoOtherAxisStyle({
1080
+ horizontal,
1081
+ needsOtherAxisSize,
1082
+ otherAxisSize
1083
+ }) {
1084
+ if (!needsOtherAxisSize || !otherAxisSize || otherAxisSize <= 0) {
1085
+ return void 0;
1086
+ }
1087
+ return horizontal ? { height: otherAxisSize } : { width: otherAxisSize };
1088
+ }
924
1089
  function ScrollAdjust() {
925
1090
  var _a3;
926
1091
  const ctx = useStateContext();
@@ -941,10 +1106,10 @@ function ScrollAdjust() {
941
1106
  }
942
1107
  );
943
1108
  }
944
- function SnapWrapper({ ScrollComponent, ...props }) {
1109
+ var SnapWrapper = React2.forwardRef(function SnapWrapperInner({ ScrollComponent, ...props }, ref) {
945
1110
  const [snapToOffsets] = useArr$(["snapToOffsets"]);
946
- return /* @__PURE__ */ React2.createElement(ScrollComponent, { ...props, snapToOffsets });
947
- }
1111
+ return /* @__PURE__ */ React2.createElement(ScrollComponent, { ...props, ref, snapToOffsets });
1112
+ });
948
1113
  function WebAnchoredEndSpace({ horizontal }) {
949
1114
  const ctx = useStateContext();
950
1115
  const [anchoredEndSpaceSize] = useArr$(["anchoredEndSpaceSize"]);
@@ -993,6 +1158,12 @@ var ListComponent = typedMemo(function ListComponent2({
993
1158
  }) {
994
1159
  const ctx = useStateContext();
995
1160
  const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
1161
+ const [otherAxisSize = 0] = useArr$(["otherAxisSize"]);
1162
+ const autoOtherAxisStyle = getAutoOtherAxisStyle({
1163
+ horizontal,
1164
+ needsOtherAxisSize: ctx.state.needsOtherAxisSize,
1165
+ otherAxisSize
1166
+ });
996
1167
  const ScrollComponent = useMemo(() => {
997
1168
  if (!renderScrollComponent) {
998
1169
  return ListComponentScrollView;
@@ -1031,10 +1202,10 @@ var ListComponent = typedMemo(function ListComponent2({
1031
1202
  ...rest,
1032
1203
  ...ScrollComponent === ListComponentScrollView ? { useWindowScroll } : {},
1033
1204
  contentContainerStyle: [
1034
- contentContainerStyle,
1035
1205
  horizontal ? {
1036
1206
  height: "100%"
1037
- } : {}
1207
+ } : {},
1208
+ contentContainerStyle
1038
1209
  ],
1039
1210
  contentOffset: initialContentOffset !== void 0 ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
1040
1211
  horizontal,
@@ -1043,7 +1214,7 @@ var ListComponent = typedMemo(function ListComponent2({
1043
1214
  onScroll: onScroll2,
1044
1215
  ref: refScrollView,
1045
1216
  ScrollComponent: snapToIndices ? ScrollComponent : void 0,
1046
- style
1217
+ style: autoOtherAxisStyle ? [autoOtherAxisStyle, style] : style
1047
1218
  },
1048
1219
  /* @__PURE__ */ React2.createElement(ScrollAdjust, null),
1049
1220
  ListHeaderComponent && /* @__PURE__ */ React2.createElement(LayoutView, { onLayoutChange: onLayoutHeader, style: ListHeaderComponentStyle }, getComponent(ListHeaderComponent)),
@@ -1231,10 +1402,9 @@ var initialScrollWatchdog = {
1231
1402
  clear(state) {
1232
1403
  initialScrollWatchdog.set(state, void 0);
1233
1404
  },
1234
- didObserveProgress(newScroll, watchdog) {
1235
- const previousDistance = Math.abs(watchdog.startScroll - watchdog.targetOffset);
1405
+ didReachTarget(newScroll, watchdog) {
1236
1406
  const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
1237
- return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET || nextDistance + INITIAL_SCROLL_MIN_TARGET_OFFSET < previousDistance;
1407
+ return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
1238
1408
  },
1239
1409
  get(state) {
1240
1410
  var _a3, _b;
@@ -1259,19 +1429,19 @@ var initialScrollWatchdog = {
1259
1429
  }
1260
1430
  };
1261
1431
  function setInitialScrollSession(state, options = {}) {
1262
- var _a3, _b, _c;
1432
+ var _a3, _b, _c, _d;
1263
1433
  const existingSession = state.initialScrollSession;
1264
1434
  const kind = (_a3 = options.kind) != null ? _a3 : existingSession == null ? void 0 : existingSession.kind;
1265
1435
  const completion = existingSession == null ? void 0 : existingSession.completion;
1266
- const hasBootstrapOverride = Object.hasOwn(options, "bootstrap");
1267
- const bootstrap = kind === "bootstrap" ? hasBootstrapOverride ? options.bootstrap : (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0 : void 0;
1436
+ const existingBootstrap = (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0;
1437
+ const bootstrap = kind === "bootstrap" ? options.bootstrap === null ? void 0 : (_b = options.bootstrap) != null ? _b : existingBootstrap : void 0;
1268
1438
  if (!kind) {
1269
1439
  return clearInitialScrollSession(state);
1270
1440
  }
1271
1441
  if (!state.initialScroll && !bootstrap && !hasInitialScrollSessionCompletion(completion)) {
1272
1442
  return clearInitialScrollSession(state);
1273
1443
  }
1274
- const previousDataLength = (_c = (_b = options.previousDataLength) != null ? _b : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _c : 0;
1444
+ const previousDataLength = (_d = (_c = options.previousDataLength) != null ? _c : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _d : 0;
1275
1445
  state.initialScrollSession = createInitialScrollSession({
1276
1446
  bootstrap,
1277
1447
  completion,
@@ -1853,7 +2023,7 @@ function checkFinishedScrollFallback(ctx) {
1853
2023
  state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, delay);
1854
2024
  };
1855
2025
  const checkHasScrolled = () => {
1856
- var _a3, _b, _c;
2026
+ var _a3, _b, _c, _d;
1857
2027
  state.timeoutCheckFinishedScrollFallback = void 0;
1858
2028
  const isStillScrollingTo = state.scrollingTo;
1859
2029
  if (isStillScrollingTo) {
@@ -1866,8 +2036,10 @@ function checkFinishedScrollFallback(ctx) {
1866
2036
  isStillScrollingTo
1867
2037
  );
1868
2038
  const completionState = getResolvedScrollCompletionState(ctx, isStillScrollingTo);
1869
- const canFinishAfterSilentNativeDispatch = silentInitialDispatch && completionState.isAtResolvedTarget && numChecks >= 1;
2039
+ const canFinishAfterSilentNativeDispatch = Platform.OS === "android" && silentInitialDispatch && completionState.isAtResolvedTarget && numChecks >= 1;
1870
2040
  const shouldRetrySilentInitialNativeScroll = Platform.OS === "android" && canFinishAfterSilentNativeDispatch && !initialScrollCompletion.didRetrySilentInitialScroll(state);
2041
+ const shouldFinishAfterObservedScroll = state.hasScrolled && (!isStillScrollingTo.isInitialScroll || completionState.isAtResolvedTarget);
2042
+ const shouldRetryUnalignedInitialScroll = isStillScrollingTo.isInitialScroll && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
1871
2043
  if (shouldRetrySilentInitialNativeScroll) {
1872
2044
  const targetOffset = (_b = (_a3 = getInitialScrollWatchdogTargetOffset(state)) != null ? _a3 : isStillScrollingTo.targetOffset) != null ? _b : 0;
1873
2045
  const jiggleOffset = targetOffset >= SILENT_INITIAL_SCROLL_TARGET_EPSILON ? targetOffset - SILENT_INITIAL_SCROLL_TARGET_EPSILON : targetOffset + SILENT_INITIAL_SCROLL_TARGET_EPSILON;
@@ -1877,10 +2049,10 @@ function checkFinishedScrollFallback(ctx) {
1877
2049
  scrollToFallbackOffset(ctx, targetOffset);
1878
2050
  });
1879
2051
  scheduleFallbackCheck(SILENT_INITIAL_SCROLL_RETRY_DELAY_MS);
1880
- } else if (shouldFinishZeroTarget || state.hasScrolled || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
2052
+ } else if (shouldFinishZeroTarget || shouldFinishAfterObservedScroll || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
1881
2053
  finishScrollTo(ctx);
1882
- } else if (isNativeInitialPending && numChecks <= maxChecks) {
1883
- const targetOffset = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : state.scrollPending;
2054
+ } else if ((isNativeInitialPending || shouldRetryUnalignedInitialScroll) && numChecks <= maxChecks) {
2055
+ const targetOffset = (_d = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : isStillScrollingTo.targetOffset) != null ? _d : state.scrollPending;
1884
2056
  scrollToFallbackOffset(ctx, targetOffset);
1885
2057
  scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
1886
2058
  } else {
@@ -1902,10 +2074,13 @@ function doScrollTo(ctx, params) {
1902
2074
  if (!scroller) {
1903
2075
  return;
1904
2076
  }
2077
+ const isHorizontal = !!horizontal;
2078
+ const contentSize = isHorizontal ? getContentSize(ctx) : void 0;
2079
+ const nativeOffset = toNativeHorizontalOffset(state, offset, contentSize);
1905
2080
  scroller.scrollTo({
1906
2081
  animated: isAnimated,
1907
- x: horizontal ? offset : 0,
1908
- y: horizontal ? 0 : offset
2082
+ x: isHorizontal ? nativeOffset : 0,
2083
+ y: isHorizontal ? 0 : offset
1909
2084
  });
1910
2085
  if (isInitialScroll) {
1911
2086
  initialScrollCompletion.markInitialScrollNativeDispatch(state);
@@ -2109,7 +2284,7 @@ function advanceMeasuredInitialScroll(ctx, options) {
2109
2284
  const activeInitialTargetOffset = scrollingTo ? (_a3 = scrollingTo.targetOffset) != null ? _a3 : scrollingTo.offset : void 0;
2110
2285
  const didOffsetChange = initialScroll.contentOffset === void 0 || Math.abs(initialScroll.contentOffset - resolvedOffset) > 1;
2111
2286
  const didActiveInitialTargetChange = activeInitialTargetOffset !== void 0 && Math.abs(activeInitialTargetOffset - resolvedOffset) > 1;
2112
- const isAlreadyAtDesiredInitialTarget = activeInitialTargetOffset !== void 0 && Math.abs(state.scroll - activeInitialTargetOffset) <= 1 && Math.abs(state.scrollPending - activeInitialTargetOffset) <= 1;
2287
+ const isAlreadyAtDesiredInitialTarget = activeInitialTargetOffset !== void 0 && Math.abs(state.scroll - resolvedOffset) <= 1 && Math.abs(state.scrollPending - resolvedOffset) <= 1;
2113
2288
  if (!(options == null ? void 0 : options.forceScroll) && !didOffsetChange && isInitialScrollInProgress && !didActiveInitialTargetChange) {
2114
2289
  return false;
2115
2290
  }
@@ -2181,6 +2356,61 @@ function checkAllSizesKnown(state, indices) {
2181
2356
  });
2182
2357
  }
2183
2358
 
2359
+ // src/utils/requestAdjust.ts
2360
+ function requestAdjust(ctx, positionDiff, dataChanged) {
2361
+ const state = ctx.state;
2362
+ if (Math.abs(positionDiff) > 0.1) {
2363
+ const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
2364
+ const doit = () => {
2365
+ if (needsScrollWorkaround) {
2366
+ scrollTo(ctx, {
2367
+ noScrollingTo: true,
2368
+ offset: state.scroll
2369
+ });
2370
+ } else {
2371
+ state.scrollAdjustHandler.requestAdjust(positionDiff);
2372
+ if (state.adjustingFromInitialMount) {
2373
+ state.adjustingFromInitialMount--;
2374
+ }
2375
+ }
2376
+ };
2377
+ state.scroll += positionDiff;
2378
+ state.scrollForNextCalculateItemsInView = void 0;
2379
+ const readyToRender = peek$(ctx, "readyToRender");
2380
+ if (readyToRender) {
2381
+ doit();
2382
+ if (Platform.OS !== "web") {
2383
+ const threshold = state.scroll - positionDiff / 2;
2384
+ if (!state.ignoreScrollFromMVCP) {
2385
+ state.ignoreScrollFromMVCP = {};
2386
+ }
2387
+ if (positionDiff > 0) {
2388
+ state.ignoreScrollFromMVCP.lt = threshold;
2389
+ } else {
2390
+ state.ignoreScrollFromMVCP.gt = threshold;
2391
+ }
2392
+ if (state.ignoreScrollFromMVCPTimeout) {
2393
+ clearTimeout(state.ignoreScrollFromMVCPTimeout);
2394
+ }
2395
+ const delay = needsScrollWorkaround ? 250 : 100;
2396
+ state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
2397
+ var _a3;
2398
+ state.ignoreScrollFromMVCP = void 0;
2399
+ const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
2400
+ if (shouldForceUpdate) {
2401
+ state.ignoreScrollFromMVCPIgnored = false;
2402
+ state.scrollPending = state.scroll;
2403
+ (_a3 = state.reprocessCurrentScroll) == null ? void 0 : _a3.call(state);
2404
+ }
2405
+ }, delay);
2406
+ }
2407
+ } else {
2408
+ state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
2409
+ requestAnimationFrame(doit);
2410
+ }
2411
+ }
2412
+ }
2413
+
2184
2414
  // src/core/bootstrapInitialScroll.ts
2185
2415
  var DEFAULT_BOOTSTRAP_REVEAL_EPSILON = 1;
2186
2416
  var DEFAULT_BOOTSTRAP_REVEAL_MAX_FRAMES = 8;
@@ -2274,7 +2504,7 @@ function clearBootstrapInitialScrollSession(state) {
2274
2504
  bootstrapInitialScroll.frameHandle = void 0;
2275
2505
  }
2276
2506
  setInitialScrollSession(state, {
2277
- bootstrap: void 0,
2507
+ bootstrap: null,
2278
2508
  kind: (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind
2279
2509
  });
2280
2510
  }
@@ -2430,15 +2660,18 @@ function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {
2430
2660
  return;
2431
2661
  }
2432
2662
  if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2433
- if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {
2434
- clearPendingInitialScrollFooterLayout(ctx, {
2435
- dataLength: state.props.data.length,
2436
- stylePaddingBottom: (_b = state.props.stylePaddingBottom) != null ? _b : 0,
2437
- target: initialScroll
2438
- });
2439
- return;
2663
+ const shouldKeepEndTargetAlive = isRetargetableBottomAlignedInitialScrollTarget(initialScroll) && peek$(ctx, "isAtEnd");
2664
+ if (!shouldKeepEndTargetAlive) {
2665
+ if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {
2666
+ clearPendingInitialScrollFooterLayout(ctx, {
2667
+ dataLength: state.props.data.length,
2668
+ stylePaddingBottom: (_b = state.props.stylePaddingBottom) != null ? _b : 0,
2669
+ target: initialScroll
2670
+ });
2671
+ } else {
2672
+ clearFinishedViewportRetargetableInitialScroll(state);
2673
+ }
2440
2674
  }
2441
- clearFinishedViewportRetargetableInitialScroll(state);
2442
2675
  }
2443
2676
  }
2444
2677
  function startBootstrapInitialScrollOnMount(ctx, options) {
@@ -2477,7 +2710,7 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
2477
2710
  }
2478
2711
  const shouldResetDidFinish = !!(state.didFinishInitialScroll && previousDataLength === 0 && dataLength > 0 && initialScroll.index !== void 0);
2479
2712
  const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2480
- const shouldClearFinishedResizePreservation = didDataChange && dataLength > 0 && state.didFinishInitialScroll && !bootstrapInitialScroll && !shouldResetDidFinish;
2713
+ const shouldClearFinishedResizePreservation = !initialScrollAtEnd && didDataChange && dataLength > 0 && state.didFinishInitialScroll && !bootstrapInitialScroll && !shouldResetDidFinish;
2481
2714
  if (shouldClearFinishedResizePreservation) {
2482
2715
  clearPreservedInitialScrollTarget(state);
2483
2716
  return;
@@ -2580,27 +2813,46 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2580
2813
  }
2581
2814
  }
2582
2815
  function handleBootstrapInitialScrollLayoutChange(ctx) {
2816
+ var _a3, _b, _c, _d;
2583
2817
  const state = ctx.state;
2584
2818
  const initialScroll = state.initialScroll;
2585
- if (isOffsetInitialScrollSession(state) || state.props.data.length === 0 || !initialScroll) {
2586
- return;
2587
- }
2588
2819
  const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2589
- if (!bootstrapInitialScroll && initialScroll.viewPosition !== 1) {
2590
- return;
2591
- }
2592
- const didFinishInitialScroll = state.didFinishInitialScroll;
2593
- if (didFinishInitialScroll) {
2594
- setInitialScrollTarget(state, initialScroll, {
2595
- resetDidFinish: true
2596
- });
2597
- state.clearPreservedInitialScrollOnNextFinish = true;
2820
+ if (initialScroll && state.props.data.length > 0 && !isOffsetInitialScrollSession(state) && (bootstrapInitialScroll || initialScroll.viewPosition === 1)) {
2821
+ const resolvedOffset = resolveInitialScrollOffset(ctx, initialScroll);
2822
+ const scrollingTo = ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) ? state.scrollingTo : void 0;
2823
+ if (!bootstrapInitialScroll && (scrollingTo || state.didFinishInitialScroll)) {
2824
+ const currentOffset = scrollingTo ? (_b = scrollingTo.targetOffset) != null ? _b : scrollingTo.offset : getObservedBootstrapInitialScrollOffset(state);
2825
+ const offsetDiff = resolvedOffset - currentOffset;
2826
+ if (Math.abs(offsetDiff) > DEFAULT_BOOTSTRAP_REVEAL_EPSILON) {
2827
+ if (scrollingTo) {
2828
+ const existingWatchdog = initialScrollWatchdog.get(state);
2829
+ scrollingTo.offset = resolvedOffset;
2830
+ scrollingTo.targetOffset = resolvedOffset;
2831
+ state.initialScroll = {
2832
+ ...initialScroll,
2833
+ contentOffset: resolvedOffset
2834
+ };
2835
+ state.hasScrolled = false;
2836
+ initialScrollWatchdog.set(state, {
2837
+ startScroll: (_c = existingWatchdog == null ? void 0 : existingWatchdog.startScroll) != null ? _c : state.scroll,
2838
+ targetOffset: resolvedOffset
2839
+ });
2840
+ }
2841
+ requestAdjust(ctx, offsetDiff);
2842
+ if (state.didFinishInitialScroll) {
2843
+ (_d = state.triggerCalculateItemsInView) == null ? void 0 : _d.call(state, { forceFullItemPositions: true });
2844
+ }
2845
+ }
2846
+ if (state.didFinishInitialScroll) {
2847
+ clearFinishedViewportRetargetableInitialScroll(state);
2848
+ }
2849
+ } else {
2850
+ rearmBootstrapInitialScroll(ctx, {
2851
+ scroll: resolvedOffset,
2852
+ targetIndexSeed: initialScroll.index
2853
+ });
2854
+ }
2598
2855
  }
2599
- rearmBootstrapInitialScroll(ctx, {
2600
- scroll: resolveInitialScrollOffset(ctx, initialScroll),
2601
- seedContentOffset: didFinishInitialScroll && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
2602
- targetIndexSeed: initialScroll.index
2603
- });
2604
2856
  }
2605
2857
  function evaluateBootstrapInitialScroll(ctx) {
2606
2858
  var _a3, _b;
@@ -2728,7 +2980,14 @@ function handleInitialScrollLayoutReady(ctx) {
2728
2980
  }
2729
2981
  function initializeInitialScrollOnMount(ctx, options) {
2730
2982
  var _a3, _b;
2731
- const { dataLength, hasFooterComponent, initialContentOffset, initialScrollAtEnd, useBootstrapInitialScroll } = options;
2983
+ const {
2984
+ alwaysDispatchInitialScroll,
2985
+ dataLength,
2986
+ hasFooterComponent,
2987
+ initialContentOffset,
2988
+ initialScrollAtEnd,
2989
+ useBootstrapInitialScroll
2990
+ } = options;
2732
2991
  const state = ctx.state;
2733
2992
  const initialScroll = state.initialScroll;
2734
2993
  const resolvedInitialContentOffset = initialContentOffset != null ? initialContentOffset : 0;
@@ -2748,7 +3007,7 @@ function initializeInitialScrollOnMount(ctx, options) {
2748
3007
  return;
2749
3008
  }
2750
3009
  const hasPendingDataDependentInitialScroll = !!initialScroll && dataLength === 0 && !(resolvedInitialContentOffset === 0 && !initialScrollAtEnd);
2751
- if (!resolvedInitialContentOffset && !hasPendingDataDependentInitialScroll) {
3010
+ if (!alwaysDispatchInitialScroll && !resolvedInitialContentOffset && !hasPendingDataDependentInitialScroll) {
2752
3011
  if (initialScroll && !initialScrollAtEnd) {
2753
3012
  finishInitialScroll(ctx, {
2754
3013
  resolvedOffset: resolvedInitialContentOffset
@@ -2787,61 +3046,6 @@ function handleInitialScrollDataChange(ctx, options) {
2787
3046
  advanceCurrentInitialScrollSession(ctx);
2788
3047
  }
2789
3048
 
2790
- // src/utils/requestAdjust.ts
2791
- function requestAdjust(ctx, positionDiff, dataChanged) {
2792
- const state = ctx.state;
2793
- if (Math.abs(positionDiff) > 0.1) {
2794
- const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
2795
- const doit = () => {
2796
- if (needsScrollWorkaround) {
2797
- scrollTo(ctx, {
2798
- noScrollingTo: true,
2799
- offset: state.scroll
2800
- });
2801
- } else {
2802
- state.scrollAdjustHandler.requestAdjust(positionDiff);
2803
- if (state.adjustingFromInitialMount) {
2804
- state.adjustingFromInitialMount--;
2805
- }
2806
- }
2807
- };
2808
- state.scroll += positionDiff;
2809
- state.scrollForNextCalculateItemsInView = void 0;
2810
- const readyToRender = peek$(ctx, "readyToRender");
2811
- if (readyToRender) {
2812
- doit();
2813
- if (Platform.OS !== "web") {
2814
- const threshold = state.scroll - positionDiff / 2;
2815
- if (!state.ignoreScrollFromMVCP) {
2816
- state.ignoreScrollFromMVCP = {};
2817
- }
2818
- if (positionDiff > 0) {
2819
- state.ignoreScrollFromMVCP.lt = threshold;
2820
- } else {
2821
- state.ignoreScrollFromMVCP.gt = threshold;
2822
- }
2823
- if (state.ignoreScrollFromMVCPTimeout) {
2824
- clearTimeout(state.ignoreScrollFromMVCPTimeout);
2825
- }
2826
- const delay = needsScrollWorkaround ? 250 : 100;
2827
- state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
2828
- var _a3;
2829
- state.ignoreScrollFromMVCP = void 0;
2830
- const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
2831
- if (shouldForceUpdate) {
2832
- state.ignoreScrollFromMVCPIgnored = false;
2833
- state.scrollPending = state.scroll;
2834
- (_a3 = state.reprocessCurrentScroll) == null ? void 0 : _a3.call(state);
2835
- }
2836
- }, delay);
2837
- }
2838
- } else {
2839
- state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
2840
- requestAnimationFrame(doit);
2841
- }
2842
- }
2843
- }
2844
-
2845
3049
  // src/core/mvcp.ts
2846
3050
  var MVCP_POSITION_EPSILON = 0.1;
2847
3051
  var MVCP_ANCHOR_LOCK_TTL_MS = 300;
@@ -3125,15 +3329,27 @@ function prepareMVCP(ctx, dataChanged) {
3125
3329
  return;
3126
3330
  }
3127
3331
  if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
3128
- requestAdjust(ctx, positionDiff, dataChanged && mvcpData);
3332
+ const shouldSkipAdjustForMaintainedEnd = state.maintainingScrollAtEnd && peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
3333
+ if (!shouldSkipAdjustForMaintainedEnd) {
3334
+ requestAdjust(ctx, positionDiff, dataChanged && mvcpData);
3335
+ }
3129
3336
  }
3130
3337
  };
3131
3338
  }
3132
3339
  }
3133
3340
 
3341
+ // src/core/resetLayoutCachesForDataChange.ts
3342
+ function resetLayoutCachesForDataChange(state) {
3343
+ state.indexByKey.clear();
3344
+ state.idCache.length = 0;
3345
+ state.positions.length = 0;
3346
+ state.columns.length = 0;
3347
+ state.columnSpans.length = 0;
3348
+ }
3349
+
3134
3350
  // src/core/syncMountedContainer.ts
3135
3351
  function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3136
- var _a3, _b, _c, _d, _e, _f, _g, _h;
3352
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i;
3137
3353
  const state = ctx.state;
3138
3354
  const {
3139
3355
  columns,
@@ -3145,7 +3361,8 @@ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3145
3361
  if (item === void 0) {
3146
3362
  return { didChangePosition: false, didRefreshData: false };
3147
3363
  }
3148
- const updateLayout = (_a3 = options == null ? void 0 : options.updateLayout) != null ? _a3 : true;
3364
+ const itemKey = (_a3 = state.idCache[itemIndex]) != null ? _a3 : getId(state, itemIndex);
3365
+ const updateLayout = (_b = options == null ? void 0 : options.updateLayout) != null ? _b : true;
3149
3366
  let didChangePosition = false;
3150
3367
  let didRefreshData = false;
3151
3368
  if (updateLayout) {
@@ -3154,7 +3371,9 @@ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3154
3371
  set$(ctx, `containerPosition${containerIndex}`, POSITION_OUT_OF_VIEW);
3155
3372
  return { didChangePosition: false, didRefreshData: false };
3156
3373
  }
3157
- const position = (positionValue || 0) - ((_b = options == null ? void 0 : options.scrollAdjustPending) != null ? _b : 0);
3374
+ const logicalPosition = (positionValue || 0) - ((_c = options == null ? void 0 : options.scrollAdjustPending) != null ? _c : 0);
3375
+ const itemSize = (_d = state.sizes.get(itemKey)) != null ? _d : getItemSize(ctx, itemKey, itemIndex, item);
3376
+ const position = toPhysicalHorizontalItemPosition(state, logicalPosition, itemSize, peek$(ctx, "totalSize"));
3158
3377
  const column = columns[itemIndex] || 1;
3159
3378
  const span = columnSpans[itemIndex] || 1;
3160
3379
  const prevPos = peek$(ctx, `containerPosition${containerIndex}`);
@@ -3173,15 +3392,15 @@ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3173
3392
  }
3174
3393
  const prevData = peek$(ctx, `containerItemData${containerIndex}`);
3175
3394
  if (prevData !== item) {
3176
- const pendingDataComparison = ((_c = state.pendingDataComparison) == null ? void 0 : _c.previousData) === state.previousData && ((_d = state.pendingDataComparison) == null ? void 0 : _d.nextData) === data ? state.pendingDataComparison : void 0;
3177
- const cachedComparison = (_e = pendingDataComparison == null ? void 0 : pendingDataComparison.byIndex[itemIndex]) != null ? _e : 0;
3395
+ const pendingDataComparison = ((_e = state.pendingDataComparison) == null ? void 0 : _e.previousData) === state.previousData && ((_f = state.pendingDataComparison) == null ? void 0 : _f.nextData) === data ? state.pendingDataComparison : void 0;
3396
+ const cachedComparison = (_g = pendingDataComparison == null ? void 0 : pendingDataComparison.byIndex[itemIndex]) != null ? _g : 0;
3178
3397
  if (cachedComparison === 2) {
3179
3398
  set$(ctx, `containerItemData${containerIndex}`, item);
3180
3399
  didRefreshData = true;
3181
3400
  } else if (cachedComparison !== 1) {
3182
- const itemKey = (_g = (_f = peek$(ctx, `containerItemKey${containerIndex}`)) != null ? _f : state.idCache[itemIndex]) != null ? _g : getId(state, itemIndex);
3401
+ const nextItemKey = (_h = peek$(ctx, `containerItemKey${containerIndex}`)) != null ? _h : itemKey;
3183
3402
  const prevKey = keyExtractor == null ? void 0 : keyExtractor(prevData, itemIndex);
3184
- if (prevData === void 0 || !keyExtractor || prevKey !== itemKey) {
3403
+ if (prevData === void 0 || !keyExtractor || prevKey !== nextItemKey) {
3185
3404
  set$(ctx, `containerItemData${containerIndex}`, item);
3186
3405
  didRefreshData = true;
3187
3406
  } else if (!itemsAreEqual) {
@@ -3198,7 +3417,7 @@ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3198
3417
  };
3199
3418
  }
3200
3419
  }
3201
- if ((_h = state.pendingDataComparison) == null ? void 0 : _h.byIndex) {
3420
+ if ((_i = state.pendingDataComparison) == null ? void 0 : _i.byIndex) {
3202
3421
  state.pendingDataComparison.byIndex[itemIndex] = isEqual ? 1 : 2;
3203
3422
  }
3204
3423
  if (!isEqual) {
@@ -3363,11 +3582,13 @@ function updateSnapToOffsets(ctx) {
3363
3582
  const {
3364
3583
  props: { snapToIndices }
3365
3584
  } = state;
3585
+ const contentSize = state.props.horizontal ? getContentSize(ctx) : void 0;
3366
3586
  const snapToOffsets = Array(snapToIndices.length);
3367
3587
  for (let i = 0; i < snapToIndices.length; i++) {
3368
3588
  const idx = snapToIndices[i];
3369
3589
  getId(state, idx);
3370
- snapToOffsets[i] = state.positions[idx];
3590
+ const logicalOffset = state.positions[idx];
3591
+ snapToOffsets[i] = toNativeHorizontalOffset(state, logicalOffset, contentSize);
3371
3592
  }
3372
3593
  set$(ctx, "snapToOffsets", snapToOffsets);
3373
3594
  }
@@ -3744,6 +3965,30 @@ function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
3744
3965
  var unstableBatchedUpdates = ReactNative.unstable_batchedUpdates;
3745
3966
  var batchedUpdates = typeof unstableBatchedUpdates === "function" ? unstableBatchedUpdates : (fn) => fn();
3746
3967
 
3968
+ // src/utils/containerPool.ts
3969
+ var MIN_INITIAL_CONTAINER_POOL_SIZE = 32;
3970
+ var MAX_INITIAL_SPARE_CONTAINERS = 64;
3971
+ function getInitialContainerPoolSize(dataLength, numContainers, initialContainerPoolRatio) {
3972
+ if (dataLength <= 0 || numContainers <= 0) {
3973
+ return 0;
3974
+ }
3975
+ const ratioPoolSize = Math.ceil(numContainers * initialContainerPoolRatio);
3976
+ const cappedSparePoolSize = numContainers + MAX_INITIAL_SPARE_CONTAINERS;
3977
+ const targetPoolSize = Math.max(
3978
+ numContainers,
3979
+ Math.min(ratioPoolSize, cappedSparePoolSize),
3980
+ Math.min(dataLength, MIN_INITIAL_CONTAINER_POOL_SIZE)
3981
+ );
3982
+ const maxUsefulPoolSize = Math.max(dataLength, numContainers);
3983
+ return Math.min(maxUsefulPoolSize, targetPoolSize);
3984
+ }
3985
+ function getExpandedContainerPoolSize(dataLength, numContainers) {
3986
+ if (dataLength <= 0 || numContainers <= 0) {
3987
+ return 0;
3988
+ }
3989
+ return Math.min(Math.max(dataLength, numContainers), Math.max(numContainers, Math.ceil(numContainers * 1.5)));
3990
+ }
3991
+
3747
3992
  // src/utils/findAvailableContainers.ts
3748
3993
  function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers, protectedKeys) {
3749
3994
  const numContainers = peek$(ctx, "numContainers");
@@ -3949,10 +4194,9 @@ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentSt
3949
4194
  function calculateItemsInView(ctx, params = {}) {
3950
4195
  const state = ctx.state;
3951
4196
  batchedUpdates(() => {
3952
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
4197
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q;
3953
4198
  const {
3954
4199
  columns,
3955
- columnSpans,
3956
4200
  containerItemKeys,
3957
4201
  enableScrollForNextCalculateItemsInView,
3958
4202
  idCache,
@@ -3996,18 +4240,38 @@ function calculateItemsInView(ctx, params = {}) {
3996
4240
  // current initial-scroll target instead of transient native adjustments.
3997
4241
  resolveInitialScrollOffset(ctx, initialScroll)
3998
4242
  ) : state.scroll;
3999
- const scrollAdjustPending = (_c = peek$(ctx, "scrollAdjustPending")) != null ? _c : 0;
4000
- const scrollAdjustPad = scrollAdjustPending - topPad;
4001
- let scroll = Math.round(scrollState + scrollExtra + scrollAdjustPad);
4002
- if (scroll + scrollLength > totalSize) {
4003
- scroll = Math.max(0, totalSize - scrollLength);
4004
- }
4243
+ let scrollAdjustPending = 0;
4244
+ let scrollAdjustPad = 0;
4245
+ let scroll = 0;
4246
+ let scrollTopBuffered = 0;
4247
+ let scrollBottom = 0;
4248
+ let scrollBottomBuffered = 0;
4249
+ let nativeScrollState = scrollState;
4250
+ const updateScroll2 = (nextScrollState) => {
4251
+ var _a4;
4252
+ nativeScrollState = nextScrollState;
4253
+ scrollAdjustPending = (_a4 = peek$(ctx, "scrollAdjustPending")) != null ? _a4 : 0;
4254
+ scrollAdjustPad = scrollAdjustPending - topPad;
4255
+ scroll = Math.round(nextScrollState + scrollExtra + scrollAdjustPad);
4256
+ if (scroll + scrollLength > totalSize) {
4257
+ scroll = Math.max(0, totalSize - scrollLength);
4258
+ }
4259
+ };
4260
+ updateScroll2(scrollState);
4005
4261
  const previousStickyIndex = peek$(ctx, "activeStickyIndex");
4006
4262
  const currentStickyIdx = stickyIndicesArr.length > 0 ? findCurrentStickyIndex(stickyIndicesArr, scroll, state) : -1;
4007
4263
  const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyIndicesArr[currentStickyIdx] : -1;
4264
+ const stickyIndexDidChange = previousStickyIndex !== nextActiveStickyIndex;
4008
4265
  if (currentStickyIdx >= 0 || previousStickyIndex >= 0) {
4009
4266
  set$(ctx, "activeStickyIndex", nextActiveStickyIndex);
4010
4267
  }
4268
+ const shouldNotifyStickyHeaderChange = !!onStickyHeaderChange && stickyIndicesArr.length > 0 && stickyIndexDidChange;
4269
+ const finishCalculateItemsInView = shouldNotifyStickyHeaderChange ? () => {
4270
+ const item = data[nextActiveStickyIndex];
4271
+ if (item !== void 0) {
4272
+ onStickyHeaderChange == null ? void 0 : onStickyHeaderChange({ index: nextActiveStickyIndex, item });
4273
+ }
4274
+ } : void 0;
4011
4275
  let scrollBufferTop = drawDistance;
4012
4276
  let scrollBufferBottom = drawDistance;
4013
4277
  if (speed > 0 || speed === 0 && scroll < Math.max(50, drawDistance)) {
@@ -4017,28 +4281,30 @@ function calculateItemsInView(ctx, params = {}) {
4017
4281
  scrollBufferTop = drawDistance * 1.5;
4018
4282
  scrollBufferBottom = drawDistance * 0.5;
4019
4283
  }
4020
- const scrollTopBuffered = scroll - scrollBufferTop;
4021
- const scrollBottom = scroll + scrollLength + (scroll < 0 ? -scroll : 0);
4022
- const scrollBottomBuffered = scrollBottom + scrollBufferBottom;
4284
+ const updateScrollRange = () => {
4285
+ const scrollStart = Math.max(0, scroll);
4286
+ const overscrollBeforeContent = Math.max(0, -nativeScrollState);
4287
+ scrollTopBuffered = scrollStart - scrollBufferTop;
4288
+ scrollBottom = Math.max(scrollStart, scroll + scrollLength + overscrollBeforeContent);
4289
+ scrollBottomBuffered = scrollBottom + scrollBufferBottom;
4290
+ };
4291
+ updateScrollRange();
4023
4292
  if (!suppressInitialScrollSideEffects && !dataChanged && !forceFullItemPositions && scrollForNextCalculateItemsInView) {
4024
4293
  const { top, bottom } = scrollForNextCalculateItemsInView;
4025
4294
  if (top === null && bottom === null) {
4026
4295
  state.scrollForNextCalculateItemsInView = void 0;
4027
4296
  } else if ((top === null || scrollTopBuffered > top) && (bottom === null || scrollBottomBuffered < bottom)) {
4028
4297
  if (Platform.OS !== "web" || !isInMVCPActiveMode(state)) {
4298
+ finishCalculateItemsInView == null ? void 0 : finishCalculateItemsInView();
4029
4299
  return;
4030
4300
  }
4031
4301
  }
4032
4302
  }
4033
4303
  const checkMVCP = doMVCP && !suppressInitialScrollSideEffects ? prepareMVCP(ctx, dataChanged) : void 0;
4034
4304
  if (dataChanged) {
4035
- indexByKey.clear();
4036
- idCache.length = 0;
4037
- positions.length = 0;
4038
- columns.length = 0;
4039
- columnSpans.length = 0;
4305
+ resetLayoutCachesForDataChange(state);
4040
4306
  }
4041
- const startIndex = forceFullItemPositions || dataChanged ? 0 : (_d = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _d : 0;
4307
+ const startIndex = forceFullItemPositions || dataChanged ? 0 : (_c = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _c : 0;
4042
4308
  const optimizeForVisibleWindow = !forceFullItemPositions && !dataChanged && numColumns > 1 && minIndexSizeChanged !== void 0;
4043
4309
  updateItemPositions(ctx, dataChanged, {
4044
4310
  doMVCP,
@@ -4063,21 +4329,25 @@ function calculateItemsInView(ctx, params = {}) {
4063
4329
  }
4064
4330
  }
4065
4331
  const scrollBeforeMVCP = state.scroll;
4066
- const scrollAdjustPendingBeforeMVCP = (_e = peek$(ctx, "scrollAdjustPending")) != null ? _e : 0;
4332
+ const scrollAdjustPendingBeforeMVCP = (_d = peek$(ctx, "scrollAdjustPending")) != null ? _d : 0;
4067
4333
  checkMVCP == null ? void 0 : checkMVCP();
4068
- const didMVCPAdjustScroll = !!checkMVCP && (state.scroll !== scrollBeforeMVCP || ((_f = peek$(ctx, "scrollAdjustPending")) != null ? _f : 0) !== scrollAdjustPendingBeforeMVCP);
4334
+ const didMVCPAdjustScroll = !!checkMVCP && (state.scroll !== scrollBeforeMVCP || ((_e = peek$(ctx, "scrollAdjustPending")) != null ? _e : 0) !== scrollAdjustPendingBeforeMVCP);
4335
+ if (didMVCPAdjustScroll && initialScroll) {
4336
+ updateScroll2(state.scroll);
4337
+ updateScrollRange();
4338
+ }
4069
4339
  let startNoBuffer = null;
4070
4340
  let startBuffered = null;
4071
4341
  let startBufferedId = null;
4072
4342
  let endNoBuffer = null;
4073
4343
  let endBuffered = null;
4074
- let loopStart = (_g = suppressInitialScrollSideEffects ? bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.targetIndexSeed : void 0) != null ? _g : !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
4344
+ let loopStart = (_f = suppressInitialScrollSideEffects ? bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.targetIndexSeed : void 0) != null ? _f : !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
4075
4345
  for (let i = loopStart; i >= 0; i--) {
4076
- const id = (_h = idCache[i]) != null ? _h : getId(state, i);
4346
+ const id = (_g = idCache[i]) != null ? _g : getId(state, i);
4077
4347
  const top = positions[i];
4078
- const size = (_i = sizes.get(id)) != null ? _i : getItemSize(ctx, id, i, data[i]);
4348
+ const size = (_h = sizes.get(id)) != null ? _h : getItemSize(ctx, id, i, data[i]);
4079
4349
  const bottom = top + size;
4080
- if (bottom > scroll - scrollBufferTop) {
4350
+ if (bottom > scrollTopBuffered) {
4081
4351
  loopStart = i;
4082
4352
  } else {
4083
4353
  break;
@@ -4106,8 +4376,8 @@ function calculateItemsInView(ctx, params = {}) {
4106
4376
  let firstFullyOnScreenIndex;
4107
4377
  const dataLength = data.length;
4108
4378
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
4109
- const id = (_j = idCache[i]) != null ? _j : getId(state, i);
4110
- const size = (_k = sizes.get(id)) != null ? _k : getItemSize(ctx, id, i, data[i]);
4379
+ const id = (_i = idCache[i]) != null ? _i : getId(state, i);
4380
+ const size = (_j = sizes.get(id)) != null ? _j : getItemSize(ctx, id, i, data[i]);
4111
4381
  const top = positions[i];
4112
4382
  if (!foundEnd) {
4113
4383
  if (startNoBuffer === null && top + size > scroll) {
@@ -4146,7 +4416,7 @@ function calculateItemsInView(ctx, params = {}) {
4146
4416
  const firstVisibleAnchorIndex = firstFullyOnScreenIndex != null ? firstFullyOnScreenIndex : startNoBuffer;
4147
4417
  if (firstVisibleAnchorIndex !== null && firstVisibleAnchorIndex !== void 0 && endNoBuffer !== null) {
4148
4418
  for (let i = firstVisibleAnchorIndex; i <= endNoBuffer; i++) {
4149
- const id = (_l = idCache[i]) != null ? _l : getId(state, i);
4419
+ const id = (_k = idCache[i]) != null ? _k : getId(state, i);
4150
4420
  idsInView.push(id);
4151
4421
  }
4152
4422
  }
@@ -4179,7 +4449,7 @@ function calculateItemsInView(ctx, params = {}) {
4179
4449
  const needNewContainers = [];
4180
4450
  const needNewContainersSet = /* @__PURE__ */ new Set();
4181
4451
  for (let i = startBuffered; i <= endBuffered; i++) {
4182
- const id = (_m = idCache[i]) != null ? _m : getId(state, i);
4452
+ const id = (_l = idCache[i]) != null ? _l : getId(state, i);
4183
4453
  if (!containerItemKeys.has(id)) {
4184
4454
  needNewContainersSet.add(i);
4185
4455
  needNewContainers.push(i);
@@ -4188,7 +4458,7 @@ function calculateItemsInView(ctx, params = {}) {
4188
4458
  if (alwaysRenderArr.length > 0) {
4189
4459
  for (const index of alwaysRenderArr) {
4190
4460
  if (index < 0 || index >= dataLength) continue;
4191
- const id = (_n = idCache[index]) != null ? _n : getId(state, index);
4461
+ const id = (_m = idCache[index]) != null ? _m : getId(state, index);
4192
4462
  if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
4193
4463
  needNewContainersSet.add(index);
4194
4464
  needNewContainers.push(index);
@@ -4227,7 +4497,7 @@ function calculateItemsInView(ctx, params = {}) {
4227
4497
  for (let idx = 0; idx < needNewContainers.length; idx++) {
4228
4498
  const i = needNewContainers[idx];
4229
4499
  const containerIndex = availableContainers[idx];
4230
- const id = (_o = idCache[i]) != null ? _o : getId(state, i);
4500
+ const id = (_n = idCache[i]) != null ? _n : getId(state, i);
4231
4501
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
4232
4502
  if (oldKey && oldKey !== id) {
4233
4503
  containerItemKeys.delete(oldKey);
@@ -4238,7 +4508,7 @@ function calculateItemsInView(ctx, params = {}) {
4238
4508
  state.containerItemTypes.set(containerIndex, requiredItemTypes[idx]);
4239
4509
  }
4240
4510
  containerItemKeys.set(id, containerIndex);
4241
- (_p = state.userScrollAnchorResetKeys) == null ? void 0 : _p.add(id);
4511
+ (_o = state.userScrollAnchorResetKeys) == null ? void 0 : _o.add(id);
4242
4512
  const containerSticky = `containerSticky${containerIndex}`;
4243
4513
  const isSticky = stickyIndicesSet.has(i);
4244
4514
  const isAlwaysRender = alwaysRenderSet.has(i);
@@ -4262,17 +4532,17 @@ function calculateItemsInView(ctx, params = {}) {
4262
4532
  if (numContainers !== prevNumContainers) {
4263
4533
  set$(ctx, "numContainers", numContainers);
4264
4534
  if (numContainers > peek$(ctx, "numContainersPooled")) {
4265
- set$(ctx, "numContainersPooled", Math.ceil(numContainers * 1.5));
4535
+ set$(ctx, "numContainersPooled", getExpandedContainerPoolSize(dataLength, numContainers));
4266
4536
  }
4267
4537
  }
4268
4538
  }
4269
- if (((_q = state.userScrollAnchorResetKeys) == null ? void 0 : _q.size) === 0) {
4539
+ if (((_p = state.userScrollAnchorResetKeys) == null ? void 0 : _p.size) === 0) {
4270
4540
  state.userScrollAnchorResetKeys = void 0;
4271
4541
  }
4272
4542
  if (alwaysRenderArr.length > 0) {
4273
4543
  for (const index of alwaysRenderArr) {
4274
4544
  if (index < 0 || index >= dataLength) continue;
4275
- const id = (_r = idCache[index]) != null ? _r : getId(state, index);
4545
+ const id = (_q = idCache[index]) != null ? _q : getId(state, index);
4276
4546
  const containerIndex = containerItemKeys.get(id);
4277
4547
  if (containerIndex !== void 0) {
4278
4548
  state.stickyContainerPool.add(containerIndex);
@@ -4346,12 +4616,7 @@ function calculateItemsInView(ctx, params = {}) {
4346
4616
  );
4347
4617
  }
4348
4618
  }
4349
- if (onStickyHeaderChange && stickyIndicesArr.length > 0 && nextActiveStickyIndex !== void 0 && nextActiveStickyIndex !== previousStickyIndex) {
4350
- const item = data[nextActiveStickyIndex];
4351
- if (item !== void 0) {
4352
- onStickyHeaderChange({ index: nextActiveStickyIndex, item });
4353
- }
4354
- }
4619
+ finishCalculateItemsInView == null ? void 0 : finishCalculateItemsInView();
4355
4620
  });
4356
4621
  }
4357
4622
 
@@ -4376,21 +4641,36 @@ function doMaintainScrollAtEnd(ctx) {
4376
4641
  if (contentSize < state.scrollLength) {
4377
4642
  state.scroll = 0;
4378
4643
  }
4379
- requestAnimationFrame(() => {
4380
- var _a3;
4381
- if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
4382
- state.maintainingScrollAtEnd = true;
4383
- (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
4384
- animated: maintainScrollAtEnd.animated
4385
- });
4386
- setTimeout(
4387
- () => {
4388
- state.maintainingScrollAtEnd = false;
4389
- },
4390
- maintainScrollAtEnd.animated ? 500 : 0
4391
- );
4392
- }
4393
- });
4644
+ if (!state.maintainingScrollAtEnd) {
4645
+ state.maintainingScrollAtEnd = true;
4646
+ requestAnimationFrame(() => {
4647
+ if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
4648
+ const scroller = refScroller.current;
4649
+ if (state.props.horizontal && isHorizontalRTL(state)) {
4650
+ const currentContentSize = getContentSize(ctx);
4651
+ const logicalEndOffset = getLogicalHorizontalMaxOffset(state, currentContentSize);
4652
+ const nativeOffset = toNativeHorizontalOffset(state, logicalEndOffset, currentContentSize);
4653
+ scroller == null ? void 0 : scroller.scrollTo({
4654
+ animated: maintainScrollAtEnd.animated,
4655
+ x: nativeOffset,
4656
+ y: 0
4657
+ });
4658
+ } else {
4659
+ scroller == null ? void 0 : scroller.scrollToEnd({
4660
+ animated: maintainScrollAtEnd.animated
4661
+ });
4662
+ }
4663
+ setTimeout(
4664
+ () => {
4665
+ state.maintainingScrollAtEnd = false;
4666
+ },
4667
+ maintainScrollAtEnd.animated ? 500 : 0
4668
+ );
4669
+ } else {
4670
+ state.maintainingScrollAtEnd = false;
4671
+ }
4672
+ });
4673
+ }
4394
4674
  return true;
4395
4675
  }
4396
4676
  return false;
@@ -4502,14 +4782,21 @@ function doInitialAllocateContainers(ctx) {
4502
4782
  } else {
4503
4783
  averageItemSize = estimatedItemSize;
4504
4784
  }
4505
- const numContainers = Math.ceil((scrollLength + drawDistance * 2) / averageItemSize * numColumns);
4785
+ const numContainers = Math.max(
4786
+ 1,
4787
+ Math.ceil((scrollLength + drawDistance * 2) / averageItemSize * numColumns)
4788
+ );
4506
4789
  for (let i = 0; i < numContainers; i++) {
4507
4790
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
4508
4791
  set$(ctx, `containerColumn${i}`, -1);
4509
4792
  set$(ctx, `containerSpan${i}`, 1);
4510
4793
  }
4511
4794
  set$(ctx, "numContainers", numContainers);
4512
- set$(ctx, "numContainersPooled", numContainers * state.props.initialContainerPoolRatio);
4795
+ set$(
4796
+ ctx,
4797
+ "numContainersPooled",
4798
+ getInitialContainerPoolSize(data.length, numContainers, state.props.initialContainerPoolRatio)
4799
+ );
4513
4800
  if (!IsNewArchitecture || state.lastLayout) {
4514
4801
  if (state.initialScroll) {
4515
4802
  requestAnimationFrame(() => {
@@ -4568,7 +4855,8 @@ function handleLayout(ctx, layoutParam, setCanRender) {
4568
4855
  }
4569
4856
  checkThresholds(ctx);
4570
4857
  if (state) {
4571
- state.needsOtherAxisSize = otherAxisSize - (state.props.stylePaddingTop || 0) < 10;
4858
+ const crossAxisPadding = state.props.horizontal ? (state.props.stylePaddingTop || 0) + (state.props.stylePaddingBottom || 0) : (state.props.stylePaddingLeft || 0) + (state.props.stylePaddingRight || 0);
4859
+ state.needsOtherAxisSize = otherAxisSize - crossAxisPadding < 10;
4572
4860
  }
4573
4861
  if (IS_DEV && measuredLength === 0) {
4574
4862
  warnDevOnce(
@@ -4661,8 +4949,8 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
4661
4949
  // src/core/onScroll.ts
4662
4950
  function trackInitialScrollNativeProgress(state, newScroll) {
4663
4951
  const initialNativeScrollWatchdog = initialScrollWatchdog.get(state);
4664
- const didInitialScrollProgress = !!initialNativeScrollWatchdog && initialScrollWatchdog.didObserveProgress(newScroll, initialNativeScrollWatchdog);
4665
- if (didInitialScrollProgress) {
4952
+ const didInitialScrollReachTarget = !!initialNativeScrollWatchdog && initialScrollWatchdog.didReachTarget(newScroll, initialNativeScrollWatchdog);
4953
+ if (didInitialScrollReachTarget) {
4666
4954
  initialScrollWatchdog.clear(state);
4667
4955
  return;
4668
4956
  }
@@ -4687,7 +4975,7 @@ function cloneScrollEvent(event) {
4687
4975
  };
4688
4976
  }
4689
4977
  function onScroll(ctx, event) {
4690
- var _a3, _b, _c, _d;
4978
+ var _a3, _b, _c, _d, _e;
4691
4979
  const state = ctx.state;
4692
4980
  const { scrollProcessingEnabled } = state;
4693
4981
  if (scrollProcessingEnabled === false) {
@@ -4706,6 +4994,9 @@ function onScroll(ctx, event) {
4706
4994
  }
4707
4995
  }
4708
4996
  let newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
4997
+ if (state.props.horizontal) {
4998
+ newScroll = toLogicalHorizontalOffset(state, newScroll, (_e = event.nativeEvent.contentSize) == null ? void 0 : _e.width);
4999
+ }
4709
5000
  if (state.scrollingTo && state.scrollingTo.offset >= newScroll) {
4710
5001
  const maxOffset = clampScrollOffset(ctx, newScroll, state.scrollingTo);
4711
5002
  if (newScroll !== maxOffset && Math.abs(newScroll - maxOffset) > 1) {
@@ -4800,16 +5091,20 @@ function maybeUpdateAnchoredEndSpace(ctx) {
4800
5091
  let contentBelowAnchor = 0;
4801
5092
  const footerSize = ctx.values.get("footerSize") || 0;
4802
5093
  const stylePaddingBottom = state.props.stylePaddingBottom || 0;
5094
+ let hasUnknownTailSize = false;
4803
5095
  for (let index = anchorIndex; index < data.length; index++) {
4804
5096
  const itemKey = getId(state, index);
4805
5097
  const size = itemKey ? state.sizesKnown.get(itemKey) : void 0;
4806
5098
  const effectiveSize = index === anchorIndex && anchorMaxSize !== void 0 ? Math.min(size || 0, Math.max(0, anchorMaxSize)) : size;
5099
+ if (size === void 0) {
5100
+ hasUnknownTailSize = true;
5101
+ }
4807
5102
  if (effectiveSize !== null && effectiveSize !== void 0 && effectiveSize > 0) {
4808
5103
  contentBelowAnchor += effectiveSize;
4809
5104
  }
4810
5105
  }
4811
5106
  contentBelowAnchor += footerSize + stylePaddingBottom;
4812
- nextSize = Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
5107
+ nextSize = hasUnknownTailSize ? previousSize || 0 : Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
4813
5108
  }
4814
5109
  }
4815
5110
  if (previousSize !== nextSize) {
@@ -4875,6 +5170,16 @@ function runOrScheduleMVCPRecalculate(ctx) {
4875
5170
  calculateItemsInView(ctx, { doMVCP: true });
4876
5171
  }
4877
5172
  }
5173
+ function updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal) {
5174
+ const state = ctx.state;
5175
+ if (state.needsOtherAxisSize) {
5176
+ const otherAxisSize = horizontal ? sizeObj.height : sizeObj.width;
5177
+ const currentOtherAxisSize = peek$(ctx, "otherAxisSize");
5178
+ if (!currentOtherAxisSize || otherAxisSize > currentOtherAxisSize) {
5179
+ set$(ctx, "otherAxisSize", otherAxisSize);
5180
+ }
5181
+ }
5182
+ }
4878
5183
  function updateItemSize(ctx, itemKey, sizeObj) {
4879
5184
  var _a3;
4880
5185
  const state = ctx.state;
@@ -4883,15 +5188,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
4883
5188
  const {
4884
5189
  didContainersLayout,
4885
5190
  sizesKnown,
4886
- props: {
4887
- getFixedItemSize,
4888
- getItemType,
4889
- horizontal,
4890
- suggestEstimatedItemSize,
4891
- onItemSizeChanged,
4892
- data,
4893
- maintainScrollAtEnd
4894
- }
5191
+ props: { getFixedItemSize, getItemType, horizontal, onItemSizeChanged, data, maintainScrollAtEnd }
4895
5192
  } = state;
4896
5193
  if (!data) return;
4897
5194
  const index = state.indexByKey.get(itemKey);
@@ -4906,13 +5203,13 @@ function updateItemSize(ctx, itemKey, sizeObj) {
4906
5203
  const type = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
4907
5204
  const size2 = getFixedItemSize(itemData, index, type);
4908
5205
  if (size2 !== void 0 && size2 === sizesKnown.get(itemKey)) {
5206
+ updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
4909
5207
  return;
4910
5208
  }
4911
5209
  }
4912
5210
  let needsRecalculate = !didContainersLayout;
4913
5211
  let shouldMaintainScrollAtEnd = false;
4914
5212
  let minIndexSizeChanged;
4915
- let maxOtherAxisSize = peek$(ctx, "otherAxisSize") || 0;
4916
5213
  const prevSizeKnown = state.sizesKnown.get(itemKey);
4917
5214
  const diff = updateOneItemSize(ctx, itemKey, sizeObj);
4918
5215
  const size = roundSize(horizontal ? sizeObj.width : sizeObj.height);
@@ -4923,10 +5220,6 @@ function updateItemSize(ctx, itemKey, sizeObj) {
4923
5220
  if (!needsRecalculate && state.containerItemKeys.has(itemKey)) {
4924
5221
  needsRecalculate = true;
4925
5222
  }
4926
- if (state.needsOtherAxisSize) {
4927
- const otherAxisSize = horizontal ? sizeObj.height : sizeObj.width;
4928
- maxOtherAxisSize = Math.max(maxOtherAxisSize, otherAxisSize);
4929
- }
4930
5223
  if (prevSizeKnown !== void 0 && Math.abs(prevSizeKnown - size) > 5) {
4931
5224
  shouldMaintainScrollAtEnd = true;
4932
5225
  }
@@ -4942,22 +5235,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
4942
5235
  if (minIndexSizeChanged !== void 0) {
4943
5236
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
4944
5237
  }
4945
- if (IS_DEV && suggestEstimatedItemSize && minIndexSizeChanged !== void 0) {
4946
- if (state.timeoutSizeMessage) clearTimeout(state.timeoutSizeMessage);
4947
- state.timeoutSizeMessage = setTimeout(() => {
4948
- var _a4;
4949
- state.timeoutSizeMessage = void 0;
4950
- const num = state.sizesKnown.size;
4951
- const avg = (_a4 = state.averageSizes[""]) == null ? void 0 : _a4.avg;
4952
- console.warn(
4953
- `[legend-list] Based on the ${num} items rendered so far, the optimal estimated size is ${avg}.`
4954
- );
4955
- }, 1e3);
4956
- }
4957
- const cur = peek$(ctx, "otherAxisSize");
4958
- if (!cur || maxOtherAxisSize > cur) {
4959
- set$(ctx, "otherAxisSize", maxOtherAxisSize);
4960
- }
5238
+ updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
4961
5239
  if (didContainersLayout || checkAllSizesKnown(state, getMountedBufferedIndices(state))) {
4962
5240
  if (needsRecalculate) {
4963
5241
  state.scrollForNextCalculateItemsInView = void 0;
@@ -5071,12 +5349,47 @@ function createColumnWrapperStyle(contentContainerStyle) {
5071
5349
  }
5072
5350
 
5073
5351
  // src/utils/createImperativeHandle.ts
5352
+ var DEFAULT_AVERAGE_ITEM_SIZE_TYPE = "default";
5353
+ function getAverageItemSizes(state) {
5354
+ const averageItemSizes = {};
5355
+ for (const itemType in state.averageSizes) {
5356
+ const averageSize = state.averageSizes[itemType];
5357
+ if (averageSize) {
5358
+ averageItemSizes[itemType || DEFAULT_AVERAGE_ITEM_SIZE_TYPE] = {
5359
+ average: averageSize.avg,
5360
+ count: averageSize.num
5361
+ };
5362
+ }
5363
+ }
5364
+ return averageItemSizes;
5365
+ }
5074
5366
  function createImperativeHandle(ctx) {
5075
5367
  const state = ctx.state;
5076
5368
  const IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS = 800;
5077
5369
  const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
5078
5370
  let imperativeScrollToken = 0;
5079
5371
  const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0;
5372
+ const isScrollToIndexReady = (targetIndex, allowEmpty = false) => {
5373
+ var _a3;
5374
+ const props = state.props;
5375
+ const dataLength = props.data.length;
5376
+ const anchorIndex = (_a3 = props.anchoredEndSpace) == null ? void 0 : _a3.anchorIndex;
5377
+ if (targetIndex < 0) {
5378
+ return allowEmpty;
5379
+ }
5380
+ if (targetIndex >= dataLength) {
5381
+ return false;
5382
+ }
5383
+ if (anchorIndex === void 0 || anchorIndex < 0 || anchorIndex >= dataLength || targetIndex < anchorIndex || props.getFixedItemSize) {
5384
+ return true;
5385
+ }
5386
+ for (let index = anchorIndex; index < dataLength; index++) {
5387
+ if (!state.sizesKnown.has(getId(state, index))) {
5388
+ return false;
5389
+ }
5390
+ }
5391
+ return true;
5392
+ };
5080
5393
  const runWhenReady = (token, run, isReady) => {
5081
5394
  const startedAt = Date.now();
5082
5395
  let stableFrames = 0;
@@ -5098,11 +5411,10 @@ function createImperativeHandle(ctx) {
5098
5411
  };
5099
5412
  requestAnimationFrame(check);
5100
5413
  };
5101
- const runScrollWithPromise = (run, options) => new Promise((resolve) => {
5102
- var _a3, _b;
5414
+ const runScrollWithPromise = (run, isReady = () => true) => new Promise((resolve) => {
5415
+ var _a3;
5103
5416
  const token = ++imperativeScrollToken;
5104
- const isReady = (_a3 = options == null ? void 0 : options.isReady) != null ? _a3 : (() => true);
5105
- (_b = state.pendingScrollResolve) == null ? void 0 : _b.call(state);
5417
+ (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
5106
5418
  state.pendingScrollResolve = resolve;
5107
5419
  const runNow = () => {
5108
5420
  if (token !== imperativeScrollToken) {
@@ -5177,6 +5489,7 @@ function createImperativeHandle(ctx) {
5177
5489
  },
5178
5490
  end: state.endNoBuffer,
5179
5491
  endBuffered: state.endBuffered,
5492
+ getAverageItemSizes: () => getAverageItemSizes(state),
5180
5493
  isAtEnd: peek$(ctx, "isAtEnd"),
5181
5494
  isAtStart: peek$(ctx, "isAtStart"),
5182
5495
  isEndReached: state.isEndReached,
@@ -5219,40 +5532,34 @@ function createImperativeHandle(ctx) {
5219
5532
  }
5220
5533
  return false;
5221
5534
  }),
5222
- scrollToEnd: (options) => runScrollWithPromise(() => {
5223
- const data = state.props.data;
5224
- const stylePaddingBottom = state.props.stylePaddingBottom;
5225
- const index = data.length - 1;
5226
- if (index !== -1) {
5227
- const paddingBottom = stylePaddingBottom || 0;
5228
- const footerSize = peek$(ctx, "footerSize") || 0;
5229
- scrollToIndex(ctx, {
5230
- ...options,
5231
- index,
5232
- viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
5233
- viewPosition: 1
5234
- });
5235
- return true;
5236
- }
5237
- return false;
5238
- }),
5239
- scrollToIndex: (params) => {
5240
- const shouldWaitForOutOfRangeTarget = params.index >= 0 && params.index >= state.props.data.length;
5241
- const options = shouldWaitForOutOfRangeTarget ? {
5242
- isReady: () => {
5243
- var _a3;
5244
- const props = state.props;
5245
- const anchorIndex = (_a3 = props.anchoredEndSpace) == null ? void 0 : _a3.anchorIndex;
5246
- const lastIndex = props.data.length - 1;
5247
- const isInRange = params.index < props.data.length;
5248
- const shouldWaitForAnchorSize = isInRange && anchorIndex !== void 0 && anchorIndex >= 0 && params.index >= anchorIndex && !props.getFixedItemSize && !state.sizesKnown.has(getId(state, lastIndex));
5249
- return isInRange && !shouldWaitForAnchorSize;
5535
+ scrollToEnd: (options) => runScrollWithPromise(
5536
+ () => {
5537
+ const data = state.props.data;
5538
+ const stylePaddingBottom = state.props.stylePaddingBottom;
5539
+ const index = data.length - 1;
5540
+ if (index !== -1) {
5541
+ const paddingBottom = stylePaddingBottom || 0;
5542
+ const footerSize = peek$(ctx, "footerSize") || 0;
5543
+ scrollToIndex(ctx, {
5544
+ ...options,
5545
+ index,
5546
+ viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
5547
+ viewPosition: 1
5548
+ });
5549
+ return true;
5250
5550
  }
5251
- } : void 0;
5252
- return runScrollWithPromise(() => {
5253
- scrollToIndex(ctx, params);
5254
- return true;
5255
- }, options);
5551
+ return false;
5552
+ },
5553
+ () => isScrollToIndexReady(state.props.data.length - 1, true)
5554
+ ),
5555
+ scrollToIndex: (params) => {
5556
+ return runScrollWithPromise(
5557
+ () => {
5558
+ scrollToIndex(ctx, params);
5559
+ return true;
5560
+ },
5561
+ params.index >= 0 ? () => isScrollToIndexReady(params.index) : void 0
5562
+ );
5256
5563
  },
5257
5564
  scrollToItem: ({ item, ...props }) => runScrollWithPromise(() => {
5258
5565
  const data = state.props.data;
@@ -5516,7 +5823,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5516
5823
  getFixedItemSize,
5517
5824
  getItemType,
5518
5825
  horizontal,
5519
- initialContainerPoolRatio = 2,
5826
+ rtl,
5827
+ initialContainerPoolRatio = 3,
5828
+ estimatedHeaderSize,
5520
5829
  initialScrollAtEnd = false,
5521
5830
  initialScrollIndex: initialScrollIndexProp,
5522
5831
  initialScrollOffset: initialScrollOffsetProp,
@@ -5557,7 +5866,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5557
5866
  stickyIndices: stickyIndicesDeprecated,
5558
5867
  // TODOV3: Remove from v3 release
5559
5868
  style: styleProp,
5560
- suggestEstimatedItemSize,
5561
5869
  useWindowScroll = false,
5562
5870
  viewabilityConfig,
5563
5871
  viewabilityConfigCallbackPairs,
@@ -5585,13 +5893,16 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5585
5893
  const style = { ...StyleSheet.flatten(styleProp) };
5586
5894
  const stylePaddingTopState = extractPadding(style, contentContainerStyle, "Top");
5587
5895
  const stylePaddingBottomState = extractPadding(style, contentContainerStyle, "Bottom");
5896
+ const stylePaddingLeftState = extractPadding(style, contentContainerStyle, "Left");
5897
+ const stylePaddingRightState = extractPadding(style, contentContainerStyle, "Right");
5588
5898
  const maintainScrollAtEndConfig = normalizeMaintainScrollAtEnd(maintainScrollAtEnd);
5589
5899
  const maintainVisibleContentPositionConfig = normalizeMaintainVisibleContentPosition(
5590
5900
  maintainVisibleContentPositionProp
5591
5901
  );
5592
5902
  const hasInitialScrollIndex = initialScrollIndexProp !== void 0 && initialScrollIndexProp !== null;
5593
5903
  const hasInitialScrollOffset = initialScrollOffsetProp !== void 0 && initialScrollOffsetProp !== null;
5594
- const initialScrollUsesOffsetOnly = !initialScrollAtEnd && !hasInitialScrollIndex && hasInitialScrollOffset;
5904
+ const shouldInitializeHorizontalRTL = !initialScrollAtEnd && !hasInitialScrollIndex && !hasInitialScrollOffset && isHorizontalRTLProps({ horizontal, rtl });
5905
+ const initialScrollUsesOffsetOnly = !initialScrollAtEnd && !hasInitialScrollIndex && (hasInitialScrollOffset || shouldInitializeHorizontalRTL);
5595
5906
  const usesBootstrapInitialScroll = initialScrollAtEnd || hasInitialScrollIndex;
5596
5907
  const initialScrollProp = initialScrollAtEnd ? {
5597
5908
  index: Math.max(0, dataProp.length - 1),
@@ -5698,7 +6009,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5698
6009
  startReachedSnapshotDataChangeEpoch: void 0,
5699
6010
  stickyContainerPool: /* @__PURE__ */ new Set(),
5700
6011
  stickyContainers: /* @__PURE__ */ new Map(),
5701
- timeoutSizeMessage: 0,
5702
6012
  timeouts: /* @__PURE__ */ new Set(),
5703
6013
  totalSize: 0,
5704
6014
  viewabilityConfigCallbackPairs: void 0
@@ -5708,6 +6018,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5708
6018
  internalState.reprocessCurrentScroll = () => updateScroll(ctx, internalState.scroll, true);
5709
6019
  set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPositionConfig);
5710
6020
  set$(ctx, "extraData", extraData);
6021
+ if (estimatedHeaderSize !== void 0) {
6022
+ set$(ctx, "headerSize", estimatedHeaderSize);
6023
+ }
5711
6024
  }
5712
6025
  refState.current = ctx.state;
5713
6026
  }
@@ -5718,7 +6031,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5718
6031
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
5719
6032
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
5720
6033
  const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
5721
- if (didDataChangeLocal && state.didFinishInitialScroll && ((_f = state.initialScroll) == null ? void 0 : _f.viewPosition) === 1 && state.props.data.length > 0) {
6034
+ if (didDataChangeLocal && !initialScrollAtEnd && state.didFinishInitialScroll && ((_f = state.initialScroll) == null ? void 0 : _f.viewPosition) === 1 && state.props.data.length > 0) {
5722
6035
  clearPreservedInitialScrollTarget(state);
5723
6036
  }
5724
6037
  if (didDataChangeLocal) {
@@ -5767,13 +6080,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5767
6080
  positionComponentInternal,
5768
6081
  recycleItems: !!recycleItems,
5769
6082
  renderItem,
6083
+ rtl,
5770
6084
  snapToIndices,
5771
6085
  stickyIndicesArr: stickyHeaderIndices != null ? stickyHeaderIndices : [],
5772
6086
  stickyIndicesSet: useMemo(() => new Set(stickyHeaderIndices != null ? stickyHeaderIndices : []), [stickyHeaderIndices == null ? void 0 : stickyHeaderIndices.join(",")]),
5773
6087
  stickyPositionComponentInternal,
5774
6088
  stylePaddingBottom: stylePaddingBottomState,
6089
+ stylePaddingLeft: stylePaddingLeftState,
6090
+ stylePaddingRight: stylePaddingRightState,
5775
6091
  stylePaddingTop: stylePaddingTopState,
5776
- suggestEstimatedItemSize: !!suggestEstimatedItemSize,
5777
6092
  useWindowScroll: useWindowScrollResolved
5778
6093
  };
5779
6094
  state.refScroller = refScroller;
@@ -5800,6 +6115,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5800
6115
  };
5801
6116
  if (isFirstLocal) {
5802
6117
  initializeStateVars(false);
6118
+ resetLayoutCachesForDataChange(state);
5803
6119
  updateItemPositions(
5804
6120
  ctx,
5805
6121
  /*dataChanged*/
@@ -5817,6 +6133,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5817
6133
  }, [usesBootstrapInitialScroll]);
5818
6134
  useLayoutEffect(() => {
5819
6135
  initializeInitialScrollOnMount(ctx, {
6136
+ alwaysDispatchInitialScroll: shouldInitializeHorizontalRTL,
5820
6137
  dataLength: dataProp.length,
5821
6138
  hasFooterComponent: !!ListFooterComponent,
5822
6139
  initialContentOffset,