@legendapp/list 3.0.0-beta.1 → 3.0.0-beta.11

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.
package/index.mjs CHANGED
@@ -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);
@@ -234,12 +277,12 @@ function findContainerId(ctx, key) {
234
277
  }
235
278
 
236
279
  // src/components/PositionView.tsx
237
- var PositionViewState = typedMemo(function PositionView({
280
+ var PositionViewState = typedMemo(function PositionViewState2({
238
281
  id,
239
282
  horizontal,
240
283
  style,
241
284
  refView,
242
- ...rest
285
+ ...props
243
286
  }) {
244
287
  const [position = POSITION_OUT_OF_VIEW] = useArr$([`containerPosition${id}`]);
245
288
  const base = {
@@ -247,7 +290,8 @@ var PositionViewState = typedMemo(function PositionView({
247
290
  };
248
291
  const composed = isArray(style) ? Object.assign({}, ...style) : style;
249
292
  const combinedStyle = horizontal ? { ...base, ...composed, left: position } : { ...base, ...composed, top: position };
250
- return /* @__PURE__ */ React3.createElement("div", { ref: refView, style: combinedStyle, ...rest });
293
+ const { animatedScrollY, stickyOffset, onLayout, ...webProps } = props;
294
+ return /* @__PURE__ */ React3.createElement("div", { ref: refView, ...webProps, style: combinedStyle });
251
295
  });
252
296
  var PositionViewSticky = typedMemo(function PositionViewSticky2({
253
297
  id,
@@ -292,63 +336,80 @@ var PositionViewSticky = typedMemo(function PositionViewSticky2({
292
336
  }, [composed, horizontal, position, index, stickyOffset, headerSize, activeStickyIndex]);
293
337
  return /* @__PURE__ */ React3.createElement("div", { ref: refView, style: viewStyle, ...rest }, children);
294
338
  });
295
- var PositionView2 = PositionViewState;
339
+ var PositionView = PositionViewState;
296
340
 
297
341
  // src/constants-platform.ts
298
342
  var IsNewArchitecture = true;
299
- var symbolFirst = Symbol();
300
343
  function useInit(cb) {
301
- const refValue = useRef(symbolFirst);
302
- if (refValue.current === symbolFirst) {
303
- refValue.current = cb();
304
- }
305
- return refValue.current;
344
+ useState(() => cb());
306
345
  }
307
346
 
308
347
  // src/state/ContextContainer.ts
309
348
  var ContextContainer = createContext(null);
349
+ function useContextContainer() {
350
+ return useContext(ContextContainer);
351
+ }
310
352
  function useViewability(callback, configId) {
311
353
  const ctx = useStateContext();
312
- const { containerId } = useContext(ContextContainer);
313
- const key = containerId + (configId != null ? configId : "");
354
+ const containerContext = useContextContainer();
314
355
  useInit(() => {
356
+ if (!containerContext) {
357
+ return;
358
+ }
359
+ const { containerId } = containerContext;
360
+ const key = containerId + (configId != null ? configId : "");
315
361
  const value = ctx.mapViewabilityValues.get(key);
316
362
  if (value) {
317
363
  callback(value);
318
364
  }
319
365
  });
320
- ctx.mapViewabilityCallbacks.set(key, callback);
321
- useEffect(
322
- () => () => {
366
+ useEffect(() => {
367
+ if (!containerContext) {
368
+ return;
369
+ }
370
+ const { containerId } = containerContext;
371
+ const key = containerId + (configId != null ? configId : "");
372
+ ctx.mapViewabilityCallbacks.set(key, callback);
373
+ return () => {
323
374
  ctx.mapViewabilityCallbacks.delete(key);
324
- },
325
- []
326
- );
375
+ };
376
+ }, [ctx, callback, configId, containerContext]);
327
377
  }
328
378
  function useViewabilityAmount(callback) {
329
379
  const ctx = useStateContext();
330
- const { containerId } = useContext(ContextContainer);
380
+ const containerContext = useContextContainer();
331
381
  useInit(() => {
382
+ if (!containerContext) {
383
+ return;
384
+ }
385
+ const { containerId } = containerContext;
332
386
  const value = ctx.mapViewabilityAmountValues.get(containerId);
333
387
  if (value) {
334
388
  callback(value);
335
389
  }
336
390
  });
337
- ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
338
- useEffect(
339
- () => () => {
391
+ useEffect(() => {
392
+ if (!containerContext) {
393
+ return;
394
+ }
395
+ const { containerId } = containerContext;
396
+ ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
397
+ return () => {
340
398
  ctx.mapViewabilityAmountCallbacks.delete(containerId);
341
- },
342
- []
343
- );
399
+ };
400
+ }, [ctx, callback, containerContext]);
344
401
  }
345
402
  function useRecyclingEffect(effect) {
346
- const { index, value } = useContext(ContextContainer);
403
+ const containerContext = useContextContainer();
347
404
  const prevValues = useRef({
348
405
  prevIndex: void 0,
349
406
  prevItem: void 0
350
407
  });
351
408
  useEffect(() => {
409
+ if (!containerContext) {
410
+ return;
411
+ }
412
+ const { index, value } = containerContext;
352
413
  let ret;
353
414
  if (prevValues.current.prevIndex !== void 0 && prevValues.current.prevItem !== void 0) {
354
415
  ret = effect({
@@ -363,48 +424,73 @@ function useRecyclingEffect(effect) {
363
424
  prevItem: value
364
425
  };
365
426
  return ret;
366
- }, [index, value, effect]);
427
+ }, [effect, containerContext]);
367
428
  }
368
429
  function useRecyclingState(valueOrFun) {
369
- const { index, value, itemKey, triggerLayout } = useContext(ContextContainer);
370
- const refState = useRef({
371
- itemKey: null,
372
- value: null
430
+ var _a3, _b;
431
+ const containerContext = useContextContainer();
432
+ const computeValue = (ctx) => {
433
+ if (isFunction(valueOrFun)) {
434
+ const initializer = valueOrFun;
435
+ return ctx ? initializer({
436
+ index: ctx.index,
437
+ item: ctx.value,
438
+ prevIndex: void 0,
439
+ prevItem: void 0
440
+ }) : initializer();
441
+ }
442
+ return valueOrFun;
443
+ };
444
+ const [stateValue, setStateValue] = useState(() => {
445
+ return computeValue(containerContext);
373
446
  });
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;
447
+ const prevItemKeyRef = useRef((_a3 = containerContext == null ? void 0 : containerContext.itemKey) != null ? _a3 : null);
448
+ const currentItemKey = (_b = containerContext == null ? void 0 : containerContext.itemKey) != null ? _b : null;
449
+ if (currentItemKey !== null && prevItemKeyRef.current !== currentItemKey) {
450
+ prevItemKeyRef.current = currentItemKey;
451
+ setStateValue(computeValue(containerContext));
384
452
  }
453
+ const triggerLayout = containerContext == null ? void 0 : containerContext.triggerLayout;
385
454
  const setState = useCallback(
386
455
  (newState) => {
387
- state.value = isFunction(newState) ? newState(state.value) : newState;
388
- setRenderNum((v) => v + 1);
456
+ if (!triggerLayout) {
457
+ return;
458
+ }
459
+ setStateValue((prevValue) => {
460
+ return isFunction(newState) ? newState(prevValue) : newState;
461
+ });
389
462
  triggerLayout();
390
463
  },
391
- [triggerLayout, state]
464
+ [triggerLayout]
392
465
  );
393
- return [state.value, setState];
466
+ return [stateValue, setState];
394
467
  }
395
468
  function useIsLastItem() {
396
- const { itemKey } = useContext(ContextContainer);
397
- const isLast = useSelector$("lastItemKeys", (lastItemKeys) => (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false);
469
+ const containerContext = useContextContainer();
470
+ const isLast = useSelector$("lastItemKeys", (lastItemKeys) => {
471
+ if (containerContext) {
472
+ const { itemKey } = containerContext;
473
+ if (!isNullOrUndefined(itemKey)) {
474
+ return (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false;
475
+ }
476
+ }
477
+ return false;
478
+ });
398
479
  return isLast;
399
480
  }
400
481
  function useListScrollSize() {
401
482
  const [scrollSize] = useArr$(["scrollSize"]);
402
483
  return scrollSize;
403
484
  }
485
+ var noop = () => {
486
+ };
404
487
  function useSyncLayout() {
405
- {
406
- const { triggerLayout: syncLayout } = useContext(ContextContainer);
488
+ const containerContext = useContextContainer();
489
+ if (containerContext) {
490
+ const { triggerLayout: syncLayout } = containerContext;
407
491
  return syncLayout;
492
+ } else {
493
+ return noop;
408
494
  }
409
495
  }
410
496
 
@@ -450,10 +536,9 @@ function createResizeObserver(element, callback) {
450
536
  }
451
537
  callbacks.add(callback);
452
538
  return () => {
453
- const callbacks2 = callbackMap.get(element);
454
- if (callbacks2) {
455
- callbacks2.delete(callback);
456
- if (callbacks2.size === 0) {
539
+ if (callbacks) {
540
+ callbacks.delete(callback);
541
+ if (callbacks.size === 0) {
457
542
  callbackMap.delete(element);
458
543
  observer.unobserve(element);
459
544
  }
@@ -488,10 +573,10 @@ function useOnLayoutSync({
488
573
  return createResizeObserver(element, (entry) => {
489
574
  var _a4;
490
575
  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);
576
+ const rectObserved = (_a4 = entry.contentRect) != null ? _a4 : target == null ? void 0 : target.getBoundingClientRect();
577
+ if (rectObserved.width !== prevRect.width || rectObserved.height !== prevRect.height) {
578
+ prevRect = rectObserved;
579
+ emit(toLayout(rectObserved), false);
495
580
  }
496
581
  });
497
582
  }, deps || []);
@@ -619,7 +704,7 @@ var Container = typedMemo(function Container2({
619
704
  },
620
705
  [itemKey, layoutRenderCount]
621
706
  );
622
- const PositionComponent = isSticky ? PositionViewSticky : PositionView2;
707
+ const PositionComponent = isSticky ? PositionViewSticky : PositionView;
623
708
  return /* @__PURE__ */ React3.createElement(
624
709
  PositionComponent,
625
710
  {
@@ -812,7 +897,8 @@ var Containers = typedMemo(function Containers2({
812
897
  return /* @__PURE__ */ React3.createElement(ContainersInner, { horizontal, numColumns, waitForInitialLayout }, containers);
813
898
  });
814
899
  function DevNumbers() {
815
- return IS_DEV && React3.memo(function DevNumbers2() {
900
+ return IS_DEV && // biome-ignore lint/nursery/noShadow: const function name shadowing is intentional
901
+ React3.memo(function DevNumbers2() {
816
902
  return Array.from({ length: 100 }).map((_, index) => /* @__PURE__ */ React3.createElement(
817
903
  "div",
818
904
  {
@@ -860,7 +946,6 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
860
946
  }, ref) {
861
947
  const scrollRef = useRef(null);
862
948
  const contentRef = useRef(null);
863
- const momentumTimeout = useRef(null);
864
949
  useImperativeHandle(ref, () => {
865
950
  const api = {
866
951
  getBoundingClientRect: () => {
@@ -926,16 +1011,6 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
926
1011
  }
927
1012
  };
928
1013
  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
1014
  },
940
1015
  [onScroll2, onMomentumScrollEnd]
941
1016
  );
@@ -997,7 +1072,8 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
997
1072
  minWidth: horizontal ? "100%" : void 0,
998
1073
  ...StyleSheet.flatten(contentContainerStyle)
999
1074
  };
1000
- return /* @__PURE__ */ React3.createElement("div", { ref: scrollRef, style: scrollViewStyle, ...props }, refreshControl, /* @__PURE__ */ React3.createElement("div", { ref: contentRef, style: contentStyle }, children));
1075
+ const { contentInset, scrollEventThrottle, ScrollComponent, ...webProps } = props;
1076
+ return /* @__PURE__ */ React3.createElement("div", { ref: scrollRef, ...webProps, style: scrollViewStyle }, refreshControl, /* @__PURE__ */ React3.createElement("div", { ref: contentRef, style: contentStyle }, children));
1001
1077
  });
1002
1078
  function Padding() {
1003
1079
  const [paddingTop] = useArr$(["alignItemsPaddingTop"]);
@@ -1038,7 +1114,7 @@ function ScrollAdjust() {
1038
1114
  const scrollAdjust = peek$(ctx, "scrollAdjust");
1039
1115
  const scrollAdjustUserOffset = peek$(ctx, "scrollAdjustUserOffset");
1040
1116
  const scrollOffset = (scrollAdjust || 0) + (scrollAdjustUserOffset || 0);
1041
- const scrollView = (_a3 = ctx.internalState) == null ? void 0 : _a3.refScroller.current;
1117
+ const scrollView = (_a3 = ctx.state) == null ? void 0 : _a3.refScroller.current;
1042
1118
  if (scrollView && scrollOffset !== lastScrollOffsetRef.current) {
1043
1119
  const scrollDelta = scrollOffset - lastScrollOffsetRef.current;
1044
1120
  if (scrollDelta !== 0) {
@@ -1046,26 +1122,23 @@ function ScrollAdjust() {
1046
1122
  const prevScroll = el.scrollTop;
1047
1123
  const nextScroll = prevScroll + scrollDelta;
1048
1124
  const totalSize = el.scrollHeight;
1049
- if (scrollDelta > 0 && !ctx.internalState.adjustingFromInitialMount && totalSize < nextScroll + el.clientHeight) {
1125
+ if (scrollDelta > 0 && !ctx.state.adjustingFromInitialMount && totalSize < nextScroll + el.clientHeight) {
1050
1126
  const child = el.firstElementChild;
1051
1127
  const prevPaddingBottom = child.style.paddingBottom;
1052
1128
  const pad = (nextScroll + el.clientHeight - totalSize) * 2;
1053
1129
  child.style.paddingBottom = `${pad}px`;
1054
1130
  void el.offsetHeight;
1055
1131
  scrollView.scrollBy(0, scrollDelta);
1056
- setTimeout(() => {
1132
+ requestAnimationFrame(() => {
1057
1133
  child.style.paddingBottom = prevPaddingBottom;
1058
- }, 100);
1134
+ });
1059
1135
  } else {
1060
1136
  scrollView.scrollBy(0, scrollDelta);
1061
1137
  }
1062
- if (IS_DEV) {
1063
- console.log("ScrollAdjust (web scrollBy)", scrollDelta, "total offset:", scrollOffset);
1064
- }
1065
1138
  }
1066
1139
  lastScrollOffsetRef.current = scrollOffset;
1067
1140
  }
1068
- }, []);
1141
+ }, [ctx]);
1069
1142
  useValueListener$("scrollAdjust", callback);
1070
1143
  useValueListener$("scrollAdjustUserOffset", callback);
1071
1144
  return null;
@@ -1123,13 +1196,6 @@ var ListComponent = typedMemo(function ListComponent2({
1123
1196
  () => React3.forwardRef((props, ref) => renderScrollComponent({ ...props, ref })),
1124
1197
  [renderScrollComponent]
1125
1198
  ) : ListComponentScrollView;
1126
- React3.useEffect(() => {
1127
- if (canRender) {
1128
- setTimeout(() => {
1129
- scrollAdjustHandler.setMounted();
1130
- }, 0);
1131
- }
1132
- }, [canRender]);
1133
1199
  const SnapOrScroll = snapToIndices ? SnapWrapper : ScrollComponent;
1134
1200
  return /* @__PURE__ */ React3.createElement(
1135
1201
  SnapOrScroll,
@@ -1193,10 +1259,11 @@ function getId(state, index) {
1193
1259
  }
1194
1260
 
1195
1261
  // src/core/calculateOffsetForIndex.ts
1196
- function calculateOffsetForIndex(ctx, state, index) {
1262
+ function calculateOffsetForIndex(ctx, index) {
1263
+ const state = ctx.state;
1197
1264
  let position = 0;
1198
1265
  if (index !== void 0) {
1199
- position = (state == null ? void 0 : state.positions.get(getId(state, index))) || 0;
1266
+ position = state.positions.get(getId(state, index)) || 0;
1200
1267
  const paddingTop = peek$(ctx, "stylePaddingTop");
1201
1268
  if (paddingTop) {
1202
1269
  position += paddingTop;
@@ -1210,7 +1277,8 @@ function calculateOffsetForIndex(ctx, state, index) {
1210
1277
  }
1211
1278
 
1212
1279
  // src/utils/setPaddingTop.ts
1213
- function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
1280
+ function setPaddingTop(ctx, { stylePaddingTop, alignItemsPaddingTop }) {
1281
+ const state = ctx.state;
1214
1282
  if (stylePaddingTop !== void 0) {
1215
1283
  const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
1216
1284
  if (stylePaddingTop < prevStylePaddingTop) {
@@ -1229,7 +1297,8 @@ function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
1229
1297
  }
1230
1298
 
1231
1299
  // src/utils/updateAlignItemsPaddingTop.ts
1232
- function updateAlignItemsPaddingTop(ctx, state) {
1300
+ function updateAlignItemsPaddingTop(ctx) {
1301
+ const state = ctx.state;
1233
1302
  const {
1234
1303
  scrollLength,
1235
1304
  props: { alignItemsAtEnd, data }
@@ -1240,12 +1309,13 @@ function updateAlignItemsPaddingTop(ctx, state) {
1240
1309
  const contentSize = getContentSize(ctx);
1241
1310
  alignItemsPaddingTop = Math.max(0, Math.floor(scrollLength - contentSize));
1242
1311
  }
1243
- setPaddingTop(ctx, state, { alignItemsPaddingTop });
1312
+ setPaddingTop(ctx, { alignItemsPaddingTop });
1244
1313
  }
1245
1314
  }
1246
1315
 
1247
1316
  // src/core/addTotalSize.ts
1248
- function addTotalSize(ctx, state, key, add) {
1317
+ function addTotalSize(ctx, key, add) {
1318
+ const state = ctx.state;
1249
1319
  const { alignItemsAtEnd } = state.props;
1250
1320
  const prevTotalSize = state.totalSize;
1251
1321
  let totalSize = state.totalSize;
@@ -1264,31 +1334,34 @@ function addTotalSize(ctx, state, key, add) {
1264
1334
  state.totalSize = totalSize;
1265
1335
  set$(ctx, "totalSize", totalSize);
1266
1336
  if (alignItemsAtEnd) {
1267
- updateAlignItemsPaddingTop(ctx, state);
1337
+ updateAlignItemsPaddingTop(ctx);
1268
1338
  }
1269
1339
  }
1270
1340
  }
1271
1341
  }
1272
1342
 
1273
1343
  // src/core/setSize.ts
1274
- function setSize(ctx, state, itemKey, size) {
1344
+ function setSize(ctx, itemKey, size) {
1345
+ const state = ctx.state;
1275
1346
  const { sizes } = state;
1276
1347
  const previousSize = sizes.get(itemKey);
1277
1348
  const diff = previousSize !== void 0 ? size - previousSize : size;
1278
1349
  if (diff !== 0) {
1279
- addTotalSize(ctx, state, itemKey, diff);
1350
+ addTotalSize(ctx, itemKey, diff);
1280
1351
  }
1281
1352
  sizes.set(itemKey, size);
1282
1353
  }
1283
1354
 
1284
1355
  // src/utils/getItemSize.ts
1285
- function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedSize) {
1356
+ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
1286
1357
  var _a3, _b;
1358
+ const state = ctx.state;
1287
1359
  const {
1288
1360
  sizesKnown,
1289
1361
  sizes,
1290
1362
  averageSizes,
1291
- props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType }
1363
+ props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType },
1364
+ scrollingTo
1292
1365
  } = state;
1293
1366
  const sizeKnown = sizesKnown.get(key);
1294
1367
  if (sizeKnown !== void 0) {
@@ -1296,7 +1369,6 @@ function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedS
1296
1369
  }
1297
1370
  let size;
1298
1371
  const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1299
- const scrollingTo = peek$(ctx, "scrollingTo");
1300
1372
  if (preferCachedSize) {
1301
1373
  const cachedSize = sizes.get(key);
1302
1374
  if (cachedSize !== void 0) {
@@ -1324,81 +1396,169 @@ function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedS
1324
1396
  if (size === void 0) {
1325
1397
  size = getEstimatedItemSize ? getEstimatedItemSize(index, data, itemType) : estimatedItemSize;
1326
1398
  }
1327
- setSize(ctx, state, key, size);
1399
+ setSize(ctx, key, size);
1328
1400
  return size;
1329
1401
  }
1330
1402
 
1331
1403
  // src/core/calculateOffsetWithOffsetPosition.ts
1332
- function calculateOffsetWithOffsetPosition(ctx, state, offsetParam, params) {
1404
+ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1405
+ const state = ctx.state;
1333
1406
  const { index, viewOffset, viewPosition } = params;
1334
1407
  let offset = offsetParam;
1335
1408
  if (viewOffset) {
1336
1409
  offset -= viewOffset;
1337
1410
  }
1338
1411
  if (viewPosition !== void 0 && index !== void 0) {
1339
- offset -= viewPosition * (state.scrollLength - getItemSize(ctx, state, getId(state, index), index, state.props.data[index]));
1412
+ const itemSize = getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1413
+ const trailingInset = getContentInsetEnd(state);
1414
+ offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1340
1415
  }
1341
1416
  return offset;
1342
1417
  }
1343
1418
 
1419
+ // src/core/clampScrollOffset.ts
1420
+ function clampScrollOffset(ctx, offset) {
1421
+ const state = ctx.state;
1422
+ const contentSize = getContentSize(ctx);
1423
+ let clampedOffset = offset;
1424
+ if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength)) {
1425
+ const maxOffset = Math.max(0, contentSize - state.scrollLength);
1426
+ clampedOffset = Math.min(offset, maxOffset);
1427
+ }
1428
+ clampedOffset = Math.max(0, clampedOffset);
1429
+ return clampedOffset;
1430
+ }
1431
+
1432
+ // src/utils/setInitialRenderState.ts
1433
+ function setInitialRenderState(ctx, {
1434
+ didLayout,
1435
+ didInitialScroll
1436
+ }) {
1437
+ const { state } = ctx;
1438
+ if (didLayout) {
1439
+ state.didContainersLayout = true;
1440
+ }
1441
+ if (didInitialScroll) {
1442
+ state.didFinishInitialScroll = true;
1443
+ }
1444
+ if (state.didContainersLayout && state.didFinishInitialScroll) {
1445
+ set$(ctx, "readyToRender", true);
1446
+ }
1447
+ }
1448
+
1344
1449
  // src/core/finishScrollTo.ts
1345
- function finishScrollTo(ctx, state) {
1450
+ function finishScrollTo(ctx) {
1346
1451
  var _a3, _b;
1347
- if (state) {
1452
+ const state = ctx.state;
1453
+ if (state == null ? void 0 : state.scrollingTo) {
1348
1454
  state.scrollHistory.length = 0;
1349
1455
  state.initialScroll = void 0;
1350
1456
  state.initialAnchor = void 0;
1351
- set$(ctx, "scrollingTo", void 0);
1457
+ state.scrollingTo = void 0;
1352
1458
  if (state.pendingTotalSize !== void 0) {
1353
- addTotalSize(ctx, state, null, state.pendingTotalSize);
1459
+ addTotalSize(ctx, null, state.pendingTotalSize);
1354
1460
  }
1355
1461
  if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1356
1462
  (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1357
1463
  }
1464
+ {
1465
+ state.scrollAdjustHandler.commitPendingAdjust();
1466
+ }
1467
+ setInitialRenderState(ctx, { didInitialScroll: true });
1468
+ }
1469
+ }
1470
+
1471
+ // src/core/doScrollTo.ts
1472
+ var SCROLL_END_IDLE_MS = 80;
1473
+ var SCROLL_END_MAX_MS = 1500;
1474
+ var SMOOTH_SCROLL_DURATION_MS = 320;
1475
+ function doScrollTo(ctx, params) {
1476
+ const state = ctx.state;
1477
+ const { animated, horizontal, offset } = params;
1478
+ const scroller = state.refScroller.current;
1479
+ const node = typeof (scroller == null ? void 0 : scroller.getScrollableNode) === "function" ? scroller.getScrollableNode() : scroller;
1480
+ if (node) {
1481
+ const left = horizontal ? offset : 0;
1482
+ const top = horizontal ? 0 : offset;
1483
+ node.scrollTo({ behavior: animated ? "smooth" : "auto", left, top });
1484
+ if (animated) {
1485
+ listenForScrollEnd(ctx, node);
1486
+ } else {
1487
+ state.scroll = offset;
1488
+ setTimeout(() => {
1489
+ finishScrollTo(ctx);
1490
+ }, 100);
1491
+ }
1492
+ }
1493
+ }
1494
+ function listenForScrollEnd(ctx, node) {
1495
+ const supportsScrollEnd = "onscrollend" in node;
1496
+ let idleTimeout;
1497
+ let maxTimeout;
1498
+ let settled = false;
1499
+ const targetToken = ctx.state.scrollingTo;
1500
+ const finish = () => {
1501
+ if (settled) return;
1502
+ settled = true;
1503
+ cleanup();
1504
+ if (targetToken === ctx.state.scrollingTo) {
1505
+ finishScrollTo(ctx);
1506
+ }
1507
+ };
1508
+ const onScroll2 = () => {
1509
+ if (idleTimeout) {
1510
+ clearTimeout(idleTimeout);
1511
+ }
1512
+ idleTimeout = setTimeout(finish, SCROLL_END_IDLE_MS);
1513
+ };
1514
+ const cleanup = () => {
1515
+ if (supportsScrollEnd) {
1516
+ node.removeEventListener("scrollend", finish);
1517
+ } else {
1518
+ node.removeEventListener("scroll", onScroll2);
1519
+ }
1520
+ if (idleTimeout) {
1521
+ clearTimeout(idleTimeout);
1522
+ }
1523
+ if (maxTimeout) {
1524
+ clearTimeout(maxTimeout);
1525
+ }
1526
+ };
1527
+ if (supportsScrollEnd) {
1528
+ node.addEventListener("scrollend", finish, { once: true });
1529
+ } else {
1530
+ node.addEventListener("scroll", onScroll2);
1531
+ idleTimeout = setTimeout(finish, SMOOTH_SCROLL_DURATION_MS);
1532
+ maxTimeout = setTimeout(finish, SCROLL_END_MAX_MS);
1358
1533
  }
1534
+ return cleanup;
1359
1535
  }
1360
1536
 
1361
1537
  // src/core/scrollTo.ts
1362
- function scrollTo(ctx, state, params) {
1363
- var _a3;
1364
- const { noScrollingTo, ...scrollTarget } = params;
1538
+ function scrollTo(ctx, params) {
1539
+ const state = ctx.state;
1540
+ const { noScrollingTo, forceScroll, ...scrollTarget } = params;
1365
1541
  const { animated, isInitialScroll, offset: scrollTargetOffset, precomputedWithViewOffset } = scrollTarget;
1366
1542
  const {
1367
- refScroller,
1368
1543
  props: { horizontal }
1369
1544
  } = 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);
1545
+ if (state.animFrameCheckFinishedScroll) {
1546
+ cancelAnimationFrame(ctx.state.animFrameCheckFinishedScroll);
1547
+ }
1548
+ if (state.timeoutCheckFinishedScrollFallback) {
1549
+ clearTimeout(ctx.state.timeoutCheckFinishedScrollFallback);
1374
1550
  }
1551
+ let offset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, scrollTargetOffset, scrollTarget);
1552
+ offset = clampScrollOffset(ctx, offset);
1375
1553
  state.scrollHistory.length = 0;
1376
1554
  if (!noScrollingTo) {
1377
- set$(ctx, "scrollingTo", scrollTarget);
1555
+ state.scrollingTo = scrollTarget;
1378
1556
  }
1379
1557
  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) {
1558
+ if (forceScroll || !isInitialScroll || Platform.OS === "android") {
1559
+ doScrollTo(ctx, { animated, horizontal, isInitialScroll, offset });
1560
+ } else {
1388
1561
  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
1562
  }
1403
1563
  }
1404
1564
 
@@ -1407,6 +1567,12 @@ var HYSTERESIS_MULTIPLIER = 1.3;
1407
1567
  var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot) => {
1408
1568
  const absDistance = Math.abs(distance);
1409
1569
  const within = atThreshold || threshold > 0 && absDistance <= threshold;
1570
+ if (wasReached === null) {
1571
+ if (!within && distance >= 0) {
1572
+ return false;
1573
+ }
1574
+ return null;
1575
+ }
1410
1576
  const updateSnapshot = () => {
1411
1577
  setSnapshot == null ? void 0 : setSnapshot({
1412
1578
  atThreshold,
@@ -1439,8 +1605,9 @@ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, co
1439
1605
  };
1440
1606
 
1441
1607
  // src/utils/checkAtBottom.ts
1442
- function checkAtBottom(ctx, state) {
1608
+ function checkAtBottom(ctx) {
1443
1609
  var _a3;
1610
+ const state = ctx.state;
1444
1611
  if (!state) {
1445
1612
  return;
1446
1613
  }
@@ -1513,15 +1680,15 @@ function checkAtTop(state) {
1513
1680
  }
1514
1681
 
1515
1682
  // src/core/updateScroll.ts
1516
- function updateScroll(ctx, state, newScroll, forceUpdate) {
1683
+ function updateScroll(ctx, newScroll, forceUpdate) {
1517
1684
  var _a3;
1518
- const scrollingTo = peek$(ctx, "scrollingTo");
1685
+ const state = ctx.state;
1686
+ const { scrollingTo, scrollAdjustHandler, lastScrollAdjustForHistory } = state;
1519
1687
  state.hasScrolled = true;
1520
1688
  state.lastBatchingAction = Date.now();
1521
1689
  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;
1690
+ const adjust = scrollAdjustHandler.getAdjust();
1691
+ const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
1525
1692
  if (adjustChanged) {
1526
1693
  state.scrollHistory.length = 0;
1527
1694
  }
@@ -1546,17 +1713,21 @@ function updateScroll(ctx, state, newScroll, forceUpdate) {
1546
1713
  return;
1547
1714
  }
1548
1715
  }
1549
- if (state.dataChangeNeedsScrollUpdate || Math.abs(state.scroll - state.scrollPrev) > 2) {
1716
+ const lastCalculated = state.scrollLastCalculate;
1717
+ const shouldUpdate = state.dataChangeNeedsScrollUpdate || state.scrollLastCalculate === void 0 || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
1718
+ if (shouldUpdate) {
1719
+ state.scrollLastCalculate = state.scroll;
1550
1720
  state.ignoreScrollFromMVCPIgnored = false;
1551
1721
  (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { doMVCP: scrollingTo !== void 0 });
1552
- checkAtBottom(ctx, state);
1722
+ checkAtBottom(ctx);
1553
1723
  checkAtTop(state);
1554
1724
  state.dataChangeNeedsScrollUpdate = false;
1555
1725
  }
1556
1726
  }
1557
1727
 
1558
1728
  // src/utils/requestAdjust.ts
1559
- function requestAdjust(ctx, state, positionDiff, dataChanged) {
1729
+ function requestAdjust(ctx, positionDiff, dataChanged) {
1730
+ const state = ctx.state;
1560
1731
  if (Math.abs(positionDiff) > 0.1) {
1561
1732
  const doit = () => {
1562
1733
  {
@@ -1568,8 +1739,8 @@ function requestAdjust(ctx, state, positionDiff, dataChanged) {
1568
1739
  };
1569
1740
  state.scroll += positionDiff;
1570
1741
  state.scrollForNextCalculateItemsInView = void 0;
1571
- const didLayout = peek$(ctx, "containersDidLayout");
1572
- if (didLayout) {
1742
+ const readyToRender = peek$(ctx, "readyToRender");
1743
+ if (readyToRender) {
1573
1744
  doit();
1574
1745
  } else {
1575
1746
  state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
@@ -1578,73 +1749,23 @@ function requestAdjust(ctx, state, positionDiff, dataChanged) {
1578
1749
  }
1579
1750
  }
1580
1751
 
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
1752
  // src/core/mvcp.ts
1634
- function prepareMVCP(ctx, state, dataChanged) {
1753
+ function prepareMVCP(ctx, dataChanged) {
1754
+ const state = ctx.state;
1635
1755
  const { idsInView, positions, props } = state;
1636
1756
  const { maintainVisibleContentPosition } = props;
1637
- const scrollingTo = peek$(ctx, "scrollingTo");
1757
+ const scrollingTo = state.scrollingTo;
1638
1758
  let prevPosition;
1639
1759
  let targetId;
1640
1760
  const idsInViewWithPositions = [];
1641
1761
  const scrollTarget = scrollingTo == null ? void 0 : scrollingTo.index;
1762
+ const scrollingToViewPosition = scrollingTo == null ? void 0 : scrollingTo.viewPosition;
1642
1763
  const shouldMVCP = !dataChanged || maintainVisibleContentPosition;
1643
1764
  const indexByKey = state.indexByKey;
1644
1765
  if (shouldMVCP) {
1645
1766
  if (scrollTarget !== void 0) {
1646
1767
  targetId = getId(state, scrollTarget);
1647
- } else if (idsInView.length > 0 && peek$(ctx, "containersDidLayout")) {
1768
+ } else if (idsInView.length > 0 && state.didContainersLayout) {
1648
1769
  if (dataChanged) {
1649
1770
  for (let i = 0; i < idsInView.length; i++) {
1650
1771
  const id = idsInView[i];
@@ -1661,7 +1782,7 @@ function prepareMVCP(ctx, state, dataChanged) {
1661
1782
  prevPosition = positions.get(targetId);
1662
1783
  }
1663
1784
  return () => {
1664
- let positionDiff;
1785
+ let positionDiff = 0;
1665
1786
  if (dataChanged && targetId === void 0 && maintainVisibleContentPosition) {
1666
1787
  for (let i = 0; i < idsInViewWithPositions.length; i++) {
1667
1788
  const { id, position } = idsInViewWithPositions[i];
@@ -1687,16 +1808,28 @@ function prepareMVCP(ctx, state, dataChanged) {
1687
1808
  positionDiff = diff;
1688
1809
  }
1689
1810
  }
1690
- if (positionDiff !== void 0 && Math.abs(positionDiff) > 0.1) {
1691
- requestAdjust(ctx, state, positionDiff);
1811
+ if (scrollingToViewPosition && scrollingToViewPosition > 0) {
1812
+ const newSize = getItemSize(ctx, targetId, scrollTarget, state.props.data[scrollTarget]);
1813
+ const prevSize = scrollingTo == null ? void 0 : scrollingTo.itemSize;
1814
+ if (newSize !== void 0 && prevSize !== void 0 && newSize !== (scrollingTo == null ? void 0 : scrollingTo.itemSize)) {
1815
+ const diff = newSize - prevSize;
1816
+ if (diff !== 0) {
1817
+ positionDiff += (newSize - prevSize) * scrollingToViewPosition;
1818
+ scrollingTo.itemSize = newSize;
1819
+ }
1820
+ }
1821
+ }
1822
+ if (Math.abs(positionDiff) > 0.1) {
1823
+ requestAdjust(ctx, positionDiff);
1692
1824
  }
1693
1825
  };
1694
1826
  }
1695
1827
  }
1696
1828
 
1697
1829
  // src/core/prepareColumnStartState.ts
1698
- function prepareColumnStartState(ctx, state, startIndex, useAverageSize) {
1830
+ function prepareColumnStartState(ctx, startIndex, useAverageSize) {
1699
1831
  var _a3;
1832
+ const state = ctx.state;
1700
1833
  const numColumns = peek$(ctx, "numColumns");
1701
1834
  let rowStartIndex = startIndex;
1702
1835
  const columnAtStart = state.columns.get(state.idCache[startIndex]);
@@ -1711,7 +1844,7 @@ function prepareColumnStartState(ctx, state, startIndex, useAverageSize) {
1711
1844
  const prevId = state.idCache[prevIndex];
1712
1845
  const prevPosition = (_a3 = state.positions.get(prevId)) != null ? _a3 : 0;
1713
1846
  const prevRowStart = findRowStartIndex(state, numColumns, prevIndex);
1714
- const prevRowHeight = calculateRowMaxSize(ctx, state, prevRowStart, prevIndex, useAverageSize);
1847
+ const prevRowHeight = calculateRowMaxSize(ctx, prevRowStart, prevIndex, useAverageSize);
1715
1848
  currentRowTop = prevPosition + prevRowHeight;
1716
1849
  }
1717
1850
  return {
@@ -1734,7 +1867,8 @@ function findRowStartIndex(state, numColumns, index) {
1734
1867
  }
1735
1868
  return rowStart;
1736
1869
  }
1737
- function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1870
+ function calculateRowMaxSize(ctx, startIndex, endIndex, useAverageSize) {
1871
+ const state = ctx.state;
1738
1872
  if (endIndex < startIndex) {
1739
1873
  return 0;
1740
1874
  }
@@ -1748,7 +1882,7 @@ function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1748
1882
  continue;
1749
1883
  }
1750
1884
  const id = state.idCache[i];
1751
- const size = getItemSize(ctx, state, id, i, data[i], useAverageSize);
1885
+ const size = getItemSize(ctx, id, i, data[i], useAverageSize);
1752
1886
  if (size > maxSize) {
1753
1887
  maxSize = size;
1754
1888
  }
@@ -1757,22 +1891,23 @@ function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1757
1891
  }
1758
1892
 
1759
1893
  // src/core/updateTotalSize.ts
1760
- function updateTotalSize(ctx, state) {
1894
+ function updateTotalSize(ctx) {
1895
+ const state = ctx.state;
1761
1896
  const {
1762
1897
  positions,
1763
1898
  props: { data }
1764
1899
  } = state;
1765
1900
  if (data.length === 0) {
1766
- addTotalSize(ctx, state, null, 0);
1901
+ addTotalSize(ctx, null, 0);
1767
1902
  } else {
1768
1903
  const lastId = getId(state, data.length - 1);
1769
1904
  if (lastId !== void 0) {
1770
1905
  const lastPosition = positions.get(lastId);
1771
1906
  if (lastPosition !== void 0) {
1772
- const lastSize = getItemSize(ctx, state, lastId, data.length - 1, data[data.length - 1]);
1907
+ const lastSize = getItemSize(ctx, lastId, data.length - 1, data[data.length - 1]);
1773
1908
  if (lastSize !== void 0) {
1774
1909
  const totalSize = lastPosition + lastSize;
1775
- addTotalSize(ctx, state, null, totalSize);
1910
+ addTotalSize(ctx, null, totalSize);
1776
1911
  }
1777
1912
  }
1778
1913
  }
@@ -1818,7 +1953,8 @@ var getScrollVelocity = (state) => {
1818
1953
  };
1819
1954
 
1820
1955
  // src/utils/updateSnapToOffsets.ts
1821
- function updateSnapToOffsets(ctx, state) {
1956
+ function updateSnapToOffsets(ctx) {
1957
+ const state = ctx.state;
1822
1958
  const {
1823
1959
  positions,
1824
1960
  props: { snapToIndices }
@@ -1833,30 +1969,30 @@ function updateSnapToOffsets(ctx, state) {
1833
1969
  }
1834
1970
 
1835
1971
  // src/core/updateItemPositions.ts
1836
- function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP } = {
1972
+ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP } = {
1837
1973
  doMVCP: false,
1838
1974
  forceFullUpdate: false,
1839
1975
  scrollBottomBuffered: -1,
1840
1976
  startIndex: 0
1841
1977
  }) {
1842
1978
  var _a3, _b, _c, _d, _e;
1979
+ const state = ctx.state;
1843
1980
  const {
1844
1981
  columns,
1845
1982
  indexByKey,
1846
1983
  positions,
1847
1984
  idCache,
1848
1985
  sizesKnown,
1849
- props: { getEstimatedItemSize, snapToIndices, enableAverages }
1986
+ props: { data, getEstimatedItemSize, snapToIndices },
1987
+ scrollingTo
1850
1988
  } = state;
1851
- const data = state.props.data;
1852
1989
  const dataLength = data.length;
1853
1990
  const numColumns = peek$(ctx, "numColumns");
1854
- const scrollingTo = peek$(ctx, "scrollingTo");
1855
1991
  const hasColumns = numColumns > 1;
1856
1992
  const indexByKeyForChecking = IS_DEV ? /* @__PURE__ */ new Map() : void 0;
1857
1993
  const shouldOptimize = !forceFullUpdate && !dataChanged && Math.abs(getScrollVelocity(state)) > 0;
1858
1994
  const maxVisibleArea = scrollBottomBuffered + 1e3;
1859
- const useAverageSize = enableAverages && !getEstimatedItemSize;
1995
+ const useAverageSize = !getEstimatedItemSize;
1860
1996
  const preferCachedSize = !doMVCP || dataChanged || state.scrollAdjustHandler.getAdjust() !== 0 || ((_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0) !== 0;
1861
1997
  let currentRowTop = 0;
1862
1998
  let column = 1;
@@ -1865,7 +2001,6 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1865
2001
  if (hasColumns) {
1866
2002
  const { startIndex: processedStartIndex, currentRowTop: initialRowTop } = prepareColumnStartState(
1867
2003
  ctx,
1868
- state,
1869
2004
  startIndex,
1870
2005
  useAverageSize
1871
2006
  );
@@ -1875,7 +2010,7 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1875
2010
  const prevIndex = startIndex - 1;
1876
2011
  const prevId = getId(state, prevIndex);
1877
2012
  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);
2013
+ const prevSize = (_c = sizesKnown.get(prevId)) != null ? _c : getItemSize(ctx, prevId, prevIndex, data[prevIndex], useAverageSize, preferCachedSize);
1879
2014
  currentRowTop = prevPosition + prevSize;
1880
2015
  }
1881
2016
  }
@@ -1892,7 +2027,7 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1892
2027
  breakAt = i + itemsPerRow + 10;
1893
2028
  }
1894
2029
  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);
2030
+ const size = (_e = sizesKnown.get(id)) != null ? _e : getItemSize(ctx, id, i, data[i], useAverageSize, preferCachedSize);
1896
2031
  if (IS_DEV && needsIndexByKey) {
1897
2032
  if (indexByKeyForChecking.has(id)) {
1898
2033
  console.error(
@@ -1901,7 +2036,10 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1901
2036
  }
1902
2037
  indexByKeyForChecking.set(id, i);
1903
2038
  }
1904
- positions.set(id, currentRowTop);
2039
+ if (currentRowTop !== positions.get(id)) {
2040
+ positions.set(id, currentRowTop);
2041
+ notifyPosition$(ctx, id, currentRowTop);
2042
+ }
1905
2043
  if (needsIndexByKey) {
1906
2044
  indexByKey.set(id, i);
1907
2045
  }
@@ -1921,10 +2059,10 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1921
2059
  }
1922
2060
  }
1923
2061
  if (!didBreakEarly) {
1924
- updateTotalSize(ctx, state);
2062
+ updateTotalSize(ctx);
1925
2063
  }
1926
2064
  if (snapToIndices) {
1927
- updateSnapToOffsets(ctx, state);
2065
+ updateSnapToOffsets(ctx);
1928
2066
  }
1929
2067
  }
1930
2068
 
@@ -2002,7 +2140,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
2002
2140
  if (previousViewableItems) {
2003
2141
  for (const viewToken of previousViewableItems) {
2004
2142
  const containerId = findContainerId(ctx, viewToken.key);
2005
- if (!isViewable(
2143
+ if (!checkIsViewable(
2006
2144
  state,
2007
2145
  ctx,
2008
2146
  viewabilityConfig,
@@ -2023,7 +2161,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
2023
2161
  if (item) {
2024
2162
  const key = getId(state, i);
2025
2163
  const containerId = findContainerId(ctx, key);
2026
- if (isViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, i)) {
2164
+ if (checkIsViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, i)) {
2027
2165
  const viewToken = {
2028
2166
  containerId,
2029
2167
  index: i,
@@ -2083,11 +2221,11 @@ function computeViewability(state, ctx, viewabilityConfig, containerId, key, scr
2083
2221
  const percentVisible = size ? isEntirelyVisible ? 100 : 100 * (sizeVisible / size) : 0;
2084
2222
  const percentOfScroller = size ? 100 * (sizeVisible / scrollSize) : 0;
2085
2223
  const percent = isEntirelyVisible ? 100 : viewAreaMode ? percentOfScroller : percentVisible;
2086
- const isViewable2 = percent >= viewablePercentThreshold;
2224
+ const isViewable = percent >= viewablePercentThreshold;
2087
2225
  const value = {
2088
2226
  containerId,
2089
2227
  index,
2090
- isViewable: isViewable2,
2228
+ isViewable,
2091
2229
  item,
2092
2230
  key,
2093
2231
  percentOfScroller,
@@ -2106,8 +2244,11 @@ function computeViewability(state, ctx, viewabilityConfig, containerId, key, scr
2106
2244
  }
2107
2245
  return value;
2108
2246
  }
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);
2247
+ function checkIsViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
2248
+ let value = ctx.mapViewabilityAmountValues.get(containerId);
2249
+ if (!value || value.key !== key) {
2250
+ value = computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index);
2251
+ }
2111
2252
  return value.isViewable;
2112
2253
  }
2113
2254
  function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
@@ -2135,8 +2276,9 @@ function checkAllSizesKnown(state) {
2135
2276
  }
2136
2277
 
2137
2278
  // src/utils/findAvailableContainers.ts
2138
- function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers) {
2279
+ function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers) {
2139
2280
  const numContainers = peek$(ctx, "numContainers");
2281
+ const state = ctx.state;
2140
2282
  const { stickyContainerPool, containerItemTypes } = state;
2141
2283
  const result = [];
2142
2284
  const availableContainers = [];
@@ -2180,14 +2322,14 @@ function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffer
2180
2322
  continue;
2181
2323
  }
2182
2324
  const key = peek$(ctx, `containerItemKey${u}`);
2183
- let isOk = key === void 0;
2184
- if (!isOk && pendingRemovalSet.has(u)) {
2185
- pendingRemovalSet.delete(u);
2186
- pendingRemovalChanged = true;
2187
- const requiredType = neededTypes[typeIndex];
2188
- isOk = canReuseContainer(u, requiredType);
2189
- }
2190
- if (isOk) {
2325
+ const requiredType = neededTypes[typeIndex];
2326
+ const isPending = key !== void 0 && pendingRemovalSet.has(u);
2327
+ const canUse = key === void 0 || isPending && canReuseContainer(u, requiredType);
2328
+ if (canUse) {
2329
+ if (isPending) {
2330
+ pendingRemovalSet.delete(u);
2331
+ pendingRemovalChanged = true;
2332
+ }
2191
2333
  result.push(u);
2192
2334
  if (requiredItemTypes) {
2193
2335
  typeIndex++;
@@ -2256,21 +2398,26 @@ function comparatorByDistance(a, b) {
2256
2398
  }
2257
2399
 
2258
2400
  // 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;
2401
+ function scrollToIndex(ctx, { index, viewOffset = 0, animated = true, viewPosition }) {
2402
+ const state = ctx.state;
2403
+ const { data } = state.props;
2404
+ if (index >= data.length) {
2405
+ index = data.length - 1;
2262
2406
  } else if (index < 0) {
2263
2407
  index = 0;
2264
2408
  }
2265
- const firstIndexOffset = calculateOffsetForIndex(ctx, state, index);
2266
- const isLast = index === state.props.data.length - 1;
2409
+ const firstIndexOffset = calculateOffsetForIndex(ctx, index);
2410
+ const isLast = index === data.length - 1;
2267
2411
  if (isLast && viewPosition === void 0) {
2268
2412
  viewPosition = 1;
2269
2413
  }
2270
2414
  state.scrollForNextCalculateItemsInView = void 0;
2271
- scrollTo(ctx, state, {
2415
+ const targetId = getId(state, index);
2416
+ const itemSize = getItemSize(ctx, targetId, index, state.props.data[index]);
2417
+ scrollTo(ctx, {
2272
2418
  animated,
2273
2419
  index,
2420
+ itemSize,
2274
2421
  offset: firstIndexOffset,
2275
2422
  viewOffset,
2276
2423
  viewPosition: viewPosition != null ? viewPosition : 0
@@ -2278,16 +2425,17 @@ function scrollToIndex(ctx, state, { index, viewOffset = 0, animated = true, vie
2278
2425
  }
2279
2426
 
2280
2427
  // src/utils/setDidLayout.ts
2281
- function setDidLayout(ctx, state) {
2428
+ function setDidLayout(ctx) {
2429
+ const state = ctx.state;
2282
2430
  const {
2283
2431
  loadStartTime,
2284
2432
  initialScroll,
2285
2433
  props: { onLoad }
2286
2434
  } = state;
2287
2435
  state.queuedInitialLayout = true;
2288
- checkAtBottom(ctx, state);
2436
+ checkAtBottom(ctx);
2289
2437
  const setIt = () => {
2290
- set$(ctx, "containersDidLayout", true);
2438
+ setInitialRenderState(ctx, { didLayout: true });
2291
2439
  if (onLoad) {
2292
2440
  onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
2293
2441
  }
@@ -2312,15 +2460,17 @@ function findCurrentStickyIndex(stickyArray, scroll, state) {
2312
2460
  }
2313
2461
  return -1;
2314
2462
  }
2315
- function getActiveStickyIndices(ctx, state, stickyHeaderIndices) {
2463
+ function getActiveStickyIndices(ctx, stickyHeaderIndices) {
2464
+ const state = ctx.state;
2316
2465
  return new Set(
2317
2466
  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
2467
  );
2319
2468
  }
2320
- function handleStickyActivation(ctx, state, stickyHeaderIndices, stickyArray, currentStickyIdx, needNewContainers, startBuffered, endBuffered) {
2469
+ function handleStickyActivation(ctx, stickyHeaderIndices, stickyArray, currentStickyIdx, needNewContainers, startBuffered, endBuffered) {
2321
2470
  var _a3;
2322
- const activeIndices = getActiveStickyIndices(ctx, state, stickyHeaderIndices);
2323
- state.activeStickyIndex = currentStickyIdx >= 0 ? stickyArray[currentStickyIdx] : void 0;
2471
+ const state = ctx.state;
2472
+ const activeIndices = getActiveStickyIndices(ctx, stickyHeaderIndices);
2473
+ set$(ctx, "activeStickyIndex", currentStickyIdx >= 0 ? stickyArray[currentStickyIdx] : -1);
2324
2474
  for (let offset = 0; offset <= 1; offset++) {
2325
2475
  const idx = currentStickyIdx - offset;
2326
2476
  if (idx < 0 || activeIndices.has(stickyArray[idx])) continue;
@@ -2331,8 +2481,9 @@ function handleStickyActivation(ctx, state, stickyHeaderIndices, stickyArray, cu
2331
2481
  }
2332
2482
  }
2333
2483
  }
2334
- function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, currentStickyIdx, pendingRemoval) {
2484
+ function handleStickyRecycling(ctx, stickyArray, scroll, scrollBuffer, currentStickyIdx, pendingRemoval) {
2335
2485
  var _a3, _b, _c;
2486
+ const state = ctx.state;
2336
2487
  for (const containerIndex of state.stickyContainerPool) {
2337
2488
  const itemKey = peek$(ctx, `containerItemKey${containerIndex}`);
2338
2489
  const itemIndex = itemKey ? state.indexByKey.get(itemKey) : void 0;
@@ -2356,7 +2507,7 @@ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, cu
2356
2507
  const currentId = (_b = state.idCache[itemIndex]) != null ? _b : getId(state, itemIndex);
2357
2508
  if (currentId) {
2358
2509
  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]);
2510
+ const currentSize = (_c = state.sizes.get(currentId)) != null ? _c : getItemSize(ctx, currentId, itemIndex, state.props.data[itemIndex]);
2360
2511
  shouldRecycle = currentPos !== void 0 && scroll > currentPos + currentSize + scrollBuffer * 3;
2361
2512
  }
2362
2513
  }
@@ -2365,7 +2516,8 @@ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, cu
2365
2516
  }
2366
2517
  }
2367
2518
  }
2368
- function calculateItemsInView(ctx, state, params = {}) {
2519
+ function calculateItemsInView(ctx, params = {}) {
2520
+ const state = ctx.state;
2369
2521
  unstable_batchedUpdates(() => {
2370
2522
  var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j;
2371
2523
  const {
@@ -2389,9 +2541,6 @@ function calculateItemsInView(ctx, state, params = {}) {
2389
2541
  const stickyIndicesSet = state.props.stickyIndicesSet || /* @__PURE__ */ new Set();
2390
2542
  const prevNumContainers = peek$(ctx, "numContainers");
2391
2543
  if (!data || scrollLength === 0 || !prevNumContainers) {
2392
- if (state.initialAnchor) {
2393
- ensureInitialAnchor(ctx, state);
2394
- }
2395
2544
  return;
2396
2545
  }
2397
2546
  const totalSize = getContentSize(ctx);
@@ -2405,15 +2554,14 @@ function calculateItemsInView(ctx, state, params = {}) {
2405
2554
  if (!queuedInitialLayout && initialScroll) {
2406
2555
  const updatedOffset = calculateOffsetWithOffsetPosition(
2407
2556
  ctx,
2408
- state,
2409
- calculateOffsetForIndex(ctx, state, initialScroll.index),
2557
+ calculateOffsetForIndex(ctx, initialScroll.index),
2410
2558
  initialScroll
2411
2559
  );
2412
2560
  scrollState = updatedOffset;
2413
2561
  }
2414
2562
  const scrollAdjustPending = (_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0;
2415
2563
  const scrollAdjustPad = scrollAdjustPending - topPad;
2416
- let scroll = scrollState + scrollExtra + scrollAdjustPad;
2564
+ let scroll = Math.round(scrollState + scrollExtra + scrollAdjustPad);
2417
2565
  if (scroll + scrollLength > totalSize) {
2418
2566
  scroll = Math.max(0, totalSize - scrollLength);
2419
2567
  }
@@ -2421,11 +2569,12 @@ function calculateItemsInView(ctx, state, params = {}) {
2421
2569
  set$(ctx, "debugRawScroll", scrollState);
2422
2570
  set$(ctx, "debugComputedScroll", scroll);
2423
2571
  }
2424
- const previousStickyIndex = state.activeStickyIndex;
2572
+ const previousStickyIndex = peek$(ctx, "activeStickyIndex");
2425
2573
  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);
2574
+ const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyIndicesArr[currentStickyIdx] : -1;
2575
+ if (currentStickyIdx >= 0 || previousStickyIndex >= 0) {
2576
+ set$(ctx, "activeStickyIndex", nextActiveStickyIndex);
2577
+ }
2429
2578
  let scrollBufferTop = scrollBuffer;
2430
2579
  let scrollBufferBottom = scrollBuffer;
2431
2580
  if (speed > 0 || speed === 0 && scroll < Math.max(50, scrollBuffer)) {
@@ -2438,23 +2587,20 @@ function calculateItemsInView(ctx, state, params = {}) {
2438
2587
  const scrollTopBuffered = scroll - scrollBufferTop;
2439
2588
  const scrollBottom = scroll + scrollLength + (scroll < 0 ? -scroll : 0);
2440
2589
  const scrollBottomBuffered = scrollBottom + scrollBufferBottom;
2441
- if (!dataChanged && scrollForNextCalculateItemsInView) {
2590
+ if (!dataChanged && !forceFullItemPositions && scrollForNextCalculateItemsInView) {
2442
2591
  const { top, bottom } = scrollForNextCalculateItemsInView;
2443
- if (scrollTopBuffered > top && scrollBottomBuffered < bottom) {
2444
- if (state.initialAnchor) {
2445
- ensureInitialAnchor(ctx, state);
2446
- }
2592
+ if ((top === null || scrollTopBuffered > top) && (bottom === null || scrollBottomBuffered < bottom)) {
2447
2593
  return;
2448
2594
  }
2449
2595
  }
2450
- const checkMVCP = doMVCP ? prepareMVCP(ctx, state, dataChanged) : void 0;
2596
+ const checkMVCP = doMVCP ? prepareMVCP(ctx, dataChanged) : void 0;
2451
2597
  if (dataChanged) {
2452
2598
  indexByKey.clear();
2453
2599
  idCache.length = 0;
2454
2600
  positions.clear();
2455
2601
  }
2456
- const startIndex = dataChanged ? 0 : (_b = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _b : 0;
2457
- updateItemPositions(ctx, state, dataChanged, {
2602
+ const startIndex = forceFullItemPositions || dataChanged ? 0 : (_b = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _b : 0;
2603
+ updateItemPositions(ctx, dataChanged, {
2458
2604
  doMVCP,
2459
2605
  forceFullUpdate: !!forceFullItemPositions,
2460
2606
  scrollBottomBuffered,
@@ -2473,9 +2619,9 @@ function calculateItemsInView(ctx, state, params = {}) {
2473
2619
  for (let i = loopStart; i >= 0; i--) {
2474
2620
  const id = (_c = idCache[i]) != null ? _c : getId(state, i);
2475
2621
  const top = positions.get(id);
2476
- const size = (_d = sizes.get(id)) != null ? _d : getItemSize(ctx, state, id, i, data[i]);
2622
+ const size = (_d = sizes.get(id)) != null ? _d : getItemSize(ctx, id, i, data[i]);
2477
2623
  const bottom = top + size;
2478
- if (bottom > scroll - scrollBuffer) {
2624
+ if (bottom > scroll - scrollBufferTop) {
2479
2625
  loopStart = i;
2480
2626
  } else {
2481
2627
  break;
@@ -2500,7 +2646,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2500
2646
  const dataLength = data.length;
2501
2647
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
2502
2648
  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]);
2649
+ const size = (_f = sizes.get(id)) != null ? _f : getItemSize(ctx, id, i, data[i]);
2504
2650
  const top = positions.get(id);
2505
2651
  if (!foundEnd) {
2506
2652
  if (startNoBuffer === null && top + size > scroll) {
@@ -2512,7 +2658,11 @@ function calculateItemsInView(ctx, state, params = {}) {
2512
2658
  if (startBuffered === null && top + size > scrollTopBuffered) {
2513
2659
  startBuffered = i;
2514
2660
  startBufferedId = id;
2515
- nextTop = top;
2661
+ if (scrollTopBuffered < 0) {
2662
+ nextTop = null;
2663
+ } else {
2664
+ nextTop = top;
2665
+ }
2516
2666
  }
2517
2667
  if (startNoBuffer !== null) {
2518
2668
  if (top <= scrollBottom) {
@@ -2520,7 +2670,11 @@ function calculateItemsInView(ctx, state, params = {}) {
2520
2670
  }
2521
2671
  if (top <= scrollBottomBuffered) {
2522
2672
  endBuffered = i;
2523
- nextBottom = top + size;
2673
+ if (scrollBottomBuffered > totalSize) {
2674
+ nextBottom = null;
2675
+ } else {
2676
+ nextBottom = top + size;
2677
+ }
2524
2678
  } else {
2525
2679
  foundEnd = true;
2526
2680
  }
@@ -2547,7 +2701,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2547
2701
  top: nextTop
2548
2702
  } : void 0;
2549
2703
  }
2550
- const numContainers = peek$(ctx, "numContainers");
2704
+ let numContainers = prevNumContainers;
2551
2705
  const pendingRemoval = [];
2552
2706
  if (dataChanged) {
2553
2707
  for (let i = 0; i < numContainers; i++) {
@@ -2558,7 +2712,6 @@ function calculateItemsInView(ctx, state, params = {}) {
2558
2712
  }
2559
2713
  }
2560
2714
  if (startBuffered !== null && endBuffered !== null) {
2561
- let numContainers2 = prevNumContainers;
2562
2715
  const needNewContainers = [];
2563
2716
  for (let i = startBuffered; i <= endBuffered; i++) {
2564
2717
  const id = (_h = idCache[i]) != null ? _h : getId(state, i);
@@ -2569,7 +2722,6 @@ function calculateItemsInView(ctx, state, params = {}) {
2569
2722
  if (stickyIndicesArr.length > 0) {
2570
2723
  handleStickyActivation(
2571
2724
  ctx,
2572
- state,
2573
2725
  stickyIndicesSet,
2574
2726
  stickyIndicesArr,
2575
2727
  currentStickyIdx,
@@ -2577,9 +2729,8 @@ function calculateItemsInView(ctx, state, params = {}) {
2577
2729
  startBuffered,
2578
2730
  endBuffered
2579
2731
  );
2580
- } else {
2581
- state.activeStickyIndex = void 0;
2582
- set$(ctx, "activeStickyIndex", void 0);
2732
+ } else if (previousStickyIndex !== -1) {
2733
+ set$(ctx, "activeStickyIndex", -1);
2583
2734
  }
2584
2735
  if (needNewContainers.length > 0) {
2585
2736
  const requiredItemTypes = getItemType ? needNewContainers.map((i) => {
@@ -2588,7 +2739,6 @@ function calculateItemsInView(ctx, state, params = {}) {
2588
2739
  }) : void 0;
2589
2740
  const availableContainers = findAvailableContainers(
2590
2741
  ctx,
2591
- state,
2592
2742
  needNewContainers.length,
2593
2743
  startBuffered,
2594
2744
  endBuffered,
@@ -2610,29 +2760,30 @@ function calculateItemsInView(ctx, state, params = {}) {
2610
2760
  state.containerItemTypes.set(containerIndex, requiredItemTypes[idx]);
2611
2761
  }
2612
2762
  containerItemKeys.add(id);
2763
+ const containerSticky = `containerSticky${containerIndex}`;
2613
2764
  if (stickyIndicesSet.has(i)) {
2614
- set$(ctx, `containerSticky${containerIndex}`, true);
2765
+ set$(ctx, containerSticky, true);
2615
2766
  const topPadding = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
2616
2767
  set$(ctx, `containerStickyOffset${containerIndex}`, topPadding);
2617
2768
  state.stickyContainerPool.add(containerIndex);
2618
- } else {
2619
- set$(ctx, `containerSticky${containerIndex}`, false);
2769
+ } else if (peek$(ctx, containerSticky)) {
2770
+ set$(ctx, containerSticky, false);
2620
2771
  state.stickyContainerPool.delete(containerIndex);
2621
2772
  }
2622
- if (containerIndex >= numContainers2) {
2623
- numContainers2 = containerIndex + 1;
2773
+ if (containerIndex >= numContainers) {
2774
+ numContainers = containerIndex + 1;
2624
2775
  }
2625
2776
  }
2626
- if (numContainers2 !== prevNumContainers) {
2627
- set$(ctx, "numContainers", numContainers2);
2628
- if (numContainers2 > peek$(ctx, "numContainersPooled")) {
2629
- set$(ctx, "numContainersPooled", Math.ceil(numContainers2 * 1.5));
2777
+ if (numContainers !== prevNumContainers) {
2778
+ set$(ctx, "numContainers", numContainers);
2779
+ if (numContainers > peek$(ctx, "numContainersPooled")) {
2780
+ set$(ctx, "numContainersPooled", Math.ceil(numContainers * 1.5));
2630
2781
  }
2631
2782
  }
2632
2783
  }
2633
2784
  }
2634
2785
  if (stickyIndicesArr.length > 0) {
2635
- handleStickyRecycling(ctx, state, stickyIndicesArr, scroll, scrollBuffer, currentStickyIdx, pendingRemoval);
2786
+ handleStickyRecycling(ctx, stickyIndicesArr, scroll, scrollBuffer, currentStickyIdx, pendingRemoval);
2636
2787
  }
2637
2788
  let didChangePositions = false;
2638
2789
  for (let i = 0; i < numContainers; i++) {
@@ -2684,7 +2835,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2684
2835
  }
2685
2836
  if (!queuedInitialLayout && endBuffered !== null) {
2686
2837
  if (checkAllSizesKnown(state)) {
2687
- setDidLayout(ctx, state);
2838
+ setDidLayout(ctx);
2688
2839
  }
2689
2840
  }
2690
2841
  if (viewabilityConfigCallbackPairs) {
@@ -2697,9 +2848,6 @@ function calculateItemsInView(ctx, state, params = {}) {
2697
2848
  }
2698
2849
  }
2699
2850
  });
2700
- if (state.initialAnchor) {
2701
- ensureInitialAnchor(ctx, state);
2702
- }
2703
2851
  }
2704
2852
 
2705
2853
  // src/core/checkActualChange.ts
@@ -2722,20 +2870,69 @@ function checkActualChange(state, dataProp, previousData) {
2722
2870
  return false;
2723
2871
  }
2724
2872
 
2873
+ // src/core/checkFinishedScroll.ts
2874
+ function checkFinishedScroll(ctx) {
2875
+ ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
2876
+ }
2877
+ function checkFinishedScrollFrame(ctx) {
2878
+ const scrollingTo = ctx.state.scrollingTo;
2879
+ if (scrollingTo) {
2880
+ const { state } = ctx;
2881
+ state.animFrameCheckFinishedScroll = void 0;
2882
+ const scroll = state.scroll;
2883
+ const adjust = state.scrollAdjustHandler.getAdjust();
2884
+ const clampedTargetOffset = clampScrollOffset(ctx, scrollingTo.offset - (scrollingTo.viewOffset || 0));
2885
+ const maxOffset = clampScrollOffset(ctx, scroll);
2886
+ const diff1 = Math.abs(scroll - clampedTargetOffset);
2887
+ const diff2 = Math.abs(diff1 - adjust);
2888
+ const isNotOverscrolled = Math.abs(scroll - maxOffset) < 1;
2889
+ if (isNotOverscrolled && (diff1 < 1 || diff2 < 1)) {
2890
+ finishScrollTo(ctx);
2891
+ }
2892
+ }
2893
+ }
2894
+ function checkFinishedScrollFallback(ctx) {
2895
+ const state = ctx.state;
2896
+ const scrollingTo = state.scrollingTo;
2897
+ const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) || !state.didContainersLayout;
2898
+ state.timeoutCheckFinishedScrollFallback = setTimeout(
2899
+ () => {
2900
+ let numChecks = 0;
2901
+ const checkHasScrolled = () => {
2902
+ state.timeoutCheckFinishedScrollFallback = void 0;
2903
+ const isStillScrollingTo = state.scrollingTo;
2904
+ if (isStillScrollingTo) {
2905
+ numChecks++;
2906
+ if (state.hasScrolled || numChecks > 5) {
2907
+ finishScrollTo(ctx);
2908
+ } else {
2909
+ state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, 100);
2910
+ }
2911
+ }
2912
+ };
2913
+ checkHasScrolled();
2914
+ },
2915
+ slowTimeout ? 500 : 100
2916
+ );
2917
+ }
2918
+
2725
2919
  // src/core/doMaintainScrollAtEnd.ts
2726
- function doMaintainScrollAtEnd(ctx, state, animated) {
2920
+ function doMaintainScrollAtEnd(ctx, animated) {
2921
+ const state = ctx.state;
2727
2922
  const {
2923
+ didContainersLayout,
2924
+ isAtEnd,
2728
2925
  refScroller,
2729
2926
  props: { maintainScrollAtEnd }
2730
2927
  } = state;
2731
- if ((state == null ? void 0 : state.isAtEnd) && maintainScrollAtEnd && peek$(ctx, "containersDidLayout")) {
2928
+ if (isAtEnd && maintainScrollAtEnd && didContainersLayout) {
2732
2929
  const paddingTop = peek$(ctx, "alignItemsPaddingTop");
2733
2930
  if (paddingTop > 0) {
2734
2931
  state.scroll = 0;
2735
2932
  }
2736
2933
  requestAnimationFrame(() => {
2737
2934
  var _a3;
2738
- if (state == null ? void 0 : state.isAtEnd) {
2935
+ if (state.isAtEnd) {
2739
2936
  state.maintainingScrollAtEnd = true;
2740
2937
  (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
2741
2938
  animated
@@ -2806,28 +3003,30 @@ function updateAveragesOnDataChange(state, oldData, newData) {
2806
3003
  }
2807
3004
 
2808
3005
  // src/core/checkResetContainers.ts
2809
- function checkResetContainers(ctx, state, dataProp) {
3006
+ function checkResetContainers(ctx, dataProp) {
3007
+ const state = ctx.state;
2810
3008
  const { previousData } = state;
2811
3009
  if (previousData) {
2812
3010
  updateAveragesOnDataChange(state, previousData, dataProp);
2813
3011
  }
2814
3012
  const { maintainScrollAtEnd } = state.props;
2815
- calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
3013
+ calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
2816
3014
  const shouldMaintainScrollAtEnd = maintainScrollAtEnd === true || maintainScrollAtEnd.onDataChange;
2817
- const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, state, false);
3015
+ const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, false);
2818
3016
  if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
2819
3017
  state.isEndReached = false;
2820
3018
  }
2821
3019
  if (!didMaintainScrollAtEnd) {
2822
3020
  checkAtTop(state);
2823
- checkAtBottom(ctx, state);
3021
+ checkAtBottom(ctx);
2824
3022
  }
2825
3023
  delete state.previousData;
2826
3024
  }
2827
3025
 
2828
3026
  // src/core/doInitialAllocateContainers.ts
2829
- function doInitialAllocateContainers(ctx, state) {
3027
+ function doInitialAllocateContainers(ctx) {
2830
3028
  var _a3, _b, _c;
3029
+ const state = ctx.state;
2831
3030
  const {
2832
3031
  scrollLength,
2833
3032
  props: {
@@ -2865,10 +3064,10 @@ function doInitialAllocateContainers(ctx, state) {
2865
3064
  if (state.lastLayout) {
2866
3065
  if (state.initialScroll) {
2867
3066
  requestAnimationFrame(() => {
2868
- calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
3067
+ calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
2869
3068
  });
2870
3069
  } else {
2871
- calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
3070
+ calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
2872
3071
  }
2873
3072
  }
2874
3073
  return true;
@@ -2876,7 +3075,8 @@ function doInitialAllocateContainers(ctx, state) {
2876
3075
  }
2877
3076
 
2878
3077
  // src/core/handleLayout.ts
2879
- function handleLayout(ctx, state, layout, setCanRender) {
3078
+ function handleLayout(ctx, layout, setCanRender) {
3079
+ const state = ctx.state;
2880
3080
  const { maintainScrollAtEnd } = state.props;
2881
3081
  const measuredLength = layout[state.props.horizontal ? "width" : "height"];
2882
3082
  const previousLength = state.scrollLength;
@@ -2892,19 +3092,19 @@ function handleLayout(ctx, state, layout, setCanRender) {
2892
3092
  state.lastBatchingAction = Date.now();
2893
3093
  state.scrollForNextCalculateItemsInView = void 0;
2894
3094
  if (scrollLength > 0) {
2895
- doInitialAllocateContainers(ctx, state);
3095
+ doInitialAllocateContainers(ctx);
2896
3096
  }
2897
3097
  if (needsCalculate) {
2898
- calculateItemsInView(ctx, state, { doMVCP: true });
3098
+ calculateItemsInView(ctx, { doMVCP: true });
2899
3099
  }
2900
3100
  if (didChange || otherAxisSize !== prevOtherAxisSize) {
2901
3101
  set$(ctx, "scrollSize", { height: layout.height, width: layout.width });
2902
3102
  }
2903
3103
  if (maintainScrollAtEnd === true || maintainScrollAtEnd.onLayout) {
2904
- doMaintainScrollAtEnd(ctx, state, false);
3104
+ doMaintainScrollAtEnd(ctx, false);
2905
3105
  }
2906
- updateAlignItemsPaddingTop(ctx, state);
2907
- checkAtBottom(ctx, state);
3106
+ updateAlignItemsPaddingTop(ctx);
3107
+ checkAtBottom(ctx);
2908
3108
  checkAtTop(state);
2909
3109
  if (state) {
2910
3110
  state.needsOtherAxisSize = otherAxisSize - (state.props.stylePaddingTop || 0) < 10;
@@ -2920,8 +3120,9 @@ function handleLayout(ctx, state, layout, setCanRender) {
2920
3120
  }
2921
3121
 
2922
3122
  // src/core/onScroll.ts
2923
- function onScroll(ctx, state, event) {
3123
+ function onScroll(ctx, event) {
2924
3124
  var _a3, _b, _c;
3125
+ const state = ctx.state;
2925
3126
  const {
2926
3127
  scrollProcessingEnabled,
2927
3128
  props: { onScroll: onScrollProp }
@@ -2932,9 +3133,23 @@ function onScroll(ctx, state, event) {
2932
3133
  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
3134
  return;
2934
3135
  }
2935
- const newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
3136
+ let newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
2936
3137
  state.scrollPending = newScroll;
2937
- updateScroll(ctx, state, newScroll);
3138
+ if (state.scrollingTo) {
3139
+ const maxOffset = clampScrollOffset(ctx, newScroll);
3140
+ if (newScroll !== maxOffset && Math.abs(newScroll - maxOffset) > 1) {
3141
+ newScroll = maxOffset;
3142
+ scrollTo(ctx, {
3143
+ forceScroll: true,
3144
+ isInitialScroll: true,
3145
+ noScrollingTo: true,
3146
+ offset: newScroll
3147
+ });
3148
+ return;
3149
+ }
3150
+ }
3151
+ updateScroll(ctx, newScroll);
3152
+ checkFinishedScroll(ctx);
2938
3153
  onScrollProp == null ? void 0 : onScrollProp(event);
2939
3154
  }
2940
3155
 
@@ -2943,51 +3158,47 @@ var ScrollAdjustHandler = class {
2943
3158
  constructor(ctx) {
2944
3159
  this.appliedAdjust = 0;
2945
3160
  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
- }
3161
+ this.ctx = ctx;
2968
3162
  }
2969
3163
  requestAdjust(add) {
2970
- const scrollingTo = peek$(this.context, "scrollingTo");
3164
+ const scrollingTo = this.ctx.state.scrollingTo;
2971
3165
  if ((scrollingTo == null ? void 0 : scrollingTo.animated) && !scrollingTo.isInitialScroll) {
2972
3166
  this.pendingAdjust += add;
2973
- set$(this.context, "scrollAdjustPending", this.pendingAdjust);
3167
+ set$(this.ctx, "scrollAdjustPending", this.pendingAdjust);
2974
3168
  } else {
2975
3169
  this.appliedAdjust += add;
2976
- set$(this.context, "scrollAdjust", this.appliedAdjust);
3170
+ set$(this.ctx, "scrollAdjust", this.appliedAdjust);
3171
+ }
3172
+ if (this.ctx.state.scrollingTo) {
3173
+ checkFinishedScroll(this.ctx);
2977
3174
  }
2978
- }
2979
- setMounted() {
2980
- this.mounted = true;
2981
3175
  }
2982
3176
  getAdjust() {
2983
3177
  return this.appliedAdjust;
2984
3178
  }
3179
+ commitPendingAdjust() {
3180
+ {
3181
+ const state = this.ctx.state;
3182
+ const pending = this.pendingAdjust;
3183
+ if (pending !== 0) {
3184
+ this.pendingAdjust = 0;
3185
+ this.appliedAdjust += pending;
3186
+ state.scroll += pending;
3187
+ state.scrollForNextCalculateItemsInView = void 0;
3188
+ set$(this.ctx, "scrollAdjustPending", 0);
3189
+ set$(this.ctx, "scrollAdjust", this.appliedAdjust);
3190
+ calculateItemsInView(this.ctx);
3191
+ }
3192
+ }
3193
+ }
2985
3194
  };
2986
3195
 
2987
3196
  // src/core/updateItemSize.ts
2988
- function updateItemSize(ctx, state, itemKey, sizeObj) {
3197
+ function updateItemSize(ctx, itemKey, sizeObj) {
2989
3198
  var _a3;
3199
+ const state = ctx.state;
2990
3200
  const {
3201
+ didContainersLayout,
2991
3202
  sizesKnown,
2992
3203
  props: {
2993
3204
  getFixedItemSize,
@@ -3015,13 +3226,12 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
3015
3226
  return;
3016
3227
  }
3017
3228
  }
3018
- const containersDidLayout = peek$(ctx, "containersDidLayout");
3019
- let needsRecalculate = !containersDidLayout;
3229
+ let needsRecalculate = !didContainersLayout;
3020
3230
  let shouldMaintainScrollAtEnd = false;
3021
3231
  let minIndexSizeChanged;
3022
3232
  let maxOtherAxisSize = peek$(ctx, "otherAxisSize") || 0;
3023
3233
  const prevSizeKnown = state.sizesKnown.get(itemKey);
3024
- const diff = updateOneItemSize(ctx, state, itemKey, sizeObj);
3234
+ const diff = updateOneItemSize(ctx, itemKey, sizeObj);
3025
3235
  const size = roundSize(horizontal ? sizeObj.width : sizeObj.height);
3026
3236
  if (diff !== 0) {
3027
3237
  minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
@@ -3070,22 +3280,22 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
3070
3280
  if (!cur || maxOtherAxisSize > cur) {
3071
3281
  set$(ctx, "otherAxisSize", maxOtherAxisSize);
3072
3282
  }
3073
- if (containersDidLayout || checkAllSizesKnown(state)) {
3283
+ if (didContainersLayout || checkAllSizesKnown(state)) {
3074
3284
  if (needsRecalculate) {
3075
3285
  state.scrollForNextCalculateItemsInView = void 0;
3076
- calculateItemsInView(ctx, state, { doMVCP: true });
3286
+ calculateItemsInView(ctx, { doMVCP: true });
3077
3287
  }
3078
3288
  if (shouldMaintainScrollAtEnd) {
3079
3289
  if (maintainScrollAtEnd === true || maintainScrollAtEnd.onItemLayout) {
3080
- doMaintainScrollAtEnd(ctx, state, false);
3290
+ doMaintainScrollAtEnd(ctx, false);
3081
3291
  }
3082
3292
  }
3083
3293
  }
3084
3294
  }
3085
- function updateOneItemSize(ctx, state, itemKey, sizeObj) {
3295
+ function updateOneItemSize(ctx, itemKey, sizeObj) {
3086
3296
  var _a3;
3297
+ const state = ctx.state;
3087
3298
  const {
3088
- sizes,
3089
3299
  indexByKey,
3090
3300
  sizesKnown,
3091
3301
  averageSizes,
@@ -3093,9 +3303,10 @@ function updateOneItemSize(ctx, state, itemKey, sizeObj) {
3093
3303
  } = state;
3094
3304
  if (!data) return 0;
3095
3305
  const index = indexByKey.get(itemKey);
3096
- const prevSize = getItemSize(ctx, state, itemKey, index, data[index]);
3306
+ const prevSize = getItemSize(ctx, itemKey, index, data[index]);
3097
3307
  const rawSize = horizontal ? sizeObj.width : sizeObj.height;
3098
3308
  const size = Math.round(rawSize) ;
3309
+ const prevSizeKnown = sizesKnown.get(itemKey);
3099
3310
  sizesKnown.set(itemKey, size);
3100
3311
  if (!getEstimatedItemSize && !getFixedItemSize && size > 0) {
3101
3312
  const itemType = getItemType ? (_a3 = getItemType(data[index], index)) != null ? _a3 : "" : "";
@@ -3103,11 +3314,15 @@ function updateOneItemSize(ctx, state, itemKey, sizeObj) {
3103
3314
  if (!averages) {
3104
3315
  averages = averageSizes[itemType] = { avg: 0, num: 0 };
3105
3316
  }
3106
- averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
3107
- averages.num++;
3317
+ if (prevSizeKnown !== void 0 && prevSizeKnown > 0) {
3318
+ averages.avg += (size - prevSizeKnown) / averages.num;
3319
+ } else {
3320
+ averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
3321
+ averages.num++;
3322
+ }
3108
3323
  }
3109
3324
  if (!prevSize || Math.abs(prevSize - size) > 0.1) {
3110
- setSize(ctx, state, itemKey, size);
3325
+ setSize(ctx, itemKey, size);
3111
3326
  return size - prevSize;
3112
3327
  }
3113
3328
  return 0;
@@ -3154,14 +3369,15 @@ function createColumnWrapperStyle(contentContainerStyle) {
3154
3369
  }
3155
3370
 
3156
3371
  // src/utils/createImperativeHandle.ts
3157
- function createImperativeHandle(ctx, state) {
3372
+ function createImperativeHandle(ctx) {
3373
+ const state = ctx.state;
3158
3374
  const scrollIndexIntoView = (options) => {
3159
3375
  if (state) {
3160
3376
  const { index, ...rest } = options;
3161
3377
  const { startNoBuffer, endNoBuffer } = state;
3162
3378
  if (index < startNoBuffer || index > endNoBuffer) {
3163
3379
  const viewPosition = index < startNoBuffer ? 0 : 1;
3164
- scrollToIndex(ctx, state, {
3380
+ scrollToIndex(ctx, {
3165
3381
  ...rest,
3166
3382
  index,
3167
3383
  viewPosition
@@ -3176,7 +3392,7 @@ function createImperativeHandle(ctx, state) {
3176
3392
  getScrollableNode: () => refScroller.current.getScrollableNode(),
3177
3393
  getScrollResponder: () => refScroller.current.getScrollResponder(),
3178
3394
  getState: () => ({
3179
- activeStickyIndex: state.activeStickyIndex,
3395
+ activeStickyIndex: peek$(ctx, "activeStickyIndex"),
3180
3396
  contentLength: state.totalSize,
3181
3397
  data: state.props.data,
3182
3398
  elementAtIndex: (index) => {
@@ -3187,6 +3403,8 @@ function createImperativeHandle(ctx, state) {
3187
3403
  endBuffered: state.endBuffered,
3188
3404
  isAtEnd: state.isAtEnd,
3189
3405
  isAtStart: state.isAtStart,
3406
+ listen: (signalName, cb) => listen$(ctx, signalName, cb),
3407
+ listenToPosition: (key, cb) => listenPosition$(ctx, key, cb),
3190
3408
  positionAtIndex: (index) => state.positions.get(getId(state, index)),
3191
3409
  positions: state.positions,
3192
3410
  scroll: state.scroll,
@@ -3211,23 +3429,23 @@ function createImperativeHandle(ctx, state) {
3211
3429
  if (index !== -1) {
3212
3430
  const paddingBottom = stylePaddingBottom || 0;
3213
3431
  const footerSize = peek$(ctx, "footerSize") || 0;
3214
- scrollToIndex(ctx, state, {
3432
+ scrollToIndex(ctx, {
3433
+ ...options,
3215
3434
  index,
3216
3435
  viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
3217
- viewPosition: 1,
3218
- ...options
3436
+ viewPosition: 1
3219
3437
  });
3220
3438
  }
3221
3439
  },
3222
- scrollToIndex: (params) => scrollToIndex(ctx, state, params),
3440
+ scrollToIndex: (params) => scrollToIndex(ctx, params),
3223
3441
  scrollToItem: ({ item, ...props }) => {
3224
3442
  const data = state.props.data;
3225
3443
  const index = data.indexOf(item);
3226
3444
  if (index !== -1) {
3227
- scrollToIndex(ctx, state, { index, ...props });
3445
+ scrollToIndex(ctx, { index, ...props });
3228
3446
  }
3229
3447
  },
3230
- scrollToOffset: (params) => scrollTo(ctx, state, params),
3448
+ scrollToOffset: (params) => scrollTo(ctx, params),
3231
3449
  setScrollProcessingEnabled: (enabled) => {
3232
3450
  state.scrollProcessingEnabled = enabled;
3233
3451
  },
@@ -3237,8 +3455,9 @@ function createImperativeHandle(ctx, state) {
3237
3455
  }
3238
3456
  };
3239
3457
  }
3240
- function getRenderedItem(ctx, state, key) {
3458
+ function getRenderedItem(ctx, key) {
3241
3459
  var _a3;
3460
+ const state = ctx.state;
3242
3461
  if (!state) {
3243
3462
  return null;
3244
3463
  }
@@ -3315,11 +3534,13 @@ function useThrottledOnScroll(originalHandler, scrollEventThrottle) {
3315
3534
  var DEFAULT_DRAW_DISTANCE = 250;
3316
3535
  var DEFAULT_ITEM_SIZE = 100;
3317
3536
  var LegendList = typedMemo(
3537
+ // biome-ignore lint/nursery/noShadow: const function name shadowing is intentional
3318
3538
  typedForwardRef(function LegendList2(props, forwardedRef) {
3319
3539
  const { children, data: dataProp, renderItem: renderItemProp, ...restProps } = props;
3320
3540
  const isChildrenMode = children !== void 0 && dataProp === void 0;
3321
3541
  const processedProps = isChildrenMode ? {
3322
3542
  ...restProps,
3543
+ childrenMode: true,
3323
3544
  data: (isArray(children) ? children : React3.Children.toArray(children)).flat(1),
3324
3545
  renderItem: ({ item }) => item
3325
3546
  } : {
@@ -3336,10 +3557,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3336
3557
  alignItemsAtEnd = false,
3337
3558
  columnWrapperStyle,
3338
3559
  contentContainerStyle: contentContainerStyleProp,
3560
+ contentInset,
3339
3561
  data: dataProp = [],
3340
3562
  dataVersion,
3341
3563
  drawDistance = 250,
3342
- enableAverages = true,
3343
3564
  estimatedItemSize: estimatedItemSizeProp,
3344
3565
  estimatedListSize,
3345
3566
  extraData,
@@ -3381,6 +3602,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3381
3602
  snapToIndices,
3382
3603
  stickyHeaderIndices: stickyHeaderIndicesProp,
3383
3604
  stickyIndices: stickyIndicesDeprecated,
3605
+ // TODOV3: Remove from v3 release
3384
3606
  style: styleProp,
3385
3607
  suggestEstimatedItemSize,
3386
3608
  viewabilityConfig,
@@ -3388,6 +3610,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3388
3610
  waitForInitialLayout = true,
3389
3611
  ...rest
3390
3612
  } = props;
3613
+ const animatedPropsInternal = props.animatedPropsInternal;
3614
+ const { childrenMode } = rest;
3391
3615
  const contentContainerStyle = { ...StyleSheet.flatten(contentContainerStyleProp) };
3392
3616
  const style = { ...StyleSheet.flatten(styleProp) };
3393
3617
  const stylePaddingTopState = extractPadding(style, contentContainerStyle, "Top");
@@ -3411,10 +3635,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3411
3635
  }
3412
3636
  const refState = useRef();
3413
3637
  if (!refState.current) {
3414
- if (!ctx.internalState) {
3638
+ if (!ctx.state) {
3415
3639
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : { height: 0, width: 0 } )[horizontal ? "width" : "height"];
3416
- ctx.internalState = {
3417
- activeStickyIndex: void 0,
3640
+ ctx.state = {
3641
+ activeStickyIndex: -1,
3418
3642
  averageSizes: {},
3419
3643
  columns: /* @__PURE__ */ new Map(),
3420
3644
  containerItemKeys: /* @__PURE__ */ new Set(),
@@ -3440,9 +3664,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3440
3664
  initialScroll: initialScrollProp,
3441
3665
  isAtEnd: false,
3442
3666
  isAtStart: false,
3443
- isEndReached: false,
3667
+ isEndReached: null,
3444
3668
  isFirst: true,
3445
- isStartReached: false,
3669
+ isStartReached: null,
3446
3670
  lastBatchingAction: Date.now(),
3447
3671
  lastLayout: void 0,
3448
3672
  loadStartTime: Date.now(),
@@ -3474,12 +3698,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3474
3698
  totalSize: 0,
3475
3699
  viewabilityConfigCallbackPairs: void 0
3476
3700
  };
3477
- const internalState = ctx.internalState;
3478
- internalState.triggerCalculateItemsInView = (params) => calculateItemsInView(ctx, internalState, params);
3701
+ const internalState = ctx.state;
3702
+ internalState.triggerCalculateItemsInView = (params) => calculateItemsInView(ctx, params);
3479
3703
  set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPosition);
3480
3704
  set$(ctx, "extraData", extraData);
3481
3705
  }
3482
- refState.current = ctx.internalState;
3706
+ refState.current = ctx.state;
3483
3707
  }
3484
3708
  const state = refState.current;
3485
3709
  const isFirstLocal = state.isFirst;
@@ -3493,9 +3717,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3493
3717
  const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
3494
3718
  state.props = {
3495
3719
  alignItemsAtEnd,
3720
+ animatedProps: animatedPropsInternal,
3721
+ contentInset,
3496
3722
  data: dataProp,
3497
3723
  dataVersion,
3498
- enableAverages,
3499
3724
  estimatedItemSize,
3500
3725
  getEstimatedItemSize,
3501
3726
  getFixedItemSize,
@@ -3538,62 +3763,52 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3538
3763
  set$(ctx, "lastItemKeys", memoizedLastItemKeys);
3539
3764
  set$(ctx, "numColumns", numColumnsProp);
3540
3765
  const prevPaddingTop = peek$(ctx, "stylePaddingTop");
3541
- setPaddingTop(ctx, state, { stylePaddingTop: stylePaddingTopState });
3766
+ setPaddingTop(ctx, { stylePaddingTop: stylePaddingTopState });
3542
3767
  refState.current.props.stylePaddingBottom = stylePaddingBottomState;
3543
3768
  let paddingDiff = stylePaddingTopState - prevPaddingTop;
3544
3769
  if (paddingDiff && prevPaddingTop !== void 0 && Platform.OS === "ios") {
3545
3770
  if (state.scroll < 0) {
3546
3771
  paddingDiff += state.scroll;
3547
3772
  }
3548
- requestAdjust(ctx, state, paddingDiff);
3773
+ requestAdjust(ctx, paddingDiff);
3549
3774
  }
3550
3775
  };
3551
3776
  if (isFirstLocal) {
3552
3777
  initializeStateVars();
3553
3778
  updateItemPositions(
3554
3779
  ctx,
3555
- state,
3556
3780
  /*dataChanged*/
3557
3781
  true
3558
3782
  );
3559
3783
  }
3560
3784
  const initialContentOffset = useMemo(() => {
3561
- var _a4, _b2;
3562
- const { initialScroll } = refState.current;
3563
- if (!initialScroll) {
3785
+ let value;
3786
+ const { initialScroll, initialAnchor } = refState.current;
3787
+ if (initialScroll) {
3788
+ if (initialScroll.contentOffset !== void 0) {
3789
+ value = initialScroll.contentOffset;
3790
+ } else {
3791
+ const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, initialScroll.index) : 0;
3792
+ const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, initialScroll);
3793
+ const clampedOffset = clampScrollOffset(ctx, resolvedOffset);
3794
+ const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
3795
+ refState.current.initialScroll = updatedInitialScroll;
3796
+ state.initialScroll = updatedInitialScroll;
3797
+ value = clampedOffset;
3798
+ }
3799
+ } else {
3564
3800
  refState.current.initialAnchor = void 0;
3565
- return 0;
3566
- }
3567
- if (initialScroll.index !== void 0 && (!refState.current.initialAnchor || ((_a4 = refState.current.initialAnchor) == null ? void 0 : _a4.index) !== initialScroll.index)) {
3568
- refState.current.initialAnchor = {
3569
- attempts: 0,
3570
- index: initialScroll.index,
3571
- settledTicks: 0,
3572
- viewOffset: (_b2 = initialScroll.viewOffset) != null ? _b2 : 0,
3573
- viewPosition: initialScroll.viewPosition
3574
- };
3801
+ value = 0;
3802
+ }
3803
+ if (!value) {
3804
+ state.didFinishInitialScroll = true;
3575
3805
  }
3576
- if (initialScroll.contentOffset !== void 0) {
3577
- return initialScroll.contentOffset;
3578
- }
3579
- const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, state, initialScroll.index) : 0;
3580
- const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, state, baseOffset, initialScroll);
3581
- let clampedOffset = resolvedOffset;
3582
- if (Number.isFinite(state.scrollLength) && Number.isFinite(state.totalSize)) {
3583
- const maxOffset = Math.max(0, state.totalSize - state.scrollLength);
3584
- clampedOffset = Math.min(clampedOffset, maxOffset);
3585
- }
3586
- clampedOffset = Math.max(0, clampedOffset);
3587
- const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
3588
- refState.current.initialScroll = updatedInitialScroll;
3589
- state.initialScroll = updatedInitialScroll;
3590
- refState.current.isStartReached = clampedOffset < refState.current.scrollLength * onStartReachedThreshold;
3591
- return clampedOffset;
3806
+ return value;
3592
3807
  }, [renderNum]);
3593
3808
  if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
3594
3809
  refState.current.lastBatchingAction = Date.now();
3595
3810
  if (!keyExtractorProp && !isFirstLocal && didDataChangeLocal) {
3596
- IS_DEV && warnDevOnce(
3811
+ IS_DEV && !childrenMode && warnDevOnce(
3597
3812
  "keyExtractor",
3598
3813
  "Changing data without a keyExtractor can cause slow performance and resetting scroll. If your list data can change you should use a keyExtractor with a unique id for best performance and behavior."
3599
3814
  );
@@ -3616,12 +3831,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3616
3831
  }
3617
3832
  }, []);
3618
3833
  const doInitialScroll = useCallback(() => {
3619
- var _a4;
3620
3834
  const initialScroll = state.initialScroll;
3621
3835
  if (initialScroll) {
3622
- scrollTo(ctx, state, {
3836
+ scrollTo(ctx, {
3623
3837
  animated: false,
3624
- index: (_a4 = state.initialScroll) == null ? void 0 : _a4.index,
3838
+ index: initialScroll == null ? void 0 : initialScroll.index,
3625
3839
  isInitialScroll: true,
3626
3840
  offset: initialContentOffset,
3627
3841
  precomputedWithViewOffset: true
@@ -3630,7 +3844,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3630
3844
  }, [initialContentOffset]);
3631
3845
  const onLayoutChange = useCallback((layout) => {
3632
3846
  doInitialScroll();
3633
- handleLayout(ctx, state, layout, setCanRender);
3847
+ handleLayout(ctx, layout, setCanRender);
3634
3848
  }, []);
3635
3849
  const { onLayout } = useOnLayoutSync({
3636
3850
  onLayoutChange,
@@ -3640,7 +3854,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3640
3854
  });
3641
3855
  useLayoutEffect(() => {
3642
3856
  if (snapToIndices) {
3643
- updateSnapToOffsets(ctx, state);
3857
+ updateSnapToOffsets(ctx);
3644
3858
  }
3645
3859
  }, [snapToIndices]);
3646
3860
  useLayoutEffect(() => {
@@ -3650,9 +3864,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3650
3864
  isFirst,
3651
3865
  props: { data }
3652
3866
  } = state;
3653
- const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx, state);
3867
+ const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx);
3654
3868
  if (!didAllocateContainers && !isFirst && (didDataChange || didColumnsChange)) {
3655
- checkResetContainers(ctx, state, data);
3869
+ checkResetContainers(ctx, data);
3656
3870
  }
3657
3871
  state.didColumnsChange = false;
3658
3872
  state.didDataChange = false;
@@ -3677,15 +3891,21 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3677
3891
  state.viewabilityConfigCallbackPairs = viewability;
3678
3892
  state.enableScrollForNextCalculateItemsInView = !viewability;
3679
3893
  }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
3680
- useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx, state), []);
3894
+ useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx), []);
3681
3895
  {
3682
3896
  useEffect(doInitialScroll, []);
3683
3897
  }
3684
3898
  const fns = useMemo(
3685
3899
  () => ({
3686
- getRenderedItem: (key) => getRenderedItem(ctx, state, key),
3687
- onScroll: (event) => onScroll(ctx, state, event),
3688
- updateItemSize: (itemKey, sizeObj) => updateItemSize(ctx, state, itemKey, sizeObj)
3900
+ getRenderedItem: (key) => getRenderedItem(ctx, key),
3901
+ onMomentumScrollEnd: (event) => {
3902
+ checkFinishedScrollFallback(ctx);
3903
+ if (onMomentumScrollEnd) {
3904
+ onMomentumScrollEnd(event);
3905
+ }
3906
+ },
3907
+ onScroll: (event) => onScroll(ctx, event),
3908
+ updateItemSize: (itemKey, sizeObj) => updateItemSize(ctx, itemKey, sizeObj)
3689
3909
  }),
3690
3910
  []
3691
3911
  );
@@ -3697,6 +3917,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3697
3917
  alignItemsAtEnd,
3698
3918
  canRender,
3699
3919
  contentContainerStyle,
3920
+ contentInset,
3700
3921
  getRenderedItem: fns.getRenderedItem,
3701
3922
  horizontal,
3702
3923
  initialContentOffset,
@@ -3705,16 +3926,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3705
3926
  maintainVisibleContentPosition,
3706
3927
  onLayout,
3707
3928
  onLayoutHeader,
3708
- onMomentumScrollEnd: (event) => {
3709
- {
3710
- requestAnimationFrame(() => {
3711
- finishScrollTo(ctx, refState.current);
3712
- });
3713
- }
3714
- if (onMomentumScrollEnd) {
3715
- onMomentumScrollEnd(event);
3716
- }
3717
- },
3929
+ onMomentumScrollEnd: fns.onMomentumScrollEnd,
3718
3930
  onScroll: onScrollHandler,
3719
3931
  recycleItems,
3720
3932
  refreshControl: refreshControl ? stylePaddingTopState > 0 ? React3.cloneElement(refreshControl, {
@@ -3729,7 +3941,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3729
3941
  ),
3730
3942
  refScrollView: combinedRef,
3731
3943
  scrollAdjustHandler: (_b = refState.current) == null ? void 0 : _b.scrollAdjustHandler,
3732
- scrollEventThrottle: 16 ,
3944
+ scrollEventThrottle: 0,
3733
3945
  snapToIndices,
3734
3946
  stickyHeaderIndices,
3735
3947
  style,