@legendapp/list 3.0.0-beta.3 → 3.0.0-beta.31

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 (47) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +1 -0
  3. package/animated.native.d.mts +9 -0
  4. package/animated.native.d.ts +9 -0
  5. package/animated.native.js +9 -0
  6. package/animated.native.mjs +7 -0
  7. package/index.d.mts +797 -10
  8. package/index.d.ts +797 -10
  9. package/index.js +1193 -685
  10. package/index.mjs +1193 -687
  11. package/index.native.d.mts +797 -10
  12. package/index.native.d.ts +797 -10
  13. package/index.native.js +1200 -641
  14. package/index.native.mjs +1199 -642
  15. package/keyboard-controller.native.d.mts +12 -0
  16. package/keyboard-controller.native.d.ts +12 -0
  17. package/keyboard-controller.native.js +69 -0
  18. package/keyboard-controller.native.mjs +48 -0
  19. package/keyboard.d.mts +2 -2
  20. package/keyboard.d.ts +2 -2
  21. package/keyboard.js +314 -25
  22. package/keyboard.mjs +317 -28
  23. package/keyboard.native.d.mts +13 -0
  24. package/keyboard.native.d.ts +13 -0
  25. package/keyboard.native.js +399 -0
  26. package/keyboard.native.mjs +377 -0
  27. package/package.json +1 -1
  28. package/reanimated.d.mts +3 -3
  29. package/reanimated.d.ts +3 -3
  30. package/reanimated.js +15 -4
  31. package/reanimated.mjs +14 -3
  32. package/reanimated.native.d.mts +18 -0
  33. package/reanimated.native.d.ts +18 -0
  34. package/reanimated.native.js +89 -0
  35. package/reanimated.native.mjs +65 -0
  36. package/section-list.d.mts +1 -2
  37. package/section-list.d.ts +1 -2
  38. package/section-list.js +36 -3678
  39. package/section-list.mjs +34 -3677
  40. package/section-list.native.d.mts +1 -2
  41. package/section-list.native.d.ts +1 -2
  42. package/section-list.native.js +36 -3458
  43. package/section-list.native.mjs +33 -3456
  44. package/types-JPHClxiw.d.mts +0 -670
  45. package/types-JPHClxiw.d.ts +0 -670
  46. package/types-YNdphn_A.d.mts +0 -670
  47. package/types-YNdphn_A.d.ts +0 -670
package/index.native.js CHANGED
@@ -28,30 +28,62 @@ var React2__namespace = /*#__PURE__*/_interopNamespace(React2);
28
28
  reactNative.Animated.View;
29
29
  var View = reactNative.View;
30
30
  var Text = reactNative.Text;
31
+
32
+ // src/state/getContentInsetEnd.ts
33
+ function getContentInsetEnd(state) {
34
+ var _a3;
35
+ const { props } = state;
36
+ const horizontal = props.horizontal;
37
+ const contentInset = props.contentInset;
38
+ const baseInset = contentInset != null ? contentInset : state.nativeContentInset;
39
+ const overrideInset = (_a3 = state.contentInsetOverride) != null ? _a3 : void 0;
40
+ if (overrideInset) {
41
+ const mergedInset = { bottom: 0, left: 0, right: 0, top: 0, ...baseInset, ...overrideInset };
42
+ return (horizontal ? mergedInset.right : mergedInset.bottom) || 0;
43
+ }
44
+ if (baseInset) {
45
+ return (horizontal ? baseInset.right : baseInset.bottom) || 0;
46
+ }
47
+ return 0;
48
+ }
49
+
50
+ // src/state/getContentSize.ts
51
+ function getContentSize(ctx) {
52
+ var _a3;
53
+ const { values, state } = ctx;
54
+ const stylePaddingTop = values.get("stylePaddingTop") || 0;
55
+ const stylePaddingBottom = state.props.stylePaddingBottom || 0;
56
+ const headerSize = values.get("headerSize") || 0;
57
+ const footerSize = values.get("footerSize") || 0;
58
+ const contentInsetBottom = getContentInsetEnd(state);
59
+ const totalSize = (_a3 = state.pendingTotalSize) != null ? _a3 : values.get("totalSize");
60
+ return headerSize + footerSize + totalSize + stylePaddingTop + stylePaddingBottom + (contentInsetBottom || 0);
61
+ }
31
62
  var createAnimatedValue = (value) => new reactNative.Animated.Value(value);
32
63
 
33
64
  // src/state/state.tsx
34
65
  var ContextState = React2__namespace.createContext(null);
66
+ var contextNum = 0;
35
67
  function StateProvider({ children }) {
36
68
  const [value] = React2__namespace.useState(() => ({
37
69
  animatedScrollY: createAnimatedValue(0),
38
70
  columnWrapperStyle: void 0,
39
- internalState: void 0,
71
+ contextNum: contextNum++,
40
72
  listeners: /* @__PURE__ */ new Map(),
41
73
  mapViewabilityAmountCallbacks: /* @__PURE__ */ new Map(),
42
74
  mapViewabilityAmountValues: /* @__PURE__ */ new Map(),
43
75
  mapViewabilityCallbacks: /* @__PURE__ */ new Map(),
44
76
  mapViewabilityConfigStates: /* @__PURE__ */ new Map(),
45
77
  mapViewabilityValues: /* @__PURE__ */ new Map(),
78
+ positionListeners: /* @__PURE__ */ new Map(),
79
+ state: void 0,
46
80
  values: /* @__PURE__ */ new Map([
47
- ["alignItemsPaddingTop", 0],
48
81
  ["stylePaddingTop", 0],
49
82
  ["headerSize", 0],
50
83
  ["numContainers", 0],
51
- ["activeStickyIndex", void 0],
84
+ ["activeStickyIndex", -1],
52
85
  ["totalSize", 0],
53
- ["scrollAdjustPending", 0],
54
- ["scrollingTo", void 0]
86
+ ["scrollAdjustPending", 0]
55
87
  ]),
56
88
  viewRefs: /* @__PURE__ */ new Map()
57
89
  }));
@@ -119,15 +151,24 @@ function set$(ctx, signalName, value) {
119
151
  }
120
152
  }
121
153
  }
