@legendapp/list 3.0.0-beta.4 → 3.0.0-beta.40

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 (45) hide show
  1. package/.DS_Store +0 -0
  2. package/CHANGELOG.md +19 -0
  3. package/README.md +8 -1
  4. package/animated.d.ts +607 -5
  5. package/animated.js +2 -2
  6. package/animated.mjs +1 -1
  7. package/index.d.ts +1192 -11
  8. package/index.js +1989 -946
  9. package/index.mjs +1988 -947
  10. package/index.native.js +1758 -866
  11. package/index.native.mjs +1740 -850
  12. package/keyboard-test.d.ts +206 -0
  13. package/keyboard-test.js +34 -0
  14. package/keyboard-test.mjs +13 -0
  15. package/keyboard.d.ts +206 -8
  16. package/keyboard.js +340 -32
  17. package/keyboard.mjs +343 -34
  18. package/package.json +52 -1
  19. package/{types-JPHClxiw.d.mts → react-native.d.ts} +399 -154
  20. package/{section-list.native.js → react-native.js} +1838 -1094
  21. package/{section-list.native.mjs → react-native.mjs} +1811 -1077
  22. package/{types-JPHClxiw.d.ts → react.d.ts} +456 -154
  23. package/react.js +4812 -0
  24. package/react.mjs +4782 -0
  25. package/reanimated.d.ts +618 -7
  26. package/reanimated.js +156 -30
  27. package/reanimated.mjs +155 -29
  28. package/section-list.d.ts +607 -5
  29. package/section-list.js +38 -3679
  30. package/section-list.mjs +34 -3676
  31. package/animated.d.mts +0 -9
  32. package/index.d.mts +0 -23
  33. package/index.native.d.mts +0 -23
  34. package/index.native.d.ts +0 -23
  35. package/keyboard-controller.d.mts +0 -12
  36. package/keyboard-controller.d.ts +0 -12
  37. package/keyboard-controller.js +0 -69
  38. package/keyboard-controller.mjs +0 -48
  39. package/keyboard.d.mts +0 -13
  40. package/reanimated.d.mts +0 -18
  41. package/section-list.d.mts +0 -113
  42. package/section-list.native.d.mts +0 -113
  43. package/section-list.native.d.ts +0 -113
  44. package/types-YNdphn_A.d.mts +0 -670
  45. package/types-YNdphn_A.d.ts +0 -670
package/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  var React3 = require('react');
4
4
  var shim = require('use-sync-external-store/shim');
5
- var reactDom = require('react-dom');
5
+ var ReactDOM = require('react-dom');
6
6
 
