@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.js CHANGED
@@ -33,31 +33,63 @@ var View = React3.forwardRef(function View2(props, ref) {
33
33
  });
34
34
  var Text = View;
35
35
 
36
+ // src/state/getContentInsetEnd.ts
37
+ function getContentInsetEnd(state) {
38
+ var _a3;
39
+ const { props } = state;
40
+ const horizontal = props.horizontal;
41
+ const contentInset = props.contentInset;
42
+ const baseInset = contentInset != null ? contentInset : state.nativeContentInset;
43
+ const overrideInset = (_a3 = state.contentInsetOverride) != null ? _a3 : void 0;
44
+ if (overrideInset) {
45
+ const mergedInset = { bottom: 0, left: 0, right: 0, top: 0, ...baseInset, ...overrideInset };
46
+ return (horizontal ? mergedInset.right : mergedInset.bottom) || 0;
47
+ }
48
+ if (baseInset) {
49
+ return (horizontal ? baseInset.right : baseInset.bottom) || 0;
50
+ }
51
+ return 0;
52
+ }
53
+
54
+ // src/state/getContentSize.ts
55
+ function getContentSize(ctx) {
56
+ var _a3;
57
+ const { values, state } = ctx;
58
+ const stylePaddingTop = values.get("stylePaddingTop") || 0;
59
+ const stylePaddingBottom = state.props.stylePaddingBottom || 0;
60
+ const headerSize = values.get("headerSize") || 0;
61
+ const footerSize = values.get("footerSize") || 0;
62
+ const contentInsetBottom = getContentInsetEnd(state);
63
+ const totalSize = (_a3 = state.pendingTotalSize) != null ? _a3 : values.get("totalSize");
64
+ return headerSize + footerSize + totalSize + stylePaddingTop + stylePaddingBottom + (contentInsetBottom || 0);
65
+ }
66
+
36
67
  // src/platform/Animated.tsx
37
68
  var createAnimatedValue = (value) => value;
38
69
 
39
70
  // src/state/state.tsx
40
71
  var ContextState = React3__namespace.createContext(null);
72
+ var contextNum = 0;
41
73
  function StateProvider({ children }) {
42
74
  const [value] = React3__namespace.useState(() => ({
43
75
  animatedScrollY: createAnimatedValue(0),
44
76
  columnWrapperStyle: void 0,
45
- internalState: void 0,
77
+ contextNum: contextNum++,
46
78
  listeners: /* @__PURE__ */ new Map(),
47
79
  mapViewabilityAmountCallbacks: /* @__PURE__ */ new Map(),
48
80
  mapViewabilityAmountValues: /* @__PURE__ */ new Map(),
49
81
  mapViewabilityCallbacks: /* @__PURE__ */ new Map(),
50
82
  mapViewabilityConfigStates: /* @__PURE__ */ new Map(),
51
83
  mapViewabilityValues: /* @__PURE__ */ new Map(),
84
+ positionListeners: /* @__PURE__ */ new Map(),
85
+ state: void 0,
52
86
  values: /* @__PURE__ */ new Map([
53
- ["alignItemsPaddingTop", 0],
54
87
  ["stylePaddingTop", 0],
55
88
  ["headerSize", 0],
56
89
  ["numContainers", 0],
57
- ["activeStickyIndex", void 0],
90
+ ["activeStickyIndex", -1],
58
91
  ["totalSize", 0],
59
- ["scrollAdjustPending", 0],
60
- ["scrollingTo", void 0]
92
+ ["scrollAdjustPending", 0]
61
93
  ]),
62
94
  viewRefs: /* @__PURE__ */ new Map()
63
95
  }));
@@ -125,15 +157,24 @@ function set$(ctx, signalName, value) {
125
157
  }
126
158
  }
127
159
  }
