@legendapp/list 3.0.0-beta.2 → 3.0.0-beta.21

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 +781 -10
  8. package/index.d.ts +781 -10
  9. package/index.js +973 -530
  10. package/index.mjs +973 -532
  11. package/index.native.d.mts +781 -10
  12. package/index.native.d.ts +781 -10
  13. package/index.native.js +981 -494
  14. package/index.native.mjs +980 -495
  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 +5 -2
  20. package/keyboard.d.ts +5 -2
  21. package/keyboard.js +232 -28
  22. package/keyboard.mjs +235 -31
  23. package/keyboard.native.d.mts +16 -0
  24. package/keyboard.native.d.ts +16 -0
  25. package/keyboard.native.js +318 -0
  26. package/keyboard.native.mjs +296 -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 -3670
  39. package/section-list.mjs +34 -3669
  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 -3449
  43. package/section-list.native.mjs +33 -3447
  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.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as React3 from 'react';
2
2
  import React3__default, { forwardRef, useReducer, useEffect, createContext, useRef, useState, useMemo, useCallback, useImperativeHandle, useLayoutEffect, memo, useContext } from 'react';
3
3
  import { useSyncExternalStore } from 'use-sync-external-store/shim';
4
- import { unstable_batchedUpdates } from 'react-dom';
4
+ import { unstable_batchedUpdates, flushSync } from 'react-dom';
5
5
 
6
6
  // src/components/LegendList.tsx
