@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.js CHANGED
@@ -33,31 +33,65 @@ var View = React3.forwardRef(function View2(props, ref) {
33
33
  });
34
34
  var Text = View;
35
35
 
36
+ // src/state/getContentInsetEnd.ts
37
+ function getContentInsetEnd(state) {
38
+ var _a3;
39
+ const { props } = state;
40
+ const horizontal = props.horizontal;
41
+ let contentInset = props.contentInset;
42
+ if (!contentInset) {
43
+ const animatedInset = (_a3 = props.animatedProps) == null ? void 0 : _a3.contentInset;
44
+ if (animatedInset) {
45
+ if ("get" in animatedInset) {
46
+ contentInset = animatedInset.get();
47
+ } else {
48
+ contentInset = animatedInset;
49
+ }
50
+ }
51
+ }
52
+ return (horizontal ? contentInset == null ? void 0 : contentInset.right : contentInset == null ? void 0 : contentInset.bottom) || 0;
53
+ }
54
+
55
+ // src/state/getContentSize.ts
56
+ function getContentSize(ctx) {
57
+ var _a3;
58
+ const { values, state } = ctx;
59
+ const stylePaddingTop = values.get("stylePaddingTop") || 0;
60
+ const stylePaddingBottom = state.props.stylePaddingBottom || 0;
61
+ const headerSize = values.get("headerSize") || 0;
62
+ const footerSize = values.get("footerSize") || 0;
63
+ const contentInsetBottom = getContentInsetEnd(state);
64
+ const totalSize = (_a3 = state.pendingTotalSize) != null ? _a3 : values.get("totalSize");
65
+ return headerSize + footerSize + totalSize + stylePaddingTop + stylePaddingBottom + (contentInsetBottom || 0);
66
+ }
67
+
36
68
  // src/platform/Animated.tsx
37
69
  var createAnimatedValue = (value) => value;
38
70
 
39
71
  // src/state/state.tsx
40
72
  var ContextState = React3__namespace.createContext(null);
73
+ var contextNum = 0;
41
74
  function StateProvider({ children }) {
42
75
  const [value] = React3__namespace.useState(() => ({
43
76
  animatedScrollY: createAnimatedValue(0),
44
77
  columnWrapperStyle: void 0,
45
- internalState: void 0,
78
+ contextNum: contextNum++,
46
79
  listeners: /* @__PURE__ */ new Map(),
47
80
  mapViewabilityAmountCallbacks: /* @__PURE__ */ new Map(),
48
81
  mapViewabilityAmountValues: /* @__PURE__ */ new Map(),
49
82
  mapViewabilityCallbacks: /* @__PURE__ */ new Map(),
50
83
  mapViewabilityConfigStates: /* @__PURE__ */ new Map(),
51
84
  mapViewabilityValues: /* @__PURE__ */ new Map(),
85
+ positionListeners: /* @__PURE__ */ new Map(),
86
+ state: void 0,
52
87
  values: /* @__PURE__ */ new Map([
53
88
  ["alignItemsPaddingTop", 0],
54
89
  ["stylePaddingTop", 0],
55
90
  ["headerSize", 0],
56
91
  ["numContainers", 0],
57
- ["activeStickyIndex", void 0],
92
+ ["activeStickyIndex", -1],
58
93
  ["totalSize", 0],
59
- ["scrollAdjustPending", 0],
60
- ["scrollingTo", void 0]
94
+ ["scrollAdjustPending", 0]
61
95
  ]),
62
96
  viewRefs: /* @__PURE__ */ new Map()
63
97
  }));
@@ -125,15 +159,24 @@ function set$(ctx, signalName, value) {
125
159
  }
126
160
  }
127
161
  }
