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