@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.js CHANGED
@@ -245,7 +245,7 @@ var _a;
245
245
  var envMode = typeof process !== "undefined" && typeof process.env === "object" && process.env ? (_a = process.env.NODE_ENV) != null ? _a : process.env.MODE : void 0;
246
246
  var processDev = typeof envMode === "string" ? envMode.toLowerCase() !== "production" : void 0;
247
247
  var _a2;
248
- var IS_DEV = (_a2 = metroDev != null ? metroDev : processDev) != null ? _a2 : false;
248
+ var IS_DEV = (_a2 = processDev != null ? processDev : metroDev) != null ? _a2 : false;
249
249
 
250
250
  // src/constants.ts
251
251
  var POSITION_OUT_OF_VIEW = -1e7;
@@ -314,8 +314,15 @@ var PositionViewState = typedMemo(function PositionViewState2({
314
314
  };
315
315
  const composed = isArray(style) ? Object.assign({}, ...style) : style;
316
316
  const combinedStyle = horizontal ? { ...base, ...composed, left: position } : { ...base, ...composed, top: position };
317
- const { animatedScrollY, onLayout, index, ...webProps } = props;
318
- return /* @__PURE__ */ React3__namespace.createElement("div", { ref: refView, ...webProps, style: combinedStyle });
317
+ const {
318
+ animatedScrollY: _animatedScrollY,
319
+ index,
320
+ onLayout: _onLayout,
321
+ onLayoutChange: _onLayoutChange,
322
+ stickyHeaderConfig: _stickyHeaderConfig,
323
+ ...webProps
324
+ } = props;
325
+ return /* @__PURE__ */ React3__namespace.createElement("div", { "data-index": index, ref: refView, ...webProps, style: combinedStyle });
319
326
  });