122
- function getContentSize(ctx) {
123
- var _a3, _b;
124
- const { values, internalState } = ctx;
125
- const stylePaddingTop = values.get("stylePaddingTop") || 0;
126
- const stylePaddingBottom = (internalState == null ? void 0 : internalState.props.stylePaddingBottom) || 0;
127
- const headerSize = values.get("headerSize") || 0;
128
- const footerSize = values.get("footerSize") || 0;
129
- const totalSize = (_b = (_a3 = ctx.internalState) == null ? void 0 : _a3.pendingTotalSize) != null ? _b : values.get("totalSize");
130
- return headerSize + footerSize + totalSize + stylePaddingTop + stylePaddingBottom;
154
+ function listenPosition$(ctx, key, cb) {
155
+ const { positionListeners } = ctx;
156
+ let setListeners = positionListeners.get(key);
157
+ if (!setListeners) {
158
+ setListeners = /* @__PURE__ */ new Set();
159
+ positionListeners.set(key, setListeners);
160
+ }
161
+ setListeners.add(cb);
162
+ return () => setListeners.delete(cb);
163
+ }
164
+ function notifyPosition$(ctx, key, value) {
165
+ const { positionListeners } = ctx;
166
+ const setListeners = positionListeners.get(key);
167
+ if (setListeners) {
168
+ for (const listener of setListeners) {
169
+ listener(value);
170
+ }
171
+ }
131
172
  }
132
173
  function useArr$(signalNames) {
133
174
  const ctx = React2__namespace.useContext(ContextState);
@@ -206,9 +247,11 @@ var ENABLE_DEVMODE = IS_DEV && false;
206
247
  var ENABLE_DEBUG_VIEW = IS_DEV && false;
207
248
 
208
249
  // src/constants-platform.native.ts
209
- var IsNewArchitecture = global.nativeFabricUIManager != null;
250
+ var f = global.nativeFabricUIManager;
251
+ var IsNewArchitecture = f !== void 0 && f != null;
210
252
  var useAnimatedValue = (initialValue) => {
211
- return React2.useRef(new reactNative.Animated.Value(initialValue)).current;
253
+ const [animAnimatedValue] = React2.useState(() => new reactNative.Animated.Value(initialValue));
254
+ return animAnimatedValue;
212
255
  };
213
256
 
214
257
  // src/utils/helpers.ts
@@ -242,6 +285,11 @@ function extractPadding(style, contentContainerStyle, type) {
242
285
  return getPadding(style, type) + getPadding(contentContainerStyle, type);
243
286
  }
244
287
  function findContainerId(ctx, key) {
288
+ var _a3, _b;
289
+ const directMatch = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.containerItemKeys) == null ? void 0 : _b.get(key);
290
+ if (directMatch !== void 0) {
291
+ return directMatch;
292
+ }
245
293
  const numContainers = peek$(ctx, "numContainers");
246
294
  for (let i = 0; i < numContainers; i++) {
247
295
  const itemKey = peek$(ctx, `containerItemKey${i}`);
@@ -295,9 +343,18 @@ function useValue$(key, params) {
295
343
  }
296
344
  var typedForwardRef = React2.forwardRef;
297
345
  var typedMemo = React2.memo;
346
+ var getComponent = (Component) => {
347
+ if (React2__namespace.isValidElement(Component)) {
348
+ return Component;
349
+ }
350
+ if (Component) {
351
+ return /* @__PURE__ */ React2__namespace.createElement(Component, null);
352
+ }
353
+ return null;
354
+ };
298
355
 
299
356
  // src/components/PositionView.native.tsx
300
- var PositionViewState = typedMemo(function PositionView({
357
+ var PositionViewState = typedMemo(function PositionViewState2({
301
358
  id,
302
359
  horizontal,
303
360
  style,
@@ -317,7 +374,7 @@ var PositionViewState = typedMemo(function PositionView({
317
374
  }
318
375
  );
319
376
  });
320
- var PositionViewAnimated = typedMemo(function PositionView2({
377
+ var PositionViewAnimated = typedMemo(function PositionViewAnimated2({
321
378
  id,
322
379
  horizontal,
323
380
  style,
@@ -341,79 +398,123 @@ var PositionViewSticky = typedMemo(function PositionViewSticky2({
341
398
  style,
342
399
  refView,
343
400
  animatedScrollY,
344
- stickyOffset,
345
401
  index,
402
+ stickyHeaderConfig,
403
+ children,
346
404
  ...rest
347
405
  }) {
348
- const [position = POSITION_OUT_OF_VIEW, headerSize] = useArr$([`containerPosition${id}`, "headerSize"]);
406
+ const [position = POSITION_OUT_OF_VIEW, headerSize = 0, stylePaddingTop = 0] = useArr$([
407
+ `containerPosition${id}`,
408
+ "headerSize",
409
+ "stylePaddingTop"
410
+ ]);
349
411
  const transform = React2__namespace.useMemo(() => {
350
- if (animatedScrollY && stickyOffset !== void 0) {
412
+ var _a3;
413
+ if (animatedScrollY) {
414
+ const stickyConfigOffset = (_a3 = stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset) != null ? _a3 : 0;
415
+ const stickyStart = position + headerSize + stylePaddingTop - stickyConfigOffset;
351
416
  const stickyPosition = animatedScrollY.interpolate({
352
417
  extrapolateLeft: "clamp",
353
418
  extrapolateRight: "extend",
354
- inputRange: [position + headerSize, position + 5e3 + headerSize],
419
+ inputRange: [
420
+ stickyStart,
421
+ stickyStart + 5e3
422
+ ],
355
423
  outputRange: [position, position + 5e3]
356
424
  });
357
425
  return horizontal ? [{ translateX: stickyPosition }] : [{ translateY: stickyPosition }];
358
426
  }
359
- }, [animatedScrollY, headerSize, horizontal, stickyOffset, position]);
427
+ }, [animatedScrollY, headerSize, horizontal, position, stylePaddingTop, stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset]);
360
428
  const viewStyle = React2__namespace.useMemo(() => [style, { zIndex: index + 1e3 }, { transform }], [style, transform]);
361
- return /* @__PURE__ */ React2__namespace.createElement(reactNative.Animated.View, { ref: refView, style: viewStyle, ...rest });
429
+ const renderStickyHeaderBackdrop = React2__namespace.useMemo(() => {
430
+ if (!(stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent)) {
431
+ return null;
432
+ }
433
+ return /* @__PURE__ */ React2__namespace.createElement(
434
+ reactNative.View,
435
+ {
436
+ style: {
437
+ inset: 0,
438
+ pointerEvents: "none",
439
+ position: "absolute"
440
+ }
441
+ },
442
+ getComponent(stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent)
443
+ );
444
+ }, [stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent]);
445
+ return /* @__PURE__ */ React2__namespace.createElement(reactNative.Animated.View, { ref: refView, style: viewStyle, ...rest }, renderStickyHeaderBackdrop, children);
362
446
  });
363
- var PositionView3 = IsNewArchitecture ? PositionViewState : PositionViewAnimated;
364
- var symbolFirst = Symbol();
447
+ var PositionView = IsNewArchitecture ? PositionViewState : PositionViewAnimated;
365
448
  function useInit(cb) {
366
- const refValue = React2.useRef(symbolFirst);
367
- if (refValue.current === symbolFirst) {
368
- refValue.current = cb();
369
- }
370
- return refValue.current;
449
+ React2.useState(() => cb());
371
450
  }
372
451
 
373
452
  // src/state/ContextContainer.ts
374
453
  var ContextContainer = React2.createContext(null);
454
+ function useContextContainer() {
455
+ return React2.useContext(ContextContainer);
456
+ }
375
457
  function useViewability(callback, configId) {
376
458
  const ctx = useStateContext();
377
- const { containerId } = React2.useContext(ContextContainer);
378
- const key = containerId + (configId != null ? configId : "");
459
+ const containerContext = useContextContainer();
379
460
  useInit(() => {
461
+ if (!containerContext) {
462
+ return;
463
+ }
464
+ const { containerId } = containerContext;
465
+ const key = containerId + (configId != null ? configId : "");
380
466
  const value = ctx.mapViewabilityValues.get(key);
381
467
  if (value) {
382
468
  callback(value);
383
469
  }
384
470
  });
385
- ctx.mapViewabilityCallbacks.set(key, callback);
386
- React2.useEffect(
387
- () => () => {
471
+ React2.useEffect(() => {
472
+ if (!containerContext) {
473
+ return;
474
+ }
475
+ const { containerId } = containerContext;
476
+ const key = containerId + (configId != null ? configId : "");
477
+ ctx.mapViewabilityCallbacks.set(key, callback);
478
+ return () => {
388
479
  ctx.mapViewabilityCallbacks.delete(key);
389
- },
390
- []
391
- );
480
+ };
481
+ }, [ctx, callback, configId, containerContext]);
392
482
  }
393
483
  function useViewabilityAmount(callback) {
394
484
  const ctx = useStateContext();
395
- const { containerId } = React2.useContext(ContextContainer);
485
+ const containerContext = useContextContainer();
396
486
  useInit(() => {
487
+ if (!containerContext) {
488
+ return;
489
+ }
490
+ const { containerId } = containerContext;
397
491
  const value = ctx.mapViewabilityAmountValues.get(containerId);
398
492
  if (value) {
399
493
  callback(value);
400
494
  }
401
495
  });
402
- ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
403
- React2.useEffect(
404
- () => () => {
496
+ React2.useEffect(() => {
497
+ if (!containerContext) {
498
+ return;
499
+ }
500
+ const { containerId } = containerContext;
501
+ ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
502
+ return () => {
405
503
  ctx.mapViewabilityAmountCallbacks.delete(containerId);
406
- },
407
- []
408
- );
504
+ };
505
+ }, [ctx, callback, containerContext]);
409
506
  }
410
507
  function useRecyclingEffect(effect) {
411
- const { index, value } = React2.useContext(ContextContainer);
508
+ const containerContext = useContextContainer();
412
509
  const prevValues = React2.useRef({
413
510
  prevIndex: void 0,
414
511
  prevItem: void 0
415
512
  });
416
513
  React2.useEffect(() => {
514
+ if (!containerContext) {
515
+ return;
516
+ }
517
+ const { index, value } = containerContext;
417
518
  let ret;
418
519
  if (prevValues.current.prevIndex !== void 0 && prevValues.current.prevItem !== void 0) {
419
520
  ret = effect({
@@ -428,38 +529,58 @@ function useRecyclingEffect(effect) {
428
529
  prevItem: value
429
530
  };
430
531
  return ret;
431
- }, [index, value, effect]);
532
+ }, [effect, containerContext]);
432
533
  }
433
534
  function useRecyclingState(valueOrFun) {
434
- const { index, value, itemKey, triggerLayout } = React2.useContext(ContextContainer);
435
- const refState = React2.useRef({
436
- itemKey: null,
437
- value: null
535
+ var _a3, _b;
536
+ const containerContext = useContextContainer();
537
+ const computeValue = (ctx) => {
538
+ if (isFunction(valueOrFun)) {
539
+ const initializer = valueOrFun;
540
+ return ctx ? initializer({
541
+ index: ctx.index,
542
+ item: ctx.value,
543
+ prevIndex: void 0,
544
+ prevItem: void 0
545
+ }) : initializer();
546
+ }
547
+ return valueOrFun;
548
+ };
549
+ const [stateValue, setStateValue] = React2.useState(() => {
550
+ return computeValue(containerContext);
438
551
  });
439
- const [_, setRenderNum] = React2.useState(0);
440
- const state = refState.current;
441
- if (state.itemKey !== itemKey) {
442
- state.itemKey = itemKey;
443
- state.value = isFunction(valueOrFun) ? valueOrFun({
444
- index,
445
- item: value,
446
- prevIndex: void 0,
447
- prevItem: void 0
448
- }) : valueOrFun;
552
+ const prevItemKeyRef = React2.useRef((_a3 = containerContext == null ? void 0 : containerContext.itemKey) != null ? _a3 : null);
553
+ const currentItemKey = (_b = containerContext == null ? void 0 : containerContext.itemKey) != null ? _b : null;
554
+ if (currentItemKey !== null && prevItemKeyRef.current !== currentItemKey) {
555
+ prevItemKeyRef.current = currentItemKey;
556
+ setStateValue(computeValue(containerContext));
449
557
  }
558
+ const triggerLayout = containerContext == null ? void 0 : containerContext.triggerLayout;
450
559
  const setState = React2.useCallback(
451
560
  (newState) => {
452
- state.value = isFunction(newState) ? newState(state.value) : newState;
453
- setRenderNum((v) => v + 1);
561
+ if (!triggerLayout) {
562
+ return;
563
+ }
564
+ setStateValue((prevValue) => {
565
+ return isFunction(newState) ? newState(prevValue) : newState;
566
+ });
454
567
  triggerLayout();
455
568
  },
456
- [triggerLayout, state]
569
+ [triggerLayout]
457
570
  );
458
- return [state.value, setState];
571
+ return [stateValue, setState];
459
572
  }
460
573
  function useIsLastItem() {
461
- const { itemKey } = React2.useContext(ContextContainer);
462
- const isLast = useSelector$("lastItemKeys", (lastItemKeys) => (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false);
574
+ const containerContext = useContextContainer();
575
+ const isLast = useSelector$("lastItemKeys", (lastItemKeys) => {
576
+ if (containerContext) {
577
+ const { itemKey } = containerContext;
578
+ if (!isNullOrUndefined(itemKey)) {
579
+ return (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false;
580
+ }
581
+ }
582
+ return false;
583
+ });
463
584
  return isLast;
464
585
  }
465
586
  function useListScrollSize() {
@@ -469,8 +590,9 @@ function useListScrollSize() {
469
590
  var noop = () => {
470
591
  };
471
592
  function useSyncLayout() {
472
- if (IsNewArchitecture) {
473
- const { triggerLayout: syncLayout } = React2.useContext(ContextContainer);
593
+ const containerContext = useContextContainer();
594
+ if (IsNewArchitecture && containerContext) {
595
+ const { triggerLayout: syncLayout } = containerContext;
474
596
  return syncLayout;
475
597
  } else {
476
598
  return noop;
@@ -513,6 +635,8 @@ function useOnLayoutSync({
513
635
  }
514
636
  return { onLayout };
515
637
  }
638
+ var Platform2 = reactNative.Platform;
639
+ var PlatformAdjustBreaksScroll = Platform2.OS === "android";
516
640
 
517
641
  // src/components/Container.tsx
518
642
  var Container = typedMemo(function Container2({
@@ -521,22 +645,25 @@ var Container = typedMemo(function Container2({
521
645
  horizontal,
522
646
  getRenderedItem: getRenderedItem2,
523
647
  updateItemSize: updateItemSize2,
524
- ItemSeparatorComponent
648
+ ItemSeparatorComponent,
649
+ stickyHeaderConfig
525
650
  }) {
526
651
  const ctx = useStateContext();
527
652
  const { columnWrapperStyle, animatedScrollY } = ctx;
528
- const [column = 0, data, itemKey, numColumns, extraData, isSticky, stickyOffset] = useArr$([
653
+ const [column = 0, span = 1, data, itemKey, numColumns = 1, extraData, isSticky] = useArr$([
529
654
  `containerColumn${id}`,
655
+ `containerSpan${id}`,
530
656
  `containerItemData${id}`,
531
657
  `containerItemKey${id}`,
532
658
  "numColumns",
533
659
  "extraData",
534
- `containerSticky${id}`,
535
- `containerStickyOffset${id}`
660
+ `containerSticky${id}`
536
661
  ]);
537
662
  const itemLayoutRef = React2.useRef({
663
+ didLayout: false,
538
664
  horizontal,
539
665
  itemKey,
666
+ pendingShrinkToken: 0,
540
667
  updateItemSize: updateItemSize2
541
668
  });
542
669
  itemLayoutRef.current.horizontal = horizontal;
@@ -544,9 +671,10 @@ var Container = typedMemo(function Container2({
544
671
  itemLayoutRef.current.updateItemSize = updateItemSize2;
545
672
  const ref = React2.useRef(null);
546
673
  const [layoutRenderCount, forceLayoutRender] = React2.useState(0);
547
- const otherAxisPos = numColumns > 1 ? `${(column - 1) / numColumns * 100}%` : 0;
548
- const otherAxisSize = numColumns > 1 ? `${1 / numColumns * 100}%` : void 0;
549
- const didLayoutRef = React2.useRef(false);
674
+ const resolvedColumn = column > 0 ? column : 1;
675
+ const resolvedSpan = Math.min(Math.max(span || 1, 1), numColumns);
676
+ const otherAxisPos = numColumns > 1 ? `${(resolvedColumn - 1) / numColumns * 100}%` : 0;
677
+ const otherAxisSize = numColumns > 1 ? `${resolvedSpan / numColumns * 100}%` : void 0;
550
678
  const style = React2.useMemo(() => {
551
679
  let paddingStyles;
552
680
  if (columnWrapperStyle) {
@@ -601,19 +729,40 @@ var Container = typedMemo(function Container2({
601
729
  const {
602
730
  horizontal: currentHorizontal,
603
731
  itemKey: currentItemKey,
604
- updateItemSize: updateItemSizeFn
732
+ updateItemSize: updateItemSizeFn,
733
+ lastSize,
734
+ pendingShrinkToken
605
735
  } = itemLayoutRef.current;
606
736
  if (isNullOrUndefined(currentItemKey)) {
607
737
  return;
608
738
  }
609
- didLayoutRef.current = true;
739
+ itemLayoutRef.current.didLayout = true;
610
740
  let layout = rectangle;
611
- const size = roundSize(rectangle[currentHorizontal ? "width" : "height"]);
741
+ const axis = currentHorizontal ? "width" : "height";
742
+ const size = roundSize(rectangle[axis]);
743
+ const prevSize = lastSize ? roundSize(lastSize[axis]) : void 0;
612
744
  const doUpdate = () => {
613
- itemLayoutRef.current.lastSize = { height: layout.height, width: layout.width };
745
+ itemLayoutRef.current.lastSize = layout;
614
746
  updateItemSizeFn(currentItemKey, layout);
615
- didLayoutRef.current = true;
747
+ itemLayoutRef.current.didLayout = true;
616
748
  };
749
+ if (Platform2.OS === "web" && prevSize !== void 0 && size + 1 < prevSize) {
750
+ const token = pendingShrinkToken + 1;
751
+ itemLayoutRef.current.pendingShrinkToken = token;
752
+ requestAnimationFrame(() => {
753
+ var _a4;
754
+ if (itemLayoutRef.current.pendingShrinkToken !== token) {
755
+ return;
756
+ }
757
+ const element = ref.current;
758
+ const rect = (_a4 = element == null ? void 0 : element.getBoundingClientRect) == null ? void 0 : _a4.call(element);
759
+ if (rect) {
760
+ layout = { height: rect.height, width: rect.width };
761
+ }
762
+ doUpdate();
763
+ });
764
+ return;
765
+ }
617
766
  if (IsNewArchitecture || size > 0) {
618
767
  doUpdate();
619
768
  } else {
@@ -633,9 +782,9 @@ var Container = typedMemo(function Container2({
633
782
  if (!IsNewArchitecture) {
634
783
  React2.useEffect(() => {
635
784
  if (!isNullOrUndefined(itemKey)) {
636
- didLayoutRef.current = false;
785
+ itemLayoutRef.current.didLayout = false;
637
786
  const timeout = setTimeout(() => {
638
- if (!didLayoutRef.current) {
787
+ if (!itemLayoutRef.current.didLayout) {
639
788
  const {
640
789
  itemKey: currentItemKey,
641
790
  lastSize,
@@ -643,7 +792,7 @@ var Container = typedMemo(function Container2({
643
792
  } = itemLayoutRef.current;
644
793
  if (lastSize && !isNullOrUndefined(currentItemKey)) {
645
794
  updateItemSizeFn(currentItemKey, lastSize);
646
- didLayoutRef.current = true;
795
+ itemLayoutRef.current.didLayout = true;
647
796
  }
648
797
  }
649
798
  }, 16);
@@ -653,7 +802,7 @@ var Container = typedMemo(function Container2({
653
802
  }
654
803
  }, [itemKey]);
655
804
  }
656
- const PositionComponent = isSticky ? PositionViewSticky : PositionView3;
805
+ const PositionComponent = isSticky ? PositionViewSticky : PositionView;
657
806
  return /* @__PURE__ */ React2__namespace.createElement(
658
807
  PositionComponent,
659
808
  {
@@ -664,7 +813,7 @@ var Container = typedMemo(function Container2({
664
813
  key: recycleItems ? void 0 : itemKey,
665
814
  onLayout,
666
815
  refView: ref,
667
- stickyOffset: isSticky ? stickyOffset : void 0,
816
+ stickyHeaderConfig,
668
817
  style
669
818
  },
670
819
  /* @__PURE__ */ React2__namespace.createElement(ContextContainer.Provider, { value: contextValue }, renderedItem, renderedItemInfo && ItemSeparatorComponent && /* @__PURE__ */ React2__namespace.createElement(Separator, { ItemSeparatorComponent, leadingItem: renderedItemInfo.item }))
@@ -677,6 +826,7 @@ var Containers = typedMemo(function Containers2({
677
826
  recycleItems,
678
827
  ItemSeparatorComponent,
679
828
  waitForInitialLayout,
829
+ stickyHeaderConfig,
680
830
  updateItemSize: updateItemSize2,
681
831
  getRenderedItem: getRenderedItem2
682
832
  }) {
@@ -688,10 +838,10 @@ var Containers = typedMemo(function Containers2({
688
838
  // If this is the initial scroll, we don't want to delay because we want to update the size immediately
689
839
  delay: (value, prevValue) => {
690
840
  var _a3;
691
- return !((_a3 = ctx.internalState) == null ? void 0 : _a3.initialScroll) ? !prevValue || value - prevValue > 20 ? 0 : 200 : void 0;
841
+ return !((_a3 = ctx.state) == null ? void 0 : _a3.initialScroll) ? !prevValue || value - prevValue > 20 ? 0 : 200 : void 0;
692
842
  }
693
843
  });
694
- const animOpacity = waitForInitialLayout && !IsNewArchitecture ? useValue$("containersDidLayout", { getValue: (value) => value ? 1 : 0 }) : void 0;
844
+ const animOpacity = waitForInitialLayout && !IsNewArchitecture ? useValue$("readyToRender", { getValue: (value) => value ? 1 : 0 }) : void 0;
695
845
  const otherAxisSize = useValue$("otherAxisSize", { delay: 0 });
696
846
  const containers = [];
697
847
  for (let i = 0; i < numContainers; i++) {
@@ -705,25 +855,26 @@ var Containers = typedMemo(function Containers2({
705
855
  id: i,
706
856
  key: i,
707
857
  recycleItems,
858
+ stickyHeaderConfig,
708
859
  updateItemSize: updateItemSize2
709
860
  }
710
861
  )
711
862
  );
712
863
  }
713
864
  const style = horizontal ? { minHeight: otherAxisSize, opacity: animOpacity, width: animSize } : { height: animSize, minWidth: otherAxisSize, opacity: animOpacity };
714
- if (columnWrapperStyle && numColumns > 1) {
865
+ if (columnWrapperStyle) {
715
866
  const { columnGap, rowGap, gap } = columnWrapperStyle;
716
867
  const gapX = columnGap || gap || 0;
717
868
  const gapY = rowGap || gap || 0;
718
869
  if (horizontal) {
719
- if (gapY) {
870
+ if (gapY && numColumns > 1) {
720
871
  style.marginVertical = -gapY / 2;
721
872
  }
722
873
  if (gapX) {
723
874
  style.marginRight = -gapX;
724
875
  }
725
876
  } else {
726
- if (gapX) {
877
+ if (gapX && numColumns > 1) {
727
878
  style.marginHorizontal = -gapX;
728
879
  }
729
880
  if (gapY) {
@@ -734,7 +885,8 @@ var Containers = typedMemo(function Containers2({
734
885
  return /* @__PURE__ */ React2__namespace.createElement(reactNative.Animated.View, { style }, containers);
735
886
  });
736
887
  function DevNumbers() {
737
- return IS_DEV && React2__namespace.memo(function DevNumbers2() {
888
+ return IS_DEV && // biome-ignore lint/nursery/noShadow: const function name shadowing is intentional
889
+ React2__namespace.memo(function DevNumbers2() {
738
890
  return Array.from({ length: 100 }).map((_, index) => /* @__PURE__ */ React2__namespace.createElement(
739
891
  reactNative.View,
740
892
  {
@@ -752,26 +904,6 @@ function DevNumbers() {
752
904
  });
753
905
  }
754
906
  var ListComponentScrollView = reactNative.Animated.ScrollView;
755
- function Padding() {
756
- const animPaddingTop = useValue$("alignItemsPaddingTop", { delay: 0 });
757
- return /* @__PURE__ */ React2__namespace.createElement(reactNative.Animated.View, { style: { paddingTop: animPaddingTop } });
758
- }
759
- function PaddingDevMode() {
760
- const animPaddingTop = useValue$("alignItemsPaddingTop", { delay: 0 });
761
- return /* @__PURE__ */ React2__namespace.createElement(React2__namespace.Fragment, null, /* @__PURE__ */ React2__namespace.createElement(reactNative.Animated.View, { style: { paddingTop: animPaddingTop } }), /* @__PURE__ */ React2__namespace.createElement(
762
- reactNative.Animated.View,
763
- {
764
- style: {
765
- backgroundColor: "green",
766
- height: animPaddingTop,
767
- left: 0,
768
- position: "absolute",
769
- right: 0,
770
- top: 0
771
- }
772
- }
773
- ));
774
- }
775
907
  function ScrollAdjust() {
776
908
  const bias = 1e7;
777
909
  const [scrollAdjust, scrollAdjustUserOffset] = useArr$(["scrollAdjust", "scrollAdjustUserOffset"]);
@@ -800,15 +932,6 @@ var LayoutView = ({ onLayoutChange, refView, ...rest }) => {
800
932
  };
801
933
 
802
934
  // src/components/ListComponent.tsx
803
- var getComponent = (Component) => {
804
- if (React2__namespace.isValidElement(Component)) {
805
- return Component;
806
- }
807
- if (Component) {
808
- return /* @__PURE__ */ React2__namespace.createElement(Component, null);
809
- }
810
- return null;
811
- };
812
935
  var ListComponent = typedMemo(function ListComponent2({
813
936
  canRender,
814
937
  style,
@@ -817,7 +940,7 @@ var ListComponent = typedMemo(function ListComponent2({
817
940
  initialContentOffset,
818
941
  recycleItems,
819
942
  ItemSeparatorComponent,
820
- alignItemsAtEnd,
943
+ alignItemsAtEnd: _alignItemsAtEnd,
821
944
  waitForInitialLayout,
822
945
  onScroll: onScroll2,
823
946
  onLayout,
@@ -829,27 +952,29 @@ var ListComponent = typedMemo(function ListComponent2({
829
952
  getRenderedItem: getRenderedItem2,
830
953
  updateItemSize: updateItemSize2,
831
954
  refScrollView,
832
- maintainVisibleContentPosition,
833
955
  renderScrollComponent,
834
956
  scrollAdjustHandler,
835
957
  onLayoutHeader,
836
958
  snapToIndices,
959
+ stickyHeaderConfig,
837
960
  stickyHeaderIndices,
838
961
  ...rest
839
962
  }) {
840
963
  const ctx = useStateContext();
964
+ const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
841
965
  const ScrollComponent = renderScrollComponent ? React2.useMemo(
842
966
  () => React2__namespace.forwardRef((props, ref) => renderScrollComponent({ ...props, ref })),
843
967
  [renderScrollComponent]
844
968
  ) : ListComponentScrollView;
845
- React2__namespace.useEffect(() => {
846
- if (canRender) {
847
- setTimeout(() => {
848
- scrollAdjustHandler.setMounted();
849
- }, 0);
850
- }
851
- }, [canRender]);
852
969
  const SnapOrScroll = snapToIndices ? SnapWrapper : ScrollComponent;
970
+ React2.useLayoutEffect(() => {
971
+ if (!ListHeaderComponent) {
972
+ set$(ctx, "headerSize", 0);
973
+ }
974
+ if (!ListFooterComponent) {
975
+ set$(ctx, "footerSize", 0);
976
+ }
977
+ }, [ListHeaderComponent, ListFooterComponent, ctx]);
853
978
  return /* @__PURE__ */ React2__namespace.createElement(
854
979
  SnapOrScroll,
855
980
  {
@@ -862,7 +987,7 @@ var ListComponent = typedMemo(function ListComponent2({
862
987
  ],
863
988
  contentOffset: initialContentOffset ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
864
989
  horizontal,
865
- maintainVisibleContentPosition: maintainVisibleContentPosition ? { minIndexForVisible: 0 } : void 0,
990
+ maintainVisibleContentPosition: maintainVisibleContentPosition.size || maintainVisibleContentPosition.data ? { minIndexForVisible: 0 } : void 0,
866
991
  onLayout,
867
992
  onScroll: onScroll2,
868
993
  ref: refScrollView,
@@ -870,7 +995,6 @@ var ListComponent = typedMemo(function ListComponent2({
870
995
  style
871
996
  },
872
997
  /* @__PURE__ */ React2__namespace.createElement(ScrollAdjust, null),
873
- ENABLE_DEVMODE ? /* @__PURE__ */ React2__namespace.createElement(PaddingDevMode, null) : /* @__PURE__ */ React2__namespace.createElement(Padding, null),
874
998
  ListHeaderComponent && /* @__PURE__ */ React2__namespace.createElement(LayoutView, { onLayoutChange: onLayoutHeader, style: ListHeaderComponentStyle }, getComponent(ListHeaderComponent)),
875
999
  ListEmptyComponent && getComponent(ListEmptyComponent),
876
1000
  canRender && !ListEmptyComponent && /* @__PURE__ */ React2__namespace.createElement(
@@ -880,6 +1004,7 @@ var ListComponent = typedMemo(function ListComponent2({
880
1004
  horizontal,
881
1005
  ItemSeparatorComponent,
882
1006
  recycleItems,
1007
+ stickyHeaderConfig,
883
1008
  updateItemSize: updateItemSize2,
884
1009
  waitForInitialLayout
885
1010
  }
@@ -912,10 +1037,11 @@ function getId(state, index) {
912
1037
  }
913
1038
 
914
1039
  // src/core/calculateOffsetForIndex.ts
915
- function calculateOffsetForIndex(ctx, state, index) {
1040
+ function calculateOffsetForIndex(ctx, index) {
1041
+ const state = ctx.state;
916
1042
  let position = 0;
917
1043
  if (index !== void 0) {
918
- position = (state == null ? void 0 : state.positions.get(getId(state, index))) || 0;
1044
+ position = state.positions.get(getId(state, index)) || 0;
919
1045
  const paddingTop = peek$(ctx, "stylePaddingTop");
920
1046
  if (paddingTop) {
921
1047
  position += paddingTop;
@@ -928,44 +1054,9 @@ function calculateOffsetForIndex(ctx, state, index) {
928
1054
  return position;
929
1055
  }
930
1056
 
931
- // src/utils/setPaddingTop.ts
932
- function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
933
- if (stylePaddingTop !== void 0) {
934
- const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
935
- if (stylePaddingTop < prevStylePaddingTop) {
936
- let prevTotalSize = peek$(ctx, "totalSize") || 0;
937
- set$(ctx, "totalSize", prevTotalSize + prevStylePaddingTop);
938
- state.timeoutSetPaddingTop = setTimeout(() => {
939
- prevTotalSize = peek$(ctx, "totalSize") || 0;
940
- set$(ctx, "totalSize", prevTotalSize - prevStylePaddingTop);
941
- }, 16);
942
- }
943
- set$(ctx, "stylePaddingTop", stylePaddingTop);
944
- }
945
- if (alignItemsPaddingTop !== void 0) {
946
- set$(ctx, "alignItemsPaddingTop", alignItemsPaddingTop);
947
- }
948
- }
949
-
950
- // src/utils/updateAlignItemsPaddingTop.ts
951
- function updateAlignItemsPaddingTop(ctx, state) {
952
- const {
953
- scrollLength,
954
- props: { alignItemsAtEnd, data }
955
- } = state;
956
- if (alignItemsAtEnd) {
957
- let alignItemsPaddingTop = 0;
958
- if ((data == null ? void 0 : data.length) > 0) {
959
- const contentSize = getContentSize(ctx);
960
- alignItemsPaddingTop = Math.max(0, Math.floor(scrollLength - contentSize));
961
- }
962
- setPaddingTop(ctx, state, { alignItemsPaddingTop });
963
- }
964
- }
965
-
966
1057
  // src/core/addTotalSize.ts
967
- function addTotalSize(ctx, state, key, add) {
968
- const { alignItemsAtEnd } = state.props;
1058
+ function addTotalSize(ctx, key, add) {
1059
+ const state = ctx.state;
969
1060
  const prevTotalSize = state.totalSize;
970
1061
  let totalSize = state.totalSize;
971
1062
  if (key === null) {
@@ -984,32 +1075,32 @@ function addTotalSize(ctx, state, key, add) {
984
1075
  state.pendingTotalSize = void 0;
985
1076
  state.totalSize = totalSize;
986
1077
  set$(ctx, "totalSize", totalSize);
987
- if (alignItemsAtEnd) {
988
- updateAlignItemsPaddingTop(ctx, state);
989
- }
990
1078
  }
991
1079
  }
992
1080
  }
993
1081
 
994
1082
  // src/core/setSize.ts
995
- function setSize(ctx, state, itemKey, size) {
1083
+ function setSize(ctx, itemKey, size) {
1084
+ const state = ctx.state;
996
1085
  const { sizes } = state;
997
1086
  const previousSize = sizes.get(itemKey);
998
1087
  const diff = previousSize !== void 0 ? size - previousSize : size;
999
1088
  if (diff !== 0) {
1000
- addTotalSize(ctx, state, itemKey, diff);
1089
+ addTotalSize(ctx, itemKey, diff);
1001
1090
  }
1002
1091
  sizes.set(itemKey, size);
1003
1092
  }
1004
1093
 
1005
1094
  // src/utils/getItemSize.ts
1006
- function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedSize) {
1095
+ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
1007
1096
  var _a3, _b;
1097
+ const state = ctx.state;
1008
1098
  const {
1009
1099
  sizesKnown,
1010
1100
  sizes,
1011
1101
  averageSizes,
1012
- props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType }
1102
+ props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType },
1103
+ scrollingTo
1013
1104
  } = state;
1014
1105
  const sizeKnown = sizesKnown.get(key);
1015
1106
  if (sizeKnown !== void 0) {
@@ -1017,7 +1108,6 @@ function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedS
1017
1108
  }
1018
1109
  let size;
1019
1110
  const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1020
- const scrollingTo = peek$(ctx, "scrollingTo");
1021
1111
  if (preferCachedSize) {
1022
1112
  const cachedSize = sizes.get(key);
1023
1113
  if (cachedSize !== void 0) {
@@ -1025,7 +1115,7 @@ function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedS
1025
1115
  }
1026
1116
  }
1027
1117
  if (getFixedItemSize) {
1028
- size = getFixedItemSize(index, data, itemType);
1118
+ size = getFixedItemSize(data, index, itemType);
1029
1119
  if (size !== void 0) {
1030
1120
  sizesKnown.set(key, size);
1031
1121
  }
@@ -1043,96 +1133,48 @@ function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedS
1043
1133
  }
1044
1134
  }
1045
1135
  if (size === void 0) {
1046
- size = getEstimatedItemSize ? getEstimatedItemSize(index, data, itemType) : estimatedItemSize;
1136
+ size = getEstimatedItemSize ? getEstimatedItemSize(data, index, itemType) : estimatedItemSize;
1047
1137
  }
1048
- setSize(ctx, state, key, size);
1138
+ setSize(ctx, key, size);
1049
1139
  return size;
1050
1140
  }
1051
1141
 
1052
1142
  // src/core/calculateOffsetWithOffsetPosition.ts
1053
- function calculateOffsetWithOffsetPosition(ctx, state, offsetParam, params) {
1143
+ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1144
+ const state = ctx.state;
1054
1145
  const { index, viewOffset, viewPosition } = params;
1055
1146
  let offset = offsetParam;
1056
1147
  if (viewOffset) {
1057
1148
  offset -= viewOffset;
1058
1149
  }
1059
1150
  if (viewPosition !== void 0 && index !== void 0) {
1060
- offset -= viewPosition * (state.scrollLength - getItemSize(ctx, state, getId(state, index), index, state.props.data[index]));
1151
+ const itemSize = getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1152
+ const trailingInset = getContentInsetEnd(state);
1153
+ offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1061
1154
  }
1062
1155
  return offset;
1063
1156
  }
1064
1157
 
1065
- // src/core/finishScrollTo.ts
1066
- function finishScrollTo(ctx, state) {
1067
- var _a3, _b;
1068
- if (state) {
1069
- state.scrollHistory.length = 0;
1070
- state.initialScroll = void 0;
1071
- state.initialAnchor = void 0;
1072
- set$(ctx, "scrollingTo", void 0);
1073
- if (state.pendingTotalSize !== void 0) {
1074
- addTotalSize(ctx, state, null, state.pendingTotalSize);
1075
- }
1076
- if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1077
- (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1078
- }
1079
- }
1080
- }
1081
- var Platform2 = reactNative.Platform;
1082
-
1083
- // src/core/scrollTo.ts
1084
- function scrollTo(ctx, state, params) {
1085
- var _a3;
1086
- const { noScrollingTo, ...scrollTarget } = params;
1087
- const { animated, isInitialScroll, offset: scrollTargetOffset, precomputedWithViewOffset } = scrollTarget;
1088
- const {
1089
- refScroller,
1090
- props: { horizontal }
1091
- } = state;
1092
- let offset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, state, scrollTargetOffset, scrollTarget);
1093
- if (Number.isFinite(state.scrollLength) && Number.isFinite(state.totalSize)) {
1094
- const maxOffset = Math.max(0, getContentSize(ctx) - state.scrollLength);
1095
- offset = Math.min(offset, maxOffset);
1096
- }
1097
- state.scrollHistory.length = 0;
1098
- if (!noScrollingTo) {
1099
- set$(ctx, "scrollingTo", scrollTarget);
1100
- }
1101
- state.scrollPending = offset;
1102
- if (!isInitialScroll || Platform2.OS === "android") {
1103
- (_a3 = refScroller.current) == null ? void 0 : _a3.scrollTo({
1104
- animated: !!animated,
1105
- x: horizontal ? offset : 0,
1106
- y: horizontal ? 0 : offset
1107
- });
1108
- }
1109
- if (!animated) {
1110
- state.scroll = offset;
1111
- if (Platform2.OS === "web") {
1112
- const unlisten = listen$(ctx, "containersDidLayout", (value) => {
1113
- if (value && peek$(ctx, "scrollingTo")) {
1114
- finishScrollTo(ctx, state);
1115
- unlisten();
1116
- }
1117
- });
1118
- } else {
1119
- setTimeout(() => finishScrollTo(ctx, state), 100);
1120
- }
1121
- if (isInitialScroll) {
1122
- setTimeout(() => {
1123
- state.initialScroll = void 0;
1124
- }, 500);
1125
- }
1158
+ // src/core/clampScrollOffset.ts
1159
+ function clampScrollOffset(ctx, offset) {
1160
+ const state = ctx.state;
1161
+ const contentSize = getContentSize(ctx);
1162
+ let clampedOffset = offset;
1163
+ if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform2.OS !== "android" || state.lastLayout)) {
1164
+ const maxOffset = Math.max(0, contentSize - state.scrollLength);
1165
+ clampedOffset = Math.min(offset, maxOffset);
1126
1166
  }
1167
+ clampedOffset = Math.max(0, clampedOffset);
1168
+ return clampedOffset;
1127
1169
  }
1128
1170
 
1129
1171
  // src/utils/checkThreshold.ts
1130
1172
  var HYSTERESIS_MULTIPLIER = 1.3;
1131
- var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot) => {
1173
+ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot, allowReentryOnChange) => {
1132
1174
  const absDistance = Math.abs(distance);
1133
1175
  const within = atThreshold || threshold > 0 && absDistance <= threshold;
1134
1176
  const updateSnapshot = () => {
1135
- setSnapshot == null ? void 0 : setSnapshot({
1177
+ setSnapshot({
1136
1178
  atThreshold,
1137
1179
  contentSize: context.contentSize,
1138
1180
  dataLength: context.dataLength,
@@ -1143,19 +1185,21 @@ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, co
1143
1185
  if (!within) {
1144
1186
  return false;
1145
1187
  }
1146
- onReached == null ? void 0 : onReached(distance);
1188
+ onReached(distance);
1147
1189
  updateSnapshot();
1148
1190
  return true;
1149
1191
  }
1150
1192
  const reset = !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
1151
1193
  if (reset) {
1152
- setSnapshot == null ? void 0 : setSnapshot(void 0);
1194
+ setSnapshot(void 0);
1153
1195
  return false;
1154
1196
  }
1155
1197
  if (within) {
1156
1198
  const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
1157
1199
  if (changed) {
1158
- onReached == null ? void 0 : onReached(distance);
1200
+ if (allowReentryOnChange) {
1201
+ onReached(distance);
1202
+ }
1159
1203
  updateSnapshot();
1160
1204
  }
1161
1205
  }
@@ -1163,9 +1207,10 @@ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, co
1163
1207
  };
1164
1208
 
1165
1209
  // src/utils/checkAtBottom.ts
1166
- function checkAtBottom(ctx, state) {
1210
+ function checkAtBottom(ctx) {
1167
1211
  var _a3;
1168
- if (!state) {
1212
+ const state = ctx.state;
1213
+ if (!state || state.initialScroll) {
1169
1214
  return;
1170
1215
  }
1171
1216
  const {
@@ -1175,9 +1220,13 @@ function checkAtBottom(ctx, state) {
1175
1220
  maintainingScrollAtEnd,
1176
1221
  props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
1177
1222
  } = state;
1223
+ if (state.initialScroll) {
1224
+ return;
1225
+ }
1178
1226
  const contentSize = getContentSize(ctx);
1179
1227
  if (contentSize > 0 && queuedInitialLayout && !maintainingScrollAtEnd) {
1180
- const distanceFromEnd = contentSize - scroll - scrollLength;
1228
+ const insetEnd = getContentInsetEnd(state);
1229
+ const distanceFromEnd = contentSize - scroll - scrollLength - insetEnd;
1181
1230
  const isContentLess = contentSize < scrollLength;
1182
1231
  state.isAtEnd = isContentLess || distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
1183
1232
  state.isEndReached = checkThreshold(
@@ -1197,15 +1246,17 @@ function checkAtBottom(ctx, state) {
1197
1246
  },
1198
1247
  (snapshot) => {
1199
1248
  state.endReachedSnapshot = snapshot;
1200
- }
1249
+ },
1250
+ true
1201
1251
  );
1202
1252
  }
1203
1253
  }
1204
1254
 
1205
1255
  // src/utils/checkAtTop.ts
1206
- function checkAtTop(state) {
1256
+ function checkAtTop(ctx) {
1207
1257
  var _a3;
1208
- if (!state) {
1258
+ const state = ctx == null ? void 0 : ctx.state;
1259
+ if (!state || state.initialScroll) {
1209
1260
  return;
1210
1261
  }
1211
1262
  const {
@@ -1232,20 +1283,164 @@ function checkAtTop(state) {
1232
1283
  },
1233
1284
  (snapshot) => {
1234
1285
  state.startReachedSnapshot = snapshot;
1286
+ },
1287
+ false
1288
+ );
1289
+ }
1290
+
1291
+ // src/utils/checkThresholds.ts
1292
+ function checkThresholds(ctx) {
1293
+ checkAtBottom(ctx);
1294
+ checkAtTop(ctx);
1295
+ }
1296
+
1297
+ // src/utils/setInitialRenderState.ts
1298
+ function setInitialRenderState(ctx, {
1299
+ didLayout,
1300
+ didInitialScroll
1301
+ }) {
1302
+ const { state } = ctx;
1303
+ if (didLayout) {
1304
+ state.didContainersLayout = true;
1305
+ }
1306
+ if (didInitialScroll) {
1307
+ state.didFinishInitialScroll = true;
1308
+ }
1309
+ if (state.didContainersLayout && state.didFinishInitialScroll) {
1310
+ set$(ctx, "readyToRender", true);
1311
+ }
1312
+ }
1313
+
1314
+ // src/core/finishScrollTo.ts
1315
+ function finishScrollTo(ctx) {
1316
+ var _a3, _b;
1317
+ const state = ctx.state;
1318
+ if (state == null ? void 0 : state.scrollingTo) {
1319
+ const scrollingTo = state.scrollingTo;
1320
+ state.scrollHistory.length = 0;
1321
+ state.initialScroll = void 0;
1322
+ state.initialAnchor = void 0;
1323
+ state.scrollingTo = void 0;
1324
+ if (state.pendingTotalSize !== void 0) {
1325
+ addTotalSize(ctx, null, state.pendingTotalSize);
1326
+ }
1327
+ if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1328
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1329
+ }
1330
+ if (PlatformAdjustBreaksScroll) {
1331
+ state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
1235
1332
  }
1333
+ setInitialRenderState(ctx, { didInitialScroll: true });
1334
+ checkThresholds(ctx);
1335
+ }
1336
+ }
1337
+
1338
+ // src/core/checkFinishedScroll.ts
1339
+ function checkFinishedScroll(ctx) {
1340
+ ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
1341
+ }
1342
+ function checkFinishedScrollFrame(ctx) {
1343
+ const scrollingTo = ctx.state.scrollingTo;
1344
+ if (scrollingTo) {
1345
+ const { state } = ctx;
1346
+ state.animFrameCheckFinishedScroll = void 0;
1347
+ const scroll = state.scrollPending;
1348
+ const adjust = state.scrollAdjustHandler.getAdjust();
1349
+ const clampedTargetOffset = clampScrollOffset(ctx, scrollingTo.offset - (scrollingTo.viewOffset || 0));
1350
+ const maxOffset = clampScrollOffset(ctx, scroll);
1351
+ const diff1 = Math.abs(scroll - clampedTargetOffset);
1352
+ const diff2 = Math.abs(diff1 - adjust);
1353
+ const isNotOverscrolled = Math.abs(scroll - maxOffset) < 1;
1354
+ if (isNotOverscrolled && (diff1 < 1 || diff2 < 1)) {
1355
+ finishScrollTo(ctx);
1356
+ }
1357
+ }
1358
+ }
1359
+ function checkFinishedScrollFallback(ctx) {
1360
+ const state = ctx.state;
1361
+ const scrollingTo = state.scrollingTo;
1362
+ const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) || !state.didContainersLayout;
1363
+ state.timeoutCheckFinishedScrollFallback = setTimeout(
1364
+ () => {
1365
+ let numChecks = 0;
1366
+ const checkHasScrolled = () => {
1367
+ state.timeoutCheckFinishedScrollFallback = void 0;
1368
+ const isStillScrollingTo = state.scrollingTo;
1369
+ if (isStillScrollingTo) {
1370
+ numChecks++;
1371
+ if (state.hasScrolled || numChecks > 5) {
1372
+ finishScrollTo(ctx);
1373
+ } else {
1374
+ state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, 100);
1375
+ }
1376
+ }
1377
+ };
1378
+ checkHasScrolled();
1379
+ },
1380
+ slowTimeout ? 500 : 100
1236
1381
  );
1237
1382
  }
1238
1383
 
1239
- // src/core/updateScroll.ts
1240
- function updateScroll(ctx, state, newScroll, forceUpdate) {
1384
+ // src/core/doScrollTo.native.ts
1385
+ function doScrollTo(ctx, params) {
1241
1386
  var _a3;
1242
- const scrollingTo = peek$(ctx, "scrollingTo");
1387
+ const state = ctx.state;
1388
+ const { animated, horizontal, offset } = params;
1389
+ const { refScroller } = state;
1390
+ (_a3 = refScroller.current) == null ? void 0 : _a3.scrollTo({
1391
+ animated: !!animated,
1392
+ x: horizontal ? offset : 0,
1393
+ y: horizontal ? 0 : offset
1394
+ });
1395
+ if (!animated) {
1396
+ state.scroll = offset;
1397
+ checkFinishedScrollFallback(ctx);
1398
+ }
1399
+ }
1400
+
1401
+ // src/core/scrollTo.ts
1402
+ function scrollTo(ctx, params) {
1403
+ const state = ctx.state;
1404
+ const { noScrollingTo, forceScroll, ...scrollTarget } = params;
1405
+ const { animated, isInitialScroll, offset: scrollTargetOffset, precomputedWithViewOffset } = scrollTarget;
1406
+ const {
1407
+ props: { horizontal }
1408
+ } = state;
1409
+ if (state.animFrameCheckFinishedScroll) {
1410
+ cancelAnimationFrame(ctx.state.animFrameCheckFinishedScroll);
1411
+ }
1412
+ if (state.timeoutCheckFinishedScrollFallback) {
1413
+ clearTimeout(ctx.state.timeoutCheckFinishedScrollFallback);
1414
+ }
1415
+ let offset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, scrollTargetOffset, scrollTarget);
1416
+ offset = clampScrollOffset(ctx, offset);
1417
+ state.scrollHistory.length = 0;
1418
+ if (!noScrollingTo) {
1419
+ state.scrollingTo = scrollTarget;
1420
+ }
1421
+ state.scrollPending = offset;
1422
+ if (forceScroll || !isInitialScroll || Platform2.OS === "android") {
1423
+ doScrollTo(ctx, { animated, horizontal, isInitialScroll, offset });
1424
+ } else {
1425
+ state.scroll = offset;
1426
+ }
1427
+ }
1428
+
1429
+ // src/platform/flushSync.native.ts
1430
+ var flushSync = (fn) => {
1431
+ fn();
1432
+ };
1433
+
1434
+ // src/core/updateScroll.ts
1435
+ function updateScroll(ctx, newScroll, forceUpdate) {
1436
+ const state = ctx.state;
1437
+ const { scrollingTo, scrollAdjustHandler, lastScrollAdjustForHistory } = state;
1438
+ const prevScroll = state.scroll;
1243
1439
  state.hasScrolled = true;
1244
1440
  state.lastBatchingAction = Date.now();
1245
1441
  const currentTime = Date.now();
1246
- const adjust = state.scrollAdjustHandler.getAdjust();
1247
- const lastHistoryAdjust = state.lastScrollAdjustForHistory;
1248
- const adjustChanged = lastHistoryAdjust !== void 0 && Math.abs(adjust - lastHistoryAdjust) > 0.1;
1442
+ const adjust = scrollAdjustHandler.getAdjust();
1443
+ const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
1249
1444
  if (adjustChanged) {
1250
1445
  state.scrollHistory.length = 0;
1251
1446
  }
@@ -1258,7 +1453,7 @@ function updateScroll(ctx, state, newScroll, forceUpdate) {
1258
1453
  if (state.scrollHistory.length > 5) {
1259
1454
  state.scrollHistory.shift();
1260
1455
  }
1261
- state.scrollPrev = state.scroll;
1456
+ state.scrollPrev = prevScroll;
1262
1457
  state.scrollPrevTime = state.scrollTime;
1263
1458
  state.scroll = newScroll;
1264
1459
  state.scrollTime = currentTime;
@@ -1270,22 +1465,37 @@ function updateScroll(ctx, state, newScroll, forceUpdate) {
1270
1465
  return;
1271
1466
  }
1272
1467
  }
1273
- if (forceUpdate || state.dataChangeNeedsScrollUpdate || Math.abs(state.scroll - state.scrollPrev) > 2) {
1468
+ const scrollDelta = Math.abs(newScroll - prevScroll);
1469
+ const scrollLength = state.scrollLength;
1470
+ const lastCalculated = state.scrollLastCalculate;
1471
+ const shouldUpdate = forceUpdate || state.dataChangeNeedsScrollUpdate || state.scrollLastCalculate === void 0 || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
1472
+ if (shouldUpdate) {
1473
+ state.scrollLastCalculate = state.scroll;
1274
1474
  state.ignoreScrollFromMVCPIgnored = false;
1275
- (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { doMVCP: scrollingTo !== void 0 });
1276
- checkAtBottom(ctx, state);
1277
- checkAtTop(state);
1475
+ state.lastScrollDelta = scrollDelta;
1476
+ const runCalculateItems = () => {
1477
+ var _a3;
1478
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { doMVCP: scrollingTo !== void 0 });
1479
+ checkThresholds(ctx);
1480
+ };
1481
+ if (Platform2.OS === "web" && scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength) {
1482
+ flushSync(runCalculateItems);
1483
+ } else {
1484
+ runCalculateItems();
1485
+ }
1278
1486
  state.dataChangeNeedsScrollUpdate = false;
1487
+ state.lastScrollDelta = 0;
1279
1488
  }
1280
1489
  }
1281
1490
 
1282
1491
  // src/utils/requestAdjust.ts
1283
- function requestAdjust(ctx, state, positionDiff, dataChanged) {
1492
+ function requestAdjust(ctx, positionDiff, dataChanged) {
1493
+ const state = ctx.state;
1284
1494
  if (Math.abs(positionDiff) > 0.1) {
1285
1495
  const needsScrollWorkaround = Platform2.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
1286
1496
  const doit = () => {
1287
1497
  if (needsScrollWorkaround) {
1288
- scrollTo(ctx, state, {
1498
+ scrollTo(ctx, {
1289
1499
  noScrollingTo: true,
1290
1500
  offset: state.scroll
1291
1501
  });
@@ -1298,8 +1508,8 @@ function requestAdjust(ctx, state, positionDiff, dataChanged) {
1298
1508
  };
1299
1509
  state.scroll += positionDiff;
1300
1510
  state.scrollForNextCalculateItemsInView = void 0;
1301
- const didLayout = peek$(ctx, "containersDidLayout");
1302
- if (didLayout) {
1511
+ const readyToRender = peek$(ctx, "readyToRender");
1512
+ if (readyToRender) {
1303
1513
  doit();
1304
1514
  if (Platform2.OS !== "web") {
1305
1515
  const threshold = state.scroll - positionDiff / 2;
@@ -1321,7 +1531,7 @@ function requestAdjust(ctx, state, positionDiff, dataChanged) {
1321
1531
  if (shouldForceUpdate) {
1322
1532
  state.ignoreScrollFromMVCPIgnored = false;
1323
1533
  state.scrollPending = state.scroll;
1324
- updateScroll(ctx, state, state.scroll, true);
1534
+ updateScroll(ctx, state.scroll, true);
1325
1535
  }
1326
1536
  }, delay);
1327
1537
  }
@@ -1336,28 +1546,27 @@ function requestAdjust(ctx, state, positionDiff, dataChanged) {
1336
1546
  var INITIAL_ANCHOR_TOLERANCE = 0.5;
1337
1547
  var INITIAL_ANCHOR_MAX_ATTEMPTS = 4;
1338
1548
  var INITIAL_ANCHOR_SETTLED_TICKS = 2;
1339
- function ensureInitialAnchor(ctx, state) {
1549
+ function ensureInitialAnchor(ctx) {
1340
1550
  var _a3, _b, _c, _d, _e;
1341
- const anchor = state.initialAnchor;
1551
+ const state = ctx.state;
1552
+ const { initialAnchor, didContainersLayout, positions, scroll, scrollLength } = state;
1553
+ const anchor = initialAnchor;
1342
1554
  const item = state.props.data[anchor.index];
1343
- const containersDidLayout = peek$(ctx, "containersDidLayout");
1344
- if (!containersDidLayout) {
1555
+ if (!didContainersLayout) {
1345
1556
  return;
1346
1557
  }
1347
1558
  const id = getId(state, anchor.index);
1348
- if (state.positions.get(id) === void 0) {
1559
+ if (positions.get(id) === void 0) {
1349
1560
  return;
1350
1561
  }
1351
- const size = getItemSize(ctx, state, id, anchor.index, item, true, true);
1562
+ const size = getItemSize(ctx, id, anchor.index, item, true, true);
1352
1563
  if (size === void 0) {
1353
1564
  return;
1354
1565
  }
1355
- const availableSpace = Math.max(0, state.scrollLength - size);
1356
- const desiredOffset = calculateOffsetForIndex(ctx, state, anchor.index) - ((_a3 = anchor.viewOffset) != null ? _a3 : 0) - ((_b = anchor.viewPosition) != null ? _b : 0) * availableSpace;
1357
- const contentSize = getContentSize(ctx);
1358
- const maxOffset = Math.max(0, contentSize - state.scrollLength);
1359
- const clampedDesiredOffset = Math.max(0, Math.min(desiredOffset, maxOffset));
1360
- const delta = clampedDesiredOffset - state.scroll;
1566
+ const availableSpace = Math.max(0, scrollLength - size);
1567
+ const desiredOffset = calculateOffsetForIndex(ctx, anchor.index) - ((_a3 = anchor.viewOffset) != null ? _a3 : 0) - ((_b = anchor.viewPosition) != null ? _b : 0) * availableSpace;
1568
+ const clampedDesiredOffset = clampScrollOffset(ctx, desiredOffset);
1569
+ const delta = clampedDesiredOffset - scroll;
1361
1570
  if (Math.abs(delta) <= INITIAL_ANCHOR_TOLERANCE) {
1362
1571
  const settledTicks = ((_c = anchor.settledTicks) != null ? _c : 0) + 1;
1363
1572
  if (settledTicks >= INITIAL_ANCHOR_SETTLED_TICKS) {
@@ -1381,19 +1590,23 @@ function ensureInitialAnchor(ctx, state) {
1381
1590
  lastDelta: delta,
1382
1591
  settledTicks: 0
1383
1592
  });
1384
- requestAdjust(ctx, state, delta);
1593
+ requestAdjust(ctx, delta);
1385
1594
  }
1386
1595
 
1387
1596
  // src/core/mvcp.ts
1388
- function prepareMVCP(ctx, state, dataChanged) {
1597
+ function prepareMVCP(ctx, dataChanged) {
1598
+ const state = ctx.state;
1389
1599
  const { idsInView, positions, props } = state;
1390
- const { maintainVisibleContentPosition } = props;
1391
- const scrollingTo = peek$(ctx, "scrollingTo");
1600
+ const {
1601
+ maintainVisibleContentPosition: { data: mvcpData, size: mvcpScroll, shouldRestorePosition }
1602
+ } = props;
1603
+ const scrollingTo = state.scrollingTo;
1392
1604
  let prevPosition;
1393
1605
  let targetId;
1394
1606
  const idsInViewWithPositions = [];
1395
1607
  const scrollTarget = scrollingTo == null ? void 0 : scrollingTo.index;
1396
- const shouldMVCP = !dataChanged || maintainVisibleContentPosition;
1608
+ const scrollingToViewPosition = scrollingTo == null ? void 0 : scrollingTo.viewPosition;
1609
+ const shouldMVCP = dataChanged ? mvcpData : mvcpScroll;
1397
1610
  const indexByKey = state.indexByKey;
1398
1611
  if (shouldMVCP) {
1399
1612
  if (scrollTarget !== void 0) {
@@ -1401,7 +1614,7 @@ function prepareMVCP(ctx, state, dataChanged) {
1401
1614
  return void 0;
1402
1615
  }
1403
1616
  targetId = getId(state, scrollTarget);
1404
- } else if (idsInView.length > 0 && peek$(ctx, "containersDidLayout")) {
1617
+ } else if (idsInView.length > 0 && state.didContainersLayout) {
1405
1618
  if (dataChanged) {
1406
1619
  for (let i = 0; i < idsInView.length; i++) {
1407
1620
  const id = idsInView[i];
@@ -1418,10 +1631,18 @@ function prepareMVCP(ctx, state, dataChanged) {
1418
1631
  prevPosition = positions.get(targetId);
1419
1632
  }
1420
1633
  return () => {
1421
- let positionDiff;
1422
- if (dataChanged && targetId === void 0 && maintainVisibleContentPosition) {
1634
+ let positionDiff = 0;
1635
+ if (dataChanged && targetId === void 0 && mvcpData) {
1636
+ const data = state.props.data;
1423
1637
  for (let i = 0; i < idsInViewWithPositions.length; i++) {
1424
1638
  const { id, position } = idsInViewWithPositions[i];
1639
+ const index = indexByKey.get(id);
1640
+ if (index !== void 0 && shouldRestorePosition) {
1641
+ const item = data[index];
1642
+ if (item === void 0 || !shouldRestorePosition(item, index, data)) {
1643
+ continue;
1644
+ }
1645
+ }
1425
1646
  const newPosition = positions.get(id);
1426
1647
  if (newPosition !== void 0) {
1427
1648
  positionDiff = newPosition - position;
@@ -1444,16 +1665,28 @@ function prepareMVCP(ctx, state, dataChanged) {
1444
1665
  positionDiff = diff;
1445
1666
  }
1446
1667
  }
1447
- if (positionDiff !== void 0 && Math.abs(positionDiff) > 0.1) {
1448
- requestAdjust(ctx, state, positionDiff, dataChanged && maintainVisibleContentPosition);
1668
+ if (scrollingToViewPosition && scrollingToViewPosition > 0) {
1669
+ const newSize = getItemSize(ctx, targetId, scrollTarget, state.props.data[scrollTarget]);
1670
+ const prevSize = scrollingTo == null ? void 0 : scrollingTo.itemSize;
1671
+ if (newSize !== void 0 && prevSize !== void 0 && newSize !== (scrollingTo == null ? void 0 : scrollingTo.itemSize)) {
1672
+ const diff = newSize - prevSize;
1673
+ if (diff !== 0) {
1674
+ positionDiff += (newSize - prevSize) * scrollingToViewPosition;
1675
+ scrollingTo.itemSize = newSize;
1676
+ }
1677
+ }
1678
+ }
1679
+ if (Math.abs(positionDiff) > 0.1) {
1680
+ requestAdjust(ctx, positionDiff, dataChanged && mvcpData);
1449
1681
  }
1450
1682
  };
1451
1683
  }
1452
1684
  }
1453
1685
 
1454
1686
  // src/core/prepareColumnStartState.ts
1455
- function prepareColumnStartState(ctx, state, startIndex, useAverageSize) {
1687
+ function prepareColumnStartState(ctx, startIndex, useAverageSize) {
1456
1688
  var _a3;
1689
+ const state = ctx.state;
1457
1690
  const numColumns = peek$(ctx, "numColumns");
1458
1691
  let rowStartIndex = startIndex;
1459
1692
  const columnAtStart = state.columns.get(state.idCache[startIndex]);
@@ -1468,7 +1701,7 @@ function prepareColumnStartState(ctx, state, startIndex, useAverageSize) {
1468
1701
  const prevId = state.idCache[prevIndex];
1469
1702
  const prevPosition = (_a3 = state.positions.get(prevId)) != null ? _a3 : 0;
1470
1703
  const prevRowStart = findRowStartIndex(state, numColumns, prevIndex);
1471
- const prevRowHeight = calculateRowMaxSize(ctx, state, prevRowStart, prevIndex, useAverageSize);
1704
+ const prevRowHeight = calculateRowMaxSize(ctx, prevRowStart, prevIndex, useAverageSize);
1472
1705
  currentRowTop = prevPosition + prevRowHeight;
1473
1706
  }
1474
1707
  return {
@@ -1491,7 +1724,8 @@ function findRowStartIndex(state, numColumns, index) {
1491
1724
  }
1492
1725
  return rowStart;
1493
1726
  }
1494
- function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1727
+ function calculateRowMaxSize(ctx, startIndex, endIndex, useAverageSize) {
1728
+ const state = ctx.state;
1495
1729
  if (endIndex < startIndex) {
1496
1730
  return 0;
1497
1731
  }
@@ -1505,7 +1739,7 @@ function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1505
1739
  continue;
1506
1740
  }
1507
1741
  const id = state.idCache[i];
1508
- const size = getItemSize(ctx, state, id, i, data[i], useAverageSize);
1742
+ const size = getItemSize(ctx, id, i, data[i], useAverageSize);
1509
1743
  if (size > maxSize) {
1510
1744
  maxSize = size;
1511
1745
  }
@@ -1514,22 +1748,46 @@ function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1514
1748
  }
1515
1749
 
1516
1750
  // src/core/updateTotalSize.ts
1517
- function updateTotalSize(ctx, state) {
1751
+ function updateTotalSize(ctx) {
1752
+ var _a3, _b, _c;
1753
+ const state = ctx.state;
1518
1754
  const {
1519
1755
  positions,
1520
1756
  props: { data }
1521
1757
  } = state;
1758
+ const numColumns = (_a3 = peek$(ctx, "numColumns")) != null ? _a3 : 1;
1522
1759
  if (data.length === 0) {
1523
- addTotalSize(ctx, state, null, 0);
1760
+ addTotalSize(ctx, null, 0);
1524
1761
  } else {
1525
1762
  const lastId = getId(state, data.length - 1);
1526
1763
  if (lastId !== void 0) {
1527
1764
  const lastPosition = positions.get(lastId);
1528
1765
  if (lastPosition !== void 0) {
1529
- const lastSize = getItemSize(ctx, state, lastId, data.length - 1, data[data.length - 1]);
1530
- if (lastSize !== void 0) {
1531
- const totalSize = lastPosition + lastSize;
1532
- addTotalSize(ctx, state, null, totalSize);
1766
+ if (numColumns > 1) {
1767
+ let rowStart = data.length - 1;
1768
+ while (rowStart > 0) {
1769
+ const rowId = (_b = state.idCache[rowStart]) != null ? _b : getId(state, rowStart);
1770
+ const column = state.columns.get(rowId);
1771
+ if (column === 1 || column === void 0) {
1772
+ break;
1773
+ }
1774
+ rowStart -= 1;
1775
+ }
1776
+ let maxSize = 0;
1777
+ for (let i = rowStart; i < data.length; i++) {
1778
+ const rowId = (_c = state.idCache[i]) != null ? _c : getId(state, i);
1779
+ const size = getItemSize(ctx, rowId, i, data[i]);
1780
+ if (size > maxSize) {
1781
+ maxSize = size;
1782
+ }
1783
+ }
1784
+ addTotalSize(ctx, null, lastPosition + maxSize);
1785
+ } else {
1786
+ const lastSize = getItemSize(ctx, lastId, data.length - 1, data[data.length - 1]);
1787
+ if (lastSize !== void 0) {
1788
+ const totalSize = lastPosition + lastSize;
1789
+ addTotalSize(ctx, null, totalSize);
1790
+ }
1533
1791
  }
1534
1792
  }
1535
1793
  }
@@ -1539,43 +1797,45 @@ function updateTotalSize(ctx, state) {
1539
1797
  // src/utils/getScrollVelocity.ts
1540
1798
  var getScrollVelocity = (state) => {
1541
1799
  const { scrollHistory } = state;
1542
- let velocity = 0;
1543
- if (scrollHistory.length >= 1) {
1544
- const newest = scrollHistory[scrollHistory.length - 1];
1545
- let oldest;
1546
- let start = 0;
1547
- const now = Date.now();
1548
- for (let i = 0; i < scrollHistory.length - 1; i++) {
1549
- const entry = scrollHistory[i];
1550
- const nextEntry = scrollHistory[i + 1];
1551
- if (i > 0) {
1552
- const prevEntry = scrollHistory[i - 1];
1553
- const prevDirection = entry.scroll - prevEntry.scroll;
1554
- const currentDirection = nextEntry.scroll - entry.scroll;
1555
- if (prevDirection > 0 && currentDirection < 0 || prevDirection < 0 && currentDirection > 0) {
1556
- start = i;
1557
- break;
1558
- }
1559
- }
1800
+ const newestIndex = scrollHistory.length - 1;
1801
+ if (newestIndex < 1) {
1802
+ return 0;
1803
+ }
1804
+ const newest = scrollHistory[newestIndex];
1805
+ const now = Date.now();
1806
+ let direction = 0;
1807
+ for (let i = newestIndex; i > 0; i--) {
1808
+ const delta = scrollHistory[i].scroll - scrollHistory[i - 1].scroll;
1809
+ if (delta !== 0) {
1810
+ direction = Math.sign(delta);
1811
+ break;
1560
1812
  }
1561
- for (let i = start; i < scrollHistory.length - 1; i++) {
1562
- const entry = scrollHistory[i];
1563
- if (now - entry.time <= 1e3) {
1564
- oldest = entry;
1565
- break;
1566
- }
1813
+ }
1814
+ if (direction === 0) {
1815
+ return 0;
1816
+ }
1817
+ let oldest = newest;
1818
+ for (let i = newestIndex - 1; i >= 0; i--) {
1819
+ const current = scrollHistory[i];
1820
+ const next = scrollHistory[i + 1];
1821
+ const delta = next.scroll - current.scroll;
1822
+ const deltaSign = Math.sign(delta);
1823
+ if (deltaSign !== 0 && deltaSign !== direction) {
1824
+ break;
1567
1825
  }
1568
- if (oldest && oldest !== newest) {
1569
- const scrollDiff = newest.scroll - oldest.scroll;
1570
- const timeDiff = newest.time - oldest.time;
1571
- velocity = timeDiff > 0 ? scrollDiff / timeDiff : 0;
1826
+ if (now - current.time > 1e3) {
1827
+ break;
1572
1828
  }
1829
+ oldest = current;
1573
1830
  }
1574
- return velocity;
1831
+ const scrollDiff = newest.scroll - oldest.scroll;
1832
+ const timeDiff = newest.time - oldest.time;
1833
+ return timeDiff > 0 ? scrollDiff / timeDiff : 0;
1575
1834
  };
1576
1835
 
1577
1836
  // src/utils/updateSnapToOffsets.ts
1578
- function updateSnapToOffsets(ctx, state) {
1837
+ function updateSnapToOffsets(ctx) {
1838
+ const state = ctx.state;
1579
1839
  const {
1580
1840
  positions,
1581
1841
  props: { snapToIndices }
@@ -1590,39 +1850,46 @@ function updateSnapToOffsets(ctx, state) {
1590
1850
  }
1591
1851
 
1592
1852
  // src/core/updateItemPositions.ts
1593
- function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP } = {
1853
+ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP } = {
1594
1854
  doMVCP: false,
1595
1855
  forceFullUpdate: false,
1596
1856
  scrollBottomBuffered: -1,
1597
1857
  startIndex: 0
1598
1858
  }) {
1599
- var _a3, _b, _c, _d, _e;
1859
+ var _a3, _b, _c, _d, _e, _f;
1860
+ const state = ctx.state;
1600
1861
  const {
1601
1862
  columns,
1863
+ columnSpans,
1602
1864
  indexByKey,
1603
1865
  positions,
1604
1866
  idCache,
1605
1867
  sizesKnown,
1606
- props: { getEstimatedItemSize, snapToIndices, enableAverages }
1868
+ props: { data, getEstimatedItemSize, overrideItemLayout, snapToIndices },
1869
+ scrollingTo
1607
1870
  } = state;
1608
- const data = state.props.data;
1609
1871
  const dataLength = data.length;
1610
- const numColumns = peek$(ctx, "numColumns");
1611
- const scrollingTo = peek$(ctx, "scrollingTo");
1872
+ const numColumns = (_a3 = peek$(ctx, "numColumns")) != null ? _a3 : 1;
1612
1873
  const hasColumns = numColumns > 1;
1613
1874
  const indexByKeyForChecking = IS_DEV ? /* @__PURE__ */ new Map() : void 0;
1614
- const shouldOptimize = !forceFullUpdate && !dataChanged && Math.abs(getScrollVelocity(state)) > 0;
1875
+ const extraData = peek$(ctx, "extraData");
1876
+ const layoutConfig = overrideItemLayout ? { span: 1 } : void 0;
1877
+ const lastScrollDelta = state.lastScrollDelta;
1878
+ const velocity = getScrollVelocity(state);
1879
+ const shouldOptimize = !forceFullUpdate && !dataChanged && (Math.abs(velocity) > 0 || Platform2.OS === "web" && state.scrollLength > 0 && lastScrollDelta > state.scrollLength);
1615
1880
  const maxVisibleArea = scrollBottomBuffered + 1e3;
1616
- const useAverageSize = enableAverages && !getEstimatedItemSize;
1617
- const preferCachedSize = !doMVCP || dataChanged || state.scrollAdjustHandler.getAdjust() !== 0 || ((_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0) !== 0;
1881
+ const useAverageSize = !getEstimatedItemSize;
1882
+ const preferCachedSize = !doMVCP || dataChanged || state.scrollAdjustHandler.getAdjust() !== 0 || ((_b = peek$(ctx, "scrollAdjustPending")) != null ? _b : 0) !== 0;
1618
1883
  let currentRowTop = 0;
1619
1884
  let column = 1;
1620
1885
  let maxSizeInRow = 0;
1886
+ if (dataChanged) {
1887
+ columnSpans.clear();
1888
+ }
1621
1889
  if (startIndex > 0) {
1622
1890
  if (hasColumns) {
1623
1891
  const { startIndex: processedStartIndex, currentRowTop: initialRowTop } = prepareColumnStartState(
1624
1892
  ctx,
1625
- state,
1626
1893
  startIndex,
1627
1894
  useAverageSize
1628
1895
  );
@@ -1631,8 +1898,8 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1631
1898
  } else if (startIndex < dataLength) {
1632
1899
  const prevIndex = startIndex - 1;
1633
1900
  const prevId = getId(state, prevIndex);
1634
- const prevPosition = (_b = positions.get(prevId)) != null ? _b : 0;
1635
- const prevSize = (_c = sizesKnown.get(prevId)) != null ? _c : getItemSize(ctx, state, prevId, prevIndex, data[prevIndex], useAverageSize, preferCachedSize);
1901
+ const prevPosition = (_c = positions.get(prevId)) != null ? _c : 0;
1902
+ const prevSize = (_d = sizesKnown.get(prevId)) != null ? _d : getItemSize(ctx, prevId, prevIndex, data[prevIndex], useAverageSize, preferCachedSize);
1636
1903
  currentRowTop = prevPosition + prevSize;
1637
1904
  }
1638
1905
  }
@@ -1648,8 +1915,22 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1648
1915
  const itemsPerRow = hasColumns ? numColumns : 1;
1649
1916
  breakAt = i + itemsPerRow + 10;
1650
1917
  }
1651
- const id = (_d = idCache[i]) != null ? _d : getId(state, i);
1652
- const size = (_e = sizesKnown.get(id)) != null ? _e : getItemSize(ctx, state, id, i, data[i], useAverageSize, preferCachedSize);
1918
+ const id = (_e = idCache[i]) != null ? _e : getId(state, i);
1919
+ let span = 1;
1920
+ if (hasColumns && overrideItemLayout && layoutConfig) {
1921
+ layoutConfig.span = 1;
1922
+ overrideItemLayout(layoutConfig, data[i], i, numColumns, extraData);
1923
+ const requestedSpan = layoutConfig.span;
1924
+ if (requestedSpan !== void 0 && Number.isFinite(requestedSpan)) {
1925
+ span = Math.max(1, Math.min(numColumns, Math.round(requestedSpan)));
1926
+ }
1927
+ }
1928
+ if (hasColumns && column + span - 1 > numColumns) {
1929
+ currentRowTop += maxSizeInRow;
1930
+ column = 1;
1931
+ maxSizeInRow = 0;
1932
+ }
1933
+ const size = (_f = sizesKnown.get(id)) != null ? _f : getItemSize(ctx, id, i, data[i], useAverageSize, preferCachedSize);
1653
1934
  if (IS_DEV && needsIndexByKey) {
1654
1935
  if (indexByKeyForChecking.has(id)) {
1655
1936
  console.error(
@@ -1658,16 +1939,20 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1658
1939
  }
1659
1940
  indexByKeyForChecking.set(id, i);
1660
1941
  }
1661
- positions.set(id, currentRowTop);
1942
+ if (currentRowTop !== positions.get(id)) {
1943
+ positions.set(id, currentRowTop);
1944
+ notifyPosition$(ctx, id, currentRowTop);
1945
+ }
1662
1946
  if (needsIndexByKey) {
1663
1947
  indexByKey.set(id, i);
1664
1948
  }
1665
1949
  columns.set(id, column);
1950
+ columnSpans.set(id, span);
1666
1951
  if (hasColumns) {
1667
1952
  if (size > maxSizeInRow) {
1668
1953
  maxSizeInRow = size;
1669
1954
  }
1670
- column++;
1955
+ column += span;
1671
1956
  if (column > numColumns) {
1672
1957
  currentRowTop += maxSizeInRow;
1673
1958
  column = 1;
@@ -1678,10 +1963,10 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1678
1963
  }
1679
1964
  }
1680
1965
  if (!didBreakEarly) {
1681
- updateTotalSize(ctx, state);
1966
+ updateTotalSize(ctx);
1682
1967
  }
1683
1968
  if (snapToIndices) {
1684
- updateSnapToOffsets(ctx, state);
1969
+ updateSnapToOffsets(ctx);
1685
1970
  }
1686
1971
  }
1687
1972
 
@@ -1759,7 +2044,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
1759
2044
  if (previousViewableItems) {
1760
2045
  for (const viewToken of previousViewableItems) {
1761
2046
  const containerId = findContainerId(ctx, viewToken.key);
1762
- if (!isViewable(
2047
+ if (!checkIsViewable(
1763
2048
  state,
1764
2049
  ctx,
1765
2050
  viewabilityConfig,
@@ -1780,7 +2065,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
1780
2065
  if (item) {
1781
2066
  const key = getId(state, i);
1782
2067
  const containerId = findContainerId(ctx, key);
1783
- if (isViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, i)) {
2068
+ if (checkIsViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, i)) {
1784
2069
  const viewToken = {
1785
2070
  containerId,
1786
2071
  index: i,
@@ -1840,11 +2125,11 @@ function computeViewability(state, ctx, viewabilityConfig, containerId, key, scr
1840
2125
  const percentVisible = size ? isEntirelyVisible ? 100 : 100 * (sizeVisible / size) : 0;
1841
2126
  const percentOfScroller = size ? 100 * (sizeVisible / scrollSize) : 0;
1842
2127
  const percent = isEntirelyVisible ? 100 : viewAreaMode ? percentOfScroller : percentVisible;
1843
- const isViewable2 = percent >= viewablePercentThreshold;
2128
+ const isViewable = percent >= viewablePercentThreshold;
1844
2129
  const value = {
1845
2130
  containerId,
1846
2131
  index,
1847
- isViewable: isViewable2,
2132
+ isViewable,
1848
2133
  item,
1849
2134
  key,
1850
2135
  percentOfScroller,
@@ -1863,8 +2148,11 @@ function computeViewability(state, ctx, viewabilityConfig, containerId, key, scr
1863
2148
  }
1864
2149
  return value;
1865
2150
  }
1866
- function isViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
1867
- const value = ctx.mapViewabilityAmountValues.get(containerId) || computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index);
2151
+ function checkIsViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
2152
+ let value = ctx.mapViewabilityAmountValues.get(containerId);
2153
+ if (!value || value.key !== key) {
2154
+ value = computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index);
2155
+ }
1868
2156
  return value.isViewable;
1869
2157
  }
1870
2158
  function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
@@ -1892,8 +2180,9 @@ function checkAllSizesKnown(state) {
1892
2180
  }
1893
2181
 
1894
2182
  // src/utils/findAvailableContainers.ts
1895
- function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers) {
2183
+ function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers) {
1896
2184
  const numContainers = peek$(ctx, "numContainers");
2185
+ const state = ctx.state;
1897
2186
  const { stickyContainerPool, containerItemTypes } = state;
1898
2187
  const result = [];
1899
2188
  const availableContainers = [];
@@ -2013,21 +2302,26 @@ function comparatorByDistance(a, b) {
2013
2302
  }
2014
2303
 
2015
2304
  // src/core/scrollToIndex.ts
2016
- function scrollToIndex(ctx, state, { index, viewOffset = 0, animated = true, viewPosition }) {
2017
- if (index >= state.props.data.length) {
2018
- index = state.props.data.length - 1;
2305
+ function scrollToIndex(ctx, { index, viewOffset = 0, animated = true, viewPosition }) {
2306
+ const state = ctx.state;
2307
+ const { data } = state.props;
2308
+ if (index >= data.length) {
2309
+ index = data.length - 1;
2019
2310
  } else if (index < 0) {
2020
2311
  index = 0;
2021
2312
  }
2022
- const firstIndexOffset = calculateOffsetForIndex(ctx, state, index);
2023
- const isLast = index === state.props.data.length - 1;
2313
+ const firstIndexOffset = calculateOffsetForIndex(ctx, index);
2314
+ const isLast = index === data.length - 1;
2024
2315
  if (isLast && viewPosition === void 0) {
2025
2316
  viewPosition = 1;
2026
2317
  }
2027
2318
  state.scrollForNextCalculateItemsInView = void 0;
2028
- scrollTo(ctx, state, {
2319
+ const targetId = getId(state, index);
2320
+ const itemSize = getItemSize(ctx, targetId, index, state.props.data[index]);
2321
+ scrollTo(ctx, {
2029
2322
  animated,
2030
2323
  index,
2324
+ itemSize,
2031
2325
  offset: firstIndexOffset,
2032
2326
  viewOffset,
2033
2327
  viewPosition: viewPosition != null ? viewPosition : 0
@@ -2035,34 +2329,28 @@ function scrollToIndex(ctx, state, { index, viewOffset = 0, animated = true, vie
2035
2329
  }
2036
2330
 
2037
2331
  // src/utils/setDidLayout.ts
2038
- function setDidLayout(ctx, state) {
2332
+ function setDidLayout(ctx) {
2333
+ const state = ctx.state;
2039
2334
  const {
2040
2335
  loadStartTime,
2041
2336
  initialScroll,
2042
2337
  props: { onLoad }
2043
2338
  } = state;
2044
2339
  state.queuedInitialLayout = true;
2045
- checkAtBottom(ctx, state);
2340
+ checkAtBottom(ctx);
2046
2341
  const setIt = () => {
2047
- set$(ctx, "containersDidLayout", true);
2342
+ setInitialRenderState(ctx, { didLayout: true });
2048
2343
  if (onLoad) {
2049
2344
  onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
2050
2345
  }
2051
2346
  };
2052
- if (Platform2.OS === "android" && initialScroll) {
2053
- if (IsNewArchitecture) {
2054
- scrollToIndex(ctx, state, { ...initialScroll, animated: false });
2055
- requestAnimationFrame(() => {
2056
- scrollToIndex(ctx, state, { ...initialScroll, animated: false });
2057
- setIt();
2058
- });
2059
- } else {
2060
- scrollToIndex(ctx, state, { ...initialScroll, animated: false });
2061
- setIt();
2062
- }
2063
- } else {
2064
- setIt();
2347
+ if ((initialScroll == null ? void 0 : initialScroll.index) !== void 0) {
2348
+ const target = initialScroll;
2349
+ const runScroll = () => scrollToIndex(ctx, { ...target, animated: false });
2350
+ runScroll();
2351
+ requestAnimationFrame(runScroll);
2065
2352
  }
2353
+ setIt();
2066
2354
  }
2067
2355
 
2068
2356
  // src/core/calculateItemsInView.ts
@@ -2080,36 +2368,40 @@ function findCurrentStickyIndex(stickyArray, scroll, state) {
2080
2368
  }
2081
2369
  return -1;
2082
2370
  }
2083
- function getActiveStickyIndices(ctx, state, stickyHeaderIndices) {
2371
+ function getActiveStickyIndices(ctx, stickyHeaderIndices) {
2372
+ const state = ctx.state;
2084
2373
  return new Set(
2085
2374
  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))
2086
2375
  );
2087
2376
  }
2088
- function handleStickyActivation(ctx, state, stickyHeaderIndices, stickyArray, currentStickyIdx, needNewContainers, startBuffered, endBuffered) {
2377
+ function handleStickyActivation(ctx, stickyHeaderIndices, stickyArray, currentStickyIdx, needNewContainers, needNewContainersSet, startBuffered, endBuffered) {
2089
2378
  var _a3;
2090
- const activeIndices = getActiveStickyIndices(ctx, state, stickyHeaderIndices);
2091
- state.activeStickyIndex = currentStickyIdx >= 0 ? stickyArray[currentStickyIdx] : void 0;
2379
+ const state = ctx.state;
2380
+ const activeIndices = getActiveStickyIndices(ctx, stickyHeaderIndices);
2381
+ set$(ctx, "activeStickyIndex", currentStickyIdx >= 0 ? stickyArray[currentStickyIdx] : -1);
2092
2382
  for (let offset = 0; offset <= 1; offset++) {
2093
2383
  const idx = currentStickyIdx - offset;
2094
2384
  if (idx < 0 || activeIndices.has(stickyArray[idx])) continue;
2095
2385
  const stickyIndex = stickyArray[idx];
2096
2386
  const stickyId = (_a3 = state.idCache[stickyIndex]) != null ? _a3 : getId(state, stickyIndex);
2097
- if (stickyId && !state.containerItemKeys.has(stickyId) && (stickyIndex < startBuffered || stickyIndex > endBuffered)) {
2387
+ if (stickyId && !state.containerItemKeys.has(stickyId) && (stickyIndex < startBuffered || stickyIndex > endBuffered) && !needNewContainersSet.has(stickyIndex)) {
2388
+ needNewContainersSet.add(stickyIndex);
2098
2389
  needNewContainers.push(stickyIndex);
2099
2390
  }
2100
2391
  }
2101
2392
  }
2102
- function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, currentStickyIdx, pendingRemoval) {
2393
+ function handleStickyRecycling(ctx, stickyArray, scroll, scrollBuffer, currentStickyIdx, pendingRemoval, alwaysRenderIndicesSet) {
2103
2394
  var _a3, _b, _c;
2395
+ const state = ctx.state;
2104
2396
  for (const containerIndex of state.stickyContainerPool) {
2105
2397
  const itemKey = peek$(ctx, `containerItemKey${containerIndex}`);
2106
2398
  const itemIndex = itemKey ? state.indexByKey.get(itemKey) : void 0;
2107
2399
  if (itemIndex === void 0) continue;
2400
+ if (alwaysRenderIndicesSet.has(itemIndex)) continue;
2108
2401
  const arrayIdx = stickyArray.indexOf(itemIndex);
2109
2402
  if (arrayIdx === -1) {
2110
2403
  state.stickyContainerPool.delete(containerIndex);
2111
2404
  set$(ctx, `containerSticky${containerIndex}`, false);
2112
- set$(ctx, `containerStickyOffset${containerIndex}`, void 0);
2113
2405
  continue;
2114
2406
  }
2115
2407
  const isRecentSticky = arrayIdx >= currentStickyIdx - 1 && arrayIdx <= currentStickyIdx + 1;
@@ -2124,7 +2416,7 @@ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, cu
2124
2416
  const currentId = (_b = state.idCache[itemIndex]) != null ? _b : getId(state, itemIndex);
2125
2417
  if (currentId) {
2126
2418
  const currentPos = state.positions.get(currentId);
2127
- const currentSize = (_c = state.sizes.get(currentId)) != null ? _c : getItemSize(ctx, state, currentId, itemIndex, state.props.data[itemIndex]);
2419
+ const currentSize = (_c = state.sizes.get(currentId)) != null ? _c : getItemSize(ctx, currentId, itemIndex, state.props.data[itemIndex]);
2128
2420
  shouldRecycle = currentPos !== void 0 && scroll > currentPos + currentSize + scrollBuffer * 3;
2129
2421
  }
2130
2422
  }
@@ -2133,11 +2425,13 @@ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, cu
2133
2425
  }
2134
2426
  }
2135
2427
  }
2136
- function calculateItemsInView(ctx, state, params = {}) {
2428
+ function calculateItemsInView(ctx, params = {}) {
2429
+ const state = ctx.state;
2137
2430
  reactNative.unstable_batchedUpdates(() => {
2138
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j;
2431
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
2139
2432
  const {
2140
2433
  columns,
2434
+ columnSpans,
2141
2435
  containerItemKeys,
2142
2436
  enableScrollForNextCalculateItemsInView,
2143
2437
  idCache,
@@ -2145,7 +2439,15 @@ function calculateItemsInView(ctx, state, params = {}) {
2145
2439
  initialScroll,
2146
2440
  minIndexSizeChanged,
2147
2441
  positions,
2148
- props: { getItemType, itemsAreEqual, keyExtractor, onStickyHeaderChange, scrollBuffer },
2442
+ props: {
2443
+ alwaysRenderIndicesArr,
2444
+ alwaysRenderIndicesSet,
2445
+ getItemType,
2446
+ itemsAreEqual,
2447
+ keyExtractor,
2448
+ onStickyHeaderChange,
2449
+ scrollBuffer
2450
+ },
2149
2451
  scrollForNextCalculateItemsInView,
2150
2452
  scrollLength,
2151
2453
  sizes,
@@ -2155,10 +2457,12 @@ function calculateItemsInView(ctx, state, params = {}) {
2155
2457
  const { data } = state.props;
2156
2458
  const stickyIndicesArr = state.props.stickyIndicesArr || [];
2157
2459
  const stickyIndicesSet = state.props.stickyIndicesSet || /* @__PURE__ */ new Set();
2460
+ const alwaysRenderArr = alwaysRenderIndicesArr || [];
2461
+ const alwaysRenderSet = alwaysRenderIndicesSet || /* @__PURE__ */ new Set();
2158
2462
  const prevNumContainers = peek$(ctx, "numContainers");
2159
2463
  if (!data || scrollLength === 0 || !prevNumContainers) {
2160
- if (state.initialAnchor) {
2161
- ensureInitialAnchor(ctx, state);
2464
+ if (!IsNewArchitecture && state.initialAnchor) {
2465
+ ensureInitialAnchor(ctx);
2162
2466
  }
2163
2467
  return;
2164
2468
  }
@@ -2173,15 +2477,14 @@ function calculateItemsInView(ctx, state, params = {}) {
2173
2477
  if (!queuedInitialLayout && initialScroll) {
2174
2478
  const updatedOffset = calculateOffsetWithOffsetPosition(
2175
2479
  ctx,
2176
- state,
2177
- calculateOffsetForIndex(ctx, state, initialScroll.index),
2480
+ calculateOffsetForIndex(ctx, initialScroll.index),
2178
2481
  initialScroll
2179
2482
  );
2180
2483
  scrollState = updatedOffset;
2181
2484
  }
2182
2485
  const scrollAdjustPending = (_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0;
2183
2486
  const scrollAdjustPad = scrollAdjustPending - topPad;
2184
- let scroll = scrollState + scrollExtra + scrollAdjustPad;
2487
+ let scroll = Math.round(scrollState + scrollExtra + scrollAdjustPad);
2185
2488
  if (scroll + scrollLength > totalSize) {
2186
2489
  scroll = Math.max(0, totalSize - scrollLength);
2187
2490
  }
@@ -2189,11 +2492,12 @@ function calculateItemsInView(ctx, state, params = {}) {
2189
2492
  set$(ctx, "debugRawScroll", scrollState);
2190
2493
  set$(ctx, "debugComputedScroll", scroll);
2191
2494
  }
2192
- const previousStickyIndex = state.activeStickyIndex;
2495
+ const previousStickyIndex = peek$(ctx, "activeStickyIndex");
2193
2496
  const currentStickyIdx = stickyIndicesArr.length > 0 ? findCurrentStickyIndex(stickyIndicesArr, scroll, state) : -1;
2194
- const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyIndicesArr[currentStickyIdx] : void 0;
2195
- state.activeStickyIndex = nextActiveStickyIndex;
2196
- set$(ctx, "activeStickyIndex", nextActiveStickyIndex);
2497
+ const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyIndicesArr[currentStickyIdx] : -1;
2498
+ if (currentStickyIdx >= 0 || previousStickyIndex >= 0) {
2499
+ set$(ctx, "activeStickyIndex", nextActiveStickyIndex);
2500
+ }
2197
2501
  let scrollBufferTop = scrollBuffer;
2198
2502
  let scrollBufferBottom = scrollBuffer;
2199
2503
  if (speed > 0 || speed === 0 && scroll < Math.max(50, scrollBuffer)) {
@@ -2206,23 +2510,25 @@ function calculateItemsInView(ctx, state, params = {}) {
2206
2510
  const scrollTopBuffered = scroll - scrollBufferTop;
2207
2511
  const scrollBottom = scroll + scrollLength + (scroll < 0 ? -scroll : 0);
2208
2512
  const scrollBottomBuffered = scrollBottom + scrollBufferBottom;
2209
- if (!dataChanged && scrollForNextCalculateItemsInView) {
2513
+ if (!dataChanged && !forceFullItemPositions && scrollForNextCalculateItemsInView) {
2210
2514
  const { top, bottom } = scrollForNextCalculateItemsInView;
2211
- if (scrollTopBuffered > top && scrollBottomBuffered < bottom) {
2212
- if (state.initialAnchor) {
2213
- ensureInitialAnchor(ctx, state);
2515
+ if (top === null && bottom === null) {
2516
+ state.scrollForNextCalculateItemsInView = void 0;
2517
+ } else if ((top === null || scrollTopBuffered > top) && (bottom === null || scrollBottomBuffered < bottom)) {
2518
+ if (!IsNewArchitecture && state.initialAnchor) {
2519
+ ensureInitialAnchor(ctx);
2214
2520
  }
2215
2521
  return;
2216
2522
  }
2217
2523
  }
2218
- const checkMVCP = doMVCP ? prepareMVCP(ctx, state, dataChanged) : void 0;
2524
+ const checkMVCP = doMVCP ? prepareMVCP(ctx, dataChanged) : void 0;
2219
2525
  if (dataChanged) {
2220
2526
  indexByKey.clear();
2221
2527
  idCache.length = 0;
2222
2528
  positions.clear();
2223
2529
  }
2224
- const startIndex = dataChanged ? 0 : (_b = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _b : 0;
2225
- updateItemPositions(ctx, state, dataChanged, {
2530
+ const startIndex = forceFullItemPositions || dataChanged ? 0 : (_b = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _b : 0;
2531
+ updateItemPositions(ctx, dataChanged, {
2226
2532
  doMVCP,
2227
2533
  forceFullUpdate: !!forceFullItemPositions,
2228
2534
  scrollBottomBuffered,
@@ -2241,17 +2547,23 @@ function calculateItemsInView(ctx, state, params = {}) {
2241
2547
  for (let i = loopStart; i >= 0; i--) {
2242
2548
  const id = (_c = idCache[i]) != null ? _c : getId(state, i);
2243
2549
  const top = positions.get(id);
2244
- const size = (_d = sizes.get(id)) != null ? _d : getItemSize(ctx, state, id, i, data[i]);
2550
+ const size = (_d = sizes.get(id)) != null ? _d : getItemSize(ctx, id, i, data[i]);
2245
2551
  const bottom = top + size;
2246
- if (bottom > scroll - scrollBuffer) {
2552
+ if (bottom > scroll - scrollBufferTop) {
2247
2553
  loopStart = i;
2248
2554
  } else {
2249
2555
  break;
2250
2556
  }
2251
2557
  }
2252
- const loopStartMod = loopStart % numColumns;
2253
- if (loopStartMod > 0) {
2254
- loopStart -= loopStartMod;
2558
+ if (numColumns > 1) {
2559
+ while (loopStart > 0) {
2560
+ const loopId = (_e = idCache[loopStart]) != null ? _e : getId(state, loopStart);
2561
+ const loopColumn = columns.get(loopId);
2562
+ if (loopColumn === 1 || loopColumn === void 0) {
2563
+ break;
2564
+ }
2565
+ loopStart -= 1;
2566
+ }
2255
2567
  }
2256
2568
  let foundEnd = false;
2257
2569
  let nextTop;
@@ -2267,8 +2579,8 @@ function calculateItemsInView(ctx, state, params = {}) {
2267
2579
  let firstFullyOnScreenIndex;
2268
2580
  const dataLength = data.length;
2269
2581
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
2270
- const id = (_e = idCache[i]) != null ? _e : getId(state, i);
2271
- const size = (_f = sizes.get(id)) != null ? _f : getItemSize(ctx, state, id, i, data[i]);
2582
+ const id = (_f = idCache[i]) != null ? _f : getId(state, i);
2583
+ const size = (_g = sizes.get(id)) != null ? _g : getItemSize(ctx, id, i, data[i]);
2272
2584
  const top = positions.get(id);
2273
2585
  if (!foundEnd) {
2274
2586
  if (startNoBuffer === null && top + size > scroll) {
@@ -2280,7 +2592,11 @@ function calculateItemsInView(ctx, state, params = {}) {
2280
2592
  if (startBuffered === null && top + size > scrollTopBuffered) {
2281
2593
  startBuffered = i;
2282
2594
  startBufferedId = id;
2283
- nextTop = top;
2595
+ if (scrollTopBuffered < 0) {
2596
+ nextTop = null;
2597
+ } else {
2598
+ nextTop = top;
2599
+ }
2284
2600
  }
2285
2601
  if (startNoBuffer !== null) {
2286
2602
  if (top <= scrollBottom) {
@@ -2288,7 +2604,11 @@ function calculateItemsInView(ctx, state, params = {}) {
2288
2604
  }
2289
2605
  if (top <= scrollBottomBuffered) {
2290
2606
  endBuffered = i;
2291
- nextBottom = top + size;
2607
+ if (scrollBottomBuffered > totalSize) {
2608
+ nextBottom = null;
2609
+ } else {
2610
+ nextBottom = top + size;
2611
+ }
2292
2612
  } else {
2293
2613
  foundEnd = true;
2294
2614
  }
@@ -2297,7 +2617,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2297
2617
  }
2298
2618
  const idsInView = [];
2299
2619
  for (let i = firstFullyOnScreenIndex; i <= endNoBuffer; i++) {
2300
- const id = (_g = idCache[i]) != null ? _g : getId(state, i);
2620
+ const id = (_h = idCache[i]) != null ? _h : getId(state, i);
2301
2621
  idsInView.push(id);
2302
2622
  }
2303
2623
  Object.assign(state, {
@@ -2310,12 +2630,12 @@ function calculateItemsInView(ctx, state, params = {}) {
2310
2630
  startNoBuffer
2311
2631
  });
2312
2632
  if (enableScrollForNextCalculateItemsInView && nextTop !== void 0 && nextBottom !== void 0) {
2313
- state.scrollForNextCalculateItemsInView = nextTop !== void 0 && nextBottom !== void 0 ? {
2633
+ state.scrollForNextCalculateItemsInView = isNullOrUndefined(nextTop) && isNullOrUndefined(nextBottom) ? void 0 : {
2314
2634
  bottom: nextBottom,
2315
2635
  top: nextTop
2316
- } : void 0;
2636
+ };
2317
2637
  }
2318
- const numContainers = peek$(ctx, "numContainers");
2638
+ let numContainers = prevNumContainers;
2319
2639
  const pendingRemoval = [];
2320
2640
  if (dataChanged) {
2321
2641
  for (let i = 0; i < numContainers; i++) {
@@ -2326,37 +2646,46 @@ function calculateItemsInView(ctx, state, params = {}) {
2326
2646
  }
2327
2647
  }
2328
2648
  if (startBuffered !== null && endBuffered !== null) {
2329
- let numContainers2 = prevNumContainers;
2330
2649
  const needNewContainers = [];
2650
+ const needNewContainersSet = /* @__PURE__ */ new Set();
2331
2651
  for (let i = startBuffered; i <= endBuffered; i++) {
2332
- const id = (_h = idCache[i]) != null ? _h : getId(state, i);
2652
+ const id = (_i = idCache[i]) != null ? _i : getId(state, i);
2333
2653
  if (!containerItemKeys.has(id)) {
2654
+ needNewContainersSet.add(i);
2334
2655
  needNewContainers.push(i);
2335
2656
  }
2336
2657
  }
2658
+ if (alwaysRenderArr.length > 0) {
2659
+ for (const index of alwaysRenderArr) {
2660
+ if (index < 0 || index >= dataLength) continue;
2661
+ const id = (_j = idCache[index]) != null ? _j : getId(state, index);
2662
+ if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
2663
+ needNewContainersSet.add(index);
2664
+ needNewContainers.push(index);
2665
+ }
2666
+ }
2667
+ }
2337
2668
  if (stickyIndicesArr.length > 0) {
2338
2669
  handleStickyActivation(
2339
2670
  ctx,
2340
- state,
2341
2671
  stickyIndicesSet,
2342
2672
  stickyIndicesArr,
2343
2673
  currentStickyIdx,
2344
2674
  needNewContainers,
2675
+ needNewContainersSet,
2345
2676
  startBuffered,
2346
2677
  endBuffered
2347
2678
  );
2348
- } else {
2349
- state.activeStickyIndex = void 0;
2350
- set$(ctx, "activeStickyIndex", void 0);
2679
+ } else if (previousStickyIndex !== -1) {
2680
+ set$(ctx, "activeStickyIndex", -1);
2351
2681
  }
2352
2682
  if (needNewContainers.length > 0) {
2353
2683
  const requiredItemTypes = getItemType ? needNewContainers.map((i) => {
2354
2684
  const itemType = getItemType(data[i], i);
2355
- return itemType ? String(itemType) : "";
2685
+ return itemType !== void 0 ? String(itemType) : "";
2356
2686
  }) : void 0;
2357
2687
  const availableContainers = findAvailableContainers(
2358
2688
  ctx,
2359
- state,
2360
2689
  needNewContainers.length,
2361
2690
  startBuffered,
2362
2691
  endBuffered,
@@ -2367,7 +2696,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2367
2696
  for (let idx = 0; idx < needNewContainers.length; idx++) {
2368
2697
  const i = needNewContainers[idx];
2369
2698
  const containerIndex = availableContainers[idx];
2370
- const id = (_i = idCache[i]) != null ? _i : getId(state, i);
2699
+ const id = (_k = idCache[i]) != null ? _k : getId(state, i);
2371
2700
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
2372
2701
  if (oldKey && oldKey !== id) {
2373
2702
  containerItemKeys.delete(oldKey);
@@ -2377,30 +2706,55 @@ function calculateItemsInView(ctx, state, params = {}) {
2377
2706
  if (requiredItemTypes) {
2378
2707
  state.containerItemTypes.set(containerIndex, requiredItemTypes[idx]);
2379
2708
  }
2380
- containerItemKeys.add(id);
2381
- if (stickyIndicesSet.has(i)) {
2382
- set$(ctx, `containerSticky${containerIndex}`, true);
2383
- const topPadding = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
2384
- set$(ctx, `containerStickyOffset${containerIndex}`, topPadding);
2709
+ containerItemKeys.set(id, containerIndex);
2710
+ const containerSticky = `containerSticky${containerIndex}`;
2711
+ const isSticky = stickyIndicesSet.has(i);
2712
+ const isAlwaysRender = alwaysRenderSet.has(i);
2713
+ if (isSticky) {
2714
+ set$(ctx, containerSticky, true);
2385
2715
  state.stickyContainerPool.add(containerIndex);
2386
2716
  } else {
2387
- set$(ctx, `containerSticky${containerIndex}`, false);
2388
- state.stickyContainerPool.delete(containerIndex);
2717
+ if (peek$(ctx, containerSticky)) {
2718
+ set$(ctx, containerSticky, false);
2719
+ }
2720
+ if (isAlwaysRender) {
2721
+ state.stickyContainerPool.add(containerIndex);
2722
+ } else if (state.stickyContainerPool.has(containerIndex)) {
2723
+ state.stickyContainerPool.delete(containerIndex);
2724
+ }
2389
2725
  }
2390
- if (containerIndex >= numContainers2) {
2391
- numContainers2 = containerIndex + 1;
2726
+ if (containerIndex >= numContainers) {
2727
+ numContainers = containerIndex + 1;
2392
2728
  }
2393
2729
  }
2394
- if (numContainers2 !== prevNumContainers) {
2395
- set$(ctx, "numContainers", numContainers2);
2396
- if (numContainers2 > peek$(ctx, "numContainersPooled")) {
2397
- set$(ctx, "numContainersPooled", Math.ceil(numContainers2 * 1.5));
2730
+ if (numContainers !== prevNumContainers) {
2731
+ set$(ctx, "numContainers", numContainers);
2732
+ if (numContainers > peek$(ctx, "numContainersPooled")) {
2733
+ set$(ctx, "numContainersPooled", Math.ceil(numContainers * 1.5));
2734
+ }
2735
+ }
2736
+ }
2737
+ if (alwaysRenderArr.length > 0) {
2738
+ for (const index of alwaysRenderArr) {
2739
+ if (index < 0 || index >= dataLength) continue;
2740
+ const id = (_l = idCache[index]) != null ? _l : getId(state, index);
2741
+ const containerIndex = containerItemKeys.get(id);
2742
+ if (containerIndex !== void 0) {
2743
+ state.stickyContainerPool.add(containerIndex);
2398
2744
  }
2399
2745
  }
2400
2746
  }
2401
2747
  }
2402
- if (stickyIndicesArr.length > 0) {
2403
- handleStickyRecycling(ctx, state, stickyIndicesArr, scroll, scrollBuffer, currentStickyIdx, pendingRemoval);
2748
+ if (state.stickyContainerPool.size > 0) {
2749
+ handleStickyRecycling(
2750
+ ctx,
2751
+ stickyIndicesArr,
2752
+ scroll,
2753
+ scrollBuffer,
2754
+ currentStickyIdx,
2755
+ pendingRemoval,
2756
+ alwaysRenderSet
2757
+ );
2404
2758
  }
2405
2759
  let didChangePositions = false;
2406
2760
  for (let i = 0; i < numContainers; i++) {
@@ -2412,26 +2766,28 @@ function calculateItemsInView(ctx, state, params = {}) {
2412
2766
  state.containerItemTypes.delete(i);
2413
2767
  if (state.stickyContainerPool.has(i)) {
2414
2768
  set$(ctx, `containerSticky${i}`, false);
2415
- set$(ctx, `containerStickyOffset${i}`, void 0);
2416
2769
  state.stickyContainerPool.delete(i);
2417
2770
  }
2418
2771
  set$(ctx, `containerItemKey${i}`, void 0);
2419
2772
  set$(ctx, `containerItemData${i}`, void 0);
2420
2773
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
2421
2774
  set$(ctx, `containerColumn${i}`, -1);
2775
+ set$(ctx, `containerSpan${i}`, 1);
2422
2776
  } else {
2423
2777
  const itemIndex = indexByKey.get(itemKey);
2424
2778
  const item = data[itemIndex];
2425
2779
  if (item !== void 0) {
2426
- const id = (_j = idCache[itemIndex]) != null ? _j : getId(state, itemIndex);
2780
+ const id = (_m = idCache[itemIndex]) != null ? _m : getId(state, itemIndex);
2427
2781
  const positionValue = positions.get(id);
2428
2782
  if (positionValue === void 0) {
2429
2783
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
2430
2784
  } else {
2431
2785
  const position = (positionValue || 0) - scrollAdjustPending;
2432
2786
  const column = columns.get(id) || 1;
2787
+ const span = columnSpans.get(id) || 1;
2433
2788
  const prevPos = peek$(ctx, `containerPosition${i}`);
2434
2789
  const prevColumn = peek$(ctx, `containerColumn${i}`);
2790
+ const prevSpan = peek$(ctx, `containerSpan${i}`);
2435
2791
  const prevData = peek$(ctx, `containerItemData${i}`);
2436
2792
  if (position > POSITION_OUT_OF_VIEW && position !== prevPos) {
2437
2793
  set$(ctx, `containerPosition${i}`, position);
@@ -2440,6 +2796,9 @@ function calculateItemsInView(ctx, state, params = {}) {
2440
2796
  if (column >= 0 && column !== prevColumn) {
2441
2797
  set$(ctx, `containerColumn${i}`, column);
2442
2798
  }
2799
+ if (span !== prevSpan) {
2800
+ set$(ctx, `containerSpan${i}`, span);
2801
+ }
2443
2802
  if (prevData !== item && (itemsAreEqual ? !itemsAreEqual(prevData, item, itemIndex, data) : true)) {
2444
2803
  set$(ctx, `containerItemData${i}`, item);
2445
2804
  }
@@ -2452,7 +2811,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2452
2811
  }
2453
2812
  if (!queuedInitialLayout && endBuffered !== null) {
2454
2813
  if (checkAllSizesKnown(state)) {
2455
- setDidLayout(ctx, state);
2814
+ setDidLayout(ctx);
2456
2815
  }
2457
2816
  }
2458
2817
  if (viewabilityConfigCallbackPairs) {
@@ -2465,8 +2824,8 @@ function calculateItemsInView(ctx, state, params = {}) {
2465
2824
  }
2466
2825
  }
2467
2826
  });
2468
- if (state.initialAnchor) {
2469
- ensureInitialAnchor(ctx, state);
2827
+ if (!IsNewArchitecture && state.initialAnchor) {
2828
+ ensureInitialAnchor(ctx);
2470
2829
  }
2471
2830
  }
2472
2831
 
@@ -2491,19 +2850,22 @@ function checkActualChange(state, dataProp, previousData) {
2491
2850
  }
2492
2851
 
2493
2852
  // src/core/doMaintainScrollAtEnd.ts
2494
- function doMaintainScrollAtEnd(ctx, state, animated) {
2853
+ function doMaintainScrollAtEnd(ctx, animated) {
2854
+ const state = ctx.state;
2495
2855
  const {
2856
+ didContainersLayout,
2857
+ isAtEnd,
2496
2858
  refScroller,
2497
2859
  props: { maintainScrollAtEnd }
2498
2860
  } = state;
2499
- if ((state == null ? void 0 : state.isAtEnd) && maintainScrollAtEnd && peek$(ctx, "containersDidLayout")) {
2500
- const paddingTop = peek$(ctx, "alignItemsPaddingTop");
2501
- if (paddingTop > 0) {
2861
+ if (isAtEnd && maintainScrollAtEnd && didContainersLayout) {
2862
+ const contentSize = getContentSize(ctx);
2863
+ if (contentSize < state.scrollLength) {
2502
2864
  state.scroll = 0;
2503
2865
  }
2504
2866
  requestAnimationFrame(() => {
2505
2867
  var _a3;
2506
- if (state == null ? void 0 : state.isAtEnd) {
2868
+ if (state.isAtEnd) {
2507
2869
  state.maintainingScrollAtEnd = true;
2508
2870
  (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
2509
2871
  animated
@@ -2574,28 +2936,29 @@ function updateAveragesOnDataChange(state, oldData, newData) {
2574
2936
  }
2575
2937
 
2576
2938
  // src/core/checkResetContainers.ts
2577
- function checkResetContainers(ctx, state, dataProp) {
2939
+ function checkResetContainers(ctx, dataProp) {
2940
+ const state = ctx.state;
2578
2941
  const { previousData } = state;
2579
2942
  if (previousData) {
2580
2943
  updateAveragesOnDataChange(state, previousData, dataProp);
2581
2944
  }
2582
2945
  const { maintainScrollAtEnd } = state.props;
2583
- calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
2946
+ calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
2584
2947
  const shouldMaintainScrollAtEnd = maintainScrollAtEnd === true || maintainScrollAtEnd.onDataChange;
2585
- const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, state, false);
2948
+ const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, false);
2586
2949
  if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
2587
2950
  state.isEndReached = false;
2588
2951
  }
2589
2952
  if (!didMaintainScrollAtEnd) {
2590
- checkAtTop(state);
2591
- checkAtBottom(ctx, state);
2953
+ checkThresholds(ctx);
2592
2954
  }
2593
2955
  delete state.previousData;
2594
2956
  }
2595
2957
 
2596
2958
  // src/core/doInitialAllocateContainers.ts
2597
- function doInitialAllocateContainers(ctx, state) {
2959
+ function doInitialAllocateContainers(ctx) {
2598
2960
  var _a3, _b, _c;
2961
+ const state = ctx.state;
2599
2962
  const {
2600
2963
  scrollLength,
2601
2964
  props: {
@@ -2616,8 +2979,10 @@ function doInitialAllocateContainers(ctx, state) {
2616
2979
  const num = Math.min(20, data.length);
2617
2980
  for (let i = 0; i < num; i++) {
2618
2981
  const item = data[i];
2619
- const itemType = getItemType ? (_a3 = getItemType(item, i)) != null ? _a3 : "" : "";
2620
- totalSize += (_c = (_b = getFixedItemSize == null ? void 0 : getFixedItemSize(i, item, itemType)) != null ? _b : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(i, item, itemType)) != null ? _c : estimatedItemSize;
2982
+ if (item !== void 0) {
2983
+ const itemType = (_a3 = getItemType == null ? void 0 : getItemType(item, i)) != null ? _a3 : "";
2984
+ totalSize += (_c = (_b = getFixedItemSize == null ? void 0 : getFixedItemSize(item, i, itemType)) != null ? _b : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(item, i, itemType)) != null ? _c : estimatedItemSize;
2985
+ }
2621
2986
  }
2622
2987
  averageItemSize = totalSize / num;
2623
2988
  } else {
@@ -2627,16 +2992,17 @@ function doInitialAllocateContainers(ctx, state) {
2627
2992
  for (let i = 0; i < numContainers; i++) {
2628
2993
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
2629
2994
  set$(ctx, `containerColumn${i}`, -1);
2995
+ set$(ctx, `containerSpan${i}`, 1);
2630
2996
  }
2631
2997
  set$(ctx, "numContainers", numContainers);
2632
2998
  set$(ctx, "numContainersPooled", numContainers * state.props.initialContainerPoolRatio);
2633
2999
  if (!IsNewArchitecture || state.lastLayout) {
2634
3000
  if (state.initialScroll) {
2635
3001
  requestAnimationFrame(() => {
2636
- calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
3002
+ calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
2637
3003
  });
2638
3004
  } else {
2639
- calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
3005
+ calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
2640
3006
  }
2641
3007
  }
2642
3008
  return true;
@@ -2644,7 +3010,8 @@ function doInitialAllocateContainers(ctx, state) {
2644
3010
  }
2645
3011
 
2646
3012
  // src/core/handleLayout.ts
2647
- function handleLayout(ctx, state, layout, setCanRender) {
3013
+ function handleLayout(ctx, layout, setCanRender) {
3014
+ const state = ctx.state;
2648
3015
  const { maintainScrollAtEnd } = state.props;
2649
3016
  const measuredLength = layout[state.props.horizontal ? "width" : "height"];
2650
3017
  const previousLength = state.scrollLength;
@@ -2660,20 +3027,18 @@ function handleLayout(ctx, state, layout, setCanRender) {
2660
3027
  state.lastBatchingAction = Date.now();
2661
3028
  state.scrollForNextCalculateItemsInView = void 0;
2662
3029
  if (scrollLength > 0) {
2663
- doInitialAllocateContainers(ctx, state);
3030
+ doInitialAllocateContainers(ctx);
2664
3031
  }
2665
3032
  if (needsCalculate) {
2666
- calculateItemsInView(ctx, state, { doMVCP: true });
3033
+ calculateItemsInView(ctx, { doMVCP: true });
2667
3034
  }
2668
3035
  if (didChange || otherAxisSize !== prevOtherAxisSize) {
2669
3036
  set$(ctx, "scrollSize", { height: layout.height, width: layout.width });
2670
3037
  }
2671
3038
  if (maintainScrollAtEnd === true || maintainScrollAtEnd.onLayout) {
2672
- doMaintainScrollAtEnd(ctx, state, false);
3039
+ doMaintainScrollAtEnd(ctx, false);
2673
3040
  }
2674
- updateAlignItemsPaddingTop(ctx, state);
2675
- checkAtBottom(ctx, state);
2676
- checkAtTop(state);
3041
+ checkThresholds(ctx);
2677
3042
  if (state) {
2678
3043
  state.needsOtherAxisSize = otherAxisSize - (state.props.stylePaddingTop || 0) < 10;
2679
3044
  }
@@ -2688,8 +3053,9 @@ function handleLayout(ctx, state, layout, setCanRender) {
2688
3053
  }
2689
3054
 
2690
3055
  // src/core/onScroll.ts
2691
- function onScroll(ctx, state, event) {
2692
- var _a3, _b, _c;
3056
+ function onScroll(ctx, event) {
3057
+ var _a3, _b, _c, _d;
3058
+ const state = ctx.state;
2693
3059
  const {
2694
3060
  scrollProcessingEnabled,
2695
3061
  props: { onScroll: onScrollProp }
@@ -2700,17 +3066,34 @@ function onScroll(ctx, state, event) {
2700
3066
  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) {
2701
3067
  return;
2702
3068
  }
3069
+ let insetChanged = false;
3070
+ if ((_d = event.nativeEvent) == null ? void 0 : _d.contentInset) {
3071
+ const { contentInset } = event.nativeEvent;
3072
+ const prevInset = state.nativeContentInset;
3073
+ if (!prevInset || prevInset.top !== contentInset.top || prevInset.bottom !== contentInset.bottom || prevInset.left !== contentInset.left || prevInset.right !== contentInset.right) {
3074
+ state.nativeContentInset = contentInset;
3075
+ insetChanged = true;
3076
+ }
3077
+ }
2703
3078
  let newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
3079
+ if (state.scrollingTo) {
3080
+ const maxOffset = clampScrollOffset(ctx, newScroll);
3081
+ if (newScroll !== maxOffset && Math.abs(newScroll - maxOffset) > 1) {
3082
+ newScroll = maxOffset;
3083
+ scrollTo(ctx, {
3084
+ forceScroll: true,
3085
+ isInitialScroll: true,
3086
+ noScrollingTo: true,
3087
+ offset: newScroll
3088
+ });
3089
+ return;
3090
+ }
3091
+ }
2704
3092
  state.scrollPending = newScroll;
2705
- const maxOffset = Math.max(0, getContentSize(ctx) - state.scrollLength);
2706
- if (state.initialScroll && newScroll > maxOffset) {
2707
- newScroll = maxOffset;
2708
- scrollTo(ctx, state, {
2709
- noScrollingTo: true,
2710
- offset: newScroll
2711
- });
3093
+ updateScroll(ctx, newScroll, insetChanged);
3094
+ if (state.scrollingTo) {
3095
+ checkFinishedScroll(ctx);
2712
3096
  }
2713
- updateScroll(ctx, state, newScroll);
2714
3097
  onScrollProp == null ? void 0 : onScrollProp(event);
2715
3098
  }
2716
3099
 
@@ -2719,51 +3102,58 @@ var ScrollAdjustHandler = class {
2719
3102
  constructor(ctx) {
2720
3103
  this.appliedAdjust = 0;
2721
3104
  this.pendingAdjust = 0;
2722
- this.mounted = false;
2723
- this.context = ctx;
2724
- if (Platform2.OS === "web") {
2725
- const commitPendingAdjust = () => {
2726
- const state = this.context.internalState;
2727
- const pending = this.pendingAdjust;
2728
- if (pending !== 0) {
2729
- this.pendingAdjust = 0;
2730
- this.appliedAdjust += pending;
2731
- state.scroll += pending;
2732
- state.scrollForNextCalculateItemsInView = void 0;
2733
- set$(this.context, "scrollAdjustPending", 0);
2734
- set$(this.context, "scrollAdjust", this.appliedAdjust);
2735
- calculateItemsInView(this.context, this.context.internalState);
2736
- }
2737
- };
2738
- listen$(this.context, "scrollingTo", (value) => {
2739
- if (value === void 0) {
2740
- commitPendingAdjust();
2741
- }
2742
- });
2743
- }
3105
+ this.ctx = ctx;
2744
3106
  }
2745
3107
  requestAdjust(add) {
2746
- const scrollingTo = peek$(this.context, "scrollingTo");
2747
- if (Platform2.OS === "web" && (scrollingTo == null ? void 0 : scrollingTo.animated) && !scrollingTo.isInitialScroll) {
3108
+ const scrollingTo = this.ctx.state.scrollingTo;
3109
+ if (PlatformAdjustBreaksScroll && (scrollingTo == null ? void 0 : scrollingTo.animated) && !scrollingTo.isInitialScroll) {
2748
3110
  this.pendingAdjust += add;
2749
- set$(this.context, "scrollAdjustPending", this.pendingAdjust);
3111
+ set$(this.ctx, "scrollAdjustPending", this.pendingAdjust);
2750
3112
  } else {
2751
3113
  this.appliedAdjust += add;
2752
- set$(this.context, "scrollAdjust", this.appliedAdjust);
3114
+ set$(this.ctx, "scrollAdjust", this.appliedAdjust);
3115
+ }
3116
+ if (this.ctx.state.scrollingTo) {
3117
+ checkFinishedScroll(this.ctx);
2753
3118
  }
2754
- }
2755
- setMounted() {
2756
- this.mounted = true;
2757
3119
  }
2758
3120
  getAdjust() {
2759
3121
  return this.appliedAdjust;
2760
3122
  }
3123
+ commitPendingAdjust(scrollTarget) {
3124
+ if (PlatformAdjustBreaksScroll) {
3125
+ const state = this.ctx.state;
3126
+ const pending = this.pendingAdjust;
3127
+ this.pendingAdjust = 0;
3128
+ if (pending !== 0) {
3129
+ let targetScroll;
3130
+ if ((scrollTarget == null ? void 0 : scrollTarget.index) !== void 0) {
3131
+ const currentOffset = calculateOffsetForIndex(this.ctx, scrollTarget.index);
3132
+ targetScroll = calculateOffsetWithOffsetPosition(this.ctx, currentOffset, scrollTarget);
3133
+ targetScroll = clampScrollOffset(this.ctx, targetScroll);
3134
+ } else {
3135
+ targetScroll = clampScrollOffset(this.ctx, state.scroll + pending);
3136
+ }
3137
+ const adjustment = targetScroll - state.scroll;
3138
+ if (Math.abs(adjustment) > 0.1 || Math.abs(pending) > 0.1) {
3139
+ this.appliedAdjust += adjustment;
3140
+ state.scroll = targetScroll;
3141
+ state.scrollForNextCalculateItemsInView = void 0;
3142
+ set$(this.ctx, "scrollAdjust", this.appliedAdjust);
3143
+ }
3144
+ set$(this.ctx, "scrollAdjustPending", 0);
3145
+ calculateItemsInView(this.ctx);
3146
+ }
3147
+ }
3148
+ }
2761
3149
  };
2762
3150
 
2763
3151
  // src/core/updateItemSize.ts
2764
- function updateItemSize(ctx, state, itemKey, sizeObj) {
3152
+ function updateItemSize(ctx, itemKey, sizeObj) {
2765
3153
  var _a3;
3154
+ const state = ctx.state;
2766
3155
  const {
3156
+ didContainersLayout,
2767
3157
  sizesKnown,
2768
3158
  props: {
2769
3159
  getFixedItemSize,
@@ -2786,31 +3176,24 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
2786
3176
  return;
2787
3177
  }
2788
3178
  const type = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
2789
- const size2 = getFixedItemSize(index, itemData, type);
3179
+ const size2 = getFixedItemSize(itemData, index, type);
2790
3180
  if (size2 !== void 0 && size2 === sizesKnown.get(itemKey)) {
2791
3181
  return;
2792
3182
  }
2793
3183
  }
2794
- const containersDidLayout = peek$(ctx, "containersDidLayout");
2795
- let needsRecalculate = !containersDidLayout;
3184
+ let needsRecalculate = !didContainersLayout;
2796
3185
  let shouldMaintainScrollAtEnd = false;
2797
3186
  let minIndexSizeChanged;
2798
3187
  let maxOtherAxisSize = peek$(ctx, "otherAxisSize") || 0;
2799
3188
  const prevSizeKnown = state.sizesKnown.get(itemKey);
2800
- const diff = updateOneItemSize(ctx, state, itemKey, sizeObj);
3189
+ const diff = updateOneItemSize(ctx, itemKey, sizeObj);
2801
3190
  const size = roundSize(horizontal ? sizeObj.width : sizeObj.height);
2802
3191
  if (diff !== 0) {
2803
3192
  minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
2804
3193
  const { startBuffered, endBuffered } = state;
2805
3194
  needsRecalculate || (needsRecalculate = index >= startBuffered && index <= endBuffered);
2806
- if (!needsRecalculate) {
2807
- const numContainers = ctx.values.get("numContainers");
2808
- for (let i = 0; i < numContainers; i++) {
2809
- if (peek$(ctx, `containerItemKey${i}`) === itemKey) {
2810
- needsRecalculate = true;
2811
- break;
2812
- }
2813
- }
3195
+ if (!needsRecalculate && state.containerItemKeys.has(itemKey)) {
3196
+ needsRecalculate = true;
2814
3197
  }
2815
3198
  if (state.needsOtherAxisSize) {
2816
3199
  const otherAxisSize = horizontal ? sizeObj.height : sizeObj.width;
@@ -2846,22 +3229,22 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
2846
3229
  if (!cur || maxOtherAxisSize > cur) {
2847
3230
  set$(ctx, "otherAxisSize", maxOtherAxisSize);
2848
3231
  }
2849
- if (containersDidLayout || checkAllSizesKnown(state)) {
3232
+ if (didContainersLayout || checkAllSizesKnown(state)) {
2850
3233
  if (needsRecalculate) {
2851
3234
  state.scrollForNextCalculateItemsInView = void 0;
2852
- calculateItemsInView(ctx, state, { doMVCP: true });
3235
+ calculateItemsInView(ctx, { doMVCP: true });
2853
3236
  }
2854
3237
  if (shouldMaintainScrollAtEnd) {
2855
3238
  if (maintainScrollAtEnd === true || maintainScrollAtEnd.onItemLayout) {
2856
- doMaintainScrollAtEnd(ctx, state, false);
3239
+ doMaintainScrollAtEnd(ctx, false);
2857
3240
  }
2858
3241
  }
2859
3242
  }
2860
3243
  }
2861
- function updateOneItemSize(ctx, state, itemKey, sizeObj) {
3244
+ function updateOneItemSize(ctx, itemKey, sizeObj) {
2862
3245
  var _a3;
3246
+ const state = ctx.state;
2863
3247
  const {
2864
- sizes,
2865
3248
  indexByKey,
2866
3249
  sizesKnown,
2867
3250
  averageSizes,
@@ -2869,9 +3252,10 @@ function updateOneItemSize(ctx, state, itemKey, sizeObj) {
2869
3252
  } = state;
2870
3253
  if (!data) return 0;
2871
3254
  const index = indexByKey.get(itemKey);
2872
- const prevSize = getItemSize(ctx, state, itemKey, index, data[index]);
3255
+ const prevSize = getItemSize(ctx, itemKey, index, data[index]);
2873
3256
  const rawSize = horizontal ? sizeObj.width : sizeObj.height;
2874
3257
  const size = Platform2.OS === "web" ? Math.round(rawSize) : roundSize(rawSize);
3258
+ const prevSizeKnown = sizesKnown.get(itemKey);
2875
3259
  sizesKnown.set(itemKey, size);
2876
3260
  if (!getEstimatedItemSize && !getFixedItemSize && size > 0) {
2877
3261
  const itemType = getItemType ? (_a3 = getItemType(data[index], index)) != null ? _a3 : "" : "";
@@ -2879,15 +3263,28 @@ function updateOneItemSize(ctx, state, itemKey, sizeObj) {
2879
3263
  if (!averages) {
2880
3264
  averages = averageSizes[itemType] = { avg: 0, num: 0 };
2881
3265
  }
2882
- averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
2883
- averages.num++;
3266
+ if (averages.num === 0) {
3267
+ averages.avg = size;
3268
+ averages.num++;
3269
+ } else if (prevSizeKnown !== void 0 && prevSizeKnown > 0) {
3270
+ averages.avg += (size - prevSizeKnown) / averages.num;
3271
+ } else {
3272
+ averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
3273
+ averages.num++;
3274
+ }
2884
3275
  }
2885
3276
  if (!prevSize || Math.abs(prevSize - size) > 0.1) {
2886
- setSize(ctx, state, itemKey, size);
3277
+ setSize(ctx, itemKey, size);
2887
3278
  return size - prevSize;
2888
3279
  }
2889
3280
  return 0;
2890
3281
  }
3282
+ function useWrapIfItem(fn) {
3283
+ return React2.useMemo(
3284
+ () => fn ? (arg1, arg2, arg3) => arg1 !== void 0 && arg2 !== void 0 ? fn(arg1, arg2, arg3) : void 0 : void 0,
3285
+ [fn]
3286
+ );
3287
+ }
2891
3288
  var useCombinedRef = (...refs) => {
2892
3289
  const callback = React2.useCallback((element) => {
2893
3290
  for (const ref of refs) {
@@ -2949,14 +3346,15 @@ function createColumnWrapperStyle(contentContainerStyle) {
2949
3346
  }
2950
3347
 
2951
3348
  // src/utils/createImperativeHandle.ts
2952
- function createImperativeHandle(ctx, state) {
3349
+ function createImperativeHandle(ctx) {
3350
+ const state = ctx.state;
2953
3351
  const scrollIndexIntoView = (options) => {
2954
3352
  if (state) {
2955
3353
  const { index, ...rest } = options;
2956
3354
  const { startNoBuffer, endNoBuffer } = state;
2957
3355
  if (index < startNoBuffer || index > endNoBuffer) {
2958
3356
  const viewPosition = index < startNoBuffer ? 0 : 1;
2959
- scrollToIndex(ctx, state, {
3357
+ scrollToIndex(ctx, {
2960
3358
  ...rest,
2961
3359
  index,
2962
3360
  viewPosition
@@ -2971,8 +3369,8 @@ function createImperativeHandle(ctx, state) {
2971
3369
  getScrollableNode: () => refScroller.current.getScrollableNode(),
2972
3370
  getScrollResponder: () => refScroller.current.getScrollResponder(),
2973
3371
  getState: () => ({
2974
- activeStickyIndex: state.activeStickyIndex,
2975
- contentLength: state.totalSize,
3372
+ activeStickyIndex: peek$(ctx, "activeStickyIndex"),
3373
+ contentLength: getContentSize(ctx),
2976
3374
  data: state.props.data,
2977
3375
  elementAtIndex: (index) => {
2978
3376
  var _a3;
@@ -2982,15 +3380,22 @@ function createImperativeHandle(ctx, state) {
2982
3380
  endBuffered: state.endBuffered,
2983
3381
  isAtEnd: state.isAtEnd,
2984
3382
  isAtStart: state.isAtStart,
3383
+ listen: (signalName, cb) => listen$(ctx, signalName, cb),
3384
+ listenToPosition: (key, cb) => listenPosition$(ctx, key, cb),
2985
3385
  positionAtIndex: (index) => state.positions.get(getId(state, index)),
2986
3386
  positions: state.positions,
2987
3387
  scroll: state.scroll,
2988
3388
  scrollLength: state.scrollLength,
3389
+ scrollVelocity: getScrollVelocity(state),
2989
3390
  sizeAtIndex: (index) => state.sizesKnown.get(getId(state, index)),
2990
3391
  sizes: state.sizesKnown,
2991
3392
  start: state.startNoBuffer,
2992
3393
  startBuffered: state.startBuffered
2993
3394
  }),
3395
+ reportContentInset: (inset) => {
3396
+ state.contentInsetOverride = inset != null ? inset : void 0;
3397
+ updateScroll(ctx, state.scroll, true);
3398
+ },
2994
3399
  scrollIndexIntoView,
2995
3400
  scrollItemIntoView: ({ item, ...props }) => {
2996
3401
  const data = state.props.data;
@@ -3006,23 +3411,23 @@ function createImperativeHandle(ctx, state) {
3006
3411
  if (index !== -1) {
3007
3412
  const paddingBottom = stylePaddingBottom || 0;
3008
3413
  const footerSize = peek$(ctx, "footerSize") || 0;
3009
- scrollToIndex(ctx, state, {
3414
+ scrollToIndex(ctx, {
3415
+ ...options,
3010
3416
  index,
3011
3417
  viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
3012
- viewPosition: 1,
3013
- ...options
3418
+ viewPosition: 1
3014
3419
  });
3015
3420
  }
3016
3421
  },
3017
- scrollToIndex: (params) => scrollToIndex(ctx, state, params),
3422
+ scrollToIndex: (params) => scrollToIndex(ctx, params),
3018
3423
  scrollToItem: ({ item, ...props }) => {
3019
3424
  const data = state.props.data;
3020
3425
  const index = data.indexOf(item);
3021
3426
  if (index !== -1) {
3022
- scrollToIndex(ctx, state, { index, ...props });
3427
+ scrollToIndex(ctx, { index, ...props });
3023
3428
  }
3024
3429
  },
3025
- scrollToOffset: (params) => scrollTo(ctx, state, params),
3430
+ scrollToOffset: (params) => scrollTo(ctx, params),
3026
3431
  setScrollProcessingEnabled: (enabled) => {
3027
3432
  state.scrollProcessingEnabled = enabled;
3028
3433
  },
@@ -3032,8 +3437,57 @@ function createImperativeHandle(ctx, state) {
3032
3437
  }
3033
3438
  };
3034
3439
  }
3035
- function getRenderedItem(ctx, state, key) {
3440
+
3441
+ // src/utils/getAlwaysRenderIndices.ts
3442
+ var sortAsc = (a, b) => a - b;
3443
+ var toCount = (value) => typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
3444
+ var addIndex = (result, dataLength, index) => {
3445
+ if (index >= 0 && index < dataLength) {
3446
+ result.add(index);
3447
+ }
3448
+ };
3449
+ function getAlwaysRenderIndices(config, data, keyExtractor) {
3450
+ var _a3, _b;
3451
+ if (!config || data.length === 0) {
3452
+ return [];
3453
+ }
3454
+ const result = /* @__PURE__ */ new Set();
3455
+ const dataLength = data.length;
3456
+ const topCount = toCount(config.top);
3457
+ if (topCount > 0) {
3458
+ for (let i = 0; i < Math.min(topCount, dataLength); i++) {
3459
+ addIndex(result, dataLength, i);
3460
+ }
3461
+ }
3462
+ const bottomCount = toCount(config.bottom);
3463
+ if (bottomCount > 0) {
3464
+ for (let i = Math.max(0, dataLength - bottomCount); i < dataLength; i++) {
3465
+ addIndex(result, dataLength, i);
3466
+ }
3467
+ }
3468
+ if ((_a3 = config.indices) == null ? void 0 : _a3.length) {
3469
+ for (const index of config.indices) {
3470
+ if (!Number.isFinite(index)) continue;
3471
+ addIndex(result, dataLength, Math.floor(index));
3472
+ }
3473
+ }
3474
+ if ((_b = config.keys) == null ? void 0 : _b.length) {
3475
+ const keys = new Set(config.keys);
3476
+ for (let i = 0; i < dataLength && keys.size > 0; i++) {
3477
+ const key = keyExtractor(data[i], i);
3478
+ if (keys.has(key)) {
3479
+ addIndex(result, dataLength, i);
3480
+ keys.delete(key);
3481
+ }
3482
+ }
3483
+ }
3484
+ const indices = Array.from(result);
3485
+ indices.sort(sortAsc);
3486
+ return indices;
3487
+ }
3488
+ function getRenderedItem(ctx, key) {
3036
3489
  var _a3;
3490
+ const state = ctx.state;
3037
3491
  if (!state) {
3038
3492
  return null;
3039
3493
  }
@@ -3060,6 +3514,42 @@ function getRenderedItem(ctx, state, key) {
3060
3514
  }
3061
3515
  return { index, item: data[index], renderedItem };
3062
3516
  }
3517
+
3518
+ // src/utils/normalizeMaintainVisibleContentPosition.ts
3519
+ function normalizeMaintainVisibleContentPosition(value) {
3520
+ var _a3, _b;
3521
+ if (value === true) {
3522
+ return { data: true, size: true };
3523
+ }
3524
+ if (value && typeof value === "object") {
3525
+ return {
3526
+ data: (_a3 = value.data) != null ? _a3 : false,
3527
+ size: (_b = value.size) != null ? _b : true,
3528
+ shouldRestorePosition: value.shouldRestorePosition
3529
+ };
3530
+ }
3531
+ if (value === false) {
3532
+ return { data: false, size: false };
3533
+ }
3534
+ return { data: false, size: true };
3535
+ }
3536
+
3537
+ // src/utils/setPaddingTop.ts
3538
+ function setPaddingTop(ctx, { stylePaddingTop }) {
3539
+ const state = ctx.state;
3540
+ if (stylePaddingTop !== void 0) {
3541
+ const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
3542
+ if (stylePaddingTop < prevStylePaddingTop) {
3543
+ let prevTotalSize = peek$(ctx, "totalSize") || 0;
3544
+ set$(ctx, "totalSize", prevTotalSize + prevStylePaddingTop);
3545
+ state.timeoutSetPaddingTop = setTimeout(() => {
3546
+ prevTotalSize = peek$(ctx, "totalSize") || 0;
3547
+ set$(ctx, "totalSize", prevTotalSize - prevStylePaddingTop);
3548
+ }, 16);
3549
+ }
3550
+ set$(ctx, "stylePaddingTop", stylePaddingTop);
3551
+ }
3552
+ }
3063
3553
  function useThrottleDebounce(mode) {
3064
3554
  const timeoutRef = React2.useRef(null);
3065
3555
  const lastCallTimeRef = React2.useRef(0);
@@ -3110,6 +3600,7 @@ function useThrottledOnScroll(originalHandler, scrollEventThrottle) {
3110
3600
  var DEFAULT_DRAW_DISTANCE = 250;
3111
3601
  var DEFAULT_ITEM_SIZE = 100;
3112
3602
  var LegendList = typedMemo(
3603
+ // biome-ignore lint/nursery/noShadow: const function name shadowing is intentional
3113
3604
  typedForwardRef(function LegendList2(props, forwardedRef) {
3114
3605
  const { children, data: dataProp, renderItem: renderItemProp, ...restProps } = props;
3115
3606
  const isChildrenMode = children !== void 0 && dataProp === void 0;
@@ -3127,15 +3618,16 @@ var LegendList = typedMemo(
3127
3618
  })
3128
3619
  );
3129
3620
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
3130
- var _a3, _b;
3621
+ var _a3, _b, _c, _d;
3131
3622
  const {
3132
3623
  alignItemsAtEnd = false,
3624
+ alwaysRender,
3133
3625
  columnWrapperStyle,
3134
3626
  contentContainerStyle: contentContainerStyleProp,
3627
+ contentInset,
3135
3628
  data: dataProp = [],
3136
3629
  dataVersion,
3137
3630
  drawDistance = 250,
3138
- enableAverages = true,
3139
3631
  estimatedItemSize: estimatedItemSizeProp,
3140
3632
  estimatedListSize,
3141
3633
  extraData,
@@ -3153,11 +3645,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3153
3645
  ListHeaderComponent,
3154
3646
  maintainScrollAtEnd = false,
3155
3647
  maintainScrollAtEndThreshold = 0.1,
3156
- maintainVisibleContentPosition = false,
3648
+ maintainVisibleContentPosition: maintainVisibleContentPositionProp,
3157
3649
  numColumns: numColumnsProp = 1,
3650
+ overrideItemLayout,
3158
3651
  onEndReached,
3159
3652
  onEndReachedThreshold = 0.5,
3160
3653
  onItemSizeChanged,
3654
+ onMetricsChange,
3161
3655
  onLayout: onLayoutProp,
3162
3656
  onLoad,
3163
3657
  onMomentumScrollEnd,
@@ -3177,6 +3671,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3177
3671
  snapToIndices,
3178
3672
  stickyHeaderIndices: stickyHeaderIndicesProp,
3179
3673
  stickyIndices: stickyIndicesDeprecated,
3674
+ // TODOV3: Remove from v3 release
3180
3675
  style: styleProp,
3181
3676
  suggestEstimatedItemSize,
3182
3677
  viewabilityConfig,
@@ -3184,13 +3679,34 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3184
3679
  waitForInitialLayout = true,
3185
3680
  ...rest
3186
3681
  } = props;
3682
+ const animatedPropsInternal = props.animatedPropsInternal;
3187
3683
  const { childrenMode } = rest;
3188
- const contentContainerStyle = { ...StyleSheet.flatten(contentContainerStyleProp) };
3684
+ const contentContainerStyleBase = StyleSheet.flatten(contentContainerStyleProp);
3685
+ const shouldFlexGrow = alignItemsAtEnd && (horizontal ? (contentContainerStyleBase == null ? void 0 : contentContainerStyleBase.minWidth) == null : (contentContainerStyleBase == null ? void 0 : contentContainerStyleBase.minHeight) == null);
3686
+ const contentContainerStyle = {
3687
+ ...contentContainerStyleBase,
3688
+ ...alignItemsAtEnd ? {
3689
+ display: "flex",
3690
+ flexDirection: horizontal ? "row" : "column",
3691
+ ...shouldFlexGrow ? { flexGrow: 1 } : {},
3692
+ justifyContent: "flex-end"
3693
+ } : {}
3694
+ };
3189
3695
  const style = { ...StyleSheet.flatten(styleProp) };
3190
3696
  const stylePaddingTopState = extractPadding(style, contentContainerStyle, "Top");
3191
3697
  const stylePaddingBottomState = extractPadding(style, contentContainerStyle, "Bottom");
3698
+ const maintainVisibleContentPositionConfig = normalizeMaintainVisibleContentPosition(
3699
+ maintainVisibleContentPositionProp
3700
+ );
3192
3701
  const [renderNum, setRenderNum] = React2.useState(0);
3193
- 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;
3702
+ const initialScrollProp = initialScrollAtEnd ? { index: Math.max(0, dataProp.length - 1), viewOffset: -stylePaddingBottomState, viewPosition: 1 } : initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? {
3703
+ index: initialScrollIndexProp.index || 0,
3704
+ viewOffset: initialScrollIndexProp.viewOffset || (initialScrollIndexProp.viewPosition === 1 ? -stylePaddingBottomState : 0),
3705
+ viewPosition: initialScrollIndexProp.viewPosition || 0
3706
+ } : {
3707
+ index: initialScrollIndexProp || 0,
3708
+ viewOffset: initialScrollOffsetProp || 0
3709
+ } : void 0;
3194
3710
  const [canRender, setCanRender] = React2__namespace.useState(!IsNewArchitecture);
3195
3711
  const ctx = useStateContext();
3196
3712
  ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
@@ -3200,6 +3716,18 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3200
3716
  const scrollBuffer = (drawDistance != null ? drawDistance : DEFAULT_DRAW_DISTANCE) || 1;
3201
3717
  const keyExtractor = keyExtractorProp != null ? keyExtractorProp : (_item, index) => index.toString();
3202
3718
  const stickyHeaderIndices = stickyHeaderIndicesProp != null ? stickyHeaderIndicesProp : stickyIndicesDeprecated;
3719
+ const alwaysRenderIndices = React2.useMemo(() => {
3720
+ const indices = getAlwaysRenderIndices(alwaysRender, dataProp, keyExtractor);
3721
+ return { arr: indices, set: new Set(indices) };
3722
+ }, [
3723
+ alwaysRender == null ? void 0 : alwaysRender.top,
3724
+ alwaysRender == null ? void 0 : alwaysRender.bottom,
3725
+ (_a3 = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _a3.join(","),
3726
+ (_b = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _b.join(","),
3727
+ dataProp,
3728
+ dataVersion,
3729
+ keyExtractor
3730
+ ]);
3203
3731
  if (IS_DEV && stickyIndicesDeprecated && !stickyHeaderIndicesProp) {
3204
3732
  warnDevOnce(
3205
3733
  "stickyIndices",
@@ -3207,15 +3735,19 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3207
3735
  );
3208
3736
  }
3209
3737
  const refState = React2.useRef();
3738
+ const hasOverrideItemLayout = !!overrideItemLayout;
3739
+ const prevHasOverrideItemLayout = React2.useRef(hasOverrideItemLayout);
3210
3740
  if (!refState.current) {
3211
- if (!ctx.internalState) {
3741
+ if (!ctx.state) {
3212
3742
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : IsNewArchitecture ? { height: 0, width: 0 } : getWindowSize())[horizontal ? "width" : "height"];
3213
- ctx.internalState = {
3214
- activeStickyIndex: void 0,
3743
+ ctx.state = {
3744
+ activeStickyIndex: -1,
3215
3745
  averageSizes: {},
3216
3746
  columns: /* @__PURE__ */ new Map(),
3217
- containerItemKeys: /* @__PURE__ */ new Set(),
3747
+ columnSpans: /* @__PURE__ */ new Map(),
3748
+ containerItemKeys: /* @__PURE__ */ new Map(),
3218
3749
  containerItemTypes: /* @__PURE__ */ new Map(),
3750
+ contentInsetOverride: void 0,
3219
3751
  dataChangeNeedsScrollUpdate: false,
3220
3752
  didColumnsChange: false,
3221
3753
  didDataChange: false,
@@ -3231,19 +3763,21 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3231
3763
  attempts: 0,
3232
3764
  index: initialScrollProp.index,
3233
3765
  settledTicks: 0,
3234
- viewOffset: (_a3 = initialScrollProp.viewOffset) != null ? _a3 : 0,
3766
+ viewOffset: (_c = initialScrollProp.viewOffset) != null ? _c : 0,
3235
3767
  viewPosition: initialScrollProp.viewPosition
3236
3768
  } : void 0,
3237
3769
  initialScroll: initialScrollProp,
3238
3770
  isAtEnd: false,
3239
3771
  isAtStart: false,
3240
- isEndReached: false,
3772
+ isEndReached: null,
3241
3773
  isFirst: true,
3242
- isStartReached: false,
3774
+ isStartReached: null,
3243
3775
  lastBatchingAction: Date.now(),
3244
3776
  lastLayout: void 0,
3777
+ lastScrollDelta: 0,
3245
3778
  loadStartTime: Date.now(),
3246
3779
  minIndexSizeChanged: 0,
3780
+ nativeContentInset: void 0,
3247
3781
  nativeMarginTop: 0,
3248
3782
  positions: /* @__PURE__ */ new Map(),
3249
3783
  props: {},
@@ -3271,12 +3805,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3271
3805
  totalSize: 0,
3272
3806
  viewabilityConfigCallbackPairs: void 0
3273
3807
  };
3274
- const internalState = ctx.internalState;
3275
- internalState.triggerCalculateItemsInView = (params) => calculateItemsInView(ctx, internalState, params);
3276
- set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPosition);
3808
+ const internalState = ctx.state;
3809
+ internalState.triggerCalculateItemsInView = (params) => calculateItemsInView(ctx, params);
3810
+ set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPositionConfig);
3277
3811
  set$(ctx, "extraData", extraData);
3278
3812
  }
3279
- refState.current = ctx.internalState;
3813
+ refState.current = ctx.state;
3280
3814
  }
3281
3815
  const state = refState.current;
3282
3816
  const isFirstLocal = state.isFirst;
@@ -3290,20 +3824,24 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3290
3824
  const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
3291
3825
  state.props = {
3292
3826
  alignItemsAtEnd,
3827
+ alwaysRender,
3828
+ alwaysRenderIndicesArr: alwaysRenderIndices.arr,
3829
+ alwaysRenderIndicesSet: alwaysRenderIndices.set,
3830
+ animatedProps: animatedPropsInternal,
3831
+ contentInset,
3293
3832
  data: dataProp,
3294
3833
  dataVersion,
3295
- enableAverages,
3296
3834
  estimatedItemSize,
3297
- getEstimatedItemSize,
3298
- getFixedItemSize,
3299
- getItemType,
3835
+ getEstimatedItemSize: useWrapIfItem(getEstimatedItemSize),
3836
+ getFixedItemSize: useWrapIfItem(getFixedItemSize),
3837
+ getItemType: useWrapIfItem(getItemType),
3300
3838
  horizontal: !!horizontal,
3301
3839
  initialContainerPoolRatio,
3302
3840
  itemsAreEqual,
3303
- keyExtractor,
3841
+ keyExtractor: useWrapIfItem(keyExtractor),
3304
3842
  maintainScrollAtEnd,
3305
3843
  maintainScrollAtEndThreshold,
3306
- maintainVisibleContentPosition,
3844
+ maintainVisibleContentPosition: maintainVisibleContentPositionConfig,
3307
3845
  numColumns: numColumnsProp,
3308
3846
  onEndReached,
3309
3847
  onEndReachedThreshold,
@@ -3313,6 +3851,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3313
3851
  onStartReached,
3314
3852
  onStartReachedThreshold,
3315
3853
  onStickyHeaderChange,
3854
+ overrideItemLayout,
3316
3855
  recycleItems: !!recycleItems,
3317
3856
  renderItem,
3318
3857
  scrollBuffer,
@@ -3331,61 +3870,61 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3331
3870
  (_, i) => getId(state, dataProp.length - 1 - i)
3332
3871
  );
3333
3872
  }, [dataProp, dataVersion, numColumnsProp]);
3334
- const initializeStateVars = () => {
3873
+ const initializeStateVars = (shouldAdjustPadding) => {
3335
3874
  set$(ctx, "lastItemKeys", memoizedLastItemKeys);
3336
3875
  set$(ctx, "numColumns", numColumnsProp);
3337
3876
  const prevPaddingTop = peek$(ctx, "stylePaddingTop");
3338
- setPaddingTop(ctx, state, { stylePaddingTop: stylePaddingTopState });
3877
+ setPaddingTop(ctx, { stylePaddingTop: stylePaddingTopState });
3339
3878
  refState.current.props.stylePaddingBottom = stylePaddingBottomState;
3340
3879
  let paddingDiff = stylePaddingTopState - prevPaddingTop;
3341
- if (paddingDiff && prevPaddingTop !== void 0 && Platform2.OS === "ios") {
3880
+ if (shouldAdjustPadding && maintainVisibleContentPositionConfig.size && paddingDiff && prevPaddingTop !== void 0 && Platform2.OS === "ios") {
3342
3881
  if (state.scroll < 0) {
3343
3882
  paddingDiff += state.scroll;
3344
3883
  }
3345
- requestAdjust(ctx, state, paddingDiff);
3884
+ requestAdjust(ctx, paddingDiff);
3346
3885
  }
3347
3886
  };
3348
3887
  if (isFirstLocal) {
3349
- initializeStateVars();
3888
+ initializeStateVars(false);
3350
3889
  updateItemPositions(
3351
3890
  ctx,
3352
- state,
3353
3891
  /*dataChanged*/
3354
3892
  true
3355
3893
  );
3356
3894
  }
3357
3895
  const initialContentOffset = React2.useMemo(() => {
3358
- var _a4, _b2;
3359
- const { initialScroll } = refState.current;
3360
- if (!initialScroll) {
3896
+ var _a4;
3897
+ let value;
3898
+ const { initialScroll, initialAnchor } = refState.current;
3899
+ if (initialScroll) {
3900
+ if (!IsNewArchitecture && initialScroll.index !== void 0 && (!initialAnchor || (initialAnchor == null ? void 0 : initialAnchor.index) !== initialScroll.index)) {
3901
+ refState.current.initialAnchor = {
3902
+ attempts: 0,
3903
+ index: initialScroll.index,
3904
+ settledTicks: 0,
3905
+ viewOffset: (_a4 = initialScroll.viewOffset) != null ? _a4 : 0,
3906
+ viewPosition: initialScroll.viewPosition
3907
+ };
3908
+ }
3909
+ if (initialScroll.contentOffset !== void 0) {
3910
+ value = initialScroll.contentOffset;
3911
+ } else {
3912
+ const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, initialScroll.index) : 0;
3913
+ const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, initialScroll);
3914
+ const clampedOffset = clampScrollOffset(ctx, resolvedOffset);
3915
+ const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
3916
+ refState.current.initialScroll = updatedInitialScroll;
3917
+ state.initialScroll = updatedInitialScroll;
3918
+ value = clampedOffset;
3919
+ }
3920
+ } else {
3361
3921
  refState.current.initialAnchor = void 0;
3362
- return 0;
3363
- }
3364
- if (initialScroll.index !== void 0 && (!refState.current.initialAnchor || ((_a4 = refState.current.initialAnchor) == null ? void 0 : _a4.index) !== initialScroll.index)) {
3365
- refState.current.initialAnchor = {
3366
- attempts: 0,
3367
- index: initialScroll.index,
3368
- settledTicks: 0,
3369
- viewOffset: (_b2 = initialScroll.viewOffset) != null ? _b2 : 0,
3370
- viewPosition: initialScroll.viewPosition
3371
- };
3922
+ value = 0;
3923
+ }
3924
+ if (!value) {
3925
+ setInitialRenderState(ctx, { didInitialScroll: true });
3372
3926
  }
3373
- if (initialScroll.contentOffset !== void 0) {
3374
- return initialScroll.contentOffset;
3375
- }
3376
- const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, state, initialScroll.index) : 0;
3377
- const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, state, baseOffset, initialScroll);
3378
- let clampedOffset = resolvedOffset;
3379
- if (Number.isFinite(state.scrollLength) && Number.isFinite(state.totalSize)) {
3380
- const maxOffset = Math.max(0, state.totalSize - state.scrollLength);
3381
- clampedOffset = Math.min(clampedOffset, maxOffset);
3382
- }
3383
- clampedOffset = Math.max(0, clampedOffset);
3384
- const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
3385
- refState.current.initialScroll = updatedInitialScroll;
3386
- state.initialScroll = updatedInitialScroll;
3387
- refState.current.isStartReached = clampedOffset < refState.current.scrollLength * onStartReachedThreshold;
3388
- return clampedOffset;
3927
+ return value;
3389
3928
  }, [renderNum]);
3390
3929
  if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
3391
3930
  refState.current.lastBatchingAction = Date.now();
@@ -3415,12 +3954,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3415
3954
  }
3416
3955
  }, []);
3417
3956
  const doInitialScroll = React2.useCallback(() => {
3418
- var _a4;
3419
- const initialScroll = state.initialScroll;
3420
- if (initialScroll) {
3421
- scrollTo(ctx, state, {
3957
+ const { initialScroll, didFinishInitialScroll, queuedInitialLayout, scrollingTo } = state;
3958
+ if (initialScroll && !queuedInitialLayout && !didFinishInitialScroll && !scrollingTo) {
3959
+ scrollTo(ctx, {
3422
3960
  animated: false,
3423
- index: (_a4 = state.initialScroll) == null ? void 0 : _a4.index,
3961
+ index: initialScroll == null ? void 0 : initialScroll.index,
3424
3962
  isInitialScroll: true,
3425
3963
  offset: initialContentOffset,
3426
3964
  precomputedWithViewOffset: true
@@ -3429,7 +3967,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3429
3967
  }, [initialContentOffset]);
3430
3968
  const onLayoutChange = React2.useCallback((layout) => {
3431
3969
  doInitialScroll();
3432
- handleLayout(ctx, state, layout, setCanRender);
3970
+ handleLayout(ctx, layout, setCanRender);
3433
3971
  }, []);
3434
3972
  const { onLayout } = useOnLayoutSync({
3435
3973
  onLayoutChange,
@@ -3439,7 +3977,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3439
3977
  });
3440
3978
  React2.useLayoutEffect(() => {
3441
3979
  if (snapToIndices) {
3442
- updateSnapToOffsets(ctx, state);
3980
+ updateSnapToOffsets(ctx);
3443
3981
  }
3444
3982
  }, [snapToIndices]);
3445
3983
  React2.useLayoutEffect(() => {
@@ -3449,24 +3987,50 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3449
3987
  isFirst,
3450
3988
  props: { data }
3451
3989
  } = state;
3452
- const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx, state);
3990
+ const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx);
3453
3991
  if (!didAllocateContainers && !isFirst && (didDataChange || didColumnsChange)) {
3454
- checkResetContainers(ctx, state, data);
3992
+ checkResetContainers(ctx, data);
3455
3993
  }
3456
3994
  state.didColumnsChange = false;
3457
3995
  state.didDataChange = false;
3458
3996
  state.isFirst = false;
3459
3997
  }, [dataProp, dataVersion, numColumnsProp]);
3460
3998
  React2.useLayoutEffect(() => {
3999
+ var _a4;
3461
4000
  set$(ctx, "extraData", extraData);
3462
- }, [extraData]);
3463
- React2.useLayoutEffect(initializeStateVars, [
3464
- dataVersion,
3465
- memoizedLastItemKeys.join(","),
3466
- numColumnsProp,
3467
- stylePaddingBottomState,
3468
- stylePaddingTopState
3469
- ]);
4001
+ const didToggleOverride = prevHasOverrideItemLayout.current !== hasOverrideItemLayout;
4002
+ prevHasOverrideItemLayout.current = hasOverrideItemLayout;
4003
+ if ((hasOverrideItemLayout || didToggleOverride) && numColumnsProp > 1) {
4004
+ (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { forceFullItemPositions: true });
4005
+ }
4006
+ }, [extraData, hasOverrideItemLayout, numColumnsProp]);
4007
+ React2.useLayoutEffect(
4008
+ () => initializeStateVars(true),
4009
+ [dataVersion, memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingBottomState, stylePaddingTopState]
4010
+ );
4011
+ React2.useEffect(() => {
4012
+ if (!onMetricsChange) {
4013
+ return;
4014
+ }
4015
+ let lastMetrics;
4016
+ const emitMetrics = () => {
4017
+ const metrics = {
4018
+ footerSize: peek$(ctx, "footerSize") || 0,
4019
+ headerSize: peek$(ctx, "headerSize") || 0
4020
+ };
4021
+ if (!lastMetrics || metrics.headerSize !== lastMetrics.headerSize || metrics.footerSize !== lastMetrics.footerSize) {
4022
+ lastMetrics = metrics;
4023
+ onMetricsChange(metrics);
4024
+ }
4025
+ };
4026
+ emitMetrics();
4027
+ const unsubscribe = [listen$(ctx, "headerSize", emitMetrics), listen$(ctx, "footerSize", emitMetrics)];
4028
+ return () => {
4029
+ for (const unsub of unsubscribe) {
4030
+ unsub();
4031
+ }
4032
+ };
4033
+ }, [ctx, onMetricsChange]);
3470
4034
  React2.useEffect(() => {
3471
4035
  const viewability = setupViewability({
3472
4036
  onViewableItemsChanged,
@@ -3478,18 +4042,24 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3478
4042
  }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
3479
4043
  if (!IsNewArchitecture) {
3480
4044
  useInit(() => {
3481
- doInitialAllocateContainers(ctx, state);
4045
+ doInitialAllocateContainers(ctx);
3482
4046
  });
3483
4047
  }
3484
- React2.useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx, state), []);
4048
+ React2.useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx), []);
3485
4049
  if (Platform2.OS === "web") {
3486
4050
  React2.useEffect(doInitialScroll, []);
3487
4051
  }
3488
4052
  const fns = React2.useMemo(
3489
4053
  () => ({
3490
- getRenderedItem: (key) => getRenderedItem(ctx, state, key),
3491
- onScroll: (event) => onScroll(ctx, state, event),
3492
- updateItemSize: (itemKey, sizeObj) => updateItemSize(ctx, state, itemKey, sizeObj)
4054
+ getRenderedItem: (key) => getRenderedItem(ctx, key),
4055
+ onMomentumScrollEnd: (event) => {
4056
+ checkFinishedScrollFallback(ctx);
4057
+ if (onMomentumScrollEnd) {
4058
+ onMomentumScrollEnd(event);
4059
+ }
4060
+ },
4061
+ onScroll: (event) => onScroll(ctx, event),
4062
+ updateItemSize: (itemKey, sizeObj) => updateItemSize(ctx, itemKey, sizeObj)
3493
4063
  }),
3494
4064
  []
3495
4065
  );
@@ -3501,28 +4071,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3501
4071
  alignItemsAtEnd,
3502
4072
  canRender,
3503
4073
  contentContainerStyle,
4074
+ contentInset,
3504
4075
  getRenderedItem: fns.getRenderedItem,
3505
4076
  horizontal,
3506
4077
  initialContentOffset,
3507
4078
  ListEmptyComponent: dataProp.length === 0 ? ListEmptyComponent : void 0,
3508
4079
  ListHeaderComponent,
3509
- maintainVisibleContentPosition,
3510
4080
  onLayout,
3511
4081
  onLayoutHeader,
3512
- onMomentumScrollEnd: (event) => {
3513
- if (IsNewArchitecture) {
3514
- requestAnimationFrame(() => {
3515
- finishScrollTo(ctx, refState.current);
3516
- });
3517
- } else {
3518
- setTimeout(() => {
3519
- finishScrollTo(ctx, refState.current);
3520
- }, 1e3);
3521
- }
3522
- if (onMomentumScrollEnd) {
3523
- onMomentumScrollEnd(event);
3524
- }
3525
- },
4082
+ onMomentumScrollEnd: fns.onMomentumScrollEnd,
3526
4083
  onScroll: onScrollHandler,
3527
4084
  recycleItems,
3528
4085
  refreshControl: refreshControl ? stylePaddingTopState > 0 ? React2__namespace.cloneElement(refreshControl, {
@@ -3536,8 +4093,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3536
4093
  }
3537
4094
  ),
3538
4095
  refScrollView: combinedRef,
3539
- scrollAdjustHandler: (_b = refState.current) == null ? void 0 : _b.scrollAdjustHandler,
3540
- scrollEventThrottle: Platform2.OS === "web" ? 16 : void 0,
4096
+ scrollAdjustHandler: (_d = refState.current) == null ? void 0 : _d.scrollAdjustHandler,
4097
+ scrollEventThrottle: 0,
3541
4098
  snapToIndices,
3542
4099
  stickyHeaderIndices,
3543
4100
  style,
@@ -3548,6 +4105,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3548
4105
  });
3549
4106
 
3550
4107
  exports.LegendList = LegendList;
4108
+ exports.typedForwardRef = typedForwardRef;
4109
+ exports.typedMemo = typedMemo;
3551
4110
  exports.useIsLastItem = useIsLastItem;
3552
4111
  exports.useListScrollSize = useListScrollSize;
3553
4112
  exports.useRecyclingEffect = useRecyclingEffect;