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

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 (49) hide show
  1. package/.DS_Store +0 -0
  2. package/CHANGELOG.md +19 -0
  3. package/README.md +9 -2
  4. package/animated.d.ts +620 -5
  5. package/animated.js +2 -2
  6. package/animated.mjs +1 -1
  7. package/index.d.ts +1227 -11
  8. package/index.js +2594 -1023
  9. package/index.mjs +2593 -1024
  10. package/index.native.js +2347 -961
  11. package/index.native.mjs +2327 -943
  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 +62 -1
  19. package/{types-JPHClxiw.d.mts → react-native.d.ts} +436 -158
  20. package/react-native.js +4942 -0
  21. package/react-native.mjs +4911 -0
  22. package/{types-JPHClxiw.d.ts → react-native.web.d.ts} +493 -158
  23. package/react-native.web.js +5357 -0
  24. package/react-native.web.mjs +5326 -0
  25. package/{types-YNdphn_A.d.mts → react.d.ts} +493 -158
  26. package/react.js +5357 -0
  27. package/react.mjs +5326 -0
  28. package/reanimated.d.ts +631 -7
  29. package/reanimated.js +156 -30
  30. package/reanimated.mjs +155 -29
  31. package/section-list.d.ts +620 -5
  32. package/section-list.js +38 -3679
  33. package/section-list.mjs +34 -3676
  34. package/animated.d.mts +0 -9
  35. package/index.d.mts +0 -23
  36. package/index.native.d.mts +0 -23
  37. package/index.native.d.ts +0 -23
  38. package/keyboard-controller.d.mts +0 -12
  39. package/keyboard-controller.d.ts +0 -12
  40. package/keyboard-controller.js +0 -69
  41. package/keyboard-controller.mjs +0 -48
  42. package/keyboard.d.mts +0 -13
  43. package/reanimated.d.mts +0 -18
  44. package/section-list.d.mts +0 -113
  45. package/section-list.native.d.mts +0 -113
  46. package/section-list.native.d.ts +0 -113
  47. package/section-list.native.js +0 -3700
  48. package/section-list.native.mjs +0 -3679
  49. package/types-YNdphn_A.d.ts +0 -670
package/index.native.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var React2 = require('react');
4
- var reactNative = require('react-native');
4
+ var ReactNative = require('react-native');
5
5
  var shim = require('use-sync-external-store/shim');
6
6
 
7
7
  function _interopNamespace(e) {
@@ -23,35 +23,68 @@ function _interopNamespace(e) {
23
23
  }
24
24
 
25
25
  var React2__namespace = /*#__PURE__*/_interopNamespace(React2);
26
+ var ReactNative__namespace = /*#__PURE__*/_interopNamespace(ReactNative);
26
27
 
27
28
  // src/components/LegendList.tsx
28
- reactNative.Animated.View;
29
- var View = reactNative.View;
30
- var Text = reactNative.Text;
31
- var createAnimatedValue = (value) => new reactNative.Animated.Value(value);
29
+ ReactNative.Animated.View;
30
+ var View = ReactNative.View;
31
+ var Text = ReactNative.Text;
32
+
33
+ // src/state/getContentInsetEnd.ts
34
+ function getContentInsetEnd(state) {
35
+ var _a3;
36
+ const { props } = state;
37
+ const horizontal = props.horizontal;
38
+ const contentInset = props.contentInset;
39
+ const baseInset = contentInset != null ? contentInset : state.nativeContentInset;
40
+ const overrideInset = (_a3 = state.contentInsetOverride) != null ? _a3 : void 0;
41
+ if (overrideInset) {
42
+ const mergedInset = { bottom: 0, right: 0, ...baseInset, ...overrideInset };
43
+ return (horizontal ? mergedInset.right : mergedInset.bottom) || 0;
44
+ }
45
+ if (baseInset) {
46
+ return (horizontal ? baseInset.right : baseInset.bottom) || 0;
47
+ }
48
+ return 0;
49
+ }
50
+
51
+ // src/state/getContentSize.ts
52
+ function getContentSize(ctx) {
53
+ var _a3;
54
+ const { values, state } = ctx;
55
+ const stylePaddingTop = values.get("stylePaddingTop") || 0;
56
+ const stylePaddingBottom = state.props.stylePaddingBottom || 0;
57
+ const headerSize = values.get("headerSize") || 0;
58
+ const footerSize = values.get("footerSize") || 0;
59
+ const contentInsetBottom = getContentInsetEnd(state);
60
+ const totalSize = (_a3 = state.pendingTotalSize) != null ? _a3 : values.get("totalSize");
61
+ return headerSize + footerSize + totalSize + stylePaddingTop + stylePaddingBottom + (contentInsetBottom || 0);
62
+ }
63
+ var createAnimatedValue = (value) => new ReactNative.Animated.Value(value);
32
64
 
33
65
  // src/state/state.tsx
34
66
  var ContextState = React2__namespace.createContext(null);
67
+ var contextNum = 0;
35
68
  function StateProvider({ children }) {
36
69
  const [value] = React2__namespace.useState(() => ({
37
70
  animatedScrollY: createAnimatedValue(0),
38
71
  columnWrapperStyle: void 0,
39
- internalState: void 0,
72
+ contextNum: contextNum++,
40
73
  listeners: /* @__PURE__ */ new Map(),
41
74
  mapViewabilityAmountCallbacks: /* @__PURE__ */ new Map(),
42
75
  mapViewabilityAmountValues: /* @__PURE__ */ new Map(),
43
76
  mapViewabilityCallbacks: /* @__PURE__ */ new Map(),
44
77
  mapViewabilityConfigStates: /* @__PURE__ */ new Map(),
45
78
  mapViewabilityValues: /* @__PURE__ */ new Map(),
79
+ positionListeners: /* @__PURE__ */ new Map(),
80
+ state: void 0,
46
81
  values: /* @__PURE__ */ new Map([
47
- ["alignItemsPaddingTop", 0],
48
82
  ["stylePaddingTop", 0],
49
83
  ["headerSize", 0],
50
84
  ["numContainers", 0],
51
- ["activeStickyIndex", void 0],
85
+ ["activeStickyIndex", -1],
52
86
  ["totalSize", 0],
53
- ["scrollAdjustPending", 0],
54
- ["scrollingTo", void 0]
87
+ ["scrollAdjustPending", 0]
55
88
  ]),
56
89
  viewRefs: /* @__PURE__ */ new Map()
57
90
  }));
@@ -119,15 +152,24 @@ function set$(ctx, signalName, value) {
119
152
  }
120
153
  }
121
154
  }