128
- function getContentSize(ctx) {
129
- var _a3, _b;
130
- const { values, internalState } = ctx;
131
- const stylePaddingTop = values.get("stylePaddingTop") || 0;
132
- const stylePaddingBottom = (internalState == null ? void 0 : internalState.props.stylePaddingBottom) || 0;
133
- const headerSize = values.get("headerSize") || 0;
134
- const footerSize = values.get("footerSize") || 0;
135
- const totalSize = (_b = (_a3 = ctx.internalState) == null ? void 0 : _a3.pendingTotalSize) != null ? _b : values.get("totalSize");
136
- return headerSize + footerSize + totalSize + stylePaddingTop + stylePaddingBottom;
160
+ function listenPosition$(ctx, key, cb) {
161
+ const { positionListeners } = ctx;
162
+ let setListeners = positionListeners.get(key);
163
+ if (!setListeners) {
164
+ setListeners = /* @__PURE__ */ new Set();
165
+ positionListeners.set(key, setListeners);
166
+ }
167
+ setListeners.add(cb);
168
+ return () => setListeners.delete(cb);
169
+ }
170
+ function notifyPosition$(ctx, key, value) {
171
+ const { positionListeners } = ctx;
172
+ const setListeners = positionListeners.get(key);
173
+ if (setListeners) {
174
+ for (const listener of setListeners) {
175
+ listener(value);
176
+ }
177
+ }
137
178
  }
138
179
  function useArr$(signalNames) {
139
180
  const ctx = React3__namespace.useContext(ContextState);
@@ -244,6 +285,11 @@ function extractPadding(style, contentContainerStyle, type) {
244
285
  return getPadding(style, type) + getPadding(contentContainerStyle, type);
245
286
  }
246
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
+ }
247
293
  const numContainers = peek$(ctx, "numContainers");
248
294
  for (let i = 0; i < numContainers; i++) {
249
295
  const itemKey = peek$(ctx, `containerItemKey${i}`);
@@ -255,12 +301,12 @@ function findContainerId(ctx, key) {
255
301
  }
256
302
 
257
303
  // src/components/PositionView.tsx
258
- var PositionViewState = typedMemo(function PositionView({
304
+ var PositionViewState = typedMemo(function PositionViewState2({
259
305
  id,
260
306
  horizontal,
261
307
  style,
262
308
  refView,
263
- ...rest
309
+ ...props
264
310
  }) {
265
311
  const [position = POSITION_OUT_OF_VIEW] = useArr$([`containerPosition${id}`]);
266
312
  const base = {
@@ -268,7 +314,8 @@ var PositionViewState = typedMemo(function PositionView({
268
314
  };
269
315
  const composed = isArray(style) ? Object.assign({}, ...style) : style;
270
316
  const combinedStyle = horizontal ? { ...base, ...composed, left: position } : { ...base, ...composed, top: position };
271
- return /* @__PURE__ */ React3__namespace.createElement("div", { ref: refView, style: combinedStyle, ...rest });
317
+ const { animatedScrollY, onLayout, index, ...webProps } = props;
318
+ return /* @__PURE__ */ React3__namespace.createElement("div", { ref: refView, ...webProps, style: combinedStyle });
272
319
  });
273
320
  var PositionViewSticky = typedMemo(function PositionViewSticky2({
274
321
  id,
@@ -276,16 +323,12 @@ var PositionViewSticky = typedMemo(function PositionViewSticky2({
276
323
  style,
277
324
  refView,
278
325
  index,
279
- stickyOffset,
280
326
  animatedScrollY: _animatedScrollY,
327
+ stickyHeaderConfig,
281
328
  children,
282
329
  ...rest
283
330
  }) {
284
- const [position = POSITION_OUT_OF_VIEW, headerSize = 0, activeStickyIndex] = useArr$([
285
- `containerPosition${id}`,
286
- "headerSize",
287
- "activeStickyIndex"
288
- ]);
331
+ const [position = POSITION_OUT_OF_VIEW, activeStickyIndex] = useArr$([`containerPosition${id}`, "activeStickyIndex"]);
289
332
  const base = {
290
333
  contain: "paint layout style"
291
334
  };
@@ -300,7 +343,8 @@ var PositionViewSticky = typedMemo(function PositionViewSticky2({
300
343
  var _a3;
301
344
  const styleBase = { ...base, ...composed };
302
345
  delete styleBase.transform;
303
- const offset = (_a3 = stickyOffset != null ? stickyOffset : headerSize) != null ? _a3 : 0;
346
+ const stickyConfigOffset = (_a3 = stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset) != null ? _a3 : 0;
347
+ const offset = stickyConfigOffset != null ? stickyConfigOffset : 0;
304
348
  const isActive = activeStickyIndex === index;
305
349
  styleBase.position = isActive ? "sticky" : "absolute";
306
350
  styleBase.zIndex = index + 1e3;
@@ -310,66 +354,83 @@ var PositionViewSticky = typedMemo(function PositionViewSticky2({
310
354
  styleBase.top = isActive ? offset : position;
311
355
  }
312
356
  return styleBase;
313
- }, [composed, horizontal, position, index, stickyOffset, headerSize, activeStickyIndex]);
357
+ }, [composed, horizontal, position, index, activeStickyIndex, stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset]);
314
358
  return /* @__PURE__ */ React3__namespace.createElement("div", { ref: refView, style: viewStyle, ...rest }, children);
315
359
  });
316
- var PositionView2 = PositionViewState;
360
+ var PositionView = PositionViewState;
317
361
 
318
362
  // src/constants-platform.ts
319
363
  var IsNewArchitecture = true;
320
- var symbolFirst = Symbol();
321
364
  function useInit(cb) {
322
- const refValue = React3.useRef(symbolFirst);
323
- if (refValue.current === symbolFirst) {
324
- refValue.current = cb();
325
- }
326
- return refValue.current;
365
+ React3.useState(() => cb());
327
366
  }
328
367
 
329
368
  // src/state/ContextContainer.ts
330
369
  var ContextContainer = React3.createContext(null);
370
+ function useContextContainer() {
371
+ return React3.useContext(ContextContainer);
372
+ }
331
373
  function useViewability(callback, configId) {
332
374
  const ctx = useStateContext();
333
- const { containerId } = React3.useContext(ContextContainer);
334
- const key = containerId + (configId != null ? configId : "");
375
+ const containerContext = useContextContainer();
335
376
  useInit(() => {
377
+ if (!containerContext) {
378
+ return;
379
+ }
380
+ const { containerId } = containerContext;
381
+ const key = containerId + (configId != null ? configId : "");
336
382
  const value = ctx.mapViewabilityValues.get(key);
337
383
  if (value) {
338
384
  callback(value);
339
385
  }
340
386
  });
341
- ctx.mapViewabilityCallbacks.set(key, callback);
342
- React3.useEffect(
343
- () => () => {
387
+ React3.useEffect(() => {
388
+ if (!containerContext) {
389
+ return;
390
+ }
391
+ const { containerId } = containerContext;
392
+ const key = containerId + (configId != null ? configId : "");
393
+ ctx.mapViewabilityCallbacks.set(key, callback);
394
+ return () => {
344
395
  ctx.mapViewabilityCallbacks.delete(key);
345
- },
346
- []
347
- );
396
+ };
397
+ }, [ctx, callback, configId, containerContext]);
348
398
  }
349
399
  function useViewabilityAmount(callback) {
350
400
  const ctx = useStateContext();
351
- const { containerId } = React3.useContext(ContextContainer);
401
+ const containerContext = useContextContainer();
352
402
  useInit(() => {
403
+ if (!containerContext) {
404
+ return;
405
+ }
406
+ const { containerId } = containerContext;
353
407
  const value = ctx.mapViewabilityAmountValues.get(containerId);
354
408
  if (value) {
355
409
  callback(value);
356
410
  }
357
411
  });
358
- ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
359
- React3.useEffect(
360
- () => () => {
412
+ React3.useEffect(() => {
413
+ if (!containerContext) {
414
+ return;
415
+ }
416
+ const { containerId } = containerContext;
417
+ ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
418
+ return () => {
361
419
  ctx.mapViewabilityAmountCallbacks.delete(containerId);
362
- },
363
- []
364
- );
420
+ };
421
+ }, [ctx, callback, containerContext]);
365
422
  }
366
423
  function useRecyclingEffect(effect) {
367
- const { index, value } = React3.useContext(ContextContainer);
424
+ const containerContext = useContextContainer();
368
425
  const prevValues = React3.useRef({
369
426
  prevIndex: void 0,
370
427
  prevItem: void 0
371
428
  });
372
429
  React3.useEffect(() => {
430
+ if (!containerContext) {
431
+ return;
432
+ }
433
+ const { index, value } = containerContext;
373
434
  let ret;
374
435
  if (prevValues.current.prevIndex !== void 0 && prevValues.current.prevItem !== void 0) {
375
436
  ret = effect({
@@ -384,48 +445,73 @@ function useRecyclingEffect(effect) {
384
445
  prevItem: value
385
446
  };
386
447
  return ret;
387
- }, [index, value, effect]);
448
+ }, [effect, containerContext]);
388
449
  }
389
450
  function useRecyclingState(valueOrFun) {
390
- const { index, value, itemKey, triggerLayout } = React3.useContext(ContextContainer);
391
- const refState = React3.useRef({
392
- itemKey: null,
393
- value: null
451
+ var _a3, _b;
452
+ const containerContext = useContextContainer();
453
+ const computeValue = (ctx) => {
454
+ if (isFunction(valueOrFun)) {
455
+ const initializer = valueOrFun;
456
+ return ctx ? initializer({
457
+ index: ctx.index,
458
+ item: ctx.value,
459
+ prevIndex: void 0,
460
+ prevItem: void 0
461
+ }) : initializer();
462
+ }
463
+ return valueOrFun;
464
+ };
465
+ const [stateValue, setStateValue] = React3.useState(() => {
466
+ return computeValue(containerContext);
394
467
  });
395
- const [_, setRenderNum] = React3.useState(0);
396
- const state = refState.current;
397
- if (state.itemKey !== itemKey) {
398
- state.itemKey = itemKey;
399
- state.value = isFunction(valueOrFun) ? valueOrFun({
400
- index,
401
- item: value,
402
- prevIndex: void 0,
403
- prevItem: void 0
404
- }) : valueOrFun;
468
+ const prevItemKeyRef = React3.useRef((_a3 = containerContext == null ? void 0 : containerContext.itemKey) != null ? _a3 : null);
469
+ const currentItemKey = (_b = containerContext == null ? void 0 : containerContext.itemKey) != null ? _b : null;
470
+ if (currentItemKey !== null && prevItemKeyRef.current !== currentItemKey) {
471
+ prevItemKeyRef.current = currentItemKey;
472
+ setStateValue(computeValue(containerContext));
405
473
  }
474
+ const triggerLayout = containerContext == null ? void 0 : containerContext.triggerLayout;
406
475
  const setState = React3.useCallback(
407
476
  (newState) => {
408
- state.value = isFunction(newState) ? newState(state.value) : newState;
409
- setRenderNum((v) => v + 1);
477
+ if (!triggerLayout) {
478
+ return;
479
+ }
480
+ setStateValue((prevValue) => {
481
+ return isFunction(newState) ? newState(prevValue) : newState;
482
+ });
410
483
  triggerLayout();
411
484
  },
412
- [triggerLayout, state]
485
+ [triggerLayout]
413
486
  );
414
- return [state.value, setState];
487
+ return [stateValue, setState];
415
488
  }
416
489
  function useIsLastItem() {
417
- const { itemKey } = React3.useContext(ContextContainer);
418
- const isLast = useSelector$("lastItemKeys", (lastItemKeys) => (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false);
490
+ const containerContext = useContextContainer();
491
+ const isLast = useSelector$("lastItemKeys", (lastItemKeys) => {
492
+ if (containerContext) {
493
+ const { itemKey } = containerContext;
494
+ if (!isNullOrUndefined(itemKey)) {
495
+ return (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false;
496
+ }
497
+ }
498
+ return false;
499
+ });
419
500
  return isLast;
420
501
  }
421
502
  function useListScrollSize() {
422
503
  const [scrollSize] = useArr$(["scrollSize"]);
423
504
  return scrollSize;
424
505
  }
506
+ var noop = () => {
507
+ };
425
508
  function useSyncLayout() {
426
- {
427
- const { triggerLayout: syncLayout } = React3.useContext(ContextContainer);
509
+ const containerContext = useContextContainer();
510
+ if (containerContext) {
511
+ const { triggerLayout: syncLayout } = containerContext;
428
512
  return syncLayout;
513
+ } else {
514
+ return noop;
429
515
  }
430
516
  }
431
517
 
@@ -471,10 +557,9 @@ function createResizeObserver(element, callback) {
471
557
  }
472
558
  callbacks.add(callback);
473
559
  return () => {
474
- const callbacks2 = callbackMap.get(element);
475
- if (callbacks2) {
476
- callbacks2.delete(callback);
477
- if (callbacks2.size === 0) {
560
+ if (callbacks) {
561
+ callbacks.delete(callback);
562
+ if (callbacks.size === 0) {
478
563
  callbackMap.delete(element);
479
564
  observer.unobserve(element);
480
565
  }
@@ -509,10 +594,10 @@ function useOnLayoutSync({
509
594
  return createResizeObserver(element, (entry) => {
510
595
  var _a4;
511
596
  const target = entry.target instanceof HTMLElement ? entry.target : void 0;
512
- const rect2 = (_a4 = entry.contentRect) != null ? _a4 : target == null ? void 0 : target.getBoundingClientRect();
513
- if (rect2.width !== prevRect.width || rect2.height !== prevRect.height) {
514
- prevRect = rect2;
515
- emit(toLayout(rect2), false);
597
+ const rectObserved = (_a4 = entry.contentRect) != null ? _a4 : target == null ? void 0 : target.getBoundingClientRect();
598
+ if (rectObserved.width !== prevRect.width || rectObserved.height !== prevRect.height) {
599
+ prevRect = rectObserved;
600
+ emit(toLayout(rectObserved), false);
516
601
  }
517
602
  });
518
603
  }, deps || []);
@@ -530,6 +615,12 @@ function toLayout(rect) {
530
615
  };
531
616
  }
532
617
 
618
+ // src/platform/Platform.ts
619
+ var Platform = {
620
+ // Widen the type to avoid unreachable-branch lints in cross-platform code that compares against other OSes
621
+ OS: "web"
622
+ };
623
+
533
624
  // src/components/Container.tsx
534
625
  var Container = typedMemo(function Container2({
535
626
  id,
@@ -537,22 +628,25 @@ var Container = typedMemo(function Container2({
537
628
  horizontal,
538
629
  getRenderedItem: getRenderedItem2,
539
630
  updateItemSize: updateItemSize2,
540
- ItemSeparatorComponent
631
+ ItemSeparatorComponent,
632
+ stickyHeaderConfig
541
633
  }) {
542
634
  const ctx = useStateContext();
543
635
  const { columnWrapperStyle, animatedScrollY } = ctx;
544
- const [column = 0, data, itemKey, numColumns, extraData, isSticky, stickyOffset] = useArr$([
636
+ const [column = 0, span = 1, data, itemKey, numColumns = 1, extraData, isSticky] = useArr$([
545
637
  `containerColumn${id}`,
638
+ `containerSpan${id}`,
546
639
  `containerItemData${id}`,
547
640
  `containerItemKey${id}`,
548
641
  "numColumns",
549
642
  "extraData",
550
- `containerSticky${id}`,
551
- `containerStickyOffset${id}`
643
+ `containerSticky${id}`
552
644
  ]);
553
645
  const itemLayoutRef = React3.useRef({
646
+ didLayout: false,
554
647
  horizontal,
555
648
  itemKey,
649
+ pendingShrinkToken: 0,
556
650
  updateItemSize: updateItemSize2
557
651
  });
558
652
  itemLayoutRef.current.horizontal = horizontal;
@@ -560,9 +654,10 @@ var Container = typedMemo(function Container2({
560
654
  itemLayoutRef.current.updateItemSize = updateItemSize2;
561
655
  const ref = React3.useRef(null);
562
656
  const [layoutRenderCount, forceLayoutRender] = React3.useState(0);
563
- const otherAxisPos = numColumns > 1 ? `${(column - 1) / numColumns * 100}%` : 0;
564
- const otherAxisSize = numColumns > 1 ? `${1 / numColumns * 100}%` : void 0;
565
- const didLayoutRef = React3.useRef(false);
657
+ const resolvedColumn = column > 0 ? column : 1;
658
+ const resolvedSpan = Math.min(Math.max(span || 1, 1), numColumns);
659
+ const otherAxisPos = numColumns > 1 ? `${(resolvedColumn - 1) / numColumns * 100}%` : 0;
660
+ const otherAxisSize = numColumns > 1 ? `${resolvedSpan / numColumns * 100}%` : void 0;
566
661
  const style = React3.useMemo(() => {
567
662
  let paddingStyles;
568
663
  if (columnWrapperStyle) {
@@ -616,19 +711,40 @@ var Container = typedMemo(function Container2({
616
711
  const {
617
712
  horizontal: currentHorizontal,
618
713
  itemKey: currentItemKey,
619
- updateItemSize: updateItemSizeFn
714
+ updateItemSize: updateItemSizeFn,
715
+ lastSize,
716
+ pendingShrinkToken
620
717
  } = itemLayoutRef.current;
621
718
  if (isNullOrUndefined(currentItemKey)) {
622
719
  return;
623
720
  }
624
- didLayoutRef.current = true;
721
+ itemLayoutRef.current.didLayout = true;
625
722
  let layout = rectangle;
626
- roundSize(rectangle[currentHorizontal ? "width" : "height"]);
723
+ const axis = currentHorizontal ? "width" : "height";
724
+ const size = roundSize(rectangle[axis]);
725
+ const prevSize = lastSize ? roundSize(lastSize[axis]) : void 0;
627
726
  const doUpdate = () => {
628
- itemLayoutRef.current.lastSize = { height: layout.height, width: layout.width };
727
+ itemLayoutRef.current.lastSize = layout;
629
728
  updateItemSizeFn(currentItemKey, layout);
630
- didLayoutRef.current = true;
729
+ itemLayoutRef.current.didLayout = true;
631
730
  };
731
+ if (prevSize !== void 0 && size + 1 < prevSize) {
732
+ const token = pendingShrinkToken + 1;
733
+ itemLayoutRef.current.pendingShrinkToken = token;
734
+ requestAnimationFrame(() => {
735
+ var _a4;
736
+ if (itemLayoutRef.current.pendingShrinkToken !== token) {
737
+ return;
738
+ }
739
+ const element = ref.current;
740
+ const rect = (_a4 = element == null ? void 0 : element.getBoundingClientRect) == null ? void 0 : _a4.call(element);
741
+ if (rect) {
742
+ layout = { height: rect.height, width: rect.width };
743
+ }
744
+ doUpdate();
745
+ });
746
+ return;
747
+ }
632
748
  {
633
749
  doUpdate();
634
750
  }
@@ -640,7 +756,7 @@ var Container = typedMemo(function Container2({
640
756
  },
641
757
  [itemKey, layoutRenderCount]
642
758
  );
643
- const PositionComponent = isSticky ? PositionViewSticky : PositionView2;
759
+ const PositionComponent = isSticky ? PositionViewSticky : PositionView;
644
760
  return /* @__PURE__ */ React3__namespace.createElement(
645
761
  PositionComponent,
646
762
  {
@@ -651,19 +767,13 @@ var Container = typedMemo(function Container2({
651
767
  key: recycleItems ? void 0 : itemKey,
652
768
  onLayout,
653
769
  refView: ref,
654
- stickyOffset: isSticky ? stickyOffset : void 0,
770
+ stickyHeaderConfig,
655
771
  style
656
772
  },
657
773
  /* @__PURE__ */ React3__namespace.createElement(ContextContainer.Provider, { value: contextValue }, renderedItem, renderedItemInfo && ItemSeparatorComponent && /* @__PURE__ */ React3__namespace.createElement(Separator, { ItemSeparatorComponent, leadingItem: renderedItemInfo.item }))
658
774
  );
659
775
  });
660
776
 
661
- // src/platform/Platform.ts
662
- var Platform = {
663
- // Widen the type to avoid unreachable-branch lints in cross-platform code that compares against other OSes
664
- OS: "web"
665
- };
666
-
667
777
  // src/utils/reordering.ts
668
778
  var mapFn = (element) => {
669
779
  const indexStr = element.getAttribute("index");
@@ -810,7 +920,8 @@ var Containers = typedMemo(function Containers2({
810
920
  ItemSeparatorComponent,
811
921
  waitForInitialLayout,
812
922
  updateItemSize: updateItemSize2,
813
- getRenderedItem: getRenderedItem2
923
+ getRenderedItem: getRenderedItem2,
924
+ stickyHeaderConfig
814
925
  }) {
815
926
  const [numContainers, numColumns] = useArr$(["numContainersPooled", "numColumns"]);
816
927
  const containers = [];
@@ -825,6 +936,7 @@ var Containers = typedMemo(function Containers2({
825
936
  id: i,
826
937
  key: i,
827
938
  recycleItems,
939
+ stickyHeaderConfig,
828
940
  updateItemSize: updateItemSize2
829
941
  }
830
942
  )
@@ -833,7 +945,8 @@ var Containers = typedMemo(function Containers2({
833
945
  return /* @__PURE__ */ React3__namespace.createElement(ContainersInner, { horizontal, numColumns, waitForInitialLayout }, containers);
834
946
  });
835
947
  function DevNumbers() {
836
- return IS_DEV && React3__namespace.memo(function DevNumbers2() {
948
+ return IS_DEV && // biome-ignore lint/nursery/noShadow: const function name shadowing is intentional
949
+ React3__namespace.memo(function DevNumbers2() {
837
950
  return Array.from({ length: 100 }).map((_, index) => /* @__PURE__ */ React3__namespace.createElement(
838
951
  "div",
839
952
  {
@@ -881,7 +994,6 @@ var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView
881
994
  }, ref) {
882
995
  const scrollRef = React3.useRef(null);
883
996
  const contentRef = React3.useRef(null);
884
- const momentumTimeout = React3.useRef(null);
885
997
  React3.useImperativeHandle(ref, () => {
886
998
  const api = {
887
999
  getBoundingClientRect: () => {
@@ -947,16 +1059,6 @@ var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView
947
1059
  }
948
1060
  };
949
1061
  onScroll2(scrollEvent);
950
- if (onMomentumScrollEnd) {
951
- if (momentumTimeout.current != null) clearTimeout(momentumTimeout.current);
952
- momentumTimeout.current = setTimeout(() => {
953
- onMomentumScrollEnd({
954
- nativeEvent: {
955
- contentOffset: scrollEvent.nativeEvent.contentOffset
956
- }
957
- });
958
- }, 100);
959
- }
960
1062
  },
961
1063
  [onScroll2, onMomentumScrollEnd]
962
1064
  );
@@ -1018,28 +1120,9 @@ var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView
1018
1120
  minWidth: horizontal ? "100%" : void 0,
1019
1121
  ...StyleSheet.flatten(contentContainerStyle)
1020
1122
  };
1021
- return /* @__PURE__ */ React3__namespace.createElement("div", { ref: scrollRef, style: scrollViewStyle, ...props }, refreshControl, /* @__PURE__ */ React3__namespace.createElement("div", { ref: contentRef, style: contentStyle }, children));
1123
+ const { contentInset, scrollEventThrottle, ScrollComponent, ...webProps } = props;
1124
+ return /* @__PURE__ */ React3__namespace.createElement("div", { ref: scrollRef, ...webProps, style: scrollViewStyle }, refreshControl, /* @__PURE__ */ React3__namespace.createElement("div", { ref: contentRef, style: contentStyle }, children));
1022
1125
  });
1023
- function Padding() {
1024
- const [paddingTop] = useArr$(["alignItemsPaddingTop"]);
1025
- return /* @__PURE__ */ React3__namespace.createElement("div", { style: { paddingTop } });
1026
- }
1027
- function PaddingDevMode() {
1028
- const [paddingTop] = useArr$(["alignItemsPaddingTop"]);
1029
- return /* @__PURE__ */ React3__namespace.createElement(React3__namespace.Fragment, null, /* @__PURE__ */ React3__namespace.createElement("div", { style: { paddingTop } }), /* @__PURE__ */ React3__namespace.createElement(
1030
- "div",
1031
- {
1032
- style: {
1033
- backgroundColor: "green",
1034
- height: paddingTop,
1035
- left: 0,
1036
- position: "absolute",
1037
- right: 0,
1038
- top: 0
1039
- }
1040
- }
1041
- ));
1042
- }
1043
1126
  function useValueListener$(key, callback) {
1044
1127
  const ctx = useStateContext();
1045
1128
  React3.useLayoutEffect(() => {
@@ -1059,7 +1142,7 @@ function ScrollAdjust() {
1059
1142
  const scrollAdjust = peek$(ctx, "scrollAdjust");
1060
1143
  const scrollAdjustUserOffset = peek$(ctx, "scrollAdjustUserOffset");
1061
1144
  const scrollOffset = (scrollAdjust || 0) + (scrollAdjustUserOffset || 0);
1062
- const scrollView = (_a3 = ctx.internalState) == null ? void 0 : _a3.refScroller.current;
1145
+ const scrollView = (_a3 = ctx.state) == null ? void 0 : _a3.refScroller.current;
1063
1146
  if (scrollView && scrollOffset !== lastScrollOffsetRef.current) {
1064
1147
  const scrollDelta = scrollOffset - lastScrollOffsetRef.current;
1065
1148
  if (scrollDelta !== 0) {
@@ -1067,26 +1150,23 @@ function ScrollAdjust() {
1067
1150
  const prevScroll = el.scrollTop;
1068
1151
  const nextScroll = prevScroll + scrollDelta;
1069
1152
  const totalSize = el.scrollHeight;
1070
- if (scrollDelta > 0 && !ctx.internalState.adjustingFromInitialMount && totalSize < nextScroll + el.clientHeight) {
1153
+ if (scrollDelta > 0 && !ctx.state.adjustingFromInitialMount && totalSize < nextScroll + el.clientHeight) {
1071
1154
  const child = el.firstElementChild;
1072
- const prevPaddingBottom = child.style.paddingBottom;
1073
1155
  const pad = (nextScroll + el.clientHeight - totalSize) * 2;
1074
1156
  child.style.paddingBottom = `${pad}px`;
1075
1157
  void el.offsetHeight;
1076
1158
  scrollView.scrollBy(0, scrollDelta);
1077
- setTimeout(() => {
1078
- child.style.paddingBottom = prevPaddingBottom;
1079
- }, 100);
1159
+ requestAnimationFrame(() => {
1160
+ const paddingBottom = ctx.state.props.stylePaddingBottom;
1161
+ child.style.paddingBottom = paddingBottom ? `${paddingBottom}px` : "0";
1162
+ });
1080
1163
  } else {
1081
1164
  scrollView.scrollBy(0, scrollDelta);
1082
1165
  }
1083
- if (IS_DEV) {
1084
- console.log("ScrollAdjust (web scrollBy)", scrollDelta, "total offset:", scrollOffset);
1085
- }
1086
1166
  }
1087
1167
  lastScrollOffsetRef.current = scrollOffset;
1088
1168
  }
1089
- }, []);
1169
+ }, [ctx]);
1090
1170
  useValueListener$("scrollAdjust", callback);
1091
1171
  useValueListener$("scrollAdjustUserOffset", callback);
1092
1172
  return null;
@@ -1100,8 +1180,6 @@ var LayoutView = ({ onLayoutChange, refView, children, ...rest }) => {
1100
1180
  useOnLayoutSync({ onLayoutChange, ref });
1101
1181
  return /* @__PURE__ */ React3__namespace.createElement("div", { ...rest, ref }, children);
1102
1182
  };
1103
-
1104
- // src/components/ListComponent.tsx
1105
1183
  var getComponent = (Component) => {
1106
1184
  if (React3__namespace.isValidElement(Component)) {
1107
1185
  return Component;
@@ -1111,6 +1189,8 @@ var getComponent = (Component) => {
1111
1189
  }
1112
1190
  return null;
1113
1191
  };
1192
+
1193
+ // src/components/ListComponent.tsx
1114
1194
  var ListComponent = typedMemo(function ListComponent2({
1115
1195
  canRender,
1116
1196
  style,
@@ -1119,7 +1199,7 @@ var ListComponent = typedMemo(function ListComponent2({
1119
1199
  initialContentOffset,
1120
1200
  recycleItems,
1121
1201
  ItemSeparatorComponent,
1122
- alignItemsAtEnd,
1202
+ alignItemsAtEnd: _alignItemsAtEnd,
1123
1203
  waitForInitialLayout,
1124
1204
  onScroll: onScroll2,
1125
1205
  onLayout,
@@ -1131,27 +1211,29 @@ var ListComponent = typedMemo(function ListComponent2({
1131
1211
  getRenderedItem: getRenderedItem2,
1132
1212
  updateItemSize: updateItemSize2,
1133
1213
  refScrollView,
1134
- maintainVisibleContentPosition,
1135
1214
  renderScrollComponent,
1136
1215
  scrollAdjustHandler,
1137
1216
  onLayoutHeader,
1138
1217
  snapToIndices,
1218
+ stickyHeaderConfig,
1139
1219
  stickyHeaderIndices,
1140
1220
  ...rest
1141
1221
  }) {
1142
1222
  const ctx = useStateContext();
1223
+ const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
1143
1224
  const ScrollComponent = renderScrollComponent ? React3.useMemo(
1144
1225
  () => React3__namespace.forwardRef((props, ref) => renderScrollComponent({ ...props, ref })),
1145
1226
  [renderScrollComponent]
1146
1227
  ) : ListComponentScrollView;
1147
- React3__namespace.useEffect(() => {
1148
- if (canRender) {
1149
- setTimeout(() => {
1150
- scrollAdjustHandler.setMounted();
1151
- }, 0);
1152
- }
1153
- }, [canRender]);
1154
1228
  const SnapOrScroll = snapToIndices ? SnapWrapper : ScrollComponent;
1229
+ React3.useLayoutEffect(() => {
1230
+ if (!ListHeaderComponent) {
1231
+ set$(ctx, "headerSize", 0);
1232
+ }
1233
+ if (!ListFooterComponent) {
1234
+ set$(ctx, "footerSize", 0);
1235
+ }
1236
+ }, [ListHeaderComponent, ListFooterComponent, ctx]);
1155
1237
  return /* @__PURE__ */ React3__namespace.createElement(
1156
1238
  SnapOrScroll,
1157
1239
  {
@@ -1164,7 +1246,7 @@ var ListComponent = typedMemo(function ListComponent2({
1164
1246
  ],
1165
1247
  contentOffset: initialContentOffset ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
1166
1248
  horizontal,
1167
- maintainVisibleContentPosition: maintainVisibleContentPosition ? { minIndexForVisible: 0 } : void 0,
1249
+ maintainVisibleContentPosition: maintainVisibleContentPosition.size || maintainVisibleContentPosition.data ? { minIndexForVisible: 0 } : void 0,
1168
1250
  onLayout,
1169
1251
  onScroll: onScroll2,
1170
1252
  ref: refScrollView,
@@ -1172,7 +1254,6 @@ var ListComponent = typedMemo(function ListComponent2({
1172
1254
  style
1173
1255
  },
1174
1256
  /* @__PURE__ */ React3__namespace.createElement(ScrollAdjust, null),
1175
- ENABLE_DEVMODE ? /* @__PURE__ */ React3__namespace.createElement(PaddingDevMode, null) : /* @__PURE__ */ React3__namespace.createElement(Padding, null),
1176
1257
  ListHeaderComponent && /* @__PURE__ */ React3__namespace.createElement(LayoutView, { onLayoutChange: onLayoutHeader, style: ListHeaderComponentStyle }, getComponent(ListHeaderComponent)),
1177
1258
  ListEmptyComponent && getComponent(ListEmptyComponent),
1178
1259
  canRender && !ListEmptyComponent && /* @__PURE__ */ React3__namespace.createElement(
@@ -1182,6 +1263,7 @@ var ListComponent = typedMemo(function ListComponent2({
1182
1263
  horizontal,
1183
1264
  ItemSeparatorComponent,
1184
1265
  recycleItems,
1266
+ stickyHeaderConfig,
1185
1267
  updateItemSize: updateItemSize2,
1186
1268
  waitForInitialLayout
1187
1269
  }
@@ -1214,10 +1296,11 @@ function getId(state, index) {
1214
1296
  }
1215
1297
 
1216
1298
  // src/core/calculateOffsetForIndex.ts
1217
- function calculateOffsetForIndex(ctx, state, index) {
1299
+ function calculateOffsetForIndex(ctx, index) {
1300
+ const state = ctx.state;
1218
1301
  let position = 0;
1219
1302
  if (index !== void 0) {
1220
- position = (state == null ? void 0 : state.positions.get(getId(state, index))) || 0;
1303
+ position = state.positions.get(getId(state, index)) || 0;
1221
1304
  const paddingTop = peek$(ctx, "stylePaddingTop");
1222
1305
  if (paddingTop) {
1223
1306
  position += paddingTop;
@@ -1230,44 +1313,9 @@ function calculateOffsetForIndex(ctx, state, index) {
1230
1313
  return position;
1231
1314
  }
1232
1315
 
1233
- // src/utils/setPaddingTop.ts
1234
- function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
1235
- if (stylePaddingTop !== void 0) {
1236
- const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
1237
- if (stylePaddingTop < prevStylePaddingTop) {
1238
- let prevTotalSize = peek$(ctx, "totalSize") || 0;
1239
- set$(ctx, "totalSize", prevTotalSize + prevStylePaddingTop);
1240
- state.timeoutSetPaddingTop = setTimeout(() => {
1241
- prevTotalSize = peek$(ctx, "totalSize") || 0;
1242
- set$(ctx, "totalSize", prevTotalSize - prevStylePaddingTop);
1243
- }, 16);
1244
- }
1245
- set$(ctx, "stylePaddingTop", stylePaddingTop);
1246
- }
1247
- if (alignItemsPaddingTop !== void 0) {
1248
- set$(ctx, "alignItemsPaddingTop", alignItemsPaddingTop);
1249
- }
1250
- }
1251
-
1252
- // src/utils/updateAlignItemsPaddingTop.ts
1253
- function updateAlignItemsPaddingTop(ctx, state) {
1254
- const {
1255
- scrollLength,
1256
- props: { alignItemsAtEnd, data }
1257
- } = state;
1258
- if (alignItemsAtEnd) {
1259
- let alignItemsPaddingTop = 0;
1260
- if ((data == null ? void 0 : data.length) > 0) {
1261
- const contentSize = getContentSize(ctx);
1262
- alignItemsPaddingTop = Math.max(0, Math.floor(scrollLength - contentSize));
1263
- }
1264
- setPaddingTop(ctx, state, { alignItemsPaddingTop });
1265
- }
1266
- }
1267
-
1268
1316
  // src/core/addTotalSize.ts
1269
- function addTotalSize(ctx, state, key, add) {
1270
- const { alignItemsAtEnd } = state.props;
1317
+ function addTotalSize(ctx, key, add) {
1318
+ const state = ctx.state;
1271
1319
  const prevTotalSize = state.totalSize;
1272
1320
  let totalSize = state.totalSize;
1273
1321
  if (key === null) {
@@ -1284,32 +1332,32 @@ function addTotalSize(ctx, state, key, add) {
1284
1332
  state.pendingTotalSize = void 0;
1285
1333
  state.totalSize = totalSize;
1286
1334
  set$(ctx, "totalSize", totalSize);
1287
- if (alignItemsAtEnd) {
1288
- updateAlignItemsPaddingTop(ctx, state);
1289
- }
1290
1335
  }
1291
1336
  }
1292
1337
  }
1293
1338
 
1294
1339
  // src/core/setSize.ts
1295
- function setSize(ctx, state, itemKey, size) {
1340
+ function setSize(ctx, itemKey, size) {
1341
+ const state = ctx.state;
1296
1342
  const { sizes } = state;
1297
1343
  const previousSize = sizes.get(itemKey);
1298
1344
  const diff = previousSize !== void 0 ? size - previousSize : size;
1299
1345
  if (diff !== 0) {
1300
- addTotalSize(ctx, state, itemKey, diff);
1346
+ addTotalSize(ctx, itemKey, diff);
1301
1347
  }
1302
1348
  sizes.set(itemKey, size);
1303
1349
  }
1304
1350
 
1305
1351
  // src/utils/getItemSize.ts
1306
- function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedSize) {
1352
+ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
1307
1353
  var _a3, _b;
1354
+ const state = ctx.state;
1308
1355
  const {
1309
1356
  sizesKnown,
1310
1357
  sizes,
1311
1358
  averageSizes,
1312
- props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType }
1359
+ props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType },
1360
+ scrollingTo
1313
1361
  } = state;
1314
1362
  const sizeKnown = sizesKnown.get(key);
1315
1363
  if (sizeKnown !== void 0) {
@@ -1317,7 +1365,6 @@ function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedS
1317
1365
  }
1318
1366
  let size;
1319
1367
  const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1320
- const scrollingTo = peek$(ctx, "scrollingTo");
1321
1368
  if (preferCachedSize) {
1322
1369
  const cachedSize = sizes.get(key);
1323
1370
  if (cachedSize !== void 0) {
@@ -1325,7 +1372,7 @@ function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedS
1325
1372
  }
1326
1373
  }
1327
1374
  if (getFixedItemSize) {
1328
- size = getFixedItemSize(index, data, itemType);
1375
+ size = getFixedItemSize(data, index, itemType);
1329
1376
  if (size !== void 0) {
1330
1377
  sizesKnown.set(key, size);
1331
1378
  }
@@ -1343,93 +1390,48 @@ function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedS
1343
1390
  }
1344
1391
  }
1345
1392
  if (size === void 0) {
1346
- size = getEstimatedItemSize ? getEstimatedItemSize(index, data, itemType) : estimatedItemSize;
1393
+ size = getEstimatedItemSize ? getEstimatedItemSize(data, index, itemType) : estimatedItemSize;
1347
1394
  }
1348
- setSize(ctx, state, key, size);
1395
+ setSize(ctx, key, size);
1349
1396
  return size;
1350
1397
  }
1351
1398
 
1352
1399
  // src/core/calculateOffsetWithOffsetPosition.ts
1353
- function calculateOffsetWithOffsetPosition(ctx, state, offsetParam, params) {
1400
+ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1401
+ const state = ctx.state;
1354
1402
  const { index, viewOffset, viewPosition } = params;
1355
1403
  let offset = offsetParam;
1356
1404
  if (viewOffset) {
1357
1405
  offset -= viewOffset;
1358
1406
  }
1359
1407
  if (viewPosition !== void 0 && index !== void 0) {
1360
- offset -= viewPosition * (state.scrollLength - getItemSize(ctx, state, getId(state, index), index, state.props.data[index]));
1408
+ const itemSize = getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1409
+ const trailingInset = getContentInsetEnd(state);
1410
+ offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1361
1411
  }
1362
1412
  return offset;
1363
1413
  }
1364
1414
 
1365
- // src/core/finishScrollTo.ts
1366
- function finishScrollTo(ctx, state) {
1367
- var _a3, _b;
1368
- if (state) {
1369
- state.scrollHistory.length = 0;
1370
- state.initialScroll = void 0;
1371
- state.initialAnchor = void 0;
1372
- set$(ctx, "scrollingTo", void 0);
1373
- if (state.pendingTotalSize !== void 0) {
1374
- addTotalSize(ctx, state, null, state.pendingTotalSize);
1375
- }
1376
- if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1377
- (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1378
- }
1379
- }
1380
- }
1381
-
1382
- // src/core/scrollTo.ts
1383
- function scrollTo(ctx, state, params) {
1384
- var _a3;
1385
- const { noScrollingTo, ...scrollTarget } = params;
1386
- const { animated, isInitialScroll, offset: scrollTargetOffset, precomputedWithViewOffset } = scrollTarget;
1387
- const {
1388
- refScroller,
1389
- props: { horizontal }
1390
- } = state;
1391
- let offset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, state, scrollTargetOffset, scrollTarget);
1392
- if (Number.isFinite(state.scrollLength) && Number.isFinite(state.totalSize)) {
1393
- const maxOffset = Math.max(0, getContentSize(ctx) - state.scrollLength);
1394
- offset = Math.min(offset, maxOffset);
1395
- }
1396
- state.scrollHistory.length = 0;
1397
- if (!noScrollingTo) {
1398
- set$(ctx, "scrollingTo", scrollTarget);
1399
- }
1400
- state.scrollPending = offset;
1401
- if (!isInitialScroll || Platform.OS === "android") {
1402
- (_a3 = refScroller.current) == null ? void 0 : _a3.scrollTo({
1403
- animated: !!animated,
1404
- x: horizontal ? offset : 0,
1405
- y: horizontal ? 0 : offset
1406
- });
1407
- }
1408
- if (!animated) {
1409
- state.scroll = offset;
1410
- {
1411
- const unlisten = listen$(ctx, "containersDidLayout", (value) => {
1412
- if (value && peek$(ctx, "scrollingTo")) {
1413
- finishScrollTo(ctx, state);
1414
- unlisten();
1415
- }
1416
- });
1417
- }
1418
- if (isInitialScroll) {
1419
- setTimeout(() => {
1420
- state.initialScroll = void 0;
1421
- }, 500);
1422
- }
1415
+ // src/core/clampScrollOffset.ts
1416
+ function clampScrollOffset(ctx, offset) {
1417
+ const state = ctx.state;
1418
+ const contentSize = getContentSize(ctx);
1419
+ let clampedOffset = offset;
1420
+ if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform.OS !== "android")) {
1421
+ const maxOffset = Math.max(0, contentSize - state.scrollLength);
1422
+ clampedOffset = Math.min(offset, maxOffset);
1423
1423
  }
1424
+ clampedOffset = Math.max(0, clampedOffset);
1425
+ return clampedOffset;
1424
1426
  }
1425
1427
 
1426
1428
  // src/utils/checkThreshold.ts
1427
1429
  var HYSTERESIS_MULTIPLIER = 1.3;
1428
- var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot) => {
1430
+ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot, allowReentryOnChange) => {
1429
1431
  const absDistance = Math.abs(distance);
1430
1432
  const within = atThreshold || threshold > 0 && absDistance <= threshold;
1431
1433
  const updateSnapshot = () => {
1432
- setSnapshot == null ? void 0 : setSnapshot({
1434
+ setSnapshot({
1433
1435
  atThreshold,
1434
1436
  contentSize: context.contentSize,
1435
1437
  dataLength: context.dataLength,
@@ -1440,19 +1442,21 @@ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, co
1440
1442
  if (!within) {
1441
1443
  return false;
1442
1444
  }
1443
- onReached == null ? void 0 : onReached(distance);
1445
+ onReached(distance);
1444
1446
  updateSnapshot();
1445
1447
  return true;
1446
1448
  }
1447
1449
  const reset = !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
1448
1450
  if (reset) {
1449
- setSnapshot == null ? void 0 : setSnapshot(void 0);
1451
+ setSnapshot(void 0);
1450
1452
  return false;
1451
1453
  }
1452
1454
  if (within) {
1453
1455
  const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
1454
1456
  if (changed) {
1455
- onReached == null ? void 0 : onReached(distance);
1457
+ if (allowReentryOnChange) {
1458
+ onReached(distance);
1459
+ }
1456
1460
  updateSnapshot();
1457
1461
  }
1458
1462
  }
@@ -1460,9 +1464,10 @@ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, co
1460
1464
  };
1461
1465
 
1462
1466
  // src/utils/checkAtBottom.ts
1463
- function checkAtBottom(ctx, state) {
1467
+ function checkAtBottom(ctx) {
1464
1468
  var _a3;
1465
- if (!state) {
1469
+ const state = ctx.state;
1470
+ if (!state || state.initialScroll) {
1466
1471
  return;
1467
1472
  }
1468
1473
  const {
@@ -1472,9 +1477,13 @@ function checkAtBottom(ctx, state) {
1472
1477
  maintainingScrollAtEnd,
1473
1478
  props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
1474
1479
  } = state;
1480
+ if (state.initialScroll) {
1481
+ return;
1482
+ }
1475
1483
  const contentSize = getContentSize(ctx);
1476
1484
  if (contentSize > 0 && queuedInitialLayout && !maintainingScrollAtEnd) {
1477
- const distanceFromEnd = contentSize - scroll - scrollLength;
1485
+ const insetEnd = getContentInsetEnd(state);
1486
+ const distanceFromEnd = contentSize - scroll - scrollLength - insetEnd;
1478
1487
  const isContentLess = contentSize < scrollLength;
1479
1488
  state.isAtEnd = isContentLess || distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
1480
1489
  state.isEndReached = checkThreshold(
@@ -1494,15 +1503,17 @@ function checkAtBottom(ctx, state) {
1494
1503
  },
1495
1504
  (snapshot) => {
1496
1505
  state.endReachedSnapshot = snapshot;
1497
- }
1506
+ },
1507
+ true
1498
1508
  );
1499
1509
  }
1500
1510
  }
1501
1511
 
1502
1512
  // src/utils/checkAtTop.ts
1503
- function checkAtTop(state) {
1513
+ function checkAtTop(ctx) {
1504
1514
  var _a3;
1505
- if (!state) {
1515
+ const state = ctx == null ? void 0 : ctx.state;
1516
+ if (!state || state.initialScroll) {
1506
1517
  return;
1507
1518
  }
1508
1519
  const {
@@ -1529,20 +1540,162 @@ function checkAtTop(state) {
1529
1540
  },
1530
1541
  (snapshot) => {
1531
1542
  state.startReachedSnapshot = snapshot;
1532
- }
1543
+ },
1544
+ false
1533
1545
  );
1534
1546
  }
1535
1547
 
1548
+ // src/utils/checkThresholds.ts
1549
+ function checkThresholds(ctx) {
1550
+ checkAtBottom(ctx);
1551
+ checkAtTop(ctx);
1552
+ }
1553
+
1554
+ // src/utils/setInitialRenderState.ts
1555
+ function setInitialRenderState(ctx, {
1556
+ didLayout,
1557
+ didInitialScroll
1558
+ }) {
1559
+ const { state } = ctx;
1560
+ if (didLayout) {
1561
+ state.didContainersLayout = true;
1562
+ }
1563
+ if (didInitialScroll) {
1564
+ state.didFinishInitialScroll = true;
1565
+ }
1566
+ if (state.didContainersLayout && state.didFinishInitialScroll) {
1567
+ set$(ctx, "readyToRender", true);
1568
+ }
1569
+ }
1570
+
1571
+ // src/core/finishScrollTo.ts
1572
+ function finishScrollTo(ctx) {
1573
+ var _a3, _b;
1574
+ const state = ctx.state;
1575
+ if (state == null ? void 0 : state.scrollingTo) {
1576
+ const scrollingTo = state.scrollingTo;
1577
+ state.scrollHistory.length = 0;
1578
+ state.initialScroll = void 0;
1579
+ state.initialAnchor = void 0;
1580
+ state.scrollingTo = void 0;
1581
+ if (state.pendingTotalSize !== void 0) {
1582
+ addTotalSize(ctx, null, state.pendingTotalSize);
1583
+ }
1584
+ if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1585
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1586
+ }
1587
+ {
1588
+ state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
1589
+ }
1590
+ setInitialRenderState(ctx, { didInitialScroll: true });
1591
+ checkThresholds(ctx);
1592
+ }
1593
+ }
1594
+
1595
+ // src/core/doScrollTo.ts
1596
+ var SCROLL_END_IDLE_MS = 80;
1597
+ var SCROLL_END_MAX_MS = 1500;
1598
+ var SMOOTH_SCROLL_DURATION_MS = 320;
1599
+ function doScrollTo(ctx, params) {
1600
+ const state = ctx.state;
1601
+ const { animated, horizontal, offset } = params;
1602
+ const scroller = state.refScroller.current;
1603
+ const node = typeof (scroller == null ? void 0 : scroller.getScrollableNode) === "function" ? scroller.getScrollableNode() : scroller;
1604
+ if (node) {
1605
+ const left = horizontal ? offset : 0;
1606
+ const top = horizontal ? 0 : offset;
1607
+ node.scrollTo({ behavior: animated ? "smooth" : "auto", left, top });
1608
+ if (animated) {
1609
+ listenForScrollEnd(ctx, node);
1610
+ } else {
1611
+ state.scroll = offset;
1612
+ setTimeout(() => {
1613
+ finishScrollTo(ctx);
1614
+ }, 100);
1615
+ }
1616
+ }
1617
+ }
1618
+ function listenForScrollEnd(ctx, node) {
1619
+ const supportsScrollEnd = "onscrollend" in node;
1620
+ let idleTimeout;
1621
+ let maxTimeout;
1622
+ let settled = false;
1623
+ const targetToken = ctx.state.scrollingTo;
1624
+ const finish = () => {
1625
+ if (settled) return;
1626
+ settled = true;
1627
+ cleanup();
1628
+ if (targetToken === ctx.state.scrollingTo) {
1629
+ finishScrollTo(ctx);
1630
+ }
1631
+ };
1632
+ const onScroll2 = () => {
1633
+ if (idleTimeout) {
1634
+ clearTimeout(idleTimeout);
1635
+ }
1636
+ idleTimeout = setTimeout(finish, SCROLL_END_IDLE_MS);
1637
+ };
1638
+ const cleanup = () => {
1639
+ if (supportsScrollEnd) {
1640
+ node.removeEventListener("scrollend", finish);
1641
+ } else {
1642
+ node.removeEventListener("scroll", onScroll2);
1643
+ }
1644
+ if (idleTimeout) {
1645
+ clearTimeout(idleTimeout);
1646
+ }
1647
+ if (maxTimeout) {
1648
+ clearTimeout(maxTimeout);
1649
+ }
1650
+ };
1651
+ if (supportsScrollEnd) {
1652
+ node.addEventListener("scrollend", finish, { once: true });
1653
+ } else {
1654
+ node.addEventListener("scroll", onScroll2);
1655
+ idleTimeout = setTimeout(finish, SMOOTH_SCROLL_DURATION_MS);
1656
+ maxTimeout = setTimeout(finish, SCROLL_END_MAX_MS);
1657
+ }
1658
+ return cleanup;
1659
+ }
1660
+
1661
+ // src/core/scrollTo.ts
1662
+ function scrollTo(ctx, params) {
1663
+ const state = ctx.state;
1664
+ const { noScrollingTo, forceScroll, ...scrollTarget } = params;
1665
+ const { animated, isInitialScroll, offset: scrollTargetOffset, precomputedWithViewOffset } = scrollTarget;
1666
+ const {
1667
+ props: { horizontal }
1668
+ } = state;
1669
+ if (state.animFrameCheckFinishedScroll) {
1670
+ cancelAnimationFrame(ctx.state.animFrameCheckFinishedScroll);
1671
+ }
1672
+ if (state.timeoutCheckFinishedScrollFallback) {
1673
+ clearTimeout(ctx.state.timeoutCheckFinishedScrollFallback);
1674
+ }
1675
+ let offset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, scrollTargetOffset, scrollTarget);
1676
+ offset = clampScrollOffset(ctx, offset);
1677
+ state.scrollHistory.length = 0;
1678
+ if (!noScrollingTo) {
1679
+ state.scrollingTo = scrollTarget;
1680
+ }
1681
+ state.scrollPending = offset;
1682
+ if (forceScroll || !isInitialScroll || Platform.OS === "android") {
1683
+ doScrollTo(ctx, { animated, horizontal, isInitialScroll, offset });
1684
+ } else {
1685
+ state.scroll = offset;
1686
+ }
1687
+ }
1688
+
1536
1689
  // src/core/updateScroll.ts
1537
- function updateScroll(ctx, state, newScroll, forceUpdate) {
1538
- var _a3;
1539
- const scrollingTo = peek$(ctx, "scrollingTo");
1690
+ function updateScroll(ctx, newScroll, forceUpdate) {
1691
+ const state = ctx.state;
1692
+ const { scrollingTo, scrollAdjustHandler, lastScrollAdjustForHistory } = state;
1693
+ const prevScroll = state.scroll;
1540
1694
  state.hasScrolled = true;
1541
1695
  state.lastBatchingAction = Date.now();
1542
1696
  const currentTime = Date.now();
1543
- const adjust = state.scrollAdjustHandler.getAdjust();
1544
- const lastHistoryAdjust = state.lastScrollAdjustForHistory;
1545
- const adjustChanged = lastHistoryAdjust !== void 0 && Math.abs(adjust - lastHistoryAdjust) > 0.1;
1697
+ const adjust = scrollAdjustHandler.getAdjust();
1698
+ const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
1546
1699
  if (adjustChanged) {
1547
1700
  state.scrollHistory.length = 0;
1548
1701
  }
@@ -1555,7 +1708,7 @@ function updateScroll(ctx, state, newScroll, forceUpdate) {
1555
1708
  if (state.scrollHistory.length > 5) {
1556
1709
  state.scrollHistory.shift();
1557
1710
  }
1558
- state.scrollPrev = state.scroll;
1711
+ state.scrollPrev = prevScroll;
1559
1712
  state.scrollPrevTime = state.scrollTime;
1560
1713
  state.scroll = newScroll;
1561
1714
  state.scrollTime = currentTime;
@@ -1567,17 +1720,32 @@ function updateScroll(ctx, state, newScroll, forceUpdate) {
1567
1720
  return;
1568
1721
  }
1569
1722
  }
1570
- if (state.dataChangeNeedsScrollUpdate || Math.abs(state.scroll - state.scrollPrev) > 2) {
1723
+ const scrollDelta = Math.abs(newScroll - prevScroll);
1724
+ const scrollLength = state.scrollLength;
1725
+ const lastCalculated = state.scrollLastCalculate;
1726
+ const shouldUpdate = forceUpdate || state.dataChangeNeedsScrollUpdate || state.scrollLastCalculate === void 0 || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
1727
+ if (shouldUpdate) {
1728
+ state.scrollLastCalculate = state.scroll;
1571
1729
  state.ignoreScrollFromMVCPIgnored = false;
1572
- (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { doMVCP: scrollingTo !== void 0 });
1573
- checkAtBottom(ctx, state);
1574
- checkAtTop(state);
1730
+ state.lastScrollDelta = scrollDelta;
1731
+ const runCalculateItems = () => {
1732
+ var _a3;
1733
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { doMVCP: scrollingTo !== void 0 });
1734
+ checkThresholds(ctx);
1735
+ };
1736
+ if (scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength) {
1737
+ reactDom.flushSync(runCalculateItems);
1738
+ } else {
1739
+ runCalculateItems();
1740
+ }
1575
1741
  state.dataChangeNeedsScrollUpdate = false;
1742
+ state.lastScrollDelta = 0;
1576
1743
  }
1577
1744
  }
1578
1745
 
1579
1746
  // src/utils/requestAdjust.ts
1580
- function requestAdjust(ctx, state, positionDiff, dataChanged) {
1747
+ function requestAdjust(ctx, positionDiff, dataChanged) {
1748
+ const state = ctx.state;
1581
1749
  if (Math.abs(positionDiff) > 0.1) {
1582
1750
  const doit = () => {
1583
1751
  {
@@ -1589,8 +1757,8 @@ function requestAdjust(ctx, state, positionDiff, dataChanged) {
1589
1757
  };
1590
1758
  state.scroll += positionDiff;
1591
1759
  state.scrollForNextCalculateItemsInView = void 0;
1592
- const didLayout = peek$(ctx, "containersDidLayout");
1593
- if (didLayout) {
1760
+ const readyToRender = peek$(ctx, "readyToRender");
1761
+ if (readyToRender) {
1594
1762
  doit();
1595
1763
  } else {
1596
1764
  state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
@@ -1599,73 +1767,25 @@ function requestAdjust(ctx, state, positionDiff, dataChanged) {
1599
1767
  }
1600
1768
  }
1601
1769
 
1602
- // src/core/ensureInitialAnchor.ts
1603
- var INITIAL_ANCHOR_TOLERANCE = 0.5;
1604
- var INITIAL_ANCHOR_MAX_ATTEMPTS = 4;
1605
- var INITIAL_ANCHOR_SETTLED_TICKS = 2;
1606
- function ensureInitialAnchor(ctx, state) {
1607
- var _a3, _b, _c, _d, _e;
1608
- const anchor = state.initialAnchor;
1609
- const item = state.props.data[anchor.index];
1610
- const containersDidLayout = peek$(ctx, "containersDidLayout");
1611
- if (!containersDidLayout) {
1612
- return;
1613
- }
1614
- const id = getId(state, anchor.index);
1615
- if (state.positions.get(id) === void 0) {
1616
- return;
1617
- }
1618
- const size = getItemSize(ctx, state, id, anchor.index, item, true, true);
1619
- if (size === void 0) {
1620
- return;
1621
- }
1622
- const availableSpace = Math.max(0, state.scrollLength - size);
1623
- const desiredOffset = calculateOffsetForIndex(ctx, state, anchor.index) - ((_a3 = anchor.viewOffset) != null ? _a3 : 0) - ((_b = anchor.viewPosition) != null ? _b : 0) * availableSpace;
1624
- const contentSize = getContentSize(ctx);
1625
- const maxOffset = Math.max(0, contentSize - state.scrollLength);
1626
- const clampedDesiredOffset = Math.max(0, Math.min(desiredOffset, maxOffset));
1627
- const delta = clampedDesiredOffset - state.scroll;
1628
- if (Math.abs(delta) <= INITIAL_ANCHOR_TOLERANCE) {
1629
- const settledTicks = ((_c = anchor.settledTicks) != null ? _c : 0) + 1;
1630
- if (settledTicks >= INITIAL_ANCHOR_SETTLED_TICKS) {
1631
- state.initialAnchor = void 0;
1632
- } else {
1633
- anchor.settledTicks = settledTicks;
1634
- }
1635
- return;
1636
- }
1637
- if (((_d = anchor.attempts) != null ? _d : 0) >= INITIAL_ANCHOR_MAX_ATTEMPTS) {
1638
- state.initialAnchor = void 0;
1639
- return;
1640
- }
1641
- const lastDelta = anchor.lastDelta;
1642
- if (lastDelta !== void 0 && Math.abs(delta) >= Math.abs(lastDelta)) {
1643
- state.initialAnchor = void 0;
1644
- return;
1645
- }
1646
- Object.assign(anchor, {
1647
- attempts: ((_e = anchor.attempts) != null ? _e : 0) + 1,
1648
- lastDelta: delta,
1649
- settledTicks: 0
1650
- });
1651
- requestAdjust(ctx, state, delta);
1652
- }
1653
-
1654
1770
  // src/core/mvcp.ts
1655
- function prepareMVCP(ctx, state, dataChanged) {
1771
+ function prepareMVCP(ctx, dataChanged) {
1772
+ const state = ctx.state;
1656
1773
  const { idsInView, positions, props } = state;
1657
- const { maintainVisibleContentPosition } = props;
1658
- const scrollingTo = peek$(ctx, "scrollingTo");
1774
+ const {
1775
+ maintainVisibleContentPosition: { data: mvcpData, size: mvcpScroll, shouldRestorePosition }
1776
+ } = props;
1777
+ const scrollingTo = state.scrollingTo;
1659
1778
  let prevPosition;
1660
1779
  let targetId;
1661
1780
  const idsInViewWithPositions = [];
1662
1781
  const scrollTarget = scrollingTo == null ? void 0 : scrollingTo.index;
1663
- const shouldMVCP = !dataChanged || maintainVisibleContentPosition;
1782
+ const scrollingToViewPosition = scrollingTo == null ? void 0 : scrollingTo.viewPosition;
1783
+ const shouldMVCP = dataChanged ? mvcpData : mvcpScroll;
1664
1784
  const indexByKey = state.indexByKey;
1665
1785
  if (shouldMVCP) {
1666
1786
  if (scrollTarget !== void 0) {
1667
1787
  targetId = getId(state, scrollTarget);
1668
- } else if (idsInView.length > 0 && peek$(ctx, "containersDidLayout")) {
1788
+ } else if (idsInView.length > 0 && state.didContainersLayout) {
1669
1789
  if (dataChanged) {
1670
1790
  for (let i = 0; i < idsInView.length; i++) {
1671
1791
  const id = idsInView[i];
@@ -1682,10 +1802,18 @@ function prepareMVCP(ctx, state, dataChanged) {
1682
1802
  prevPosition = positions.get(targetId);
1683
1803
  }
1684
1804
  return () => {
1685
- let positionDiff;
1686
- if (dataChanged && targetId === void 0 && maintainVisibleContentPosition) {
1805
+ let positionDiff = 0;
1806
+ if (dataChanged && targetId === void 0 && mvcpData) {
1807
+ const data = state.props.data;
1687
1808
  for (let i = 0; i < idsInViewWithPositions.length; i++) {
1688
1809
  const { id, position } = idsInViewWithPositions[i];
1810
+ const index = indexByKey.get(id);
1811
+ if (index !== void 0 && shouldRestorePosition) {
1812
+ const item = data[index];
1813
+ if (item === void 0 || !shouldRestorePosition(item, index, data)) {
1814
+ continue;
1815
+ }
1816
+ }
1689
1817
  const newPosition = positions.get(id);
1690
1818
  if (newPosition !== void 0) {
1691
1819
  positionDiff = newPosition - position;
@@ -1708,16 +1836,28 @@ function prepareMVCP(ctx, state, dataChanged) {
1708
1836
  positionDiff = diff;
1709
1837
  }
1710
1838
  }
1711
- if (positionDiff !== void 0 && Math.abs(positionDiff) > 0.1) {
1712
- requestAdjust(ctx, state, positionDiff);
1839
+ if (scrollingToViewPosition && scrollingToViewPosition > 0) {
1840
+ const newSize = getItemSize(ctx, targetId, scrollTarget, state.props.data[scrollTarget]);
1841
+ const prevSize = scrollingTo == null ? void 0 : scrollingTo.itemSize;
1842
+ if (newSize !== void 0 && prevSize !== void 0 && newSize !== (scrollingTo == null ? void 0 : scrollingTo.itemSize)) {
1843
+ const diff = newSize - prevSize;
1844
+ if (diff !== 0) {
1845
+ positionDiff += (newSize - prevSize) * scrollingToViewPosition;
1846
+ scrollingTo.itemSize = newSize;
1847
+ }
1848
+ }
1849
+ }
1850
+ if (Math.abs(positionDiff) > 0.1) {
1851
+ requestAdjust(ctx, positionDiff);
1713
1852
  }
1714
1853
  };
1715
1854
  }
1716
1855
  }
1717
1856
 
1718
1857
  // src/core/prepareColumnStartState.ts
1719
- function prepareColumnStartState(ctx, state, startIndex, useAverageSize) {
1858
+ function prepareColumnStartState(ctx, startIndex, useAverageSize) {
1720
1859
  var _a3;
1860
+ const state = ctx.state;
1721
1861
  const numColumns = peek$(ctx, "numColumns");
1722
1862
  let rowStartIndex = startIndex;
1723
1863
  const columnAtStart = state.columns.get(state.idCache[startIndex]);
@@ -1732,7 +1872,7 @@ function prepareColumnStartState(ctx, state, startIndex, useAverageSize) {
1732
1872
  const prevId = state.idCache[prevIndex];
1733
1873
  const prevPosition = (_a3 = state.positions.get(prevId)) != null ? _a3 : 0;
1734
1874
  const prevRowStart = findRowStartIndex(state, numColumns, prevIndex);
1735
- const prevRowHeight = calculateRowMaxSize(ctx, state, prevRowStart, prevIndex, useAverageSize);
1875
+ const prevRowHeight = calculateRowMaxSize(ctx, prevRowStart, prevIndex, useAverageSize);
1736
1876
  currentRowTop = prevPosition + prevRowHeight;
1737
1877
  }
1738
1878
  return {
@@ -1755,7 +1895,8 @@ function findRowStartIndex(state, numColumns, index) {
1755
1895
  }
1756
1896
  return rowStart;
1757
1897
  }
1758
- function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1898
+ function calculateRowMaxSize(ctx, startIndex, endIndex, useAverageSize) {
1899
+ const state = ctx.state;
1759
1900
  if (endIndex < startIndex) {
1760
1901
  return 0;
1761
1902
  }
@@ -1769,7 +1910,7 @@ function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1769
1910
  continue;
1770
1911
  }
1771
1912
  const id = state.idCache[i];
1772
- const size = getItemSize(ctx, state, id, i, data[i], useAverageSize);
1913
+ const size = getItemSize(ctx, id, i, data[i], useAverageSize);
1773
1914
  if (size > maxSize) {
1774
1915
  maxSize = size;
1775
1916
  }
@@ -1778,22 +1919,46 @@ function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1778
1919
  }
1779
1920
 
1780
1921
  // src/core/updateTotalSize.ts
1781
- function updateTotalSize(ctx, state) {
1922
+ function updateTotalSize(ctx) {
1923
+ var _a3, _b, _c;
1924
+ const state = ctx.state;
1782
1925
  const {
1783
1926
  positions,
1784
1927
  props: { data }
1785
1928
  } = state;
1929
+ const numColumns = (_a3 = peek$(ctx, "numColumns")) != null ? _a3 : 1;
1786
1930
  if (data.length === 0) {
1787
- addTotalSize(ctx, state, null, 0);
1931
+ addTotalSize(ctx, null, 0);
1788
1932
  } else {
1789
1933
  const lastId = getId(state, data.length - 1);
1790
1934
  if (lastId !== void 0) {
1791
1935
  const lastPosition = positions.get(lastId);
1792
1936
  if (lastPosition !== void 0) {
1793
- const lastSize = getItemSize(ctx, state, lastId, data.length - 1, data[data.length - 1]);
1794
- if (lastSize !== void 0) {
1795
- const totalSize = lastPosition + lastSize;
1796
- addTotalSize(ctx, state, null, totalSize);
1937
+ if (numColumns > 1) {
1938
+ let rowStart = data.length - 1;
1939
+ while (rowStart > 0) {
1940
+ const rowId = (_b = state.idCache[rowStart]) != null ? _b : getId(state, rowStart);
1941
+ const column = state.columns.get(rowId);
1942
+ if (column === 1 || column === void 0) {
1943
+ break;
1944
+ }
1945
+ rowStart -= 1;
1946
+ }
1947
+ let maxSize = 0;
1948
+ for (let i = rowStart; i < data.length; i++) {
1949
+ const rowId = (_c = state.idCache[i]) != null ? _c : getId(state, i);
1950
+ const size = getItemSize(ctx, rowId, i, data[i]);
1951
+ if (size > maxSize) {
1952
+ maxSize = size;
1953
+ }
1954
+ }
1955
+ addTotalSize(ctx, null, lastPosition + maxSize);
1956
+ } else {
1957
+ const lastSize = getItemSize(ctx, lastId, data.length - 1, data[data.length - 1]);
1958
+ if (lastSize !== void 0) {
1959
+ const totalSize = lastPosition + lastSize;
1960
+ addTotalSize(ctx, null, totalSize);
1961
+ }
1797
1962
  }
1798
1963
  }
1799
1964
  }
@@ -1803,43 +1968,45 @@ function updateTotalSize(ctx, state) {
1803
1968
  // src/utils/getScrollVelocity.ts
1804
1969
  var getScrollVelocity = (state) => {
1805
1970
  const { scrollHistory } = state;
1806
- let velocity = 0;
1807
- if (scrollHistory.length >= 1) {
1808
- const newest = scrollHistory[scrollHistory.length - 1];
1809
- let oldest;
1810
- let start = 0;
1811
- const now = Date.now();
1812
- for (let i = 0; i < scrollHistory.length - 1; i++) {
1813
- const entry = scrollHistory[i];
1814
- const nextEntry = scrollHistory[i + 1];
1815
- if (i > 0) {
1816
- const prevEntry = scrollHistory[i - 1];
1817
- const prevDirection = entry.scroll - prevEntry.scroll;
1818
- const currentDirection = nextEntry.scroll - entry.scroll;
1819
- if (prevDirection > 0 && currentDirection < 0 || prevDirection < 0 && currentDirection > 0) {
1820
- start = i;
1821
- break;
1822
- }
1823
- }
1971
+ const newestIndex = scrollHistory.length - 1;
1972
+ if (newestIndex < 1) {
1973
+ return 0;
1974
+ }
1975
+ const newest = scrollHistory[newestIndex];
1976
+ const now = Date.now();
1977
+ let direction = 0;
1978
+ for (let i = newestIndex; i > 0; i--) {
1979
+ const delta = scrollHistory[i].scroll - scrollHistory[i - 1].scroll;
1980
+ if (delta !== 0) {
1981
+ direction = Math.sign(delta);
1982
+ break;
1824
1983
  }
1825
- for (let i = start; i < scrollHistory.length - 1; i++) {
1826
- const entry = scrollHistory[i];
1827
- if (now - entry.time <= 1e3) {
1828
- oldest = entry;
1829
- break;
1830
- }
1984
+ }
1985
+ if (direction === 0) {
1986
+ return 0;
1987
+ }
1988
+ let oldest = newest;
1989
+ for (let i = newestIndex - 1; i >= 0; i--) {
1990
+ const current = scrollHistory[i];
1991
+ const next = scrollHistory[i + 1];
1992
+ const delta = next.scroll - current.scroll;
1993
+ const deltaSign = Math.sign(delta);
1994
+ if (deltaSign !== 0 && deltaSign !== direction) {
1995
+ break;
1831
1996
  }
1832
- if (oldest && oldest !== newest) {
1833
- const scrollDiff = newest.scroll - oldest.scroll;
1834
- const timeDiff = newest.time - oldest.time;
1835
- velocity = timeDiff > 0 ? scrollDiff / timeDiff : 0;
1997
+ if (now - current.time > 1e3) {
1998
+ break;
1836
1999
  }
2000
+ oldest = current;
1837
2001
  }
1838
- return velocity;
2002
+ const scrollDiff = newest.scroll - oldest.scroll;
2003
+ const timeDiff = newest.time - oldest.time;
2004
+ return timeDiff > 0 ? scrollDiff / timeDiff : 0;
1839
2005
  };
1840
2006
 
1841
2007
  // src/utils/updateSnapToOffsets.ts
1842
- function updateSnapToOffsets(ctx, state) {
2008
+ function updateSnapToOffsets(ctx) {
2009
+ const state = ctx.state;
1843
2010
  const {
1844
2011
  positions,
1845
2012
  props: { snapToIndices }
@@ -1854,39 +2021,46 @@ function updateSnapToOffsets(ctx, state) {
1854
2021
  }
1855
2022
 
1856
2023
  // src/core/updateItemPositions.ts
1857
- function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP } = {
2024
+ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP } = {
1858
2025
  doMVCP: false,
1859
2026
  forceFullUpdate: false,
1860
2027
  scrollBottomBuffered: -1,
1861
2028
  startIndex: 0
1862
2029
  }) {
1863
- var _a3, _b, _c, _d, _e;
2030
+ var _a3, _b, _c, _d, _e, _f;
2031
+ const state = ctx.state;
1864
2032
  const {
1865
2033
  columns,
2034
+ columnSpans,
1866
2035
  indexByKey,
1867
2036
  positions,
1868
2037
  idCache,
1869
2038
  sizesKnown,
1870
- props: { getEstimatedItemSize, snapToIndices, enableAverages }
2039
+ props: { data, getEstimatedItemSize, overrideItemLayout, snapToIndices },
2040
+ scrollingTo
1871
2041
  } = state;
1872
- const data = state.props.data;
1873
2042
  const dataLength = data.length;
1874
- const numColumns = peek$(ctx, "numColumns");
1875
- const scrollingTo = peek$(ctx, "scrollingTo");
2043
+ const numColumns = (_a3 = peek$(ctx, "numColumns")) != null ? _a3 : 1;
1876
2044
  const hasColumns = numColumns > 1;
1877
2045
  const indexByKeyForChecking = IS_DEV ? /* @__PURE__ */ new Map() : void 0;
1878
- const shouldOptimize = !forceFullUpdate && !dataChanged && Math.abs(getScrollVelocity(state)) > 0;
2046
+ const extraData = peek$(ctx, "extraData");
2047
+ const layoutConfig = overrideItemLayout ? { span: 1 } : void 0;
2048
+ const lastScrollDelta = state.lastScrollDelta;
2049
+ const velocity = getScrollVelocity(state);
2050
+ const shouldOptimize = !forceFullUpdate && !dataChanged && (Math.abs(velocity) > 0 || state.scrollLength > 0 && lastScrollDelta > state.scrollLength);
1879
2051
  const maxVisibleArea = scrollBottomBuffered + 1e3;
1880
- const useAverageSize = enableAverages && !getEstimatedItemSize;
1881
- const preferCachedSize = !doMVCP || dataChanged || state.scrollAdjustHandler.getAdjust() !== 0 || ((_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0) !== 0;
2052
+ const useAverageSize = !getEstimatedItemSize;
2053
+ const preferCachedSize = !doMVCP || dataChanged || state.scrollAdjustHandler.getAdjust() !== 0 || ((_b = peek$(ctx, "scrollAdjustPending")) != null ? _b : 0) !== 0;
1882
2054
  let currentRowTop = 0;
1883
2055
  let column = 1;
1884
2056
  let maxSizeInRow = 0;
2057
+ if (dataChanged) {
2058
+ columnSpans.clear();
2059
+ }
1885
2060
  if (startIndex > 0) {
1886
2061
  if (hasColumns) {
1887
2062
  const { startIndex: processedStartIndex, currentRowTop: initialRowTop } = prepareColumnStartState(
1888
2063
  ctx,
1889
- state,
1890
2064
  startIndex,
1891
2065
  useAverageSize
1892
2066
  );
@@ -1895,8 +2069,8 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1895
2069
  } else if (startIndex < dataLength) {
1896
2070
  const prevIndex = startIndex - 1;
1897
2071
  const prevId = getId(state, prevIndex);
1898
- const prevPosition = (_b = positions.get(prevId)) != null ? _b : 0;
1899
- const prevSize = (_c = sizesKnown.get(prevId)) != null ? _c : getItemSize(ctx, state, prevId, prevIndex, data[prevIndex], useAverageSize, preferCachedSize);
2072
+ const prevPosition = (_c = positions.get(prevId)) != null ? _c : 0;
2073
+ const prevSize = (_d = sizesKnown.get(prevId)) != null ? _d : getItemSize(ctx, prevId, prevIndex, data[prevIndex], useAverageSize, preferCachedSize);
1900
2074
  currentRowTop = prevPosition + prevSize;
1901
2075
  }
1902
2076
  }
@@ -1912,8 +2086,22 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1912
2086
  const itemsPerRow = hasColumns ? numColumns : 1;
1913
2087
  breakAt = i + itemsPerRow + 10;
1914
2088
  }
1915
- const id = (_d = idCache[i]) != null ? _d : getId(state, i);
1916
- const size = (_e = sizesKnown.get(id)) != null ? _e : getItemSize(ctx, state, id, i, data[i], useAverageSize, preferCachedSize);
2089
+ const id = (_e = idCache[i]) != null ? _e : getId(state, i);
2090
+ let span = 1;
2091
+ if (hasColumns && overrideItemLayout && layoutConfig) {
2092
+ layoutConfig.span = 1;
2093
+ overrideItemLayout(layoutConfig, data[i], i, numColumns, extraData);
2094
+ const requestedSpan = layoutConfig.span;
2095
+ if (requestedSpan !== void 0 && Number.isFinite(requestedSpan)) {
2096
+ span = Math.max(1, Math.min(numColumns, Math.round(requestedSpan)));
2097
+ }
2098
+ }
2099
+ if (hasColumns && column + span - 1 > numColumns) {
2100
+ currentRowTop += maxSizeInRow;
2101
+ column = 1;
2102
+ maxSizeInRow = 0;
2103
+ }
2104
+ const size = (_f = sizesKnown.get(id)) != null ? _f : getItemSize(ctx, id, i, data[i], useAverageSize, preferCachedSize);
1917
2105
  if (IS_DEV && needsIndexByKey) {
1918
2106
  if (indexByKeyForChecking.has(id)) {
1919
2107
  console.error(
@@ -1922,16 +2110,20 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1922
2110
  }
1923
2111
  indexByKeyForChecking.set(id, i);
1924
2112
  }
1925
- positions.set(id, currentRowTop);
2113
+ if (currentRowTop !== positions.get(id)) {
2114
+ positions.set(id, currentRowTop);
2115
+ notifyPosition$(ctx, id, currentRowTop);
2116
+ }
1926
2117
  if (needsIndexByKey) {
1927
2118
  indexByKey.set(id, i);
1928
2119
  }
1929
2120
  columns.set(id, column);
2121
+ columnSpans.set(id, span);
1930
2122
  if (hasColumns) {
1931
2123
  if (size > maxSizeInRow) {
1932
2124
  maxSizeInRow = size;
1933
2125
  }
1934
- column++;
2126
+ column += span;
1935
2127
  if (column > numColumns) {
1936
2128
  currentRowTop += maxSizeInRow;
1937
2129
  column = 1;
@@ -1942,10 +2134,10 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1942
2134
  }
1943
2135
  }
1944
2136
  if (!didBreakEarly) {
1945
- updateTotalSize(ctx, state);
2137
+ updateTotalSize(ctx);
1946
2138
  }
1947
2139
  if (snapToIndices) {
1948
- updateSnapToOffsets(ctx, state);
2140
+ updateSnapToOffsets(ctx);
1949
2141
  }
1950
2142
  }
1951
2143
 
@@ -2023,7 +2215,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
2023
2215
  if (previousViewableItems) {
2024
2216
  for (const viewToken of previousViewableItems) {
2025
2217
  const containerId = findContainerId(ctx, viewToken.key);
2026
- if (!isViewable(
2218
+ if (!checkIsViewable(
2027
2219
  state,
2028
2220
  ctx,
2029
2221
  viewabilityConfig,
@@ -2044,7 +2236,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
2044
2236
  if (item) {
2045
2237
  const key = getId(state, i);
2046
2238
  const containerId = findContainerId(ctx, key);
2047
- if (isViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, i)) {
2239
+ if (checkIsViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, i)) {
2048
2240
  const viewToken = {
2049
2241
  containerId,
2050
2242
  index: i,
@@ -2104,11 +2296,11 @@ function computeViewability(state, ctx, viewabilityConfig, containerId, key, scr
2104
2296
  const percentVisible = size ? isEntirelyVisible ? 100 : 100 * (sizeVisible / size) : 0;
2105
2297
  const percentOfScroller = size ? 100 * (sizeVisible / scrollSize) : 0;
2106
2298
  const percent = isEntirelyVisible ? 100 : viewAreaMode ? percentOfScroller : percentVisible;
2107
- const isViewable2 = percent >= viewablePercentThreshold;
2299
+ const isViewable = percent >= viewablePercentThreshold;
2108
2300
  const value = {
2109
2301
  containerId,
2110
2302
  index,
2111
- isViewable: isViewable2,
2303
+ isViewable,
2112
2304
  item,
2113
2305
  key,
2114
2306
  percentOfScroller,
@@ -2127,8 +2319,11 @@ function computeViewability(state, ctx, viewabilityConfig, containerId, key, scr
2127
2319
  }
2128
2320
  return value;
2129
2321
  }
2130
- function isViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
2131
- const value = ctx.mapViewabilityAmountValues.get(containerId) || computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index);
2322
+ function checkIsViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
2323
+ let value = ctx.mapViewabilityAmountValues.get(containerId);
2324
+ if (!value || value.key !== key) {
2325
+ value = computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index);
2326
+ }
2132
2327
  return value.isViewable;
2133
2328
  }
2134
2329
  function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
@@ -2156,8 +2351,9 @@ function checkAllSizesKnown(state) {
2156
2351
  }
2157
2352
 
2158
2353
  // src/utils/findAvailableContainers.ts
2159
- function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers) {
2354
+ function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers) {
2160
2355
  const numContainers = peek$(ctx, "numContainers");
2356
+ const state = ctx.state;
2161
2357
  const { stickyContainerPool, containerItemTypes } = state;
2162
2358
  const result = [];
2163
2359
  const availableContainers = [];
@@ -2277,21 +2473,26 @@ function comparatorByDistance(a, b) {
2277
2473
  }
2278
2474
 
2279
2475
  // src/core/scrollToIndex.ts
2280
- function scrollToIndex(ctx, state, { index, viewOffset = 0, animated = true, viewPosition }) {
2281
- if (index >= state.props.data.length) {
2282
- index = state.props.data.length - 1;
2476
+ function scrollToIndex(ctx, { index, viewOffset = 0, animated = true, viewPosition }) {
2477
+ const state = ctx.state;
2478
+ const { data } = state.props;
2479
+ if (index >= data.length) {
2480
+ index = data.length - 1;
2283
2481
  } else if (index < 0) {
2284
2482
  index = 0;
2285
2483
  }
2286
- const firstIndexOffset = calculateOffsetForIndex(ctx, state, index);
2287
- const isLast = index === state.props.data.length - 1;
2484
+ const firstIndexOffset = calculateOffsetForIndex(ctx, index);
2485
+ const isLast = index === data.length - 1;
2288
2486
  if (isLast && viewPosition === void 0) {
2289
2487
  viewPosition = 1;
2290
2488
  }
2291
2489
  state.scrollForNextCalculateItemsInView = void 0;
2292
- scrollTo(ctx, state, {
2490
+ const targetId = getId(state, index);
2491
+ const itemSize = getItemSize(ctx, targetId, index, state.props.data[index]);
2492
+ scrollTo(ctx, {
2293
2493
  animated,
2294
2494
  index,
2495
+ itemSize,
2295
2496
  offset: firstIndexOffset,
2296
2497
  viewOffset,
2297
2498
  viewPosition: viewPosition != null ? viewPosition : 0
@@ -2299,23 +2500,28 @@ function scrollToIndex(ctx, state, { index, viewOffset = 0, animated = true, vie
2299
2500
  }
2300
2501
 
2301
2502
  // src/utils/setDidLayout.ts
2302
- function setDidLayout(ctx, state) {
2503
+ function setDidLayout(ctx) {
2504
+ const state = ctx.state;
2303
2505
  const {
2304
2506
  loadStartTime,
2305
2507
  initialScroll,
2306
2508
  props: { onLoad }
2307
2509
  } = state;
2308
2510
  state.queuedInitialLayout = true;
2309
- checkAtBottom(ctx, state);
2511
+ checkAtBottom(ctx);
2310
2512
  const setIt = () => {
2311
- set$(ctx, "containersDidLayout", true);
2513
+ setInitialRenderState(ctx, { didLayout: true });
2312
2514
  if (onLoad) {
2313
2515
  onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
2314
2516
  }
2315
2517
  };
2316
- {
2317
- setIt();
2518
+ if ((initialScroll == null ? void 0 : initialScroll.index) !== void 0) {
2519
+ const target = initialScroll;
2520
+ const runScroll = () => scrollToIndex(ctx, { ...target, animated: false });
2521
+ runScroll();
2522
+ requestAnimationFrame(runScroll);
2318
2523
  }
2524
+ setIt();
2319
2525
  }
2320
2526
 
2321
2527
  // src/core/calculateItemsInView.ts
@@ -2333,36 +2539,40 @@ function findCurrentStickyIndex(stickyArray, scroll, state) {
2333
2539
  }
2334
2540
  return -1;
2335
2541
  }
2336
- function getActiveStickyIndices(ctx, state, stickyHeaderIndices) {
2542
+ function getActiveStickyIndices(ctx, stickyHeaderIndices) {
2543
+ const state = ctx.state;
2337
2544
  return new Set(
2338
2545
  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))
2339
2546
  );
2340
2547
  }
2341
- function handleStickyActivation(ctx, state, stickyHeaderIndices, stickyArray, currentStickyIdx, needNewContainers, startBuffered, endBuffered) {
2548
+ function handleStickyActivation(ctx, stickyHeaderIndices, stickyArray, currentStickyIdx, needNewContainers, needNewContainersSet, startBuffered, endBuffered) {
2342
2549
  var _a3;
2343
- const activeIndices = getActiveStickyIndices(ctx, state, stickyHeaderIndices);
2344
- state.activeStickyIndex = currentStickyIdx >= 0 ? stickyArray[currentStickyIdx] : void 0;
2550
+ const state = ctx.state;
2551
+ const activeIndices = getActiveStickyIndices(ctx, stickyHeaderIndices);
2552
+ set$(ctx, "activeStickyIndex", currentStickyIdx >= 0 ? stickyArray[currentStickyIdx] : -1);
2345
2553
  for (let offset = 0; offset <= 1; offset++) {
2346
2554
  const idx = currentStickyIdx - offset;
2347
2555
  if (idx < 0 || activeIndices.has(stickyArray[idx])) continue;
2348
2556
  const stickyIndex = stickyArray[idx];
2349
2557
  const stickyId = (_a3 = state.idCache[stickyIndex]) != null ? _a3 : getId(state, stickyIndex);
2350
- if (stickyId && !state.containerItemKeys.has(stickyId) && (stickyIndex < startBuffered || stickyIndex > endBuffered)) {
2558
+ if (stickyId && !state.containerItemKeys.has(stickyId) && (stickyIndex < startBuffered || stickyIndex > endBuffered) && !needNewContainersSet.has(stickyIndex)) {
2559
+ needNewContainersSet.add(stickyIndex);
2351
2560
  needNewContainers.push(stickyIndex);
2352
2561
  }
2353
2562
  }
2354
2563
  }
2355
- function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, currentStickyIdx, pendingRemoval) {
2564
+ function handleStickyRecycling(ctx, stickyArray, scroll, scrollBuffer, currentStickyIdx, pendingRemoval, alwaysRenderIndicesSet) {
2356
2565
  var _a3, _b, _c;
2566
+ const state = ctx.state;
2357
2567
  for (const containerIndex of state.stickyContainerPool) {
2358
2568
  const itemKey = peek$(ctx, `containerItemKey${containerIndex}`);
2359
2569
  const itemIndex = itemKey ? state.indexByKey.get(itemKey) : void 0;
2360
2570
  if (itemIndex === void 0) continue;
2571
+ if (alwaysRenderIndicesSet.has(itemIndex)) continue;
2361
2572
  const arrayIdx = stickyArray.indexOf(itemIndex);
2362
2573
  if (arrayIdx === -1) {
2363
2574
  state.stickyContainerPool.delete(containerIndex);
2364
2575
  set$(ctx, `containerSticky${containerIndex}`, false);
2365
- set$(ctx, `containerStickyOffset${containerIndex}`, void 0);
2366
2576
  continue;
2367
2577
  }
2368
2578
  const isRecentSticky = arrayIdx >= currentStickyIdx - 1 && arrayIdx <= currentStickyIdx + 1;
@@ -2377,7 +2587,7 @@ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, cu
2377
2587
  const currentId = (_b = state.idCache[itemIndex]) != null ? _b : getId(state, itemIndex);
2378
2588
  if (currentId) {
2379
2589
  const currentPos = state.positions.get(currentId);
2380
- const currentSize = (_c = state.sizes.get(currentId)) != null ? _c : getItemSize(ctx, state, currentId, itemIndex, state.props.data[itemIndex]);
2590
+ const currentSize = (_c = state.sizes.get(currentId)) != null ? _c : getItemSize(ctx, currentId, itemIndex, state.props.data[itemIndex]);
2381
2591
  shouldRecycle = currentPos !== void 0 && scroll > currentPos + currentSize + scrollBuffer * 3;
2382
2592
  }
2383
2593
  }
@@ -2386,11 +2596,13 @@ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, cu
2386
2596
  }
2387
2597
  }
2388
2598
  }
2389
- function calculateItemsInView(ctx, state, params = {}) {
2599
+ function calculateItemsInView(ctx, params = {}) {
2600
+ const state = ctx.state;
2390
2601
  reactDom.unstable_batchedUpdates(() => {
2391
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j;
2602
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
2392
2603
  const {
2393
2604
  columns,
2605
+ columnSpans,
2394
2606
  containerItemKeys,
2395
2607
  enableScrollForNextCalculateItemsInView,
2396
2608
  idCache,
@@ -2398,7 +2610,15 @@ function calculateItemsInView(ctx, state, params = {}) {
2398
2610
  initialScroll,
2399
2611
  minIndexSizeChanged,
2400
2612
  positions,
2401
- props: { getItemType, itemsAreEqual, keyExtractor, onStickyHeaderChange, scrollBuffer },
2613
+ props: {
2614
+ alwaysRenderIndicesArr,
2615
+ alwaysRenderIndicesSet,
2616
+ getItemType,
2617
+ itemsAreEqual,
2618
+ keyExtractor,
2619
+ onStickyHeaderChange,
2620
+ scrollBuffer
2621
+ },
2402
2622
  scrollForNextCalculateItemsInView,
2403
2623
  scrollLength,
2404
2624
  sizes,
@@ -2408,11 +2628,10 @@ function calculateItemsInView(ctx, state, params = {}) {
2408
2628
  const { data } = state.props;
2409
2629
  const stickyIndicesArr = state.props.stickyIndicesArr || [];
2410
2630
  const stickyIndicesSet = state.props.stickyIndicesSet || /* @__PURE__ */ new Set();
2631
+ const alwaysRenderArr = alwaysRenderIndicesArr || [];
2632
+ const alwaysRenderSet = alwaysRenderIndicesSet || /* @__PURE__ */ new Set();
2411
2633
  const prevNumContainers = peek$(ctx, "numContainers");
2412
2634
  if (!data || scrollLength === 0 || !prevNumContainers) {
2413
- if (state.initialAnchor) {
2414
- ensureInitialAnchor(ctx, state);
2415
- }
2416
2635
  return;
2417
2636
  }
2418
2637
  const totalSize = getContentSize(ctx);
@@ -2426,15 +2645,14 @@ function calculateItemsInView(ctx, state, params = {}) {
2426
2645
  if (!queuedInitialLayout && initialScroll) {
2427
2646
  const updatedOffset = calculateOffsetWithOffsetPosition(
2428
2647
  ctx,
2429
- state,
2430
- calculateOffsetForIndex(ctx, state, initialScroll.index),
2648
+ calculateOffsetForIndex(ctx, initialScroll.index),
2431
2649
  initialScroll
2432
2650
  );
2433
2651
  scrollState = updatedOffset;
2434
2652
  }
2435
2653
  const scrollAdjustPending = (_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0;
2436
2654
  const scrollAdjustPad = scrollAdjustPending - topPad;
2437
- let scroll = scrollState + scrollExtra + scrollAdjustPad;
2655
+ let scroll = Math.round(scrollState + scrollExtra + scrollAdjustPad);
2438
2656
  if (scroll + scrollLength > totalSize) {
2439
2657
  scroll = Math.max(0, totalSize - scrollLength);
2440
2658
  }
@@ -2442,11 +2660,12 @@ function calculateItemsInView(ctx, state, params = {}) {
2442
2660
  set$(ctx, "debugRawScroll", scrollState);
2443
2661
  set$(ctx, "debugComputedScroll", scroll);
2444
2662
  }
2445
- const previousStickyIndex = state.activeStickyIndex;
2663
+ const previousStickyIndex = peek$(ctx, "activeStickyIndex");
2446
2664
  const currentStickyIdx = stickyIndicesArr.length > 0 ? findCurrentStickyIndex(stickyIndicesArr, scroll, state) : -1;
2447
- const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyIndicesArr[currentStickyIdx] : void 0;
2448
- state.activeStickyIndex = nextActiveStickyIndex;
2449
- set$(ctx, "activeStickyIndex", nextActiveStickyIndex);
2665
+ const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyIndicesArr[currentStickyIdx] : -1;
2666
+ if (currentStickyIdx >= 0 || previousStickyIndex >= 0) {
2667
+ set$(ctx, "activeStickyIndex", nextActiveStickyIndex);
2668
+ }
2450
2669
  let scrollBufferTop = scrollBuffer;
2451
2670
  let scrollBufferBottom = scrollBuffer;
2452
2671
  if (speed > 0 || speed === 0 && scroll < Math.max(50, scrollBuffer)) {
@@ -2459,23 +2678,22 @@ function calculateItemsInView(ctx, state, params = {}) {
2459
2678
  const scrollTopBuffered = scroll - scrollBufferTop;
2460
2679
  const scrollBottom = scroll + scrollLength + (scroll < 0 ? -scroll : 0);
2461
2680
  const scrollBottomBuffered = scrollBottom + scrollBufferBottom;
2462
- if (!dataChanged && scrollForNextCalculateItemsInView) {
2681
+ if (!dataChanged && !forceFullItemPositions && scrollForNextCalculateItemsInView) {
2463
2682
  const { top, bottom } = scrollForNextCalculateItemsInView;
2464
- if (scrollTopBuffered > top && scrollBottomBuffered < bottom) {
2465
- if (state.initialAnchor) {
2466
- ensureInitialAnchor(ctx, state);
2467
- }
2683
+ if (top === null && bottom === null) {
2684
+ state.scrollForNextCalculateItemsInView = void 0;
2685
+ } else if ((top === null || scrollTopBuffered > top) && (bottom === null || scrollBottomBuffered < bottom)) {
2468
2686
  return;
2469
2687
  }
2470
2688
  }
2471
- const checkMVCP = doMVCP ? prepareMVCP(ctx, state, dataChanged) : void 0;
2689
+ const checkMVCP = doMVCP ? prepareMVCP(ctx, dataChanged) : void 0;
2472
2690
  if (dataChanged) {
2473
2691
  indexByKey.clear();
2474
2692
  idCache.length = 0;
2475
2693
  positions.clear();
2476
2694
  }
2477
- const startIndex = dataChanged ? 0 : (_b = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _b : 0;
2478
- updateItemPositions(ctx, state, dataChanged, {
2695
+ const startIndex = forceFullItemPositions || dataChanged ? 0 : (_b = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _b : 0;
2696
+ updateItemPositions(ctx, dataChanged, {
2479
2697
  doMVCP,
2480
2698
  forceFullUpdate: !!forceFullItemPositions,
2481
2699
  scrollBottomBuffered,
@@ -2494,17 +2712,23 @@ function calculateItemsInView(ctx, state, params = {}) {
2494
2712
  for (let i = loopStart; i >= 0; i--) {
2495
2713
  const id = (_c = idCache[i]) != null ? _c : getId(state, i);
2496
2714
  const top = positions.get(id);
2497
- const size = (_d = sizes.get(id)) != null ? _d : getItemSize(ctx, state, id, i, data[i]);
2715
+ const size = (_d = sizes.get(id)) != null ? _d : getItemSize(ctx, id, i, data[i]);
2498
2716
  const bottom = top + size;
2499
- if (bottom > scroll - scrollBuffer) {
2717
+ if (bottom > scroll - scrollBufferTop) {
2500
2718
  loopStart = i;
2501
2719
  } else {
2502
2720
  break;
2503
2721
  }
2504
2722
  }
2505
- const loopStartMod = loopStart % numColumns;
2506
- if (loopStartMod > 0) {
2507
- loopStart -= loopStartMod;
2723
+ if (numColumns > 1) {
2724
+ while (loopStart > 0) {
2725
+ const loopId = (_e = idCache[loopStart]) != null ? _e : getId(state, loopStart);
2726
+ const loopColumn = columns.get(loopId);
2727
+ if (loopColumn === 1 || loopColumn === void 0) {
2728
+ break;
2729
+ }
2730
+ loopStart -= 1;
2731
+ }
2508
2732
  }
2509
2733
  let foundEnd = false;
2510
2734
  let nextTop;
@@ -2520,8 +2744,8 @@ function calculateItemsInView(ctx, state, params = {}) {
2520
2744
  let firstFullyOnScreenIndex;
2521
2745
  const dataLength = data.length;
2522
2746
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
2523
- const id = (_e = idCache[i]) != null ? _e : getId(state, i);
2524
- const size = (_f = sizes.get(id)) != null ? _f : getItemSize(ctx, state, id, i, data[i]);
2747
+ const id = (_f = idCache[i]) != null ? _f : getId(state, i);
2748
+ const size = (_g = sizes.get(id)) != null ? _g : getItemSize(ctx, id, i, data[i]);
2525
2749
  const top = positions.get(id);
2526
2750
  if (!foundEnd) {
2527
2751
  if (startNoBuffer === null && top + size > scroll) {
@@ -2533,7 +2757,11 @@ function calculateItemsInView(ctx, state, params = {}) {
2533
2757
  if (startBuffered === null && top + size > scrollTopBuffered) {
2534
2758
  startBuffered = i;
2535
2759
  startBufferedId = id;
2536
- nextTop = top;
2760
+ if (scrollTopBuffered < 0) {
2761
+ nextTop = null;
2762
+ } else {
2763
+ nextTop = top;
2764
+ }
2537
2765
  }
2538
2766
  if (startNoBuffer !== null) {
2539
2767
  if (top <= scrollBottom) {
@@ -2541,7 +2769,11 @@ function calculateItemsInView(ctx, state, params = {}) {
2541
2769
  }
2542
2770
  if (top <= scrollBottomBuffered) {
2543
2771
  endBuffered = i;
2544
- nextBottom = top + size;
2772
+ if (scrollBottomBuffered > totalSize) {
2773
+ nextBottom = null;
2774
+ } else {
2775
+ nextBottom = top + size;
2776
+ }
2545
2777
  } else {
2546
2778
  foundEnd = true;
2547
2779
  }
@@ -2550,7 +2782,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2550
2782
  }
2551
2783
  const idsInView = [];
2552
2784
  for (let i = firstFullyOnScreenIndex; i <= endNoBuffer; i++) {
2553
- const id = (_g = idCache[i]) != null ? _g : getId(state, i);
2785
+ const id = (_h = idCache[i]) != null ? _h : getId(state, i);
2554
2786
  idsInView.push(id);
2555
2787
  }
2556
2788
  Object.assign(state, {
@@ -2563,12 +2795,12 @@ function calculateItemsInView(ctx, state, params = {}) {
2563
2795
  startNoBuffer
2564
2796
  });
2565
2797
  if (enableScrollForNextCalculateItemsInView && nextTop !== void 0 && nextBottom !== void 0) {
2566
- state.scrollForNextCalculateItemsInView = nextTop !== void 0 && nextBottom !== void 0 ? {
2798
+ state.scrollForNextCalculateItemsInView = isNullOrUndefined(nextTop) && isNullOrUndefined(nextBottom) ? void 0 : {
2567
2799
  bottom: nextBottom,
2568
2800
  top: nextTop
2569
- } : void 0;
2801
+ };
2570
2802
  }
2571
- const numContainers = peek$(ctx, "numContainers");
2803
+ let numContainers = prevNumContainers;
2572
2804
  const pendingRemoval = [];
2573
2805
  if (dataChanged) {
2574
2806
  for (let i = 0; i < numContainers; i++) {
@@ -2579,37 +2811,46 @@ function calculateItemsInView(ctx, state, params = {}) {
2579
2811
  }
2580
2812
  }
2581
2813
  if (startBuffered !== null && endBuffered !== null) {
2582
- let numContainers2 = prevNumContainers;
2583
2814
  const needNewContainers = [];
2815
+ const needNewContainersSet = /* @__PURE__ */ new Set();
2584
2816
  for (let i = startBuffered; i <= endBuffered; i++) {
2585
- const id = (_h = idCache[i]) != null ? _h : getId(state, i);
2817
+ const id = (_i = idCache[i]) != null ? _i : getId(state, i);
2586
2818
  if (!containerItemKeys.has(id)) {
2819
+ needNewContainersSet.add(i);
2587
2820
  needNewContainers.push(i);
2588
2821
  }
2589
2822
  }
2823
+ if (alwaysRenderArr.length > 0) {
2824
+ for (const index of alwaysRenderArr) {
2825
+ if (index < 0 || index >= dataLength) continue;
2826
+ const id = (_j = idCache[index]) != null ? _j : getId(state, index);
2827
+ if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
2828
+ needNewContainersSet.add(index);
2829
+ needNewContainers.push(index);
2830
+ }
2831
+ }
2832
+ }
2590
2833
  if (stickyIndicesArr.length > 0) {
2591
2834
  handleStickyActivation(
2592
2835
  ctx,
2593
- state,
2594
2836
  stickyIndicesSet,
2595
2837
  stickyIndicesArr,
2596
2838
  currentStickyIdx,
2597
2839
  needNewContainers,
2840
+ needNewContainersSet,
2598
2841
  startBuffered,
2599
2842
  endBuffered
2600
2843
  );
2601
- } else {
2602
- state.activeStickyIndex = void 0;
2603
- set$(ctx, "activeStickyIndex", void 0);
2844
+ } else if (previousStickyIndex !== -1) {
2845
+ set$(ctx, "activeStickyIndex", -1);
2604
2846
  }
2605
2847
  if (needNewContainers.length > 0) {
2606
2848
  const requiredItemTypes = getItemType ? needNewContainers.map((i) => {
2607
2849
  const itemType = getItemType(data[i], i);
2608
- return itemType ? String(itemType) : "";
2850
+ return itemType !== void 0 ? String(itemType) : "";
2609
2851
  }) : void 0;
2610
2852
  const availableContainers = findAvailableContainers(
2611
2853
  ctx,
2612
- state,
2613
2854
  needNewContainers.length,
2614
2855
  startBuffered,
2615
2856
  endBuffered,
@@ -2620,7 +2861,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2620
2861
  for (let idx = 0; idx < needNewContainers.length; idx++) {
2621
2862
  const i = needNewContainers[idx];
2622
2863
  const containerIndex = availableContainers[idx];
2623
- const id = (_i = idCache[i]) != null ? _i : getId(state, i);
2864
+ const id = (_k = idCache[i]) != null ? _k : getId(state, i);
2624
2865
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
2625
2866
  if (oldKey && oldKey !== id) {
2626
2867
  containerItemKeys.delete(oldKey);
@@ -2630,30 +2871,55 @@ function calculateItemsInView(ctx, state, params = {}) {
2630
2871
  if (requiredItemTypes) {
2631
2872
  state.containerItemTypes.set(containerIndex, requiredItemTypes[idx]);
2632
2873
  }
2633
- containerItemKeys.add(id);
2634
- if (stickyIndicesSet.has(i)) {
2635
- set$(ctx, `containerSticky${containerIndex}`, true);
2636
- const topPadding = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
2637
- set$(ctx, `containerStickyOffset${containerIndex}`, topPadding);
2874
+ containerItemKeys.set(id, containerIndex);
2875
+ const containerSticky = `containerSticky${containerIndex}`;
2876
+ const isSticky = stickyIndicesSet.has(i);
2877
+ const isAlwaysRender = alwaysRenderSet.has(i);
2878
+ if (isSticky) {
2879
+ set$(ctx, containerSticky, true);
2638
2880
  state.stickyContainerPool.add(containerIndex);
2639
2881
  } else {
2640
- set$(ctx, `containerSticky${containerIndex}`, false);
2641
- state.stickyContainerPool.delete(containerIndex);
2882
+ if (peek$(ctx, containerSticky)) {
2883
+ set$(ctx, containerSticky, false);
2884
+ }
2885
+ if (isAlwaysRender) {
2886
+ state.stickyContainerPool.add(containerIndex);
2887
+ } else if (state.stickyContainerPool.has(containerIndex)) {
2888
+ state.stickyContainerPool.delete(containerIndex);
2889
+ }
2890
+ }
2891
+ if (containerIndex >= numContainers) {
2892
+ numContainers = containerIndex + 1;
2642
2893
  }
2643
- if (containerIndex >= numContainers2) {
2644
- numContainers2 = containerIndex + 1;
2894
+ }
2895
+ if (numContainers !== prevNumContainers) {
2896
+ set$(ctx, "numContainers", numContainers);
2897
+ if (numContainers > peek$(ctx, "numContainersPooled")) {
2898
+ set$(ctx, "numContainersPooled", Math.ceil(numContainers * 1.5));
2645
2899
  }
2646
2900
  }
2647
- if (numContainers2 !== prevNumContainers) {
2648
- set$(ctx, "numContainers", numContainers2);
2649
- if (numContainers2 > peek$(ctx, "numContainersPooled")) {
2650
- set$(ctx, "numContainersPooled", Math.ceil(numContainers2 * 1.5));
2901
+ }
2902
+ if (alwaysRenderArr.length > 0) {
2903
+ for (const index of alwaysRenderArr) {
2904
+ if (index < 0 || index >= dataLength) continue;
2905
+ const id = (_l = idCache[index]) != null ? _l : getId(state, index);
2906
+ const containerIndex = containerItemKeys.get(id);
2907
+ if (containerIndex !== void 0) {
2908
+ state.stickyContainerPool.add(containerIndex);
2651
2909
  }
2652
2910
  }
2653
2911
  }
2654
2912
  }
2655
- if (stickyIndicesArr.length > 0) {
2656
- handleStickyRecycling(ctx, state, stickyIndicesArr, scroll, scrollBuffer, currentStickyIdx, pendingRemoval);
2913
+ if (state.stickyContainerPool.size > 0) {
2914
+ handleStickyRecycling(
2915
+ ctx,
2916
+ stickyIndicesArr,
2917
+ scroll,
2918
+ scrollBuffer,
2919
+ currentStickyIdx,
2920
+ pendingRemoval,
2921
+ alwaysRenderSet
2922
+ );
2657
2923
  }
2658
2924
  let didChangePositions = false;
2659
2925
  for (let i = 0; i < numContainers; i++) {
@@ -2665,26 +2931,28 @@ function calculateItemsInView(ctx, state, params = {}) {
2665
2931
  state.containerItemTypes.delete(i);
2666
2932
  if (state.stickyContainerPool.has(i)) {
2667
2933
  set$(ctx, `containerSticky${i}`, false);
2668
- set$(ctx, `containerStickyOffset${i}`, void 0);
2669
2934
  state.stickyContainerPool.delete(i);
2670
2935
  }
2671
2936
  set$(ctx, `containerItemKey${i}`, void 0);
2672
2937
  set$(ctx, `containerItemData${i}`, void 0);
2673
2938
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
2674
2939
  set$(ctx, `containerColumn${i}`, -1);
2940
+ set$(ctx, `containerSpan${i}`, 1);
2675
2941
  } else {
2676
2942
  const itemIndex = indexByKey.get(itemKey);
2677
2943
  const item = data[itemIndex];
2678
2944
  if (item !== void 0) {
2679
- const id = (_j = idCache[itemIndex]) != null ? _j : getId(state, itemIndex);
2945
+ const id = (_m = idCache[itemIndex]) != null ? _m : getId(state, itemIndex);
2680
2946
  const positionValue = positions.get(id);
2681
2947
  if (positionValue === void 0) {
2682
2948
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
2683
2949
  } else {
2684
2950
  const position = (positionValue || 0) - scrollAdjustPending;
2685
2951
  const column = columns.get(id) || 1;
2952
+ const span = columnSpans.get(id) || 1;
2686
2953
  const prevPos = peek$(ctx, `containerPosition${i}`);
2687
2954
  const prevColumn = peek$(ctx, `containerColumn${i}`);
2955
+ const prevSpan = peek$(ctx, `containerSpan${i}`);
2688
2956
  const prevData = peek$(ctx, `containerItemData${i}`);
2689
2957
  if (position > POSITION_OUT_OF_VIEW && position !== prevPos) {
2690
2958
  set$(ctx, `containerPosition${i}`, position);
@@ -2693,6 +2961,9 @@ function calculateItemsInView(ctx, state, params = {}) {
2693
2961
  if (column >= 0 && column !== prevColumn) {
2694
2962
  set$(ctx, `containerColumn${i}`, column);
2695
2963
  }
2964
+ if (span !== prevSpan) {
2965
+ set$(ctx, `containerSpan${i}`, span);
2966
+ }
2696
2967
  if (prevData !== item && (itemsAreEqual ? !itemsAreEqual(prevData, item, itemIndex, data) : true)) {
2697
2968
  set$(ctx, `containerItemData${i}`, item);
2698
2969
  }
@@ -2705,7 +2976,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2705
2976
  }
2706
2977
  if (!queuedInitialLayout && endBuffered !== null) {
2707
2978
  if (checkAllSizesKnown(state)) {
2708
- setDidLayout(ctx, state);
2979
+ setDidLayout(ctx);
2709
2980
  }
2710
2981
  }
2711
2982
  if (viewabilityConfigCallbackPairs) {
@@ -2718,9 +2989,6 @@ function calculateItemsInView(ctx, state, params = {}) {
2718
2989
  }
2719
2990
  }
2720
2991
  });
2721
- if (state.initialAnchor) {
2722
- ensureInitialAnchor(ctx, state);
2723
- }
2724
2992
  }
2725
2993
 
2726
2994
  // src/core/checkActualChange.ts
@@ -2743,20 +3011,69 @@ function checkActualChange(state, dataProp, previousData) {
2743
3011
  return false;
2744
3012
  }
2745
3013
 
3014
+ // src/core/checkFinishedScroll.ts
3015
+ function checkFinishedScroll(ctx) {
3016
+ ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
3017
+ }
3018
+ function checkFinishedScrollFrame(ctx) {
3019
+ const scrollingTo = ctx.state.scrollingTo;
3020
+ if (scrollingTo) {
3021
+ const { state } = ctx;
3022
+ state.animFrameCheckFinishedScroll = void 0;
3023
+ const scroll = state.scrollPending;
3024
+ const adjust = state.scrollAdjustHandler.getAdjust();
3025
+ const clampedTargetOffset = clampScrollOffset(ctx, scrollingTo.offset - (scrollingTo.viewOffset || 0));
3026
+ const maxOffset = clampScrollOffset(ctx, scroll);
3027
+ const diff1 = Math.abs(scroll - clampedTargetOffset);
3028
+ const diff2 = Math.abs(diff1 - adjust);
3029
+ const isNotOverscrolled = Math.abs(scroll - maxOffset) < 1;
3030
+ if (isNotOverscrolled && (diff1 < 1 || diff2 < 1)) {
3031
+ finishScrollTo(ctx);
3032
+ }
3033
+ }
3034
+ }
3035
+ function checkFinishedScrollFallback(ctx) {
3036
+ const state = ctx.state;
3037
+ const scrollingTo = state.scrollingTo;
3038
+ const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) || !state.didContainersLayout;
3039
+ state.timeoutCheckFinishedScrollFallback = setTimeout(
3040
+ () => {
3041
+ let numChecks = 0;
3042
+ const checkHasScrolled = () => {
3043
+ state.timeoutCheckFinishedScrollFallback = void 0;
3044
+ const isStillScrollingTo = state.scrollingTo;
3045
+ if (isStillScrollingTo) {
3046
+ numChecks++;
3047
+ if (state.hasScrolled || numChecks > 5) {
3048
+ finishScrollTo(ctx);
3049
+ } else {
3050
+ state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, 100);
3051
+ }
3052
+ }
3053
+ };
3054
+ checkHasScrolled();
3055
+ },
3056
+ slowTimeout ? 500 : 100
3057
+ );
3058
+ }
3059
+
2746
3060
  // src/core/doMaintainScrollAtEnd.ts
2747
- function doMaintainScrollAtEnd(ctx, state, animated) {
3061
+ function doMaintainScrollAtEnd(ctx, animated) {
3062
+ const state = ctx.state;
2748
3063
  const {
3064
+ didContainersLayout,
3065
+ isAtEnd,
2749
3066
  refScroller,
2750
3067
  props: { maintainScrollAtEnd }
2751
3068
  } = state;
2752
- if ((state == null ? void 0 : state.isAtEnd) && maintainScrollAtEnd && peek$(ctx, "containersDidLayout")) {
2753
- const paddingTop = peek$(ctx, "alignItemsPaddingTop");
2754
- if (paddingTop > 0) {
3069
+ if (isAtEnd && maintainScrollAtEnd && didContainersLayout) {
3070
+ const contentSize = getContentSize(ctx);
3071
+ if (contentSize < state.scrollLength) {
2755
3072
  state.scroll = 0;
2756
3073
  }
2757
3074
  requestAnimationFrame(() => {
2758
3075
  var _a3;
2759
- if (state == null ? void 0 : state.isAtEnd) {
3076
+ if (state.isAtEnd) {
2760
3077
  state.maintainingScrollAtEnd = true;
2761
3078
  (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
2762
3079
  animated
@@ -2827,28 +3144,29 @@ function updateAveragesOnDataChange(state, oldData, newData) {
2827
3144
  }
2828
3145
 
2829
3146
  // src/core/checkResetContainers.ts
2830
- function checkResetContainers(ctx, state, dataProp) {
3147
+ function checkResetContainers(ctx, dataProp) {
3148
+ const state = ctx.state;
2831
3149
  const { previousData } = state;
2832
3150
  if (previousData) {
2833
3151
  updateAveragesOnDataChange(state, previousData, dataProp);
2834
3152
  }
2835
3153
  const { maintainScrollAtEnd } = state.props;
2836
- calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
3154
+ calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
2837
3155
  const shouldMaintainScrollAtEnd = maintainScrollAtEnd === true || maintainScrollAtEnd.onDataChange;
2838
- const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, state, false);
3156
+ const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, false);
2839
3157
  if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
2840
3158
  state.isEndReached = false;
2841
3159
  }
2842
3160
  if (!didMaintainScrollAtEnd) {
2843
- checkAtTop(state);
2844
- checkAtBottom(ctx, state);
3161
+ checkThresholds(ctx);
2845
3162
  }
2846
3163
  delete state.previousData;
2847
3164
  }
2848
3165
 
2849
3166
  // src/core/doInitialAllocateContainers.ts
2850
- function doInitialAllocateContainers(ctx, state) {
3167
+ function doInitialAllocateContainers(ctx) {
2851
3168
  var _a3, _b, _c;
3169
+ const state = ctx.state;
2852
3170
  const {
2853
3171
  scrollLength,
2854
3172
  props: {
@@ -2869,8 +3187,10 @@ function doInitialAllocateContainers(ctx, state) {
2869
3187
  const num = Math.min(20, data.length);
2870
3188
  for (let i = 0; i < num; i++) {
2871
3189
  const item = data[i];
2872
- const itemType = getItemType ? (_a3 = getItemType(item, i)) != null ? _a3 : "" : "";
2873
- totalSize += (_c = (_b = getFixedItemSize == null ? void 0 : getFixedItemSize(i, item, itemType)) != null ? _b : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(i, item, itemType)) != null ? _c : estimatedItemSize;
3190
+ if (item !== void 0) {
3191
+ const itemType = (_a3 = getItemType == null ? void 0 : getItemType(item, i)) != null ? _a3 : "";
3192
+ totalSize += (_c = (_b = getFixedItemSize == null ? void 0 : getFixedItemSize(item, i, itemType)) != null ? _b : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(item, i, itemType)) != null ? _c : estimatedItemSize;
3193
+ }
2874
3194
  }
2875
3195
  averageItemSize = totalSize / num;
2876
3196
  } else {
@@ -2880,16 +3200,17 @@ function doInitialAllocateContainers(ctx, state) {
2880
3200
  for (let i = 0; i < numContainers; i++) {
2881
3201
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
2882
3202
  set$(ctx, `containerColumn${i}`, -1);
3203
+ set$(ctx, `containerSpan${i}`, 1);
2883
3204
  }
2884
3205
  set$(ctx, "numContainers", numContainers);
2885
3206
  set$(ctx, "numContainersPooled", numContainers * state.props.initialContainerPoolRatio);
2886
3207
  if (state.lastLayout) {
2887
3208
  if (state.initialScroll) {
2888
3209
  requestAnimationFrame(() => {
2889
- calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
3210
+ calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
2890
3211
  });
2891
3212
  } else {
2892
- calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
3213
+ calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
2893
3214
  }
2894
3215
  }
2895
3216
  return true;
@@ -2897,7 +3218,8 @@ function doInitialAllocateContainers(ctx, state) {
2897
3218
  }
2898
3219
 
2899
3220
  // src/core/handleLayout.ts
2900
- function handleLayout(ctx, state, layout, setCanRender) {
3221
+ function handleLayout(ctx, layout, setCanRender) {
3222
+ const state = ctx.state;
2901
3223
  const { maintainScrollAtEnd } = state.props;
2902
3224
  const measuredLength = layout[state.props.horizontal ? "width" : "height"];
2903
3225
  const previousLength = state.scrollLength;
@@ -2913,20 +3235,18 @@ function handleLayout(ctx, state, layout, setCanRender) {
2913
3235
  state.lastBatchingAction = Date.now();
2914
3236
  state.scrollForNextCalculateItemsInView = void 0;
2915
3237
  if (scrollLength > 0) {
2916
- doInitialAllocateContainers(ctx, state);
3238
+ doInitialAllocateContainers(ctx);
2917
3239
  }
2918
3240
  if (needsCalculate) {
2919
- calculateItemsInView(ctx, state, { doMVCP: true });
3241
+ calculateItemsInView(ctx, { doMVCP: true });
2920
3242
  }
2921
3243
  if (didChange || otherAxisSize !== prevOtherAxisSize) {
2922
3244
  set$(ctx, "scrollSize", { height: layout.height, width: layout.width });
2923
3245
  }
2924
3246
  if (maintainScrollAtEnd === true || maintainScrollAtEnd.onLayout) {
2925
- doMaintainScrollAtEnd(ctx, state, false);
3247
+ doMaintainScrollAtEnd(ctx, false);
2926
3248
  }
2927
- updateAlignItemsPaddingTop(ctx, state);
2928
- checkAtBottom(ctx, state);
2929
- checkAtTop(state);
3249
+ checkThresholds(ctx);
2930
3250
  if (state) {
2931
3251
  state.needsOtherAxisSize = otherAxisSize - (state.props.stylePaddingTop || 0) < 10;
2932
3252
  }
@@ -2941,8 +3261,9 @@ function handleLayout(ctx, state, layout, setCanRender) {
2941
3261
  }
2942
3262
 
2943
3263
  // src/core/onScroll.ts
2944
- function onScroll(ctx, state, event) {
2945
- var _a3, _b, _c;
3264
+ function onScroll(ctx, event) {
3265
+ var _a3, _b, _c, _d;
3266
+ const state = ctx.state;
2946
3267
  const {
2947
3268
  scrollProcessingEnabled,
2948
3269
  props: { onScroll: onScrollProp }
@@ -2953,17 +3274,34 @@ function onScroll(ctx, state, event) {
2953
3274
  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) {
2954
3275
  return;
2955
3276
  }
3277
+ let insetChanged = false;
3278
+ if ((_d = event.nativeEvent) == null ? void 0 : _d.contentInset) {
3279
+ const { contentInset } = event.nativeEvent;
3280
+ const prevInset = state.nativeContentInset;
3281
+ if (!prevInset || prevInset.top !== contentInset.top || prevInset.bottom !== contentInset.bottom || prevInset.left !== contentInset.left || prevInset.right !== contentInset.right) {
3282
+ state.nativeContentInset = contentInset;
3283
+ insetChanged = true;
3284
+ }
3285
+ }
2956
3286
  let newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
3287
+ if (state.scrollingTo) {
3288
+ const maxOffset = clampScrollOffset(ctx, newScroll);
3289
+ if (newScroll !== maxOffset && Math.abs(newScroll - maxOffset) > 1) {
3290
+ newScroll = maxOffset;
3291
+ scrollTo(ctx, {
3292
+ forceScroll: true,
3293
+ isInitialScroll: true,
3294
+ noScrollingTo: true,
3295
+ offset: newScroll
3296
+ });
3297
+ return;
3298
+ }
3299
+ }
2957
3300
  state.scrollPending = newScroll;
2958
- const maxOffset = Math.max(0, getContentSize(ctx) - state.scrollLength);
2959
- if (state.initialScroll && newScroll > maxOffset) {
2960
- newScroll = maxOffset;
2961
- scrollTo(ctx, state, {
2962
- noScrollingTo: true,
2963
- offset: newScroll
2964
- });
3301
+ updateScroll(ctx, newScroll, insetChanged);
3302
+ if (state.scrollingTo) {
3303
+ checkFinishedScroll(ctx);
2965
3304
  }
2966
- updateScroll(ctx, state, newScroll);
2967
3305
  onScrollProp == null ? void 0 : onScrollProp(event);
2968
3306
  }
2969
3307
 
@@ -2972,51 +3310,58 @@ var ScrollAdjustHandler = class {
2972
3310
  constructor(ctx) {
2973
3311
  this.appliedAdjust = 0;
2974
3312
  this.pendingAdjust = 0;
2975
- this.mounted = false;
2976
- this.context = ctx;
2977
- {
2978
- const commitPendingAdjust = () => {
2979
- const state = this.context.internalState;
2980
- const pending = this.pendingAdjust;
2981
- if (pending !== 0) {
2982
- this.pendingAdjust = 0;
2983
- this.appliedAdjust += pending;
2984
- state.scroll += pending;
2985
- state.scrollForNextCalculateItemsInView = void 0;
2986
- set$(this.context, "scrollAdjustPending", 0);
2987
- set$(this.context, "scrollAdjust", this.appliedAdjust);
2988
- calculateItemsInView(this.context, this.context.internalState);
2989
- }
2990
- };
2991
- listen$(this.context, "scrollingTo", (value) => {
2992
- if (value === void 0) {
2993
- commitPendingAdjust();
2994
- }
2995
- });
2996
- }
3313
+ this.ctx = ctx;
2997
3314
  }
2998
3315
  requestAdjust(add) {
2999
- const scrollingTo = peek$(this.context, "scrollingTo");
3316
+ const scrollingTo = this.ctx.state.scrollingTo;
3000
3317
  if ((scrollingTo == null ? void 0 : scrollingTo.animated) && !scrollingTo.isInitialScroll) {
3001
3318
  this.pendingAdjust += add;
3002
- set$(this.context, "scrollAdjustPending", this.pendingAdjust);
3319
+ set$(this.ctx, "scrollAdjustPending", this.pendingAdjust);
3003
3320
  } else {
3004
3321
  this.appliedAdjust += add;
3005
- set$(this.context, "scrollAdjust", this.appliedAdjust);
3322
+ set$(this.ctx, "scrollAdjust", this.appliedAdjust);
3323
+ }
3324
+ if (this.ctx.state.scrollingTo) {
3325
+ checkFinishedScroll(this.ctx);
3006
3326
  }
3007
- }
3008
- setMounted() {
3009
- this.mounted = true;
3010
3327
  }
3011
3328
  getAdjust() {
3012
3329
  return this.appliedAdjust;
3013
3330
  }
3331
+ commitPendingAdjust(scrollTarget) {
3332
+ {
3333
+ const state = this.ctx.state;
3334
+ const pending = this.pendingAdjust;
3335
+ this.pendingAdjust = 0;
3336
+ if (pending !== 0) {
3337
+ let targetScroll;
3338
+ if ((scrollTarget == null ? void 0 : scrollTarget.index) !== void 0) {
3339
+ const currentOffset = calculateOffsetForIndex(this.ctx, scrollTarget.index);
3340
+ targetScroll = calculateOffsetWithOffsetPosition(this.ctx, currentOffset, scrollTarget);
3341
+ targetScroll = clampScrollOffset(this.ctx, targetScroll);
3342
+ } else {
3343
+ targetScroll = clampScrollOffset(this.ctx, state.scroll + pending);
3344
+ }
3345
+ const adjustment = targetScroll - state.scroll;
3346
+ if (Math.abs(adjustment) > 0.1 || Math.abs(pending) > 0.1) {
3347
+ this.appliedAdjust += adjustment;
3348
+ state.scroll = targetScroll;
3349
+ state.scrollForNextCalculateItemsInView = void 0;
3350
+ set$(this.ctx, "scrollAdjust", this.appliedAdjust);
3351
+ }
3352
+ set$(this.ctx, "scrollAdjustPending", 0);
3353
+ calculateItemsInView(this.ctx);
3354
+ }
3355
+ }
3356
+ }
3014
3357
  };
3015
3358
 
3016
3359
  // src/core/updateItemSize.ts
3017
- function updateItemSize(ctx, state, itemKey, sizeObj) {
3360
+ function updateItemSize(ctx, itemKey, sizeObj) {
3018
3361
  var _a3;
3362
+ const state = ctx.state;
3019
3363
  const {
3364
+ didContainersLayout,
3020
3365
  sizesKnown,
3021
3366
  props: {
3022
3367
  getFixedItemSize,
@@ -3039,31 +3384,24 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
3039
3384
  return;
3040
3385
  }
3041
3386
  const type = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
3042
- const size2 = getFixedItemSize(index, itemData, type);
3387
+ const size2 = getFixedItemSize(itemData, index, type);
3043
3388
  if (size2 !== void 0 && size2 === sizesKnown.get(itemKey)) {
3044
3389
  return;
3045
3390
  }
3046
3391
  }
3047
- const containersDidLayout = peek$(ctx, "containersDidLayout");
3048
- let needsRecalculate = !containersDidLayout;
3392
+ let needsRecalculate = !didContainersLayout;
3049
3393
  let shouldMaintainScrollAtEnd = false;
3050
3394
  let minIndexSizeChanged;
3051
3395
  let maxOtherAxisSize = peek$(ctx, "otherAxisSize") || 0;
3052
3396
  const prevSizeKnown = state.sizesKnown.get(itemKey);
3053
- const diff = updateOneItemSize(ctx, state, itemKey, sizeObj);
3397
+ const diff = updateOneItemSize(ctx, itemKey, sizeObj);
3054
3398
  const size = roundSize(horizontal ? sizeObj.width : sizeObj.height);
3055
3399
  if (diff !== 0) {
3056
3400
  minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
3057
3401
  const { startBuffered, endBuffered } = state;
3058
3402
  needsRecalculate || (needsRecalculate = index >= startBuffered && index <= endBuffered);
3059
- if (!needsRecalculate) {
3060
- const numContainers = ctx.values.get("numContainers");
3061
- for (let i = 0; i < numContainers; i++) {
3062
- if (peek$(ctx, `containerItemKey${i}`) === itemKey) {
3063
- needsRecalculate = true;
3064
- break;
3065
- }
3066
- }
3403
+ if (!needsRecalculate && state.containerItemKeys.has(itemKey)) {
3404
+ needsRecalculate = true;
3067
3405
  }
3068
3406
  if (state.needsOtherAxisSize) {
3069
3407
  const otherAxisSize = horizontal ? sizeObj.height : sizeObj.width;
@@ -3099,22 +3437,22 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
3099
3437
  if (!cur || maxOtherAxisSize > cur) {
3100
3438
  set$(ctx, "otherAxisSize", maxOtherAxisSize);
3101
3439
  }
3102
- if (containersDidLayout || checkAllSizesKnown(state)) {
3440
+ if (didContainersLayout || checkAllSizesKnown(state)) {
3103
3441
  if (needsRecalculate) {
3104
3442
  state.scrollForNextCalculateItemsInView = void 0;
3105
- calculateItemsInView(ctx, state, { doMVCP: true });
3443
+ calculateItemsInView(ctx, { doMVCP: true });
3106
3444
  }
3107
3445
  if (shouldMaintainScrollAtEnd) {
3108
3446
  if (maintainScrollAtEnd === true || maintainScrollAtEnd.onItemLayout) {
3109
- doMaintainScrollAtEnd(ctx, state, false);
3447
+ doMaintainScrollAtEnd(ctx, false);
3110
3448
  }
3111
3449
  }
3112
3450
  }
3113
3451
  }
3114
- function updateOneItemSize(ctx, state, itemKey, sizeObj) {
3452
+ function updateOneItemSize(ctx, itemKey, sizeObj) {
3115
3453
  var _a3;
3454
+ const state = ctx.state;
3116
3455
  const {
3117
- sizes,
3118
3456
  indexByKey,
3119
3457
  sizesKnown,
3120
3458
  averageSizes,
@@ -3122,9 +3460,10 @@ function updateOneItemSize(ctx, state, itemKey, sizeObj) {
3122
3460
  } = state;
3123
3461
  if (!data) return 0;
3124
3462
  const index = indexByKey.get(itemKey);
3125
- const prevSize = getItemSize(ctx, state, itemKey, index, data[index]);
3463
+ const prevSize = getItemSize(ctx, itemKey, index, data[index]);
3126
3464
  const rawSize = horizontal ? sizeObj.width : sizeObj.height;
3127
3465
  const size = Math.round(rawSize) ;
3466
+ const prevSizeKnown = sizesKnown.get(itemKey);
3128
3467
  sizesKnown.set(itemKey, size);
3129
3468
  if (!getEstimatedItemSize && !getFixedItemSize && size > 0) {
3130
3469
  const itemType = getItemType ? (_a3 = getItemType(data[index], index)) != null ? _a3 : "" : "";
@@ -3132,15 +3471,28 @@ function updateOneItemSize(ctx, state, itemKey, sizeObj) {
3132
3471
  if (!averages) {
3133
3472
  averages = averageSizes[itemType] = { avg: 0, num: 0 };
3134
3473
  }
3135
- averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
3136
- averages.num++;
3474
+ if (averages.num === 0) {
3475
+ averages.avg = size;
3476
+ averages.num++;
3477
+ } else if (prevSizeKnown !== void 0 && prevSizeKnown > 0) {
3478
+ averages.avg += (size - prevSizeKnown) / averages.num;
3479
+ } else {
3480
+ averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
3481
+ averages.num++;
3482
+ }
3137
3483
  }
3138
3484
  if (!prevSize || Math.abs(prevSize - size) > 0.1) {
3139
- setSize(ctx, state, itemKey, size);
3485
+ setSize(ctx, itemKey, size);
3140
3486
  return size - prevSize;
3141
3487
  }
3142
3488
  return 0;
3143
3489
  }
3490
+ function useWrapIfItem(fn) {
3491
+ return React3.useMemo(
3492
+ () => fn ? (arg1, arg2, arg3) => arg1 !== void 0 && arg2 !== void 0 ? fn(arg1, arg2, arg3) : void 0 : void 0,
3493
+ [fn]
3494
+ );
3495
+ }
3144
3496
  var useCombinedRef = (...refs) => {
3145
3497
  const callback = React3.useCallback((element) => {
3146
3498
  for (const ref of refs) {
@@ -3183,14 +3535,15 @@ function createColumnWrapperStyle(contentContainerStyle) {
3183
3535
  }
3184
3536
 
3185
3537
  // src/utils/createImperativeHandle.ts
3186
- function createImperativeHandle(ctx, state) {
3538
+ function createImperativeHandle(ctx) {
3539
+ const state = ctx.state;
3187
3540
  const scrollIndexIntoView = (options) => {
3188
3541
  if (state) {
3189
3542
  const { index, ...rest } = options;
3190
3543
  const { startNoBuffer, endNoBuffer } = state;
3191
3544
  if (index < startNoBuffer || index > endNoBuffer) {
3192
3545
  const viewPosition = index < startNoBuffer ? 0 : 1;
3193
- scrollToIndex(ctx, state, {
3546
+ scrollToIndex(ctx, {
3194
3547
  ...rest,
3195
3548
  index,
3196
3549
  viewPosition
@@ -3205,8 +3558,8 @@ function createImperativeHandle(ctx, state) {
3205
3558
  getScrollableNode: () => refScroller.current.getScrollableNode(),
3206
3559
  getScrollResponder: () => refScroller.current.getScrollResponder(),
3207
3560
  getState: () => ({
3208
- activeStickyIndex: state.activeStickyIndex,
3209
- contentLength: state.totalSize,
3561
+ activeStickyIndex: peek$(ctx, "activeStickyIndex"),
3562
+ contentLength: getContentSize(ctx),
3210
3563
  data: state.props.data,
3211
3564
  elementAtIndex: (index) => {
3212
3565
  var _a3;
@@ -3216,15 +3569,22 @@ function createImperativeHandle(ctx, state) {
3216
3569
  endBuffered: state.endBuffered,
3217
3570
  isAtEnd: state.isAtEnd,
3218
3571
  isAtStart: state.isAtStart,
3572
+ listen: (signalName, cb) => listen$(ctx, signalName, cb),
3573
+ listenToPosition: (key, cb) => listenPosition$(ctx, key, cb),
3219
3574
  positionAtIndex: (index) => state.positions.get(getId(state, index)),
3220
3575
  positions: state.positions,
3221
3576
  scroll: state.scroll,
3222
3577
  scrollLength: state.scrollLength,
3578
+ scrollVelocity: getScrollVelocity(state),
3223
3579
  sizeAtIndex: (index) => state.sizesKnown.get(getId(state, index)),
3224
3580
  sizes: state.sizesKnown,
3225
3581
  start: state.startNoBuffer,
3226
3582
  startBuffered: state.startBuffered
3227
3583
  }),
3584
+ reportContentInset: (inset) => {
3585
+ state.contentInsetOverride = inset != null ? inset : void 0;
3586
+ updateScroll(ctx, state.scroll, true);
3587
+ },
3228
3588
  scrollIndexIntoView,
3229
3589
  scrollItemIntoView: ({ item, ...props }) => {
3230
3590
  const data = state.props.data;
@@ -3240,23 +3600,23 @@ function createImperativeHandle(ctx, state) {
3240
3600
  if (index !== -1) {
3241
3601
  const paddingBottom = stylePaddingBottom || 0;
3242
3602
  const footerSize = peek$(ctx, "footerSize") || 0;
3243
- scrollToIndex(ctx, state, {
3603
+ scrollToIndex(ctx, {
3604
+ ...options,
3244
3605
  index,
3245
3606
  viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
3246
- viewPosition: 1,
3247
- ...options
3607
+ viewPosition: 1
3248
3608
  });
3249
3609
  }
3250
3610
  },
3251
- scrollToIndex: (params) => scrollToIndex(ctx, state, params),
3611
+ scrollToIndex: (params) => scrollToIndex(ctx, params),
3252
3612
  scrollToItem: ({ item, ...props }) => {
3253
3613
  const data = state.props.data;
3254
3614
  const index = data.indexOf(item);
3255
3615
  if (index !== -1) {
3256
- scrollToIndex(ctx, state, { index, ...props });
3616
+ scrollToIndex(ctx, { index, ...props });
3257
3617
  }
3258
3618
  },
3259
- scrollToOffset: (params) => scrollTo(ctx, state, params),
3619
+ scrollToOffset: (params) => scrollTo(ctx, params),
3260
3620
  setScrollProcessingEnabled: (enabled) => {
3261
3621
  state.scrollProcessingEnabled = enabled;
3262
3622
  },
@@ -3266,8 +3626,57 @@ function createImperativeHandle(ctx, state) {
3266
3626
  }
3267
3627
  };
3268
3628
  }
3269
- function getRenderedItem(ctx, state, key) {
3629
+
3630
+ // src/utils/getAlwaysRenderIndices.ts
3631
+ var sortAsc = (a, b) => a - b;
3632
+ var toCount = (value) => typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
3633
+ var addIndex = (result, dataLength, index) => {
3634
+ if (index >= 0 && index < dataLength) {
3635
+ result.add(index);
3636
+ }
3637
+ };
3638
+ function getAlwaysRenderIndices(config, data, keyExtractor) {
3639
+ var _a3, _b;
3640
+ if (!config || data.length === 0) {
3641
+ return [];
3642
+ }
3643
+ const result = /* @__PURE__ */ new Set();
3644
+ const dataLength = data.length;
3645
+ const topCount = toCount(config.top);
3646
+ if (topCount > 0) {
3647
+ for (let i = 0; i < Math.min(topCount, dataLength); i++) {
3648
+ addIndex(result, dataLength, i);
3649
+ }
3650
+ }
3651
+ const bottomCount = toCount(config.bottom);
3652
+ if (bottomCount > 0) {
3653
+ for (let i = Math.max(0, dataLength - bottomCount); i < dataLength; i++) {
3654
+ addIndex(result, dataLength, i);
3655
+ }
3656
+ }
3657
+ if ((_a3 = config.indices) == null ? void 0 : _a3.length) {
3658
+ for (const index of config.indices) {
3659
+ if (!Number.isFinite(index)) continue;
3660
+ addIndex(result, dataLength, Math.floor(index));
3661
+ }
3662
+ }
3663
+ if ((_b = config.keys) == null ? void 0 : _b.length) {
3664
+ const keys = new Set(config.keys);
3665
+ for (let i = 0; i < dataLength && keys.size > 0; i++) {
3666
+ const key = keyExtractor(data[i], i);
3667
+ if (keys.has(key)) {
3668
+ addIndex(result, dataLength, i);
3669
+ keys.delete(key);
3670
+ }
3671
+ }
3672
+ }
3673
+ const indices = Array.from(result);
3674
+ indices.sort(sortAsc);
3675
+ return indices;
3676
+ }
3677
+ function getRenderedItem(ctx, key) {
3270
3678
  var _a3;
3679
+ const state = ctx.state;
3271
3680
  if (!state) {
3272
3681
  return null;
3273
3682
  }
@@ -3294,6 +3703,42 @@ function getRenderedItem(ctx, state, key) {
3294
3703
  }
3295
3704
  return { index, item: data[index], renderedItem };
3296
3705
  }
3706
+
3707
+ // src/utils/normalizeMaintainVisibleContentPosition.ts
3708
+ function normalizeMaintainVisibleContentPosition(value) {
3709
+ var _a3, _b;
3710
+ if (value === true) {
3711
+ return { data: true, size: true };
3712
+ }
3713
+ if (value && typeof value === "object") {
3714
+ return {
3715
+ data: (_a3 = value.data) != null ? _a3 : false,
3716
+ size: (_b = value.size) != null ? _b : true,
3717
+ shouldRestorePosition: value.shouldRestorePosition
3718
+ };
3719
+ }
3720
+ if (value === false) {
3721
+ return { data: false, size: false };
3722
+ }
3723
+ return { data: false, size: true };
3724
+ }
3725
+
3726
+ // src/utils/setPaddingTop.ts
3727
+ function setPaddingTop(ctx, { stylePaddingTop }) {
3728
+ const state = ctx.state;
3729
+ if (stylePaddingTop !== void 0) {
3730
+ const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
3731
+ if (stylePaddingTop < prevStylePaddingTop) {
3732
+ let prevTotalSize = peek$(ctx, "totalSize") || 0;
3733
+ set$(ctx, "totalSize", prevTotalSize + prevStylePaddingTop);
3734
+ state.timeoutSetPaddingTop = setTimeout(() => {
3735
+ prevTotalSize = peek$(ctx, "totalSize") || 0;
3736
+ set$(ctx, "totalSize", prevTotalSize - prevStylePaddingTop);
3737
+ }, 16);
3738
+ }
3739
+ set$(ctx, "stylePaddingTop", stylePaddingTop);
3740
+ }
3741
+ }
3297
3742
  function useThrottleDebounce(mode) {
3298
3743
  const timeoutRef = React3.useRef(null);
3299
3744
  const lastCallTimeRef = React3.useRef(0);
@@ -3344,6 +3789,7 @@ function useThrottledOnScroll(originalHandler, scrollEventThrottle) {
3344
3789
  var DEFAULT_DRAW_DISTANCE = 250;
3345
3790
  var DEFAULT_ITEM_SIZE = 100;
3346
3791
  var LegendList = typedMemo(
3792
+ // biome-ignore lint/nursery/noShadow: const function name shadowing is intentional
3347
3793
  typedForwardRef(function LegendList2(props, forwardedRef) {
3348
3794
  const { children, data: dataProp, renderItem: renderItemProp, ...restProps } = props;
3349
3795
  const isChildrenMode = children !== void 0 && dataProp === void 0;
@@ -3361,15 +3807,16 @@ var LegendList = typedMemo(
3361
3807
  })
3362
3808
  );
3363
3809
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
3364
- var _a3, _b;
3810
+ var _a3, _b, _c, _d;
3365
3811
  const {
3366
3812
  alignItemsAtEnd = false,
3813
+ alwaysRender,
3367
3814
  columnWrapperStyle,
3368
3815
  contentContainerStyle: contentContainerStyleProp,
3816
+ contentInset,
3369
3817
  data: dataProp = [],
3370
3818
  dataVersion,
3371
3819
  drawDistance = 250,
3372
- enableAverages = true,
3373
3820
  estimatedItemSize: estimatedItemSizeProp,
3374
3821
  estimatedListSize,
3375
3822
  extraData,
@@ -3387,11 +3834,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3387
3834
  ListHeaderComponent,
3388
3835
  maintainScrollAtEnd = false,
3389
3836
  maintainScrollAtEndThreshold = 0.1,
3390
- maintainVisibleContentPosition = false,
3837
+ maintainVisibleContentPosition: maintainVisibleContentPositionProp,
3391
3838
  numColumns: numColumnsProp = 1,
3839
+ overrideItemLayout,
3392
3840
  onEndReached,
3393
3841
  onEndReachedThreshold = 0.5,
3394
3842
  onItemSizeChanged,
3843
+ onMetricsChange,
3395
3844
  onLayout: onLayoutProp,
3396
3845
  onLoad,
3397
3846
  onMomentumScrollEnd,
@@ -3411,6 +3860,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3411
3860
  snapToIndices,
3412
3861
  stickyHeaderIndices: stickyHeaderIndicesProp,
3413
3862
  stickyIndices: stickyIndicesDeprecated,
3863
+ // TODOV3: Remove from v3 release
3414
3864
  style: styleProp,
3415
3865
  suggestEstimatedItemSize,
3416
3866
  viewabilityConfig,
@@ -3418,13 +3868,34 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3418
3868
  waitForInitialLayout = true,
3419
3869
  ...rest
3420
3870
  } = props;
3871
+ const animatedPropsInternal = props.animatedPropsInternal;
3421
3872
  const { childrenMode } = rest;
3422
- const contentContainerStyle = { ...StyleSheet.flatten(contentContainerStyleProp) };
3873
+ const contentContainerStyleBase = StyleSheet.flatten(contentContainerStyleProp);
3874
+ const shouldFlexGrow = alignItemsAtEnd && (horizontal ? (contentContainerStyleBase == null ? void 0 : contentContainerStyleBase.minWidth) == null : (contentContainerStyleBase == null ? void 0 : contentContainerStyleBase.minHeight) == null);
3875
+ const contentContainerStyle = {
3876
+ ...contentContainerStyleBase,
3877
+ ...alignItemsAtEnd ? {
3878
+ display: "flex",
3879
+ flexDirection: horizontal ? "row" : "column",
3880
+ ...shouldFlexGrow ? { flexGrow: 1 } : {},
3881
+ justifyContent: "flex-end"
3882
+ } : {}
3883
+ };
3423
3884
  const style = { ...StyleSheet.flatten(styleProp) };
3424
3885
  const stylePaddingTopState = extractPadding(style, contentContainerStyle, "Top");
3425
3886
  const stylePaddingBottomState = extractPadding(style, contentContainerStyle, "Bottom");
3887
+ const maintainVisibleContentPositionConfig = normalizeMaintainVisibleContentPosition(
3888
+ maintainVisibleContentPositionProp
3889
+ );
3426
3890
  const [renderNum, setRenderNum] = React3.useState(0);
3427
- 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;
3891
+ const initialScrollProp = initialScrollAtEnd ? { index: Math.max(0, dataProp.length - 1), viewOffset: -stylePaddingBottomState, viewPosition: 1 } : initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? {
3892
+ index: initialScrollIndexProp.index || 0,
3893
+ viewOffset: initialScrollIndexProp.viewOffset || (initialScrollIndexProp.viewPosition === 1 ? -stylePaddingBottomState : 0),
3894
+ viewPosition: initialScrollIndexProp.viewPosition || 0
3895
+ } : {
3896
+ index: initialScrollIndexProp || 0,
3897
+ viewOffset: initialScrollOffsetProp || 0
3898
+ } : void 0;
3428
3899
  const [canRender, setCanRender] = React3__namespace.useState(!IsNewArchitecture);
3429
3900
  const ctx = useStateContext();
3430
3901
  ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
@@ -3434,6 +3905,18 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3434
3905
  const scrollBuffer = (drawDistance != null ? drawDistance : DEFAULT_DRAW_DISTANCE) || 1;
3435
3906
  const keyExtractor = keyExtractorProp != null ? keyExtractorProp : (_item, index) => index.toString();
3436
3907
  const stickyHeaderIndices = stickyHeaderIndicesProp != null ? stickyHeaderIndicesProp : stickyIndicesDeprecated;
3908
+ const alwaysRenderIndices = React3.useMemo(() => {
3909
+ const indices = getAlwaysRenderIndices(alwaysRender, dataProp, keyExtractor);
3910
+ return { arr: indices, set: new Set(indices) };
3911
+ }, [
3912
+ alwaysRender == null ? void 0 : alwaysRender.top,
3913
+ alwaysRender == null ? void 0 : alwaysRender.bottom,
3914
+ (_a3 = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _a3.join(","),
3915
+ (_b = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _b.join(","),
3916
+ dataProp,
3917
+ dataVersion,
3918
+ keyExtractor
3919
+ ]);
3437
3920
  if (IS_DEV && stickyIndicesDeprecated && !stickyHeaderIndicesProp) {
3438
3921
  warnDevOnce(
3439
3922
  "stickyIndices",
@@ -3441,15 +3924,19 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3441
3924
  );
3442
3925
  }
3443
3926
  const refState = React3.useRef();
3927
+ const hasOverrideItemLayout = !!overrideItemLayout;
3928
+ const prevHasOverrideItemLayout = React3.useRef(hasOverrideItemLayout);
3444
3929
  if (!refState.current) {
3445
- if (!ctx.internalState) {
3930
+ if (!ctx.state) {
3446
3931
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : { height: 0, width: 0 } )[horizontal ? "width" : "height"];
3447
- ctx.internalState = {
3448
- activeStickyIndex: void 0,
3932
+ ctx.state = {
3933
+ activeStickyIndex: -1,
3449
3934
  averageSizes: {},
3450
3935
  columns: /* @__PURE__ */ new Map(),
3451
- containerItemKeys: /* @__PURE__ */ new Set(),
3936
+ columnSpans: /* @__PURE__ */ new Map(),
3937
+ containerItemKeys: /* @__PURE__ */ new Map(),
3452
3938
  containerItemTypes: /* @__PURE__ */ new Map(),
3939
+ contentInsetOverride: void 0,
3453
3940
  dataChangeNeedsScrollUpdate: false,
3454
3941
  didColumnsChange: false,
3455
3942
  didDataChange: false,
@@ -3465,19 +3952,21 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3465
3952
  attempts: 0,
3466
3953
  index: initialScrollProp.index,
3467
3954
  settledTicks: 0,
3468
- viewOffset: (_a3 = initialScrollProp.viewOffset) != null ? _a3 : 0,
3955
+ viewOffset: (_c = initialScrollProp.viewOffset) != null ? _c : 0,
3469
3956
  viewPosition: initialScrollProp.viewPosition
3470
3957
  } : void 0,
3471
3958
  initialScroll: initialScrollProp,
3472
3959
  isAtEnd: false,
3473
3960
  isAtStart: false,
3474
- isEndReached: false,
3961
+ isEndReached: null,
3475
3962
  isFirst: true,
3476
- isStartReached: false,
3963
+ isStartReached: null,
3477
3964
  lastBatchingAction: Date.now(),
3478
3965
  lastLayout: void 0,
3966
+ lastScrollDelta: 0,
3479
3967
  loadStartTime: Date.now(),
3480
3968
  minIndexSizeChanged: 0,
3969
+ nativeContentInset: void 0,
3481
3970
  nativeMarginTop: 0,
3482
3971
  positions: /* @__PURE__ */ new Map(),
3483
3972
  props: {},
@@ -3505,12 +3994,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3505
3994
  totalSize: 0,
3506
3995
  viewabilityConfigCallbackPairs: void 0
3507
3996
  };
3508
- const internalState = ctx.internalState;
3509
- internalState.triggerCalculateItemsInView = (params) => calculateItemsInView(ctx, internalState, params);
3510
- set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPosition);
3997
+ const internalState = ctx.state;
3998
+ internalState.triggerCalculateItemsInView = (params) => calculateItemsInView(ctx, params);
3999
+ set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPositionConfig);
3511
4000
  set$(ctx, "extraData", extraData);
3512
4001
  }
3513
- refState.current = ctx.internalState;
4002
+ refState.current = ctx.state;
3514
4003
  }
3515
4004
  const state = refState.current;
3516
4005
  const isFirstLocal = state.isFirst;
@@ -3524,20 +4013,24 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3524
4013
  const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
3525
4014
  state.props = {
3526
4015
  alignItemsAtEnd,
4016
+ alwaysRender,
4017
+ alwaysRenderIndicesArr: alwaysRenderIndices.arr,
4018
+ alwaysRenderIndicesSet: alwaysRenderIndices.set,
4019
+ animatedProps: animatedPropsInternal,
4020
+ contentInset,
3527
4021
  data: dataProp,
3528
4022
  dataVersion,
3529
- enableAverages,
3530
4023
  estimatedItemSize,
3531
- getEstimatedItemSize,
3532
- getFixedItemSize,
3533
- getItemType,
4024
+ getEstimatedItemSize: useWrapIfItem(getEstimatedItemSize),
4025
+ getFixedItemSize: useWrapIfItem(getFixedItemSize),
4026
+ getItemType: useWrapIfItem(getItemType),
3534
4027
  horizontal: !!horizontal,
3535
4028
  initialContainerPoolRatio,
3536
4029
  itemsAreEqual,
3537
- keyExtractor,
4030
+ keyExtractor: useWrapIfItem(keyExtractor),
3538
4031
  maintainScrollAtEnd,
3539
4032
  maintainScrollAtEndThreshold,
3540
- maintainVisibleContentPosition,
4033
+ maintainVisibleContentPosition: maintainVisibleContentPositionConfig,
3541
4034
  numColumns: numColumnsProp,
3542
4035
  onEndReached,
3543
4036
  onEndReachedThreshold,
@@ -3547,6 +4040,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3547
4040
  onStartReached,
3548
4041
  onStartReachedThreshold,
3549
4042
  onStickyHeaderChange,
4043
+ overrideItemLayout,
3550
4044
  recycleItems: !!recycleItems,
3551
4045
  renderItem,
3552
4046
  scrollBuffer,
@@ -3565,61 +4059,51 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3565
4059
  (_, i) => getId(state, dataProp.length - 1 - i)
3566
4060
  );
3567
4061
  }, [dataProp, dataVersion, numColumnsProp]);
3568
- const initializeStateVars = () => {
4062
+ const initializeStateVars = (shouldAdjustPadding) => {
3569
4063
  set$(ctx, "lastItemKeys", memoizedLastItemKeys);
3570
4064
  set$(ctx, "numColumns", numColumnsProp);
3571
4065
  const prevPaddingTop = peek$(ctx, "stylePaddingTop");
3572
- setPaddingTop(ctx, state, { stylePaddingTop: stylePaddingTopState });
4066
+ setPaddingTop(ctx, { stylePaddingTop: stylePaddingTopState });
3573
4067
  refState.current.props.stylePaddingBottom = stylePaddingBottomState;
3574
4068
  let paddingDiff = stylePaddingTopState - prevPaddingTop;
3575
- if (paddingDiff && prevPaddingTop !== void 0 && Platform.OS === "ios") {
4069
+ if (shouldAdjustPadding && maintainVisibleContentPositionConfig.size && paddingDiff && prevPaddingTop !== void 0 && Platform.OS === "ios") {
3576
4070
  if (state.scroll < 0) {
3577
4071
  paddingDiff += state.scroll;
3578
4072
  }
3579
- requestAdjust(ctx, state, paddingDiff);
4073
+ requestAdjust(ctx, paddingDiff);
3580
4074
  }
3581
4075
  };
3582
4076
  if (isFirstLocal) {
3583
- initializeStateVars();
4077
+ initializeStateVars(false);
3584
4078
  updateItemPositions(
3585
4079
  ctx,
3586
- state,
3587
4080
  /*dataChanged*/
3588
4081
  true
3589
4082
  );
3590
4083
  }
3591
4084
  const initialContentOffset = React3.useMemo(() => {
3592
- var _a4, _b2;
3593
- const { initialScroll } = refState.current;
3594
- if (!initialScroll) {
4085
+ let value;
4086
+ const { initialScroll, initialAnchor } = refState.current;
4087
+ if (initialScroll) {
4088
+ if (initialScroll.contentOffset !== void 0) {
4089
+ value = initialScroll.contentOffset;
4090
+ } else {
4091
+ const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, initialScroll.index) : 0;
4092
+ const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, initialScroll);
4093
+ const clampedOffset = clampScrollOffset(ctx, resolvedOffset);
4094
+ const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
4095
+ refState.current.initialScroll = updatedInitialScroll;
4096
+ state.initialScroll = updatedInitialScroll;
4097
+ value = clampedOffset;
4098
+ }
4099
+ } else {
3595
4100
  refState.current.initialAnchor = void 0;
3596
- return 0;
3597
- }
3598
- if (initialScroll.index !== void 0 && (!refState.current.initialAnchor || ((_a4 = refState.current.initialAnchor) == null ? void 0 : _a4.index) !== initialScroll.index)) {
3599
- refState.current.initialAnchor = {
3600
- attempts: 0,
3601
- index: initialScroll.index,
3602
- settledTicks: 0,
3603
- viewOffset: (_b2 = initialScroll.viewOffset) != null ? _b2 : 0,
3604
- viewPosition: initialScroll.viewPosition
3605
- };
4101
+ value = 0;
4102
+ }
4103
+ if (!value) {
4104
+ setInitialRenderState(ctx, { didInitialScroll: true });
3606
4105
  }
3607
- if (initialScroll.contentOffset !== void 0) {
3608
- return initialScroll.contentOffset;
3609
- }
3610
- const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, state, initialScroll.index) : 0;
3611
- const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, state, baseOffset, initialScroll);
3612
- let clampedOffset = resolvedOffset;
3613
- if (Number.isFinite(state.scrollLength) && Number.isFinite(state.totalSize)) {
3614
- const maxOffset = Math.max(0, state.totalSize - state.scrollLength);
3615
- clampedOffset = Math.min(clampedOffset, maxOffset);
3616
- }
3617
- clampedOffset = Math.max(0, clampedOffset);
3618
- const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
3619
- refState.current.initialScroll = updatedInitialScroll;
3620
- state.initialScroll = updatedInitialScroll;
3621
- refState.current.isStartReached = clampedOffset < refState.current.scrollLength * onStartReachedThreshold;
3622
- return clampedOffset;
4106
+ return value;
3623
4107
  }, [renderNum]);
3624
4108
  if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
3625
4109
  refState.current.lastBatchingAction = Date.now();
@@ -3647,12 +4131,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3647
4131
  }
3648
4132
  }, []);
3649
4133
  const doInitialScroll = React3.useCallback(() => {
3650
- var _a4;
3651
- const initialScroll = state.initialScroll;
3652
- if (initialScroll) {
3653
- scrollTo(ctx, state, {
4134
+ const { initialScroll, didFinishInitialScroll, queuedInitialLayout, scrollingTo } = state;
4135
+ if (initialScroll && !queuedInitialLayout && !didFinishInitialScroll && !scrollingTo) {
4136
+ scrollTo(ctx, {
3654
4137
  animated: false,
3655
- index: (_a4 = state.initialScroll) == null ? void 0 : _a4.index,
4138
+ index: initialScroll == null ? void 0 : initialScroll.index,
3656
4139
  isInitialScroll: true,
3657
4140
  offset: initialContentOffset,
3658
4141
  precomputedWithViewOffset: true
@@ -3661,7 +4144,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3661
4144
  }, [initialContentOffset]);
3662
4145
  const onLayoutChange = React3.useCallback((layout) => {
3663
4146
  doInitialScroll();
3664
- handleLayout(ctx, state, layout, setCanRender);
4147
+ handleLayout(ctx, layout, setCanRender);
3665
4148
  }, []);
3666
4149
  const { onLayout } = useOnLayoutSync({
3667
4150
  onLayoutChange,
@@ -3671,7 +4154,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3671
4154
  });
3672
4155
  React3.useLayoutEffect(() => {
3673
4156
  if (snapToIndices) {
3674
- updateSnapToOffsets(ctx, state);
4157
+ updateSnapToOffsets(ctx);
3675
4158
  }
3676
4159
  }, [snapToIndices]);
3677
4160
  React3.useLayoutEffect(() => {
@@ -3681,24 +4164,50 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3681
4164
  isFirst,
3682
4165
  props: { data }
3683
4166
  } = state;
3684
- const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx, state);
4167
+ const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx);
3685
4168
  if (!didAllocateContainers && !isFirst && (didDataChange || didColumnsChange)) {
3686
- checkResetContainers(ctx, state, data);
4169
+ checkResetContainers(ctx, data);
3687
4170
  }
3688
4171
  state.didColumnsChange = false;
3689
4172
  state.didDataChange = false;
3690
4173
  state.isFirst = false;
3691
4174
  }, [dataProp, dataVersion, numColumnsProp]);
3692
4175
  React3.useLayoutEffect(() => {
4176
+ var _a4;
3693
4177
  set$(ctx, "extraData", extraData);
3694
- }, [extraData]);
3695
- React3.useLayoutEffect(initializeStateVars, [
3696
- dataVersion,
3697
- memoizedLastItemKeys.join(","),
3698
- numColumnsProp,
3699
- stylePaddingBottomState,
3700
- stylePaddingTopState
3701
- ]);
4178
+ const didToggleOverride = prevHasOverrideItemLayout.current !== hasOverrideItemLayout;
4179
+ prevHasOverrideItemLayout.current = hasOverrideItemLayout;
4180
+ if ((hasOverrideItemLayout || didToggleOverride) && numColumnsProp > 1) {
4181
+ (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { forceFullItemPositions: true });
4182
+ }
4183
+ }, [extraData, hasOverrideItemLayout, numColumnsProp]);
4184
+ React3.useLayoutEffect(
4185
+ () => initializeStateVars(true),
4186
+ [dataVersion, memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingBottomState, stylePaddingTopState]
4187
+ );
4188
+ React3.useEffect(() => {
4189
+ if (!onMetricsChange) {
4190
+ return;
4191
+ }
4192
+ let lastMetrics;
4193
+ const emitMetrics = () => {
4194
+ const metrics = {
4195
+ footerSize: peek$(ctx, "footerSize") || 0,
4196
+ headerSize: peek$(ctx, "headerSize") || 0
4197
+ };
4198
+ if (!lastMetrics || metrics.headerSize !== lastMetrics.headerSize || metrics.footerSize !== lastMetrics.footerSize) {
4199
+ lastMetrics = metrics;
4200
+ onMetricsChange(metrics);
4201
+ }
4202
+ };
4203
+ emitMetrics();
4204
+ const unsubscribe = [listen$(ctx, "headerSize", emitMetrics), listen$(ctx, "footerSize", emitMetrics)];
4205
+ return () => {
4206
+ for (const unsub of unsubscribe) {
4207
+ unsub();
4208
+ }
4209
+ };
4210
+ }, [ctx, onMetricsChange]);
3702
4211
  React3.useEffect(() => {
3703
4212
  const viewability = setupViewability({
3704
4213
  onViewableItemsChanged,
@@ -3708,15 +4217,21 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3708
4217
  state.viewabilityConfigCallbackPairs = viewability;
3709
4218
  state.enableScrollForNextCalculateItemsInView = !viewability;
3710
4219
  }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
3711
- React3.useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx, state), []);
4220
+ React3.useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx), []);
3712
4221
  {
3713
4222
  React3.useEffect(doInitialScroll, []);
3714
4223
  }
3715
4224
  const fns = React3.useMemo(
3716
4225
  () => ({
3717
- getRenderedItem: (key) => getRenderedItem(ctx, state, key),
3718
- onScroll: (event) => onScroll(ctx, state, event),
3719
- updateItemSize: (itemKey, sizeObj) => updateItemSize(ctx, state, itemKey, sizeObj)
4226
+ getRenderedItem: (key) => getRenderedItem(ctx, key),
4227
+ onMomentumScrollEnd: (event) => {
4228
+ checkFinishedScrollFallback(ctx);
4229
+ if (onMomentumScrollEnd) {
4230
+ onMomentumScrollEnd(event);
4231
+ }
4232
+ },
4233
+ onScroll: (event) => onScroll(ctx, event),
4234
+ updateItemSize: (itemKey, sizeObj) => updateItemSize(ctx, itemKey, sizeObj)
3720
4235
  }),
3721
4236
  []
3722
4237
  );
@@ -3728,24 +4243,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3728
4243
  alignItemsAtEnd,
3729
4244
  canRender,
3730
4245
  contentContainerStyle,
4246
+ contentInset,
3731
4247
  getRenderedItem: fns.getRenderedItem,
3732
4248
  horizontal,
3733
4249
  initialContentOffset,
3734
4250
  ListEmptyComponent: dataProp.length === 0 ? ListEmptyComponent : void 0,
3735
4251
  ListHeaderComponent,
3736
- maintainVisibleContentPosition,
3737
4252
  onLayout,
3738
4253
  onLayoutHeader,
3739
- onMomentumScrollEnd: (event) => {
3740
- {
3741
- requestAnimationFrame(() => {
3742
- finishScrollTo(ctx, refState.current);
3743
- });
3744
- }
3745
- if (onMomentumScrollEnd) {
3746
- onMomentumScrollEnd(event);
3747
- }
3748
- },
4254
+ onMomentumScrollEnd: fns.onMomentumScrollEnd,
3749
4255
  onScroll: onScrollHandler,
3750
4256
  recycleItems,
3751
4257
  refreshControl: refreshControl ? stylePaddingTopState > 0 ? React3__namespace.cloneElement(refreshControl, {
@@ -3759,8 +4265,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3759
4265
  }
3760
4266
  ),
3761
4267
  refScrollView: combinedRef,
3762
- scrollAdjustHandler: (_b = refState.current) == null ? void 0 : _b.scrollAdjustHandler,
3763
- scrollEventThrottle: 16 ,
4268
+ scrollAdjustHandler: (_d = refState.current) == null ? void 0 : _d.scrollAdjustHandler,
4269
+ scrollEventThrottle: 0,
3764
4270
  snapToIndices,
3765
4271
  stickyHeaderIndices,
3766
4272
  style,
@@ -3771,6 +4277,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3771
4277
  });
3772
4278
 
3773
4279
  exports.LegendList = LegendList;
4280
+ exports.typedForwardRef = typedForwardRef;
4281
+ exports.typedMemo = typedMemo;
3774
4282
  exports.useIsLastItem = useIsLastItem;
3775
4283
  exports.useListScrollSize = useListScrollSize;
3776
4284
  exports.useRecyclingEffect = useRecyclingEffect;