@legendapp/list 3.0.0-beta.32 → 3.0.0-beta.34

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.
Files changed (54) hide show
  1. package/README.md +7 -1
  2. package/animated.d.ts +605 -6
  3. package/animated.js +2 -2
  4. package/animated.mjs +1 -1
  5. package/index.d.ts +503 -118
  6. package/index.js +607 -275
  7. package/index.mjs +607 -275
  8. package/index.native.js +348 -189
  9. package/index.native.mjs +347 -188
  10. package/keyboard-controller.d.ts +616 -6
  11. package/keyboard-controller.js +2 -2
  12. package/keyboard-controller.mjs +1 -1
  13. package/keyboard.d.ts +204 -8
  14. package/keyboard.js +68 -53
  15. package/keyboard.mjs +71 -55
  16. package/{index.d.mts → list-react-native.d.ts} +138 -42
  17. package/list-react-native.js +4348 -0
  18. package/list-react-native.mjs +4318 -0
  19. package/{index.native.d.mts → list-react.d.ts} +195 -42
  20. package/list-react.js +4709 -0
  21. package/list-react.mjs +4679 -0
  22. package/package.json +52 -1
  23. package/reanimated.d.ts +605 -7
  24. package/reanimated.js +180 -12
  25. package/reanimated.mjs +177 -9
  26. package/section-list.d.ts +615 -14
  27. package/section-list.js +6 -6
  28. package/section-list.mjs +1 -1
  29. package/animated.d.mts +0 -9
  30. package/animated.native.d.mts +0 -9
  31. package/animated.native.d.ts +0 -9
  32. package/animated.native.js +0 -9
  33. package/animated.native.mjs +0 -7
  34. package/index.native.d.ts +0 -817
  35. package/keyboard-controller.d.mts +0 -12
  36. package/keyboard-controller.native.d.mts +0 -12
  37. package/keyboard-controller.native.d.ts +0 -12
  38. package/keyboard-controller.native.js +0 -69
  39. package/keyboard-controller.native.mjs +0 -48
  40. package/keyboard.d.mts +0 -13
  41. package/keyboard.native.d.mts +0 -13
  42. package/keyboard.native.d.ts +0 -13
  43. package/keyboard.native.js +0 -399
  44. package/keyboard.native.mjs +0 -377
  45. package/reanimated.d.mts +0 -18
  46. package/reanimated.native.d.mts +0 -18
  47. package/reanimated.native.d.ts +0 -18
  48. package/reanimated.native.js +0 -89
  49. package/reanimated.native.mjs +0 -65
  50. package/section-list.d.mts +0 -112
  51. package/section-list.native.d.mts +0 -112
  52. package/section-list.native.d.ts +0 -112
  53. package/section-list.native.js +0 -293
  54. package/section-list.native.mjs +0 -271
package/index.mjs CHANGED
@@ -224,7 +224,7 @@ var _a;
224
224
  var envMode = typeof process !== "undefined" && typeof process.env === "object" && process.env ? (_a = process.env.NODE_ENV) != null ? _a : process.env.MODE : void 0;
225
225
  var processDev = typeof envMode === "string" ? envMode.toLowerCase() !== "production" : void 0;
226
226
  var _a2;
227
- var IS_DEV = (_a2 = metroDev != null ? metroDev : processDev) != null ? _a2 : false;
227
+ var IS_DEV = (_a2 = processDev != null ? processDev : metroDev) != null ? _a2 : false;
228
228
 
229
229
  // src/constants.ts
230
230
  var POSITION_OUT_OF_VIEW = -1e7;
@@ -293,8 +293,15 @@ var PositionViewState = typedMemo(function PositionViewState2({
293
293
  };
294
294
  const composed = isArray(style) ? Object.assign({}, ...style) : style;
295
295
  const combinedStyle = horizontal ? { ...base, ...composed, left: position } : { ...base, ...composed, top: position };
296
- const { animatedScrollY, onLayout, index, ...webProps } = props;
297
- return /* @__PURE__ */ React3.createElement("div", { ref: refView, ...webProps, style: combinedStyle });
296
+ const {
297
+ animatedScrollY: _animatedScrollY,
298
+ index,
299
+ onLayout: _onLayout,
300
+ onLayoutChange: _onLayoutChange,
301
+ stickyHeaderConfig: _stickyHeaderConfig,
302
+ ...webProps
303
+ } = props;
304
+ return /* @__PURE__ */ React3.createElement("div", { "data-index": index, ref: refView, ...webProps, style: combinedStyle });
298
305
  });