320
327
  var PositionViewSticky = typedMemo(function PositionViewSticky2({
321
328
  id,
@@ -325,10 +332,15 @@ var PositionViewSticky = typedMemo(function PositionViewSticky2({
325
332
  index,
326
333
  animatedScrollY: _animatedScrollY,
327
334
  stickyHeaderConfig,
335
+ onLayout: _onLayout,
336
+ onLayoutChange: _onLayoutChange,
328
337
  children,
329
- ...rest
338
+ ...webProps
330
339
  }) {
331
- const [position = POSITION_OUT_OF_VIEW, activeStickyIndex] = useArr$([`containerPosition${id}`, "activeStickyIndex"]);
340
+ const [position = POSITION_OUT_OF_VIEW, activeStickyIndex] = useArr$([
341
+ `containerPosition${id}`,
342
+ "activeStickyIndex"
343
+ ]);
332
344
  const base = {
333
345
  contain: "paint layout style"
334
346
  };
@@ -355,7 +367,7 @@ var PositionViewSticky = typedMemo(function PositionViewSticky2({
355
367
  }
356
368
  return styleBase;
357
369
  }, [composed, horizontal, position, index, activeStickyIndex, stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset]);
358
- return /* @__PURE__ */ React3__namespace.createElement("div", { ref: refView, style: viewStyle, ...rest }, children);
370
+ return /* @__PURE__ */ React3__namespace.createElement("div", { "data-index": index, ref: refView, style: viewStyle, ...webProps }, children);
359
371
  });
360
372
  var PositionView = PositionViewState;
361
373
  function useInit(cb) {
@@ -642,6 +654,8 @@ var Container = typedMemo(function Container2({
642
654
  }) {
643
655
  const ctx = useStateContext();
644
656
  const { columnWrapperStyle, animatedScrollY } = ctx;
657
+ const positionComponentInternal = ctx.state.props.positionComponentInternal;
658
+ const stickyPositionComponentInternal = ctx.state.props.stickyPositionComponentInternal;
645
659
  const [column = 0, span = 1, data, itemKey, numColumns = 1, extraData, isSticky] = useArr$([
646
660
  `containerColumn${id}`,
647
661
  `containerSpan${id}`,
@@ -767,7 +781,7 @@ var Container = typedMemo(function Container2({
767
781
  },
768
782
  [itemKey, layoutRenderCount]
769
783
  );
770
- const PositionComponent = isSticky ? PositionViewSticky : PositionView;
784
+ const PositionComponent = isSticky ? stickyPositionComponentInternal ? stickyPositionComponentInternal : PositionViewSticky : positionComponentInternal ? positionComponentInternal : PositionView;
771
785
  return /* @__PURE__ */ React3__namespace.createElement(
772
786
  PositionComponent,
773
787
  {
@@ -787,8 +801,12 @@ var Container = typedMemo(function Container2({
787
801
 
788
802
  // src/utils/reordering.ts
789
803
  var mapFn = (element) => {
790
- const indexStr = element.getAttribute("index");
791
- return [element, indexStr === null ? null : parseInt(indexStr)];
804
+ const indexStr = element.getAttribute("data-index");
805
+ if (indexStr === null) {
806
+ return [element, null];
807
+ }
808
+ const index = Number.parseInt(indexStr, 10);
809
+ return [element, Number.isNaN(index) ? null : index];
792
810
  };
793
811
  function sortDOMElements(container) {
794
812
  const elements = Array.from(container.children);
@@ -968,6 +986,89 @@ var StyleSheet = {
968
986
  flatten: (style) => flattenStyles(style)
969
987
  };
970
988
 
989
+ // src/components/webScrollUtils.ts
990
+ function getDocumentScrollerNode() {
991
+ if (typeof document === "undefined") {
992
+ return null;
993
+ }
994
+ return document.scrollingElement || document.documentElement || document.body;
995
+ }
996
+ function getWindowScrollPosition() {
997
+ var _a3, _b, _c, _d;
998
+ if (typeof window === "undefined") {
999
+ return { x: 0, y: 0 };
1000
+ }
1001
+ return {
1002
+ x: (_b = (_a3 = window.scrollX) != null ? _a3 : window.pageXOffset) != null ? _b : 0,
1003
+ y: (_d = (_c = window.scrollY) != null ? _c : window.pageYOffset) != null ? _d : 0
1004
+ };
1005
+ }
1006
+ function getElementDocumentPosition(element, scroll) {
1007
+ var _a3, _b;
1008
+ const rect = element == null ? void 0 : element.getBoundingClientRect();
1009
+ return {
1010
+ left: ((_a3 = rect == null ? void 0 : rect.left) != null ? _a3 : 0) + scroll.x,
1011
+ top: ((_b = rect == null ? void 0 : rect.top) != null ? _b : 0) + scroll.y
1012
+ };
1013
+ }
1014
+ function getContentSize2(content) {
1015
+ var _a3, _b;
1016
+ return {
1017
+ height: (_a3 = content == null ? void 0 : content.scrollHeight) != null ? _a3 : 0,
1018
+ width: (_b = content == null ? void 0 : content.scrollWidth) != null ? _b : 0
1019
+ };
1020
+ }
1021
+ function getScrollContentSize(scrollElement, contentElement, isWindowScroll) {
1022
+ return getContentSize2(isWindowScroll ? contentElement : scrollElement);
1023
+ }
1024
+ function getLayoutMeasurement(scrollElement, isWindowScroll, horizontal) {
1025
+ var _a3, _b, _c, _d, _e, _f;
1026
+ if (isWindowScroll && typeof window !== "undefined") {
1027
+ const rect = scrollElement == null ? void 0 : scrollElement.getBoundingClientRect();
1028
+ return {
1029
+ // In window-scroll mode, use viewport size on the scroll axis.
1030
+ height: horizontal ? (_b = (_a3 = rect == null ? void 0 : rect.height) != null ? _a3 : scrollElement == null ? void 0 : scrollElement.clientHeight) != null ? _b : window.innerHeight : window.innerHeight,
1031
+ // Keep the cross-axis size list-relative to avoid inflating container measurements.
1032
+ width: horizontal ? window.innerWidth : (_d = (_c = rect == null ? void 0 : rect.width) != null ? _c : scrollElement == null ? void 0 : scrollElement.clientWidth) != null ? _d : window.innerWidth
1033
+ };
1034
+ }
1035
+ return {
1036
+ height: (_e = scrollElement == null ? void 0 : scrollElement.clientHeight) != null ? _e : 0,
1037
+ width: (_f = scrollElement == null ? void 0 : scrollElement.clientWidth) != null ? _f : 0
1038
+ };
1039
+ }
1040
+ function clampOffset(offset, maxOffset) {
1041
+ return Math.max(0, Math.min(offset, maxOffset));
1042
+ }
1043
+ function getAxisSize(size, horizontal) {
1044
+ return horizontal ? size.width : size.height;
1045
+ }
1046
+ function getMaxOffset(contentSize, layoutMeasurement, horizontal) {
1047
+ return Math.max(0, getAxisSize(contentSize, horizontal) - getAxisSize(layoutMeasurement, horizontal));
1048
+ }
1049
+ function resolveScrollableNode(scrollElement, isWindowScroll) {
1050
+ return isWindowScroll ? getDocumentScrollerNode() || scrollElement : scrollElement;
1051
+ }
1052
+ function resolveScrollEventTarget(scrollElement, isWindowScroll) {
1053
+ return isWindowScroll && typeof window !== "undefined" ? window : scrollElement;
1054
+ }
1055
+ function getLayoutRectangle(element, isWindowScroll, horizontal) {
1056
+ const rect = element.getBoundingClientRect();
1057
+ const scroll = getWindowScrollPosition();
1058
+ return {
1059
+ height: isWindowScroll && typeof window !== "undefined" && !horizontal ? window.innerHeight : rect.height,
1060
+ width: isWindowScroll && typeof window !== "undefined" && horizontal ? window.innerWidth : rect.width,
1061
+ x: isWindowScroll ? rect.left + scroll.x : rect.left,
1062
+ y: isWindowScroll ? rect.top + scroll.y : rect.top
1063
+ };
1064
+ }
1065
+ function resolveWindowScrollTarget({ clampedOffset, horizontal, listPos, scroll }) {
1066
+ return {
1067
+ left: horizontal ? listPos.left + clampedOffset : scroll.x,
1068
+ top: horizontal ? scroll.y : listPos.top + clampedOffset
1069
+ };
1070
+ }
1071
+
971
1072
  // src/components/ListComponentScrollView.tsx
972
1073
  var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView2({
973
1074
  children,
@@ -977,114 +1078,163 @@ var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView
977
1078
  contentOffset,
978
1079
  maintainVisibleContentPosition,
979
1080
  onScroll: onScroll2,
980
- onMomentumScrollEnd,
1081
+ onMomentumScrollEnd: _onMomentumScrollEnd,
981
1082
  showsHorizontalScrollIndicator = true,
982
1083
  showsVerticalScrollIndicator = true,
983
1084
  refreshControl,
1085
+ useWindowScroll = false,
984
1086
  onLayout,
985
1087
  ...props
986
1088
  }, ref) {
987
1089
  const scrollRef = React3.useRef(null);
988
1090
  const contentRef = React3.useRef(null);
1091
+ const isWindowScroll = useWindowScroll;
1092
+ const getScrollTarget = React3.useCallback(
1093
+ () => resolveScrollEventTarget(scrollRef.current, isWindowScroll),
1094
+ [isWindowScroll]
1095
+ );
1096
+ const getMaxScrollOffset = React3.useCallback(() => {
1097
+ const scrollElement = scrollRef.current;
1098
+ const contentSize = getScrollContentSize(scrollElement, contentRef.current, isWindowScroll);
1099
+ const layoutMeasurement = getLayoutMeasurement(scrollElement, isWindowScroll, horizontal);
1100
+ return getMaxOffset(contentSize, layoutMeasurement, horizontal);
1101
+ }, [horizontal, isWindowScroll]);
1102
+ const getCurrentScrollOffset = React3.useCallback(() => {
1103
+ const scrollElement = scrollRef.current;
1104
+ if (isWindowScroll) {
1105
+ const maxOffset = getMaxScrollOffset();
1106
+ const scroll = getWindowScrollPosition();
1107
+ const listPos = getElementDocumentPosition(scrollElement, scroll);
1108
+ const rawOffset = horizontal ? scroll.x - listPos.left : scroll.y - listPos.top;
1109
+ return clampOffset(rawOffset, maxOffset);
1110
+ }
1111
+ if (!scrollElement) {
1112
+ return 0;
1113
+ }
1114
+ return horizontal ? scrollElement.scrollLeft : scrollElement.scrollTop;
1115
+ }, [getMaxScrollOffset, horizontal, isWindowScroll]);
1116
+ const scrollToLocalOffset = React3.useCallback(
1117
+ (offset, animated) => {
1118
+ const scrollElement = scrollRef.current;
1119
+ const target = getScrollTarget();
1120
+ if (!target || typeof target.scrollTo !== "function") {
1121
+ return;
1122
+ }
1123
+ const maxOffset = getMaxScrollOffset();
1124
+ const clampedOffset = clampOffset(offset, maxOffset);
1125
+ const behavior = animated ? "smooth" : "auto";
1126
+ const options = { behavior };
1127
+ if (isWindowScroll) {
1128
+ const scroll = getWindowScrollPosition();
1129
+ const listPos = getElementDocumentPosition(scrollElement, scroll);
1130
+ const { left, top } = resolveWindowScrollTarget({
1131
+ clampedOffset,
1132
+ horizontal,
1133
+ listPos,
1134
+ scroll
1135
+ });
1136
+ options.left = left;
1137
+ options.top = top;
1138
+ } else if (horizontal) {
1139
+ options.left = clampedOffset;
1140
+ } else {
1141
+ options.top = clampedOffset;
1142
+ }
1143
+ target.scrollTo(options);
1144
+ },
1145
+ [getMaxScrollOffset, getScrollTarget, horizontal, isWindowScroll]
1146
+ );
989
1147
  React3.useImperativeHandle(ref, () => {
990
1148
  const api = {
991
1149
  getBoundingClientRect: () => {
992
1150
  var _a3;
993
1151
  return (_a3 = scrollRef.current) == null ? void 0 : _a3.getBoundingClientRect();
994
1152
  },
995
- getScrollableNode: () => scrollRef.current,
996
- getScrollResponder: () => scrollRef.current,
1153
+ getContentNode: () => contentRef.current,
1154
+ getCurrentScrollOffset,
1155
+ getScrollableNode: () => resolveScrollableNode(scrollRef.current, isWindowScroll),
1156
+ getScrollEventTarget: () => getScrollTarget(),
1157
+ getScrollResponder: () => resolveScrollableNode(scrollRef.current, isWindowScroll),
1158
+ isWindowScroll: () => isWindowScroll,
997
1159
  scrollBy: (x, y) => {
998
- const el = scrollRef.current;
999
- if (!el) return;
1000
- el.scrollBy(x, y);
1160
+ const target = getScrollTarget();
1161
+ if (!target || typeof target.scrollBy !== "function") {
1162
+ return;
1163
+ }
1164
+ target.scrollBy({ behavior: "auto", left: x, top: y });
1001
1165
  },
1002
1166
  scrollTo: (options) => {
1003
- const el = scrollRef.current;
1004
- if (!el) return;
1005
1167
  const { x = 0, y = 0, animated = true } = options;
1006
- el.scrollTo({ behavior: animated ? "smooth" : "auto", left: x, top: y });
1168
+ scrollToLocalOffset(horizontal ? x : y, animated);
1007
1169
  },
1008
1170
  scrollToEnd: (options = {}) => {
1009
- const el = scrollRef.current;
1010
- if (!el) return;
1011
1171
  const { animated = true } = options;
1012
- if (horizontal) {
1013
- el.scrollTo({ behavior: animated ? "smooth" : "auto", left: el.scrollWidth });
1014
- } else {
1015
- el.scrollTo({ behavior: animated ? "smooth" : "auto", top: el.scrollHeight });
1016
- }
1172
+ const endOffset = getMaxScrollOffset();
1173
+ scrollToLocalOffset(endOffset, animated);
1017
1174
  },
1018
1175
  scrollToOffset: (params) => {
1019
- const el = scrollRef.current;
1020
- if (!el) return;
1021
1176
  const { offset, animated = true } = params;
1022
- if (horizontal) {
1023
- el.scrollTo({ behavior: animated ? "smooth" : "auto", left: offset });
1024
- } else {
1025
- el.scrollTo({ behavior: animated ? "smooth" : "auto", top: offset });
1026
- }
1177
+ scrollToLocalOffset(offset, animated);
1027
1178
  }
1028
1179
  };
1029
1180
  return api;
1030
- }, [horizontal]);
1181
+ }, [getCurrentScrollOffset, getMaxScrollOffset, getScrollTarget, horizontal, isWindowScroll, scrollToLocalOffset]);
1031
1182
  const handleScroll = React3.useCallback(
1032
- (event) => {
1033
- if (!onScroll2 || !(event == null ? void 0 : event.target)) {
1183
+ (_event) => {
1184
+ if (!onScroll2) {
1034
1185
  return;
1035
1186
  }
1036
- const target = event.target;
1187
+ const target = scrollRef.current;
1188
+ if (!target) {
1189
+ return;
1190
+ }
1191
+ const contentSize = getContentSize2(contentRef.current);
1192
+ const layoutMeasurement = getLayoutMeasurement(scrollRef.current, isWindowScroll, horizontal);
1193
+ const offset = getCurrentScrollOffset();
1037
1194
  const scrollEvent = {
1038
1195
  nativeEvent: {
1039
1196
  contentOffset: {
1040
- x: target.scrollLeft,
1041
- y: target.scrollTop
1197
+ x: horizontal ? offset : 0,
1198
+ y: horizontal ? 0 : offset
1042
1199
  },
1043
1200
  contentSize: {
1044
- height: target.scrollHeight,
1045
- width: target.scrollWidth
1201
+ height: contentSize.height,
1202
+ width: contentSize.width
1046
1203
  },
1047
1204
  layoutMeasurement: {
1048
- height: target.clientHeight,
1049
- width: target.clientWidth
1205
+ height: layoutMeasurement.height,
1206
+ width: layoutMeasurement.width
1050
1207
  }
1051
1208
  }
1052
1209
  };
1053
1210
  onScroll2(scrollEvent);
1054
1211
  },
1055
- [onScroll2, onMomentumScrollEnd]
1212
+ [getCurrentScrollOffset, horizontal, isWindowScroll, onScroll2]
1056
1213
  );
1057
1214
  React3.useLayoutEffect(() => {
1058
- const element = scrollRef.current;
1059
- if (!element) return;
1060
- element.addEventListener("scroll", handleScroll);
1215
+ const target = getScrollTarget();
1216
+ if (!target) return;
1217
+ target.addEventListener("scroll", handleScroll, { passive: true });
1061
1218
  return () => {
1062
- element.removeEventListener("scroll", handleScroll);
1219
+ target.removeEventListener("scroll", handleScroll);
1063
1220
  };
1064
- }, [handleScroll]);
1221
+ }, [getScrollTarget, handleScroll]);
1065
1222
  React3.useEffect(() => {
1066
1223
  const doScroll = () => {
1067
- if (contentOffset && scrollRef.current) {
1068
- scrollRef.current.scrollLeft = contentOffset.x || 0;
1069
- scrollRef.current.scrollTop = contentOffset.y || 0;
1224
+ if (contentOffset) {
1225
+ scrollToLocalOffset(horizontal ? contentOffset.x || 0 : contentOffset.y || 0, false);
1070
1226
  }
1071
1227
  };
1072
1228
  doScroll();
1073
1229
  requestAnimationFrame(doScroll);
1074
- }, [contentOffset == null ? void 0 : contentOffset.x, contentOffset == null ? void 0 : contentOffset.y]);
1230
+ }, [contentOffset == null ? void 0 : contentOffset.x, contentOffset == null ? void 0 : contentOffset.y, horizontal, scrollToLocalOffset]);
1075
1231
  React3.useLayoutEffect(() => {
1076
1232
  if (!onLayout || !scrollRef.current) return;
1077
1233
  const element = scrollRef.current;
1078
1234
  const fireLayout = () => {
1079
- const rect = element.getBoundingClientRect();
1080
1235
  onLayout({
1081
1236
  nativeEvent: {
1082
- layout: {
1083
- height: rect.height,
1084
- width: rect.width,
1085
- x: rect.left,
1086
- y: rect.top
1087
- }
1237
+ layout: getLayoutRectangle(element, isWindowScroll, horizontal)
1088
1238
  }
1089
1239
  });
1090
1240
  };
@@ -1093,16 +1243,27 @@ var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView
1093
1243
  fireLayout();
1094
1244
  });
1095
1245
  resizeObserver.observe(element);
1096
- return () => resizeObserver.disconnect();
1097
- }, [onLayout]);
1246
+ const onWindowResize = () => {
1247
+ fireLayout();
1248
+ };
1249
+ if (isWindowScroll && typeof window !== "undefined" && typeof window.addEventListener === "function") {
1250
+ window.addEventListener("resize", onWindowResize);
1251
+ }
1252
+ return () => {
1253
+ resizeObserver.disconnect();
1254
+ if (isWindowScroll && typeof window !== "undefined" && typeof window.removeEventListener === "function") {
1255
+ window.removeEventListener("resize", onWindowResize);
1256
+ }
1257
+ };
1258
+ }, [isWindowScroll, onLayout]);
1098
1259
  const scrollViewStyle = {
1099
- overflow: "auto",
1100
- overflowX: horizontal ? "auto" : showsHorizontalScrollIndicator ? "auto" : "hidden",
1101
- overflowY: horizontal ? showsVerticalScrollIndicator ? "auto" : "hidden" : "auto",
1102
- position: "relative",
1103
- // Ensure proper positioning context
1104
- WebkitOverflowScrolling: "touch",
1105
- // iOS momentum scrolling
1260
+ ...isWindowScroll ? {} : {
1261
+ overflow: "auto",
1262
+ overflowX: horizontal ? "auto" : showsHorizontalScrollIndicator ? "auto" : "hidden",
1263
+ overflowY: horizontal ? showsVerticalScrollIndicator ? "auto" : "hidden" : "auto",
1264
+ WebkitOverflowScrolling: "touch"
1265
+ // iOS momentum scrolling
1266
+ },
1106
1267
  ...StyleSheet.flatten(style)
1107
1268
  };
1108
1269
  const contentStyle = {
@@ -1112,7 +1273,13 @@ var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView
1112
1273
  minWidth: horizontal ? "100%" : void 0,
1113
1274
  ...StyleSheet.flatten(contentContainerStyle)
1114
1275
  };
1115
- const { contentInset, scrollEventThrottle, ScrollComponent, ...webProps } = props;
1276
+ const {
1277
+ contentInset: _contentInset,
1278
+ scrollEventThrottle: _scrollEventThrottle,
1279
+ ScrollComponent: _ScrollComponent,
1280
+ useWindowScroll: _useWindowScroll,
1281
+ ...webProps
1282
+ } = props;
1116
1283
  return /* @__PURE__ */ React3__namespace.createElement("div", { ref: scrollRef, ...webProps, style: scrollViewStyle }, refreshControl, /* @__PURE__ */ React3__namespace.createElement("div", { ref: contentRef, style: contentStyle }, children));
1117
1284
  });
1118
1285
  function useValueListener$(key, callback) {
@@ -1139,23 +1306,29 @@ function ScrollAdjust() {
1139
1306
  if (scrollView && scrollOffset !== lastScrollOffsetRef.current) {
1140
1307
  const scrollDelta = scrollOffset - lastScrollOffsetRef.current;
1141
1308
  if (scrollDelta !== 0) {
1309
+ const contentNode = scrollView.getContentNode();
1310
+ const prevScroll = scrollView.getCurrentScrollOffset();
1142
1311
  const el = scrollView.getScrollableNode();
1143
- const prevScroll = el.scrollTop;
1312
+ if (!contentNode) {
1313
+ scrollView.scrollBy(0, scrollDelta);
1314
+ lastScrollOffsetRef.current = scrollOffset;
1315
+ return;
1316
+ }
1317
+ const totalSize = contentNode.scrollHeight;
1318
+ const viewportSize = el.clientHeight;
1144
1319
  const nextScroll = prevScroll + scrollDelta;
1145
- const totalSize = el.scrollHeight;
1146
- if (scrollDelta > 0 && !ctx.state.adjustingFromInitialMount && totalSize < nextScroll + el.clientHeight) {
1320
+ if (scrollDelta > 0 && !ctx.state.adjustingFromInitialMount && totalSize < nextScroll + viewportSize) {
1147
1321
  const paddingBottom = ctx.state.props.stylePaddingBottom || 0;
1148
- const child = el.firstElementChild;
1149
- const pad = (nextScroll + el.clientHeight - totalSize) * 2;
1150
- child.style.paddingBottom = `${pad}px`;
1151
- void el.offsetHeight;
1322
+ const pad = (nextScroll + viewportSize - totalSize) * 2;
1323
+ contentNode.style.paddingBottom = `${pad}px`;
1324
+ void contentNode.offsetHeight;
1152
1325
  scrollView.scrollBy(0, scrollDelta);
1153
1326
  if (resetPaddingRafRef.current !== void 0) {
1154
1327
  cancelAnimationFrame(resetPaddingRafRef.current);
1155
1328
  }
1156
1329
  resetPaddingRafRef.current = requestAnimationFrame(() => {
1157
1330
  resetPaddingRafRef.current = void 0;
1158
- child.style.paddingBottom = paddingBottom ? `${paddingBottom}px` : "0";
1331
+ contentNode.style.paddingBottom = paddingBottom ? `${paddingBottom}px` : "0";
1159
1332
  });
1160
1333
  } else {
1161
1334
  scrollView.scrollBy(0, scrollDelta);
@@ -1214,12 +1387,15 @@ var ListComponent = typedMemo(function ListComponent2({
1214
1387
  snapToIndices,
1215
1388
  stickyHeaderConfig,
1216
1389
  stickyHeaderIndices,
1390
+ useWindowScroll = false,
1217
1391
  ...rest
1218
1392
  }) {
1219
1393
  const ctx = useStateContext();
1220
1394
  const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
1221
1395
  const ScrollComponent = renderScrollComponent ? React3.useMemo(
1222
- () => React3__namespace.forwardRef((props, ref) => renderScrollComponent({ ...props, ref })),
1396
+ () => React3__namespace.forwardRef(
1397
+ (props, ref) => renderScrollComponent({ ...props, ref })
1398
+ ),
1223
1399
  [renderScrollComponent]
1224
1400
  ) : ListComponentScrollView;
1225
1401
  const SnapOrScroll = snapToIndices ? SnapWrapper : ScrollComponent;
@@ -1235,6 +1411,7 @@ var ListComponent = typedMemo(function ListComponent2({
1235
1411
  SnapOrScroll,
1236
1412
  {
1237
1413
  ...rest,
1414
+ ...ScrollComponent === ListComponentScrollView ? { useWindowScroll } : {},
1238
1415
  contentContainerStyle: [
1239
1416
  contentContainerStyle,
1240
1417
  horizontal ? {
@@ -1280,24 +1457,12 @@ var ListComponent = typedMemo(function ListComponent2({
1280
1457
  );
1281
1458
  });
1282
1459
 
1283
- // src/utils/getId.ts
1284
- function getId(state, index) {
1285
- const { data, keyExtractor } = state.props;
1286
- if (!data) {
1287
- return "";
1288
- }
1289
- const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
1290
- const id = ret;
1291
- state.idCache[index] = id;
1292
- return id;
1293
- }
1294
-
1295
1460
  // src/core/calculateOffsetForIndex.ts
1296
1461
  function calculateOffsetForIndex(ctx, index) {
1297
1462
  const state = ctx.state;
1298
1463
  let position = 0;
1299
1464
  if (index !== void 0) {
1300
- position = state.positions.get(getId(state, index)) || 0;
1465
+ position = state.positions[index] || 0;
1301
1466
  const paddingTop = peek$(ctx, "stylePaddingTop");
1302
1467
  if (paddingTop) {
1303
1468
  position += paddingTop;
@@ -1310,6 +1475,18 @@ function calculateOffsetForIndex(ctx, index) {
1310
1475
  return position;
1311
1476
  }
1312
1477
 
1478
+ // src/utils/getId.ts
1479
+ function getId(state, index) {
1480
+ const { data, keyExtractor } = state.props;
1481
+ if (!data) {
1482
+ return "";
1483
+ }
1484
+ const ret = index < data.length ? keyExtractor ? keyExtractor(data[index], index) : index : null;
1485
+ const id = ret;
1486
+ state.idCache[index] = id;
1487
+ return id;
1488
+ }
1489
+
1313
1490
  // src/core/addTotalSize.ts
1314
1491
  function addTotalSize(ctx, key, add) {
1315
1492
  const state = ctx.state;
@@ -1361,13 +1538,13 @@ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
1361
1538
  return sizeKnown;
1362
1539
  }
1363
1540
  let size;
1364
- const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1365
1541
  if (preferCachedSize) {
1366
1542
  const cachedSize = sizes.get(key);
1367
1543
  if (cachedSize !== void 0) {
1368
1544
  return cachedSize;
1369
1545
  }
1370
1546
  }
1547
+ const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1371
1548
  if (getFixedItemSize) {
1372
1549
  size = getFixedItemSize(data, index, itemType);
1373
1550
  if (size !== void 0) {
@@ -1410,12 +1587,15 @@ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1410
1587
  }
1411
1588
 
1412
1589
  // src/core/clampScrollOffset.ts
1413
- function clampScrollOffset(ctx, offset) {
1590
+ function clampScrollOffset(ctx, offset, scrollTarget) {
1414
1591
  const state = ctx.state;
1415
1592
  const contentSize = getContentSize(ctx);
1416
1593
  let clampedOffset = offset;
1417
1594
  if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform.OS !== "android")) {
1418
- const maxOffset = Math.max(0, contentSize - state.scrollLength);
1595
+ const baseMaxOffset = Math.max(0, contentSize - state.scrollLength);
1596
+ const viewOffset = scrollTarget == null ? void 0 : scrollTarget.viewOffset;
1597
+ const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
1598
+ const maxOffset = baseMaxOffset + extraEndOffset;
1419
1599
  clampedOffset = Math.min(offset, maxOffset);
1420
1600
  }
1421
1601
  clampedOffset = Math.max(0, clampedOffset);
@@ -1508,37 +1688,54 @@ function checkAtBottom(ctx) {
1508
1688
 
1509
1689
  // src/utils/checkAtTop.ts
1510
1690
  function checkAtTop(ctx) {
1511
- var _a3;
1512
1691
  const state = ctx == null ? void 0 : ctx.state;
1513
- if (!state || state.initialScroll) {
1692
+ if (!state || state.initialScroll || state.scrollingTo) {
1514
1693
  return;
1515
1694
  }
1516
1695
  const {
1517
- scrollLength,
1696
+ dataChangeEpoch,
1697
+ isStartReached,
1698
+ props: { data, onStartReachedThreshold },
1518
1699
  scroll,
1519
- props: { onStartReachedThreshold }
1700
+ scrollLength,
1701
+ startReachedSnapshot,
1702
+ startReachedSnapshotDataChangeEpoch,
1703
+ totalSize
1520
1704
  } = state;
1521
- const distanceFromTop = scroll;
1522
- state.isAtStart = distanceFromTop <= 0;
1705
+ const dataLength = data.length;
1706
+ const threshold = onStartReachedThreshold * scrollLength;
1707
+ const dataChanged = startReachedSnapshotDataChangeEpoch !== dataChangeEpoch;
1708
+ const withinThreshold = threshold > 0 && Math.abs(scroll) <= threshold;
1709
+ const allowReentryOnDataChange = !!isStartReached && withinThreshold && !!dataChanged && !isInMVCPActiveMode(state);
1710
+ if (isStartReached && threshold > 0 && scroll > threshold && startReachedSnapshot && (dataChanged || startReachedSnapshot.contentSize !== totalSize || startReachedSnapshot.dataLength !== dataLength)) {
1711
+ state.isStartReached = false;
1712
+ state.startReachedSnapshot = void 0;
1713
+ state.startReachedSnapshotDataChangeEpoch = void 0;
1714
+ }
1715
+ state.isAtStart = scroll <= 0;
1716
+ if (isStartReached && withinThreshold && dataChanged && !allowReentryOnDataChange) {
1717
+ return;
1718
+ }
1523
1719
  state.isStartReached = checkThreshold(
1524
- distanceFromTop,
1720
+ scroll,
1525
1721
  false,
1526
- onStartReachedThreshold * scrollLength,
1722
+ threshold,
1527
1723
  state.isStartReached,
1528
- state.startReachedSnapshot,
1724
+ allowReentryOnDataChange ? void 0 : startReachedSnapshot,
1529
1725
  {
1530
- contentSize: state.totalSize,
1531
- dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1726
+ contentSize: totalSize,
1727
+ dataLength,
1532
1728
  scrollPosition: scroll
1533
1729
  },
1534
1730
  (distance) => {
1535
- var _a4, _b;
1536
- return (_b = (_a4 = state.props).onStartReached) == null ? void 0 : _b.call(_a4, { distanceFromStart: distance });
1731
+ var _a3, _b;
1732
+ return (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
1537
1733
  },
1538
1734
  (snapshot) => {
1539
1735
  state.startReachedSnapshot = snapshot;
1736
+ state.startReachedSnapshotDataChangeEpoch = snapshot ? dataChangeEpoch : void 0;
1540
1737
  },
1541
- false
1738
+ allowReentryOnDataChange
1542
1739
  );
1543
1740
  }
1544
1741
 
@@ -1554,14 +1751,22 @@ function setInitialRenderState(ctx, {
1554
1751
  didInitialScroll
1555
1752
  }) {
1556
1753
  const { state } = ctx;
1754
+ const {
1755
+ loadStartTime,
1756
+ props: { onLoad }
1757
+ } = state;
1557
1758
  if (didLayout) {
1558
1759
  state.didContainersLayout = true;
1559
1760
  }
1560
1761
  if (didInitialScroll) {
1561
1762
  state.didFinishInitialScroll = true;
1562
1763
  }
1563
- if (state.didContainersLayout && state.didFinishInitialScroll) {
1764
+ const isReadyToRender = Boolean(state.didContainersLayout && state.didFinishInitialScroll);
1765
+ if (isReadyToRender && !peek$(ctx, "readyToRender")) {
1564
1766
  set$(ctx, "readyToRender", true);
1767
+ if (onLoad) {
1768
+ onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1769
+ }
1565
1770
  }
1566
1771
  }
1567
1772
 
@@ -1570,6 +1775,8 @@ function finishScrollTo(ctx) {
1570
1775
  var _a3, _b;
1571
1776
  const state = ctx.state;
1572
1777
  if (state == null ? void 0 : state.scrollingTo) {
1778
+ const resolvePendingScroll = state.pendingScrollResolve;
1779
+ state.pendingScrollResolve = void 0;
1573
1780
  const scrollingTo = state.scrollingTo;
1574
1781
  state.scrollHistory.length = 0;
1575
1782
  state.initialScroll = void 0;
@@ -1586,6 +1793,7 @@ function finishScrollTo(ctx) {
1586
1793
  }
1587
1794
  setInitialRenderState(ctx, { didInitialScroll: true });
1588
1795
  checkThresholds(ctx);
1796
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1589
1797
  }
1590
1798
  }
1591
1799
 
@@ -1598,42 +1806,49 @@ function doScrollTo(ctx, params) {
1598
1806
  const state = ctx.state;
1599
1807
  const { animated, horizontal, offset } = params;
1600
1808
  const scroller = state.refScroller.current;
1601
- const node = typeof (scroller == null ? void 0 : scroller.getScrollableNode) === "function" ? scroller.getScrollableNode() : scroller;
1602
- if (node) {
1603
- const left = horizontal ? offset : 0;
1604
- const top = horizontal ? 0 : offset;
1605
- node.scrollTo({ behavior: animated ? "smooth" : "auto", left, top });
1606
- if (animated) {
1607
- listenForScrollEnd(ctx, node, {
1608
- horizontal: !!horizontal,
1609
- targetOffset: offset
1610
- });
1611
- } else {
1612
- state.scroll = offset;
1613
- setTimeout(() => {
1614
- finishScrollTo(ctx);
1615
- }, 100);
1616
- }
1809
+ const node = scroller == null ? void 0 : scroller.getScrollableNode();
1810
+ if (!scroller || !node) {
1811
+ return;
1812
+ }
1813
+ const isAnimated = !!animated;
1814
+ const isHorizontal = !!horizontal;
1815
+ const left = isHorizontal ? offset : 0;
1816
+ const top = isHorizontal ? 0 : offset;
1817
+ scroller.scrollTo({ animated: isAnimated, x: left, y: top });
1818
+ if (isAnimated) {
1819
+ const target = scroller.getScrollEventTarget();
1820
+ listenForScrollEnd(ctx, {
1821
+ readOffset: () => scroller.getCurrentScrollOffset(),
1822
+ target,
1823
+ targetOffset: offset
1824
+ });
1825
+ } else {
1826
+ state.scroll = offset;
1827
+ setTimeout(() => {
1828
+ finishScrollTo(ctx);
1829
+ }, 100);
1617
1830
  }
1618
1831
  }
1619
- function listenForScrollEnd(ctx, node, params) {
1620
- const { horizontal, targetOffset } = params;
1621
- const supportsScrollEnd = "onscrollend" in node;
1832
+ function listenForScrollEnd(ctx, params) {
1833
+ const { readOffset, target, targetOffset } = params;
1834
+ if (!target) {
1835
+ finishScrollTo(ctx);
1836
+ return;
1837
+ }
1838
+ const supportsScrollEnd = "onscrollend" in target;
1622
1839
  let idleTimeout;
1623
- let maxTimeout;
1624
1840
  let settled = false;
1625
1841
  const targetToken = ctx.state.scrollingTo;
1842
+ const maxTimeout = setTimeout(() => finish("max"), SCROLL_END_MAX_MS);
1626
1843
  const cleanup = () => {
1627
- node.removeEventListener("scroll", onScroll2);
1844
+ target.removeEventListener("scroll", onScroll2);
1628
1845
  if (supportsScrollEnd) {
1629
- node.removeEventListener("scrollend", onScrollEnd);
1846
+ target.removeEventListener("scrollend", onScrollEnd);
1630
1847
  }
1631
1848
  if (idleTimeout) {
1632
1849
  clearTimeout(idleTimeout);
1633
1850
  }
1634
- if (maxTimeout) {
1635
- clearTimeout(maxTimeout);
1636
- }
1851
+ clearTimeout(maxTimeout);
1637
1852
  };
1638
1853
  const finish = (reason) => {
1639
1854
  if (settled) return;
@@ -1642,7 +1857,7 @@ function listenForScrollEnd(ctx, node, params) {
1642
1857
  cleanup();
1643
1858
  return;
1644
1859
  }
1645
- const currentOffset = horizontal ? node.scrollLeft : node.scrollTop;
1860
+ const currentOffset = readOffset();
1646
1861
  const isNearTarget = Math.abs(currentOffset - targetOffset) <= SCROLL_END_TARGET_EPSILON;
1647
1862
  if (reason === "scrollend" && !isNearTarget) {
1648
1863
  return;
@@ -1658,13 +1873,11 @@ function listenForScrollEnd(ctx, node, params) {
1658
1873
  idleTimeout = setTimeout(() => finish("idle"), SCROLL_END_IDLE_MS);
1659
1874
  };
1660
1875
  const onScrollEnd = () => finish("scrollend");
1661
- node.addEventListener("scroll", onScroll2);
1876
+ target.addEventListener("scroll", onScroll2);
1662
1877
  if (supportsScrollEnd) {
1663
- node.addEventListener("scrollend", onScrollEnd);
1664
- maxTimeout = setTimeout(() => finish("max"), SCROLL_END_MAX_MS);
1878
+ target.addEventListener("scrollend", onScrollEnd);
1665
1879
  } else {
1666
1880
  idleTimeout = setTimeout(() => finish("idle"), SMOOTH_SCROLL_DURATION_MS);
1667
- maxTimeout = setTimeout(() => finish("max"), SCROLL_END_MAX_MS);
1668
1881
  }
1669
1882
  }
1670
1883
 
@@ -1683,7 +1896,7 @@ function scrollTo(ctx, params) {
1683
1896
  clearTimeout(ctx.state.timeoutCheckFinishedScrollFallback);
1684
1897
  }
1685
1898
  let offset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, scrollTargetOffset, scrollTarget);
1686
- offset = clampScrollOffset(ctx, offset);
1899
+ offset = clampScrollOffset(ctx, offset, scrollTarget);
1687
1900
  state.scrollHistory.length = 0;
1688
1901
  if (!noScrollingTo) {
1689
1902
  state.scrollingTo = scrollTarget;
@@ -1699,7 +1912,7 @@ function scrollTo(ctx, params) {
1699
1912
  // src/core/updateScroll.ts
1700
1913
  function updateScroll(ctx, newScroll, forceUpdate) {
1701
1914
  const state = ctx.state;
1702
- const { scrollingTo, scrollAdjustHandler, lastScrollAdjustForHistory } = state;
1915
+ const { ignoreScrollFromMVCP, lastScrollAdjustForHistory, scrollAdjustHandler, scrollHistory, scrollingTo } = state;
1703
1916
  const prevScroll = state.scroll;
1704
1917
  state.hasScrolled = true;
1705
1918
  state.lastBatchingAction = Date.now();
@@ -1707,22 +1920,17 @@ function updateScroll(ctx, newScroll, forceUpdate) {
1707
1920
  const adjust = scrollAdjustHandler.getAdjust();
1708
1921
  const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
1709
1922
  if (adjustChanged) {
1710
- state.scrollHistory.length = 0;
1923
+ scrollHistory.length = 0;
1711
1924
  }
1712
1925
  state.lastScrollAdjustForHistory = adjust;
1713
- if (scrollingTo === void 0 && !(state.scrollHistory.length === 0 && newScroll === state.scroll)) {
1926
+ if (scrollingTo === void 0 && !(scrollHistory.length === 0 && newScroll === state.scroll)) {
1714
1927
  if (!adjustChanged) {
1715
- state.scrollHistory.push({ scroll: newScroll, time: currentTime });
1928
+ scrollHistory.push({ scroll: newScroll, time: currentTime });
1716
1929
  }
1717
1930
  }
1718
- if (state.scrollHistory.length > 5) {
1719
- state.scrollHistory.shift();
1931
+ if (scrollHistory.length > 5) {
1932
+ scrollHistory.shift();
1720
1933
  }
1721
- state.scrollPrev = prevScroll;
1722
- state.scrollPrevTime = state.scrollTime;
1723
- state.scroll = newScroll;
1724
- state.scrollTime = currentTime;
1725
- const ignoreScrollFromMVCP = state.ignoreScrollFromMVCP;
1726
1934
  if (ignoreScrollFromMVCP && !scrollingTo) {
1727
1935
  const { lt, gt } = ignoreScrollFromMVCP;
1728
1936
  if (lt && newScroll < lt || gt && newScroll > gt) {
@@ -1730,6 +1938,10 @@ function updateScroll(ctx, newScroll, forceUpdate) {
1730
1938
  return;
1731
1939
  }
1732
1940
  }
1941
+ state.scrollPrev = prevScroll;
1942
+ state.scrollPrevTime = state.scrollTime;
1943
+ state.scroll = newScroll;
1944
+ state.scrollTime = currentTime;
1733
1945
  const scrollDelta = Math.abs(newScroll - prevScroll);
1734
1946
  const scrollLength = state.scrollLength;
1735
1947
  const lastCalculated = state.scrollLastCalculate;
@@ -1836,6 +2048,7 @@ function prepareMVCP(ctx, dataChanged) {
1836
2048
  const idsInViewWithPositions = [];
1837
2049
  const scrollTarget = scrollingTo == null ? void 0 : scrollingTo.index;
1838
2050
  const scrollingToViewPosition = scrollingTo == null ? void 0 : scrollingTo.viewPosition;
2051
+ const isEndAnchoredScrollTarget = scrollTarget !== void 0 && state.props.data.length > 0 && scrollTarget >= state.props.data.length - 1 && (scrollingToViewPosition != null ? scrollingToViewPosition : 0) > 0;
1839
2052
  const shouldMVCP = dataChanged ? mvcpData : mvcpScroll;
1840
2053
  const indexByKey = state.indexByKey;
1841
2054
  if (shouldMVCP) {
@@ -1852,12 +2065,18 @@ function prepareMVCP(ctx, dataChanged) {
1852
2065
  const id = idsInView[i];
1853
2066
  const index = indexByKey.get(id);
1854
2067
  if (index !== void 0) {
1855
- idsInViewWithPositions.push({ id, position: positions.get(id) });
2068
+ const position = positions[index];
2069
+ if (position !== void 0) {
2070
+ idsInViewWithPositions.push({ id, position });
2071
+ }
1856
2072
  }
1857
2073
  }
1858
2074
  }
1859
2075
  if (targetId !== void 0 && prevPosition === void 0) {
1860
- prevPosition = positions.get(targetId);
2076
+ const targetIndex = indexByKey.get(targetId);
2077
+ if (targetIndex !== void 0) {
2078
+ prevPosition = positions[targetIndex];
2079
+ }
1861
2080
  }
1862
2081
  return () => {
1863
2082
  let positionDiff = 0;
@@ -1876,7 +2095,13 @@ function prepareMVCP(ctx, dataChanged) {
1876
2095
  }
1877
2096
  }
1878
2097
  }
1879
- const shouldUseFallbackVisibleAnchor = dataChanged && mvcpData && scrollTarget === void 0 && (targetId === void 0 || positions.get(targetId) === void 0 || skipTargetAnchor);
2098
+ const shouldUseFallbackVisibleAnchor = dataChanged && mvcpData && scrollTarget === void 0 && (() => {
2099
+ if (targetId === void 0 || skipTargetAnchor) {
2100
+ return true;
2101
+ }
2102
+ const targetIndex = indexByKey.get(targetId);
2103
+ return targetIndex === void 0 || positions[targetIndex] === void 0;
2104
+ })();
1880
2105
  if (shouldUseFallbackVisibleAnchor) {
1881
2106
  for (let i = 0; i < idsInViewWithPositions.length; i++) {
1882
2107
  const { id, position } = idsInViewWithPositions[i];
@@ -1887,7 +2112,7 @@ function prepareMVCP(ctx, dataChanged) {
1887
2112
  continue;
1888
2113
  }
1889
2114
  }
1890
- const newPosition = positions.get(id);
2115
+ const newPosition = index !== void 0 ? positions[index] : void 0;
1891
2116
  if (newPosition !== void 0) {
1892
2117
  positionDiff = newPosition - position;
1893
2118
  anchorIdForLock = id;
@@ -1897,11 +2122,12 @@ function prepareMVCP(ctx, dataChanged) {
1897
2122
  }
1898
2123
  }
1899
2124
  if (!skipTargetAnchor && targetId !== void 0 && prevPosition !== void 0) {
1900
- const newPosition = positions.get(targetId);
2125
+ const targetIndex = indexByKey.get(targetId);
2126
+ const newPosition = targetIndex !== void 0 ? positions[targetIndex] : void 0;
1901
2127
  if (newPosition !== void 0) {
1902
2128
  const totalSize = getContentSize(ctx);
1903
2129
  let diff = newPosition - prevPosition;
1904
- if (diff !== 0 && state.scroll + state.scrollLength > totalSize) {
2130
+ if (diff !== 0 && isEndAnchoredScrollTarget && state.scroll + state.scrollLength > totalSize) {
1905
2131
  if (diff > 0) {
1906
2132
  diff = Math.max(0, totalSize - state.scroll - state.scrollLength);
1907
2133
  } else {
@@ -1944,17 +2170,15 @@ function prepareColumnStartState(ctx, startIndex, useAverageSize) {
1944
2170
  const state = ctx.state;
1945
2171
  const numColumns = peek$(ctx, "numColumns");
1946
2172
  let rowStartIndex = startIndex;
1947
- const columnAtStart = state.columns.get(state.idCache[startIndex]);
2173
+ const columnAtStart = state.columns[startIndex];
1948
2174
  if (columnAtStart !== 1) {
1949
2175
  rowStartIndex = findRowStartIndex(state, numColumns, startIndex);
1950
2176
  }
1951
2177
  let currentRowTop = 0;
1952
- const curId = state.idCache[rowStartIndex];
1953
- const column = state.columns.get(curId);
2178
+ const column = state.columns[rowStartIndex];
1954
2179
  if (rowStartIndex > 0) {
1955
2180
  const prevIndex = rowStartIndex - 1;
1956
- const prevId = state.idCache[prevIndex];
1957
- const prevPosition = (_a3 = state.positions.get(prevId)) != null ? _a3 : 0;
2181
+ const prevPosition = (_a3 = state.positions[prevIndex]) != null ? _a3 : 0;
1958
2182
  const prevRowStart = findRowStartIndex(state, numColumns, prevIndex);
1959
2183
  const prevRowHeight = calculateRowMaxSize(ctx, prevRowStart, prevIndex, useAverageSize);
1960
2184
  currentRowTop = prevPosition + prevRowHeight;
@@ -1971,7 +2195,7 @@ function findRowStartIndex(state, numColumns, index) {
1971
2195
  }
1972
2196
  let rowStart = Math.max(0, index);
1973
2197
  while (rowStart > 0) {
1974
- const columnForIndex = state.columns.get(state.idCache[rowStart]);
2198
+ const columnForIndex = state.columns[rowStart];
1975
2199
  if (columnForIndex === 1) {
1976
2200
  break;
1977
2201
  }
@@ -2004,7 +2228,7 @@ function calculateRowMaxSize(ctx, startIndex, endIndex, useAverageSize) {
2004
2228
 
2005
2229
  // src/core/updateTotalSize.ts
2006
2230
  function updateTotalSize(ctx) {
2007
- var _a3, _b, _c;
2231
+ var _a3, _b;
2008
2232
  const state = ctx.state;
2009
2233
  const {
2010
2234
  positions,
@@ -2014,36 +2238,34 @@ function updateTotalSize(ctx) {
2014
2238
  if (data.length === 0) {
2015
2239
  addTotalSize(ctx, null, 0);
2016
2240
  } else {
2017
- const lastId = getId(state, data.length - 1);
2018
- if (lastId !== void 0) {
2019
- const lastPosition = positions.get(lastId);
2020
- if (lastPosition !== void 0) {
2021
- if (numColumns > 1) {
2022
- let rowStart = data.length - 1;
2023
- while (rowStart > 0) {
2024
- const rowId = (_b = state.idCache[rowStart]) != null ? _b : getId(state, rowStart);
2025
- const column = state.columns.get(rowId);
2026
- if (column === 1 || column === void 0) {
2027
- break;
2028
- }
2029
- rowStart -= 1;
2030
- }
2031
- let maxSize = 0;
2032
- for (let i = rowStart; i < data.length; i++) {
2033
- const rowId = (_c = state.idCache[i]) != null ? _c : getId(state, i);
2034
- const size = getItemSize(ctx, rowId, i, data[i]);
2035
- if (size > maxSize) {
2036
- maxSize = size;
2037
- }
2241
+ const lastIndex = data.length - 1;
2242
+ const lastId = getId(state, lastIndex);
2243
+ const lastPosition = positions[lastIndex];
2244
+ if (lastId !== void 0 && lastPosition !== void 0) {
2245
+ if (numColumns > 1) {
2246
+ let rowStart = lastIndex;
2247
+ while (rowStart > 0) {
2248
+ const column = state.columns[rowStart];
2249
+ if (column === 1 || column === void 0) {
2250
+ break;
2038
2251
  }
2039
- addTotalSize(ctx, null, lastPosition + maxSize);
2040
- } else {
2041
- const lastSize = getItemSize(ctx, lastId, data.length - 1, data[data.length - 1]);
2042
- if (lastSize !== void 0) {
2043
- const totalSize = lastPosition + lastSize;
2044
- addTotalSize(ctx, null, totalSize);
2252
+ rowStart -= 1;
2253
+ }
2254
+ let maxSize = 0;
2255
+ for (let i = rowStart; i <= lastIndex; i++) {
2256
+ const rowId = (_b = state.idCache[i]) != null ? _b : getId(state, i);
2257
+ const size = getItemSize(ctx, rowId, i, data[i]);
2258
+ if (size > maxSize) {
2259
+ maxSize = size;
2045
2260
  }
2046
2261
  }
2262
+ addTotalSize(ctx, null, lastPosition + maxSize);
2263
+ } else {
2264
+ const lastSize = getItemSize(ctx, lastId, lastIndex, data[lastIndex]);
2265
+ if (lastSize !== void 0) {
2266
+ const totalSize = lastPosition + lastSize;
2267
+ addTotalSize(ctx, null, totalSize);
2268
+ }
2047
2269
  }
2048
2270
  }
2049
2271
  }
@@ -2092,14 +2314,13 @@ var getScrollVelocity = (state) => {
2092
2314
  function updateSnapToOffsets(ctx) {
2093
2315
  const state = ctx.state;
2094
2316
  const {
2095
- positions,
2096
2317
  props: { snapToIndices }
2097
2318
  } = state;
2098
2319
  const snapToOffsets = Array(snapToIndices.length);
2099
2320
  for (let i = 0; i < snapToIndices.length; i++) {
2100
2321
  const idx = snapToIndices[i];
2101
- const key = getId(state, idx);
2102
- snapToOffsets[i] = positions.get(key);
2322
+ getId(state, idx);
2323
+ snapToOffsets[i] = state.positions[idx];
2103
2324
  }
2104
2325
  set$(ctx, "snapToOffsets", snapToOffsets);
2105
2326
  }
@@ -2111,8 +2332,9 @@ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffere
2111
2332
  scrollBottomBuffered: -1,
2112
2333
  startIndex: 0
2113
2334
  }) {
2114
- var _a3, _b, _c, _d, _e, _f;
2335
+ var _a3, _b, _c, _d, _e;
2115
2336
  const state = ctx.state;
2337
+ const hasPositionListeners = ctx.positionListeners.size > 0;
2116
2338
  const {
2117
2339
  columns,
2118
2340
  columnSpans,
@@ -2139,7 +2361,15 @@ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffere
2139
2361
  let column = 1;
2140
2362
  let maxSizeInRow = 0;
2141
2363
  if (dataChanged) {
2142
- columnSpans.clear();
2364
+ columnSpans.length = 0;
2365
+ }
2366
+ if (!hasColumns) {
2367
+ if (columns.length) {
2368
+ columns.length = 0;
2369
+ }
2370
+ if (columnSpans.length) {
2371
+ columnSpans.length = 0;
2372
+ }
2143
2373
  }
2144
2374
  if (startIndex > 0) {
2145
2375
  if (hasColumns) {
@@ -2153,12 +2383,13 @@ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffere
2153
2383
  } else if (startIndex < dataLength) {
2154
2384
  const prevIndex = startIndex - 1;
2155
2385
  const prevId = getId(state, prevIndex);
2156
- const prevPosition = (_c = positions.get(prevId)) != null ? _c : 0;
2386
+ const prevPosition = (_c = positions[prevIndex]) != null ? _c : 0;
2157
2387
  const prevSize = (_d = sizesKnown.get(prevId)) != null ? _d : getItemSize(ctx, prevId, prevIndex, data[prevIndex], useAverageSize, preferCachedSize);
2158
2388
  currentRowTop = prevPosition + prevSize;
2159
2389
  }
2160
2390
  }
2161
2391
  const needsIndexByKey = dataChanged || indexByKey.size === 0;
2392
+ const canOverrideSpan = hasColumns && !!overrideItemLayout && !!layoutConfig;
2162
2393
  let didBreakEarly = false;
2163
2394
  let breakAt;
2164
2395
  for (let i = startIndex; i < dataLength; i++) {
@@ -2172,7 +2403,7 @@ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffere
2172
2403
  }
2173
2404
  const id = (_e = idCache[i]) != null ? _e : getId(state, i);
2174
2405
  let span = 1;
2175
- if (hasColumns && overrideItemLayout && layoutConfig) {
2406
+ if (canOverrideSpan) {
2176
2407
  layoutConfig.span = 1;
2177
2408
  overrideItemLayout(layoutConfig, data[i], i, numColumns, extraData);
2178
2409
  const requestedSpan = layoutConfig.span;
@@ -2185,7 +2416,8 @@ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffere
2185
2416
  column = 1;
2186
2417
  maxSizeInRow = 0;
2187
2418
  }
2188
- const size = (_f = sizesKnown.get(id)) != null ? _f : getItemSize(ctx, id, i, data[i], useAverageSize, preferCachedSize);
2419
+ const knownSize = sizesKnown.get(id);
2420
+ const size = knownSize !== void 0 ? knownSize : getItemSize(ctx, id, i, data[i], useAverageSize, preferCachedSize);
2189
2421
  if (IS_DEV && needsIndexByKey) {
2190
2422
  if (indexByKeyForChecking.has(id)) {
2191
2423
  console.error(
@@ -2194,16 +2426,20 @@ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffere
2194
2426
  }
2195
2427
  indexByKeyForChecking.set(id, i);
2196
2428
  }
2197
- if (currentRowTop !== positions.get(id)) {
2198
- positions.set(id, currentRowTop);
2199
- notifyPosition$(ctx, id, currentRowTop);
2429
+ if (currentRowTop !== positions[i]) {
2430
+ positions[i] = currentRowTop;
2431
+ if (hasPositionListeners) {
2432
+ notifyPosition$(ctx, id, currentRowTop);
2433
+ }
2200
2434
  }
2201
2435
  if (needsIndexByKey) {
2202
2436
  indexByKey.set(id, i);
2203
2437
  }
2204
- columns.set(id, column);
2205
- columnSpans.set(id, span);
2206
- if (hasColumns) {
2438
+ if (!hasColumns) {
2439
+ currentRowTop += size;
2440
+ } else {
2441
+ columns[i] = column;
2442
+ columnSpans[i] = span;
2207
2443
  if (size > maxSizeInRow) {
2208
2444
  maxSizeInRow = size;
2209
2445
  }
@@ -2213,8 +2449,6 @@ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffere
2213
2449
  column = 1;
2214
2450
  maxSizeInRow = 0;
2215
2451
  }
2216
- } else {
2217
- currentRowTop += size;
2218
2452
  }
2219
2453
  }
2220
2454
  if (!didBreakEarly) {
@@ -2366,14 +2600,38 @@ function shallowEqual(prev, next) {
2366
2600
  return true;
2367
2601
  }
2368
2602
  function computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
2369
- const { sizes, positions, scroll: scrollState } = state;
2603
+ const { sizes, scroll: scrollState } = state;
2370
2604
  const topPad = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
2371
2605
  const { itemVisiblePercentThreshold, viewAreaCoveragePercentThreshold } = viewabilityConfig;
2372
2606
  const viewAreaMode = viewAreaCoveragePercentThreshold != null;
2373
2607
  const viewablePercentThreshold = viewAreaMode ? viewAreaCoveragePercentThreshold : itemVisiblePercentThreshold;
2374
2608
  const scroll = scrollState - topPad;
2375
- const top = positions.get(key) - scroll;
2609
+ const position = state.positions[index];
2376
2610
  const size = sizes.get(key) || 0;
2611
+ if (position === void 0) {
2612
+ const value2 = {
2613
+ containerId,
2614
+ index,
2615
+ isViewable: false,
2616
+ item,
2617
+ key,
2618
+ percentOfScroller: 0,
2619
+ percentVisible: 0,
2620
+ scrollSize,
2621
+ size,
2622
+ sizeVisible: -1
2623
+ };
2624
+ const prev2 = ctx.mapViewabilityAmountValues.get(containerId);
2625
+ if (!shallowEqual(prev2, value2)) {
2626
+ ctx.mapViewabilityAmountValues.set(containerId, value2);
2627
+ const cb = ctx.mapViewabilityAmountCallbacks.get(containerId);
2628
+ if (cb) {
2629
+ cb(value2);
2630
+ }
2631
+ }
2632
+ return value2;
2633
+ }
2634
+ const top = position - scroll;
2377
2635
  const bottom = top + size;
2378
2636
  const isEntirelyVisible = top >= 0 && bottom <= scrollSize && bottom > top;
2379
2637
  const sizeVisible = isEntirelyVisible ? size : Math.min(bottom, scrollSize) - Math.max(top, 0);
@@ -2586,37 +2844,24 @@ function scrollToIndex(ctx, { index, viewOffset = 0, animated = true, viewPositi
2586
2844
  // src/utils/setDidLayout.ts
2587
2845
  function setDidLayout(ctx) {
2588
2846
  const state = ctx.state;
2589
- const {
2590
- loadStartTime,
2591
- initialScroll,
2592
- props: { onLoad }
2593
- } = state;
2847
+ const { initialScroll } = state;
2594
2848
  state.queuedInitialLayout = true;
2595
2849
  checkAtBottom(ctx);
2596
- const setIt = () => {
2597
- setInitialRenderState(ctx, { didLayout: true });
2598
- if (onLoad) {
2599
- onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
2600
- }
2601
- };
2602
2850
  if ((initialScroll == null ? void 0 : initialScroll.index) !== void 0) {
2603
2851
  const target = initialScroll;
2604
2852
  const runScroll = () => scrollToIndex(ctx, { ...target, animated: false });
2605
2853
  runScroll();
2606
2854
  requestAnimationFrame(runScroll);
2607
2855
  }
2608
- setIt();
2856
+ setInitialRenderState(ctx, { didLayout: true });
2609
2857
  }
2610
2858
 
2611
2859
  // src/core/calculateItemsInView.ts
2612
2860
  function findCurrentStickyIndex(stickyArray, scroll, state) {
2613
- var _a3;
2614
- const idCache = state.idCache;
2615
2861
  const positions = state.positions;
2616
2862
  for (let i = stickyArray.length - 1; i >= 0; i--) {
2617
2863
  const stickyIndex = stickyArray[i];
2618
- const stickyId = (_a3 = idCache[stickyIndex]) != null ? _a3 : getId(state, stickyIndex);
2619
- const stickyPos = stickyId ? positions.get(stickyId) : void 0;
2864
+ const stickyPos = positions[stickyIndex];
2620
2865
  if (stickyPos !== void 0 && scroll >= stickyPos) {
2621
2866
  return i;
2622
2867
  }
@@ -2646,7 +2891,7 @@ function handleStickyActivation(ctx, stickyHeaderIndices, stickyArray, currentSt
2646
2891
  }
2647
2892
  }
2648
2893
  function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentStickyIdx, pendingRemoval, alwaysRenderIndicesSet) {
2649
- var _a3, _b, _c;
2894
+ var _a3, _b;
2650
2895
  const state = ctx.state;
2651
2896
  for (const containerIndex of state.stickyContainerPool) {
2652
2897
  const itemKey = peek$(ctx, `containerItemKey${containerIndex}`);
@@ -2664,14 +2909,13 @@ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentSt
2664
2909
  const nextIndex = stickyArray[arrayIdx + 1];
2665
2910
  let shouldRecycle = false;
2666
2911
  if (nextIndex) {
2667
- const nextId = (_a3 = state.idCache[nextIndex]) != null ? _a3 : getId(state, nextIndex);
2668
- const nextPos = nextId ? state.positions.get(nextId) : void 0;
2912
+ const nextPos = state.positions[nextIndex];
2669
2913
  shouldRecycle = nextPos !== void 0 && scroll > nextPos + drawDistance * 2;
2670
2914
  } else {
2671
- const currentId = (_b = state.idCache[itemIndex]) != null ? _b : getId(state, itemIndex);
2915
+ const currentId = (_a3 = state.idCache[itemIndex]) != null ? _a3 : getId(state, itemIndex);
2672
2916
  if (currentId) {
2673
- const currentPos = state.positions.get(currentId);
2674
- const currentSize = (_c = state.sizes.get(currentId)) != null ? _c : getItemSize(ctx, currentId, itemIndex, state.props.data[itemIndex]);
2917
+ const currentPos = state.positions[itemIndex];
2918
+ const currentSize = (_b = state.sizes.get(currentId)) != null ? _b : getItemSize(ctx, currentId, itemIndex, state.props.data[itemIndex]);
2675
2919
  shouldRecycle = currentPos !== void 0 && scroll > currentPos + currentSize + drawDistance * 3;
2676
2920
  }
2677
2921
  }
@@ -2683,7 +2927,7 @@ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentSt
2683
2927
  function calculateItemsInView(ctx, params = {}) {
2684
2928
  const state = ctx.state;
2685
2929
  reactDom.unstable_batchedUpdates(() => {
2686
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
2930
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
2687
2931
  const {
2688
2932
  columns,
2689
2933
  columnSpans,
@@ -2772,7 +3016,9 @@ function calculateItemsInView(ctx, params = {}) {
2772
3016
  if (dataChanged) {
2773
3017
  indexByKey.clear();
2774
3018
  idCache.length = 0;
2775
- positions.clear();
3019
+ positions.length = 0;
3020
+ columns.length = 0;
3021
+ columnSpans.length = 0;
2776
3022
  }
2777
3023
  const startIndex = forceFullItemPositions || dataChanged ? 0 : (_b = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _b : 0;
2778
3024
  updateItemPositions(ctx, dataChanged, {
@@ -2793,7 +3039,7 @@ function calculateItemsInView(ctx, params = {}) {
2793
3039
  let loopStart = !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
2794
3040
  for (let i = loopStart; i >= 0; i--) {
2795
3041
  const id = (_c = idCache[i]) != null ? _c : getId(state, i);
2796
- const top = positions.get(id);
3042
+ const top = positions[i];
2797
3043
  const size = (_d = sizes.get(id)) != null ? _d : getItemSize(ctx, id, i, data[i]);
2798
3044
  const bottom = top + size;
2799
3045
  if (bottom > scroll - scrollBufferTop) {
@@ -2804,8 +3050,7 @@ function calculateItemsInView(ctx, params = {}) {
2804
3050
  }
2805
3051
  if (numColumns > 1) {
2806
3052
  while (loopStart > 0) {
2807
- const loopId = (_e = idCache[loopStart]) != null ? _e : getId(state, loopStart);
2808
- const loopColumn = columns.get(loopId);
3053
+ const loopColumn = columns[loopStart];
2809
3054
  if (loopColumn === 1 || loopColumn === void 0) {
2810
3055
  break;
2811
3056
  }
@@ -2826,9 +3071,9 @@ function calculateItemsInView(ctx, params = {}) {
2826
3071
  let firstFullyOnScreenIndex;
2827
3072
  const dataLength = data.length;
2828
3073
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
2829
- const id = (_f = idCache[i]) != null ? _f : getId(state, i);
2830
- const size = (_g = sizes.get(id)) != null ? _g : getItemSize(ctx, id, i, data[i]);
2831
- const top = positions.get(id);
3074
+ const id = (_e = idCache[i]) != null ? _e : getId(state, i);
3075
+ const size = (_f = sizes.get(id)) != null ? _f : getItemSize(ctx, id, i, data[i]);
3076
+ const top = positions[i];
2832
3077
  if (!foundEnd) {
2833
3078
  if (startNoBuffer === null && top + size > scroll) {
2834
3079
  startNoBuffer = i;
@@ -2864,7 +3109,7 @@ function calculateItemsInView(ctx, params = {}) {
2864
3109
  }
2865
3110
  const idsInView = [];
2866
3111
  for (let i = firstFullyOnScreenIndex; i <= endNoBuffer; i++) {
2867
- const id = (_h = idCache[i]) != null ? _h : getId(state, i);
3112
+ const id = (_g = idCache[i]) != null ? _g : getId(state, i);
2868
3113
  idsInView.push(id);
2869
3114
  }
2870
3115
  Object.assign(state, {
@@ -2896,7 +3141,7 @@ function calculateItemsInView(ctx, params = {}) {
2896
3141
  const needNewContainers = [];
2897
3142
  const needNewContainersSet = /* @__PURE__ */ new Set();
2898
3143
  for (let i = startBuffered; i <= endBuffered; i++) {
2899
- const id = (_i = idCache[i]) != null ? _i : getId(state, i);
3144
+ const id = (_h = idCache[i]) != null ? _h : getId(state, i);
2900
3145
  if (!containerItemKeys.has(id)) {
2901
3146
  needNewContainersSet.add(i);
2902
3147
  needNewContainers.push(i);
@@ -2905,7 +3150,7 @@ function calculateItemsInView(ctx, params = {}) {
2905
3150
  if (alwaysRenderArr.length > 0) {
2906
3151
  for (const index of alwaysRenderArr) {
2907
3152
  if (index < 0 || index >= dataLength) continue;
2908
- const id = (_j = idCache[index]) != null ? _j : getId(state, index);
3153
+ const id = (_i = idCache[index]) != null ? _i : getId(state, index);
2909
3154
  if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
2910
3155
  needNewContainersSet.add(index);
2911
3156
  needNewContainers.push(index);
@@ -2943,7 +3188,7 @@ function calculateItemsInView(ctx, params = {}) {
2943
3188
  for (let idx = 0; idx < needNewContainers.length; idx++) {
2944
3189
  const i = needNewContainers[idx];
2945
3190
  const containerIndex = availableContainers[idx];
2946
- const id = (_k = idCache[i]) != null ? _k : getId(state, i);
3191
+ const id = (_j = idCache[i]) != null ? _j : getId(state, i);
2947
3192
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
2948
3193
  if (oldKey && oldKey !== id) {
2949
3194
  containerItemKeys.delete(oldKey);
@@ -2984,7 +3229,7 @@ function calculateItemsInView(ctx, params = {}) {
2984
3229
  if (alwaysRenderArr.length > 0) {
2985
3230
  for (const index of alwaysRenderArr) {
2986
3231
  if (index < 0 || index >= dataLength) continue;
2987
- const id = (_l = idCache[index]) != null ? _l : getId(state, index);
3232
+ const id = (_k = idCache[index]) != null ? _k : getId(state, index);
2988
3233
  const containerIndex = containerItemKeys.get(id);
2989
3234
  if (containerIndex !== void 0) {
2990
3235
  state.stickyContainerPool.add(containerIndex);
@@ -3024,14 +3269,13 @@ function calculateItemsInView(ctx, params = {}) {
3024
3269
  const itemIndex = indexByKey.get(itemKey);
3025
3270
  const item = data[itemIndex];
3026
3271
  if (item !== void 0) {
3027
- const id = (_m = idCache[itemIndex]) != null ? _m : getId(state, itemIndex);
3028
- const positionValue = positions.get(id);
3272
+ const positionValue = positions[itemIndex];
3029
3273
  if (positionValue === void 0) {
3030
3274
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
3031
3275
  } else {
3032
3276
  const position = (positionValue || 0) - scrollAdjustPending;
3033
- const column = columns.get(id) || 1;
3034
- const span = columnSpans.get(id) || 1;
3277
+ const column = columns[itemIndex] || 1;
3278
+ const span = columnSpans[itemIndex] || 1;
3035
3279
  const prevPos = peek$(ctx, `containerPosition${i}`);
3036
3280
  const prevColumn = peek$(ctx, `containerColumn${i}`);
3037
3281
  const prevSpan = peek$(ctx, `containerSpan${i}`);
@@ -3104,8 +3348,12 @@ function checkFinishedScrollFrame(ctx) {
3104
3348
  state.animFrameCheckFinishedScroll = void 0;
3105
3349
  const scroll = state.scrollPending;
3106
3350
  const adjust = state.scrollAdjustHandler.getAdjust();
3107
- const clampedTargetOffset = clampScrollOffset(ctx, scrollingTo.offset - (scrollingTo.viewOffset || 0));
3108
- const maxOffset = clampScrollOffset(ctx, scroll);
3351
+ const clampedTargetOffset = clampScrollOffset(
3352
+ ctx,
3353
+ scrollingTo.offset - (scrollingTo.viewOffset || 0),
3354
+ scrollingTo
3355
+ );
3356
+ const maxOffset = clampScrollOffset(ctx, scroll, scrollingTo);
3109
3357
  const diff1 = Math.abs(scroll - clampedTargetOffset);
3110
3358
  const diff2 = Math.abs(diff1 - adjust);
3111
3359
  const isNotOverscrolled = Math.abs(scroll - maxOffset) < 1;
@@ -3368,7 +3616,7 @@ function onScroll(ctx, event) {
3368
3616
  }
3369
3617
  let newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
3370
3618
  if (state.scrollingTo && state.scrollingTo.offset >= newScroll) {
3371
- const maxOffset = clampScrollOffset(ctx, newScroll);
3619
+ const maxOffset = clampScrollOffset(ctx, newScroll, state.scrollingTo);
3372
3620
  if (newScroll !== maxOffset && Math.abs(newScroll - maxOffset) > 1) {
3373
3621
  newScroll = maxOffset;
3374
3622
  scrollTo(ctx, {
@@ -3421,7 +3669,7 @@ var ScrollAdjustHandler = class {
3421
3669
  if ((scrollTarget == null ? void 0 : scrollTarget.index) !== void 0) {
3422
3670
  const currentOffset = calculateOffsetForIndex(this.ctx, scrollTarget.index);
3423
3671
  targetScroll = calculateOffsetWithOffsetPosition(this.ctx, currentOffset, scrollTarget);
3424
- targetScroll = clampScrollOffset(this.ctx, targetScroll);
3672
+ targetScroll = clampScrollOffset(this.ctx, targetScroll, scrollTarget);
3425
3673
  } else {
3426
3674
  targetScroll = clampScrollOffset(this.ctx, state.scroll + pending);
3427
3675
  }
@@ -3640,6 +3888,18 @@ function createColumnWrapperStyle(contentContainerStyle) {
3640
3888
  // src/utils/createImperativeHandle.ts
3641
3889
  function createImperativeHandle(ctx) {
3642
3890
  const state = ctx.state;
3891
+ const runScrollWithPromise = (run) => new Promise((resolve) => {
3892
+ var _a3;
3893
+ (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
3894
+ state.pendingScrollResolve = resolve;
3895
+ const didStartScroll = run();
3896
+ if (!didStartScroll || !state.scrollingTo) {
3897
+ if (state.pendingScrollResolve === resolve) {
3898
+ state.pendingScrollResolve = void 0;
3899
+ }
3900
+ resolve();
3901
+ }
3902
+ });
3643
3903
  const scrollIndexIntoView = (options) => {
3644
3904
  if (state) {
3645
3905
  const { index, ...rest } = options;
@@ -3651,11 +3911,36 @@ function createImperativeHandle(ctx) {
3651
3911
  index,
3652
3912
  viewPosition
3653
3913
  });
3914
+ return true;
3654
3915
  }
3655
3916
  }
3917
+ return false;
3656
3918
  };
3657
3919
  const refScroller = state.refScroller;
3920
+ const clearCaches = (options) => {
3921
+ var _a3, _b;
3922
+ const mode = (_a3 = options == null ? void 0 : options.mode) != null ? _a3 : "sizes";
3923
+ state.sizes.clear();
3924
+ state.sizesKnown.clear();
3925
+ for (const key in state.averageSizes) {
3926
+ delete state.averageSizes[key];
3927
+ }
3928
+ state.minIndexSizeChanged = 0;
3929
+ state.scrollForNextCalculateItemsInView = void 0;
3930
+ state.pendingTotalSize = void 0;
3931
+ state.totalSize = 0;
3932
+ set$(ctx, "totalSize", 0);
3933
+ if (mode === "full") {
3934
+ state.indexByKey.clear();
3935
+ state.idCache.length = 0;
3936
+ state.positions.length = 0;
3937
+ state.columns.length = 0;
3938
+ state.columnSpans.length = 0;
3939
+ }
3940
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
3941
+ };
3658
3942
  return {
3943
+ clearCaches,
3659
3944
  flashScrollIndicators: () => refScroller.current.flashScrollIndicators(),
3660
3945
  getNativeScrollRef: () => refScroller.current,
3661
3946
  getScrollableNode: () => refScroller.current.getScrollableNode(),
@@ -3674,8 +3959,11 @@ function createImperativeHandle(ctx) {
3674
3959
  isAtStart: state.isAtStart,
3675
3960
  listen: (signalName, cb) => listen$(ctx, signalName, cb),
3676
3961
  listenToPosition: (key, cb) => listenPosition$(ctx, key, cb),
3677
- positionAtIndex: (index) => state.positions.get(getId(state, index)),
3678
- positions: state.positions,
3962
+ positionAtIndex: (index) => state.positions[index],
3963
+ positionByKey: (key) => {
3964
+ const index = state.indexByKey.get(key);
3965
+ return index === void 0 ? void 0 : state.positions[index];
3966
+ },
3679
3967
  scroll: state.scroll,
3680
3968
  scrollLength: state.scrollLength,
3681
3969
  scrollVelocity: getScrollVelocity(state),
@@ -3688,15 +3976,17 @@ function createImperativeHandle(ctx) {
3688
3976
  state.contentInsetOverride = inset != null ? inset : void 0;
3689
3977
  updateScroll(ctx, state.scroll, true);
3690
3978
  },
3691
- scrollIndexIntoView,
3692
- scrollItemIntoView: ({ item, ...props }) => {
3979
+ scrollIndexIntoView: (options) => runScrollWithPromise(() => scrollIndexIntoView(options)),
3980
+ scrollItemIntoView: ({ item, ...props }) => runScrollWithPromise(() => {
3693
3981
  const data = state.props.data;
3694
3982
  const index = data.indexOf(item);
3695
3983
  if (index !== -1) {
3696
3984
  scrollIndexIntoView({ index, ...props });
3985
+ return true;
3697
3986
  }
3698
- },
3699
- scrollToEnd: (options) => {
3987
+ return false;
3988
+ }),
3989
+ scrollToEnd: (options) => runScrollWithPromise(() => {
3700
3990
  const data = state.props.data;
3701
3991
  const stylePaddingBottom = state.props.stylePaddingBottom;
3702
3992
  const index = data.length - 1;
@@ -3709,17 +3999,27 @@ function createImperativeHandle(ctx) {
3709
3999
  viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
3710
4000
  viewPosition: 1
3711
4001
  });
4002
+ return true;
3712
4003
  }
3713
- },
3714
- scrollToIndex: (params) => scrollToIndex(ctx, params),
3715
- scrollToItem: ({ item, ...props }) => {
4004
+ return false;
4005
+ }),
4006
+ scrollToIndex: (params) => runScrollWithPromise(() => {
4007
+ scrollToIndex(ctx, params);
4008
+ return true;
4009
+ }),
4010
+ scrollToItem: ({ item, ...props }) => runScrollWithPromise(() => {
3716
4011
  const data = state.props.data;
3717
4012
  const index = data.indexOf(item);
3718
4013
  if (index !== -1) {
3719
4014
  scrollToIndex(ctx, { index, ...props });
4015
+ return true;
3720
4016
  }
3721
- },
3722
- scrollToOffset: (params) => scrollTo(ctx, params),
4017
+ return false;
4018
+ }),
4019
+ scrollToOffset: (params) => runScrollWithPromise(() => {
4020
+ scrollTo(ctx, params);
4021
+ return true;
4022
+ }),
3723
4023
  setScrollProcessingEnabled: (enabled) => {
3724
4024
  state.scrollProcessingEnabled = enabled;
3725
4025
  },
@@ -3816,8 +4116,8 @@ function normalizeMaintainVisibleContentPosition(value) {
3816
4116
  if (value && typeof value === "object") {
3817
4117
  return {
3818
4118
  data: (_a3 = value.data) != null ? _a3 : false,
3819
- size: (_b = value.size) != null ? _b : true,
3820
- shouldRestorePosition: value.shouldRestorePosition
4119
+ shouldRestorePosition: value.shouldRestorePosition,
4120
+ size: (_b = value.size) != null ? _b : true
3821
4121
  };
3822
4122
  }
3823
4123
  if (value === false) {
@@ -3956,6 +4256,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3956
4256
  refreshControl,
3957
4257
  refreshing,
3958
4258
  refScrollView,
4259
+ renderScrollComponent,
3959
4260
  renderItem,
3960
4261
  scrollEventThrottle,
3961
4262
  snapToIndices,
@@ -3964,13 +4265,21 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3964
4265
  // TODOV3: Remove from v3 release
3965
4266
  style: styleProp,
3966
4267
  suggestEstimatedItemSize,
4268
+ useWindowScroll = false,
3967
4269
  viewabilityConfig,
3968
4270
  viewabilityConfigCallbackPairs,
3969
4271
  waitForInitialLayout = true,
3970
4272
  ...rest
3971
4273
  } = props;
3972
4274
  const animatedPropsInternal = props.animatedPropsInternal;
3973
- const { childrenMode } = rest;
4275
+ const positionComponentInternal = props.positionComponentInternal;
4276
+ const stickyPositionComponentInternal = props.stickyPositionComponentInternal;
4277
+ const {
4278
+ childrenMode,
4279
+ positionComponentInternal: _positionComponentInternal,
4280
+ stickyPositionComponentInternal: _stickyPositionComponentInternal,
4281
+ ...restProps
4282
+ } = rest;
3974
4283
  const contentContainerStyleBase = StyleSheet.flatten(contentContainerStyleProp);
3975
4284
  const shouldFlexGrow = alignItemsAtEnd && (horizontal ? (contentContainerStyleBase == null ? void 0 : contentContainerStyleBase.minWidth) == null : (contentContainerStyleBase == null ? void 0 : contentContainerStyleBase.minHeight) == null);
3976
4285
  const contentContainerStyle = {
@@ -4022,6 +4331,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4022
4331
  "stickyIndices has been renamed to stickyHeaderIndices. Please update your props to use stickyHeaderIndices."
4023
4332
  );
4024
4333
  }
4334
+ if (IS_DEV && useWindowScroll && renderScrollComponent) {
4335
+ warnDevOnce(
4336
+ "useWindowScrollRenderScrollComponent",
4337
+ "useWindowScroll is not supported when renderScrollComponent is provided."
4338
+ );
4339
+ }
4340
+ const useWindowScrollResolved = !!useWindowScroll && !renderScrollComponent;
4025
4341
  const refState = React3.useRef();
4026
4342
  const hasOverrideItemLayout = !!overrideItemLayout;
4027
4343
  const prevHasOverrideItemLayout = React3.useRef(hasOverrideItemLayout);
@@ -4031,11 +4347,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4031
4347
  ctx.state = {
4032
4348
  activeStickyIndex: -1,
4033
4349
  averageSizes: {},
4034
- columnSpans: /* @__PURE__ */ new Map(),
4035
- columns: /* @__PURE__ */ new Map(),
4350
+ columnSpans: [],
4351
+ columns: [],
4036
4352
  containerItemKeys: /* @__PURE__ */ new Map(),
4037
4353
  containerItemTypes: /* @__PURE__ */ new Map(),
4038
4354
  contentInsetOverride: void 0,
4355
+ dataChangeEpoch: 0,
4039
4356
  dataChangeNeedsScrollUpdate: false,
4040
4357
  didColumnsChange: false,
4041
4358
  didDataChange: false,
@@ -4067,10 +4384,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4067
4384
  minIndexSizeChanged: 0,
4068
4385
  nativeContentInset: void 0,
4069
4386
  nativeMarginTop: 0,
4070
- positions: /* @__PURE__ */ new Map(),
4387
+ positions: [],
4071
4388
  props: {},
4072
4389
  queuedCalculateItemsInView: 0,
4073
- refScroller: void 0,
4390
+ refScroller: { current: null },
4074
4391
  scroll: 0,
4075
4392
  scrollAdjustHandler: new ScrollAdjustHandler(ctx),
4076
4393
  scrollForNextCalculateItemsInView: void 0,
@@ -4086,6 +4403,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4086
4403
  startBuffered: -1,
4087
4404
  startNoBuffer: -1,
4088
4405
  startReachedSnapshot: void 0,
4406
+ startReachedSnapshotDataChangeEpoch: void 0,
4089
4407
  stickyContainerPool: /* @__PURE__ */ new Set(),
4090
4408
  stickyContainers: /* @__PURE__ */ new Map(),
4091
4409
  timeoutSizeMessage: 0,
@@ -4105,6 +4423,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4105
4423
  state.didColumnsChange = numColumnsProp !== state.props.numColumns;
4106
4424
  const didDataChangeLocal = state.props.dataVersion !== dataVersion || state.props.data !== dataProp && checkActualChange(state, dataProp, state.props.data);
4107
4425
  if (didDataChangeLocal) {
4426
+ state.dataChangeEpoch += 1;
4108
4427
  state.dataChangeNeedsScrollUpdate = true;
4109
4428
  state.didDataChange = true;
4110
4429
  state.previousData = state.props.data;
@@ -4141,14 +4460,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4141
4460
  onStartReachedThreshold,
4142
4461
  onStickyHeaderChange,
4143
4462
  overrideItemLayout,
4463
+ positionComponentInternal,
4144
4464
  recycleItems: !!recycleItems,
4145
4465
  renderItem,
4146
4466
  snapToIndices,
4147
4467
  stickyIndicesArr: stickyHeaderIndices != null ? stickyHeaderIndices : [],
4148
4468
  stickyIndicesSet: React3.useMemo(() => new Set(stickyHeaderIndices != null ? stickyHeaderIndices : []), [stickyHeaderIndices == null ? void 0 : stickyHeaderIndices.join(",")]),
4469
+ stickyPositionComponentInternal,
4149
4470
  stylePaddingBottom: stylePaddingBottomState,
4150
4471
  stylePaddingTop: stylePaddingTopState,
4151
- suggestEstimatedItemSize: !!suggestEstimatedItemSize
4472
+ suggestEstimatedItemSize: !!suggestEstimatedItemSize,
4473
+ useWindowScroll: useWindowScrollResolved
4152
4474
  };
4153
4475
  state.refScroller = refScroller;
4154
4476
  const memoizedLastItemKeys = React3.useMemo(() => {
@@ -4184,7 +4506,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4184
4506
  } else {
4185
4507
  const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, initialScroll.index) : 0;
4186
4508
  const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, initialScroll);
4187
- const clampedOffset = clampScrollOffset(ctx, resolvedOffset);
4509
+ const clampedOffset = clampScrollOffset(ctx, resolvedOffset, initialScroll);
4188
4510
  const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
4189
4511
  refState.current.initialScroll = updatedInitialScroll;
4190
4512
  state.initialScroll = updatedInitialScroll;
@@ -4207,7 +4529,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4207
4529
  "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."
4208
4530
  );
4209
4531
  refState.current.sizes.clear();
4210
- refState.current.positions.clear();
4532
+ refState.current.positions.length = 0;
4211
4533
  refState.current.totalSize = 0;
4212
4534
  set$(ctx, "totalSize", 0);
4213
4535
  }
@@ -4333,7 +4655,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4333
4655
  return /* @__PURE__ */ React3__namespace.createElement(React3__namespace.Fragment, null, /* @__PURE__ */ React3__namespace.createElement(
4334
4656
  ListComponent,
4335
4657
  {
4336
- ...rest,
4658
+ ...restProps,
4337
4659
  alignItemsAtEnd,
4338
4660
  canRender,
4339
4661
  contentContainerStyle,
@@ -4359,18 +4681,28 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
4359
4681
  }
4360
4682
  ),
4361
4683
  refScrollView: combinedRef,
4684
+ renderScrollComponent,
4362
4685
  scrollAdjustHandler: (_d = refState.current) == null ? void 0 : _d.scrollAdjustHandler,
4363
4686
  scrollEventThrottle: 0,
4364
4687
  snapToIndices,
4365
4688
  stickyHeaderIndices,
4366
4689
  style,
4367
4690
  updateItemSize: fns.updateItemSize,
4691
+ useWindowScroll: useWindowScrollResolved,
4368
4692
  waitForInitialLayout
4369
4693
  }
4370
4694
  ), IS_DEV && ENABLE_DEBUG_VIEW);
4371
4695
  });
4372
4696
 
4373
- exports.LegendList = LegendList;
4697
+ // src/index.ts
4698
+ var LegendList3 = LegendList;
4699
+ if (IS_DEV) {
4700
+ console.warn(
4701
+ "[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."
4702
+ );
4703
+ }
4704
+
4705
+ exports.LegendList = LegendList3;
4374
4706
  exports.typedForwardRef = typedForwardRef;
4375
4707
  exports.typedMemo = typedMemo;
4376
4708
  exports.useIsLastItem = useIsLastItem;