7
7
  function _interopNamespace(e) {
8
8
  if (e && e.__esModule) return e;
@@ -23,6 +23,7 @@ function _interopNamespace(e) {
23
23
  }
24
24
 
25
25
  var React3__namespace = /*#__PURE__*/_interopNamespace(React3);
26
+ var ReactDOM__namespace = /*#__PURE__*/_interopNamespace(ReactDOM);
26
27
 
27
28
  // src/components/LegendList.tsx
28
29
  React3.forwardRef(function AnimatedView2(props, ref) {
@@ -33,31 +34,63 @@ var View = React3.forwardRef(function View2(props, ref) {
33
34
  });
34
35
  var Text = View;
35
36
 
37
+ // src/state/getContentInsetEnd.ts
38
+ function getContentInsetEnd(state) {
39
+ var _a3;
40
+ const { props } = state;
41
+ const horizontal = props.horizontal;
42
+ const contentInset = props.contentInset;
43
+ const baseInset = contentInset != null ? contentInset : state.nativeContentInset;
44
+ const overrideInset = (_a3 = state.contentInsetOverride) != null ? _a3 : void 0;
45
+ if (overrideInset) {
46
+ const mergedInset = { bottom: 0, right: 0, ...baseInset, ...overrideInset };
47
+ return (horizontal ? mergedInset.right : mergedInset.bottom) || 0;
48
+ }
49
+ if (baseInset) {
50
+ return (horizontal ? baseInset.right : baseInset.bottom) || 0;
51
+ }
52
+ return 0;
53
+ }
54
+
55
+ // src/state/getContentSize.ts
56
+ function getContentSize(ctx) {
57
+ var _a3;
58
+ const { values, state } = ctx;
59
+ const stylePaddingTop = values.get("stylePaddingTop") || 0;
60
+ const stylePaddingBottom = state.props.stylePaddingBottom || 0;
61
+ const headerSize = values.get("headerSize") || 0;
62
+ const footerSize = values.get("footerSize") || 0;
63
+ const contentInsetBottom = getContentInsetEnd(state);
64
+ const totalSize = (_a3 = state.pendingTotalSize) != null ? _a3 : values.get("totalSize");
65
+ return headerSize + footerSize + totalSize + stylePaddingTop + stylePaddingBottom + (contentInsetBottom || 0);
66
+ }
67
+
36
68
  // src/platform/Animated.tsx
37
69
  var createAnimatedValue = (value) => value;
38
70
 
39
71
  // src/state/state.tsx
40
72
  var ContextState = React3__namespace.createContext(null);
73
+ var contextNum = 0;
41
74
  function StateProvider({ children }) {
42
75
  const [value] = React3__namespace.useState(() => ({
43
76
  animatedScrollY: createAnimatedValue(0),
44
77
  columnWrapperStyle: void 0,
45
- internalState: void 0,
78
+ contextNum: contextNum++,
46
79
  listeners: /* @__PURE__ */ new Map(),
47
80
  mapViewabilityAmountCallbacks: /* @__PURE__ */ new Map(),
48
81
  mapViewabilityAmountValues: /* @__PURE__ */ new Map(),
49
82
  mapViewabilityCallbacks: /* @__PURE__ */ new Map(),
50
83
  mapViewabilityConfigStates: /* @__PURE__ */ new Map(),
51
84
  mapViewabilityValues: /* @__PURE__ */ new Map(),
85
+ positionListeners: /* @__PURE__ */ new Map(),
86
+ state: void 0,
52
87
  values: /* @__PURE__ */ new Map([
53
- ["alignItemsPaddingTop", 0],
54
88
  ["stylePaddingTop", 0],
55
89
  ["headerSize", 0],
56
90
  ["numContainers", 0],
57
- ["activeStickyIndex", void 0],
91
+ ["activeStickyIndex", -1],
58
92
  ["totalSize", 0],
59
- ["scrollAdjustPending", 0],
60
- ["scrollingTo", void 0]
93
+ ["scrollAdjustPending", 0]
61
94
  ]),
62
95
  viewRefs: /* @__PURE__ */ new Map()
63
96
  }));
@@ -125,15 +158,24 @@ function set$(ctx, signalName, value) {
125
158
  }
126
159
  }
127
160
  }
128
- function getContentSize(ctx) {
129
- var _a3, _b;
130
- const { values, internalState } = ctx;
131
- const stylePaddingTop = values.get("stylePaddingTop") || 0;
132
- const stylePaddingBottom = (internalState == null ? void 0 : internalState.props.stylePaddingBottom) || 0;
133
- const headerSize = values.get("headerSize") || 0;
134
- const footerSize = values.get("footerSize") || 0;
135
- const totalSize = (_b = (_a3 = ctx.internalState) == null ? void 0 : _a3.pendingTotalSize) != null ? _b : values.get("totalSize");
136
- return headerSize + footerSize + totalSize + stylePaddingTop + stylePaddingBottom;
161
+ function listenPosition$(ctx, key, cb) {
162
+ const { positionListeners } = ctx;
163
+ let setListeners = positionListeners.get(key);
164
+ if (!setListeners) {
165
+ setListeners = /* @__PURE__ */ new Set();
166
+ positionListeners.set(key, setListeners);
167
+ }
168
+ setListeners.add(cb);
169
+ return () => setListeners.delete(cb);
170
+ }
171
+ function notifyPosition$(ctx, key, value) {
172
+ const { positionListeners } = ctx;
173
+ const setListeners = positionListeners.get(key);
174
+ if (setListeners) {
175
+ for (const listener of setListeners) {
176
+ listener(value);
177
+ }
178
+ }
137
179
  }
138
180
  function useArr$(signalNames) {
139
181
  const ctx = React3__namespace.useContext(ContextState);
@@ -152,7 +194,7 @@ function useSelector$(signalName, selector) {
152
194
  var DebugRow = ({ children }) => {
153
195
  return /* @__PURE__ */ React3__namespace.createElement(View, { style: { alignItems: "center", flexDirection: "row", justifyContent: "space-between" } }, children);
154
196
  };
155
- var DebugView = React3__namespace.memo(function DebugView2({ state }) {
197
+ React3__namespace.memo(function DebugView2({ state }) {
156
198
  const ctx = useStateContext();
157
199
  const [totalSize = 0, scrollAdjust = 0, rawScroll = 0, scroll = 0, _numContainers = 0, _numContainersPooled = 0] = useArr$([
158
200
  "totalSize",
@@ -204,14 +246,14 @@ var _a;
204
246
  var envMode = typeof process !== "undefined" && typeof process.env === "object" && process.env ? (_a = process.env.NODE_ENV) != null ? _a : process.env.MODE : void 0;
205
247
  var processDev = typeof envMode === "string" ? envMode.toLowerCase() !== "production" : void 0;
206
248
  var _a2;
207
- var IS_DEV = (_a2 = metroDev != null ? metroDev : processDev) != null ? _a2 : false;
249
+ var IS_DEV = (_a2 = processDev != null ? processDev : metroDev) != null ? _a2 : false;
208
250
 
209
251
  // src/constants.ts
210
252
  var POSITION_OUT_OF_VIEW = -1e7;
211
253
  var ENABLE_DEVMODE = IS_DEV && false;
212
254
  var ENABLE_DEBUG_VIEW = IS_DEV && false;
213
- var typedForwardRef = React3.forwardRef;
214
- var typedMemo = React3.memo;
255
+ var typedForwardRef = React3__namespace.forwardRef;
256
+ var typedMemo = React3__namespace.memo;
215
257
 
216
258
  // src/utils/helpers.ts
217
259
  function isFunction(obj) {
@@ -244,6 +286,11 @@ function extractPadding(style, contentContainerStyle, type) {
244
286
  return getPadding(style, type) + getPadding(contentContainerStyle, type);
245
287
  }
246
288
  function findContainerId(ctx, key) {
289
+ var _a3, _b;
290
+ const directMatch = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.containerItemKeys) == null ? void 0 : _b.get(key);
291
+ if (directMatch !== void 0) {
292
+ return directMatch;
293
+ }
247
294
  const numContainers = peek$(ctx, "numContainers");
248
295
  for (let i = 0; i < numContainers; i++) {
249
296
  const itemKey = peek$(ctx, `containerItemKey${i}`);
@@ -255,12 +302,12 @@ function findContainerId(ctx, key) {
255
302
  }
256
303
 
257
304
  // src/components/PositionView.tsx
258
- var PositionViewState = typedMemo(function PositionView({
305
+ var PositionViewState = typedMemo(function PositionViewState2({
259
306
  id,
260
307
  horizontal,
261
308
  style,
262
309
  refView,
263
- ...rest
310
+ ...props
264
311
  }) {
265
312
  const [position = POSITION_OUT_OF_VIEW] = useArr$([`containerPosition${id}`]);
266
313
  const base = {
@@ -268,7 +315,15 @@ var PositionViewState = typedMemo(function PositionView({
268
315
  };
269
316
  const composed = isArray(style) ? Object.assign({}, ...style) : style;
270
317
  const combinedStyle = horizontal ? { ...base, ...composed, left: position } : { ...base, ...composed, top: position };
271
- return /* @__PURE__ */ React3__namespace.createElement("div", { ref: refView, style: combinedStyle, ...rest });
318
+ const {
319
+ animatedScrollY: _animatedScrollY,
320
+ index,
321
+ onLayout: _onLayout,
322
+ onLayoutChange: _onLayoutChange,
323
+ stickyHeaderConfig: _stickyHeaderConfig,
324
+ ...webProps
325
+ } = props;
326
+ return /* @__PURE__ */ React3__namespace.createElement("div", { "data-index": index, ref: refView, ...webProps, style: combinedStyle });
272
327
  });
273
328
  var PositionViewSticky = typedMemo(function PositionViewSticky2({
274
329
  id,
@@ -276,14 +331,15 @@ var PositionViewSticky = typedMemo(function PositionViewSticky2({
276
331
  style,
277
332
  refView,
278
333
  index,
279
- stickyOffset,
280
334
  animatedScrollY: _animatedScrollY,
335
+ stickyHeaderConfig,
336
+ onLayout: _onLayout,
337
+ onLayoutChange: _onLayoutChange,
281
338
  children,
282
- ...rest
339
+ ...webProps
283
340
  }) {
284
- const [position = POSITION_OUT_OF_VIEW, headerSize = 0, activeStickyIndex] = useArr$([
341
+ const [position = POSITION_OUT_OF_VIEW, activeStickyIndex] = useArr$([
285
342
  `containerPosition${id}`,
286
- "headerSize",
287
343
  "activeStickyIndex"
288
344
  ]);
289
345
  const base = {
@@ -300,7 +356,8 @@ var PositionViewSticky = typedMemo(function PositionViewSticky2({
300
356
  var _a3;
301
357
  const styleBase = { ...base, ...composed };
302
358
  delete styleBase.transform;
303
- const offset = (_a3 = stickyOffset != null ? stickyOffset : headerSize) != null ? _a3 : 0;
359
+ const stickyConfigOffset = (_a3 = stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset) != null ? _a3 : 0;
360
+ const offset = stickyConfigOffset != null ? stickyConfigOffset : 0;
304
361
  const isActive = activeStickyIndex === index;
305
362
  styleBase.position = isActive ? "sticky" : "absolute";
306
363
  styleBase.zIndex = index + 1e3;
@@ -310,61 +367,89 @@ var PositionViewSticky = typedMemo(function PositionViewSticky2({
310
367
  styleBase.top = isActive ? offset : position;
311
368
  }
312
369
  return styleBase;
313
- }, [composed, horizontal, position, index, stickyOffset, headerSize, activeStickyIndex]);
314
- return /* @__PURE__ */ React3__namespace.createElement("div", { ref: refView, style: viewStyle, ...rest }, children);
370
+ }, [composed, horizontal, position, index, activeStickyIndex, stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset]);
371
+ return /* @__PURE__ */ React3__namespace.createElement(
372
+ "div",
373
+ {
374
+ "data-index": index,
375
+ ref: refView,
376
+ style: viewStyle,
377
+ ...webProps
378
+ },
379
+ children
380
+ );
315
381
  });
316
- var PositionView2 = PositionViewState;
317
-
318
- // src/constants-platform.ts
319
- var IsNewArchitecture = true;
382
+ var PositionView = PositionViewState;
320
383
  function useInit(cb) {
321
384
  React3.useState(() => cb());
322
385
  }
323
386
 
324
387
  // src/state/ContextContainer.ts
325
388
  var ContextContainer = React3.createContext(null);
389
+ function useContextContainer() {
390
+ return React3.useContext(ContextContainer);
391
+ }
326
392
  function useViewability(callback, configId) {
327
393
  const ctx = useStateContext();
328
- const { containerId } = React3.useContext(ContextContainer);
329
- const key = containerId + (configId != null ? configId : "");
394
+ const containerContext = useContextContainer();
330
395
  useInit(() => {
396
+ if (!containerContext) {
397
+ return;
398
+ }
399
+ const { containerId } = containerContext;
400
+ const key = containerId + (configId != null ? configId : "");
331
401
  const value = ctx.mapViewabilityValues.get(key);
332
402
  if (value) {
333
403
  callback(value);
334
404
  }
335
405
  });
336
- ctx.mapViewabilityCallbacks.set(key, callback);
337
- React3.useEffect(
338
- () => () => {
406
+ React3.useEffect(() => {
407
+ if (!containerContext) {
408
+ return;
409
+ }
410
+ const { containerId } = containerContext;
411
+ const key = containerId + (configId != null ? configId : "");
412
+ ctx.mapViewabilityCallbacks.set(key, callback);
413
+ return () => {
339
414
  ctx.mapViewabilityCallbacks.delete(key);
340
- },
341
- []
342
- );
415
+ };
416
+ }, [ctx, callback, configId, containerContext]);
343
417
  }
344
418
  function useViewabilityAmount(callback) {
345
419
  const ctx = useStateContext();
346
- const { containerId } = React3.useContext(ContextContainer);
420
+ const containerContext = useContextContainer();
347
421
  useInit(() => {
422
+ if (!containerContext) {
423
+ return;
424
+ }
425
+ const { containerId } = containerContext;
348
426
  const value = ctx.mapViewabilityAmountValues.get(containerId);
349
427
  if (value) {
350
428
  callback(value);
351
429
  }
352
430
  });
353
- ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
354
- React3.useEffect(
355
- () => () => {
431
+ React3.useEffect(() => {
432
+ if (!containerContext) {
433
+ return;
434
+ }
435
+ const { containerId } = containerContext;
436
+ ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
437
+ return () => {
356
438
  ctx.mapViewabilityAmountCallbacks.delete(containerId);
357
- },
358
- []
359
- );
439
+ };
440
+ }, [ctx, callback, containerContext]);
360
441
  }
361
442
  function useRecyclingEffect(effect) {
362
- const { index, value } = React3.useContext(ContextContainer);
443
+ const containerContext = useContextContainer();
363
444
  const prevValues = React3.useRef({
364
445
  prevIndex: void 0,
365
446
  prevItem: void 0
366
447
  });
367
448
  React3.useEffect(() => {
449
+ if (!containerContext) {
450
+ return;
451
+ }
452
+ const { index, value } = containerContext;
368
453
  let ret;
369
454
  if (prevValues.current.prevIndex !== void 0 && prevValues.current.prevItem !== void 0) {
370
455
  ret = effect({
@@ -379,48 +464,73 @@ function useRecyclingEffect(effect) {
379
464
  prevItem: value
380
465
  };
381
466
  return ret;
382
- }, [index, value, effect]);
467
+ }, [effect, containerContext]);
383
468
  }
384
469
  function useRecyclingState(valueOrFun) {
385
- const { index, value, itemKey, triggerLayout } = React3.useContext(ContextContainer);
386
- const refState = React3.useRef({
387
- itemKey: null,
388
- value: null
470
+ var _a3, _b;
471
+ const containerContext = useContextContainer();
472
+ const computeValue = (ctx) => {
473
+ if (isFunction(valueOrFun)) {
474
+ const initializer = valueOrFun;
475
+ return ctx ? initializer({
476
+ index: ctx.index,
477
+ item: ctx.value,
478
+ prevIndex: void 0,
479
+ prevItem: void 0
480
+ }) : initializer();
481
+ }
482
+ return valueOrFun;
483
+ };
484
+ const [stateValue, setStateValue] = React3.useState(() => {
485
+ return computeValue(containerContext);
389
486
  });
390
- const [_, setRenderNum] = React3.useState(0);
391
- const state = refState.current;
392
- if (state.itemKey !== itemKey) {
393
- state.itemKey = itemKey;
394
- state.value = isFunction(valueOrFun) ? valueOrFun({
395
- index,
396
- item: value,
397
- prevIndex: void 0,
398
- prevItem: void 0
399
- }) : valueOrFun;
487
+ const prevItemKeyRef = React3.useRef((_a3 = containerContext == null ? void 0 : containerContext.itemKey) != null ? _a3 : null);
488
+ const currentItemKey = (_b = containerContext == null ? void 0 : containerContext.itemKey) != null ? _b : null;
489
+ if (currentItemKey !== null && prevItemKeyRef.current !== currentItemKey) {
490
+ prevItemKeyRef.current = currentItemKey;
491
+ setStateValue(computeValue(containerContext));
400
492
  }
493
+ const triggerLayout = containerContext == null ? void 0 : containerContext.triggerLayout;
401
494
  const setState = React3.useCallback(
402
495
  (newState) => {
403
- state.value = isFunction(newState) ? newState(state.value) : newState;
404
- setRenderNum((v) => v + 1);
496
+ if (!triggerLayout) {
497
+ return;
498
+ }
499
+ setStateValue((prevValue) => {
500
+ return isFunction(newState) ? newState(prevValue) : newState;
501
+ });
405
502
  triggerLayout();
406
503
  },
407
- [triggerLayout, state]
504
+ [triggerLayout]
408
505
  );
409
- return [state.value, setState];
506
+ return [stateValue, setState];
410
507
  }
411
508
  function useIsLastItem() {
412
- const { itemKey } = React3.useContext(ContextContainer);
413
- const isLast = useSelector$("lastItemKeys", (lastItemKeys) => (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false);
509
+ const containerContext = useContextContainer();
510
+ const isLast = useSelector$("lastItemKeys", (lastItemKeys) => {
511
+ if (containerContext) {
512
+ const { itemKey } = containerContext;
513
+ if (!isNullOrUndefined(itemKey)) {
514
+ return (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false;
515
+ }
516
+ }
517
+ return false;
518
+ });
414
519
  return isLast;
415
520
  }
416
521
  function useListScrollSize() {
417
522
  const [scrollSize] = useArr$(["scrollSize"]);
418
523
  return scrollSize;
419
524
  }
525
+ var noop = () => {
526
+ };
420
527
  function useSyncLayout() {
421
- {
422
- const { triggerLayout: syncLayout } = React3.useContext(ContextContainer);
528
+ const containerContext = useContextContainer();
529
+ if (containerContext) {
530
+ const { triggerLayout: syncLayout } = containerContext;
423
531
  return syncLayout;
532
+ } else {
533
+ return noop;
424
534
  }
425
535
  }
426
536
 
@@ -466,10 +576,9 @@ function createResizeObserver(element, callback) {
466
576
  }
467
577
  callbacks.add(callback);
468
578
  return () => {
469
- const callbacks2 = callbackMap.get(element);
470
- if (callbacks2) {
471
- callbacks2.delete(callback);
472
- if (callbacks2.size === 0) {
579
+ if (callbacks) {
580
+ callbacks.delete(callback);
581
+ if (callbacks.size === 0) {
473
582
  callbackMap.delete(element);
474
583
  observer.unobserve(element);
475
584
  }
@@ -481,7 +590,8 @@ function createResizeObserver(element, callback) {
481
590
  function useOnLayoutSync({
482
591
  ref,
483
592
  onLayoutProp,
484
- onLayoutChange
593
+ onLayoutChange,
594
+ webLayoutResync
485
595
  }, deps) {
486
596
  React3.useLayoutEffect(() => {
487
597
  var _a3, _b;
@@ -504,10 +614,12 @@ function useOnLayoutSync({
504
614
  return createResizeObserver(element, (entry) => {
505
615
  var _a4;
506
616
  const target = entry.target instanceof HTMLElement ? entry.target : void 0;
507
- const rect2 = (_a4 = entry.contentRect) != null ? _a4 : target == null ? void 0 : target.getBoundingClientRect();
508
- if (rect2.width !== prevRect.width || rect2.height !== prevRect.height) {
509
- prevRect = rect2;
510
- emit(toLayout(rect2), false);
617
+ const rectObserved = (_a4 = entry.contentRect) != null ? _a4 : target == null ? void 0 : target.getBoundingClientRect();
618
+ const didSizeChange = rectObserved.width !== prevRect.width || rectObserved.height !== prevRect.height;
619
+ const shouldResyncLayout = !!(webLayoutResync == null ? void 0 : webLayoutResync());
620
+ if (didSizeChange || shouldResyncLayout) {
621
+ prevRect = rectObserved;
622
+ emit(toLayout(rectObserved), false);
511
623
  }
512
624
  });
513
625
  }, deps || []);
@@ -525,6 +637,30 @@ function toLayout(rect) {
525
637
  };
526
638
  }
527
639
 
640
+ // src/platform/Platform.ts
641
+ var Platform = {
642
+ // Widen the type to avoid unreachable-branch lints in cross-platform code that compares against other OSes
643
+ OS: "web"
644
+ };
645
+
646
+ // src/utils/hasActiveMVCPAnchorLock.ts
647
+ function hasActiveMVCPAnchorLock(state) {
648
+ const lock = state.mvcpAnchorLock;
649
+ if (!lock) {
650
+ return false;
651
+ }
652
+ if (Date.now() > lock.expiresAt) {
653
+ state.mvcpAnchorLock = void 0;
654
+ return false;
655
+ }
656
+ return true;
657
+ }
658
+
659
+ // src/utils/isInMVCPActiveMode.ts
660
+ function isInMVCPActiveMode(state) {
661
+ return state.dataChangeNeedsScrollUpdate || hasActiveMVCPAnchorLock(state);
662
+ }
663
+
528
664
  // src/components/Container.tsx
529
665
  var Container = typedMemo(function Container2({
530
666
  id,
@@ -532,22 +668,27 @@ var Container = typedMemo(function Container2({
532
668
  horizontal,
533
669
  getRenderedItem: getRenderedItem2,
534
670
  updateItemSize: updateItemSize2,
535
- ItemSeparatorComponent
671
+ ItemSeparatorComponent,
672
+ stickyHeaderConfig
536
673
  }) {
537
674
  const ctx = useStateContext();
538
675
  const { columnWrapperStyle, animatedScrollY } = ctx;
539
- const [column = 0, data, itemKey, numColumns, extraData, isSticky, stickyOffset] = useArr$([
676
+ const positionComponentInternal = ctx.state.props.positionComponentInternal;
677
+ const stickyPositionComponentInternal = ctx.state.props.stickyPositionComponentInternal;
678
+ const [column = 0, span = 1, data, itemKey, numColumns = 1, extraData, isSticky] = useArr$([
540
679
  `containerColumn${id}`,
680
+ `containerSpan${id}`,
541
681
  `containerItemData${id}`,
542
682
  `containerItemKey${id}`,
543
683
  "numColumns",
544
684
  "extraData",
545
- `containerSticky${id}`,
546
- `containerStickyOffset${id}`
685
+ `containerSticky${id}`
547
686
  ]);
548
687
  const itemLayoutRef = React3.useRef({
688
+ didLayout: false,
549
689
  horizontal,
550
690
  itemKey,
691
+ pendingShrinkToken: 0,
551
692
  updateItemSize: updateItemSize2
552
693
  });
553
694
  itemLayoutRef.current.horizontal = horizontal;
@@ -555,9 +696,10 @@ var Container = typedMemo(function Container2({
555
696
  itemLayoutRef.current.updateItemSize = updateItemSize2;
556
697
  const ref = React3.useRef(null);
557
698
  const [layoutRenderCount, forceLayoutRender] = React3.useState(0);
558
- const otherAxisPos = numColumns > 1 ? `${(column - 1) / numColumns * 100}%` : 0;
559
- const otherAxisSize = numColumns > 1 ? `${1 / numColumns * 100}%` : void 0;
560
- const didLayoutRef = React3.useRef(false);
699
+ const resolvedColumn = column > 0 ? column : 1;
700
+ const resolvedSpan = Math.min(Math.max(span || 1, 1), numColumns);
701
+ const otherAxisPos = numColumns > 1 ? `${(resolvedColumn - 1) / numColumns * 100}%` : 0;
702
+ const otherAxisSize = numColumns > 1 ? `${resolvedSpan / numColumns * 100}%` : void 0;
561
703
  const style = React3.useMemo(() => {
562
704
  let paddingStyles;
563
705
  if (columnWrapperStyle) {
@@ -611,19 +753,41 @@ var Container = typedMemo(function Container2({
611
753
  const {
612
754
  horizontal: currentHorizontal,
613
755
  itemKey: currentItemKey,
614
- updateItemSize: updateItemSizeFn
756
+ updateItemSize: updateItemSizeFn,
757
+ lastSize,
758
+ pendingShrinkToken
615
759
  } = itemLayoutRef.current;
616
760
  if (isNullOrUndefined(currentItemKey)) {
617
761
  return;
618
762
  }
619
- didLayoutRef.current = true;
763
+ itemLayoutRef.current.didLayout = true;
620
764
  let layout = rectangle;
621
- roundSize(rectangle[currentHorizontal ? "width" : "height"]);
765
+ const axis = currentHorizontal ? "width" : "height";
766
+ const size = roundSize(rectangle[axis]);
767
+ const prevSize = lastSize ? roundSize(lastSize[axis]) : void 0;
622
768
  const doUpdate = () => {
623
- itemLayoutRef.current.lastSize = { height: layout.height, width: layout.width };
769
+ itemLayoutRef.current.lastSize = layout;
624
770
  updateItemSizeFn(currentItemKey, layout);
625
- didLayoutRef.current = true;
771
+ itemLayoutRef.current.didLayout = true;
626
772
  };
773
+ const shouldDeferWebShrinkLayoutUpdate = !isInMVCPActiveMode(ctx.state) && prevSize !== void 0 && size + 1 < prevSize;
774
+ if (shouldDeferWebShrinkLayoutUpdate) {
775
+ const token = pendingShrinkToken + 1;
776
+ itemLayoutRef.current.pendingShrinkToken = token;
777
+ requestAnimationFrame(() => {
778
+ var _a4;
779
+ if (itemLayoutRef.current.pendingShrinkToken !== token) {
780
+ return;
781
+ }
782
+ const element = ref.current;
783
+ const rect = (_a4 = element == null ? void 0 : element.getBoundingClientRect) == null ? void 0 : _a4.call(element);
784
+ if (rect) {
785
+ layout = { height: rect.height, width: rect.width };
786
+ }
787
+ doUpdate();
788
+ });
789
+ return;
790
+ }
627
791
  {
628
792
  doUpdate();
629
793
  }
@@ -631,11 +795,12 @@ var Container = typedMemo(function Container2({
631
795
  const { onLayout } = useOnLayoutSync(
632
796
  {
633
797
  onLayoutChange,
634
- ref
798
+ ref,
799
+ webLayoutResync: () => isInMVCPActiveMode(ctx.state)
635
800
  },
636
801
  [itemKey, layoutRenderCount]
637
802
  );
638
- const PositionComponent = isSticky ? PositionViewSticky : PositionView2;
803
+ const PositionComponent = isSticky ? stickyPositionComponentInternal ? stickyPositionComponentInternal : PositionViewSticky : positionComponentInternal ? positionComponentInternal : PositionView;
639
804
  return /* @__PURE__ */ React3__namespace.createElement(
640
805
  PositionComponent,
641
806
  {
@@ -646,23 +811,21 @@ var Container = typedMemo(function Container2({
646
811
  key: recycleItems ? void 0 : itemKey,
647
812
  onLayout,
648
813
  refView: ref,
649
- stickyOffset: isSticky ? stickyOffset : void 0,
814
+ stickyHeaderConfig,
650
815
  style
651
816
  },
652
817
  /* @__PURE__ */ React3__namespace.createElement(ContextContainer.Provider, { value: contextValue }, renderedItem, renderedItemInfo && ItemSeparatorComponent && /* @__PURE__ */ React3__namespace.createElement(Separator, { ItemSeparatorComponent, leadingItem: renderedItemInfo.item }))
653
818
  );
654
819
  });
655
820
 
656
- // src/platform/Platform.ts
657
- var Platform = {
658
- // Widen the type to avoid unreachable-branch lints in cross-platform code that compares against other OSes
659
- OS: "web"
660
- };
661
-
662
821
  // src/utils/reordering.ts
663
822
  var mapFn = (element) => {
664
- const indexStr = element.getAttribute("index");
665
- return [element, indexStr === null ? null : parseInt(indexStr)];
823
+ const indexStr = element.getAttribute("data-index");
824
+ if (indexStr === null) {
825
+ return [element, null];
826
+ }
827
+ const index = Number.parseInt(indexStr, 10);
828
+ return [element, Number.isNaN(index) ? null : index];
666
829
  };
667
830
  function sortDOMElements(container) {
668
831
  const elements = Array.from(container.children);
@@ -805,7 +968,8 @@ var Containers = typedMemo(function Containers2({
805
968
  ItemSeparatorComponent,
806
969
  waitForInitialLayout,
807
970
  updateItemSize: updateItemSize2,
808
- getRenderedItem: getRenderedItem2
971
+ getRenderedItem: getRenderedItem2,
972
+ stickyHeaderConfig
809
973
  }) {
810
974
  const [numContainers, numColumns] = useArr$(["numContainersPooled", "numColumns"]);
811
975
  const containers = [];
@@ -820,6 +984,7 @@ var Containers = typedMemo(function Containers2({
820
984
  id: i,
821
985
  key: i,
822
986
  recycleItems,
987
+ stickyHeaderConfig,
823
988
  updateItemSize: updateItemSize2
824
989
  }
825
990
  )
@@ -827,24 +992,6 @@ var Containers = typedMemo(function Containers2({
827
992
  }
828
993
  return /* @__PURE__ */ React3__namespace.createElement(ContainersInner, { horizontal, numColumns, waitForInitialLayout }, containers);
829
994
  });
830
- function DevNumbers() {
831
- return IS_DEV && React3__namespace.memo(function DevNumbers2() {
832
- return Array.from({ length: 100 }).map((_, index) => /* @__PURE__ */ React3__namespace.createElement(
833
- "div",
834
- {
835
- key: index,
836
- style: {
837
- height: 100,
838
- pointerEvents: "none",
839
- position: "absolute",
840
- top: index * 100,
841
- width: "100%"
842
- }
843
- },
844
- /* @__PURE__ */ React3__namespace.createElement("div", { style: { color: "red" } }, index * 100)
845
- ));
846
- });
847
- }
848
995
 
849
996
  // src/platform/StyleSheet.tsx
850
997
  function flattenStyles(styles) {
@@ -858,6 +1005,89 @@ var StyleSheet = {
858
1005
  flatten: (style) => flattenStyles(style)
859
1006
  };
860
1007
 
1008
+ // src/components/webScrollUtils.ts
1009
+ function getDocumentScrollerNode() {
1010
+ if (typeof document === "undefined") {
1011
+ return null;
1012
+ }
1013
+ return document.scrollingElement || document.documentElement || document.body;
1014
+ }
1015
+ function getWindowScrollPosition() {
1016
+ var _a3, _b, _c, _d;
1017
+ if (typeof window === "undefined") {
1018
+ return { x: 0, y: 0 };
1019
+ }
1020
+ return {
1021
+ x: (_b = (_a3 = window.scrollX) != null ? _a3 : window.pageXOffset) != null ? _b : 0,
1022
+ y: (_d = (_c = window.scrollY) != null ? _c : window.pageYOffset) != null ? _d : 0
1023
+ };
1024
+ }
1025
+ function getElementDocumentPosition(element, scroll) {
1026
+ var _a3, _b;
1027
+ const rect = element == null ? void 0 : element.getBoundingClientRect();
1028
+ return {
1029
+ left: ((_a3 = rect == null ? void 0 : rect.left) != null ? _a3 : 0) + scroll.x,
1030
+ top: ((_b = rect == null ? void 0 : rect.top) != null ? _b : 0) + scroll.y
1031
+ };
1032
+ }
1033
+ function getContentSize2(content) {
1034
+ var _a3, _b;
1035
+ return {
1036
+ height: (_a3 = content == null ? void 0 : content.scrollHeight) != null ? _a3 : 0,
1037
+ width: (_b = content == null ? void 0 : content.scrollWidth) != null ? _b : 0
1038
+ };
1039
+ }
1040
+ function getScrollContentSize(scrollElement, contentElement, isWindowScroll) {
1041
+ return getContentSize2(isWindowScroll ? contentElement : scrollElement);
1042
+ }
1043
+ function getLayoutMeasurement(scrollElement, isWindowScroll, horizontal) {
1044
+ var _a3, _b, _c, _d, _e, _f;
1045
+ if (isWindowScroll && typeof window !== "undefined") {
1046
+ const rect = scrollElement == null ? void 0 : scrollElement.getBoundingClientRect();
1047
+ return {
1048
+ // In window-scroll mode, use viewport size on the scroll axis.
1049
+ height: horizontal ? (_b = (_a3 = rect == null ? void 0 : rect.height) != null ? _a3 : scrollElement == null ? void 0 : scrollElement.clientHeight) != null ? _b : window.innerHeight : window.innerHeight,
1050
+ // Keep the cross-axis size list-relative to avoid inflating container measurements.
1051
+ width: horizontal ? window.innerWidth : (_d = (_c = rect == null ? void 0 : rect.width) != null ? _c : scrollElement == null ? void 0 : scrollElement.clientWidth) != null ? _d : window.innerWidth
1052
+ };
1053
+ }
1054
+ return {
1055
+ height: (_e = scrollElement == null ? void 0 : scrollElement.clientHeight) != null ? _e : 0,
1056
+ width: (_f = scrollElement == null ? void 0 : scrollElement.clientWidth) != null ? _f : 0
1057
+ };
1058
+ }
1059
+ function clampOffset(offset, maxOffset) {
1060
+ return Math.max(0, Math.min(offset, maxOffset));
1061
+ }
1062
+ function getAxisSize(size, horizontal) {
1063
+ return horizontal ? size.width : size.height;
1064
+ }
1065
+ function getMaxOffset(contentSize, layoutMeasurement, horizontal) {
1066
+ return Math.max(0, getAxisSize(contentSize, horizontal) - getAxisSize(layoutMeasurement, horizontal));
1067
+ }
1068
+ function resolveScrollableNode(scrollElement, isWindowScroll) {
1069
+ return isWindowScroll ? getDocumentScrollerNode() || scrollElement : scrollElement;
1070
+ }
1071
+ function resolveScrollEventTarget(scrollElement, isWindowScroll) {
1072
+ return isWindowScroll && typeof window !== "undefined" ? window : scrollElement;
1073
+ }
1074
+ function getLayoutRectangle(element, isWindowScroll, horizontal) {
1075
+ const rect = element.getBoundingClientRect();
1076
+ const scroll = getWindowScrollPosition();
1077
+ return {
1078
+ height: isWindowScroll && typeof window !== "undefined" && !horizontal ? window.innerHeight : rect.height,
1079
+ width: isWindowScroll && typeof window !== "undefined" && horizontal ? window.innerWidth : rect.width,
1080
+ x: isWindowScroll ? rect.left + scroll.x : rect.left,
1081
+ y: isWindowScroll ? rect.top + scroll.y : rect.top
1082
+ };
1083
+ }
1084
+ function resolveWindowScrollTarget({ clampedOffset, horizontal, listPos, scroll }) {
1085
+ return {
1086
+ left: horizontal ? listPos.left + clampedOffset : scroll.x,
1087
+ top: horizontal ? scroll.y : listPos.top + clampedOffset
1088
+ };
1089
+ }
1090
+
861
1091
  // src/components/ListComponentScrollView.tsx
862
1092
  var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView2({
863
1093
  children,
@@ -867,125 +1097,163 @@ var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView
867
1097
  contentOffset,
868
1098
  maintainVisibleContentPosition,
869
1099
  onScroll: onScroll2,
870
- onMomentumScrollEnd,
1100
+ onMomentumScrollEnd: _onMomentumScrollEnd,
871
1101
  showsHorizontalScrollIndicator = true,
872
1102
  showsVerticalScrollIndicator = true,
873
1103
  refreshControl,
1104
+ useWindowScroll = false,
874
1105
  onLayout,
875
1106
  ...props
876
1107
  }, ref) {
877
1108
  const scrollRef = React3.useRef(null);
878
1109
  const contentRef = React3.useRef(null);
879
- const momentumTimeout = React3.useRef(null);
1110
+ const isWindowScroll = useWindowScroll;
1111
+ const getScrollTarget = React3.useCallback(
1112
+ () => resolveScrollEventTarget(scrollRef.current, isWindowScroll),
1113
+ [isWindowScroll]
1114
+ );
1115
+ const getMaxScrollOffset = React3.useCallback(() => {
1116
+ const scrollElement = scrollRef.current;
1117
+ const contentSize = getScrollContentSize(scrollElement, contentRef.current, isWindowScroll);
1118
+ const layoutMeasurement = getLayoutMeasurement(scrollElement, isWindowScroll, horizontal);
1119
+ return getMaxOffset(contentSize, layoutMeasurement, horizontal);
1120
+ }, [horizontal, isWindowScroll]);
1121
+ const getCurrentScrollOffset = React3.useCallback(() => {
1122
+ const scrollElement = scrollRef.current;
1123
+ if (isWindowScroll) {
1124
+ const maxOffset = getMaxScrollOffset();
1125
+ const scroll = getWindowScrollPosition();
1126
+ const listPos = getElementDocumentPosition(scrollElement, scroll);
1127
+ const rawOffset = horizontal ? scroll.x - listPos.left : scroll.y - listPos.top;
1128
+ return clampOffset(rawOffset, maxOffset);
1129
+ }
1130
+ if (!scrollElement) {
1131
+ return 0;
1132
+ }
1133
+ return horizontal ? scrollElement.scrollLeft : scrollElement.scrollTop;
1134
+ }, [getMaxScrollOffset, horizontal, isWindowScroll]);
1135
+ const scrollToLocalOffset = React3.useCallback(
1136
+ (offset, animated) => {
1137
+ const scrollElement = scrollRef.current;
1138
+ const target = getScrollTarget();
1139
+ if (!target || typeof target.scrollTo !== "function") {
1140
+ return;
1141
+ }
1142
+ const maxOffset = getMaxScrollOffset();
1143
+ const clampedOffset = clampOffset(offset, maxOffset);
1144
+ const behavior = animated ? "smooth" : "auto";
1145
+ const options = { behavior };
1146
+ if (isWindowScroll) {
1147
+ const scroll = getWindowScrollPosition();
1148
+ const listPos = getElementDocumentPosition(scrollElement, scroll);
1149
+ const { left, top } = resolveWindowScrollTarget({
1150
+ clampedOffset,
1151
+ horizontal,
1152
+ listPos,
1153
+ scroll
1154
+ });
1155
+ options.left = left;
1156
+ options.top = top;
1157
+ } else if (horizontal) {
1158
+ options.left = clampedOffset;
1159
+ } else {
1160
+ options.top = clampedOffset;
1161
+ }
1162
+ target.scrollTo(options);
1163
+ },
1164
+ [getMaxScrollOffset, getScrollTarget, horizontal, isWindowScroll]
1165
+ );
880
1166
  React3.useImperativeHandle(ref, () => {
881
1167
  const api = {
882
1168
  getBoundingClientRect: () => {
883
1169
  var _a3;
884
1170
  return (_a3 = scrollRef.current) == null ? void 0 : _a3.getBoundingClientRect();
885
1171
  },
886
- getScrollableNode: () => scrollRef.current,
887
- getScrollResponder: () => scrollRef.current,
1172
+ getContentNode: () => contentRef.current,
1173
+ getCurrentScrollOffset,
1174
+ getScrollableNode: () => resolveScrollableNode(scrollRef.current, isWindowScroll),
1175
+ getScrollEventTarget: () => getScrollTarget(),
1176
+ getScrollResponder: () => resolveScrollableNode(scrollRef.current, isWindowScroll),
1177
+ isWindowScroll: () => isWindowScroll,
888
1178
  scrollBy: (x, y) => {
889
- const el = scrollRef.current;
890
- if (!el) return;
891
- el.scrollBy(x, y);
1179
+ const target = getScrollTarget();
1180
+ if (!target || typeof target.scrollBy !== "function") {
1181
+ return;
1182
+ }
1183
+ target.scrollBy({ behavior: "auto", left: x, top: y });
892
1184
  },
893
1185
  scrollTo: (options) => {
894
- const el = scrollRef.current;
895
- if (!el) return;
896
1186
  const { x = 0, y = 0, animated = true } = options;
897
- el.scrollTo({ behavior: animated ? "smooth" : "auto", left: x, top: y });
1187
+ scrollToLocalOffset(horizontal ? x : y, animated);
898
1188
  },
899
1189
  scrollToEnd: (options = {}) => {
900
- const el = scrollRef.current;
901
- if (!el) return;
902
1190
  const { animated = true } = options;
903
- if (horizontal) {
904
- el.scrollTo({ behavior: animated ? "smooth" : "auto", left: el.scrollWidth });
905
- } else {
906
- el.scrollTo({ behavior: animated ? "smooth" : "auto", top: el.scrollHeight });
907
- }
1191
+ const endOffset = getMaxScrollOffset();
1192
+ scrollToLocalOffset(endOffset, animated);
908
1193
  },
909
1194
  scrollToOffset: (params) => {
910
- const el = scrollRef.current;
911
- if (!el) return;
912
1195
  const { offset, animated = true } = params;
913
- if (horizontal) {
914
- el.scrollTo({ behavior: animated ? "smooth" : "auto", left: offset });
915
- } else {
916
- el.scrollTo({ behavior: animated ? "smooth" : "auto", top: offset });
917
- }
1196
+ scrollToLocalOffset(offset, animated);
918
1197
  }
919
1198
  };
920
1199
  return api;
921
- }, [horizontal]);
1200
+ }, [getCurrentScrollOffset, getMaxScrollOffset, getScrollTarget, horizontal, isWindowScroll, scrollToLocalOffset]);
922
1201
  const handleScroll = React3.useCallback(
923
- (event) => {
924
- if (!onScroll2 || !(event == null ? void 0 : event.target)) {
1202
+ (_event) => {
1203
+ if (!onScroll2) {
1204
+ return;
1205
+ }
1206
+ const target = scrollRef.current;
1207
+ if (!target) {
925
1208
  return;
926
1209
  }
927
- const target = event.target;
1210
+ const contentSize = getContentSize2(contentRef.current);
1211
+ const layoutMeasurement = getLayoutMeasurement(scrollRef.current, isWindowScroll, horizontal);
1212
+ const offset = getCurrentScrollOffset();
928
1213
  const scrollEvent = {
929
1214
  nativeEvent: {
930
1215
  contentOffset: {
931
- x: target.scrollLeft,
932
- y: target.scrollTop
1216
+ x: horizontal ? offset : 0,
1217
+ y: horizontal ? 0 : offset
933
1218
  },
934
1219
  contentSize: {
935
- height: target.scrollHeight,
936
- width: target.scrollWidth
1220
+ height: contentSize.height,
1221
+ width: contentSize.width
937
1222
  },
938
1223
  layoutMeasurement: {
939
- height: target.clientHeight,
940
- width: target.clientWidth
1224
+ height: layoutMeasurement.height,
1225
+ width: layoutMeasurement.width
941
1226
  }
942
1227
  }
943
1228
  };
944
1229
  onScroll2(scrollEvent);
945
- if (onMomentumScrollEnd) {
946
- if (momentumTimeout.current != null) clearTimeout(momentumTimeout.current);
947
- momentumTimeout.current = setTimeout(() => {
948
- onMomentumScrollEnd({
949
- nativeEvent: {
950
- contentOffset: scrollEvent.nativeEvent.contentOffset
951
- }
952
- });
953
- }, 100);
954
- }
955
1230
  },
956
- [onScroll2, onMomentumScrollEnd]
1231
+ [getCurrentScrollOffset, horizontal, isWindowScroll, onScroll2]
957
1232
  );
958
1233
  React3.useLayoutEffect(() => {
959
- const element = scrollRef.current;
960
- if (!element) return;
961
- element.addEventListener("scroll", handleScroll);
1234
+ const target = getScrollTarget();
1235
+ if (!target) return;
1236
+ target.addEventListener("scroll", handleScroll, { passive: true });
962
1237
  return () => {
963
- element.removeEventListener("scroll", handleScroll);
1238
+ target.removeEventListener("scroll", handleScroll);
964
1239
  };
965
- }, [handleScroll]);
1240
+ }, [getScrollTarget, handleScroll]);
966
1241
  React3.useEffect(() => {
967
1242
  const doScroll = () => {
968
- if (contentOffset && scrollRef.current) {
969
- scrollRef.current.scrollLeft = contentOffset.x || 0;
970
- scrollRef.current.scrollTop = contentOffset.y || 0;
1243
+ if (contentOffset) {
1244
+ scrollToLocalOffset(horizontal ? contentOffset.x || 0 : contentOffset.y || 0, false);
971
1245
  }
972
1246
  };
973
1247
  doScroll();
974
1248
  requestAnimationFrame(doScroll);
975
- }, [contentOffset == null ? void 0 : contentOffset.x, contentOffset == null ? void 0 : contentOffset.y]);
1249
+ }, [contentOffset == null ? void 0 : contentOffset.x, contentOffset == null ? void 0 : contentOffset.y, horizontal, scrollToLocalOffset]);
976
1250
  React3.useLayoutEffect(() => {
977
1251
  if (!onLayout || !scrollRef.current) return;
978
1252
  const element = scrollRef.current;
979
1253
  const fireLayout = () => {
980
- const rect = element.getBoundingClientRect();
981
1254
  onLayout({
982
1255
  nativeEvent: {
983
- layout: {
984
- height: rect.height,
985
- width: rect.width,
986
- x: rect.left,
987
- y: rect.top
988
- }
1256
+ layout: getLayoutRectangle(element, isWindowScroll, horizontal)
989
1257
  }
990
1258
  });
991
1259
  };
@@ -994,16 +1262,27 @@ var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView
994
1262
  fireLayout();
995
1263
  });
996
1264
  resizeObserver.observe(element);
997
- return () => resizeObserver.disconnect();
998
- }, [onLayout]);
1265
+ const onWindowResize = () => {
1266
+ fireLayout();
1267
+ };
1268
+ if (isWindowScroll && typeof window !== "undefined" && typeof window.addEventListener === "function") {
1269
+ window.addEventListener("resize", onWindowResize);
1270
+ }
1271
+ return () => {
1272
+ resizeObserver.disconnect();
1273
+ if (isWindowScroll && typeof window !== "undefined" && typeof window.removeEventListener === "function") {
1274
+ window.removeEventListener("resize", onWindowResize);
1275
+ }
1276
+ };
1277
+ }, [isWindowScroll, onLayout]);
999
1278
  const scrollViewStyle = {
1000
- overflow: "auto",
1001
- overflowX: horizontal ? "auto" : showsHorizontalScrollIndicator ? "auto" : "hidden",
1002
- overflowY: horizontal ? showsVerticalScrollIndicator ? "auto" : "hidden" : "auto",
1003
- position: "relative",
1004
- // Ensure proper positioning context
1005
- WebkitOverflowScrolling: "touch",
1006
- // iOS momentum scrolling
1279
+ ...isWindowScroll ? {} : {
1280
+ overflow: "auto",
1281
+ overflowX: horizontal ? "auto" : showsHorizontalScrollIndicator ? "auto" : "hidden",
1282
+ overflowY: horizontal ? showsVerticalScrollIndicator ? "auto" : "hidden" : "auto",
1283
+ WebkitOverflowScrolling: "touch"
1284
+ // iOS momentum scrolling
1285
+ },
1007
1286
  ...StyleSheet.flatten(style)
1008
1287
  };
1009
1288
  const contentStyle = {
@@ -1013,28 +1292,15 @@ var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView
1013
1292
  minWidth: horizontal ? "100%" : void 0,
1014
1293
  ...StyleSheet.flatten(contentContainerStyle)
1015
1294
  };
1016
- return /* @__PURE__ */ React3__namespace.createElement("div", { ref: scrollRef, style: scrollViewStyle, ...props }, refreshControl, /* @__PURE__ */ React3__namespace.createElement("div", { ref: contentRef, style: contentStyle }, children));
1295
+ const {
1296
+ contentInset: _contentInset,
1297
+ scrollEventThrottle: _scrollEventThrottle,
1298
+ ScrollComponent: _ScrollComponent,
1299
+ useWindowScroll: _useWindowScroll,
1300
+ ...webProps
1301
+ } = props;
1302
+ return /* @__PURE__ */ React3__namespace.createElement("div", { ref: scrollRef, ...webProps, style: scrollViewStyle }, refreshControl, /* @__PURE__ */ React3__namespace.createElement("div", { ref: contentRef, style: contentStyle }, children));
1017
1303
  });
1018
- function Padding() {
1019
- const [paddingTop] = useArr$(["alignItemsPaddingTop"]);
1020
- return /* @__PURE__ */ React3__namespace.createElement("div", { style: { paddingTop } });
1021
- }
1022
- function PaddingDevMode() {
1023
- const [paddingTop] = useArr$(["alignItemsPaddingTop"]);
1024
- return /* @__PURE__ */ React3__namespace.createElement(React3__namespace.Fragment, null, /* @__PURE__ */ React3__namespace.createElement("div", { style: { paddingTop } }), /* @__PURE__ */ React3__namespace.createElement(
1025
- "div",
1026
- {
1027
- style: {
1028
- backgroundColor: "green",
1029
- height: paddingTop,
1030
- left: 0,
1031
- position: "absolute",
1032
- right: 0,
1033
- top: 0
1034
- }
1035
- }
1036
- ));
1037
- }
1038
1304
  function useValueListener$(key, callback) {
1039
1305
  const ctx = useStateContext();
1040
1306
  React3.useLayoutEffect(() => {
@@ -1049,35 +1315,43 @@ function useValueListener$(key, callback) {
1049
1315
  function ScrollAdjust() {
1050
1316
  const ctx = useStateContext();
1051
1317
  const lastScrollOffsetRef = React3__namespace.useRef(0);
1318
+ const resetPaddingRafRef = React3__namespace.useRef(void 0);
1052
1319
  const callback = React3__namespace.useCallback(() => {
1053
1320
  var _a3;
1054
1321
  const scrollAdjust = peek$(ctx, "scrollAdjust");
1055
1322
  const scrollAdjustUserOffset = peek$(ctx, "scrollAdjustUserOffset");
1056
1323
  const scrollOffset = (scrollAdjust || 0) + (scrollAdjustUserOffset || 0);
1057
- const scrollView = (_a3 = ctx.internalState) == null ? void 0 : _a3.refScroller.current;
1324
+ const scrollView = (_a3 = ctx.state) == null ? void 0 : _a3.refScroller.current;
1058
1325
  if (scrollView && scrollOffset !== lastScrollOffsetRef.current) {
1059
1326
  const scrollDelta = scrollOffset - lastScrollOffsetRef.current;
1060
1327
  if (scrollDelta !== 0) {
1328
+ const contentNode = scrollView.getContentNode();
1329
+ const prevScroll = scrollView.getCurrentScrollOffset();
1061
1330
  const el = scrollView.getScrollableNode();
1062
- const prevScroll = el.scrollTop;
1331
+ if (!contentNode) {
1332
+ scrollView.scrollBy(0, scrollDelta);
1333
+ lastScrollOffsetRef.current = scrollOffset;
1334
+ return;
1335
+ }
1336
+ const totalSize = contentNode.scrollHeight;
1337
+ const viewportSize = el.clientHeight;
1063
1338
  const nextScroll = prevScroll + scrollDelta;
1064
- const totalSize = el.scrollHeight;
1065
- if (scrollDelta > 0 && !ctx.internalState.adjustingFromInitialMount && totalSize < nextScroll + el.clientHeight) {
1066
- const child = el.firstElementChild;
1067
- const prevPaddingBottom = child.style.paddingBottom;
1068
- const pad = (nextScroll + el.clientHeight - totalSize) * 2;
1069
- child.style.paddingBottom = `${pad}px`;
1070
- void el.offsetHeight;
1339
+ if (scrollDelta > 0 && !ctx.state.adjustingFromInitialMount && totalSize < nextScroll + viewportSize) {
1340
+ const paddingBottom = ctx.state.props.stylePaddingBottom || 0;
1341
+ const pad = (nextScroll + viewportSize - totalSize) * 2;
1342
+ contentNode.style.paddingBottom = `${pad}px`;
1343
+ void contentNode.offsetHeight;
1071
1344
  scrollView.scrollBy(0, scrollDelta);
1072
- requestAnimationFrame(() => {
1073
- child.style.paddingBottom = prevPaddingBottom;
1345
+ if (resetPaddingRafRef.current !== void 0) {
1346
+ cancelAnimationFrame(resetPaddingRafRef.current);
1347
+ }
1348
+ resetPaddingRafRef.current = requestAnimationFrame(() => {
1349
+ resetPaddingRafRef.current = void 0;
1350
+ contentNode.style.paddingBottom = paddingBottom ? `${paddingBottom}px` : "0";
1074
1351
  });
1075
1352
  } else {
1076
1353
  scrollView.scrollBy(0, scrollDelta);
1077
1354
  }
1078
- if (IS_DEV) {
1079
- console.log("ScrollAdjust (web scrollBy)", scrollDelta, "total offset:", scrollOffset);
1080
- }
1081
1355
  }
1082
1356
  lastScrollOffsetRef.current = scrollOffset;
1083
1357
  }
@@ -1091,12 +1365,10 @@ function SnapWrapper({ ScrollComponent, ...props }) {
1091
1365
  return /* @__PURE__ */ React3__namespace.createElement(ScrollComponent, { ...props, snapToOffsets });
1092
1366
  }
1093
1367
  var LayoutView = ({ onLayoutChange, refView, children, ...rest }) => {
1094
- const ref = refView != null ? refView : React3.useRef();
1368
+ const ref = refView != null ? refView : React3.useRef(null);
1095
1369
  useOnLayoutSync({ onLayoutChange, ref });
1096
1370
  return /* @__PURE__ */ React3__namespace.createElement("div", { ...rest, ref }, children);
1097
1371
  };
1098
-
1099
- // src/components/ListComponent.tsx
1100
1372
  var getComponent = (Component) => {
1101
1373
  if (React3__namespace.isValidElement(Component)) {
1102
1374
  return Component;
@@ -1106,6 +1378,8 @@ var getComponent = (Component) => {
1106
1378
  }
1107
1379
  return null;
1108
1380
  };
1381
+
1382
+ // src/components/ListComponent.tsx
1109
1383
  var ListComponent = typedMemo(function ListComponent2({
1110
1384
  canRender,
1111
1385
  style,
@@ -1114,7 +1388,7 @@ var ListComponent = typedMemo(function ListComponent2({
1114
1388
  initialContentOffset,
1115
1389
  recycleItems,
1116
1390
  ItemSeparatorComponent,
1117
- alignItemsAtEnd,
1391
+ alignItemsAtEnd: _alignItemsAtEnd,
1118
1392
  waitForInitialLayout,
1119
1393
  onScroll: onScroll2,
1120
1394
  onLayout,
@@ -1126,31 +1400,52 @@ var ListComponent = typedMemo(function ListComponent2({
1126
1400
  getRenderedItem: getRenderedItem2,
1127
1401
  updateItemSize: updateItemSize2,
1128
1402
  refScrollView,
1129
- maintainVisibleContentPosition,
1130
1403
  renderScrollComponent,
1404
+ onLayoutFooter,
1131
1405
  scrollAdjustHandler,
1132
- onLayoutHeader,
1133
1406
  snapToIndices,
1407
+ stickyHeaderConfig,
1134
1408
  stickyHeaderIndices,
1409
+ useWindowScroll = false,
1135
1410
  ...rest
1136
1411
  }) {
1137
1412
  const ctx = useStateContext();
1413
+ const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
1138
1414
  const ScrollComponent = renderScrollComponent ? React3.useMemo(
1139
- () => React3__namespace.forwardRef((props, ref) => renderScrollComponent({ ...props, ref })),
1415
+ () => React3__namespace.forwardRef(
1416
+ (props, ref) => renderScrollComponent({ ...props, ref })
1417
+ ),
1140
1418
  [renderScrollComponent]
1141
1419
  ) : ListComponentScrollView;
1142
- React3__namespace.useEffect(() => {
1143
- if (canRender) {
1144
- setTimeout(() => {
1145
- scrollAdjustHandler.setMounted();
1146
- }, 0);
1147
- }
1148
- }, [canRender]);
1149
1420
  const SnapOrScroll = snapToIndices ? SnapWrapper : ScrollComponent;
1421
+ React3.useLayoutEffect(() => {
1422
+ if (!ListHeaderComponent) {
1423
+ set$(ctx, "headerSize", 0);
1424
+ }
1425
+ if (!ListFooterComponent) {
1426
+ set$(ctx, "footerSize", 0);
1427
+ }
1428
+ }, [ListHeaderComponent, ListFooterComponent, ctx]);
1429
+ const onLayoutHeader = React3.useCallback(
1430
+ (rect) => {
1431
+ const size = rect[horizontal ? "width" : "height"];
1432
+ set$(ctx, "headerSize", size);
1433
+ },
1434
+ [ctx, horizontal]
1435
+ );
1436
+ const onLayoutFooterInternal = React3.useCallback(
1437
+ (rect, fromLayoutEffect) => {
1438
+ const size = rect[horizontal ? "width" : "height"];
1439
+ set$(ctx, "footerSize", size);
1440
+ onLayoutFooter == null ? void 0 : onLayoutFooter(rect, fromLayoutEffect);
1441
+ },
1442
+ [ctx, horizontal, onLayoutFooter]
1443
+ );
1150
1444
  return /* @__PURE__ */ React3__namespace.createElement(
1151
1445
  SnapOrScroll,
1152
1446
  {
1153
1447
  ...rest,
1448
+ ...ScrollComponent === ListComponentScrollView ? { useWindowScroll } : {},
1154
1449
  contentContainerStyle: [
1155
1450
  contentContainerStyle,
1156
1451
  horizontal ? {
@@ -1159,7 +1454,7 @@ var ListComponent = typedMemo(function ListComponent2({
1159
1454
  ],
1160
1455
  contentOffset: initialContentOffset ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
1161
1456
  horizontal,
1162
- maintainVisibleContentPosition: maintainVisibleContentPosition ? { minIndexForVisible: 0 } : void 0,
1457
+ maintainVisibleContentPosition: maintainVisibleContentPosition.size || maintainVisibleContentPosition.data ? { minIndexForVisible: 0 } : void 0,
1163
1458
  onLayout,
1164
1459
  onScroll: onScroll2,
1165
1460
  ref: refScrollView,
@@ -1167,7 +1462,6 @@ var ListComponent = typedMemo(function ListComponent2({
1167
1462
  style
1168
1463
  },
1169
1464
  /* @__PURE__ */ React3__namespace.createElement(ScrollAdjust, null),
1170
- ENABLE_DEVMODE ? /* @__PURE__ */ React3__namespace.createElement(PaddingDevMode, null) : /* @__PURE__ */ React3__namespace.createElement(Padding, null),
1171
1465
  ListHeaderComponent && /* @__PURE__ */ React3__namespace.createElement(LayoutView, { onLayoutChange: onLayoutHeader, style: ListHeaderComponentStyle }, getComponent(ListHeaderComponent)),
1172
1466
  ListEmptyComponent && getComponent(ListEmptyComponent),
1173
1467
  canRender && !ListEmptyComponent && /* @__PURE__ */ React3__namespace.createElement(
@@ -1177,25 +1471,27 @@ var ListComponent = typedMemo(function ListComponent2({
1177
1471
  horizontal,
1178
1472
  ItemSeparatorComponent,
1179
1473
  recycleItems,
1474
+ stickyHeaderConfig,
1180
1475
  updateItemSize: updateItemSize2,
1181
1476
  waitForInitialLayout
1182
1477
  }
1183
1478
  ),
1184
- ListFooterComponent && /* @__PURE__ */ React3__namespace.createElement(
1185
- LayoutView,
1186
- {
1187
- onLayoutChange: (layout) => {
1188
- const size = layout[horizontal ? "width" : "height"];
1189
- set$(ctx, "footerSize", size);
1190
- },
1191
- style: ListFooterComponentStyle
1192
- },
1193
- getComponent(ListFooterComponent)
1194
- ),
1195
- IS_DEV && ENABLE_DEVMODE && /* @__PURE__ */ React3__namespace.createElement(DevNumbers, null)
1479
+ ListFooterComponent && /* @__PURE__ */ React3__namespace.createElement(LayoutView, { onLayoutChange: onLayoutFooterInternal, style: ListFooterComponentStyle }, getComponent(ListFooterComponent)),
1480
+ IS_DEV && ENABLE_DEVMODE
1196
1481
  );
1197
1482
  });
1198
1483
 
1484
+ // src/core/calculateOffsetForIndex.ts
1485
+ function calculateOffsetForIndex(ctx, index) {
1486
+ const state = ctx.state;
1487
+ return index !== void 0 ? state.positions[index] || 0 : 0;
1488
+ }
1489
+
1490
+ // src/core/getTopOffsetAdjustment.ts
1491
+ function getTopOffsetAdjustment(ctx) {
1492
+ return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
1493
+ }
1494
+
1199
1495
  // src/utils/getId.ts
1200
1496
  function getId(state, index) {
1201
1497
  const { data, keyExtractor } = state.props;
@@ -1208,61 +1504,9 @@ function getId(state, index) {
1208
1504
  return id;
1209
1505
  }
1210
1506
 
1211
- // src/core/calculateOffsetForIndex.ts
1212
- function calculateOffsetForIndex(ctx, state, index) {
1213
- let position = 0;
1214
- if (index !== void 0) {
1215
- position = (state == null ? void 0 : state.positions.get(getId(state, index))) || 0;
1216
- const paddingTop = peek$(ctx, "stylePaddingTop");
1217
- if (paddingTop) {
1218
- position += paddingTop;
1219
- }
1220
- const headerSize = peek$(ctx, "headerSize");
1221
- if (headerSize) {
1222
- position += headerSize;
1223
- }
1224
- }
1225
- return position;
1226
- }
1227
-
1228
- // src/utils/setPaddingTop.ts
1229
- function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
1230
- if (stylePaddingTop !== void 0) {
1231
- const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
1232
- if (stylePaddingTop < prevStylePaddingTop) {
1233
- let prevTotalSize = peek$(ctx, "totalSize") || 0;
1234
- set$(ctx, "totalSize", prevTotalSize + prevStylePaddingTop);
1235
- state.timeoutSetPaddingTop = setTimeout(() => {
1236
- prevTotalSize = peek$(ctx, "totalSize") || 0;
1237
- set$(ctx, "totalSize", prevTotalSize - prevStylePaddingTop);
1238
- }, 16);
1239
- }
1240
- set$(ctx, "stylePaddingTop", stylePaddingTop);
1241
- }
1242
- if (alignItemsPaddingTop !== void 0) {
1243
- set$(ctx, "alignItemsPaddingTop", alignItemsPaddingTop);
1244
- }
1245
- }
1246
-
1247
- // src/utils/updateAlignItemsPaddingTop.ts
1248
- function updateAlignItemsPaddingTop(ctx, state) {
1249
- const {
1250
- scrollLength,
1251
- props: { alignItemsAtEnd, data }
1252
- } = state;
1253
- if (alignItemsAtEnd) {
1254
- let alignItemsPaddingTop = 0;
1255
- if ((data == null ? void 0 : data.length) > 0) {
1256
- const contentSize = getContentSize(ctx);
1257
- alignItemsPaddingTop = Math.max(0, Math.floor(scrollLength - contentSize));
1258
- }
1259
- setPaddingTop(ctx, state, { alignItemsPaddingTop });
1260
- }
1261
- }
1262
-
1263
1507
  // src/core/addTotalSize.ts
1264
- function addTotalSize(ctx, state, key, add) {
1265
- const { alignItemsAtEnd } = state.props;
1508
+ function addTotalSize(ctx, key, add) {
1509
+ const state = ctx.state;
1266
1510
  const prevTotalSize = state.totalSize;
1267
1511
  let totalSize = state.totalSize;
1268
1512
  if (key === null) {
@@ -1279,48 +1523,47 @@ function addTotalSize(ctx, state, key, add) {
1279
1523
  state.pendingTotalSize = void 0;
1280
1524
  state.totalSize = totalSize;
1281
1525
  set$(ctx, "totalSize", totalSize);
1282
- if (alignItemsAtEnd) {
1283
- updateAlignItemsPaddingTop(ctx, state);
1284
- }
1285
1526
  }
1286
1527
  }
1287
1528
  }
1288
1529
 
1289
1530
  // src/core/setSize.ts
1290
- function setSize(ctx, state, itemKey, size) {
1531
+ function setSize(ctx, itemKey, size) {
1532
+ const state = ctx.state;
1291
1533
  const { sizes } = state;
1292
1534
  const previousSize = sizes.get(itemKey);
1293
1535
  const diff = previousSize !== void 0 ? size - previousSize : size;
1294
1536
  if (diff !== 0) {
1295
- addTotalSize(ctx, state, itemKey, diff);
1537
+ addTotalSize(ctx, itemKey, diff);
1296
1538
  }
1297
1539
  sizes.set(itemKey, size);
1298
1540
  }
1299
1541
 
1300
1542
  // src/utils/getItemSize.ts
1301
- function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedSize) {
1543
+ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
1302
1544
  var _a3, _b;
1545
+ const state = ctx.state;
1303
1546
  const {
1304
1547
  sizesKnown,
1305
1548
  sizes,
1306
1549
  averageSizes,
1307
- props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType }
1550
+ props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType },
1551
+ scrollingTo
1308
1552
  } = state;
1309
1553
  const sizeKnown = sizesKnown.get(key);
1310
1554
  if (sizeKnown !== void 0) {
1311
1555
  return sizeKnown;
1312
1556
  }
1313
1557
  let size;
1314
- const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1315
- const scrollingTo = peek$(ctx, "scrollingTo");
1316
1558
  if (preferCachedSize) {
1317
1559
  const cachedSize = sizes.get(key);
1318
1560
  if (cachedSize !== void 0) {
1319
1561
  return cachedSize;
1320
1562
  }
1321
1563
  }
1564
+ const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1322
1565
  if (getFixedItemSize) {
1323
- size = getFixedItemSize(index, data, itemType);
1566
+ size = getFixedItemSize(data, index, itemType);
1324
1567
  if (size !== void 0) {
1325
1568
  sizesKnown.set(key, size);
1326
1569
  }
@@ -1338,93 +1581,61 @@ function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedS
1338
1581
  }
1339
1582
  }
1340
1583
  if (size === void 0) {
1341
- size = getEstimatedItemSize ? getEstimatedItemSize(index, data, itemType) : estimatedItemSize;
1584
+ size = getEstimatedItemSize ? getEstimatedItemSize(data, index, itemType) : estimatedItemSize;
1342
1585
  }
1343
- setSize(ctx, state, key, size);
1586
+ setSize(ctx, key, size);
1344
1587
  return size;
1345
1588
  }
1346
1589
 
1347
1590
  // src/core/calculateOffsetWithOffsetPosition.ts
1348
- function calculateOffsetWithOffsetPosition(ctx, state, offsetParam, params) {
1591
+ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1592
+ const state = ctx.state;
1349
1593
  const { index, viewOffset, viewPosition } = params;
1350
1594
  let offset = offsetParam;
1351
1595
  if (viewOffset) {
1352
1596
  offset -= viewOffset;
1353
1597
  }
1354
- if (viewPosition !== void 0 && index !== void 0) {
1355
- offset -= viewPosition * (state.scrollLength - getItemSize(ctx, state, getId(state, index), index, state.props.data[index]));
1356
- }
1357
- return offset;
1358
- }
1359
-
1360
- // src/core/finishScrollTo.ts
1361
- function finishScrollTo(ctx, state) {
1362
- var _a3, _b;
1363
- if (state) {
1364
- state.scrollHistory.length = 0;
1365
- state.initialScroll = void 0;
1366
- state.initialAnchor = void 0;
1367
- set$(ctx, "scrollingTo", void 0);
1368
- if (state.pendingTotalSize !== void 0) {
1369
- addTotalSize(ctx, state, null, state.pendingTotalSize);
1598
+ if (index !== void 0) {
1599
+ const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
1600
+ if (topOffsetAdjustment) {
1601
+ offset += topOffsetAdjustment;
1370
1602
  }
1371
- if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1372
- (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1603
+ }
1604
+ if (viewPosition !== void 0 && index !== void 0) {
1605
+ const itemSize = getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1606
+ const trailingInset = getContentInsetEnd(state);
1607
+ offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1608
+ if (index === state.props.data.length - 1) {
1609
+ const footerSize = peek$(ctx, "footerSize") || 0;
1610
+ offset += footerSize;
1373
1611
  }
1374
1612
  }
1613
+ return offset;
1375
1614
  }
1376
1615
 
1377
- // src/core/scrollTo.ts
1378
- function scrollTo(ctx, state, params) {
1379
- var _a3;
1380
- const { noScrollingTo, ...scrollTarget } = params;
1381
- const { animated, isInitialScroll, offset: scrollTargetOffset, precomputedWithViewOffset } = scrollTarget;
1382
- const {
1383
- refScroller,
1384
- props: { horizontal }
1385
- } = state;
1386
- let offset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, state, scrollTargetOffset, scrollTarget);
1387
- if (Number.isFinite(state.scrollLength) && Number.isFinite(state.totalSize)) {
1388
- const maxOffset = Math.max(0, getContentSize(ctx) - state.scrollLength);
1389
- offset = Math.min(offset, maxOffset);
1390
- }
1391
- state.scrollHistory.length = 0;
1392
- if (!noScrollingTo) {
1393
- set$(ctx, "scrollingTo", scrollTarget);
1394
- }
1395
- state.scrollPending = offset;
1396
- if (!isInitialScroll || Platform.OS === "android") {
1397
- (_a3 = refScroller.current) == null ? void 0 : _a3.scrollTo({
1398
- animated: !!animated,
1399
- x: horizontal ? offset : 0,
1400
- y: horizontal ? 0 : offset
1401
- });
1402
- }
1403
- if (!animated) {
1404
- state.scroll = offset;
1405
- {
1406
- const unlisten = listen$(ctx, "containersDidLayout", (value) => {
1407
- if (value && peek$(ctx, "scrollingTo")) {
1408
- finishScrollTo(ctx, state);
1409
- unlisten();
1410
- }
1411
- });
1412
- }
1413
- if (isInitialScroll) {
1414
- setTimeout(() => {
1415
- state.initialScroll = void 0;
1416
- }, 500);
1417
- }
1418
- }
1616
+ // src/core/clampScrollOffset.ts
1617
+ function clampScrollOffset(ctx, offset, scrollTarget) {
1618
+ const state = ctx.state;
1619
+ const contentSize = getContentSize(ctx);
1620
+ let clampedOffset = offset;
1621
+ if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform.OS !== "android")) {
1622
+ const baseMaxOffset = Math.max(0, contentSize - state.scrollLength);
1623
+ const viewOffset = scrollTarget == null ? void 0 : scrollTarget.viewOffset;
1624
+ const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
1625
+ const maxOffset = baseMaxOffset + extraEndOffset;
1626
+ clampedOffset = Math.min(offset, maxOffset);
1627
+ }
1628
+ clampedOffset = Math.max(0, clampedOffset);
1629
+ return clampedOffset;
1419
1630
  }
1420
1631
 
1421
1632
  // src/utils/checkThreshold.ts
1422
1633
  var HYSTERESIS_MULTIPLIER = 1.3;
1423
- var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot) => {
1634
+ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot, allowReentryOnChange) => {
1424
1635
  const absDistance = Math.abs(distance);
1425
1636
  const within = atThreshold || threshold > 0 && absDistance <= threshold;
1426
1637
  const updateSnapshot = () => {
1427
- setSnapshot == null ? void 0 : setSnapshot({
1638
+ setSnapshot({
1428
1639
  atThreshold,
1429
1640
  contentSize: context.contentSize,
1430
1641
  dataLength: context.dataLength,
@@ -1435,19 +1646,21 @@ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, co
1435
1646
  if (!within) {
1436
1647
  return false;
1437
1648
  }
1438
- onReached == null ? void 0 : onReached(distance);
1649
+ onReached(distance);
1439
1650
  updateSnapshot();
1440
1651
  return true;
1441
1652
  }
1442
1653
  const reset = !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
1443
1654
  if (reset) {
1444
- setSnapshot == null ? void 0 : setSnapshot(void 0);
1655
+ setSnapshot(void 0);
1445
1656
  return false;
1446
1657
  }
1447
1658
  if (within) {
1448
1659
  const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
1449
1660
  if (changed) {
1450
- onReached == null ? void 0 : onReached(distance);
1661
+ if (allowReentryOnChange) {
1662
+ onReached(distance);
1663
+ }
1451
1664
  updateSnapshot();
1452
1665
  }
1453
1666
  }
@@ -1455,9 +1668,10 @@ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, co
1455
1668
  };
1456
1669
 
1457
1670
  // src/utils/checkAtBottom.ts
1458
- function checkAtBottom(ctx, state) {
1671
+ function checkAtBottom(ctx) {
1459
1672
  var _a3;
1460
- if (!state) {
1673
+ const state = ctx.state;
1674
+ if (!state || state.initialScroll) {
1461
1675
  return;
1462
1676
  }
1463
1677
  const {
@@ -1467,9 +1681,13 @@ function checkAtBottom(ctx, state) {
1467
1681
  maintainingScrollAtEnd,
1468
1682
  props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
1469
1683
  } = state;
1684
+ if (state.initialScroll) {
1685
+ return;
1686
+ }
1470
1687
  const contentSize = getContentSize(ctx);
1471
1688
  if (contentSize > 0 && queuedInitialLayout && !maintainingScrollAtEnd) {
1472
- const distanceFromEnd = contentSize - scroll - scrollLength;
1689
+ const insetEnd = getContentInsetEnd(state);
1690
+ const distanceFromEnd = contentSize - scroll - scrollLength - insetEnd;
1473
1691
  const isContentLess = contentSize < scrollLength;
1474
1692
  state.isAtEnd = isContentLess || distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
1475
1693
  state.isEndReached = checkThreshold(
@@ -1489,72 +1707,257 @@ function checkAtBottom(ctx, state) {
1489
1707
  },
1490
1708
  (snapshot) => {
1491
1709
  state.endReachedSnapshot = snapshot;
1492
- }
1710
+ },
1711
+ true
1493
1712
  );
1494
1713
  }
1495
1714
  }
1496
1715
 
1497
1716
  // src/utils/checkAtTop.ts
1498
- function checkAtTop(state) {
1499
- var _a3;
1500
- if (!state) {
1717
+ function checkAtTop(ctx) {
1718
+ const state = ctx == null ? void 0 : ctx.state;
1719
+ if (!state || state.initialScroll || state.scrollingTo) {
1501
1720
  return;
1502
1721
  }
1503
1722
  const {
1504
- scrollLength,
1723
+ dataChangeEpoch,
1724
+ isStartReached,
1725
+ props: { data, onStartReachedThreshold },
1505
1726
  scroll,
1506
- props: { onStartReachedThreshold }
1727
+ scrollLength,
1728
+ startReachedSnapshot,
1729
+ startReachedSnapshotDataChangeEpoch,
1730
+ totalSize
1507
1731
  } = state;
1508
- const distanceFromTop = scroll;
1509
- state.isAtStart = distanceFromTop <= 0;
1732
+ const dataLength = data.length;
1733
+ const threshold = onStartReachedThreshold * scrollLength;
1734
+ const dataChanged = startReachedSnapshotDataChangeEpoch !== dataChangeEpoch;
1735
+ const withinThreshold = threshold > 0 && Math.abs(scroll) <= threshold;
1736
+ const allowReentryOnDataChange = !!isStartReached && withinThreshold && !!dataChanged && !isInMVCPActiveMode(state);
1737
+ if (isStartReached && threshold > 0 && scroll > threshold && startReachedSnapshot && (dataChanged || startReachedSnapshot.contentSize !== totalSize || startReachedSnapshot.dataLength !== dataLength)) {
1738
+ state.isStartReached = false;
1739
+ state.startReachedSnapshot = void 0;
1740
+ state.startReachedSnapshotDataChangeEpoch = void 0;
1741
+ }
1742
+ state.isAtStart = scroll <= 0;
1743
+ if (isStartReached && withinThreshold && dataChanged && !allowReentryOnDataChange) {
1744
+ return;
1745
+ }
1510
1746
  state.isStartReached = checkThreshold(
1511
- distanceFromTop,
1747
+ scroll,
1512
1748
  false,
1513
- onStartReachedThreshold * scrollLength,
1749
+ threshold,
1514
1750
  state.isStartReached,
1515
- state.startReachedSnapshot,
1751
+ allowReentryOnDataChange ? void 0 : startReachedSnapshot,
1516
1752
  {
1517
- contentSize: state.totalSize,
1518
- dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1753
+ contentSize: totalSize,
1754
+ dataLength,
1519
1755
  scrollPosition: scroll
1520
1756
  },
1521
1757
  (distance) => {
1522
- var _a4, _b;
1523
- return (_b = (_a4 = state.props).onStartReached) == null ? void 0 : _b.call(_a4, { distanceFromStart: distance });
1758
+ var _a3, _b;
1759
+ return (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
1524
1760
  },
1525
1761
  (snapshot) => {
1526
1762
  state.startReachedSnapshot = snapshot;
1527
- }
1763
+ state.startReachedSnapshotDataChangeEpoch = snapshot ? dataChangeEpoch : void 0;
1764
+ },
1765
+ allowReentryOnDataChange
1528
1766
  );
1529
1767
  }
1530
1768
 
1769
+ // src/utils/checkThresholds.ts
1770
+ function checkThresholds(ctx) {
1771
+ checkAtBottom(ctx);
1772
+ checkAtTop(ctx);
1773
+ }
1774
+
1775
+ // src/utils/setInitialRenderState.ts
1776
+ function setInitialRenderState(ctx, {
1777
+ didLayout,
1778
+ didInitialScroll
1779
+ }) {
1780
+ const { state } = ctx;
1781
+ const {
1782
+ loadStartTime,
1783
+ props: { onLoad }
1784
+ } = state;
1785
+ if (didLayout) {
1786
+ state.didContainersLayout = true;
1787
+ }
1788
+ if (didInitialScroll) {
1789
+ state.didFinishInitialScroll = true;
1790
+ }
1791
+ const isReadyToRender = Boolean(state.didContainersLayout && state.didFinishInitialScroll);
1792
+ if (isReadyToRender && !peek$(ctx, "readyToRender")) {
1793
+ set$(ctx, "readyToRender", true);
1794
+ if (onLoad) {
1795
+ onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1796
+ }
1797
+ }
1798
+ }
1799
+
1800
+ // src/core/finishScrollTo.ts
1801
+ function finishScrollTo(ctx) {
1802
+ var _a3, _b;
1803
+ const state = ctx.state;
1804
+ if (state == null ? void 0 : state.scrollingTo) {
1805
+ const resolvePendingScroll = state.pendingScrollResolve;
1806
+ state.pendingScrollResolve = void 0;
1807
+ const scrollingTo = state.scrollingTo;
1808
+ state.scrollHistory.length = 0;
1809
+ state.initialScroll = void 0;
1810
+ state.initialAnchor = void 0;
1811
+ state.scrollingTo = void 0;
1812
+ if (state.pendingTotalSize !== void 0) {
1813
+ addTotalSize(ctx, null, state.pendingTotalSize);
1814
+ }
1815
+ if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1816
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1817
+ }
1818
+ {
1819
+ state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
1820
+ }
1821
+ setInitialRenderState(ctx, { didInitialScroll: true });
1822
+ checkThresholds(ctx);
1823
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1824
+ }
1825
+ }
1826
+
1827
+ // src/core/doScrollTo.ts
1828
+ var SCROLL_END_IDLE_MS = 80;
1829
+ var SCROLL_END_MAX_MS = 1500;
1830
+ var SMOOTH_SCROLL_DURATION_MS = 320;
1831
+ var SCROLL_END_TARGET_EPSILON = 1;
1832
+ function doScrollTo(ctx, params) {
1833
+ const state = ctx.state;
1834
+ const { animated, horizontal, offset } = params;
1835
+ const scroller = state.refScroller.current;
1836
+ const node = scroller == null ? void 0 : scroller.getScrollableNode();
1837
+ if (!scroller || !node) {
1838
+ return;
1839
+ }
1840
+ const isAnimated = !!animated;
1841
+ const isHorizontal = !!horizontal;
1842
+ const left = isHorizontal ? offset : 0;
1843
+ const top = isHorizontal ? 0 : offset;
1844
+ scroller.scrollTo({ animated: isAnimated, x: left, y: top });
1845
+ if (isAnimated) {
1846
+ const target = scroller.getScrollEventTarget();
1847
+ listenForScrollEnd(ctx, {
1848
+ readOffset: () => scroller.getCurrentScrollOffset(),
1849
+ target,
1850
+ targetOffset: offset
1851
+ });
1852
+ } else {
1853
+ state.scroll = offset;
1854
+ setTimeout(() => {
1855
+ finishScrollTo(ctx);
1856
+ }, 100);
1857
+ }
1858
+ }
1859
+ function listenForScrollEnd(ctx, params) {
1860
+ const { readOffset, target, targetOffset } = params;
1861
+ if (!target) {
1862
+ finishScrollTo(ctx);
1863
+ return;
1864
+ }
1865
+ const supportsScrollEnd = "onscrollend" in target;
1866
+ let idleTimeout;
1867
+ let settled = false;
1868
+ const targetToken = ctx.state.scrollingTo;
1869
+ const maxTimeout = setTimeout(() => finish("max"), SCROLL_END_MAX_MS);
1870
+ const cleanup = () => {
1871
+ target.removeEventListener("scroll", onScroll2);
1872
+ if (supportsScrollEnd) {
1873
+ target.removeEventListener("scrollend", onScrollEnd);
1874
+ }
1875
+ if (idleTimeout) {
1876
+ clearTimeout(idleTimeout);
1877
+ }
1878
+ clearTimeout(maxTimeout);
1879
+ };
1880
+ const finish = (reason) => {
1881
+ if (settled) return;
1882
+ if (targetToken !== ctx.state.scrollingTo) {
1883
+ settled = true;
1884
+ cleanup();
1885
+ return;
1886
+ }
1887
+ const currentOffset = readOffset();
1888
+ const isNearTarget = Math.abs(currentOffset - targetOffset) <= SCROLL_END_TARGET_EPSILON;
1889
+ if (reason === "scrollend" && !isNearTarget) {
1890
+ return;
1891
+ }
1892
+ settled = true;
1893
+ cleanup();
1894
+ finishScrollTo(ctx);
1895
+ };
1896
+ const onScroll2 = () => {
1897
+ if (idleTimeout) {
1898
+ clearTimeout(idleTimeout);
1899
+ }
1900
+ idleTimeout = setTimeout(() => finish("idle"), SCROLL_END_IDLE_MS);
1901
+ };
1902
+ const onScrollEnd = () => finish("scrollend");
1903
+ target.addEventListener("scroll", onScroll2);
1904
+ if (supportsScrollEnd) {
1905
+ target.addEventListener("scrollend", onScrollEnd);
1906
+ } else {
1907
+ idleTimeout = setTimeout(() => finish("idle"), SMOOTH_SCROLL_DURATION_MS);
1908
+ }
1909
+ }
1910
+
1911
+ // src/core/scrollTo.ts
1912
+ function scrollTo(ctx, params) {
1913
+ const state = ctx.state;
1914
+ const { noScrollingTo, forceScroll, ...scrollTarget } = params;
1915
+ const { animated, isInitialScroll, offset: scrollTargetOffset, precomputedWithViewOffset } = scrollTarget;
1916
+ const {
1917
+ props: { horizontal }
1918
+ } = state;
1919
+ if (state.animFrameCheckFinishedScroll) {
1920
+ cancelAnimationFrame(ctx.state.animFrameCheckFinishedScroll);
1921
+ }
1922
+ if (state.timeoutCheckFinishedScrollFallback) {
1923
+ clearTimeout(ctx.state.timeoutCheckFinishedScrollFallback);
1924
+ }
1925
+ let offset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, scrollTargetOffset, scrollTarget);
1926
+ offset = clampScrollOffset(ctx, offset, scrollTarget);
1927
+ state.scrollHistory.length = 0;
1928
+ if (!noScrollingTo) {
1929
+ state.scrollingTo = scrollTarget;
1930
+ }
1931
+ state.scrollPending = offset;
1932
+ if (forceScroll || !isInitialScroll || Platform.OS === "android") {
1933
+ doScrollTo(ctx, { animated, horizontal, offset });
1934
+ } else {
1935
+ state.scroll = offset;
1936
+ }
1937
+ }
1938
+
1531
1939
  // src/core/updateScroll.ts
1532
- function updateScroll(ctx, state, newScroll, forceUpdate) {
1533
- var _a3;
1534
- const scrollingTo = peek$(ctx, "scrollingTo");
1940
+ function updateScroll(ctx, newScroll, forceUpdate) {
1941
+ const state = ctx.state;
1942
+ const { ignoreScrollFromMVCP, lastScrollAdjustForHistory, scrollAdjustHandler, scrollHistory, scrollingTo } = state;
1943
+ const prevScroll = state.scroll;
1535
1944
  state.hasScrolled = true;
1536
1945
  state.lastBatchingAction = Date.now();
1537
1946
  const currentTime = Date.now();
1538
- const adjust = state.scrollAdjustHandler.getAdjust();
1539
- const lastHistoryAdjust = state.lastScrollAdjustForHistory;
1540
- const adjustChanged = lastHistoryAdjust !== void 0 && Math.abs(adjust - lastHistoryAdjust) > 0.1;
1947
+ const adjust = scrollAdjustHandler.getAdjust();
1948
+ const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
1541
1949
  if (adjustChanged) {
1542
- state.scrollHistory.length = 0;
1950
+ scrollHistory.length = 0;
1543
1951
  }
1544
1952
  state.lastScrollAdjustForHistory = adjust;
1545
- if (scrollingTo === void 0 && !(state.scrollHistory.length === 0 && newScroll === state.scroll)) {
1953
+ if (scrollingTo === void 0 && !(scrollHistory.length === 0 && newScroll === state.scroll)) {
1546
1954
  if (!adjustChanged) {
1547
- state.scrollHistory.push({ scroll: newScroll, time: currentTime });
1955
+ scrollHistory.push({ scroll: newScroll, time: currentTime });
1548
1956
  }
1549
1957
  }
1550
- if (state.scrollHistory.length > 5) {
1551
- state.scrollHistory.shift();
1958
+ if (scrollHistory.length > 5) {
1959
+ scrollHistory.shift();
1552
1960
  }
1553
- state.scrollPrev = state.scroll;
1554
- state.scrollPrevTime = state.scrollTime;
1555
- state.scroll = newScroll;
1556
- state.scrollTime = currentTime;
1557
- const ignoreScrollFromMVCP = state.ignoreScrollFromMVCP;
1558
1961
  if (ignoreScrollFromMVCP && !scrollingTo) {
1559
1962
  const { lt, gt } = ignoreScrollFromMVCP;
1560
1963
  if (lt && newScroll < lt || gt && newScroll > gt) {
@@ -1562,17 +1965,37 @@ function updateScroll(ctx, state, newScroll, forceUpdate) {
1562
1965
  return;
1563
1966
  }
1564
1967
  }
1565
- if (state.dataChangeNeedsScrollUpdate || Math.abs(state.scroll - state.scrollPrev) > 2) {
1968
+ state.scrollPrev = prevScroll;
1969
+ state.scrollPrevTime = state.scrollTime;
1970
+ state.scroll = newScroll;
1971
+ state.scrollTime = currentTime;
1972
+ const scrollDelta = Math.abs(newScroll - prevScroll);
1973
+ const scrollLength = state.scrollLength;
1974
+ const lastCalculated = state.scrollLastCalculate;
1975
+ const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
1976
+ const shouldUpdate = useAggressiveItemRecalculation || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
1977
+ if (shouldUpdate) {
1978
+ state.scrollLastCalculate = state.scroll;
1566
1979
  state.ignoreScrollFromMVCPIgnored = false;
1567
- (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { doMVCP: scrollingTo !== void 0 });
1568
- checkAtBottom(ctx, state);
1569
- checkAtTop(state);
1980
+ state.lastScrollDelta = scrollDelta;
1981
+ const runCalculateItems = () => {
1982
+ var _a3;
1983
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { doMVCP: scrollingTo !== void 0 });
1984
+ checkThresholds(ctx);
1985
+ };
1986
+ if (scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength) {
1987
+ ReactDOM.flushSync(runCalculateItems);
1988
+ } else {
1989
+ runCalculateItems();
1990
+ }
1570
1991
  state.dataChangeNeedsScrollUpdate = false;
1992
+ state.lastScrollDelta = 0;
1571
1993
  }
1572
1994
  }
1573
1995
 
1574
1996
  // src/utils/requestAdjust.ts
1575
- function requestAdjust(ctx, state, positionDiff, dataChanged) {
1997
+ function requestAdjust(ctx, positionDiff, dataChanged) {
1998
+ const state = ctx.state;
1576
1999
  if (Math.abs(positionDiff) > 0.1) {
1577
2000
  const doit = () => {
1578
2001
  {
@@ -1584,8 +2007,8 @@ function requestAdjust(ctx, state, positionDiff, dataChanged) {
1584
2007
  };
1585
2008
  state.scroll += positionDiff;
1586
2009
  state.scrollForNextCalculateItemsInView = void 0;
1587
- const didLayout = peek$(ctx, "containersDidLayout");
1588
- if (didLayout) {
2010
+ const readyToRender = peek$(ctx, "readyToRender");
2011
+ if (readyToRender) {
1589
2012
  doit();
1590
2013
  } else {
1591
2014
  state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
@@ -1594,106 +2017,144 @@ function requestAdjust(ctx, state, positionDiff, dataChanged) {
1594
2017
  }
1595
2018
  }
1596
2019
 
1597
- // src/core/ensureInitialAnchor.ts
1598
- var INITIAL_ANCHOR_TOLERANCE = 0.5;
1599
- var INITIAL_ANCHOR_MAX_ATTEMPTS = 4;
1600
- var INITIAL_ANCHOR_SETTLED_TICKS = 2;
1601
- function ensureInitialAnchor(ctx, state) {
1602
- var _a3, _b, _c, _d, _e;
1603
- const anchor = state.initialAnchor;
1604
- const item = state.props.data[anchor.index];
1605
- const containersDidLayout = peek$(ctx, "containersDidLayout");
1606
- if (!containersDidLayout) {
1607
- return;
1608
- }
1609
- const id = getId(state, anchor.index);
1610
- if (state.positions.get(id) === void 0) {
1611
- return;
1612
- }
1613
- const size = getItemSize(ctx, state, id, anchor.index, item, true, true);
1614
- if (size === void 0) {
1615
- return;
1616
- }
1617
- const availableSpace = Math.max(0, state.scrollLength - size);
1618
- const desiredOffset = calculateOffsetForIndex(ctx, state, anchor.index) - ((_a3 = anchor.viewOffset) != null ? _a3 : 0) - ((_b = anchor.viewPosition) != null ? _b : 0) * availableSpace;
1619
- const contentSize = getContentSize(ctx);
1620
- const maxOffset = Math.max(0, contentSize - state.scrollLength);
1621
- const clampedDesiredOffset = Math.max(0, Math.min(desiredOffset, maxOffset));
1622
- const delta = clampedDesiredOffset - state.scroll;
1623
- if (Math.abs(delta) <= INITIAL_ANCHOR_TOLERANCE) {
1624
- const settledTicks = ((_c = anchor.settledTicks) != null ? _c : 0) + 1;
1625
- if (settledTicks >= INITIAL_ANCHOR_SETTLED_TICKS) {
1626
- state.initialAnchor = void 0;
1627
- } else {
1628
- anchor.settledTicks = settledTicks;
2020
+ // src/core/mvcp.ts
2021
+ var MVCP_POSITION_EPSILON = 0.1;
2022
+ var MVCP_ANCHOR_LOCK_TTL_MS = 300;
2023
+ var MVCP_ANCHOR_LOCK_QUIET_PASSES_TO_RELEASE = 2;
2024
+ function resolveAnchorLock(state, enableMVCPAnchorLock, mvcpData, now) {
2025
+ if (!enableMVCPAnchorLock) {
2026
+ state.mvcpAnchorLock = void 0;
2027
+ return void 0;
2028
+ }
2029
+ const lock = state.mvcpAnchorLock;
2030
+ if (!lock) {
2031
+ return void 0;
2032
+ }
2033
+ const isExpired = now > lock.expiresAt;
2034
+ const isMissing = state.indexByKey.get(lock.id) === void 0;
2035
+ if (isExpired || isMissing || !mvcpData) {
2036
+ state.mvcpAnchorLock = void 0;
2037
+ return void 0;
2038
+ }
2039
+ return lock;
2040
+ }
2041
+ function updateAnchorLock(state, params) {
2042
+ {
2043
+ const { anchorId, anchorPosition, dataChanged, now, positionDiff } = params;
2044
+ const enableMVCPAnchorLock = !!dataChanged || !!state.mvcpAnchorLock;
2045
+ const mvcpData = state.props.maintainVisibleContentPosition.data;
2046
+ if (!enableMVCPAnchorLock || !mvcpData || state.scrollingTo || !anchorId || anchorPosition === void 0) {
2047
+ return;
1629
2048
  }
1630
- return;
1631
- }
1632
- if (((_d = anchor.attempts) != null ? _d : 0) >= INITIAL_ANCHOR_MAX_ATTEMPTS) {
1633
- state.initialAnchor = void 0;
1634
- return;
1635
- }
1636
- const lastDelta = anchor.lastDelta;
1637
- if (lastDelta !== void 0 && Math.abs(delta) >= Math.abs(lastDelta)) {
1638
- state.initialAnchor = void 0;
1639
- return;
2049
+ const existingLock = state.mvcpAnchorLock;
2050
+ const quietPasses = !dataChanged && Math.abs(positionDiff) <= MVCP_POSITION_EPSILON && (existingLock == null ? void 0 : existingLock.id) === anchorId ? existingLock.quietPasses + 1 : 0;
2051
+ if (!dataChanged && quietPasses >= MVCP_ANCHOR_LOCK_QUIET_PASSES_TO_RELEASE) {
2052
+ state.mvcpAnchorLock = void 0;
2053
+ return;
2054
+ }
2055
+ state.mvcpAnchorLock = {
2056
+ expiresAt: now + MVCP_ANCHOR_LOCK_TTL_MS,
2057
+ id: anchorId,
2058
+ position: anchorPosition,
2059
+ quietPasses
2060
+ };
1640
2061
  }
1641
- Object.assign(anchor, {
1642
- attempts: ((_e = anchor.attempts) != null ? _e : 0) + 1,
1643
- lastDelta: delta,
1644
- settledTicks: 0
1645
- });
1646
- requestAdjust(ctx, state, delta);
1647
2062
  }
1648
-
1649
- // src/core/mvcp.ts
1650
- function prepareMVCP(ctx, state, dataChanged) {
2063
+ function prepareMVCP(ctx, dataChanged) {
2064
+ const state = ctx.state;
1651
2065
  const { idsInView, positions, props } = state;
1652
- const { maintainVisibleContentPosition } = props;
1653
- const scrollingTo = peek$(ctx, "scrollingTo");
2066
+ const {
2067
+ maintainVisibleContentPosition: { data: mvcpData, size: mvcpScroll, shouldRestorePosition }
2068
+ } = props;
2069
+ const now = Date.now();
2070
+ const enableMVCPAnchorLock = (!!dataChanged || !!state.mvcpAnchorLock);
2071
+ const scrollingTo = state.scrollingTo;
2072
+ const anchorLock = resolveAnchorLock(state, enableMVCPAnchorLock, mvcpData, now) ;
1654
2073
  let prevPosition;
1655
2074
  let targetId;
1656
2075
  const idsInViewWithPositions = [];
1657
2076
  const scrollTarget = scrollingTo == null ? void 0 : scrollingTo.index;
1658
- const shouldMVCP = !dataChanged || maintainVisibleContentPosition;
2077
+ const scrollingToViewPosition = scrollingTo == null ? void 0 : scrollingTo.viewPosition;
2078
+ const isEndAnchoredScrollTarget = scrollTarget !== void 0 && state.props.data.length > 0 && scrollTarget >= state.props.data.length - 1 && (scrollingToViewPosition != null ? scrollingToViewPosition : 0) > 0;
2079
+ const shouldMVCP = dataChanged ? mvcpData : mvcpScroll;
1659
2080
  const indexByKey = state.indexByKey;
1660
2081
  if (shouldMVCP) {
1661
- if (scrollTarget !== void 0) {
2082
+ if (anchorLock && scrollTarget === void 0) {
2083
+ targetId = anchorLock.id;
2084
+ prevPosition = anchorLock.position;
2085
+ } else if (scrollTarget !== void 0) {
1662
2086
  targetId = getId(state, scrollTarget);
1663
- } else if (idsInView.length > 0 && peek$(ctx, "containersDidLayout")) {
1664
- if (dataChanged) {
1665
- for (let i = 0; i < idsInView.length; i++) {
1666
- const id = idsInView[i];
1667
- const index = indexByKey.get(id);
1668
- if (index !== void 0) {
1669
- idsInViewWithPositions.push({ id, position: positions.get(id) });
2087
+ } else if (idsInView.length > 0 && state.didContainersLayout && !dataChanged) {
2088
+ targetId = idsInView.find((id) => indexByKey.get(id) !== void 0);
2089
+ }
2090
+ if (dataChanged && idsInView.length > 0 && state.didContainersLayout) {
2091
+ for (let i = 0; i < idsInView.length; i++) {
2092
+ const id = idsInView[i];
2093
+ const index = indexByKey.get(id);
2094
+ if (index !== void 0) {
2095
+ const position = positions[index];
2096
+ if (position !== void 0) {
2097
+ idsInViewWithPositions.push({ id, position });
1670
2098
  }
1671
2099
  }
1672
- } else {
1673
- targetId = idsInView.find((id) => indexByKey.get(id) !== void 0);
1674
2100
  }
1675
2101
  }
1676
- if (targetId !== void 0) {
1677
- prevPosition = positions.get(targetId);
2102
+ if (targetId !== void 0 && prevPosition === void 0) {
2103
+ const targetIndex = indexByKey.get(targetId);
2104
+ if (targetIndex !== void 0) {
2105
+ prevPosition = positions[targetIndex];
2106
+ }
1678
2107
  }
1679
2108
  return () => {
1680
- let positionDiff;
1681
- if (dataChanged && targetId === void 0 && maintainVisibleContentPosition) {
2109
+ let positionDiff = 0;
2110
+ let anchorIdForLock = anchorLock == null ? void 0 : anchorLock.id;
2111
+ let anchorPositionForLock;
2112
+ let skipTargetAnchor = false;
2113
+ const data = state.props.data;
2114
+ const shouldValidateLockedAnchor = dataChanged && mvcpData && scrollTarget === void 0 && targetId !== void 0 && (anchorLock == null ? void 0 : anchorLock.id) === targetId && shouldRestorePosition !== void 0;
2115
+ if (shouldValidateLockedAnchor && targetId !== void 0) {
2116
+ const index = indexByKey.get(targetId);
2117
+ if (index !== void 0) {
2118
+ const item = data[index];
2119
+ skipTargetAnchor = item === void 0 || !shouldRestorePosition(item, index, data);
2120
+ if (skipTargetAnchor && (anchorLock == null ? void 0 : anchorLock.id) === targetId) {
2121
+ state.mvcpAnchorLock = void 0;
2122
+ }
2123
+ }
2124
+ }
2125
+ const shouldUseFallbackVisibleAnchor = dataChanged && mvcpData && scrollTarget === void 0 && (() => {
2126
+ if (targetId === void 0 || skipTargetAnchor) {
2127
+ return true;
2128
+ }
2129
+ const targetIndex = indexByKey.get(targetId);
2130
+ return targetIndex === void 0 || positions[targetIndex] === void 0;
2131
+ })();
2132
+ if (shouldUseFallbackVisibleAnchor) {
1682
2133
  for (let i = 0; i < idsInViewWithPositions.length; i++) {
1683
2134
  const { id, position } = idsInViewWithPositions[i];
1684
- const newPosition = positions.get(id);
2135
+ const index = indexByKey.get(id);
2136
+ if (index !== void 0 && shouldRestorePosition) {
2137
+ const item = data[index];
2138
+ if (item === void 0 || !shouldRestorePosition(item, index, data)) {
2139
+ continue;
2140
+ }
2141
+ }
2142
+ const newPosition = index !== void 0 ? positions[index] : void 0;
1685
2143
  if (newPosition !== void 0) {
1686
2144
  positionDiff = newPosition - position;
2145
+ anchorIdForLock = id;
2146
+ anchorPositionForLock = newPosition;
1687
2147
  break;
1688
2148
  }
1689
2149
  }
1690
2150
  }
1691
- if (targetId !== void 0 && prevPosition !== void 0) {
1692
- const newPosition = positions.get(targetId);
2151
+ if (!skipTargetAnchor && targetId !== void 0 && prevPosition !== void 0) {
2152
+ const targetIndex = indexByKey.get(targetId);
2153
+ const newPosition = targetIndex !== void 0 ? positions[targetIndex] : void 0;
1693
2154
  if (newPosition !== void 0) {
1694
2155
  const totalSize = getContentSize(ctx);
1695
2156
  let diff = newPosition - prevPosition;
1696
- if (diff !== 0 && state.scroll + state.scrollLength > totalSize) {
2157
+ if (diff !== 0 && isEndAnchoredScrollTarget && state.scroll + state.scrollLength > totalSize) {
1697
2158
  if (diff > 0) {
1698
2159
  diff = Math.max(0, totalSize - state.scroll - state.scrollLength);
1699
2160
  } else {
@@ -1701,33 +2162,52 @@ function prepareMVCP(ctx, state, dataChanged) {
1701
2162
  }
1702
2163
  }
1703
2164
  positionDiff = diff;
2165
+ anchorIdForLock = targetId;
2166
+ anchorPositionForLock = newPosition;
2167
+ }
2168
+ }
2169
+ if (scrollingToViewPosition && scrollingToViewPosition > 0) {
2170
+ const newSize = getItemSize(ctx, targetId, scrollTarget, state.props.data[scrollTarget]);
2171
+ const prevSize = scrollingTo == null ? void 0 : scrollingTo.itemSize;
2172
+ if (newSize !== void 0 && prevSize !== void 0 && newSize !== prevSize) {
2173
+ const diff = newSize - prevSize;
2174
+ if (diff !== 0) {
2175
+ positionDiff += diff * scrollingToViewPosition;
2176
+ scrollingTo.itemSize = newSize;
2177
+ }
1704
2178
  }
1705
2179
  }
1706
- if (positionDiff !== void 0 && Math.abs(positionDiff) > 0.1) {
1707
- requestAdjust(ctx, state, positionDiff);
2180
+ updateAnchorLock(state, {
2181
+ anchorId: anchorIdForLock,
2182
+ anchorPosition: anchorPositionForLock,
2183
+ dataChanged,
2184
+ now,
2185
+ positionDiff
2186
+ });
2187
+ if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
2188
+ requestAdjust(ctx, positionDiff);
1708
2189
  }
1709
2190
  };
1710
2191
  }
1711
2192
  }
1712
2193
 
1713
2194
  // src/core/prepareColumnStartState.ts
1714
- function prepareColumnStartState(ctx, state, startIndex, useAverageSize) {
2195
+ function prepareColumnStartState(ctx, startIndex, useAverageSize) {
1715
2196
  var _a3;
2197
+ const state = ctx.state;
1716
2198
  const numColumns = peek$(ctx, "numColumns");
1717
2199
  let rowStartIndex = startIndex;
1718
- const columnAtStart = state.columns.get(state.idCache[startIndex]);
2200
+ const columnAtStart = state.columns[startIndex];
1719
2201
  if (columnAtStart !== 1) {
1720
2202
  rowStartIndex = findRowStartIndex(state, numColumns, startIndex);
1721
2203
  }
1722
2204
  let currentRowTop = 0;
1723
- const curId = state.idCache[rowStartIndex];
1724
- const column = state.columns.get(curId);
2205
+ const column = state.columns[rowStartIndex];
1725
2206
  if (rowStartIndex > 0) {
1726
2207
  const prevIndex = rowStartIndex - 1;
1727
- const prevId = state.idCache[prevIndex];
1728
- const prevPosition = (_a3 = state.positions.get(prevId)) != null ? _a3 : 0;
2208
+ const prevPosition = (_a3 = state.positions[prevIndex]) != null ? _a3 : 0;
1729
2209
  const prevRowStart = findRowStartIndex(state, numColumns, prevIndex);
1730
- const prevRowHeight = calculateRowMaxSize(ctx, state, prevRowStart, prevIndex, useAverageSize);
2210
+ const prevRowHeight = calculateRowMaxSize(ctx, prevRowStart, prevIndex, useAverageSize);
1731
2211
  currentRowTop = prevPosition + prevRowHeight;
1732
2212
  }
1733
2213
  return {
@@ -1742,7 +2222,7 @@ function findRowStartIndex(state, numColumns, index) {
1742
2222
  }
1743
2223
  let rowStart = Math.max(0, index);
1744
2224
  while (rowStart > 0) {
1745
- const columnForIndex = state.columns.get(state.idCache[rowStart]);
2225
+ const columnForIndex = state.columns[rowStart];
1746
2226
  if (columnForIndex === 1) {
1747
2227
  break;
1748
2228
  }
@@ -1750,7 +2230,8 @@ function findRowStartIndex(state, numColumns, index) {
1750
2230
  }
1751
2231
  return rowStart;
1752
2232
  }
1753
- function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
2233
+ function calculateRowMaxSize(ctx, startIndex, endIndex, useAverageSize) {
2234
+ const state = ctx.state;
1754
2235
  if (endIndex < startIndex) {
1755
2236
  return 0;
1756
2237
  }
@@ -1764,7 +2245,7 @@ function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1764
2245
  continue;
1765
2246
  }
1766
2247
  const id = state.idCache[i];
1767
- const size = getItemSize(ctx, state, id, i, data[i], useAverageSize);
2248
+ const size = getItemSize(ctx, id, i, data[i], useAverageSize);
1768
2249
  if (size > maxSize) {
1769
2250
  maxSize = size;
1770
2251
  }
@@ -1773,22 +2254,44 @@ function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1773
2254
  }
1774
2255
 
1775
2256
  // src/core/updateTotalSize.ts
1776
- function updateTotalSize(ctx, state) {
2257
+ function updateTotalSize(ctx) {
2258
+ var _a3, _b;
2259
+ const state = ctx.state;
1777
2260
  const {
1778
2261
  positions,
1779
2262
  props: { data }
1780
2263
  } = state;
2264
+ const numColumns = (_a3 = peek$(ctx, "numColumns")) != null ? _a3 : 1;
1781
2265
  if (data.length === 0) {
1782
- addTotalSize(ctx, state, null, 0);
2266
+ addTotalSize(ctx, null, 0);
1783
2267
  } else {
1784
- const lastId = getId(state, data.length - 1);
1785
- if (lastId !== void 0) {
1786
- const lastPosition = positions.get(lastId);
1787
- if (lastPosition !== void 0) {
1788
- const lastSize = getItemSize(ctx, state, lastId, data.length - 1, data[data.length - 1]);
2268
+ const lastIndex = data.length - 1;
2269
+ const lastId = getId(state, lastIndex);
2270
+ const lastPosition = positions[lastIndex];
2271
+ if (lastId !== void 0 && lastPosition !== void 0) {
2272
+ if (numColumns > 1) {
2273
+ let rowStart = lastIndex;
2274
+ while (rowStart > 0) {
2275
+ const column = state.columns[rowStart];
2276
+ if (column === 1 || column === void 0) {
2277
+ break;
2278
+ }
2279
+ rowStart -= 1;
2280
+ }
2281
+ let maxSize = 0;
2282
+ for (let i = rowStart; i <= lastIndex; i++) {
2283
+ const rowId = (_b = state.idCache[i]) != null ? _b : getId(state, i);
2284
+ const size = getItemSize(ctx, rowId, i, data[i]);
2285
+ if (size > maxSize) {
2286
+ maxSize = size;
2287
+ }
2288
+ }
2289
+ addTotalSize(ctx, null, lastPosition + maxSize);
2290
+ } else {
2291
+ const lastSize = getItemSize(ctx, lastId, lastIndex, data[lastIndex]);
1789
2292
  if (lastSize !== void 0) {
1790
2293
  const totalSize = lastPosition + lastSize;
1791
- addTotalSize(ctx, state, null, totalSize);
2294
+ addTotalSize(ctx, null, totalSize);
1792
2295
  }
1793
2296
  }
1794
2297
  }
@@ -1798,90 +2301,107 @@ function updateTotalSize(ctx, state) {
1798
2301
  // src/utils/getScrollVelocity.ts
1799
2302
  var getScrollVelocity = (state) => {
1800
2303
  const { scrollHistory } = state;
1801
- let velocity = 0;
1802
- if (scrollHistory.length >= 1) {
1803
- const newest = scrollHistory[scrollHistory.length - 1];
1804
- let oldest;
1805
- let start = 0;
1806
- const now = Date.now();
1807
- for (let i = 0; i < scrollHistory.length - 1; i++) {
1808
- const entry = scrollHistory[i];
1809
- const nextEntry = scrollHistory[i + 1];
1810
- if (i > 0) {
1811
- const prevEntry = scrollHistory[i - 1];
1812
- const prevDirection = entry.scroll - prevEntry.scroll;
1813
- const currentDirection = nextEntry.scroll - entry.scroll;
1814
- if (prevDirection > 0 && currentDirection < 0 || prevDirection < 0 && currentDirection > 0) {
1815
- start = i;
1816
- break;
1817
- }
1818
- }
2304
+ const newestIndex = scrollHistory.length - 1;
2305
+ if (newestIndex < 1) {
2306
+ return 0;
2307
+ }
2308
+ const newest = scrollHistory[newestIndex];
2309
+ const now = Date.now();
2310
+ let direction = 0;
2311
+ for (let i = newestIndex; i > 0; i--) {
2312
+ const delta = scrollHistory[i].scroll - scrollHistory[i - 1].scroll;
2313
+ if (delta !== 0) {
2314
+ direction = Math.sign(delta);
2315
+ break;
1819
2316
  }
1820
- for (let i = start; i < scrollHistory.length - 1; i++) {
1821
- const entry = scrollHistory[i];
1822
- if (now - entry.time <= 1e3) {
1823
- oldest = entry;
1824
- break;
1825
- }
2317
+ }
2318
+ if (direction === 0) {
2319
+ return 0;
2320
+ }
2321
+ let oldest = newest;
2322
+ for (let i = newestIndex - 1; i >= 0; i--) {
2323
+ const current = scrollHistory[i];
2324
+ const next = scrollHistory[i + 1];
2325
+ const delta = next.scroll - current.scroll;
2326
+ const deltaSign = Math.sign(delta);
2327
+ if (deltaSign !== 0 && deltaSign !== direction) {
2328
+ break;
1826
2329
  }
1827
- if (oldest && oldest !== newest) {
1828
- const scrollDiff = newest.scroll - oldest.scroll;
1829
- const timeDiff = newest.time - oldest.time;
1830
- velocity = timeDiff > 0 ? scrollDiff / timeDiff : 0;
2330
+ if (now - current.time > 1e3) {
2331
+ break;
1831
2332
  }
2333
+ oldest = current;
1832
2334
  }
1833
- return velocity;
2335
+ const scrollDiff = newest.scroll - oldest.scroll;
2336
+ const timeDiff = newest.time - oldest.time;
2337
+ return timeDiff > 0 ? scrollDiff / timeDiff : 0;
1834
2338
  };
1835
2339
 
1836
2340
  // src/utils/updateSnapToOffsets.ts
1837
- function updateSnapToOffsets(ctx, state) {
2341
+ function updateSnapToOffsets(ctx) {
2342
+ const state = ctx.state;
1838
2343
  const {
1839
- positions,
1840
2344
  props: { snapToIndices }
1841
2345
  } = state;
1842
2346
  const snapToOffsets = Array(snapToIndices.length);
1843
2347
  for (let i = 0; i < snapToIndices.length; i++) {
1844
2348
  const idx = snapToIndices[i];
1845
- const key = getId(state, idx);
1846
- snapToOffsets[i] = positions.get(key);
2349
+ getId(state, idx);
2350
+ snapToOffsets[i] = state.positions[idx];
1847
2351
  }
1848
2352
  set$(ctx, "snapToOffsets", snapToOffsets);
1849
2353
  }
1850
2354
 
1851
2355
  // src/core/updateItemPositions.ts
1852
- function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP } = {
2356
+ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP } = {
1853
2357
  doMVCP: false,
1854
2358
  forceFullUpdate: false,
1855
2359
  scrollBottomBuffered: -1,
1856
2360
  startIndex: 0
1857
2361
  }) {
1858
2362
  var _a3, _b, _c, _d, _e;
2363
+ const state = ctx.state;
2364
+ const hasPositionListeners = ctx.positionListeners.size > 0;
1859
2365
  const {
1860
2366
  columns,
2367
+ columnSpans,
1861
2368
  indexByKey,
1862
2369
  positions,
1863
2370
  idCache,
1864
2371
  sizesKnown,
1865
- props: { getEstimatedItemSize, snapToIndices, enableAverages }
2372
+ props: { data, getEstimatedItemSize, overrideItemLayout, snapToIndices },
2373
+ scrollingTo
1866
2374
  } = state;
1867
- const data = state.props.data;
1868
2375
  const dataLength = data.length;
1869
- const numColumns = peek$(ctx, "numColumns");
1870
- const scrollingTo = peek$(ctx, "scrollingTo");
2376
+ const numColumns = (_a3 = peek$(ctx, "numColumns")) != null ? _a3 : 1;
1871
2377
  const hasColumns = numColumns > 1;
1872
2378
  const indexByKeyForChecking = IS_DEV ? /* @__PURE__ */ new Map() : void 0;
1873
- const shouldOptimize = !forceFullUpdate && !dataChanged && Math.abs(getScrollVelocity(state)) > 0;
2379
+ const extraData = peek$(ctx, "extraData");
2380
+ const layoutConfig = overrideItemLayout ? { span: 1 } : void 0;
2381
+ const lastScrollDelta = state.lastScrollDelta;
2382
+ const velocity = getScrollVelocity(state);
2383
+ const shouldOptimize = !forceFullUpdate && !dataChanged && (Math.abs(velocity) > 0 || state.scrollLength > 0 && lastScrollDelta > state.scrollLength);
1874
2384
  const maxVisibleArea = scrollBottomBuffered + 1e3;
1875
- const useAverageSize = enableAverages && !getEstimatedItemSize;
1876
- const preferCachedSize = !doMVCP || dataChanged || state.scrollAdjustHandler.getAdjust() !== 0 || ((_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0) !== 0;
2385
+ const useAverageSize = !getEstimatedItemSize;
2386
+ const preferCachedSize = !doMVCP || dataChanged || state.scrollAdjustHandler.getAdjust() !== 0 || ((_b = peek$(ctx, "scrollAdjustPending")) != null ? _b : 0) !== 0;
1877
2387
  let currentRowTop = 0;
1878
2388
  let column = 1;
1879
2389
  let maxSizeInRow = 0;
2390
+ if (dataChanged) {
2391
+ columnSpans.length = 0;
2392
+ }
2393
+ if (!hasColumns) {
2394
+ if (columns.length) {
2395
+ columns.length = 0;
2396
+ }
2397
+ if (columnSpans.length) {
2398
+ columnSpans.length = 0;
2399
+ }
2400
+ }
1880
2401
  if (startIndex > 0) {
1881
2402
  if (hasColumns) {
1882
2403
  const { startIndex: processedStartIndex, currentRowTop: initialRowTop } = prepareColumnStartState(
1883
2404
  ctx,
1884
- state,
1885
2405
  startIndex,
1886
2406
  useAverageSize
1887
2407
  );
@@ -1890,12 +2410,13 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1890
2410
  } else if (startIndex < dataLength) {
1891
2411
  const prevIndex = startIndex - 1;
1892
2412
  const prevId = getId(state, prevIndex);
1893
- const prevPosition = (_b = positions.get(prevId)) != null ? _b : 0;
1894
- const prevSize = (_c = sizesKnown.get(prevId)) != null ? _c : getItemSize(ctx, state, prevId, prevIndex, data[prevIndex], useAverageSize, preferCachedSize);
2413
+ const prevPosition = (_c = positions[prevIndex]) != null ? _c : 0;
2414
+ const prevSize = (_d = sizesKnown.get(prevId)) != null ? _d : getItemSize(ctx, prevId, prevIndex, data[prevIndex], useAverageSize, preferCachedSize);
1895
2415
  currentRowTop = prevPosition + prevSize;
1896
2416
  }
1897
2417
  }
1898
2418
  const needsIndexByKey = dataChanged || indexByKey.size === 0;
2419
+ const canOverrideSpan = hasColumns && !!overrideItemLayout && !!layoutConfig;
1899
2420
  let didBreakEarly = false;
1900
2421
  let breakAt;
1901
2422
  for (let i = startIndex; i < dataLength; i++) {
@@ -1907,8 +2428,23 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1907
2428
  const itemsPerRow = hasColumns ? numColumns : 1;
1908
2429
  breakAt = i + itemsPerRow + 10;
1909
2430
  }
1910
- const id = (_d = idCache[i]) != null ? _d : getId(state, i);
1911
- const size = (_e = sizesKnown.get(id)) != null ? _e : getItemSize(ctx, state, id, i, data[i], useAverageSize, preferCachedSize);
2431
+ const id = (_e = idCache[i]) != null ? _e : getId(state, i);
2432
+ let span = 1;
2433
+ if (canOverrideSpan) {
2434
+ layoutConfig.span = 1;
2435
+ overrideItemLayout(layoutConfig, data[i], i, numColumns, extraData);
2436
+ const requestedSpan = layoutConfig.span;
2437
+ if (requestedSpan !== void 0 && Number.isFinite(requestedSpan)) {
2438
+ span = Math.max(1, Math.min(numColumns, Math.round(requestedSpan)));
2439
+ }
2440
+ }
2441
+ if (hasColumns && column + span - 1 > numColumns) {
2442
+ currentRowTop += maxSizeInRow;
2443
+ column = 1;
2444
+ maxSizeInRow = 0;
2445
+ }
2446
+ const knownSize = sizesKnown.get(id);
2447
+ const size = knownSize !== void 0 ? knownSize : getItemSize(ctx, id, i, data[i], useAverageSize, preferCachedSize);
1912
2448
  if (IS_DEV && needsIndexByKey) {
1913
2449
  if (indexByKeyForChecking.has(id)) {
1914
2450
  console.error(
@@ -1917,30 +2453,36 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1917
2453
  }
1918
2454
  indexByKeyForChecking.set(id, i);
1919
2455
  }
1920
- positions.set(id, currentRowTop);
2456
+ if (currentRowTop !== positions[i]) {
2457
+ positions[i] = currentRowTop;
2458
+ if (hasPositionListeners) {
2459
+ notifyPosition$(ctx, id, currentRowTop);
2460
+ }
2461
+ }
1921
2462
  if (needsIndexByKey) {
1922
2463
  indexByKey.set(id, i);
1923
2464
  }
1924
- columns.set(id, column);
1925
- if (hasColumns) {
2465
+ if (!hasColumns) {
2466
+ currentRowTop += size;
2467
+ } else {
2468
+ columns[i] = column;
2469
+ columnSpans[i] = span;
1926
2470
  if (size > maxSizeInRow) {
1927
2471
  maxSizeInRow = size;
1928
2472
  }
1929
- column++;
2473
+ column += span;
1930
2474
  if (column > numColumns) {
1931
2475
  currentRowTop += maxSizeInRow;
1932
2476
  column = 1;
1933
2477
  maxSizeInRow = 0;
1934
2478
  }
1935
- } else {
1936
- currentRowTop += size;
1937
2479
  }
1938
2480
  }
1939
2481
  if (!didBreakEarly) {
1940
- updateTotalSize(ctx, state);
2482
+ updateTotalSize(ctx);
1941
2483
  }
1942
2484
  if (snapToIndices) {
1943
- updateSnapToOffsets(ctx, state);
2485
+ updateSnapToOffsets(ctx);
1944
2486
  }
1945
2487
  }
1946
2488
 
@@ -2018,7 +2560,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
2018
2560
  if (previousViewableItems) {
2019
2561
  for (const viewToken of previousViewableItems) {
2020
2562
  const containerId = findContainerId(ctx, viewToken.key);
2021
- if (!isViewable(
2563
+ if (!checkIsViewable(
2022
2564
  state,
2023
2565
  ctx,
2024
2566
  viewabilityConfig,
@@ -2039,7 +2581,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
2039
2581
  if (item) {
2040
2582
  const key = getId(state, i);
2041
2583
  const containerId = findContainerId(ctx, key);
2042
- if (isViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, i)) {
2584
+ if (checkIsViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, i)) {
2043
2585
  const viewToken = {
2044
2586
  containerId,
2045
2587
  index: i,
@@ -2085,25 +2627,49 @@ function shallowEqual(prev, next) {
2085
2627
  return true;
2086
2628
  }
2087
2629
  function computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
2088
- const { sizes, positions, scroll: scrollState } = state;
2630
+ const { sizes, scroll: scrollState } = state;
2089
2631
  const topPad = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
2090
2632
  const { itemVisiblePercentThreshold, viewAreaCoveragePercentThreshold } = viewabilityConfig;
2091
2633
  const viewAreaMode = viewAreaCoveragePercentThreshold != null;
2092
2634
  const viewablePercentThreshold = viewAreaMode ? viewAreaCoveragePercentThreshold : itemVisiblePercentThreshold;
2093
2635
  const scroll = scrollState - topPad;
2094
- const top = positions.get(key) - scroll;
2636
+ const position = state.positions[index];
2095
2637
  const size = sizes.get(key) || 0;
2638
+ if (position === void 0) {
2639
+ const value2 = {
2640
+ containerId,
2641
+ index,
2642
+ isViewable: false,
2643
+ item,
2644
+ key,
2645
+ percentOfScroller: 0,
2646
+ percentVisible: 0,
2647
+ scrollSize,
2648
+ size,
2649
+ sizeVisible: -1
2650
+ };
2651
+ const prev2 = ctx.mapViewabilityAmountValues.get(containerId);
2652
+ if (!shallowEqual(prev2, value2)) {
2653
+ ctx.mapViewabilityAmountValues.set(containerId, value2);
2654
+ const cb = ctx.mapViewabilityAmountCallbacks.get(containerId);
2655
+ if (cb) {
2656
+ cb(value2);
2657
+ }
2658
+ }
2659
+ return value2;
2660
+ }
2661
+ const top = position - scroll;
2096
2662
  const bottom = top + size;
2097
2663
  const isEntirelyVisible = top >= 0 && bottom <= scrollSize && bottom > top;
2098
2664
  const sizeVisible = isEntirelyVisible ? size : Math.min(bottom, scrollSize) - Math.max(top, 0);
2099
2665
  const percentVisible = size ? isEntirelyVisible ? 100 : 100 * (sizeVisible / size) : 0;
2100
2666
  const percentOfScroller = size ? 100 * (sizeVisible / scrollSize) : 0;
2101
2667
  const percent = isEntirelyVisible ? 100 : viewAreaMode ? percentOfScroller : percentVisible;
2102
- const isViewable2 = percent >= viewablePercentThreshold;
2668
+ const isViewable = percent >= viewablePercentThreshold;
2103
2669
  const value = {
2104
2670
  containerId,
2105
2671
  index,
2106
- isViewable: isViewable2,
2672
+ isViewable,
2107
2673
  item,
2108
2674
  key,
2109
2675
  percentOfScroller,
@@ -2122,8 +2688,11 @@ function computeViewability(state, ctx, viewabilityConfig, containerId, key, scr
2122
2688
  }
2123
2689
  return value;
2124
2690
  }
2125
- function isViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
2126
- const value = ctx.mapViewabilityAmountValues.get(containerId) || computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index);
2691
+ function checkIsViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
2692
+ let value = ctx.mapViewabilityAmountValues.get(containerId);
2693
+ if (!value || value.key !== key) {
2694
+ value = computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index);
2695
+ }
2127
2696
  return value.isViewable;
2128
2697
  }
2129
2698
  function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
@@ -2132,6 +2701,8 @@ function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
2132
2701
  const cb = ctx.mapViewabilityCallbacks.get(key);
2133
2702
  cb == null ? void 0 : cb(viewToken);
2134
2703
  }
2704
+ var unstableBatchedUpdates = ReactDOM__namespace.unstable_batchedUpdates;
2705
+ var batchedUpdates = typeof unstableBatchedUpdates === "function" ? unstableBatchedUpdates : (fn) => fn();
2135
2706
 
2136
2707
  // src/utils/checkAllSizesKnown.ts
2137
2708
  function isNullOrUndefined2(value) {
@@ -2151,8 +2722,9 @@ function checkAllSizesKnown(state) {
2151
2722
  }
2152
2723
 
2153
2724
  // src/utils/findAvailableContainers.ts
2154
- function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers) {
2725
+ function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers) {
2155
2726
  const numContainers = peek$(ctx, "numContainers");
2727
+ const state = ctx.state;
2156
2728
  const { stickyContainerPool, containerItemTypes } = state;
2157
2729
  const result = [];
2158
2730
  const availableContainers = [];
@@ -2272,92 +2844,93 @@ function comparatorByDistance(a, b) {
2272
2844
  }
2273
2845
 
2274
2846
  // src/core/scrollToIndex.ts
2275
- function scrollToIndex(ctx, state, { index, viewOffset = 0, animated = true, viewPosition }) {
2276
- if (index >= state.props.data.length) {
2277
- index = state.props.data.length - 1;
2847
+ function scrollToIndex(ctx, { index, viewOffset = 0, animated = true, viewPosition }) {
2848
+ const state = ctx.state;
2849
+ const { data } = state.props;
2850
+ if (index >= data.length) {
2851
+ index = data.length - 1;
2278
2852
  } else if (index < 0) {
2279
2853
  index = 0;
2280
2854
  }
2281
- const firstIndexOffset = calculateOffsetForIndex(ctx, state, index);
2282
- const isLast = index === state.props.data.length - 1;
2855
+ const firstIndexOffset = calculateOffsetForIndex(ctx, index);
2856
+ const isLast = index === data.length - 1;
2283
2857
  if (isLast && viewPosition === void 0) {
2284
2858
  viewPosition = 1;
2285
2859
  }
2286
2860
  state.scrollForNextCalculateItemsInView = void 0;
2287
- scrollTo(ctx, state, {
2861
+ const targetId = getId(state, index);
2862
+ const itemSize = getItemSize(ctx, targetId, index, state.props.data[index]);
2863
+ scrollTo(ctx, {
2288
2864
  animated,
2289
2865
  index,
2866
+ itemSize,
2290
2867
  offset: firstIndexOffset,
2291
2868
  viewOffset,
2292
2869
  viewPosition: viewPosition != null ? viewPosition : 0
2293
2870
  });
2294
- }
2295
-
2296
- // src/utils/setDidLayout.ts
2297
- function setDidLayout(ctx, state) {
2298
- const {
2299
- loadStartTime,
2300
- initialScroll,
2301
- props: { onLoad }
2302
- } = state;
2871
+ }
2872
+
2873
+ // src/utils/setDidLayout.ts
2874
+ function setDidLayout(ctx) {
2875
+ const state = ctx.state;
2876
+ const { initialScroll } = state;
2303
2877
  state.queuedInitialLayout = true;
2304
- checkAtBottom(ctx, state);
2305
- const setIt = () => {
2306
- set$(ctx, "containersDidLayout", true);
2307
- if (onLoad) {
2308
- onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
2309
- }
2310
- };
2311
- {
2312
- setIt();
2313
- }
2878
+ checkAtBottom(ctx);
2879
+ if ((initialScroll == null ? void 0 : initialScroll.index) !== void 0) {
2880
+ const target = initialScroll;
2881
+ const runScroll = () => scrollToIndex(ctx, { ...target, animated: false });
2882
+ runScroll();
2883
+ requestAnimationFrame(runScroll);
2884
+ }
2885
+ setInitialRenderState(ctx, { didLayout: true });
2314
2886
  }
2315
2887
 
2316
2888
  // src/core/calculateItemsInView.ts
2317
2889
  function findCurrentStickyIndex(stickyArray, scroll, state) {
2318
- var _a3;
2319
- const idCache = state.idCache;
2320
2890
  const positions = state.positions;
2321
2891
  for (let i = stickyArray.length - 1; i >= 0; i--) {
2322
2892
  const stickyIndex = stickyArray[i];
2323
- const stickyId = (_a3 = idCache[stickyIndex]) != null ? _a3 : getId(state, stickyIndex);
2324
- const stickyPos = stickyId ? positions.get(stickyId) : void 0;
2893
+ const stickyPos = positions[stickyIndex];
2325
2894
  if (stickyPos !== void 0 && scroll >= stickyPos) {
2326
2895
  return i;
2327
2896
  }
2328
2897
  }
2329
2898
  return -1;
2330
2899
  }
2331
- function getActiveStickyIndices(ctx, state, stickyHeaderIndices) {
2900
+ function getActiveStickyIndices(ctx, stickyHeaderIndices) {
2901
+ const state = ctx.state;
2332
2902
  return new Set(
2333
2903
  Array.from(state.stickyContainerPool).map((i) => peek$(ctx, `containerItemKey${i}`)).map((key) => key ? state.indexByKey.get(key) : void 0).filter((idx) => idx !== void 0 && stickyHeaderIndices.has(idx))
2334
2904
  );
2335
2905
  }
2336
- function handleStickyActivation(ctx, state, stickyHeaderIndices, stickyArray, currentStickyIdx, needNewContainers, startBuffered, endBuffered) {
2906
+ function handleStickyActivation(ctx, stickyHeaderIndices, stickyArray, currentStickyIdx, needNewContainers, needNewContainersSet, startBuffered, endBuffered) {
2337
2907
  var _a3;
2338
- const activeIndices = getActiveStickyIndices(ctx, state, stickyHeaderIndices);
2339
- state.activeStickyIndex = currentStickyIdx >= 0 ? stickyArray[currentStickyIdx] : void 0;
2908
+ const state = ctx.state;
2909
+ const activeIndices = getActiveStickyIndices(ctx, stickyHeaderIndices);
2910
+ set$(ctx, "activeStickyIndex", currentStickyIdx >= 0 ? stickyArray[currentStickyIdx] : -1);
2340
2911
  for (let offset = 0; offset <= 1; offset++) {
2341
2912
  const idx = currentStickyIdx - offset;
2342
2913
  if (idx < 0 || activeIndices.has(stickyArray[idx])) continue;
2343
2914
  const stickyIndex = stickyArray[idx];
2344
2915
  const stickyId = (_a3 = state.idCache[stickyIndex]) != null ? _a3 : getId(state, stickyIndex);
2345
- if (stickyId && !state.containerItemKeys.has(stickyId) && (stickyIndex < startBuffered || stickyIndex > endBuffered)) {
2916
+ if (stickyId && !state.containerItemKeys.has(stickyId) && (stickyIndex < startBuffered || stickyIndex > endBuffered) && !needNewContainersSet.has(stickyIndex)) {
2917
+ needNewContainersSet.add(stickyIndex);
2346
2918
  needNewContainers.push(stickyIndex);
2347
2919
  }
2348
2920
  }
2349
2921
  }
2350
- function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, currentStickyIdx, pendingRemoval) {
2351
- var _a3, _b, _c;
2922
+ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentStickyIdx, pendingRemoval, alwaysRenderIndicesSet) {
2923
+ var _a3, _b;
2924
+ const state = ctx.state;
2352
2925
  for (const containerIndex of state.stickyContainerPool) {
2353
2926
  const itemKey = peek$(ctx, `containerItemKey${containerIndex}`);
2354
2927
  const itemIndex = itemKey ? state.indexByKey.get(itemKey) : void 0;
2355
2928
  if (itemIndex === void 0) continue;
2929
+ if (alwaysRenderIndicesSet.has(itemIndex)) continue;
2356
2930
  const arrayIdx = stickyArray.indexOf(itemIndex);
2357
2931
  if (arrayIdx === -1) {
2358
2932
  state.stickyContainerPool.delete(containerIndex);
2359
2933
  set$(ctx, `containerSticky${containerIndex}`, false);
2360
- set$(ctx, `containerStickyOffset${containerIndex}`, void 0);
2361
2934
  continue;
2362
2935
  }
2363
2936
  const isRecentSticky = arrayIdx >= currentStickyIdx - 1 && arrayIdx <= currentStickyIdx + 1;
@@ -2365,15 +2938,14 @@ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, cu
2365
2938
  const nextIndex = stickyArray[arrayIdx + 1];
2366
2939
  let shouldRecycle = false;
2367
2940
  if (nextIndex) {
2368
- const nextId = (_a3 = state.idCache[nextIndex]) != null ? _a3 : getId(state, nextIndex);
2369
- const nextPos = nextId ? state.positions.get(nextId) : void 0;
2370
- shouldRecycle = nextPos !== void 0 && scroll > nextPos + scrollBuffer * 2;
2941
+ const nextPos = state.positions[nextIndex];
2942
+ shouldRecycle = nextPos !== void 0 && scroll > nextPos + drawDistance * 2;
2371
2943
  } else {
2372
- const currentId = (_b = state.idCache[itemIndex]) != null ? _b : getId(state, itemIndex);
2944
+ const currentId = (_a3 = state.idCache[itemIndex]) != null ? _a3 : getId(state, itemIndex);
2373
2945
  if (currentId) {
2374
- const currentPos = state.positions.get(currentId);
2375
- const currentSize = (_c = state.sizes.get(currentId)) != null ? _c : getItemSize(ctx, state, currentId, itemIndex, state.props.data[itemIndex]);
2376
- shouldRecycle = currentPos !== void 0 && scroll > currentPos + currentSize + scrollBuffer * 3;
2946
+ const currentPos = state.positions[itemIndex];
2947
+ const currentSize = (_b = state.sizes.get(currentId)) != null ? _b : getItemSize(ctx, currentId, itemIndex, state.props.data[itemIndex]);
2948
+ shouldRecycle = currentPos !== void 0 && scroll > currentPos + currentSize + drawDistance * 3;
2377
2949
  }
2378
2950
  }
2379
2951
  if (shouldRecycle) {
@@ -2381,11 +2953,13 @@ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, cu
2381
2953
  }
2382
2954
  }
2383
2955
  }
2384
- function calculateItemsInView(ctx, state, params = {}) {
2385
- reactDom.unstable_batchedUpdates(() => {
2386
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j;
2956
+ function calculateItemsInView(ctx, params = {}) {
2957
+ const state = ctx.state;
2958
+ batchedUpdates(() => {
2959
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
2387
2960
  const {
2388
2961
  columns,
2962
+ columnSpans,
2389
2963
  containerItemKeys,
2390
2964
  enableScrollForNextCalculateItemsInView,
2391
2965
  idCache,
@@ -2393,7 +2967,15 @@ function calculateItemsInView(ctx, state, params = {}) {
2393
2967
  initialScroll,
2394
2968
  minIndexSizeChanged,
2395
2969
  positions,
2396
- props: { getItemType, itemsAreEqual, keyExtractor, onStickyHeaderChange, scrollBuffer },
2970
+ props: {
2971
+ alwaysRenderIndicesArr,
2972
+ alwaysRenderIndicesSet,
2973
+ drawDistance,
2974
+ getItemType,
2975
+ itemsAreEqual,
2976
+ keyExtractor,
2977
+ onStickyHeaderChange
2978
+ },
2397
2979
  scrollForNextCalculateItemsInView,
2398
2980
  scrollLength,
2399
2981
  sizes,
@@ -2403,17 +2985,16 @@ function calculateItemsInView(ctx, state, params = {}) {
2403
2985
  const { data } = state.props;
2404
2986
  const stickyIndicesArr = state.props.stickyIndicesArr || [];
2405
2987
  const stickyIndicesSet = state.props.stickyIndicesSet || /* @__PURE__ */ new Set();
2988
+ const alwaysRenderArr = alwaysRenderIndicesArr || [];
2989
+ const alwaysRenderSet = alwaysRenderIndicesSet || /* @__PURE__ */ new Set();
2990
+ const { dataChanged, doMVCP, forceFullItemPositions } = params;
2406
2991
  const prevNumContainers = peek$(ctx, "numContainers");
2407
2992
  if (!data || scrollLength === 0 || !prevNumContainers) {
2408
- if (state.initialAnchor) {
2409
- ensureInitialAnchor(ctx, state);
2410
- }
2411
2993
  return;
2412
2994
  }
2413
2995
  const totalSize = getContentSize(ctx);
2414
2996
  const topPad = peek$(ctx, "stylePaddingTop") + peek$(ctx, "headerSize");
2415
2997
  const numColumns = peek$(ctx, "numColumns");
2416
- const { dataChanged, doMVCP, forceFullItemPositions } = params;
2417
2998
  const speed = getScrollVelocity(state);
2418
2999
  const scrollExtra = 0;
2419
3000
  const { queuedInitialLayout } = state;
@@ -2421,56 +3002,55 @@ function calculateItemsInView(ctx, state, params = {}) {
2421
3002
  if (!queuedInitialLayout && initialScroll) {
2422
3003
  const updatedOffset = calculateOffsetWithOffsetPosition(
2423
3004
  ctx,
2424
- state,
2425
- calculateOffsetForIndex(ctx, state, initialScroll.index),
3005
+ calculateOffsetForIndex(ctx, initialScroll.index),
2426
3006
  initialScroll
2427
3007
  );
2428
3008
  scrollState = updatedOffset;
2429
3009
  }
2430
3010
  const scrollAdjustPending = (_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0;
2431
3011
  const scrollAdjustPad = scrollAdjustPending - topPad;
2432
- let scroll = scrollState + scrollExtra + scrollAdjustPad;
3012
+ let scroll = Math.round(scrollState + scrollExtra + scrollAdjustPad);
2433
3013
  if (scroll + scrollLength > totalSize) {
2434
3014
  scroll = Math.max(0, totalSize - scrollLength);
2435
3015
  }
2436
- if (ENABLE_DEBUG_VIEW) {
2437
- set$(ctx, "debugRawScroll", scrollState);
2438
- set$(ctx, "debugComputedScroll", scroll);
2439
- }
2440
- const previousStickyIndex = state.activeStickyIndex;
3016
+ const previousStickyIndex = peek$(ctx, "activeStickyIndex");
2441
3017
  const currentStickyIdx = stickyIndicesArr.length > 0 ? findCurrentStickyIndex(stickyIndicesArr, scroll, state) : -1;
2442
- const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyIndicesArr[currentStickyIdx] : void 0;
2443
- state.activeStickyIndex = nextActiveStickyIndex;
2444
- set$(ctx, "activeStickyIndex", nextActiveStickyIndex);
2445
- let scrollBufferTop = scrollBuffer;
2446
- let scrollBufferBottom = scrollBuffer;
2447
- if (speed > 0 || speed === 0 && scroll < Math.max(50, scrollBuffer)) {
2448
- scrollBufferTop = scrollBuffer * 0.5;
2449
- scrollBufferBottom = scrollBuffer * 1.5;
3018
+ const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyIndicesArr[currentStickyIdx] : -1;
3019
+ if (currentStickyIdx >= 0 || previousStickyIndex >= 0) {
3020
+ set$(ctx, "activeStickyIndex", nextActiveStickyIndex);
3021
+ }
3022
+ let scrollBufferTop = drawDistance;
3023
+ let scrollBufferBottom = drawDistance;
3024
+ if (speed > 0 || speed === 0 && scroll < Math.max(50, drawDistance)) {
3025
+ scrollBufferTop = drawDistance * 0.5;
3026
+ scrollBufferBottom = drawDistance * 1.5;
2450
3027
  } else {
2451
- scrollBufferTop = scrollBuffer * 1.5;
2452
- scrollBufferBottom = scrollBuffer * 0.5;
3028
+ scrollBufferTop = drawDistance * 1.5;
3029
+ scrollBufferBottom = drawDistance * 0.5;
2453
3030
  }
2454
3031
  const scrollTopBuffered = scroll - scrollBufferTop;
2455
3032
  const scrollBottom = scroll + scrollLength + (scroll < 0 ? -scroll : 0);
2456
3033
  const scrollBottomBuffered = scrollBottom + scrollBufferBottom;
2457
- if (!dataChanged && scrollForNextCalculateItemsInView) {
3034
+ if (!dataChanged && !forceFullItemPositions && scrollForNextCalculateItemsInView) {
2458
3035
  const { top, bottom } = scrollForNextCalculateItemsInView;
2459
- if (scrollTopBuffered > top && scrollBottomBuffered < bottom) {
2460
- if (state.initialAnchor) {
2461
- ensureInitialAnchor(ctx, state);
3036
+ if (top === null && bottom === null) {
3037
+ state.scrollForNextCalculateItemsInView = void 0;
3038
+ } else if ((top === null || scrollTopBuffered > top) && (bottom === null || scrollBottomBuffered < bottom)) {
3039
+ if (!isInMVCPActiveMode(state)) {
3040
+ return;
2462
3041
  }
2463
- return;
2464
3042
  }
2465
3043
  }
2466
- const checkMVCP = doMVCP ? prepareMVCP(ctx, state, dataChanged) : void 0;
3044
+ const checkMVCP = doMVCP ? prepareMVCP(ctx, dataChanged) : void 0;
2467
3045
  if (dataChanged) {
2468
3046
  indexByKey.clear();
2469
3047
  idCache.length = 0;
2470
- positions.clear();
3048
+ positions.length = 0;
3049
+ columns.length = 0;
3050
+ columnSpans.length = 0;
2471
3051
  }
2472
- const startIndex = dataChanged ? 0 : (_b = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _b : 0;
2473
- updateItemPositions(ctx, state, dataChanged, {
3052
+ const startIndex = forceFullItemPositions || dataChanged ? 0 : (_b = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _b : 0;
3053
+ updateItemPositions(ctx, dataChanged, {
2474
3054
  doMVCP,
2475
3055
  forceFullUpdate: !!forceFullItemPositions,
2476
3056
  scrollBottomBuffered,
@@ -2488,18 +3068,23 @@ function calculateItemsInView(ctx, state, params = {}) {
2488
3068
  let loopStart = !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
2489
3069
  for (let i = loopStart; i >= 0; i--) {
2490
3070
  const id = (_c = idCache[i]) != null ? _c : getId(state, i);
2491
- const top = positions.get(id);
2492
- const size = (_d = sizes.get(id)) != null ? _d : getItemSize(ctx, state, id, i, data[i]);
3071
+ const top = positions[i];
3072
+ const size = (_d = sizes.get(id)) != null ? _d : getItemSize(ctx, id, i, data[i]);
2493
3073
  const bottom = top + size;
2494
- if (bottom > scroll - scrollBuffer) {
3074
+ if (bottom > scroll - scrollBufferTop) {
2495
3075
  loopStart = i;
2496
3076
  } else {
2497
3077
  break;
2498
3078
  }
2499
3079
  }
2500
- const loopStartMod = loopStart % numColumns;
2501
- if (loopStartMod > 0) {
2502
- loopStart -= loopStartMod;
3080
+ if (numColumns > 1) {
3081
+ while (loopStart > 0) {
3082
+ const loopColumn = columns[loopStart];
3083
+ if (loopColumn === 1 || loopColumn === void 0) {
3084
+ break;
3085
+ }
3086
+ loopStart -= 1;
3087
+ }
2503
3088
  }
2504
3089
  let foundEnd = false;
2505
3090
  let nextTop;
@@ -2516,8 +3101,8 @@ function calculateItemsInView(ctx, state, params = {}) {
2516
3101
  const dataLength = data.length;
2517
3102
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
2518
3103
  const id = (_e = idCache[i]) != null ? _e : getId(state, i);
2519
- const size = (_f = sizes.get(id)) != null ? _f : getItemSize(ctx, state, id, i, data[i]);
2520
- const top = positions.get(id);
3104
+ const size = (_f = sizes.get(id)) != null ? _f : getItemSize(ctx, id, i, data[i]);
3105
+ const top = positions[i];
2521
3106
  if (!foundEnd) {
2522
3107
  if (startNoBuffer === null && top + size > scroll) {
2523
3108
  startNoBuffer = i;
@@ -2528,7 +3113,11 @@ function calculateItemsInView(ctx, state, params = {}) {
2528
3113
  if (startBuffered === null && top + size > scrollTopBuffered) {
2529
3114
  startBuffered = i;
2530
3115
  startBufferedId = id;
2531
- nextTop = top;
3116
+ if (scrollTopBuffered < 0) {
3117
+ nextTop = null;
3118
+ } else {
3119
+ nextTop = top;
3120
+ }
2532
3121
  }
2533
3122
  if (startNoBuffer !== null) {
2534
3123
  if (top <= scrollBottom) {
@@ -2536,7 +3125,11 @@ function calculateItemsInView(ctx, state, params = {}) {
2536
3125
  }
2537
3126
  if (top <= scrollBottomBuffered) {
2538
3127
  endBuffered = i;
2539
- nextBottom = top + size;
3128
+ if (scrollBottomBuffered > totalSize) {
3129
+ nextBottom = null;
3130
+ } else {
3131
+ nextBottom = top + size;
3132
+ }
2540
3133
  } else {
2541
3134
  foundEnd = true;
2542
3135
  }
@@ -2558,12 +3151,12 @@ function calculateItemsInView(ctx, state, params = {}) {
2558
3151
  startNoBuffer
2559
3152
  });
2560
3153
  if (enableScrollForNextCalculateItemsInView && nextTop !== void 0 && nextBottom !== void 0) {
2561
- state.scrollForNextCalculateItemsInView = nextTop !== void 0 && nextBottom !== void 0 ? {
3154
+ state.scrollForNextCalculateItemsInView = isNullOrUndefined(nextTop) && isNullOrUndefined(nextBottom) ? void 0 : {
2562
3155
  bottom: nextBottom,
2563
3156
  top: nextTop
2564
- } : void 0;
3157
+ };
2565
3158
  }
2566
- const numContainers = peek$(ctx, "numContainers");
3159
+ let numContainers = prevNumContainers;
2567
3160
  const pendingRemoval = [];
2568
3161
  if (dataChanged) {
2569
3162
  for (let i = 0; i < numContainers; i++) {
@@ -2574,37 +3167,46 @@ function calculateItemsInView(ctx, state, params = {}) {
2574
3167
  }
2575
3168
  }
2576
3169
  if (startBuffered !== null && endBuffered !== null) {
2577
- let numContainers2 = prevNumContainers;
2578
3170
  const needNewContainers = [];
3171
+ const needNewContainersSet = /* @__PURE__ */ new Set();
2579
3172
  for (let i = startBuffered; i <= endBuffered; i++) {
2580
3173
  const id = (_h = idCache[i]) != null ? _h : getId(state, i);
2581
3174
  if (!containerItemKeys.has(id)) {
3175
+ needNewContainersSet.add(i);
2582
3176
  needNewContainers.push(i);
2583
3177
  }
2584
3178
  }
3179
+ if (alwaysRenderArr.length > 0) {
3180
+ for (const index of alwaysRenderArr) {
3181
+ if (index < 0 || index >= dataLength) continue;
3182
+ const id = (_i = idCache[index]) != null ? _i : getId(state, index);
3183
+ if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
3184
+ needNewContainersSet.add(index);
3185
+ needNewContainers.push(index);
3186
+ }
3187
+ }
3188
+ }
2585
3189
  if (stickyIndicesArr.length > 0) {
2586
3190
  handleStickyActivation(
2587
3191
  ctx,
2588
- state,
2589
3192
  stickyIndicesSet,
2590
3193
  stickyIndicesArr,
2591
3194
  currentStickyIdx,
2592
3195
  needNewContainers,
3196
+ needNewContainersSet,
2593
3197
  startBuffered,
2594
3198
  endBuffered
2595
3199
  );
2596
- } else {
2597
- state.activeStickyIndex = void 0;
2598
- set$(ctx, "activeStickyIndex", void 0);
3200
+ } else if (previousStickyIndex !== -1) {
3201
+ set$(ctx, "activeStickyIndex", -1);
2599
3202
  }
2600
3203
  if (needNewContainers.length > 0) {
2601
3204
  const requiredItemTypes = getItemType ? needNewContainers.map((i) => {
2602
3205
  const itemType = getItemType(data[i], i);
2603
- return itemType ? String(itemType) : "";
3206
+ return itemType !== void 0 ? String(itemType) : "";
2604
3207
  }) : void 0;
2605
3208
  const availableContainers = findAvailableContainers(
2606
3209
  ctx,
2607
- state,
2608
3210
  needNewContainers.length,
2609
3211
  startBuffered,
2610
3212
  endBuffered,
@@ -2615,7 +3217,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2615
3217
  for (let idx = 0; idx < needNewContainers.length; idx++) {
2616
3218
  const i = needNewContainers[idx];
2617
3219
  const containerIndex = availableContainers[idx];
2618
- const id = (_i = idCache[i]) != null ? _i : getId(state, i);
3220
+ const id = (_j = idCache[i]) != null ? _j : getId(state, i);
2619
3221
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
2620
3222
  if (oldKey && oldKey !== id) {
2621
3223
  containerItemKeys.delete(oldKey);
@@ -2625,30 +3227,55 @@ function calculateItemsInView(ctx, state, params = {}) {
2625
3227
  if (requiredItemTypes) {
2626
3228
  state.containerItemTypes.set(containerIndex, requiredItemTypes[idx]);
2627
3229
  }
2628
- containerItemKeys.add(id);
2629
- if (stickyIndicesSet.has(i)) {
2630
- set$(ctx, `containerSticky${containerIndex}`, true);
2631
- const topPadding = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
2632
- set$(ctx, `containerStickyOffset${containerIndex}`, topPadding);
3230
+ containerItemKeys.set(id, containerIndex);
3231
+ const containerSticky = `containerSticky${containerIndex}`;
3232
+ const isSticky = stickyIndicesSet.has(i);
3233
+ const isAlwaysRender = alwaysRenderSet.has(i);
3234
+ if (isSticky) {
3235
+ set$(ctx, containerSticky, true);
2633
3236
  state.stickyContainerPool.add(containerIndex);
2634
3237
  } else {
2635
- set$(ctx, `containerSticky${containerIndex}`, false);
2636
- state.stickyContainerPool.delete(containerIndex);
3238
+ if (peek$(ctx, containerSticky)) {
3239
+ set$(ctx, containerSticky, false);
3240
+ }
3241
+ if (isAlwaysRender) {
3242
+ state.stickyContainerPool.add(containerIndex);
3243
+ } else if (state.stickyContainerPool.has(containerIndex)) {
3244
+ state.stickyContainerPool.delete(containerIndex);
3245
+ }
3246
+ }
3247
+ if (containerIndex >= numContainers) {
3248
+ numContainers = containerIndex + 1;
2637
3249
  }
2638
- if (containerIndex >= numContainers2) {
2639
- numContainers2 = containerIndex + 1;
3250
+ }
3251
+ if (numContainers !== prevNumContainers) {
3252
+ set$(ctx, "numContainers", numContainers);
3253
+ if (numContainers > peek$(ctx, "numContainersPooled")) {
3254
+ set$(ctx, "numContainersPooled", Math.ceil(numContainers * 1.5));
2640
3255
  }
2641
3256
  }
2642
- if (numContainers2 !== prevNumContainers) {
2643
- set$(ctx, "numContainers", numContainers2);
2644
- if (numContainers2 > peek$(ctx, "numContainersPooled")) {
2645
- set$(ctx, "numContainersPooled", Math.ceil(numContainers2 * 1.5));
3257
+ }
3258
+ if (alwaysRenderArr.length > 0) {
3259
+ for (const index of alwaysRenderArr) {
3260
+ if (index < 0 || index >= dataLength) continue;
3261
+ const id = (_k = idCache[index]) != null ? _k : getId(state, index);
3262
+ const containerIndex = containerItemKeys.get(id);
3263
+ if (containerIndex !== void 0) {
3264
+ state.stickyContainerPool.add(containerIndex);
2646
3265
  }
2647
3266
  }
2648
3267
  }
2649
3268
  }
2650
- if (stickyIndicesArr.length > 0) {
2651
- handleStickyRecycling(ctx, state, stickyIndicesArr, scroll, scrollBuffer, currentStickyIdx, pendingRemoval);
3269
+ if (state.stickyContainerPool.size > 0) {
3270
+ handleStickyRecycling(
3271
+ ctx,
3272
+ stickyIndicesArr,
3273
+ scroll,
3274
+ drawDistance,
3275
+ currentStickyIdx,
3276
+ pendingRemoval,
3277
+ alwaysRenderSet
3278
+ );
2652
3279
  }
2653
3280
  let didChangePositions = false;
2654
3281
  for (let i = 0; i < numContainers; i++) {
@@ -2660,26 +3287,27 @@ function calculateItemsInView(ctx, state, params = {}) {
2660
3287
  state.containerItemTypes.delete(i);
2661
3288
  if (state.stickyContainerPool.has(i)) {
2662
3289
  set$(ctx, `containerSticky${i}`, false);
2663
- set$(ctx, `containerStickyOffset${i}`, void 0);
2664
3290
  state.stickyContainerPool.delete(i);
2665
3291
  }
2666
3292
  set$(ctx, `containerItemKey${i}`, void 0);
2667
3293
  set$(ctx, `containerItemData${i}`, void 0);
2668
3294
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
2669
3295
  set$(ctx, `containerColumn${i}`, -1);
3296
+ set$(ctx, `containerSpan${i}`, 1);
2670
3297
  } else {
2671
3298
  const itemIndex = indexByKey.get(itemKey);
2672
3299
  const item = data[itemIndex];
2673
3300
  if (item !== void 0) {
2674
- const id = (_j = idCache[itemIndex]) != null ? _j : getId(state, itemIndex);
2675
- const positionValue = positions.get(id);
3301
+ const positionValue = positions[itemIndex];
2676
3302
  if (positionValue === void 0) {
2677
3303
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
2678
3304
  } else {
2679
3305
  const position = (positionValue || 0) - scrollAdjustPending;
2680
- const column = columns.get(id) || 1;
3306
+ const column = columns[itemIndex] || 1;
3307
+ const span = columnSpans[itemIndex] || 1;
2681
3308
  const prevPos = peek$(ctx, `containerPosition${i}`);
2682
3309
  const prevColumn = peek$(ctx, `containerColumn${i}`);
3310
+ const prevSpan = peek$(ctx, `containerSpan${i}`);
2683
3311
  const prevData = peek$(ctx, `containerItemData${i}`);
2684
3312
  if (position > POSITION_OUT_OF_VIEW && position !== prevPos) {
2685
3313
  set$(ctx, `containerPosition${i}`, position);
@@ -2688,6 +3316,9 @@ function calculateItemsInView(ctx, state, params = {}) {
2688
3316
  if (column >= 0 && column !== prevColumn) {
2689
3317
  set$(ctx, `containerColumn${i}`, column);
2690
3318
  }
3319
+ if (span !== prevSpan) {
3320
+ set$(ctx, `containerSpan${i}`, span);
3321
+ }
2691
3322
  if (prevData !== item && (itemsAreEqual ? !itemsAreEqual(prevData, item, itemIndex, data) : true)) {
2692
3323
  set$(ctx, `containerItemData${i}`, item);
2693
3324
  }
@@ -2700,7 +3331,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2700
3331
  }
2701
3332
  if (!queuedInitialLayout && endBuffered !== null) {
2702
3333
  if (checkAllSizesKnown(state)) {
2703
- setDidLayout(ctx, state);
3334
+ setDidLayout(ctx);
2704
3335
  }
2705
3336
  }
2706
3337
  if (viewabilityConfigCallbackPairs) {
@@ -2713,9 +3344,6 @@ function calculateItemsInView(ctx, state, params = {}) {
2713
3344
  }
2714
3345
  }
2715
3346
  });
2716
- if (state.initialAnchor) {
2717
- ensureInitialAnchor(ctx, state);
2718
- }
2719
3347
  }
2720
3348
 
2721
3349
  // src/core/checkActualChange.ts
@@ -2738,20 +3366,74 @@ function checkActualChange(state, dataProp, previousData) {
2738
3366
  return false;
2739
3367
  }
2740
3368
 
3369
+ // src/core/checkFinishedScroll.ts
3370
+ function checkFinishedScroll(ctx) {
3371
+ ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
3372
+ }
3373
+ function checkFinishedScrollFrame(ctx) {
3374
+ const scrollingTo = ctx.state.scrollingTo;
3375
+ if (scrollingTo) {
3376
+ const { state } = ctx;
3377
+ state.animFrameCheckFinishedScroll = void 0;
3378
+ const scroll = state.scrollPending;
3379
+ const adjust = state.scrollAdjustHandler.getAdjust();
3380
+ const clampedTargetOffset = clampScrollOffset(
3381
+ ctx,
3382
+ scrollingTo.offset - (scrollingTo.viewOffset || 0),
3383
+ scrollingTo
3384
+ );
3385
+ const maxOffset = clampScrollOffset(ctx, scroll, scrollingTo);
3386
+ const diff1 = Math.abs(scroll - clampedTargetOffset);
3387
+ const diff2 = Math.abs(diff1 - adjust);
3388
+ const isNotOverscrolled = Math.abs(scroll - maxOffset) < 1;
3389
+ const isAtTarget = diff1 < 1 || !scrollingTo.animated && diff2 < 1;
3390
+ if (isNotOverscrolled && isAtTarget) {
3391
+ finishScrollTo(ctx);
3392
+ }
3393
+ }
3394
+ }
3395
+ function checkFinishedScrollFallback(ctx) {
3396
+ const state = ctx.state;
3397
+ const scrollingTo = state.scrollingTo;
3398
+ const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) || !state.didContainersLayout;
3399
+ state.timeoutCheckFinishedScrollFallback = setTimeout(
3400
+ () => {
3401
+ let numChecks = 0;
3402
+ const checkHasScrolled = () => {
3403
+ state.timeoutCheckFinishedScrollFallback = void 0;
3404
+ const isStillScrollingTo = state.scrollingTo;
3405
+ if (isStillScrollingTo) {
3406
+ numChecks++;
3407
+ if (state.hasScrolled || numChecks > 5) {
3408
+ finishScrollTo(ctx);
3409
+ } else {
3410
+ state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, 100);
3411
+ }
3412
+ }
3413
+ };
3414
+ checkHasScrolled();
3415
+ },
3416
+ slowTimeout ? 500 : 100
3417
+ );
3418
+ }
3419
+
2741
3420
  // src/core/doMaintainScrollAtEnd.ts
2742
- function doMaintainScrollAtEnd(ctx, state, animated) {
3421
+ function doMaintainScrollAtEnd(ctx, animated) {
3422
+ const state = ctx.state;
2743
3423
  const {
3424
+ didContainersLayout,
3425
+ isAtEnd,
2744
3426
  refScroller,
2745
3427
  props: { maintainScrollAtEnd }
2746
3428
  } = state;
2747
- if ((state == null ? void 0 : state.isAtEnd) && maintainScrollAtEnd && peek$(ctx, "containersDidLayout")) {
2748
- const paddingTop = peek$(ctx, "alignItemsPaddingTop");
2749
- if (paddingTop > 0) {
3429
+ if (isAtEnd && maintainScrollAtEnd && didContainersLayout) {
3430
+ const contentSize = getContentSize(ctx);
3431
+ if (contentSize < state.scrollLength) {
2750
3432
  state.scroll = 0;
2751
3433
  }
2752
3434
  requestAnimationFrame(() => {
2753
3435
  var _a3;
2754
- if (state == null ? void 0 : state.isAtEnd) {
3436
+ if (state.isAtEnd) {
2755
3437
  state.maintainingScrollAtEnd = true;
2756
3438
  (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
2757
3439
  animated
@@ -2822,36 +3504,37 @@ function updateAveragesOnDataChange(state, oldData, newData) {
2822
3504
  }
2823
3505
 
2824
3506
  // src/core/checkResetContainers.ts
2825
- function checkResetContainers(ctx, state, dataProp) {
3507
+ function checkResetContainers(ctx, dataProp) {
3508
+ const state = ctx.state;
2826
3509
  const { previousData } = state;
2827
3510
  if (previousData) {
2828
3511
  updateAveragesOnDataChange(state, previousData, dataProp);
2829
3512
  }
2830
3513
  const { maintainScrollAtEnd } = state.props;
2831
- calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
3514
+ calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
2832
3515
  const shouldMaintainScrollAtEnd = maintainScrollAtEnd === true || maintainScrollAtEnd.onDataChange;
2833
- const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, state, false);
3516
+ const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, false);
2834
3517
  if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
2835
3518
  state.isEndReached = false;
2836
3519
  }
2837
3520
  if (!didMaintainScrollAtEnd) {
2838
- checkAtTop(state);
2839
- checkAtBottom(ctx, state);
3521
+ checkThresholds(ctx);
2840
3522
  }
2841
3523
  delete state.previousData;
2842
3524
  }
2843
3525
 
2844
3526
  // src/core/doInitialAllocateContainers.ts
2845
- function doInitialAllocateContainers(ctx, state, dataChanged) {
3527
+ function doInitialAllocateContainers(ctx) {
2846
3528
  var _a3, _b, _c;
3529
+ const state = ctx.state;
2847
3530
  const {
2848
3531
  scrollLength,
2849
3532
  props: {
2850
3533
  data,
3534
+ drawDistance,
2851
3535
  getEstimatedItemSize,
2852
3536
  getFixedItemSize,
2853
3537
  getItemType,
2854
- scrollBuffer,
2855
3538
  numColumns,
2856
3539
  estimatedItemSize
2857
3540
  }
@@ -2864,40 +3547,62 @@ function doInitialAllocateContainers(ctx, state, dataChanged) {
2864
3547
  const num = Math.min(20, data.length);
2865
3548
  for (let i = 0; i < num; i++) {
2866
3549
  const item = data[i];
2867
- const itemType = getItemType ? (_a3 = getItemType(item, i)) != null ? _a3 : "" : "";
2868
- totalSize += (_c = (_b = getFixedItemSize == null ? void 0 : getFixedItemSize(i, item, itemType)) != null ? _b : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(i, item, itemType)) != null ? _c : estimatedItemSize;
3550
+ if (item !== void 0) {
3551
+ const itemType = (_a3 = getItemType == null ? void 0 : getItemType(item, i)) != null ? _a3 : "";
3552
+ totalSize += (_c = (_b = getFixedItemSize == null ? void 0 : getFixedItemSize(item, i, itemType)) != null ? _b : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(item, i, itemType)) != null ? _c : estimatedItemSize;
3553
+ }
2869
3554
  }
2870
3555
  averageItemSize = totalSize / num;
2871
3556
  } else {
2872
3557
  averageItemSize = estimatedItemSize;
2873
3558
  }
2874
- const numContainers = Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize * numColumns);
3559
+ const numContainers = Math.ceil((scrollLength + drawDistance * 2) / averageItemSize * numColumns);
2875
3560
  for (let i = 0; i < numContainers; i++) {
2876
3561
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
2877
3562
  set$(ctx, `containerColumn${i}`, -1);
3563
+ set$(ctx, `containerSpan${i}`, 1);
2878
3564
  }
2879
3565
  set$(ctx, "numContainers", numContainers);
2880
3566
  set$(ctx, "numContainersPooled", numContainers * state.props.initialContainerPoolRatio);
2881
3567
  if (state.lastLayout) {
2882
3568
  if (state.initialScroll) {
2883
3569
  requestAnimationFrame(() => {
2884
- calculateItemsInView(ctx, state, { dataChanged, doMVCP: true });
3570
+ calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
2885
3571
  });
2886
3572
  } else {
2887
- calculateItemsInView(ctx, state, { dataChanged, doMVCP: true });
3573
+ calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
2888
3574
  }
2889
3575
  }
2890
3576
  return true;
2891
3577
  }
2892
3578
  }
2893
3579
 
3580
+ // src/platform/getWindowSize.ts
3581
+ function getWindowSize() {
3582
+ if (typeof window === "undefined") {
3583
+ return { height: 0, width: 0 };
3584
+ }
3585
+ return {
3586
+ height: window.innerHeight,
3587
+ width: window.innerWidth
3588
+ };
3589
+ }
3590
+
2894
3591
  // src/core/handleLayout.ts
2895
- function handleLayout(ctx, state, layout, setCanRender) {
2896
- const { maintainScrollAtEnd } = state.props;
2897
- const measuredLength = layout[state.props.horizontal ? "width" : "height"];
3592
+ function handleLayout(ctx, layoutParam, setCanRender) {
3593
+ const state = ctx.state;
3594
+ const { maintainScrollAtEnd, useWindowScroll } = state.props;
3595
+ const scrollAxis = state.props.horizontal ? "width" : "height";
3596
+ const otherAxis = state.props.horizontal ? "height" : "width";
3597
+ let layout = layoutParam;
3598
+ if (useWindowScroll) {
3599
+ const windowScrollAxisLength = getWindowSize()[scrollAxis];
3600
+ layout = windowScrollAxisLength > 0 ? { ...layoutParam, [scrollAxis]: windowScrollAxisLength } : layoutParam;
3601
+ }
3602
+ const measuredLength = layout[scrollAxis];
2898
3603
  const previousLength = state.scrollLength;
2899
3604
  const scrollLength = measuredLength > 0 ? measuredLength : previousLength;
2900
- const otherAxisSize = layout[state.props.horizontal ? "height" : "width"];
3605
+ const otherAxisSize = layout[otherAxis];
2901
3606
  const needsCalculate = !state.lastLayout || scrollLength > state.scrollLength || state.lastLayout.x !== layout.x || state.lastLayout.y !== layout.y;
2902
3607
  state.lastLayout = layout;
2903
3608
  const prevOtherAxisSize = state.otherAxisSize;
@@ -2908,20 +3613,18 @@ function handleLayout(ctx, state, layout, setCanRender) {
2908
3613
  state.lastBatchingAction = Date.now();
2909
3614
  state.scrollForNextCalculateItemsInView = void 0;
2910
3615
  if (scrollLength > 0) {
2911
- doInitialAllocateContainers(ctx, state);
3616
+ doInitialAllocateContainers(ctx);
2912
3617
  }
2913
3618
  if (needsCalculate) {
2914
- calculateItemsInView(ctx, state, { doMVCP: true });
3619
+ calculateItemsInView(ctx, { doMVCP: true });
2915
3620
  }
2916
3621
  if (didChange || otherAxisSize !== prevOtherAxisSize) {
2917
3622
  set$(ctx, "scrollSize", { height: layout.height, width: layout.width });
2918
3623
  }
2919
3624
  if (maintainScrollAtEnd === true || maintainScrollAtEnd.onLayout) {
2920
- doMaintainScrollAtEnd(ctx, state, false);
3625
+ doMaintainScrollAtEnd(ctx, false);
2921
3626
  }
2922
- updateAlignItemsPaddingTop(ctx, state);
2923
- checkAtBottom(ctx, state);
2924
- checkAtTop(state);
3627
+ checkThresholds(ctx);
2925
3628
  if (state) {
2926
3629
  state.needsOtherAxisSize = otherAxisSize - (state.props.stylePaddingTop || 0) < 10;
2927
3630
  }
@@ -2936,8 +3639,9 @@ function handleLayout(ctx, state, layout, setCanRender) {
2936
3639
  }
2937
3640
 
2938
3641
  // src/core/onScroll.ts
2939
- function onScroll(ctx, state, event) {
2940
- var _a3, _b, _c;
3642
+ function onScroll(ctx, event) {
3643
+ var _a3, _b, _c, _d;
3644
+ const state = ctx.state;
2941
3645
  const {
2942
3646
  scrollProcessingEnabled,
2943
3647
  props: { onScroll: onScrollProp }
@@ -2948,17 +3652,34 @@ function onScroll(ctx, state, event) {
2948
3652
  if (((_b = (_a3 = event.nativeEvent) == null ? void 0 : _a3.contentSize) == null ? void 0 : _b.height) === 0 && ((_c = event.nativeEvent.contentSize) == null ? void 0 : _c.width) === 0) {
2949
3653
  return;
2950
3654
  }
3655
+ let insetChanged = false;
3656
+ if ((_d = event.nativeEvent) == null ? void 0 : _d.contentInset) {
3657
+ const { contentInset } = event.nativeEvent;
3658
+ const prevInset = state.nativeContentInset;
3659
+ if (!prevInset || prevInset.top !== contentInset.top || prevInset.bottom !== contentInset.bottom || prevInset.left !== contentInset.left || prevInset.right !== contentInset.right) {
3660
+ state.nativeContentInset = contentInset;
3661
+ insetChanged = true;
3662
+ }
3663
+ }
2951
3664
  let newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
3665
+ if (state.scrollingTo && state.scrollingTo.offset >= newScroll) {
3666
+ const maxOffset = clampScrollOffset(ctx, newScroll, state.scrollingTo);
3667
+ if (newScroll !== maxOffset && Math.abs(newScroll - maxOffset) > 1) {
3668
+ newScroll = maxOffset;
3669
+ scrollTo(ctx, {
3670
+ forceScroll: true,
3671
+ isInitialScroll: true,
3672
+ noScrollingTo: true,
3673
+ offset: newScroll
3674
+ });
3675
+ return;
3676
+ }
3677
+ }
2952
3678
  state.scrollPending = newScroll;
2953
- const maxOffset = Math.max(0, getContentSize(ctx) - state.scrollLength);
2954
- if (state.initialScroll && newScroll > maxOffset) {
2955
- newScroll = maxOffset;
2956
- scrollTo(ctx, state, {
2957
- noScrollingTo: true,
2958
- offset: newScroll
2959
- });
3679
+ updateScroll(ctx, newScroll, insetChanged);
3680
+ if (state.scrollingTo) {
3681
+ checkFinishedScroll(ctx);
2960
3682
  }
2961
- updateScroll(ctx, state, newScroll);
2962
3683
  onScrollProp == null ? void 0 : onScrollProp(event);
2963
3684
  }
2964
3685
 
@@ -2967,51 +3688,78 @@ var ScrollAdjustHandler = class {
2967
3688
  constructor(ctx) {
2968
3689
  this.appliedAdjust = 0;
2969
3690
  this.pendingAdjust = 0;
2970
- this.mounted = false;
2971
- this.context = ctx;
2972
- {
2973
- const commitPendingAdjust = () => {
2974
- const state = this.context.internalState;
2975
- const pending = this.pendingAdjust;
2976
- if (pending !== 0) {
2977
- this.pendingAdjust = 0;
2978
- this.appliedAdjust += pending;
2979
- state.scroll += pending;
2980
- state.scrollForNextCalculateItemsInView = void 0;
2981
- set$(this.context, "scrollAdjustPending", 0);
2982
- set$(this.context, "scrollAdjust", this.appliedAdjust);
2983
- calculateItemsInView(this.context, this.context.internalState);
2984
- }
2985
- };
2986
- listen$(this.context, "scrollingTo", (value) => {
2987
- if (value === void 0) {
2988
- commitPendingAdjust();
2989
- }
2990
- });
2991
- }
3691
+ this.ctx = ctx;
2992
3692
  }
2993
3693
  requestAdjust(add) {
2994
- const scrollingTo = peek$(this.context, "scrollingTo");
3694
+ const scrollingTo = this.ctx.state.scrollingTo;
2995
3695
  if ((scrollingTo == null ? void 0 : scrollingTo.animated) && !scrollingTo.isInitialScroll) {
2996
3696
  this.pendingAdjust += add;
2997
- set$(this.context, "scrollAdjustPending", this.pendingAdjust);
3697
+ set$(this.ctx, "scrollAdjustPending", this.pendingAdjust);
2998
3698
  } else {
2999
3699
  this.appliedAdjust += add;
3000
- set$(this.context, "scrollAdjust", this.appliedAdjust);
3700
+ set$(this.ctx, "scrollAdjust", this.appliedAdjust);
3701
+ }
3702
+ if (this.ctx.state.scrollingTo) {
3703
+ checkFinishedScroll(this.ctx);
3001
3704
  }
3002
- }
3003
- setMounted() {
3004
- this.mounted = true;
3005
3705
  }
3006
3706
  getAdjust() {
3007
3707
  return this.appliedAdjust;
3008
3708
  }
3709
+ commitPendingAdjust(scrollTarget) {
3710
+ {
3711
+ const state = this.ctx.state;
3712
+ const pending = this.pendingAdjust;
3713
+ this.pendingAdjust = 0;
3714
+ if (pending !== 0) {
3715
+ let targetScroll;
3716
+ if ((scrollTarget == null ? void 0 : scrollTarget.index) !== void 0) {
3717
+ const currentOffset = calculateOffsetForIndex(this.ctx, scrollTarget.index);
3718
+ targetScroll = calculateOffsetWithOffsetPosition(this.ctx, currentOffset, scrollTarget);
3719
+ targetScroll = clampScrollOffset(this.ctx, targetScroll, scrollTarget);
3720
+ } else {
3721
+ targetScroll = clampScrollOffset(this.ctx, state.scroll + pending);
3722
+ }
3723
+ const adjustment = targetScroll - state.scroll;
3724
+ if (Math.abs(adjustment) > 0.1 || Math.abs(pending) > 0.1) {
3725
+ this.appliedAdjust += adjustment;
3726
+ state.scroll = targetScroll;
3727
+ state.scrollForNextCalculateItemsInView = void 0;
3728
+ set$(this.ctx, "scrollAdjust", this.appliedAdjust);
3729
+ }
3730
+ set$(this.ctx, "scrollAdjustPending", 0);
3731
+ calculateItemsInView(this.ctx);
3732
+ }
3733
+ }
3734
+ }
3009
3735
  };
3010
3736
 
3011
3737
  // src/core/updateItemSize.ts
3012
- function updateItemSize(ctx, state, itemKey, sizeObj) {
3738
+ function runOrScheduleMVCPRecalculate(ctx) {
3739
+ const state = ctx.state;
3740
+ {
3741
+ if (!state.mvcpAnchorLock) {
3742
+ if (state.queuedMVCPRecalculate !== void 0) {
3743
+ cancelAnimationFrame(state.queuedMVCPRecalculate);
3744
+ state.queuedMVCPRecalculate = void 0;
3745
+ }
3746
+ calculateItemsInView(ctx, { doMVCP: true });
3747
+ return;
3748
+ }
3749
+ if (state.queuedMVCPRecalculate !== void 0) {
3750
+ return;
3751
+ }
3752
+ state.queuedMVCPRecalculate = requestAnimationFrame(() => {
3753
+ state.queuedMVCPRecalculate = void 0;
3754
+ calculateItemsInView(ctx, { doMVCP: true });
3755
+ });
3756
+ }
3757
+ }
3758
+ function updateItemSize(ctx, itemKey, sizeObj) {
3013
3759
  var _a3;
3760
+ const state = ctx.state;
3014
3761
  const {
3762
+ didContainersLayout,
3015
3763
  sizesKnown,
3016
3764
  props: {
3017
3765
  getFixedItemSize,
@@ -3034,31 +3782,24 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
3034
3782
  return;
3035
3783
  }
3036
3784
  const type = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
3037
- const size2 = getFixedItemSize(index, itemData, type);
3785
+ const size2 = getFixedItemSize(itemData, index, type);
3038
3786
  if (size2 !== void 0 && size2 === sizesKnown.get(itemKey)) {
3039
3787
  return;
3040
3788
  }
3041
3789
  }
3042
- const containersDidLayout = peek$(ctx, "containersDidLayout");
3043
- let needsRecalculate = !containersDidLayout;
3790
+ let needsRecalculate = !didContainersLayout;
3044
3791
  let shouldMaintainScrollAtEnd = false;
3045
3792
  let minIndexSizeChanged;
3046
3793
  let maxOtherAxisSize = peek$(ctx, "otherAxisSize") || 0;
3047
3794
  const prevSizeKnown = state.sizesKnown.get(itemKey);
3048
- const diff = updateOneItemSize(ctx, state, itemKey, sizeObj);
3795
+ const diff = updateOneItemSize(ctx, itemKey, sizeObj);
3049
3796
  const size = roundSize(horizontal ? sizeObj.width : sizeObj.height);
3050
3797
  if (diff !== 0) {
3051
3798
  minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
3052
3799
  const { startBuffered, endBuffered } = state;
3053
3800
  needsRecalculate || (needsRecalculate = index >= startBuffered && index <= endBuffered);
3054
- if (!needsRecalculate) {
3055
- const numContainers = ctx.values.get("numContainers");
3056
- for (let i = 0; i < numContainers; i++) {
3057
- if (peek$(ctx, `containerItemKey${i}`) === itemKey) {
3058
- needsRecalculate = true;
3059
- break;
3060
- }
3061
- }
3801
+ if (!needsRecalculate && state.containerItemKeys.has(itemKey)) {
3802
+ needsRecalculate = true;
3062
3803
  }
3063
3804
  if (state.needsOtherAxisSize) {
3064
3805
  const otherAxisSize = horizontal ? sizeObj.height : sizeObj.width;
@@ -3094,20 +3835,21 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
3094
3835
  if (!cur || maxOtherAxisSize > cur) {
3095
3836
  set$(ctx, "otherAxisSize", maxOtherAxisSize);
3096
3837
  }
3097
- if (containersDidLayout || checkAllSizesKnown(state)) {
3838
+ if (didContainersLayout || checkAllSizesKnown(state)) {
3098
3839
  if (needsRecalculate) {
3099
3840
  state.scrollForNextCalculateItemsInView = void 0;
3100
- calculateItemsInView(ctx, state, { doMVCP: true });
3841
+ runOrScheduleMVCPRecalculate(ctx);
3101
3842
  }
3102
3843
  if (shouldMaintainScrollAtEnd) {
3103
3844
  if (maintainScrollAtEnd === true || maintainScrollAtEnd.onItemLayout) {
3104
- doMaintainScrollAtEnd(ctx, state, false);
3845
+ doMaintainScrollAtEnd(ctx, false);
3105
3846
  }
3106
3847
  }
3107
3848
  }
3108
3849
  }
3109
- function updateOneItemSize(ctx, state, itemKey, sizeObj) {
3850
+ function updateOneItemSize(ctx, itemKey, sizeObj) {
3110
3851
  var _a3;
3852
+ const state = ctx.state;
3111
3853
  const {
3112
3854
  indexByKey,
3113
3855
  sizesKnown,
@@ -3116,9 +3858,10 @@ function updateOneItemSize(ctx, state, itemKey, sizeObj) {
3116
3858
  } = state;
3117
3859
  if (!data) return 0;
3118
3860
  const index = indexByKey.get(itemKey);
3119
- const prevSize = getItemSize(ctx, state, itemKey, index, data[index]);
3861
+ const prevSize = getItemSize(ctx, itemKey, index, data[index]);
3120
3862
  const rawSize = horizontal ? sizeObj.width : sizeObj.height;
3121
3863
  const size = Math.round(rawSize) ;
3864
+ const prevSizeKnown = sizesKnown.get(itemKey);
3122
3865
  sizesKnown.set(itemKey, size);
3123
3866
  if (!getEstimatedItemSize && !getFixedItemSize && size > 0) {
3124
3867
  const itemType = getItemType ? (_a3 = getItemType(data[index], index)) != null ? _a3 : "" : "";
@@ -3126,15 +3869,28 @@ function updateOneItemSize(ctx, state, itemKey, sizeObj) {
3126
3869
  if (!averages) {
3127
3870
  averages = averageSizes[itemType] = { avg: 0, num: 0 };
3128
3871
  }
3129
- averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
3130
- averages.num++;
3872
+ if (averages.num === 0) {
3873
+ averages.avg = size;
3874
+ averages.num++;
3875
+ } else if (prevSizeKnown !== void 0 && prevSizeKnown > 0) {
3876
+ averages.avg += (size - prevSizeKnown) / averages.num;
3877
+ } else {
3878
+ averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
3879
+ averages.num++;
3880
+ }
3131
3881
  }
3132
3882
  if (!prevSize || Math.abs(prevSize - size) > 0.1) {
3133
- setSize(ctx, state, itemKey, size);
3883
+ setSize(ctx, itemKey, size);
3134
3884
  return size - prevSize;
3135
3885
  }
3136
3886
  return 0;
3137
3887
  }
3888
+ function useWrapIfItem(fn) {
3889
+ return React3.useMemo(
3890
+ () => fn ? (arg1, arg2, arg3) => arg1 !== void 0 && arg2 !== void 0 ? fn(arg1, arg2, arg3) : void 0 : void 0,
3891
+ [fn]
3892
+ );
3893
+ }
3138
3894
  var useCombinedRef = (...refs) => {
3139
3895
  const callback = React3.useCallback((element) => {
3140
3896
  for (const ref of refs) {
@@ -3177,30 +3933,104 @@ function createColumnWrapperStyle(contentContainerStyle) {
3177
3933
  }
3178
3934
 
3179
3935
  // src/utils/createImperativeHandle.ts
3180
- function createImperativeHandle(ctx, state) {
3936
+ function createImperativeHandle(ctx) {
3937
+ const state = ctx.state;
3938
+ const IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS = 800;
3939
+ const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
3940
+ let imperativeScrollToken = 0;
3941
+ const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0 || hasActiveMVCPAnchorLock(state);
3942
+ const runWhenSettled = (token, run) => {
3943
+ const startedAt = Date.now();
3944
+ let stableFrames = 0;
3945
+ const check = () => {
3946
+ if (token !== imperativeScrollToken) {
3947
+ return;
3948
+ }
3949
+ if (isSettlingAfterDataChange()) {
3950
+ stableFrames = 0;
3951
+ } else {
3952
+ stableFrames += 1;
3953
+ }
3954
+ const timedOut = Date.now() - startedAt >= IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS;
3955
+ if (stableFrames >= IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES || timedOut) {
3956
+ run();
3957
+ return;
3958
+ }
3959
+ requestAnimationFrame(check);
3960
+ };
3961
+ requestAnimationFrame(check);
3962
+ };
3963
+ const runScrollWithPromise = (run) => new Promise((resolve) => {
3964
+ var _a3;
3965
+ const token = ++imperativeScrollToken;
3966
+ (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
3967
+ state.pendingScrollResolve = resolve;
3968
+ const runNow = () => {
3969
+ if (token !== imperativeScrollToken) {
3970
+ return;
3971
+ }
3972
+ const didStartScroll = run();
3973
+ if (!didStartScroll || !state.scrollingTo) {
3974
+ if (state.pendingScrollResolve === resolve) {
3975
+ state.pendingScrollResolve = void 0;
3976
+ }
3977
+ resolve();
3978
+ }
3979
+ };
3980
+ if (isSettlingAfterDataChange()) {
3981
+ runWhenSettled(token, runNow);
3982
+ return;
3983
+ }
3984
+ runNow();
3985
+ });
3181
3986
  const scrollIndexIntoView = (options) => {
3182
3987
  if (state) {
3183
3988
  const { index, ...rest } = options;
3184
3989
  const { startNoBuffer, endNoBuffer } = state;
3185
3990
  if (index < startNoBuffer || index > endNoBuffer) {
3186
3991
  const viewPosition = index < startNoBuffer ? 0 : 1;
3187
- scrollToIndex(ctx, state, {
3992
+ scrollToIndex(ctx, {
3188
3993
  ...rest,
3189
3994
  index,
3190
3995
  viewPosition
3191
3996
  });
3997
+ return true;
3192
3998
  }
3193
3999
  }
4000
+ return false;
3194
4001
  };
3195
4002
  const refScroller = state.refScroller;
4003
+ const clearCaches = (options) => {
4004
+ var _a3, _b;
4005
+ const mode = (_a3 = options == null ? void 0 : options.mode) != null ? _a3 : "sizes";
4006
+ state.sizes.clear();
4007
+ state.sizesKnown.clear();
4008
+ for (const key in state.averageSizes) {
4009
+ delete state.averageSizes[key];
4010
+ }
4011
+ state.minIndexSizeChanged = 0;
4012
+ state.scrollForNextCalculateItemsInView = void 0;
4013
+ state.pendingTotalSize = void 0;
4014
+ state.totalSize = 0;
4015
+ set$(ctx, "totalSize", 0);
4016
+ if (mode === "full") {
4017
+ state.indexByKey.clear();
4018
+ state.idCache.length = 0;
4019
+ state.positions.length = 0;
4020
+ state.columns.length = 0;
4021
+ state.columnSpans.length = 0;
4022
+ }
4023
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
4024
+ };
3196
4025
  return {
4026
+ clearCaches,
3197
4027
  flashScrollIndicators: () => refScroller.current.flashScrollIndicators(),
3198
4028
  getNativeScrollRef: () => refScroller.current,
3199
4029
  getScrollableNode: () => refScroller.current.getScrollableNode(),
3200
4030
  getScrollResponder: () => refScroller.current.getScrollResponder(),
3201
4031
  getState: () => ({
3202
- activeStickyIndex: state.activeStickyIndex,
3203
- contentLength: state.totalSize,
4032
+ activeStickyIndex: peek$(ctx, "activeStickyIndex"),
4033
+ contentLength: getContentSize(ctx),
3204
4034
  data: state.props.data,
3205
4035
  elementAtIndex: (index) => {
3206
4036
  var _a3;
@@ -3210,47 +4040,71 @@ function createImperativeHandle(ctx, state) {
3210
4040
  endBuffered: state.endBuffered,
3211
4041
  isAtEnd: state.isAtEnd,
3212
4042
  isAtStart: state.isAtStart,
3213
- positionAtIndex: (index) => state.positions.get(getId(state, index)),
3214
- positions: state.positions,
4043
+ isEndReached: state.isEndReached,
4044
+ isStartReached: state.isStartReached,
4045
+ listen: (signalName, cb) => listen$(ctx, signalName, cb),
4046
+ listenToPosition: (key, cb) => listenPosition$(ctx, key, cb),
4047
+ positionAtIndex: (index) => state.positions[index],
4048
+ positionByKey: (key) => {
4049
+ const index = state.indexByKey.get(key);
4050
+ return index === void 0 ? void 0 : state.positions[index];
4051
+ },
3215
4052
  scroll: state.scroll,
3216
4053
  scrollLength: state.scrollLength,
4054
+ scrollVelocity: getScrollVelocity(state),
3217
4055
  sizeAtIndex: (index) => state.sizesKnown.get(getId(state, index)),
3218
4056
  sizes: state.sizesKnown,
3219
4057
  start: state.startNoBuffer,
3220
4058
  startBuffered: state.startBuffered
3221
4059
  }),
3222
- scrollIndexIntoView,
3223
- scrollItemIntoView: ({ item, ...props }) => {
4060
+ reportContentInset: (inset) => {
4061
+ state.contentInsetOverride = inset != null ? inset : void 0;
4062
+ updateScroll(ctx, state.scroll, true);
4063
+ },
4064
+ scrollIndexIntoView: (options) => runScrollWithPromise(() => scrollIndexIntoView(options)),
4065
+ scrollItemIntoView: ({ item, ...props }) => runScrollWithPromise(() => {
3224
4066
  const data = state.props.data;
3225
4067
  const index = data.indexOf(item);
3226
4068
  if (index !== -1) {
3227
4069
  scrollIndexIntoView({ index, ...props });
4070
+ return true;
3228
4071
  }
3229
- },
3230
- scrollToEnd: (options) => {
4072
+ return false;
4073
+ }),
4074
+ scrollToEnd: (options) => runScrollWithPromise(() => {
3231
4075
  const data = state.props.data;
3232
4076
  const stylePaddingBottom = state.props.stylePaddingBottom;
3233
4077
  const index = data.length - 1;
3234
4078
  if (index !== -1) {
3235
4079
  const paddingBottom = stylePaddingBottom || 0;
3236
4080
  const footerSize = peek$(ctx, "footerSize") || 0;
3237
- scrollToIndex(ctx, state, {
4081
+ scrollToIndex(ctx, {
4082
+ ...options,
3238
4083
  index,
3239
4084
  viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
3240
- viewPosition: 1,
3241
- ...options
4085
+ viewPosition: 1
3242
4086
  });
4087
+ return true;
3243
4088
  }
3244
- },
3245
- scrollToIndex: (params) => scrollToIndex(ctx, state, params),
3246
- scrollToItem: ({ item, ...props }) => {
4089
+ return false;
4090
+ }),
4091
+ scrollToIndex: (params) => runScrollWithPromise(() => {
4092
+ scrollToIndex(ctx, params);
4093
+ return true;
4094
+ }),
4095
+ scrollToItem: ({ item, ...props }) => runScrollWithPromise(() => {
3247
4096
  const data = state.props.data;
3248
4097
  const index = data.indexOf(item);
3249
4098
  if (index !== -1) {
3250
- scrollToIndex(ctx, state, { index, ...props });
4099
+ scrollToIndex(ctx, { index, ...props });
4100
+ return true;
3251
4101
  }
3252
- },
3253
- scrollToOffset: (params) => scrollTo(ctx, state, params),
4102
+ return false;
4103
+ }),
4104
+ scrollToOffset: (params) => runScrollWithPromise(() => {
4105
+ scrollTo(ctx, params);
4106
+ return true;
4107
+ }),
3254
4108
  setScrollProcessingEnabled: (enabled) => {
3255
4109
  state.scrollProcessingEnabled = enabled;
3256
4110
  },
@@ -3260,8 +4114,57 @@ function createImperativeHandle(ctx, state) {
3260
4114
  }
3261
4115
  };
3262
4116
  }
3263
- function getRenderedItem(ctx, state, key) {
4117
+
4118
+ // src/utils/getAlwaysRenderIndices.ts
4119
+ var sortAsc = (a, b) => a - b;
4120
+ var toCount = (value) => typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
4121
+ var addIndex = (result, dataLength, index) => {
4122
+ if (index >= 0 && index < dataLength) {
4123
+ result.add(index);
4124
+ }
4125
+ };
4126
+ function getAlwaysRenderIndices(config, data, keyExtractor) {
4127
+ var _a3, _b;
4128
+ if (!config || data.length === 0) {
4129
+ return [];
4130
+ }
4131
+ const result = /* @__PURE__ */ new Set();
4132
+ const dataLength = data.length;
4133
+ const topCount = toCount(config.top);
4134
+ if (topCount > 0) {
4135
+ for (let i = 0; i < Math.min(topCount, dataLength); i++) {
4136
+ addIndex(result, dataLength, i);
4137
+ }
4138
+ }
4139
+ const bottomCount = toCount(config.bottom);
4140
+ if (bottomCount > 0) {
4141
+ for (let i = Math.max(0, dataLength - bottomCount); i < dataLength; i++) {
4142
+ addIndex(result, dataLength, i);
4143
+ }
4144
+ }
4145
+ if ((_a3 = config.indices) == null ? void 0 : _a3.length) {
4146
+ for (const index of config.indices) {
4147
+ if (!Number.isFinite(index)) continue;
4148
+ addIndex(result, dataLength, Math.floor(index));
4149
+ }
4150
+ }
4151
+ if ((_b = config.keys) == null ? void 0 : _b.length) {
4152
+ const keys = new Set(config.keys);
4153
+ for (let i = 0; i < dataLength && keys.size > 0; i++) {
4154
+ const key = keyExtractor(data[i], i);
4155
+ if (keys.has(key)) {
4156
+ addIndex(result, dataLength, i);
4157
+ keys.delete(key);
4158
+ }
4159
+ }
4160
+ }
4161
+ const indices = Array.from(result);
4162
+ indices.sort(sortAsc);
4163
+ return indices;
4164
+ }
4165
+ function getRenderedItem(ctx, key) {
3264
4166
  var _a3;
4167
+ const state = ctx.state;
3265
4168
  if (!state) {
3266
4169
  return null;
3267
4170
  }
@@ -3288,6 +4191,42 @@ function getRenderedItem(ctx, state, key) {
3288
4191
  }
3289
4192
  return { index, item: data[index], renderedItem };
3290
4193
  }
4194
+
4195
+ // src/utils/normalizeMaintainVisibleContentPosition.ts
4196
+ function normalizeMaintainVisibleContentPosition(value) {
4197
+ var _a3, _b;
4198
+ if (value === true) {
4199
+ return { data: true, size: true };
4200
+ }
4201
+ if (value && typeof value === "object") {
4202
+ return {
4203
+ data: (_a3 = value.data) != null ? _a3 : false,
4204
+ shouldRestorePosition: value.shouldRestorePosition,
4205
+ size: (_b = value.size) != null ? _b : true
4206
+ };
4207
+ }
4208
+ if (value === false) {
4209
+ return { data: false, size: false };
4210
+ }
4211
+ return { data: false, size: true };
4212
+ }
4213
+
4214
+ // src/utils/setPaddingTop.ts
4215
+ function setPaddingTop(ctx, { stylePaddingTop }) {
4216
+ const state = ctx.state;
4217
+ if (stylePaddingTop !== void 0) {
4218
+ const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
4219
+ if (stylePaddingTop < prevStylePaddingTop) {
4220
+ let prevTotalSize = peek$(ctx, "totalSize") || 0;
4221
+ set$(ctx, "totalSize", prevTotalSize + prevStylePaddingTop);
4222
+ state.timeoutSetPaddingTop = setTimeout(() => {
4223
+ prevTotalSize = peek$(ctx, "totalSize") || 0;
4224
+ set$(ctx, "totalSize", prevTotalSize - prevStylePaddingTop);
4225
+ }, 16);
4226
+ }
4227
+ set$(ctx, "stylePaddingTop", stylePaddingTop);
4228
+ }
4229
+ }
3291
4230
  function useThrottleDebounce(mode) {
3292
4231
  const timeoutRef = React3.useRef(null);
3293
4232
  const lastCallTimeRef = React3.useRef(0);
@@ -3335,9 +4274,8 @@ function useThrottledOnScroll(originalHandler, scrollEventThrottle) {
3335
4274
  }
3336
4275
 
3337
4276
  // src/components/LegendList.tsx
3338
- var DEFAULT_DRAW_DISTANCE = 250;
3339
- var DEFAULT_ITEM_SIZE = 100;
3340
4277
  var LegendList = typedMemo(
4278
+ // biome-ignore lint/nursery/noShadow: const function name shadowing is intentional
3341
4279
  typedForwardRef(function LegendList2(props, forwardedRef) {
3342
4280
  const { children, data: dataProp, renderItem: renderItemProp, ...restProps } = props;
3343
4281
  const isChildrenMode = children !== void 0 && dataProp === void 0;
@@ -3355,16 +4293,17 @@ var LegendList = typedMemo(
3355
4293
  })
3356
4294
  );
3357
4295
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
3358
- var _a3, _b;
4296
+ var _a3, _b, _c, _d, _e;
3359
4297
  const {
3360
4298
  alignItemsAtEnd = false,
4299
+ alwaysRender,
3361
4300
  columnWrapperStyle,
3362
4301
  contentContainerStyle: contentContainerStyleProp,
4302
+ contentInset,
3363
4303
  data: dataProp = [],
3364
4304
  dataVersion,
3365
4305
  drawDistance = 250,
3366
- enableAverages = true,
3367
- estimatedItemSize: estimatedItemSizeProp,
4306
+ estimatedItemSize = 100,
3368
4307
  estimatedListSize,
3369
4308
  extraData,
3370
4309
  getEstimatedItemSize,
@@ -3381,11 +4320,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3381
4320
  ListHeaderComponent,
3382
4321
  maintainScrollAtEnd = false,
3383
4322
  maintainScrollAtEndThreshold = 0.1,
3384
- maintainVisibleContentPosition = false,
4323
+ maintainVisibleContentPosition: maintainVisibleContentPositionProp,
3385
4324
  numColumns: numColumnsProp = 1,
4325
+ overrideItemLayout,
3386
4326
  onEndReached,
3387
4327
  onEndReachedThreshold = 0.5,
3388
4328
  onItemSizeChanged,
4329
+ onMetricsChange,
3389
4330
  onLayout: onLayoutProp,
3390
4331
  onLoad,
3391
4332
  onMomentumScrollEnd,
@@ -3400,50 +4341,102 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3400
4341
  refreshControl,
3401
4342
  refreshing,
3402
4343
  refScrollView,
4344
+ renderScrollComponent,
3403
4345
  renderItem,
3404
4346
  scrollEventThrottle,
3405
4347
  snapToIndices,
3406
4348
  stickyHeaderIndices: stickyHeaderIndicesProp,
3407
4349
  stickyIndices: stickyIndicesDeprecated,
4350
+ // TODOV3: Remove from v3 release
3408
4351
  style: styleProp,
3409
4352
  suggestEstimatedItemSize,
4353
+ useWindowScroll = false,
3410
4354
  viewabilityConfig,
3411
4355
  viewabilityConfigCallbackPairs,
3412
4356
  waitForInitialLayout = true,
3413
4357
  ...rest
3414
4358
  } = props;
3415
- const { childrenMode } = rest;
3416
- const contentContainerStyle = { ...StyleSheet.flatten(contentContainerStyleProp) };
4359
+ const animatedPropsInternal = props.animatedPropsInternal;
4360
+ const positionComponentInternal = props.positionComponentInternal;
4361
+ const stickyPositionComponentInternal = props.stickyPositionComponentInternal;
4362
+ const {
4363
+ childrenMode,
4364
+ positionComponentInternal: _positionComponentInternal,
4365
+ stickyPositionComponentInternal: _stickyPositionComponentInternal,
4366
+ ...restProps
4367
+ } = rest;
4368
+ const contentContainerStyleBase = StyleSheet.flatten(contentContainerStyleProp);
4369
+ const shouldFlexGrow = alignItemsAtEnd && (horizontal ? (contentContainerStyleBase == null ? void 0 : contentContainerStyleBase.minWidth) == null : (contentContainerStyleBase == null ? void 0 : contentContainerStyleBase.minHeight) == null);
4370
+ const contentContainerStyle = {
4371
+ ...contentContainerStyleBase,
4372
+ ...alignItemsAtEnd ? {
4373
+ display: "flex",
4374
+ flexDirection: horizontal ? "row" : "column",
4375
+ ...shouldFlexGrow ? { flexGrow: 1 } : {},
4376
+ justifyContent: "flex-end"
4377
+ } : {}
4378
+ };
3417
4379
  const style = { ...StyleSheet.flatten(styleProp) };
3418
4380
  const stylePaddingTopState = extractPadding(style, contentContainerStyle, "Top");
3419
4381
  const stylePaddingBottomState = extractPadding(style, contentContainerStyle, "Bottom");
3420
- const [renderNum, setRenderNum] = React3.useState(0);
3421
- const initialScrollProp = initialScrollAtEnd ? { index: Math.max(0, dataProp.length - 1), viewOffset: -stylePaddingBottomState } : initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? { index: initialScrollIndexProp.index || 0, viewOffset: initialScrollIndexProp.viewOffset || 0 } : { index: initialScrollIndexProp || 0, viewOffset: initialScrollOffsetProp || 0 } : void 0;
3422
- const [canRender, setCanRender] = React3__namespace.useState(!IsNewArchitecture);
4382
+ const maintainVisibleContentPositionConfig = normalizeMaintainVisibleContentPosition(
4383
+ maintainVisibleContentPositionProp
4384
+ );
4385
+ const initialScrollProp = initialScrollAtEnd ? { index: Math.max(0, dataProp.length - 1), viewOffset: -stylePaddingBottomState, viewPosition: 1 } : initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? {
4386
+ index: initialScrollIndexProp.index || 0,
4387
+ viewOffset: initialScrollIndexProp.viewOffset || (initialScrollIndexProp.viewPosition === 1 ? -stylePaddingBottomState : 0),
4388
+ viewPosition: initialScrollIndexProp.viewPosition || 0
4389
+ } : {
4390
+ index: initialScrollIndexProp || 0,
4391
+ viewOffset: initialScrollOffsetProp || 0
4392
+ } : void 0;
4393
+ const [canRender, setCanRender] = React3__namespace.useState(false);
3423
4394
  const ctx = useStateContext();
3424
4395
  ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
3425
4396
  const refScroller = React3.useRef(null);
3426
4397
  const combinedRef = useCombinedRef(refScroller, refScrollView);
3427
- const estimatedItemSize = estimatedItemSizeProp != null ? estimatedItemSizeProp : DEFAULT_ITEM_SIZE;
3428
- const scrollBuffer = (drawDistance != null ? drawDistance : DEFAULT_DRAW_DISTANCE) || 1;
3429
- const keyExtractor = keyExtractorProp != null ? keyExtractorProp : (_item, index) => index.toString();
4398
+ const keyExtractor = keyExtractorProp != null ? keyExtractorProp : ((_item, index) => index.toString());
3430
4399
  const stickyHeaderIndices = stickyHeaderIndicesProp != null ? stickyHeaderIndicesProp : stickyIndicesDeprecated;
4400
+ const alwaysRenderIndices = React3.useMemo(() => {
4401
+ const indices = getAlwaysRenderIndices(alwaysRender, dataProp, keyExtractor);
4402
+ return { arr: indices, set: new Set(indices) };
4403
+ }, [
4404
+ alwaysRender == null ? void 0 : alwaysRender.top,
4405
+ alwaysRender == null ? void 0 : alwaysRender.bottom,
4406
+ (_a3 = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _a3.join(","),
4407
+ (_b = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _b.join(","),
4408
+ dataProp,
4409
+ dataVersion,
4410
+ keyExtractor
4411
+ ]);
3431
4412
  if (IS_DEV && stickyIndicesDeprecated && !stickyHeaderIndicesProp) {
3432
4413
  warnDevOnce(
3433
4414
  "stickyIndices",
3434
4415
  "stickyIndices has been renamed to stickyHeaderIndices. Please update your props to use stickyHeaderIndices."
3435
4416
  );
3436
4417
  }
3437
- const refState = React3.useRef();
4418
+ if (IS_DEV && useWindowScroll && renderScrollComponent) {
4419
+ warnDevOnce(
4420
+ "useWindowScrollRenderScrollComponent",
4421
+ "useWindowScroll is not supported when renderScrollComponent is provided."
4422
+ );
4423
+ }
4424
+ const useWindowScrollResolved = !!useWindowScroll && !renderScrollComponent;
4425
+ const refState = React3.useRef(void 0);
4426
+ const hasOverrideItemLayout = !!overrideItemLayout;
4427
+ const prevHasOverrideItemLayout = React3.useRef(hasOverrideItemLayout);
3438
4428
  if (!refState.current) {
3439
- if (!ctx.internalState) {
4429
+ if (!ctx.state) {
3440
4430
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : { height: 0, width: 0 } )[horizontal ? "width" : "height"];
3441
- ctx.internalState = {
3442
- activeStickyIndex: void 0,
4431
+ ctx.state = {
4432
+ activeStickyIndex: -1,
3443
4433
  averageSizes: {},
3444
- columns: /* @__PURE__ */ new Map(),
3445
- containerItemKeys: /* @__PURE__ */ new Set(),
4434
+ columnSpans: [],
4435
+ columns: [],
4436
+ containerItemKeys: /* @__PURE__ */ new Map(),
3446
4437
  containerItemTypes: /* @__PURE__ */ new Map(),
4438
+ contentInsetOverride: void 0,
4439
+ dataChangeEpoch: 0,
3447
4440
  dataChangeNeedsScrollUpdate: false,
3448
4441
  didColumnsChange: false,
3449
4442
  didDataChange: false,
@@ -3459,24 +4452,26 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3459
4452
  attempts: 0,
3460
4453
  index: initialScrollProp.index,
3461
4454
  settledTicks: 0,
3462
- viewOffset: (_a3 = initialScrollProp.viewOffset) != null ? _a3 : 0,
4455
+ viewOffset: (_c = initialScrollProp.viewOffset) != null ? _c : 0,
3463
4456
  viewPosition: initialScrollProp.viewPosition
3464
4457
  } : void 0,
3465
4458
  initialScroll: initialScrollProp,
3466
4459
  isAtEnd: false,
3467
4460
  isAtStart: false,
3468
- isEndReached: false,
4461
+ isEndReached: null,
3469
4462
  isFirst: true,
3470
- isStartReached: false,
4463
+ isStartReached: null,
3471
4464
  lastBatchingAction: Date.now(),
3472
4465
  lastLayout: void 0,
4466
+ lastScrollDelta: 0,
3473
4467
  loadStartTime: Date.now(),
3474
4468
  minIndexSizeChanged: 0,
4469
+ nativeContentInset: void 0,
3475
4470
  nativeMarginTop: 0,
3476
- positions: /* @__PURE__ */ new Map(),
4471
+ positions: [],
3477
4472
  props: {},
3478
4473
  queuedCalculateItemsInView: 0,
3479
- refScroller: void 0,
4474
+ refScroller: { current: null },
3480
4475
  scroll: 0,
3481
4476
  scrollAdjustHandler: new ScrollAdjustHandler(ctx),
3482
4477
  scrollForNextCalculateItemsInView: void 0,
@@ -3492,6 +4487,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3492
4487
  startBuffered: -1,
3493
4488
  startNoBuffer: -1,
3494
4489
  startReachedSnapshot: void 0,
4490
+ startReachedSnapshotDataChangeEpoch: void 0,
3495
4491
  stickyContainerPool: /* @__PURE__ */ new Set(),
3496
4492
  stickyContainers: /* @__PURE__ */ new Map(),
3497
4493
  timeoutSizeMessage: 0,
@@ -3499,18 +4495,19 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3499
4495
  totalSize: 0,
3500
4496
  viewabilityConfigCallbackPairs: void 0
3501
4497
  };
3502
- const internalState = ctx.internalState;
3503
- internalState.triggerCalculateItemsInView = (params) => calculateItemsInView(ctx, internalState, params);
3504
- set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPosition);
4498
+ const internalState = ctx.state;
4499
+ internalState.triggerCalculateItemsInView = (params) => calculateItemsInView(ctx, params);
4500
+ set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPositionConfig);
3505
4501
  set$(ctx, "extraData", extraData);
3506
4502
  }
3507
- refState.current = ctx.internalState;
4503
+ refState.current = ctx.state;
3508
4504
  }
3509
4505
  const state = refState.current;
3510
4506
  const isFirstLocal = state.isFirst;
3511
4507
  state.didColumnsChange = numColumnsProp !== state.props.numColumns;
3512
4508
  const didDataChangeLocal = state.props.dataVersion !== dataVersion || state.props.data !== dataProp && checkActualChange(state, dataProp, state.props.data);
3513
4509
  if (didDataChangeLocal) {
4510
+ state.dataChangeEpoch += 1;
3514
4511
  state.dataChangeNeedsScrollUpdate = true;
3515
4512
  state.didDataChange = true;
3516
4513
  state.previousData = state.props.data;
@@ -3518,20 +4515,25 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3518
4515
  const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
3519
4516
  state.props = {
3520
4517
  alignItemsAtEnd,
4518
+ alwaysRender,
4519
+ alwaysRenderIndicesArr: alwaysRenderIndices.arr,
4520
+ alwaysRenderIndicesSet: alwaysRenderIndices.set,
4521
+ animatedProps: animatedPropsInternal,
4522
+ contentInset,
3521
4523
  data: dataProp,
3522
4524
  dataVersion,
3523
- enableAverages,
4525
+ drawDistance,
3524
4526
  estimatedItemSize,
3525
- getEstimatedItemSize,
3526
- getFixedItemSize,
3527
- getItemType,
4527
+ getEstimatedItemSize: useWrapIfItem(getEstimatedItemSize),
4528
+ getFixedItemSize: useWrapIfItem(getFixedItemSize),
4529
+ getItemType: useWrapIfItem(getItemType),
3528
4530
  horizontal: !!horizontal,
3529
4531
  initialContainerPoolRatio,
3530
4532
  itemsAreEqual,
3531
- keyExtractor,
4533
+ keyExtractor: useWrapIfItem(keyExtractor),
3532
4534
  maintainScrollAtEnd,
3533
4535
  maintainScrollAtEndThreshold,
3534
- maintainVisibleContentPosition,
4536
+ maintainVisibleContentPosition: maintainVisibleContentPositionConfig,
3535
4537
  numColumns: numColumnsProp,
3536
4538
  onEndReached,
3537
4539
  onEndReachedThreshold,
@@ -3541,15 +4543,18 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3541
4543
  onStartReached,
3542
4544
  onStartReachedThreshold,
3543
4545
  onStickyHeaderChange,
4546
+ overrideItemLayout,
4547
+ positionComponentInternal,
3544
4548
  recycleItems: !!recycleItems,
3545
4549
  renderItem,
3546
- scrollBuffer,
3547
4550
  snapToIndices,
3548
4551
  stickyIndicesArr: stickyHeaderIndices != null ? stickyHeaderIndices : [],
3549
4552
  stickyIndicesSet: React3.useMemo(() => new Set(stickyHeaderIndices != null ? stickyHeaderIndices : []), [stickyHeaderIndices == null ? void 0 : stickyHeaderIndices.join(",")]),
4553
+ stickyPositionComponentInternal,
3550
4554
  stylePaddingBottom: stylePaddingBottomState,
3551
4555
  stylePaddingTop: stylePaddingTopState,
3552
- suggestEstimatedItemSize: !!suggestEstimatedItemSize
4556
+ suggestEstimatedItemSize: !!suggestEstimatedItemSize,
4557
+ useWindowScroll: useWindowScrollResolved
3553
4558
  };
3554
4559
  state.refScroller = refScroller;
3555
4560
  const memoizedLastItemKeys = React3.useMemo(() => {
@@ -3559,62 +4564,50 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3559
4564
  (_, i) => getId(state, dataProp.length - 1 - i)
3560
4565
  );
3561
4566
  }, [dataProp, dataVersion, numColumnsProp]);
3562
- const initializeStateVars = () => {
4567
+ const initializeStateVars = (shouldAdjustPadding) => {
3563
4568
  set$(ctx, "lastItemKeys", memoizedLastItemKeys);
3564
4569
  set$(ctx, "numColumns", numColumnsProp);
3565
4570
  const prevPaddingTop = peek$(ctx, "stylePaddingTop");
3566
- setPaddingTop(ctx, state, { stylePaddingTop: stylePaddingTopState });
4571
+ setPaddingTop(ctx, { stylePaddingTop: stylePaddingTopState });
3567
4572
  refState.current.props.stylePaddingBottom = stylePaddingBottomState;
3568
4573
  let paddingDiff = stylePaddingTopState - prevPaddingTop;
3569
- if (paddingDiff && prevPaddingTop !== void 0 && Platform.OS === "ios") {
3570
- if (state.scroll < 0) {
3571
- paddingDiff += state.scroll;
3572
- }
3573
- requestAdjust(ctx, state, paddingDiff);
3574
- }
4574
+ if (shouldAdjustPadding && maintainVisibleContentPositionConfig.size && paddingDiff && prevPaddingTop !== void 0 && Platform.OS === "ios") ;
3575
4575
  };
3576
4576
  if (isFirstLocal) {
3577
- initializeStateVars();
4577
+ initializeStateVars(false);
3578
4578
  updateItemPositions(
3579
4579
  ctx,
3580
- state,
3581
4580
  /*dataChanged*/
3582
4581
  true
3583
4582
  );
3584
4583
  }
4584
+ const resolveInitialScrollOffset = React3.useCallback((initialScroll) => {
4585
+ const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, initialScroll.index) : 0;
4586
+ const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, initialScroll);
4587
+ return clampScrollOffset(ctx, resolvedOffset, initialScroll);
4588
+ }, []);
3585
4589
  const initialContentOffset = React3.useMemo(() => {
3586
- var _a4, _b2;
3587
- const { initialScroll } = refState.current;
3588
- if (!initialScroll) {
4590
+ let value;
4591
+ const { initialScroll, initialAnchor } = refState.current;
4592
+ if (initialScroll) {
4593
+ if (initialScroll.contentOffset !== void 0) {
4594
+ value = initialScroll.contentOffset;
4595
+ } else {
4596
+ const clampedOffset = resolveInitialScrollOffset(initialScroll);
4597
+ const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
4598
+ refState.current.initialScroll = updatedInitialScroll;
4599
+ state.initialScroll = updatedInitialScroll;
4600
+ value = clampedOffset;
4601
+ }
4602
+ } else {
3589
4603
  refState.current.initialAnchor = void 0;
3590
- return 0;
4604
+ value = 0;
3591
4605
  }
3592
- if (initialScroll.index !== void 0 && (!refState.current.initialAnchor || ((_a4 = refState.current.initialAnchor) == null ? void 0 : _a4.index) !== initialScroll.index)) {
3593
- refState.current.initialAnchor = {
3594
- attempts: 0,
3595
- index: initialScroll.index,
3596
- settledTicks: 0,
3597
- viewOffset: (_b2 = initialScroll.viewOffset) != null ? _b2 : 0,
3598
- viewPosition: initialScroll.viewPosition
3599
- };
4606
+ if (!value) {
4607
+ setInitialRenderState(ctx, { didInitialScroll: true });
3600
4608
  }
3601
- if (initialScroll.contentOffset !== void 0) {
3602
- return initialScroll.contentOffset;
3603
- }
3604
- const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, state, initialScroll.index) : 0;
3605
- const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, state, baseOffset, initialScroll);
3606
- let clampedOffset = resolvedOffset;
3607
- if (Number.isFinite(state.scrollLength) && Number.isFinite(state.totalSize)) {
3608
- const maxOffset = Math.max(0, state.totalSize - state.scrollLength);
3609
- clampedOffset = Math.min(clampedOffset, maxOffset);
3610
- }
3611
- clampedOffset = Math.max(0, clampedOffset);
3612
- const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
3613
- refState.current.initialScroll = updatedInitialScroll;
3614
- state.initialScroll = updatedInitialScroll;
3615
- refState.current.isStartReached = clampedOffset < refState.current.scrollLength * onStartReachedThreshold;
3616
- return clampedOffset;
3617
- }, [renderNum]);
4609
+ return value;
4610
+ }, []);
3618
4611
  if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
3619
4612
  refState.current.lastBatchingAction = Date.now();
3620
4613
  if (!keyExtractorProp && !isFirstLocal && didDataChangeLocal) {
@@ -3623,39 +4616,53 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3623
4616
  "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."
3624
4617
  );
3625
4618
  refState.current.sizes.clear();
3626
- refState.current.positions.clear();
4619
+ refState.current.positions.length = 0;
3627
4620
  refState.current.totalSize = 0;
3628
4621
  set$(ctx, "totalSize", 0);
3629
4622
  }
3630
4623
  }
3631
- const onLayoutHeader = React3.useCallback((rect, fromLayoutEffect) => {
3632
- const { initialScroll } = refState.current;
3633
- const size = rect[horizontal ? "width" : "height"];
3634
- set$(ctx, "headerSize", size);
3635
- if ((initialScroll == null ? void 0 : initialScroll.index) !== void 0) {
3636
- {
3637
- if (fromLayoutEffect) {
3638
- setRenderNum((v) => v + 1);
3639
- }
3640
- }
3641
- }
3642
- }, []);
3643
4624
  const doInitialScroll = React3.useCallback(() => {
3644
- var _a4;
3645
- const initialScroll = state.initialScroll;
3646
- if (initialScroll) {
3647
- scrollTo(ctx, state, {
4625
+ const { initialScroll, didFinishInitialScroll, queuedInitialLayout, scrollingTo } = state;
4626
+ if (initialScroll && !queuedInitialLayout && !didFinishInitialScroll && !scrollingTo) {
4627
+ const offset = resolveInitialScrollOffset(initialScroll);
4628
+ const updatedInitialScroll = { ...initialScroll, contentOffset: offset };
4629
+ refState.current.initialScroll = updatedInitialScroll;
4630
+ state.initialScroll = updatedInitialScroll;
4631
+ scrollTo(ctx, {
3648
4632
  animated: false,
3649
- index: (_a4 = state.initialScroll) == null ? void 0 : _a4.index,
4633
+ index: initialScroll.index,
3650
4634
  isInitialScroll: true,
3651
- offset: initialContentOffset,
4635
+ offset,
3652
4636
  precomputedWithViewOffset: true
3653
4637
  });
3654
4638
  }
3655
- }, [initialContentOffset]);
4639
+ }, []);
4640
+ const onLayoutFooter = React3.useCallback(
4641
+ (layout) => {
4642
+ if (!initialScrollAtEnd) {
4643
+ return;
4644
+ }
4645
+ const { initialScroll } = state;
4646
+ if (!initialScroll) {
4647
+ return;
4648
+ }
4649
+ const lastIndex = Math.max(0, dataProp.length - 1);
4650
+ if (initialScroll.index !== lastIndex || initialScroll.viewPosition !== 1) {
4651
+ return;
4652
+ }
4653
+ const footerSize = layout[horizontal ? "width" : "height"];
4654
+ const viewOffset = -stylePaddingBottomState - footerSize;
4655
+ if (initialScroll.viewOffset !== viewOffset) {
4656
+ const updatedInitialScroll = { ...initialScroll, viewOffset };
4657
+ refState.current.initialScroll = updatedInitialScroll;
4658
+ state.initialScroll = updatedInitialScroll;
4659
+ }
4660
+ },
4661
+ [dataProp.length, horizontal, initialScrollAtEnd, stylePaddingBottomState]
4662
+ );
3656
4663
  const onLayoutChange = React3.useCallback((layout) => {
3657
4664
  doInitialScroll();
3658
- handleLayout(ctx, state, layout, setCanRender);
4665
+ handleLayout(ctx, layout, setCanRender);
3659
4666
  }, []);
3660
4667
  const { onLayout } = useOnLayoutSync({
3661
4668
  onLayoutChange,
@@ -3665,7 +4672,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3665
4672
  });
3666
4673
  React3.useLayoutEffect(() => {
3667
4674
  if (snapToIndices) {
3668
- updateSnapToOffsets(ctx, state);
4675
+ updateSnapToOffsets(ctx);
3669
4676
  }
3670
4677
  }, [snapToIndices]);
3671
4678
  React3.useLayoutEffect(() => {
@@ -3675,24 +4682,50 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3675
4682
  isFirst,
3676
4683
  props: { data }
3677
4684
  } = state;
3678
- const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx, state, !!(didDataChange || didColumnsChange));
4685
+ const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx);
3679
4686
  if (!didAllocateContainers && !isFirst && (didDataChange || didColumnsChange)) {
3680
- checkResetContainers(ctx, state, data);
4687
+ checkResetContainers(ctx, data);
3681
4688
  }
3682
4689
  state.didColumnsChange = false;
3683
4690
  state.didDataChange = false;
3684
4691
  state.isFirst = false;
3685
4692
  }, [dataProp, dataVersion, numColumnsProp]);
3686
4693
  React3.useLayoutEffect(() => {
4694
+ var _a4;
3687
4695
  set$(ctx, "extraData", extraData);
3688
- }, [extraData]);
3689
- React3.useLayoutEffect(initializeStateVars, [
3690
- dataVersion,
3691
- memoizedLastItemKeys.join(","),
3692
- numColumnsProp,
3693
- stylePaddingBottomState,
3694
- stylePaddingTopState
3695
- ]);
4696
+ const didToggleOverride = prevHasOverrideItemLayout.current !== hasOverrideItemLayout;
4697
+ prevHasOverrideItemLayout.current = hasOverrideItemLayout;
4698
+ if ((hasOverrideItemLayout || didToggleOverride) && numColumnsProp > 1) {
4699
+ (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { forceFullItemPositions: true });
4700
+ }
4701
+ }, [extraData, hasOverrideItemLayout, numColumnsProp]);
4702
+ React3.useLayoutEffect(
4703
+ () => initializeStateVars(true),
4704
+ [dataVersion, memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingBottomState, stylePaddingTopState]
4705
+ );
4706
+ React3.useEffect(() => {
4707
+ if (!onMetricsChange) {
4708
+ return;
4709
+ }
4710
+ let lastMetrics;
4711
+ const emitMetrics = () => {
4712
+ const metrics = {
4713
+ footerSize: peek$(ctx, "footerSize") || 0,
4714
+ headerSize: peek$(ctx, "headerSize") || 0
4715
+ };
4716
+ if (!lastMetrics || metrics.headerSize !== lastMetrics.headerSize || metrics.footerSize !== lastMetrics.footerSize) {
4717
+ lastMetrics = metrics;
4718
+ onMetricsChange(metrics);
4719
+ }
4720
+ };
4721
+ emitMetrics();
4722
+ const unsubscribe = [listen$(ctx, "headerSize", emitMetrics), listen$(ctx, "footerSize", emitMetrics)];
4723
+ return () => {
4724
+ for (const unsub of unsubscribe) {
4725
+ unsub();
4726
+ }
4727
+ };
4728
+ }, [ctx, onMetricsChange]);
3696
4729
  React3.useEffect(() => {
3697
4730
  const viewability = setupViewability({
3698
4731
  onViewableItemsChanged,
@@ -3702,49 +4735,47 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3702
4735
  state.viewabilityConfigCallbackPairs = viewability;
3703
4736
  state.enableScrollForNextCalculateItemsInView = !viewability;
3704
4737
  }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
3705
- React3.useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx, state), []);
4738
+ React3.useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx), []);
3706
4739
  {
3707
4740
  React3.useEffect(doInitialScroll, []);
3708
4741
  }
3709
4742
  const fns = React3.useMemo(
3710
4743
  () => ({
3711
- getRenderedItem: (key) => getRenderedItem(ctx, state, key),
3712
- onScroll: (event) => onScroll(ctx, state, event),
3713
- updateItemSize: (itemKey, sizeObj) => updateItemSize(ctx, state, itemKey, sizeObj)
4744
+ getRenderedItem: (key) => getRenderedItem(ctx, key),
4745
+ onMomentumScrollEnd: (event) => {
4746
+ checkFinishedScrollFallback(ctx);
4747
+ if (onMomentumScrollEnd) {
4748
+ onMomentumScrollEnd(event);
4749
+ }
4750
+ },
4751
+ onScroll: (event) => onScroll(ctx, event),
4752
+ updateItemSize: (itemKey, sizeObj) => updateItemSize(ctx, itemKey, sizeObj)
3714
4753
  }),
3715
4754
  []
3716
4755
  );
3717
4756
  const onScrollHandler = useStickyScrollHandler(stickyHeaderIndices, horizontal, ctx, fns.onScroll);
4757
+ const refreshControlElement = refreshControl;
3718
4758
  return /* @__PURE__ */ React3__namespace.createElement(React3__namespace.Fragment, null, /* @__PURE__ */ React3__namespace.createElement(
3719
4759
  ListComponent,
3720
4760
  {
3721
- ...rest,
4761
+ ...restProps,
3722
4762
  alignItemsAtEnd,
3723
4763
  canRender,
3724
4764
  contentContainerStyle,
4765
+ contentInset,
3725
4766
  getRenderedItem: fns.getRenderedItem,
3726
4767
  horizontal,
3727
4768
  initialContentOffset,
3728
4769
  ListEmptyComponent: dataProp.length === 0 ? ListEmptyComponent : void 0,
3729
4770
  ListHeaderComponent,
3730
- maintainVisibleContentPosition,
3731
4771
  onLayout,
3732
- onLayoutHeader,
3733
- onMomentumScrollEnd: (event) => {
3734
- {
3735
- requestAnimationFrame(() => {
3736
- finishScrollTo(ctx, refState.current);
3737
- });
3738
- }
3739
- if (onMomentumScrollEnd) {
3740
- onMomentumScrollEnd(event);
3741
- }
3742
- },
4772
+ onLayoutFooter,
4773
+ onMomentumScrollEnd: fns.onMomentumScrollEnd,
3743
4774
  onScroll: onScrollHandler,
3744
4775
  recycleItems,
3745
- refreshControl: refreshControl ? stylePaddingTopState > 0 ? React3__namespace.cloneElement(refreshControl, {
3746
- progressViewOffset: (refreshControl.props.progressViewOffset || 0) + stylePaddingTopState
3747
- }) : refreshControl : onRefresh && /* @__PURE__ */ React3__namespace.createElement(
4776
+ refreshControl: refreshControlElement ? stylePaddingTopState > 0 ? React3__namespace.cloneElement(refreshControlElement, {
4777
+ progressViewOffset: ((_d = refreshControlElement.props.progressViewOffset) != null ? _d : 0) + stylePaddingTopState
4778
+ }) : refreshControlElement : onRefresh && /* @__PURE__ */ React3__namespace.createElement(
3748
4779
  RefreshControl,
3749
4780
  {
3750
4781
  onRefresh,
@@ -3753,18 +4784,30 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3753
4784
  }
3754
4785
  ),
3755
4786
  refScrollView: combinedRef,
3756
- scrollAdjustHandler: (_b = refState.current) == null ? void 0 : _b.scrollAdjustHandler,
3757
- scrollEventThrottle: 16 ,
4787
+ renderScrollComponent,
4788
+ scrollAdjustHandler: (_e = refState.current) == null ? void 0 : _e.scrollAdjustHandler,
4789
+ scrollEventThrottle: 0,
3758
4790
  snapToIndices,
3759
4791
  stickyHeaderIndices,
3760
4792
  style,
3761
4793
  updateItemSize: fns.updateItemSize,
4794
+ useWindowScroll: useWindowScrollResolved,
3762
4795
  waitForInitialLayout
3763
4796
  }
3764
- ), IS_DEV && ENABLE_DEBUG_VIEW && /* @__PURE__ */ React3__namespace.createElement(DebugView, { state: refState.current }));
4797
+ ), IS_DEV && ENABLE_DEBUG_VIEW);
3765
4798
  });
3766
4799
 
3767
- exports.LegendList = LegendList;
4800
+ // src/index.ts
4801
+ var LegendList3 = LegendList;
4802
+ if (IS_DEV) {
4803
+ console.warn(
4804
+ "[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."
4805
+ );
4806
+ }
4807
+
4808
+ exports.LegendList = LegendList3;
4809
+ exports.typedForwardRef = typedForwardRef;
4810
+ exports.typedMemo = typedMemo;
3768
4811
  exports.useIsLastItem = useIsLastItem;
3769
4812
  exports.useListScrollSize = useListScrollSize;
3770
4813
  exports.useRecyclingEffect = useRecyclingEffect;