299
306
  var PositionViewSticky = typedMemo(function PositionViewSticky2({
300
307
  id,
@@ -304,10 +311,15 @@ var PositionViewSticky = typedMemo(function PositionViewSticky2({
304
311
  index,
305
312
  animatedScrollY: _animatedScrollY,
306
313
  stickyHeaderConfig,
314
+ onLayout: _onLayout,
315
+ onLayoutChange: _onLayoutChange,
307
316
  children,
308
- ...rest
317
+ ...webProps
309
318
  }) {
310
- const [position = POSITION_OUT_OF_VIEW, activeStickyIndex] = useArr$([`containerPosition${id}`, "activeStickyIndex"]);
319
+ const [position = POSITION_OUT_OF_VIEW, activeStickyIndex] = useArr$([
320
+ `containerPosition${id}`,
321
+ "activeStickyIndex"
322
+ ]);
311
323
  const base = {
312
324
  contain: "paint layout style"
313
325
  };
@@ -334,7 +346,7 @@ var PositionViewSticky = typedMemo(function PositionViewSticky2({
334
346
  }
335
347
  return styleBase;
336
348
  }, [composed, horizontal, position, index, activeStickyIndex, stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset]);
337
- return /* @__PURE__ */ React3.createElement("div", { ref: refView, style: viewStyle, ...rest }, children);
349
+ return /* @__PURE__ */ React3.createElement("div", { "data-index": index, ref: refView, style: viewStyle, ...webProps }, children);
338
350
  });
339
351
  var PositionView = PositionViewState;
340
352
  function useInit(cb) {
@@ -621,6 +633,8 @@ var Container = typedMemo(function Container2({
621
633
  }) {
622
634
  const ctx = useStateContext();
623
635
  const { columnWrapperStyle, animatedScrollY } = ctx;
636
+ const positionComponentInternal = ctx.state.props.positionComponentInternal;
637
+ const stickyPositionComponentInternal = ctx.state.props.stickyPositionComponentInternal;
624
638
  const [column = 0, span = 1, data, itemKey, numColumns = 1, extraData, isSticky] = useArr$([
625
639
  `containerColumn${id}`,
626
640
  `containerSpan${id}`,
@@ -746,7 +760,7 @@ var Container = typedMemo(function Container2({
746
760
  },
747
761
  [itemKey, layoutRenderCount]
748
762
  );
749
- const PositionComponent = isSticky ? PositionViewSticky : PositionView;
763
+ const PositionComponent = isSticky ? stickyPositionComponentInternal ? stickyPositionComponentInternal : PositionViewSticky : positionComponentInternal ? positionComponentInternal : PositionView;
750
764
  return /* @__PURE__ */ React3.createElement(
751
765
  PositionComponent,
752
766
  {
@@ -766,8 +780,12 @@ var Container = typedMemo(function Container2({
766
780
 
767
781
  // src/utils/reordering.ts
768
782
  var mapFn = (element) => {
769
- const indexStr = element.getAttribute("index");
770
- return [element, indexStr === null ? null : parseInt(indexStr)];
783
+ const indexStr = element.getAttribute("data-index");
784
+ if (indexStr === null) {
785
+ return [element, null];
786
+ }
787
+ const index = Number.parseInt(indexStr, 10);
788
+ return [element, Number.isNaN(index) ? null : index];
771
789
  };
772
790
  function sortDOMElements(container) {
773
791
  const elements = Array.from(container.children);
@@ -947,6 +965,89 @@ var StyleSheet = {
947
965
  flatten: (style) => flattenStyles(style)
948
966
  };
949
967
 
968
+ // src/components/webScrollUtils.ts
969
+ function getDocumentScrollerNode() {
970
+ if (typeof document === "undefined") {
971
+ return null;
972
+ }
973
+ return document.scrollingElement || document.documentElement || document.body;
974
+ }
975
+ function getWindowScrollPosition() {
976
+ var _a3, _b, _c, _d;
977
+ if (typeof window === "undefined") {
978
+ return { x: 0, y: 0 };
979
+ }
980
+ return {
981
+ x: (_b = (_a3 = window.scrollX) != null ? _a3 : window.pageXOffset) != null ? _b : 0,
982
+ y: (_d = (_c = window.scrollY) != null ? _c : window.pageYOffset) != null ? _d : 0
983
+ };
984
+ }
985
+ function getElementDocumentPosition(element, scroll) {
986
+ var _a3, _b;
987
+ const rect = element == null ? void 0 : element.getBoundingClientRect();
988
+ return {
989
+ left: ((_a3 = rect == null ? void 0 : rect.left) != null ? _a3 : 0) + scroll.x,
990
+ top: ((_b = rect == null ? void 0 : rect.top) != null ? _b : 0) + scroll.y
991
+ };
992
+ }
993
+ function getContentSize2(content) {
994
+ var _a3, _b;
995
+ return {
996
+ height: (_a3 = content == null ? void 0 : content.scrollHeight) != null ? _a3 : 0,
997
+ width: (_b = content == null ? void 0 : content.scrollWidth) != null ? _b : 0
998
+ };
999
+ }
1000
+ function getScrollContentSize(scrollElement, contentElement, isWindowScroll) {
1001
+ return getContentSize2(isWindowScroll ? contentElement : scrollElement);
1002
+ }
1003
+ function getLayoutMeasurement(scrollElement, isWindowScroll, horizontal) {
1004
+ var _a3, _b, _c, _d, _e, _f;
1005
+ if (isWindowScroll && typeof window !== "undefined") {
1006
+ const rect = scrollElement == null ? void 0 : scrollElement.getBoundingClientRect();
1007
+ return {
1008
+ // In window-scroll mode, use viewport size on the scroll axis.
1009
+ height: horizontal ? (_b = (_a3 = rect == null ? void 0 : rect.height) != null ? _a3 : scrollElement == null ? void 0 : scrollElement.clientHeight) != null ? _b : window.innerHeight : window.innerHeight,
1010
+ // Keep the cross-axis size list-relative to avoid inflating container measurements.
1011
+ width: horizontal ? window.innerWidth : (_d = (_c = rect == null ? void 0 : rect.width) != null ? _c : scrollElement == null ? void 0 : scrollElement.clientWidth) != null ? _d : window.innerWidth
1012
+ };
1013
+ }
1014
+ return {
1015
+ height: (_e = scrollElement == null ? void 0 : scrollElement.clientHeight) != null ? _e : 0,
1016
+ width: (_f = scrollElement == null ? void 0 : scrollElement.clientWidth) != null ? _f : 0
1017
+ };
1018
+ }
1019
+ function clampOffset(offset, maxOffset) {
1020
+ return Math.max(0, Math.min(offset, maxOffset));
1021
+ }
1022
+ function getAxisSize(size, horizontal) {
1023
+ return horizontal ? size.width : size.height;
1024
+ }
1025
+ function getMaxOffset(contentSize, layoutMeasurement, horizontal) {
1026
+ return Math.max(0, getAxisSize(contentSize, horizontal) - getAxisSize(layoutMeasurement, horizontal));
1027
+ }
1028
+ function resolveScrollableNode(scrollElement, isWindowScroll) {
1029
+ return isWindowScroll ? getDocumentScrollerNode() || scrollElement : scrollElement;
1030
+ }
1031
+ function resolveScrollEventTarget(scrollElement, isWindowScroll) {
1032
+ return isWindowScroll && typeof window !== "undefined" ? window : scrollElement;
1033
+ }
1034
+ function getLayoutRectangle(element, isWindowScroll, horizontal) {
1035
+ const rect = element.getBoundingClientRect();
1036
+ const scroll = getWindowScrollPosition();
1037
+ return {
1038
+ height: isWindowScroll && typeof window !== "undefined" && !horizontal ? window.innerHeight : rect.height,
1039
+ width: isWindowScroll && typeof window !== "undefined" && horizontal ? window.innerWidth : rect.width,
1040
+ x: isWindowScroll ? rect.left + scroll.x : rect.left,
1041
+ y: isWindowScroll ? rect.top + scroll.y : rect.top
1042
+ };
1043
+ }
1044
+ function resolveWindowScrollTarget({ clampedOffset, horizontal, listPos, scroll }) {
1045
+ return {
1046
+ left: horizontal ? listPos.left + clampedOffset : scroll.x,
1047
+ top: horizontal ? scroll.y : listPos.top + clampedOffset
1048
+ };
1049
+ }
1050
+
950
1051
  // src/components/ListComponentScrollView.tsx
951
1052
  var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
952
1053
  children,
@@ -956,114 +1057,163 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
956
1057
  contentOffset,
957
1058
  maintainVisibleContentPosition,
958
1059
  onScroll: onScroll2,
959
- onMomentumScrollEnd,
1060
+ onMomentumScrollEnd: _onMomentumScrollEnd,
960
1061
  showsHorizontalScrollIndicator = true,
961
1062
  showsVerticalScrollIndicator = true,
962
1063
  refreshControl,
1064
+ useWindowScroll = false,
963
1065
  onLayout,
964
1066
  ...props
965
1067
  }, ref) {
966
1068
  const scrollRef = useRef(null);
967
1069
  const contentRef = useRef(null);
1070
+ const isWindowScroll = useWindowScroll;
1071
+ const getScrollTarget = useCallback(
1072
+ () => resolveScrollEventTarget(scrollRef.current, isWindowScroll),
1073
+ [isWindowScroll]
1074
+ );
1075
+ const getMaxScrollOffset = useCallback(() => {
1076
+ const scrollElement = scrollRef.current;
1077
+ const contentSize = getScrollContentSize(scrollElement, contentRef.current, isWindowScroll);
1078
+ const layoutMeasurement = getLayoutMeasurement(scrollElement, isWindowScroll, horizontal);
1079
+ return getMaxOffset(contentSize, layoutMeasurement, horizontal);
1080
+ }, [horizontal, isWindowScroll]);
1081
+ const getCurrentScrollOffset = useCallback(() => {
1082
+ const scrollElement = scrollRef.current;
1083
+ if (isWindowScroll) {
1084
+ const maxOffset = getMaxScrollOffset();
1085
+ const scroll = getWindowScrollPosition();
1086
+ const listPos = getElementDocumentPosition(scrollElement, scroll);
1087
+ const rawOffset = horizontal ? scroll.x - listPos.left : scroll.y - listPos.top;
1088
+ return clampOffset(rawOffset, maxOffset);
1089
+ }
1090
+ if (!scrollElement) {
1091
+ return 0;
1092
+ }
1093
+ return horizontal ? scrollElement.scrollLeft : scrollElement.scrollTop;
1094
+ }, [getMaxScrollOffset, horizontal, isWindowScroll]);
1095
+ const scrollToLocalOffset = useCallback(
1096
+ (offset, animated) => {
1097
+ const scrollElement = scrollRef.current;
1098
+ const target = getScrollTarget();
1099
+ if (!target || typeof target.scrollTo !== "function") {
1100
+ return;
1101
+ }
1102
+ const maxOffset = getMaxScrollOffset();
1103
+ const clampedOffset = clampOffset(offset, maxOffset);
1104
+ const behavior = animated ? "smooth" : "auto";
1105
+ const options = { behavior };
1106
+ if (isWindowScroll) {
1107
+ const scroll = getWindowScrollPosition();
1108
+ const listPos = getElementDocumentPosition(scrollElement, scroll);
1109
+ const { left, top } = resolveWindowScrollTarget({
1110
+ clampedOffset,
1111
+ horizontal,
1112
+ listPos,
1113
+ scroll
1114
+ });
1115
+ options.left = left;
1116
+ options.top = top;
1117
+ } else if (horizontal) {
1118
+ options.left = clampedOffset;
1119
+ } else {
1120
+ options.top = clampedOffset;
1121
+ }
1122
+ target.scrollTo(options);
1123
+ },
1124
+ [getMaxScrollOffset, getScrollTarget, horizontal, isWindowScroll]
1125
+ );
968
1126
  useImperativeHandle(ref, () => {
969
1127
  const api = {
970
1128
  getBoundingClientRect: () => {
971
1129
  var _a3;
972
1130
  return (_a3 = scrollRef.current) == null ? void 0 : _a3.getBoundingClientRect();
973
1131
  },
974
- getScrollableNode: () => scrollRef.current,
975
- getScrollResponder: () => scrollRef.current,
1132
+ getContentNode: () => contentRef.current,
1133
+ getCurrentScrollOffset,
1134
+ getScrollableNode: () => resolveScrollableNode(scrollRef.current, isWindowScroll),
1135
+ getScrollEventTarget: () => getScrollTarget(),
1136
+ getScrollResponder: () => resolveScrollableNode(scrollRef.current, isWindowScroll),
1137
+ isWindowScroll: () => isWindowScroll,
976
1138
  scrollBy: (x, y) => {
977
- const el = scrollRef.current;
978
- if (!el) return;
979
- el.scrollBy(x, y);
1139
+ const target = getScrollTarget();
1140
+ if (!target || typeof target.scrollBy !== "function") {
1141
+ return;
1142
+ }
1143
+ target.scrollBy({ behavior: "auto", left: x, top: y });
980
1144
  },
981
1145
  scrollTo: (options) => {
982
- const el = scrollRef.current;
983
- if (!el) return;
984
1146
  const { x = 0, y = 0, animated = true } = options;
985
- el.scrollTo({ behavior: animated ? "smooth" : "auto", left: x, top: y });
1147
+ scrollToLocalOffset(horizontal ? x : y, animated);
986
1148
  },
987
1149
  scrollToEnd: (options = {}) => {
988
- const el = scrollRef.current;
989
- if (!el) return;
990
1150
  const { animated = true } = options;
991
- if (horizontal) {
992
- el.scrollTo({ behavior: animated ? "smooth" : "auto", left: el.scrollWidth });
993
- } else {
994
- el.scrollTo({ behavior: animated ? "smooth" : "auto", top: el.scrollHeight });
995
- }
1151
+ const endOffset = getMaxScrollOffset();
1152
+ scrollToLocalOffset(endOffset, animated);
996
1153
  },
997
1154
  scrollToOffset: (params) => {
998
- const el = scrollRef.current;
999
- if (!el) return;
1000
1155
  const { offset, animated = true } = params;
1001
- if (horizontal) {
1002
- el.scrollTo({ behavior: animated ? "smooth" : "auto", left: offset });
1003
- } else {
1004
- el.scrollTo({ behavior: animated ? "smooth" : "auto", top: offset });
1005
- }
1156
+ scrollToLocalOffset(offset, animated);
1006
1157
  }
1007
1158
  };
1008
1159
  return api;
1009
- }, [horizontal]);
1160
+ }, [getCurrentScrollOffset, getMaxScrollOffset, getScrollTarget, horizontal, isWindowScroll, scrollToLocalOffset]);
1010
1161
  const handleScroll = useCallback(
1011
- (event) => {
1012
- if (!onScroll2 || !(event == null ? void 0 : event.target)) {
1162
+ (_event) => {
1163
+ if (!onScroll2) {
1013
1164
  return;
1014
1165
  }
1015
- const target = event.target;
1166
+ const target = scrollRef.current;
1167
+ if (!target) {
1168
+ return;
1169
+ }
1170
+ const contentSize = getContentSize2(contentRef.current);
1171
+ const layoutMeasurement = getLayoutMeasurement(scrollRef.current, isWindowScroll, horizontal);
1172
+ const offset = getCurrentScrollOffset();
1016
1173
  const scrollEvent = {
1017
1174
  nativeEvent: {
1018
1175
  contentOffset: {
1019
- x: target.scrollLeft,
1020
- y: target.scrollTop
1176
+ x: horizontal ? offset : 0,
1177
+ y: horizontal ? 0 : offset
1021
1178
  },
1022
1179
  contentSize: {
1023
- height: target.scrollHeight,
1024
- width: target.scrollWidth
1180
+ height: contentSize.height,
1181
+ width: contentSize.width
1025
1182
  },
1026
1183
  layoutMeasurement: {
1027
- height: target.clientHeight,
1028
- width: target.clientWidth
1184
+ height: layoutMeasurement.height,
1185
+ width: layoutMeasurement.width
1029
1186
  }
1030
1187
  }
1031
1188
  };
1032
1189
  onScroll2(scrollEvent);
1033
1190
  },
1034
- [onScroll2, onMomentumScrollEnd]
1191
+ [getCurrentScrollOffset, horizontal, isWindowScroll, onScroll2]
1035
1192
  );
1036
1193
  useLayoutEffect(() => {
1037
- const element = scrollRef.current;
1038
- if (!element) return;
1039
- element.addEventListener("scroll", handleScroll);
1194
+ const target = getScrollTarget();
1195
+ if (!target) return;
1196
+ target.addEventListener("scroll", handleScroll, { passive: true });
1040
1197
  return () => {
1041
- element.removeEventListener("scroll", handleScroll);
1198
+ target.removeEventListener("scroll", handleScroll);
1042
1199
  };
1043
- }, [handleScroll]);
1200
+ }, [getScrollTarget, handleScroll]);
1044
1201
  useEffect(() => {
1045
1202
  const doScroll = () => {
1046
- if (contentOffset && scrollRef.current) {
1047
- scrollRef.current.scrollLeft = contentOffset.x || 0;
1048
- scrollRef.current.scrollTop = contentOffset.y || 0;
1203
+ if (contentOffset) {
1204
+ scrollToLocalOffset(horizontal ? contentOffset.x || 0 : contentOffset.y || 0, false);
1049
1205
  }
1050
1206
  };
1051
1207
  doScroll();
1052
1208
  requestAnimationFrame(doScroll);
1053
- }, [contentOffset == null ? void 0 : contentOffset.x, contentOffset == null ? void 0 : contentOffset.y]);
1209
+ }, [contentOffset == null ? void 0 : contentOffset.x, contentOffset == null ? void 0 : contentOffset.y, horizontal, scrollToLocalOffset]);
1054
1210
  useLayoutEffect(() => {
1055
1211
  if (!onLayout || !scrollRef.current) return;
1056
1212
  const element = scrollRef.current;
1057
1213
  const fireLayout = () => {
1058
- const rect = element.getBoundingClientRect();
1059
1214
  onLayout({
1060
1215
  nativeEvent: {
1061
- layout: {
1062
- height: rect.height,
1063
- width: rect.width,
1064
- x: rect.left,
1065
- y: rect.top
1066
- }
1216
+ layout: getLayoutRectangle(element, isWindowScroll, horizontal)
1067
1217
  }
1068
1218
  });
1069
1219
  };
@@ -1072,16 +1222,27 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
1072
1222
  fireLayout();
1073
1223
  });
1074
1224
  resizeObserver.observe(element);
1075
- return () => resizeObserver.disconnect();
1076
- }, [onLayout]);
1225
+ const onWindowResize = () => {
1226
+ fireLayout();
1227
+ };
1228
+ if (isWindowScroll && typeof window !== "undefined" && typeof window.addEventListener === "function") {
1229
+ window.addEventListener("resize", onWindowResize);
1230
+ }
1231
+ return () => {
1232
+ resizeObserver.disconnect();
1233
+ if (isWindowScroll && typeof window !== "undefined" && typeof window.removeEventListener === "function") {
1234
+ window.removeEventListener("resize", onWindowResize);
1235
+ }
1236
+ };
1237
+ }, [isWindowScroll, onLayout]);
1077
1238
  const scrollViewStyle = {
1078
- overflow: "auto",
1079
- overflowX: horizontal ? "auto" : showsHorizontalScrollIndicator ? "auto" : "hidden",
1080
- overflowY: horizontal ? showsVerticalScrollIndicator ? "auto" : "hidden" : "auto",
1081
- position: "relative",
1082
- // Ensure proper positioning context
1083
- WebkitOverflowScrolling: "touch",
1084
- // iOS momentum scrolling
1239
+ ...isWindowScroll ? {} : {
1240
+ overflow: "auto",
1241
+ overflowX: horizontal ? "auto" : showsHorizontalScrollIndicator ? "auto" : "hidden",
1242
+ overflowY: horizontal ? showsVerticalScrollIndicator ? "auto" : "hidden" : "auto",
1243
+ WebkitOverflowScrolling: "touch"
1244
+ // iOS momentum scrolling
1245
+ },
1085
1246
  ...StyleSheet.flatten(style)
1086
1247
  };
1087
1248
  const contentStyle = {
@@ -1091,7 +1252,13 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
1091
1252
  minWidth: horizontal ? "100%" : void 0,
1092
1253
  ...StyleSheet.flatten(contentContainerStyle)
1093
1254
  };
1094
- const { contentInset, scrollEventThrottle, ScrollComponent, ...webProps } = props;
1255
+ const {
1256
+ contentInset: _contentInset,
1257
+ scrollEventThrottle: _scrollEventThrottle,
1258
+ ScrollComponent: _ScrollComponent,
1259
+ useWindowScroll: _useWindowScroll,
1260
+ ...webProps
1261
+ } = props;
1095
1262
  return /* @__PURE__ */ React3.createElement("div", { ref: scrollRef, ...webProps, style: scrollViewStyle }, refreshControl, /* @__PURE__ */ React3.createElement("div", { ref: contentRef, style: contentStyle }, children));
1096
1263
  });
1097
1264
  function useValueListener$(key, callback) {
@@ -1118,23 +1285,29 @@ function ScrollAdjust() {
1118
1285
  if (scrollView && scrollOffset !== lastScrollOffsetRef.current) {
1119
1286
  const scrollDelta = scrollOffset - lastScrollOffsetRef.current;
1120
1287
  if (scrollDelta !== 0) {
1288
+ const contentNode = scrollView.getContentNode();
1289
+ const prevScroll = scrollView.getCurrentScrollOffset();
1121
1290
  const el = scrollView.getScrollableNode();
1122
- const prevScroll = el.scrollTop;
1291
+ if (!contentNode) {
1292
+ scrollView.scrollBy(0, scrollDelta);
1293
+ lastScrollOffsetRef.current = scrollOffset;
1294
+ return;
1295
+ }
1296
+ const totalSize = contentNode.scrollHeight;
1297
+ const viewportSize = el.clientHeight;
1123
1298
  const nextScroll = prevScroll + scrollDelta;
1124
- const totalSize = el.scrollHeight;
1125
- if (scrollDelta > 0 && !ctx.state.adjustingFromInitialMount && totalSize < nextScroll + el.clientHeight) {
1299
+ if (scrollDelta > 0 && !ctx.state.adjustingFromInitialMount && totalSize < nextScroll + viewportSize) {
1126
1300
  const paddingBottom = ctx.state.props.stylePaddingBottom || 0;
1127
- const child = el.firstElementChild;
1128
- const pad = (nextScroll + el.clientHeight - totalSize) * 2;
1129
- child.style.paddingBottom = `${pad}px`;
1130
- void el.offsetHeight;
1301
+ const pad = (nextScroll + viewportSize - totalSize) * 2;
1302
+ contentNode.style.paddingBottom = `${pad}px`;
1303
+ void contentNode.offsetHeight;
1131
1304
  scrollView.scrollBy(0, scrollDelta);
1132
1305
  if (resetPaddingRafRef.current !== void 0) {
1133
1306
  cancelAnimationFrame(resetPaddingRafRef.current);
1134
1307
  }
1135
1308
  resetPaddingRafRef.current = requestAnimationFrame(() => {
1136
1309
  resetPaddingRafRef.current = void 0;
1137
- child.style.paddingBottom = paddingBottom ? `${paddingBottom}px` : "0";
1310
+ contentNode.style.paddingBottom = paddingBottom ? `${paddingBottom}px` : "0";
1138
1311
  });
1139
1312
  } else {
1140
1313
  scrollView.scrollBy(0, scrollDelta);
@@ -1193,12 +1366,15 @@ var ListComponent = typedMemo(function ListComponent2({
1193
1366
  snapToIndices,
1194
1367
  stickyHeaderConfig,
1195
1368
  stickyHeaderIndices,
1369
+ useWindowScroll = false,
1196
1370
  ...rest
1197
1371
  }) {
1198
1372
  const ctx = useStateContext();
1199
1373
  const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
1200
1374
  const ScrollComponent = renderScrollComponent ? useMemo(
1201
- () => React3.forwardRef((props, ref) => renderScrollComponent({ ...props, ref })),
1375
+ () => React3.forwardRef(
1376
+ (props, ref) => renderScrollComponent({ ...props, ref })
1377
+ ),
1202
1378
  [renderScrollComponent]
1203
1379
  ) : ListComponentScrollView;
1204
1380
  const SnapOrScroll = snapToIndices ? SnapWrapper : ScrollComponent;
@@ -1214,6 +1390,7 @@ var ListComponent = typedMemo(function ListComponent2({
1214
1390
  SnapOrScroll,
1215
1391
  {
1216
1392
  ...rest,
1393
+ ...ScrollComponent === ListComponentScrollView ? { useWindowScroll } : {},
1217
1394
  contentContainerStyle: [
1218
1395
  contentContainerStyle,
1219
1396
  horizontal ? {
@@ -1259,24 +1436,12 @@ var ListComponent = typedMemo(function ListComponent2({
1259
1436
  );
1260
1437
  });
1261
1438
 
1262
- // src/utils/getId.ts
1263
- function getId(state, index) {
1264
- const { data, keyExtractor } = state.props;
1265
- if (!data) {
1266
- return "";
1267
- }
1268
- const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
1269
- const id = ret;
1270
- state.idCache[index] = id;
1271
- return id;
1272
- }
1273
-
1274
1439
  // src/core/calculateOffsetForIndex.ts
1275
1440
  function calculateOffsetForIndex(ctx, index) {
1276
1441
  const state = ctx.state;
1277
1442
  let position = 0;
1278
1443
  if (index !== void 0) {
1279
- position = state.positions.get(getId(state, index)) || 0;
1444
+ position = state.positions[index] || 0;
1280
1445
  const paddingTop = peek$(ctx, "stylePaddingTop");
1281
1446
  if (paddingTop) {
1282
1447
  position += paddingTop;
@@ -1289,6 +1454,18 @@ function calculateOffsetForIndex(ctx, index) {
1289
1454
  return position;
1290
1455
  }
1291
1456
 
1457
+ // src/utils/getId.ts
1458
+ function getId(state, index) {
1459
+ const { data, keyExtractor } = state.props;
1460
+ if (!data) {
1461
+ return "";
1462
+ }
1463
+ const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
1464
+ const id = ret;
1465
+ state.idCache[index] = id;
1466
+ return id;
1467
+ }
1468
+
1292
1469
  // src/core/addTotalSize.ts
1293
1470
  function addTotalSize(ctx, key, add) {
1294
1471
  const state = ctx.state;
@@ -1340,13 +1517,13 @@ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
1340
1517
  return sizeKnown;
1341
1518
  }
1342
1519
  let size;
1343
- const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1344
1520
  if (preferCachedSize) {
1345
1521
  const cachedSize = sizes.get(key);
1346
1522
  if (cachedSize !== void 0) {
1347
1523
  return cachedSize;
1348
1524
  }
1349
1525
  }
1526
+ const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1350
1527
  if (getFixedItemSize) {
1351
1528
  size = getFixedItemSize(data, index, itemType);
1352
1529
  if (size !== void 0) {
@@ -1389,12 +1566,15 @@ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1389
1566
  }
1390
1567
 
1391
1568
  // src/core/clampScrollOffset.ts
1392
- function clampScrollOffset(ctx, offset) {
1569
+ function clampScrollOffset(ctx, offset, scrollTarget) {
1393
1570
  const state = ctx.state;
1394
1571
  const contentSize = getContentSize(ctx);
1395
1572
  let clampedOffset = offset;
1396
1573
  if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform.OS !== "android")) {
1397
- const maxOffset = Math.max(0, contentSize - state.scrollLength);
1574
+ const baseMaxOffset = Math.max(0, contentSize - state.scrollLength);
1575
+ const viewOffset = scrollTarget == null ? void 0 : scrollTarget.viewOffset;
1576
+ const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
1577
+ const maxOffset = baseMaxOffset + extraEndOffset;
1398
1578
  clampedOffset = Math.min(offset, maxOffset);
1399
1579
  }
1400
1580
  clampedOffset = Math.max(0, clampedOffset);
@@ -1487,37 +1667,54 @@ function checkAtBottom(ctx) {
1487
1667
 
1488
1668
  // src/utils/checkAtTop.ts
1489
1669
  function checkAtTop(ctx) {
1490
- var _a3;
1491
1670
  const state = ctx == null ? void 0 : ctx.state;
1492
- if (!state || state.initialScroll) {
1671
+ if (!state || state.initialScroll || state.scrollingTo) {
1493
1672
  return;
1494
1673
  }
1495
1674
  const {
1496
- scrollLength,
1675
+ dataChangeEpoch,
1676
+ isStartReached,
1677
+ props: { data, onStartReachedThreshold },
1497
1678
  scroll,
1498
- props: { onStartReachedThreshold }
1679
+ scrollLength,
1680
+ startReachedSnapshot,
1681
+ startReachedSnapshotDataChangeEpoch,
1682
+ totalSize
1499
1683
  } = state;
1500
- const distanceFromTop = scroll;
1501
- state.isAtStart = distanceFromTop <= 0;
1684
+ const dataLength = data.length;
1685
+ const threshold = onStartReachedThreshold * scrollLength;
1686
+ const dataChanged = startReachedSnapshotDataChangeEpoch !== dataChangeEpoch;
1687
+ const withinThreshold = threshold > 0 && Math.abs(scroll) <= threshold;
1688
+ const allowReentryOnDataChange = !!isStartReached && withinThreshold && !!dataChanged && !isInMVCPActiveMode(state);
1689
+ if (isStartReached && threshold > 0 && scroll > threshold && startReachedSnapshot && (dataChanged || startReachedSnapshot.contentSize !== totalSize || startReachedSnapshot.dataLength !== dataLength)) {
1690
+ state.isStartReached = false;
1691
+ state.startReachedSnapshot = void 0;
1692
+ state.startReachedSnapshotDataChangeEpoch = void 0;
1693
+ }
1694
+ state.isAtStart = scroll <= 0;
1695
+ if (isStartReached && withinThreshold && dataChanged && !allowReentryOnDataChange) {
1696
+ return;
1697
+ }
1502
1698
  state.isStartReached = checkThreshold(
1503
- distanceFromTop,
1699
+ scroll,
1504
1700
  false,
1505
- onStartReachedThreshold * scrollLength,
1701
+ threshold,
1506
1702
  state.isStartReached,
1507
- state.startReachedSnapshot,
1703
+ allowReentryOnDataChange ? void 0 : startReachedSnapshot,
1508
1704
  {
1509
- contentSize: state.totalSize,
1510
- dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1705
+ contentSize: totalSize,
1706
+ dataLength,
1511
1707
  scrollPosition: scroll
1512
1708
  },
1513
1709
  (distance) => {
1514
- var _a4, _b;
1515
- return (_b = (_a4 = state.props).onStartReached) == null ? void 0 : _b.call(_a4, { distanceFromStart: distance });
1710
+ var _a3, _b;
1711
+ return (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
1516
1712
  },
1517
1713
  (snapshot) => {
1518
1714
  state.startReachedSnapshot = snapshot;
1715
+ state.startReachedSnapshotDataChangeEpoch = snapshot ? dataChangeEpoch : void 0;
1519
1716
  },
1520
- false
1717
+ allowReentryOnDataChange
1521
1718
  );
1522
1719
  }
1523
1720
 
@@ -1533,14 +1730,22 @@ function setInitialRenderState(ctx, {
1533
1730
  didInitialScroll
1534
1731
  }) {
1535
1732
  const { state } = ctx;
1733
+ const {
1734
+ loadStartTime,
1735
+ props: { onLoad }
1736
+ } = state;
1536
1737
  if (didLayout) {
1537
1738
  state.didContainersLayout = true;
1538
1739
  }
1539
1740
  if (didInitialScroll) {
1540
1741
  state.didFinishInitialScroll = true;
1541
1742
  }
1542
- if (state.didContainersLayout && state.didFinishInitialScroll) {
1743
+ const isReadyToRender = Boolean(state.didContainersLayout && state.didFinishInitialScroll);
1744
+ if (isReadyToRender && !peek$(ctx, "readyToRender")) {
1543
1745
  set$(ctx, "readyToRender", true);
1746
+ if (onLoad) {
1747
+ onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1748
+ }
1544
1749
  }
1545
1750
  }
1546
1751
 
@@ -1549,6 +1754,8 @@ function finishScrollTo(ctx) {
1549
1754
  var _a3, _b;
1550
1755
  const state = ctx.state;
1551
1756
  if (state == null ? void 0 : state.scrollingTo) {
1757
+ const resolvePendingScroll = state.pendingScrollResolve;
1758
+ state.pendingScrollResolve = void 0;
1552
1759
  const scrollingTo = state.scrollingTo;
1553
1760
  state.scrollHistory.length = 0;
1554
1761
  state.initialScroll = void 0;
@@ -1565,6 +1772,7 @@ function finishScrollTo(ctx) {
1565
1772
  }
1566
1773
  setInitialRenderState(ctx, { didInitialScroll: true });
1567
1774
  checkThresholds(ctx);
1775
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1568
1776
  }
1569
1777
  }
1570
1778
 
@@ -1577,42 +1785,49 @@ function doScrollTo(ctx, params) {
1577
1785
  const state = ctx.state;
1578
1786
  const { animated, horizontal, offset } = params;
1579
1787
  const scroller = state.refScroller.current;
1580
- const node = typeof (scroller == null ? void 0 : scroller.getScrollableNode) === "function" ? scroller.getScrollableNode() : scroller;
1581
- if (node) {
1582
- const left = horizontal ? offset : 0;
1583
- const top = horizontal ? 0 : offset;
1584
- node.scrollTo({ behavior: animated ? "smooth" : "auto", left, top });
1585
- if (animated) {
1586
- listenForScrollEnd(ctx, node, {
1587
- horizontal: !!horizontal,
1588
- targetOffset: offset
1589
- });
1590
- } else {
1591
- state.scroll = offset;
1592
- setTimeout(() => {
1593
- finishScrollTo(ctx);
1594
- }, 100);
1595
- }
1788
+ const node = scroller == null ? void 0 : scroller.getScrollableNode();
1789
+ if (!scroller || !node) {
1790
+ return;
1791
+ }
1792
+ const isAnimated = !!animated;
1793
+ const isHorizontal = !!horizontal;
1794
+ const left = isHorizontal ? offset : 0;
1795
+ const top = isHorizontal ? 0 : offset;
1796
+ scroller.scrollTo({ animated: isAnimated, x: left, y: top });
1797
+ if (isAnimated) {
1798
+ const target = scroller.getScrollEventTarget();
1799
+ listenForScrollEnd(ctx, {
1800
+ readOffset: () => scroller.getCurrentScrollOffset(),
1801
+ target,
1802
+ targetOffset: offset
1803
+ });
1804
+ } else {
1805
+ state.scroll = offset;
1806
+ setTimeout(() => {
1807
+ finishScrollTo(ctx);
1808
+ }, 100);
1596
1809
  }
1597
1810
  }
1598
- function listenForScrollEnd(ctx, node, params) {
1599
- const { horizontal, targetOffset } = params;
1600
- const supportsScrollEnd = "onscrollend" in node;
1811
+ function listenForScrollEnd(ctx, params) {
1812
+ const { readOffset, target, targetOffset } = params;
1813
+ if (!target) {
1814
+ finishScrollTo(ctx);
1815
+ return;
1816
+ }
1817
+ const supportsScrollEnd = "onscrollend" in target;
1601
1818
  let idleTimeout;
1602
- let maxTimeout;
1603
1819
  let settled = false;
1604
1820
  const targetToken = ctx.state.scrollingTo;
1821
+ const maxTimeout = setTimeout(() => finish("max"), SCROLL_END_MAX_MS);
1605
1822
  const cleanup = () => {
1606
- node.removeEventListener("scroll", onScroll2);
1823
+ target.removeEventListener("scroll", onScroll2);
1607
1824
  if (supportsScrollEnd) {
1608
- node.removeEventListener("scrollend", onScrollEnd);
1825
+ target.removeEventListener("scrollend", onScrollEnd);
1609
1826
  }
1610
1827
  if (idleTimeout) {
1611
1828
  clearTimeout(idleTimeout);
1612
1829
  }
1613
- if (maxTimeout) {
1614
- clearTimeout(maxTimeout);
1615
- }
1830
+ clearTimeout(maxTimeout);
1616
1831
  };
1617
1832
  const finish = (reason) => {
1618
1833
  if (settled) return;
@@ -1621,7 +1836,7 @@ function listenForScrollEnd(ctx, node, params) {
1621
1836
  cleanup();
1622
1837
  return;
1623
1838
  }
1624
- const currentOffset = horizontal ? node.scrollLeft : node.scrollTop;
1839
+ const currentOffset = readOffset();
1625
1840
  const isNearTarget = Math.abs(currentOffset - targetOffset) <= SCROLL_END_TARGET_EPSILON;
1626
1841
  if (reason === "scrollend" && !isNearTarget) {
1627
1842
  return;
@@ -1637,13 +1852,11 @@ function listenForScrollEnd(ctx, node, params) {
1637
1852
  idleTimeout = setTimeout(() => finish("idle"), SCROLL_END_IDLE_MS);
1638
1853
  };
1639
1854
  const onScrollEnd = () => finish("scrollend");
1640
- node.addEventListener("scroll", onScroll2);
1855
+ target.addEventListener("scroll", onScroll2);
1641
1856
  if (supportsScrollEnd) {
1642
- node.addEventListener("scrollend", onScrollEnd);
1643
- maxTimeout = setTimeout(() => finish("max"), SCROLL_END_MAX_MS);
1857
+ target.addEventListener("scrollend", onScrollEnd);
1644
1858
  } else {
1645
1859
  idleTimeout = setTimeout(() => finish("idle"), SMOOTH_SCROLL_DURATION_MS);
1646
- maxTimeout = setTimeout(() => finish("max"), SCROLL_END_MAX_MS);
1647
1860
  }
1648
1861
  }
1649
1862
 
@@ -1662,7 +1875,7 @@ function scrollTo(ctx, params) {
1662
1875
  clearTimeout(ctx.state.timeoutCheckFinishedScrollFallback);
1663
1876
  }
1664
1877
  let offset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, scrollTargetOffset, scrollTarget);
1665
- offset = clampScrollOffset(ctx, offset);
1878
+ offset = clampScrollOffset(ctx, offset, scrollTarget);
1666
1879
  state.scrollHistory.length = 0;
1667
1880
  if (!noScrollingTo) {
1668
1881
  state.scrollingTo = scrollTarget;
@@ -1678,7 +1891,7 @@ function scrollTo(ctx, params) {
1678
1891
  // src/core/updateScroll.ts
1679
1892
  function updateScroll(ctx, newScroll, forceUpdate) {
1680
1893
  const state = ctx.state;
1681
- const { scrollingTo, scrollAdjustHandler, lastScrollAdjustForHistory } = state;
1894
+ const { ignoreScrollFromMVCP, lastScrollAdjustForHistory, scrollAdjustHandler, scrollHistory, scrollingTo } = state;
1682
1895
  const prevScroll = state.scroll;
1683
1896
  state.hasScrolled = true;
1684
1897
  state.lastBatchingAction = Date.now();
@@ -1686,22 +1899,17 @@ function updateScroll(ctx, newScroll, forceUpdate) {
1686
1899
  const adjust = scrollAdjustHandler.getAdjust();
1687
1900
  const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
1688
1901
  if (adjustChanged) {
1689
- state.scrollHistory.length = 0;
1902
+ scrollHistory.length = 0;
1690
1903
  }
1691
1904
  state.lastScrollAdjustForHistory = adjust;
1692
- if (scrollingTo === void 0 && !(state.scrollHistory.length === 0 && newScroll === state.scroll)) {
1905
+ if (scrollingTo === void 0 && !(scrollHistory.length === 0 && newScroll === state.scroll)) {
1693
1906
  if (!adjustChanged) {
1694
- state.scrollHistory.push({ scroll: newScroll, time: currentTime });
1907
+ scrollHistory.push({ scroll: newScroll, time: currentTime });
1695
1908
  }
1696
1909
  }
1697
- if (state.scrollHistory.length > 5) {
1698
- state.scrollHistory.shift();
1910
+ if (scrollHistory.length > 5) {
1911
+ scrollHistory.shift();
1699
1912
  }
1700
- state.scrollPrev = prevScroll;
1701
- state.scrollPrevTime = state.scrollTime;
1702
- state.scroll = newScroll;
1703
- state.scrollTime = currentTime;
1704
- const ignoreScrollFromMVCP = state.ignoreScrollFromMVCP;
1705
1913
  if (ignoreScrollFromMVCP && !scrollingTo) {
1706
1914
  const { lt, gt } = ignoreScrollFromMVCP;
1707
1915
  if (lt && newScroll < lt || gt && newScroll > gt) {
@@ -1709,6 +1917,10 @@ function updateScroll(ctx, newScroll, forceUpdate) {
1709
1917
  return;
1710
1918
  }
1711
1919
  }
1920
+ state.scrollPrev = prevScroll;
1921
+ state.scrollPrevTime = state.scrollTime;
1922
+ state.scroll = newScroll;
1923
+ state.scrollTime = currentTime;
1712
1924
  const scrollDelta = Math.abs(newScroll - prevScroll);
1713
1925
  const scrollLength = state.scrollLength;
1714
1926
  const lastCalculated = state.scrollLastCalculate;
@@ -1815,6 +2027,7 @@ function prepareMVCP(ctx, dataChanged) {
1815
2027
  const idsInViewWithPositions = [];
1816
2028
  const scrollTarget = scrollingTo == null ? void 0 : scrollingTo.index;
1817
2029
  const scrollingToViewPosition = scrollingTo == null ? void 0 : scrollingTo.viewPosition;
2030
+ const isEndAnchoredScrollTarget = scrollTarget !== void 0 && state.props.data.length > 0 && scrollTarget >= state.props.data.length - 1 && (scrollingToViewPosition != null ? scrollingToViewPosition : 0) > 0;
1818
2031
  const shouldMVCP = dataChanged ? mvcpData : mvcpScroll;
1819
2032
  const indexByKey = state.indexByKey;
1820
2033
  if (shouldMVCP) {
@@ -1831,12 +2044,18 @@ function prepareMVCP(ctx, dataChanged) {
1831
2044
  const id = idsInView[i];
1832
2045
  const index = indexByKey.get(id);
1833
2046
  if (index !== void 0) {
1834
- idsInViewWithPositions.push({ id, position: positions.get(id) });
2047
+ const position = positions[index];
2048
+ if (position !== void 0) {
2049
+ idsInViewWithPositions.push({ id, position });
2050
+ }
1835
2051
  }
1836
2052
  }
1837
2053
  }
1838
2054
  if (targetId !== void 0 && prevPosition === void 0) {
1839
- prevPosition = positions.get(targetId);
2055
+ const targetIndex = indexByKey.get(targetId);
2056
+ if (targetIndex !== void 0) {
2057
+ prevPosition = positions[targetIndex];
2058
+ }
1840
2059
  }
1841
2060
  return () => {
1842
2061
  let positionDiff = 0;
@@ -1855,7 +2074,13 @@ function prepareMVCP(ctx, dataChanged) {
1855
2074
  }
1856
2075
  }
1857
2076
  }
1858
- const shouldUseFallbackVisibleAnchor = dataChanged && mvcpData && scrollTarget === void 0 && (targetId === void 0 || positions.get(targetId) === void 0 || skipTargetAnchor);
2077
+ const shouldUseFallbackVisibleAnchor = dataChanged && mvcpData && scrollTarget === void 0 && (() => {
2078
+ if (targetId === void 0 || skipTargetAnchor) {
2079
+ return true;
2080
+ }
2081
+ const targetIndex = indexByKey.get(targetId);
2082
+ return targetIndex === void 0 || positions[targetIndex] === void 0;
2083
+ })();
1859
2084
  if (shouldUseFallbackVisibleAnchor) {
1860
2085
  for (let i = 0; i < idsInViewWithPositions.length; i++) {
1861
2086
  const { id, position } = idsInViewWithPositions[i];
@@ -1866,7 +2091,7 @@ function prepareMVCP(ctx, dataChanged) {
1866
2091
  continue;
1867
2092
  }
1868
2093
  }
1869
- const newPosition = positions.get(id);
2094
+ const newPosition = index !== void 0 ? positions[index] : void 0;
1870
2095
  if (newPosition !== void 0) {
1871
2096
  positionDiff = newPosition - position;
1872
2097
  anchorIdForLock = id;
@@ -1876,11 +2101,12 @@ function prepareMVCP(ctx, dataChanged) {
1876
2101
  }
1877
2102
  }
1878
2103
  if (!skipTargetAnchor && targetId !== void 0 && prevPosition !== void 0) {
1879
- const newPosition = positions.get(targetId);
2104
+ const targetIndex = indexByKey.get(targetId);
2105
+ const newPosition = targetIndex !== void 0 ? positions[targetIndex] : void 0;
1880
2106
  if (newPosition !== void 0) {
1881
2107
  const totalSize = getContentSize(ctx);
1882
2108
  let diff = newPosition - prevPosition;
1883
- if (diff !== 0 && state.scroll + state.scrollLength > totalSize) {
2109
+ if (diff !== 0 && isEndAnchoredScrollTarget && state.scroll + state.scrollLength > totalSize) {
1884
2110
  if (diff > 0) {
1885
2111
  diff = Math.max(0, totalSize - state.scroll - state.scrollLength);
1886
2112
  } else {
@@ -1923,17 +2149,15 @@ function prepareColumnStartState(ctx, startIndex, useAverageSize) {
1923
2149
  const state = ctx.state;
1924
2150
  const numColumns = peek$(ctx, "numColumns");
1925
2151
  let rowStartIndex = startIndex;
1926
- const columnAtStart = state.columns.get(state.idCache[startIndex]);
2152
+ const columnAtStart = state.columns[startIndex];
1927
2153
  if (columnAtStart !== 1) {
1928
2154
  rowStartIndex = findRowStartIndex(state, numColumns, startIndex);
1929
2155
  }
1930
2156
  let currentRowTop = 0;
1931
- const curId = state.idCache[rowStartIndex];
1932
- const column = state.columns.get(curId);
2157
+ const column = state.columns[rowStartIndex];
1933
2158
  if (rowStartIndex > 0) {
1934
2159
  const prevIndex = rowStartIndex - 1;
1935
- const prevId = state.idCache[prevIndex];
1936
- const prevPosition = (_a3 = state.positions.get(prevId)) != null ? _a3 : 0;
2160
+ const prevPosition = (_a3 = state.positions[prevIndex]) != null ? _a3 : 0;
1937
2161
  const prevRowStart = findRowStartIndex(state, numColumns, prevIndex);
1938
2162
  const prevRowHeight = calculateRowMaxSize(ctx, prevRowStart, prevIndex, useAverageSize);
1939
2163
  currentRowTop = prevPosition + prevRowHeight;
@@ -1950,7 +2174,7 @@ function findRowStartIndex(state, numColumns, index) {
1950
2174
  }
1951
2175
  let rowStart = Math.max(0, index);
1952
2176
  while (rowStart > 0) {
1953
- const columnForIndex = state.columns.get(state.idCache[rowStart]);
2177
+ const columnForIndex = state.columns[rowStart];
1954
2178
  if (columnForIndex === 1) {
1955
2179
  break;
1956
2180
  }
@@ -1983,7 +2207,7 @@ function calculateRowMaxSize(ctx, startIndex, endIndex, useAverageSize) {
1983
2207
 
1984
2208
  // src/core/updateTotalSize.ts
1985
2209
  function updateTotalSize(ctx) {
1986
- var _a3, _b, _c;
2210
+ var _a3, _b;
1987
2211
  const state = ctx.state;
1988
2212
  const {
1989
2213
  positions,
@@ -1993,36 +2217,34 @@ function updateTotalSize(ctx) {
1993
2217
  if (data.length === 0) {
1994
2218
  addTotalSize(ctx, null, 0);
1995
2219
  } else {
1996
- const lastId = getId(state, data.length - 1);
1997
- if (lastId !== void 0) {
1998
- const lastPosition = positions.get(lastId);
1999
- if (lastPosition !== void 0) {
2000
- if (numColumns > 1) {
2001
- let rowStart = data.length - 1;
2002
- while (rowStart > 0) {
2003
- const rowId = (_b = state.idCache[rowStart]) != null ? _b : getId(state, rowStart);
2004
- const column = state.columns.get(rowId);
2005
- if (column === 1 || column === void 0) {
2006
- break;
2007
- }
2008
- rowStart -= 1;
2009
- }
2010
- let maxSize = 0;
2011
- for (let i = rowStart; i < data.length; i++) {
2012
- const rowId = (_c = state.idCache[i]) != null ? _c : getId(state, i);
2013
- const size = getItemSize(ctx, rowId, i, data[i]);
2014
- if (size > maxSize) {
2015
- maxSize = size;
2016
- }
2220
+ const lastIndex = data.length - 1;
2221
+ const lastId = getId(state, lastIndex);
2222
+ const lastPosition = positions[lastIndex];
2223
+ if (lastId !== void 0 && lastPosition !== void 0) {
2224
+ if (numColumns > 1) {
2225
+ let rowStart = lastIndex;
2226
+ while (rowStart > 0) {
2227
+ const column = state.columns[rowStart];
2228
+ if (column === 1 || column === void 0) {
2229
+ break;
2017
2230
  }
2018
- addTotalSize(ctx, null, lastPosition + maxSize);
2019
- } else {
2020
- const lastSize = getItemSize(ctx, lastId, data.length - 1, data[data.length - 1]);
2021
- if (lastSize !== void 0) {
2022
- const totalSize = lastPosition + lastSize;
2023
- addTotalSize(ctx, null, totalSize);
2231
+ rowStart -= 1;
2232
+ }
2233
+ let maxSize = 0;
2234
+ for (let i = rowStart; i <= lastIndex; i++) {
2235
+ const rowId = (_b = state.idCache[i]) != null ? _b : getId(state, i);
2236
+ const size = getItemSize(ctx, rowId, i, data[i]);
2237
+ if (size > maxSize) {
2238
+ maxSize = size;
2024
2239
  }
2025
2240
  }
2241
+ addTotalSize(ctx, null, lastPosition + maxSize);
2242
+ } else {
2243
+ const lastSize = getItemSize(ctx, lastId, lastIndex, data[lastIndex]);
2244
+ if (lastSize !== void 0) {
2245
+ const totalSize = lastPosition + lastSize;
2246
+ addTotalSize(ctx, null, totalSize);
2247
+ }
2026
2248
  }
2027
2249
  }
2028
2250
  }
@@ -2071,14 +2293,13 @@ var getScrollVelocity = (state) => {
2071
2293
  function updateSnapToOffsets(ctx) {
2072
2294
  const state = ctx.state;
2073
2295
  const {
2074
- positions,
2075
2296
  props: { snapToIndices }
2076
2297
  } = state;
2077
2298
  const snapToOffsets = Array(snapToIndices.length);
2078
2299
  for (let i = 0; i < snapToIndices.length; i++) {
2079
2300
  const idx = snapToIndices[i];
2080
- const key = getId(state, idx);
2081
- snapToOffsets[i] = positions.get(key);
2301
+ getId(state, idx);
2302
+ snapToOffsets[i] = state.positions[idx];
2082
2303
  }
2083
2304
  set$(ctx, "snapToOffsets", snapToOffsets);
2084
2305
  }
@@ -2090,8 +2311,9 @@ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffere
2090
2311
  scrollBottomBuffered: -1,
2091
2312
  startIndex: 0
2092
2313
  }) {
2093
- var _a3, _b, _c, _d, _e, _f;
2314
+ var _a3, _b, _c, _d, _e;
2094
2315
  const state = ctx.state;
2316
+ const hasPositionListeners = ctx.positionListeners.size > 0;
2095
2317
  const {
2096
2318
  columns,
2097
2319
  columnSpans,
@@ -2118,7 +2340,15 @@ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffere
2118
2340
  let column = 1;
2119
2341
  let maxSizeInRow = 0;
2120
2342
  if (dataChanged) {
2121
- columnSpans.clear();
2343
+ columnSpans.length = 0;
2344
+ }
2345
+ if (!hasColumns) {
2346
+ if (columns.length) {
2347
+ columns.length = 0;
2348
+ }
2349
+ if (columnSpans.length) {
2350
+ columnSpans.length = 0;
2351
+ }
2122
2352
  }
2123
2353
  if (startIndex > 0) {
2124
2354
  if (hasColumns) {
@@ -2132,12 +2362,13 @@ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffere
2132
2362
  } else if (startIndex < dataLength) {
2133
2363
  const prevIndex = startIndex - 1;
2134
2364
  const prevId = getId(state, prevIndex);
2135
- const prevPosition = (_c = positions.get(prevId)) != null ? _c : 0;
2365
+ const prevPosition = (_c = positions[prevIndex]) != null ? _c : 0;
2136
2366
  const prevSize = (_d = sizesKnown.get(prevId)) != null ? _d : getItemSize(ctx, prevId, prevIndex, data[prevIndex], useAverageSize, preferCachedSize);
2137
2367
  currentRowTop = prevPosition + prevSize;
2138
2368
  }
2139
2369
  }
2140
2370
  const needsIndexByKey = dataChanged || indexByKey.size === 0;
2371
+ const canOverrideSpan = hasColumns && !!overrideItemLayout && !!layoutConfig;
2141
2372
  let didBreakEarly = false;
2142
2373
  let breakAt;
2143
2374
  for (let i = startIndex; i < dataLength; i++) {
@@ -2151,7 +2382,7 @@ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffere
2151
2382
  }
2152
2383
  const id = (_e = idCache[i]) != null ? _e : getId(state, i);
2153
2384
  let span = 1;
2154
- if (hasColumns && overrideItemLayout && layoutConfig) {
2385
+ if (canOverrideSpan) {
2155
2386
  layoutConfig.span = 1;
2156
2387
  overrideItemLayout(layoutConfig, data[i], i, numColumns, extraData);
2157
2388
  const requestedSpan = layoutConfig.span;
@@ -2164,7 +2395,8 @@ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffere
2164
2395
  column = 1;
2165
2396
  maxSizeInRow = 0;
2166
2397
  }
2167
- const size = (_f = sizesKnown.get(id)) != null ? _f : getItemSize(ctx, id, i, data[i], useAverageSize, preferCachedSize);
2398
+ const knownSize = sizesKnown.get(id);
2399
+ const size = knownSize !== void 0 ? knownSize : getItemSize(ctx, id, i, data[i], useAverageSize, preferCachedSize);
2168
2400
  if (IS_DEV && needsIndexByKey) {
2169
2401
  if (indexByKeyForChecking.has(id)) {
2170
2402
  console.error(
@@ -2173,16 +2405,20 @@ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffere
2173
2405
  }
2174
2406
  indexByKeyForChecking.set(id, i);
2175
2407
  }
2176
- if (currentRowTop !== positions.get(id)) {
2177
- positions.set(id, currentRowTop);
2178
- notifyPosition$(ctx, id, currentRowTop);
2408
+ if (currentRowTop !== positions[i]) {
2409
+ positions[i] = currentRowTop;
2410
+ if (hasPositionListeners) {
2411
+ notifyPosition$(ctx, id, currentRowTop);
2412
+ }
2179
2413
  }
2180
2414
  if (needsIndexByKey) {
2181
2415
  indexByKey.set(id, i);
2182
2416
  }
2183
- columns.set(id, column);
2184
- columnSpans.set(id, span);
2185
- if (hasColumns) {
2417
+ if (!hasColumns) {
2418
+ currentRowTop += size;
2419
+ } else {
2420
+ columns[i] = column;
2421
+ columnSpans[i] = span;
2186
2422
  if (size > maxSizeInRow) {
2187
2423
  maxSizeInRow = size;
2188
2424
  }
@@ -2192,8 +2428,6 @@ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffere
2192
2428
  column = 1;
2193
2429
  maxSizeInRow = 0;
2194
2430
  }
2195
- } else {
2196
- currentRowTop += size;
2197
2431
  }
2198
2432
  }
2199
2433
  if (!didBreakEarly) {
@@ -2345,14 +2579,38 @@ function shallowEqual(prev, next) {
2345
2579
  return true;
2346
2580
  }
2347
2581
  function computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
2348
- const { sizes, positions, scroll: scrollState } = state;
2582
+ const { sizes, scroll: scrollState } = state;
2349
2583
  const topPad = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
2350
2584
  const { itemVisiblePercentThreshold, viewAreaCoveragePercentThreshold } = viewabilityConfig;
2351
2585
  const viewAreaMode = viewAreaCoveragePercentThreshold != null;
2352
2586
  const viewablePercentThreshold = viewAreaMode ? viewAreaCoveragePercentThreshold : itemVisiblePercentThreshold;
2353
2587
  const scroll = scrollState - topPad;
2354
- const top = positions.get(key) - scroll;
2588
+ const position = state.positions[index];
2355
2589
  const size = sizes.get(key) || 0;
2590
+ if (position === void 0) {
2591
+ const value2 = {
2592
+ containerId,
2593
+ index,
2594
+ isViewable: false,
2595
+ item,
2596
+ key,
2597
+ percentOfScroller: 0,
2598
+ percentVisible: 0,
2599
+ scrollSize,
2600
+ size,
2601
+ sizeVisible: -1
2602
+ };
2603
+ const prev2 = ctx.mapViewabilityAmountValues.get(containerId);
2604
+ if (!shallowEqual(prev2, value2)) {
2605
+ ctx.mapViewabilityAmountValues.set(containerId, value2);
2606
+ const cb = ctx.mapViewabilityAmountCallbacks.get(containerId);
2607
+ if (cb) {
2608
+ cb(value2);
2609
+ }
2610
+ }
2611
+ return value2;
2612
+ }
2613
+ const top = position - scroll;
2356
2614
  const bottom = top + size;
2357
2615
  const isEntirelyVisible = top >= 0 && bottom <= scrollSize && bottom > top;
2358
2616
  const sizeVisible = isEntirelyVisible ? size : Math.min(bottom, scrollSize) - Math.max(top, 0);
@@ -2565,37 +2823,24 @@ function scrollToIndex(ctx, { index, viewOffset = 0, animated = true, viewPositi
2565
2823
  // src/utils/setDidLayout.ts
2566
2824
  function setDidLayout(ctx) {
2567
2825
  const state = ctx.state;
2568
- const {
2569
- loadStartTime,
2570
- initialScroll,
2571
- props: { onLoad }
2572
- } = state;
2826
+ const { initialScroll } = state;
2573
2827
  state.queuedInitialLayout = true;
2574
2828
  checkAtBottom(ctx);
2575
- const setIt = () => {
2576
- setInitialRenderState(ctx, { didLayout: true });
2577
- if (onLoad) {
2578
- onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
2579
- }
2580
- };
2581
2829
  if ((initialScroll == null ? void 0 : initialScroll.index) !== void 0) {
2582
2830
  const target = initialScroll;
2583
2831
  const runScroll = () => scrollToIndex(ctx, { ...target, animated: false });
2584
2832
  runScroll();
2585
2833
  requestAnimationFrame(runScroll);
2586
2834
  }
2587
- setIt();
2835
+ setInitialRenderState(ctx, { didLayout: true });
2588
2836
  }
2589
2837
 
2590
2838
  // src/core/calculateItemsInView.ts
2591
2839
  function findCurrentStickyIndex(stickyArray, scroll, state) {
2592
- var _a3;
2593
- const idCache = state.idCache;
2594
2840
  const positions = state.positions;
2595
2841
  for (let i = stickyArray.length - 1; i >= 0; i--) {
2596
2842
  const stickyIndex = stickyArray[i];
2597
- const stickyId = (_a3 = idCache[stickyIndex]) != null ? _a3 : getId(state, stickyIndex);
2598
- const stickyPos = stickyId ? positions.get(stickyId) : void 0;
2843
+ const stickyPos = positions[stickyIndex];
2599
2844
  if (stickyPos !== void 0 && scroll >= stickyPos) {
2600
2845
  return i;
2601
2846
  }
@@ -2625,7 +2870,7 @@ function handleStickyActivation(ctx, stickyHeaderIndices, stickyArray, currentSt
2625
2870
  }
2626
2871
  }
2627
2872
  function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentStickyIdx, pendingRemoval, alwaysRenderIndicesSet) {
2628
- var _a3, _b, _c;
2873
+ var _a3, _b;
2629
2874
  const state = ctx.state;
2630
2875
  for (const containerIndex of state.stickyContainerPool) {
2631
2876
  const itemKey = peek$(ctx, `containerItemKey${containerIndex}`);
@@ -2643,14 +2888,13 @@ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentSt
2643
2888
  const nextIndex = stickyArray[arrayIdx + 1];
2644
2889
  let shouldRecycle = false;
2645
2890
  if (nextIndex) {
2646
- const nextId = (_a3 = state.idCache[nextIndex]) != null ? _a3 : getId(state, nextIndex);
2647
- const nextPos = nextId ? state.positions.get(nextId) : void 0;
2891
+ const nextPos = state.positions[nextIndex];
2648
2892
  shouldRecycle = nextPos !== void 0 && scroll > nextPos + drawDistance * 2;
2649
2893
  } else {
2650
- const currentId = (_b = state.idCache[itemIndex]) != null ? _b : getId(state, itemIndex);
2894
+ const currentId = (_a3 = state.idCache[itemIndex]) != null ? _a3 : getId(state, itemIndex);
2651
2895
  if (currentId) {
2652
- const currentPos = state.positions.get(currentId);
2653
- const currentSize = (_c = state.sizes.get(currentId)) != null ? _c : getItemSize(ctx, currentId, itemIndex, state.props.data[itemIndex]);
2896
+ const currentPos = state.positions[itemIndex];
2897
+ const currentSize = (_b = state.sizes.get(currentId)) != null ? _b : getItemSize(ctx, currentId, itemIndex, state.props.data[itemIndex]);
2654
2898
  shouldRecycle = currentPos !== void 0 && scroll > currentPos + currentSize + drawDistance * 3;
2655
2899
  }
2656
2900
  }
@@ -2662,7 +2906,7 @@ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentSt
2662
2906
  function calculateItemsInView(ctx, params = {}) {
2663
2907
  const state = ctx.state;
2664
2908
  unstable_batchedUpdates(() => {
2665
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
2909
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
2666
2910
  const {
2667
2911
  columns,
2668
2912
  columnSpans,
@@ -2751,7 +2995,9 @@ function calculateItemsInView(ctx, params = {}) {
2751
2995
  if (dataChanged) {
2752
2996
  indexByKey.clear();
2753
2997
  idCache.length = 0;
2754
- positions.clear();
2998
+ positions.length = 0;
2999
+ columns.length = 0;
3000
+ columnSpans.length = 0;
2755
3001
  }
2756
3002
  const startIndex = forceFullItemPositions || dataChanged ? 0 : (_b = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _b : 0;
2757
3003
  updateItemPositions(ctx, dataChanged, {
@@ -2772,7 +3018,7 @@ function calculateItemsInView(ctx, params = {}) {
2772
3018
  let loopStart = !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
2773
3019
  for (let i = loopStart; i >= 0; i--) {
2774
3020
  const id = (_c = idCache[i]) != null ? _c : getId(state, i);
2775
- const top = positions.get(id);
3021
+ const top = positions[i];
2776
3022
  const size = (_d = sizes.get(id)) != null ? _d : getItemSize(ctx, id, i, data[i]);
2777
3023
  const bottom = top + size;
2778
3024
  if (bottom > scroll - scrollBufferTop) {
@@ -2783,8 +3029,7 @@ function calculateItemsInView(ctx, params = {}) {
2783
3029
  }
2784
3030
  if (numColumns > 1) {
2785
3031
  while (loopStart > 0) {
2786
- const loopId = (_e = idCache[loopStart]) != null ? _e : getId(state, loopStart);
2787
- const loopColumn = columns.get(loopId);
3032
+ const loopColumn = columns[loopStart];
2788
3033
  if (loopColumn === 1 || loopColumn === void 0) {
2789
3034
  break;
2790
3035
  }
@@ -2805,9 +3050,9 @@ function calculateItemsInView(ctx, params = {}) {
2805
3050
  let firstFullyOnScreenIndex;
2806
3051
  const dataLength = data.length;
2807
3052
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
2808
- const id = (_f = idCache[i]) != null ? _f : getId(state, i);
2809
- const size = (_g = sizes.get(id)) != null ? _g : getItemSize(ctx, id, i, data[i]);
2810
- const top = positions.get(id);
3053
+ const id = (_e = idCache[i]) != null ? _e : getId(state, i);
3054
+ const size = (_f = sizes.get(id)) != null ? _f : getItemSize(ctx, id, i, data[i]);
3055
+ const top = positions[i];
2811
3056
  if (!foundEnd) {
2812
3057
  if (startNoBuffer === null && top + size > scroll) {
2813
3058
  startNoBuffer = i;
@@ -2843,7 +3088,7 @@ function calculateItemsInView(ctx, params = {}) {
2843
3088
  }
2844
3089
  const idsInView = [];
2845
3090
  for (let i = firstFullyOnScreenIndex; i <= endNoBuffer; i++) {
2846
- const id = (_h = idCache[i]) != null ? _h : getId(state, i);
3091
+ const id = (_g = idCache[i]) != null ? _g : getId(state, i);
2847
3092
  idsInView.push(id);
2848
3093
  }
2849
3094
  Object.assign(state, {
@@ -2875,7 +3120,7 @@ function calculateItemsInView(ctx, params = {}) {
2875
3120
  const needNewContainers = [];
2876
3121
  const needNewContainersSet = /* @__PURE__ */ new Set();
2877
3122
  for (let i = startBuffered; i <= endBuffered; i++) {
2878
- const id = (_i = idCache[i]) != null ? _i : getId(state, i);
3123
+ const id = (_h = idCache[i]) != null ? _h : getId(state, i);
2879
3124
  if (!containerItemKeys.has(id)) {
2880
3125
  needNewContainersSet.add(i);
2881
3126
  needNewContainers.push(i);
@@ -2884,7 +3129,7 @@ function calculateItemsInView(ctx, params = {}) {
2884
3129
  if (alwaysRenderArr.length > 0) {
2885
3130
  for (const index of alwaysRenderArr) {
2886
3131
  if (index < 0 || index >= dataLength) continue;
2887
- const id = (_j = idCache[index]) != null ? _j : getId(state, index);
3132
+ const id = (_i = idCache[index]) != null ? _i : getId(state, index);
2888
3133
  if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
2889
3134
  needNewContainersSet.add(index);
2890
3135
  needNewContainers.push(index);
@@ -2922,7 +3167,7 @@ function calculateItemsInView(ctx, params = {}) {
2922
3167
  for (let idx = 0; idx < needNewContainers.length; idx++) {
2923
3168
  const i = needNewContainers[idx];
2924
3169
  const containerIndex = availableContainers[idx];
2925
- const id = (_k = idCache[i]) != null ? _k : getId(state, i);
3170
+ const id = (_j = idCache[i]) != null ? _j : getId(state, i);
2926
3171
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
2927
3172
  if (oldKey && oldKey !== id) {
2928
3173
  containerItemKeys.delete(oldKey);
@@ -2963,7 +3208,7 @@ function calculateItemsInView(ctx, params = {}) {
2963
3208
  if (alwaysRenderArr.length > 0) {
2964
3209
  for (const index of alwaysRenderArr) {
2965
3210
  if (index < 0 || index >= dataLength) continue;
2966
- const id = (_l = idCache[index]) != null ? _l : getId(state, index);
3211
+ const id = (_k = idCache[index]) != null ? _k : getId(state, index);
2967
3212
  const containerIndex = containerItemKeys.get(id);
2968
3213
  if (containerIndex !== void 0) {
2969
3214
  state.stickyContainerPool.add(containerIndex);
@@ -3003,14 +3248,13 @@ function calculateItemsInView(ctx, params = {}) {
3003
3248
  const itemIndex = indexByKey.get(itemKey);
3004
3249
  const item = data[itemIndex];
3005
3250
  if (item !== void 0) {
3006
- const id = (_m = idCache[itemIndex]) != null ? _m : getId(state, itemIndex);
3007
- const positionValue = positions.get(id);
3251
+ const positionValue = positions[itemIndex];
3008
3252
  if (positionValue === void 0) {
3009
3253
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
3010
3254
  } else {
3011
3255
  const position = (positionValue || 0) - scrollAdjustPending;
3012
- const column = columns.get(id) || 1;
3013
- const span = columnSpans.get(id) || 1;
3256
+ const column = columns[itemIndex] || 1;
3257
+ const span = columnSpans[itemIndex] || 1;
3014
3258
  const prevPos = peek$(ctx, `containerPosition${i}`);
3015
3259
  const prevColumn = peek$(ctx, `containerColumn${i}`);
3016
3260
  const prevSpan = peek$(ctx, `containerSpan${i}`);
@@ -3083,8 +3327,12 @@ function checkFinishedScrollFrame(ctx) {
3083
3327
  state.animFrameCheckFinishedScroll = void 0;
3084
3328
  const scroll = state.scrollPending;
3085
3329
  const adjust = state.scrollAdjustHandler.getAdjust();
3086
- const clampedTargetOffset = clampScrollOffset(ctx, scrollingTo.offset - (scrollingTo.viewOffset || 0));
3087
- const maxOffset = clampScrollOffset(ctx, scroll);
3330
+ const clampedTargetOffset = clampScrollOffset(
3331
+ ctx,
3332
+ scrollingTo.offset - (scrollingTo.viewOffset || 0),
3333
+ scrollingTo
3334
+ );
3335
+ const maxOffset = clampScrollOffset(ctx, scroll, scrollingTo);
3088
3336
  const diff1 = Math.abs(scroll - clampedTargetOffset);
3089
3337
  const diff2 = Math.abs(diff1 - adjust);
3090
3338
  const isNotOverscrolled = Math.abs(scroll - maxOffset) < 1;
@@ -3347,7 +3595,7 @@ function onScroll(ctx, event) {
3347
3595
  }
3348
3596
  let newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
3349
3597
  if (state.scrollingTo && state.scrollingTo.offset >= newScroll) {
3350
- const maxOffset = clampScrollOffset(ctx, newScroll);
3598
+ const maxOffset = clampScrollOffset(ctx, newScroll, state.scrollingTo);
3351
3599
  if (newScroll !== maxOffset && Math.abs(newScroll - maxOffset) > 1) {
3352
3600
  newScroll = maxOffset;
3353
3601
  scrollTo(ctx, {
@@ -3400,7 +3648,7 @@ var ScrollAdjustHandler = class {
3400
3648
  if ((scrollTarget == null ? void 0 : scrollTarget.index) !== void 0) {
3401
3649
  const currentOffset = calculateOffsetForIndex(this.ctx, scrollTarget.index);
3402
3650
  targetScroll = calculateOffsetWithOffsetPosition(this.ctx, currentOffset, scrollTarget);
3403
- targetScroll = clampScrollOffset(this.ctx, targetScroll);
3651
+ targetScroll = clampScrollOffset(this.ctx, targetScroll, scrollTarget);
3404
3652
  } else {
3405
3653
  targetScroll = clampScrollOffset(this.ctx, state.scroll + pending);
3406
3654
  }
@@ -3619,6 +3867,18 @@ function createColumnWrapperStyle(contentContainerStyle) {
3619
3867
  // src/utils/createImperativeHandle.ts
3620
3868
  function createImperativeHandle(ctx) {
3621
3869
  const state = ctx.state;
3870
+ const runScrollWithPromise = (run) => new Promise((resolve) => {
3871
+ var _a3;
3872
+ (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
3873
+ state.pendingScrollResolve = resolve;
3874
+ const didStartScroll = run();
3875
+ if (!didStartScroll || !state.scrollingTo) {
3876
+ if (state.pendingScrollResolve === resolve) {
3877
+ state.pendingScrollResolve = void 0;
3878
+ }
3879
+ resolve();
3880
+ }
3881
+ });
3622
3882
  const scrollIndexIntoView = (options) => {
3623
3883
  if (state) {
3624
3884
  const { index, ...rest } = options;
@@ -3630,11 +3890,36 @@ function createImperativeHandle(ctx) {
3630
3890
  index,
3631
3891
  viewPosition
3632
3892
  });
3893
+ return true;
3633
3894
  }
3634
3895
  }
3896
+ return false;
3635
3897
  };
3636
3898
  const refScroller = state.refScroller;
3899
+ const clearCaches = (options) => {
3900
+ var _a3, _b;
3901
+ const mode = (_a3 = options == null ? void 0 : options.mode) != null ? _a3 : "sizes";
3902
+ state.sizes.clear();
3903
+ state.sizesKnown.clear();
3904
+ for (const key in state.averageSizes) {
3905
+ delete state.averageSizes[key];
3906
+ }
3907
+ state.minIndexSizeChanged = 0;
3908
+ state.scrollForNextCalculateItemsInView = void 0;
3909
+ state.pendingTotalSize = void 0;
3910
+ state.totalSize = 0;
3911
+ set$(ctx, "totalSize", 0);
3912
+ if (mode === "full") {
3913
+ state.indexByKey.clear();
3914
+ state.idCache.length = 0;
3915
+ state.positions.length = 0;
3916
+ state.columns.length = 0;
3917
+ state.columnSpans.length = 0;
3918
+ }
3919
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
3920
+ };
3637
3921
  return {
3922
+ clearCaches,
3638
3923
  flashScrollIndicators: () => refScroller.current.flashScrollIndicators(),
3639
3924
  getNativeScrollRef: () => refScroller.current,
3640
3925
  getScrollableNode: () => refScroller.current.getScrollableNode(),
@@ -3653,8 +3938,11 @@ function createImperativeHandle(ctx) {
3653
3938
  isAtStart: state.isAtStart,
3654
3939
  listen: (signalName, cb) => listen$(ctx, signalName, cb),
3655
3940
  listenToPosition: (key, cb) => listenPosition$(ctx, key, cb),
3656
- positionAtIndex: (index) => state.positions.get(getId(state, index)),
3657
- positions: state.positions,
3941
+ positionAtIndex: (index) => state.positions[index],
3942
+ positionByKey: (key) => {
3943
+ const index = state.indexByKey.get(key);
3944
+ return index === void 0 ? void 0 : state.positions[index];
3945
+ },
3658
3946
  scroll: state.scroll,
3659
3947
  scrollLength: state.scrollLength,
3660
3948
  scrollVelocity: getScrollVelocity(state),
@@ -3667,15 +3955,17 @@ function createImperativeHandle(ctx) {
3667
3955
  state.contentInsetOverride = inset != null ? inset : void 0;
3668
3956
  updateScroll(ctx, state.scroll, true);
3669
3957
  },
3670
- scrollIndexIntoView,
3671
- scrollItemIntoView: ({ item, ...props }) => {
3958
+ scrollIndexIntoView: (options) => runScrollWithPromise(() => scrollIndexIntoView(options)),
3959
+ scrollItemIntoView: ({ item, ...props }) => runScrollWithPromise(() => {
3672
3960
  const data = state.props.data;
3673
3961
  const index = data.indexOf(item);
3674
3962
  if (index !== -1) {
3675
3963
  scrollIndexIntoView({ index, ...props });
3964
+ return true;
3676
3965
  }
3677
- },
3678
- scrollToEnd: (options) => {
3966
+ return false;
3967
+ }),
3968
+ scrollToEnd: (options) => runScrollWithPromise(() => {
3679
3969
  const data = state.props.data;
3680
3970
  const stylePaddingBottom = state.props.stylePaddingBottom;
3681
3971
  const index = data.length - 1;
@@ -3688,17 +3978,27 @@ function createImperativeHandle(ctx) {
3688
3978
  viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
3689
3979
  viewPosition: 1
3690
3980
  });
3981
+ return true;
3691
3982
  }
3692
- },
3693
- scrollToIndex: (params) => scrollToIndex(ctx, params),
3694
- scrollToItem: ({ item, ...props }) => {
3983
+ return false;
3984
+ }),
3985
+ scrollToIndex: (params) => runScrollWithPromise(() => {
3986
+ scrollToIndex(ctx, params);
3987
+ return true;
3988
+ }),
3989
+ scrollToItem: ({ item, ...props }) => runScrollWithPromise(() => {
3695
3990
  const data = state.props.data;
3696
3991
  const index = data.indexOf(item);
3697
3992
  if (index !== -1) {
3698
3993
  scrollToIndex(ctx, { index, ...props });
3994
+ return true;
3699
3995
  }
3700
- },
3701
- scrollToOffset: (params) => scrollTo(ctx, params),
3996
+ return false;
3997
+ }),
3998
+ scrollToOffset: (params) => runScrollWithPromise(() => {
3999
+ scrollTo(ctx, params);
4000
+ return true;
4001
+ }),
3702
4002
  setScrollProcessingEnabled: (enabled) => {
3703
4003
  state.scrollProcessingEnabled = enabled;
3704
4004
  },
@@ -3795,8 +4095,8 @@ function normalizeMaintainVisibleContentPosition(value) {
3795
4095
  if (value && typeof value === "object") {
3796
4096
  return {
3797
4097
  data: (_a3 = value.data) != null ? _a3 : false,
3798
- size: (_b = value.size) != null ? _b : true,
3799
- shouldRestorePosition: value.shouldRestorePosition
4098
+ shouldRestorePosition: value.shouldRestorePosition,
4099
+ size: (_b = value.size) != null ? _b : true
3800
4100
  };
3801
4101
  }
3802
4102
  if (value === false) {
@@ -3935,6 +4235,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3935
4235
  refreshControl,
3936
4236
  refreshing,
3937
4237
  refScrollView,
4238
+ renderScrollComponent,
3938
4239
  renderItem,
3939
4240
  scrollEventThrottle,
3940
4241
  snapToIndices,
@@ -3943,13 +4244,21 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3943
4244
  // TODOV3: Remove from v3 release
3944
4245
  style: styleProp,
3945
4246
  suggestEstimatedItemSize,
4247
+ useWindowScroll = false,
3946
4248
  viewabilityConfig,
3947
4249
  viewabilityConfigCallbackPairs,
3948
4250
  waitForInitialLayout = true,
3949
4251
  ...rest
3950
4252
  } = props;
3951
4253
  const animatedPropsInternal = props.animatedPropsInternal;
3952
- const { childrenMode } = rest;
4254
+ const positionComponentInternal = props.positionComponentInternal;
4255
+ const stickyPositionComponentInternal = props.stickyPositionComponentInternal;
4256
+ const {
4257
+ childrenMode,
4258
+ positionComponentInternal: _positionComponentInternal,
4259
+ stickyPositionComponentInternal: _stickyPositionComponentInternal,
4260
+ ...restProps
4261
+ } = rest;
3953
4262
  const contentContainerStyleBase = StyleSheet.flatten(contentContainerStyleProp);
3954
4263
  const shouldFlexGrow = alignItemsAtEnd && (horizontal ? (contentContainerStyleBase == null ? void 0 : contentContainerStyleBase.minWidth) == null : (contentContainerStyleBase == null ? void 0 : contentContainerStyleBase.minHeight) == null);
3955
4264
  const contentContainerStyle = {
@@ -4001,6 +4310,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4001
4310
  "stickyIndices has been renamed to stickyHeaderIndices. Please update your props to use stickyHeaderIndices."
4002
4311
  );
4003
4312
  }
4313
+ if (IS_DEV && useWindowScroll && renderScrollComponent) {
4314
+ warnDevOnce(
4315
+ "useWindowScrollRenderScrollComponent",
4316
+ "useWindowScroll is not supported when renderScrollComponent is provided."
4317
+ );
4318
+ }
4319
+ const useWindowScrollResolved = !!useWindowScroll && !renderScrollComponent;
4004
4320
  const refState = useRef();
4005
4321
  const hasOverrideItemLayout = !!overrideItemLayout;
4006
4322
  const prevHasOverrideItemLayout = useRef(hasOverrideItemLayout);
@@ -4010,11 +4326,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4010
4326
  ctx.state = {
4011
4327
  activeStickyIndex: -1,
4012
4328
  averageSizes: {},
4013
- columnSpans: /* @__PURE__ */ new Map(),
4014
- columns: /* @__PURE__ */ new Map(),
4329
+ columnSpans: [],
4330
+ columns: [],
4015
4331
  containerItemKeys: /* @__PURE__ */ new Map(),
4016
4332
  containerItemTypes: /* @__PURE__ */ new Map(),
4017
4333
  contentInsetOverride: void 0,
4334
+ dataChangeEpoch: 0,
4018
4335
  dataChangeNeedsScrollUpdate: false,
4019
4336
  didColumnsChange: false,
4020
4337
  didDataChange: false,
@@ -4046,10 +4363,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4046
4363
  minIndexSizeChanged: 0,
4047
4364
  nativeContentInset: void 0,
4048
4365
  nativeMarginTop: 0,
4049
- positions: /* @__PURE__ */ new Map(),
4366
+ positions: [],
4050
4367
  props: {},
4051
4368
  queuedCalculateItemsInView: 0,
4052
- refScroller: void 0,
4369
+ refScroller: { current: null },
4053
4370
  scroll: 0,
4054
4371
  scrollAdjustHandler: new ScrollAdjustHandler(ctx),
4055
4372
  scrollForNextCalculateItemsInView: void 0,
@@ -4065,6 +4382,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4065
4382
  startBuffered: -1,
4066
4383
  startNoBuffer: -1,
4067
4384
  startReachedSnapshot: void 0,
4385
+ startReachedSnapshotDataChangeEpoch: void 0,
4068
4386
  stickyContainerPool: /* @__PURE__ */ new Set(),
4069
4387
  stickyContainers: /* @__PURE__ */ new Map(),
4070
4388
  timeoutSizeMessage: 0,
@@ -4084,6 +4402,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4084
4402
  state.didColumnsChange = numColumnsProp !== state.props.numColumns;
4085
4403
  const didDataChangeLocal = state.props.dataVersion !== dataVersion || state.props.data !== dataProp && checkActualChange(state, dataProp, state.props.data);
4086
4404
  if (didDataChangeLocal) {
4405
+ state.dataChangeEpoch += 1;
4087
4406
  state.dataChangeNeedsScrollUpdate = true;
4088
4407
  state.didDataChange = true;
4089
4408
  state.previousData = state.props.data;
@@ -4120,14 +4439,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4120
4439
  onStartReachedThreshold,
4121
4440
  onStickyHeaderChange,
4122
4441
  overrideItemLayout,
4442
+ positionComponentInternal,
4123
4443
  recycleItems: !!recycleItems,
4124
4444
  renderItem,
4125
4445
  snapToIndices,
4126
4446
  stickyIndicesArr: stickyHeaderIndices != null ? stickyHeaderIndices : [],
4127
4447
  stickyIndicesSet: useMemo(() => new Set(stickyHeaderIndices != null ? stickyHeaderIndices : []), [stickyHeaderIndices == null ? void 0 : stickyHeaderIndices.join(",")]),
4448
+ stickyPositionComponentInternal,
4128
4449
  stylePaddingBottom: stylePaddingBottomState,
4129
4450
  stylePaddingTop: stylePaddingTopState,
4130
- suggestEstimatedItemSize: !!suggestEstimatedItemSize
4451
+ suggestEstimatedItemSize: !!suggestEstimatedItemSize,
4452
+ useWindowScroll: useWindowScrollResolved
4131
4453
  };
4132
4454
  state.refScroller = refScroller;
4133
4455
  const memoizedLastItemKeys = useMemo(() => {
@@ -4163,7 +4485,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4163
4485
  } else {
4164
4486
  const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, initialScroll.index) : 0;
4165
4487
  const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, initialScroll);
4166
- const clampedOffset = clampScrollOffset(ctx, resolvedOffset);
4488
+ const clampedOffset = clampScrollOffset(ctx, resolvedOffset, initialScroll);
4167
4489
  const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
4168
4490
  refState.current.initialScroll = updatedInitialScroll;
4169
4491
  state.initialScroll = updatedInitialScroll;
@@ -4186,7 +4508,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4186
4508
  "Changing data without a keyExtractor can cause slow performance and resetting scroll. If your list data can change you should use a keyExtractor with a unique id for best performance and behavior."
4187
4509
  );
4188
4510
  refState.current.sizes.clear();
4189
- refState.current.positions.clear();
4511
+ refState.current.positions.length = 0;
4190
4512
  refState.current.totalSize = 0;
4191
4513
  set$(ctx, "totalSize", 0);
4192
4514
  }
@@ -4312,7 +4634,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4312
4634
  return /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(
4313
4635
  ListComponent,
4314
4636
  {
4315
- ...rest,
4637
+ ...restProps,
4316
4638
  alignItemsAtEnd,
4317
4639
  canRender,
4318
4640
  contentContainerStyle,
@@ -4338,15 +4660,25 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4338
4660
  }
4339
4661
  ),
4340
4662
  refScrollView: combinedRef,
4663
+ renderScrollComponent,
4341
4664
  scrollAdjustHandler: (_d = refState.current) == null ? void 0 : _d.scrollAdjustHandler,
4342
4665
  scrollEventThrottle: 0,
4343
4666
  snapToIndices,
4344
4667
  stickyHeaderIndices,
4345
4668
  style,
4346
4669
  updateItemSize: fns.updateItemSize,
4670
+ useWindowScroll: useWindowScrollResolved,
4347
4671
  waitForInitialLayout
4348
4672
  }
4349
4673
  ), IS_DEV && ENABLE_DEBUG_VIEW);
4350
4674
  });
4351
4675
 
4352
- export { LegendList, typedForwardRef, typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };
4676
+ // src/index.ts
4677
+ var LegendList3 = LegendList;
4678
+ if (IS_DEV) {
4679
+ console.warn(
4680
+ "[legend-list] Legend List 3.0 deprecates the root import (@legendapp/list) because it now supports both react and react-native. The root import is fully functional, but please switch to platform-specific imports for strict platform types:\n - React Native: @legendapp/list/react-native\n - React: @legendapp/list/react\nSee README for details."
4681
+ );
4682
+ }
4683
+
4684
+ export { LegendList3 as LegendList, typedForwardRef, typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };