@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/react-native.js CHANGED
@@ -29,6 +29,111 @@ var ReactNative__namespace = /*#__PURE__*/_interopNamespace(ReactNative);
29
29
  ReactNative.Animated.View;
30
30
  var View = ReactNative.View;
31
31
  var Text = ReactNative.Text;
32
+ var Platform = ReactNative.Platform;
33
+ var PlatformAdjustBreaksScroll = Platform.OS === "android";
34
+
35
+ // src/utils/rtl.ts
36
+ function clampHorizontalOffset(offset, maxOffset) {
37
+ if (maxOffset === void 0) {
38
+ return offset;
39
+ }
40
+ return Math.max(0, Math.min(maxOffset, offset));
41
+ }
42
+ function getHorizontalMaxOffset(state, contentWidth) {
43
+ if (contentWidth === void 0 || !Number.isFinite(contentWidth) || !Number.isFinite(state.scrollLength) || contentWidth <= state.scrollLength) {
44
+ return contentWidth !== void 0 && Number.isFinite(contentWidth) && Number.isFinite(state.scrollLength) ? 0 : void 0;
45
+ }
46
+ return Math.max(0, contentWidth - state.scrollLength);
47
+ }
48
+ function getDefaultHorizontalRTLScrollType() {
49
+ return Platform.OS === "web" ? "normal" : "inverted";
50
+ }
51
+ function getNativeHorizontalRTLScrollType(state) {
52
+ var _a3;
53
+ return (_a3 = state == null ? void 0 : state.horizontalRTLScrollType) != null ? _a3 : getDefaultHorizontalRTLScrollType();
54
+ }
55
+ function isRTLProps(props) {
56
+ var _a3;
57
+ return (_a3 = props == null ? void 0 : props.rtl) != null ? _a3 : !!ReactNative.I18nManager.isRTL;
58
+ }
59
+ function isHorizontalRTL(state) {
60
+ return isHorizontalRTLProps(state == null ? void 0 : state.props);
61
+ }
62
+ function isHorizontalRTLProps(props) {
63
+ return !!(props == null ? void 0 : props.horizontal) && isRTLProps(props);
64
+ }
65
+ function getLogicalHorizontalMaxOffset(state, contentWidth) {
66
+ var _a3;
67
+ return (_a3 = getHorizontalMaxOffset(state, contentWidth)) != null ? _a3 : 0;
68
+ }
69
+ function getHorizontalInsetEnd(state, inset) {
70
+ if (!inset) {
71
+ return 0;
72
+ }
73
+ return (isHorizontalRTL(state) ? inset.left : inset.right) || 0;
74
+ }
75
+ function toPhysicalHorizontalItemPosition(state, logicalPosition, itemSize, listSize) {
76
+ if (!isHorizontalRTL(state) || listSize === void 0 || !Number.isFinite(listSize)) {
77
+ return logicalPosition;
78
+ }
79
+ return Math.max(0, listSize - logicalPosition - itemSize);
80
+ }
81
+ function toNativeHorizontalOffset(state, logicalOffset, contentWidth) {
82
+ if (!state || !isHorizontalRTL(state)) {
83
+ return logicalOffset;
84
+ }
85
+ const maxOffset = getHorizontalMaxOffset(state, contentWidth);
86
+ const clampedLogicalOffset = clampHorizontalOffset(logicalOffset, maxOffset);
87
+ const mode = getNativeHorizontalRTLScrollType(state);
88
+ if (mode === "negative") {
89
+ return clampedLogicalOffset === 0 ? 0 : -clampedLogicalOffset;
90
+ }
91
+ if (mode === "inverted") {
92
+ if (maxOffset === void 0) {
93
+ return clampedLogicalOffset;
94
+ }
95
+ return clampHorizontalOffset(maxOffset - clampedLogicalOffset, maxOffset);
96
+ }
97
+ return clampedLogicalOffset;
98
+ }
99
+ function toLogicalHorizontalOffset(state, rawOffset, contentWidth) {
100
+ if (!isHorizontalRTL(state)) {
101
+ state.horizontalRTLScrollType = void 0;
102
+ return rawOffset;
103
+ }
104
+ const maxOffset = getHorizontalMaxOffset(state, contentWidth);
105
+ if (rawOffset < 0) {
106
+ state.horizontalRTLScrollType = "negative";
107
+ return clampHorizontalOffset(-rawOffset, maxOffset);
108
+ }
109
+ if (maxOffset === void 0) {
110
+ return rawOffset;
111
+ }
112
+ const normalOffset = rawOffset;
113
+ const invertedOffset = maxOffset - rawOffset;
114
+ if (!Number.isFinite(invertedOffset)) {
115
+ state.horizontalRTLScrollType = "normal";
116
+ return normalOffset;
117
+ }
118
+ const previousMode = state.horizontalRTLScrollType;
119
+ if (previousMode === "inverted") {
120
+ return clampHorizontalOffset(invertedOffset, maxOffset);
121
+ }
122
+ if (previousMode === "normal") {
123
+ return clampHorizontalOffset(normalOffset, maxOffset);
124
+ }
125
+ if (!state.hasScrolled) {
126
+ const defaultMode = getDefaultHorizontalRTLScrollType();
127
+ state.horizontalRTLScrollType = defaultMode;
128
+ return clampHorizontalOffset(defaultMode === "inverted" ? invertedOffset : normalOffset, maxOffset);
129
+ }
130
+ const referenceScroll = state.scroll;
131
+ const distanceNormal = Math.abs(normalOffset - referenceScroll);
132
+ const distanceInverted = Math.abs(invertedOffset - referenceScroll);
133
+ const useInverted = distanceInverted + 0.5 < distanceNormal;
134
+ state.horizontalRTLScrollType = useInverted ? "inverted" : "normal";
135
+ return clampHorizontalOffset(useInverted ? invertedOffset : normalOffset, maxOffset);
136
+ }
32
137
  var createAnimatedValue = (value) => new ReactNative.Animated.Value(value);
33
138
 
34
139
  // src/state/state.tsx
@@ -170,7 +275,7 @@ function getContentInsetEnd(ctx, contentInsetEndAdjustmentOverride) {
170
275
  const horizontal = props.horizontal;
171
276
  const contentInset = props.contentInset;
172
277
  const baseInset = contentInset != null ? contentInset : state.nativeContentInset;
173
- const baseEndInset = (horizontal ? baseInset == null ? void 0 : baseInset.right : baseInset == null ? void 0 : baseInset.bottom) || 0;
278
+ const baseEndInset = (horizontal ? getHorizontalInsetEnd(state, baseInset) : baseInset == null ? void 0 : baseInset.bottom) || 0;
174
279
  const contentInsetEndAdjustment = getContentInsetEndAdjustmentEnd(
175
280
  contentInsetEndAdjustmentOverride != null ? contentInsetEndAdjustmentOverride : props.contentInsetEndAdjustment
176
281
  );
@@ -179,9 +284,9 @@ function getContentInsetEnd(ctx, contentInsetEndAdjustmentOverride) {
179
284
  const overrideInset = (_b = state.contentInsetOverride) != null ? _b : void 0;
180
285
  const adjustedBaseEndInset = baseEndInset + contentInsetEndAdjustment;
181
286
  if (overrideInset) {
182
- const mergedInset = { bottom: 0, right: 0, ...baseInset, ...overrideInset };
287
+ const mergedInset = { bottom: 0, left: 0, right: 0, ...baseInset, ...overrideInset };
183
288
  return Math.max(
184
- ((horizontal ? mergedInset.right : mergedInset.bottom) || 0) + contentInsetEndAdjustment,
289
+ ((horizontal ? getHorizontalInsetEnd(state, mergedInset) : mergedInset.bottom) || 0) + contentInsetEndAdjustment,
185
290
  anchoredEndInset
186
291
  );
187
292
  }
@@ -461,7 +566,8 @@ function comparatorDefault(a, b) {
461
566
  }
462
567
  function getPadding(s, type) {
463
568
  var _a3, _b, _c;
464
- return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : s.paddingVertical) != null ? _b : s.padding) != null ? _c : 0;
569
+ const axisPadding = type === "Left" || type === "Right" ? s.paddingHorizontal : s.paddingVertical;
570
+ return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : axisPadding) != null ? _b : s.padding) != null ? _c : 0;
465
571
  }