7
7
  forwardRef(function AnimatedView2(props, ref) {
@@ -12,31 +12,65 @@ var View = forwardRef(function View2(props, ref) {
12
12
  });
13
13
  var Text = View;
14
14
 
15
+ // src/state/getContentInsetEnd.ts
16
+ function getContentInsetEnd(state) {
17
+ var _a3;
18
+ const { props } = state;
19
+ const horizontal = props.horizontal;
20
+ let contentInset = props.contentInset;
21
+ if (!contentInset) {
22
+ const animatedInset = (_a3 = props.animatedProps) == null ? void 0 : _a3.contentInset;
23
+ if (animatedInset) {
24
+ if ("get" in animatedInset) {
25
+ contentInset = animatedInset.get();
26
+ } else {
27
+ contentInset = animatedInset;
28
+ }
29
+ }
30
+ }
31
+ return (horizontal ? contentInset == null ? void 0 : contentInset.right : contentInset == null ? void 0 : contentInset.bottom) || 0;
32
+ }
33
+
34
+ // src/state/getContentSize.ts
35
+ function getContentSize(ctx) {
36
+ var _a3;
37
+ const { values, state } = ctx;
38
+ const stylePaddingTop = values.get("stylePaddingTop") || 0;
39
+ const stylePaddingBottom = state.props.stylePaddingBottom || 0;
40
+ const headerSize = values.get("headerSize") || 0;
41
+ const footerSize = values.get("footerSize") || 0;
42
+ const contentInsetBottom = getContentInsetEnd(state);
43
+ const totalSize = (_a3 = state.pendingTotalSize) != null ? _a3 : values.get("totalSize");
44
+ return headerSize + footerSize + totalSize + stylePaddingTop + stylePaddingBottom + (contentInsetBottom || 0);
45
+ }
46
+
15
47
  // src/platform/Animated.tsx
16
48
  var createAnimatedValue = (value) => value;
17
49
 
18
50
  // src/state/state.tsx
19
51
  var ContextState = React3.createContext(null);
52
+ var contextNum = 0;
20
53
  function StateProvider({ children }) {
21
54
  const [value] = React3.useState(() => ({
22
55
  animatedScrollY: createAnimatedValue(0),
23
56
  columnWrapperStyle: void 0,
24
- internalState: void 0,
57
+ contextNum: contextNum++,
25
58
  listeners: /* @__PURE__ */ new Map(),
26
59
  mapViewabilityAmountCallbacks: /* @__PURE__ */ new Map(),
27
60
  mapViewabilityAmountValues: /* @__PURE__ */ new Map(),
28
61
  mapViewabilityCallbacks: /* @__PURE__ */ new Map(),
29
62
  mapViewabilityConfigStates: /* @__PURE__ */ new Map(),
30
63
  mapViewabilityValues: /* @__PURE__ */ new Map(),
64
+ positionListeners: /* @__PURE__ */ new Map(),
65
+ state: void 0,
31
66
  values: /* @__PURE__ */ new Map([
32
67
  ["alignItemsPaddingTop", 0],
33
68
  ["stylePaddingTop", 0],
34
69
  ["headerSize", 0],
35
70
  ["numContainers", 0],
36
- ["activeStickyIndex", void 0],
71
+ ["activeStickyIndex", -1],
37
72
  ["totalSize", 0],
38
- ["scrollAdjustPending", 0],
39
- ["scrollingTo", void 0]
73
+ ["scrollAdjustPending", 0]
40
74
  ]),
41
75
  viewRefs: /* @__PURE__ */ new Map()
42
76
  }));
@@ -104,15 +138,24 @@ function set$(ctx, signalName, value) {
104
138
  }
105
139
  }
106
140
  }
107
- function getContentSize(ctx) {
108
- var _a3, _b;
109
- const { values, internalState } = ctx;
110
- const stylePaddingTop = values.get("stylePaddingTop") || 0;
111
- const stylePaddingBottom = (internalState == null ? void 0 : internalState.props.stylePaddingBottom) || 0;
112
- const headerSize = values.get("headerSize") || 0;
113
- const footerSize = values.get("footerSize") || 0;
114
- const totalSize = (_b = (_a3 = ctx.internalState) == null ? void 0 : _a3.pendingTotalSize) != null ? _b : values.get("totalSize");
115
- return headerSize + footerSize + totalSize + stylePaddingTop + stylePaddingBottom;
141
+ function listenPosition$(ctx, key, cb) {
142
+ const { positionListeners } = ctx;
143
+ let setListeners = positionListeners.get(key);
144
+ if (!setListeners) {
145
+ setListeners = /* @__PURE__ */ new Set();
146
+ positionListeners.set(key, setListeners);
147
+ }
148
+ setListeners.add(cb);
149
+ return () => setListeners.delete(cb);
150
+ }
151
+ function notifyPosition$(ctx, key, value) {
152
+ const { positionListeners } = ctx;
153
+ const setListeners = positionListeners.get(key);
154
+ if (setListeners) {
155
+ for (const listener of setListeners) {
156
+ listener(value);
157
+ }
158
+ }
116
159
  }
117
160
  function useArr$(signalNames) {
118
161
  const ctx = React3.useContext(ContextState);
@@ -223,6 +266,11 @@ function extractPadding(style, contentContainerStyle, type) {
223
266
  return getPadding(style, type) + getPadding(contentContainerStyle, type);
224
267
  }
225
268
  function findContainerId(ctx, key) {
269
+ var _a3, _b;
270
+ const directMatch = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.containerItemKeys) == null ? void 0 : _b.get(key);
271
+ if (directMatch !== void 0) {
272
+ return directMatch;
273
+ }
226
274
  const numContainers = peek$(ctx, "numContainers");
227
275
  for (let i = 0; i < numContainers; i++) {
228
276
  const itemKey = peek$(ctx, `containerItemKey${i}`);
@@ -234,12 +282,12 @@ function findContainerId(ctx, key) {
234
282
  }
235
283
 
236
284
  // src/components/PositionView.tsx
237
- var PositionViewState = typedMemo(function PositionView({
285
+ var PositionViewState = typedMemo(function PositionViewState2({
238
286
  id,
239
287
  horizontal,
240
288
  style,
241
289
  refView,
242
- ...rest
290
+ ...props
243
291
  }) {
244
292
  const [position = POSITION_OUT_OF_VIEW] = useArr$([`containerPosition${id}`]);
245
293
  const base = {
@@ -247,7 +295,8 @@ var PositionViewState = typedMemo(function PositionView({
247
295
  };
248
296
  const composed = isArray(style) ? Object.assign({}, ...style) : style;
249
297
  const combinedStyle = horizontal ? { ...base, ...composed, left: position } : { ...base, ...composed, top: position };
250
- return /* @__PURE__ */ React3.createElement("div", { ref: refView, style: combinedStyle, ...rest });
298
+ const { animatedScrollY, stickyOffset, onLayout, index, ...webProps } = props;
299
+ return /* @__PURE__ */ React3.createElement("div", { ref: refView, ...webProps, style: combinedStyle });
251
300
  });
252
301
  var PositionViewSticky = typedMemo(function PositionViewSticky2({
253
302
  id,
@@ -292,63 +341,80 @@ var PositionViewSticky = typedMemo(function PositionViewSticky2({
292
341
  }, [composed, horizontal, position, index, stickyOffset, headerSize, activeStickyIndex]);
293
342
  return /* @__PURE__ */ React3.createElement("div", { ref: refView, style: viewStyle, ...rest }, children);
294
343
  });
295
- var PositionView2 = PositionViewState;
344
+ var PositionView = PositionViewState;
296
345
 
297
346
  // src/constants-platform.ts
298
347
  var IsNewArchitecture = true;
299
- var symbolFirst = Symbol();
300
348
  function useInit(cb) {
301
- const refValue = useRef(symbolFirst);
302
- if (refValue.current === symbolFirst) {
303
- refValue.current = cb();
304
- }
305
- return refValue.current;
349
+ useState(() => cb());
306
350
  }
307
351
 
308
352
  // src/state/ContextContainer.ts
309
353
  var ContextContainer = createContext(null);
354
+ function useContextContainer() {
355
+ return useContext(ContextContainer);
356
+ }
310
357
  function useViewability(callback, configId) {
311
358
  const ctx = useStateContext();
312
- const { containerId } = useContext(ContextContainer);
313
- const key = containerId + (configId != null ? configId : "");
359
+ const containerContext = useContextContainer();
314
360
  useInit(() => {
361
+ if (!containerContext) {
362
+ return;
363
+ }
364
+ const { containerId } = containerContext;
365
+ const key = containerId + (configId != null ? configId : "");
315
366
  const value = ctx.mapViewabilityValues.get(key);
316
367
  if (value) {
317
368
  callback(value);
318
369
  }
319
370
  });
320
- ctx.mapViewabilityCallbacks.set(key, callback);
321
- useEffect(
322
- () => () => {
371
+ useEffect(() => {
372
+ if (!containerContext) {
373
+ return;
374
+ }
375
+ const { containerId } = containerContext;
376
+ const key = containerId + (configId != null ? configId : "");
377
+ ctx.mapViewabilityCallbacks.set(key, callback);
378
+ return () => {
323
379
  ctx.mapViewabilityCallbacks.delete(key);
324
- },
325
- []
326
- );
380
+ };
381
+ }, [ctx, callback, configId, containerContext]);
327
382
  }
328
383
  function useViewabilityAmount(callback) {
329
384
  const ctx = useStateContext();
330
- const { containerId } = useContext(ContextContainer);
385
+ const containerContext = useContextContainer();
331
386
  useInit(() => {
387
+ if (!containerContext) {
388
+ return;
389
+ }
390
+ const { containerId } = containerContext;
332
391
  const value = ctx.mapViewabilityAmountValues.get(containerId);
333
392
  if (value) {
334
393
  callback(value);
335
394
  }
336
395
  });
337
- ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
338
- useEffect(
339
- () => () => {
396
+ useEffect(() => {
397
+ if (!containerContext) {
398
+ return;
399
+ }
400
+ const { containerId } = containerContext;
401
+ ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
402
+ return () => {
340
403
  ctx.mapViewabilityAmountCallbacks.delete(containerId);
341
- },
342
- []
343
- );
404
+ };
405
+ }, [ctx, callback, containerContext]);
344
406
  }
345
407
  function useRecyclingEffect(effect) {
346
- const { index, value } = useContext(ContextContainer);
408
+ const containerContext = useContextContainer();
347
409
  const prevValues = useRef({
348
410
  prevIndex: void 0,
349
411
  prevItem: void 0
350
412
  });
351
413
  useEffect(() => {
414
+ if (!containerContext) {
415
+ return;
416
+ }
417
+ const { index, value } = containerContext;
352
418
  let ret;
353
419
  if (prevValues.current.prevIndex !== void 0 && prevValues.current.prevItem !== void 0) {
354
420
  ret = effect({
@@ -363,48 +429,73 @@ function useRecyclingEffect(effect) {
363
429
  prevItem: value
364
430
  };
365
431
  return ret;
366
- }, [index, value, effect]);
432
+ }, [effect, containerContext]);
367
433
  }
368
434
  function useRecyclingState(valueOrFun) {
369
- const { index, value, itemKey, triggerLayout } = useContext(ContextContainer);
370
- const refState = useRef({
371
- itemKey: null,
372
- value: null
435
+ var _a3, _b;
436
+ const containerContext = useContextContainer();
437
+ const computeValue = (ctx) => {
438
+ if (isFunction(valueOrFun)) {
439
+ const initializer = valueOrFun;
440
+ return ctx ? initializer({
441
+ index: ctx.index,
442
+ item: ctx.value,
443
+ prevIndex: void 0,
444
+ prevItem: void 0
445
+ }) : initializer();
446
+ }
447
+ return valueOrFun;
448
+ };
449
+ const [stateValue, setStateValue] = useState(() => {
450
+ return computeValue(containerContext);
373
451
  });
374
- const [_, setRenderNum] = useState(0);
375
- const state = refState.current;
376
- if (state.itemKey !== itemKey) {
377
- state.itemKey = itemKey;
378
- state.value = isFunction(valueOrFun) ? valueOrFun({
379
- index,
380
- item: value,
381
- prevIndex: void 0,
382
- prevItem: void 0
383
- }) : valueOrFun;
452
+ const prevItemKeyRef = useRef((_a3 = containerContext == null ? void 0 : containerContext.itemKey) != null ? _a3 : null);
453
+ const currentItemKey = (_b = containerContext == null ? void 0 : containerContext.itemKey) != null ? _b : null;
454
+ if (currentItemKey !== null && prevItemKeyRef.current !== currentItemKey) {
455
+ prevItemKeyRef.current = currentItemKey;
456
+ setStateValue(computeValue(containerContext));
384
457
  }
458
+ const triggerLayout = containerContext == null ? void 0 : containerContext.triggerLayout;
385
459
  const setState = useCallback(
386
460
  (newState) => {
387
- state.value = isFunction(newState) ? newState(state.value) : newState;
388
- setRenderNum((v) => v + 1);
461
+ if (!triggerLayout) {
462
+ return;
463
+ }
464
+ setStateValue((prevValue) => {
465
+ return isFunction(newState) ? newState(prevValue) : newState;
466
+ });
389
467
  triggerLayout();
390
468
  },
391
- [triggerLayout, state]
469
+ [triggerLayout]
392
470
  );
393
- return [state.value, setState];
471
+ return [stateValue, setState];
394
472
  }
395
473
  function useIsLastItem() {
396
- const { itemKey } = useContext(ContextContainer);
397
- const isLast = useSelector$("lastItemKeys", (lastItemKeys) => (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false);
474
+ const containerContext = useContextContainer();
475
+ const isLast = useSelector$("lastItemKeys", (lastItemKeys) => {
476
+ if (containerContext) {
477
+ const { itemKey } = containerContext;
478
+ if (!isNullOrUndefined(itemKey)) {
479
+ return (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false;
480
+ }
481
+ }
482
+ return false;
483
+ });
398
484
  return isLast;
399
485
  }
400
486
  function useListScrollSize() {
401
487
  const [scrollSize] = useArr$(["scrollSize"]);
402
488
  return scrollSize;
403
489
  }
490
+ var noop = () => {
491
+ };
404
492
  function useSyncLayout() {
405
- {
406
- const { triggerLayout: syncLayout } = useContext(ContextContainer);
493
+ const containerContext = useContextContainer();
494
+ if (containerContext) {
495
+ const { triggerLayout: syncLayout } = containerContext;
407
496
  return syncLayout;
497
+ } else {
498
+ return noop;
408
499
  }
409
500
  }
410
501
 
@@ -450,10 +541,9 @@ function createResizeObserver(element, callback) {
450
541
  }
451
542
  callbacks.add(callback);
452
543
  return () => {
453
- const callbacks2 = callbackMap.get(element);
454
- if (callbacks2) {
455
- callbacks2.delete(callback);
456
- if (callbacks2.size === 0) {
544
+ if (callbacks) {
545
+ callbacks.delete(callback);
546
+ if (callbacks.size === 0) {
457
547
  callbackMap.delete(element);
458
548
  observer.unobserve(element);
459
549
  }
@@ -488,10 +578,10 @@ function useOnLayoutSync({
488
578
  return createResizeObserver(element, (entry) => {
489
579
  var _a4;
490
580
  const target = entry.target instanceof HTMLElement ? entry.target : void 0;
491
- const rect2 = (_a4 = entry.contentRect) != null ? _a4 : target == null ? void 0 : target.getBoundingClientRect();
492
- if (rect2.width !== prevRect.width || rect2.height !== prevRect.height) {
493
- prevRect = rect2;
494
- emit(toLayout(rect2), false);
581
+ const rectObserved = (_a4 = entry.contentRect) != null ? _a4 : target == null ? void 0 : target.getBoundingClientRect();
582
+ if (rectObserved.width !== prevRect.width || rectObserved.height !== prevRect.height) {
583
+ prevRect = rectObserved;
584
+ emit(toLayout(rectObserved), false);
495
585
  }
496
586
  });
497
587
  }, deps || []);
@@ -516,7 +606,8 @@ var Container = typedMemo(function Container2({
516
606
  horizontal,
517
607
  getRenderedItem: getRenderedItem2,
518
608
  updateItemSize: updateItemSize2,
519
- ItemSeparatorComponent
609
+ ItemSeparatorComponent,
610
+ stickyHeaderConfig
520
611
  }) {
521
612
  const ctx = useStateContext();
522
613
  const { columnWrapperStyle, animatedScrollY } = ctx;
@@ -619,7 +710,7 @@ var Container = typedMemo(function Container2({
619
710
  },
620
711
  [itemKey, layoutRenderCount]
621
712
  );
622
- const PositionComponent = isSticky ? PositionViewSticky : PositionView2;
713
+ const PositionComponent = isSticky ? PositionViewSticky : PositionView;
623
714
  return /* @__PURE__ */ React3.createElement(
624
715
  PositionComponent,
625
716
  {
@@ -630,6 +721,7 @@ var Container = typedMemo(function Container2({
630
721
  key: recycleItems ? void 0 : itemKey,
631
722
  onLayout,
632
723
  refView: ref,
724
+ stickyHeaderConfig,
633
725
  stickyOffset: isSticky ? stickyOffset : void 0,
634
726
  style
635
727
  },
@@ -789,7 +881,8 @@ var Containers = typedMemo(function Containers2({
789
881
  ItemSeparatorComponent,
790
882
  waitForInitialLayout,
791
883
  updateItemSize: updateItemSize2,
792
- getRenderedItem: getRenderedItem2
884
+ getRenderedItem: getRenderedItem2,
885
+ stickyHeaderConfig
793
886
  }) {
794
887
  const [numContainers, numColumns] = useArr$(["numContainersPooled", "numColumns"]);
795
888
  const containers = [];
@@ -804,6 +897,7 @@ var Containers = typedMemo(function Containers2({
804
897
  id: i,
805
898
  key: i,
806
899
  recycleItems,
900
+ stickyHeaderConfig,
807
901
  updateItemSize: updateItemSize2
808
902
  }
809
903
  )
@@ -812,7 +906,8 @@ var Containers = typedMemo(function Containers2({
812
906
  return /* @__PURE__ */ React3.createElement(ContainersInner, { horizontal, numColumns, waitForInitialLayout }, containers);
813
907
  });
814
908
  function DevNumbers() {
815
- return IS_DEV && React3.memo(function DevNumbers2() {
909
+ return IS_DEV && // biome-ignore lint/nursery/noShadow: const function name shadowing is intentional
910
+ React3.memo(function DevNumbers2() {
816
911
  return Array.from({ length: 100 }).map((_, index) => /* @__PURE__ */ React3.createElement(
817
912
  "div",
818
913
  {
@@ -860,7 +955,6 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
860
955
  }, ref) {
861
956
  const scrollRef = useRef(null);
862
957
  const contentRef = useRef(null);
863
- const momentumTimeout = useRef(null);
864
958
  useImperativeHandle(ref, () => {
865
959
  const api = {
866
960
  getBoundingClientRect: () => {
@@ -926,16 +1020,6 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
926
1020
  }
927
1021
  };
928
1022
  onScroll2(scrollEvent);
929
- if (onMomentumScrollEnd) {
930
- if (momentumTimeout.current != null) clearTimeout(momentumTimeout.current);
931
- momentumTimeout.current = setTimeout(() => {
932
- onMomentumScrollEnd({
933
- nativeEvent: {
934
- contentOffset: scrollEvent.nativeEvent.contentOffset
935
- }
936
- });
937
- }, 100);
938
- }
939
1023
  },
940
1024
  [onScroll2, onMomentumScrollEnd]
941
1025
  );
@@ -997,7 +1081,8 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
997
1081
  minWidth: horizontal ? "100%" : void 0,
998
1082
  ...StyleSheet.flatten(contentContainerStyle)
999
1083
  };
1000
- return /* @__PURE__ */ React3.createElement("div", { ref: scrollRef, style: scrollViewStyle, ...props }, refreshControl, /* @__PURE__ */ React3.createElement("div", { ref: contentRef, style: contentStyle }, children));
1084
+ const { contentInset, scrollEventThrottle, ScrollComponent, ...webProps } = props;
1085
+ return /* @__PURE__ */ React3.createElement("div", { ref: scrollRef, ...webProps, style: scrollViewStyle }, refreshControl, /* @__PURE__ */ React3.createElement("div", { ref: contentRef, style: contentStyle }, children));
1001
1086
  });
1002
1087
  function Padding() {
1003
1088
  const [paddingTop] = useArr$(["alignItemsPaddingTop"]);
@@ -1038,7 +1123,7 @@ function ScrollAdjust() {
1038
1123
  const scrollAdjust = peek$(ctx, "scrollAdjust");
1039
1124
  const scrollAdjustUserOffset = peek$(ctx, "scrollAdjustUserOffset");
1040
1125
  const scrollOffset = (scrollAdjust || 0) + (scrollAdjustUserOffset || 0);
1041
- const scrollView = (_a3 = ctx.internalState) == null ? void 0 : _a3.refScroller.current;
1126
+ const scrollView = (_a3 = ctx.state) == null ? void 0 : _a3.refScroller.current;
1042
1127
  if (scrollView && scrollOffset !== lastScrollOffsetRef.current) {
1043
1128
  const scrollDelta = scrollOffset - lastScrollOffsetRef.current;
1044
1129
  if (scrollDelta !== 0) {
@@ -1046,26 +1131,23 @@ function ScrollAdjust() {
1046
1131
  const prevScroll = el.scrollTop;
1047
1132
  const nextScroll = prevScroll + scrollDelta;
1048
1133
  const totalSize = el.scrollHeight;
1049
- if (scrollDelta > 0 && !ctx.internalState.adjustingFromInitialMount && totalSize < nextScroll + el.clientHeight) {
1134
+ if (scrollDelta > 0 && !ctx.state.adjustingFromInitialMount && totalSize < nextScroll + el.clientHeight) {
1050
1135
  const child = el.firstElementChild;
1051
1136
  const prevPaddingBottom = child.style.paddingBottom;
1052
1137
  const pad = (nextScroll + el.clientHeight - totalSize) * 2;
1053
1138
  child.style.paddingBottom = `${pad}px`;
1054
1139
  void el.offsetHeight;
1055
1140
  scrollView.scrollBy(0, scrollDelta);
1056
- setTimeout(() => {
1141
+ requestAnimationFrame(() => {
1057
1142
  child.style.paddingBottom = prevPaddingBottom;
1058
- }, 100);
1143
+ });
1059
1144
  } else {
1060
1145
  scrollView.scrollBy(0, scrollDelta);
1061
1146
  }
1062
- if (IS_DEV) {
1063
- console.log("ScrollAdjust (web scrollBy)", scrollDelta, "total offset:", scrollOffset);
1064
- }
1065
1147
  }
1066
1148
  lastScrollOffsetRef.current = scrollOffset;
1067
1149
  }
1068
- }, []);
1150
+ }, [ctx]);
1069
1151
  useValueListener$("scrollAdjust", callback);
1070
1152
  useValueListener$("scrollAdjustUserOffset", callback);
1071
1153
  return null;
@@ -1079,8 +1161,6 @@ var LayoutView = ({ onLayoutChange, refView, children, ...rest }) => {
1079
1161
  useOnLayoutSync({ onLayoutChange, ref });
1080
1162
  return /* @__PURE__ */ React3.createElement("div", { ...rest, ref }, children);
1081
1163
  };
1082
-
1083
- // src/components/ListComponent.tsx
1084
1164
  var getComponent = (Component) => {
1085
1165
  if (React3.isValidElement(Component)) {
1086
1166
  return Component;
@@ -1090,6 +1170,8 @@ var getComponent = (Component) => {
1090
1170
  }
1091
1171
  return null;
1092
1172
  };
1173
+
1174
+ // src/components/ListComponent.tsx
1093
1175
  var ListComponent = typedMemo(function ListComponent2({
1094
1176
  canRender,
1095
1177
  style,
@@ -1110,26 +1192,20 @@ var ListComponent = typedMemo(function ListComponent2({
1110
1192
  getRenderedItem: getRenderedItem2,
1111
1193
  updateItemSize: updateItemSize2,
1112
1194
  refScrollView,
1113
- maintainVisibleContentPosition,
1114
1195
  renderScrollComponent,
1115
1196
  scrollAdjustHandler,
1116
1197
  onLayoutHeader,
1117
1198
  snapToIndices,
1199
+ stickyHeaderConfig,
1118
1200
  stickyHeaderIndices,
1119
1201
  ...rest
1120
1202
  }) {
1121
1203
  const ctx = useStateContext();
1204
+ const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
1122
1205
  const ScrollComponent = renderScrollComponent ? useMemo(
1123
1206
  () => React3.forwardRef((props, ref) => renderScrollComponent({ ...props, ref })),
1124
1207
  [renderScrollComponent]
1125
1208
  ) : ListComponentScrollView;
1126
- React3.useEffect(() => {
1127
- if (canRender) {
1128
- setTimeout(() => {
1129
- scrollAdjustHandler.setMounted();
1130
- }, 0);
1131
- }
1132
- }, [canRender]);
1133
1209
  const SnapOrScroll = snapToIndices ? SnapWrapper : ScrollComponent;
1134
1210
  return /* @__PURE__ */ React3.createElement(
1135
1211
  SnapOrScroll,
@@ -1143,7 +1219,7 @@ var ListComponent = typedMemo(function ListComponent2({
1143
1219
  ],
1144
1220
  contentOffset: initialContentOffset ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
1145
1221
  horizontal,
1146
- maintainVisibleContentPosition: maintainVisibleContentPosition ? { minIndexForVisible: 0 } : void 0,
1222
+ maintainVisibleContentPosition: maintainVisibleContentPosition.size || maintainVisibleContentPosition.data ? { minIndexForVisible: 0 } : void 0,
1147
1223
  onLayout,
1148
1224
  onScroll: onScroll2,
1149
1225
  ref: refScrollView,
@@ -1161,6 +1237,7 @@ var ListComponent = typedMemo(function ListComponent2({
1161
1237
  horizontal,
1162
1238
  ItemSeparatorComponent,
1163
1239
  recycleItems,
1240
+ stickyHeaderConfig,
1164
1241
  updateItemSize: updateItemSize2,
1165
1242
  waitForInitialLayout
1166
1243
  }
@@ -1193,10 +1270,11 @@ function getId(state, index) {
1193
1270
  }
1194
1271
 
1195
1272
  // src/core/calculateOffsetForIndex.ts
1196
- function calculateOffsetForIndex(ctx, state, index) {
1273
+ function calculateOffsetForIndex(ctx, index) {
1274
+ const state = ctx.state;
1197
1275
  let position = 0;
1198
1276
  if (index !== void 0) {
1199
- position = (state == null ? void 0 : state.positions.get(getId(state, index))) || 0;
1277
+ position = state.positions.get(getId(state, index)) || 0;
1200
1278
  const paddingTop = peek$(ctx, "stylePaddingTop");
1201
1279
  if (paddingTop) {
1202
1280
  position += paddingTop;
@@ -1210,7 +1288,8 @@ function calculateOffsetForIndex(ctx, state, index) {
1210
1288
  }
1211
1289
 
1212
1290
  // src/utils/setPaddingTop.ts
1213
- function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
1291
+ function setPaddingTop(ctx, { stylePaddingTop, alignItemsPaddingTop }) {
1292
+ const state = ctx.state;
1214
1293
  if (stylePaddingTop !== void 0) {
1215
1294
  const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
1216
1295
  if (stylePaddingTop < prevStylePaddingTop) {
@@ -1229,7 +1308,8 @@ function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
1229
1308
  }
1230
1309
 
1231
1310
  // src/utils/updateAlignItemsPaddingTop.ts
1232
- function updateAlignItemsPaddingTop(ctx, state) {
1311
+ function updateAlignItemsPaddingTop(ctx) {
1312
+ const state = ctx.state;
1233
1313
  const {
1234
1314
  scrollLength,
1235
1315
  props: { alignItemsAtEnd, data }
@@ -1240,12 +1320,13 @@ function updateAlignItemsPaddingTop(ctx, state) {
1240
1320
  const contentSize = getContentSize(ctx);
1241
1321
  alignItemsPaddingTop = Math.max(0, Math.floor(scrollLength - contentSize));
1242
1322
  }
1243
- setPaddingTop(ctx, state, { alignItemsPaddingTop });
1323
+ setPaddingTop(ctx, { alignItemsPaddingTop });
1244
1324
  }
1245
1325
  }
1246
1326
 
1247
1327
  // src/core/addTotalSize.ts
1248
- function addTotalSize(ctx, state, key, add) {
1328
+ function addTotalSize(ctx, key, add) {
1329
+ const state = ctx.state;
1249
1330
  const { alignItemsAtEnd } = state.props;
1250
1331
  const prevTotalSize = state.totalSize;
1251
1332
  let totalSize = state.totalSize;
@@ -1264,31 +1345,34 @@ function addTotalSize(ctx, state, key, add) {
1264
1345
  state.totalSize = totalSize;
1265
1346
  set$(ctx, "totalSize", totalSize);
1266
1347
  if (alignItemsAtEnd) {
1267
- updateAlignItemsPaddingTop(ctx, state);
1348
+ updateAlignItemsPaddingTop(ctx);
1268
1349
  }
1269
1350
  }
1270
1351
  }
1271
1352
  }
1272
1353
 
1273
1354
  // src/core/setSize.ts
1274
- function setSize(ctx, state, itemKey, size) {
1355
+ function setSize(ctx, itemKey, size) {
1356
+ const state = ctx.state;
1275
1357
  const { sizes } = state;
1276
1358
  const previousSize = sizes.get(itemKey);
1277
1359
  const diff = previousSize !== void 0 ? size - previousSize : size;
1278
1360
  if (diff !== 0) {
1279
- addTotalSize(ctx, state, itemKey, diff);
1361
+ addTotalSize(ctx, itemKey, diff);
1280
1362
  }
1281
1363
  sizes.set(itemKey, size);
1282
1364
  }
1283
1365
 
1284
1366
  // src/utils/getItemSize.ts
1285
- function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedSize) {
1367
+ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
1286
1368
  var _a3, _b;
1369
+ const state = ctx.state;
1287
1370
  const {
1288
1371
  sizesKnown,
1289
1372
  sizes,
1290
1373
  averageSizes,
1291
- props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType }
1374
+ props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType },
1375
+ scrollingTo
1292
1376
  } = state;
1293
1377
  const sizeKnown = sizesKnown.get(key);
1294
1378
  if (sizeKnown !== void 0) {
@@ -1296,7 +1380,6 @@ function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedS
1296
1380
  }
1297
1381
  let size;
1298
1382
  const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1299
- const scrollingTo = peek$(ctx, "scrollingTo");
1300
1383
  if (preferCachedSize) {
1301
1384
  const cachedSize = sizes.get(key);
1302
1385
  if (cachedSize !== void 0) {
@@ -1304,7 +1387,7 @@ function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedS
1304
1387
  }
1305
1388
  }
1306
1389
  if (getFixedItemSize) {
1307
- size = getFixedItemSize(index, data, itemType);
1390
+ size = getFixedItemSize(data, index, itemType);
1308
1391
  if (size !== void 0) {
1309
1392
  sizesKnown.set(key, size);
1310
1393
  }
@@ -1322,93 +1405,188 @@ function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedS
1322
1405
  }
1323
1406
  }
1324
1407
  if (size === void 0) {
1325
- size = getEstimatedItemSize ? getEstimatedItemSize(index, data, itemType) : estimatedItemSize;
1408
+ size = getEstimatedItemSize ? getEstimatedItemSize(data, index, itemType) : estimatedItemSize;
1326
1409
  }
1327
- setSize(ctx, state, key, size);
1410
+ setSize(ctx, key, size);
1328
1411
  return size;
1329
1412
  }
1330
1413
 
1331
1414
  // src/core/calculateOffsetWithOffsetPosition.ts
1332
- function calculateOffsetWithOffsetPosition(ctx, state, offsetParam, params) {
1415
+ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1416
+ const state = ctx.state;
1333
1417
  const { index, viewOffset, viewPosition } = params;
1334
1418
  let offset = offsetParam;
1335
1419
  if (viewOffset) {
1336
1420
  offset -= viewOffset;
1337
1421
  }
1338
1422
  if (viewPosition !== void 0 && index !== void 0) {
1339
- offset -= viewPosition * (state.scrollLength - getItemSize(ctx, state, getId(state, index), index, state.props.data[index]));
1423
+ const itemSize = getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1424
+ const trailingInset = getContentInsetEnd(state);
1425
+ offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1340
1426
  }
1341
1427
  return offset;
1342
1428
  }
1343
1429
 
1430
+ // src/core/clampScrollOffset.ts
1431
+ function clampScrollOffset(ctx, offset) {
1432
+ const state = ctx.state;
1433
+ const contentSize = getContentSize(ctx);
1434
+ let clampedOffset = offset;
1435
+ if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform.OS !== "android")) {
1436
+ const maxOffset = Math.max(0, contentSize - state.scrollLength);
1437
+ clampedOffset = Math.min(offset, maxOffset);
1438
+ }
1439
+ clampedOffset = Math.max(0, clampedOffset);
1440
+ return clampedOffset;
1441
+ }
1442
+
1443
+ // src/utils/setInitialRenderState.ts
1444
+ function setInitialRenderState(ctx, {
1445
+ didLayout,
1446
+ didInitialScroll
1447
+ }) {
1448
+ const { state } = ctx;
1449
+ if (didLayout) {
1450
+ state.didContainersLayout = true;
1451
+ }
1452
+ if (didInitialScroll) {
1453
+ state.didFinishInitialScroll = true;
1454
+ }
1455
+ if (state.didContainersLayout && state.didFinishInitialScroll) {
1456
+ set$(ctx, "readyToRender", true);
1457
+ }
1458
+ }
1459
+
1344
1460
  // src/core/finishScrollTo.ts
1345
- function finishScrollTo(ctx, state) {
1461
+ function finishScrollTo(ctx) {
1346
1462
  var _a3, _b;
1347
- if (state) {
1463
+ const state = ctx.state;
1464
+ if (state == null ? void 0 : state.scrollingTo) {
1465
+ const scrollingTo = state.scrollingTo;
1348
1466
  state.scrollHistory.length = 0;
1349
1467
  state.initialScroll = void 0;
1350
1468
  state.initialAnchor = void 0;
1351
- set$(ctx, "scrollingTo", void 0);
1469
+ state.scrollingTo = void 0;
1352
1470
  if (state.pendingTotalSize !== void 0) {
1353
- addTotalSize(ctx, state, null, state.pendingTotalSize);
1471
+ addTotalSize(ctx, null, state.pendingTotalSize);
1354
1472
  }
1355
1473
  if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1356
1474
  (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1357
1475
  }
1476
+ {
1477
+ state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
1478
+ }
1479
+ setInitialRenderState(ctx, { didInitialScroll: true });
1358
1480
  }
1359
1481
  }
1360
1482
 
1483
+ // src/core/doScrollTo.ts
1484
+ var SCROLL_END_IDLE_MS = 80;
1485
+ var SCROLL_END_MAX_MS = 1500;
1486
+ var SMOOTH_SCROLL_DURATION_MS = 320;
1487
+ function doScrollTo(ctx, params) {
1488
+ const state = ctx.state;
1489
+ const { animated, horizontal, offset } = params;
1490
+ const scroller = state.refScroller.current;
1491
+ const node = typeof (scroller == null ? void 0 : scroller.getScrollableNode) === "function" ? scroller.getScrollableNode() : scroller;
1492
+ if (node) {
1493
+ const left = horizontal ? offset : 0;
1494
+ const top = horizontal ? 0 : offset;
1495
+ node.scrollTo({ behavior: animated ? "smooth" : "auto", left, top });
1496
+ if (animated) {
1497
+ listenForScrollEnd(ctx, node);
1498
+ } else {
1499
+ state.scroll = offset;
1500
+ setTimeout(() => {
1501
+ finishScrollTo(ctx);
1502
+ }, 100);
1503
+ }
1504
+ }
1505
+ }
1506
+ function listenForScrollEnd(ctx, node) {
1507
+ const supportsScrollEnd = "onscrollend" in node;
1508
+ let idleTimeout;
1509
+ let maxTimeout;
1510
+ let settled = false;
1511
+ const targetToken = ctx.state.scrollingTo;
1512
+ const finish = () => {
1513
+ if (settled) return;
1514
+ settled = true;
1515
+ cleanup();
1516
+ if (targetToken === ctx.state.scrollingTo) {
1517
+ finishScrollTo(ctx);
1518
+ }
1519
+ };
1520
+ const onScroll2 = () => {
1521
+ if (idleTimeout) {
1522
+ clearTimeout(idleTimeout);
1523
+ }
1524
+ idleTimeout = setTimeout(finish, SCROLL_END_IDLE_MS);
1525
+ };
1526
+ const cleanup = () => {
1527
+ if (supportsScrollEnd) {
1528
+ node.removeEventListener("scrollend", finish);
1529
+ } else {
1530
+ node.removeEventListener("scroll", onScroll2);
1531
+ }
1532
+ if (idleTimeout) {
1533
+ clearTimeout(idleTimeout);
1534
+ }
1535
+ if (maxTimeout) {
1536
+ clearTimeout(maxTimeout);
1537
+ }
1538
+ };
1539
+ if (supportsScrollEnd) {
1540
+ node.addEventListener("scrollend", finish, { once: true });
1541
+ } else {
1542
+ node.addEventListener("scroll", onScroll2);
1543
+ idleTimeout = setTimeout(finish, SMOOTH_SCROLL_DURATION_MS);
1544
+ maxTimeout = setTimeout(finish, SCROLL_END_MAX_MS);
1545
+ }
1546
+ return cleanup;
1547
+ }
1548
+
1361
1549
  // src/core/scrollTo.ts
1362
- function scrollTo(ctx, state, params) {
1363
- var _a3;
1364
- const { noScrollingTo, ...scrollTarget } = params;
1550
+ function scrollTo(ctx, params) {
1551
+ const state = ctx.state;
1552
+ const { noScrollingTo, forceScroll, ...scrollTarget } = params;
1365
1553
  const { animated, isInitialScroll, offset: scrollTargetOffset, precomputedWithViewOffset } = scrollTarget;
1366
1554
  const {
1367
- refScroller,
1368
1555
  props: { horizontal }
1369
1556
  } = state;
1370
- let offset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, state, scrollTargetOffset, scrollTarget);
1371
- if (Number.isFinite(state.scrollLength) && Number.isFinite(state.totalSize)) {
1372
- const maxOffset = Math.max(0, getContentSize(ctx) - state.scrollLength);
1373
- offset = Math.min(offset, maxOffset);
1557
+ if (state.animFrameCheckFinishedScroll) {
1558
+ cancelAnimationFrame(ctx.state.animFrameCheckFinishedScroll);
1374
1559
  }
1560
+ if (state.timeoutCheckFinishedScrollFallback) {
1561
+ clearTimeout(ctx.state.timeoutCheckFinishedScrollFallback);
1562
+ }
1563
+ let offset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, scrollTargetOffset, scrollTarget);
1564
+ offset = clampScrollOffset(ctx, offset);
1375
1565
  state.scrollHistory.length = 0;
1376
1566
  if (!noScrollingTo) {
1377
- set$(ctx, "scrollingTo", scrollTarget);
1567
+ state.scrollingTo = scrollTarget;
1378
1568
  }
1379
1569
  state.scrollPending = offset;
1380
- if (!isInitialScroll || Platform.OS === "android") {
1381
- (_a3 = refScroller.current) == null ? void 0 : _a3.scrollTo({
1382
- animated: !!animated,
1383
- x: horizontal ? offset : 0,
1384
- y: horizontal ? 0 : offset
1385
- });
1386
- }
1387
- if (!animated) {
1570
+ if (forceScroll || !isInitialScroll || Platform.OS === "android") {
1571
+ doScrollTo(ctx, { animated, horizontal, isInitialScroll, offset });
1572
+ } else {
1388
1573
  state.scroll = offset;
1389
- {
1390
- const unlisten = listen$(ctx, "containersDidLayout", (value) => {
1391
- if (value && peek$(ctx, "scrollingTo")) {
1392
- finishScrollTo(ctx, state);
1393
- unlisten();
1394
- }
1395
- });
1396
- }
1397
- if (isInitialScroll) {
1398
- setTimeout(() => {
1399
- state.initialScroll = void 0;
1400
- }, 500);
1401
- }
1402
1574
  }
1403
1575
  }
1404
1576
 
1405
1577
  // src/utils/checkThreshold.ts
1406
1578
  var HYSTERESIS_MULTIPLIER = 1.3;
1407
- var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot) => {
1579
+ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot, allowReentryOnChange) => {
1408
1580
  const absDistance = Math.abs(distance);
1409
1581
  const within = atThreshold || threshold > 0 && absDistance <= threshold;
1582
+ if (wasReached === null) {
1583
+ if (!within && distance >= 0) {
1584
+ return false;
1585
+ }
1586
+ return null;
1587
+ }
1410
1588
  const updateSnapshot = () => {
1411
- setSnapshot == null ? void 0 : setSnapshot({
1589
+ setSnapshot({
1412
1590
  atThreshold,
1413
1591
  contentSize: context.contentSize,
1414
1592
  dataLength: context.dataLength,
@@ -1419,19 +1597,21 @@ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, co
1419
1597
  if (!within) {
1420
1598
  return false;
1421
1599
  }
1422
- onReached == null ? void 0 : onReached(distance);
1600
+ onReached(distance);
1423
1601
  updateSnapshot();
1424
1602
  return true;
1425
1603
  }
1426
1604
  const reset = !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
1427
1605
  if (reset) {
1428
- setSnapshot == null ? void 0 : setSnapshot(void 0);
1606
+ setSnapshot(void 0);
1429
1607
  return false;
1430
1608
  }
1431
1609
  if (within) {
1432
1610
  const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
1433
1611
  if (changed) {
1434
- onReached == null ? void 0 : onReached(distance);
1612
+ if (allowReentryOnChange) {
1613
+ onReached(distance);
1614
+ }
1435
1615
  updateSnapshot();
1436
1616
  }
1437
1617
  }
@@ -1439,8 +1619,9 @@ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, co
1439
1619
  };
1440
1620
 
1441
1621
  // src/utils/checkAtBottom.ts
1442
- function checkAtBottom(ctx, state) {
1622
+ function checkAtBottom(ctx) {
1443
1623
  var _a3;
1624
+ const state = ctx.state;
1444
1625
  if (!state) {
1445
1626
  return;
1446
1627
  }
@@ -1473,7 +1654,8 @@ function checkAtBottom(ctx, state) {
1473
1654
  },
1474
1655
  (snapshot) => {
1475
1656
  state.endReachedSnapshot = snapshot;
1476
- }
1657
+ },
1658
+ true
1477
1659
  );
1478
1660
  }
1479
1661
  }
@@ -1508,20 +1690,21 @@ function checkAtTop(state) {
1508
1690
  },
1509
1691
  (snapshot) => {
1510
1692
  state.startReachedSnapshot = snapshot;
1511
- }
1693
+ },
1694
+ false
1512
1695
  );
1513
1696
  }
1514
1697
 
1515
1698
  // src/core/updateScroll.ts
1516
- function updateScroll(ctx, state, newScroll, forceUpdate) {
1517
- var _a3;
1518
- const scrollingTo = peek$(ctx, "scrollingTo");
1699
+ function updateScroll(ctx, newScroll, forceUpdate) {
1700
+ const state = ctx.state;
1701
+ const { scrollingTo, scrollAdjustHandler, lastScrollAdjustForHistory } = state;
1702
+ const prevScroll = state.scroll;
1519
1703
  state.hasScrolled = true;
1520
1704
  state.lastBatchingAction = Date.now();
1521
1705
  const currentTime = Date.now();
1522
- const adjust = state.scrollAdjustHandler.getAdjust();
1523
- const lastHistoryAdjust = state.lastScrollAdjustForHistory;
1524
- const adjustChanged = lastHistoryAdjust !== void 0 && Math.abs(adjust - lastHistoryAdjust) > 0.1;
1706
+ const adjust = scrollAdjustHandler.getAdjust();
1707
+ const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
1525
1708
  if (adjustChanged) {
1526
1709
  state.scrollHistory.length = 0;
1527
1710
  }
@@ -1534,7 +1717,7 @@ function updateScroll(ctx, state, newScroll, forceUpdate) {
1534
1717
  if (state.scrollHistory.length > 5) {
1535
1718
  state.scrollHistory.shift();
1536
1719
  }
1537
- state.scrollPrev = state.scroll;
1720
+ state.scrollPrev = prevScroll;
1538
1721
  state.scrollPrevTime = state.scrollTime;
1539
1722
  state.scroll = newScroll;
1540
1723
  state.scrollTime = currentTime;
@@ -1546,17 +1729,33 @@ function updateScroll(ctx, state, newScroll, forceUpdate) {
1546
1729
  return;
1547
1730
  }
1548
1731
  }
1549
- if (state.dataChangeNeedsScrollUpdate || Math.abs(state.scroll - state.scrollPrev) > 2) {
1732
+ const scrollDelta = Math.abs(newScroll - prevScroll);
1733
+ const scrollLength = state.scrollLength;
1734
+ const lastCalculated = state.scrollLastCalculate;
1735
+ const shouldUpdate = state.dataChangeNeedsScrollUpdate || state.scrollLastCalculate === void 0 || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
1736
+ if (shouldUpdate) {
1737
+ state.scrollLastCalculate = state.scroll;
1550
1738
  state.ignoreScrollFromMVCPIgnored = false;
1551
- (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { doMVCP: scrollingTo !== void 0 });
1552
- checkAtBottom(ctx, state);
1553
- checkAtTop(state);
1739
+ state.lastScrollDelta = scrollDelta;
1740
+ const runCalculateItems = () => {
1741
+ var _a3;
1742
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { doMVCP: scrollingTo !== void 0 });
1743
+ checkAtBottom(ctx);
1744
+ checkAtTop(state);
1745
+ };
1746
+ if (scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength) {
1747
+ flushSync(runCalculateItems);
1748
+ } else {
1749
+ runCalculateItems();
1750
+ }
1554
1751
  state.dataChangeNeedsScrollUpdate = false;
1752
+ state.lastScrollDelta = 0;
1555
1753
  }
1556
1754
  }
1557
1755
 
1558
1756
  // src/utils/requestAdjust.ts
1559
- function requestAdjust(ctx, state, positionDiff, dataChanged) {
1757
+ function requestAdjust(ctx, positionDiff, dataChanged) {
1758
+ const state = ctx.state;
1560
1759
  if (Math.abs(positionDiff) > 0.1) {
1561
1760
  const doit = () => {
1562
1761
  {
@@ -1568,8 +1767,8 @@ function requestAdjust(ctx, state, positionDiff, dataChanged) {
1568
1767
  };
1569
1768
  state.scroll += positionDiff;
1570
1769
  state.scrollForNextCalculateItemsInView = void 0;
1571
- const didLayout = peek$(ctx, "containersDidLayout");
1572
- if (didLayout) {
1770
+ const readyToRender = peek$(ctx, "readyToRender");
1771
+ if (readyToRender) {
1573
1772
  doit();
1574
1773
  } else {
1575
1774
  state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
@@ -1578,73 +1777,25 @@ function requestAdjust(ctx, state, positionDiff, dataChanged) {
1578
1777
  }
1579
1778
  }
1580
1779
 
1581
- // src/core/ensureInitialAnchor.ts
1582
- var INITIAL_ANCHOR_TOLERANCE = 0.5;
1583
- var INITIAL_ANCHOR_MAX_ATTEMPTS = 4;
1584
- var INITIAL_ANCHOR_SETTLED_TICKS = 2;
1585
- function ensureInitialAnchor(ctx, state) {
1586
- var _a3, _b, _c, _d, _e;
1587
- const anchor = state.initialAnchor;
1588
- const item = state.props.data[anchor.index];
1589
- const containersDidLayout = peek$(ctx, "containersDidLayout");
1590
- if (!containersDidLayout) {
1591
- return;
1592
- }
1593
- const id = getId(state, anchor.index);
1594
- if (state.positions.get(id) === void 0) {
1595
- return;
1596
- }
1597
- const size = getItemSize(ctx, state, id, anchor.index, item, true, true);
1598
- if (size === void 0) {
1599
- return;
1600
- }
1601
- const availableSpace = Math.max(0, state.scrollLength - size);
1602
- const desiredOffset = calculateOffsetForIndex(ctx, state, anchor.index) - ((_a3 = anchor.viewOffset) != null ? _a3 : 0) - ((_b = anchor.viewPosition) != null ? _b : 0) * availableSpace;
1603
- const contentSize = getContentSize(ctx);
1604
- const maxOffset = Math.max(0, contentSize - state.scrollLength);
1605
- const clampedDesiredOffset = Math.max(0, Math.min(desiredOffset, maxOffset));
1606
- const delta = clampedDesiredOffset - state.scroll;
1607
- if (Math.abs(delta) <= INITIAL_ANCHOR_TOLERANCE) {
1608
- const settledTicks = ((_c = anchor.settledTicks) != null ? _c : 0) + 1;
1609
- if (settledTicks >= INITIAL_ANCHOR_SETTLED_TICKS) {
1610
- state.initialAnchor = void 0;
1611
- } else {
1612
- anchor.settledTicks = settledTicks;
1613
- }
1614
- return;
1615
- }
1616
- if (((_d = anchor.attempts) != null ? _d : 0) >= INITIAL_ANCHOR_MAX_ATTEMPTS) {
1617
- state.initialAnchor = void 0;
1618
- return;
1619
- }
1620
- const lastDelta = anchor.lastDelta;
1621
- if (lastDelta !== void 0 && Math.abs(delta) >= Math.abs(lastDelta)) {
1622
- state.initialAnchor = void 0;
1623
- return;
1624
- }
1625
- Object.assign(anchor, {
1626
- attempts: ((_e = anchor.attempts) != null ? _e : 0) + 1,
1627
- lastDelta: delta,
1628
- settledTicks: 0
1629
- });
1630
- requestAdjust(ctx, state, delta);
1631
- }
1632
-
1633
1780
  // src/core/mvcp.ts
1634
- function prepareMVCP(ctx, state, dataChanged) {
1781
+ function prepareMVCP(ctx, dataChanged) {
1782
+ const state = ctx.state;
1635
1783
  const { idsInView, positions, props } = state;
1636
- const { maintainVisibleContentPosition } = props;
1637
- const scrollingTo = peek$(ctx, "scrollingTo");
1784
+ const {
1785
+ maintainVisibleContentPosition: { data: mvcpData, size: mvcpScroll, shouldRestorePosition }
1786
+ } = props;
1787
+ const scrollingTo = state.scrollingTo;
1638
1788
  let prevPosition;
1639
1789
  let targetId;
1640
1790
  const idsInViewWithPositions = [];
1641
1791
  const scrollTarget = scrollingTo == null ? void 0 : scrollingTo.index;
1642
- const shouldMVCP = !dataChanged || maintainVisibleContentPosition;
1792
+ const scrollingToViewPosition = scrollingTo == null ? void 0 : scrollingTo.viewPosition;
1793
+ const shouldMVCP = dataChanged ? mvcpData : mvcpScroll;
1643
1794
  const indexByKey = state.indexByKey;
1644
1795
  if (shouldMVCP) {
1645
1796
  if (scrollTarget !== void 0) {
1646
1797
  targetId = getId(state, scrollTarget);
1647
- } else if (idsInView.length > 0 && peek$(ctx, "containersDidLayout")) {
1798
+ } else if (idsInView.length > 0 && state.didContainersLayout) {
1648
1799
  if (dataChanged) {
1649
1800
  for (let i = 0; i < idsInView.length; i++) {
1650
1801
  const id = idsInView[i];
@@ -1661,10 +1812,18 @@ function prepareMVCP(ctx, state, dataChanged) {
1661
1812
  prevPosition = positions.get(targetId);
1662
1813
  }
1663
1814
  return () => {
1664
- let positionDiff;
1665
- if (dataChanged && targetId === void 0 && maintainVisibleContentPosition) {
1815
+ let positionDiff = 0;
1816
+ if (dataChanged && targetId === void 0 && mvcpData) {
1817
+ const data = state.props.data;
1666
1818
  for (let i = 0; i < idsInViewWithPositions.length; i++) {
1667
1819
  const { id, position } = idsInViewWithPositions[i];
1820
+ const index = indexByKey.get(id);
1821
+ if (index !== void 0 && shouldRestorePosition) {
1822
+ const item = data[index];
1823
+ if (item === void 0 || !shouldRestorePosition(item, index, data)) {
1824
+ continue;
1825
+ }
1826
+ }
1668
1827
  const newPosition = positions.get(id);
1669
1828
  if (newPosition !== void 0) {
1670
1829
  positionDiff = newPosition - position;
@@ -1687,16 +1846,28 @@ function prepareMVCP(ctx, state, dataChanged) {
1687
1846
  positionDiff = diff;
1688
1847
  }
1689
1848
  }
1690
- if (positionDiff !== void 0 && Math.abs(positionDiff) > 0.1) {
1691
- requestAdjust(ctx, state, positionDiff);
1849
+ if (scrollingToViewPosition && scrollingToViewPosition > 0) {
1850
+ const newSize = getItemSize(ctx, targetId, scrollTarget, state.props.data[scrollTarget]);
1851
+ const prevSize = scrollingTo == null ? void 0 : scrollingTo.itemSize;
1852
+ if (newSize !== void 0 && prevSize !== void 0 && newSize !== (scrollingTo == null ? void 0 : scrollingTo.itemSize)) {
1853
+ const diff = newSize - prevSize;
1854
+ if (diff !== 0) {
1855
+ positionDiff += (newSize - prevSize) * scrollingToViewPosition;
1856
+ scrollingTo.itemSize = newSize;
1857
+ }
1858
+ }
1859
+ }
1860
+ if (Math.abs(positionDiff) > 0.1) {
1861
+ requestAdjust(ctx, positionDiff);
1692
1862
  }
1693
1863
  };
1694
1864
  }
1695
1865
  }
1696
1866
 
1697
1867
  // src/core/prepareColumnStartState.ts
1698
- function prepareColumnStartState(ctx, state, startIndex, useAverageSize) {
1868
+ function prepareColumnStartState(ctx, startIndex, useAverageSize) {
1699
1869
  var _a3;
1870
+ const state = ctx.state;
1700
1871
  const numColumns = peek$(ctx, "numColumns");
1701
1872
  let rowStartIndex = startIndex;
1702
1873
  const columnAtStart = state.columns.get(state.idCache[startIndex]);
@@ -1711,7 +1882,7 @@ function prepareColumnStartState(ctx, state, startIndex, useAverageSize) {
1711
1882
  const prevId = state.idCache[prevIndex];
1712
1883
  const prevPosition = (_a3 = state.positions.get(prevId)) != null ? _a3 : 0;
1713
1884
  const prevRowStart = findRowStartIndex(state, numColumns, prevIndex);
1714
- const prevRowHeight = calculateRowMaxSize(ctx, state, prevRowStart, prevIndex, useAverageSize);
1885
+ const prevRowHeight = calculateRowMaxSize(ctx, prevRowStart, prevIndex, useAverageSize);
1715
1886
  currentRowTop = prevPosition + prevRowHeight;
1716
1887
  }
1717
1888
  return {
@@ -1734,7 +1905,8 @@ function findRowStartIndex(state, numColumns, index) {
1734
1905
  }
1735
1906
  return rowStart;
1736
1907
  }
1737
- function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1908
+ function calculateRowMaxSize(ctx, startIndex, endIndex, useAverageSize) {
1909
+ const state = ctx.state;
1738
1910
  if (endIndex < startIndex) {
1739
1911
  return 0;
1740
1912
  }
@@ -1748,7 +1920,7 @@ function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1748
1920
  continue;
1749
1921
  }
1750
1922
  const id = state.idCache[i];
1751
- const size = getItemSize(ctx, state, id, i, data[i], useAverageSize);
1923
+ const size = getItemSize(ctx, id, i, data[i], useAverageSize);
1752
1924
  if (size > maxSize) {
1753
1925
  maxSize = size;
1754
1926
  }
@@ -1757,22 +1929,23 @@ function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1757
1929
  }
1758
1930
 
1759
1931
  // src/core/updateTotalSize.ts
1760
- function updateTotalSize(ctx, state) {
1932
+ function updateTotalSize(ctx) {
1933
+ const state = ctx.state;
1761
1934
  const {
1762
1935
  positions,
1763
1936
  props: { data }
1764
1937
  } = state;
1765
1938
  if (data.length === 0) {
1766
- addTotalSize(ctx, state, null, 0);
1939
+ addTotalSize(ctx, null, 0);
1767
1940
  } else {
1768
1941
  const lastId = getId(state, data.length - 1);
1769
1942
  if (lastId !== void 0) {
1770
1943
  const lastPosition = positions.get(lastId);
1771
1944
  if (lastPosition !== void 0) {
1772
- const lastSize = getItemSize(ctx, state, lastId, data.length - 1, data[data.length - 1]);
1945
+ const lastSize = getItemSize(ctx, lastId, data.length - 1, data[data.length - 1]);
1773
1946
  if (lastSize !== void 0) {
1774
1947
  const totalSize = lastPosition + lastSize;
1775
- addTotalSize(ctx, state, null, totalSize);
1948
+ addTotalSize(ctx, null, totalSize);
1776
1949
  }
1777
1950
  }
1778
1951
  }
@@ -1782,43 +1955,45 @@ function updateTotalSize(ctx, state) {
1782
1955
  // src/utils/getScrollVelocity.ts
1783
1956
  var getScrollVelocity = (state) => {
1784
1957
  const { scrollHistory } = state;
1785
- let velocity = 0;
1786
- if (scrollHistory.length >= 1) {
1787
- const newest = scrollHistory[scrollHistory.length - 1];
1788
- let oldest;
1789
- let start = 0;
1790
- const now = Date.now();
1791
- for (let i = 0; i < scrollHistory.length - 1; i++) {
1792
- const entry = scrollHistory[i];
1793
- const nextEntry = scrollHistory[i + 1];
1794
- if (i > 0) {
1795
- const prevEntry = scrollHistory[i - 1];
1796
- const prevDirection = entry.scroll - prevEntry.scroll;
1797
- const currentDirection = nextEntry.scroll - entry.scroll;
1798
- if (prevDirection > 0 && currentDirection < 0 || prevDirection < 0 && currentDirection > 0) {
1799
- start = i;
1800
- break;
1801
- }
1802
- }
1958
+ const newestIndex = scrollHistory.length - 1;
1959
+ if (newestIndex < 1) {
1960
+ return 0;
1961
+ }
1962
+ const newest = scrollHistory[newestIndex];
1963
+ const now = Date.now();
1964
+ let direction = 0;
1965
+ for (let i = newestIndex; i > 0; i--) {
1966
+ const delta = scrollHistory[i].scroll - scrollHistory[i - 1].scroll;
1967
+ if (delta !== 0) {
1968
+ direction = Math.sign(delta);
1969
+ break;
1803
1970
  }
1804
- for (let i = start; i < scrollHistory.length - 1; i++) {
1805
- const entry = scrollHistory[i];
1806
- if (now - entry.time <= 1e3) {
1807
- oldest = entry;
1808
- break;
1809
- }
1971
+ }
1972
+ if (direction === 0) {
1973
+ return 0;
1974
+ }
1975
+ let oldest = newest;
1976
+ for (let i = newestIndex - 1; i >= 0; i--) {
1977
+ const current = scrollHistory[i];
1978
+ const next = scrollHistory[i + 1];
1979
+ const delta = next.scroll - current.scroll;
1980
+ const deltaSign = Math.sign(delta);
1981
+ if (deltaSign !== 0 && deltaSign !== direction) {
1982
+ break;
1810
1983
  }
1811
- if (oldest && oldest !== newest) {
1812
- const scrollDiff = newest.scroll - oldest.scroll;
1813
- const timeDiff = newest.time - oldest.time;
1814
- velocity = timeDiff > 0 ? scrollDiff / timeDiff : 0;
1984
+ if (now - current.time > 1e3) {
1985
+ break;
1815
1986
  }
1987
+ oldest = current;
1816
1988
  }
1817
- return velocity;
1989
+ const scrollDiff = newest.scroll - oldest.scroll;
1990
+ const timeDiff = newest.time - oldest.time;
1991
+ return timeDiff > 0 ? scrollDiff / timeDiff : 0;
1818
1992
  };
1819
1993
 
1820
1994
  // src/utils/updateSnapToOffsets.ts
1821
- function updateSnapToOffsets(ctx, state) {
1995
+ function updateSnapToOffsets(ctx) {
1996
+ const state = ctx.state;
1822
1997
  const {
1823
1998
  positions,
1824
1999
  props: { snapToIndices }
@@ -1833,30 +2008,32 @@ function updateSnapToOffsets(ctx, state) {
1833
2008
  }
1834
2009
 
1835
2010
  // src/core/updateItemPositions.ts
1836
- function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP } = {
2011
+ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP } = {
1837
2012
  doMVCP: false,
1838
2013
  forceFullUpdate: false,
1839
2014
  scrollBottomBuffered: -1,
1840
2015
  startIndex: 0
1841
2016
  }) {
1842
2017
  var _a3, _b, _c, _d, _e;
2018
+ const state = ctx.state;
1843
2019
  const {
1844
2020
  columns,
1845
2021
  indexByKey,
1846
2022
  positions,
1847
2023
  idCache,
1848
2024
  sizesKnown,
1849
- props: { getEstimatedItemSize, snapToIndices, enableAverages }
2025
+ props: { data, getEstimatedItemSize, snapToIndices },
2026
+ scrollingTo
1850
2027
  } = state;
1851
- const data = state.props.data;
1852
2028
  const dataLength = data.length;
1853
2029
  const numColumns = peek$(ctx, "numColumns");
1854
- const scrollingTo = peek$(ctx, "scrollingTo");
1855
2030
  const hasColumns = numColumns > 1;
1856
2031
  const indexByKeyForChecking = IS_DEV ? /* @__PURE__ */ new Map() : void 0;
1857
- const shouldOptimize = !forceFullUpdate && !dataChanged && Math.abs(getScrollVelocity(state)) > 0;
2032
+ const lastScrollDelta = state.lastScrollDelta;
2033
+ const velocity = getScrollVelocity(state);
2034
+ const shouldOptimize = !forceFullUpdate && !dataChanged && (Math.abs(velocity) > 0 || state.scrollLength > 0 && lastScrollDelta > state.scrollLength);
1858
2035
  const maxVisibleArea = scrollBottomBuffered + 1e3;
1859
- const useAverageSize = enableAverages && !getEstimatedItemSize;
2036
+ const useAverageSize = !getEstimatedItemSize;
1860
2037
  const preferCachedSize = !doMVCP || dataChanged || state.scrollAdjustHandler.getAdjust() !== 0 || ((_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0) !== 0;
1861
2038
  let currentRowTop = 0;
1862
2039
  let column = 1;
@@ -1865,7 +2042,6 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1865
2042
  if (hasColumns) {
1866
2043
  const { startIndex: processedStartIndex, currentRowTop: initialRowTop } = prepareColumnStartState(
1867
2044
  ctx,
1868
- state,
1869
2045
  startIndex,
1870
2046
  useAverageSize
1871
2047
  );
@@ -1875,7 +2051,7 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1875
2051
  const prevIndex = startIndex - 1;
1876
2052
  const prevId = getId(state, prevIndex);
1877
2053
  const prevPosition = (_b = positions.get(prevId)) != null ? _b : 0;
1878
- const prevSize = (_c = sizesKnown.get(prevId)) != null ? _c : getItemSize(ctx, state, prevId, prevIndex, data[prevIndex], useAverageSize, preferCachedSize);
2054
+ const prevSize = (_c = sizesKnown.get(prevId)) != null ? _c : getItemSize(ctx, prevId, prevIndex, data[prevIndex], useAverageSize, preferCachedSize);
1879
2055
  currentRowTop = prevPosition + prevSize;
1880
2056
  }
1881
2057
  }
@@ -1892,7 +2068,7 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1892
2068
  breakAt = i + itemsPerRow + 10;
1893
2069
  }
1894
2070
  const id = (_d = idCache[i]) != null ? _d : getId(state, i);
1895
- const size = (_e = sizesKnown.get(id)) != null ? _e : getItemSize(ctx, state, id, i, data[i], useAverageSize, preferCachedSize);
2071
+ const size = (_e = sizesKnown.get(id)) != null ? _e : getItemSize(ctx, id, i, data[i], useAverageSize, preferCachedSize);
1896
2072
  if (IS_DEV && needsIndexByKey) {
1897
2073
  if (indexByKeyForChecking.has(id)) {
1898
2074
  console.error(
@@ -1901,7 +2077,10 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1901
2077
  }
1902
2078
  indexByKeyForChecking.set(id, i);
1903
2079
  }
1904
- positions.set(id, currentRowTop);
2080
+ if (currentRowTop !== positions.get(id)) {
2081
+ positions.set(id, currentRowTop);
2082
+ notifyPosition$(ctx, id, currentRowTop);
2083
+ }
1905
2084
  if (needsIndexByKey) {
1906
2085
  indexByKey.set(id, i);
1907
2086
  }
@@ -1921,10 +2100,10 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1921
2100
  }
1922
2101
  }
1923
2102
  if (!didBreakEarly) {
1924
- updateTotalSize(ctx, state);
2103
+ updateTotalSize(ctx);
1925
2104
  }
1926
2105
  if (snapToIndices) {
1927
- updateSnapToOffsets(ctx, state);
2106
+ updateSnapToOffsets(ctx);
1928
2107
  }
1929
2108
  }
1930
2109
 
@@ -2002,7 +2181,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
2002
2181
  if (previousViewableItems) {
2003
2182
  for (const viewToken of previousViewableItems) {
2004
2183
  const containerId = findContainerId(ctx, viewToken.key);
2005
- if (!isViewable(
2184
+ if (!checkIsViewable(
2006
2185
  state,
2007
2186
  ctx,
2008
2187
  viewabilityConfig,
@@ -2023,7 +2202,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
2023
2202
  if (item) {
2024
2203
  const key = getId(state, i);
2025
2204
  const containerId = findContainerId(ctx, key);
2026
- if (isViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, i)) {
2205
+ if (checkIsViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, i)) {
2027
2206
  const viewToken = {
2028
2207
  containerId,
2029
2208
  index: i,
@@ -2083,11 +2262,11 @@ function computeViewability(state, ctx, viewabilityConfig, containerId, key, scr
2083
2262
  const percentVisible = size ? isEntirelyVisible ? 100 : 100 * (sizeVisible / size) : 0;
2084
2263
  const percentOfScroller = size ? 100 * (sizeVisible / scrollSize) : 0;
2085
2264
  const percent = isEntirelyVisible ? 100 : viewAreaMode ? percentOfScroller : percentVisible;
2086
- const isViewable2 = percent >= viewablePercentThreshold;
2265
+ const isViewable = percent >= viewablePercentThreshold;
2087
2266
  const value = {
2088
2267
  containerId,
2089
2268
  index,
2090
- isViewable: isViewable2,
2269
+ isViewable,
2091
2270
  item,
2092
2271
  key,
2093
2272
  percentOfScroller,
@@ -2106,8 +2285,11 @@ function computeViewability(state, ctx, viewabilityConfig, containerId, key, scr
2106
2285
  }
2107
2286
  return value;
2108
2287
  }
2109
- function isViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
2110
- const value = ctx.mapViewabilityAmountValues.get(containerId) || computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index);
2288
+ function checkIsViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
2289
+ let value = ctx.mapViewabilityAmountValues.get(containerId);
2290
+ if (!value || value.key !== key) {
2291
+ value = computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index);
2292
+ }
2111
2293
  return value.isViewable;
2112
2294
  }
2113
2295
  function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
@@ -2135,8 +2317,9 @@ function checkAllSizesKnown(state) {
2135
2317
  }
2136
2318
 
2137
2319
  // src/utils/findAvailableContainers.ts
2138
- function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers) {
2320
+ function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers) {
2139
2321
  const numContainers = peek$(ctx, "numContainers");
2322
+ const state = ctx.state;
2140
2323
  const { stickyContainerPool, containerItemTypes } = state;
2141
2324
  const result = [];
2142
2325
  const availableContainers = [];
@@ -2256,21 +2439,26 @@ function comparatorByDistance(a, b) {
2256
2439
  }
2257
2440
 
2258
2441
  // src/core/scrollToIndex.ts
2259
- function scrollToIndex(ctx, state, { index, viewOffset = 0, animated = true, viewPosition }) {
2260
- if (index >= state.props.data.length) {
2261
- index = state.props.data.length - 1;
2442
+ function scrollToIndex(ctx, { index, viewOffset = 0, animated = true, viewPosition }) {
2443
+ const state = ctx.state;
2444
+ const { data } = state.props;
2445
+ if (index >= data.length) {
2446
+ index = data.length - 1;
2262
2447
  } else if (index < 0) {
2263
2448
  index = 0;
2264
2449
  }
2265
- const firstIndexOffset = calculateOffsetForIndex(ctx, state, index);
2266
- const isLast = index === state.props.data.length - 1;
2450
+ const firstIndexOffset = calculateOffsetForIndex(ctx, index);
2451
+ const isLast = index === data.length - 1;
2267
2452
  if (isLast && viewPosition === void 0) {
2268
2453
  viewPosition = 1;
2269
2454
  }
2270
2455
  state.scrollForNextCalculateItemsInView = void 0;
2271
- scrollTo(ctx, state, {
2456
+ const targetId = getId(state, index);
2457
+ const itemSize = getItemSize(ctx, targetId, index, state.props.data[index]);
2458
+ scrollTo(ctx, {
2272
2459
  animated,
2273
2460
  index,
2461
+ itemSize,
2274
2462
  offset: firstIndexOffset,
2275
2463
  viewOffset,
2276
2464
  viewPosition: viewPosition != null ? viewPosition : 0
@@ -2278,23 +2466,28 @@ function scrollToIndex(ctx, state, { index, viewOffset = 0, animated = true, vie
2278
2466
  }
2279
2467
 
2280
2468
  // src/utils/setDidLayout.ts
2281
- function setDidLayout(ctx, state) {
2469
+ function setDidLayout(ctx) {
2470
+ const state = ctx.state;
2282
2471
  const {
2283
2472
  loadStartTime,
2284
2473
  initialScroll,
2285
2474
  props: { onLoad }
2286
2475
  } = state;
2287
2476
  state.queuedInitialLayout = true;
2288
- checkAtBottom(ctx, state);
2477
+ checkAtBottom(ctx);
2289
2478
  const setIt = () => {
2290
- set$(ctx, "containersDidLayout", true);
2479
+ setInitialRenderState(ctx, { didLayout: true });
2291
2480
  if (onLoad) {
2292
2481
  onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
2293
2482
  }
2294
2483
  };
2295
- {
2296
- setIt();
2484
+ if ((initialScroll == null ? void 0 : initialScroll.index) !== void 0) {
2485
+ const target = initialScroll;
2486
+ const runScroll = () => scrollToIndex(ctx, { ...target, animated: false });
2487
+ runScroll();
2488
+ requestAnimationFrame(runScroll);
2297
2489
  }
2490
+ setIt();
2298
2491
  }
2299
2492
 
2300
2493
  // src/core/calculateItemsInView.ts
@@ -2312,31 +2505,36 @@ function findCurrentStickyIndex(stickyArray, scroll, state) {
2312
2505
  }
2313
2506
  return -1;
2314
2507
  }
2315
- function getActiveStickyIndices(ctx, state, stickyHeaderIndices) {
2508
+ function getActiveStickyIndices(ctx, stickyHeaderIndices) {
2509
+ const state = ctx.state;
2316
2510
  return new Set(
2317
2511
  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))
2318
2512
  );
2319
2513
  }
2320
- function handleStickyActivation(ctx, state, stickyHeaderIndices, stickyArray, currentStickyIdx, needNewContainers, startBuffered, endBuffered) {
2514
+ function handleStickyActivation(ctx, stickyHeaderIndices, stickyArray, currentStickyIdx, needNewContainers, needNewContainersSet, startBuffered, endBuffered) {
2321
2515
  var _a3;
2322
- const activeIndices = getActiveStickyIndices(ctx, state, stickyHeaderIndices);
2323
- state.activeStickyIndex = currentStickyIdx >= 0 ? stickyArray[currentStickyIdx] : void 0;
2516
+ const state = ctx.state;
2517
+ const activeIndices = getActiveStickyIndices(ctx, stickyHeaderIndices);
2518
+ set$(ctx, "activeStickyIndex", currentStickyIdx >= 0 ? stickyArray[currentStickyIdx] : -1);
2324
2519
  for (let offset = 0; offset <= 1; offset++) {
2325
2520
  const idx = currentStickyIdx - offset;
2326
2521
  if (idx < 0 || activeIndices.has(stickyArray[idx])) continue;
2327
2522
  const stickyIndex = stickyArray[idx];
2328
2523
  const stickyId = (_a3 = state.idCache[stickyIndex]) != null ? _a3 : getId(state, stickyIndex);
2329
- if (stickyId && !state.containerItemKeys.has(stickyId) && (stickyIndex < startBuffered || stickyIndex > endBuffered)) {
2524
+ if (stickyId && !state.containerItemKeys.has(stickyId) && (stickyIndex < startBuffered || stickyIndex > endBuffered) && !needNewContainersSet.has(stickyIndex)) {
2525
+ needNewContainersSet.add(stickyIndex);
2330
2526
  needNewContainers.push(stickyIndex);
2331
2527
  }
2332
2528
  }
2333
2529
  }
2334
- function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, currentStickyIdx, pendingRemoval) {
2530
+ function handleStickyRecycling(ctx, stickyArray, scroll, scrollBuffer, currentStickyIdx, pendingRemoval, alwaysRenderIndicesSet) {
2335
2531
  var _a3, _b, _c;
2532
+ const state = ctx.state;
2336
2533
  for (const containerIndex of state.stickyContainerPool) {
2337
2534
  const itemKey = peek$(ctx, `containerItemKey${containerIndex}`);
2338
2535
  const itemIndex = itemKey ? state.indexByKey.get(itemKey) : void 0;
2339
2536
  if (itemIndex === void 0) continue;
2537
+ if (alwaysRenderIndicesSet.has(itemIndex)) continue;
2340
2538
  const arrayIdx = stickyArray.indexOf(itemIndex);
2341
2539
  if (arrayIdx === -1) {
2342
2540
  state.stickyContainerPool.delete(containerIndex);
@@ -2356,7 +2554,7 @@ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, cu
2356
2554
  const currentId = (_b = state.idCache[itemIndex]) != null ? _b : getId(state, itemIndex);
2357
2555
  if (currentId) {
2358
2556
  const currentPos = state.positions.get(currentId);
2359
- const currentSize = (_c = state.sizes.get(currentId)) != null ? _c : getItemSize(ctx, state, currentId, itemIndex, state.props.data[itemIndex]);
2557
+ const currentSize = (_c = state.sizes.get(currentId)) != null ? _c : getItemSize(ctx, currentId, itemIndex, state.props.data[itemIndex]);
2360
2558
  shouldRecycle = currentPos !== void 0 && scroll > currentPos + currentSize + scrollBuffer * 3;
2361
2559
  }
2362
2560
  }
@@ -2365,9 +2563,10 @@ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, cu
2365
2563
  }
2366
2564
  }
2367
2565
  }
2368
- function calculateItemsInView(ctx, state, params = {}) {
2566
+ function calculateItemsInView(ctx, params = {}) {
2567
+ const state = ctx.state;
2369
2568
  unstable_batchedUpdates(() => {
2370
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j;
2569
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
2371
2570
  const {
2372
2571
  columns,
2373
2572
  containerItemKeys,
@@ -2377,7 +2576,15 @@ function calculateItemsInView(ctx, state, params = {}) {
2377
2576
  initialScroll,
2378
2577
  minIndexSizeChanged,
2379
2578
  positions,
2380
- props: { getItemType, itemsAreEqual, keyExtractor, onStickyHeaderChange, scrollBuffer },
2579
+ props: {
2580
+ alwaysRenderIndicesArr,
2581
+ alwaysRenderIndicesSet,
2582
+ getItemType,
2583
+ itemsAreEqual,
2584
+ keyExtractor,
2585
+ onStickyHeaderChange,
2586
+ scrollBuffer
2587
+ },
2381
2588
  scrollForNextCalculateItemsInView,
2382
2589
  scrollLength,
2383
2590
  sizes,
@@ -2387,11 +2594,10 @@ function calculateItemsInView(ctx, state, params = {}) {
2387
2594
  const { data } = state.props;
2388
2595
  const stickyIndicesArr = state.props.stickyIndicesArr || [];
2389
2596
  const stickyIndicesSet = state.props.stickyIndicesSet || /* @__PURE__ */ new Set();
2597
+ const alwaysRenderArr = alwaysRenderIndicesArr || [];
2598
+ const alwaysRenderSet = alwaysRenderIndicesSet || /* @__PURE__ */ new Set();
2390
2599
  const prevNumContainers = peek$(ctx, "numContainers");
2391
2600
  if (!data || scrollLength === 0 || !prevNumContainers) {
2392
- if (state.initialAnchor) {
2393
- ensureInitialAnchor(ctx, state);
2394
- }
2395
2601
  return;
2396
2602
  }
2397
2603
  const totalSize = getContentSize(ctx);
@@ -2405,15 +2611,14 @@ function calculateItemsInView(ctx, state, params = {}) {
2405
2611
  if (!queuedInitialLayout && initialScroll) {
2406
2612
  const updatedOffset = calculateOffsetWithOffsetPosition(
2407
2613
  ctx,
2408
- state,
2409
- calculateOffsetForIndex(ctx, state, initialScroll.index),
2614
+ calculateOffsetForIndex(ctx, initialScroll.index),
2410
2615
  initialScroll
2411
2616
  );
2412
2617
  scrollState = updatedOffset;
2413
2618
  }
2414
2619
  const scrollAdjustPending = (_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0;
2415
2620
  const scrollAdjustPad = scrollAdjustPending - topPad;
2416
- let scroll = scrollState + scrollExtra + scrollAdjustPad;
2621
+ let scroll = Math.round(scrollState + scrollExtra + scrollAdjustPad);
2417
2622
  if (scroll + scrollLength > totalSize) {
2418
2623
  scroll = Math.max(0, totalSize - scrollLength);
2419
2624
  }
@@ -2421,11 +2626,12 @@ function calculateItemsInView(ctx, state, params = {}) {
2421
2626
  set$(ctx, "debugRawScroll", scrollState);
2422
2627
  set$(ctx, "debugComputedScroll", scroll);
2423
2628
  }
2424
- const previousStickyIndex = state.activeStickyIndex;
2629
+ const previousStickyIndex = peek$(ctx, "activeStickyIndex");
2425
2630
  const currentStickyIdx = stickyIndicesArr.length > 0 ? findCurrentStickyIndex(stickyIndicesArr, scroll, state) : -1;
2426
- const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyIndicesArr[currentStickyIdx] : void 0;
2427
- state.activeStickyIndex = nextActiveStickyIndex;
2428
- set$(ctx, "activeStickyIndex", nextActiveStickyIndex);
2631
+ const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyIndicesArr[currentStickyIdx] : -1;
2632
+ if (currentStickyIdx >= 0 || previousStickyIndex >= 0) {
2633
+ set$(ctx, "activeStickyIndex", nextActiveStickyIndex);
2634
+ }
2429
2635
  let scrollBufferTop = scrollBuffer;
2430
2636
  let scrollBufferBottom = scrollBuffer;
2431
2637
  if (speed > 0 || speed === 0 && scroll < Math.max(50, scrollBuffer)) {
@@ -2438,23 +2644,22 @@ function calculateItemsInView(ctx, state, params = {}) {
2438
2644
  const scrollTopBuffered = scroll - scrollBufferTop;
2439
2645
  const scrollBottom = scroll + scrollLength + (scroll < 0 ? -scroll : 0);
2440
2646
  const scrollBottomBuffered = scrollBottom + scrollBufferBottom;
2441
- if (!dataChanged && scrollForNextCalculateItemsInView) {
2647
+ if (!dataChanged && !forceFullItemPositions && scrollForNextCalculateItemsInView) {
2442
2648
  const { top, bottom } = scrollForNextCalculateItemsInView;
2443
- if (scrollTopBuffered > top && scrollBottomBuffered < bottom) {
2444
- if (state.initialAnchor) {
2445
- ensureInitialAnchor(ctx, state);
2446
- }
2649
+ if (top === null && bottom === null) {
2650
+ state.scrollForNextCalculateItemsInView = void 0;
2651
+ } else if ((top === null || scrollTopBuffered > top) && (bottom === null || scrollBottomBuffered < bottom)) {
2447
2652
  return;
2448
2653
  }
2449
2654
  }
2450
- const checkMVCP = doMVCP ? prepareMVCP(ctx, state, dataChanged) : void 0;
2655
+ const checkMVCP = doMVCP ? prepareMVCP(ctx, dataChanged) : void 0;
2451
2656
  if (dataChanged) {
2452
2657
  indexByKey.clear();
2453
2658
  idCache.length = 0;
2454
2659
  positions.clear();
2455
2660
  }
2456
- const startIndex = dataChanged ? 0 : (_b = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _b : 0;
2457
- updateItemPositions(ctx, state, dataChanged, {
2661
+ const startIndex = forceFullItemPositions || dataChanged ? 0 : (_b = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _b : 0;
2662
+ updateItemPositions(ctx, dataChanged, {
2458
2663
  doMVCP,
2459
2664
  forceFullUpdate: !!forceFullItemPositions,
2460
2665
  scrollBottomBuffered,
@@ -2473,9 +2678,9 @@ function calculateItemsInView(ctx, state, params = {}) {
2473
2678
  for (let i = loopStart; i >= 0; i--) {
2474
2679
  const id = (_c = idCache[i]) != null ? _c : getId(state, i);
2475
2680
  const top = positions.get(id);
2476
- const size = (_d = sizes.get(id)) != null ? _d : getItemSize(ctx, state, id, i, data[i]);
2681
+ const size = (_d = sizes.get(id)) != null ? _d : getItemSize(ctx, id, i, data[i]);
2477
2682
  const bottom = top + size;
2478
- if (bottom > scroll - scrollBuffer) {
2683
+ if (bottom > scroll - scrollBufferTop) {
2479
2684
  loopStart = i;
2480
2685
  } else {
2481
2686
  break;
@@ -2500,7 +2705,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2500
2705
  const dataLength = data.length;
2501
2706
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
2502
2707
  const id = (_e = idCache[i]) != null ? _e : getId(state, i);
2503
- const size = (_f = sizes.get(id)) != null ? _f : getItemSize(ctx, state, id, i, data[i]);
2708
+ const size = (_f = sizes.get(id)) != null ? _f : getItemSize(ctx, id, i, data[i]);
2504
2709
  const top = positions.get(id);
2505
2710
  if (!foundEnd) {
2506
2711
  if (startNoBuffer === null && top + size > scroll) {
@@ -2512,7 +2717,11 @@ function calculateItemsInView(ctx, state, params = {}) {
2512
2717
  if (startBuffered === null && top + size > scrollTopBuffered) {
2513
2718
  startBuffered = i;
2514
2719
  startBufferedId = id;
2515
- nextTop = top;
2720
+ if (scrollTopBuffered < 0) {
2721
+ nextTop = null;
2722
+ } else {
2723
+ nextTop = top;
2724
+ }
2516
2725
  }
2517
2726
  if (startNoBuffer !== null) {
2518
2727
  if (top <= scrollBottom) {
@@ -2520,7 +2729,11 @@ function calculateItemsInView(ctx, state, params = {}) {
2520
2729
  }
2521
2730
  if (top <= scrollBottomBuffered) {
2522
2731
  endBuffered = i;
2523
- nextBottom = top + size;
2732
+ if (scrollBottomBuffered > totalSize) {
2733
+ nextBottom = null;
2734
+ } else {
2735
+ nextBottom = top + size;
2736
+ }
2524
2737
  } else {
2525
2738
  foundEnd = true;
2526
2739
  }
@@ -2542,12 +2755,12 @@ function calculateItemsInView(ctx, state, params = {}) {
2542
2755
  startNoBuffer
2543
2756
  });
2544
2757
  if (enableScrollForNextCalculateItemsInView && nextTop !== void 0 && nextBottom !== void 0) {
2545
- state.scrollForNextCalculateItemsInView = nextTop !== void 0 && nextBottom !== void 0 ? {
2758
+ state.scrollForNextCalculateItemsInView = isNullOrUndefined(nextTop) && isNullOrUndefined(nextBottom) ? void 0 : {
2546
2759
  bottom: nextBottom,
2547
2760
  top: nextTop
2548
- } : void 0;
2761
+ };
2549
2762
  }
2550
- const numContainers = peek$(ctx, "numContainers");
2763
+ let numContainers = prevNumContainers;
2551
2764
  const pendingRemoval = [];
2552
2765
  if (dataChanged) {
2553
2766
  for (let i = 0; i < numContainers; i++) {
@@ -2558,37 +2771,46 @@ function calculateItemsInView(ctx, state, params = {}) {
2558
2771
  }
2559
2772
  }
2560
2773
  if (startBuffered !== null && endBuffered !== null) {
2561
- let numContainers2 = prevNumContainers;
2562
2774
  const needNewContainers = [];
2775
+ const needNewContainersSet = /* @__PURE__ */ new Set();
2563
2776
  for (let i = startBuffered; i <= endBuffered; i++) {
2564
2777
  const id = (_h = idCache[i]) != null ? _h : getId(state, i);
2565
2778
  if (!containerItemKeys.has(id)) {
2779
+ needNewContainersSet.add(i);
2566
2780
  needNewContainers.push(i);
2567
2781
  }
2568
2782
  }
2783
+ if (alwaysRenderArr.length > 0) {
2784
+ for (const index of alwaysRenderArr) {
2785
+ if (index < 0 || index >= dataLength) continue;
2786
+ const id = (_i = idCache[index]) != null ? _i : getId(state, index);
2787
+ if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
2788
+ needNewContainersSet.add(index);
2789
+ needNewContainers.push(index);
2790
+ }
2791
+ }
2792
+ }
2569
2793
  if (stickyIndicesArr.length > 0) {
2570
2794
  handleStickyActivation(
2571
2795
  ctx,
2572
- state,
2573
2796
  stickyIndicesSet,
2574
2797
  stickyIndicesArr,
2575
2798
  currentStickyIdx,
2576
2799
  needNewContainers,
2800
+ needNewContainersSet,
2577
2801
  startBuffered,
2578
2802
  endBuffered
2579
2803
  );
2580
- } else {
2581
- state.activeStickyIndex = void 0;
2582
- set$(ctx, "activeStickyIndex", void 0);
2804
+ } else if (previousStickyIndex !== -1) {
2805
+ set$(ctx, "activeStickyIndex", -1);
2583
2806
  }
2584
2807
  if (needNewContainers.length > 0) {
2585
2808
  const requiredItemTypes = getItemType ? needNewContainers.map((i) => {
2586
2809
  const itemType = getItemType(data[i], i);
2587
- return itemType ? String(itemType) : "";
2810
+ return itemType !== void 0 ? String(itemType) : "";
2588
2811
  }) : void 0;
2589
2812
  const availableContainers = findAvailableContainers(
2590
2813
  ctx,
2591
- state,
2592
2814
  needNewContainers.length,
2593
2815
  startBuffered,
2594
2816
  endBuffered,
@@ -2599,7 +2821,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2599
2821
  for (let idx = 0; idx < needNewContainers.length; idx++) {
2600
2822
  const i = needNewContainers[idx];
2601
2823
  const containerIndex = availableContainers[idx];
2602
- const id = (_i = idCache[i]) != null ? _i : getId(state, i);
2824
+ const id = (_j = idCache[i]) != null ? _j : getId(state, i);
2603
2825
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
2604
2826
  if (oldKey && oldKey !== id) {
2605
2827
  containerItemKeys.delete(oldKey);
@@ -2609,30 +2831,58 @@ function calculateItemsInView(ctx, state, params = {}) {
2609
2831
  if (requiredItemTypes) {
2610
2832
  state.containerItemTypes.set(containerIndex, requiredItemTypes[idx]);
2611
2833
  }
2612
- containerItemKeys.add(id);
2613
- if (stickyIndicesSet.has(i)) {
2614
- set$(ctx, `containerSticky${containerIndex}`, true);
2834
+ containerItemKeys.set(id, containerIndex);
2835
+ const containerSticky = `containerSticky${containerIndex}`;
2836
+ const isSticky = stickyIndicesSet.has(i);
2837
+ const isAlwaysRender = alwaysRenderSet.has(i);
2838
+ if (isSticky) {
2839
+ set$(ctx, containerSticky, true);
2615
2840
  const topPadding = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
2616
2841
  set$(ctx, `containerStickyOffset${containerIndex}`, topPadding);
2617
2842
  state.stickyContainerPool.add(containerIndex);
2618
2843
  } else {
2619
- set$(ctx, `containerSticky${containerIndex}`, false);
2620
- state.stickyContainerPool.delete(containerIndex);
2844
+ if (peek$(ctx, containerSticky)) {
2845
+ set$(ctx, containerSticky, false);
2846
+ set$(ctx, `containerStickyOffset${containerIndex}`, void 0);
2847
+ }
2848
+ if (isAlwaysRender) {
2849
+ state.stickyContainerPool.add(containerIndex);
2850
+ } else if (state.stickyContainerPool.has(containerIndex)) {
2851
+ state.stickyContainerPool.delete(containerIndex);
2852
+ }
2621
2853
  }
2622
- if (containerIndex >= numContainers2) {
2623
- numContainers2 = containerIndex + 1;
2854
+ if (containerIndex >= numContainers) {
2855
+ numContainers = containerIndex + 1;
2624
2856
  }
2625
2857
  }
2626
- if (numContainers2 !== prevNumContainers) {
2627
- set$(ctx, "numContainers", numContainers2);
2628
- if (numContainers2 > peek$(ctx, "numContainersPooled")) {
2629
- set$(ctx, "numContainersPooled", Math.ceil(numContainers2 * 1.5));
2858
+ if (numContainers !== prevNumContainers) {
2859
+ set$(ctx, "numContainers", numContainers);
2860
+ if (numContainers > peek$(ctx, "numContainersPooled")) {
2861
+ set$(ctx, "numContainersPooled", Math.ceil(numContainers * 1.5));
2862
+ }
2863
+ }
2864
+ }
2865
+ if (alwaysRenderArr.length > 0) {
2866
+ for (const index of alwaysRenderArr) {
2867
+ if (index < 0 || index >= dataLength) continue;
2868
+ const id = (_k = idCache[index]) != null ? _k : getId(state, index);
2869
+ const containerIndex = containerItemKeys.get(id);
2870
+ if (containerIndex !== void 0) {
2871
+ state.stickyContainerPool.add(containerIndex);
2630
2872
  }
2631
2873
  }
2632
2874
  }
2633
2875
  }
2634
- if (stickyIndicesArr.length > 0) {
2635
- handleStickyRecycling(ctx, state, stickyIndicesArr, scroll, scrollBuffer, currentStickyIdx, pendingRemoval);
2876
+ if (state.stickyContainerPool.size > 0) {
2877
+ handleStickyRecycling(
2878
+ ctx,
2879
+ stickyIndicesArr,
2880
+ scroll,
2881
+ scrollBuffer,
2882
+ currentStickyIdx,
2883
+ pendingRemoval,
2884
+ alwaysRenderSet
2885
+ );
2636
2886
  }
2637
2887
  let didChangePositions = false;
2638
2888
  for (let i = 0; i < numContainers; i++) {
@@ -2655,7 +2905,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2655
2905
  const itemIndex = indexByKey.get(itemKey);
2656
2906
  const item = data[itemIndex];
2657
2907
  if (item !== void 0) {
2658
- const id = (_j = idCache[itemIndex]) != null ? _j : getId(state, itemIndex);
2908
+ const id = (_l = idCache[itemIndex]) != null ? _l : getId(state, itemIndex);
2659
2909
  const positionValue = positions.get(id);
2660
2910
  if (positionValue === void 0) {
2661
2911
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
@@ -2684,7 +2934,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2684
2934
  }
2685
2935
  if (!queuedInitialLayout && endBuffered !== null) {
2686
2936
  if (checkAllSizesKnown(state)) {
2687
- setDidLayout(ctx, state);
2937
+ setDidLayout(ctx);
2688
2938
  }
2689
2939
  }
2690
2940
  if (viewabilityConfigCallbackPairs) {
@@ -2697,9 +2947,6 @@ function calculateItemsInView(ctx, state, params = {}) {
2697
2947
  }
2698
2948
  }
2699
2949
  });
2700
- if (state.initialAnchor) {
2701
- ensureInitialAnchor(ctx, state);
2702
- }
2703
2950
  }
2704
2951
 
2705
2952
  // src/core/checkActualChange.ts
@@ -2722,20 +2969,69 @@ function checkActualChange(state, dataProp, previousData) {
2722
2969
  return false;
2723
2970
  }
2724
2971
 
2972
+ // src/core/checkFinishedScroll.ts
2973
+ function checkFinishedScroll(ctx) {
2974
+ ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
2975
+ }
2976
+ function checkFinishedScrollFrame(ctx) {
2977
+ const scrollingTo = ctx.state.scrollingTo;
2978
+ if (scrollingTo) {
2979
+ const { state } = ctx;
2980
+ state.animFrameCheckFinishedScroll = void 0;
2981
+ const scroll = state.scrollPending;
2982
+ const adjust = state.scrollAdjustHandler.getAdjust();
2983
+ const clampedTargetOffset = clampScrollOffset(ctx, scrollingTo.offset - (scrollingTo.viewOffset || 0));
2984
+ const maxOffset = clampScrollOffset(ctx, scroll);
2985
+ const diff1 = Math.abs(scroll - clampedTargetOffset);
2986
+ const diff2 = Math.abs(diff1 - adjust);
2987
+ const isNotOverscrolled = Math.abs(scroll - maxOffset) < 1;
2988
+ if (isNotOverscrolled && (diff1 < 1 || diff2 < 1)) {
2989
+ finishScrollTo(ctx);
2990
+ }
2991
+ }
2992
+ }
2993
+ function checkFinishedScrollFallback(ctx) {
2994
+ const state = ctx.state;
2995
+ const scrollingTo = state.scrollingTo;
2996
+ const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) || !state.didContainersLayout;
2997
+ state.timeoutCheckFinishedScrollFallback = setTimeout(
2998
+ () => {
2999
+ let numChecks = 0;
3000
+ const checkHasScrolled = () => {
3001
+ state.timeoutCheckFinishedScrollFallback = void 0;
3002
+ const isStillScrollingTo = state.scrollingTo;
3003
+ if (isStillScrollingTo) {
3004
+ numChecks++;
3005
+ if (state.hasScrolled || numChecks > 5) {
3006
+ finishScrollTo(ctx);
3007
+ } else {
3008
+ state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, 100);
3009
+ }
3010
+ }
3011
+ };
3012
+ checkHasScrolled();
3013
+ },
3014
+ slowTimeout ? 500 : 100
3015
+ );
3016
+ }
3017
+
2725
3018
  // src/core/doMaintainScrollAtEnd.ts
2726
- function doMaintainScrollAtEnd(ctx, state, animated) {
3019
+ function doMaintainScrollAtEnd(ctx, animated) {
3020
+ const state = ctx.state;
2727
3021
  const {
3022
+ didContainersLayout,
3023
+ isAtEnd,
2728
3024
  refScroller,
2729
3025
  props: { maintainScrollAtEnd }
2730
3026
  } = state;
2731
- if ((state == null ? void 0 : state.isAtEnd) && maintainScrollAtEnd && peek$(ctx, "containersDidLayout")) {
3027
+ if (isAtEnd && maintainScrollAtEnd && didContainersLayout) {
2732
3028
  const paddingTop = peek$(ctx, "alignItemsPaddingTop");
2733
3029
  if (paddingTop > 0) {
2734
3030
  state.scroll = 0;
2735
3031
  }
2736
3032
  requestAnimationFrame(() => {
2737
3033
  var _a3;
2738
- if (state == null ? void 0 : state.isAtEnd) {
3034
+ if (state.isAtEnd) {
2739
3035
  state.maintainingScrollAtEnd = true;
2740
3036
  (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
2741
3037
  animated
@@ -2806,28 +3102,30 @@ function updateAveragesOnDataChange(state, oldData, newData) {
2806
3102
  }
2807
3103
 
2808
3104
  // src/core/checkResetContainers.ts
2809
- function checkResetContainers(ctx, state, dataProp) {
3105
+ function checkResetContainers(ctx, dataProp) {
3106
+ const state = ctx.state;
2810
3107
  const { previousData } = state;
2811
3108
  if (previousData) {
2812
3109
  updateAveragesOnDataChange(state, previousData, dataProp);
2813
3110
  }
2814
3111
  const { maintainScrollAtEnd } = state.props;
2815
- calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
3112
+ calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
2816
3113
  const shouldMaintainScrollAtEnd = maintainScrollAtEnd === true || maintainScrollAtEnd.onDataChange;
2817
- const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, state, false);
3114
+ const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, false);
2818
3115
  if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
2819
3116
  state.isEndReached = false;
2820
3117
  }
2821
3118
  if (!didMaintainScrollAtEnd) {
2822
3119
  checkAtTop(state);
2823
- checkAtBottom(ctx, state);
3120
+ checkAtBottom(ctx);
2824
3121
  }
2825
3122
  delete state.previousData;
2826
3123
  }
2827
3124
 
2828
3125
  // src/core/doInitialAllocateContainers.ts
2829
- function doInitialAllocateContainers(ctx, state) {
3126
+ function doInitialAllocateContainers(ctx) {
2830
3127
  var _a3, _b, _c;
3128
+ const state = ctx.state;
2831
3129
  const {
2832
3130
  scrollLength,
2833
3131
  props: {
@@ -2848,8 +3146,10 @@ function doInitialAllocateContainers(ctx, state) {
2848
3146
  const num = Math.min(20, data.length);
2849
3147
  for (let i = 0; i < num; i++) {
2850
3148
  const item = data[i];
2851
- const itemType = getItemType ? (_a3 = getItemType(item, i)) != null ? _a3 : "" : "";
2852
- totalSize += (_c = (_b = getFixedItemSize == null ? void 0 : getFixedItemSize(i, item, itemType)) != null ? _b : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(i, item, itemType)) != null ? _c : estimatedItemSize;
3149
+ if (item !== void 0) {
3150
+ const itemType = (_a3 = getItemType == null ? void 0 : getItemType(item, i)) != null ? _a3 : "";
3151
+ totalSize += (_c = (_b = getFixedItemSize == null ? void 0 : getFixedItemSize(item, i, itemType)) != null ? _b : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(item, i, itemType)) != null ? _c : estimatedItemSize;
3152
+ }
2853
3153
  }
2854
3154
  averageItemSize = totalSize / num;
2855
3155
  } else {
@@ -2865,10 +3165,10 @@ function doInitialAllocateContainers(ctx, state) {
2865
3165
  if (state.lastLayout) {
2866
3166
  if (state.initialScroll) {
2867
3167
  requestAnimationFrame(() => {
2868
- calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
3168
+ calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
2869
3169
  });
2870
3170
  } else {
2871
- calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
3171
+ calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
2872
3172
  }
2873
3173
  }
2874
3174
  return true;
@@ -2876,7 +3176,8 @@ function doInitialAllocateContainers(ctx, state) {
2876
3176
  }
2877
3177
 
2878
3178
  // src/core/handleLayout.ts
2879
- function handleLayout(ctx, state, layout, setCanRender) {
3179
+ function handleLayout(ctx, layout, setCanRender) {
3180
+ const state = ctx.state;
2880
3181
  const { maintainScrollAtEnd } = state.props;
2881
3182
  const measuredLength = layout[state.props.horizontal ? "width" : "height"];
2882
3183
  const previousLength = state.scrollLength;
@@ -2892,19 +3193,19 @@ function handleLayout(ctx, state, layout, setCanRender) {
2892
3193
  state.lastBatchingAction = Date.now();
2893
3194
  state.scrollForNextCalculateItemsInView = void 0;
2894
3195
  if (scrollLength > 0) {
2895
- doInitialAllocateContainers(ctx, state);
3196
+ doInitialAllocateContainers(ctx);
2896
3197
  }
2897
3198
  if (needsCalculate) {
2898
- calculateItemsInView(ctx, state, { doMVCP: true });
3199
+ calculateItemsInView(ctx, { doMVCP: true });
2899
3200
  }
2900
3201
  if (didChange || otherAxisSize !== prevOtherAxisSize) {
2901
3202
  set$(ctx, "scrollSize", { height: layout.height, width: layout.width });
2902
3203
  }
2903
3204
  if (maintainScrollAtEnd === true || maintainScrollAtEnd.onLayout) {
2904
- doMaintainScrollAtEnd(ctx, state, false);
3205
+ doMaintainScrollAtEnd(ctx, false);
2905
3206
  }
2906
- updateAlignItemsPaddingTop(ctx, state);
2907
- checkAtBottom(ctx, state);
3207
+ updateAlignItemsPaddingTop(ctx);
3208
+ checkAtBottom(ctx);
2908
3209
  checkAtTop(state);
2909
3210
  if (state) {
2910
3211
  state.needsOtherAxisSize = otherAxisSize - (state.props.stylePaddingTop || 0) < 10;
@@ -2920,8 +3221,9 @@ function handleLayout(ctx, state, layout, setCanRender) {
2920
3221
  }
2921
3222
 
2922
3223
  // src/core/onScroll.ts
2923
- function onScroll(ctx, state, event) {
3224
+ function onScroll(ctx, event) {
2924
3225
  var _a3, _b, _c;
3226
+ const state = ctx.state;
2925
3227
  const {
2926
3228
  scrollProcessingEnabled,
2927
3229
  props: { onScroll: onScrollProp }
@@ -2932,9 +3234,25 @@ function onScroll(ctx, state, event) {
2932
3234
  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) {
2933
3235
  return;
2934
3236
  }
2935
- const newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
3237
+ let newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
3238
+ if (state.scrollingTo) {
3239
+ const maxOffset = clampScrollOffset(ctx, newScroll);
3240
+ if (newScroll !== maxOffset && Math.abs(newScroll - maxOffset) > 1) {
3241
+ newScroll = maxOffset;
3242
+ scrollTo(ctx, {
3243
+ forceScroll: true,
3244
+ isInitialScroll: true,
3245
+ noScrollingTo: true,
3246
+ offset: newScroll
3247
+ });
3248
+ return;
3249
+ }
3250
+ }
2936
3251
  state.scrollPending = newScroll;
2937
- updateScroll(ctx, state, newScroll);
3252
+ updateScroll(ctx, newScroll);
3253
+ if (state.scrollingTo) {
3254
+ checkFinishedScroll(ctx);
3255
+ }
2938
3256
  onScrollProp == null ? void 0 : onScrollProp(event);
2939
3257
  }
2940
3258
 
@@ -2943,51 +3261,58 @@ var ScrollAdjustHandler = class {
2943
3261
  constructor(ctx) {
2944
3262
  this.appliedAdjust = 0;
2945
3263
  this.pendingAdjust = 0;
2946
- this.mounted = false;
2947
- this.context = ctx;
2948
- {
2949
- const commitPendingAdjust = () => {
2950
- const state = this.context.internalState;
2951
- const pending = this.pendingAdjust;
2952
- if (pending !== 0) {
2953
- this.pendingAdjust = 0;
2954
- this.appliedAdjust += pending;
2955
- state.scroll += pending;
2956
- state.scrollForNextCalculateItemsInView = void 0;
2957
- set$(this.context, "scrollAdjustPending", 0);
2958
- set$(this.context, "scrollAdjust", this.appliedAdjust);
2959
- calculateItemsInView(this.context, this.context.internalState);
2960
- }
2961
- };
2962
- listen$(this.context, "scrollingTo", (value) => {
2963
- if (value === void 0) {
2964
- commitPendingAdjust();
2965
- }
2966
- });
2967
- }
3264
+ this.ctx = ctx;
2968
3265
  }
2969
3266
  requestAdjust(add) {
2970
- const scrollingTo = peek$(this.context, "scrollingTo");
3267
+ const scrollingTo = this.ctx.state.scrollingTo;
2971
3268
  if ((scrollingTo == null ? void 0 : scrollingTo.animated) && !scrollingTo.isInitialScroll) {
2972
3269
  this.pendingAdjust += add;
2973
- set$(this.context, "scrollAdjustPending", this.pendingAdjust);
3270
+ set$(this.ctx, "scrollAdjustPending", this.pendingAdjust);
2974
3271
  } else {
2975
3272
  this.appliedAdjust += add;
2976
- set$(this.context, "scrollAdjust", this.appliedAdjust);
3273
+ set$(this.ctx, "scrollAdjust", this.appliedAdjust);
3274
+ }
3275
+ if (this.ctx.state.scrollingTo) {
3276
+ checkFinishedScroll(this.ctx);
2977
3277
  }
2978
- }
2979
- setMounted() {
2980
- this.mounted = true;
2981
3278
  }
2982
3279
  getAdjust() {
2983
3280
  return this.appliedAdjust;
2984
3281
  }
3282
+ commitPendingAdjust(scrollTarget) {
3283
+ {
3284
+ const state = this.ctx.state;
3285
+ const pending = this.pendingAdjust;
3286
+ this.pendingAdjust = 0;
3287
+ if (pending !== 0) {
3288
+ let targetScroll;
3289
+ if ((scrollTarget == null ? void 0 : scrollTarget.index) !== void 0) {
3290
+ const currentOffset = calculateOffsetForIndex(this.ctx, scrollTarget.index);
3291
+ targetScroll = calculateOffsetWithOffsetPosition(this.ctx, currentOffset, scrollTarget);
3292
+ targetScroll = clampScrollOffset(this.ctx, targetScroll);
3293
+ } else {
3294
+ targetScroll = clampScrollOffset(this.ctx, state.scroll + pending);
3295
+ }
3296
+ const adjustment = targetScroll - state.scroll;
3297
+ if (Math.abs(adjustment) > 0.1 || Math.abs(pending) > 0.1) {
3298
+ this.appliedAdjust += adjustment;
3299
+ state.scroll = targetScroll;
3300
+ state.scrollForNextCalculateItemsInView = void 0;
3301
+ set$(this.ctx, "scrollAdjust", this.appliedAdjust);
3302
+ }
3303
+ set$(this.ctx, "scrollAdjustPending", 0);
3304
+ calculateItemsInView(this.ctx);
3305
+ }
3306
+ }
3307
+ }
2985
3308
  };
2986
3309
 
2987
3310
  // src/core/updateItemSize.ts
2988
- function updateItemSize(ctx, state, itemKey, sizeObj) {
3311
+ function updateItemSize(ctx, itemKey, sizeObj) {
2989
3312
  var _a3;
3313
+ const state = ctx.state;
2990
3314
  const {
3315
+ didContainersLayout,
2991
3316
  sizesKnown,
2992
3317
  props: {
2993
3318
  getFixedItemSize,
@@ -3010,31 +3335,24 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
3010
3335
  return;
3011
3336
  }
3012
3337
  const type = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
3013
- const size2 = getFixedItemSize(index, itemData, type);
3338
+ const size2 = getFixedItemSize(itemData, index, type);
3014
3339
  if (size2 !== void 0 && size2 === sizesKnown.get(itemKey)) {
3015
3340
  return;
3016
3341
  }
3017
3342
  }
3018
- const containersDidLayout = peek$(ctx, "containersDidLayout");
3019
- let needsRecalculate = !containersDidLayout;
3343
+ let needsRecalculate = !didContainersLayout;
3020
3344
  let shouldMaintainScrollAtEnd = false;
3021
3345
  let minIndexSizeChanged;
3022
3346
  let maxOtherAxisSize = peek$(ctx, "otherAxisSize") || 0;
3023
3347
  const prevSizeKnown = state.sizesKnown.get(itemKey);
3024
- const diff = updateOneItemSize(ctx, state, itemKey, sizeObj);
3348
+ const diff = updateOneItemSize(ctx, itemKey, sizeObj);
3025
3349
  const size = roundSize(horizontal ? sizeObj.width : sizeObj.height);
3026
3350
  if (diff !== 0) {
3027
3351
  minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
3028
3352
  const { startBuffered, endBuffered } = state;
3029
3353
  needsRecalculate || (needsRecalculate = index >= startBuffered && index <= endBuffered);
3030
- if (!needsRecalculate) {
3031
- const numContainers = ctx.values.get("numContainers");
3032
- for (let i = 0; i < numContainers; i++) {
3033
- if (peek$(ctx, `containerItemKey${i}`) === itemKey) {
3034
- needsRecalculate = true;
3035
- break;
3036
- }
3037
- }
3354
+ if (!needsRecalculate && state.containerItemKeys.has(itemKey)) {
3355
+ needsRecalculate = true;
3038
3356
  }
3039
3357
  if (state.needsOtherAxisSize) {
3040
3358
  const otherAxisSize = horizontal ? sizeObj.height : sizeObj.width;
@@ -3070,22 +3388,22 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
3070
3388
  if (!cur || maxOtherAxisSize > cur) {
3071
3389
  set$(ctx, "otherAxisSize", maxOtherAxisSize);
3072
3390
  }
3073
- if (containersDidLayout || checkAllSizesKnown(state)) {
3391
+ if (didContainersLayout || checkAllSizesKnown(state)) {
3074
3392
  if (needsRecalculate) {
3075
3393
  state.scrollForNextCalculateItemsInView = void 0;
3076
- calculateItemsInView(ctx, state, { doMVCP: true });
3394
+ calculateItemsInView(ctx, { doMVCP: true });
3077
3395
  }
3078
3396
  if (shouldMaintainScrollAtEnd) {
3079
3397
  if (maintainScrollAtEnd === true || maintainScrollAtEnd.onItemLayout) {
3080
- doMaintainScrollAtEnd(ctx, state, false);
3398
+ doMaintainScrollAtEnd(ctx, false);
3081
3399
  }
3082
3400
  }
3083
3401
  }
3084
3402
  }
3085
- function updateOneItemSize(ctx, state, itemKey, sizeObj) {
3403
+ function updateOneItemSize(ctx, itemKey, sizeObj) {
3086
3404
  var _a3;
3405
+ const state = ctx.state;
3087
3406
  const {
3088
- sizes,
3089
3407
  indexByKey,
3090
3408
  sizesKnown,
3091
3409
  averageSizes,
@@ -3093,9 +3411,10 @@ function updateOneItemSize(ctx, state, itemKey, sizeObj) {
3093
3411
  } = state;
3094
3412
  if (!data) return 0;
3095
3413
  const index = indexByKey.get(itemKey);
3096
- const prevSize = getItemSize(ctx, state, itemKey, index, data[index]);
3414
+ const prevSize = getItemSize(ctx, itemKey, index, data[index]);
3097
3415
  const rawSize = horizontal ? sizeObj.width : sizeObj.height;
3098
3416
  const size = Math.round(rawSize) ;
3417
+ const prevSizeKnown = sizesKnown.get(itemKey);
3099
3418
  sizesKnown.set(itemKey, size);
3100
3419
  if (!getEstimatedItemSize && !getFixedItemSize && size > 0) {
3101
3420
  const itemType = getItemType ? (_a3 = getItemType(data[index], index)) != null ? _a3 : "" : "";
@@ -3103,15 +3422,25 @@ function updateOneItemSize(ctx, state, itemKey, sizeObj) {
3103
3422
  if (!averages) {
3104
3423
  averages = averageSizes[itemType] = { avg: 0, num: 0 };
3105
3424
  }
3106
- averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
3107
- averages.num++;
3425
+ if (prevSizeKnown !== void 0 && prevSizeKnown > 0) {
3426
+ averages.avg += (size - prevSizeKnown) / averages.num;
3427
+ } else {
3428
+ averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
3429
+ averages.num++;
3430
+ }
3108
3431
  }
3109
3432
  if (!prevSize || Math.abs(prevSize - size) > 0.1) {
3110
- setSize(ctx, state, itemKey, size);
3433
+ setSize(ctx, itemKey, size);
3111
3434
  return size - prevSize;
3112
3435
  }
3113
3436
  return 0;
3114
3437
  }
3438
+ function useWrapIfItem(fn) {
3439
+ return useMemo(
3440
+ () => fn ? (arg1, arg2, arg3) => arg1 !== void 0 && arg2 !== void 0 ? fn(arg1, arg2, arg3) : void 0 : void 0,
3441
+ [fn]
3442
+ );
3443
+ }
3115
3444
  var useCombinedRef = (...refs) => {
3116
3445
  const callback = useCallback((element) => {
3117
3446
  for (const ref of refs) {
@@ -3154,14 +3483,15 @@ function createColumnWrapperStyle(contentContainerStyle) {
3154
3483
  }
3155
3484
 
3156
3485
  // src/utils/createImperativeHandle.ts
3157
- function createImperativeHandle(ctx, state) {
3486
+ function createImperativeHandle(ctx) {
3487
+ const state = ctx.state;
3158
3488
  const scrollIndexIntoView = (options) => {
3159
3489
  if (state) {
3160
3490
  const { index, ...rest } = options;
3161
3491
  const { startNoBuffer, endNoBuffer } = state;
3162
3492
  if (index < startNoBuffer || index > endNoBuffer) {
3163
3493
  const viewPosition = index < startNoBuffer ? 0 : 1;
3164
- scrollToIndex(ctx, state, {
3494
+ scrollToIndex(ctx, {
3165
3495
  ...rest,
3166
3496
  index,
3167
3497
  viewPosition
@@ -3176,7 +3506,7 @@ function createImperativeHandle(ctx, state) {
3176
3506
  getScrollableNode: () => refScroller.current.getScrollableNode(),
3177
3507
  getScrollResponder: () => refScroller.current.getScrollResponder(),
3178
3508
  getState: () => ({
3179
- activeStickyIndex: state.activeStickyIndex,
3509
+ activeStickyIndex: peek$(ctx, "activeStickyIndex"),
3180
3510
  contentLength: state.totalSize,
3181
3511
  data: state.props.data,
3182
3512
  elementAtIndex: (index) => {
@@ -3187,6 +3517,8 @@ function createImperativeHandle(ctx, state) {
3187
3517
  endBuffered: state.endBuffered,
3188
3518
  isAtEnd: state.isAtEnd,
3189
3519
  isAtStart: state.isAtStart,
3520
+ listen: (signalName, cb) => listen$(ctx, signalName, cb),
3521
+ listenToPosition: (key, cb) => listenPosition$(ctx, key, cb),
3190
3522
  positionAtIndex: (index) => state.positions.get(getId(state, index)),
3191
3523
  positions: state.positions,
3192
3524
  scroll: state.scroll,
@@ -3211,23 +3543,23 @@ function createImperativeHandle(ctx, state) {
3211
3543
  if (index !== -1) {
3212
3544
  const paddingBottom = stylePaddingBottom || 0;
3213
3545
  const footerSize = peek$(ctx, "footerSize") || 0;
3214
- scrollToIndex(ctx, state, {
3546
+ scrollToIndex(ctx, {
3547
+ ...options,
3215
3548
  index,
3216
3549
  viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
3217
- viewPosition: 1,
3218
- ...options
3550
+ viewPosition: 1
3219
3551
  });
3220
3552
  }
3221
3553
  },
3222
- scrollToIndex: (params) => scrollToIndex(ctx, state, params),
3554
+ scrollToIndex: (params) => scrollToIndex(ctx, params),
3223
3555
  scrollToItem: ({ item, ...props }) => {
3224
3556
  const data = state.props.data;
3225
3557
  const index = data.indexOf(item);
3226
3558
  if (index !== -1) {
3227
- scrollToIndex(ctx, state, { index, ...props });
3559
+ scrollToIndex(ctx, { index, ...props });
3228
3560
  }
3229
3561
  },
3230
- scrollToOffset: (params) => scrollTo(ctx, state, params),
3562
+ scrollToOffset: (params) => scrollTo(ctx, params),
3231
3563
  setScrollProcessingEnabled: (enabled) => {
3232
3564
  state.scrollProcessingEnabled = enabled;
3233
3565
  },
@@ -3237,8 +3569,57 @@ function createImperativeHandle(ctx, state) {
3237
3569
  }
3238
3570
  };
3239
3571
  }
3240
- function getRenderedItem(ctx, state, key) {
3572
+
3573
+ // src/utils/getAlwaysRenderIndices.ts
3574
+ var sortAsc = (a, b) => a - b;
3575
+ var toCount = (value) => typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
3576
+ var addIndex = (result, dataLength, index) => {
3577
+ if (index >= 0 && index < dataLength) {
3578
+ result.add(index);
3579
+ }
3580
+ };
3581
+ function getAlwaysRenderIndices(config, data, keyExtractor) {
3582
+ var _a3, _b;
3583
+ if (!config || data.length === 0) {
3584
+ return [];
3585
+ }
3586
+ const result = /* @__PURE__ */ new Set();
3587
+ const dataLength = data.length;
3588
+ const topCount = toCount(config.top);
3589
+ if (topCount > 0) {
3590
+ for (let i = 0; i < Math.min(topCount, dataLength); i++) {
3591
+ addIndex(result, dataLength, i);
3592
+ }
3593
+ }
3594
+ const bottomCount = toCount(config.bottom);
3595
+ if (bottomCount > 0) {
3596
+ for (let i = Math.max(0, dataLength - bottomCount); i < dataLength; i++) {
3597
+ addIndex(result, dataLength, i);
3598
+ }
3599
+ }
3600
+ if ((_a3 = config.indices) == null ? void 0 : _a3.length) {
3601
+ for (const index of config.indices) {
3602
+ if (!Number.isFinite(index)) continue;
3603
+ addIndex(result, dataLength, Math.floor(index));
3604
+ }
3605
+ }
3606
+ if ((_b = config.keys) == null ? void 0 : _b.length) {
3607
+ const keys = new Set(config.keys);
3608
+ for (let i = 0; i < dataLength && keys.size > 0; i++) {
3609
+ const key = keyExtractor(data[i], i);
3610
+ if (keys.has(key)) {
3611
+ addIndex(result, dataLength, i);
3612
+ keys.delete(key);
3613
+ }
3614
+ }
3615
+ }
3616
+ const indices = Array.from(result);
3617
+ indices.sort(sortAsc);
3618
+ return indices;
3619
+ }
3620
+ function getRenderedItem(ctx, key) {
3241
3621
  var _a3;
3622
+ const state = ctx.state;
3242
3623
  if (!state) {
3243
3624
  return null;
3244
3625
  }
@@ -3265,6 +3646,25 @@ function getRenderedItem(ctx, state, key) {
3265
3646
  }
3266
3647
  return { index, item: data[index], renderedItem };
3267
3648
  }
3649
+
3650
+ // src/utils/normalizeMaintainVisibleContentPosition.ts
3651
+ function normalizeMaintainVisibleContentPosition(value) {
3652
+ var _a3, _b;
3653
+ if (value === true) {
3654
+ return { data: true, size: true };
3655
+ }
3656
+ if (value && typeof value === "object") {
3657
+ return {
3658
+ data: (_a3 = value.data) != null ? _a3 : false,
3659
+ size: (_b = value.size) != null ? _b : true,
3660
+ shouldRestorePosition: value.shouldRestorePosition
3661
+ };
3662
+ }
3663
+ if (value === false) {
3664
+ return { data: false, size: false };
3665
+ }
3666
+ return { data: false, size: true };
3667
+ }
3268
3668
  function useThrottleDebounce(mode) {
3269
3669
  const timeoutRef = useRef(null);
3270
3670
  const lastCallTimeRef = useRef(0);
@@ -3315,6 +3715,7 @@ function useThrottledOnScroll(originalHandler, scrollEventThrottle) {
3315
3715
  var DEFAULT_DRAW_DISTANCE = 250;
3316
3716
  var DEFAULT_ITEM_SIZE = 100;
3317
3717
  var LegendList = typedMemo(
3718
+ // biome-ignore lint/nursery/noShadow: const function name shadowing is intentional
3318
3719
  typedForwardRef(function LegendList2(props, forwardedRef) {
3319
3720
  const { children, data: dataProp, renderItem: renderItemProp, ...restProps } = props;
3320
3721
  const isChildrenMode = children !== void 0 && dataProp === void 0;
@@ -3332,15 +3733,16 @@ var LegendList = typedMemo(
3332
3733
  })
3333
3734
  );
3334
3735
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
3335
- var _a3, _b;
3736
+ var _a3, _b, _c, _d;
3336
3737
  const {
3337
3738
  alignItemsAtEnd = false,
3739
+ alwaysRender,
3338
3740
  columnWrapperStyle,
3339
3741
  contentContainerStyle: contentContainerStyleProp,
3742
+ contentInset,
3340
3743
  data: dataProp = [],
3341
3744
  dataVersion,
3342
3745
  drawDistance = 250,
3343
- enableAverages = true,
3344
3746
  estimatedItemSize: estimatedItemSizeProp,
3345
3747
  estimatedListSize,
3346
3748
  extraData,
@@ -3358,11 +3760,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3358
3760
  ListHeaderComponent,
3359
3761
  maintainScrollAtEnd = false,
3360
3762
  maintainScrollAtEndThreshold = 0.1,
3361
- maintainVisibleContentPosition = false,
3763
+ maintainVisibleContentPosition: maintainVisibleContentPositionProp,
3362
3764
  numColumns: numColumnsProp = 1,
3363
3765
  onEndReached,
3364
3766
  onEndReachedThreshold = 0.5,
3365
3767
  onItemSizeChanged,
3768
+ onMetricsChange,
3366
3769
  onLayout: onLayoutProp,
3367
3770
  onLoad,
3368
3771
  onMomentumScrollEnd,
@@ -3382,20 +3785,26 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3382
3785
  snapToIndices,
3383
3786
  stickyHeaderIndices: stickyHeaderIndicesProp,
3384
3787
  stickyIndices: stickyIndicesDeprecated,
3788
+ // TODOV3: Remove from v3 release
3385
3789
  style: styleProp,
3386
3790
  suggestEstimatedItemSize,
3387
3791
  viewabilityConfig,
3388
3792
  viewabilityConfigCallbackPairs,
3389
3793
  waitForInitialLayout = true,
3794
+ stickyHeaderConfig,
3390
3795
  ...rest
3391
3796
  } = props;
3797
+ const animatedPropsInternal = props.animatedPropsInternal;
3392
3798
  const { childrenMode } = rest;
3393
3799
  const contentContainerStyle = { ...StyleSheet.flatten(contentContainerStyleProp) };
3394
3800
  const style = { ...StyleSheet.flatten(styleProp) };
3395
3801
  const stylePaddingTopState = extractPadding(style, contentContainerStyle, "Top");
3396
3802
  const stylePaddingBottomState = extractPadding(style, contentContainerStyle, "Bottom");
3803
+ const maintainVisibleContentPositionConfig = normalizeMaintainVisibleContentPosition(
3804
+ maintainVisibleContentPositionProp
3805
+ );
3397
3806
  const [renderNum, setRenderNum] = useState(0);
3398
- 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;
3807
+ const initialScrollProp = initialScrollAtEnd ? { index: Math.max(0, dataProp.length - 1), viewOffset: -stylePaddingBottomState, viewPosition: 1 } : initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? { index: initialScrollIndexProp.index || 0, viewOffset: initialScrollIndexProp.viewOffset || 0 } : { index: initialScrollIndexProp || 0, viewOffset: initialScrollOffsetProp || 0 } : void 0;
3399
3808
  const [canRender, setCanRender] = React3.useState(!IsNewArchitecture);
3400
3809
  const ctx = useStateContext();
3401
3810
  ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
@@ -3405,6 +3814,18 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3405
3814
  const scrollBuffer = (drawDistance != null ? drawDistance : DEFAULT_DRAW_DISTANCE) || 1;
3406
3815
  const keyExtractor = keyExtractorProp != null ? keyExtractorProp : (_item, index) => index.toString();
3407
3816
  const stickyHeaderIndices = stickyHeaderIndicesProp != null ? stickyHeaderIndicesProp : stickyIndicesDeprecated;
3817
+ const alwaysRenderIndices = useMemo(() => {
3818
+ const indices = getAlwaysRenderIndices(alwaysRender, dataProp, keyExtractor);
3819
+ return { arr: indices, set: new Set(indices) };
3820
+ }, [
3821
+ alwaysRender == null ? void 0 : alwaysRender.top,
3822
+ alwaysRender == null ? void 0 : alwaysRender.bottom,
3823
+ (_a3 = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _a3.join(","),
3824
+ (_b = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _b.join(","),
3825
+ dataProp,
3826
+ dataVersion,
3827
+ keyExtractor
3828
+ ]);
3408
3829
  if (IS_DEV && stickyIndicesDeprecated && !stickyHeaderIndicesProp) {
3409
3830
  warnDevOnce(
3410
3831
  "stickyIndices",
@@ -3413,13 +3834,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3413
3834
  }
3414
3835
  const refState = useRef();
3415
3836
  if (!refState.current) {
3416
- if (!ctx.internalState) {
3837
+ if (!ctx.state) {
3417
3838
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : { height: 0, width: 0 } )[horizontal ? "width" : "height"];
3418
- ctx.internalState = {
3419
- activeStickyIndex: void 0,
3839
+ ctx.state = {
3840
+ activeStickyIndex: -1,
3420
3841
  averageSizes: {},
3421
3842
  columns: /* @__PURE__ */ new Map(),
3422
- containerItemKeys: /* @__PURE__ */ new Set(),
3843
+ containerItemKeys: /* @__PURE__ */ new Map(),
3423
3844
  containerItemTypes: /* @__PURE__ */ new Map(),
3424
3845
  dataChangeNeedsScrollUpdate: false,
3425
3846
  didColumnsChange: false,
@@ -3436,17 +3857,18 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3436
3857
  attempts: 0,
3437
3858
  index: initialScrollProp.index,
3438
3859
  settledTicks: 0,
3439
- viewOffset: (_a3 = initialScrollProp.viewOffset) != null ? _a3 : 0,
3860
+ viewOffset: (_c = initialScrollProp.viewOffset) != null ? _c : 0,
3440
3861
  viewPosition: initialScrollProp.viewPosition
3441
3862
  } : void 0,
3442
3863
  initialScroll: initialScrollProp,
3443
3864
  isAtEnd: false,
3444
3865
  isAtStart: false,
3445
- isEndReached: false,
3866
+ isEndReached: null,
3446
3867
  isFirst: true,
3447
- isStartReached: false,
3868
+ isStartReached: null,
3448
3869
  lastBatchingAction: Date.now(),
3449
3870
  lastLayout: void 0,
3871
+ lastScrollDelta: 0,
3450
3872
  loadStartTime: Date.now(),
3451
3873
  minIndexSizeChanged: 0,
3452
3874
  nativeMarginTop: 0,
@@ -3476,12 +3898,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3476
3898
  totalSize: 0,
3477
3899
  viewabilityConfigCallbackPairs: void 0
3478
3900
  };
3479
- const internalState = ctx.internalState;
3480
- internalState.triggerCalculateItemsInView = (params) => calculateItemsInView(ctx, internalState, params);
3481
- set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPosition);
3901
+ const internalState = ctx.state;
3902
+ internalState.triggerCalculateItemsInView = (params) => calculateItemsInView(ctx, params);
3903
+ set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPositionConfig);
3482
3904
  set$(ctx, "extraData", extraData);
3483
3905
  }
3484
- refState.current = ctx.internalState;
3906
+ refState.current = ctx.state;
3485
3907
  }
3486
3908
  const state = refState.current;
3487
3909
  const isFirstLocal = state.isFirst;
@@ -3495,20 +3917,24 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3495
3917
  const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
3496
3918
  state.props = {
3497
3919
  alignItemsAtEnd,
3920
+ alwaysRender,
3921
+ alwaysRenderIndicesArr: alwaysRenderIndices.arr,
3922
+ alwaysRenderIndicesSet: alwaysRenderIndices.set,
3923
+ animatedProps: animatedPropsInternal,
3924
+ contentInset,
3498
3925
  data: dataProp,
3499
3926
  dataVersion,
3500
- enableAverages,
3501
3927
  estimatedItemSize,
3502
- getEstimatedItemSize,
3503
- getFixedItemSize,
3504
- getItemType,
3928
+ getEstimatedItemSize: useWrapIfItem(getEstimatedItemSize),
3929
+ getFixedItemSize: useWrapIfItem(getFixedItemSize),
3930
+ getItemType: useWrapIfItem(getItemType),
3505
3931
  horizontal: !!horizontal,
3506
3932
  initialContainerPoolRatio,
3507
3933
  itemsAreEqual,
3508
- keyExtractor,
3934
+ keyExtractor: useWrapIfItem(keyExtractor),
3509
3935
  maintainScrollAtEnd,
3510
3936
  maintainScrollAtEndThreshold,
3511
- maintainVisibleContentPosition,
3937
+ maintainVisibleContentPosition: maintainVisibleContentPositionConfig,
3512
3938
  numColumns: numColumnsProp,
3513
3939
  onEndReached,
3514
3940
  onEndReachedThreshold,
@@ -3540,57 +3966,47 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3540
3966
  set$(ctx, "lastItemKeys", memoizedLastItemKeys);
3541
3967
  set$(ctx, "numColumns", numColumnsProp);
3542
3968
  const prevPaddingTop = peek$(ctx, "stylePaddingTop");
3543
- setPaddingTop(ctx, state, { stylePaddingTop: stylePaddingTopState });
3969
+ setPaddingTop(ctx, { stylePaddingTop: stylePaddingTopState });
3544
3970
  refState.current.props.stylePaddingBottom = stylePaddingBottomState;
3545
3971
  let paddingDiff = stylePaddingTopState - prevPaddingTop;
3546
- if (paddingDiff && prevPaddingTop !== void 0 && Platform.OS === "ios") {
3972
+ if (maintainVisibleContentPositionConfig.size && paddingDiff && prevPaddingTop !== void 0 && Platform.OS === "ios") {
3547
3973
  if (state.scroll < 0) {
3548
3974
  paddingDiff += state.scroll;
3549
3975
  }
3550
- requestAdjust(ctx, state, paddingDiff);
3976
+ requestAdjust(ctx, paddingDiff);
3551
3977
  }
3552
3978
  };
3553
3979
  if (isFirstLocal) {
3554
3980
  initializeStateVars();
3555
3981
  updateItemPositions(
3556
3982
  ctx,
3557
- state,
3558
3983
  /*dataChanged*/
3559
3984
  true
3560
3985
  );
3561
3986
  }
3562
3987
  const initialContentOffset = useMemo(() => {
3563
- var _a4, _b2;
3564
- const { initialScroll } = refState.current;
3565
- if (!initialScroll) {
3988
+ let value;
3989
+ const { initialScroll, initialAnchor } = refState.current;
3990
+ if (initialScroll) {
3991
+ if (initialScroll.contentOffset !== void 0) {
3992
+ value = initialScroll.contentOffset;
3993
+ } else {
3994
+ const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, initialScroll.index) : 0;
3995
+ const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, initialScroll);
3996
+ const clampedOffset = clampScrollOffset(ctx, resolvedOffset);
3997
+ const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
3998
+ refState.current.initialScroll = updatedInitialScroll;
3999
+ state.initialScroll = updatedInitialScroll;
4000
+ value = clampedOffset;
4001
+ }
4002
+ } else {
3566
4003
  refState.current.initialAnchor = void 0;
3567
- return 0;
3568
- }
3569
- if (initialScroll.index !== void 0 && (!refState.current.initialAnchor || ((_a4 = refState.current.initialAnchor) == null ? void 0 : _a4.index) !== initialScroll.index)) {
3570
- refState.current.initialAnchor = {
3571
- attempts: 0,
3572
- index: initialScroll.index,
3573
- settledTicks: 0,
3574
- viewOffset: (_b2 = initialScroll.viewOffset) != null ? _b2 : 0,
3575
- viewPosition: initialScroll.viewPosition
3576
- };
4004
+ value = 0;
4005
+ }
4006
+ if (!value) {
4007
+ setInitialRenderState(ctx, { didInitialScroll: true });
3577
4008
  }
3578
- if (initialScroll.contentOffset !== void 0) {
3579
- return initialScroll.contentOffset;
3580
- }
3581
- const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, state, initialScroll.index) : 0;
3582
- const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, state, baseOffset, initialScroll);
3583
- let clampedOffset = resolvedOffset;
3584
- if (Number.isFinite(state.scrollLength) && Number.isFinite(state.totalSize)) {
3585
- const maxOffset = Math.max(0, state.totalSize - state.scrollLength);
3586
- clampedOffset = Math.min(clampedOffset, maxOffset);
3587
- }
3588
- clampedOffset = Math.max(0, clampedOffset);
3589
- const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
3590
- refState.current.initialScroll = updatedInitialScroll;
3591
- state.initialScroll = updatedInitialScroll;
3592
- refState.current.isStartReached = clampedOffset < refState.current.scrollLength * onStartReachedThreshold;
3593
- return clampedOffset;
4009
+ return value;
3594
4010
  }, [renderNum]);
3595
4011
  if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
3596
4012
  refState.current.lastBatchingAction = Date.now();
@@ -3618,12 +4034,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3618
4034
  }
3619
4035
  }, []);
3620
4036
  const doInitialScroll = useCallback(() => {
3621
- var _a4;
3622
- const initialScroll = state.initialScroll;
3623
- if (initialScroll) {
3624
- scrollTo(ctx, state, {
4037
+ const { initialScroll, didFinishInitialScroll, queuedInitialLayout, scrollingTo } = state;
4038
+ if (initialScroll && !queuedInitialLayout && !didFinishInitialScroll && !scrollingTo) {
4039
+ scrollTo(ctx, {
3625
4040
  animated: false,
3626
- index: (_a4 = state.initialScroll) == null ? void 0 : _a4.index,
4041
+ index: initialScroll == null ? void 0 : initialScroll.index,
3627
4042
  isInitialScroll: true,
3628
4043
  offset: initialContentOffset,
3629
4044
  precomputedWithViewOffset: true
@@ -3632,7 +4047,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3632
4047
  }, [initialContentOffset]);
3633
4048
  const onLayoutChange = useCallback((layout) => {
3634
4049
  doInitialScroll();
3635
- handleLayout(ctx, state, layout, setCanRender);
4050
+ handleLayout(ctx, layout, setCanRender);
3636
4051
  }, []);
3637
4052
  const { onLayout } = useOnLayoutSync({
3638
4053
  onLayoutChange,
@@ -3642,7 +4057,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3642
4057
  });
3643
4058
  useLayoutEffect(() => {
3644
4059
  if (snapToIndices) {
3645
- updateSnapToOffsets(ctx, state);
4060
+ updateSnapToOffsets(ctx);
3646
4061
  }
3647
4062
  }, [snapToIndices]);
3648
4063
  useLayoutEffect(() => {
@@ -3652,9 +4067,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3652
4067
  isFirst,
3653
4068
  props: { data }
3654
4069
  } = state;
3655
- const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx, state);
4070
+ const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx);
3656
4071
  if (!didAllocateContainers && !isFirst && (didDataChange || didColumnsChange)) {
3657
- checkResetContainers(ctx, state, data);
4072
+ checkResetContainers(ctx, data);
3658
4073
  }
3659
4074
  state.didColumnsChange = false;
3660
4075
  state.didDataChange = false;
@@ -3670,6 +4085,34 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3670
4085
  stylePaddingBottomState,
3671
4086
  stylePaddingTopState
3672
4087
  ]);
4088
+ useEffect(() => {
4089
+ if (!onMetricsChange) {
4090
+ return;
4091
+ }
4092
+ let lastMetrics;
4093
+ const emitMetrics = () => {
4094
+ const metrics = {
4095
+ alignItemsAtEndPadding: peek$(ctx, "alignItemsPaddingTop") || 0,
4096
+ footerSize: peek$(ctx, "footerSize") || 0,
4097
+ headerSize: peek$(ctx, "headerSize") || 0
4098
+ };
4099
+ if (!lastMetrics || metrics.alignItemsAtEndPadding !== lastMetrics.alignItemsAtEndPadding || metrics.headerSize !== lastMetrics.headerSize || metrics.footerSize !== lastMetrics.footerSize) {
4100
+ lastMetrics = metrics;
4101
+ onMetricsChange(metrics);
4102
+ }
4103
+ };
4104
+ emitMetrics();
4105
+ const unsubscribe = [
4106
+ listen$(ctx, "alignItemsPaddingTop", emitMetrics),
4107
+ listen$(ctx, "headerSize", emitMetrics),
4108
+ listen$(ctx, "footerSize", emitMetrics)
4109
+ ];
4110
+ return () => {
4111
+ for (const unsub of unsubscribe) {
4112
+ unsub();
4113
+ }
4114
+ };
4115
+ }, [ctx, onMetricsChange]);
3673
4116
  useEffect(() => {
3674
4117
  const viewability = setupViewability({
3675
4118
  onViewableItemsChanged,
@@ -3679,15 +4122,21 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3679
4122
  state.viewabilityConfigCallbackPairs = viewability;
3680
4123
  state.enableScrollForNextCalculateItemsInView = !viewability;
3681
4124
  }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
3682
- useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx, state), []);
4125
+ useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx), []);
3683
4126
  {
3684
4127
  useEffect(doInitialScroll, []);
3685
4128
  }
3686
4129
  const fns = useMemo(
3687
4130
  () => ({
3688
- getRenderedItem: (key) => getRenderedItem(ctx, state, key),
3689
- onScroll: (event) => onScroll(ctx, state, event),
3690
- updateItemSize: (itemKey, sizeObj) => updateItemSize(ctx, state, itemKey, sizeObj)
4131
+ getRenderedItem: (key) => getRenderedItem(ctx, key),
4132
+ onMomentumScrollEnd: (event) => {
4133
+ checkFinishedScrollFallback(ctx);
4134
+ if (onMomentumScrollEnd) {
4135
+ onMomentumScrollEnd(event);
4136
+ }
4137
+ },
4138
+ onScroll: (event) => onScroll(ctx, event),
4139
+ updateItemSize: (itemKey, sizeObj) => updateItemSize(ctx, itemKey, sizeObj)
3691
4140
  }),
3692
4141
  []
3693
4142
  );
@@ -3699,24 +4148,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3699
4148
  alignItemsAtEnd,
3700
4149
  canRender,
3701
4150
  contentContainerStyle,
4151
+ contentInset,
3702
4152
  getRenderedItem: fns.getRenderedItem,
3703
4153
  horizontal,
3704
4154
  initialContentOffset,
3705
4155
  ListEmptyComponent: dataProp.length === 0 ? ListEmptyComponent : void 0,
3706
4156
  ListHeaderComponent,
3707
- maintainVisibleContentPosition,
3708
4157
  onLayout,
3709
4158
  onLayoutHeader,
3710
- onMomentumScrollEnd: (event) => {
3711
- {
3712
- requestAnimationFrame(() => {
3713
- finishScrollTo(ctx, refState.current);
3714
- });
3715
- }
3716
- if (onMomentumScrollEnd) {
3717
- onMomentumScrollEnd(event);
3718
- }
3719
- },
4159
+ onMomentumScrollEnd: fns.onMomentumScrollEnd,
3720
4160
  onScroll: onScrollHandler,
3721
4161
  recycleItems,
3722
4162
  refreshControl: refreshControl ? stylePaddingTopState > 0 ? React3.cloneElement(refreshControl, {
@@ -3730,9 +4170,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3730
4170
  }
3731
4171
  ),
3732
4172
  refScrollView: combinedRef,
3733
- scrollAdjustHandler: (_b = refState.current) == null ? void 0 : _b.scrollAdjustHandler,
3734
- scrollEventThrottle: 16 ,
4173
+ scrollAdjustHandler: (_d = refState.current) == null ? void 0 : _d.scrollAdjustHandler,
4174
+ scrollEventThrottle: 0,
3735
4175
  snapToIndices,
4176
+ stickyHeaderConfig,
3736
4177
  stickyHeaderIndices,
3737
4178
  style,
3738
4179
  updateItemSize: fns.updateItemSize,
@@ -3741,4 +4182,4 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3741
4182
  ), IS_DEV && ENABLE_DEBUG_VIEW && /* @__PURE__ */ React3.createElement(DebugView, { state: refState.current }));
3742
4183
  });
3743
4184
 
3744
- export { LegendList, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };
4185
+ export { LegendList, typedForwardRef, typedMemo, useIsLastItem, useListScrollSize, useRecyclingEffect, useRecyclingState, useSyncLayout, useViewability, useViewabilityAmount };