128
- function getContentSize(ctx) {
129
- var _a3, _b;
130
- const { values, internalState } = ctx;
131
- const stylePaddingTop = values.get("stylePaddingTop") || 0;
132
- const stylePaddingBottom = (internalState == null ? void 0 : internalState.props.stylePaddingBottom) || 0;
133
- const headerSize = values.get("headerSize") || 0;
134
- const footerSize = values.get("footerSize") || 0;
135
- const totalSize = (_b = (_a3 = ctx.internalState) == null ? void 0 : _a3.pendingTotalSize) != null ? _b : values.get("totalSize");
136
- return headerSize + footerSize + totalSize + stylePaddingTop + stylePaddingBottom;
162
+ function listenPosition$(ctx, key, cb) {
163
+ const { positionListeners } = ctx;
164
+ let setListeners = positionListeners.get(key);
165
+ if (!setListeners) {
166
+ setListeners = /* @__PURE__ */ new Set();
167
+ positionListeners.set(key, setListeners);
168
+ }
169
+ setListeners.add(cb);
170
+ return () => setListeners.delete(cb);
171
+ }
172
+ function notifyPosition$(ctx, key, value) {
173
+ const { positionListeners } = ctx;
174
+ const setListeners = positionListeners.get(key);
175
+ if (setListeners) {
176
+ for (const listener of setListeners) {
177
+ listener(value);
178
+ }
179
+ }
137
180
  }
138
181
  function useArr$(signalNames) {
139
182
  const ctx = React3__namespace.useContext(ContextState);
@@ -255,12 +298,12 @@ function findContainerId(ctx, key) {
255
298
  }
256
299
 
257
300
  // src/components/PositionView.tsx
258
- var PositionViewState = typedMemo(function PositionView({
301
+ var PositionViewState = typedMemo(function PositionViewState2({
259
302
  id,
260
303
  horizontal,
261
304
  style,
262
305
  refView,
263
- ...rest
306
+ ...props
264
307
  }) {
265
308
  const [position = POSITION_OUT_OF_VIEW] = useArr$([`containerPosition${id}`]);
266
309
  const base = {
@@ -268,7 +311,8 @@ var PositionViewState = typedMemo(function PositionView({
268
311
  };
269
312
  const composed = isArray(style) ? Object.assign({}, ...style) : style;
270
313
  const combinedStyle = horizontal ? { ...base, ...composed, left: position } : { ...base, ...composed, top: position };
271
- return /* @__PURE__ */ React3__namespace.createElement("div", { ref: refView, style: combinedStyle, ...rest });
314
+ const { animatedScrollY, stickyOffset, onLayout, ...webProps } = props;
315
+ return /* @__PURE__ */ React3__namespace.createElement("div", { ref: refView, ...webProps, style: combinedStyle });
272
316
  });
273
317
  var PositionViewSticky = typedMemo(function PositionViewSticky2({
274
318
  id,
@@ -313,63 +357,80 @@ var PositionViewSticky = typedMemo(function PositionViewSticky2({
313
357
  }, [composed, horizontal, position, index, stickyOffset, headerSize, activeStickyIndex]);
314
358
  return /* @__PURE__ */ React3__namespace.createElement("div", { ref: refView, style: viewStyle, ...rest }, children);
315
359
  });
316
- var PositionView2 = PositionViewState;
360
+ var PositionView = PositionViewState;
317
361
 
318
362
  // src/constants-platform.ts
319
363
  var IsNewArchitecture = true;
320
- var symbolFirst = Symbol();
321
364
  function useInit(cb) {
322
- const refValue = React3.useRef(symbolFirst);
323
- if (refValue.current === symbolFirst) {
324
- refValue.current = cb();
325
- }
326
- return refValue.current;
365
+ React3.useState(() => cb());
327
366
  }
328
367
 
329
368
  // src/state/ContextContainer.ts
330
369
  var ContextContainer = React3.createContext(null);
370
+ function useContextContainer() {
371
+ return React3.useContext(ContextContainer);
372
+ }
331
373
  function useViewability(callback, configId) {
332
374
  const ctx = useStateContext();
333
- const { containerId } = React3.useContext(ContextContainer);
334
- const key = containerId + (configId != null ? configId : "");
375
+ const containerContext = useContextContainer();
335
376
  useInit(() => {
377
+ if (!containerContext) {
378
+ return;
379
+ }
380
+ const { containerId } = containerContext;
381
+ const key = containerId + (configId != null ? configId : "");
336
382
  const value = ctx.mapViewabilityValues.get(key);
337
383
  if (value) {
338
384
  callback(value);
339
385
  }
340
386
  });
341
- ctx.mapViewabilityCallbacks.set(key, callback);
342
- React3.useEffect(
343
- () => () => {
387
+ React3.useEffect(() => {
388
+ if (!containerContext) {
389
+ return;
390
+ }
391
+ const { containerId } = containerContext;
392
+ const key = containerId + (configId != null ? configId : "");
393
+ ctx.mapViewabilityCallbacks.set(key, callback);
394
+ return () => {
344
395
  ctx.mapViewabilityCallbacks.delete(key);
345
- },
346
- []
347
- );
396
+ };
397
+ }, [ctx, callback, configId, containerContext]);
348
398
  }
349
399
  function useViewabilityAmount(callback) {
350
400
  const ctx = useStateContext();
351
- const { containerId } = React3.useContext(ContextContainer);
401
+ const containerContext = useContextContainer();
352
402
  useInit(() => {
403
+ if (!containerContext) {
404
+ return;
405
+ }
406
+ const { containerId } = containerContext;
353
407
  const value = ctx.mapViewabilityAmountValues.get(containerId);
354
408
  if (value) {
355
409
  callback(value);
356
410
  }
357
411
  });
358
- ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
359
- React3.useEffect(
360
- () => () => {
412
+ React3.useEffect(() => {
413
+ if (!containerContext) {
414
+ return;
415
+ }
416
+ const { containerId } = containerContext;
417
+ ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
418
+ return () => {
361
419
  ctx.mapViewabilityAmountCallbacks.delete(containerId);
362
- },
363
- []
364
- );
420
+ };
421
+ }, [ctx, callback, containerContext]);
365
422
  }
366
423
  function useRecyclingEffect(effect) {
367
- const { index, value } = React3.useContext(ContextContainer);
424
+ const containerContext = useContextContainer();
368
425
  const prevValues = React3.useRef({
369
426
  prevIndex: void 0,
370
427
  prevItem: void 0
371
428
  });
372
429
  React3.useEffect(() => {
430
+ if (!containerContext) {
431
+ return;
432
+ }
433
+ const { index, value } = containerContext;
373
434
  let ret;
374
435
  if (prevValues.current.prevIndex !== void 0 && prevValues.current.prevItem !== void 0) {
375
436
  ret = effect({
@@ -384,48 +445,73 @@ function useRecyclingEffect(effect) {
384
445
  prevItem: value
385
446
  };
386
447
  return ret;
387
- }, [index, value, effect]);
448
+ }, [effect, containerContext]);
388
449
  }
389
450
  function useRecyclingState(valueOrFun) {
390
- const { index, value, itemKey, triggerLayout } = React3.useContext(ContextContainer);
391
- const refState = React3.useRef({
392
- itemKey: null,
393
- value: null
451
+ var _a3, _b;
452
+ const containerContext = useContextContainer();
453
+ const computeValue = (ctx) => {
454
+ if (isFunction(valueOrFun)) {
455
+ const initializer = valueOrFun;
456
+ return ctx ? initializer({
457
+ index: ctx.index,
458
+ item: ctx.value,
459
+ prevIndex: void 0,
460
+ prevItem: void 0
461
+ }) : initializer();
462
+ }
463
+ return valueOrFun;
464
+ };
465
+ const [stateValue, setStateValue] = React3.useState(() => {
466
+ return computeValue(containerContext);
394
467
  });
395
- const [_, setRenderNum] = React3.useState(0);
396
- const state = refState.current;
397
- if (state.itemKey !== itemKey) {
398
- state.itemKey = itemKey;
399
- state.value = isFunction(valueOrFun) ? valueOrFun({
400
- index,
401
- item: value,
402
- prevIndex: void 0,
403
- prevItem: void 0
404
- }) : valueOrFun;
468
+ const prevItemKeyRef = React3.useRef((_a3 = containerContext == null ? void 0 : containerContext.itemKey) != null ? _a3 : null);
469
+ const currentItemKey = (_b = containerContext == null ? void 0 : containerContext.itemKey) != null ? _b : null;
470
+ if (currentItemKey !== null && prevItemKeyRef.current !== currentItemKey) {
471
+ prevItemKeyRef.current = currentItemKey;
472
+ setStateValue(computeValue(containerContext));
405
473
  }
474
+ const triggerLayout = containerContext == null ? void 0 : containerContext.triggerLayout;
406
475
  const setState = React3.useCallback(
407
476
  (newState) => {
408
- state.value = isFunction(newState) ? newState(state.value) : newState;
409
- setRenderNum((v) => v + 1);
477
+ if (!triggerLayout) {
478
+ return;
479
+ }
480
+ setStateValue((prevValue) => {
481
+ return isFunction(newState) ? newState(prevValue) : newState;
482
+ });
410
483
  triggerLayout();
411
484
  },
412
- [triggerLayout, state]
485
+ [triggerLayout]
413
486
  );
414
- return [state.value, setState];
487
+ return [stateValue, setState];
415
488
  }
416
489
  function useIsLastItem() {
417
- const { itemKey } = React3.useContext(ContextContainer);
418
- const isLast = useSelector$("lastItemKeys", (lastItemKeys) => (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false);
490
+ const containerContext = useContextContainer();
491
+ const isLast = useSelector$("lastItemKeys", (lastItemKeys) => {
492
+ if (containerContext) {
493
+ const { itemKey } = containerContext;
494
+ if (!isNullOrUndefined(itemKey)) {
495
+ return (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false;
496
+ }
497
+ }
498
+ return false;
499
+ });
419
500
  return isLast;
420
501
  }
421
502
  function useListScrollSize() {
422
503
  const [scrollSize] = useArr$(["scrollSize"]);
423
504
  return scrollSize;
424
505
  }
506
+ var noop = () => {
507
+ };
425
508
  function useSyncLayout() {
426
- {
427
- const { triggerLayout: syncLayout } = React3.useContext(ContextContainer);
509
+ const containerContext = useContextContainer();
510
+ if (containerContext) {
511
+ const { triggerLayout: syncLayout } = containerContext;
428
512
  return syncLayout;
513
+ } else {
514
+ return noop;
429
515
  }
430
516
  }
431
517
 
@@ -471,10 +557,9 @@ function createResizeObserver(element, callback) {
471
557
  }
472
558
  callbacks.add(callback);
473
559
  return () => {
474
- const callbacks2 = callbackMap.get(element);
475
- if (callbacks2) {
476
- callbacks2.delete(callback);
477
- if (callbacks2.size === 0) {
560
+ if (callbacks) {
561
+ callbacks.delete(callback);
562
+ if (callbacks.size === 0) {
478
563
  callbackMap.delete(element);
479
564
  observer.unobserve(element);
480
565
  }
@@ -509,10 +594,10 @@ function useOnLayoutSync({
509
594
  return createResizeObserver(element, (entry) => {
510
595
  var _a4;
511
596
  const target = entry.target instanceof HTMLElement ? entry.target : void 0;
512
- const rect2 = (_a4 = entry.contentRect) != null ? _a4 : target == null ? void 0 : target.getBoundingClientRect();
513
- if (rect2.width !== prevRect.width || rect2.height !== prevRect.height) {
514
- prevRect = rect2;
515
- emit(toLayout(rect2), false);
597
+ const rectObserved = (_a4 = entry.contentRect) != null ? _a4 : target == null ? void 0 : target.getBoundingClientRect();
598
+ if (rectObserved.width !== prevRect.width || rectObserved.height !== prevRect.height) {
599
+ prevRect = rectObserved;
600
+ emit(toLayout(rectObserved), false);
516
601
  }
517
602
  });
518
603
  }, deps || []);
@@ -640,7 +725,7 @@ var Container = typedMemo(function Container2({
640
725
  },
641
726
  [itemKey, layoutRenderCount]
642
727
  );
643
- const PositionComponent = isSticky ? PositionViewSticky : PositionView2;
728
+ const PositionComponent = isSticky ? PositionViewSticky : PositionView;
644
729
  return /* @__PURE__ */ React3__namespace.createElement(
645
730
  PositionComponent,
646
731
  {
@@ -833,7 +918,8 @@ var Containers = typedMemo(function Containers2({
833
918
  return /* @__PURE__ */ React3__namespace.createElement(ContainersInner, { horizontal, numColumns, waitForInitialLayout }, containers);
834
919
  });
835
920
  function DevNumbers() {
836
- return IS_DEV && React3__namespace.memo(function DevNumbers2() {
921
+ return IS_DEV && // biome-ignore lint/nursery/noShadow: const function name shadowing is intentional
922
+ React3__namespace.memo(function DevNumbers2() {
837
923
  return Array.from({ length: 100 }).map((_, index) => /* @__PURE__ */ React3__namespace.createElement(
838
924
  "div",
839
925
  {
@@ -881,7 +967,6 @@ var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView
881
967
  }, ref) {
882
968
  const scrollRef = React3.useRef(null);
883
969
  const contentRef = React3.useRef(null);
884
- const momentumTimeout = React3.useRef(null);
885
970
  React3.useImperativeHandle(ref, () => {
886
971
  const api = {
887
972
  getBoundingClientRect: () => {
@@ -947,16 +1032,6 @@ var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView
947
1032
  }
948
1033
  };
949
1034
  onScroll2(scrollEvent);
950
- if (onMomentumScrollEnd) {
951
- if (momentumTimeout.current != null) clearTimeout(momentumTimeout.current);
952
- momentumTimeout.current = setTimeout(() => {
953
- onMomentumScrollEnd({
954
- nativeEvent: {
955
- contentOffset: scrollEvent.nativeEvent.contentOffset
956
- }
957
- });
958
- }, 100);
959
- }
960
1035
  },
961
1036
  [onScroll2, onMomentumScrollEnd]
962
1037
  );
@@ -1018,7 +1093,8 @@ var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView
1018
1093
  minWidth: horizontal ? "100%" : void 0,
1019
1094
  ...StyleSheet.flatten(contentContainerStyle)
1020
1095
  };
1021
- return /* @__PURE__ */ React3__namespace.createElement("div", { ref: scrollRef, style: scrollViewStyle, ...props }, refreshControl, /* @__PURE__ */ React3__namespace.createElement("div", { ref: contentRef, style: contentStyle }, children));
1096
+ const { contentInset, scrollEventThrottle, ScrollComponent, ...webProps } = props;
1097
+ return /* @__PURE__ */ React3__namespace.createElement("div", { ref: scrollRef, ...webProps, style: scrollViewStyle }, refreshControl, /* @__PURE__ */ React3__namespace.createElement("div", { ref: contentRef, style: contentStyle }, children));
1022
1098
  });
1023
1099
  function Padding() {
1024
1100
  const [paddingTop] = useArr$(["alignItemsPaddingTop"]);
@@ -1059,7 +1135,7 @@ function ScrollAdjust() {
1059
1135
  const scrollAdjust = peek$(ctx, "scrollAdjust");
1060
1136
  const scrollAdjustUserOffset = peek$(ctx, "scrollAdjustUserOffset");
1061
1137
  const scrollOffset = (scrollAdjust || 0) + (scrollAdjustUserOffset || 0);
1062
- const scrollView = (_a3 = ctx.internalState) == null ? void 0 : _a3.refScroller.current;
1138
+ const scrollView = (_a3 = ctx.state) == null ? void 0 : _a3.refScroller.current;
1063
1139
  if (scrollView && scrollOffset !== lastScrollOffsetRef.current) {
1064
1140
  const scrollDelta = scrollOffset - lastScrollOffsetRef.current;
1065
1141
  if (scrollDelta !== 0) {
@@ -1067,26 +1143,23 @@ function ScrollAdjust() {
1067
1143
  const prevScroll = el.scrollTop;
1068
1144
  const nextScroll = prevScroll + scrollDelta;
1069
1145
  const totalSize = el.scrollHeight;
1070
- if (scrollDelta > 0 && !ctx.internalState.adjustingFromInitialMount && totalSize < nextScroll + el.clientHeight) {
1146
+ if (scrollDelta > 0 && !ctx.state.adjustingFromInitialMount && totalSize < nextScroll + el.clientHeight) {
1071
1147
  const child = el.firstElementChild;
1072
1148
  const prevPaddingBottom = child.style.paddingBottom;
1073
1149
  const pad = (nextScroll + el.clientHeight - totalSize) * 2;
1074
1150
  child.style.paddingBottom = `${pad}px`;
1075
1151
  void el.offsetHeight;
1076
1152
  scrollView.scrollBy(0, scrollDelta);
1077
- setTimeout(() => {
1153
+ requestAnimationFrame(() => {
1078
1154
  child.style.paddingBottom = prevPaddingBottom;
1079
- }, 100);
1155
+ });
1080
1156
  } else {
1081
1157
  scrollView.scrollBy(0, scrollDelta);
1082
1158
  }
1083
- if (IS_DEV) {
1084
- console.log("ScrollAdjust (web scrollBy)", scrollDelta, "total offset:", scrollOffset);
1085
- }
1086
1159
  }
1087
1160
  lastScrollOffsetRef.current = scrollOffset;
1088
1161
  }
1089
- }, []);
1162
+ }, [ctx]);
1090
1163
  useValueListener$("scrollAdjust", callback);
1091
1164
  useValueListener$("scrollAdjustUserOffset", callback);
1092
1165
  return null;
@@ -1144,13 +1217,6 @@ var ListComponent = typedMemo(function ListComponent2({
1144
1217
  () => React3__namespace.forwardRef((props, ref) => renderScrollComponent({ ...props, ref })),
1145
1218
  [renderScrollComponent]
1146
1219
  ) : ListComponentScrollView;
1147
- React3__namespace.useEffect(() => {
1148
- if (canRender) {
1149
- setTimeout(() => {
1150
- scrollAdjustHandler.setMounted();
1151
- }, 0);
1152
- }
1153
- }, [canRender]);
1154
1220
  const SnapOrScroll = snapToIndices ? SnapWrapper : ScrollComponent;
1155
1221
  return /* @__PURE__ */ React3__namespace.createElement(
1156
1222
  SnapOrScroll,
@@ -1214,10 +1280,11 @@ function getId(state, index) {
1214
1280
  }
1215
1281
 
1216
1282
  // src/core/calculateOffsetForIndex.ts
1217
- function calculateOffsetForIndex(ctx, state, index) {
1283
+ function calculateOffsetForIndex(ctx, index) {
1284
+ const state = ctx.state;
1218
1285
  let position = 0;
1219
1286
  if (index !== void 0) {
1220
- position = (state == null ? void 0 : state.positions.get(getId(state, index))) || 0;
1287
+ position = state.positions.get(getId(state, index)) || 0;
1221
1288
  const paddingTop = peek$(ctx, "stylePaddingTop");
1222
1289
  if (paddingTop) {
1223
1290
  position += paddingTop;
@@ -1231,7 +1298,8 @@ function calculateOffsetForIndex(ctx, state, index) {
1231
1298
  }
1232
1299
 
1233
1300
  // src/utils/setPaddingTop.ts
1234
- function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
1301
+ function setPaddingTop(ctx, { stylePaddingTop, alignItemsPaddingTop }) {
1302
+ const state = ctx.state;
1235
1303
  if (stylePaddingTop !== void 0) {
1236
1304
  const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
1237
1305
  if (stylePaddingTop < prevStylePaddingTop) {
@@ -1250,7 +1318,8 @@ function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
1250
1318
  }
1251
1319
 
1252
1320
  // src/utils/updateAlignItemsPaddingTop.ts
1253
- function updateAlignItemsPaddingTop(ctx, state) {
1321
+ function updateAlignItemsPaddingTop(ctx) {
1322
+ const state = ctx.state;
1254
1323
  const {
1255
1324
  scrollLength,
1256
1325
  props: { alignItemsAtEnd, data }
@@ -1261,12 +1330,13 @@ function updateAlignItemsPaddingTop(ctx, state) {
1261
1330
  const contentSize = getContentSize(ctx);
1262
1331
  alignItemsPaddingTop = Math.max(0, Math.floor(scrollLength - contentSize));
1263
1332
  }
1264
- setPaddingTop(ctx, state, { alignItemsPaddingTop });
1333
+ setPaddingTop(ctx, { alignItemsPaddingTop });
1265
1334
  }
1266
1335
  }
1267
1336
 
1268
1337
  // src/core/addTotalSize.ts
1269
- function addTotalSize(ctx, state, key, add) {
1338
+ function addTotalSize(ctx, key, add) {
1339
+ const state = ctx.state;
1270
1340
  const { alignItemsAtEnd } = state.props;
1271
1341
  const prevTotalSize = state.totalSize;
1272
1342
  let totalSize = state.totalSize;
@@ -1285,31 +1355,34 @@ function addTotalSize(ctx, state, key, add) {
1285
1355
  state.totalSize = totalSize;
1286
1356
  set$(ctx, "totalSize", totalSize);
1287
1357
  if (alignItemsAtEnd) {
1288
- updateAlignItemsPaddingTop(ctx, state);
1358
+ updateAlignItemsPaddingTop(ctx);
1289
1359
  }
1290
1360
  }
1291
1361
  }
1292
1362
  }
1293
1363
 
1294
1364
  // src/core/setSize.ts
1295
- function setSize(ctx, state, itemKey, size) {
1365
+ function setSize(ctx, itemKey, size) {
1366
+ const state = ctx.state;
1296
1367
  const { sizes } = state;
1297
1368
  const previousSize = sizes.get(itemKey);
1298
1369
  const diff = previousSize !== void 0 ? size - previousSize : size;
1299
1370
  if (diff !== 0) {
1300
- addTotalSize(ctx, state, itemKey, diff);
1371
+ addTotalSize(ctx, itemKey, diff);
1301
1372
  }
1302
1373
  sizes.set(itemKey, size);
1303
1374
  }
1304
1375
 
1305
1376
  // src/utils/getItemSize.ts
1306
- function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedSize) {
1377
+ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
1307
1378
  var _a3, _b;
1379
+ const state = ctx.state;
1308
1380
  const {
1309
1381
  sizesKnown,
1310
1382
  sizes,
1311
1383
  averageSizes,
1312
- props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType }
1384
+ props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType },
1385
+ scrollingTo
1313
1386
  } = state;
1314
1387
  const sizeKnown = sizesKnown.get(key);
1315
1388
  if (sizeKnown !== void 0) {
@@ -1317,7 +1390,6 @@ function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedS
1317
1390
  }
1318
1391
  let size;
1319
1392
  const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1320
- const scrollingTo = peek$(ctx, "scrollingTo");
1321
1393
  if (preferCachedSize) {
1322
1394
  const cachedSize = sizes.get(key);
1323
1395
  if (cachedSize !== void 0) {
@@ -1345,81 +1417,169 @@ function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedS
1345
1417
  if (size === void 0) {
1346
1418
  size = getEstimatedItemSize ? getEstimatedItemSize(index, data, itemType) : estimatedItemSize;
1347
1419
  }
1348
- setSize(ctx, state, key, size);
1420
+ setSize(ctx, key, size);
1349
1421
  return size;
1350
1422
  }
1351
1423
 
1352
1424
  // src/core/calculateOffsetWithOffsetPosition.ts
1353
- function calculateOffsetWithOffsetPosition(ctx, state, offsetParam, params) {
1425
+ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1426
+ const state = ctx.state;
1354
1427
  const { index, viewOffset, viewPosition } = params;
1355
1428
  let offset = offsetParam;
1356
1429
  if (viewOffset) {
1357
1430
  offset -= viewOffset;
1358
1431
  }
1359
1432
  if (viewPosition !== void 0 && index !== void 0) {
1360
- offset -= viewPosition * (state.scrollLength - getItemSize(ctx, state, getId(state, index), index, state.props.data[index]));
1433
+ const itemSize = getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1434
+ const trailingInset = getContentInsetEnd(state);
1435
+ offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1361
1436
  }
1362
1437
  return offset;
1363
1438
  }
1364
1439
 
1440
+ // src/core/clampScrollOffset.ts
1441
+ function clampScrollOffset(ctx, offset) {
1442
+ const state = ctx.state;
1443
+ const contentSize = getContentSize(ctx);
1444
+ let clampedOffset = offset;
1445
+ if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength)) {
1446
+ const maxOffset = Math.max(0, contentSize - state.scrollLength);
1447
+ clampedOffset = Math.min(offset, maxOffset);
1448
+ }
1449
+ clampedOffset = Math.max(0, clampedOffset);
1450
+ return clampedOffset;
1451
+ }
1452
+
1453
+ // src/utils/setInitialRenderState.ts
1454
+ function setInitialRenderState(ctx, {
1455
+ didLayout,
1456
+ didInitialScroll
1457
+ }) {
1458
+ const { state } = ctx;
1459
+ if (didLayout) {
1460
+ state.didContainersLayout = true;
1461
+ }
1462
+ if (didInitialScroll) {
1463
+ state.didFinishInitialScroll = true;
1464
+ }
1465
+ if (state.didContainersLayout && state.didFinishInitialScroll) {
1466
+ set$(ctx, "readyToRender", true);
1467
+ }
1468
+ }
1469
+
1365
1470
  // src/core/finishScrollTo.ts
1366
- function finishScrollTo(ctx, state) {
1471
+ function finishScrollTo(ctx) {
1367
1472
  var _a3, _b;
1368
- if (state) {
1473
+ const state = ctx.state;
1474
+ if (state == null ? void 0 : state.scrollingTo) {
1369
1475
  state.scrollHistory.length = 0;
1370
1476
  state.initialScroll = void 0;
1371
1477
  state.initialAnchor = void 0;
1372
- set$(ctx, "scrollingTo", void 0);
1478
+ state.scrollingTo = void 0;
1373
1479
  if (state.pendingTotalSize !== void 0) {
1374
- addTotalSize(ctx, state, null, state.pendingTotalSize);
1480
+ addTotalSize(ctx, null, state.pendingTotalSize);
1375
1481
  }
1376
1482
  if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1377
1483
  (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1378
1484
  }
1485
+ {
1486
+ state.scrollAdjustHandler.commitPendingAdjust();
1487
+ }
1488
+ setInitialRenderState(ctx, { didInitialScroll: true });
1489
+ }
1490
+ }
1491
+
1492
+ // src/core/doScrollTo.ts
1493
+ var SCROLL_END_IDLE_MS = 80;
1494
+ var SCROLL_END_MAX_MS = 1500;
1495
+ var SMOOTH_SCROLL_DURATION_MS = 320;
1496
+ function doScrollTo(ctx, params) {
1497
+ const state = ctx.state;
1498
+ const { animated, horizontal, offset } = params;
1499
+ const scroller = state.refScroller.current;
1500
+ const node = typeof (scroller == null ? void 0 : scroller.getScrollableNode) === "function" ? scroller.getScrollableNode() : scroller;
1501
+ if (node) {
1502
+ const left = horizontal ? offset : 0;
1503
+ const top = horizontal ? 0 : offset;
1504
+ node.scrollTo({ behavior: animated ? "smooth" : "auto", left, top });
1505
+ if (animated) {
1506
+ listenForScrollEnd(ctx, node);
1507
+ } else {
1508
+ state.scroll = offset;
1509
+ setTimeout(() => {
1510
+ finishScrollTo(ctx);
1511
+ }, 100);
1512
+ }
1513
+ }
1514
+ }
1515
+ function listenForScrollEnd(ctx, node) {
1516
+ const supportsScrollEnd = "onscrollend" in node;
1517
+ let idleTimeout;
1518
+ let maxTimeout;
1519
+ let settled = false;
1520
+ const targetToken = ctx.state.scrollingTo;
1521
+ const finish = () => {
1522
+ if (settled) return;
1523
+ settled = true;
1524
+ cleanup();
1525
+ if (targetToken === ctx.state.scrollingTo) {
1526
+ finishScrollTo(ctx);
1527
+ }
1528
+ };
1529
+ const onScroll2 = () => {
1530
+ if (idleTimeout) {
1531
+ clearTimeout(idleTimeout);
1532
+ }
1533
+ idleTimeout = setTimeout(finish, SCROLL_END_IDLE_MS);
1534
+ };
1535
+ const cleanup = () => {
1536
+ if (supportsScrollEnd) {
1537
+ node.removeEventListener("scrollend", finish);
1538
+ } else {
1539
+ node.removeEventListener("scroll", onScroll2);
1540
+ }
1541
+ if (idleTimeout) {
1542
+ clearTimeout(idleTimeout);
1543
+ }
1544
+ if (maxTimeout) {
1545
+ clearTimeout(maxTimeout);
1546
+ }
1547
+ };
1548
+ if (supportsScrollEnd) {
1549
+ node.addEventListener("scrollend", finish, { once: true });
1550
+ } else {
1551
+ node.addEventListener("scroll", onScroll2);
1552
+ idleTimeout = setTimeout(finish, SMOOTH_SCROLL_DURATION_MS);
1553
+ maxTimeout = setTimeout(finish, SCROLL_END_MAX_MS);
1379
1554
  }
1555
+ return cleanup;
1380
1556
  }
1381
1557
 
1382
1558
  // src/core/scrollTo.ts
1383
- function scrollTo(ctx, state, params) {
1384
- var _a3;
1385
- const { noScrollingTo, ...scrollTarget } = params;
1559
+ function scrollTo(ctx, params) {
1560
+ const state = ctx.state;
1561
+ const { noScrollingTo, forceScroll, ...scrollTarget } = params;
1386
1562
  const { animated, isInitialScroll, offset: scrollTargetOffset, precomputedWithViewOffset } = scrollTarget;
1387
1563
  const {
1388
- refScroller,
1389
1564
  props: { horizontal }
1390
1565
  } = state;
1391
- let offset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, state, scrollTargetOffset, scrollTarget);
1392
- if (Number.isFinite(state.scrollLength) && Number.isFinite(state.totalSize)) {
1393
- const maxOffset = Math.max(0, getContentSize(ctx) - state.scrollLength);
1394
- offset = Math.min(offset, maxOffset);
1566
+ if (state.animFrameCheckFinishedScroll) {
1567
+ cancelAnimationFrame(ctx.state.animFrameCheckFinishedScroll);
1568
+ }
1569
+ if (state.timeoutCheckFinishedScrollFallback) {
1570
+ clearTimeout(ctx.state.timeoutCheckFinishedScrollFallback);
1395
1571
  }
1572
+ let offset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, scrollTargetOffset, scrollTarget);
1573
+ offset = clampScrollOffset(ctx, offset);
1396
1574
  state.scrollHistory.length = 0;
1397
1575
  if (!noScrollingTo) {
1398
- set$(ctx, "scrollingTo", scrollTarget);
1576
+ state.scrollingTo = scrollTarget;
1399
1577
  }
1400
1578
  state.scrollPending = offset;
1401
- if (!isInitialScroll || Platform.OS === "android") {
1402
- (_a3 = refScroller.current) == null ? void 0 : _a3.scrollTo({
1403
- animated: !!animated,
1404
- x: horizontal ? offset : 0,
1405
- y: horizontal ? 0 : offset
1406
- });
1407
- }
1408
- if (!animated) {
1579
+ if (forceScroll || !isInitialScroll || Platform.OS === "android") {
1580
+ doScrollTo(ctx, { animated, horizontal, isInitialScroll, offset });
1581
+ } else {
1409
1582
  state.scroll = offset;
1410
- {
1411
- const unlisten = listen$(ctx, "containersDidLayout", (value) => {
1412
- if (value && peek$(ctx, "scrollingTo")) {
1413
- finishScrollTo(ctx, state);
1414
- unlisten();
1415
- }
1416
- });
1417
- }
1418
- if (isInitialScroll) {
1419
- setTimeout(() => {
1420
- state.initialScroll = void 0;
1421
- }, 500);
1422
- }
1423
1583
  }
1424
1584
  }
1425
1585
 
@@ -1428,6 +1588,12 @@ var HYSTERESIS_MULTIPLIER = 1.3;
1428
1588
  var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot) => {
1429
1589
  const absDistance = Math.abs(distance);
1430
1590
  const within = atThreshold || threshold > 0 && absDistance <= threshold;
1591
+ if (wasReached === null) {
1592
+ if (!within && distance >= 0) {
1593
+ return false;
1594
+ }
1595
+ return null;
1596
+ }
1431
1597
  const updateSnapshot = () => {
1432
1598
  setSnapshot == null ? void 0 : setSnapshot({
1433
1599
  atThreshold,
@@ -1460,8 +1626,9 @@ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, co
1460
1626
  };
1461
1627
 
1462
1628
  // src/utils/checkAtBottom.ts
1463
- function checkAtBottom(ctx, state) {
1629
+ function checkAtBottom(ctx) {
1464
1630
  var _a3;
1631
+ const state = ctx.state;
1465
1632
  if (!state) {
1466
1633
  return;
1467
1634
  }
@@ -1534,15 +1701,15 @@ function checkAtTop(state) {
1534
1701
  }
1535
1702
 
1536
1703
  // src/core/updateScroll.ts
1537
- function updateScroll(ctx, state, newScroll, forceUpdate) {
1704
+ function updateScroll(ctx, newScroll, forceUpdate) {
1538
1705
  var _a3;
1539
- const scrollingTo = peek$(ctx, "scrollingTo");
1706
+ const state = ctx.state;
1707
+ const { scrollingTo, scrollAdjustHandler, lastScrollAdjustForHistory } = state;
1540
1708
  state.hasScrolled = true;
1541
1709
  state.lastBatchingAction = Date.now();
1542
1710
  const currentTime = Date.now();
1543
- const adjust = state.scrollAdjustHandler.getAdjust();
1544
- const lastHistoryAdjust = state.lastScrollAdjustForHistory;
1545
- const adjustChanged = lastHistoryAdjust !== void 0 && Math.abs(adjust - lastHistoryAdjust) > 0.1;
1711
+ const adjust = scrollAdjustHandler.getAdjust();
1712
+ const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
1546
1713
  if (adjustChanged) {
1547
1714
  state.scrollHistory.length = 0;
1548
1715
  }
@@ -1567,17 +1734,21 @@ function updateScroll(ctx, state, newScroll, forceUpdate) {
1567
1734
  return;
1568
1735
  }
1569
1736
  }
1570
- if (state.dataChangeNeedsScrollUpdate || Math.abs(state.scroll - state.scrollPrev) > 2) {
1737
+ const lastCalculated = state.scrollLastCalculate;
1738
+ const shouldUpdate = state.dataChangeNeedsScrollUpdate || state.scrollLastCalculate === void 0 || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
1739
+ if (shouldUpdate) {
1740
+ state.scrollLastCalculate = state.scroll;
1571
1741
  state.ignoreScrollFromMVCPIgnored = false;
1572
1742
  (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { doMVCP: scrollingTo !== void 0 });
1573
- checkAtBottom(ctx, state);
1743
+ checkAtBottom(ctx);
1574
1744
  checkAtTop(state);
1575
1745
  state.dataChangeNeedsScrollUpdate = false;
1576
1746
  }
1577
1747
  }
1578
1748
 
1579
1749
  // src/utils/requestAdjust.ts
1580
- function requestAdjust(ctx, state, positionDiff, dataChanged) {
1750
+ function requestAdjust(ctx, positionDiff, dataChanged) {
1751
+ const state = ctx.state;
1581
1752
  if (Math.abs(positionDiff) > 0.1) {
1582
1753
  const doit = () => {
1583
1754
  {
@@ -1589,8 +1760,8 @@ function requestAdjust(ctx, state, positionDiff, dataChanged) {
1589
1760
  };
1590
1761
  state.scroll += positionDiff;
1591
1762
  state.scrollForNextCalculateItemsInView = void 0;
1592
- const didLayout = peek$(ctx, "containersDidLayout");
1593
- if (didLayout) {
1763
+ const readyToRender = peek$(ctx, "readyToRender");
1764
+ if (readyToRender) {
1594
1765
  doit();
1595
1766
  } else {
1596
1767
  state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
@@ -1599,73 +1770,23 @@ function requestAdjust(ctx, state, positionDiff, dataChanged) {
1599
1770
  }
1600
1771
  }
1601
1772
 
1602
- // src/core/ensureInitialAnchor.ts
1603
- var INITIAL_ANCHOR_TOLERANCE = 0.5;
1604
- var INITIAL_ANCHOR_MAX_ATTEMPTS = 4;
1605
- var INITIAL_ANCHOR_SETTLED_TICKS = 2;
1606
- function ensureInitialAnchor(ctx, state) {
1607
- var _a3, _b, _c, _d, _e;
1608
- const anchor = state.initialAnchor;
1609
- const item = state.props.data[anchor.index];
1610
- const containersDidLayout = peek$(ctx, "containersDidLayout");
1611
- if (!containersDidLayout) {
1612
- return;
1613
- }
1614
- const id = getId(state, anchor.index);
1615
- if (state.positions.get(id) === void 0) {
1616
- return;
1617
- }
1618
- const size = getItemSize(ctx, state, id, anchor.index, item, true, true);
1619
- if (size === void 0) {
1620
- return;
1621
- }
1622
- const availableSpace = Math.max(0, state.scrollLength - size);
1623
- const desiredOffset = calculateOffsetForIndex(ctx, state, anchor.index) - ((_a3 = anchor.viewOffset) != null ? _a3 : 0) - ((_b = anchor.viewPosition) != null ? _b : 0) * availableSpace;
1624
- const contentSize = getContentSize(ctx);
1625
- const maxOffset = Math.max(0, contentSize - state.scrollLength);
1626
- const clampedDesiredOffset = Math.max(0, Math.min(desiredOffset, maxOffset));
1627
- const delta = clampedDesiredOffset - state.scroll;
1628
- if (Math.abs(delta) <= INITIAL_ANCHOR_TOLERANCE) {
1629
- const settledTicks = ((_c = anchor.settledTicks) != null ? _c : 0) + 1;
1630
- if (settledTicks >= INITIAL_ANCHOR_SETTLED_TICKS) {
1631
- state.initialAnchor = void 0;
1632
- } else {
1633
- anchor.settledTicks = settledTicks;
1634
- }
1635
- return;
1636
- }
1637
- if (((_d = anchor.attempts) != null ? _d : 0) >= INITIAL_ANCHOR_MAX_ATTEMPTS) {
1638
- state.initialAnchor = void 0;
1639
- return;
1640
- }
1641
- const lastDelta = anchor.lastDelta;
1642
- if (lastDelta !== void 0 && Math.abs(delta) >= Math.abs(lastDelta)) {
1643
- state.initialAnchor = void 0;
1644
- return;
1645
- }
1646
- Object.assign(anchor, {
1647
- attempts: ((_e = anchor.attempts) != null ? _e : 0) + 1,
1648
- lastDelta: delta,
1649
- settledTicks: 0
1650
- });
1651
- requestAdjust(ctx, state, delta);
1652
- }
1653
-
1654
1773
  // src/core/mvcp.ts
1655
- function prepareMVCP(ctx, state, dataChanged) {
1774
+ function prepareMVCP(ctx, dataChanged) {
1775
+ const state = ctx.state;
1656
1776
  const { idsInView, positions, props } = state;
1657
1777
  const { maintainVisibleContentPosition } = props;
1658
- const scrollingTo = peek$(ctx, "scrollingTo");
1778
+ const scrollingTo = state.scrollingTo;
1659
1779
  let prevPosition;
1660
1780
  let targetId;
1661
1781
  const idsInViewWithPositions = [];
1662
1782
  const scrollTarget = scrollingTo == null ? void 0 : scrollingTo.index;
1783
+ const scrollingToViewPosition = scrollingTo == null ? void 0 : scrollingTo.viewPosition;
1663
1784
  const shouldMVCP = !dataChanged || maintainVisibleContentPosition;
1664
1785
  const indexByKey = state.indexByKey;
1665
1786
  if (shouldMVCP) {
1666
1787
  if (scrollTarget !== void 0) {
1667
1788
  targetId = getId(state, scrollTarget);
1668
- } else if (idsInView.length > 0 && peek$(ctx, "containersDidLayout")) {
1789
+ } else if (idsInView.length > 0 && state.didContainersLayout) {
1669
1790
  if (dataChanged) {
1670
1791
  for (let i = 0; i < idsInView.length; i++) {
1671
1792
  const id = idsInView[i];
@@ -1682,7 +1803,7 @@ function prepareMVCP(ctx, state, dataChanged) {
1682
1803
  prevPosition = positions.get(targetId);
1683
1804
  }
1684
1805
  return () => {
1685
- let positionDiff;
1806
+ let positionDiff = 0;
1686
1807
  if (dataChanged && targetId === void 0 && maintainVisibleContentPosition) {
1687
1808
  for (let i = 0; i < idsInViewWithPositions.length; i++) {
1688
1809
  const { id, position } = idsInViewWithPositions[i];
@@ -1708,16 +1829,28 @@ function prepareMVCP(ctx, state, dataChanged) {
1708
1829
  positionDiff = diff;
1709
1830
  }
1710
1831
  }
1711
- if (positionDiff !== void 0 && Math.abs(positionDiff) > 0.1) {
1712
- requestAdjust(ctx, state, positionDiff);
1832
+ if (scrollingToViewPosition && scrollingToViewPosition > 0) {
1833
+ const newSize = getItemSize(ctx, targetId, scrollTarget, state.props.data[scrollTarget]);
1834
+ const prevSize = scrollingTo == null ? void 0 : scrollingTo.itemSize;
1835
+ if (newSize !== void 0 && prevSize !== void 0 && newSize !== (scrollingTo == null ? void 0 : scrollingTo.itemSize)) {
1836
+ const diff = newSize - prevSize;
1837
+ if (diff !== 0) {
1838
+ positionDiff += (newSize - prevSize) * scrollingToViewPosition;
1839
+ scrollingTo.itemSize = newSize;
1840
+ }
1841
+ }
1842
+ }
1843
+ if (Math.abs(positionDiff) > 0.1) {
1844
+ requestAdjust(ctx, positionDiff);
1713
1845
  }
1714
1846
  };
1715
1847
  }
1716
1848
  }
1717
1849
 
1718
1850
  // src/core/prepareColumnStartState.ts
1719
- function prepareColumnStartState(ctx, state, startIndex, useAverageSize) {
1851
+ function prepareColumnStartState(ctx, startIndex, useAverageSize) {
1720
1852
  var _a3;
1853
+ const state = ctx.state;
1721
1854
  const numColumns = peek$(ctx, "numColumns");
1722
1855
  let rowStartIndex = startIndex;
1723
1856
  const columnAtStart = state.columns.get(state.idCache[startIndex]);
@@ -1732,7 +1865,7 @@ function prepareColumnStartState(ctx, state, startIndex, useAverageSize) {
1732
1865
  const prevId = state.idCache[prevIndex];
1733
1866
  const prevPosition = (_a3 = state.positions.get(prevId)) != null ? _a3 : 0;
1734
1867
  const prevRowStart = findRowStartIndex(state, numColumns, prevIndex);
1735
- const prevRowHeight = calculateRowMaxSize(ctx, state, prevRowStart, prevIndex, useAverageSize);
1868
+ const prevRowHeight = calculateRowMaxSize(ctx, prevRowStart, prevIndex, useAverageSize);
1736
1869
  currentRowTop = prevPosition + prevRowHeight;
1737
1870
  }
1738
1871
  return {
@@ -1755,7 +1888,8 @@ function findRowStartIndex(state, numColumns, index) {
1755
1888
  }
1756
1889
  return rowStart;
1757
1890
  }
1758
- function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1891
+ function calculateRowMaxSize(ctx, startIndex, endIndex, useAverageSize) {
1892
+ const state = ctx.state;
1759
1893
  if (endIndex < startIndex) {
1760
1894
  return 0;
1761
1895
  }
@@ -1769,7 +1903,7 @@ function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1769
1903
  continue;
1770
1904
  }
1771
1905
  const id = state.idCache[i];
1772
- const size = getItemSize(ctx, state, id, i, data[i], useAverageSize);
1906
+ const size = getItemSize(ctx, id, i, data[i], useAverageSize);
1773
1907
  if (size > maxSize) {
1774
1908
  maxSize = size;
1775
1909
  }
@@ -1778,22 +1912,23 @@ function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1778
1912
  }
1779
1913
 
1780
1914
  // src/core/updateTotalSize.ts
1781
- function updateTotalSize(ctx, state) {
1915
+ function updateTotalSize(ctx) {
1916
+ const state = ctx.state;
1782
1917
  const {
1783
1918
  positions,
1784
1919
  props: { data }
1785
1920
  } = state;
1786
1921
  if (data.length === 0) {
1787
- addTotalSize(ctx, state, null, 0);
1922
+ addTotalSize(ctx, null, 0);
1788
1923
  } else {
1789
1924
  const lastId = getId(state, data.length - 1);
1790
1925
  if (lastId !== void 0) {
1791
1926
  const lastPosition = positions.get(lastId);
1792
1927
  if (lastPosition !== void 0) {
1793
- const lastSize = getItemSize(ctx, state, lastId, data.length - 1, data[data.length - 1]);
1928
+ const lastSize = getItemSize(ctx, lastId, data.length - 1, data[data.length - 1]);
1794
1929
  if (lastSize !== void 0) {
1795
1930
  const totalSize = lastPosition + lastSize;
1796
- addTotalSize(ctx, state, null, totalSize);
1931
+ addTotalSize(ctx, null, totalSize);
1797
1932
  }
1798
1933
  }
1799
1934
  }
@@ -1839,7 +1974,8 @@ var getScrollVelocity = (state) => {
1839
1974
  };
1840
1975
 
1841
1976
  // src/utils/updateSnapToOffsets.ts
1842
- function updateSnapToOffsets(ctx, state) {
1977
+ function updateSnapToOffsets(ctx) {
1978
+ const state = ctx.state;
1843
1979
  const {
1844
1980
  positions,
1845
1981
  props: { snapToIndices }
@@ -1854,30 +1990,30 @@ function updateSnapToOffsets(ctx, state) {
1854
1990
  }
1855
1991
 
1856
1992
  // src/core/updateItemPositions.ts
1857
- function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP } = {
1993
+ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP } = {
1858
1994
  doMVCP: false,
1859
1995
  forceFullUpdate: false,
1860
1996
  scrollBottomBuffered: -1,
1861
1997
  startIndex: 0
1862
1998
  }) {
1863
1999
  var _a3, _b, _c, _d, _e;
2000
+ const state = ctx.state;
1864
2001
  const {
1865
2002
  columns,
1866
2003
  indexByKey,
1867
2004
  positions,
1868
2005
  idCache,
1869
2006
  sizesKnown,
1870
- props: { getEstimatedItemSize, snapToIndices, enableAverages }
2007
+ props: { data, getEstimatedItemSize, snapToIndices },
2008
+ scrollingTo
1871
2009
  } = state;
1872
- const data = state.props.data;
1873
2010
  const dataLength = data.length;
1874
2011
  const numColumns = peek$(ctx, "numColumns");
1875
- const scrollingTo = peek$(ctx, "scrollingTo");
1876
2012
  const hasColumns = numColumns > 1;
1877
2013
  const indexByKeyForChecking = IS_DEV ? /* @__PURE__ */ new Map() : void 0;
1878
2014
  const shouldOptimize = !forceFullUpdate && !dataChanged && Math.abs(getScrollVelocity(state)) > 0;
1879
2015
  const maxVisibleArea = scrollBottomBuffered + 1e3;
1880
- const useAverageSize = enableAverages && !getEstimatedItemSize;
2016
+ const useAverageSize = !getEstimatedItemSize;
1881
2017
  const preferCachedSize = !doMVCP || dataChanged || state.scrollAdjustHandler.getAdjust() !== 0 || ((_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0) !== 0;
1882
2018
  let currentRowTop = 0;
1883
2019
  let column = 1;
@@ -1886,7 +2022,6 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1886
2022
  if (hasColumns) {
1887
2023
  const { startIndex: processedStartIndex, currentRowTop: initialRowTop } = prepareColumnStartState(
1888
2024
  ctx,
1889
- state,
1890
2025
  startIndex,
1891
2026
  useAverageSize
1892
2027
  );
@@ -1896,7 +2031,7 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1896
2031
  const prevIndex = startIndex - 1;
1897
2032
  const prevId = getId(state, prevIndex);
1898
2033
  const prevPosition = (_b = positions.get(prevId)) != null ? _b : 0;
1899
- const prevSize = (_c = sizesKnown.get(prevId)) != null ? _c : getItemSize(ctx, state, prevId, prevIndex, data[prevIndex], useAverageSize, preferCachedSize);
2034
+ const prevSize = (_c = sizesKnown.get(prevId)) != null ? _c : getItemSize(ctx, prevId, prevIndex, data[prevIndex], useAverageSize, preferCachedSize);
1900
2035
  currentRowTop = prevPosition + prevSize;
1901
2036
  }
1902
2037
  }
@@ -1913,7 +2048,7 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1913
2048
  breakAt = i + itemsPerRow + 10;
1914
2049
  }
1915
2050
  const id = (_d = idCache[i]) != null ? _d : getId(state, i);
1916
- const size = (_e = sizesKnown.get(id)) != null ? _e : getItemSize(ctx, state, id, i, data[i], useAverageSize, preferCachedSize);
2051
+ const size = (_e = sizesKnown.get(id)) != null ? _e : getItemSize(ctx, id, i, data[i], useAverageSize, preferCachedSize);
1917
2052
  if (IS_DEV && needsIndexByKey) {
1918
2053
  if (indexByKeyForChecking.has(id)) {
1919
2054
  console.error(
@@ -1922,7 +2057,10 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1922
2057
  }
1923
2058
  indexByKeyForChecking.set(id, i);
1924
2059
  }
1925
- positions.set(id, currentRowTop);
2060
+ if (currentRowTop !== positions.get(id)) {
2061
+ positions.set(id, currentRowTop);
2062
+ notifyPosition$(ctx, id, currentRowTop);
2063
+ }
1926
2064
  if (needsIndexByKey) {
1927
2065
  indexByKey.set(id, i);
1928
2066
  }
@@ -1942,10 +2080,10 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1942
2080
  }
1943
2081
  }
1944
2082
  if (!didBreakEarly) {
1945
- updateTotalSize(ctx, state);
2083
+ updateTotalSize(ctx);
1946
2084
  }
1947
2085
  if (snapToIndices) {
1948
- updateSnapToOffsets(ctx, state);
2086
+ updateSnapToOffsets(ctx);
1949
2087
  }
1950
2088
  }
1951
2089
 
@@ -2023,7 +2161,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
2023
2161
  if (previousViewableItems) {
2024
2162
  for (const viewToken of previousViewableItems) {
2025
2163
  const containerId = findContainerId(ctx, viewToken.key);
2026
- if (!isViewable(
2164
+ if (!checkIsViewable(
2027
2165
  state,
2028
2166
  ctx,
2029
2167
  viewabilityConfig,
@@ -2044,7 +2182,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
2044
2182
  if (item) {
2045
2183
  const key = getId(state, i);
2046
2184
  const containerId = findContainerId(ctx, key);
2047
- if (isViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, i)) {
2185
+ if (checkIsViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, i)) {
2048
2186
  const viewToken = {
2049
2187
  containerId,
2050
2188
  index: i,
@@ -2104,11 +2242,11 @@ function computeViewability(state, ctx, viewabilityConfig, containerId, key, scr
2104
2242
  const percentVisible = size ? isEntirelyVisible ? 100 : 100 * (sizeVisible / size) : 0;
2105
2243
  const percentOfScroller = size ? 100 * (sizeVisible / scrollSize) : 0;
2106
2244
  const percent = isEntirelyVisible ? 100 : viewAreaMode ? percentOfScroller : percentVisible;
2107
- const isViewable2 = percent >= viewablePercentThreshold;
2245
+ const isViewable = percent >= viewablePercentThreshold;
2108
2246
  const value = {
2109
2247
  containerId,
2110
2248
  index,
2111
- isViewable: isViewable2,
2249
+ isViewable,
2112
2250
  item,
2113
2251
  key,
2114
2252
  percentOfScroller,
@@ -2127,8 +2265,11 @@ function computeViewability(state, ctx, viewabilityConfig, containerId, key, scr
2127
2265
  }
2128
2266
  return value;
2129
2267
  }
2130
- function isViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
2131
- const value = ctx.mapViewabilityAmountValues.get(containerId) || computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index);
2268
+ function checkIsViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
2269
+ let value = ctx.mapViewabilityAmountValues.get(containerId);
2270
+ if (!value || value.key !== key) {
2271
+ value = computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index);
2272
+ }
2132
2273
  return value.isViewable;
2133
2274
  }
2134
2275
  function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
@@ -2156,8 +2297,9 @@ function checkAllSizesKnown(state) {
2156
2297
  }
2157
2298
 
2158
2299
  // src/utils/findAvailableContainers.ts
2159
- function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers) {
2300
+ function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers) {
2160
2301
  const numContainers = peek$(ctx, "numContainers");
2302
+ const state = ctx.state;
2161
2303
  const { stickyContainerPool, containerItemTypes } = state;
2162
2304
  const result = [];
2163
2305
  const availableContainers = [];
@@ -2201,14 +2343,14 @@ function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffer
2201
2343
  continue;
2202
2344
  }
2203
2345
  const key = peek$(ctx, `containerItemKey${u}`);
2204
- let isOk = key === void 0;
2205
- if (!isOk && pendingRemovalSet.has(u)) {
2206
- pendingRemovalSet.delete(u);
2207
- pendingRemovalChanged = true;
2208
- const requiredType = neededTypes[typeIndex];
2209
- isOk = canReuseContainer(u, requiredType);
2210
- }
2211
- if (isOk) {
2346
+ const requiredType = neededTypes[typeIndex];
2347
+ const isPending = key !== void 0 && pendingRemovalSet.has(u);
2348
+ const canUse = key === void 0 || isPending && canReuseContainer(u, requiredType);
2349
+ if (canUse) {
2350
+ if (isPending) {
2351
+ pendingRemovalSet.delete(u);
2352
+ pendingRemovalChanged = true;
2353
+ }
2212
2354
  result.push(u);
2213
2355
  if (requiredItemTypes) {
2214
2356
  typeIndex++;
@@ -2277,21 +2419,26 @@ function comparatorByDistance(a, b) {
2277
2419
  }
2278
2420
 
2279
2421
  // src/core/scrollToIndex.ts
2280
- function scrollToIndex(ctx, state, { index, viewOffset = 0, animated = true, viewPosition }) {
2281
- if (index >= state.props.data.length) {
2282
- index = state.props.data.length - 1;
2422
+ function scrollToIndex(ctx, { index, viewOffset = 0, animated = true, viewPosition }) {
2423
+ const state = ctx.state;
2424
+ const { data } = state.props;
2425
+ if (index >= data.length) {
2426
+ index = data.length - 1;
2283
2427
  } else if (index < 0) {
2284
2428
  index = 0;
2285
2429
  }
2286
- const firstIndexOffset = calculateOffsetForIndex(ctx, state, index);
2287
- const isLast = index === state.props.data.length - 1;
2430
+ const firstIndexOffset = calculateOffsetForIndex(ctx, index);
2431
+ const isLast = index === data.length - 1;
2288
2432
  if (isLast && viewPosition === void 0) {
2289
2433
  viewPosition = 1;
2290
2434
  }
2291
2435
  state.scrollForNextCalculateItemsInView = void 0;
2292
- scrollTo(ctx, state, {
2436
+ const targetId = getId(state, index);
2437
+ const itemSize = getItemSize(ctx, targetId, index, state.props.data[index]);
2438
+ scrollTo(ctx, {
2293
2439
  animated,
2294
2440
  index,
2441
+ itemSize,
2295
2442
  offset: firstIndexOffset,
2296
2443
  viewOffset,
2297
2444
  viewPosition: viewPosition != null ? viewPosition : 0
@@ -2299,16 +2446,17 @@ function scrollToIndex(ctx, state, { index, viewOffset = 0, animated = true, vie
2299
2446
  }
2300
2447
 
2301
2448
  // src/utils/setDidLayout.ts
2302
- function setDidLayout(ctx, state) {
2449
+ function setDidLayout(ctx) {
2450
+ const state = ctx.state;
2303
2451
  const {
2304
2452
  loadStartTime,
2305
2453
  initialScroll,
2306
2454
  props: { onLoad }
2307
2455
  } = state;
2308
2456
  state.queuedInitialLayout = true;
2309
- checkAtBottom(ctx, state);
2457
+ checkAtBottom(ctx);
2310
2458
  const setIt = () => {
2311
- set$(ctx, "containersDidLayout", true);
2459
+ setInitialRenderState(ctx, { didLayout: true });
2312
2460
  if (onLoad) {
2313
2461
  onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
2314
2462
  }
@@ -2333,15 +2481,17 @@ function findCurrentStickyIndex(stickyArray, scroll, state) {
2333
2481
  }
2334
2482
  return -1;
2335
2483
  }
2336
- function getActiveStickyIndices(ctx, state, stickyHeaderIndices) {
2484
+ function getActiveStickyIndices(ctx, stickyHeaderIndices) {
2485
+ const state = ctx.state;
2337
2486
  return new Set(
2338
2487
  Array.from(state.stickyContainerPool).map((i) => peek$(ctx, `containerItemKey${i}`)).map((key) => key ? state.indexByKey.get(key) : void 0).filter((idx) => idx !== void 0 && stickyHeaderIndices.has(idx))
2339
2488
  );
2340
2489
  }
2341
- function handleStickyActivation(ctx, state, stickyHeaderIndices, stickyArray, currentStickyIdx, needNewContainers, startBuffered, endBuffered) {
2490
+ function handleStickyActivation(ctx, stickyHeaderIndices, stickyArray, currentStickyIdx, needNewContainers, startBuffered, endBuffered) {
2342
2491
  var _a3;
2343
- const activeIndices = getActiveStickyIndices(ctx, state, stickyHeaderIndices);
2344
- state.activeStickyIndex = currentStickyIdx >= 0 ? stickyArray[currentStickyIdx] : void 0;
2492
+ const state = ctx.state;
2493
+ const activeIndices = getActiveStickyIndices(ctx, stickyHeaderIndices);
2494
+ set$(ctx, "activeStickyIndex", currentStickyIdx >= 0 ? stickyArray[currentStickyIdx] : -1);
2345
2495
  for (let offset = 0; offset <= 1; offset++) {
2346
2496
  const idx = currentStickyIdx - offset;
2347
2497
  if (idx < 0 || activeIndices.has(stickyArray[idx])) continue;
@@ -2352,8 +2502,9 @@ function handleStickyActivation(ctx, state, stickyHeaderIndices, stickyArray, cu
2352
2502
  }
2353
2503
  }
2354
2504
  }
2355
- function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, currentStickyIdx, pendingRemoval) {
2505
+ function handleStickyRecycling(ctx, stickyArray, scroll, scrollBuffer, currentStickyIdx, pendingRemoval) {
2356
2506
  var _a3, _b, _c;
2507
+ const state = ctx.state;
2357
2508
  for (const containerIndex of state.stickyContainerPool) {
2358
2509
  const itemKey = peek$(ctx, `containerItemKey${containerIndex}`);
2359
2510
  const itemIndex = itemKey ? state.indexByKey.get(itemKey) : void 0;
@@ -2377,7 +2528,7 @@ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, cu
2377
2528
  const currentId = (_b = state.idCache[itemIndex]) != null ? _b : getId(state, itemIndex);
2378
2529
  if (currentId) {
2379
2530
  const currentPos = state.positions.get(currentId);
2380
- const currentSize = (_c = state.sizes.get(currentId)) != null ? _c : getItemSize(ctx, state, currentId, itemIndex, state.props.data[itemIndex]);
2531
+ const currentSize = (_c = state.sizes.get(currentId)) != null ? _c : getItemSize(ctx, currentId, itemIndex, state.props.data[itemIndex]);
2381
2532
  shouldRecycle = currentPos !== void 0 && scroll > currentPos + currentSize + scrollBuffer * 3;
2382
2533
  }
2383
2534
  }
@@ -2386,7 +2537,8 @@ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, cu
2386
2537
  }
2387
2538
  }
2388
2539
  }
2389
- function calculateItemsInView(ctx, state, params = {}) {
2540
+ function calculateItemsInView(ctx, params = {}) {
2541
+ const state = ctx.state;
2390
2542
  reactDom.unstable_batchedUpdates(() => {
2391
2543
  var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j;
2392
2544
  const {
@@ -2410,9 +2562,6 @@ function calculateItemsInView(ctx, state, params = {}) {
2410
2562
  const stickyIndicesSet = state.props.stickyIndicesSet || /* @__PURE__ */ new Set();
2411
2563
  const prevNumContainers = peek$(ctx, "numContainers");
2412
2564
  if (!data || scrollLength === 0 || !prevNumContainers) {
2413
- if (state.initialAnchor) {
2414
- ensureInitialAnchor(ctx, state);
2415
- }
2416
2565
  return;
2417
2566
  }
2418
2567
  const totalSize = getContentSize(ctx);
@@ -2426,15 +2575,14 @@ function calculateItemsInView(ctx, state, params = {}) {
2426
2575
  if (!queuedInitialLayout && initialScroll) {
2427
2576
  const updatedOffset = calculateOffsetWithOffsetPosition(
2428
2577
  ctx,
2429
- state,
2430
- calculateOffsetForIndex(ctx, state, initialScroll.index),
2578
+ calculateOffsetForIndex(ctx, initialScroll.index),
2431
2579
  initialScroll
2432
2580
  );
2433
2581
  scrollState = updatedOffset;
2434
2582
  }
2435
2583
  const scrollAdjustPending = (_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0;
2436
2584
  const scrollAdjustPad = scrollAdjustPending - topPad;
2437
- let scroll = scrollState + scrollExtra + scrollAdjustPad;
2585
+ let scroll = Math.round(scrollState + scrollExtra + scrollAdjustPad);
2438
2586
  if (scroll + scrollLength > totalSize) {
2439
2587
  scroll = Math.max(0, totalSize - scrollLength);
2440
2588
  }
@@ -2442,11 +2590,12 @@ function calculateItemsInView(ctx, state, params = {}) {
2442
2590
  set$(ctx, "debugRawScroll", scrollState);
2443
2591
  set$(ctx, "debugComputedScroll", scroll);
2444
2592
  }
2445
- const previousStickyIndex = state.activeStickyIndex;
2593
+ const previousStickyIndex = peek$(ctx, "activeStickyIndex");
2446
2594
  const currentStickyIdx = stickyIndicesArr.length > 0 ? findCurrentStickyIndex(stickyIndicesArr, scroll, state) : -1;
2447
- const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyIndicesArr[currentStickyIdx] : void 0;
2448
- state.activeStickyIndex = nextActiveStickyIndex;
2449
- set$(ctx, "activeStickyIndex", nextActiveStickyIndex);
2595
+ const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyIndicesArr[currentStickyIdx] : -1;
2596
+ if (currentStickyIdx >= 0 || previousStickyIndex >= 0) {
2597
+ set$(ctx, "activeStickyIndex", nextActiveStickyIndex);
2598
+ }
2450
2599
  let scrollBufferTop = scrollBuffer;
2451
2600
  let scrollBufferBottom = scrollBuffer;
2452
2601
  if (speed > 0 || speed === 0 && scroll < Math.max(50, scrollBuffer)) {
@@ -2459,23 +2608,20 @@ function calculateItemsInView(ctx, state, params = {}) {
2459
2608
  const scrollTopBuffered = scroll - scrollBufferTop;
2460
2609
  const scrollBottom = scroll + scrollLength + (scroll < 0 ? -scroll : 0);
2461
2610
  const scrollBottomBuffered = scrollBottom + scrollBufferBottom;
2462
- if (!dataChanged && scrollForNextCalculateItemsInView) {
2611
+ if (!dataChanged && !forceFullItemPositions && scrollForNextCalculateItemsInView) {
2463
2612
  const { top, bottom } = scrollForNextCalculateItemsInView;
2464
- if (scrollTopBuffered > top && scrollBottomBuffered < bottom) {
2465
- if (state.initialAnchor) {
2466
- ensureInitialAnchor(ctx, state);
2467
- }
2613
+ if ((top === null || scrollTopBuffered > top) && (bottom === null || scrollBottomBuffered < bottom)) {
2468
2614
  return;
2469
2615
  }
2470
2616
  }
2471
- const checkMVCP = doMVCP ? prepareMVCP(ctx, state, dataChanged) : void 0;
2617
+ const checkMVCP = doMVCP ? prepareMVCP(ctx, dataChanged) : void 0;
2472
2618
  if (dataChanged) {
2473
2619
  indexByKey.clear();
2474
2620
  idCache.length = 0;
2475
2621
  positions.clear();
2476
2622
  }
2477
- const startIndex = dataChanged ? 0 : (_b = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _b : 0;
2478
- updateItemPositions(ctx, state, dataChanged, {
2623
+ const startIndex = forceFullItemPositions || dataChanged ? 0 : (_b = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _b : 0;
2624
+ updateItemPositions(ctx, dataChanged, {
2479
2625
  doMVCP,
2480
2626
  forceFullUpdate: !!forceFullItemPositions,
2481
2627
  scrollBottomBuffered,
@@ -2494,9 +2640,9 @@ function calculateItemsInView(ctx, state, params = {}) {
2494
2640
  for (let i = loopStart; i >= 0; i--) {
2495
2641
  const id = (_c = idCache[i]) != null ? _c : getId(state, i);
2496
2642
  const top = positions.get(id);
2497
- const size = (_d = sizes.get(id)) != null ? _d : getItemSize(ctx, state, id, i, data[i]);
2643
+ const size = (_d = sizes.get(id)) != null ? _d : getItemSize(ctx, id, i, data[i]);
2498
2644
  const bottom = top + size;
2499
- if (bottom > scroll - scrollBuffer) {
2645
+ if (bottom > scroll - scrollBufferTop) {
2500
2646
  loopStart = i;
2501
2647
  } else {
2502
2648
  break;
@@ -2521,7 +2667,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2521
2667
  const dataLength = data.length;
2522
2668
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
2523
2669
  const id = (_e = idCache[i]) != null ? _e : getId(state, i);
2524
- const size = (_f = sizes.get(id)) != null ? _f : getItemSize(ctx, state, id, i, data[i]);
2670
+ const size = (_f = sizes.get(id)) != null ? _f : getItemSize(ctx, id, i, data[i]);
2525
2671
  const top = positions.get(id);
2526
2672
  if (!foundEnd) {
2527
2673
  if (startNoBuffer === null && top + size > scroll) {
@@ -2533,7 +2679,11 @@ function calculateItemsInView(ctx, state, params = {}) {
2533
2679
  if (startBuffered === null && top + size > scrollTopBuffered) {
2534
2680
  startBuffered = i;
2535
2681
  startBufferedId = id;
2536
- nextTop = top;
2682
+ if (scrollTopBuffered < 0) {
2683
+ nextTop = null;
2684
+ } else {
2685
+ nextTop = top;
2686
+ }
2537
2687
  }
2538
2688
  if (startNoBuffer !== null) {
2539
2689
  if (top <= scrollBottom) {
@@ -2541,7 +2691,11 @@ function calculateItemsInView(ctx, state, params = {}) {
2541
2691
  }
2542
2692
  if (top <= scrollBottomBuffered) {
2543
2693
  endBuffered = i;
2544
- nextBottom = top + size;
2694
+ if (scrollBottomBuffered > totalSize) {
2695
+ nextBottom = null;
2696
+ } else {
2697
+ nextBottom = top + size;
2698
+ }
2545
2699
  } else {
2546
2700
  foundEnd = true;
2547
2701
  }
@@ -2568,7 +2722,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2568
2722
  top: nextTop
2569
2723
  } : void 0;
2570
2724
  }
2571
- const numContainers = peek$(ctx, "numContainers");
2725
+ let numContainers = prevNumContainers;
2572
2726
  const pendingRemoval = [];
2573
2727
  if (dataChanged) {
2574
2728
  for (let i = 0; i < numContainers; i++) {
@@ -2579,7 +2733,6 @@ function calculateItemsInView(ctx, state, params = {}) {
2579
2733
  }
2580
2734
  }
2581
2735
  if (startBuffered !== null && endBuffered !== null) {
2582
- let numContainers2 = prevNumContainers;
2583
2736
  const needNewContainers = [];
2584
2737
  for (let i = startBuffered; i <= endBuffered; i++) {
2585
2738
  const id = (_h = idCache[i]) != null ? _h : getId(state, i);
@@ -2590,7 +2743,6 @@ function calculateItemsInView(ctx, state, params = {}) {
2590
2743
  if (stickyIndicesArr.length > 0) {
2591
2744
  handleStickyActivation(
2592
2745
  ctx,
2593
- state,
2594
2746
  stickyIndicesSet,
2595
2747
  stickyIndicesArr,
2596
2748
  currentStickyIdx,
@@ -2598,9 +2750,8 @@ function calculateItemsInView(ctx, state, params = {}) {
2598
2750
  startBuffered,
2599
2751
  endBuffered
2600
2752
  );
2601
- } else {
2602
- state.activeStickyIndex = void 0;
2603
- set$(ctx, "activeStickyIndex", void 0);
2753
+ } else if (previousStickyIndex !== -1) {
2754
+ set$(ctx, "activeStickyIndex", -1);
2604
2755
  }
2605
2756
  if (needNewContainers.length > 0) {
2606
2757
  const requiredItemTypes = getItemType ? needNewContainers.map((i) => {
@@ -2609,7 +2760,6 @@ function calculateItemsInView(ctx, state, params = {}) {
2609
2760
  }) : void 0;
2610
2761
  const availableContainers = findAvailableContainers(
2611
2762
  ctx,
2612
- state,
2613
2763
  needNewContainers.length,
2614
2764
  startBuffered,
2615
2765
  endBuffered,
@@ -2631,29 +2781,30 @@ function calculateItemsInView(ctx, state, params = {}) {
2631
2781
  state.containerItemTypes.set(containerIndex, requiredItemTypes[idx]);
2632
2782
  }
2633
2783
  containerItemKeys.add(id);
2784
+ const containerSticky = `containerSticky${containerIndex}`;
2634
2785
  if (stickyIndicesSet.has(i)) {
2635
- set$(ctx, `containerSticky${containerIndex}`, true);
2786
+ set$(ctx, containerSticky, true);
2636
2787
  const topPadding = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
2637
2788
  set$(ctx, `containerStickyOffset${containerIndex}`, topPadding);
2638
2789
  state.stickyContainerPool.add(containerIndex);
2639
- } else {
2640
- set$(ctx, `containerSticky${containerIndex}`, false);
2790
+ } else if (peek$(ctx, containerSticky)) {
2791
+ set$(ctx, containerSticky, false);
2641
2792
  state.stickyContainerPool.delete(containerIndex);
2642
2793
  }
2643
- if (containerIndex >= numContainers2) {
2644
- numContainers2 = containerIndex + 1;
2794
+ if (containerIndex >= numContainers) {
2795
+ numContainers = containerIndex + 1;
2645
2796
  }
2646
2797
  }
2647
- if (numContainers2 !== prevNumContainers) {
2648
- set$(ctx, "numContainers", numContainers2);
2649
- if (numContainers2 > peek$(ctx, "numContainersPooled")) {
2650
- set$(ctx, "numContainersPooled", Math.ceil(numContainers2 * 1.5));
2798
+ if (numContainers !== prevNumContainers) {
2799
+ set$(ctx, "numContainers", numContainers);
2800
+ if (numContainers > peek$(ctx, "numContainersPooled")) {
2801
+ set$(ctx, "numContainersPooled", Math.ceil(numContainers * 1.5));
2651
2802
  }
2652
2803
  }
2653
2804
  }
2654
2805
  }
2655
2806
  if (stickyIndicesArr.length > 0) {
2656
- handleStickyRecycling(ctx, state, stickyIndicesArr, scroll, scrollBuffer, currentStickyIdx, pendingRemoval);
2807
+ handleStickyRecycling(ctx, stickyIndicesArr, scroll, scrollBuffer, currentStickyIdx, pendingRemoval);
2657
2808
  }
2658
2809
  let didChangePositions = false;
2659
2810
  for (let i = 0; i < numContainers; i++) {
@@ -2705,7 +2856,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2705
2856
  }
2706
2857
  if (!queuedInitialLayout && endBuffered !== null) {
2707
2858
  if (checkAllSizesKnown(state)) {
2708
- setDidLayout(ctx, state);
2859
+ setDidLayout(ctx);
2709
2860
  }
2710
2861
  }
2711
2862
  if (viewabilityConfigCallbackPairs) {
@@ -2718,9 +2869,6 @@ function calculateItemsInView(ctx, state, params = {}) {
2718
2869
  }
2719
2870
  }
2720
2871
  });
2721
- if (state.initialAnchor) {
2722
- ensureInitialAnchor(ctx, state);
2723
- }
2724
2872
  }
2725
2873
 
2726
2874
  // src/core/checkActualChange.ts
@@ -2743,20 +2891,69 @@ function checkActualChange(state, dataProp, previousData) {
2743
2891
  return false;
2744
2892
  }
2745
2893
 
2894
+ // src/core/checkFinishedScroll.ts
2895
+ function checkFinishedScroll(ctx) {
2896
+ ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
2897
+ }
2898
+ function checkFinishedScrollFrame(ctx) {
2899
+ const scrollingTo = ctx.state.scrollingTo;
2900
+ if (scrollingTo) {
2901
+ const { state } = ctx;
2902
+ state.animFrameCheckFinishedScroll = void 0;
2903
+ const scroll = state.scroll;
2904
+ const adjust = state.scrollAdjustHandler.getAdjust();
2905
+ const clampedTargetOffset = clampScrollOffset(ctx, scrollingTo.offset - (scrollingTo.viewOffset || 0));
2906
+ const maxOffset = clampScrollOffset(ctx, scroll);
2907
+ const diff1 = Math.abs(scroll - clampedTargetOffset);
2908
+ const diff2 = Math.abs(diff1 - adjust);
2909
+ const isNotOverscrolled = Math.abs(scroll - maxOffset) < 1;
2910
+ if (isNotOverscrolled && (diff1 < 1 || diff2 < 1)) {
2911
+ finishScrollTo(ctx);
2912
+ }
2913
+ }
2914
+ }
2915
+ function checkFinishedScrollFallback(ctx) {
2916
+ const state = ctx.state;
2917
+ const scrollingTo = state.scrollingTo;
2918
+ const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) || !state.didContainersLayout;
2919
+ state.timeoutCheckFinishedScrollFallback = setTimeout(
2920
+ () => {
2921
+ let numChecks = 0;
2922
+ const checkHasScrolled = () => {
2923
+ state.timeoutCheckFinishedScrollFallback = void 0;
2924
+ const isStillScrollingTo = state.scrollingTo;
2925
+ if (isStillScrollingTo) {
2926
+ numChecks++;
2927
+ if (state.hasScrolled || numChecks > 5) {
2928
+ finishScrollTo(ctx);
2929
+ } else {
2930
+ state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, 100);
2931
+ }
2932
+ }
2933
+ };
2934
+ checkHasScrolled();
2935
+ },
2936
+ slowTimeout ? 500 : 100
2937
+ );
2938
+ }
2939
+
2746
2940
  // src/core/doMaintainScrollAtEnd.ts
2747
- function doMaintainScrollAtEnd(ctx, state, animated) {
2941
+ function doMaintainScrollAtEnd(ctx, animated) {
2942
+ const state = ctx.state;
2748
2943
  const {
2944
+ didContainersLayout,
2945
+ isAtEnd,
2749
2946
  refScroller,
2750
2947
  props: { maintainScrollAtEnd }
2751
2948
  } = state;
2752
- if ((state == null ? void 0 : state.isAtEnd) && maintainScrollAtEnd && peek$(ctx, "containersDidLayout")) {
2949
+ if (isAtEnd && maintainScrollAtEnd && didContainersLayout) {
2753
2950
  const paddingTop = peek$(ctx, "alignItemsPaddingTop");
2754
2951
  if (paddingTop > 0) {
2755
2952
  state.scroll = 0;
2756
2953
  }
2757
2954
  requestAnimationFrame(() => {
2758
2955
  var _a3;
2759
- if (state == null ? void 0 : state.isAtEnd) {
2956
+ if (state.isAtEnd) {
2760
2957
  state.maintainingScrollAtEnd = true;
2761
2958
  (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
2762
2959
  animated
@@ -2827,28 +3024,30 @@ function updateAveragesOnDataChange(state, oldData, newData) {
2827
3024
  }
2828
3025
 
2829
3026
  // src/core/checkResetContainers.ts
2830
- function checkResetContainers(ctx, state, dataProp) {
3027
+ function checkResetContainers(ctx, dataProp) {
3028
+ const state = ctx.state;
2831
3029
  const { previousData } = state;
2832
3030
  if (previousData) {
2833
3031
  updateAveragesOnDataChange(state, previousData, dataProp);
2834
3032
  }
2835
3033
  const { maintainScrollAtEnd } = state.props;
2836
- calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
3034
+ calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
2837
3035
  const shouldMaintainScrollAtEnd = maintainScrollAtEnd === true || maintainScrollAtEnd.onDataChange;
2838
- const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, state, false);
3036
+ const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, false);
2839
3037
  if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
2840
3038
  state.isEndReached = false;
2841
3039
  }
2842
3040
  if (!didMaintainScrollAtEnd) {
2843
3041
  checkAtTop(state);
2844
- checkAtBottom(ctx, state);
3042
+ checkAtBottom(ctx);
2845
3043
  }
2846
3044
  delete state.previousData;
2847
3045
  }
2848
3046
 
2849
3047
  // src/core/doInitialAllocateContainers.ts
2850
- function doInitialAllocateContainers(ctx, state) {
3048
+ function doInitialAllocateContainers(ctx) {
2851
3049
  var _a3, _b, _c;
3050
+ const state = ctx.state;
2852
3051
  const {
2853
3052
  scrollLength,
2854
3053
  props: {
@@ -2886,10 +3085,10 @@ function doInitialAllocateContainers(ctx, state) {
2886
3085
  if (state.lastLayout) {
2887
3086
  if (state.initialScroll) {
2888
3087
  requestAnimationFrame(() => {
2889
- calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
3088
+ calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
2890
3089
  });
2891
3090
  } else {
2892
- calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
3091
+ calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
2893
3092
  }
2894
3093
  }
2895
3094
  return true;
@@ -2897,7 +3096,8 @@ function doInitialAllocateContainers(ctx, state) {
2897
3096
  }
2898
3097
 
2899
3098
  // src/core/handleLayout.ts
2900
- function handleLayout(ctx, state, layout, setCanRender) {
3099
+ function handleLayout(ctx, layout, setCanRender) {
3100
+ const state = ctx.state;
2901
3101
  const { maintainScrollAtEnd } = state.props;
2902
3102
  const measuredLength = layout[state.props.horizontal ? "width" : "height"];
2903
3103
  const previousLength = state.scrollLength;
@@ -2913,19 +3113,19 @@ function handleLayout(ctx, state, layout, setCanRender) {
2913
3113
  state.lastBatchingAction = Date.now();
2914
3114
  state.scrollForNextCalculateItemsInView = void 0;
2915
3115
  if (scrollLength > 0) {
2916
- doInitialAllocateContainers(ctx, state);
3116
+ doInitialAllocateContainers(ctx);
2917
3117
  }
2918
3118
  if (needsCalculate) {
2919
- calculateItemsInView(ctx, state, { doMVCP: true });
3119
+ calculateItemsInView(ctx, { doMVCP: true });
2920
3120
  }
2921
3121
  if (didChange || otherAxisSize !== prevOtherAxisSize) {
2922
3122
  set$(ctx, "scrollSize", { height: layout.height, width: layout.width });
2923
3123
  }
2924
3124
  if (maintainScrollAtEnd === true || maintainScrollAtEnd.onLayout) {
2925
- doMaintainScrollAtEnd(ctx, state, false);
3125
+ doMaintainScrollAtEnd(ctx, false);
2926
3126
  }
2927
- updateAlignItemsPaddingTop(ctx, state);
2928
- checkAtBottom(ctx, state);
3127
+ updateAlignItemsPaddingTop(ctx);
3128
+ checkAtBottom(ctx);
2929
3129
  checkAtTop(state);
2930
3130
  if (state) {
2931
3131
  state.needsOtherAxisSize = otherAxisSize - (state.props.stylePaddingTop || 0) < 10;
@@ -2941,8 +3141,9 @@ function handleLayout(ctx, state, layout, setCanRender) {
2941
3141
  }
2942
3142
 
2943
3143
  // src/core/onScroll.ts
2944
- function onScroll(ctx, state, event) {
3144
+ function onScroll(ctx, event) {
2945
3145
  var _a3, _b, _c;
3146
+ const state = ctx.state;
2946
3147
  const {
2947
3148
  scrollProcessingEnabled,
2948
3149
  props: { onScroll: onScrollProp }
@@ -2953,9 +3154,23 @@ function onScroll(ctx, state, event) {
2953
3154
  if (((_b = (_a3 = event.nativeEvent) == null ? void 0 : _a3.contentSize) == null ? void 0 : _b.height) === 0 && ((_c = event.nativeEvent.contentSize) == null ? void 0 : _c.width) === 0) {
2954
3155
  return;
2955
3156
  }
2956
- const newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
3157
+ let newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
2957
3158
  state.scrollPending = newScroll;
2958
- updateScroll(ctx, state, newScroll);
3159
+ if (state.scrollingTo) {
3160
+ const maxOffset = clampScrollOffset(ctx, newScroll);
3161
+ if (newScroll !== maxOffset && Math.abs(newScroll - maxOffset) > 1) {
3162
+ newScroll = maxOffset;
3163
+ scrollTo(ctx, {
3164
+ forceScroll: true,
3165
+ isInitialScroll: true,
3166
+ noScrollingTo: true,
3167
+ offset: newScroll
3168
+ });
3169
+ return;
3170
+ }
3171
+ }
3172
+ updateScroll(ctx, newScroll);
3173
+ checkFinishedScroll(ctx);
2959
3174
  onScrollProp == null ? void 0 : onScrollProp(event);
2960
3175
  }
2961
3176
 
@@ -2964,51 +3179,47 @@ var ScrollAdjustHandler = class {
2964
3179
  constructor(ctx) {
2965
3180
  this.appliedAdjust = 0;
2966
3181
  this.pendingAdjust = 0;
2967
- this.mounted = false;
2968
- this.context = ctx;
2969
- {
2970
- const commitPendingAdjust = () => {
2971
- const state = this.context.internalState;
2972
- const pending = this.pendingAdjust;
2973
- if (pending !== 0) {
2974
- this.pendingAdjust = 0;
2975
- this.appliedAdjust += pending;
2976
- state.scroll += pending;
2977
- state.scrollForNextCalculateItemsInView = void 0;
2978
- set$(this.context, "scrollAdjustPending", 0);
2979
- set$(this.context, "scrollAdjust", this.appliedAdjust);
2980
- calculateItemsInView(this.context, this.context.internalState);
2981
- }
2982
- };
2983
- listen$(this.context, "scrollingTo", (value) => {
2984
- if (value === void 0) {
2985
- commitPendingAdjust();
2986
- }
2987
- });
2988
- }
3182
+ this.ctx = ctx;
2989
3183
  }
2990
3184
  requestAdjust(add) {
2991
- const scrollingTo = peek$(this.context, "scrollingTo");
3185
+ const scrollingTo = this.ctx.state.scrollingTo;
2992
3186
  if ((scrollingTo == null ? void 0 : scrollingTo.animated) && !scrollingTo.isInitialScroll) {
2993
3187
  this.pendingAdjust += add;
2994
- set$(this.context, "scrollAdjustPending", this.pendingAdjust);
3188
+ set$(this.ctx, "scrollAdjustPending", this.pendingAdjust);
2995
3189
  } else {
2996
3190
  this.appliedAdjust += add;
2997
- set$(this.context, "scrollAdjust", this.appliedAdjust);
3191
+ set$(this.ctx, "scrollAdjust", this.appliedAdjust);
3192
+ }
3193
+ if (this.ctx.state.scrollingTo) {
3194
+ checkFinishedScroll(this.ctx);
2998
3195
  }
2999
- }
3000
- setMounted() {
3001
- this.mounted = true;
3002
3196
  }
3003
3197
  getAdjust() {
3004
3198
  return this.appliedAdjust;
3005
3199
  }
3200
+ commitPendingAdjust() {
3201
+ {
3202
+ const state = this.ctx.state;
3203
+ const pending = this.pendingAdjust;
3204
+ if (pending !== 0) {
3205
+ this.pendingAdjust = 0;
3206
+ this.appliedAdjust += pending;
3207
+ state.scroll += pending;
3208
+ state.scrollForNextCalculateItemsInView = void 0;
3209
+ set$(this.ctx, "scrollAdjustPending", 0);
3210
+ set$(this.ctx, "scrollAdjust", this.appliedAdjust);
3211
+ calculateItemsInView(this.ctx);
3212
+ }
3213
+ }
3214
+ }
3006
3215
  };
3007
3216
 
3008
3217
  // src/core/updateItemSize.ts
3009
- function updateItemSize(ctx, state, itemKey, sizeObj) {
3218
+ function updateItemSize(ctx, itemKey, sizeObj) {
3010
3219
  var _a3;
3220
+ const state = ctx.state;
3011
3221
  const {
3222
+ didContainersLayout,
3012
3223
  sizesKnown,
3013
3224
  props: {
3014
3225
  getFixedItemSize,
@@ -3036,13 +3247,12 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
3036
3247
  return;
3037
3248
  }
3038
3249
  }
3039
- const containersDidLayout = peek$(ctx, "containersDidLayout");
3040
- let needsRecalculate = !containersDidLayout;
3250
+ let needsRecalculate = !didContainersLayout;
3041
3251
  let shouldMaintainScrollAtEnd = false;
3042
3252
  let minIndexSizeChanged;
3043
3253
  let maxOtherAxisSize = peek$(ctx, "otherAxisSize") || 0;
3044
3254
  const prevSizeKnown = state.sizesKnown.get(itemKey);
3045
- const diff = updateOneItemSize(ctx, state, itemKey, sizeObj);
3255
+ const diff = updateOneItemSize(ctx, itemKey, sizeObj);
3046
3256
  const size = roundSize(horizontal ? sizeObj.width : sizeObj.height);
3047
3257
  if (diff !== 0) {
3048
3258
  minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
@@ -3091,22 +3301,22 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
3091
3301
  if (!cur || maxOtherAxisSize > cur) {
3092
3302
  set$(ctx, "otherAxisSize", maxOtherAxisSize);
3093
3303
  }
3094
- if (containersDidLayout || checkAllSizesKnown(state)) {
3304
+ if (didContainersLayout || checkAllSizesKnown(state)) {
3095
3305
  if (needsRecalculate) {
3096
3306
  state.scrollForNextCalculateItemsInView = void 0;
3097
- calculateItemsInView(ctx, state, { doMVCP: true });
3307
+ calculateItemsInView(ctx, { doMVCP: true });
3098
3308
  }
3099
3309
  if (shouldMaintainScrollAtEnd) {
3100
3310
  if (maintainScrollAtEnd === true || maintainScrollAtEnd.onItemLayout) {
3101
- doMaintainScrollAtEnd(ctx, state, false);
3311
+ doMaintainScrollAtEnd(ctx, false);
3102
3312
  }
3103
3313
  }
3104
3314
  }
3105
3315
  }
3106
- function updateOneItemSize(ctx, state, itemKey, sizeObj) {
3316
+ function updateOneItemSize(ctx, itemKey, sizeObj) {
3107
3317
  var _a3;
3318
+ const state = ctx.state;
3108
3319
  const {
3109
- sizes,
3110
3320
  indexByKey,
3111
3321
  sizesKnown,
3112
3322
  averageSizes,
@@ -3114,9 +3324,10 @@ function updateOneItemSize(ctx, state, itemKey, sizeObj) {
3114
3324
  } = state;
3115
3325
  if (!data) return 0;
3116
3326
  const index = indexByKey.get(itemKey);
3117
- const prevSize = getItemSize(ctx, state, itemKey, index, data[index]);
3327
+ const prevSize = getItemSize(ctx, itemKey, index, data[index]);
3118
3328
  const rawSize = horizontal ? sizeObj.width : sizeObj.height;
3119
3329
  const size = Math.round(rawSize) ;
3330
+ const prevSizeKnown = sizesKnown.get(itemKey);
3120
3331
  sizesKnown.set(itemKey, size);
3121
3332
  if (!getEstimatedItemSize && !getFixedItemSize && size > 0) {
3122
3333
  const itemType = getItemType ? (_a3 = getItemType(data[index], index)) != null ? _a3 : "" : "";
@@ -3124,11 +3335,15 @@ function updateOneItemSize(ctx, state, itemKey, sizeObj) {
3124
3335
  if (!averages) {
3125
3336
  averages = averageSizes[itemType] = { avg: 0, num: 0 };
3126
3337
  }
3127
- averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
3128
- averages.num++;
3338
+ if (prevSizeKnown !== void 0 && prevSizeKnown > 0) {
3339
+ averages.avg += (size - prevSizeKnown) / averages.num;
3340
+ } else {
3341
+ averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
3342
+ averages.num++;
3343
+ }
3129
3344
  }
3130
3345
  if (!prevSize || Math.abs(prevSize - size) > 0.1) {
3131
- setSize(ctx, state, itemKey, size);
3346
+ setSize(ctx, itemKey, size);
3132
3347
  return size - prevSize;
3133
3348
  }
3134
3349
  return 0;
@@ -3175,14 +3390,15 @@ function createColumnWrapperStyle(contentContainerStyle) {
3175
3390
  }
3176
3391
 
3177
3392
  // src/utils/createImperativeHandle.ts
3178
- function createImperativeHandle(ctx, state) {
3393
+ function createImperativeHandle(ctx) {
3394
+ const state = ctx.state;
3179
3395
  const scrollIndexIntoView = (options) => {
3180
3396
  if (state) {
3181
3397
  const { index, ...rest } = options;
3182
3398
  const { startNoBuffer, endNoBuffer } = state;
3183
3399
  if (index < startNoBuffer || index > endNoBuffer) {
3184
3400
  const viewPosition = index < startNoBuffer ? 0 : 1;
3185
- scrollToIndex(ctx, state, {
3401
+ scrollToIndex(ctx, {
3186
3402
  ...rest,
3187
3403
  index,
3188
3404
  viewPosition
@@ -3197,7 +3413,7 @@ function createImperativeHandle(ctx, state) {
3197
3413
  getScrollableNode: () => refScroller.current.getScrollableNode(),
3198
3414
  getScrollResponder: () => refScroller.current.getScrollResponder(),
3199
3415
  getState: () => ({
3200
- activeStickyIndex: state.activeStickyIndex,
3416
+ activeStickyIndex: peek$(ctx, "activeStickyIndex"),
3201
3417
  contentLength: state.totalSize,
3202
3418
  data: state.props.data,
3203
3419
  elementAtIndex: (index) => {
@@ -3208,6 +3424,8 @@ function createImperativeHandle(ctx, state) {
3208
3424
  endBuffered: state.endBuffered,
3209
3425
  isAtEnd: state.isAtEnd,
3210
3426
  isAtStart: state.isAtStart,
3427
+ listen: (signalName, cb) => listen$(ctx, signalName, cb),
3428
+ listenToPosition: (key, cb) => listenPosition$(ctx, key, cb),
3211
3429
  positionAtIndex: (index) => state.positions.get(getId(state, index)),
3212
3430
  positions: state.positions,
3213
3431
  scroll: state.scroll,
@@ -3232,23 +3450,23 @@ function createImperativeHandle(ctx, state) {
3232
3450
  if (index !== -1) {
3233
3451
  const paddingBottom = stylePaddingBottom || 0;
3234
3452
  const footerSize = peek$(ctx, "footerSize") || 0;
3235
- scrollToIndex(ctx, state, {
3453
+ scrollToIndex(ctx, {
3454
+ ...options,
3236
3455
  index,
3237
3456
  viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
3238
- viewPosition: 1,
3239
- ...options
3457
+ viewPosition: 1
3240
3458
  });
3241
3459
  }
3242
3460
  },
3243
- scrollToIndex: (params) => scrollToIndex(ctx, state, params),
3461
+ scrollToIndex: (params) => scrollToIndex(ctx, params),
3244
3462
  scrollToItem: ({ item, ...props }) => {
3245
3463
  const data = state.props.data;
3246
3464
  const index = data.indexOf(item);
3247
3465
  if (index !== -1) {
3248
- scrollToIndex(ctx, state, { index, ...props });
3466
+ scrollToIndex(ctx, { index, ...props });
3249
3467
  }
3250
3468
  },
3251
- scrollToOffset: (params) => scrollTo(ctx, state, params),
3469
+ scrollToOffset: (params) => scrollTo(ctx, params),
3252
3470
  setScrollProcessingEnabled: (enabled) => {
3253
3471
  state.scrollProcessingEnabled = enabled;
3254
3472
  },
@@ -3258,8 +3476,9 @@ function createImperativeHandle(ctx, state) {
3258
3476
  }
3259
3477
  };
3260
3478
  }
3261
- function getRenderedItem(ctx, state, key) {
3479
+ function getRenderedItem(ctx, key) {
3262
3480
  var _a3;
3481
+ const state = ctx.state;
3263
3482
  if (!state) {
3264
3483
  return null;
3265
3484
  }
@@ -3336,11 +3555,13 @@ function useThrottledOnScroll(originalHandler, scrollEventThrottle) {
3336
3555
  var DEFAULT_DRAW_DISTANCE = 250;
3337
3556
  var DEFAULT_ITEM_SIZE = 100;
3338
3557
  var LegendList = typedMemo(
3558
+ // biome-ignore lint/nursery/noShadow: const function name shadowing is intentional
3339
3559
  typedForwardRef(function LegendList2(props, forwardedRef) {
3340
3560
  const { children, data: dataProp, renderItem: renderItemProp, ...restProps } = props;
3341
3561
  const isChildrenMode = children !== void 0 && dataProp === void 0;
3342
3562
  const processedProps = isChildrenMode ? {
3343
3563
  ...restProps,
3564
+ childrenMode: true,
3344
3565
  data: (isArray(children) ? children : React3__namespace.Children.toArray(children)).flat(1),
3345
3566
  renderItem: ({ item }) => item
3346
3567
  } : {
@@ -3357,10 +3578,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3357
3578
  alignItemsAtEnd = false,
3358
3579
  columnWrapperStyle,
3359
3580
  contentContainerStyle: contentContainerStyleProp,
3581
+ contentInset,
3360
3582
  data: dataProp = [],
3361
3583
  dataVersion,
3362
3584
  drawDistance = 250,
3363
- enableAverages = true,
3364
3585
  estimatedItemSize: estimatedItemSizeProp,
3365
3586
  estimatedListSize,
3366
3587
  extraData,
@@ -3402,6 +3623,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3402
3623
  snapToIndices,
3403
3624
  stickyHeaderIndices: stickyHeaderIndicesProp,
3404
3625
  stickyIndices: stickyIndicesDeprecated,
3626
+ // TODOV3: Remove from v3 release
3405
3627
  style: styleProp,
3406
3628
  suggestEstimatedItemSize,
3407
3629
  viewabilityConfig,
@@ -3409,6 +3631,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3409
3631
  waitForInitialLayout = true,
3410
3632
  ...rest
3411
3633
  } = props;
3634
+ const animatedPropsInternal = props.animatedPropsInternal;
3635
+ const { childrenMode } = rest;
3412
3636
  const contentContainerStyle = { ...StyleSheet.flatten(contentContainerStyleProp) };
3413
3637
  const style = { ...StyleSheet.flatten(styleProp) };
3414
3638
  const stylePaddingTopState = extractPadding(style, contentContainerStyle, "Top");
@@ -3432,10 +3656,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3432
3656
  }
3433
3657
  const refState = React3.useRef();
3434
3658
  if (!refState.current) {
3435
- if (!ctx.internalState) {
3659
+ if (!ctx.state) {
3436
3660
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : { height: 0, width: 0 } )[horizontal ? "width" : "height"];
3437
- ctx.internalState = {
3438
- activeStickyIndex: void 0,
3661
+ ctx.state = {
3662
+ activeStickyIndex: -1,
3439
3663
  averageSizes: {},
3440
3664
  columns: /* @__PURE__ */ new Map(),
3441
3665
  containerItemKeys: /* @__PURE__ */ new Set(),
@@ -3461,9 +3685,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3461
3685
  initialScroll: initialScrollProp,
3462
3686
  isAtEnd: false,
3463
3687
  isAtStart: false,
3464
- isEndReached: false,
3688
+ isEndReached: null,
3465
3689
  isFirst: true,
3466
- isStartReached: false,
3690
+ isStartReached: null,
3467
3691
  lastBatchingAction: Date.now(),
3468
3692
  lastLayout: void 0,
3469
3693
  loadStartTime: Date.now(),
@@ -3495,12 +3719,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3495
3719
  totalSize: 0,
3496
3720
  viewabilityConfigCallbackPairs: void 0
3497
3721
  };
3498
- const internalState = ctx.internalState;
3499
- internalState.triggerCalculateItemsInView = (params) => calculateItemsInView(ctx, internalState, params);
3722
+ const internalState = ctx.state;
3723
+ internalState.triggerCalculateItemsInView = (params) => calculateItemsInView(ctx, params);
3500
3724
  set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPosition);
3501
3725
  set$(ctx, "extraData", extraData);
3502
3726
  }
3503
- refState.current = ctx.internalState;
3727
+ refState.current = ctx.state;
3504
3728
  }
3505
3729
  const state = refState.current;
3506
3730
  const isFirstLocal = state.isFirst;
@@ -3514,9 +3738,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3514
3738
  const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
3515
3739
  state.props = {
3516
3740
  alignItemsAtEnd,
3741
+ animatedProps: animatedPropsInternal,
3742
+ contentInset,
3517
3743
  data: dataProp,
3518
3744
  dataVersion,
3519
- enableAverages,
3520
3745
  estimatedItemSize,
3521
3746
  getEstimatedItemSize,
3522
3747
  getFixedItemSize,
@@ -3559,62 +3784,52 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3559
3784
  set$(ctx, "lastItemKeys", memoizedLastItemKeys);
3560
3785
  set$(ctx, "numColumns", numColumnsProp);
3561
3786
  const prevPaddingTop = peek$(ctx, "stylePaddingTop");
3562
- setPaddingTop(ctx, state, { stylePaddingTop: stylePaddingTopState });
3787
+ setPaddingTop(ctx, { stylePaddingTop: stylePaddingTopState });
3563
3788
  refState.current.props.stylePaddingBottom = stylePaddingBottomState;
3564
3789
  let paddingDiff = stylePaddingTopState - prevPaddingTop;
3565
3790
  if (paddingDiff && prevPaddingTop !== void 0 && Platform.OS === "ios") {
3566
3791
  if (state.scroll < 0) {
3567
3792
  paddingDiff += state.scroll;
3568
3793
  }
3569
- requestAdjust(ctx, state, paddingDiff);
3794
+ requestAdjust(ctx, paddingDiff);
3570
3795
  }
3571
3796
  };
3572
3797
  if (isFirstLocal) {
3573
3798
  initializeStateVars();
3574
3799
  updateItemPositions(
3575
3800
  ctx,
3576
- state,
3577
3801
  /*dataChanged*/
3578
3802
  true
3579
3803
  );
3580
3804
  }
3581
3805
  const initialContentOffset = React3.useMemo(() => {
3582
- var _a4, _b2;
3583
- const { initialScroll } = refState.current;
3584
- if (!initialScroll) {
3806
+ let value;
3807
+ const { initialScroll, initialAnchor } = refState.current;
3808
+ if (initialScroll) {
3809
+ if (initialScroll.contentOffset !== void 0) {
3810
+ value = initialScroll.contentOffset;
3811
+ } else {
3812
+ const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, initialScroll.index) : 0;
3813
+ const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, initialScroll);
3814
+ const clampedOffset = clampScrollOffset(ctx, resolvedOffset);
3815
+ const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
3816
+ refState.current.initialScroll = updatedInitialScroll;
3817
+ state.initialScroll = updatedInitialScroll;
3818
+ value = clampedOffset;
3819
+ }
3820
+ } else {
3585
3821
  refState.current.initialAnchor = void 0;
3586
- return 0;
3587
- }
3588
- if (initialScroll.index !== void 0 && (!refState.current.initialAnchor || ((_a4 = refState.current.initialAnchor) == null ? void 0 : _a4.index) !== initialScroll.index)) {
3589
- refState.current.initialAnchor = {
3590
- attempts: 0,
3591
- index: initialScroll.index,
3592
- settledTicks: 0,
3593
- viewOffset: (_b2 = initialScroll.viewOffset) != null ? _b2 : 0,
3594
- viewPosition: initialScroll.viewPosition
3595
- };
3822
+ value = 0;
3823
+ }
3824
+ if (!value) {
3825
+ state.didFinishInitialScroll = true;
3596
3826
  }
3597
- if (initialScroll.contentOffset !== void 0) {
3598
- return initialScroll.contentOffset;
3599
- }
3600
- const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, state, initialScroll.index) : 0;
3601
- const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, state, baseOffset, initialScroll);
3602
- let clampedOffset = resolvedOffset;
3603
- if (Number.isFinite(state.scrollLength) && Number.isFinite(state.totalSize)) {
3604
- const maxOffset = Math.max(0, state.totalSize - state.scrollLength);
3605
- clampedOffset = Math.min(clampedOffset, maxOffset);
3606
- }
3607
- clampedOffset = Math.max(0, clampedOffset);
3608
- const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
3609
- refState.current.initialScroll = updatedInitialScroll;
3610
- state.initialScroll = updatedInitialScroll;
3611
- refState.current.isStartReached = clampedOffset < refState.current.scrollLength * onStartReachedThreshold;
3612
- return clampedOffset;
3827
+ return value;
3613
3828
  }, [renderNum]);
3614
3829
  if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
3615
3830
  refState.current.lastBatchingAction = Date.now();
3616
3831
  if (!keyExtractorProp && !isFirstLocal && didDataChangeLocal) {
3617
- IS_DEV && warnDevOnce(
3832
+ IS_DEV && !childrenMode && warnDevOnce(
3618
3833
  "keyExtractor",
3619
3834
  "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."
3620
3835
  );
@@ -3637,12 +3852,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3637
3852
  }
3638
3853
  }, []);
3639
3854
  const doInitialScroll = React3.useCallback(() => {
3640
- var _a4;
3641
3855
  const initialScroll = state.initialScroll;
3642
3856
  if (initialScroll) {
3643
- scrollTo(ctx, state, {
3857
+ scrollTo(ctx, {
3644
3858
  animated: false,
3645
- index: (_a4 = state.initialScroll) == null ? void 0 : _a4.index,
3859
+ index: initialScroll == null ? void 0 : initialScroll.index,
3646
3860
  isInitialScroll: true,
3647
3861
  offset: initialContentOffset,
3648
3862
  precomputedWithViewOffset: true
@@ -3651,7 +3865,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3651
3865
  }, [initialContentOffset]);
3652
3866
  const onLayoutChange = React3.useCallback((layout) => {
3653
3867
  doInitialScroll();
3654
- handleLayout(ctx, state, layout, setCanRender);
3868
+ handleLayout(ctx, layout, setCanRender);
3655
3869
  }, []);
3656
3870
  const { onLayout } = useOnLayoutSync({
3657
3871
  onLayoutChange,
@@ -3661,7 +3875,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3661
3875
  });
3662
3876
  React3.useLayoutEffect(() => {
3663
3877
  if (snapToIndices) {
3664
- updateSnapToOffsets(ctx, state);
3878
+ updateSnapToOffsets(ctx);
3665
3879
  }
3666
3880
  }, [snapToIndices]);
3667
3881
  React3.useLayoutEffect(() => {
@@ -3671,9 +3885,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3671
3885
  isFirst,
3672
3886
  props: { data }
3673
3887
  } = state;
3674
- const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx, state);
3888
+ const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx);
3675
3889
  if (!didAllocateContainers && !isFirst && (didDataChange || didColumnsChange)) {
3676
- checkResetContainers(ctx, state, data);
3890
+ checkResetContainers(ctx, data);
3677
3891
  }
3678
3892
  state.didColumnsChange = false;
3679
3893
  state.didDataChange = false;
@@ -3698,15 +3912,21 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3698
3912
  state.viewabilityConfigCallbackPairs = viewability;
3699
3913
  state.enableScrollForNextCalculateItemsInView = !viewability;
3700
3914
  }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
3701
- React3.useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx, state), []);
3915
+ React3.useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx), []);
3702
3916
  {
3703
3917
  React3.useEffect(doInitialScroll, []);
3704
3918
  }
3705
3919
  const fns = React3.useMemo(
3706
3920
  () => ({
3707
- getRenderedItem: (key) => getRenderedItem(ctx, state, key),
3708
- onScroll: (event) => onScroll(ctx, state, event),
3709
- updateItemSize: (itemKey, sizeObj) => updateItemSize(ctx, state, itemKey, sizeObj)
3921
+ getRenderedItem: (key) => getRenderedItem(ctx, key),
3922
+ onMomentumScrollEnd: (event) => {
3923
+ checkFinishedScrollFallback(ctx);
3924
+ if (onMomentumScrollEnd) {
3925
+ onMomentumScrollEnd(event);
3926
+ }
3927
+ },
3928
+ onScroll: (event) => onScroll(ctx, event),
3929
+ updateItemSize: (itemKey, sizeObj) => updateItemSize(ctx, itemKey, sizeObj)
3710
3930
  }),
3711
3931
  []
3712
3932
  );
@@ -3718,6 +3938,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3718
3938
  alignItemsAtEnd,
3719
3939
  canRender,
3720
3940
  contentContainerStyle,
3941
+ contentInset,
3721
3942
  getRenderedItem: fns.getRenderedItem,
3722
3943
  horizontal,
3723
3944
  initialContentOffset,
@@ -3726,16 +3947,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3726
3947
  maintainVisibleContentPosition,
3727
3948
  onLayout,
3728
3949
  onLayoutHeader,
3729
- onMomentumScrollEnd: (event) => {
3730
- {
3731
- requestAnimationFrame(() => {
3732
- finishScrollTo(ctx, refState.current);
3733
- });
3734
- }
3735
- if (onMomentumScrollEnd) {
3736
- onMomentumScrollEnd(event);
3737
- }
3738
- },
3950
+ onMomentumScrollEnd: fns.onMomentumScrollEnd,
3739
3951
  onScroll: onScrollHandler,
3740
3952
  recycleItems,
3741
3953
  refreshControl: refreshControl ? stylePaddingTopState > 0 ? React3__namespace.cloneElement(refreshControl, {
@@ -3750,7 +3962,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3750
3962
  ),
3751
3963
  refScrollView: combinedRef,
3752
3964
  scrollAdjustHandler: (_b = refState.current) == null ? void 0 : _b.scrollAdjustHandler,
3753
- scrollEventThrottle: 16 ,
3965
+ scrollEventThrottle: 0,
3754
3966
  snapToIndices,
3755
3967
  stickyHeaderIndices,
3756
3968
  style,