466
572
  function extractPadding(style, contentContainerStyle, type) {
467
573
  return getPadding(style, type) + getPadding(contentContainerStyle, type);
@@ -668,8 +774,6 @@ function useOnLayoutSync({
668
774
  }
669
775
  return { onLayout };
670
776
  }
671
- var Platform = ReactNative.Platform;
672
- var PlatformAdjustBreaksScroll = Platform.OS === "android";
673
777
 
674
778
  // src/utils/isInMVCPActiveMode.native.ts
675
779
  function isInMVCPActiveMode(state) {
@@ -681,6 +785,7 @@ function getContainerPositionStyle({
681
785
  columnWrapperStyle,
682
786
  horizontal,
683
787
  hasItemSeparator,
788
+ isHorizontalRTLList,
684
789
  numColumns,
685
790
  otherAxisPos,
686
791
  otherAxisSize
@@ -704,6 +809,7 @@ function getContainerPositionStyle({
704
809
  }
705
810
  return horizontal ? {
706
811
  boxSizing: paddingStyles ? "border-box" : void 0,
812
+ direction: isHorizontalRTLList && Platform.OS === "web" ? "ltr" : void 0,
707
813
  flexDirection: hasItemSeparator ? "row" : void 0,
708
814
  height: otherAxisSize,
709
815
  left: 0,
@@ -722,6 +828,7 @@ function getContainerPositionStyle({
722
828
  }
723
829
  var Container = typedMemo(function Container2({
724
830
  id,
831
+ itemKey,
725
832
  recycleItems,
726
833
  horizontal,
727
834
  getRenderedItem: getRenderedItem2,
@@ -731,13 +838,13 @@ var Container = typedMemo(function Container2({
731
838
  }) {
732
839
  const ctx = useStateContext();
733
840
  const { columnWrapperStyle, animatedScrollY } = ctx;
841
+ const isHorizontalRTLList = isHorizontalRTL(ctx.state);
734
842
  const positionComponentInternal = ctx.state.props.positionComponentInternal;
735
843
  const stickyPositionComponentInternal = ctx.state.props.stickyPositionComponentInternal;
736
- const [column = 0, span = 1, data, itemKey, numColumns = 1, extraData, isSticky] = useArr$([
844
+ const [column = 0, span = 1, data, numColumns = 1, extraData, isSticky] = useArr$([
737
845
  `containerColumn${id}`,
738
846
  `containerSpan${id}`,
739
847
  `containerItemData${id}`,
740
- `containerItemKey${id}`,
741
848
  "numColumns",
742
849
  "extraData",
743
850
  `containerSticky${id}`
@@ -763,11 +870,20 @@ var Container = typedMemo(function Container2({
763
870
  columnWrapperStyle,
764
871
  hasItemSeparator: !!ItemSeparatorComponent,
765
872
  horizontal,
873
+ isHorizontalRTLList,
766
874
  numColumns,
767
875
  otherAxisPos,
768
876
  otherAxisSize
769
877
  }),
770
- [horizontal, otherAxisPos, otherAxisSize, columnWrapperStyle, numColumns, ItemSeparatorComponent]
878
+ [
879
+ horizontal,
880
+ isHorizontalRTLList,
881
+ otherAxisPos,
882
+ otherAxisSize,
883
+ columnWrapperStyle,
884
+ numColumns,
885
+ ItemSeparatorComponent
886
+ ]
771
887
  );
772
888
  const renderedItemInfo = React2.useMemo(
773
889
  () => itemKey !== void 0 ? getRenderedItem2(itemKey) : null,
@@ -882,14 +998,51 @@ var Container = typedMemo(function Container2({
882
998
  );
883
999
  });
884
1000
 
1001
+ // src/components/ContainerSlot.tsx
1002
+ function ContainerSlotBase({
1003
+ id,
1004
+ horizontal,
1005
+ recycleItems,
1006
+ ItemSeparatorComponent,
1007
+ updateItemSize: updateItemSize2,
1008
+ getRenderedItem: getRenderedItem2,
1009
+ stickyHeaderConfig,
1010
+ ContainerComponent = Container
1011
+ }) {
1012
+ const [itemKey] = useArr$([`containerItemKey${id}`]);
1013
+ if (itemKey === void 0) {
1014
+ return null;
1015
+ }
1016
+ return /* @__PURE__ */ React2__namespace.createElement(
1017
+ ContainerComponent,
1018
+ {
1019
+ getRenderedItem: getRenderedItem2,
1020
+ horizontal,
1021
+ ItemSeparatorComponent,
1022
+ id,
1023
+ itemKey,
1024
+ recycleItems,
1025
+ stickyHeaderConfig,
1026
+ updateItemSize: updateItemSize2
1027
+ }
1028
+ );
1029
+ }
1030
+ var ContainerSlot = typedMemo(function ContainerSlot2(props) {
1031
+ return /* @__PURE__ */ React2__namespace.createElement(ContainerSlotBase, { ...props });
1032
+ });
1033
+
885
1034
  // src/components/Containers.native.tsx
886
1035
  var ContainersLayer = typedMemo(function ContainersLayer2({ children, horizontal }) {
887
1036
  const ctx = useStateContext();
888
1037
  const columnWrapperStyle = ctx.columnWrapperStyle;
889
1038
  const animSize = useValue$("totalSize");
890
- const otherAxisSize = useValue$("otherAxisSize");
891
- const [readyToRender, numColumns] = useArr$(["readyToRender", "numColumns"]);
892
- const style = horizontal ? { minHeight: otherAxisSize, opacity: readyToRender ? 1 : 0, width: animSize } : { height: animSize, minWidth: otherAxisSize, opacity: readyToRender ? 1 : 0 };
1039
+ const [readyToRender, numColumns, otherAxisSize = 0] = useArr$(["readyToRender", "numColumns", "otherAxisSize"]);
1040
+ const style = horizontal ? {
1041
+ height: otherAxisSize || "100%",
1042
+ minHeight: otherAxisSize,
1043
+ opacity: readyToRender ? 1 : 0,
1044
+ width: animSize
1045
+ } : { height: animSize, minWidth: otherAxisSize, opacity: readyToRender ? 1 : 0 };
893
1046
  if (columnWrapperStyle) {
894
1047
  const { columnGap, rowGap, gap } = columnWrapperStyle;
895
1048
  const gapX = columnGap || gap || 0;
@@ -920,12 +1073,12 @@ var Containers = typedMemo(function Containers2({
920
1073
  updateItemSize: updateItemSize2,
921
1074
  getRenderedItem: getRenderedItem2
922
1075
  }) {
923
- const [numContainers] = useArr$(["numContainersPooled"]);
1076
+ const [numContainersPooled] = useArr$(["numContainersPooled"]);
924
1077
  const containers = [];
925
- for (let i = 0; i < numContainers; i++) {
1078
+ for (let i = 0; i < numContainersPooled; i++) {
926
1079
  containers.push(
927
1080
  /* @__PURE__ */ React2__namespace.createElement(
928
- Container,
1081
+ ContainerSlot,
929
1082
  {
930
1083
  getRenderedItem: getRenderedItem2,
931
1084
  horizontal,
@@ -942,6 +1095,18 @@ var Containers = typedMemo(function Containers2({
942
1095
  return /* @__PURE__ */ React2__namespace.createElement(ContainersLayer, { horizontal }, containers);
943
1096
  });
944
1097
  var ListComponentScrollView = ReactNative.Animated.ScrollView;
1098
+
1099
+ // src/components/listComponentStyles.ts
1100
+ function getAutoOtherAxisStyle({
1101
+ horizontal,
1102
+ needsOtherAxisSize,
1103
+ otherAxisSize
1104
+ }) {
1105
+ if (!needsOtherAxisSize || !otherAxisSize || otherAxisSize <= 0) {
1106
+ return void 0;
1107
+ }
1108
+ return horizontal ? { height: otherAxisSize } : { width: otherAxisSize };
1109
+ }
945
1110
  function ScrollAdjust() {
946
1111
  var _a3;
947
1112
  const ctx = useStateContext();
@@ -962,10 +1127,10 @@ function ScrollAdjust() {
962
1127
  }
963
1128
  );
964
1129
  }
965
- function SnapWrapper({ ScrollComponent, ...props }) {
1130
+ var SnapWrapper = React2__namespace.forwardRef(function SnapWrapperInner({ ScrollComponent, ...props }, ref) {
966
1131
  const [snapToOffsets] = useArr$(["snapToOffsets"]);
967
- return /* @__PURE__ */ React2__namespace.createElement(ScrollComponent, { ...props, snapToOffsets });
968
- }
1132
+ return /* @__PURE__ */ React2__namespace.createElement(ScrollComponent, { ...props, ref, snapToOffsets });
1133
+ });
969
1134
  function WebAnchoredEndSpace({ horizontal }) {
970
1135
  const ctx = useStateContext();
971
1136
  const [anchoredEndSpaceSize] = useArr$(["anchoredEndSpaceSize"]);
@@ -1014,6 +1179,12 @@ var ListComponent = typedMemo(function ListComponent2({
1014
1179
  }) {
1015
1180
  const ctx = useStateContext();
1016
1181
  const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
1182
+ const [otherAxisSize = 0] = useArr$(["otherAxisSize"]);
1183
+ const autoOtherAxisStyle = getAutoOtherAxisStyle({
1184
+ horizontal,
1185
+ needsOtherAxisSize: ctx.state.needsOtherAxisSize,
1186
+ otherAxisSize
1187
+ });
1017
1188
  const ScrollComponent = React2.useMemo(() => {
1018
1189
  if (!renderScrollComponent) {
1019
1190
  return ListComponentScrollView;
@@ -1052,10 +1223,10 @@ var ListComponent = typedMemo(function ListComponent2({
1052
1223
  ...rest,
1053
1224
  ...ScrollComponent === ListComponentScrollView ? { useWindowScroll } : {},
1054
1225
  contentContainerStyle: [
1055
- contentContainerStyle,
1056
1226
  horizontal ? {
1057
1227
  height: "100%"
1058
- } : {}
1228
+ } : {},
1229
+ contentContainerStyle
1059
1230
  ],
1060
1231
  contentOffset: initialContentOffset !== void 0 ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
1061
1232
  horizontal,
@@ -1064,7 +1235,7 @@ var ListComponent = typedMemo(function ListComponent2({
1064
1235
  onScroll: onScroll2,
1065
1236
  ref: refScrollView,
1066
1237
  ScrollComponent: snapToIndices ? ScrollComponent : void 0,
1067
- style
1238
+ style: autoOtherAxisStyle ? [autoOtherAxisStyle, style] : style
1068
1239
  },
1069
1240
  /* @__PURE__ */ React2__namespace.createElement(ScrollAdjust, null),
1070
1241
  ListHeaderComponent && /* @__PURE__ */ React2__namespace.createElement(LayoutView, { onLayoutChange: onLayoutHeader, style: ListHeaderComponentStyle }, getComponent(ListHeaderComponent)),
@@ -1252,10 +1423,9 @@ var initialScrollWatchdog = {
1252
1423
  clear(state) {
1253
1424
  initialScrollWatchdog.set(state, void 0);
1254
1425
  },
1255
- didObserveProgress(newScroll, watchdog) {
1256
- const previousDistance = Math.abs(watchdog.startScroll - watchdog.targetOffset);
1426
+ didReachTarget(newScroll, watchdog) {
1257
1427
  const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
1258
- return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET || nextDistance + INITIAL_SCROLL_MIN_TARGET_OFFSET < previousDistance;
1428
+ return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
1259
1429
  },
1260
1430
  get(state) {
1261
1431
  var _a3, _b;
@@ -1280,19 +1450,19 @@ var initialScrollWatchdog = {
1280
1450
  }
1281
1451
  };
1282
1452
  function setInitialScrollSession(state, options = {}) {
1283
- var _a3, _b, _c;
1453
+ var _a3, _b, _c, _d;
1284
1454
  const existingSession = state.initialScrollSession;
1285
1455
  const kind = (_a3 = options.kind) != null ? _a3 : existingSession == null ? void 0 : existingSession.kind;
1286
1456
  const completion = existingSession == null ? void 0 : existingSession.completion;
1287
- const hasBootstrapOverride = Object.hasOwn(options, "bootstrap");
1288
- const bootstrap = kind === "bootstrap" ? hasBootstrapOverride ? options.bootstrap : (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0 : void 0;
1457
+ const existingBootstrap = (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0;
1458
+ const bootstrap = kind === "bootstrap" ? options.bootstrap === null ? void 0 : (_b = options.bootstrap) != null ? _b : existingBootstrap : void 0;
1289
1459
  if (!kind) {
1290
1460
  return clearInitialScrollSession(state);
1291
1461
  }
1292
1462
  if (!state.initialScroll && !bootstrap && !hasInitialScrollSessionCompletion(completion)) {
1293
1463
  return clearInitialScrollSession(state);
1294
1464
  }
1295
- const previousDataLength = (_c = (_b = options.previousDataLength) != null ? _b : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _c : 0;
1465
+ const previousDataLength = (_d = (_c = options.previousDataLength) != null ? _c : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _d : 0;
1296
1466
  state.initialScrollSession = createInitialScrollSession({
1297
1467
  bootstrap,
1298
1468
  completion,
@@ -1874,7 +2044,7 @@ function checkFinishedScrollFallback(ctx) {
1874
2044
  state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, delay);
1875
2045
  };
1876
2046
  const checkHasScrolled = () => {
1877
- var _a3, _b, _c;
2047
+ var _a3, _b, _c, _d;
1878
2048
  state.timeoutCheckFinishedScrollFallback = void 0;
1879
2049
  const isStillScrollingTo = state.scrollingTo;
1880
2050
  if (isStillScrollingTo) {
@@ -1887,8 +2057,10 @@ function checkFinishedScrollFallback(ctx) {
1887
2057
  isStillScrollingTo
1888
2058
  );
1889
2059
  const completionState = getResolvedScrollCompletionState(ctx, isStillScrollingTo);
1890
- const canFinishAfterSilentNativeDispatch = silentInitialDispatch && completionState.isAtResolvedTarget && numChecks >= 1;
2060
+ const canFinishAfterSilentNativeDispatch = Platform.OS === "android" && silentInitialDispatch && completionState.isAtResolvedTarget && numChecks >= 1;
1891
2061
  const shouldRetrySilentInitialNativeScroll = Platform.OS === "android" && canFinishAfterSilentNativeDispatch && !initialScrollCompletion.didRetrySilentInitialScroll(state);
2062
+ const shouldFinishAfterObservedScroll = state.hasScrolled && (!isStillScrollingTo.isInitialScroll || completionState.isAtResolvedTarget);
2063
+ const shouldRetryUnalignedInitialScroll = isStillScrollingTo.isInitialScroll && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
1892
2064
  if (shouldRetrySilentInitialNativeScroll) {
1893
2065
  const targetOffset = (_b = (_a3 = getInitialScrollWatchdogTargetOffset(state)) != null ? _a3 : isStillScrollingTo.targetOffset) != null ? _b : 0;
1894
2066
  const jiggleOffset = targetOffset >= SILENT_INITIAL_SCROLL_TARGET_EPSILON ? targetOffset - SILENT_INITIAL_SCROLL_TARGET_EPSILON : targetOffset + SILENT_INITIAL_SCROLL_TARGET_EPSILON;
@@ -1898,10 +2070,10 @@ function checkFinishedScrollFallback(ctx) {
1898
2070
  scrollToFallbackOffset(ctx, targetOffset);
1899
2071
  });
1900
2072
  scheduleFallbackCheck(SILENT_INITIAL_SCROLL_RETRY_DELAY_MS);
1901
- } else if (shouldFinishZeroTarget || state.hasScrolled || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
2073
+ } else if (shouldFinishZeroTarget || shouldFinishAfterObservedScroll || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
1902
2074
  finishScrollTo(ctx);
1903
- } else if (isNativeInitialPending && numChecks <= maxChecks) {
1904
- const targetOffset = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : state.scrollPending;
2075
+ } else if ((isNativeInitialPending || shouldRetryUnalignedInitialScroll) && numChecks <= maxChecks) {
2076
+ const targetOffset = (_d = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : isStillScrollingTo.targetOffset) != null ? _d : state.scrollPending;
1905
2077
  scrollToFallbackOffset(ctx, targetOffset);
1906
2078
  scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
1907
2079
  } else {
@@ -1923,10 +2095,13 @@ function doScrollTo(ctx, params) {
1923
2095
  if (!scroller) {
1924
2096
  return;
1925
2097
  }
2098
+ const isHorizontal = !!horizontal;
2099
+ const contentSize = isHorizontal ? getContentSize(ctx) : void 0;
2100
+ const nativeOffset = toNativeHorizontalOffset(state, offset, contentSize);
1926
2101
  scroller.scrollTo({
1927
2102
  animated: isAnimated,
1928
- x: horizontal ? offset : 0,
1929
- y: horizontal ? 0 : offset
2103
+ x: isHorizontal ? nativeOffset : 0,
2104
+ y: isHorizontal ? 0 : offset
1930
2105
  });
1931
2106
  if (isInitialScroll) {
1932
2107
  initialScrollCompletion.markInitialScrollNativeDispatch(state);
@@ -2130,7 +2305,7 @@ function advanceMeasuredInitialScroll(ctx, options) {
2130
2305
  const activeInitialTargetOffset = scrollingTo ? (_a3 = scrollingTo.targetOffset) != null ? _a3 : scrollingTo.offset : void 0;
2131
2306
  const didOffsetChange = initialScroll.contentOffset === void 0 || Math.abs(initialScroll.contentOffset - resolvedOffset) > 1;
2132
2307
  const didActiveInitialTargetChange = activeInitialTargetOffset !== void 0 && Math.abs(activeInitialTargetOffset - resolvedOffset) > 1;
2133
- const isAlreadyAtDesiredInitialTarget = activeInitialTargetOffset !== void 0 && Math.abs(state.scroll - activeInitialTargetOffset) <= 1 && Math.abs(state.scrollPending - activeInitialTargetOffset) <= 1;
2308
+ const isAlreadyAtDesiredInitialTarget = activeInitialTargetOffset !== void 0 && Math.abs(state.scroll - resolvedOffset) <= 1 && Math.abs(state.scrollPending - resolvedOffset) <= 1;
2134
2309
  if (!(options == null ? void 0 : options.forceScroll) && !didOffsetChange && isInitialScrollInProgress && !didActiveInitialTargetChange) {
2135
2310
  return false;
2136
2311
  }
@@ -2202,6 +2377,61 @@ function checkAllSizesKnown(state, indices) {
2202
2377
  });
2203
2378
  }
2204
2379
 
2380
+ // src/utils/requestAdjust.ts
2381
+ function requestAdjust(ctx, positionDiff, dataChanged) {
2382
+ const state = ctx.state;
2383
+ if (Math.abs(positionDiff) > 0.1) {
2384
+ const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
2385
+ const doit = () => {
2386
+ if (needsScrollWorkaround) {
2387
+ scrollTo(ctx, {
2388
+ noScrollingTo: true,
2389
+ offset: state.scroll
2390
+ });
2391
+ } else {
2392
+ state.scrollAdjustHandler.requestAdjust(positionDiff);
2393
+ if (state.adjustingFromInitialMount) {
2394
+ state.adjustingFromInitialMount--;
2395
+ }
2396
+ }
2397
+ };
2398
+ state.scroll += positionDiff;
2399
+ state.scrollForNextCalculateItemsInView = void 0;
2400
+ const readyToRender = peek$(ctx, "readyToRender");
2401
+ if (readyToRender) {
2402
+ doit();
2403
+ if (Platform.OS !== "web") {
2404
+ const threshold = state.scroll - positionDiff / 2;
2405
+ if (!state.ignoreScrollFromMVCP) {
2406
+ state.ignoreScrollFromMVCP = {};
2407
+ }
2408
+ if (positionDiff > 0) {
2409
+ state.ignoreScrollFromMVCP.lt = threshold;
2410
+ } else {
2411
+ state.ignoreScrollFromMVCP.gt = threshold;
2412
+ }
2413
+ if (state.ignoreScrollFromMVCPTimeout) {
2414
+ clearTimeout(state.ignoreScrollFromMVCPTimeout);
2415
+ }
2416
+ const delay = needsScrollWorkaround ? 250 : 100;
2417
+ state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
2418
+ var _a3;
2419
+ state.ignoreScrollFromMVCP = void 0;
2420
+ const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
2421
+ if (shouldForceUpdate) {
2422
+ state.ignoreScrollFromMVCPIgnored = false;
2423
+ state.scrollPending = state.scroll;
2424
+ (_a3 = state.reprocessCurrentScroll) == null ? void 0 : _a3.call(state);
2425
+ }
2426
+ }, delay);
2427
+ }
2428
+ } else {
2429
+ state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
2430
+ requestAnimationFrame(doit);
2431
+ }
2432
+ }
2433
+ }
2434
+
2205
2435
  // src/core/bootstrapInitialScroll.ts
2206
2436
  var DEFAULT_BOOTSTRAP_REVEAL_EPSILON = 1;
2207
2437
  var DEFAULT_BOOTSTRAP_REVEAL_MAX_FRAMES = 8;
@@ -2295,7 +2525,7 @@ function clearBootstrapInitialScrollSession(state) {
2295
2525
  bootstrapInitialScroll.frameHandle = void 0;
2296
2526
  }
2297
2527
  setInitialScrollSession(state, {
2298
- bootstrap: void 0,
2528
+ bootstrap: null,
2299
2529
  kind: (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind
2300
2530
  });
2301
2531
  }
@@ -2451,15 +2681,18 @@ function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {
2451
2681
  return;
2452
2682
  }
2453
2683
  if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2454
- if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {
2455
- clearPendingInitialScrollFooterLayout(ctx, {
2456
- dataLength: state.props.data.length,
2457
- stylePaddingBottom: (_b = state.props.stylePaddingBottom) != null ? _b : 0,
2458
- target: initialScroll
2459
- });
2460
- return;
2684
+ const shouldKeepEndTargetAlive = isRetargetableBottomAlignedInitialScrollTarget(initialScroll) && peek$(ctx, "isAtEnd");
2685
+ if (!shouldKeepEndTargetAlive) {
2686
+ if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {
2687
+ clearPendingInitialScrollFooterLayout(ctx, {
2688
+ dataLength: state.props.data.length,
2689
+ stylePaddingBottom: (_b = state.props.stylePaddingBottom) != null ? _b : 0,
2690
+ target: initialScroll
2691
+ });
2692
+ } else {
2693
+ clearFinishedViewportRetargetableInitialScroll(state);
2694
+ }
2461
2695
  }
2462
- clearFinishedViewportRetargetableInitialScroll(state);
2463
2696
  }
2464
2697
  }
2465
2698
  function startBootstrapInitialScrollOnMount(ctx, options) {
@@ -2498,7 +2731,7 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
2498
2731
  }
2499
2732
  const shouldResetDidFinish = !!(state.didFinishInitialScroll && previousDataLength === 0 && dataLength > 0 && initialScroll.index !== void 0);
2500
2733
  const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2501
- const shouldClearFinishedResizePreservation = didDataChange && dataLength > 0 && state.didFinishInitialScroll && !bootstrapInitialScroll && !shouldResetDidFinish;
2734
+ const shouldClearFinishedResizePreservation = !initialScrollAtEnd && didDataChange && dataLength > 0 && state.didFinishInitialScroll && !bootstrapInitialScroll && !shouldResetDidFinish;
2502
2735
  if (shouldClearFinishedResizePreservation) {
2503
2736
  clearPreservedInitialScrollTarget(state);
2504
2737
  return;
@@ -2601,27 +2834,46 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2601
2834
  }
2602
2835
  }
2603
2836
  function handleBootstrapInitialScrollLayoutChange(ctx) {
2837
+ var _a3, _b, _c, _d;
2604
2838
  const state = ctx.state;
2605
2839
  const initialScroll = state.initialScroll;
2606
- if (isOffsetInitialScrollSession(state) || state.props.data.length === 0 || !initialScroll) {
2607
- return;
2608
- }
2609
2840
  const bootstrapInitialScroll = getBootstrapInitialScrollSession(state);
2610
- if (!bootstrapInitialScroll && initialScroll.viewPosition !== 1) {
2611
- return;
2612
- }
2613
- const didFinishInitialScroll = state.didFinishInitialScroll;
2614
- if (didFinishInitialScroll) {
2615
- setInitialScrollTarget(state, initialScroll, {
2616
- resetDidFinish: true
2617
- });
2618
- state.clearPreservedInitialScrollOnNextFinish = true;
2841
+ if (initialScroll && state.props.data.length > 0 && !isOffsetInitialScrollSession(state) && (bootstrapInitialScroll || initialScroll.viewPosition === 1)) {
2842
+ const resolvedOffset = resolveInitialScrollOffset(ctx, initialScroll);
2843
+ const scrollingTo = ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) ? state.scrollingTo : void 0;
2844
+ if (!bootstrapInitialScroll && (scrollingTo || state.didFinishInitialScroll)) {
2845
+ const currentOffset = scrollingTo ? (_b = scrollingTo.targetOffset) != null ? _b : scrollingTo.offset : getObservedBootstrapInitialScrollOffset(state);
2846
+ const offsetDiff = resolvedOffset - currentOffset;
2847
+ if (Math.abs(offsetDiff) > DEFAULT_BOOTSTRAP_REVEAL_EPSILON) {
2848
+ if (scrollingTo) {
2849
+ const existingWatchdog = initialScrollWatchdog.get(state);
2850
+ scrollingTo.offset = resolvedOffset;
2851
+ scrollingTo.targetOffset = resolvedOffset;
2852
+ state.initialScroll = {
2853
+ ...initialScroll,
2854
+ contentOffset: resolvedOffset
2855
+ };
2856
+ state.hasScrolled = false;
2857
+ initialScrollWatchdog.set(state, {
2858
+ startScroll: (_c = existingWatchdog == null ? void 0 : existingWatchdog.startScroll) != null ? _c : state.scroll,
2859
+ targetOffset: resolvedOffset
2860
+ });
2861
+ }
2862
+ requestAdjust(ctx, offsetDiff);
2863
+ if (state.didFinishInitialScroll) {
2864
+ (_d = state.triggerCalculateItemsInView) == null ? void 0 : _d.call(state, { forceFullItemPositions: true });
2865
+ }
2866
+ }
2867
+ if (state.didFinishInitialScroll) {
2868
+ clearFinishedViewportRetargetableInitialScroll(state);
2869
+ }
2870
+ } else {
2871
+ rearmBootstrapInitialScroll(ctx, {
2872
+ scroll: resolvedOffset,
2873
+ targetIndexSeed: initialScroll.index
2874
+ });
2875
+ }
2619
2876
  }
2620
- rearmBootstrapInitialScroll(ctx, {
2621
- scroll: resolveInitialScrollOffset(ctx, initialScroll),
2622
- seedContentOffset: didFinishInitialScroll && !bootstrapInitialScroll ? getObservedBootstrapInitialScrollOffset(state) : void 0,
2623
- targetIndexSeed: initialScroll.index
2624
- });
2625
2877
  }
2626
2878
  function evaluateBootstrapInitialScroll(ctx) {
2627
2879
  var _a3, _b;
@@ -2749,7 +3001,14 @@ function handleInitialScrollLayoutReady(ctx) {
2749
3001
  }
2750
3002
  function initializeInitialScrollOnMount(ctx, options) {
2751
3003
  var _a3, _b;
2752
- const { dataLength, hasFooterComponent, initialContentOffset, initialScrollAtEnd, useBootstrapInitialScroll } = options;
3004
+ const {
3005
+ alwaysDispatchInitialScroll,
3006
+ dataLength,
3007
+ hasFooterComponent,
3008
+ initialContentOffset,
3009
+ initialScrollAtEnd,
3010
+ useBootstrapInitialScroll
3011
+ } = options;
2753
3012
  const state = ctx.state;
2754
3013
  const initialScroll = state.initialScroll;
2755
3014
  const resolvedInitialContentOffset = initialContentOffset != null ? initialContentOffset : 0;
@@ -2769,7 +3028,7 @@ function initializeInitialScrollOnMount(ctx, options) {
2769
3028
  return;
2770
3029
  }
2771
3030
  const hasPendingDataDependentInitialScroll = !!initialScroll && dataLength === 0 && !(resolvedInitialContentOffset === 0 && !initialScrollAtEnd);
2772
- if (!resolvedInitialContentOffset && !hasPendingDataDependentInitialScroll) {
3031
+ if (!alwaysDispatchInitialScroll && !resolvedInitialContentOffset && !hasPendingDataDependentInitialScroll) {
2773
3032
  if (initialScroll && !initialScrollAtEnd) {
2774
3033
  finishInitialScroll(ctx, {
2775
3034
  resolvedOffset: resolvedInitialContentOffset
@@ -2808,61 +3067,6 @@ function handleInitialScrollDataChange(ctx, options) {
2808
3067
  advanceCurrentInitialScrollSession(ctx);
2809
3068
  }
2810
3069
 
2811
- // src/utils/requestAdjust.ts
2812
- function requestAdjust(ctx, positionDiff, dataChanged) {
2813
- const state = ctx.state;
2814
- if (Math.abs(positionDiff) > 0.1) {
2815
- const needsScrollWorkaround = Platform.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
2816
- const doit = () => {
2817
- if (needsScrollWorkaround) {
2818
- scrollTo(ctx, {
2819
- noScrollingTo: true,
2820
- offset: state.scroll
2821
- });
2822
- } else {
2823
- state.scrollAdjustHandler.requestAdjust(positionDiff);
2824
- if (state.adjustingFromInitialMount) {
2825
- state.adjustingFromInitialMount--;
2826
- }
2827
- }
2828
- };
2829
- state.scroll += positionDiff;
2830
- state.scrollForNextCalculateItemsInView = void 0;
2831
- const readyToRender = peek$(ctx, "readyToRender");
2832
- if (readyToRender) {
2833
- doit();
2834
- if (Platform.OS !== "web") {
2835
- const threshold = state.scroll - positionDiff / 2;
2836
- if (!state.ignoreScrollFromMVCP) {
2837
- state.ignoreScrollFromMVCP = {};
2838
- }
2839
- if (positionDiff > 0) {
2840
- state.ignoreScrollFromMVCP.lt = threshold;
2841
- } else {
2842
- state.ignoreScrollFromMVCP.gt = threshold;
2843
- }
2844
- if (state.ignoreScrollFromMVCPTimeout) {
2845
- clearTimeout(state.ignoreScrollFromMVCPTimeout);
2846
- }
2847
- const delay = needsScrollWorkaround ? 250 : 100;
2848
- state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
2849
- var _a3;
2850
- state.ignoreScrollFromMVCP = void 0;
2851
- const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
2852
- if (shouldForceUpdate) {
2853
- state.ignoreScrollFromMVCPIgnored = false;
2854
- state.scrollPending = state.scroll;
2855
- (_a3 = state.reprocessCurrentScroll) == null ? void 0 : _a3.call(state);
2856
- }
2857
- }, delay);
2858
- }
2859
- } else {
2860
- state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
2861
- requestAnimationFrame(doit);
2862
- }
2863
- }
2864
- }
2865
-
2866
3070
  // src/core/mvcp.ts
2867
3071
  var MVCP_POSITION_EPSILON = 0.1;
2868
3072
  var MVCP_ANCHOR_LOCK_TTL_MS = 300;
@@ -3146,15 +3350,27 @@ function prepareMVCP(ctx, dataChanged) {
3146
3350
  return;
3147
3351
  }
3148
3352
  if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
3149
- requestAdjust(ctx, positionDiff, dataChanged && mvcpData);
3353
+ const shouldSkipAdjustForMaintainedEnd = state.maintainingScrollAtEnd && peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
3354
+ if (!shouldSkipAdjustForMaintainedEnd) {
3355
+ requestAdjust(ctx, positionDiff, dataChanged && mvcpData);
3356
+ }
3150
3357
  }
3151
3358
  };
3152
3359
  }
3153
3360
  }
3154
3361
 
3362
+ // src/core/resetLayoutCachesForDataChange.ts
3363
+ function resetLayoutCachesForDataChange(state) {
3364
+ state.indexByKey.clear();
3365
+ state.idCache.length = 0;
3366
+ state.positions.length = 0;
3367
+ state.columns.length = 0;
3368
+ state.columnSpans.length = 0;
3369
+ }
3370
+
3155
3371
  // src/core/syncMountedContainer.ts
3156
3372
  function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3157
- var _a3, _b, _c, _d, _e, _f, _g, _h;
3373
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i;
3158
3374
  const state = ctx.state;
3159
3375
  const {
3160
3376
  columns,
@@ -3166,7 +3382,8 @@ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3166
3382
  if (item === void 0) {
3167
3383
  return { didChangePosition: false, didRefreshData: false };
3168
3384
  }
3169
- const updateLayout = (_a3 = options == null ? void 0 : options.updateLayout) != null ? _a3 : true;
3385
+ const itemKey = (_a3 = state.idCache[itemIndex]) != null ? _a3 : getId(state, itemIndex);
3386
+ const updateLayout = (_b = options == null ? void 0 : options.updateLayout) != null ? _b : true;
3170
3387
  let didChangePosition = false;
3171
3388
  let didRefreshData = false;
3172
3389
  if (updateLayout) {
@@ -3175,7 +3392,9 @@ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3175
3392
  set$(ctx, `containerPosition${containerIndex}`, POSITION_OUT_OF_VIEW);
3176
3393
  return { didChangePosition: false, didRefreshData: false };
3177
3394
  }
3178
- const position = (positionValue || 0) - ((_b = options == null ? void 0 : options.scrollAdjustPending) != null ? _b : 0);
3395
+ const logicalPosition = (positionValue || 0) - ((_c = options == null ? void 0 : options.scrollAdjustPending) != null ? _c : 0);
3396
+ const itemSize = (_d = state.sizes.get(itemKey)) != null ? _d : getItemSize(ctx, itemKey, itemIndex, item);
3397
+ const position = toPhysicalHorizontalItemPosition(state, logicalPosition, itemSize, peek$(ctx, "totalSize"));
3179
3398
  const column = columns[itemIndex] || 1;
3180
3399
  const span = columnSpans[itemIndex] || 1;
3181
3400
  const prevPos = peek$(ctx, `containerPosition${containerIndex}`);
@@ -3194,15 +3413,15 @@ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3194
3413
  }
3195
3414
  const prevData = peek$(ctx, `containerItemData${containerIndex}`);
3196
3415
  if (prevData !== item) {
3197
- 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;
3198
- const cachedComparison = (_e = pendingDataComparison == null ? void 0 : pendingDataComparison.byIndex[itemIndex]) != null ? _e : 0;
3416
+ 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;
3417
+ const cachedComparison = (_g = pendingDataComparison == null ? void 0 : pendingDataComparison.byIndex[itemIndex]) != null ? _g : 0;
3199
3418
  if (cachedComparison === 2) {
3200
3419
  set$(ctx, `containerItemData${containerIndex}`, item);
3201
3420
  didRefreshData = true;
3202
3421
  } else if (cachedComparison !== 1) {
3203
- const itemKey = (_g = (_f = peek$(ctx, `containerItemKey${containerIndex}`)) != null ? _f : state.idCache[itemIndex]) != null ? _g : getId(state, itemIndex);
3422
+ const nextItemKey = (_h = peek$(ctx, `containerItemKey${containerIndex}`)) != null ? _h : itemKey;
3204
3423
  const prevKey = keyExtractor == null ? void 0 : keyExtractor(prevData, itemIndex);
3205
- if (prevData === void 0 || !keyExtractor || prevKey !== itemKey) {
3424
+ if (prevData === void 0 || !keyExtractor || prevKey !== nextItemKey) {
3206
3425
  set$(ctx, `containerItemData${containerIndex}`, item);
3207
3426
  didRefreshData = true;
3208
3427
  } else if (!itemsAreEqual) {
@@ -3219,7 +3438,7 @@ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3219
3438
  };
3220
3439
  }
3221
3440
  }
3222
- if ((_h = state.pendingDataComparison) == null ? void 0 : _h.byIndex) {
3441
+ if ((_i = state.pendingDataComparison) == null ? void 0 : _i.byIndex) {
3223
3442
  state.pendingDataComparison.byIndex[itemIndex] = isEqual ? 1 : 2;
3224
3443
  }
3225
3444
  if (!isEqual) {
@@ -3384,11 +3603,13 @@ function updateSnapToOffsets(ctx) {
3384
3603
  const {
3385
3604
  props: { snapToIndices }
3386
3605
  } = state;
3606
+ const contentSize = state.props.horizontal ? getContentSize(ctx) : void 0;
3387
3607
  const snapToOffsets = Array(snapToIndices.length);
3388
3608
  for (let i = 0; i < snapToIndices.length; i++) {
3389
3609
  const idx = snapToIndices[i];
3390
3610
  getId(state, idx);
3391
- snapToOffsets[i] = state.positions[idx];
3611
+ const logicalOffset = state.positions[idx];
3612
+ snapToOffsets[i] = toNativeHorizontalOffset(state, logicalOffset, contentSize);
3392
3613
  }
3393
3614
  set$(ctx, "snapToOffsets", snapToOffsets);
3394
3615
  }
@@ -3765,6 +3986,30 @@ function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
3765
3986
  var unstableBatchedUpdates = ReactNative__namespace.unstable_batchedUpdates;
3766
3987
  var batchedUpdates = typeof unstableBatchedUpdates === "function" ? unstableBatchedUpdates : (fn) => fn();
3767
3988
 
3989
+ // src/utils/containerPool.ts
3990
+ var MIN_INITIAL_CONTAINER_POOL_SIZE = 32;
3991
+ var MAX_INITIAL_SPARE_CONTAINERS = 64;
3992
+ function getInitialContainerPoolSize(dataLength, numContainers, initialContainerPoolRatio) {
3993
+ if (dataLength <= 0 || numContainers <= 0) {
3994
+ return 0;
3995
+ }
3996
+ const ratioPoolSize = Math.ceil(numContainers * initialContainerPoolRatio);
3997
+ const cappedSparePoolSize = numContainers + MAX_INITIAL_SPARE_CONTAINERS;
3998
+ const targetPoolSize = Math.max(
3999
+ numContainers,
4000
+ Math.min(ratioPoolSize, cappedSparePoolSize),
4001
+ Math.min(dataLength, MIN_INITIAL_CONTAINER_POOL_SIZE)
4002
+ );
4003
+ const maxUsefulPoolSize = Math.max(dataLength, numContainers);
4004
+ return Math.min(maxUsefulPoolSize, targetPoolSize);
4005
+ }
4006
+ function getExpandedContainerPoolSize(dataLength, numContainers) {
4007
+ if (dataLength <= 0 || numContainers <= 0) {
4008
+ return 0;
4009
+ }
4010
+ return Math.min(Math.max(dataLength, numContainers), Math.max(numContainers, Math.ceil(numContainers * 1.5)));
4011
+ }
4012
+
3768
4013
  // src/utils/findAvailableContainers.ts
3769
4014
  function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers, protectedKeys) {
3770
4015
  const numContainers = peek$(ctx, "numContainers");
@@ -3970,10 +4215,9 @@ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentSt
3970
4215
  function calculateItemsInView(ctx, params = {}) {
3971
4216
  const state = ctx.state;
3972
4217
  batchedUpdates(() => {
3973
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
4218
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q;
3974
4219
  const {
3975
4220
  columns,
3976
- columnSpans,
3977
4221
  containerItemKeys,
3978
4222
  enableScrollForNextCalculateItemsInView,
3979
4223
  idCache,
@@ -4017,18 +4261,38 @@ function calculateItemsInView(ctx, params = {}) {
4017
4261
  // current initial-scroll target instead of transient native adjustments.
4018
4262
  resolveInitialScrollOffset(ctx, initialScroll)
4019
4263
  ) : state.scroll;
4020
- const scrollAdjustPending = (_c = peek$(ctx, "scrollAdjustPending")) != null ? _c : 0;
4021
- const scrollAdjustPad = scrollAdjustPending - topPad;
4022
- let scroll = Math.round(scrollState + scrollExtra + scrollAdjustPad);
4023
- if (scroll + scrollLength > totalSize) {
4024
- scroll = Math.max(0, totalSize - scrollLength);
4025
- }
4264
+ let scrollAdjustPending = 0;
4265
+ let scrollAdjustPad = 0;
4266
+ let scroll = 0;
4267
+ let scrollTopBuffered = 0;
4268
+ let scrollBottom = 0;
4269
+ let scrollBottomBuffered = 0;
4270
+ let nativeScrollState = scrollState;
4271
+ const updateScroll2 = (nextScrollState) => {
4272
+ var _a4;
4273
+ nativeScrollState = nextScrollState;
4274
+ scrollAdjustPending = (_a4 = peek$(ctx, "scrollAdjustPending")) != null ? _a4 : 0;
4275
+ scrollAdjustPad = scrollAdjustPending - topPad;
4276
+ scroll = Math.round(nextScrollState + scrollExtra + scrollAdjustPad);
4277
+ if (scroll + scrollLength > totalSize) {
4278
+ scroll = Math.max(0, totalSize - scrollLength);
4279
+ }
4280
+ };
4281
+ updateScroll2(scrollState);
4026
4282
  const previousStickyIndex = peek$(ctx, "activeStickyIndex");
4027
4283
  const currentStickyIdx = stickyIndicesArr.length > 0 ? findCurrentStickyIndex(stickyIndicesArr, scroll, state) : -1;
4028
4284
  const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyIndicesArr[currentStickyIdx] : -1;
4285
+ const stickyIndexDidChange = previousStickyIndex !== nextActiveStickyIndex;
4029
4286
  if (currentStickyIdx >= 0 || previousStickyIndex >= 0) {
4030
4287
  set$(ctx, "activeStickyIndex", nextActiveStickyIndex);
4031
4288
  }
4289
+ const shouldNotifyStickyHeaderChange = !!onStickyHeaderChange && stickyIndicesArr.length > 0 && stickyIndexDidChange;
4290
+ const finishCalculateItemsInView = shouldNotifyStickyHeaderChange ? () => {
4291
+ const item = data[nextActiveStickyIndex];
4292
+ if (item !== void 0) {
4293
+ onStickyHeaderChange == null ? void 0 : onStickyHeaderChange({ index: nextActiveStickyIndex, item });
4294
+ }
4295
+ } : void 0;
4032
4296
  let scrollBufferTop = drawDistance;
4033
4297
  let scrollBufferBottom = drawDistance;
4034
4298
  if (speed > 0 || speed === 0 && scroll < Math.max(50, drawDistance)) {
@@ -4038,28 +4302,30 @@ function calculateItemsInView(ctx, params = {}) {
4038
4302
  scrollBufferTop = drawDistance * 1.5;
4039
4303
  scrollBufferBottom = drawDistance * 0.5;
4040
4304
  }
4041
- const scrollTopBuffered = scroll - scrollBufferTop;
4042
- const scrollBottom = scroll + scrollLength + (scroll < 0 ? -scroll : 0);
4043
- const scrollBottomBuffered = scrollBottom + scrollBufferBottom;
4305
+ const updateScrollRange = () => {
4306
+ const scrollStart = Math.max(0, scroll);
4307
+ const overscrollBeforeContent = Math.max(0, -nativeScrollState);
4308
+ scrollTopBuffered = scrollStart - scrollBufferTop;
4309
+ scrollBottom = Math.max(scrollStart, scroll + scrollLength + overscrollBeforeContent);
4310
+ scrollBottomBuffered = scrollBottom + scrollBufferBottom;
4311
+ };
4312
+ updateScrollRange();
4044
4313
  if (!suppressInitialScrollSideEffects && !dataChanged && !forceFullItemPositions && scrollForNextCalculateItemsInView) {
4045
4314
  const { top, bottom } = scrollForNextCalculateItemsInView;
4046
4315
  if (top === null && bottom === null) {
4047
4316
  state.scrollForNextCalculateItemsInView = void 0;
4048
4317
  } else if ((top === null || scrollTopBuffered > top) && (bottom === null || scrollBottomBuffered < bottom)) {
4049
4318
  if (Platform.OS !== "web" || !isInMVCPActiveMode(state)) {
4319
+ finishCalculateItemsInView == null ? void 0 : finishCalculateItemsInView();
4050
4320
  return;
4051
4321
  }
4052
4322
  }
4053
4323
  }
4054
4324
  const checkMVCP = doMVCP && !suppressInitialScrollSideEffects ? prepareMVCP(ctx, dataChanged) : void 0;
4055
4325
  if (dataChanged) {
4056
- indexByKey.clear();
4057
- idCache.length = 0;
4058
- positions.length = 0;
4059
- columns.length = 0;
4060
- columnSpans.length = 0;
4326
+ resetLayoutCachesForDataChange(state);
4061
4327
  }
4062
- const startIndex = forceFullItemPositions || dataChanged ? 0 : (_d = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _d : 0;
4328
+ const startIndex = forceFullItemPositions || dataChanged ? 0 : (_c = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _c : 0;
4063
4329
  const optimizeForVisibleWindow = !forceFullItemPositions && !dataChanged && numColumns > 1 && minIndexSizeChanged !== void 0;
4064
4330
  updateItemPositions(ctx, dataChanged, {
4065
4331
  doMVCP,
@@ -4084,21 +4350,25 @@ function calculateItemsInView(ctx, params = {}) {
4084
4350
  }
4085
4351
  }
4086
4352
  const scrollBeforeMVCP = state.scroll;
4087
- const scrollAdjustPendingBeforeMVCP = (_e = peek$(ctx, "scrollAdjustPending")) != null ? _e : 0;
4353
+ const scrollAdjustPendingBeforeMVCP = (_d = peek$(ctx, "scrollAdjustPending")) != null ? _d : 0;
4088
4354
  checkMVCP == null ? void 0 : checkMVCP();
4089
- const didMVCPAdjustScroll = !!checkMVCP && (state.scroll !== scrollBeforeMVCP || ((_f = peek$(ctx, "scrollAdjustPending")) != null ? _f : 0) !== scrollAdjustPendingBeforeMVCP);
4355
+ const didMVCPAdjustScroll = !!checkMVCP && (state.scroll !== scrollBeforeMVCP || ((_e = peek$(ctx, "scrollAdjustPending")) != null ? _e : 0) !== scrollAdjustPendingBeforeMVCP);
4356
+ if (didMVCPAdjustScroll && initialScroll) {
4357
+ updateScroll2(state.scroll);
4358
+ updateScrollRange();
4359
+ }
4090
4360
  let startNoBuffer = null;
4091
4361
  let startBuffered = null;
4092
4362
  let startBufferedId = null;
4093
4363
  let endNoBuffer = null;
4094
4364
  let endBuffered = null;
4095
- let loopStart = (_g = suppressInitialScrollSideEffects ? bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.targetIndexSeed : void 0) != null ? _g : !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
4365
+ let loopStart = (_f = suppressInitialScrollSideEffects ? bootstrapInitialScrollState == null ? void 0 : bootstrapInitialScrollState.targetIndexSeed : void 0) != null ? _f : !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
4096
4366
  for (let i = loopStart; i >= 0; i--) {
4097
- const id = (_h = idCache[i]) != null ? _h : getId(state, i);
4367
+ const id = (_g = idCache[i]) != null ? _g : getId(state, i);
4098
4368
  const top = positions[i];
4099
- const size = (_i = sizes.get(id)) != null ? _i : getItemSize(ctx, id, i, data[i]);
4369
+ const size = (_h = sizes.get(id)) != null ? _h : getItemSize(ctx, id, i, data[i]);
4100
4370
  const bottom = top + size;
4101
- if (bottom > scroll - scrollBufferTop) {
4371
+ if (bottom > scrollTopBuffered) {
4102
4372
  loopStart = i;
4103
4373
  } else {
4104
4374
  break;
@@ -4127,8 +4397,8 @@ function calculateItemsInView(ctx, params = {}) {
4127
4397
  let firstFullyOnScreenIndex;
4128
4398
  const dataLength = data.length;
4129
4399
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
4130
- const id = (_j = idCache[i]) != null ? _j : getId(state, i);
4131
- const size = (_k = sizes.get(id)) != null ? _k : getItemSize(ctx, id, i, data[i]);
4400
+ const id = (_i = idCache[i]) != null ? _i : getId(state, i);
4401
+ const size = (_j = sizes.get(id)) != null ? _j : getItemSize(ctx, id, i, data[i]);
4132
4402
  const top = positions[i];
4133
4403
  if (!foundEnd) {
4134
4404
  if (startNoBuffer === null && top + size > scroll) {
@@ -4167,7 +4437,7 @@ function calculateItemsInView(ctx, params = {}) {
4167
4437
  const firstVisibleAnchorIndex = firstFullyOnScreenIndex != null ? firstFullyOnScreenIndex : startNoBuffer;
4168
4438
  if (firstVisibleAnchorIndex !== null && firstVisibleAnchorIndex !== void 0 && endNoBuffer !== null) {
4169
4439
  for (let i = firstVisibleAnchorIndex; i <= endNoBuffer; i++) {
4170
- const id = (_l = idCache[i]) != null ? _l : getId(state, i);
4440
+ const id = (_k = idCache[i]) != null ? _k : getId(state, i);
4171
4441
  idsInView.push(id);
4172
4442
  }
4173
4443
  }
@@ -4200,7 +4470,7 @@ function calculateItemsInView(ctx, params = {}) {
4200
4470
  const needNewContainers = [];
4201
4471
  const needNewContainersSet = /* @__PURE__ */ new Set();
4202
4472
  for (let i = startBuffered; i <= endBuffered; i++) {
4203
- const id = (_m = idCache[i]) != null ? _m : getId(state, i);
4473
+ const id = (_l = idCache[i]) != null ? _l : getId(state, i);
4204
4474
  if (!containerItemKeys.has(id)) {
4205
4475
  needNewContainersSet.add(i);
4206
4476
  needNewContainers.push(i);
@@ -4209,7 +4479,7 @@ function calculateItemsInView(ctx, params = {}) {
4209
4479
  if (alwaysRenderArr.length > 0) {
4210
4480
  for (const index of alwaysRenderArr) {
4211
4481
  if (index < 0 || index >= dataLength) continue;
4212
- const id = (_n = idCache[index]) != null ? _n : getId(state, index);
4482
+ const id = (_m = idCache[index]) != null ? _m : getId(state, index);
4213
4483
  if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
4214
4484
  needNewContainersSet.add(index);
4215
4485
  needNewContainers.push(index);
@@ -4248,7 +4518,7 @@ function calculateItemsInView(ctx, params = {}) {
4248
4518
  for (let idx = 0; idx < needNewContainers.length; idx++) {
4249
4519
  const i = needNewContainers[idx];
4250
4520
  const containerIndex = availableContainers[idx];
4251
- const id = (_o = idCache[i]) != null ? _o : getId(state, i);
4521
+ const id = (_n = idCache[i]) != null ? _n : getId(state, i);
4252
4522
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
4253
4523
  if (oldKey && oldKey !== id) {
4254
4524
  containerItemKeys.delete(oldKey);
@@ -4259,7 +4529,7 @@ function calculateItemsInView(ctx, params = {}) {
4259
4529
  state.containerItemTypes.set(containerIndex, requiredItemTypes[idx]);
4260
4530
  }
4261
4531
  containerItemKeys.set(id, containerIndex);
4262
- (_p = state.userScrollAnchorResetKeys) == null ? void 0 : _p.add(id);
4532
+ (_o = state.userScrollAnchorResetKeys) == null ? void 0 : _o.add(id);
4263
4533
  const containerSticky = `containerSticky${containerIndex}`;
4264
4534
  const isSticky = stickyIndicesSet.has(i);
4265
4535
  const isAlwaysRender = alwaysRenderSet.has(i);
@@ -4283,17 +4553,17 @@ function calculateItemsInView(ctx, params = {}) {
4283
4553
  if (numContainers !== prevNumContainers) {
4284
4554
  set$(ctx, "numContainers", numContainers);
4285
4555
  if (numContainers > peek$(ctx, "numContainersPooled")) {
4286
- set$(ctx, "numContainersPooled", Math.ceil(numContainers * 1.5));
4556
+ set$(ctx, "numContainersPooled", getExpandedContainerPoolSize(dataLength, numContainers));
4287
4557
  }
4288
4558
  }
4289
4559
  }
4290
- if (((_q = state.userScrollAnchorResetKeys) == null ? void 0 : _q.size) === 0) {
4560
+ if (((_p = state.userScrollAnchorResetKeys) == null ? void 0 : _p.size) === 0) {
4291
4561
  state.userScrollAnchorResetKeys = void 0;
4292
4562
  }
4293
4563
  if (alwaysRenderArr.length > 0) {
4294
4564
  for (const index of alwaysRenderArr) {
4295
4565
  if (index < 0 || index >= dataLength) continue;
4296
- const id = (_r = idCache[index]) != null ? _r : getId(state, index);
4566
+ const id = (_q = idCache[index]) != null ? _q : getId(state, index);
4297
4567
  const containerIndex = containerItemKeys.get(id);
4298
4568
  if (containerIndex !== void 0) {
4299
4569
  state.stickyContainerPool.add(containerIndex);
@@ -4367,12 +4637,7 @@ function calculateItemsInView(ctx, params = {}) {
4367
4637
  );
4368
4638
  }
4369
4639
  }
4370
- if (onStickyHeaderChange && stickyIndicesArr.length > 0 && nextActiveStickyIndex !== void 0 && nextActiveStickyIndex !== previousStickyIndex) {
4371
- const item = data[nextActiveStickyIndex];
4372
- if (item !== void 0) {
4373
- onStickyHeaderChange({ index: nextActiveStickyIndex, item });
4374
- }
4375
- }
4640
+ finishCalculateItemsInView == null ? void 0 : finishCalculateItemsInView();
4376
4641
  });
4377
4642
  }
4378
4643
 
@@ -4397,21 +4662,36 @@ function doMaintainScrollAtEnd(ctx) {
4397
4662
  if (contentSize < state.scrollLength) {
4398
4663
  state.scroll = 0;
4399
4664
  }
4400
- requestAnimationFrame(() => {
4401
- var _a3;
4402
- if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
4403
- state.maintainingScrollAtEnd = true;
4404
- (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
4405
- animated: maintainScrollAtEnd.animated
4406
- });
4407
- setTimeout(
4408
- () => {
4409
- state.maintainingScrollAtEnd = false;
4410
- },
4411
- maintainScrollAtEnd.animated ? 500 : 0
4412
- );
4413
- }
4414
- });
4665
+ if (!state.maintainingScrollAtEnd) {
4666
+ state.maintainingScrollAtEnd = true;
4667
+ requestAnimationFrame(() => {
4668
+ if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
4669
+ const scroller = refScroller.current;
4670
+ if (state.props.horizontal && isHorizontalRTL(state)) {
4671
+ const currentContentSize = getContentSize(ctx);
4672
+ const logicalEndOffset = getLogicalHorizontalMaxOffset(state, currentContentSize);
4673
+ const nativeOffset = toNativeHorizontalOffset(state, logicalEndOffset, currentContentSize);
4674
+ scroller == null ? void 0 : scroller.scrollTo({
4675
+ animated: maintainScrollAtEnd.animated,
4676
+ x: nativeOffset,
4677
+ y: 0
4678
+ });
4679
+ } else {
4680
+ scroller == null ? void 0 : scroller.scrollToEnd({
4681
+ animated: maintainScrollAtEnd.animated
4682
+ });
4683
+ }
4684
+ setTimeout(
4685
+ () => {
4686
+ state.maintainingScrollAtEnd = false;
4687
+ },
4688
+ maintainScrollAtEnd.animated ? 500 : 0
4689
+ );
4690
+ } else {
4691
+ state.maintainingScrollAtEnd = false;
4692
+ }
4693
+ });
4694
+ }
4415
4695
  return true;
4416
4696
  }
4417
4697
  return false;
@@ -4523,14 +4803,21 @@ function doInitialAllocateContainers(ctx) {
4523
4803
  } else {
4524
4804
  averageItemSize = estimatedItemSize;
4525
4805
  }
4526
- const numContainers = Math.ceil((scrollLength + drawDistance * 2) / averageItemSize * numColumns);
4806
+ const numContainers = Math.max(
4807
+ 1,
4808
+ Math.ceil((scrollLength + drawDistance * 2) / averageItemSize * numColumns)
4809
+ );
4527
4810
  for (let i = 0; i < numContainers; i++) {
4528
4811
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
4529
4812
  set$(ctx, `containerColumn${i}`, -1);
4530
4813
  set$(ctx, `containerSpan${i}`, 1);
4531
4814
  }
4532
4815
  set$(ctx, "numContainers", numContainers);
4533
- set$(ctx, "numContainersPooled", numContainers * state.props.initialContainerPoolRatio);
4816
+ set$(
4817
+ ctx,
4818
+ "numContainersPooled",
4819
+ getInitialContainerPoolSize(data.length, numContainers, state.props.initialContainerPoolRatio)
4820
+ );
4534
4821
  if (!IsNewArchitecture || state.lastLayout) {
4535
4822
  if (state.initialScroll) {
4536
4823
  requestAnimationFrame(() => {
@@ -4589,7 +4876,8 @@ function handleLayout(ctx, layoutParam, setCanRender) {
4589
4876
  }
4590
4877
  checkThresholds(ctx);
4591
4878
  if (state) {
4592
- state.needsOtherAxisSize = otherAxisSize - (state.props.stylePaddingTop || 0) < 10;
4879
+ const crossAxisPadding = state.props.horizontal ? (state.props.stylePaddingTop || 0) + (state.props.stylePaddingBottom || 0) : (state.props.stylePaddingLeft || 0) + (state.props.stylePaddingRight || 0);
4880
+ state.needsOtherAxisSize = otherAxisSize - crossAxisPadding < 10;
4593
4881
  }
4594
4882
  if (IS_DEV && measuredLength === 0) {
4595
4883
  warnDevOnce(
@@ -4682,8 +4970,8 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
4682
4970
  // src/core/onScroll.ts
4683
4971
  function trackInitialScrollNativeProgress(state, newScroll) {
4684
4972
  const initialNativeScrollWatchdog = initialScrollWatchdog.get(state);
4685
- const didInitialScrollProgress = !!initialNativeScrollWatchdog && initialScrollWatchdog.didObserveProgress(newScroll, initialNativeScrollWatchdog);
4686
- if (didInitialScrollProgress) {
4973
+ const didInitialScrollReachTarget = !!initialNativeScrollWatchdog && initialScrollWatchdog.didReachTarget(newScroll, initialNativeScrollWatchdog);
4974
+ if (didInitialScrollReachTarget) {
4687
4975
  initialScrollWatchdog.clear(state);
4688
4976
  return;
4689
4977
  }
@@ -4708,7 +4996,7 @@ function cloneScrollEvent(event) {
4708
4996
  };
4709
4997
  }
4710
4998
  function onScroll(ctx, event) {
4711
- var _a3, _b, _c, _d;
4999
+ var _a3, _b, _c, _d, _e;
4712
5000
  const state = ctx.state;
4713
5001
  const { scrollProcessingEnabled } = state;
4714
5002
  if (scrollProcessingEnabled === false) {
@@ -4727,6 +5015,9 @@ function onScroll(ctx, event) {
4727
5015
  }
4728
5016
  }
4729
5017
  let newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
5018
+ if (state.props.horizontal) {
5019
+ newScroll = toLogicalHorizontalOffset(state, newScroll, (_e = event.nativeEvent.contentSize) == null ? void 0 : _e.width);
5020
+ }
4730
5021
  if (state.scrollingTo && state.scrollingTo.offset >= newScroll) {
4731
5022
  const maxOffset = clampScrollOffset(ctx, newScroll, state.scrollingTo);
4732
5023
  if (newScroll !== maxOffset && Math.abs(newScroll - maxOffset) > 1) {
@@ -4821,16 +5112,20 @@ function maybeUpdateAnchoredEndSpace(ctx) {
4821
5112
  let contentBelowAnchor = 0;
4822
5113
  const footerSize = ctx.values.get("footerSize") || 0;
4823
5114
  const stylePaddingBottom = state.props.stylePaddingBottom || 0;
5115
+ let hasUnknownTailSize = false;
4824
5116
  for (let index = anchorIndex; index < data.length; index++) {
4825
5117
  const itemKey = getId(state, index);
4826
5118
  const size = itemKey ? state.sizesKnown.get(itemKey) : void 0;
4827
5119
  const effectiveSize = index === anchorIndex && anchorMaxSize !== void 0 ? Math.min(size || 0, Math.max(0, anchorMaxSize)) : size;
5120
+ if (size === void 0) {
5121
+ hasUnknownTailSize = true;
5122
+ }
4828
5123
  if (effectiveSize !== null && effectiveSize !== void 0 && effectiveSize > 0) {
4829
5124
  contentBelowAnchor += effectiveSize;
4830
5125
  }
4831
5126
  }
4832
5127
  contentBelowAnchor += footerSize + stylePaddingBottom;
4833
- nextSize = Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
5128
+ nextSize = hasUnknownTailSize ? previousSize || 0 : Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
4834
5129
  }
4835
5130
  }
4836
5131
  if (previousSize !== nextSize) {
@@ -4896,6 +5191,16 @@ function runOrScheduleMVCPRecalculate(ctx) {
4896
5191
  calculateItemsInView(ctx, { doMVCP: true });
4897
5192
  }
4898
5193
  }
5194
+ function updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal) {
5195
+ const state = ctx.state;
5196
+ if (state.needsOtherAxisSize) {
5197
+ const otherAxisSize = horizontal ? sizeObj.height : sizeObj.width;
5198
+ const currentOtherAxisSize = peek$(ctx, "otherAxisSize");
5199
+ if (!currentOtherAxisSize || otherAxisSize > currentOtherAxisSize) {
5200
+ set$(ctx, "otherAxisSize", otherAxisSize);
5201
+ }
5202
+ }
5203
+ }
4899
5204
  function updateItemSize(ctx, itemKey, sizeObj) {
4900
5205
  var _a3;
4901
5206
  const state = ctx.state;
@@ -4904,15 +5209,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
4904
5209
  const {
4905
5210
  didContainersLayout,
4906
5211
  sizesKnown,
4907
- props: {
4908
- getFixedItemSize,
4909
- getItemType,
4910
- horizontal,
4911
- suggestEstimatedItemSize,
4912
- onItemSizeChanged,
4913
- data,
4914
- maintainScrollAtEnd
4915
- }
5212
+ props: { getFixedItemSize, getItemType, horizontal, onItemSizeChanged, data, maintainScrollAtEnd }
4916
5213
  } = state;
4917
5214
  if (!data) return;
4918
5215
  const index = state.indexByKey.get(itemKey);
@@ -4927,13 +5224,13 @@ function updateItemSize(ctx, itemKey, sizeObj) {
4927
5224
  const type = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
4928
5225
  const size2 = getFixedItemSize(itemData, index, type);
4929
5226
  if (size2 !== void 0 && size2 === sizesKnown.get(itemKey)) {
5227
+ updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
4930
5228
  return;
4931
5229
  }
4932
5230
  }
4933
5231
  let needsRecalculate = !didContainersLayout;
4934
5232
  let shouldMaintainScrollAtEnd = false;
4935
5233
  let minIndexSizeChanged;
4936
- let maxOtherAxisSize = peek$(ctx, "otherAxisSize") || 0;
4937
5234
  const prevSizeKnown = state.sizesKnown.get(itemKey);
4938
5235
  const diff = updateOneItemSize(ctx, itemKey, sizeObj);
4939
5236
  const size = roundSize(horizontal ? sizeObj.width : sizeObj.height);
@@ -4944,10 +5241,6 @@ function updateItemSize(ctx, itemKey, sizeObj) {
4944
5241
  if (!needsRecalculate && state.containerItemKeys.has(itemKey)) {
4945
5242
  needsRecalculate = true;
4946
5243
  }
4947
- if (state.needsOtherAxisSize) {
4948
- const otherAxisSize = horizontal ? sizeObj.height : sizeObj.width;
4949
- maxOtherAxisSize = Math.max(maxOtherAxisSize, otherAxisSize);
4950
- }
4951
5244
  if (prevSizeKnown !== void 0 && Math.abs(prevSizeKnown - size) > 5) {
4952
5245
  shouldMaintainScrollAtEnd = true;
4953
5246
  }
@@ -4963,22 +5256,7 @@ function updateItemSize(ctx, itemKey, sizeObj) {
4963
5256
  if (minIndexSizeChanged !== void 0) {
4964
5257
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
4965
5258
  }
4966
- if (IS_DEV && suggestEstimatedItemSize && minIndexSizeChanged !== void 0) {
4967
- if (state.timeoutSizeMessage) clearTimeout(state.timeoutSizeMessage);
4968
- state.timeoutSizeMessage = setTimeout(() => {
4969
- var _a4;
4970
- state.timeoutSizeMessage = void 0;
4971
- const num = state.sizesKnown.size;
4972
- const avg = (_a4 = state.averageSizes[""]) == null ? void 0 : _a4.avg;
4973
- console.warn(
4974
- `[legend-list] Based on the ${num} items rendered so far, the optimal estimated size is ${avg}.`
4975
- );
4976
- }, 1e3);
4977
- }
4978
- const cur = peek$(ctx, "otherAxisSize");
4979
- if (!cur || maxOtherAxisSize > cur) {
4980
- set$(ctx, "otherAxisSize", maxOtherAxisSize);
4981
- }
5259
+ updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
4982
5260
  if (didContainersLayout || checkAllSizesKnown(state, getMountedBufferedIndices(state))) {
4983
5261
  if (needsRecalculate) {
4984
5262
  state.scrollForNextCalculateItemsInView = void 0;
@@ -5092,12 +5370,47 @@ function createColumnWrapperStyle(contentContainerStyle) {
5092
5370
  }
5093
5371
 
5094
5372
  // src/utils/createImperativeHandle.ts
5373
+ var DEFAULT_AVERAGE_ITEM_SIZE_TYPE = "default";
5374
+ function getAverageItemSizes(state) {
5375
+ const averageItemSizes = {};
5376
+ for (const itemType in state.averageSizes) {
5377
+ const averageSize = state.averageSizes[itemType];
5378
+ if (averageSize) {
5379
+ averageItemSizes[itemType || DEFAULT_AVERAGE_ITEM_SIZE_TYPE] = {
5380
+ average: averageSize.avg,
5381
+ count: averageSize.num
5382
+ };
5383
+ }
5384
+ }
5385
+ return averageItemSizes;
5386
+ }
5095
5387
  function createImperativeHandle(ctx) {
5096
5388
  const state = ctx.state;
5097
5389
  const IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS = 800;
5098
5390
  const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
5099
5391
  let imperativeScrollToken = 0;
5100
5392
  const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0;
5393
+ const isScrollToIndexReady = (targetIndex, allowEmpty = false) => {
5394
+ var _a3;
5395
+ const props = state.props;
5396
+ const dataLength = props.data.length;
5397
+ const anchorIndex = (_a3 = props.anchoredEndSpace) == null ? void 0 : _a3.anchorIndex;
5398
+ if (targetIndex < 0) {
5399
+ return allowEmpty;
5400
+ }
5401
+ if (targetIndex >= dataLength) {
5402
+ return false;
5403
+ }
5404
+ if (anchorIndex === void 0 || anchorIndex < 0 || anchorIndex >= dataLength || targetIndex < anchorIndex || props.getFixedItemSize) {
5405
+ return true;
5406
+ }
5407
+ for (let index = anchorIndex; index < dataLength; index++) {
5408
+ if (!state.sizesKnown.has(getId(state, index))) {
5409
+ return false;
5410
+ }
5411
+ }
5412
+ return true;
5413
+ };
5101
5414
  const runWhenReady = (token, run, isReady) => {
5102
5415
  const startedAt = Date.now();
5103
5416
  let stableFrames = 0;
@@ -5119,11 +5432,10 @@ function createImperativeHandle(ctx) {
5119
5432
  };
5120
5433
  requestAnimationFrame(check);
5121
5434
  };
5122
- const runScrollWithPromise = (run, options) => new Promise((resolve) => {
5123
- var _a3, _b;
5435
+ const runScrollWithPromise = (run, isReady = () => true) => new Promise((resolve) => {
5436
+ var _a3;
5124
5437
  const token = ++imperativeScrollToken;
5125
- const isReady = (_a3 = options == null ? void 0 : options.isReady) != null ? _a3 : (() => true);
5126
- (_b = state.pendingScrollResolve) == null ? void 0 : _b.call(state);
5438
+ (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
5127
5439
  state.pendingScrollResolve = resolve;
5128
5440
  const runNow = () => {
5129
5441
  if (token !== imperativeScrollToken) {
@@ -5198,6 +5510,7 @@ function createImperativeHandle(ctx) {
5198
5510
  },
5199
5511
  end: state.endNoBuffer,
5200
5512
  endBuffered: state.endBuffered,
5513
+ getAverageItemSizes: () => getAverageItemSizes(state),
5201
5514
  isAtEnd: peek$(ctx, "isAtEnd"),
5202
5515
  isAtStart: peek$(ctx, "isAtStart"),
5203
5516
  isEndReached: state.isEndReached,
@@ -5240,40 +5553,34 @@ function createImperativeHandle(ctx) {
5240
5553
  }
5241
5554
  return false;
5242
5555
  }),
5243
- scrollToEnd: (options) => runScrollWithPromise(() => {
5244
- const data = state.props.data;
5245
- const stylePaddingBottom = state.props.stylePaddingBottom;
5246
- const index = data.length - 1;
5247
- if (index !== -1) {
5248
- const paddingBottom = stylePaddingBottom || 0;
5249
- const footerSize = peek$(ctx, "footerSize") || 0;
5250
- scrollToIndex(ctx, {
5251
- ...options,
5252
- index,
5253
- viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
5254
- viewPosition: 1
5255
- });
5256
- return true;
5257
- }
5258
- return false;
5259
- }),
5260
- scrollToIndex: (params) => {
5261
- const shouldWaitForOutOfRangeTarget = params.index >= 0 && params.index >= state.props.data.length;
5262
- const options = shouldWaitForOutOfRangeTarget ? {
5263
- isReady: () => {
5264
- var _a3;
5265
- const props = state.props;
5266
- const anchorIndex = (_a3 = props.anchoredEndSpace) == null ? void 0 : _a3.anchorIndex;
5267
- const lastIndex = props.data.length - 1;
5268
- const isInRange = params.index < props.data.length;
5269
- const shouldWaitForAnchorSize = isInRange && anchorIndex !== void 0 && anchorIndex >= 0 && params.index >= anchorIndex && !props.getFixedItemSize && !state.sizesKnown.has(getId(state, lastIndex));
5270
- return isInRange && !shouldWaitForAnchorSize;
5556
+ scrollToEnd: (options) => runScrollWithPromise(
5557
+ () => {
5558
+ const data = state.props.data;
5559
+ const stylePaddingBottom = state.props.stylePaddingBottom;
5560
+ const index = data.length - 1;
5561
+ if (index !== -1) {
5562
+ const paddingBottom = stylePaddingBottom || 0;
5563
+ const footerSize = peek$(ctx, "footerSize") || 0;
5564
+ scrollToIndex(ctx, {
5565
+ ...options,
5566
+ index,
5567
+ viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
5568
+ viewPosition: 1
5569
+ });
5570
+ return true;
5271
5571
  }
5272
- } : void 0;
5273
- return runScrollWithPromise(() => {
5274
- scrollToIndex(ctx, params);
5275
- return true;
5276
- }, options);
5572
+ return false;
5573
+ },
5574
+ () => isScrollToIndexReady(state.props.data.length - 1, true)
5575
+ ),
5576
+ scrollToIndex: (params) => {
5577
+ return runScrollWithPromise(
5578
+ () => {
5579
+ scrollToIndex(ctx, params);
5580
+ return true;
5581
+ },
5582
+ params.index >= 0 ? () => isScrollToIndexReady(params.index) : void 0
5583
+ );
5277
5584
  },
5278
5585
  scrollToItem: ({ item, ...props }) => runScrollWithPromise(() => {
5279
5586
  const data = state.props.data;
@@ -5537,7 +5844,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5537
5844
  getFixedItemSize,
5538
5845
  getItemType,
5539
5846
  horizontal,
5540
- initialContainerPoolRatio = 2,
5847
+ rtl,
5848
+ initialContainerPoolRatio = 3,
5849
+ estimatedHeaderSize,
5541
5850
  initialScrollAtEnd = false,
5542
5851
  initialScrollIndex: initialScrollIndexProp,
5543
5852
  initialScrollOffset: initialScrollOffsetProp,
@@ -5578,7 +5887,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5578
5887
  stickyIndices: stickyIndicesDeprecated,
5579
5888
  // TODOV3: Remove from v3 release
5580
5889
  style: styleProp,
5581
- suggestEstimatedItemSize,
5582
5890
  useWindowScroll = false,
5583
5891
  viewabilityConfig,
5584
5892
  viewabilityConfigCallbackPairs,
@@ -5606,13 +5914,16 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5606
5914
  const style = { ...StyleSheet.flatten(styleProp) };
5607
5915
  const stylePaddingTopState = extractPadding(style, contentContainerStyle, "Top");
5608
5916
  const stylePaddingBottomState = extractPadding(style, contentContainerStyle, "Bottom");
5917
+ const stylePaddingLeftState = extractPadding(style, contentContainerStyle, "Left");
5918
+ const stylePaddingRightState = extractPadding(style, contentContainerStyle, "Right");
5609
5919
  const maintainScrollAtEndConfig = normalizeMaintainScrollAtEnd(maintainScrollAtEnd);
5610
5920
  const maintainVisibleContentPositionConfig = normalizeMaintainVisibleContentPosition(
5611
5921
  maintainVisibleContentPositionProp
5612
5922
  );
5613
5923
  const hasInitialScrollIndex = initialScrollIndexProp !== void 0 && initialScrollIndexProp !== null;
5614
5924
  const hasInitialScrollOffset = initialScrollOffsetProp !== void 0 && initialScrollOffsetProp !== null;
5615
- const initialScrollUsesOffsetOnly = !initialScrollAtEnd && !hasInitialScrollIndex && hasInitialScrollOffset;
5925
+ const shouldInitializeHorizontalRTL = !initialScrollAtEnd && !hasInitialScrollIndex && !hasInitialScrollOffset && isHorizontalRTLProps({ horizontal, rtl });
5926
+ const initialScrollUsesOffsetOnly = !initialScrollAtEnd && !hasInitialScrollIndex && (hasInitialScrollOffset || shouldInitializeHorizontalRTL);
5616
5927
  const usesBootstrapInitialScroll = initialScrollAtEnd || hasInitialScrollIndex;
5617
5928
  const initialScrollProp = initialScrollAtEnd ? {
5618
5929
  index: Math.max(0, dataProp.length - 1),
@@ -5719,7 +6030,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5719
6030
  startReachedSnapshotDataChangeEpoch: void 0,
5720
6031
  stickyContainerPool: /* @__PURE__ */ new Set(),
5721
6032
  stickyContainers: /* @__PURE__ */ new Map(),
5722
- timeoutSizeMessage: 0,
5723
6033
  timeouts: /* @__PURE__ */ new Set(),
5724
6034
  totalSize: 0,
5725
6035
  viewabilityConfigCallbackPairs: void 0
@@ -5729,6 +6039,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5729
6039
  internalState.reprocessCurrentScroll = () => updateScroll(ctx, internalState.scroll, true);
5730
6040
  set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPositionConfig);
5731
6041
  set$(ctx, "extraData", extraData);
6042
+ if (estimatedHeaderSize !== void 0) {
6043
+ set$(ctx, "headerSize", estimatedHeaderSize);
6044
+ }
5732
6045
  }
5733
6046
  refState.current = ctx.state;
5734
6047
  }
@@ -5739,7 +6052,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5739
6052
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
5740
6053
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
5741
6054
  const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
5742
- if (didDataChangeLocal && state.didFinishInitialScroll && ((_f = state.initialScroll) == null ? void 0 : _f.viewPosition) === 1 && state.props.data.length > 0) {
6055
+ if (didDataChangeLocal && !initialScrollAtEnd && state.didFinishInitialScroll && ((_f = state.initialScroll) == null ? void 0 : _f.viewPosition) === 1 && state.props.data.length > 0) {
5743
6056
  clearPreservedInitialScrollTarget(state);
5744
6057
  }
5745
6058
  if (didDataChangeLocal) {
@@ -5788,13 +6101,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5788
6101
  positionComponentInternal,
5789
6102
  recycleItems: !!recycleItems,
5790
6103
  renderItem,
6104
+ rtl,
5791
6105
  snapToIndices,
5792
6106
  stickyIndicesArr: stickyHeaderIndices != null ? stickyHeaderIndices : [],
5793
6107
  stickyIndicesSet: React2.useMemo(() => new Set(stickyHeaderIndices != null ? stickyHeaderIndices : []), [stickyHeaderIndices == null ? void 0 : stickyHeaderIndices.join(",")]),
5794
6108
  stickyPositionComponentInternal,
5795
6109
  stylePaddingBottom: stylePaddingBottomState,
6110
+ stylePaddingLeft: stylePaddingLeftState,
6111
+ stylePaddingRight: stylePaddingRightState,
5796
6112
  stylePaddingTop: stylePaddingTopState,
5797
- suggestEstimatedItemSize: !!suggestEstimatedItemSize,
5798
6113
  useWindowScroll: useWindowScrollResolved
5799
6114
  };
5800
6115
  state.refScroller = refScroller;
@@ -5821,6 +6136,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5821
6136
  };
5822
6137
  if (isFirstLocal) {
5823
6138
  initializeStateVars(false);
6139
+ resetLayoutCachesForDataChange(state);
5824
6140
  updateItemPositions(
5825
6141
  ctx,
5826
6142
  /*dataChanged*/
@@ -5838,6 +6154,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
5838
6154
  }, [usesBootstrapInitialScroll]);
5839
6155
  React2.useLayoutEffect(() => {
5840
6156
  initializeInitialScrollOnMount(ctx, {
6157
+ alwaysDispatchInitialScroll: shouldInitializeHorizontalRTL,
5841
6158
  dataLength: dataProp.length,
5842
6159
  hasFooterComponent: !!ListFooterComponent,
5843
6160
  initialContentOffset,