122
- function getContentSize(ctx) {
123
- var _a3, _b;
124
- const { values, internalState } = ctx;
125
- const stylePaddingTop = values.get("stylePaddingTop") || 0;
126
- const stylePaddingBottom = (internalState == null ? void 0 : internalState.props.stylePaddingBottom) || 0;
127
- const headerSize = values.get("headerSize") || 0;
128
- const footerSize = values.get("footerSize") || 0;
129
- const totalSize = (_b = (_a3 = ctx.internalState) == null ? void 0 : _a3.pendingTotalSize) != null ? _b : values.get("totalSize");
130
- return headerSize + footerSize + totalSize + stylePaddingTop + stylePaddingBottom;
155
+ function listenPosition$(ctx, key, cb) {
156
+ const { positionListeners } = ctx;
157
+ let setListeners = positionListeners.get(key);
158
+ if (!setListeners) {
159
+ setListeners = /* @__PURE__ */ new Set();
160
+ positionListeners.set(key, setListeners);
161
+ }
162
+ setListeners.add(cb);
163
+ return () => setListeners.delete(cb);
164
+ }
165
+ function notifyPosition$(ctx, key, value) {
166
+ const { positionListeners } = ctx;
167
+ const setListeners = positionListeners.get(key);
168
+ if (setListeners) {
169
+ for (const listener of setListeners) {
170
+ listener(value);
171
+ }
172
+ }
131
173
  }
132
174
  function useArr$(signalNames) {
133
175
  const ctx = React2__namespace.useContext(ContextState);
@@ -146,7 +188,7 @@ function useSelector$(signalName, selector) {
146
188
  var DebugRow = ({ children }) => {
147
189
  return /* @__PURE__ */ React2__namespace.createElement(View, { style: { alignItems: "center", flexDirection: "row", justifyContent: "space-between" } }, children);
148
190
  };
149
- var DebugView = React2__namespace.memo(function DebugView2({ state }) {
191
+ React2__namespace.memo(function DebugView2({ state }) {
150
192
  const ctx = useStateContext();
151
193
  const [totalSize = 0, scrollAdjust = 0, rawScroll = 0, scroll = 0, _numContainers = 0, _numContainersPooled = 0] = useArr$([
152
194
  "totalSize",
@@ -198,7 +240,7 @@ var _a;
198
240
  var envMode = typeof process !== "undefined" && typeof process.env === "object" && process.env ? (_a = process.env.NODE_ENV) != null ? _a : process.env.MODE : void 0;
199
241
  var processDev = typeof envMode === "string" ? envMode.toLowerCase() !== "production" : void 0;
200
242
  var _a2;
201
- var IS_DEV = (_a2 = metroDev != null ? metroDev : processDev) != null ? _a2 : false;
243
+ var IS_DEV = (_a2 = processDev != null ? processDev : metroDev) != null ? _a2 : false;
202
244
 
203
245
  // src/constants.ts
204
246
  var POSITION_OUT_OF_VIEW = -1e7;
@@ -206,9 +248,10 @@ var ENABLE_DEVMODE = IS_DEV && false;
206
248
  var ENABLE_DEBUG_VIEW = IS_DEV && false;
207
249
 
208
250
  // src/constants-platform.native.ts
209
- var IsNewArchitecture = global.nativeFabricUIManager != null;
251
+ var f = global.nativeFabricUIManager;
252
+ var IsNewArchitecture = f !== void 0 && f != null;
210
253
  var useAnimatedValue = (initialValue) => {
211
- const [animAnimatedValue] = React2.useState(() => new reactNative.Animated.Value(initialValue));
254
+ const [animAnimatedValue] = React2.useState(() => new ReactNative.Animated.Value(initialValue));
212
255
  return animAnimatedValue;
213
256
  };
214
257
 
@@ -243,6 +286,11 @@ function extractPadding(style, contentContainerStyle, type) {
243
286
  return getPadding(style, type) + getPadding(contentContainerStyle, type);
244
287
  }
245
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
+ }
246
294
  const numContainers = peek$(ctx, "numContainers");
247
295
  for (let i = 0; i < numContainers; i++) {
248
296
  const itemKey = peek$(ctx, `containerItemKey${i}`);
@@ -265,7 +313,7 @@ function useValue$(key, params) {
265
313
  React2.useMemo(() => {
266
314
  let prevValue;
267
315
  let didQueueTask = false;
268
- listen$(ctx, key, (v) => {
316
+ listen$(ctx, key, () => {
269
317
  const newValue = getNewValue();
270
318
  if (delay !== void 0) {
271
319
  const fn = () => {
@@ -294,11 +342,19 @@ function useValue$(key, params) {
294
342
  }, []);
295
343
  return animValue;
296
344
  }
297
- var typedForwardRef = React2.forwardRef;
298
- var typedMemo = React2.memo;
345
+ var typedMemo = React2__namespace.memo;
346
+ var getComponent = (Component) => {
347
+ if (React2__namespace.isValidElement(Component)) {
348
+ return Component;
349
+ }
350
+ if (Component) {
351
+ return /* @__PURE__ */ React2__namespace.createElement(Component, null);
352
+ }
353
+ return null;
354
+ };
299
355
 
300
356
  // src/components/PositionView.native.tsx
301
- var PositionViewState = typedMemo(function PositionView({
357
+ var PositionViewState = typedMemo(function PositionViewState2({
302
358
  id,
303
359
  horizontal,
304
360
  style,
@@ -307,7 +363,7 @@ var PositionViewState = typedMemo(function PositionView({
307
363
  }) {
308
364
  const [position = POSITION_OUT_OF_VIEW] = useArr$([`containerPosition${id}`]);
309
365
  return /* @__PURE__ */ React2__namespace.createElement(
310
- reactNative.View,
366
+ ReactNative.View,
311
367
  {
312
368
  ref: refView,
313
369
  style: [
@@ -318,7 +374,7 @@ var PositionViewState = typedMemo(function PositionView({
318
374
  }
319
375
  );
320
376
  });
321
- var PositionViewAnimated = typedMemo(function PositionView2({
377
+ var PositionViewAnimated = typedMemo(function PositionViewAnimated2({
322
378
  id,
323
379
  horizontal,
324
380
  style,
@@ -329,12 +385,12 @@ var PositionViewAnimated = typedMemo(function PositionView2({
329
385
  getValue: (v) => v != null ? v : POSITION_OUT_OF_VIEW
330
386
  });
331
387
  let position;
332
- if (reactNative.Platform.OS === "ios" || reactNative.Platform.OS === "android") {
388
+ if (ReactNative.Platform.OS === "ios" || ReactNative.Platform.OS === "android") {
333
389
  position = horizontal ? { transform: [{ translateX: position$ }] } : { transform: [{ translateY: position$ }] };
334
390
  } else {
335
391
  position = horizontal ? { left: position$ } : { top: position$ };
336
392
  }
337
- return /* @__PURE__ */ React2__namespace.createElement(reactNative.Animated.View, { ref: refView, style: [style, position], ...rest });
393
+ return /* @__PURE__ */ React2__namespace.createElement(ReactNative.Animated.View, { ref: refView, style: [style, position], ...rest });
338
394
  });
339
395
  var PositionViewSticky = typedMemo(function PositionViewSticky2({
340
396
  id,
@@ -342,74 +398,120 @@ var PositionViewSticky = typedMemo(function PositionViewSticky2({
342
398
  style,
343
399
  refView,
344
400
  animatedScrollY,
345
- stickyOffset,
346
401
  index,
402
+ stickyHeaderConfig,
403
+ children,
347
404
  ...rest
348
405
  }) {
349
- const [position = POSITION_OUT_OF_VIEW, headerSize] = useArr$([`containerPosition${id}`, "headerSize"]);
406
+ const [position = POSITION_OUT_OF_VIEW, headerSize = 0, stylePaddingTop = 0] = useArr$([
407
+ `containerPosition${id}`,
408
+ "headerSize",
409
+ "stylePaddingTop"
410
+ ]);
350
411
  const transform = React2__namespace.useMemo(() => {
351
- if (animatedScrollY && stickyOffset !== void 0) {
412
+ var _a3;
413
+ if (animatedScrollY) {
414
+ const stickyConfigOffset = (_a3 = stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset) != null ? _a3 : 0;
415
+ const stickyStart = position + headerSize + stylePaddingTop - stickyConfigOffset;
352
416
  const stickyPosition = animatedScrollY.interpolate({
353
417
  extrapolateLeft: "clamp",
354
418
  extrapolateRight: "extend",
355
- inputRange: [position + headerSize, position + 5e3 + headerSize],
419
+ inputRange: [stickyStart, stickyStart + 5e3],
356
420
  outputRange: [position, position + 5e3]
357
421
  });
358
422
  return horizontal ? [{ translateX: stickyPosition }] : [{ translateY: stickyPosition }];
359
423
  }
360
- }, [animatedScrollY, headerSize, horizontal, stickyOffset, position]);
424
+ }, [animatedScrollY, headerSize, horizontal, position, stylePaddingTop, stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset]);
361
425
  const viewStyle = React2__namespace.useMemo(() => [style, { zIndex: index + 1e3 }, { transform }], [style, transform]);
362
- return /* @__PURE__ */ React2__namespace.createElement(reactNative.Animated.View, { ref: refView, style: viewStyle, ...rest });
426
+ const renderStickyHeaderBackdrop = React2__namespace.useMemo(() => {
427
+ if (!(stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent)) {
428
+ return null;
429
+ }
430
+ return /* @__PURE__ */ React2__namespace.createElement(
431
+ ReactNative.View,
432
+ {
433
+ style: {
434
+ inset: 0,
435
+ pointerEvents: "none",
436
+ position: "absolute"
437
+ }
438
+ },
439
+ getComponent(stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent)
440
+ );
441
+ }, [stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent]);
442
+ return /* @__PURE__ */ React2__namespace.createElement(ReactNative.Animated.View, { ref: refView, style: viewStyle, ...rest }, renderStickyHeaderBackdrop, children);
363
443
  });
364
- var PositionView3 = IsNewArchitecture ? PositionViewState : PositionViewAnimated;
444
+ var PositionView = IsNewArchitecture ? PositionViewState : PositionViewAnimated;
365
445
  function useInit(cb) {
366
446
  React2.useState(() => cb());
367
447
  }
368
448
 
369
449
  // src/state/ContextContainer.ts
370
450
  var ContextContainer = React2.createContext(null);
451
+ function useContextContainer() {
452
+ return React2.useContext(ContextContainer);
453
+ }
371
454
  function useViewability(callback, configId) {
372
455
  const ctx = useStateContext();
373
- const { containerId } = React2.useContext(ContextContainer);
374
- const key = containerId + (configId != null ? configId : "");
456
+ const containerContext = useContextContainer();
375
457
  useInit(() => {
458
+ if (!containerContext) {
459
+ return;
460
+ }
461
+ const { containerId } = containerContext;
462
+ const key = containerId + (configId != null ? configId : "");
376
463
  const value = ctx.mapViewabilityValues.get(key);
377
464
  if (value) {
378
465
  callback(value);
379
466
  }
380
467
  });
381
- ctx.mapViewabilityCallbacks.set(key, callback);
382
- React2.useEffect(
383
- () => () => {
468
+ React2.useEffect(() => {
469
+ if (!containerContext) {
470
+ return;
471
+ }
472
+ const { containerId } = containerContext;
473
+ const key = containerId + (configId != null ? configId : "");
474
+ ctx.mapViewabilityCallbacks.set(key, callback);
475
+ return () => {
384
476
  ctx.mapViewabilityCallbacks.delete(key);
385
- },
386
- []
387
- );
477
+ };
478
+ }, [ctx, callback, configId, containerContext]);
388
479
  }
389
480
  function useViewabilityAmount(callback) {
390
481
  const ctx = useStateContext();
391
- const { containerId } = React2.useContext(ContextContainer);
482
+ const containerContext = useContextContainer();
392
483
  useInit(() => {
484
+ if (!containerContext) {
485
+ return;
486
+ }
487
+ const { containerId } = containerContext;
393
488
  const value = ctx.mapViewabilityAmountValues.get(containerId);
394
489
  if (value) {
395
490
  callback(value);
396
491
  }
397
492
  });
398
- ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
399
- React2.useEffect(
400
- () => () => {
493
+ React2.useEffect(() => {
494
+ if (!containerContext) {
495
+ return;
496
+ }
497
+ const { containerId } = containerContext;
498
+ ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
499
+ return () => {
401
500
  ctx.mapViewabilityAmountCallbacks.delete(containerId);
402
- },
403
- []
404
- );
501
+ };
502
+ }, [ctx, callback, containerContext]);
405
503
  }
406
504
  function useRecyclingEffect(effect) {
407
- const { index, value } = React2.useContext(ContextContainer);
505
+ const containerContext = useContextContainer();
408
506
  const prevValues = React2.useRef({
409
507
  prevIndex: void 0,
410
508
  prevItem: void 0
411
509
  });
412
510
  React2.useEffect(() => {
511
+ if (!containerContext) {
512
+ return;
513
+ }
514
+ const { index, value } = containerContext;
413
515
  let ret;
414
516
  if (prevValues.current.prevIndex !== void 0 && prevValues.current.prevItem !== void 0) {
415
517
  ret = effect({
@@ -424,38 +526,58 @@ function useRecyclingEffect(effect) {
424
526
  prevItem: value
425
527
  };
426
528
  return ret;
427
- }, [index, value, effect]);
529
+ }, [effect, containerContext]);
428
530
  }
429
531
  function useRecyclingState(valueOrFun) {
430
- const { index, value, itemKey, triggerLayout } = React2.useContext(ContextContainer);
431
- const refState = React2.useRef({
432
- itemKey: null,
433
- value: null
532
+ var _a3, _b;
533
+ const containerContext = useContextContainer();
534
+ const computeValue = (ctx) => {
535
+ if (isFunction(valueOrFun)) {
536
+ const initializer = valueOrFun;
537
+ return ctx ? initializer({
538
+ index: ctx.index,
539
+ item: ctx.value,
540
+ prevIndex: void 0,
541
+ prevItem: void 0
542
+ }) : initializer();
543
+ }
544
+ return valueOrFun;
545
+ };
546
+ const [stateValue, setStateValue] = React2.useState(() => {
547
+ return computeValue(containerContext);
434
548
  });
435
- const [_, setRenderNum] = React2.useState(0);
436
- const state = refState.current;
437
- if (state.itemKey !== itemKey) {
438
- state.itemKey = itemKey;
439
- state.value = isFunction(valueOrFun) ? valueOrFun({
440
- index,
441
- item: value,
442
- prevIndex: void 0,
443
- prevItem: void 0
444
- }) : valueOrFun;
549
+ const prevItemKeyRef = React2.useRef((_a3 = containerContext == null ? void 0 : containerContext.itemKey) != null ? _a3 : null);
550
+ const currentItemKey = (_b = containerContext == null ? void 0 : containerContext.itemKey) != null ? _b : null;
551
+ if (currentItemKey !== null && prevItemKeyRef.current !== currentItemKey) {
552
+ prevItemKeyRef.current = currentItemKey;
553
+ setStateValue(computeValue(containerContext));
445
554
  }
555
+ const triggerLayout = containerContext == null ? void 0 : containerContext.triggerLayout;
446
556
  const setState = React2.useCallback(
447
557
  (newState) => {
448
- state.value = isFunction(newState) ? newState(state.value) : newState;
449
- setRenderNum((v) => v + 1);
558
+ if (!triggerLayout) {
559
+ return;
560
+ }
561
+ setStateValue((prevValue) => {
562
+ return isFunction(newState) ? newState(prevValue) : newState;
563
+ });
450
564
  triggerLayout();
451
565
  },
452
- [triggerLayout, state]
566
+ [triggerLayout]
453
567
  );
454
- return [state.value, setState];
568
+ return [stateValue, setState];
455
569
  }
456
570
  function useIsLastItem() {
457
- const { itemKey } = React2.useContext(ContextContainer);
458
- const isLast = useSelector$("lastItemKeys", (lastItemKeys) => (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false);
571
+ const containerContext = useContextContainer();
572
+ const isLast = useSelector$("lastItemKeys", (lastItemKeys) => {
573
+ if (containerContext) {
574
+ const { itemKey } = containerContext;
575
+ if (!isNullOrUndefined(itemKey)) {
576
+ return (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false;
577
+ }
578
+ }
579
+ return false;
580
+ });
459
581
  return isLast;
460
582
  }
461
583
  function useListScrollSize() {
@@ -465,8 +587,9 @@ function useListScrollSize() {
465
587
  var noop = () => {
466
588
  };
467
589
  function useSyncLayout() {
468
- if (IsNewArchitecture) {
469
- const { triggerLayout: syncLayout } = React2.useContext(ContextContainer);
590
+ const containerContext = useContextContainer();
591
+ if (IsNewArchitecture && containerContext) {
592
+ const { triggerLayout: syncLayout } = containerContext;
470
593
  return syncLayout;
471
594
  } else {
472
595
  return noop;
@@ -509,30 +632,44 @@ function useOnLayoutSync({
509
632
  }
510
633
  return { onLayout };
511
634
  }
635
+ var Platform2 = ReactNative.Platform;
636
+ var PlatformAdjustBreaksScroll = Platform2.OS === "android";
637
+ var typedForwardRef = React2__namespace.forwardRef;
638
+ var typedMemo2 = React2__namespace.memo;
639
+
640
+ // src/utils/isInMVCPActiveMode.native.ts
641
+ function isInMVCPActiveMode(state) {
642
+ return state.dataChangeNeedsScrollUpdate;
643
+ }
512
644
 
513
645
  // src/components/Container.tsx
514
- var Container = typedMemo(function Container2({
646
+ var Container = typedMemo2(function Container2({
515
647
  id,
516
648
  recycleItems,
517
649
  horizontal,
518
650
  getRenderedItem: getRenderedItem2,
519
651
  updateItemSize: updateItemSize2,
520
- ItemSeparatorComponent
652
+ ItemSeparatorComponent,
653
+ stickyHeaderConfig
521
654
  }) {
522
655
  const ctx = useStateContext();
523
656
  const { columnWrapperStyle, animatedScrollY } = ctx;
524
- const [column = 0, data, itemKey, numColumns, extraData, isSticky, stickyOffset] = useArr$([
657
+ const positionComponentInternal = ctx.state.props.positionComponentInternal;
658
+ const stickyPositionComponentInternal = ctx.state.props.stickyPositionComponentInternal;
659
+ const [column = 0, span = 1, data, itemKey, numColumns = 1, extraData, isSticky] = useArr$([
525
660
  `containerColumn${id}`,
661
+ `containerSpan${id}`,
526
662
  `containerItemData${id}`,
527
663
  `containerItemKey${id}`,
528
664
  "numColumns",
529
665
  "extraData",
530
- `containerSticky${id}`,
531
- `containerStickyOffset${id}`
666
+ `containerSticky${id}`
532
667
  ]);
533
668
  const itemLayoutRef = React2.useRef({
669
+ didLayout: false,
534
670
  horizontal,
535
671
  itemKey,
672
+ pendingShrinkToken: 0,
536
673
  updateItemSize: updateItemSize2
537
674
  });
538
675
  itemLayoutRef.current.horizontal = horizontal;
@@ -540,9 +677,10 @@ var Container = typedMemo(function Container2({
540
677
  itemLayoutRef.current.updateItemSize = updateItemSize2;
541
678
  const ref = React2.useRef(null);
542
679
  const [layoutRenderCount, forceLayoutRender] = React2.useState(0);
543
- const otherAxisPos = numColumns > 1 ? `${(column - 1) / numColumns * 100}%` : 0;
544
- const otherAxisSize = numColumns > 1 ? `${1 / numColumns * 100}%` : void 0;
545
- const didLayoutRef = React2.useRef(false);
680
+ const resolvedColumn = column > 0 ? column : 1;
681
+ const resolvedSpan = Math.min(Math.max(span || 1, 1), numColumns);
682
+ const otherAxisPos = numColumns > 1 ? `${(resolvedColumn - 1) / numColumns * 100}%` : 0;
683
+ const otherAxisSize = numColumns > 1 ? `${resolvedSpan / numColumns * 100}%` : void 0;
546
684
  const style = React2.useMemo(() => {
547
685
  let paddingStyles;
548
686
  if (columnWrapperStyle) {
@@ -597,19 +735,41 @@ var Container = typedMemo(function Container2({
597
735
  const {
598
736
  horizontal: currentHorizontal,
599
737
  itemKey: currentItemKey,
600
- updateItemSize: updateItemSizeFn
738
+ updateItemSize: updateItemSizeFn,
739
+ lastSize,
740
+ pendingShrinkToken
601
741
  } = itemLayoutRef.current;
602
742
  if (isNullOrUndefined(currentItemKey)) {
603
743
  return;
604
744
  }
605
- didLayoutRef.current = true;
745
+ itemLayoutRef.current.didLayout = true;
606
746
  let layout = rectangle;
607
- const size = roundSize(rectangle[currentHorizontal ? "width" : "height"]);
747
+ const axis = currentHorizontal ? "width" : "height";
748
+ const size = roundSize(rectangle[axis]);
749
+ const prevSize = lastSize ? roundSize(lastSize[axis]) : void 0;
608
750
  const doUpdate = () => {
609
- itemLayoutRef.current.lastSize = { height: layout.height, width: layout.width };
751
+ itemLayoutRef.current.lastSize = layout;
610
752
  updateItemSizeFn(currentItemKey, layout);
611
- didLayoutRef.current = true;
753
+ itemLayoutRef.current.didLayout = true;
612
754
  };
755
+ const shouldDeferWebShrinkLayoutUpdate = Platform2.OS === "web" && !isInMVCPActiveMode(ctx.state) && prevSize !== void 0 && size + 1 < prevSize;
756
+ if (shouldDeferWebShrinkLayoutUpdate) {
757
+ const token = pendingShrinkToken + 1;
758
+ itemLayoutRef.current.pendingShrinkToken = token;
759
+ requestAnimationFrame(() => {
760
+ var _a4;
761
+ if (itemLayoutRef.current.pendingShrinkToken !== token) {
762
+ return;
763
+ }
764
+ const element = ref.current;
765
+ const rect = (_a4 = element == null ? void 0 : element.getBoundingClientRect) == null ? void 0 : _a4.call(element);
766
+ if (rect) {
767
+ layout = { height: rect.height, width: rect.width };
768
+ }
769
+ doUpdate();
770
+ });
771
+ return;
772
+ }
613
773
  if (IsNewArchitecture || size > 0) {
614
774
  doUpdate();
615
775
  } else {
@@ -622,16 +782,15 @@ var Container = typedMemo(function Container2({
622
782
  const { onLayout } = useOnLayoutSync(
623
783
  {
624
784
  onLayoutChange,
625
- ref
626
- },
785
+ ref},
627
786
  [itemKey, layoutRenderCount]
628
787
  );
629
788
  if (!IsNewArchitecture) {
630
789
  React2.useEffect(() => {
631
790
  if (!isNullOrUndefined(itemKey)) {
632
- didLayoutRef.current = false;
791
+ itemLayoutRef.current.didLayout = false;
633
792
  const timeout = setTimeout(() => {
634
- if (!didLayoutRef.current) {
793
+ if (!itemLayoutRef.current.didLayout) {
635
794
  const {
636
795
  itemKey: currentItemKey,
637
796
  lastSize,
@@ -639,7 +798,7 @@ var Container = typedMemo(function Container2({
639
798
  } = itemLayoutRef.current;
640
799
  if (lastSize && !isNullOrUndefined(currentItemKey)) {
641
800
  updateItemSizeFn(currentItemKey, lastSize);
642
- didLayoutRef.current = true;
801
+ itemLayoutRef.current.didLayout = true;
643
802
  }
644
803
  }
645
804
  }, 16);
@@ -649,7 +808,7 @@ var Container = typedMemo(function Container2({
649
808
  }
650
809
  }, [itemKey]);
651
810
  }
652
- const PositionComponent = isSticky ? PositionViewSticky : PositionView3;
811
+ const PositionComponent = isSticky ? stickyPositionComponentInternal ? stickyPositionComponentInternal : PositionViewSticky : positionComponentInternal ? positionComponentInternal : PositionView;
653
812
  return /* @__PURE__ */ React2__namespace.createElement(
654
813
  PositionComponent,
655
814
  {
@@ -660,7 +819,7 @@ var Container = typedMemo(function Container2({
660
819
  key: recycleItems ? void 0 : itemKey,
661
820
  onLayout,
662
821
  refView: ref,
663
- stickyOffset: isSticky ? stickyOffset : void 0,
822
+ stickyHeaderConfig,
664
823
  style
665
824
  },
666
825
  /* @__PURE__ */ React2__namespace.createElement(ContextContainer.Provider, { value: contextValue }, renderedItem, renderedItemInfo && ItemSeparatorComponent && /* @__PURE__ */ React2__namespace.createElement(Separator, { ItemSeparatorComponent, leadingItem: renderedItemInfo.item }))
@@ -673,6 +832,7 @@ var Containers = typedMemo(function Containers2({
673
832
  recycleItems,
674
833
  ItemSeparatorComponent,
675
834
  waitForInitialLayout,
835
+ stickyHeaderConfig,
676
836
  updateItemSize: updateItemSize2,
677
837
  getRenderedItem: getRenderedItem2
678
838
  }) {
@@ -684,10 +844,10 @@ var Containers = typedMemo(function Containers2({
684
844
  // If this is the initial scroll, we don't want to delay because we want to update the size immediately
685
845
  delay: (value, prevValue) => {
686
846
  var _a3;
687
- return !((_a3 = ctx.internalState) == null ? void 0 : _a3.initialScroll) ? !prevValue || value - prevValue > 20 ? 0 : 200 : void 0;
847
+ return !((_a3 = ctx.state) == null ? void 0 : _a3.initialScroll) ? !prevValue || value - prevValue > 20 ? 0 : 200 : void 0;
688
848
  }
689
849
  });
690
- const animOpacity = waitForInitialLayout && !IsNewArchitecture ? useValue$("containersDidLayout", { getValue: (value) => value ? 1 : 0 }) : void 0;
850
+ const animOpacity = waitForInitialLayout ? useValue$("readyToRender", { getValue: (value) => value ? 1 : 0 }) : void 0;
691
851
  const otherAxisSize = useValue$("otherAxisSize", { delay: 0 });
692
852
  const containers = [];
693
853
  for (let i = 0; i < numContainers; i++) {
@@ -701,25 +861,26 @@ var Containers = typedMemo(function Containers2({
701
861
  id: i,
702
862
  key: i,
703
863
  recycleItems,
864
+ stickyHeaderConfig,
704
865
  updateItemSize: updateItemSize2
705
866
  }
706
867
  )
707
868
  );
708
869
  }
709
870
  const style = horizontal ? { minHeight: otherAxisSize, opacity: animOpacity, width: animSize } : { height: animSize, minWidth: otherAxisSize, opacity: animOpacity };
710
- if (columnWrapperStyle && numColumns > 1) {
871
+ if (columnWrapperStyle) {
711
872
  const { columnGap, rowGap, gap } = columnWrapperStyle;
712
873
  const gapX = columnGap || gap || 0;
713
874
  const gapY = rowGap || gap || 0;
714
875
  if (horizontal) {
715
- if (gapY) {
876
+ if (gapY && numColumns > 1) {
716
877
  style.marginVertical = -gapY / 2;
717
878
  }
718
879
  if (gapX) {
719
880
  style.marginRight = -gapX;
720
881
  }
721
882
  } else {
722
- if (gapX) {
883
+ if (gapX && numColumns > 1) {
723
884
  style.marginHorizontal = -gapX;
724
885
  }
725
886
  if (gapY) {
@@ -727,53 +888,15 @@ var Containers = typedMemo(function Containers2({
727
888
  }
728
889
  }
729
890
  }
730
- return /* @__PURE__ */ React2__namespace.createElement(reactNative.Animated.View, { style }, containers);
891
+ return /* @__PURE__ */ React2__namespace.createElement(ReactNative.Animated.View, { style }, containers);
731
892
  });
732
- function DevNumbers() {
733
- return IS_DEV && React2__namespace.memo(function DevNumbers2() {
734
- return Array.from({ length: 100 }).map((_, index) => /* @__PURE__ */ React2__namespace.createElement(
735
- reactNative.View,
736
- {
737
- key: index,
738
- style: {
739
- height: 100,
740
- pointerEvents: "none",
741
- position: "absolute",
742
- top: index * 100,
743
- width: "100%"
744
- }
745
- },
746
- /* @__PURE__ */ React2__namespace.createElement(reactNative.Text, { style: { color: "red" } }, index * 100)
747
- ));
748
- });
749
- }
750
- var ListComponentScrollView = reactNative.Animated.ScrollView;
751
- function Padding() {
752
- const animPaddingTop = useValue$("alignItemsPaddingTop", { delay: 0 });
753
- return /* @__PURE__ */ React2__namespace.createElement(reactNative.Animated.View, { style: { paddingTop: animPaddingTop } });
754
- }
755
- function PaddingDevMode() {
756
- const animPaddingTop = useValue$("alignItemsPaddingTop", { delay: 0 });
757
- return /* @__PURE__ */ React2__namespace.createElement(React2__namespace.Fragment, null, /* @__PURE__ */ React2__namespace.createElement(reactNative.Animated.View, { style: { paddingTop: animPaddingTop } }), /* @__PURE__ */ React2__namespace.createElement(
758
- reactNative.Animated.View,
759
- {
760
- style: {
761
- backgroundColor: "green",
762
- height: animPaddingTop,
763
- left: 0,
764
- position: "absolute",
765
- right: 0,
766
- top: 0
767
- }
768
- }
769
- ));
770
- }
893
+ var ListComponentScrollView = ReactNative.Animated.ScrollView;
771
894
  function ScrollAdjust() {
772
895
  const bias = 1e7;
773
896
  const [scrollAdjust, scrollAdjustUserOffset] = useArr$(["scrollAdjust", "scrollAdjustUserOffset"]);
774
897
  const scrollOffset = (scrollAdjust || 0) + (scrollAdjustUserOffset || 0) + bias;
775
898
  return /* @__PURE__ */ React2__namespace.createElement(
776
- reactNative.View,
899
+ ReactNative.View,
777
900
  {
778
901
  style: {
779
902
  height: 0,
@@ -790,22 +913,13 @@ function SnapWrapper({ ScrollComponent, ...props }) {
790
913
  return /* @__PURE__ */ React2__namespace.createElement(ScrollComponent, { ...props, snapToOffsets });
791
914
  }
792
915
  var LayoutView = ({ onLayoutChange, refView, ...rest }) => {
793
- const ref = refView != null ? refView : React2.useRef();
916
+ const ref = refView != null ? refView : React2.useRef(null);
794
917
  const { onLayout } = useOnLayoutSync({ onLayoutChange, ref });
795
- return /* @__PURE__ */ React2__namespace.createElement(reactNative.View, { ...rest, onLayout, ref });
918
+ return /* @__PURE__ */ React2__namespace.createElement(ReactNative.View, { ...rest, onLayout, ref });
796
919
  };
797
920
 
798
921
  // src/components/ListComponent.tsx
799
- var getComponent = (Component) => {
800
- if (React2__namespace.isValidElement(Component)) {
801
- return Component;
802
- }
803
- if (Component) {
804
- return /* @__PURE__ */ React2__namespace.createElement(Component, null);
805
- }
806
- return null;
807
- };
808
- var ListComponent = typedMemo(function ListComponent2({
922
+ var ListComponent = typedMemo2(function ListComponent2({
809
923
  canRender,
810
924
  style,
811
925
  contentContainerStyle,
@@ -813,7 +927,7 @@ var ListComponent = typedMemo(function ListComponent2({
813
927
  initialContentOffset,
814
928
  recycleItems,
815
929
  ItemSeparatorComponent,
816
- alignItemsAtEnd,
930
+ alignItemsAtEnd: _alignItemsAtEnd,
817
931
  waitForInitialLayout,
818
932
  onScroll: onScroll2,
819
933
  onLayout,
@@ -825,31 +939,52 @@ var ListComponent = typedMemo(function ListComponent2({
825
939
  getRenderedItem: getRenderedItem2,
826
940
  updateItemSize: updateItemSize2,
827
941
  refScrollView,
828
- maintainVisibleContentPosition,
829
942
  renderScrollComponent,
943
+ onLayoutFooter,
830
944
  scrollAdjustHandler,
831
- onLayoutHeader,
832
945
  snapToIndices,
946
+ stickyHeaderConfig,
833
947
  stickyHeaderIndices,
948
+ useWindowScroll = false,
834
949
  ...rest
835
950
  }) {
836
951
  const ctx = useStateContext();
952
+ const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
837
953
  const ScrollComponent = renderScrollComponent ? React2.useMemo(
838
- () => React2__namespace.forwardRef((props, ref) => renderScrollComponent({ ...props, ref })),
954
+ () => React2__namespace.forwardRef(
955
+ (props, ref) => renderScrollComponent({ ...props, ref })
956
+ ),
839
957
  [renderScrollComponent]
840
958
  ) : ListComponentScrollView;
841
- React2__namespace.useEffect(() => {
842
- if (canRender) {
843
- setTimeout(() => {
844
- scrollAdjustHandler.setMounted();
845
- }, 0);
846
- }
847
- }, [canRender]);
848
959
  const SnapOrScroll = snapToIndices ? SnapWrapper : ScrollComponent;
960
+ React2.useLayoutEffect(() => {
961
+ if (!ListHeaderComponent) {
962
+ set$(ctx, "headerSize", 0);
963
+ }
964
+ if (!ListFooterComponent) {
965
+ set$(ctx, "footerSize", 0);
966
+ }
967
+ }, [ListHeaderComponent, ListFooterComponent, ctx]);
968
+ const onLayoutHeader = React2.useCallback(
969
+ (rect) => {
970
+ const size = rect[horizontal ? "width" : "height"];
971
+ set$(ctx, "headerSize", size);
972
+ },
973
+ [ctx, horizontal]
974
+ );
975
+ const onLayoutFooterInternal = React2.useCallback(
976
+ (rect, fromLayoutEffect) => {
977
+ const size = rect[horizontal ? "width" : "height"];
978
+ set$(ctx, "footerSize", size);
979
+ onLayoutFooter == null ? void 0 : onLayoutFooter(rect, fromLayoutEffect);
980
+ },
981
+ [ctx, horizontal, onLayoutFooter]
982
+ );
849
983
  return /* @__PURE__ */ React2__namespace.createElement(
850
984
  SnapOrScroll,
851
985
  {
852
986
  ...rest,
987
+ ...ScrollComponent === ListComponentScrollView ? { useWindowScroll } : {},
853
988
  contentContainerStyle: [
854
989
  contentContainerStyle,
855
990
  horizontal ? {
@@ -858,7 +993,7 @@ var ListComponent = typedMemo(function ListComponent2({
858
993
  ],
859
994
  contentOffset: initialContentOffset ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
860
995
  horizontal,
861
- maintainVisibleContentPosition: maintainVisibleContentPosition ? { minIndexForVisible: 0 } : void 0,
996
+ maintainVisibleContentPosition: maintainVisibleContentPosition.size || maintainVisibleContentPosition.data ? { minIndexForVisible: 0 } : void 0,
862
997
  onLayout,
863
998
  onScroll: onScroll2,
864
999
  ref: refScrollView,
@@ -866,7 +1001,6 @@ var ListComponent = typedMemo(function ListComponent2({
866
1001
  style
867
1002
  },
868
1003
  /* @__PURE__ */ React2__namespace.createElement(ScrollAdjust, null),
869
- ENABLE_DEVMODE ? /* @__PURE__ */ React2__namespace.createElement(PaddingDevMode, null) : /* @__PURE__ */ React2__namespace.createElement(Padding, null),
870
1004
  ListHeaderComponent && /* @__PURE__ */ React2__namespace.createElement(LayoutView, { onLayoutChange: onLayoutHeader, style: ListHeaderComponentStyle }, getComponent(ListHeaderComponent)),
871
1005
  ListEmptyComponent && getComponent(ListEmptyComponent),
872
1006
  canRender && !ListEmptyComponent && /* @__PURE__ */ React2__namespace.createElement(
@@ -876,25 +1010,27 @@ var ListComponent = typedMemo(function ListComponent2({
876
1010
  horizontal,
877
1011
  ItemSeparatorComponent,
878
1012
  recycleItems,
1013
+ stickyHeaderConfig,
879
1014
  updateItemSize: updateItemSize2,
880
1015
  waitForInitialLayout
881
1016
  }
882
1017
  ),
883
- ListFooterComponent && /* @__PURE__ */ React2__namespace.createElement(
884
- LayoutView,
885
- {
886
- onLayoutChange: (layout) => {
887
- const size = layout[horizontal ? "width" : "height"];
888
- set$(ctx, "footerSize", size);
889
- },
890
- style: ListFooterComponentStyle
891
- },
892
- getComponent(ListFooterComponent)
893
- ),
894
- IS_DEV && ENABLE_DEVMODE && /* @__PURE__ */ React2__namespace.createElement(DevNumbers, null)
1018
+ ListFooterComponent && /* @__PURE__ */ React2__namespace.createElement(LayoutView, { onLayoutChange: onLayoutFooterInternal, style: ListFooterComponentStyle }, getComponent(ListFooterComponent)),
1019
+ IS_DEV && ENABLE_DEVMODE
895
1020
  );
896
1021
  });
897
1022
 
1023
+ // src/core/calculateOffsetForIndex.ts
1024
+ function calculateOffsetForIndex(ctx, index) {
1025
+ const state = ctx.state;
1026
+ return index !== void 0 ? state.positions[index] || 0 : 0;
1027
+ }
1028
+
1029
+ // src/core/getTopOffsetAdjustment.ts
1030
+ function getTopOffsetAdjustment(ctx) {
1031
+ return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
1032
+ }
1033
+
898
1034
  // src/utils/getId.ts
899
1035
  function getId(state, index) {
900
1036
  const { data, keyExtractor } = state.props;
@@ -907,61 +1043,9 @@ function getId(state, index) {
907
1043
  return id;
908
1044
  }
909
1045
 
910
- // src/core/calculateOffsetForIndex.ts
911
- function calculateOffsetForIndex(ctx, state, index) {
912
- let position = 0;
913
- if (index !== void 0) {
914
- position = (state == null ? void 0 : state.positions.get(getId(state, index))) || 0;
915
- const paddingTop = peek$(ctx, "stylePaddingTop");
916
- if (paddingTop) {
917
- position += paddingTop;
918
- }
919
- const headerSize = peek$(ctx, "headerSize");
920
- if (headerSize) {
921
- position += headerSize;
922
- }
923
- }
924
- return position;
925
- }
926
-
927
- // src/utils/setPaddingTop.ts
928
- function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
929
- if (stylePaddingTop !== void 0) {
930
- const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
931
- if (stylePaddingTop < prevStylePaddingTop) {
932
- let prevTotalSize = peek$(ctx, "totalSize") || 0;
933
- set$(ctx, "totalSize", prevTotalSize + prevStylePaddingTop);
934
- state.timeoutSetPaddingTop = setTimeout(() => {
935
- prevTotalSize = peek$(ctx, "totalSize") || 0;
936
- set$(ctx, "totalSize", prevTotalSize - prevStylePaddingTop);
937
- }, 16);
938
- }
939
- set$(ctx, "stylePaddingTop", stylePaddingTop);
940
- }
941
- if (alignItemsPaddingTop !== void 0) {
942
- set$(ctx, "alignItemsPaddingTop", alignItemsPaddingTop);
943
- }
944
- }
945
-
946
- // src/utils/updateAlignItemsPaddingTop.ts
947
- function updateAlignItemsPaddingTop(ctx, state) {
948
- const {
949
- scrollLength,
950
- props: { alignItemsAtEnd, data }
951
- } = state;
952
- if (alignItemsAtEnd) {
953
- let alignItemsPaddingTop = 0;
954
- if ((data == null ? void 0 : data.length) > 0) {
955
- const contentSize = getContentSize(ctx);
956
- alignItemsPaddingTop = Math.max(0, Math.floor(scrollLength - contentSize));
957
- }
958
- setPaddingTop(ctx, state, { alignItemsPaddingTop });
959
- }
960
- }
961
-
962
1046
  // src/core/addTotalSize.ts
963
- function addTotalSize(ctx, state, key, add) {
964
- const { alignItemsAtEnd } = state.props;
1047
+ function addTotalSize(ctx, key, add) {
1048
+ const state = ctx.state;
965
1049
  const prevTotalSize = state.totalSize;
966
1050
  let totalSize = state.totalSize;
967
1051
  if (key === null) {
@@ -980,48 +1064,47 @@ function addTotalSize(ctx, state, key, add) {
980
1064
  state.pendingTotalSize = void 0;
981
1065
  state.totalSize = totalSize;
982
1066
  set$(ctx, "totalSize", totalSize);
983
- if (alignItemsAtEnd) {
984
- updateAlignItemsPaddingTop(ctx, state);
985
- }
986
1067
  }
987
1068
  }
988
1069
  }
989
1070
 
990
1071
  // src/core/setSize.ts
991
- function setSize(ctx, state, itemKey, size) {
1072
+ function setSize(ctx, itemKey, size) {
1073
+ const state = ctx.state;
992
1074
  const { sizes } = state;
993
1075
  const previousSize = sizes.get(itemKey);
994
1076
  const diff = previousSize !== void 0 ? size - previousSize : size;
995
1077
  if (diff !== 0) {
996
- addTotalSize(ctx, state, itemKey, diff);
1078
+ addTotalSize(ctx, itemKey, diff);
997
1079
  }
998
1080
  sizes.set(itemKey, size);
999
1081
  }
1000
1082
 
1001
1083
  // src/utils/getItemSize.ts
1002
- function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedSize) {
1084
+ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
1003
1085
  var _a3, _b;
1086
+ const state = ctx.state;
1004
1087
  const {
1005
1088
  sizesKnown,
1006
1089
  sizes,
1007
1090
  averageSizes,
1008
- props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType }
1091
+ props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType },
1092
+ scrollingTo
1009
1093
  } = state;
1010
1094
  const sizeKnown = sizesKnown.get(key);
1011
1095
  if (sizeKnown !== void 0) {
1012
1096
  return sizeKnown;
1013
1097
  }
1014
1098
  let size;
1015
- const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1016
- const scrollingTo = peek$(ctx, "scrollingTo");
1017
1099
  if (preferCachedSize) {
1018
1100
  const cachedSize = sizes.get(key);
1019
1101
  if (cachedSize !== void 0) {
1020
1102
  return cachedSize;
1021
1103
  }
1022
1104
  }
1105
+ const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1023
1106
  if (getFixedItemSize) {
1024
- size = getFixedItemSize(index, data, itemType);
1107
+ size = getFixedItemSize(data, index, itemType);
1025
1108
  if (size !== void 0) {
1026
1109
  sizesKnown.set(key, size);
1027
1110
  }
@@ -1039,96 +1122,68 @@ function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedS
1039
1122
  }
1040
1123
  }
1041
1124
  if (size === void 0) {
1042
- size = getEstimatedItemSize ? getEstimatedItemSize(index, data, itemType) : estimatedItemSize;
1125
+ size = getEstimatedItemSize ? getEstimatedItemSize(data, index, itemType) : estimatedItemSize;
1043
1126
  }
1044
- setSize(ctx, state, key, size);
1127
+ setSize(ctx, key, size);
1045
1128
  return size;
1046
1129
  }
1047
1130
 
1048
1131
  // src/core/calculateOffsetWithOffsetPosition.ts
1049
- function calculateOffsetWithOffsetPosition(ctx, state, offsetParam, params) {
1132
+ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1133
+ var _a3;
1134
+ const state = ctx.state;
1050
1135
  const { index, viewOffset, viewPosition } = params;
1051
1136
  let offset = offsetParam;
1052
1137
  if (viewOffset) {
1053
1138
  offset -= viewOffset;
1054
1139
  }
1055
- if (viewPosition !== void 0 && index !== void 0) {
1056
- offset -= viewPosition * (state.scrollLength - getItemSize(ctx, state, getId(state, index), index, state.props.data[index]));
1140
+ if (index !== void 0) {
1141
+ const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
1142
+ if (topOffsetAdjustment) {
1143
+ offset += topOffsetAdjustment;
1144
+ }
1057
1145
  }
1058
- return offset;
1059
- }
1060
-
1061
- // src/core/finishScrollTo.ts
1062
- function finishScrollTo(ctx, state) {
1063
- var _a3, _b;
1064
- if (state) {
1065
- state.scrollHistory.length = 0;
1066
- state.initialScroll = void 0;
1067
- state.initialAnchor = void 0;
1068
- set$(ctx, "scrollingTo", void 0);
1069
- if (state.pendingTotalSize !== void 0) {
1070
- addTotalSize(ctx, state, null, state.pendingTotalSize);
1146
+ if (viewPosition !== void 0 && index !== void 0) {
1147
+ const dataLength = state.props.data.length;
1148
+ if (dataLength === 0) {
1149
+ return offset;
1071
1150
  }
1072
- if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1073
- (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1151
+ const isOutOfBounds = index < 0 || index >= dataLength;
1152
+ const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
1153
+ const itemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1154
+ const trailingInset = getContentInsetEnd(state);
1155
+ offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1156
+ if (!isOutOfBounds && index === state.props.data.length - 1) {
1157
+ const footerSize = peek$(ctx, "footerSize") || 0;
1158
+ offset += footerSize;
1074
1159
  }
1075
1160
  }
1161
+ return offset;
1076
1162
  }
1077
- var Platform2 = reactNative.Platform;
1078
1163
 
1079
- // src/core/scrollTo.ts
1080
- function scrollTo(ctx, state, params) {
1081
- var _a3;
1082
- const { noScrollingTo, ...scrollTarget } = params;
1083
- const { animated, isInitialScroll, offset: scrollTargetOffset, precomputedWithViewOffset } = scrollTarget;
1084
- const {
1085
- refScroller,
1086
- props: { horizontal }
1087
- } = state;
1088
- let offset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, state, scrollTargetOffset, scrollTarget);
1089
- if (Number.isFinite(state.scrollLength) && Number.isFinite(state.totalSize)) {
1090
- const maxOffset = Math.max(0, getContentSize(ctx) - state.scrollLength);
1091
- offset = Math.min(offset, maxOffset);
1092
- }
1093
- state.scrollHistory.length = 0;
1094
- if (!noScrollingTo) {
1095
- set$(ctx, "scrollingTo", scrollTarget);
1096
- }
1097
- state.scrollPending = offset;
1098
- if (!isInitialScroll || Platform2.OS === "android") {
1099
- (_a3 = refScroller.current) == null ? void 0 : _a3.scrollTo({
1100
- animated: !!animated,
1101
- x: horizontal ? offset : 0,
1102
- y: horizontal ? 0 : offset
1103
- });
1104
- }
1105
- if (!animated) {
1106
- state.scroll = offset;
1107
- if (Platform2.OS === "web") {
1108
- const unlisten = listen$(ctx, "containersDidLayout", (value) => {
1109
- if (value && peek$(ctx, "scrollingTo")) {
1110
- finishScrollTo(ctx, state);
1111
- unlisten();
1112
- }
1113
- });
1114
- } else {
1115
- setTimeout(() => finishScrollTo(ctx, state), 100);
1116
- }
1117
- if (isInitialScroll) {
1118
- setTimeout(() => {
1119
- state.initialScroll = void 0;
1120
- }, 500);
1121
- }
1122
- }
1164
+ // src/core/clampScrollOffset.ts
1165
+ function clampScrollOffset(ctx, offset, scrollTarget) {
1166
+ const state = ctx.state;
1167
+ const contentSize = getContentSize(ctx);
1168
+ let clampedOffset = offset;
1169
+ if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform2.OS !== "android" || state.lastLayout)) {
1170
+ const baseMaxOffset = Math.max(0, contentSize - state.scrollLength);
1171
+ const viewOffset = scrollTarget == null ? void 0 : scrollTarget.viewOffset;
1172
+ const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
1173
+ const maxOffset = baseMaxOffset + extraEndOffset;
1174
+ clampedOffset = Math.min(offset, maxOffset);
1175
+ }
1176
+ clampedOffset = Math.max(0, clampedOffset);
1177
+ return clampedOffset;
1123
1178
  }
1124
1179
 
1125
1180
  // src/utils/checkThreshold.ts
1126
1181
  var HYSTERESIS_MULTIPLIER = 1.3;
1127
- var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot) => {
1182
+ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot, allowReentryOnChange) => {
1128
1183
  const absDistance = Math.abs(distance);
1129
1184
  const within = atThreshold || threshold > 0 && absDistance <= threshold;
1130
1185
  const updateSnapshot = () => {
1131
- setSnapshot == null ? void 0 : setSnapshot({
1186
+ setSnapshot({
1132
1187
  atThreshold,
1133
1188
  contentSize: context.contentSize,
1134
1189
  dataLength: context.dataLength,
@@ -1139,19 +1194,21 @@ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, co
1139
1194
  if (!within) {
1140
1195
  return false;
1141
1196
  }
1142
- onReached == null ? void 0 : onReached(distance);
1197
+ onReached(distance);
1143
1198
  updateSnapshot();
1144
1199
  return true;
1145
1200
  }
1146
1201
  const reset = !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
1147
1202
  if (reset) {
1148
- setSnapshot == null ? void 0 : setSnapshot(void 0);
1203
+ setSnapshot(void 0);
1149
1204
  return false;
1150
1205
  }
1151
1206
  if (within) {
1152
1207
  const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
1153
1208
  if (changed) {
1154
- onReached == null ? void 0 : onReached(distance);
1209
+ if (allowReentryOnChange) {
1210
+ onReached(distance);
1211
+ }
1155
1212
  updateSnapshot();
1156
1213
  }
1157
1214
  }
@@ -1159,9 +1216,10 @@ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, co
1159
1216
  };
1160
1217
 
1161
1218
  // src/utils/checkAtBottom.ts
1162
- function checkAtBottom(ctx, state) {
1219
+ function checkAtBottom(ctx) {
1163
1220
  var _a3;
1164
- if (!state) {
1221
+ const state = ctx.state;
1222
+ if (!state || state.initialScroll) {
1165
1223
  return;
1166
1224
  }
1167
1225
  const {
@@ -1171,9 +1229,13 @@ function checkAtBottom(ctx, state) {
1171
1229
  maintainingScrollAtEnd,
1172
1230
  props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
1173
1231
  } = state;
1232
+ if (state.initialScroll) {
1233
+ return;
1234
+ }
1174
1235
  const contentSize = getContentSize(ctx);
1175
1236
  if (contentSize > 0 && queuedInitialLayout && !maintainingScrollAtEnd) {
1176
- const distanceFromEnd = contentSize - scroll - scrollLength;
1237
+ const insetEnd = getContentInsetEnd(state);
1238
+ const distanceFromEnd = contentSize - scroll - scrollLength - insetEnd;
1177
1239
  const isContentLess = contentSize < scrollLength;
1178
1240
  state.isAtEnd = isContentLess || distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
1179
1241
  state.isEndReached = checkThreshold(
@@ -1193,95 +1255,659 @@ function checkAtBottom(ctx, state) {
1193
1255
  },
1194
1256
  (snapshot) => {
1195
1257
  state.endReachedSnapshot = snapshot;
1196
- }
1258
+ },
1259
+ true
1197
1260
  );
1198
1261
  }
1199
1262
  }
1200
1263
 
1201
1264
  // src/utils/checkAtTop.ts
1202
- function checkAtTop(state) {
1203
- var _a3;
1204
- if (!state) {
1265
+ function checkAtTop(ctx) {
1266
+ const state = ctx == null ? void 0 : ctx.state;
1267
+ if (!state || state.initialScroll || state.scrollingTo) {
1205
1268
  return;
1206
1269
  }
1207
1270
  const {
1208
- scrollLength,
1271
+ dataChangeEpoch,
1272
+ isStartReached,
1273
+ props: { data, onStartReachedThreshold },
1209
1274
  scroll,
1210
- props: { onStartReachedThreshold }
1275
+ scrollLength,
1276
+ startReachedSnapshot,
1277
+ startReachedSnapshotDataChangeEpoch,
1278
+ totalSize
1211
1279
  } = state;
1212
- const distanceFromTop = scroll;
1213
- state.isAtStart = distanceFromTop <= 0;
1280
+ const dataLength = data.length;
1281
+ const threshold = onStartReachedThreshold * scrollLength;
1282
+ const dataChanged = startReachedSnapshotDataChangeEpoch !== dataChangeEpoch;
1283
+ const withinThreshold = threshold > 0 && Math.abs(scroll) <= threshold;
1284
+ const allowReentryOnDataChange = !!isStartReached && withinThreshold && !!dataChanged && !isInMVCPActiveMode(state);
1285
+ if (isStartReached && threshold > 0 && scroll > threshold && startReachedSnapshot && (dataChanged || startReachedSnapshot.contentSize !== totalSize || startReachedSnapshot.dataLength !== dataLength)) {
1286
+ state.isStartReached = false;
1287
+ state.startReachedSnapshot = void 0;
1288
+ state.startReachedSnapshotDataChangeEpoch = void 0;
1289
+ }
1290
+ state.isAtStart = scroll <= 0;
1291
+ if (isStartReached && withinThreshold && dataChanged && !allowReentryOnDataChange) {
1292
+ return;
1293
+ }
1214
1294
  state.isStartReached = checkThreshold(
1215
- distanceFromTop,
1295
+ scroll,
1216
1296
  false,
1217
- onStartReachedThreshold * scrollLength,
1297
+ threshold,
1218
1298
  state.isStartReached,
1219
- state.startReachedSnapshot,
1299
+ allowReentryOnDataChange ? void 0 : startReachedSnapshot,
1220
1300
  {
1221
- contentSize: state.totalSize,
1222
- dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1301
+ contentSize: totalSize,
1302
+ dataLength,
1223
1303
  scrollPosition: scroll
1224
1304
  },
1225
1305
  (distance) => {
1226
- var _a4, _b;
1227
- return (_b = (_a4 = state.props).onStartReached) == null ? void 0 : _b.call(_a4, { distanceFromStart: distance });
1306
+ var _a3, _b;
1307
+ return (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
1228
1308
  },
1229
1309
  (snapshot) => {
1230
1310
  state.startReachedSnapshot = snapshot;
1231
- }
1311
+ state.startReachedSnapshotDataChangeEpoch = snapshot ? dataChangeEpoch : void 0;
1312
+ },
1313
+ allowReentryOnDataChange
1232
1314
  );
1233
1315
  }
1234
1316
 
1235
- // src/core/updateScroll.ts
1236
- function updateScroll(ctx, state, newScroll, forceUpdate) {
1237
- var _a3;
1238
- const scrollingTo = peek$(ctx, "scrollingTo");
1239
- state.hasScrolled = true;
1240
- state.lastBatchingAction = Date.now();
1241
- const currentTime = Date.now();
1242
- const adjust = state.scrollAdjustHandler.getAdjust();
1243
- const lastHistoryAdjust = state.lastScrollAdjustForHistory;
1244
- const adjustChanged = lastHistoryAdjust !== void 0 && Math.abs(adjust - lastHistoryAdjust) > 0.1;
1245
- if (adjustChanged) {
1246
- state.scrollHistory.length = 0;
1247
- }
1248
- state.lastScrollAdjustForHistory = adjust;
1249
- if (scrollingTo === void 0 && !(state.scrollHistory.length === 0 && newScroll === state.scroll)) {
1250
- if (!adjustChanged) {
1251
- state.scrollHistory.push({ scroll: newScroll, time: currentTime });
1252
- }
1317
+ // src/utils/checkThresholds.ts
1318
+ function checkThresholds(ctx) {
1319
+ checkAtBottom(ctx);
1320
+ checkAtTop(ctx);
1321
+ }
1322
+
1323
+ // src/utils/setInitialRenderState.ts
1324
+ function setInitialRenderState(ctx, {
1325
+ didLayout,
1326
+ didInitialScroll
1327
+ }) {
1328
+ const { state } = ctx;
1329
+ const {
1330
+ loadStartTime,
1331
+ props: { onLoad }
1332
+ } = state;
1333
+ if (didLayout) {
1334
+ state.didContainersLayout = true;
1253
1335
  }
1254
- if (state.scrollHistory.length > 5) {
1255
- state.scrollHistory.shift();
1336
+ if (didInitialScroll) {
1337
+ state.didFinishInitialScroll = true;
1256
1338
  }
1257
- state.scrollPrev = state.scroll;
1258
- state.scrollPrevTime = state.scrollTime;
1259
- state.scroll = newScroll;
1260
- state.scrollTime = currentTime;
1261
- const ignoreScrollFromMVCP = state.ignoreScrollFromMVCP;
1262
- if (ignoreScrollFromMVCP && !scrollingTo) {
1263
- const { lt, gt } = ignoreScrollFromMVCP;
1264
- if (lt && newScroll < lt || gt && newScroll > gt) {
1265
- state.ignoreScrollFromMVCPIgnored = true;
1266
- return;
1339
+ const isReadyToRender = Boolean(state.didContainersLayout && state.didFinishInitialScroll);
1340
+ if (isReadyToRender && !peek$(ctx, "readyToRender")) {
1341
+ set$(ctx, "readyToRender", true);
1342
+ if (onLoad) {
1343
+ onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1267
1344
  }
1268
1345
  }
1269
- if (forceUpdate || state.dataChangeNeedsScrollUpdate || Math.abs(state.scroll - state.scrollPrev) > 2) {
1270
- state.ignoreScrollFromMVCPIgnored = false;
1271
- (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { doMVCP: scrollingTo !== void 0 });
1272
- checkAtBottom(ctx, state);
1273
- checkAtTop(state);
1346
+ }
1347
+
1348
+ // src/core/finishScrollTo.ts
1349
+ function finishScrollTo(ctx) {
1350
+ var _a3, _b;
1351
+ const state = ctx.state;
1352
+ if (state == null ? void 0 : state.scrollingTo) {
1353
+ const resolvePendingScroll = state.pendingScrollResolve;
1354
+ state.pendingScrollResolve = void 0;
1355
+ const scrollingTo = state.scrollingTo;
1356
+ state.scrollHistory.length = 0;
1357
+ state.initialScroll = void 0;
1358
+ state.initialScrollUsesOffset = false;
1359
+ state.initialAnchor = void 0;
1360
+ state.initialNativeScrollWatchdog = void 0;
1361
+ state.scrollingTo = void 0;
1362
+ if (state.pendingTotalSize !== void 0) {
1363
+ addTotalSize(ctx, null, state.pendingTotalSize);
1364
+ }
1365
+ if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1366
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1367
+ }
1368
+ if (PlatformAdjustBreaksScroll) {
1369
+ state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
1370
+ }
1371
+ setInitialRenderState(ctx, { didInitialScroll: true });
1372
+ checkThresholds(ctx);
1373
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1374
+ }
1375
+ }
1376
+
1377
+ // src/core/checkFinishedScroll.ts
1378
+ var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
1379
+ var INITIAL_SCROLL_MAX_FALLBACK_CHECKS = 20;
1380
+ var INITIAL_SCROLL_ZERO_TARGET_EPSILON = 1;
1381
+ function checkFinishedScroll(ctx) {
1382
+ ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
1383
+ }
1384
+ function checkFinishedScrollFrame(ctx) {
1385
+ var _a3;
1386
+ const scrollingTo = ctx.state.scrollingTo;
1387
+ if (scrollingTo) {
1388
+ const { state } = ctx;
1389
+ state.animFrameCheckFinishedScroll = void 0;
1390
+ const scroll = state.scrollPending;
1391
+ const adjust = state.scrollAdjustHandler.getAdjust();
1392
+ const clampedTargetOffset = (_a3 = scrollingTo.targetOffset) != null ? _a3 : clampScrollOffset(ctx, scrollingTo.offset - (scrollingTo.viewOffset || 0), scrollingTo);
1393
+ const maxOffset = clampScrollOffset(ctx, scroll, scrollingTo);
1394
+ const diff1 = Math.abs(scroll - clampedTargetOffset);
1395
+ const diff2 = Math.abs(diff1 - adjust);
1396
+ const isNotOverscrolled = Math.abs(scroll - maxOffset) < 1;
1397
+ const isAtTarget = diff1 < 1 || !scrollingTo.animated && diff2 < 1;
1398
+ if (isNotOverscrolled && isAtTarget) {
1399
+ finishScrollTo(ctx);
1400
+ }
1401
+ }
1402
+ }
1403
+ function checkFinishedScrollFallback(ctx) {
1404
+ const state = ctx.state;
1405
+ const scrollingTo = state.scrollingTo;
1406
+ const shouldFinishInitialZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
1407
+ const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && !shouldFinishInitialZeroTarget || !state.didContainersLayout;
1408
+ state.timeoutCheckFinishedScrollFallback = setTimeout(
1409
+ () => {
1410
+ let numChecks = 0;
1411
+ const checkHasScrolled = () => {
1412
+ var _a3, _b;
1413
+ state.timeoutCheckFinishedScrollFallback = void 0;
1414
+ const isStillScrollingTo = state.scrollingTo;
1415
+ if (isStillScrollingTo) {
1416
+ numChecks++;
1417
+ const isNativeInitialPending = isNativeInitialNonZeroTarget(state) && !state.hasScrolled;
1418
+ const maxChecks = isNativeInitialPending ? INITIAL_SCROLL_MAX_FALLBACK_CHECKS : 5;
1419
+ const shouldFinishZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
1420
+ if (shouldFinishZeroTarget || state.hasScrolled || numChecks > maxChecks) {
1421
+ finishScrollTo(ctx);
1422
+ } else if (isNativeInitialPending && numChecks <= maxChecks) {
1423
+ const targetOffset = (_b = (_a3 = state.initialNativeScrollWatchdog) == null ? void 0 : _a3.targetOffset) != null ? _b : state.scrollPending;
1424
+ const scroller = state.refScroller.current;
1425
+ if (scroller) {
1426
+ scroller.scrollTo({
1427
+ animated: false,
1428
+ x: state.props.horizontal ? targetOffset : 0,
1429
+ y: state.props.horizontal ? 0 : targetOffset
1430
+ });
1431
+ }
1432
+ state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, 100);
1433
+ } else {
1434
+ state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, 100);
1435
+ }
1436
+ }
1437
+ };
1438
+ checkHasScrolled();
1439
+ },
1440
+ slowTimeout ? 500 : 100
1441
+ );
1442
+ }
1443
+ function isNativeInitialNonZeroTarget(state) {
1444
+ return !state.didFinishInitialScroll && !!state.initialNativeScrollWatchdog && state.initialNativeScrollWatchdog.targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
1445
+ }
1446
+ function shouldFinishInitialZeroTargetScroll(ctx) {
1447
+ var _a3;
1448
+ const { state } = ctx;
1449
+ return !!((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) && state.props.data.length > 0 && getContentSize(ctx) <= state.scrollLength && state.scrollPending <= INITIAL_SCROLL_ZERO_TARGET_EPSILON;
1450
+ }
1451
+
1452
+ // src/core/doScrollTo.native.ts
1453
+ function doScrollTo(ctx, params) {
1454
+ const state = ctx.state;
1455
+ const { animated, horizontal, offset } = params;
1456
+ const isAnimated = !!animated;
1457
+ const { refScroller } = state;
1458
+ const scroller = refScroller.current;
1459
+ if (!scroller) {
1460
+ return;
1461
+ }
1462
+ scroller.scrollTo({
1463
+ animated: isAnimated,
1464
+ x: horizontal ? offset : 0,
1465
+ y: horizontal ? 0 : offset
1466
+ });
1467
+ if (!isAnimated) {
1468
+ state.scroll = offset;
1469
+ checkFinishedScrollFallback(ctx);
1470
+ }
1471
+ }
1472
+
1473
+ // src/core/scrollTo.ts
1474
+ var WATCHDOG_OFFSET_EPSILON = 1;
1475
+ function scrollTo(ctx, params) {
1476
+ var _a3, _b;
1477
+ const state = ctx.state;
1478
+ const { noScrollingTo, forceScroll, ...scrollTarget } = params;
1479
+ const { animated, isInitialScroll, offset: scrollTargetOffset, precomputedWithViewOffset } = scrollTarget;
1480
+ const {
1481
+ props: { horizontal }
1482
+ } = state;
1483
+ if (state.animFrameCheckFinishedScroll) {
1484
+ cancelAnimationFrame(ctx.state.animFrameCheckFinishedScroll);
1485
+ }
1486
+ if (state.timeoutCheckFinishedScrollFallback) {
1487
+ clearTimeout(ctx.state.timeoutCheckFinishedScrollFallback);
1488
+ }
1489
+ let offset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, scrollTargetOffset, scrollTarget);
1490
+ offset = clampScrollOffset(ctx, offset, scrollTarget);
1491
+ state.scrollHistory.length = 0;
1492
+ if (!noScrollingTo) {
1493
+ state.scrollingTo = {
1494
+ ...scrollTarget,
1495
+ targetOffset: offset
1496
+ };
1497
+ }
1498
+ state.scrollPending = offset;
1499
+ const shouldWatchInitialNativeScroll = !state.didFinishInitialScroll && (isInitialScroll || !!state.initialNativeScrollWatchdog) && offset > WATCHDOG_OFFSET_EPSILON;
1500
+ const shouldClearInitialNativeScrollWatchdog = !state.didFinishInitialScroll && !!state.initialNativeScrollWatchdog && offset <= WATCHDOG_OFFSET_EPSILON;
1501
+ if (shouldWatchInitialNativeScroll) {
1502
+ state.hasScrolled = false;
1503
+ state.initialNativeScrollWatchdog = {
1504
+ startScroll: (_b = (_a3 = state.initialNativeScrollWatchdog) == null ? void 0 : _a3.startScroll) != null ? _b : state.scroll,
1505
+ targetOffset: offset
1506
+ };
1507
+ } else if (shouldClearInitialNativeScrollWatchdog) {
1508
+ state.initialNativeScrollWatchdog = void 0;
1509
+ }
1510
+ if (forceScroll || !isInitialScroll || Platform2.OS === "android") {
1511
+ doScrollTo(ctx, { animated, horizontal, offset });
1512
+ } else {
1513
+ state.scroll = offset;
1514
+ }
1515
+ }
1516
+
1517
+ // src/core/doMaintainScrollAtEnd.ts
1518
+ function doMaintainScrollAtEnd(ctx) {
1519
+ const state = ctx.state;
1520
+ const {
1521
+ didContainersLayout,
1522
+ isAtEnd,
1523
+ pendingNativeMVCPAdjust,
1524
+ refScroller,
1525
+ props: { maintainScrollAtEnd }
1526
+ } = state;
1527
+ const shouldMaintainScrollAtEnd = !!(isAtEnd && maintainScrollAtEnd && didContainersLayout);
1528
+ if (pendingNativeMVCPAdjust) {
1529
+ state.pendingMaintainScrollAtEnd = shouldMaintainScrollAtEnd;
1530
+ return false;
1531
+ }
1532
+ state.pendingMaintainScrollAtEnd = false;
1533
+ if (shouldMaintainScrollAtEnd) {
1534
+ const contentSize = getContentSize(ctx);
1535
+ if (contentSize < state.scrollLength) {
1536
+ state.scroll = 0;
1537
+ }
1538
+ requestAnimationFrame(() => {
1539
+ var _a3;
1540
+ if (state.isAtEnd) {
1541
+ state.maintainingScrollAtEnd = true;
1542
+ (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
1543
+ animated: maintainScrollAtEnd.animated
1544
+ });
1545
+ setTimeout(
1546
+ () => {
1547
+ state.maintainingScrollAtEnd = false;
1548
+ },
1549
+ maintainScrollAtEnd.animated ? 500 : 0
1550
+ );
1551
+ }
1552
+ });
1553
+ return true;
1554
+ }
1555
+ return false;
1556
+ }
1557
+
1558
+ // src/core/mvcp.ts
1559
+ var MVCP_POSITION_EPSILON = 0.1;
1560
+ var MVCP_ANCHOR_LOCK_TTL_MS = 300;
1561
+ var MVCP_ANCHOR_LOCK_QUIET_PASSES_TO_RELEASE = 2;
1562
+ var NATIVE_END_CLAMP_EPSILON = 1;
1563
+ function resolveAnchorLock(state, enableMVCPAnchorLock, mvcpData, now) {
1564
+ if (!enableMVCPAnchorLock) {
1565
+ state.mvcpAnchorLock = void 0;
1566
+ return void 0;
1567
+ }
1568
+ const lock = state.mvcpAnchorLock;
1569
+ if (!lock) {
1570
+ return void 0;
1571
+ }
1572
+ const isExpired = now > lock.expiresAt;
1573
+ const isMissing = state.indexByKey.get(lock.id) === void 0;
1574
+ if (isExpired || isMissing || !mvcpData) {
1575
+ state.mvcpAnchorLock = void 0;
1576
+ return void 0;
1577
+ }
1578
+ return lock;
1579
+ }
1580
+ function updateAnchorLock(state, params) {
1581
+ if (Platform2.OS === "web") {
1582
+ const { anchorId, anchorPosition, dataChanged, now, positionDiff } = params;
1583
+ const enableMVCPAnchorLock = !!dataChanged || !!state.mvcpAnchorLock;
1584
+ const mvcpData = state.props.maintainVisibleContentPosition.data;
1585
+ if (!enableMVCPAnchorLock || !mvcpData || state.scrollingTo || !anchorId || anchorPosition === void 0) {
1586
+ return;
1587
+ }
1588
+ const existingLock = state.mvcpAnchorLock;
1589
+ const quietPasses = !dataChanged && Math.abs(positionDiff) <= MVCP_POSITION_EPSILON && (existingLock == null ? void 0 : existingLock.id) === anchorId ? existingLock.quietPasses + 1 : 0;
1590
+ if (!dataChanged && quietPasses >= MVCP_ANCHOR_LOCK_QUIET_PASSES_TO_RELEASE) {
1591
+ state.mvcpAnchorLock = void 0;
1592
+ return;
1593
+ }
1594
+ state.mvcpAnchorLock = {
1595
+ expiresAt: now + MVCP_ANCHOR_LOCK_TTL_MS,
1596
+ id: anchorId,
1597
+ position: anchorPosition,
1598
+ quietPasses
1599
+ };
1600
+ }
1601
+ }
1602
+ function shouldQueueNativeMVCPAdjust(dataChanged, state, positionDiff, prevTotalSize, prevScroll, scrollTarget) {
1603
+ if (!dataChanged || Platform2.OS === "web" || !state.props.maintainVisibleContentPosition.data || scrollTarget !== void 0 || positionDiff >= -MVCP_POSITION_EPSILON) {
1604
+ return false;
1605
+ }
1606
+ const distanceFromEnd = prevTotalSize - prevScroll - state.scrollLength;
1607
+ return distanceFromEnd < Math.abs(positionDiff) - MVCP_POSITION_EPSILON;
1608
+ }
1609
+ function getPredictedNativeClamp(state, unresolvedAmount, totalSize) {
1610
+ if (Math.abs(unresolvedAmount) <= MVCP_POSITION_EPSILON) {
1611
+ return 0;
1612
+ }
1613
+ const maxScroll = Math.max(0, totalSize - state.scrollLength);
1614
+ const clampDelta = maxScroll - state.scroll;
1615
+ if (unresolvedAmount < 0) {
1616
+ return Math.max(unresolvedAmount, Math.min(0, clampDelta));
1617
+ }
1618
+ if (unresolvedAmount > 0) {
1619
+ return Math.min(unresolvedAmount, Math.max(0, clampDelta));
1620
+ }
1621
+ return 0;
1622
+ }
1623
+ function maybeApplyPredictedNativeMVCPAdjust(ctx) {
1624
+ const state = ctx.state;
1625
+ const pending = state.pendingNativeMVCPAdjust;
1626
+ if (!pending || Math.abs(pending.manualApplied) > MVCP_POSITION_EPSILON) {
1627
+ return;
1628
+ }
1629
+ const totalSize = getContentSize(ctx);
1630
+ const predictedNativeClamp = getPredictedNativeClamp(state, pending.amount, totalSize);
1631
+ if (Math.abs(predictedNativeClamp) <= MVCP_POSITION_EPSILON) {
1632
+ return;
1633
+ }
1634
+ const manualDesired = pending.amount - predictedNativeClamp;
1635
+ if (Math.abs(manualDesired) <= MVCP_POSITION_EPSILON) {
1636
+ return;
1637
+ }
1638
+ pending.manualApplied = manualDesired;
1639
+ requestAdjust(ctx, manualDesired, true);
1640
+ }
1641
+ function resolvePendingNativeMVCPAdjust(ctx, newScroll) {
1642
+ const state = ctx.state;
1643
+ const pending = state.pendingNativeMVCPAdjust;
1644
+ if (!pending) {
1645
+ return false;
1646
+ }
1647
+ const remainingAfterManual = pending.amount - pending.manualApplied;
1648
+ const nativeDelta = newScroll - (pending.startScroll + pending.manualApplied);
1649
+ const isWrongDirection = remainingAfterManual < 0 && nativeDelta > MVCP_POSITION_EPSILON || remainingAfterManual > 0 && nativeDelta < -MVCP_POSITION_EPSILON;
1650
+ if (Math.abs(remainingAfterManual) <= MVCP_POSITION_EPSILON) {
1651
+ state.pendingNativeMVCPAdjust = void 0;
1652
+ return true;
1653
+ }
1654
+ if (isWrongDirection) {
1655
+ state.pendingNativeMVCPAdjust = void 0;
1656
+ return false;
1657
+ }
1658
+ const expectedNativeClampScroll = Math.max(0, getContentSize(ctx) - state.scrollLength);
1659
+ const distanceToClamp = Math.abs(newScroll - expectedNativeClampScroll);
1660
+ const didApproachClamp = distanceToClamp < pending.closestDistanceToClamp - MVCP_POSITION_EPSILON;
1661
+ const didMoveAwayAfterApproach = pending.hasApproachedClamp && distanceToClamp > pending.closestDistanceToClamp + MVCP_POSITION_EPSILON;
1662
+ if (didApproachClamp) {
1663
+ pending.closestDistanceToClamp = distanceToClamp;
1664
+ pending.hasApproachedClamp = true;
1665
+ } else if (didMoveAwayAfterApproach) {
1666
+ state.pendingNativeMVCPAdjust = void 0;
1667
+ return false;
1668
+ }
1669
+ const isAtExpectedNativeClamp = distanceToClamp <= NATIVE_END_CLAMP_EPSILON;
1670
+ if (!isAtExpectedNativeClamp) {
1671
+ return false;
1672
+ }
1673
+ state.pendingNativeMVCPAdjust = void 0;
1674
+ const remaining = remainingAfterManual - nativeDelta;
1675
+ if (Math.abs(remaining) > MVCP_POSITION_EPSILON) {
1676
+ requestAdjust(ctx, remaining, true);
1677
+ }
1678
+ return true;
1679
+ }
1680
+ function prepareMVCP(ctx, dataChanged) {
1681
+ const state = ctx.state;
1682
+ const { idsInView, positions, props } = state;
1683
+ const {
1684
+ maintainVisibleContentPosition: { data: mvcpData, size: mvcpScroll, shouldRestorePosition }
1685
+ } = props;
1686
+ const isWeb = Platform2.OS === "web";
1687
+ const now = Date.now();
1688
+ const enableMVCPAnchorLock = isWeb && (!!dataChanged || !!state.mvcpAnchorLock);
1689
+ const scrollingTo = state.scrollingTo;
1690
+ const anchorLock = isWeb ? resolveAnchorLock(state, enableMVCPAnchorLock, mvcpData, now) : void 0;
1691
+ let prevPosition;
1692
+ let targetId;
1693
+ const idsInViewWithPositions = [];
1694
+ const scrollTarget = scrollingTo == null ? void 0 : scrollingTo.index;
1695
+ const scrollingToViewPosition = scrollingTo == null ? void 0 : scrollingTo.viewPosition;
1696
+ const isEndAnchoredScrollTarget = scrollTarget !== void 0 && state.props.data.length > 0 && scrollTarget >= state.props.data.length - 1 && (scrollingToViewPosition != null ? scrollingToViewPosition : 0) > 0;
1697
+ const shouldMVCP = dataChanged ? mvcpData : mvcpScroll;
1698
+ const indexByKey = state.indexByKey;
1699
+ const prevScroll = state.scroll;
1700
+ const prevTotalSize = getContentSize(ctx);
1701
+ if (shouldMVCP) {
1702
+ if (!isWeb && state.pendingNativeMVCPAdjust && scrollTarget === void 0) {
1703
+ maybeApplyPredictedNativeMVCPAdjust(ctx);
1704
+ return void 0;
1705
+ }
1706
+ if (anchorLock && scrollTarget === void 0) {
1707
+ targetId = anchorLock.id;
1708
+ prevPosition = anchorLock.position;
1709
+ } else if (scrollTarget !== void 0) {
1710
+ if (!IsNewArchitecture && (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll)) {
1711
+ return void 0;
1712
+ }
1713
+ targetId = getId(state, scrollTarget);
1714
+ } else if (idsInView.length > 0 && state.didContainersLayout && !dataChanged) {
1715
+ targetId = idsInView.find((id) => indexByKey.get(id) !== void 0);
1716
+ }
1717
+ if (dataChanged && idsInView.length > 0 && state.didContainersLayout) {
1718
+ for (let i = 0; i < idsInView.length; i++) {
1719
+ const id = idsInView[i];
1720
+ const index = indexByKey.get(id);
1721
+ if (index !== void 0) {
1722
+ const position = positions[index];
1723
+ if (position !== void 0) {
1724
+ idsInViewWithPositions.push({ id, position });
1725
+ }
1726
+ }
1727
+ }
1728
+ }
1729
+ if (targetId !== void 0 && prevPosition === void 0) {
1730
+ const targetIndex = indexByKey.get(targetId);
1731
+ if (targetIndex !== void 0) {
1732
+ prevPosition = positions[targetIndex];
1733
+ }
1734
+ }
1735
+ return () => {
1736
+ let positionDiff = 0;
1737
+ let anchorIdForLock = anchorLock == null ? void 0 : anchorLock.id;
1738
+ let anchorPositionForLock;
1739
+ let skipTargetAnchor = false;
1740
+ const data = state.props.data;
1741
+ const shouldValidateLockedAnchor = isWeb && dataChanged && mvcpData && scrollTarget === void 0 && targetId !== void 0 && (anchorLock == null ? void 0 : anchorLock.id) === targetId && shouldRestorePosition !== void 0;
1742
+ if (shouldValidateLockedAnchor && targetId !== void 0) {
1743
+ const index = indexByKey.get(targetId);
1744
+ if (index !== void 0) {
1745
+ const item = data[index];
1746
+ skipTargetAnchor = item === void 0 || !shouldRestorePosition(item, index, data);
1747
+ if (skipTargetAnchor && (anchorLock == null ? void 0 : anchorLock.id) === targetId) {
1748
+ state.mvcpAnchorLock = void 0;
1749
+ }
1750
+ }
1751
+ }
1752
+ const shouldUseFallbackVisibleAnchor = dataChanged && mvcpData && scrollTarget === void 0 && (() => {
1753
+ if (targetId === void 0 || skipTargetAnchor) {
1754
+ return true;
1755
+ }
1756
+ const targetIndex = indexByKey.get(targetId);
1757
+ return targetIndex === void 0 || positions[targetIndex] === void 0;
1758
+ })();
1759
+ if (shouldUseFallbackVisibleAnchor) {
1760
+ for (let i = 0; i < idsInViewWithPositions.length; i++) {
1761
+ const { id, position } = idsInViewWithPositions[i];
1762
+ const index = indexByKey.get(id);
1763
+ if (index !== void 0 && shouldRestorePosition) {
1764
+ const item = data[index];
1765
+ if (item === void 0 || !shouldRestorePosition(item, index, data)) {
1766
+ continue;
1767
+ }
1768
+ }
1769
+ const newPosition = index !== void 0 ? positions[index] : void 0;
1770
+ if (newPosition !== void 0) {
1771
+ positionDiff = newPosition - position;
1772
+ anchorIdForLock = id;
1773
+ anchorPositionForLock = newPosition;
1774
+ break;
1775
+ }
1776
+ }
1777
+ }
1778
+ if (!skipTargetAnchor && targetId !== void 0 && prevPosition !== void 0) {
1779
+ const targetIndex = indexByKey.get(targetId);
1780
+ const newPosition = targetIndex !== void 0 ? positions[targetIndex] : void 0;
1781
+ if (newPosition !== void 0) {
1782
+ const totalSize = getContentSize(ctx);
1783
+ let diff = newPosition - prevPosition;
1784
+ if (diff !== 0 && isEndAnchoredScrollTarget && state.scroll + state.scrollLength > totalSize) {
1785
+ if (diff > 0) {
1786
+ diff = Math.max(0, totalSize - state.scroll - state.scrollLength);
1787
+ } else {
1788
+ diff = 0;
1789
+ }
1790
+ }
1791
+ positionDiff = diff;
1792
+ anchorIdForLock = targetId;
1793
+ anchorPositionForLock = newPosition;
1794
+ }
1795
+ }
1796
+ if (scrollingToViewPosition && scrollingToViewPosition > 0) {
1797
+ const newSize = getItemSize(ctx, targetId, scrollTarget, state.props.data[scrollTarget]);
1798
+ const prevSize = scrollingTo == null ? void 0 : scrollingTo.itemSize;
1799
+ if (newSize !== void 0 && prevSize !== void 0 && newSize !== prevSize) {
1800
+ const diff = newSize - prevSize;
1801
+ if (diff !== 0) {
1802
+ positionDiff += diff * scrollingToViewPosition;
1803
+ scrollingTo.itemSize = newSize;
1804
+ }
1805
+ }
1806
+ }
1807
+ updateAnchorLock(state, {
1808
+ anchorId: anchorIdForLock,
1809
+ anchorPosition: anchorPositionForLock,
1810
+ dataChanged,
1811
+ now,
1812
+ positionDiff
1813
+ });
1814
+ if (shouldQueueNativeMVCPAdjust(dataChanged, state, positionDiff, prevTotalSize, prevScroll, scrollTarget)) {
1815
+ state.pendingNativeMVCPAdjust = {
1816
+ amount: positionDiff,
1817
+ closestDistanceToClamp: Math.abs(
1818
+ prevScroll - Math.max(0, getContentSize(ctx) - state.scrollLength)
1819
+ ),
1820
+ hasApproachedClamp: false,
1821
+ manualApplied: 0,
1822
+ startScroll: prevScroll
1823
+ };
1824
+ maybeApplyPredictedNativeMVCPAdjust(ctx);
1825
+ return;
1826
+ }
1827
+ if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
1828
+ requestAdjust(ctx, positionDiff, dataChanged && mvcpData);
1829
+ }
1830
+ };
1831
+ }
1832
+ }
1833
+
1834
+ // src/platform/flushSync.native.ts
1835
+ var flushSync = (fn) => {
1836
+ fn();
1837
+ };
1838
+
1839
+ // src/core/updateScroll.ts
1840
+ function updateScroll(ctx, newScroll, forceUpdate) {
1841
+ var _a3;
1842
+ const state = ctx.state;
1843
+ const { ignoreScrollFromMVCP, lastScrollAdjustForHistory, scrollAdjustHandler, scrollHistory, scrollingTo } = state;
1844
+ const prevScroll = state.scroll;
1845
+ state.hasScrolled = true;
1846
+ state.lastBatchingAction = Date.now();
1847
+ const currentTime = Date.now();
1848
+ const adjust = scrollAdjustHandler.getAdjust();
1849
+ const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
1850
+ if (adjustChanged) {
1851
+ scrollHistory.length = 0;
1852
+ }
1853
+ state.lastScrollAdjustForHistory = adjust;
1854
+ if (scrollingTo === void 0 && !(scrollHistory.length === 0 && newScroll === state.scroll)) {
1855
+ if (!adjustChanged) {
1856
+ scrollHistory.push({ scroll: newScroll, time: currentTime });
1857
+ }
1858
+ }
1859
+ if (scrollHistory.length > 5) {
1860
+ scrollHistory.shift();
1861
+ }
1862
+ if (ignoreScrollFromMVCP && !scrollingTo) {
1863
+ const { lt, gt } = ignoreScrollFromMVCP;
1864
+ if (lt && newScroll < lt || gt && newScroll > gt) {
1865
+ state.ignoreScrollFromMVCPIgnored = true;
1866
+ return;
1867
+ }
1868
+ }
1869
+ state.scrollPrev = prevScroll;
1870
+ state.scrollPrevTime = state.scrollTime;
1871
+ state.scroll = newScroll;
1872
+ state.scrollTime = currentTime;
1873
+ const scrollDelta = Math.abs(newScroll - prevScroll);
1874
+ const didResolvePendingNativeMVCPAdjust = resolvePendingNativeMVCPAdjust(ctx, newScroll);
1875
+ const scrollLength = state.scrollLength;
1876
+ const lastCalculated = state.scrollLastCalculate;
1877
+ const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
1878
+ const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
1879
+ if (shouldUpdate) {
1880
+ state.scrollLastCalculate = state.scroll;
1881
+ state.ignoreScrollFromMVCPIgnored = false;
1882
+ state.lastScrollDelta = scrollDelta;
1883
+ const runCalculateItems = () => {
1884
+ var _a4;
1885
+ (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { doMVCP: scrollingTo !== void 0 });
1886
+ checkThresholds(ctx);
1887
+ };
1888
+ if (Platform2.OS === "web" && scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength) {
1889
+ flushSync(runCalculateItems);
1890
+ } else {
1891
+ runCalculateItems();
1892
+ }
1893
+ const shouldMaintainScrollAtEndAfterPendingSettle = !!state.pendingMaintainScrollAtEnd || !!((_a3 = state.props.maintainScrollAtEnd) == null ? void 0 : _a3.onDataChange);
1894
+ if (didResolvePendingNativeMVCPAdjust && shouldMaintainScrollAtEndAfterPendingSettle) {
1895
+ state.pendingMaintainScrollAtEnd = false;
1896
+ doMaintainScrollAtEnd(ctx);
1897
+ }
1274
1898
  state.dataChangeNeedsScrollUpdate = false;
1899
+ state.lastScrollDelta = 0;
1275
1900
  }
1276
1901
  }
1277
1902
 
1278
1903
  // src/utils/requestAdjust.ts
1279
- function requestAdjust(ctx, state, positionDiff, dataChanged) {
1904
+ function requestAdjust(ctx, positionDiff, dataChanged) {
1905
+ const state = ctx.state;
1280
1906
  if (Math.abs(positionDiff) > 0.1) {
1281
1907
  const needsScrollWorkaround = Platform2.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
1282
1908
  const doit = () => {
1283
1909
  if (needsScrollWorkaround) {
1284
- scrollTo(ctx, state, {
1910
+ scrollTo(ctx, {
1285
1911
  noScrollingTo: true,
1286
1912
  offset: state.scroll
1287
1913
  });
@@ -1294,8 +1920,8 @@ function requestAdjust(ctx, state, positionDiff, dataChanged) {
1294
1920
  };
1295
1921
  state.scroll += positionDiff;
1296
1922
  state.scrollForNextCalculateItemsInView = void 0;
1297
- const didLayout = peek$(ctx, "containersDidLayout");
1298
- if (didLayout) {
1923
+ const readyToRender = peek$(ctx, "readyToRender");
1924
+ if (readyToRender) {
1299
1925
  doit();
1300
1926
  if (Platform2.OS !== "web") {
1301
1927
  const threshold = state.scroll - positionDiff / 2;
@@ -1317,7 +1943,7 @@ function requestAdjust(ctx, state, positionDiff, dataChanged) {
1317
1943
  if (shouldForceUpdate) {
1318
1944
  state.ignoreScrollFromMVCPIgnored = false;
1319
1945
  state.scrollPending = state.scroll;
1320
- updateScroll(ctx, state, state.scroll, true);
1946
+ updateScroll(ctx, state.scroll, true);
1321
1947
  }
1322
1948
  }, delay);
1323
1949
  }
@@ -1332,30 +1958,33 @@ function requestAdjust(ctx, state, positionDiff, dataChanged) {
1332
1958
  var INITIAL_ANCHOR_TOLERANCE = 0.5;
1333
1959
  var INITIAL_ANCHOR_MAX_ATTEMPTS = 4;
1334
1960
  var INITIAL_ANCHOR_SETTLED_TICKS = 2;
1335
- function ensureInitialAnchor(ctx, state) {
1336
- var _a3, _b, _c, _d, _e;
1337
- const anchor = state.initialAnchor;
1961
+ function ensureInitialAnchor(ctx) {
1962
+ var _a3, _b, _c, _d, _e, _f;
1963
+ const state = ctx.state;
1964
+ const { initialAnchor, didContainersLayout, scroll, scrollLength } = state;
1965
+ const anchor = initialAnchor;
1966
+ if (state.initialScroll || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll)) {
1967
+ return;
1968
+ }
1338
1969
  const item = state.props.data[anchor.index];
1339
- const containersDidLayout = peek$(ctx, "containersDidLayout");
1340
- if (!containersDidLayout) {
1970
+ if (!didContainersLayout) {
1341
1971
  return;
1342
1972
  }
1343
1973
  const id = getId(state, anchor.index);
1344
- if (state.positions.get(id) === void 0) {
1974
+ if (state.positions[anchor.index] === void 0) {
1345
1975
  return;
1346
1976
  }
1347
- const size = getItemSize(ctx, state, id, anchor.index, item, true, true);
1977
+ const size = getItemSize(ctx, id, anchor.index, item, true, true);
1348
1978
  if (size === void 0) {
1349
1979
  return;
1350
1980
  }
1351
- const availableSpace = Math.max(0, state.scrollLength - size);
1352
- const desiredOffset = calculateOffsetForIndex(ctx, state, anchor.index) - ((_a3 = anchor.viewOffset) != null ? _a3 : 0) - ((_b = anchor.viewPosition) != null ? _b : 0) * availableSpace;
1353
- const contentSize = getContentSize(ctx);
1354
- const maxOffset = Math.max(0, contentSize - state.scrollLength);
1355
- const clampedDesiredOffset = Math.max(0, Math.min(desiredOffset, maxOffset));
1356
- const delta = clampedDesiredOffset - state.scroll;
1981
+ const availableSpace = Math.max(0, scrollLength - size);
1982
+ const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
1983
+ const desiredOffset = calculateOffsetForIndex(ctx, anchor.index) + topOffsetAdjustment - ((_b = anchor.viewOffset) != null ? _b : 0) - ((_c = anchor.viewPosition) != null ? _c : 0) * availableSpace;
1984
+ const clampedDesiredOffset = clampScrollOffset(ctx, desiredOffset, anchor);
1985
+ const delta = clampedDesiredOffset - scroll;
1357
1986
  if (Math.abs(delta) <= INITIAL_ANCHOR_TOLERANCE) {
1358
- const settledTicks = ((_c = anchor.settledTicks) != null ? _c : 0) + 1;
1987
+ const settledTicks = ((_d = anchor.settledTicks) != null ? _d : 0) + 1;
1359
1988
  if (settledTicks >= INITIAL_ANCHOR_SETTLED_TICKS) {
1360
1989
  state.initialAnchor = void 0;
1361
1990
  } else {
@@ -1363,7 +1992,7 @@ function ensureInitialAnchor(ctx, state) {
1363
1992
  }
1364
1993
  return;
1365
1994
  }
1366
- if (((_d = anchor.attempts) != null ? _d : 0) >= INITIAL_ANCHOR_MAX_ATTEMPTS) {
1995
+ if (((_e = anchor.attempts) != null ? _e : 0) >= INITIAL_ANCHOR_MAX_ATTEMPTS) {
1367
1996
  state.initialAnchor = void 0;
1368
1997
  return;
1369
1998
  }
@@ -1373,98 +2002,30 @@ function ensureInitialAnchor(ctx, state) {
1373
2002
  return;
1374
2003
  }
1375
2004
  Object.assign(anchor, {
1376
- attempts: ((_e = anchor.attempts) != null ? _e : 0) + 1,
2005
+ attempts: ((_f = anchor.attempts) != null ? _f : 0) + 1,
1377
2006
  lastDelta: delta,
1378
2007
  settledTicks: 0
1379
2008
  });
1380
- requestAdjust(ctx, state, delta);
1381
- }
1382
-
1383
- // src/core/mvcp.ts
1384
- function prepareMVCP(ctx, state, dataChanged) {
1385
- const { idsInView, positions, props } = state;
1386
- const { maintainVisibleContentPosition } = props;
1387
- const scrollingTo = peek$(ctx, "scrollingTo");
1388
- let prevPosition;
1389
- let targetId;
1390
- const idsInViewWithPositions = [];
1391
- const scrollTarget = scrollingTo == null ? void 0 : scrollingTo.index;
1392
- const shouldMVCP = !dataChanged || maintainVisibleContentPosition;
1393
- const indexByKey = state.indexByKey;
1394
- if (shouldMVCP) {
1395
- if (scrollTarget !== void 0) {
1396
- if (!IsNewArchitecture && (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll)) {
1397
- return void 0;
1398
- }
1399
- targetId = getId(state, scrollTarget);
1400
- } else if (idsInView.length > 0 && peek$(ctx, "containersDidLayout")) {
1401
- if (dataChanged) {
1402
- for (let i = 0; i < idsInView.length; i++) {
1403
- const id = idsInView[i];
1404
- const index = indexByKey.get(id);
1405
- if (index !== void 0) {
1406
- idsInViewWithPositions.push({ id, position: positions.get(id) });
1407
- }
1408
- }
1409
- } else {
1410
- targetId = idsInView.find((id) => indexByKey.get(id) !== void 0);
1411
- }
1412
- }
1413
- if (targetId !== void 0) {
1414
- prevPosition = positions.get(targetId);
1415
- }
1416
- return () => {
1417
- let positionDiff;
1418
- if (dataChanged && targetId === void 0 && maintainVisibleContentPosition) {
1419
- for (let i = 0; i < idsInViewWithPositions.length; i++) {
1420
- const { id, position } = idsInViewWithPositions[i];
1421
- const newPosition = positions.get(id);
1422
- if (newPosition !== void 0) {
1423
- positionDiff = newPosition - position;
1424
- break;
1425
- }
1426
- }
1427
- }
1428
- if (targetId !== void 0 && prevPosition !== void 0) {
1429
- const newPosition = positions.get(targetId);
1430
- if (newPosition !== void 0) {
1431
- const totalSize = getContentSize(ctx);
1432
- let diff = newPosition - prevPosition;
1433
- if (diff !== 0 && state.scroll + state.scrollLength > totalSize) {
1434
- if (diff > 0) {
1435
- diff = Math.max(0, totalSize - state.scroll - state.scrollLength);
1436
- } else {
1437
- diff = 0;
1438
- }
1439
- }
1440
- positionDiff = diff;
1441
- }
1442
- }
1443
- if (positionDiff !== void 0 && Math.abs(positionDiff) > 0.1) {
1444
- requestAdjust(ctx, state, positionDiff, dataChanged && maintainVisibleContentPosition);
1445
- }
1446
- };
1447
- }
2009
+ requestAdjust(ctx, delta);
1448
2010
  }
1449
2011
 
1450
2012
  // src/core/prepareColumnStartState.ts
1451
- function prepareColumnStartState(ctx, state, startIndex, useAverageSize) {
2013
+ function prepareColumnStartState(ctx, startIndex, useAverageSize) {
1452
2014
  var _a3;
2015
+ const state = ctx.state;
1453
2016
  const numColumns = peek$(ctx, "numColumns");
1454
2017
  let rowStartIndex = startIndex;
1455
- const columnAtStart = state.columns.get(state.idCache[startIndex]);
2018
+ const columnAtStart = state.columns[startIndex];
1456
2019
  if (columnAtStart !== 1) {
1457
2020
  rowStartIndex = findRowStartIndex(state, numColumns, startIndex);
1458
2021
  }
1459
2022
  let currentRowTop = 0;
1460
- const curId = state.idCache[rowStartIndex];
1461
- const column = state.columns.get(curId);
2023
+ const column = state.columns[rowStartIndex];
1462
2024
  if (rowStartIndex > 0) {
1463
2025
  const prevIndex = rowStartIndex - 1;
1464
- const prevId = state.idCache[prevIndex];
1465
- const prevPosition = (_a3 = state.positions.get(prevId)) != null ? _a3 : 0;
2026
+ const prevPosition = (_a3 = state.positions[prevIndex]) != null ? _a3 : 0;
1466
2027
  const prevRowStart = findRowStartIndex(state, numColumns, prevIndex);
1467
- const prevRowHeight = calculateRowMaxSize(ctx, state, prevRowStart, prevIndex, useAverageSize);
2028
+ const prevRowHeight = calculateRowMaxSize(ctx, prevRowStart, prevIndex, useAverageSize);
1468
2029
  currentRowTop = prevPosition + prevRowHeight;
1469
2030
  }
1470
2031
  return {
@@ -1479,7 +2040,7 @@ function findRowStartIndex(state, numColumns, index) {
1479
2040
  }
1480
2041
  let rowStart = Math.max(0, index);
1481
2042
  while (rowStart > 0) {
1482
- const columnForIndex = state.columns.get(state.idCache[rowStart]);
2043
+ const columnForIndex = state.columns[rowStart];
1483
2044
  if (columnForIndex === 1) {
1484
2045
  break;
1485
2046
  }
@@ -1487,7 +2048,8 @@ function findRowStartIndex(state, numColumns, index) {
1487
2048
  }
1488
2049
  return rowStart;
1489
2050
  }
1490
- function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
2051
+ function calculateRowMaxSize(ctx, startIndex, endIndex, useAverageSize) {
2052
+ const state = ctx.state;
1491
2053
  if (endIndex < startIndex) {
1492
2054
  return 0;
1493
2055
  }
@@ -1501,7 +2063,7 @@ function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1501
2063
  continue;
1502
2064
  }
1503
2065
  const id = state.idCache[i];
1504
- const size = getItemSize(ctx, state, id, i, data[i], useAverageSize);
2066
+ const size = getItemSize(ctx, id, i, data[i], useAverageSize);
1505
2067
  if (size > maxSize) {
1506
2068
  maxSize = size;
1507
2069
  }
@@ -1510,22 +2072,44 @@ function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1510
2072
  }
1511
2073
 
1512
2074
  // src/core/updateTotalSize.ts
1513
- function updateTotalSize(ctx, state) {
2075
+ function updateTotalSize(ctx) {
2076
+ var _a3, _b;
2077
+ const state = ctx.state;
1514
2078
  const {
1515
2079
  positions,
1516
2080
  props: { data }
1517
2081
  } = state;
2082
+ const numColumns = (_a3 = peek$(ctx, "numColumns")) != null ? _a3 : 1;
1518
2083
  if (data.length === 0) {
1519
- addTotalSize(ctx, state, null, 0);
2084
+ addTotalSize(ctx, null, 0);
1520
2085
  } else {
1521
- const lastId = getId(state, data.length - 1);
1522
- if (lastId !== void 0) {
1523
- const lastPosition = positions.get(lastId);
1524
- if (lastPosition !== void 0) {
1525
- const lastSize = getItemSize(ctx, state, lastId, data.length - 1, data[data.length - 1]);
2086
+ const lastIndex = data.length - 1;
2087
+ const lastId = getId(state, lastIndex);
2088
+ const lastPosition = positions[lastIndex];
2089
+ if (lastId !== void 0 && lastPosition !== void 0) {
2090
+ if (numColumns > 1) {
2091
+ let rowStart = lastIndex;
2092
+ while (rowStart > 0) {
2093
+ const column = state.columns[rowStart];
2094
+ if (column === 1 || column === void 0) {
2095
+ break;
2096
+ }
2097
+ rowStart -= 1;
2098
+ }
2099
+ let maxSize = 0;
2100
+ for (let i = rowStart; i <= lastIndex; i++) {
2101
+ const rowId = (_b = state.idCache[i]) != null ? _b : getId(state, i);
2102
+ const size = getItemSize(ctx, rowId, i, data[i]);
2103
+ if (size > maxSize) {
2104
+ maxSize = size;
2105
+ }
2106
+ }
2107
+ addTotalSize(ctx, null, lastPosition + maxSize);
2108
+ } else {
2109
+ const lastSize = getItemSize(ctx, lastId, lastIndex, data[lastIndex]);
1526
2110
  if (lastSize !== void 0) {
1527
2111
  const totalSize = lastPosition + lastSize;
1528
- addTotalSize(ctx, state, null, totalSize);
2112
+ addTotalSize(ctx, null, totalSize);
1529
2113
  }
1530
2114
  }
1531
2115
  }
@@ -1535,90 +2119,107 @@ function updateTotalSize(ctx, state) {
1535
2119
  // src/utils/getScrollVelocity.ts
1536
2120
  var getScrollVelocity = (state) => {
1537
2121
  const { scrollHistory } = state;
1538
- let velocity = 0;
1539
- if (scrollHistory.length >= 1) {
1540
- const newest = scrollHistory[scrollHistory.length - 1];
1541
- let oldest;
1542
- let start = 0;
1543
- const now = Date.now();
1544
- for (let i = 0; i < scrollHistory.length - 1; i++) {
1545
- const entry = scrollHistory[i];
1546
- const nextEntry = scrollHistory[i + 1];
1547
- if (i > 0) {
1548
- const prevEntry = scrollHistory[i - 1];
1549
- const prevDirection = entry.scroll - prevEntry.scroll;
1550
- const currentDirection = nextEntry.scroll - entry.scroll;
1551
- if (prevDirection > 0 && currentDirection < 0 || prevDirection < 0 && currentDirection > 0) {
1552
- start = i;
1553
- break;
1554
- }
1555
- }
2122
+ const newestIndex = scrollHistory.length - 1;
2123
+ if (newestIndex < 1) {
2124
+ return 0;
2125
+ }
2126
+ const newest = scrollHistory[newestIndex];
2127
+ const now = Date.now();
2128
+ let direction = 0;
2129
+ for (let i = newestIndex; i > 0; i--) {
2130
+ const delta = scrollHistory[i].scroll - scrollHistory[i - 1].scroll;
2131
+ if (delta !== 0) {
2132
+ direction = Math.sign(delta);
2133
+ break;
1556
2134
  }
1557
- for (let i = start; i < scrollHistory.length - 1; i++) {
1558
- const entry = scrollHistory[i];
1559
- if (now - entry.time <= 1e3) {
1560
- oldest = entry;
1561
- break;
1562
- }
2135
+ }
2136
+ if (direction === 0) {
2137
+ return 0;
2138
+ }
2139
+ let oldest = newest;
2140
+ for (let i = newestIndex - 1; i >= 0; i--) {
2141
+ const current = scrollHistory[i];
2142
+ const next = scrollHistory[i + 1];
2143
+ const delta = next.scroll - current.scroll;
2144
+ const deltaSign = Math.sign(delta);
2145
+ if (deltaSign !== 0 && deltaSign !== direction) {
2146
+ break;
1563
2147
  }
1564
- if (oldest && oldest !== newest) {
1565
- const scrollDiff = newest.scroll - oldest.scroll;
1566
- const timeDiff = newest.time - oldest.time;
1567
- velocity = timeDiff > 0 ? scrollDiff / timeDiff : 0;
2148
+ if (now - current.time > 1e3) {
2149
+ break;
1568
2150
  }
2151
+ oldest = current;
1569
2152
  }
1570
- return velocity;
2153
+ const scrollDiff = newest.scroll - oldest.scroll;
2154
+ const timeDiff = newest.time - oldest.time;
2155
+ return timeDiff > 0 ? scrollDiff / timeDiff : 0;
1571
2156
  };
1572
2157
 
1573
2158
  // src/utils/updateSnapToOffsets.ts
1574
- function updateSnapToOffsets(ctx, state) {
2159
+ function updateSnapToOffsets(ctx) {
2160
+ const state = ctx.state;
1575
2161
  const {
1576
- positions,
1577
2162
  props: { snapToIndices }
1578
2163
  } = state;
1579
2164
  const snapToOffsets = Array(snapToIndices.length);
1580
2165
  for (let i = 0; i < snapToIndices.length; i++) {
1581
2166
  const idx = snapToIndices[i];
1582
- const key = getId(state, idx);
1583
- snapToOffsets[i] = positions.get(key);
2167
+ getId(state, idx);
2168
+ snapToOffsets[i] = state.positions[idx];
1584
2169
  }
1585
2170
  set$(ctx, "snapToOffsets", snapToOffsets);
1586
2171
  }
1587
2172
 
1588
2173
  // src/core/updateItemPositions.ts
1589
- function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP } = {
2174
+ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP } = {
1590
2175
  doMVCP: false,
1591
2176
  forceFullUpdate: false,
1592
2177
  scrollBottomBuffered: -1,
1593
2178
  startIndex: 0
1594
2179
  }) {
1595
2180
  var _a3, _b, _c, _d, _e;
2181
+ const state = ctx.state;
2182
+ const hasPositionListeners = ctx.positionListeners.size > 0;
1596
2183
  const {
1597
2184
  columns,
2185
+ columnSpans,
1598
2186
  indexByKey,
1599
2187
  positions,
1600
2188
  idCache,
1601
2189
  sizesKnown,
1602
- props: { getEstimatedItemSize, snapToIndices, enableAverages }
2190
+ props: { data, getEstimatedItemSize, overrideItemLayout, snapToIndices },
2191
+ scrollingTo
1603
2192
  } = state;
1604
- const data = state.props.data;
1605
2193
  const dataLength = data.length;
1606
- const numColumns = peek$(ctx, "numColumns");
1607
- const scrollingTo = peek$(ctx, "scrollingTo");
2194
+ const numColumns = (_a3 = peek$(ctx, "numColumns")) != null ? _a3 : 1;
1608
2195
  const hasColumns = numColumns > 1;
1609
2196
  const indexByKeyForChecking = IS_DEV ? /* @__PURE__ */ new Map() : void 0;
1610
- const shouldOptimize = !forceFullUpdate && !dataChanged && Math.abs(getScrollVelocity(state)) > 0;
2197
+ const extraData = peek$(ctx, "extraData");
2198
+ const layoutConfig = overrideItemLayout ? { span: 1 } : void 0;
2199
+ const lastScrollDelta = state.lastScrollDelta;
2200
+ const velocity = getScrollVelocity(state);
2201
+ const shouldOptimize = !forceFullUpdate && !dataChanged && (Math.abs(velocity) > 0 || Platform2.OS === "web" && state.scrollLength > 0 && lastScrollDelta > state.scrollLength);
1611
2202
  const maxVisibleArea = scrollBottomBuffered + 1e3;
1612
- const useAverageSize = enableAverages && !getEstimatedItemSize;
1613
- const preferCachedSize = !doMVCP || dataChanged || state.scrollAdjustHandler.getAdjust() !== 0 || ((_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0) !== 0;
2203
+ const useAverageSize = !getEstimatedItemSize;
2204
+ const preferCachedSize = !doMVCP || dataChanged || state.scrollAdjustHandler.getAdjust() !== 0 || ((_b = peek$(ctx, "scrollAdjustPending")) != null ? _b : 0) !== 0;
1614
2205
  let currentRowTop = 0;
1615
2206
  let column = 1;
1616
2207
  let maxSizeInRow = 0;
2208
+ if (dataChanged) {
2209
+ columnSpans.length = 0;
2210
+ }
2211
+ if (!hasColumns) {
2212
+ if (columns.length) {
2213
+ columns.length = 0;
2214
+ }
2215
+ if (columnSpans.length) {
2216
+ columnSpans.length = 0;
2217
+ }
2218
+ }
1617
2219
  if (startIndex > 0) {
1618
2220
  if (hasColumns) {
1619
2221
  const { startIndex: processedStartIndex, currentRowTop: initialRowTop } = prepareColumnStartState(
1620
2222
  ctx,
1621
- state,
1622
2223
  startIndex,
1623
2224
  useAverageSize
1624
2225
  );
@@ -1627,12 +2228,13 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1627
2228
  } else if (startIndex < dataLength) {
1628
2229
  const prevIndex = startIndex - 1;
1629
2230
  const prevId = getId(state, prevIndex);
1630
- const prevPosition = (_b = positions.get(prevId)) != null ? _b : 0;
1631
- const prevSize = (_c = sizesKnown.get(prevId)) != null ? _c : getItemSize(ctx, state, prevId, prevIndex, data[prevIndex], useAverageSize, preferCachedSize);
2231
+ const prevPosition = (_c = positions[prevIndex]) != null ? _c : 0;
2232
+ const prevSize = (_d = sizesKnown.get(prevId)) != null ? _d : getItemSize(ctx, prevId, prevIndex, data[prevIndex], useAverageSize, preferCachedSize);
1632
2233
  currentRowTop = prevPosition + prevSize;
1633
2234
  }
1634
2235
  }
1635
2236
  const needsIndexByKey = dataChanged || indexByKey.size === 0;
2237
+ const canOverrideSpan = hasColumns && !!overrideItemLayout && !!layoutConfig;
1636
2238
  let didBreakEarly = false;
1637
2239
  let breakAt;
1638
2240
  for (let i = startIndex; i < dataLength; i++) {
@@ -1644,8 +2246,23 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1644
2246
  const itemsPerRow = hasColumns ? numColumns : 1;
1645
2247
  breakAt = i + itemsPerRow + 10;
1646
2248
  }
1647
- const id = (_d = idCache[i]) != null ? _d : getId(state, i);
1648
- const size = (_e = sizesKnown.get(id)) != null ? _e : getItemSize(ctx, state, id, i, data[i], useAverageSize, preferCachedSize);
2249
+ const id = (_e = idCache[i]) != null ? _e : getId(state, i);
2250
+ let span = 1;
2251
+ if (canOverrideSpan) {
2252
+ layoutConfig.span = 1;
2253
+ overrideItemLayout(layoutConfig, data[i], i, numColumns, extraData);
2254
+ const requestedSpan = layoutConfig.span;
2255
+ if (requestedSpan !== void 0 && Number.isFinite(requestedSpan)) {
2256
+ span = Math.max(1, Math.min(numColumns, Math.round(requestedSpan)));
2257
+ }
2258
+ }
2259
+ if (hasColumns && column + span - 1 > numColumns) {
2260
+ currentRowTop += maxSizeInRow;
2261
+ column = 1;
2262
+ maxSizeInRow = 0;
2263
+ }
2264
+ const knownSize = sizesKnown.get(id);
2265
+ const size = knownSize !== void 0 ? knownSize : getItemSize(ctx, id, i, data[i], useAverageSize, preferCachedSize);
1649
2266
  if (IS_DEV && needsIndexByKey) {
1650
2267
  if (indexByKeyForChecking.has(id)) {
1651
2268
  console.error(
@@ -1654,30 +2271,36 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1654
2271
  }
1655
2272
  indexByKeyForChecking.set(id, i);
1656
2273
  }
1657
- positions.set(id, currentRowTop);
2274
+ if (currentRowTop !== positions[i]) {
2275
+ positions[i] = currentRowTop;
2276
+ if (hasPositionListeners) {
2277
+ notifyPosition$(ctx, id, currentRowTop);
2278
+ }
2279
+ }
1658
2280
  if (needsIndexByKey) {
1659
2281
  indexByKey.set(id, i);
1660
2282
  }
1661
- columns.set(id, column);
1662
- if (hasColumns) {
2283
+ if (!hasColumns) {
2284
+ currentRowTop += size;
2285
+ } else {
2286
+ columns[i] = column;
2287
+ columnSpans[i] = span;
1663
2288
  if (size > maxSizeInRow) {
1664
2289
  maxSizeInRow = size;
1665
2290
  }
1666
- column++;
2291
+ column += span;
1667
2292
  if (column > numColumns) {
1668
2293
  currentRowTop += maxSizeInRow;
1669
2294
  column = 1;
1670
2295
  maxSizeInRow = 0;
1671
2296
  }
1672
- } else {
1673
- currentRowTop += size;
1674
2297
  }
1675
2298
  }
1676
2299
  if (!didBreakEarly) {
1677
- updateTotalSize(ctx, state);
2300
+ updateTotalSize(ctx);
1678
2301
  }
1679
2302
  if (snapToIndices) {
1680
- updateSnapToOffsets(ctx, state);
2303
+ updateSnapToOffsets(ctx);
1681
2304
  }
1682
2305
  }
1683
2306
 
@@ -1755,7 +2378,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
1755
2378
  if (previousViewableItems) {
1756
2379
  for (const viewToken of previousViewableItems) {
1757
2380
  const containerId = findContainerId(ctx, viewToken.key);
1758
- if (!isViewable(
2381
+ if (!checkIsViewable(
1759
2382
  state,
1760
2383
  ctx,
1761
2384
  viewabilityConfig,
@@ -1776,7 +2399,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
1776
2399
  if (item) {
1777
2400
  const key = getId(state, i);
1778
2401
  const containerId = findContainerId(ctx, key);
1779
- if (isViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, i)) {
2402
+ if (checkIsViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, i)) {
1780
2403
  const viewToken = {
1781
2404
  containerId,
1782
2405
  index: i,
@@ -1822,25 +2445,49 @@ function shallowEqual(prev, next) {
1822
2445
  return true;
1823
2446
  }
1824
2447
  function computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
1825
- const { sizes, positions, scroll: scrollState } = state;
2448
+ const { sizes, scroll: scrollState } = state;
1826
2449
  const topPad = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
1827
2450
  const { itemVisiblePercentThreshold, viewAreaCoveragePercentThreshold } = viewabilityConfig;
1828
2451
  const viewAreaMode = viewAreaCoveragePercentThreshold != null;
1829
2452
  const viewablePercentThreshold = viewAreaMode ? viewAreaCoveragePercentThreshold : itemVisiblePercentThreshold;
1830
2453
  const scroll = scrollState - topPad;
1831
- const top = positions.get(key) - scroll;
2454
+ const position = state.positions[index];
1832
2455
  const size = sizes.get(key) || 0;
2456
+ if (position === void 0) {
2457
+ const value2 = {
2458
+ containerId,
2459
+ index,
2460
+ isViewable: false,
2461
+ item,
2462
+ key,
2463
+ percentOfScroller: 0,
2464
+ percentVisible: 0,
2465
+ scrollSize,
2466
+ size,
2467
+ sizeVisible: -1
2468
+ };
2469
+ const prev2 = ctx.mapViewabilityAmountValues.get(containerId);
2470
+ if (!shallowEqual(prev2, value2)) {
2471
+ ctx.mapViewabilityAmountValues.set(containerId, value2);
2472
+ const cb = ctx.mapViewabilityAmountCallbacks.get(containerId);
2473
+ if (cb) {
2474
+ cb(value2);
2475
+ }
2476
+ }
2477
+ return value2;
2478
+ }
2479
+ const top = position - scroll;
1833
2480
  const bottom = top + size;
1834
2481
  const isEntirelyVisible = top >= 0 && bottom <= scrollSize && bottom > top;
1835
2482
  const sizeVisible = isEntirelyVisible ? size : Math.min(bottom, scrollSize) - Math.max(top, 0);
1836
2483
  const percentVisible = size ? isEntirelyVisible ? 100 : 100 * (sizeVisible / size) : 0;
1837
2484
  const percentOfScroller = size ? 100 * (sizeVisible / scrollSize) : 0;
1838
2485
  const percent = isEntirelyVisible ? 100 : viewAreaMode ? percentOfScroller : percentVisible;
1839
- const isViewable2 = percent >= viewablePercentThreshold;
2486
+ const isViewable = percent >= viewablePercentThreshold;
1840
2487
  const value = {
1841
2488
  containerId,
1842
2489
  index,
1843
- isViewable: isViewable2,
2490
+ isViewable,
1844
2491
  item,
1845
2492
  key,
1846
2493
  percentOfScroller,
@@ -1859,8 +2506,11 @@ function computeViewability(state, ctx, viewabilityConfig, containerId, key, scr
1859
2506
  }
1860
2507
  return value;
1861
2508
  }
1862
- function isViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
1863
- const value = ctx.mapViewabilityAmountValues.get(containerId) || computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index);
2509
+ function checkIsViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
2510
+ let value = ctx.mapViewabilityAmountValues.get(containerId);
2511
+ if (!value || value.key !== key) {
2512
+ value = computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index);
2513
+ }
1864
2514
  return value.isViewable;
1865
2515
  }
1866
2516
  function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
@@ -1869,6 +2519,8 @@ function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
1869
2519
  const cb = ctx.mapViewabilityCallbacks.get(key);
1870
2520
  cb == null ? void 0 : cb(viewToken);
1871
2521
  }
2522
+ var unstableBatchedUpdates = ReactNative__namespace.unstable_batchedUpdates;
2523
+ var batchedUpdates = typeof unstableBatchedUpdates === "function" ? unstableBatchedUpdates : (fn) => fn();
1872
2524
 
1873
2525
  // src/utils/checkAllSizesKnown.ts
1874
2526
  function isNullOrUndefined2(value) {
@@ -1888,8 +2540,9 @@ function checkAllSizesKnown(state) {
1888
2540
  }
1889
2541
 
1890
2542
  // src/utils/findAvailableContainers.ts
1891
- function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers) {
2543
+ function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers) {
1892
2544
  const numContainers = peek$(ctx, "numContainers");
2545
+ const state = ctx.state;
1893
2546
  const { stickyContainerPool, containerItemTypes } = state;
1894
2547
  const result = [];
1895
2548
  const availableContainers = [];
@@ -2009,103 +2662,145 @@ function comparatorByDistance(a, b) {
2009
2662
  }
2010
2663
 
2011
2664
  // src/core/scrollToIndex.ts
2012
- function scrollToIndex(ctx, state, { index, viewOffset = 0, animated = true, viewPosition }) {
2013
- if (index >= state.props.data.length) {
2014
- index = state.props.data.length - 1;
2665
+ function scrollToIndex(ctx, {
2666
+ index,
2667
+ viewOffset = 0,
2668
+ animated = true,
2669
+ forceScroll,
2670
+ isInitialScroll,
2671
+ viewPosition
2672
+ }) {
2673
+ const state = ctx.state;
2674
+ const { data } = state.props;
2675
+ if (index >= data.length) {
2676
+ index = data.length - 1;
2015
2677
  } else if (index < 0) {
2016
2678
  index = 0;
2017
2679
  }
2018
- const firstIndexOffset = calculateOffsetForIndex(ctx, state, index);
2019
- const isLast = index === state.props.data.length - 1;
2680
+ const firstIndexOffset = calculateOffsetForIndex(ctx, index);
2681
+ const isLast = index === data.length - 1;
2020
2682
  if (isLast && viewPosition === void 0) {
2021
2683
  viewPosition = 1;
2022
2684
  }
2023
2685
  state.scrollForNextCalculateItemsInView = void 0;
2024
- scrollTo(ctx, state, {
2686
+ const targetId = getId(state, index);
2687
+ const itemSize = getItemSize(ctx, targetId, index, state.props.data[index]);
2688
+ scrollTo(ctx, {
2025
2689
  animated,
2690
+ forceScroll,
2026
2691
  index,
2692
+ isInitialScroll,
2693
+ itemSize,
2027
2694
  offset: firstIndexOffset,
2028
2695
  viewOffset,
2029
2696
  viewPosition: viewPosition != null ? viewPosition : 0
2030
2697
  });
2031
2698
  }
2032
2699
 
2700
+ // src/utils/performInitialScroll.ts
2701
+ function performInitialScroll(ctx, params) {
2702
+ var _a3;
2703
+ const { forceScroll, initialScrollUsesOffset, resolvedOffset, target } = params;
2704
+ if (initialScrollUsesOffset || resolvedOffset !== void 0) {
2705
+ scrollTo(ctx, {
2706
+ animated: false,
2707
+ forceScroll,
2708
+ index: initialScrollUsesOffset ? void 0 : target.index,
2709
+ isInitialScroll: true,
2710
+ offset: (_a3 = resolvedOffset != null ? resolvedOffset : target.contentOffset) != null ? _a3 : 0,
2711
+ precomputedWithViewOffset: resolvedOffset !== void 0
2712
+ });
2713
+ return;
2714
+ }
2715
+ if (target.index === void 0) {
2716
+ return;
2717
+ }
2718
+ scrollToIndex(ctx, {
2719
+ ...target,
2720
+ animated: false,
2721
+ forceScroll,
2722
+ isInitialScroll: true
2723
+ });
2724
+ }
2725
+
2033
2726
  // src/utils/setDidLayout.ts
2034
- function setDidLayout(ctx, state) {
2035
- const {
2036
- loadStartTime,
2037
- initialScroll,
2038
- props: { onLoad }
2039
- } = state;
2727
+ function setDidLayout(ctx) {
2728
+ const state = ctx.state;
2729
+ const { initialScroll } = state;
2040
2730
  state.queuedInitialLayout = true;
2041
- checkAtBottom(ctx, state);
2042
- const setIt = () => {
2043
- set$(ctx, "containersDidLayout", true);
2044
- if (onLoad) {
2045
- onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
2046
- }
2047
- };
2048
- if (Platform2.OS === "android" && initialScroll) {
2049
- if (IsNewArchitecture) {
2050
- scrollToIndex(ctx, state, { ...initialScroll, animated: false });
2051
- requestAnimationFrame(() => {
2052
- scrollToIndex(ctx, state, { ...initialScroll, animated: false });
2053
- setIt();
2054
- });
2055
- } else {
2056
- scrollToIndex(ctx, state, { ...initialScroll, animated: false });
2057
- setIt();
2058
- }
2059
- } else {
2060
- setIt();
2731
+ checkAtBottom(ctx);
2732
+ if (initialScroll) {
2733
+ const runScroll = () => {
2734
+ var _a3, _b;
2735
+ const target = state.initialScroll;
2736
+ if (!target) {
2737
+ return;
2738
+ }
2739
+ const activeInitialTargetOffset = ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) ? (_b = state.scrollingTo.targetOffset) != null ? _b : state.scrollingTo.offset : void 0;
2740
+ const desiredInitialTargetOffset = state.initialScrollUsesOffset ? target.contentOffset : activeInitialTargetOffset;
2741
+ const isAlreadyAtDesiredInitialTarget = desiredInitialTargetOffset !== void 0 && Math.abs(state.scroll - desiredInitialTargetOffset) <= 1 && Math.abs(state.scrollPending - desiredInitialTargetOffset) <= 1;
2742
+ if (!isAlreadyAtDesiredInitialTarget) {
2743
+ performInitialScroll(ctx, {
2744
+ forceScroll: true,
2745
+ initialScrollUsesOffset: state.initialScrollUsesOffset,
2746
+ // Offset-based initial scrolls do not need item lookup, so they can run even before data exists.
2747
+ // Re-run on the next frame to pick up measured viewport size without waiting for index resolution.
2748
+ target
2749
+ });
2750
+ }
2751
+ };
2752
+ runScroll();
2753
+ requestAnimationFrame(runScroll);
2061
2754
  }
2755
+ setInitialRenderState(ctx, { didLayout: true });
2062
2756
  }
2063
2757
 
2064
2758
  // src/core/calculateItemsInView.ts
2065
2759
  function findCurrentStickyIndex(stickyArray, scroll, state) {
2066
- var _a3;
2067
- const idCache = state.idCache;
2068
2760
  const positions = state.positions;
2069
2761
  for (let i = stickyArray.length - 1; i >= 0; i--) {
2070
2762
  const stickyIndex = stickyArray[i];
2071
- const stickyId = (_a3 = idCache[stickyIndex]) != null ? _a3 : getId(state, stickyIndex);
2072
- const stickyPos = stickyId ? positions.get(stickyId) : void 0;
2763
+ const stickyPos = positions[stickyIndex];
2073
2764
  if (stickyPos !== void 0 && scroll >= stickyPos) {
2074
2765
  return i;
2075
2766
  }
2076
2767
  }
2077
2768
  return -1;
2078
2769
  }
2079
- function getActiveStickyIndices(ctx, state, stickyHeaderIndices) {
2770
+ function getActiveStickyIndices(ctx, stickyHeaderIndices) {
2771
+ const state = ctx.state;
2080
2772
  return new Set(
2081
2773
  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))
2082
2774
  );
2083
2775
  }
2084
- function handleStickyActivation(ctx, state, stickyHeaderIndices, stickyArray, currentStickyIdx, needNewContainers, startBuffered, endBuffered) {
2776
+ function handleStickyActivation(ctx, stickyHeaderIndices, stickyArray, currentStickyIdx, needNewContainers, needNewContainersSet, startBuffered, endBuffered) {
2085
2777
  var _a3;
2086
- const activeIndices = getActiveStickyIndices(ctx, state, stickyHeaderIndices);
2087
- state.activeStickyIndex = currentStickyIdx >= 0 ? stickyArray[currentStickyIdx] : void 0;
2778
+ const state = ctx.state;
2779
+ const activeIndices = getActiveStickyIndices(ctx, stickyHeaderIndices);
2780
+ set$(ctx, "activeStickyIndex", currentStickyIdx >= 0 ? stickyArray[currentStickyIdx] : -1);
2088
2781
  for (let offset = 0; offset <= 1; offset++) {
2089
2782
  const idx = currentStickyIdx - offset;
2090
2783
  if (idx < 0 || activeIndices.has(stickyArray[idx])) continue;
2091
2784
  const stickyIndex = stickyArray[idx];
2092
2785
  const stickyId = (_a3 = state.idCache[stickyIndex]) != null ? _a3 : getId(state, stickyIndex);
2093
- if (stickyId && !state.containerItemKeys.has(stickyId) && (stickyIndex < startBuffered || stickyIndex > endBuffered)) {
2786
+ if (stickyId && !state.containerItemKeys.has(stickyId) && (stickyIndex < startBuffered || stickyIndex > endBuffered) && !needNewContainersSet.has(stickyIndex)) {
2787
+ needNewContainersSet.add(stickyIndex);
2094
2788
  needNewContainers.push(stickyIndex);
2095
2789
  }
2096
2790
  }
2097
2791
  }
2098
- function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, currentStickyIdx, pendingRemoval) {
2099
- var _a3, _b, _c;
2792
+ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentStickyIdx, pendingRemoval, alwaysRenderIndicesSet) {
2793
+ var _a3, _b;
2794
+ const state = ctx.state;
2100
2795
  for (const containerIndex of state.stickyContainerPool) {
2101
2796
  const itemKey = peek$(ctx, `containerItemKey${containerIndex}`);
2102
2797
  const itemIndex = itemKey ? state.indexByKey.get(itemKey) : void 0;
2103
2798
  if (itemIndex === void 0) continue;
2799
+ if (alwaysRenderIndicesSet.has(itemIndex)) continue;
2104
2800
  const arrayIdx = stickyArray.indexOf(itemIndex);
2105
2801
  if (arrayIdx === -1) {
2106
2802
  state.stickyContainerPool.delete(containerIndex);
2107
2803
  set$(ctx, `containerSticky${containerIndex}`, false);
2108
- set$(ctx, `containerStickyOffset${containerIndex}`, void 0);
2109
2804
  continue;
2110
2805
  }
2111
2806
  const isRecentSticky = arrayIdx >= currentStickyIdx - 1 && arrayIdx <= currentStickyIdx + 1;
@@ -2113,15 +2808,14 @@ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, cu
2113
2808
  const nextIndex = stickyArray[arrayIdx + 1];
2114
2809
  let shouldRecycle = false;
2115
2810
  if (nextIndex) {
2116
- const nextId = (_a3 = state.idCache[nextIndex]) != null ? _a3 : getId(state, nextIndex);
2117
- const nextPos = nextId ? state.positions.get(nextId) : void 0;
2118
- shouldRecycle = nextPos !== void 0 && scroll > nextPos + scrollBuffer * 2;
2811
+ const nextPos = state.positions[nextIndex];
2812
+ shouldRecycle = nextPos !== void 0 && scroll > nextPos + drawDistance * 2;
2119
2813
  } else {
2120
- const currentId = (_b = state.idCache[itemIndex]) != null ? _b : getId(state, itemIndex);
2814
+ const currentId = (_a3 = state.idCache[itemIndex]) != null ? _a3 : getId(state, itemIndex);
2121
2815
  if (currentId) {
2122
- const currentPos = state.positions.get(currentId);
2123
- const currentSize = (_c = state.sizes.get(currentId)) != null ? _c : getItemSize(ctx, state, currentId, itemIndex, state.props.data[itemIndex]);
2124
- shouldRecycle = currentPos !== void 0 && scroll > currentPos + currentSize + scrollBuffer * 3;
2816
+ const currentPos = state.positions[itemIndex];
2817
+ const currentSize = (_b = state.sizes.get(currentId)) != null ? _b : getItemSize(ctx, currentId, itemIndex, state.props.data[itemIndex]);
2818
+ shouldRecycle = currentPos !== void 0 && scroll > currentPos + currentSize + drawDistance * 3;
2125
2819
  }
2126
2820
  }
2127
2821
  if (shouldRecycle) {
@@ -2129,11 +2823,13 @@ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, cu
2129
2823
  }
2130
2824
  }
2131
2825
  }
2132
- function calculateItemsInView(ctx, state, params = {}) {
2133
- reactNative.unstable_batchedUpdates(() => {
2134
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j;
2826
+ function calculateItemsInView(ctx, params = {}) {
2827
+ const state = ctx.state;
2828
+ batchedUpdates(() => {
2829
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
2135
2830
  const {
2136
2831
  columns,
2832
+ columnSpans,
2137
2833
  containerItemKeys,
2138
2834
  enableScrollForNextCalculateItemsInView,
2139
2835
  idCache,
@@ -2141,7 +2837,15 @@ function calculateItemsInView(ctx, state, params = {}) {
2141
2837
  initialScroll,
2142
2838
  minIndexSizeChanged,
2143
2839
  positions,
2144
- props: { getItemType, itemsAreEqual, keyExtractor, onStickyHeaderChange, scrollBuffer },
2840
+ props: {
2841
+ alwaysRenderIndicesArr,
2842
+ alwaysRenderIndicesSet,
2843
+ drawDistance,
2844
+ getItemType,
2845
+ itemsAreEqual,
2846
+ keyExtractor,
2847
+ onStickyHeaderChange
2848
+ },
2145
2849
  scrollForNextCalculateItemsInView,
2146
2850
  scrollLength,
2147
2851
  sizes,
@@ -2151,79 +2855,84 @@ function calculateItemsInView(ctx, state, params = {}) {
2151
2855
  const { data } = state.props;
2152
2856
  const stickyIndicesArr = state.props.stickyIndicesArr || [];
2153
2857
  const stickyIndicesSet = state.props.stickyIndicesSet || /* @__PURE__ */ new Set();
2858
+ const alwaysRenderArr = alwaysRenderIndicesArr || [];
2859
+ const alwaysRenderSet = alwaysRenderIndicesSet || /* @__PURE__ */ new Set();
2860
+ const { dataChanged, doMVCP, forceFullItemPositions } = params;
2154
2861
  const prevNumContainers = peek$(ctx, "numContainers");
2155
2862
  if (!data || scrollLength === 0 || !prevNumContainers) {
2156
- if (state.initialAnchor) {
2157
- ensureInitialAnchor(ctx, state);
2863
+ if (!IsNewArchitecture && state.initialAnchor) {
2864
+ ensureInitialAnchor(ctx);
2158
2865
  }
2159
2866
  return;
2160
2867
  }
2161
- const totalSize = getContentSize(ctx);
2868
+ let totalSize = getContentSize(ctx);
2162
2869
  const topPad = peek$(ctx, "stylePaddingTop") + peek$(ctx, "headerSize");
2163
2870
  const numColumns = peek$(ctx, "numColumns");
2164
- const { dataChanged, doMVCP, forceFullItemPositions } = params;
2165
2871
  const speed = getScrollVelocity(state);
2166
2872
  const scrollExtra = 0;
2167
2873
  const { queuedInitialLayout } = state;
2168
2874
  let { scroll: scrollState } = state;
2169
2875
  if (!queuedInitialLayout && initialScroll) {
2170
- const updatedOffset = calculateOffsetWithOffsetPosition(
2876
+ const updatedOffset = state.initialScrollUsesOffset ? (_a3 = initialScroll.contentOffset) != null ? _a3 : 0 : calculateOffsetWithOffsetPosition(
2171
2877
  ctx,
2172
- state,
2173
- calculateOffsetForIndex(ctx, state, initialScroll.index),
2878
+ calculateOffsetForIndex(ctx, initialScroll.index),
2174
2879
  initialScroll
2175
2880
  );
2176
2881
  scrollState = updatedOffset;
2177
2882
  }
2178
- const scrollAdjustPending = (_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0;
2883
+ const scrollAdjustPending = (_b = peek$(ctx, "scrollAdjustPending")) != null ? _b : 0;
2179
2884
  const scrollAdjustPad = scrollAdjustPending - topPad;
2180
- let scroll = scrollState + scrollExtra + scrollAdjustPad;
2885
+ let scroll = Math.round(scrollState + scrollExtra + scrollAdjustPad);
2181
2886
  if (scroll + scrollLength > totalSize) {
2182
2887
  scroll = Math.max(0, totalSize - scrollLength);
2183
2888
  }
2184
- if (ENABLE_DEBUG_VIEW) {
2185
- set$(ctx, "debugRawScroll", scrollState);
2186
- set$(ctx, "debugComputedScroll", scroll);
2187
- }
2188
- const previousStickyIndex = state.activeStickyIndex;
2889
+ const previousStickyIndex = peek$(ctx, "activeStickyIndex");
2189
2890
  const currentStickyIdx = stickyIndicesArr.length > 0 ? findCurrentStickyIndex(stickyIndicesArr, scroll, state) : -1;
2190
- const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyIndicesArr[currentStickyIdx] : void 0;
2191
- state.activeStickyIndex = nextActiveStickyIndex;
2192
- set$(ctx, "activeStickyIndex", nextActiveStickyIndex);
2193
- let scrollBufferTop = scrollBuffer;
2194
- let scrollBufferBottom = scrollBuffer;
2195
- if (speed > 0 || speed === 0 && scroll < Math.max(50, scrollBuffer)) {
2196
- scrollBufferTop = scrollBuffer * 0.5;
2197
- scrollBufferBottom = scrollBuffer * 1.5;
2891
+ const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyIndicesArr[currentStickyIdx] : -1;
2892
+ if (currentStickyIdx >= 0 || previousStickyIndex >= 0) {
2893
+ set$(ctx, "activeStickyIndex", nextActiveStickyIndex);
2894
+ }
2895
+ let scrollBufferTop = drawDistance;
2896
+ let scrollBufferBottom = drawDistance;
2897
+ if (speed > 0 || speed === 0 && scroll < Math.max(50, drawDistance)) {
2898
+ scrollBufferTop = drawDistance * 0.5;
2899
+ scrollBufferBottom = drawDistance * 1.5;
2198
2900
  } else {
2199
- scrollBufferTop = scrollBuffer * 1.5;
2200
- scrollBufferBottom = scrollBuffer * 0.5;
2901
+ scrollBufferTop = drawDistance * 1.5;
2902
+ scrollBufferBottom = drawDistance * 0.5;
2201
2903
  }
2202
2904
  const scrollTopBuffered = scroll - scrollBufferTop;
2203
2905
  const scrollBottom = scroll + scrollLength + (scroll < 0 ? -scroll : 0);
2204
2906
  const scrollBottomBuffered = scrollBottom + scrollBufferBottom;
2205
- if (!dataChanged && scrollForNextCalculateItemsInView) {
2907
+ if (!dataChanged && !forceFullItemPositions && scrollForNextCalculateItemsInView) {
2206
2908
  const { top, bottom } = scrollForNextCalculateItemsInView;
2207
- if (scrollTopBuffered > top && scrollBottomBuffered < bottom) {
2208
- if (state.initialAnchor) {
2209
- ensureInitialAnchor(ctx, state);
2909
+ if (top === null && bottom === null) {
2910
+ state.scrollForNextCalculateItemsInView = void 0;
2911
+ } else if ((top === null || scrollTopBuffered > top) && (bottom === null || scrollBottomBuffered < bottom)) {
2912
+ if (!IsNewArchitecture && state.initialAnchor) {
2913
+ ensureInitialAnchor(ctx);
2914
+ }
2915
+ if (Platform2.OS !== "web" || !isInMVCPActiveMode(state)) {
2916
+ return;
2210
2917
  }
2211
- return;
2212
2918
  }
2213
2919
  }
2214
- const checkMVCP = doMVCP ? prepareMVCP(ctx, state, dataChanged) : void 0;
2920
+ const checkMVCP = doMVCP ? prepareMVCP(ctx, dataChanged) : void 0;
2215
2921
  if (dataChanged) {
2216
2922
  indexByKey.clear();
2217
2923
  idCache.length = 0;
2218
- positions.clear();
2924
+ positions.length = 0;
2925
+ columns.length = 0;
2926
+ columnSpans.length = 0;
2219
2927
  }
2220
- const startIndex = dataChanged ? 0 : (_b = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _b : 0;
2221
- updateItemPositions(ctx, state, dataChanged, {
2928
+ const startIndex = forceFullItemPositions || dataChanged ? 0 : (_c = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _c : 0;
2929
+ updateItemPositions(ctx, dataChanged, {
2222
2930
  doMVCP,
2223
2931
  forceFullUpdate: !!forceFullItemPositions,
2224
2932
  scrollBottomBuffered,
2225
2933
  startIndex
2226
2934
  });
2935
+ totalSize = getContentSize(ctx);
2227
2936
  if (minIndexSizeChanged !== void 0) {
2228
2937
  state.minIndexSizeChanged = void 0;
2229
2938
  }
@@ -2235,19 +2944,24 @@ function calculateItemsInView(ctx, state, params = {}) {
2235
2944
  let endBuffered = null;
2236
2945
  let loopStart = !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
2237
2946
  for (let i = loopStart; i >= 0; i--) {
2238
- const id = (_c = idCache[i]) != null ? _c : getId(state, i);
2239
- const top = positions.get(id);
2240
- const size = (_d = sizes.get(id)) != null ? _d : getItemSize(ctx, state, id, i, data[i]);
2947
+ const id = (_d = idCache[i]) != null ? _d : getId(state, i);
2948
+ const top = positions[i];
2949
+ const size = (_e = sizes.get(id)) != null ? _e : getItemSize(ctx, id, i, data[i]);
2241
2950
  const bottom = top + size;
2242
- if (bottom > scroll - scrollBuffer) {
2951
+ if (bottom > scroll - scrollBufferTop) {
2243
2952
  loopStart = i;
2244
2953
  } else {
2245
2954
  break;
2246
2955
  }
2247
2956
  }
2248
- const loopStartMod = loopStart % numColumns;
2249
- if (loopStartMod > 0) {
2250
- loopStart -= loopStartMod;
2957
+ if (numColumns > 1) {
2958
+ while (loopStart > 0) {
2959
+ const loopColumn = columns[loopStart];
2960
+ if (loopColumn === 1 || loopColumn === void 0) {
2961
+ break;
2962
+ }
2963
+ loopStart -= 1;
2964
+ }
2251
2965
  }
2252
2966
  let foundEnd = false;
2253
2967
  let nextTop;
@@ -2263,20 +2977,24 @@ function calculateItemsInView(ctx, state, params = {}) {
2263
2977
  let firstFullyOnScreenIndex;
2264
2978
  const dataLength = data.length;
2265
2979
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
2266
- const id = (_e = idCache[i]) != null ? _e : getId(state, i);
2267
- const size = (_f = sizes.get(id)) != null ? _f : getItemSize(ctx, state, id, i, data[i]);
2268
- const top = positions.get(id);
2980
+ const id = (_f = idCache[i]) != null ? _f : getId(state, i);
2981
+ const size = (_g = sizes.get(id)) != null ? _g : getItemSize(ctx, id, i, data[i]);
2982
+ const top = positions[i];
2269
2983
  if (!foundEnd) {
2270
2984
  if (startNoBuffer === null && top + size > scroll) {
2271
2985
  startNoBuffer = i;
2272
2986
  }
2273
- if (firstFullyOnScreenIndex === void 0 && top >= scroll - 10) {
2987
+ if (firstFullyOnScreenIndex === void 0 && top >= scroll - 10 && top <= scrollBottom) {
2274
2988
  firstFullyOnScreenIndex = i;
2275
2989
  }
2276
2990
  if (startBuffered === null && top + size > scrollTopBuffered) {
2277
2991
  startBuffered = i;
2278
2992
  startBufferedId = id;
2279
- nextTop = top;
2993
+ if (scrollTopBuffered < 0) {
2994
+ nextTop = null;
2995
+ } else {
2996
+ nextTop = top;
2997
+ }
2280
2998
  }
2281
2999
  if (startNoBuffer !== null) {
2282
3000
  if (top <= scrollBottom) {
@@ -2284,7 +3002,11 @@ function calculateItemsInView(ctx, state, params = {}) {
2284
3002
  }
2285
3003
  if (top <= scrollBottomBuffered) {
2286
3004
  endBuffered = i;
2287
- nextBottom = top + size;
3005
+ if (scrollBottomBuffered > totalSize) {
3006
+ nextBottom = null;
3007
+ } else {
3008
+ nextBottom = top + size;
3009
+ }
2288
3010
  } else {
2289
3011
  foundEnd = true;
2290
3012
  }
@@ -2292,9 +3014,12 @@ function calculateItemsInView(ctx, state, params = {}) {
2292
3014
  }
2293
3015
  }
2294
3016
  const idsInView = [];
2295
- for (let i = firstFullyOnScreenIndex; i <= endNoBuffer; i++) {
2296
- const id = (_g = idCache[i]) != null ? _g : getId(state, i);
2297
- idsInView.push(id);
3017
+ const firstVisibleAnchorIndex = firstFullyOnScreenIndex != null ? firstFullyOnScreenIndex : startNoBuffer;
3018
+ if (firstVisibleAnchorIndex !== null && firstVisibleAnchorIndex !== void 0 && endNoBuffer !== null) {
3019
+ for (let i = firstVisibleAnchorIndex; i <= endNoBuffer; i++) {
3020
+ const id = (_h = idCache[i]) != null ? _h : getId(state, i);
3021
+ idsInView.push(id);
3022
+ }
2298
3023
  }
2299
3024
  Object.assign(state, {
2300
3025
  endBuffered,
@@ -2306,12 +3031,12 @@ function calculateItemsInView(ctx, state, params = {}) {
2306
3031
  startNoBuffer
2307
3032
  });
2308
3033
  if (enableScrollForNextCalculateItemsInView && nextTop !== void 0 && nextBottom !== void 0) {
2309
- state.scrollForNextCalculateItemsInView = nextTop !== void 0 && nextBottom !== void 0 ? {
3034
+ state.scrollForNextCalculateItemsInView = isNullOrUndefined(nextTop) && isNullOrUndefined(nextBottom) ? void 0 : {
2310
3035
  bottom: nextBottom,
2311
3036
  top: nextTop
2312
- } : void 0;
3037
+ };
2313
3038
  }
2314
- const numContainers = peek$(ctx, "numContainers");
3039
+ let numContainers = prevNumContainers;
2315
3040
  const pendingRemoval = [];
2316
3041
  if (dataChanged) {
2317
3042
  for (let i = 0; i < numContainers; i++) {
@@ -2322,37 +3047,46 @@ function calculateItemsInView(ctx, state, params = {}) {
2322
3047
  }
2323
3048
  }
2324
3049
  if (startBuffered !== null && endBuffered !== null) {
2325
- let numContainers2 = prevNumContainers;
2326
3050
  const needNewContainers = [];
3051
+ const needNewContainersSet = /* @__PURE__ */ new Set();
2327
3052
  for (let i = startBuffered; i <= endBuffered; i++) {
2328
- const id = (_h = idCache[i]) != null ? _h : getId(state, i);
3053
+ const id = (_i = idCache[i]) != null ? _i : getId(state, i);
2329
3054
  if (!containerItemKeys.has(id)) {
3055
+ needNewContainersSet.add(i);
2330
3056
  needNewContainers.push(i);
2331
3057
  }
2332
3058
  }
3059
+ if (alwaysRenderArr.length > 0) {
3060
+ for (const index of alwaysRenderArr) {
3061
+ if (index < 0 || index >= dataLength) continue;
3062
+ const id = (_j = idCache[index]) != null ? _j : getId(state, index);
3063
+ if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
3064
+ needNewContainersSet.add(index);
3065
+ needNewContainers.push(index);
3066
+ }
3067
+ }
3068
+ }
2333
3069
  if (stickyIndicesArr.length > 0) {
2334
3070
  handleStickyActivation(
2335
3071
  ctx,
2336
- state,
2337
3072
  stickyIndicesSet,
2338
3073
  stickyIndicesArr,
2339
3074
  currentStickyIdx,
2340
3075
  needNewContainers,
3076
+ needNewContainersSet,
2341
3077
  startBuffered,
2342
3078
  endBuffered
2343
3079
  );
2344
- } else {
2345
- state.activeStickyIndex = void 0;
2346
- set$(ctx, "activeStickyIndex", void 0);
3080
+ } else if (previousStickyIndex !== -1) {
3081
+ set$(ctx, "activeStickyIndex", -1);
2347
3082
  }
2348
3083
  if (needNewContainers.length > 0) {
2349
3084
  const requiredItemTypes = getItemType ? needNewContainers.map((i) => {
2350
3085
  const itemType = getItemType(data[i], i);
2351
- return itemType ? String(itemType) : "";
3086
+ return itemType !== void 0 ? String(itemType) : "";
2352
3087
  }) : void 0;
2353
3088
  const availableContainers = findAvailableContainers(
2354
3089
  ctx,
2355
- state,
2356
3090
  needNewContainers.length,
2357
3091
  startBuffered,
2358
3092
  endBuffered,
@@ -2363,7 +3097,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2363
3097
  for (let idx = 0; idx < needNewContainers.length; idx++) {
2364
3098
  const i = needNewContainers[idx];
2365
3099
  const containerIndex = availableContainers[idx];
2366
- const id = (_i = idCache[i]) != null ? _i : getId(state, i);
3100
+ const id = (_k = idCache[i]) != null ? _k : getId(state, i);
2367
3101
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
2368
3102
  if (oldKey && oldKey !== id) {
2369
3103
  containerItemKeys.delete(oldKey);
@@ -2373,30 +3107,55 @@ function calculateItemsInView(ctx, state, params = {}) {
2373
3107
  if (requiredItemTypes) {
2374
3108
  state.containerItemTypes.set(containerIndex, requiredItemTypes[idx]);
2375
3109
  }
2376
- containerItemKeys.add(id);
2377
- if (stickyIndicesSet.has(i)) {
2378
- set$(ctx, `containerSticky${containerIndex}`, true);
2379
- const topPadding = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
2380
- set$(ctx, `containerStickyOffset${containerIndex}`, topPadding);
3110
+ containerItemKeys.set(id, containerIndex);
3111
+ const containerSticky = `containerSticky${containerIndex}`;
3112
+ const isSticky = stickyIndicesSet.has(i);
3113
+ const isAlwaysRender = alwaysRenderSet.has(i);
3114
+ if (isSticky) {
3115
+ set$(ctx, containerSticky, true);
2381
3116
  state.stickyContainerPool.add(containerIndex);
2382
3117
  } else {
2383
- set$(ctx, `containerSticky${containerIndex}`, false);
2384
- state.stickyContainerPool.delete(containerIndex);
3118
+ if (peek$(ctx, containerSticky)) {
3119
+ set$(ctx, containerSticky, false);
3120
+ }
3121
+ if (isAlwaysRender) {
3122
+ state.stickyContainerPool.add(containerIndex);
3123
+ } else if (state.stickyContainerPool.has(containerIndex)) {
3124
+ state.stickyContainerPool.delete(containerIndex);
3125
+ }
2385
3126
  }
2386
- if (containerIndex >= numContainers2) {
2387
- numContainers2 = containerIndex + 1;
3127
+ if (containerIndex >= numContainers) {
3128
+ numContainers = containerIndex + 1;
2388
3129
  }
2389
3130
  }
2390
- if (numContainers2 !== prevNumContainers) {
2391
- set$(ctx, "numContainers", numContainers2);
2392
- if (numContainers2 > peek$(ctx, "numContainersPooled")) {
2393
- set$(ctx, "numContainersPooled", Math.ceil(numContainers2 * 1.5));
3131
+ if (numContainers !== prevNumContainers) {
3132
+ set$(ctx, "numContainers", numContainers);
3133
+ if (numContainers > peek$(ctx, "numContainersPooled")) {
3134
+ set$(ctx, "numContainersPooled", Math.ceil(numContainers * 1.5));
3135
+ }
3136
+ }
3137
+ }
3138
+ if (alwaysRenderArr.length > 0) {
3139
+ for (const index of alwaysRenderArr) {
3140
+ if (index < 0 || index >= dataLength) continue;
3141
+ const id = (_l = idCache[index]) != null ? _l : getId(state, index);
3142
+ const containerIndex = containerItemKeys.get(id);
3143
+ if (containerIndex !== void 0) {
3144
+ state.stickyContainerPool.add(containerIndex);
2394
3145
  }
2395
3146
  }
2396
3147
  }
2397
3148
  }
2398
- if (stickyIndicesArr.length > 0) {
2399
- handleStickyRecycling(ctx, state, stickyIndicesArr, scroll, scrollBuffer, currentStickyIdx, pendingRemoval);
3149
+ if (state.stickyContainerPool.size > 0) {
3150
+ handleStickyRecycling(
3151
+ ctx,
3152
+ stickyIndicesArr,
3153
+ scroll,
3154
+ drawDistance,
3155
+ currentStickyIdx,
3156
+ pendingRemoval,
3157
+ alwaysRenderSet
3158
+ );
2400
3159
  }
2401
3160
  let didChangePositions = false;
2402
3161
  for (let i = 0; i < numContainers; i++) {
@@ -2408,26 +3167,27 @@ function calculateItemsInView(ctx, state, params = {}) {
2408
3167
  state.containerItemTypes.delete(i);
2409
3168
  if (state.stickyContainerPool.has(i)) {
2410
3169
  set$(ctx, `containerSticky${i}`, false);
2411
- set$(ctx, `containerStickyOffset${i}`, void 0);
2412
3170
  state.stickyContainerPool.delete(i);
2413
3171
  }
2414
3172
  set$(ctx, `containerItemKey${i}`, void 0);
2415
3173
  set$(ctx, `containerItemData${i}`, void 0);
2416
3174
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
2417
3175
  set$(ctx, `containerColumn${i}`, -1);
3176
+ set$(ctx, `containerSpan${i}`, 1);
2418
3177
  } else {
2419
3178
  const itemIndex = indexByKey.get(itemKey);
2420
3179
  const item = data[itemIndex];
2421
3180
  if (item !== void 0) {
2422
- const id = (_j = idCache[itemIndex]) != null ? _j : getId(state, itemIndex);
2423
- const positionValue = positions.get(id);
3181
+ const positionValue = positions[itemIndex];
2424
3182
  if (positionValue === void 0) {
2425
3183
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
2426
3184
  } else {
2427
3185
  const position = (positionValue || 0) - scrollAdjustPending;
2428
- const column = columns.get(id) || 1;
3186
+ const column = columns[itemIndex] || 1;
3187
+ const span = columnSpans[itemIndex] || 1;
2429
3188
  const prevPos = peek$(ctx, `containerPosition${i}`);
2430
3189
  const prevColumn = peek$(ctx, `containerColumn${i}`);
3190
+ const prevSpan = peek$(ctx, `containerSpan${i}`);
2431
3191
  const prevData = peek$(ctx, `containerItemData${i}`);
2432
3192
  if (position > POSITION_OUT_OF_VIEW && position !== prevPos) {
2433
3193
  set$(ctx, `containerPosition${i}`, position);
@@ -2436,6 +3196,9 @@ function calculateItemsInView(ctx, state, params = {}) {
2436
3196
  if (column >= 0 && column !== prevColumn) {
2437
3197
  set$(ctx, `containerColumn${i}`, column);
2438
3198
  }
3199
+ if (span !== prevSpan) {
3200
+ set$(ctx, `containerSpan${i}`, span);
3201
+ }
2439
3202
  if (prevData !== item && (itemsAreEqual ? !itemsAreEqual(prevData, item, itemIndex, data) : true)) {
2440
3203
  set$(ctx, `containerItemData${i}`, item);
2441
3204
  }
@@ -2448,7 +3211,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2448
3211
  }
2449
3212
  if (!queuedInitialLayout && endBuffered !== null) {
2450
3213
  if (checkAllSizesKnown(state)) {
2451
- setDidLayout(ctx, state);
3214
+ setDidLayout(ctx);
2452
3215
  }
2453
3216
  }
2454
3217
  if (viewabilityConfigCallbackPairs) {
@@ -2461,8 +3224,8 @@ function calculateItemsInView(ctx, state, params = {}) {
2461
3224
  }
2462
3225
  }
2463
3226
  });
2464
- if (state.initialAnchor) {
2465
- ensureInitialAnchor(ctx, state);
3227
+ if (!IsNewArchitecture && state.initialAnchor) {
3228
+ ensureInitialAnchor(ctx);
2466
3229
  }
2467
3230
  }
2468
3231
 
@@ -2486,37 +3249,6 @@ function checkActualChange(state, dataProp, previousData) {
2486
3249
  return false;
2487
3250
  }
2488
3251
 
2489
- // src/core/doMaintainScrollAtEnd.ts
2490
- function doMaintainScrollAtEnd(ctx, state, animated) {
2491
- const {
2492
- refScroller,
2493
- props: { maintainScrollAtEnd }
2494
- } = state;
2495
- if ((state == null ? void 0 : state.isAtEnd) && maintainScrollAtEnd && peek$(ctx, "containersDidLayout")) {
2496
- const paddingTop = peek$(ctx, "alignItemsPaddingTop");
2497
- if (paddingTop > 0) {
2498
- state.scroll = 0;
2499
- }
2500
- requestAnimationFrame(() => {
2501
- var _a3;
2502
- if (state == null ? void 0 : state.isAtEnd) {
2503
- state.maintainingScrollAtEnd = true;
2504
- (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
2505
- animated
2506
- });
2507
- setTimeout(
2508
- () => {
2509
- state.maintainingScrollAtEnd = false;
2510
- },
2511
- 0
2512
- );
2513
- }
2514
- });
2515
- return true;
2516
- }
2517
- return false;
2518
- }
2519
-
2520
3252
  // src/utils/updateAveragesOnDataChange.ts
2521
3253
  function updateAveragesOnDataChange(state, oldData, newData) {
2522
3254
  var _a3;
@@ -2570,36 +3302,37 @@ function updateAveragesOnDataChange(state, oldData, newData) {
2570
3302
  }
2571
3303
 
2572
3304
  // src/core/checkResetContainers.ts
2573
- function checkResetContainers(ctx, state, dataProp) {
3305
+ function checkResetContainers(ctx, dataProp) {
3306
+ const state = ctx.state;
2574
3307
  const { previousData } = state;
2575
3308
  if (previousData) {
2576
3309
  updateAveragesOnDataChange(state, previousData, dataProp);
2577
3310
  }
2578
3311
  const { maintainScrollAtEnd } = state.props;
2579
- calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
2580
- const shouldMaintainScrollAtEnd = maintainScrollAtEnd === true || maintainScrollAtEnd.onDataChange;
2581
- const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, state, false);
3312
+ calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
3313
+ const shouldMaintainScrollAtEnd = maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onDataChange;
3314
+ const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx);
2582
3315
  if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
2583
3316
  state.isEndReached = false;
2584
3317
  }
2585
3318
  if (!didMaintainScrollAtEnd) {
2586
- checkAtTop(state);
2587
- checkAtBottom(ctx, state);
3319
+ checkThresholds(ctx);
2588
3320
  }
2589
3321
  delete state.previousData;
2590
3322
  }
2591
3323
 
2592
3324
  // src/core/doInitialAllocateContainers.ts
2593
- function doInitialAllocateContainers(ctx, state, dataChanged) {
3325
+ function doInitialAllocateContainers(ctx) {
2594
3326
  var _a3, _b, _c;
3327
+ const state = ctx.state;
2595
3328
  const {
2596
3329
  scrollLength,
2597
3330
  props: {
2598
3331
  data,
3332
+ drawDistance,
2599
3333
  getEstimatedItemSize,
2600
3334
  getFixedItemSize,
2601
3335
  getItemType,
2602
- scrollBuffer,
2603
3336
  numColumns,
2604
3337
  estimatedItemSize
2605
3338
  }
@@ -2612,40 +3345,58 @@ function doInitialAllocateContainers(ctx, state, dataChanged) {
2612
3345
  const num = Math.min(20, data.length);
2613
3346
  for (let i = 0; i < num; i++) {
2614
3347
  const item = data[i];
2615
- const itemType = getItemType ? (_a3 = getItemType(item, i)) != null ? _a3 : "" : "";
2616
- totalSize += (_c = (_b = getFixedItemSize == null ? void 0 : getFixedItemSize(i, item, itemType)) != null ? _b : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(i, item, itemType)) != null ? _c : estimatedItemSize;
3348
+ if (item !== void 0) {
3349
+ const itemType = (_a3 = getItemType == null ? void 0 : getItemType(item, i)) != null ? _a3 : "";
3350
+ totalSize += (_c = (_b = getFixedItemSize == null ? void 0 : getFixedItemSize(item, i, itemType)) != null ? _b : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(item, i, itemType)) != null ? _c : estimatedItemSize;
3351
+ }
2617
3352
  }
2618
3353
  averageItemSize = totalSize / num;
2619
3354
  } else {
2620
3355
  averageItemSize = estimatedItemSize;
2621
3356
  }
2622
- const numContainers = Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize * numColumns);
3357
+ const numContainers = Math.ceil((scrollLength + drawDistance * 2) / averageItemSize * numColumns);
2623
3358
  for (let i = 0; i < numContainers; i++) {
2624
3359
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
2625
3360
  set$(ctx, `containerColumn${i}`, -1);
3361
+ set$(ctx, `containerSpan${i}`, 1);
2626
3362
  }
2627
3363
  set$(ctx, "numContainers", numContainers);
2628
3364
  set$(ctx, "numContainersPooled", numContainers * state.props.initialContainerPoolRatio);
2629
3365
  if (!IsNewArchitecture || state.lastLayout) {
2630
3366
  if (state.initialScroll) {
2631
3367
  requestAnimationFrame(() => {
2632
- calculateItemsInView(ctx, state, { dataChanged, doMVCP: true });
3368
+ calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
2633
3369
  });
2634
3370
  } else {
2635
- calculateItemsInView(ctx, state, { dataChanged, doMVCP: true });
3371
+ calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
2636
3372
  }
2637
3373
  }
2638
3374
  return true;
2639
3375
  }
2640
3376
  }
3377
+ function getWindowSize() {
3378
+ const screenSize = ReactNative.Dimensions.get("window");
3379
+ return {
3380
+ height: screenSize.height,
3381
+ width: screenSize.width
3382
+ };
3383
+ }
2641
3384
 
2642
3385
  // src/core/handleLayout.ts
2643
- function handleLayout(ctx, state, layout, setCanRender) {
2644
- const { maintainScrollAtEnd } = state.props;
2645
- const measuredLength = layout[state.props.horizontal ? "width" : "height"];
3386
+ function handleLayout(ctx, layoutParam, setCanRender) {
3387
+ const state = ctx.state;
3388
+ const { maintainScrollAtEnd, useWindowScroll } = state.props;
3389
+ const scrollAxis = state.props.horizontal ? "width" : "height";
3390
+ const otherAxis = state.props.horizontal ? "height" : "width";
3391
+ let layout = layoutParam;
3392
+ if (useWindowScroll) {
3393
+ const windowScrollAxisLength = getWindowSize()[scrollAxis];
3394
+ layout = windowScrollAxisLength > 0 ? { ...layoutParam, [scrollAxis]: windowScrollAxisLength } : layoutParam;
3395
+ }
3396
+ const measuredLength = layout[scrollAxis];
2646
3397
  const previousLength = state.scrollLength;
2647
3398
  const scrollLength = measuredLength > 0 ? measuredLength : previousLength;
2648
- const otherAxisSize = layout[state.props.horizontal ? "height" : "width"];
3399
+ const otherAxisSize = layout[otherAxis];
2649
3400
  const needsCalculate = !state.lastLayout || scrollLength > state.scrollLength || state.lastLayout.x !== layout.x || state.lastLayout.y !== layout.y;
2650
3401
  state.lastLayout = layout;
2651
3402
  const prevOtherAxisSize = state.otherAxisSize;
@@ -2656,20 +3407,18 @@ function handleLayout(ctx, state, layout, setCanRender) {
2656
3407
  state.lastBatchingAction = Date.now();
2657
3408
  state.scrollForNextCalculateItemsInView = void 0;
2658
3409
  if (scrollLength > 0) {
2659
- doInitialAllocateContainers(ctx, state);
3410
+ doInitialAllocateContainers(ctx);
2660
3411
  }
2661
3412
  if (needsCalculate) {
2662
- calculateItemsInView(ctx, state, { doMVCP: true });
3413
+ calculateItemsInView(ctx, { doMVCP: true });
2663
3414
  }
2664
3415
  if (didChange || otherAxisSize !== prevOtherAxisSize) {
2665
3416
  set$(ctx, "scrollSize", { height: layout.height, width: layout.width });
2666
3417
  }
2667
- if (maintainScrollAtEnd === true || maintainScrollAtEnd.onLayout) {
2668
- doMaintainScrollAtEnd(ctx, state, false);
3418
+ if (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onLayout) {
3419
+ doMaintainScrollAtEnd(ctx);
2669
3420
  }
2670
- updateAlignItemsPaddingTop(ctx, state);
2671
- checkAtBottom(ctx, state);
2672
- checkAtTop(state);
3421
+ checkThresholds(ctx);
2673
3422
  if (state) {
2674
3423
  state.needsOtherAxisSize = otherAxisSize - (state.props.stylePaddingTop || 0) < 10;
2675
3424
  }
@@ -2684,8 +3433,15 @@ function handleLayout(ctx, state, layout, setCanRender) {
2684
3433
  }
2685
3434
 
2686
3435
  // src/core/onScroll.ts
2687
- function onScroll(ctx, state, event) {
2688
- var _a3, _b, _c;
3436
+ var INITIAL_SCROLL_PROGRESS_EPSILON = 1;
3437
+ function didObserveInitialScrollProgress(newScroll, watchdog) {
3438
+ const previousDistance = Math.abs(watchdog.startScroll - watchdog.targetOffset);
3439
+ const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
3440
+ return nextDistance <= INITIAL_SCROLL_PROGRESS_EPSILON || nextDistance + INITIAL_SCROLL_PROGRESS_EPSILON < previousDistance;
3441
+ }
3442
+ function onScroll(ctx, event) {
3443
+ var _a3, _b, _c, _d;
3444
+ const state = ctx.state;
2689
3445
  const {
2690
3446
  scrollProcessingEnabled,
2691
3447
  props: { onScroll: onScrollProp }
@@ -2696,17 +3452,43 @@ function onScroll(ctx, state, event) {
2696
3452
  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) {
2697
3453
  return;
2698
3454
  }
3455
+ let insetChanged = false;
3456
+ if ((_d = event.nativeEvent) == null ? void 0 : _d.contentInset) {
3457
+ const { contentInset } = event.nativeEvent;
3458
+ const prevInset = state.nativeContentInset;
3459
+ if (!prevInset || prevInset.top !== contentInset.top || prevInset.bottom !== contentInset.bottom || prevInset.left !== contentInset.left || prevInset.right !== contentInset.right) {
3460
+ state.nativeContentInset = contentInset;
3461
+ insetChanged = true;
3462
+ }
3463
+ }
2699
3464
  let newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
3465
+ if (state.scrollingTo && state.scrollingTo.offset >= newScroll) {
3466
+ const maxOffset = clampScrollOffset(ctx, newScroll, state.scrollingTo);
3467
+ if (newScroll !== maxOffset && Math.abs(newScroll - maxOffset) > 1) {
3468
+ newScroll = maxOffset;
3469
+ scrollTo(ctx, {
3470
+ forceScroll: true,
3471
+ isInitialScroll: true,
3472
+ noScrollingTo: true,
3473
+ offset: newScroll
3474
+ });
3475
+ return;
3476
+ }
3477
+ }
2700
3478
  state.scrollPending = newScroll;
2701
- const maxOffset = Math.max(0, getContentSize(ctx) - state.scrollLength);
2702
- if (state.initialScroll && newScroll > maxOffset) {
2703
- newScroll = maxOffset;
2704
- scrollTo(ctx, state, {
2705
- noScrollingTo: true,
2706
- offset: newScroll
2707
- });
3479
+ const initialNativeScrollWatchdog = state.initialNativeScrollWatchdog;
3480
+ const didInitialScrollProgress = !!initialNativeScrollWatchdog && didObserveInitialScrollProgress(newScroll, initialNativeScrollWatchdog);
3481
+ if (didInitialScrollProgress) {
3482
+ state.initialNativeScrollWatchdog = void 0;
3483
+ }
3484
+ updateScroll(ctx, newScroll, insetChanged);
3485
+ if (initialNativeScrollWatchdog && !didInitialScrollProgress) {
3486
+ state.hasScrolled = false;
3487
+ state.initialNativeScrollWatchdog = initialNativeScrollWatchdog;
3488
+ }
3489
+ if (state.scrollingTo) {
3490
+ checkFinishedScroll(ctx);
2708
3491
  }
2709
- updateScroll(ctx, state, newScroll);
2710
3492
  onScrollProp == null ? void 0 : onScrollProp(event);
2711
3493
  }
2712
3494
 
@@ -2715,51 +3497,80 @@ var ScrollAdjustHandler = class {
2715
3497
  constructor(ctx) {
2716
3498
  this.appliedAdjust = 0;
2717
3499
  this.pendingAdjust = 0;
2718
- this.mounted = false;
2719
- this.context = ctx;
2720
- if (Platform2.OS === "web") {
2721
- const commitPendingAdjust = () => {
2722
- const state = this.context.internalState;
2723
- const pending = this.pendingAdjust;
2724
- if (pending !== 0) {
2725
- this.pendingAdjust = 0;
2726
- this.appliedAdjust += pending;
2727
- state.scroll += pending;
2728
- state.scrollForNextCalculateItemsInView = void 0;
2729
- set$(this.context, "scrollAdjustPending", 0);
2730
- set$(this.context, "scrollAdjust", this.appliedAdjust);
2731
- calculateItemsInView(this.context, this.context.internalState);
2732
- }
2733
- };
2734
- listen$(this.context, "scrollingTo", (value) => {
2735
- if (value === void 0) {
2736
- commitPendingAdjust();
2737
- }
2738
- });
2739
- }
3500
+ this.ctx = ctx;
2740
3501
  }
2741
3502
  requestAdjust(add) {
2742
- const scrollingTo = peek$(this.context, "scrollingTo");
2743
- if (Platform2.OS === "web" && (scrollingTo == null ? void 0 : scrollingTo.animated) && !scrollingTo.isInitialScroll) {
3503
+ const scrollingTo = this.ctx.state.scrollingTo;
3504
+ if (PlatformAdjustBreaksScroll && (scrollingTo == null ? void 0 : scrollingTo.animated) && !scrollingTo.isInitialScroll) {
2744
3505
  this.pendingAdjust += add;
2745
- set$(this.context, "scrollAdjustPending", this.pendingAdjust);
3506
+ set$(this.ctx, "scrollAdjustPending", this.pendingAdjust);
2746
3507
  } else {
2747
3508
  this.appliedAdjust += add;
2748
- set$(this.context, "scrollAdjust", this.appliedAdjust);
3509
+ set$(this.ctx, "scrollAdjust", this.appliedAdjust);
3510
+ }
3511
+ if (this.ctx.state.scrollingTo) {
3512
+ checkFinishedScroll(this.ctx);
2749
3513
  }
2750
- }
2751
- setMounted() {
2752
- this.mounted = true;
2753
3514
  }
2754
3515
  getAdjust() {
2755
3516
  return this.appliedAdjust;
2756
3517
  }
3518
+ commitPendingAdjust(scrollTarget) {
3519
+ if (PlatformAdjustBreaksScroll) {
3520
+ const state = this.ctx.state;
3521
+ const pending = this.pendingAdjust;
3522
+ this.pendingAdjust = 0;
3523
+ if (pending !== 0) {
3524
+ let targetScroll;
3525
+ if ((scrollTarget == null ? void 0 : scrollTarget.index) !== void 0) {
3526
+ const currentOffset = calculateOffsetForIndex(this.ctx, scrollTarget.index);
3527
+ targetScroll = calculateOffsetWithOffsetPosition(this.ctx, currentOffset, scrollTarget);
3528
+ targetScroll = clampScrollOffset(this.ctx, targetScroll, scrollTarget);
3529
+ } else {
3530
+ targetScroll = clampScrollOffset(this.ctx, state.scroll + pending);
3531
+ }
3532
+ const adjustment = targetScroll - state.scroll;
3533
+ if (Math.abs(adjustment) > 0.1 || Math.abs(pending) > 0.1) {
3534
+ this.appliedAdjust += adjustment;
3535
+ state.scroll = targetScroll;
3536
+ state.scrollForNextCalculateItemsInView = void 0;
3537
+ set$(this.ctx, "scrollAdjust", this.appliedAdjust);
3538
+ }
3539
+ set$(this.ctx, "scrollAdjustPending", 0);
3540
+ calculateItemsInView(this.ctx);
3541
+ }
3542
+ }
3543
+ }
2757
3544
  };
2758
3545
 
2759
3546
  // src/core/updateItemSize.ts
2760
- function updateItemSize(ctx, state, itemKey, sizeObj) {
3547
+ function runOrScheduleMVCPRecalculate(ctx) {
3548
+ const state = ctx.state;
3549
+ if (Platform2.OS === "web") {
3550
+ if (!state.mvcpAnchorLock) {
3551
+ if (state.queuedMVCPRecalculate !== void 0) {
3552
+ cancelAnimationFrame(state.queuedMVCPRecalculate);
3553
+ state.queuedMVCPRecalculate = void 0;
3554
+ }
3555
+ calculateItemsInView(ctx, { doMVCP: true });
3556
+ return;
3557
+ }
3558
+ if (state.queuedMVCPRecalculate !== void 0) {
3559
+ return;
3560
+ }
3561
+ state.queuedMVCPRecalculate = requestAnimationFrame(() => {
3562
+ state.queuedMVCPRecalculate = void 0;
3563
+ calculateItemsInView(ctx, { doMVCP: true });
3564
+ });
3565
+ } else {
3566
+ calculateItemsInView(ctx, { doMVCP: true });
3567
+ }
3568
+ }
3569
+ function updateItemSize(ctx, itemKey, sizeObj) {
2761
3570
  var _a3;
3571
+ const state = ctx.state;
2762
3572
  const {
3573
+ didContainersLayout,
2763
3574
  sizesKnown,
2764
3575
  props: {
2765
3576
  getFixedItemSize,
@@ -2782,31 +3593,24 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
2782
3593
  return;
2783
3594
  }
2784
3595
  const type = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
2785
- const size2 = getFixedItemSize(index, itemData, type);
3596
+ const size2 = getFixedItemSize(itemData, index, type);
2786
3597
  if (size2 !== void 0 && size2 === sizesKnown.get(itemKey)) {
2787
3598
  return;
2788
3599
  }
2789
3600
  }
2790
- const containersDidLayout = peek$(ctx, "containersDidLayout");
2791
- let needsRecalculate = !containersDidLayout;
3601
+ let needsRecalculate = !didContainersLayout;
2792
3602
  let shouldMaintainScrollAtEnd = false;
2793
3603
  let minIndexSizeChanged;
2794
3604
  let maxOtherAxisSize = peek$(ctx, "otherAxisSize") || 0;
2795
3605
  const prevSizeKnown = state.sizesKnown.get(itemKey);
2796
- const diff = updateOneItemSize(ctx, state, itemKey, sizeObj);
3606
+ const diff = updateOneItemSize(ctx, itemKey, sizeObj);
2797
3607
  const size = roundSize(horizontal ? sizeObj.width : sizeObj.height);
2798
3608
  if (diff !== 0) {
2799
3609
  minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
2800
3610
  const { startBuffered, endBuffered } = state;
2801
3611
  needsRecalculate || (needsRecalculate = index >= startBuffered && index <= endBuffered);
2802
- if (!needsRecalculate) {
2803
- const numContainers = ctx.values.get("numContainers");
2804
- for (let i = 0; i < numContainers; i++) {
2805
- if (peek$(ctx, `containerItemKey${i}`) === itemKey) {
2806
- needsRecalculate = true;
2807
- break;
2808
- }
2809
- }
3612
+ if (!needsRecalculate && state.containerItemKeys.has(itemKey)) {
3613
+ needsRecalculate = true;
2810
3614
  }
2811
3615
  if (state.needsOtherAxisSize) {
2812
3616
  const otherAxisSize = horizontal ? sizeObj.height : sizeObj.width;
@@ -2842,20 +3646,21 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
2842
3646
  if (!cur || maxOtherAxisSize > cur) {
2843
3647
  set$(ctx, "otherAxisSize", maxOtherAxisSize);
2844
3648
  }
2845
- if (containersDidLayout || checkAllSizesKnown(state)) {
3649
+ if (didContainersLayout || checkAllSizesKnown(state)) {
2846
3650
  if (needsRecalculate) {
2847
3651
  state.scrollForNextCalculateItemsInView = void 0;
2848
- calculateItemsInView(ctx, state, { doMVCP: true });
3652
+ runOrScheduleMVCPRecalculate(ctx);
2849
3653
  }
2850
3654
  if (shouldMaintainScrollAtEnd) {
2851
- if (maintainScrollAtEnd === true || maintainScrollAtEnd.onItemLayout) {
2852
- doMaintainScrollAtEnd(ctx, state, false);
3655
+ if (maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onItemLayout) {
3656
+ doMaintainScrollAtEnd(ctx);
2853
3657
  }
2854
3658
  }
2855
3659
  }
2856
3660
  }
2857
- function updateOneItemSize(ctx, state, itemKey, sizeObj) {
3661
+ function updateOneItemSize(ctx, itemKey, sizeObj) {
2858
3662
  var _a3;
3663
+ const state = ctx.state;
2859
3664
  const {
2860
3665
  indexByKey,
2861
3666
  sizesKnown,
@@ -2864,9 +3669,10 @@ function updateOneItemSize(ctx, state, itemKey, sizeObj) {
2864
3669
  } = state;
2865
3670
  if (!data) return 0;
2866
3671
  const index = indexByKey.get(itemKey);
2867
- const prevSize = getItemSize(ctx, state, itemKey, index, data[index]);
3672
+ const prevSize = getItemSize(ctx, itemKey, index, data[index]);
2868
3673
  const rawSize = horizontal ? sizeObj.width : sizeObj.height;
2869
3674
  const size = Platform2.OS === "web" ? Math.round(rawSize) : roundSize(rawSize);
3675
+ const prevSizeKnown = sizesKnown.get(itemKey);
2870
3676
  sizesKnown.set(itemKey, size);
2871
3677
  if (!getEstimatedItemSize && !getFixedItemSize && size > 0) {
2872
3678
  const itemType = getItemType ? (_a3 = getItemType(data[index], index)) != null ? _a3 : "" : "";
@@ -2874,15 +3680,28 @@ function updateOneItemSize(ctx, state, itemKey, sizeObj) {
2874
3680
  if (!averages) {
2875
3681
  averages = averageSizes[itemType] = { avg: 0, num: 0 };
2876
3682
  }
2877
- averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
2878
- averages.num++;
3683
+ if (averages.num === 0) {
3684
+ averages.avg = size;
3685
+ averages.num++;
3686
+ } else if (prevSizeKnown !== void 0 && prevSizeKnown > 0) {
3687
+ averages.avg += (size - prevSizeKnown) / averages.num;
3688
+ } else {
3689
+ averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
3690
+ averages.num++;
3691
+ }
2879
3692
  }
2880
3693
  if (!prevSize || Math.abs(prevSize - size) > 0.1) {
2881
- setSize(ctx, state, itemKey, size);
3694
+ setSize(ctx, itemKey, size);
2882
3695
  return size - prevSize;
2883
3696
  }
2884
3697
  return 0;
2885
3698
  }
3699
+ function useWrapIfItem(fn) {
3700
+ return React2.useMemo(
3701
+ () => fn ? (arg1, arg2, arg3) => arg1 !== void 0 && arg2 !== void 0 ? fn(arg1, arg2, arg3) : void 0 : void 0,
3702
+ [fn]
3703
+ );
3704
+ }
2886
3705
  var useCombinedRef = (...refs) => {
2887
3706
  const callback = React2.useCallback((element) => {
2888
3707
  for (const ref of refs) {
@@ -2898,19 +3717,13 @@ var useCombinedRef = (...refs) => {
2898
3717
  }, refs);
2899
3718
  return callback;
2900
3719
  };
2901
- function getWindowSize() {
2902
- const screenSize = reactNative.Dimensions.get("window");
2903
- return {
2904
- height: screenSize.height,
2905
- width: screenSize.width
2906
- };
2907
- }
2908
- var StyleSheet = reactNative.StyleSheet;
3720
+ var StyleSheet = ReactNative.StyleSheet;
2909
3721
  function useStickyScrollHandler(stickyHeaderIndices, horizontal, ctx, onScroll2) {
3722
+ const shouldUseRnAnimatedEngine = !ctx.state.props.stickyPositionComponentInternal;
2910
3723
  return React2.useMemo(() => {
2911
- if (stickyHeaderIndices == null ? void 0 : stickyHeaderIndices.length) {
3724
+ if ((stickyHeaderIndices == null ? void 0 : stickyHeaderIndices.length) && shouldUseRnAnimatedEngine) {
2912
3725
  const { animatedScrollY } = ctx;
2913
- return reactNative.Animated.event(
3726
+ return ReactNative.Animated.event(
2914
3727
  [
2915
3728
  {
2916
3729
  nativeEvent: {
@@ -2925,7 +3738,7 @@ function useStickyScrollHandler(stickyHeaderIndices, horizontal, ctx, onScroll2)
2925
3738
  );
2926
3739
  }
2927
3740
  return onScroll2;
2928
- }, [stickyHeaderIndices == null ? void 0 : stickyHeaderIndices.join(","), horizontal]);
3741
+ }, [stickyHeaderIndices == null ? void 0 : stickyHeaderIndices.join(","), horizontal, shouldUseRnAnimatedEngine]);
2929
3742
  }
2930
3743
 
2931
3744
  // src/utils/createColumnWrapperStyle.ts
@@ -2943,31 +3756,118 @@ function createColumnWrapperStyle(contentContainerStyle) {
2943
3756
  }
2944
3757
  }
2945
3758
 
3759
+ // src/utils/hasActiveMVCPAnchorLock.ts
3760
+ function hasActiveMVCPAnchorLock(state) {
3761
+ const lock = state.mvcpAnchorLock;
3762
+ if (!lock) {
3763
+ return false;
3764
+ }
3765
+ if (Date.now() > lock.expiresAt) {
3766
+ state.mvcpAnchorLock = void 0;
3767
+ return false;
3768
+ }
3769
+ return true;
3770
+ }
3771
+
2946
3772
  // src/utils/createImperativeHandle.ts
2947
- function createImperativeHandle(ctx, state) {
3773
+ function createImperativeHandle(ctx) {
3774
+ const state = ctx.state;
3775
+ const IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS = 800;
3776
+ const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
3777
+ let imperativeScrollToken = 0;
3778
+ const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0 || hasActiveMVCPAnchorLock(state);
3779
+ const runWhenSettled = (token, run) => {
3780
+ const startedAt = Date.now();
3781
+ let stableFrames = 0;
3782
+ const check = () => {
3783
+ if (token !== imperativeScrollToken) {
3784
+ return;
3785
+ }
3786
+ if (isSettlingAfterDataChange()) {
3787
+ stableFrames = 0;
3788
+ } else {
3789
+ stableFrames += 1;
3790
+ }
3791
+ const timedOut = Date.now() - startedAt >= IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS;
3792
+ if (stableFrames >= IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES || timedOut) {
3793
+ run();
3794
+ return;
3795
+ }
3796
+ requestAnimationFrame(check);
3797
+ };
3798
+ requestAnimationFrame(check);
3799
+ };
3800
+ const runScrollWithPromise = (run) => new Promise((resolve) => {
3801
+ var _a3;
3802
+ const token = ++imperativeScrollToken;
3803
+ (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
3804
+ state.pendingScrollResolve = resolve;
3805
+ const runNow = () => {
3806
+ if (token !== imperativeScrollToken) {
3807
+ return;
3808
+ }
3809
+ const didStartScroll = run();
3810
+ if (!didStartScroll || !state.scrollingTo) {
3811
+ if (state.pendingScrollResolve === resolve) {
3812
+ state.pendingScrollResolve = void 0;
3813
+ }
3814
+ resolve();
3815
+ }
3816
+ };
3817
+ if (isSettlingAfterDataChange()) {
3818
+ runWhenSettled(token, runNow);
3819
+ return;
3820
+ }
3821
+ runNow();
3822
+ });
2948
3823
  const scrollIndexIntoView = (options) => {
2949
3824
  if (state) {
2950
3825
  const { index, ...rest } = options;
2951
3826
  const { startNoBuffer, endNoBuffer } = state;
2952
3827
  if (index < startNoBuffer || index > endNoBuffer) {
2953
3828
  const viewPosition = index < startNoBuffer ? 0 : 1;
2954
- scrollToIndex(ctx, state, {
3829
+ scrollToIndex(ctx, {
2955
3830
  ...rest,
2956
3831
  index,
2957
3832
  viewPosition
2958
3833
  });
3834
+ return true;
2959
3835
  }
2960
3836
  }
3837
+ return false;
2961
3838
  };
2962
3839
  const refScroller = state.refScroller;
3840
+ const clearCaches = (options) => {
3841
+ var _a3, _b;
3842
+ const mode = (_a3 = options == null ? void 0 : options.mode) != null ? _a3 : "sizes";
3843
+ state.sizes.clear();
3844
+ state.sizesKnown.clear();
3845
+ for (const key in state.averageSizes) {
3846
+ delete state.averageSizes[key];
3847
+ }
3848
+ state.minIndexSizeChanged = 0;
3849
+ state.scrollForNextCalculateItemsInView = void 0;
3850
+ state.pendingTotalSize = void 0;
3851
+ state.totalSize = 0;
3852
+ set$(ctx, "totalSize", 0);
3853
+ if (mode === "full") {
3854
+ state.indexByKey.clear();
3855
+ state.idCache.length = 0;
3856
+ state.positions.length = 0;
3857
+ state.columns.length = 0;
3858
+ state.columnSpans.length = 0;
3859
+ }
3860
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
3861
+ };
2963
3862
  return {
3863
+ clearCaches,
2964
3864
  flashScrollIndicators: () => refScroller.current.flashScrollIndicators(),
2965
3865
  getNativeScrollRef: () => refScroller.current,
2966
3866
  getScrollableNode: () => refScroller.current.getScrollableNode(),
2967
3867
  getScrollResponder: () => refScroller.current.getScrollResponder(),
2968
3868
  getState: () => ({
2969
- activeStickyIndex: state.activeStickyIndex,
2970
- contentLength: state.totalSize,
3869
+ activeStickyIndex: peek$(ctx, "activeStickyIndex"),
3870
+ contentLength: getContentSize(ctx),
2971
3871
  data: state.props.data,
2972
3872
  elementAtIndex: (index) => {
2973
3873
  var _a3;
@@ -2977,47 +3877,71 @@ function createImperativeHandle(ctx, state) {
2977
3877
  endBuffered: state.endBuffered,
2978
3878
  isAtEnd: state.isAtEnd,
2979
3879
  isAtStart: state.isAtStart,
2980
- positionAtIndex: (index) => state.positions.get(getId(state, index)),
2981
- positions: state.positions,
3880
+ isEndReached: state.isEndReached,
3881
+ isStartReached: state.isStartReached,
3882
+ listen: (signalName, cb) => listen$(ctx, signalName, cb),
3883
+ listenToPosition: (key, cb) => listenPosition$(ctx, key, cb),
3884
+ positionAtIndex: (index) => state.positions[index],
3885
+ positionByKey: (key) => {
3886
+ const index = state.indexByKey.get(key);
3887
+ return index === void 0 ? void 0 : state.positions[index];
3888
+ },
2982
3889
  scroll: state.scroll,
2983
3890
  scrollLength: state.scrollLength,
3891
+ scrollVelocity: getScrollVelocity(state),
2984
3892
  sizeAtIndex: (index) => state.sizesKnown.get(getId(state, index)),
2985
3893
  sizes: state.sizesKnown,
2986
3894
  start: state.startNoBuffer,
2987
3895
  startBuffered: state.startBuffered
2988
3896
  }),
2989
- scrollIndexIntoView,
2990
- scrollItemIntoView: ({ item, ...props }) => {
3897
+ reportContentInset: (inset) => {
3898
+ state.contentInsetOverride = inset != null ? inset : void 0;
3899
+ updateScroll(ctx, state.scroll, true);
3900
+ },
3901
+ scrollIndexIntoView: (options) => runScrollWithPromise(() => scrollIndexIntoView(options)),
3902
+ scrollItemIntoView: ({ item, ...props }) => runScrollWithPromise(() => {
2991
3903
  const data = state.props.data;
2992
3904
  const index = data.indexOf(item);
2993
3905
  if (index !== -1) {
2994
3906
  scrollIndexIntoView({ index, ...props });
3907
+ return true;
2995
3908
  }
2996
- },
2997
- scrollToEnd: (options) => {
3909
+ return false;
3910
+ }),
3911
+ scrollToEnd: (options) => runScrollWithPromise(() => {
2998
3912
  const data = state.props.data;
2999
3913
  const stylePaddingBottom = state.props.stylePaddingBottom;
3000
3914
  const index = data.length - 1;
3001
3915
  if (index !== -1) {
3002
3916
  const paddingBottom = stylePaddingBottom || 0;
3003
3917
  const footerSize = peek$(ctx, "footerSize") || 0;
3004
- scrollToIndex(ctx, state, {
3918
+ scrollToIndex(ctx, {
3919
+ ...options,
3005
3920
  index,
3006
3921
  viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
3007
- viewPosition: 1,
3008
- ...options
3922
+ viewPosition: 1
3009
3923
  });
3924
+ return true;
3010
3925
  }
3011
- },
3012
- scrollToIndex: (params) => scrollToIndex(ctx, state, params),
3013
- scrollToItem: ({ item, ...props }) => {
3926
+ return false;
3927
+ }),
3928
+ scrollToIndex: (params) => runScrollWithPromise(() => {
3929
+ scrollToIndex(ctx, params);
3930
+ return true;
3931
+ }),
3932
+ scrollToItem: ({ item, ...props }) => runScrollWithPromise(() => {
3014
3933
  const data = state.props.data;
3015
3934
  const index = data.indexOf(item);
3016
3935
  if (index !== -1) {
3017
- scrollToIndex(ctx, state, { index, ...props });
3936
+ scrollToIndex(ctx, { index, ...props });
3937
+ return true;
3018
3938
  }
3019
- },
3020
- scrollToOffset: (params) => scrollTo(ctx, state, params),
3939
+ return false;
3940
+ }),
3941
+ scrollToOffset: (params) => runScrollWithPromise(() => {
3942
+ scrollTo(ctx, params);
3943
+ return true;
3944
+ }),
3021
3945
  setScrollProcessingEnabled: (enabled) => {
3022
3946
  state.scrollProcessingEnabled = enabled;
3023
3947
  },
@@ -3027,8 +3951,57 @@ function createImperativeHandle(ctx, state) {
3027
3951
  }
3028
3952
  };
3029
3953
  }
3030
- function getRenderedItem(ctx, state, key) {
3954
+
3955
+ // src/utils/getAlwaysRenderIndices.ts
3956
+ var sortAsc = (a, b) => a - b;
3957
+ var toCount = (value) => typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
3958
+ var addIndex = (result, dataLength, index) => {
3959
+ if (index >= 0 && index < dataLength) {
3960
+ result.add(index);
3961
+ }
3962
+ };
3963
+ function getAlwaysRenderIndices(config, data, keyExtractor) {
3964
+ var _a3, _b;
3965
+ if (!config || data.length === 0) {
3966
+ return [];
3967
+ }
3968
+ const result = /* @__PURE__ */ new Set();
3969
+ const dataLength = data.length;
3970
+ const topCount = toCount(config.top);
3971
+ if (topCount > 0) {
3972
+ for (let i = 0; i < Math.min(topCount, dataLength); i++) {
3973
+ addIndex(result, dataLength, i);
3974
+ }
3975
+ }
3976
+ const bottomCount = toCount(config.bottom);
3977
+ if (bottomCount > 0) {
3978
+ for (let i = Math.max(0, dataLength - bottomCount); i < dataLength; i++) {
3979
+ addIndex(result, dataLength, i);
3980
+ }
3981
+ }
3982
+ if ((_a3 = config.indices) == null ? void 0 : _a3.length) {
3983
+ for (const index of config.indices) {
3984
+ if (!Number.isFinite(index)) continue;
3985
+ addIndex(result, dataLength, Math.floor(index));
3986
+ }
3987
+ }
3988
+ if ((_b = config.keys) == null ? void 0 : _b.length) {
3989
+ const keys = new Set(config.keys);
3990
+ for (let i = 0; i < dataLength && keys.size > 0; i++) {
3991
+ const key = keyExtractor(data[i], i);
3992
+ if (keys.has(key)) {
3993
+ addIndex(result, dataLength, i);
3994
+ keys.delete(key);
3995
+ }
3996
+ }
3997
+ }
3998
+ const indices = Array.from(result);
3999
+ indices.sort(sortAsc);
4000
+ return indices;
4001
+ }
4002
+ function getRenderedItem(ctx, key) {
3031
4003
  var _a3;
4004
+ const state = ctx.state;
3032
4005
  if (!state) {
3033
4006
  return null;
3034
4007
  }
@@ -3055,6 +4028,70 @@ function getRenderedItem(ctx, state, key) {
3055
4028
  }
3056
4029
  return { index, item: data[index], renderedItem };
3057
4030
  }
4031
+
4032
+ // src/utils/normalizeMaintainScrollAtEnd.ts
4033
+ function normalizeMaintainScrollAtEndOn(on, hasExplicitOn) {
4034
+ var _a3, _b, _c;
4035
+ return {
4036
+ animated: false,
4037
+ onDataChange: hasExplicitOn ? (_a3 = on == null ? void 0 : on.dataChange) != null ? _a3 : false : true,
4038
+ onItemLayout: hasExplicitOn ? (_b = on == null ? void 0 : on.itemLayout) != null ? _b : false : true,
4039
+ onLayout: hasExplicitOn ? (_c = on == null ? void 0 : on.layout) != null ? _c : false : true
4040
+ };
4041
+ }
4042
+ function normalizeMaintainScrollAtEnd(value) {
4043
+ var _a3;
4044
+ if (!value) {
4045
+ return void 0;
4046
+ }
4047
+ if (value === true) {
4048
+ return {
4049
+ ...normalizeMaintainScrollAtEndOn(void 0, false),
4050
+ animated: false
4051
+ };
4052
+ }
4053
+ const normalizedTriggers = normalizeMaintainScrollAtEndOn(value.on, "on" in value);
4054
+ return {
4055
+ ...normalizedTriggers,
4056
+ animated: (_a3 = value.animated) != null ? _a3 : false
4057
+ };
4058
+ }
4059
+
4060
+ // src/utils/normalizeMaintainVisibleContentPosition.ts
4061
+ function normalizeMaintainVisibleContentPosition(value) {
4062
+ var _a3, _b;
4063
+ if (value === true) {
4064
+ return { data: true, size: true };
4065
+ }
4066
+ if (value && typeof value === "object") {
4067
+ return {
4068
+ data: (_a3 = value.data) != null ? _a3 : false,
4069
+ shouldRestorePosition: value.shouldRestorePosition,
4070
+ size: (_b = value.size) != null ? _b : true
4071
+ };
4072
+ }
4073
+ if (value === false) {
4074
+ return { data: false, size: false };
4075
+ }
4076
+ return { data: false, size: true };
4077
+ }
4078
+
4079
+ // src/utils/setPaddingTop.ts
4080
+ function setPaddingTop(ctx, { stylePaddingTop }) {
4081
+ const state = ctx.state;
4082
+ if (stylePaddingTop !== void 0) {
4083
+ const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
4084
+ if (stylePaddingTop < prevStylePaddingTop) {
4085
+ let prevTotalSize = peek$(ctx, "totalSize") || 0;
4086
+ set$(ctx, "totalSize", prevTotalSize + prevStylePaddingTop);
4087
+ state.timeoutSetPaddingTop = setTimeout(() => {
4088
+ prevTotalSize = peek$(ctx, "totalSize") || 0;
4089
+ set$(ctx, "totalSize", prevTotalSize - prevStylePaddingTop);
4090
+ }, 16);
4091
+ }
4092
+ set$(ctx, "stylePaddingTop", stylePaddingTop);
4093
+ }
4094
+ }
3058
4095
  function useThrottleDebounce(mode) {
3059
4096
  const timeoutRef = React2.useRef(null);
3060
4097
  const lastCallTimeRef = React2.useRef(0);
@@ -3102,9 +4139,8 @@ function useThrottledOnScroll(originalHandler, scrollEventThrottle) {
3102
4139
  }
3103
4140
 
3104
4141
  // src/components/LegendList.tsx
3105
- var DEFAULT_DRAW_DISTANCE = 250;
3106
- var DEFAULT_ITEM_SIZE = 100;
3107
- var LegendList = typedMemo(
4142
+ var LegendList = typedMemo2(
4143
+ // biome-ignore lint/nursery/noShadow: const function name shadowing is intentional
3108
4144
  typedForwardRef(function LegendList2(props, forwardedRef) {
3109
4145
  const { children, data: dataProp, renderItem: renderItemProp, ...restProps } = props;
3110
4146
  const isChildrenMode = children !== void 0 && dataProp === void 0;
@@ -3122,16 +4158,17 @@ var LegendList = typedMemo(
3122
4158
  })
3123
4159
  );
3124
4160
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
3125
- var _a3, _b;
4161
+ var _a3, _b, _c, _d, _e, _f, _g, _h;
3126
4162
  const {
3127
4163
  alignItemsAtEnd = false,
4164
+ alwaysRender,
3128
4165
  columnWrapperStyle,
3129
4166
  contentContainerStyle: contentContainerStyleProp,
4167
+ contentInset,
3130
4168
  data: dataProp = [],
3131
4169
  dataVersion,
3132
4170
  drawDistance = 250,
3133
- enableAverages = true,
3134
- estimatedItemSize: estimatedItemSizeProp,
4171
+ estimatedItemSize = 100,
3135
4172
  estimatedListSize,
3136
4173
  extraData,
3137
4174
  getEstimatedItemSize,
@@ -3148,11 +4185,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3148
4185
  ListHeaderComponent,
3149
4186
  maintainScrollAtEnd = false,
3150
4187
  maintainScrollAtEndThreshold = 0.1,
3151
- maintainVisibleContentPosition = false,
4188
+ maintainVisibleContentPosition: maintainVisibleContentPositionProp,
3152
4189
  numColumns: numColumnsProp = 1,
4190
+ overrideItemLayout,
3153
4191
  onEndReached,
3154
4192
  onEndReachedThreshold = 0.5,
3155
4193
  onItemSizeChanged,
4194
+ onMetricsChange,
3156
4195
  onLayout: onLayoutProp,
3157
4196
  onLoad,
3158
4197
  onMomentumScrollEnd,
@@ -3167,50 +4206,110 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3167
4206
  refreshControl,
3168
4207
  refreshing,
3169
4208
  refScrollView,
4209
+ renderScrollComponent,
3170
4210
  renderItem,
3171
4211
  scrollEventThrottle,
3172
4212
  snapToIndices,
3173
4213
  stickyHeaderIndices: stickyHeaderIndicesProp,
3174
4214
  stickyIndices: stickyIndicesDeprecated,
4215
+ // TODOV3: Remove from v3 release
3175
4216
  style: styleProp,
3176
4217
  suggestEstimatedItemSize,
4218
+ useWindowScroll = false,
3177
4219
  viewabilityConfig,
3178
4220
  viewabilityConfigCallbackPairs,
3179
4221
  waitForInitialLayout = true,
3180
4222
  ...rest
3181
4223
  } = props;
3182
- const { childrenMode } = rest;
3183
- const contentContainerStyle = { ...StyleSheet.flatten(contentContainerStyleProp) };
4224
+ const animatedPropsInternal = props.animatedPropsInternal;
4225
+ const positionComponentInternal = props.positionComponentInternal;
4226
+ const stickyPositionComponentInternal = props.stickyPositionComponentInternal;
4227
+ const {
4228
+ childrenMode,
4229
+ positionComponentInternal: _positionComponentInternal,
4230
+ stickyPositionComponentInternal: _stickyPositionComponentInternal,
4231
+ ...restProps
4232
+ } = rest;
4233
+ const contentContainerStyleBase = StyleSheet.flatten(contentContainerStyleProp);
4234
+ const shouldFlexGrow = alignItemsAtEnd && (horizontal ? (contentContainerStyleBase == null ? void 0 : contentContainerStyleBase.minWidth) == null : (contentContainerStyleBase == null ? void 0 : contentContainerStyleBase.minHeight) == null);
4235
+ const contentContainerStyle = {
4236
+ ...contentContainerStyleBase,
4237
+ ...alignItemsAtEnd ? {
4238
+ display: "flex",
4239
+ flexDirection: horizontal ? "row" : "column",
4240
+ ...shouldFlexGrow ? { flexGrow: 1 } : {},
4241
+ justifyContent: "flex-end"
4242
+ } : {}
4243
+ };
3184
4244
  const style = { ...StyleSheet.flatten(styleProp) };
3185
4245
  const stylePaddingTopState = extractPadding(style, contentContainerStyle, "Top");
3186
4246
  const stylePaddingBottomState = extractPadding(style, contentContainerStyle, "Bottom");
3187
- const [renderNum, setRenderNum] = React2.useState(0);
3188
- 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;
4247
+ const maintainScrollAtEndConfig = normalizeMaintainScrollAtEnd(maintainScrollAtEnd);
4248
+ const maintainVisibleContentPositionConfig = normalizeMaintainVisibleContentPosition(
4249
+ maintainVisibleContentPositionProp
4250
+ );
4251
+ const hasInitialScrollIndex = initialScrollIndexProp !== void 0 && initialScrollIndexProp !== null;
4252
+ const hasInitialScrollOffset = initialScrollOffsetProp !== void 0 && initialScrollOffsetProp !== null;
4253
+ const initialScrollUsesOffsetOnly = !initialScrollAtEnd && !hasInitialScrollIndex && hasInitialScrollOffset;
4254
+ const initialScrollProp = initialScrollAtEnd ? { index: Math.max(0, dataProp.length - 1), viewOffset: -stylePaddingBottomState, viewPosition: 1 } : hasInitialScrollIndex ? typeof initialScrollIndexProp === "object" ? {
4255
+ index: (_a3 = initialScrollIndexProp.index) != null ? _a3 : 0,
4256
+ viewOffset: (_b = initialScrollIndexProp.viewOffset) != null ? _b : initialScrollIndexProp.viewPosition === 1 ? -stylePaddingBottomState : 0,
4257
+ viewPosition: (_c = initialScrollIndexProp.viewPosition) != null ? _c : 0
4258
+ } : {
4259
+ index: initialScrollIndexProp != null ? initialScrollIndexProp : 0,
4260
+ viewOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : 0
4261
+ } : initialScrollUsesOffsetOnly ? {
4262
+ contentOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : 0,
4263
+ index: 0,
4264
+ viewOffset: 0
4265
+ } : void 0;
3189
4266
  const [canRender, setCanRender] = React2__namespace.useState(!IsNewArchitecture);
3190
4267
  const ctx = useStateContext();
3191
4268
  ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
3192
4269
  const refScroller = React2.useRef(null);
3193
4270
  const combinedRef = useCombinedRef(refScroller, refScrollView);
3194
- const estimatedItemSize = estimatedItemSizeProp != null ? estimatedItemSizeProp : DEFAULT_ITEM_SIZE;
3195
- const scrollBuffer = (drawDistance != null ? drawDistance : DEFAULT_DRAW_DISTANCE) || 1;
3196
- const keyExtractor = keyExtractorProp != null ? keyExtractorProp : (_item, index) => index.toString();
4271
+ const keyExtractor = keyExtractorProp != null ? keyExtractorProp : ((_item, index) => index.toString());
3197
4272
  const stickyHeaderIndices = stickyHeaderIndicesProp != null ? stickyHeaderIndicesProp : stickyIndicesDeprecated;
4273
+ const alwaysRenderIndices = React2.useMemo(() => {
4274
+ const indices = getAlwaysRenderIndices(alwaysRender, dataProp, keyExtractor);
4275
+ return { arr: indices, set: new Set(indices) };
4276
+ }, [
4277
+ alwaysRender == null ? void 0 : alwaysRender.top,
4278
+ alwaysRender == null ? void 0 : alwaysRender.bottom,
4279
+ (_d = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _d.join(","),
4280
+ (_e = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _e.join(","),
4281
+ dataProp,
4282
+ dataVersion,
4283
+ keyExtractor
4284
+ ]);
3198
4285
  if (IS_DEV && stickyIndicesDeprecated && !stickyHeaderIndicesProp) {
3199
4286
  warnDevOnce(
3200
4287
  "stickyIndices",
3201
4288
  "stickyIndices has been renamed to stickyHeaderIndices. Please update your props to use stickyHeaderIndices."
3202
4289
  );
3203
4290
  }
3204
- const refState = React2.useRef();
4291
+ if (IS_DEV && useWindowScroll && renderScrollComponent) {
4292
+ warnDevOnce(
4293
+ "useWindowScrollRenderScrollComponent",
4294
+ "useWindowScroll is not supported when renderScrollComponent is provided."
4295
+ );
4296
+ }
4297
+ const useWindowScrollResolved = Platform2.OS === "web" && !!useWindowScroll && !renderScrollComponent;
4298
+ const refState = React2.useRef(void 0);
4299
+ const hasOverrideItemLayout = !!overrideItemLayout;
4300
+ const prevHasOverrideItemLayout = React2.useRef(hasOverrideItemLayout);
3205
4301
  if (!refState.current) {
3206
- if (!ctx.internalState) {
4302
+ if (!ctx.state) {
3207
4303
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : IsNewArchitecture ? { height: 0, width: 0 } : getWindowSize())[horizontal ? "width" : "height"];
3208
- ctx.internalState = {
3209
- activeStickyIndex: void 0,
4304
+ ctx.state = {
4305
+ activeStickyIndex: -1,
3210
4306
  averageSizes: {},
3211
- columns: /* @__PURE__ */ new Map(),
3212
- containerItemKeys: /* @__PURE__ */ new Set(),
4307
+ columnSpans: [],
4308
+ columns: [],
4309
+ containerItemKeys: /* @__PURE__ */ new Map(),
3213
4310
  containerItemTypes: /* @__PURE__ */ new Map(),
4311
+ contentInsetOverride: void 0,
4312
+ dataChangeEpoch: 0,
3214
4313
  dataChangeNeedsScrollUpdate: false,
3215
4314
  didColumnsChange: false,
3216
4315
  didDataChange: false,
@@ -3222,28 +4321,39 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3222
4321
  idCache: [],
3223
4322
  idsInView: [],
3224
4323
  indexByKey: /* @__PURE__ */ new Map(),
3225
- initialAnchor: (initialScrollProp == null ? void 0 : initialScrollProp.index) !== void 0 && (initialScrollProp == null ? void 0 : initialScrollProp.viewPosition) !== void 0 ? {
4324
+ initialAnchor: !initialScrollUsesOffsetOnly && (initialScrollProp == null ? void 0 : initialScrollProp.index) !== void 0 && (initialScrollProp == null ? void 0 : initialScrollProp.viewPosition) !== void 0 ? {
3226
4325
  attempts: 0,
3227
4326
  index: initialScrollProp.index,
3228
4327
  settledTicks: 0,
3229
- viewOffset: (_a3 = initialScrollProp.viewOffset) != null ? _a3 : 0,
4328
+ viewOffset: (_f = initialScrollProp.viewOffset) != null ? _f : 0,
3230
4329
  viewPosition: initialScrollProp.viewPosition
3231
4330
  } : void 0,
4331
+ initialNativeScrollWatchdog: void 0,
3232
4332
  initialScroll: initialScrollProp,
4333
+ initialScrollLastDidFinish: false,
4334
+ initialScrollLastTarget: initialScrollProp,
4335
+ initialScrollLastTargetUsesOffset: initialScrollUsesOffsetOnly,
4336
+ initialScrollPreviousDataLength: dataProp.length,
4337
+ initialScrollRetryLastLength: void 0,
4338
+ initialScrollRetryWindowUntil: 0,
4339
+ initialScrollUsesOffset: initialScrollUsesOffsetOnly,
3233
4340
  isAtEnd: false,
3234
4341
  isAtStart: false,
3235
- isEndReached: false,
4342
+ isEndReached: null,
3236
4343
  isFirst: true,
3237
- isStartReached: false,
4344
+ isStartReached: null,
3238
4345
  lastBatchingAction: Date.now(),
3239
4346
  lastLayout: void 0,
4347
+ lastScrollDelta: 0,
3240
4348
  loadStartTime: Date.now(),
3241
4349
  minIndexSizeChanged: 0,
4350
+ nativeContentInset: void 0,
3242
4351
  nativeMarginTop: 0,
3243
- positions: /* @__PURE__ */ new Map(),
4352
+ pendingNativeMVCPAdjust: void 0,
4353
+ positions: [],
3244
4354
  props: {},
3245
4355
  queuedCalculateItemsInView: 0,
3246
- refScroller: void 0,
4356
+ refScroller: { current: null },
3247
4357
  scroll: 0,
3248
4358
  scrollAdjustHandler: new ScrollAdjustHandler(ctx),
3249
4359
  scrollForNextCalculateItemsInView: void 0,
@@ -3259,6 +4369,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3259
4369
  startBuffered: -1,
3260
4370
  startNoBuffer: -1,
3261
4371
  startReachedSnapshot: void 0,
4372
+ startReachedSnapshotDataChangeEpoch: void 0,
3262
4373
  stickyContainerPool: /* @__PURE__ */ new Set(),
3263
4374
  stickyContainers: /* @__PURE__ */ new Map(),
3264
4375
  timeoutSizeMessage: 0,
@@ -3266,18 +4377,21 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3266
4377
  totalSize: 0,
3267
4378
  viewabilityConfigCallbackPairs: void 0
3268
4379
  };
3269
- const internalState = ctx.internalState;
3270
- internalState.triggerCalculateItemsInView = (params) => calculateItemsInView(ctx, internalState, params);
3271
- set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPosition);
4380
+ const internalState = ctx.state;
4381
+ internalState.triggerCalculateItemsInView = (params) => calculateItemsInView(ctx, params);
4382
+ set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPositionConfig);
3272
4383
  set$(ctx, "extraData", extraData);
3273
4384
  }
3274
- refState.current = ctx.internalState;
4385
+ refState.current = ctx.state;
3275
4386
  }
3276
4387
  const state = refState.current;
3277
4388
  const isFirstLocal = state.isFirst;
3278
4389
  state.didColumnsChange = numColumnsProp !== state.props.numColumns;
3279
- const didDataChangeLocal = state.props.dataVersion !== dataVersion || state.props.data !== dataProp && checkActualChange(state, dataProp, state.props.data);
4390
+ const didDataReferenceChangeLocal = state.props.data !== dataProp;
4391
+ const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
4392
+ const didDataChangeLocal = didDataVersionChangeLocal || didDataReferenceChangeLocal && checkActualChange(state, dataProp, state.props.data);
3280
4393
  if (didDataChangeLocal) {
4394
+ state.dataChangeEpoch += 1;
3281
4395
  state.dataChangeNeedsScrollUpdate = true;
3282
4396
  state.didDataChange = true;
3283
4397
  state.previousData = state.props.data;
@@ -3285,20 +4399,25 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3285
4399
  const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
3286
4400
  state.props = {
3287
4401
  alignItemsAtEnd,
4402
+ alwaysRender,
4403
+ alwaysRenderIndicesArr: alwaysRenderIndices.arr,
4404
+ alwaysRenderIndicesSet: alwaysRenderIndices.set,
4405
+ animatedProps: animatedPropsInternal,
4406
+ contentInset,
3288
4407
  data: dataProp,
3289
4408
  dataVersion,
3290
- enableAverages,
4409
+ drawDistance,
3291
4410
  estimatedItemSize,
3292
- getEstimatedItemSize,
3293
- getFixedItemSize,
3294
- getItemType,
4411
+ getEstimatedItemSize: useWrapIfItem(getEstimatedItemSize),
4412
+ getFixedItemSize: useWrapIfItem(getFixedItemSize),
4413
+ getItemType: useWrapIfItem(getItemType),
3295
4414
  horizontal: !!horizontal,
3296
4415
  initialContainerPoolRatio,
3297
4416
  itemsAreEqual,
3298
- keyExtractor,
3299
- maintainScrollAtEnd,
4417
+ keyExtractor: useWrapIfItem(keyExtractor),
4418
+ maintainScrollAtEnd: maintainScrollAtEndConfig,
3300
4419
  maintainScrollAtEndThreshold,
3301
- maintainVisibleContentPosition,
4420
+ maintainVisibleContentPosition: maintainVisibleContentPositionConfig,
3302
4421
  numColumns: numColumnsProp,
3303
4422
  onEndReached,
3304
4423
  onEndReachedThreshold,
@@ -3308,15 +4427,18 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3308
4427
  onStartReached,
3309
4428
  onStartReachedThreshold,
3310
4429
  onStickyHeaderChange,
4430
+ overrideItemLayout,
4431
+ positionComponentInternal,
3311
4432
  recycleItems: !!recycleItems,
3312
4433
  renderItem,
3313
- scrollBuffer,
3314
4434
  snapToIndices,
3315
4435
  stickyIndicesArr: stickyHeaderIndices != null ? stickyHeaderIndices : [],
3316
4436
  stickyIndicesSet: React2.useMemo(() => new Set(stickyHeaderIndices != null ? stickyHeaderIndices : []), [stickyHeaderIndices == null ? void 0 : stickyHeaderIndices.join(",")]),
4437
+ stickyPositionComponentInternal,
3317
4438
  stylePaddingBottom: stylePaddingBottomState,
3318
4439
  stylePaddingTop: stylePaddingTopState,
3319
- suggestEstimatedItemSize: !!suggestEstimatedItemSize
4440
+ suggestEstimatedItemSize: !!suggestEstimatedItemSize,
4441
+ useWindowScroll: useWindowScrollResolved
3320
4442
  };
3321
4443
  state.refScroller = refScroller;
3322
4444
  const memoizedLastItemKeys = React2.useMemo(() => {
@@ -3326,62 +4448,138 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3326
4448
  (_, i) => getId(state, dataProp.length - 1 - i)
3327
4449
  );
3328
4450
  }, [dataProp, dataVersion, numColumnsProp]);
3329
- const initializeStateVars = () => {
4451
+ const initializeStateVars = (shouldAdjustPadding) => {
3330
4452
  set$(ctx, "lastItemKeys", memoizedLastItemKeys);
3331
4453
  set$(ctx, "numColumns", numColumnsProp);
3332
4454
  const prevPaddingTop = peek$(ctx, "stylePaddingTop");
3333
- setPaddingTop(ctx, state, { stylePaddingTop: stylePaddingTopState });
4455
+ setPaddingTop(ctx, { stylePaddingTop: stylePaddingTopState });
3334
4456
  refState.current.props.stylePaddingBottom = stylePaddingBottomState;
3335
4457
  let paddingDiff = stylePaddingTopState - prevPaddingTop;
3336
- if (paddingDiff && prevPaddingTop !== void 0 && Platform2.OS === "ios") {
4458
+ if (shouldAdjustPadding && maintainVisibleContentPositionConfig.size && paddingDiff && prevPaddingTop !== void 0 && Platform2.OS === "ios") {
3337
4459
  if (state.scroll < 0) {
3338
4460
  paddingDiff += state.scroll;
3339
4461
  }
3340
- requestAdjust(ctx, state, paddingDiff);
4462
+ requestAdjust(ctx, paddingDiff);
3341
4463
  }
3342
4464
  };
3343
4465
  if (isFirstLocal) {
3344
- initializeStateVars();
4466
+ initializeStateVars(false);
3345
4467
  updateItemPositions(
3346
4468
  ctx,
3347
- state,
3348
4469
  /*dataChanged*/
3349
4470
  true
3350
4471
  );
3351
4472
  }
4473
+ const resolveInitialScrollOffset = React2.useCallback((initialScroll) => {
4474
+ var _a4;
4475
+ if (state.initialScrollUsesOffset) {
4476
+ return clampScrollOffset(ctx, (_a4 = initialScroll.contentOffset) != null ? _a4 : 0);
4477
+ }
4478
+ const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, initialScroll.index) : 0;
4479
+ const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, initialScroll);
4480
+ return clampScrollOffset(ctx, resolvedOffset, initialScroll);
4481
+ }, []);
4482
+ const finishInitialScrollWithoutScroll = React2.useCallback(() => {
4483
+ refState.current.initialAnchor = void 0;
4484
+ refState.current.initialScroll = void 0;
4485
+ state.initialAnchor = void 0;
4486
+ state.initialScroll = void 0;
4487
+ state.initialScrollUsesOffset = false;
4488
+ state.initialScrollLastTarget = void 0;
4489
+ state.initialScrollLastTargetUsesOffset = false;
4490
+ setInitialRenderState(ctx, { didInitialScroll: true });
4491
+ }, []);
4492
+ const setActiveInitialScrollTarget = React2.useCallback(
4493
+ (target, options) => {
4494
+ var _a4;
4495
+ const usesOffset = !!(options == null ? void 0 : options.usesOffset);
4496
+ state.initialScrollUsesOffset = usesOffset;
4497
+ state.initialScrollLastTarget = target;
4498
+ state.initialScrollLastTargetUsesOffset = usesOffset;
4499
+ refState.current.initialScroll = target;
4500
+ state.initialScroll = target;
4501
+ if ((options == null ? void 0 : options.resetDidFinish) && state.didFinishInitialScroll) {
4502
+ state.didFinishInitialScroll = false;
4503
+ }
4504
+ if (!(options == null ? void 0 : options.syncAnchor)) {
4505
+ return;
4506
+ }
4507
+ if (!IsNewArchitecture && !usesOffset && target.index !== void 0 && target.viewPosition !== void 0) {
4508
+ state.initialAnchor = {
4509
+ attempts: 0,
4510
+ index: target.index,
4511
+ settledTicks: 0,
4512
+ viewOffset: (_a4 = target.viewOffset) != null ? _a4 : 0,
4513
+ viewPosition: target.viewPosition
4514
+ };
4515
+ }
4516
+ },
4517
+ []
4518
+ );
4519
+ const shouldFinishInitialScrollAtOrigin = React2.useCallback(
4520
+ (initialScroll, offset) => {
4521
+ var _a4, _b2, _c2;
4522
+ if (offset !== 0 || initialScrollAtEnd) {
4523
+ return false;
4524
+ }
4525
+ if (state.initialScrollUsesOffset) {
4526
+ return Math.abs((_a4 = initialScroll.contentOffset) != null ? _a4 : 0) <= 1;
4527
+ }
4528
+ return initialScroll.index === 0 && ((_b2 = initialScroll.viewPosition) != null ? _b2 : 0) === 0 && Math.abs((_c2 = initialScroll.viewOffset) != null ? _c2 : 0) <= 1;
4529
+ },
4530
+ [initialScrollAtEnd]
4531
+ );
4532
+ const shouldFinishEmptyInitialScrollAtEnd = React2.useCallback(
4533
+ (initialScroll, offset) => {
4534
+ return dataProp.length === 0 && initialScrollAtEnd && offset === 0 && initialScroll.viewPosition === 1;
4535
+ },
4536
+ [dataProp.length, initialScrollAtEnd]
4537
+ );
4538
+ const shouldRearmFinishedEmptyInitialScrollAtEnd = React2.useCallback(
4539
+ (initialScroll) => {
4540
+ var _a4;
4541
+ return !!(state.didFinishInitialScroll && dataProp.length > 0 && initialScroll && !state.initialScrollUsesOffset && initialScroll.index === 0 && initialScroll.viewPosition === 1 && ((_a4 = initialScroll.contentOffset) != null ? _a4 : 0) === 0);
4542
+ },
4543
+ [dataProp.length]
4544
+ );
3352
4545
  const initialContentOffset = React2.useMemo(() => {
3353
- var _a4, _b2;
3354
- const { initialScroll } = refState.current;
3355
- if (!initialScroll) {
4546
+ var _a4;
4547
+ let value;
4548
+ const { initialScroll, initialAnchor } = refState.current;
4549
+ if (initialScroll) {
4550
+ if (!state.initialScrollUsesOffset && !IsNewArchitecture && initialScroll.index !== void 0 && (!initialAnchor || (initialAnchor == null ? void 0 : initialAnchor.index) !== initialScroll.index)) {
4551
+ refState.current.initialAnchor = {
4552
+ attempts: 0,
4553
+ index: initialScroll.index,
4554
+ settledTicks: 0,
4555
+ viewOffset: (_a4 = initialScroll.viewOffset) != null ? _a4 : 0,
4556
+ viewPosition: initialScroll.viewPosition
4557
+ };
4558
+ }
4559
+ if (initialScroll.contentOffset !== void 0) {
4560
+ value = initialScroll.contentOffset;
4561
+ } else {
4562
+ const clampedOffset = resolveInitialScrollOffset(initialScroll);
4563
+ const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
4564
+ setActiveInitialScrollTarget(updatedInitialScroll, {
4565
+ usesOffset: state.initialScrollUsesOffset
4566
+ });
4567
+ value = clampedOffset;
4568
+ }
4569
+ } else {
3356
4570
  refState.current.initialAnchor = void 0;
3357
- return 0;
3358
- }
3359
- if (initialScroll.index !== void 0 && (!refState.current.initialAnchor || ((_a4 = refState.current.initialAnchor) == null ? void 0 : _a4.index) !== initialScroll.index)) {
3360
- refState.current.initialAnchor = {
3361
- attempts: 0,
3362
- index: initialScroll.index,
3363
- settledTicks: 0,
3364
- viewOffset: (_b2 = initialScroll.viewOffset) != null ? _b2 : 0,
3365
- viewPosition: initialScroll.viewPosition
3366
- };
4571
+ value = 0;
4572
+ }
4573
+ const hasPendingDataDependentInitialScroll = !!initialScroll && dataProp.length === 0 && !shouldFinishInitialScrollAtOrigin(initialScroll, value) && !shouldFinishEmptyInitialScrollAtEnd(initialScroll, value);
4574
+ if (!value && !hasPendingDataDependentInitialScroll) {
4575
+ if (initialScroll && shouldFinishInitialScrollAtOrigin(initialScroll, value)) {
4576
+ finishInitialScrollWithoutScroll();
4577
+ } else {
4578
+ setInitialRenderState(ctx, { didInitialScroll: true });
4579
+ }
3367
4580
  }
3368
- if (initialScroll.contentOffset !== void 0) {
3369
- return initialScroll.contentOffset;
3370
- }
3371
- const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, state, initialScroll.index) : 0;
3372
- const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, state, baseOffset, initialScroll);
3373
- let clampedOffset = resolvedOffset;
3374
- if (Number.isFinite(state.scrollLength) && Number.isFinite(state.totalSize)) {
3375
- const maxOffset = Math.max(0, state.totalSize - state.scrollLength);
3376
- clampedOffset = Math.min(clampedOffset, maxOffset);
3377
- }
3378
- clampedOffset = Math.max(0, clampedOffset);
3379
- const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
3380
- refState.current.initialScroll = updatedInitialScroll;
3381
- state.initialScroll = updatedInitialScroll;
3382
- refState.current.isStartReached = clampedOffset < refState.current.scrollLength * onStartReachedThreshold;
3383
- return clampedOffset;
3384
- }, [renderNum]);
4581
+ return value;
4582
+ }, []);
3385
4583
  if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
3386
4584
  refState.current.lastBatchingAction = Date.now();
3387
4585
  if (!keyExtractorProp && !isFirstLocal && didDataChangeLocal) {
@@ -3390,41 +4588,192 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3390
4588
  "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."
3391
4589
  );
3392
4590
  refState.current.sizes.clear();
3393
- refState.current.positions.clear();
4591
+ refState.current.positions.length = 0;
3394
4592
  refState.current.totalSize = 0;
3395
4593
  set$(ctx, "totalSize", 0);
3396
4594
  }
3397
4595
  }
3398
- const onLayoutHeader = React2.useCallback((rect, fromLayoutEffect) => {
3399
- const { initialScroll } = refState.current;
3400
- const size = rect[horizontal ? "width" : "height"];
3401
- set$(ctx, "headerSize", size);
3402
- if ((initialScroll == null ? void 0 : initialScroll.index) !== void 0) {
3403
- if (IsNewArchitecture && Platform2.OS !== "android") {
3404
- if (fromLayoutEffect) {
3405
- setRenderNum((v) => v + 1);
4596
+ const doInitialScroll = React2.useCallback((options) => {
4597
+ var _a4, _b2;
4598
+ const allowPostFinishRetry = !!(options == null ? void 0 : options.allowPostFinishRetry);
4599
+ const { didFinishInitialScroll, queuedInitialLayout, scrollingTo } = state;
4600
+ const initialScroll = (_a4 = state.initialScroll) != null ? _a4 : allowPostFinishRetry ? state.initialScrollLastTarget : void 0;
4601
+ const isInitialScrollInProgress = !!(scrollingTo == null ? void 0 : scrollingTo.isInitialScroll);
4602
+ const needsContainerLayoutForInitialScroll = !state.initialScrollUsesOffset;
4603
+ const shouldWaitForInitialLayout = waitForInitialLayout && needsContainerLayoutForInitialScroll && !queuedInitialLayout && !allowPostFinishRetry && !isInitialScrollInProgress;
4604
+ if (!initialScroll || shouldWaitForInitialLayout || didFinishInitialScroll && !allowPostFinishRetry || scrollingTo && !isInitialScrollInProgress) {
4605
+ return;
4606
+ }
4607
+ if (allowPostFinishRetry && state.initialScrollLastTargetUsesOffset) {
4608
+ return;
4609
+ }
4610
+ const didMoveAwayFromInitialTarget = allowPostFinishRetry && initialScroll.contentOffset !== void 0 && Math.abs(state.scroll - initialScroll.contentOffset) > 1;
4611
+ if (didMoveAwayFromInitialTarget) {
4612
+ state.initialScrollRetryWindowUntil = 0;
4613
+ return;
4614
+ }
4615
+ const offset = resolveInitialScrollOffset(initialScroll);
4616
+ const activeInitialTargetOffset = isInitialScrollInProgress ? (_b2 = scrollingTo.targetOffset) != null ? _b2 : scrollingTo.offset : void 0;
4617
+ const didOffsetChange = initialScroll.contentOffset === void 0 || Math.abs(initialScroll.contentOffset - offset) > 1;
4618
+ const didActiveInitialTargetChange = activeInitialTargetOffset !== void 0 && Math.abs(activeInitialTargetOffset - offset) > 1;
4619
+ if (!didOffsetChange && (allowPostFinishRetry || isInitialScrollInProgress && !didActiveInitialTargetChange)) {
4620
+ return;
4621
+ }
4622
+ if (didOffsetChange) {
4623
+ const updatedInitialScroll = { ...initialScroll, contentOffset: offset };
4624
+ if (!state.initialScrollUsesOffset) {
4625
+ state.initialScrollLastTarget = updatedInitialScroll;
4626
+ state.initialScrollLastTargetUsesOffset = false;
4627
+ if (state.initialScroll) {
4628
+ refState.current.initialScroll = updatedInitialScroll;
4629
+ state.initialScroll = updatedInitialScroll;
3406
4630
  }
3407
- } else {
3408
- setTimeout(doInitialScroll, 17);
3409
4631
  }
3410
4632
  }
4633
+ const hasMeasuredScrollLayout = !!state.lastLayout && state.scrollLength > 0;
4634
+ const shouldForceNativeInitialScroll = state.initialScrollUsesOffset && hasMeasuredScrollLayout || allowPostFinishRetry || !!queuedInitialLayout || isInitialScrollInProgress && didOffsetChange;
4635
+ performInitialScroll(ctx, {
4636
+ forceScroll: shouldForceNativeInitialScroll,
4637
+ initialScrollUsesOffset: state.initialScrollUsesOffset,
4638
+ resolvedOffset: offset,
4639
+ target: initialScroll
4640
+ });
3411
4641
  }, []);
3412
- const doInitialScroll = React2.useCallback(() => {
4642
+ React2.useLayoutEffect(() => {
3413
4643
  var _a4;
3414
- const initialScroll = state.initialScroll;
3415
- if (initialScroll) {
3416
- scrollTo(ctx, state, {
3417
- animated: false,
3418
- index: (_a4 = state.initialScroll) == null ? void 0 : _a4.index,
3419
- isInitialScroll: true,
3420
- offset: initialContentOffset,
3421
- precomputedWithViewOffset: true
4644
+ const previousDataLength = state.initialScrollPreviousDataLength;
4645
+ state.initialScrollPreviousDataLength = dataProp.length;
4646
+ if (previousDataLength !== 0 || dataProp.length === 0 || !state.initialScroll || !state.queuedInitialLayout) {
4647
+ return;
4648
+ }
4649
+ if (initialScrollAtEnd) {
4650
+ const lastIndex = Math.max(0, dataProp.length - 1);
4651
+ const initialScroll = state.initialScroll;
4652
+ const shouldRearm = shouldRearmFinishedEmptyInitialScrollAtEnd(initialScroll);
4653
+ if (state.didFinishInitialScroll && !shouldRearm) {
4654
+ return;
4655
+ }
4656
+ if (initialScroll && !state.initialScrollUsesOffset && initialScroll.index === lastIndex && initialScroll.viewPosition === 1 && !shouldRearm) {
4657
+ return;
4658
+ }
4659
+ const updatedInitialScroll = {
4660
+ contentOffset: void 0,
4661
+ index: lastIndex,
4662
+ viewOffset: (_a4 = initialScroll == null ? void 0 : initialScroll.viewOffset) != null ? _a4 : -stylePaddingBottomState,
4663
+ viewPosition: 1
4664
+ };
4665
+ setActiveInitialScrollTarget(updatedInitialScroll, {
4666
+ resetDidFinish: shouldRearm,
4667
+ syncAnchor: true
3422
4668
  });
4669
+ doInitialScroll();
4670
+ return;
4671
+ }
4672
+ if (state.didFinishInitialScroll) {
4673
+ return;
4674
+ }
4675
+ doInitialScroll();
4676
+ }, [
4677
+ dataProp.length,
4678
+ doInitialScroll,
4679
+ initialScrollAtEnd,
4680
+ shouldRearmFinishedEmptyInitialScrollAtEnd,
4681
+ stylePaddingBottomState
4682
+ ]);
4683
+ React2.useLayoutEffect(() => {
4684
+ var _a4;
4685
+ if (!initialScrollAtEnd) {
4686
+ return;
4687
+ }
4688
+ const lastIndex = Math.max(0, dataProp.length - 1);
4689
+ const initialScroll = state.initialScroll;
4690
+ const shouldRearm = shouldRearmFinishedEmptyInitialScrollAtEnd(initialScroll);
4691
+ if (state.didFinishInitialScroll && !shouldRearm) {
4692
+ return;
4693
+ }
4694
+ if (shouldRearm) {
4695
+ state.didFinishInitialScroll = false;
3423
4696
  }
3424
- }, [initialContentOffset]);
4697
+ if (initialScroll && !state.initialScrollUsesOffset && initialScroll.index === lastIndex && initialScroll.viewPosition === 1 && !shouldRearm) {
4698
+ return;
4699
+ }
4700
+ const updatedInitialScroll = {
4701
+ contentOffset: void 0,
4702
+ index: lastIndex,
4703
+ viewOffset: (_a4 = initialScroll == null ? void 0 : initialScroll.viewOffset) != null ? _a4 : -stylePaddingBottomState,
4704
+ viewPosition: 1
4705
+ };
4706
+ setActiveInitialScrollTarget(updatedInitialScroll, {
4707
+ resetDidFinish: shouldRearm,
4708
+ syncAnchor: true
4709
+ });
4710
+ doInitialScroll();
4711
+ }, [
4712
+ dataProp.length,
4713
+ doInitialScroll,
4714
+ initialScrollAtEnd,
4715
+ shouldRearmFinishedEmptyInitialScrollAtEnd,
4716
+ stylePaddingBottomState
4717
+ ]);
4718
+ const onLayoutFooter = React2.useCallback(
4719
+ (layout) => {
4720
+ var _a4;
4721
+ if (!initialScrollAtEnd) {
4722
+ return;
4723
+ }
4724
+ const { initialScroll } = state;
4725
+ if (!initialScroll) {
4726
+ return;
4727
+ }
4728
+ const lastIndex = Math.max(0, dataProp.length - 1);
4729
+ if (initialScroll.index !== lastIndex || initialScroll.viewPosition !== 1) {
4730
+ return;
4731
+ }
4732
+ const footerSize = layout[horizontal ? "width" : "height"];
4733
+ const viewOffset = -stylePaddingBottomState - footerSize;
4734
+ if (initialScroll.viewOffset !== viewOffset) {
4735
+ const previousTargetOffset = (_a4 = initialScroll.contentOffset) != null ? _a4 : resolveInitialScrollOffset(initialScroll);
4736
+ const didMoveAwayFromFinishedInitialTarget = state.didFinishInitialScroll && Math.abs(state.scroll - previousTargetOffset) > 1;
4737
+ if (didMoveAwayFromFinishedInitialTarget) {
4738
+ return;
4739
+ }
4740
+ const updatedInitialScroll = { ...initialScroll, viewOffset };
4741
+ setActiveInitialScrollTarget(updatedInitialScroll, {
4742
+ resetDidFinish: true
4743
+ });
4744
+ doInitialScroll();
4745
+ }
4746
+ },
4747
+ [
4748
+ dataProp.length,
4749
+ doInitialScroll,
4750
+ horizontal,
4751
+ initialScrollAtEnd,
4752
+ resolveInitialScrollOffset,
4753
+ stylePaddingBottomState
4754
+ ]
4755
+ );
3425
4756
  const onLayoutChange = React2.useCallback((layout) => {
4757
+ var _a4;
4758
+ handleLayout(ctx, layout, setCanRender);
4759
+ const SCROLL_LENGTH_RETRY_WINDOW_MS = 600;
4760
+ const now = Date.now();
4761
+ const didFinishInitialScroll = !!state.didFinishInitialScroll;
4762
+ if (didFinishInitialScroll && !state.initialScrollLastDidFinish) {
4763
+ state.initialScrollRetryWindowUntil = now + SCROLL_LENGTH_RETRY_WINDOW_MS;
4764
+ }
4765
+ state.initialScrollLastDidFinish = didFinishInitialScroll;
4766
+ const previousScrollLength = state.initialScrollRetryLastLength;
4767
+ const currentScrollLength = state.scrollLength;
4768
+ const didScrollLengthChange = previousScrollLength === void 0 || Math.abs(currentScrollLength - previousScrollLength) > 1;
4769
+ if (didScrollLengthChange) {
4770
+ state.initialScrollRetryLastLength = currentScrollLength;
4771
+ }
4772
+ if (didFinishInitialScroll && didScrollLengthChange && now <= state.initialScrollRetryWindowUntil && !state.initialScrollLastTargetUsesOffset && ((_a4 = state.initialScrollLastTarget) == null ? void 0 : _a4.index) !== void 0) {
4773
+ doInitialScroll({ allowPostFinishRetry: true });
4774
+ return;
4775
+ }
3426
4776
  doInitialScroll();
3427
- handleLayout(ctx, state, layout, setCanRender);
3428
4777
  }, []);
3429
4778
  const { onLayout } = useOnLayoutSync({
3430
4779
  onLayoutChange,
@@ -3434,7 +4783,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3434
4783
  });
3435
4784
  React2.useLayoutEffect(() => {
3436
4785
  if (snapToIndices) {
3437
- updateSnapToOffsets(ctx, state);
4786
+ updateSnapToOffsets(ctx);
3438
4787
  }
3439
4788
  }, [snapToIndices]);
3440
4789
  React2.useLayoutEffect(() => {
@@ -3444,24 +4793,50 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3444
4793
  isFirst,
3445
4794
  props: { data }
3446
4795
  } = state;
3447
- const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx, state, !!(didDataChange || didColumnsChange));
4796
+ const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx);
3448
4797
  if (!didAllocateContainers && !isFirst && (didDataChange || didColumnsChange)) {
3449
- checkResetContainers(ctx, state, data);
4798
+ checkResetContainers(ctx, data);
3450
4799
  }
3451
4800
  state.didColumnsChange = false;
3452
4801
  state.didDataChange = false;
3453
4802
  state.isFirst = false;
3454
4803
  }, [dataProp, dataVersion, numColumnsProp]);
3455
4804
  React2.useLayoutEffect(() => {
4805
+ var _a4;
3456
4806
  set$(ctx, "extraData", extraData);
3457
- }, [extraData]);
3458
- React2.useLayoutEffect(initializeStateVars, [
3459
- dataVersion,
3460
- memoizedLastItemKeys.join(","),
3461
- numColumnsProp,
3462
- stylePaddingBottomState,
3463
- stylePaddingTopState
3464
- ]);
4807
+ const didToggleOverride = prevHasOverrideItemLayout.current !== hasOverrideItemLayout;
4808
+ prevHasOverrideItemLayout.current = hasOverrideItemLayout;
4809
+ if ((hasOverrideItemLayout || didToggleOverride) && numColumnsProp > 1) {
4810
+ (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { forceFullItemPositions: true });
4811
+ }
4812
+ }, [extraData, hasOverrideItemLayout, numColumnsProp]);
4813
+ React2.useLayoutEffect(
4814
+ () => initializeStateVars(true),
4815
+ [dataVersion, memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingBottomState, stylePaddingTopState]
4816
+ );
4817
+ React2.useEffect(() => {
4818
+ if (!onMetricsChange) {
4819
+ return;
4820
+ }
4821
+ let lastMetrics;
4822
+ const emitMetrics = () => {
4823
+ const metrics = {
4824
+ footerSize: peek$(ctx, "footerSize") || 0,
4825
+ headerSize: peek$(ctx, "headerSize") || 0
4826
+ };
4827
+ if (!lastMetrics || metrics.headerSize !== lastMetrics.headerSize || metrics.footerSize !== lastMetrics.footerSize) {
4828
+ lastMetrics = metrics;
4829
+ onMetricsChange(metrics);
4830
+ }
4831
+ };
4832
+ emitMetrics();
4833
+ const unsubscribe = [listen$(ctx, "headerSize", emitMetrics), listen$(ctx, "footerSize", emitMetrics)];
4834
+ return () => {
4835
+ for (const unsub of unsubscribe) {
4836
+ unsub();
4837
+ }
4838
+ };
4839
+ }, [ctx, onMetricsChange]);
3465
4840
  React2.useEffect(() => {
3466
4841
  const viewability = setupViewability({
3467
4842
  onViewableItemsChanged,
@@ -3471,54 +4846,53 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3471
4846
  state.viewabilityConfigCallbackPairs = viewability;
3472
4847
  state.enableScrollForNextCalculateItemsInView = !viewability;
3473
4848
  }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
3474
- React2.useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx, state), []);
4849
+ if (!IsNewArchitecture) {
4850
+ useInit(() => {
4851
+ doInitialAllocateContainers(ctx);
4852
+ });
4853
+ }
4854
+ React2.useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx), []);
3475
4855
  if (Platform2.OS === "web") {
3476
4856
  React2.useEffect(doInitialScroll, []);
3477
4857
  }
3478
4858
  const fns = React2.useMemo(
3479
4859
  () => ({
3480
- getRenderedItem: (key) => getRenderedItem(ctx, state, key),
3481
- onScroll: (event) => onScroll(ctx, state, event),
3482
- updateItemSize: (itemKey, sizeObj) => updateItemSize(ctx, state, itemKey, sizeObj)
4860
+ getRenderedItem: (key) => getRenderedItem(ctx, key),
4861
+ onMomentumScrollEnd: (event) => {
4862
+ checkFinishedScrollFallback(ctx);
4863
+ if (onMomentumScrollEnd) {
4864
+ onMomentumScrollEnd(event);
4865
+ }
4866
+ },
4867
+ onScroll: (event) => onScroll(ctx, event),
4868
+ updateItemSize: (itemKey, sizeObj) => updateItemSize(ctx, itemKey, sizeObj)
3483
4869
  }),
3484
4870
  []
3485
4871
  );
3486
4872
  const onScrollHandler = useStickyScrollHandler(stickyHeaderIndices, horizontal, ctx, fns.onScroll);
4873
+ const refreshControlElement = refreshControl;
3487
4874
  return /* @__PURE__ */ React2__namespace.createElement(React2__namespace.Fragment, null, /* @__PURE__ */ React2__namespace.createElement(
3488
4875
  ListComponent,
3489
4876
  {
3490
- ...rest,
4877
+ ...restProps,
3491
4878
  alignItemsAtEnd,
3492
4879
  canRender,
3493
4880
  contentContainerStyle,
4881
+ contentInset,
3494
4882
  getRenderedItem: fns.getRenderedItem,
3495
4883
  horizontal,
3496
4884
  initialContentOffset,
3497
4885
  ListEmptyComponent: dataProp.length === 0 ? ListEmptyComponent : void 0,
3498
4886
  ListHeaderComponent,
3499
- maintainVisibleContentPosition,
3500
4887
  onLayout,
3501
- onLayoutHeader,
3502
- onMomentumScrollEnd: (event) => {
3503
- if (IsNewArchitecture) {
3504
- requestAnimationFrame(() => {
3505
- finishScrollTo(ctx, refState.current);
3506
- });
3507
- } else {
3508
- setTimeout(() => {
3509
- finishScrollTo(ctx, refState.current);
3510
- }, 1e3);
3511
- }
3512
- if (onMomentumScrollEnd) {
3513
- onMomentumScrollEnd(event);
3514
- }
3515
- },
4888
+ onLayoutFooter,
4889
+ onMomentumScrollEnd: fns.onMomentumScrollEnd,
3516
4890
  onScroll: onScrollHandler,
3517
4891
  recycleItems,
3518
- refreshControl: refreshControl ? stylePaddingTopState > 0 ? React2__namespace.cloneElement(refreshControl, {
3519
- progressViewOffset: (refreshControl.props.progressViewOffset || 0) + stylePaddingTopState
3520
- }) : refreshControl : onRefresh && /* @__PURE__ */ React2__namespace.createElement(
3521
- reactNative.RefreshControl,
4892
+ refreshControl: refreshControlElement ? stylePaddingTopState > 0 ? React2__namespace.cloneElement(refreshControlElement, {
4893
+ progressViewOffset: ((_g = refreshControlElement.props.progressViewOffset) != null ? _g : 0) + stylePaddingTopState
4894
+ }) : refreshControlElement : onRefresh && /* @__PURE__ */ React2__namespace.createElement(
4895
+ ReactNative.RefreshControl,
3522
4896
  {
3523
4897
  onRefresh,
3524
4898
  progressViewOffset: (progressViewOffset || 0) + stylePaddingTopState,
@@ -3526,18 +4900,30 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3526
4900
  }
3527
4901
  ),
3528
4902
  refScrollView: combinedRef,
3529
- scrollAdjustHandler: (_b = refState.current) == null ? void 0 : _b.scrollAdjustHandler,
3530
- scrollEventThrottle: Platform2.OS === "web" ? 16 : void 0,
4903
+ renderScrollComponent,
4904
+ scrollAdjustHandler: (_h = refState.current) == null ? void 0 : _h.scrollAdjustHandler,
4905
+ scrollEventThrottle: 0,
3531
4906
  snapToIndices,
3532
4907
  stickyHeaderIndices,
3533
4908
  style,
3534
4909
  updateItemSize: fns.updateItemSize,
4910
+ useWindowScroll: useWindowScrollResolved,
3535
4911
  waitForInitialLayout
3536
4912
  }
3537
- ), IS_DEV && ENABLE_DEBUG_VIEW && /* @__PURE__ */ React2__namespace.createElement(DebugView, { state: refState.current }));
4913
+ ), IS_DEV && ENABLE_DEBUG_VIEW);
3538
4914
  });
3539
4915
 
3540
- exports.LegendList = LegendList;
4916
+ // src/index.ts
4917
+ var LegendList3 = LegendList;
4918
+ if (IS_DEV) {
4919
+ console.warn(
4920
+ "[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."
4921
+ );
4922
+ }
4923
+
4924
+ exports.LegendList = LegendList3;
4925
+ exports.typedForwardRef = typedForwardRef;
4926
+ exports.typedMemo = typedMemo2;
3541
4927
  exports.useIsLastItem = useIsLastItem;
3542
4928
  exports.useListScrollSize = useListScrollSize;
3543
4929
  exports.useRecyclingEffect = useRecyclingEffect;