@legendapp/list 3.0.0-beta.4 → 3.0.0-beta.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/.DS_Store +0 -0
  2. package/CHANGELOG.md +19 -0
  3. package/README.md +8 -1
  4. package/animated.d.ts +607 -5
  5. package/animated.js +2 -2
  6. package/animated.mjs +1 -1
  7. package/index.d.ts +1192 -11
  8. package/index.js +1989 -946
  9. package/index.mjs +1988 -947
  10. package/index.native.js +1758 -866
  11. package/index.native.mjs +1740 -850
  12. package/keyboard-test.d.ts +206 -0
  13. package/keyboard-test.js +34 -0
  14. package/keyboard-test.mjs +13 -0
  15. package/keyboard.d.ts +206 -8
  16. package/keyboard.js +340 -32
  17. package/keyboard.mjs +343 -34
  18. package/package.json +52 -1
  19. package/{types-JPHClxiw.d.mts → react-native.d.ts} +399 -154
  20. package/{section-list.native.js → react-native.js} +1838 -1094
  21. package/{section-list.native.mjs → react-native.mjs} +1811 -1077
  22. package/{types-JPHClxiw.d.ts → react.d.ts} +456 -154
  23. package/react.js +4812 -0
  24. package/react.mjs +4782 -0
  25. package/reanimated.d.ts +618 -7
  26. package/reanimated.js +156 -30
  27. package/reanimated.mjs +155 -29
  28. package/section-list.d.ts +607 -5
  29. package/section-list.js +38 -3679
  30. package/section-list.mjs +34 -3676
  31. package/animated.d.mts +0 -9
  32. package/index.d.mts +0 -23
  33. package/index.native.d.mts +0 -23
  34. package/index.native.d.ts +0 -23
  35. package/keyboard-controller.d.mts +0 -12
  36. package/keyboard-controller.d.ts +0 -12
  37. package/keyboard-controller.js +0 -69
  38. package/keyboard-controller.mjs +0 -48
  39. package/keyboard.d.mts +0 -13
  40. package/reanimated.d.mts +0 -18
  41. package/section-list.d.mts +0 -113
  42. package/section-list.native.d.mts +0 -113
  43. package/section-list.native.d.ts +0 -113
  44. package/types-YNdphn_A.d.mts +0 -670
  45. package/types-YNdphn_A.d.ts +0 -670
package/index.native.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var React2 = require('react');
4
- var reactNative = require('react-native');
4
+ var ReactNative = require('react-native');
5
5
  var shim = require('use-sync-external-store/shim');
6
6
 
7
7
  function _interopNamespace(e) {
@@ -23,35 +23,68 @@ function _interopNamespace(e) {
23
23
  }
24
24
 
25
25
  var React2__namespace = /*#__PURE__*/_interopNamespace(React2);
26
+ var ReactNative__namespace = /*#__PURE__*/_interopNamespace(ReactNative);
26
27
 
27
28
  // src/components/LegendList.tsx
28
- reactNative.Animated.View;
29
- var View = reactNative.View;
30
- var Text = reactNative.Text;
31
- var createAnimatedValue = (value) => new reactNative.Animated.Value(value);
29
+ ReactNative.Animated.View;
30
+ var View = ReactNative.View;
31
+ var Text = ReactNative.Text;
32
+
33
+ // src/state/getContentInsetEnd.ts
34
+ function getContentInsetEnd(state) {
35
+ var _a3;
36
+ const { props } = state;
37
+ const horizontal = props.horizontal;
38
+ const contentInset = props.contentInset;
39
+ const baseInset = contentInset != null ? contentInset : state.nativeContentInset;
40
+ const overrideInset = (_a3 = state.contentInsetOverride) != null ? _a3 : void 0;
41
+ if (overrideInset) {
42
+ const mergedInset = { bottom: 0, right: 0, ...baseInset, ...overrideInset };
43
+ return (horizontal ? mergedInset.right : mergedInset.bottom) || 0;
44
+ }
45
+ if (baseInset) {
46
+ return (horizontal ? baseInset.right : baseInset.bottom) || 0;
47
+ }
48
+ return 0;
49
+ }
50
+
51
+ // src/state/getContentSize.ts
52
+ function getContentSize(ctx) {
53
+ var _a3;
54
+ const { values, state } = ctx;
55
+ const stylePaddingTop = values.get("stylePaddingTop") || 0;
56
+ const stylePaddingBottom = state.props.stylePaddingBottom || 0;
57
+ const headerSize = values.get("headerSize") || 0;
58
+ const footerSize = values.get("footerSize") || 0;
59
+ const contentInsetBottom = getContentInsetEnd(state);
60
+ const totalSize = (_a3 = state.pendingTotalSize) != null ? _a3 : values.get("totalSize");
61
+ return headerSize + footerSize + totalSize + stylePaddingTop + stylePaddingBottom + (contentInsetBottom || 0);
62
+ }
63
+ var createAnimatedValue = (value) => new ReactNative.Animated.Value(value);
32
64
 
33
65
  // src/state/state.tsx
34
66
  var ContextState = React2__namespace.createContext(null);
67
+ var contextNum = 0;
35
68
  function StateProvider({ children }) {
36
69
  const [value] = React2__namespace.useState(() => ({
37
70
  animatedScrollY: createAnimatedValue(0),
38
71
  columnWrapperStyle: void 0,
39
- internalState: void 0,
72
+ contextNum: contextNum++,
40
73
  listeners: /* @__PURE__ */ new Map(),
41
74
  mapViewabilityAmountCallbacks: /* @__PURE__ */ new Map(),
42
75
  mapViewabilityAmountValues: /* @__PURE__ */ new Map(),
43
76
  mapViewabilityCallbacks: /* @__PURE__ */ new Map(),
44
77
  mapViewabilityConfigStates: /* @__PURE__ */ new Map(),
45
78
  mapViewabilityValues: /* @__PURE__ */ new Map(),
79
+ positionListeners: /* @__PURE__ */ new Map(),
80
+ state: void 0,
46
81
  values: /* @__PURE__ */ new Map([
47
- ["alignItemsPaddingTop", 0],
48
82
  ["stylePaddingTop", 0],
49
83
  ["headerSize", 0],
50
84
  ["numContainers", 0],
51
- ["activeStickyIndex", void 0],
85
+ ["activeStickyIndex", -1],
52
86
  ["totalSize", 0],
53
- ["scrollAdjustPending", 0],
54
- ["scrollingTo", void 0]
87
+ ["scrollAdjustPending", 0]
55
88
  ]),
56
89
  viewRefs: /* @__PURE__ */ new Map()
57
90
  }));
@@ -119,15 +152,24 @@ function set$(ctx, signalName, value) {
119
152
  }
120
153
  }
121
154
  }
122
- function getContentSize(ctx) {
123
- var _a3, _b;
124
- const { values, internalState } = ctx;
125
- const stylePaddingTop = values.get("stylePaddingTop") || 0;
126
- const stylePaddingBottom = (internalState == null ? void 0 : internalState.props.stylePaddingBottom) || 0;
127
- const headerSize = values.get("headerSize") || 0;
128
- const footerSize = values.get("footerSize") || 0;
129
- const totalSize = (_b = (_a3 = ctx.internalState) == null ? void 0 : _a3.pendingTotalSize) != null ? _b : values.get("totalSize");
130
- return headerSize + footerSize + totalSize + stylePaddingTop + stylePaddingBottom;
155
+ function listenPosition$(ctx, key, cb) {
156
+ const { positionListeners } = ctx;
157
+ let setListeners = positionListeners.get(key);
158
+ if (!setListeners) {
159
+ setListeners = /* @__PURE__ */ new Set();
160
+ positionListeners.set(key, setListeners);
161
+ }
162
+ setListeners.add(cb);
163
+ return () => setListeners.delete(cb);
164
+ }
165
+ function notifyPosition$(ctx, key, value) {
166
+ const { positionListeners } = ctx;
167
+ const setListeners = positionListeners.get(key);
168
+ if (setListeners) {
169
+ for (const listener of setListeners) {
170
+ listener(value);
171
+ }
172
+ }
131
173
  }
132
174
  function useArr$(signalNames) {
133
175
  const ctx = React2__namespace.useContext(ContextState);
@@ -146,7 +188,7 @@ function useSelector$(signalName, selector) {
146
188
  var DebugRow = ({ children }) => {
147
189
  return /* @__PURE__ */ React2__namespace.createElement(View, { style: { alignItems: "center", flexDirection: "row", justifyContent: "space-between" } }, children);
148
190
  };
149
- var DebugView = React2__namespace.memo(function DebugView2({ state }) {
191
+ React2__namespace.memo(function DebugView2({ state }) {
150
192
  const ctx = useStateContext();
151
193
  const [totalSize = 0, scrollAdjust = 0, rawScroll = 0, scroll = 0, _numContainers = 0, _numContainersPooled = 0] = useArr$([
152
194
  "totalSize",
@@ -198,7 +240,7 @@ var _a;
198
240
  var envMode = typeof process !== "undefined" && typeof process.env === "object" && process.env ? (_a = process.env.NODE_ENV) != null ? _a : process.env.MODE : void 0;
199
241
  var processDev = typeof envMode === "string" ? envMode.toLowerCase() !== "production" : void 0;
200
242
  var _a2;
201
- var IS_DEV = (_a2 = metroDev != null ? metroDev : processDev) != null ? _a2 : false;
243
+ var IS_DEV = (_a2 = processDev != null ? processDev : metroDev) != null ? _a2 : false;
202
244
 
203
245
  // src/constants.ts
204
246
  var POSITION_OUT_OF_VIEW = -1e7;
@@ -206,9 +248,10 @@ var ENABLE_DEVMODE = IS_DEV && false;
206
248
  var ENABLE_DEBUG_VIEW = IS_DEV && false;
207
249
 
208
250
  // src/constants-platform.native.ts
209
- var IsNewArchitecture = global.nativeFabricUIManager != null;
251
+ var f = global.nativeFabricUIManager;
252
+ var IsNewArchitecture = f !== void 0 && f != null;
210
253
  var useAnimatedValue = (initialValue) => {
211
- const [animAnimatedValue] = React2.useState(() => new reactNative.Animated.Value(initialValue));
254
+ const [animAnimatedValue] = React2.useState(() => new ReactNative.Animated.Value(initialValue));
212
255
  return animAnimatedValue;
213
256
  };
214
257
 
@@ -243,6 +286,11 @@ function extractPadding(style, contentContainerStyle, type) {
243
286
  return getPadding(style, type) + getPadding(contentContainerStyle, type);
244
287
  }
245
288
  function findContainerId(ctx, key) {
289
+ var _a3, _b;
290
+ const directMatch = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.containerItemKeys) == null ? void 0 : _b.get(key);
291
+ if (directMatch !== void 0) {
292
+ return directMatch;
293
+ }
246
294
  const numContainers = peek$(ctx, "numContainers");
247
295
  for (let i = 0; i < numContainers; i++) {
248
296
  const itemKey = peek$(ctx, `containerItemKey${i}`);
@@ -265,7 +313,7 @@ function useValue$(key, params) {
265
313
  React2.useMemo(() => {
266
314
  let prevValue;
267
315
  let didQueueTask = false;
268
- listen$(ctx, key, (v) => {
316
+ listen$(ctx, key, () => {
269
317
  const newValue = getNewValue();
270
318
  if (delay !== void 0) {
271
319
  const fn = () => {
@@ -294,11 +342,19 @@ function useValue$(key, params) {
294
342
  }, []);
295
343
  return animValue;
296
344
  }
297
- var typedForwardRef = React2.forwardRef;
298
- var typedMemo = React2.memo;
345
+ var typedMemo = React2__namespace.memo;
346
+ var getComponent = (Component) => {
347
+ if (React2__namespace.isValidElement(Component)) {
348
+ return Component;
349
+ }
350
+ if (Component) {
351
+ return /* @__PURE__ */ React2__namespace.createElement(Component, null);
352
+ }
353
+ return null;
354
+ };
299
355
 
300
356
  // src/components/PositionView.native.tsx
301
- var PositionViewState = typedMemo(function PositionView({
357
+ var PositionViewState = typedMemo(function PositionViewState2({
302
358
  id,
303
359
  horizontal,
304
360
  style,
@@ -307,7 +363,7 @@ var PositionViewState = typedMemo(function PositionView({
307
363
  }) {
308
364
  const [position = POSITION_OUT_OF_VIEW] = useArr$([`containerPosition${id}`]);
309
365
  return /* @__PURE__ */ React2__namespace.createElement(
310
- reactNative.View,
366
+ ReactNative.View,
311
367
  {
312
368
  ref: refView,
313
369
  style: [
@@ -318,7 +374,7 @@ var PositionViewState = typedMemo(function PositionView({
318
374
  }
319
375
  );
320
376
  });
321
- var PositionViewAnimated = typedMemo(function PositionView2({
377
+ var PositionViewAnimated = typedMemo(function PositionViewAnimated2({
322
378
  id,
323
379
  horizontal,
324
380
  style,
@@ -329,12 +385,12 @@ var PositionViewAnimated = typedMemo(function PositionView2({
329
385
  getValue: (v) => v != null ? v : POSITION_OUT_OF_VIEW
330
386
  });
331
387
  let position;
332
- if (reactNative.Platform.OS === "ios" || reactNative.Platform.OS === "android") {
388
+ if (ReactNative.Platform.OS === "ios" || ReactNative.Platform.OS === "android") {
333
389
  position = horizontal ? { transform: [{ translateX: position$ }] } : { transform: [{ translateY: position$ }] };
334
390
  } else {
335
391
  position = horizontal ? { left: position$ } : { top: position$ };
336
392
  }
337
- return /* @__PURE__ */ React2__namespace.createElement(reactNative.Animated.View, { ref: refView, style: [style, position], ...rest });
393
+ return /* @__PURE__ */ React2__namespace.createElement(ReactNative.Animated.View, { ref: refView, style: [style, position], ...rest });
338
394
  });
339
395
  var PositionViewSticky = typedMemo(function PositionViewSticky2({
340
396
  id,
@@ -342,74 +398,120 @@ var PositionViewSticky = typedMemo(function PositionViewSticky2({
342
398
  style,
343
399
  refView,
344
400
  animatedScrollY,
345
- stickyOffset,
346
401
  index,
402
+ stickyHeaderConfig,
403
+ children,
347
404
  ...rest
348
405
  }) {
349
- const [position = POSITION_OUT_OF_VIEW, headerSize] = useArr$([`containerPosition${id}`, "headerSize"]);
406
+ const [position = POSITION_OUT_OF_VIEW, headerSize = 0, stylePaddingTop = 0] = useArr$([
407
+ `containerPosition${id}`,
408
+ "headerSize",
409
+ "stylePaddingTop"
410
+ ]);
350
411
  const transform = React2__namespace.useMemo(() => {
351
- if (animatedScrollY && stickyOffset !== void 0) {
412
+ var _a3;
413
+ if (animatedScrollY) {
414
+ const stickyConfigOffset = (_a3 = stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset) != null ? _a3 : 0;
415
+ const stickyStart = position + headerSize + stylePaddingTop - stickyConfigOffset;
352
416
  const stickyPosition = animatedScrollY.interpolate({
353
417
  extrapolateLeft: "clamp",
354
418
  extrapolateRight: "extend",
355
- inputRange: [position + headerSize, position + 5e3 + headerSize],
419
+ inputRange: [stickyStart, stickyStart + 5e3],
356
420
  outputRange: [position, position + 5e3]
357
421
  });
358
422
  return horizontal ? [{ translateX: stickyPosition }] : [{ translateY: stickyPosition }];
359
423
  }
360
- }, [animatedScrollY, headerSize, horizontal, stickyOffset, position]);
424
+ }, [animatedScrollY, headerSize, horizontal, position, stylePaddingTop, stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset]);
361
425
  const viewStyle = React2__namespace.useMemo(() => [style, { zIndex: index + 1e3 }, { transform }], [style, transform]);
362
- return /* @__PURE__ */ React2__namespace.createElement(reactNative.Animated.View, { ref: refView, style: viewStyle, ...rest });
426
+ const renderStickyHeaderBackdrop = React2__namespace.useMemo(() => {
427
+ if (!(stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent)) {
428
+ return null;
429
+ }
430
+ return /* @__PURE__ */ React2__namespace.createElement(
431
+ ReactNative.View,
432
+ {
433
+ style: {
434
+ inset: 0,
435
+ pointerEvents: "none",
436
+ position: "absolute"
437
+ }
438
+ },
439
+ getComponent(stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent)
440
+ );
441
+ }, [stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent]);
442
+ return /* @__PURE__ */ React2__namespace.createElement(ReactNative.Animated.View, { ref: refView, style: viewStyle, ...rest }, renderStickyHeaderBackdrop, children);
363
443
  });
364
- var PositionView3 = IsNewArchitecture ? PositionViewState : PositionViewAnimated;
444
+ var PositionView = IsNewArchitecture ? PositionViewState : PositionViewAnimated;
365
445
  function useInit(cb) {
366
446
  React2.useState(() => cb());
367
447
  }
368
448
 
369
449
  // src/state/ContextContainer.ts
370
450
  var ContextContainer = React2.createContext(null);
451
+ function useContextContainer() {
452
+ return React2.useContext(ContextContainer);
453
+ }
371
454
  function useViewability(callback, configId) {
372
455
  const ctx = useStateContext();
373
- const { containerId } = React2.useContext(ContextContainer);
374
- const key = containerId + (configId != null ? configId : "");
456
+ const containerContext = useContextContainer();
375
457
  useInit(() => {
458
+ if (!containerContext) {
459
+ return;
460
+ }
461
+ const { containerId } = containerContext;
462
+ const key = containerId + (configId != null ? configId : "");
376
463
  const value = ctx.mapViewabilityValues.get(key);
377
464
  if (value) {
378
465
  callback(value);
379
466
  }
380
467
  });
381
- ctx.mapViewabilityCallbacks.set(key, callback);
382
- React2.useEffect(
383
- () => () => {
468
+ React2.useEffect(() => {
469
+ if (!containerContext) {
470
+ return;
471
+ }
472
+ const { containerId } = containerContext;
473
+ const key = containerId + (configId != null ? configId : "");
474
+ ctx.mapViewabilityCallbacks.set(key, callback);
475
+ return () => {
384
476
  ctx.mapViewabilityCallbacks.delete(key);
385
- },
386
- []
387
- );
477
+ };
478
+ }, [ctx, callback, configId, containerContext]);
388
479
  }
389
480
  function useViewabilityAmount(callback) {
390
481
  const ctx = useStateContext();
391
- const { containerId } = React2.useContext(ContextContainer);
482
+ const containerContext = useContextContainer();
392
483
  useInit(() => {
484
+ if (!containerContext) {
485
+ return;
486
+ }
487
+ const { containerId } = containerContext;
393
488
  const value = ctx.mapViewabilityAmountValues.get(containerId);
394
489
  if (value) {
395
490
  callback(value);
396
491
  }
397
492
  });
398
- ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
399
- React2.useEffect(
400
- () => () => {
493
+ React2.useEffect(() => {
494
+ if (!containerContext) {
495
+ return;
496
+ }
497
+ const { containerId } = containerContext;
498
+ ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
499
+ return () => {
401
500
  ctx.mapViewabilityAmountCallbacks.delete(containerId);
402
- },
403
- []
404
- );
501
+ };
502
+ }, [ctx, callback, containerContext]);
405
503
  }
406
504
  function useRecyclingEffect(effect) {
407
- const { index, value } = React2.useContext(ContextContainer);
505
+ const containerContext = useContextContainer();
408
506
  const prevValues = React2.useRef({
409
507
  prevIndex: void 0,
410
508
  prevItem: void 0
411
509
  });
412
510
  React2.useEffect(() => {
511
+ if (!containerContext) {
512
+ return;
513
+ }
514
+ const { index, value } = containerContext;
413
515
  let ret;
414
516
  if (prevValues.current.prevIndex !== void 0 && prevValues.current.prevItem !== void 0) {
415
517
  ret = effect({
@@ -424,38 +526,58 @@ function useRecyclingEffect(effect) {
424
526
  prevItem: value
425
527
  };
426
528
  return ret;
427
- }, [index, value, effect]);
529
+ }, [effect, containerContext]);
428
530
  }
429
531
  function useRecyclingState(valueOrFun) {
430
- const { index, value, itemKey, triggerLayout } = React2.useContext(ContextContainer);
431
- const refState = React2.useRef({
432
- itemKey: null,
433
- value: null
532
+ var _a3, _b;
533
+ const containerContext = useContextContainer();
534
+ const computeValue = (ctx) => {
535
+ if (isFunction(valueOrFun)) {
536
+ const initializer = valueOrFun;
537
+ return ctx ? initializer({
538
+ index: ctx.index,
539
+ item: ctx.value,
540
+ prevIndex: void 0,
541
+ prevItem: void 0
542
+ }) : initializer();
543
+ }
544
+ return valueOrFun;
545
+ };
546
+ const [stateValue, setStateValue] = React2.useState(() => {
547
+ return computeValue(containerContext);
434
548
  });
435
- const [_, setRenderNum] = React2.useState(0);
436
- const state = refState.current;
437
- if (state.itemKey !== itemKey) {
438
- state.itemKey = itemKey;
439
- state.value = isFunction(valueOrFun) ? valueOrFun({
440
- index,
441
- item: value,
442
- prevIndex: void 0,
443
- prevItem: void 0
444
- }) : valueOrFun;
549
+ const prevItemKeyRef = React2.useRef((_a3 = containerContext == null ? void 0 : containerContext.itemKey) != null ? _a3 : null);
550
+ const currentItemKey = (_b = containerContext == null ? void 0 : containerContext.itemKey) != null ? _b : null;
551
+ if (currentItemKey !== null && prevItemKeyRef.current !== currentItemKey) {
552
+ prevItemKeyRef.current = currentItemKey;
553
+ setStateValue(computeValue(containerContext));
445
554
  }
555
+ const triggerLayout = containerContext == null ? void 0 : containerContext.triggerLayout;
446
556
  const setState = React2.useCallback(
447
557
  (newState) => {
448
- state.value = isFunction(newState) ? newState(state.value) : newState;
449
- setRenderNum((v) => v + 1);
558
+ if (!triggerLayout) {
559
+ return;
560
+ }
561
+ setStateValue((prevValue) => {
562
+ return isFunction(newState) ? newState(prevValue) : newState;
563
+ });
450
564
  triggerLayout();
451
565
  },
452
- [triggerLayout, state]
566
+ [triggerLayout]
453
567
  );
454
- return [state.value, setState];
568
+ return [stateValue, setState];
455
569
  }
456
570
  function useIsLastItem() {
457
- const { itemKey } = React2.useContext(ContextContainer);
458
- const isLast = useSelector$("lastItemKeys", (lastItemKeys) => (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false);
571
+ const containerContext = useContextContainer();
572
+ const isLast = useSelector$("lastItemKeys", (lastItemKeys) => {
573
+ if (containerContext) {
574
+ const { itemKey } = containerContext;
575
+ if (!isNullOrUndefined(itemKey)) {
576
+ return (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false;
577
+ }
578
+ }
579
+ return false;
580
+ });
459
581
  return isLast;
460
582
  }
461
583
  function useListScrollSize() {
@@ -465,8 +587,9 @@ function useListScrollSize() {
465
587
  var noop = () => {
466
588
  };
467
589
  function useSyncLayout() {
468
- if (IsNewArchitecture) {
469
- const { triggerLayout: syncLayout } = React2.useContext(ContextContainer);
590
+ const containerContext = useContextContainer();
591
+ if (IsNewArchitecture && containerContext) {
592
+ const { triggerLayout: syncLayout } = containerContext;
470
593
  return syncLayout;
471
594
  } else {
472
595
  return noop;
@@ -509,30 +632,44 @@ function useOnLayoutSync({
509
632
  }
510
633
  return { onLayout };
511
634
  }
635
+ var Platform2 = ReactNative.Platform;
636
+ var PlatformAdjustBreaksScroll = Platform2.OS === "android";
637
+ var typedForwardRef = React2__namespace.forwardRef;
638
+ var typedMemo2 = React2__namespace.memo;
639
+
640
+ // src/utils/isInMVCPActiveMode.native.ts
641
+ function isInMVCPActiveMode(state) {
642
+ return state.dataChangeNeedsScrollUpdate;
643
+ }
512
644
 
513
645
  // src/components/Container.tsx
514
- var Container = typedMemo(function Container2({
646
+ var Container = typedMemo2(function Container2({
515
647
  id,
516
648
  recycleItems,
517
649
  horizontal,
518
650
  getRenderedItem: getRenderedItem2,
519
651
  updateItemSize: updateItemSize2,
520
- ItemSeparatorComponent
652
+ ItemSeparatorComponent,
653
+ stickyHeaderConfig
521
654
  }) {
522
655
  const ctx = useStateContext();
523
656
  const { columnWrapperStyle, animatedScrollY } = ctx;
524
- const [column = 0, data, itemKey, numColumns, extraData, isSticky, stickyOffset] = useArr$([
657
+ const positionComponentInternal = ctx.state.props.positionComponentInternal;
658
+ const stickyPositionComponentInternal = ctx.state.props.stickyPositionComponentInternal;
659
+ const [column = 0, span = 1, data, itemKey, numColumns = 1, extraData, isSticky] = useArr$([
525
660
  `containerColumn${id}`,
661
+ `containerSpan${id}`,
526
662
  `containerItemData${id}`,
527
663
  `containerItemKey${id}`,
528
664
  "numColumns",
529
665
  "extraData",
530
- `containerSticky${id}`,
531
- `containerStickyOffset${id}`
666
+ `containerSticky${id}`
532
667
  ]);
533
668
  const itemLayoutRef = React2.useRef({
669
+ didLayout: false,
534
670
  horizontal,
535
671
  itemKey,
672
+ pendingShrinkToken: 0,
536
673
  updateItemSize: updateItemSize2
537
674
  });
538
675
  itemLayoutRef.current.horizontal = horizontal;
@@ -540,9 +677,10 @@ var Container = typedMemo(function Container2({
540
677
  itemLayoutRef.current.updateItemSize = updateItemSize2;
541
678
  const ref = React2.useRef(null);
542
679
  const [layoutRenderCount, forceLayoutRender] = React2.useState(0);
543
- const otherAxisPos = numColumns > 1 ? `${(column - 1) / numColumns * 100}%` : 0;
544
- const otherAxisSize = numColumns > 1 ? `${1 / numColumns * 100}%` : void 0;
545
- const didLayoutRef = React2.useRef(false);
680
+ const resolvedColumn = column > 0 ? column : 1;
681
+ const resolvedSpan = Math.min(Math.max(span || 1, 1), numColumns);
682
+ const otherAxisPos = numColumns > 1 ? `${(resolvedColumn - 1) / numColumns * 100}%` : 0;
683
+ const otherAxisSize = numColumns > 1 ? `${resolvedSpan / numColumns * 100}%` : void 0;
546
684
  const style = React2.useMemo(() => {
547
685
  let paddingStyles;
548
686
  if (columnWrapperStyle) {
@@ -597,19 +735,41 @@ var Container = typedMemo(function Container2({
597
735
  const {
598
736
  horizontal: currentHorizontal,
599
737
  itemKey: currentItemKey,
600
- updateItemSize: updateItemSizeFn
738
+ updateItemSize: updateItemSizeFn,
739
+ lastSize,
740
+ pendingShrinkToken
601
741
  } = itemLayoutRef.current;
602
742
  if (isNullOrUndefined(currentItemKey)) {
603
743
  return;
604
744
  }
605
- didLayoutRef.current = true;
745
+ itemLayoutRef.current.didLayout = true;
606
746
  let layout = rectangle;
607
- const size = roundSize(rectangle[currentHorizontal ? "width" : "height"]);
747
+ const axis = currentHorizontal ? "width" : "height";
748
+ const size = roundSize(rectangle[axis]);
749
+ const prevSize = lastSize ? roundSize(lastSize[axis]) : void 0;
608
750
  const doUpdate = () => {
609
- itemLayoutRef.current.lastSize = { height: layout.height, width: layout.width };
751
+ itemLayoutRef.current.lastSize = layout;
610
752
  updateItemSizeFn(currentItemKey, layout);
611
- didLayoutRef.current = true;
753
+ itemLayoutRef.current.didLayout = true;
612
754
  };
755
+ const shouldDeferWebShrinkLayoutUpdate = Platform2.OS === "web" && !isInMVCPActiveMode(ctx.state) && prevSize !== void 0 && size + 1 < prevSize;
756
+ if (shouldDeferWebShrinkLayoutUpdate) {
757
+ const token = pendingShrinkToken + 1;
758
+ itemLayoutRef.current.pendingShrinkToken = token;
759
+ requestAnimationFrame(() => {
760
+ var _a4;
761
+ if (itemLayoutRef.current.pendingShrinkToken !== token) {
762
+ return;
763
+ }
764
+ const element = ref.current;
765
+ const rect = (_a4 = element == null ? void 0 : element.getBoundingClientRect) == null ? void 0 : _a4.call(element);
766
+ if (rect) {
767
+ layout = { height: rect.height, width: rect.width };
768
+ }
769
+ doUpdate();
770
+ });
771
+ return;
772
+ }
613
773
  if (IsNewArchitecture || size > 0) {
614
774
  doUpdate();
615
775
  } else {
@@ -622,16 +782,15 @@ var Container = typedMemo(function Container2({
622
782
  const { onLayout } = useOnLayoutSync(
623
783
  {
624
784
  onLayoutChange,
625
- ref
626
- },
785
+ ref},
627
786
  [itemKey, layoutRenderCount]
628
787
  );
629
788
  if (!IsNewArchitecture) {
630
789
  React2.useEffect(() => {
631
790
  if (!isNullOrUndefined(itemKey)) {
632
- didLayoutRef.current = false;
791
+ itemLayoutRef.current.didLayout = false;
633
792
  const timeout = setTimeout(() => {
634
- if (!didLayoutRef.current) {
793
+ if (!itemLayoutRef.current.didLayout) {
635
794
  const {
636
795
  itemKey: currentItemKey,
637
796
  lastSize,
@@ -639,7 +798,7 @@ var Container = typedMemo(function Container2({
639
798
  } = itemLayoutRef.current;
640
799
  if (lastSize && !isNullOrUndefined(currentItemKey)) {
641
800
  updateItemSizeFn(currentItemKey, lastSize);
642
- didLayoutRef.current = true;
801
+ itemLayoutRef.current.didLayout = true;
643
802
  }
644
803
  }
645
804
  }, 16);
@@ -649,7 +808,7 @@ var Container = typedMemo(function Container2({
649
808
  }
650
809
  }, [itemKey]);
651
810
  }
652
- const PositionComponent = isSticky ? PositionViewSticky : PositionView3;
811
+ const PositionComponent = isSticky ? stickyPositionComponentInternal ? stickyPositionComponentInternal : PositionViewSticky : positionComponentInternal ? positionComponentInternal : PositionView;
653
812
  return /* @__PURE__ */ React2__namespace.createElement(
654
813
  PositionComponent,
655
814
  {
@@ -660,7 +819,7 @@ var Container = typedMemo(function Container2({
660
819
  key: recycleItems ? void 0 : itemKey,
661
820
  onLayout,
662
821
  refView: ref,
663
- stickyOffset: isSticky ? stickyOffset : void 0,
822
+ stickyHeaderConfig,
664
823
  style
665
824
  },
666
825
  /* @__PURE__ */ React2__namespace.createElement(ContextContainer.Provider, { value: contextValue }, renderedItem, renderedItemInfo && ItemSeparatorComponent && /* @__PURE__ */ React2__namespace.createElement(Separator, { ItemSeparatorComponent, leadingItem: renderedItemInfo.item }))
@@ -673,6 +832,7 @@ var Containers = typedMemo(function Containers2({
673
832
  recycleItems,
674
833
  ItemSeparatorComponent,
675
834
  waitForInitialLayout,
835
+ stickyHeaderConfig,
676
836
  updateItemSize: updateItemSize2,
677
837
  getRenderedItem: getRenderedItem2
678
838
  }) {
@@ -684,10 +844,10 @@ var Containers = typedMemo(function Containers2({
684
844
  // If this is the initial scroll, we don't want to delay because we want to update the size immediately
685
845
  delay: (value, prevValue) => {
686
846
  var _a3;
687
- return !((_a3 = ctx.internalState) == null ? void 0 : _a3.initialScroll) ? !prevValue || value - prevValue > 20 ? 0 : 200 : void 0;
847
+ return !((_a3 = ctx.state) == null ? void 0 : _a3.initialScroll) ? !prevValue || value - prevValue > 20 ? 0 : 200 : void 0;
688
848
  }
689
849
  });
690
- const animOpacity = waitForInitialLayout && !IsNewArchitecture ? useValue$("containersDidLayout", { getValue: (value) => value ? 1 : 0 }) : void 0;
850
+ const animOpacity = waitForInitialLayout && !IsNewArchitecture ? useValue$("readyToRender", { getValue: (value) => value ? 1 : 0 }) : void 0;
691
851
  const otherAxisSize = useValue$("otherAxisSize", { delay: 0 });
692
852
  const containers = [];
693
853
  for (let i = 0; i < numContainers; i++) {
@@ -701,25 +861,26 @@ var Containers = typedMemo(function Containers2({
701
861
  id: i,
702
862
  key: i,
703
863
  recycleItems,
864
+ stickyHeaderConfig,
704
865
  updateItemSize: updateItemSize2
705
866
  }
706
867
  )
707
868
  );
708
869
  }
709
870
  const style = horizontal ? { minHeight: otherAxisSize, opacity: animOpacity, width: animSize } : { height: animSize, minWidth: otherAxisSize, opacity: animOpacity };
710
- if (columnWrapperStyle && numColumns > 1) {
871
+ if (columnWrapperStyle) {
711
872
  const { columnGap, rowGap, gap } = columnWrapperStyle;
712
873
  const gapX = columnGap || gap || 0;
713
874
  const gapY = rowGap || gap || 0;
714
875
  if (horizontal) {
715
- if (gapY) {
876
+ if (gapY && numColumns > 1) {
716
877
  style.marginVertical = -gapY / 2;
717
878
  }
718
879
  if (gapX) {
719
880
  style.marginRight = -gapX;
720
881
  }
721
882
  } else {
722
- if (gapX) {
883
+ if (gapX && numColumns > 1) {
723
884
  style.marginHorizontal = -gapX;
724
885
  }
725
886
  if (gapY) {
@@ -727,53 +888,15 @@ var Containers = typedMemo(function Containers2({
727
888
  }
728
889
  }
729
890
  }
730
- return /* @__PURE__ */ React2__namespace.createElement(reactNative.Animated.View, { style }, containers);
891
+ return /* @__PURE__ */ React2__namespace.createElement(ReactNative.Animated.View, { style }, containers);
731
892
  });
732
- function DevNumbers() {
733
- return IS_DEV && React2__namespace.memo(function DevNumbers2() {
734
- return Array.from({ length: 100 }).map((_, index) => /* @__PURE__ */ React2__namespace.createElement(
735
- reactNative.View,
736
- {
737
- key: index,
738
- style: {
739
- height: 100,
740
- pointerEvents: "none",
741
- position: "absolute",
742
- top: index * 100,
743
- width: "100%"
744
- }
745
- },
746
- /* @__PURE__ */ React2__namespace.createElement(reactNative.Text, { style: { color: "red" } }, index * 100)
747
- ));
748
- });
749
- }
750
- var ListComponentScrollView = reactNative.Animated.ScrollView;
751
- function Padding() {
752
- const animPaddingTop = useValue$("alignItemsPaddingTop", { delay: 0 });
753
- return /* @__PURE__ */ React2__namespace.createElement(reactNative.Animated.View, { style: { paddingTop: animPaddingTop } });
754
- }
755
- function PaddingDevMode() {
756
- const animPaddingTop = useValue$("alignItemsPaddingTop", { delay: 0 });
757
- return /* @__PURE__ */ React2__namespace.createElement(React2__namespace.Fragment, null, /* @__PURE__ */ React2__namespace.createElement(reactNative.Animated.View, { style: { paddingTop: animPaddingTop } }), /* @__PURE__ */ React2__namespace.createElement(
758
- reactNative.Animated.View,
759
- {
760
- style: {
761
- backgroundColor: "green",
762
- height: animPaddingTop,
763
- left: 0,
764
- position: "absolute",
765
- right: 0,
766
- top: 0
767
- }
768
- }
769
- ));
770
- }
893
+ var ListComponentScrollView = ReactNative.Animated.ScrollView;
771
894
  function ScrollAdjust() {
772
895
  const bias = 1e7;
773
896
  const [scrollAdjust, scrollAdjustUserOffset] = useArr$(["scrollAdjust", "scrollAdjustUserOffset"]);
774
897
  const scrollOffset = (scrollAdjust || 0) + (scrollAdjustUserOffset || 0) + bias;
775
898
  return /* @__PURE__ */ React2__namespace.createElement(
776
- reactNative.View,
899
+ ReactNative.View,
777
900
  {
778
901
  style: {
779
902
  height: 0,
@@ -790,22 +913,13 @@ function SnapWrapper({ ScrollComponent, ...props }) {
790
913
  return /* @__PURE__ */ React2__namespace.createElement(ScrollComponent, { ...props, snapToOffsets });
791
914
  }
792
915
  var LayoutView = ({ onLayoutChange, refView, ...rest }) => {
793
- const ref = refView != null ? refView : React2.useRef();
916
+ const ref = refView != null ? refView : React2.useRef(null);
794
917
  const { onLayout } = useOnLayoutSync({ onLayoutChange, ref });
795
- return /* @__PURE__ */ React2__namespace.createElement(reactNative.View, { ...rest, onLayout, ref });
918
+ return /* @__PURE__ */ React2__namespace.createElement(ReactNative.View, { ...rest, onLayout, ref });
796
919
  };
797
920
 
798
921
  // src/components/ListComponent.tsx
799
- var getComponent = (Component) => {
800
- if (React2__namespace.isValidElement(Component)) {
801
- return Component;
802
- }
803
- if (Component) {
804
- return /* @__PURE__ */ React2__namespace.createElement(Component, null);
805
- }
806
- return null;
807
- };
808
- var ListComponent = typedMemo(function ListComponent2({
922
+ var ListComponent = typedMemo2(function ListComponent2({
809
923
  canRender,
810
924
  style,
811
925
  contentContainerStyle,
@@ -813,7 +927,7 @@ var ListComponent = typedMemo(function ListComponent2({
813
927
  initialContentOffset,
814
928
  recycleItems,
815
929
  ItemSeparatorComponent,
816
- alignItemsAtEnd,
930
+ alignItemsAtEnd: _alignItemsAtEnd,
817
931
  waitForInitialLayout,
818
932
  onScroll: onScroll2,
819
933
  onLayout,
@@ -825,31 +939,52 @@ var ListComponent = typedMemo(function ListComponent2({
825
939
  getRenderedItem: getRenderedItem2,
826
940
  updateItemSize: updateItemSize2,
827
941
  refScrollView,
828
- maintainVisibleContentPosition,
829
942
  renderScrollComponent,
943
+ onLayoutFooter,
830
944
  scrollAdjustHandler,
831
- onLayoutHeader,
832
945
  snapToIndices,
946
+ stickyHeaderConfig,
833
947
  stickyHeaderIndices,
948
+ useWindowScroll = false,
834
949
  ...rest
835
950
  }) {
836
951
  const ctx = useStateContext();
952
+ const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
837
953
  const ScrollComponent = renderScrollComponent ? React2.useMemo(
838
- () => React2__namespace.forwardRef((props, ref) => renderScrollComponent({ ...props, ref })),
954
+ () => React2__namespace.forwardRef(
955
+ (props, ref) => renderScrollComponent({ ...props, ref })
956
+ ),
839
957
  [renderScrollComponent]
840
958
  ) : ListComponentScrollView;
841
- React2__namespace.useEffect(() => {
842
- if (canRender) {
843
- setTimeout(() => {
844
- scrollAdjustHandler.setMounted();
845
- }, 0);
846
- }
847
- }, [canRender]);
848
959
  const SnapOrScroll = snapToIndices ? SnapWrapper : ScrollComponent;
960
+ React2.useLayoutEffect(() => {
961
+ if (!ListHeaderComponent) {
962
+ set$(ctx, "headerSize", 0);
963
+ }
964
+ if (!ListFooterComponent) {
965
+ set$(ctx, "footerSize", 0);
966
+ }
967
+ }, [ListHeaderComponent, ListFooterComponent, ctx]);
968
+ const onLayoutHeader = React2.useCallback(
969
+ (rect) => {
970
+ const size = rect[horizontal ? "width" : "height"];
971
+ set$(ctx, "headerSize", size);
972
+ },
973
+ [ctx, horizontal]
974
+ );
975
+ const onLayoutFooterInternal = React2.useCallback(
976
+ (rect, fromLayoutEffect) => {
977
+ const size = rect[horizontal ? "width" : "height"];
978
+ set$(ctx, "footerSize", size);
979
+ onLayoutFooter == null ? void 0 : onLayoutFooter(rect, fromLayoutEffect);
980
+ },
981
+ [ctx, horizontal, onLayoutFooter]
982
+ );
849
983
  return /* @__PURE__ */ React2__namespace.createElement(
850
984
  SnapOrScroll,
851
985
  {
852
986
  ...rest,
987
+ ...ScrollComponent === ListComponentScrollView ? { useWindowScroll } : {},
853
988
  contentContainerStyle: [
854
989
  contentContainerStyle,
855
990
  horizontal ? {
@@ -858,7 +993,7 @@ var ListComponent = typedMemo(function ListComponent2({
858
993
  ],
859
994
  contentOffset: initialContentOffset ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
860
995
  horizontal,
861
- maintainVisibleContentPosition: maintainVisibleContentPosition ? { minIndexForVisible: 0 } : void 0,
996
+ maintainVisibleContentPosition: maintainVisibleContentPosition.size || maintainVisibleContentPosition.data ? { minIndexForVisible: 0 } : void 0,
862
997
  onLayout,
863
998
  onScroll: onScroll2,
864
999
  ref: refScrollView,
@@ -866,7 +1001,6 @@ var ListComponent = typedMemo(function ListComponent2({
866
1001
  style
867
1002
  },
868
1003
  /* @__PURE__ */ React2__namespace.createElement(ScrollAdjust, null),
869
- ENABLE_DEVMODE ? /* @__PURE__ */ React2__namespace.createElement(PaddingDevMode, null) : /* @__PURE__ */ React2__namespace.createElement(Padding, null),
870
1004
  ListHeaderComponent && /* @__PURE__ */ React2__namespace.createElement(LayoutView, { onLayoutChange: onLayoutHeader, style: ListHeaderComponentStyle }, getComponent(ListHeaderComponent)),
871
1005
  ListEmptyComponent && getComponent(ListEmptyComponent),
872
1006
  canRender && !ListEmptyComponent && /* @__PURE__ */ React2__namespace.createElement(
@@ -876,25 +1010,27 @@ var ListComponent = typedMemo(function ListComponent2({
876
1010
  horizontal,
877
1011
  ItemSeparatorComponent,
878
1012
  recycleItems,
1013
+ stickyHeaderConfig,
879
1014
  updateItemSize: updateItemSize2,
880
1015
  waitForInitialLayout
881
1016
  }
882
1017
  ),
883
- ListFooterComponent && /* @__PURE__ */ React2__namespace.createElement(
884
- LayoutView,
885
- {
886
- onLayoutChange: (layout) => {
887
- const size = layout[horizontal ? "width" : "height"];
888
- set$(ctx, "footerSize", size);
889
- },
890
- style: ListFooterComponentStyle
891
- },
892
- getComponent(ListFooterComponent)
893
- ),
894
- IS_DEV && ENABLE_DEVMODE && /* @__PURE__ */ React2__namespace.createElement(DevNumbers, null)
1018
+ ListFooterComponent && /* @__PURE__ */ React2__namespace.createElement(LayoutView, { onLayoutChange: onLayoutFooterInternal, style: ListFooterComponentStyle }, getComponent(ListFooterComponent)),
1019
+ IS_DEV && ENABLE_DEVMODE
895
1020
  );
896
1021
  });
897
1022
 
1023
+ // src/core/calculateOffsetForIndex.ts
1024
+ function calculateOffsetForIndex(ctx, index) {
1025
+ const state = ctx.state;
1026
+ return index !== void 0 ? state.positions[index] || 0 : 0;
1027
+ }
1028
+
1029
+ // src/core/getTopOffsetAdjustment.ts
1030
+ function getTopOffsetAdjustment(ctx) {
1031
+ return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
1032
+ }
1033
+
898
1034
  // src/utils/getId.ts
899
1035
  function getId(state, index) {
900
1036
  const { data, keyExtractor } = state.props;
@@ -907,61 +1043,9 @@ function getId(state, index) {
907
1043
  return id;
908
1044
  }
909
1045
 
910
- // src/core/calculateOffsetForIndex.ts
911
- function calculateOffsetForIndex(ctx, state, index) {
912
- let position = 0;
913
- if (index !== void 0) {
914
- position = (state == null ? void 0 : state.positions.get(getId(state, index))) || 0;
915
- const paddingTop = peek$(ctx, "stylePaddingTop");
916
- if (paddingTop) {
917
- position += paddingTop;
918
- }
919
- const headerSize = peek$(ctx, "headerSize");
920
- if (headerSize) {
921
- position += headerSize;
922
- }
923
- }
924
- return position;
925
- }
926
-
927
- // src/utils/setPaddingTop.ts
928
- function setPaddingTop(ctx, state, { stylePaddingTop, alignItemsPaddingTop }) {
929
- if (stylePaddingTop !== void 0) {
930
- const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
931
- if (stylePaddingTop < prevStylePaddingTop) {
932
- let prevTotalSize = peek$(ctx, "totalSize") || 0;
933
- set$(ctx, "totalSize", prevTotalSize + prevStylePaddingTop);
934
- state.timeoutSetPaddingTop = setTimeout(() => {
935
- prevTotalSize = peek$(ctx, "totalSize") || 0;
936
- set$(ctx, "totalSize", prevTotalSize - prevStylePaddingTop);
937
- }, 16);
938
- }
939
- set$(ctx, "stylePaddingTop", stylePaddingTop);
940
- }
941
- if (alignItemsPaddingTop !== void 0) {
942
- set$(ctx, "alignItemsPaddingTop", alignItemsPaddingTop);
943
- }
944
- }
945
-
946
- // src/utils/updateAlignItemsPaddingTop.ts
947
- function updateAlignItemsPaddingTop(ctx, state) {
948
- const {
949
- scrollLength,
950
- props: { alignItemsAtEnd, data }
951
- } = state;
952
- if (alignItemsAtEnd) {
953
- let alignItemsPaddingTop = 0;
954
- if ((data == null ? void 0 : data.length) > 0) {
955
- const contentSize = getContentSize(ctx);
956
- alignItemsPaddingTop = Math.max(0, Math.floor(scrollLength - contentSize));
957
- }
958
- setPaddingTop(ctx, state, { alignItemsPaddingTop });
959
- }
960
- }
961
-
962
1046
  // src/core/addTotalSize.ts
963
- function addTotalSize(ctx, state, key, add) {
964
- const { alignItemsAtEnd } = state.props;
1047
+ function addTotalSize(ctx, key, add) {
1048
+ const state = ctx.state;
965
1049
  const prevTotalSize = state.totalSize;
966
1050
  let totalSize = state.totalSize;
967
1051
  if (key === null) {
@@ -980,48 +1064,47 @@ function addTotalSize(ctx, state, key, add) {
980
1064
  state.pendingTotalSize = void 0;
981
1065
  state.totalSize = totalSize;
982
1066
  set$(ctx, "totalSize", totalSize);
983
- if (alignItemsAtEnd) {
984
- updateAlignItemsPaddingTop(ctx, state);
985
- }
986
1067
  }
987
1068
  }
988
1069
  }
989
1070
 
990
1071
  // src/core/setSize.ts
991
- function setSize(ctx, state, itemKey, size) {
1072
+ function setSize(ctx, itemKey, size) {
1073
+ const state = ctx.state;
992
1074
  const { sizes } = state;
993
1075
  const previousSize = sizes.get(itemKey);
994
1076
  const diff = previousSize !== void 0 ? size - previousSize : size;
995
1077
  if (diff !== 0) {
996
- addTotalSize(ctx, state, itemKey, diff);
1078
+ addTotalSize(ctx, itemKey, diff);
997
1079
  }
998
1080
  sizes.set(itemKey, size);
999
1081
  }
1000
1082
 
1001
1083
  // src/utils/getItemSize.ts
1002
- function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedSize) {
1084
+ function getItemSize(ctx, key, index, data, useAverageSize, preferCachedSize) {
1003
1085
  var _a3, _b;
1086
+ const state = ctx.state;
1004
1087
  const {
1005
1088
  sizesKnown,
1006
1089
  sizes,
1007
1090
  averageSizes,
1008
- props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType }
1091
+ props: { estimatedItemSize, getEstimatedItemSize, getFixedItemSize, getItemType },
1092
+ scrollingTo
1009
1093
  } = state;
1010
1094
  const sizeKnown = sizesKnown.get(key);
1011
1095
  if (sizeKnown !== void 0) {
1012
1096
  return sizeKnown;
1013
1097
  }
1014
1098
  let size;
1015
- const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1016
- const scrollingTo = peek$(ctx, "scrollingTo");
1017
1099
  if (preferCachedSize) {
1018
1100
  const cachedSize = sizes.get(key);
1019
1101
  if (cachedSize !== void 0) {
1020
1102
  return cachedSize;
1021
1103
  }
1022
1104
  }
1105
+ const itemType = getItemType ? (_a3 = getItemType(data, index)) != null ? _a3 : "" : "";
1023
1106
  if (getFixedItemSize) {
1024
- size = getFixedItemSize(index, data, itemType);
1107
+ size = getFixedItemSize(data, index, itemType);
1025
1108
  if (size !== void 0) {
1026
1109
  sizesKnown.set(key, size);
1027
1110
  }
@@ -1039,96 +1122,61 @@ function getItemSize(ctx, state, key, index, data, useAverageSize, preferCachedS
1039
1122
  }
1040
1123
  }
1041
1124
  if (size === void 0) {
1042
- size = getEstimatedItemSize ? getEstimatedItemSize(index, data, itemType) : estimatedItemSize;
1125
+ size = getEstimatedItemSize ? getEstimatedItemSize(data, index, itemType) : estimatedItemSize;
1043
1126
  }
1044
- setSize(ctx, state, key, size);
1127
+ setSize(ctx, key, size);
1045
1128
  return size;
1046
1129
  }
1047
1130
 
1048
1131
  // src/core/calculateOffsetWithOffsetPosition.ts
1049
- function calculateOffsetWithOffsetPosition(ctx, state, offsetParam, params) {
1132
+ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1133
+ const state = ctx.state;
1050
1134
  const { index, viewOffset, viewPosition } = params;
1051
1135
  let offset = offsetParam;
1052
1136
  if (viewOffset) {
1053
1137
  offset -= viewOffset;
1054
1138
  }
1055
- if (viewPosition !== void 0 && index !== void 0) {
1056
- offset -= viewPosition * (state.scrollLength - getItemSize(ctx, state, getId(state, index), index, state.props.data[index]));
1057
- }
1058
- return offset;
1059
- }
1060
-
1061
- // src/core/finishScrollTo.ts
1062
- function finishScrollTo(ctx, state) {
1063
- var _a3, _b;
1064
- if (state) {
1065
- state.scrollHistory.length = 0;
1066
- state.initialScroll = void 0;
1067
- state.initialAnchor = void 0;
1068
- set$(ctx, "scrollingTo", void 0);
1069
- if (state.pendingTotalSize !== void 0) {
1070
- addTotalSize(ctx, state, null, state.pendingTotalSize);
1139
+ if (index !== void 0) {
1140
+ const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
1141
+ if (topOffsetAdjustment) {
1142
+ offset += topOffsetAdjustment;
1071
1143
  }
1072
- if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1073
- (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1144
+ }
1145
+ if (viewPosition !== void 0 && index !== void 0) {
1146
+ const itemSize = getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1147
+ const trailingInset = getContentInsetEnd(state);
1148
+ offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1149
+ if (index === state.props.data.length - 1) {
1150
+ const footerSize = peek$(ctx, "footerSize") || 0;
1151
+ offset += footerSize;
1074
1152
  }
1075
1153
  }
1154
+ return offset;
1076
1155
  }
1077
- var Platform2 = reactNative.Platform;
1078
1156
 
1079
- // src/core/scrollTo.ts
1080
- function scrollTo(ctx, state, params) {
1081
- var _a3;
1082
- const { noScrollingTo, ...scrollTarget } = params;
1083
- const { animated, isInitialScroll, offset: scrollTargetOffset, precomputedWithViewOffset } = scrollTarget;
1084
- const {
1085
- refScroller,
1086
- props: { horizontal }
1087
- } = state;
1088
- let offset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, state, scrollTargetOffset, scrollTarget);
1089
- if (Number.isFinite(state.scrollLength) && Number.isFinite(state.totalSize)) {
1090
- const maxOffset = Math.max(0, getContentSize(ctx) - state.scrollLength);
1091
- offset = Math.min(offset, maxOffset);
1092
- }
1093
- state.scrollHistory.length = 0;
1094
- if (!noScrollingTo) {
1095
- set$(ctx, "scrollingTo", scrollTarget);
1096
- }
1097
- state.scrollPending = offset;
1098
- if (!isInitialScroll || Platform2.OS === "android") {
1099
- (_a3 = refScroller.current) == null ? void 0 : _a3.scrollTo({
1100
- animated: !!animated,
1101
- x: horizontal ? offset : 0,
1102
- y: horizontal ? 0 : offset
1103
- });
1104
- }
1105
- if (!animated) {
1106
- state.scroll = offset;
1107
- if (Platform2.OS === "web") {
1108
- const unlisten = listen$(ctx, "containersDidLayout", (value) => {
1109
- if (value && peek$(ctx, "scrollingTo")) {
1110
- finishScrollTo(ctx, state);
1111
- unlisten();
1112
- }
1113
- });
1114
- } else {
1115
- setTimeout(() => finishScrollTo(ctx, state), 100);
1116
- }
1117
- if (isInitialScroll) {
1118
- setTimeout(() => {
1119
- state.initialScroll = void 0;
1120
- }, 500);
1121
- }
1122
- }
1157
+ // src/core/clampScrollOffset.ts
1158
+ function clampScrollOffset(ctx, offset, scrollTarget) {
1159
+ const state = ctx.state;
1160
+ const contentSize = getContentSize(ctx);
1161
+ let clampedOffset = offset;
1162
+ if (Number.isFinite(contentSize) && Number.isFinite(state.scrollLength) && (Platform2.OS !== "android" || state.lastLayout)) {
1163
+ const baseMaxOffset = Math.max(0, contentSize - state.scrollLength);
1164
+ const viewOffset = scrollTarget == null ? void 0 : scrollTarget.viewOffset;
1165
+ const extraEndOffset = typeof viewOffset === "number" && viewOffset < 0 ? -viewOffset : 0;
1166
+ const maxOffset = baseMaxOffset + extraEndOffset;
1167
+ clampedOffset = Math.min(offset, maxOffset);
1168
+ }
1169
+ clampedOffset = Math.max(0, clampedOffset);
1170
+ return clampedOffset;
1123
1171
  }
1124
1172
 
1125
1173
  // src/utils/checkThreshold.ts
1126
1174
  var HYSTERESIS_MULTIPLIER = 1.3;
1127
- var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot) => {
1175
+ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot, allowReentryOnChange) => {
1128
1176
  const absDistance = Math.abs(distance);
1129
1177
  const within = atThreshold || threshold > 0 && absDistance <= threshold;
1130
1178
  const updateSnapshot = () => {
1131
- setSnapshot == null ? void 0 : setSnapshot({
1179
+ setSnapshot({
1132
1180
  atThreshold,
1133
1181
  contentSize: context.contentSize,
1134
1182
  dataLength: context.dataLength,
@@ -1139,19 +1187,21 @@ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, co
1139
1187
  if (!within) {
1140
1188
  return false;
1141
1189
  }
1142
- onReached == null ? void 0 : onReached(distance);
1190
+ onReached(distance);
1143
1191
  updateSnapshot();
1144
1192
  return true;
1145
1193
  }
1146
1194
  const reset = !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
1147
1195
  if (reset) {
1148
- setSnapshot == null ? void 0 : setSnapshot(void 0);
1196
+ setSnapshot(void 0);
1149
1197
  return false;
1150
1198
  }
1151
1199
  if (within) {
1152
1200
  const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
1153
1201
  if (changed) {
1154
- onReached == null ? void 0 : onReached(distance);
1202
+ if (allowReentryOnChange) {
1203
+ onReached(distance);
1204
+ }
1155
1205
  updateSnapshot();
1156
1206
  }
1157
1207
  }
@@ -1159,9 +1209,10 @@ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, co
1159
1209
  };
1160
1210
 
1161
1211
  // src/utils/checkAtBottom.ts
1162
- function checkAtBottom(ctx, state) {
1212
+ function checkAtBottom(ctx) {
1163
1213
  var _a3;
1164
- if (!state) {
1214
+ const state = ctx.state;
1215
+ if (!state || state.initialScroll) {
1165
1216
  return;
1166
1217
  }
1167
1218
  const {
@@ -1171,9 +1222,13 @@ function checkAtBottom(ctx, state) {
1171
1222
  maintainingScrollAtEnd,
1172
1223
  props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
1173
1224
  } = state;
1225
+ if (state.initialScroll) {
1226
+ return;
1227
+ }
1174
1228
  const contentSize = getContentSize(ctx);
1175
1229
  if (contentSize > 0 && queuedInitialLayout && !maintainingScrollAtEnd) {
1176
- const distanceFromEnd = contentSize - scroll - scrollLength;
1230
+ const insetEnd = getContentInsetEnd(state);
1231
+ const distanceFromEnd = contentSize - scroll - scrollLength - insetEnd;
1177
1232
  const isContentLess = contentSize < scrollLength;
1178
1233
  state.isAtEnd = isContentLess || distanceFromEnd < scrollLength * maintainScrollAtEndThreshold;
1179
1234
  state.isEndReached = checkThreshold(
@@ -1193,72 +1248,250 @@ function checkAtBottom(ctx, state) {
1193
1248
  },
1194
1249
  (snapshot) => {
1195
1250
  state.endReachedSnapshot = snapshot;
1196
- }
1251
+ },
1252
+ true
1197
1253
  );
1198
1254
  }
1199
1255
  }
1200
1256
 
1201
1257
  // src/utils/checkAtTop.ts
1202
- function checkAtTop(state) {
1203
- var _a3;
1204
- if (!state) {
1258
+ function checkAtTop(ctx) {
1259
+ const state = ctx == null ? void 0 : ctx.state;
1260
+ if (!state || state.initialScroll || state.scrollingTo) {
1205
1261
  return;
1206
1262
  }
1207
1263
  const {
1208
- scrollLength,
1264
+ dataChangeEpoch,
1265
+ isStartReached,
1266
+ props: { data, onStartReachedThreshold },
1209
1267
  scroll,
1210
- props: { onStartReachedThreshold }
1268
+ scrollLength,
1269
+ startReachedSnapshot,
1270
+ startReachedSnapshotDataChangeEpoch,
1271
+ totalSize
1211
1272
  } = state;
1212
- const distanceFromTop = scroll;
1213
- state.isAtStart = distanceFromTop <= 0;
1273
+ const dataLength = data.length;
1274
+ const threshold = onStartReachedThreshold * scrollLength;
1275
+ const dataChanged = startReachedSnapshotDataChangeEpoch !== dataChangeEpoch;
1276
+ const withinThreshold = threshold > 0 && Math.abs(scroll) <= threshold;
1277
+ const allowReentryOnDataChange = !!isStartReached && withinThreshold && !!dataChanged && !isInMVCPActiveMode(state);
1278
+ if (isStartReached && threshold > 0 && scroll > threshold && startReachedSnapshot && (dataChanged || startReachedSnapshot.contentSize !== totalSize || startReachedSnapshot.dataLength !== dataLength)) {
1279
+ state.isStartReached = false;
1280
+ state.startReachedSnapshot = void 0;
1281
+ state.startReachedSnapshotDataChangeEpoch = void 0;
1282
+ }
1283
+ state.isAtStart = scroll <= 0;
1284
+ if (isStartReached && withinThreshold && dataChanged && !allowReentryOnDataChange) {
1285
+ return;
1286
+ }
1214
1287
  state.isStartReached = checkThreshold(
1215
- distanceFromTop,
1288
+ scroll,
1216
1289
  false,
1217
- onStartReachedThreshold * scrollLength,
1290
+ threshold,
1218
1291
  state.isStartReached,
1219
- state.startReachedSnapshot,
1292
+ allowReentryOnDataChange ? void 0 : startReachedSnapshot,
1220
1293
  {
1221
- contentSize: state.totalSize,
1222
- dataLength: (_a3 = state.props.data) == null ? void 0 : _a3.length,
1294
+ contentSize: totalSize,
1295
+ dataLength,
1223
1296
  scrollPosition: scroll
1224
1297
  },
1225
1298
  (distance) => {
1226
- var _a4, _b;
1227
- return (_b = (_a4 = state.props).onStartReached) == null ? void 0 : _b.call(_a4, { distanceFromStart: distance });
1299
+ var _a3, _b;
1300
+ return (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
1228
1301
  },
1229
1302
  (snapshot) => {
1230
1303
  state.startReachedSnapshot = snapshot;
1304
+ state.startReachedSnapshotDataChangeEpoch = snapshot ? dataChangeEpoch : void 0;
1305
+ },
1306
+ allowReentryOnDataChange
1307
+ );
1308
+ }
1309
+
1310
+ // src/utils/checkThresholds.ts
1311
+ function checkThresholds(ctx) {
1312
+ checkAtBottom(ctx);
1313
+ checkAtTop(ctx);
1314
+ }
1315
+
1316
+ // src/utils/setInitialRenderState.ts
1317
+ function setInitialRenderState(ctx, {
1318
+ didLayout,
1319
+ didInitialScroll
1320
+ }) {
1321
+ const { state } = ctx;
1322
+ const {
1323
+ loadStartTime,
1324
+ props: { onLoad }
1325
+ } = state;
1326
+ if (didLayout) {
1327
+ state.didContainersLayout = true;
1328
+ }
1329
+ if (didInitialScroll) {
1330
+ state.didFinishInitialScroll = true;
1331
+ }
1332
+ const isReadyToRender = Boolean(state.didContainersLayout && state.didFinishInitialScroll);
1333
+ if (isReadyToRender && !peek$(ctx, "readyToRender")) {
1334
+ set$(ctx, "readyToRender", true);
1335
+ if (onLoad) {
1336
+ onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
1337
+ }
1338
+ }
1339
+ }
1340
+
1341
+ // src/core/finishScrollTo.ts
1342
+ function finishScrollTo(ctx) {
1343
+ var _a3, _b;
1344
+ const state = ctx.state;
1345
+ if (state == null ? void 0 : state.scrollingTo) {
1346
+ const resolvePendingScroll = state.pendingScrollResolve;
1347
+ state.pendingScrollResolve = void 0;
1348
+ const scrollingTo = state.scrollingTo;
1349
+ state.scrollHistory.length = 0;
1350
+ state.initialScroll = void 0;
1351
+ state.initialAnchor = void 0;
1352
+ state.scrollingTo = void 0;
1353
+ if (state.pendingTotalSize !== void 0) {
1354
+ addTotalSize(ctx, null, state.pendingTotalSize);
1355
+ }
1356
+ if ((_a3 = state.props) == null ? void 0 : _a3.data) {
1357
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
1231
1358
  }
1359
+ if (PlatformAdjustBreaksScroll) {
1360
+ state.scrollAdjustHandler.commitPendingAdjust(scrollingTo);
1361
+ }
1362
+ setInitialRenderState(ctx, { didInitialScroll: true });
1363
+ checkThresholds(ctx);
1364
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1365
+ }
1366
+ }
1367
+
1368
+ // src/core/checkFinishedScroll.ts
1369
+ function checkFinishedScroll(ctx) {
1370
+ ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
1371
+ }
1372
+ function checkFinishedScrollFrame(ctx) {
1373
+ const scrollingTo = ctx.state.scrollingTo;
1374
+ if (scrollingTo) {
1375
+ const { state } = ctx;
1376
+ state.animFrameCheckFinishedScroll = void 0;
1377
+ const scroll = state.scrollPending;
1378
+ const adjust = state.scrollAdjustHandler.getAdjust();
1379
+ const clampedTargetOffset = clampScrollOffset(
1380
+ ctx,
1381
+ scrollingTo.offset - (scrollingTo.viewOffset || 0),
1382
+ scrollingTo
1383
+ );
1384
+ const maxOffset = clampScrollOffset(ctx, scroll, scrollingTo);
1385
+ const diff1 = Math.abs(scroll - clampedTargetOffset);
1386
+ const diff2 = Math.abs(diff1 - adjust);
1387
+ const isNotOverscrolled = Math.abs(scroll - maxOffset) < 1;
1388
+ const isAtTarget = diff1 < 1 || !scrollingTo.animated && diff2 < 1;
1389
+ if (isNotOverscrolled && isAtTarget) {
1390
+ finishScrollTo(ctx);
1391
+ }
1392
+ }
1393
+ }
1394
+ function checkFinishedScrollFallback(ctx) {
1395
+ const state = ctx.state;
1396
+ const scrollingTo = state.scrollingTo;
1397
+ const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) || !state.didContainersLayout;
1398
+ state.timeoutCheckFinishedScrollFallback = setTimeout(
1399
+ () => {
1400
+ let numChecks = 0;
1401
+ const checkHasScrolled = () => {
1402
+ state.timeoutCheckFinishedScrollFallback = void 0;
1403
+ const isStillScrollingTo = state.scrollingTo;
1404
+ if (isStillScrollingTo) {
1405
+ numChecks++;
1406
+ if (state.hasScrolled || numChecks > 5) {
1407
+ finishScrollTo(ctx);
1408
+ } else {
1409
+ state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, 100);
1410
+ }
1411
+ }
1412
+ };
1413
+ checkHasScrolled();
1414
+ },
1415
+ slowTimeout ? 500 : 100
1232
1416
  );
1233
1417
  }
1234
1418
 
1419
+ // src/core/doScrollTo.native.ts
1420
+ function doScrollTo(ctx, params) {
1421
+ const state = ctx.state;
1422
+ const { animated, horizontal, offset } = params;
1423
+ const isAnimated = !!animated;
1424
+ const { refScroller } = state;
1425
+ const scroller = refScroller.current;
1426
+ if (!scroller) {
1427
+ return;
1428
+ }
1429
+ scroller.scrollTo({
1430
+ animated: isAnimated,
1431
+ x: horizontal ? offset : 0,
1432
+ y: horizontal ? 0 : offset
1433
+ });
1434
+ if (!isAnimated) {
1435
+ state.scroll = offset;
1436
+ checkFinishedScrollFallback(ctx);
1437
+ }
1438
+ }
1439
+
1440
+ // src/core/scrollTo.ts
1441
+ function scrollTo(ctx, params) {
1442
+ const state = ctx.state;
1443
+ const { noScrollingTo, forceScroll, ...scrollTarget } = params;
1444
+ const { animated, isInitialScroll, offset: scrollTargetOffset, precomputedWithViewOffset } = scrollTarget;
1445
+ const {
1446
+ props: { horizontal }
1447
+ } = state;
1448
+ if (state.animFrameCheckFinishedScroll) {
1449
+ cancelAnimationFrame(ctx.state.animFrameCheckFinishedScroll);
1450
+ }
1451
+ if (state.timeoutCheckFinishedScrollFallback) {
1452
+ clearTimeout(ctx.state.timeoutCheckFinishedScrollFallback);
1453
+ }
1454
+ let offset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, scrollTargetOffset, scrollTarget);
1455
+ offset = clampScrollOffset(ctx, offset, scrollTarget);
1456
+ state.scrollHistory.length = 0;
1457
+ if (!noScrollingTo) {
1458
+ state.scrollingTo = scrollTarget;
1459
+ }
1460
+ state.scrollPending = offset;
1461
+ if (forceScroll || !isInitialScroll || Platform2.OS === "android") {
1462
+ doScrollTo(ctx, { animated, horizontal, offset });
1463
+ } else {
1464
+ state.scroll = offset;
1465
+ }
1466
+ }
1467
+
1468
+ // src/platform/flushSync.native.ts
1469
+ var flushSync = (fn) => {
1470
+ fn();
1471
+ };
1472
+
1235
1473
  // src/core/updateScroll.ts
1236
- function updateScroll(ctx, state, newScroll, forceUpdate) {
1237
- var _a3;
1238
- const scrollingTo = peek$(ctx, "scrollingTo");
1474
+ function updateScroll(ctx, newScroll, forceUpdate) {
1475
+ const state = ctx.state;
1476
+ const { ignoreScrollFromMVCP, lastScrollAdjustForHistory, scrollAdjustHandler, scrollHistory, scrollingTo } = state;
1477
+ const prevScroll = state.scroll;
1239
1478
  state.hasScrolled = true;
1240
1479
  state.lastBatchingAction = Date.now();
1241
1480
  const currentTime = Date.now();
1242
- const adjust = state.scrollAdjustHandler.getAdjust();
1243
- const lastHistoryAdjust = state.lastScrollAdjustForHistory;
1244
- const adjustChanged = lastHistoryAdjust !== void 0 && Math.abs(adjust - lastHistoryAdjust) > 0.1;
1481
+ const adjust = scrollAdjustHandler.getAdjust();
1482
+ const adjustChanged = lastScrollAdjustForHistory !== void 0 && Math.abs(adjust - lastScrollAdjustForHistory) > 0.1;
1245
1483
  if (adjustChanged) {
1246
- state.scrollHistory.length = 0;
1484
+ scrollHistory.length = 0;
1247
1485
  }
1248
1486
  state.lastScrollAdjustForHistory = adjust;
1249
- if (scrollingTo === void 0 && !(state.scrollHistory.length === 0 && newScroll === state.scroll)) {
1487
+ if (scrollingTo === void 0 && !(scrollHistory.length === 0 && newScroll === state.scroll)) {
1250
1488
  if (!adjustChanged) {
1251
- state.scrollHistory.push({ scroll: newScroll, time: currentTime });
1489
+ scrollHistory.push({ scroll: newScroll, time: currentTime });
1252
1490
  }
1253
1491
  }
1254
- if (state.scrollHistory.length > 5) {
1255
- state.scrollHistory.shift();
1492
+ if (scrollHistory.length > 5) {
1493
+ scrollHistory.shift();
1256
1494
  }
1257
- state.scrollPrev = state.scroll;
1258
- state.scrollPrevTime = state.scrollTime;
1259
- state.scroll = newScroll;
1260
- state.scrollTime = currentTime;
1261
- const ignoreScrollFromMVCP = state.ignoreScrollFromMVCP;
1262
1495
  if (ignoreScrollFromMVCP && !scrollingTo) {
1263
1496
  const { lt, gt } = ignoreScrollFromMVCP;
1264
1497
  if (lt && newScroll < lt || gt && newScroll > gt) {
@@ -1266,22 +1499,42 @@ function updateScroll(ctx, state, newScroll, forceUpdate) {
1266
1499
  return;
1267
1500
  }
1268
1501
  }
1269
- if (forceUpdate || state.dataChangeNeedsScrollUpdate || Math.abs(state.scroll - state.scrollPrev) > 2) {
1502
+ state.scrollPrev = prevScroll;
1503
+ state.scrollPrevTime = state.scrollTime;
1504
+ state.scroll = newScroll;
1505
+ state.scrollTime = currentTime;
1506
+ const scrollDelta = Math.abs(newScroll - prevScroll);
1507
+ const scrollLength = state.scrollLength;
1508
+ const lastCalculated = state.scrollLastCalculate;
1509
+ const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
1510
+ const shouldUpdate = useAggressiveItemRecalculation || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
1511
+ if (shouldUpdate) {
1512
+ state.scrollLastCalculate = state.scroll;
1270
1513
  state.ignoreScrollFromMVCPIgnored = false;
1271
- (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { doMVCP: scrollingTo !== void 0 });
1272
- checkAtBottom(ctx, state);
1273
- checkAtTop(state);
1514
+ state.lastScrollDelta = scrollDelta;
1515
+ const runCalculateItems = () => {
1516
+ var _a3;
1517
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state, { doMVCP: scrollingTo !== void 0 });
1518
+ checkThresholds(ctx);
1519
+ };
1520
+ if (Platform2.OS === "web" && scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength) {
1521
+ flushSync(runCalculateItems);
1522
+ } else {
1523
+ runCalculateItems();
1524
+ }
1274
1525
  state.dataChangeNeedsScrollUpdate = false;
1526
+ state.lastScrollDelta = 0;
1275
1527
  }
1276
1528
  }
1277
1529
 
1278
1530
  // src/utils/requestAdjust.ts
1279
- function requestAdjust(ctx, state, positionDiff, dataChanged) {
1531
+ function requestAdjust(ctx, positionDiff, dataChanged) {
1532
+ const state = ctx.state;
1280
1533
  if (Math.abs(positionDiff) > 0.1) {
1281
1534
  const needsScrollWorkaround = Platform2.OS === "android" && !IsNewArchitecture && dataChanged && state.scroll <= positionDiff;
1282
1535
  const doit = () => {
1283
1536
  if (needsScrollWorkaround) {
1284
- scrollTo(ctx, state, {
1537
+ scrollTo(ctx, {
1285
1538
  noScrollingTo: true,
1286
1539
  offset: state.scroll
1287
1540
  });
@@ -1294,8 +1547,8 @@ function requestAdjust(ctx, state, positionDiff, dataChanged) {
1294
1547
  };
1295
1548
  state.scroll += positionDiff;
1296
1549
  state.scrollForNextCalculateItemsInView = void 0;
1297
- const didLayout = peek$(ctx, "containersDidLayout");
1298
- if (didLayout) {
1550
+ const readyToRender = peek$(ctx, "readyToRender");
1551
+ if (readyToRender) {
1299
1552
  doit();
1300
1553
  if (Platform2.OS !== "web") {
1301
1554
  const threshold = state.scroll - positionDiff / 2;
@@ -1317,7 +1570,7 @@ function requestAdjust(ctx, state, positionDiff, dataChanged) {
1317
1570
  if (shouldForceUpdate) {
1318
1571
  state.ignoreScrollFromMVCPIgnored = false;
1319
1572
  state.scrollPending = state.scroll;
1320
- updateScroll(ctx, state, state.scroll, true);
1573
+ updateScroll(ctx, state.scroll, true);
1321
1574
  }
1322
1575
  }, delay);
1323
1576
  }
@@ -1332,28 +1585,28 @@ function requestAdjust(ctx, state, positionDiff, dataChanged) {
1332
1585
  var INITIAL_ANCHOR_TOLERANCE = 0.5;
1333
1586
  var INITIAL_ANCHOR_MAX_ATTEMPTS = 4;
1334
1587
  var INITIAL_ANCHOR_SETTLED_TICKS = 2;
1335
- function ensureInitialAnchor(ctx, state) {
1588
+ function ensureInitialAnchor(ctx) {
1336
1589
  var _a3, _b, _c, _d, _e;
1337
- const anchor = state.initialAnchor;
1590
+ const state = ctx.state;
1591
+ const { initialAnchor, didContainersLayout, scroll, scrollLength } = state;
1592
+ const anchor = initialAnchor;
1338
1593
  const item = state.props.data[anchor.index];
1339
- const containersDidLayout = peek$(ctx, "containersDidLayout");
1340
- if (!containersDidLayout) {
1594
+ if (!didContainersLayout) {
1341
1595
  return;
1342
1596
  }
1343
1597
  const id = getId(state, anchor.index);
1344
- if (state.positions.get(id) === void 0) {
1598
+ if (state.positions[anchor.index] === void 0) {
1345
1599
  return;
1346
1600
  }
1347
- const size = getItemSize(ctx, state, id, anchor.index, item, true, true);
1601
+ const size = getItemSize(ctx, id, anchor.index, item, true, true);
1348
1602
  if (size === void 0) {
1349
1603
  return;
1350
1604
  }
1351
- const availableSpace = Math.max(0, state.scrollLength - size);
1352
- const desiredOffset = calculateOffsetForIndex(ctx, state, anchor.index) - ((_a3 = anchor.viewOffset) != null ? _a3 : 0) - ((_b = anchor.viewPosition) != null ? _b : 0) * availableSpace;
1353
- const contentSize = getContentSize(ctx);
1354
- const maxOffset = Math.max(0, contentSize - state.scrollLength);
1355
- const clampedDesiredOffset = Math.max(0, Math.min(desiredOffset, maxOffset));
1356
- const delta = clampedDesiredOffset - state.scroll;
1605
+ const availableSpace = Math.max(0, scrollLength - size);
1606
+ const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
1607
+ const desiredOffset = calculateOffsetForIndex(ctx, anchor.index) + topOffsetAdjustment - ((_a3 = anchor.viewOffset) != null ? _a3 : 0) - ((_b = anchor.viewPosition) != null ? _b : 0) * availableSpace;
1608
+ const clampedDesiredOffset = clampScrollOffset(ctx, desiredOffset, anchor);
1609
+ const delta = clampedDesiredOffset - scroll;
1357
1610
  if (Math.abs(delta) <= INITIAL_ANCHOR_TOLERANCE) {
1358
1611
  const settledTicks = ((_c = anchor.settledTicks) != null ? _c : 0) + 1;
1359
1612
  if (settledTicks >= INITIAL_ANCHOR_SETTLED_TICKS) {
@@ -1377,60 +1630,151 @@ function ensureInitialAnchor(ctx, state) {
1377
1630
  lastDelta: delta,
1378
1631
  settledTicks: 0
1379
1632
  });
1380
- requestAdjust(ctx, state, delta);
1633
+ requestAdjust(ctx, delta);
1381
1634
  }
1382
1635
 
1383
1636
  // src/core/mvcp.ts
1384
- function prepareMVCP(ctx, state, dataChanged) {
1637
+ var MVCP_POSITION_EPSILON = 0.1;
1638
+ var MVCP_ANCHOR_LOCK_TTL_MS = 300;
1639
+ var MVCP_ANCHOR_LOCK_QUIET_PASSES_TO_RELEASE = 2;
1640
+ function resolveAnchorLock(state, enableMVCPAnchorLock, mvcpData, now) {
1641
+ if (!enableMVCPAnchorLock) {
1642
+ state.mvcpAnchorLock = void 0;
1643
+ return void 0;
1644
+ }
1645
+ const lock = state.mvcpAnchorLock;
1646
+ if (!lock) {
1647
+ return void 0;
1648
+ }
1649
+ const isExpired = now > lock.expiresAt;
1650
+ const isMissing = state.indexByKey.get(lock.id) === void 0;
1651
+ if (isExpired || isMissing || !mvcpData) {
1652
+ state.mvcpAnchorLock = void 0;
1653
+ return void 0;
1654
+ }
1655
+ return lock;
1656
+ }
1657
+ function updateAnchorLock(state, params) {
1658
+ if (Platform2.OS === "web") {
1659
+ const { anchorId, anchorPosition, dataChanged, now, positionDiff } = params;
1660
+ const enableMVCPAnchorLock = !!dataChanged || !!state.mvcpAnchorLock;
1661
+ const mvcpData = state.props.maintainVisibleContentPosition.data;
1662
+ if (!enableMVCPAnchorLock || !mvcpData || state.scrollingTo || !anchorId || anchorPosition === void 0) {
1663
+ return;
1664
+ }
1665
+ const existingLock = state.mvcpAnchorLock;
1666
+ const quietPasses = !dataChanged && Math.abs(positionDiff) <= MVCP_POSITION_EPSILON && (existingLock == null ? void 0 : existingLock.id) === anchorId ? existingLock.quietPasses + 1 : 0;
1667
+ if (!dataChanged && quietPasses >= MVCP_ANCHOR_LOCK_QUIET_PASSES_TO_RELEASE) {
1668
+ state.mvcpAnchorLock = void 0;
1669
+ return;
1670
+ }
1671
+ state.mvcpAnchorLock = {
1672
+ expiresAt: now + MVCP_ANCHOR_LOCK_TTL_MS,
1673
+ id: anchorId,
1674
+ position: anchorPosition,
1675
+ quietPasses
1676
+ };
1677
+ }
1678
+ }
1679
+ function prepareMVCP(ctx, dataChanged) {
1680
+ const state = ctx.state;
1385
1681
  const { idsInView, positions, props } = state;
1386
- const { maintainVisibleContentPosition } = props;
1387
- const scrollingTo = peek$(ctx, "scrollingTo");
1682
+ const {
1683
+ maintainVisibleContentPosition: { data: mvcpData, size: mvcpScroll, shouldRestorePosition }
1684
+ } = props;
1685
+ const isWeb = Platform2.OS === "web";
1686
+ const now = Date.now();
1687
+ const enableMVCPAnchorLock = isWeb && (!!dataChanged || !!state.mvcpAnchorLock);
1688
+ const scrollingTo = state.scrollingTo;
1689
+ const anchorLock = isWeb ? resolveAnchorLock(state, enableMVCPAnchorLock, mvcpData, now) : void 0;
1388
1690
  let prevPosition;
1389
1691
  let targetId;
1390
1692
  const idsInViewWithPositions = [];
1391
1693
  const scrollTarget = scrollingTo == null ? void 0 : scrollingTo.index;
1392
- const shouldMVCP = !dataChanged || maintainVisibleContentPosition;
1694
+ const scrollingToViewPosition = scrollingTo == null ? void 0 : scrollingTo.viewPosition;
1695
+ const isEndAnchoredScrollTarget = scrollTarget !== void 0 && state.props.data.length > 0 && scrollTarget >= state.props.data.length - 1 && (scrollingToViewPosition != null ? scrollingToViewPosition : 0) > 0;
1696
+ const shouldMVCP = dataChanged ? mvcpData : mvcpScroll;
1393
1697
  const indexByKey = state.indexByKey;
1394
1698
  if (shouldMVCP) {
1395
- if (scrollTarget !== void 0) {
1699
+ if (anchorLock && scrollTarget === void 0) {
1700
+ targetId = anchorLock.id;
1701
+ prevPosition = anchorLock.position;
1702
+ } else if (scrollTarget !== void 0) {
1396
1703
  if (!IsNewArchitecture && (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll)) {
1397
1704
  return void 0;
1398
1705
  }
1399
1706
  targetId = getId(state, scrollTarget);
1400
- } else if (idsInView.length > 0 && peek$(ctx, "containersDidLayout")) {
1401
- if (dataChanged) {
1402
- for (let i = 0; i < idsInView.length; i++) {
1403
- const id = idsInView[i];
1404
- const index = indexByKey.get(id);
1405
- if (index !== void 0) {
1406
- idsInViewWithPositions.push({ id, position: positions.get(id) });
1707
+ } else if (idsInView.length > 0 && state.didContainersLayout && !dataChanged) {
1708
+ targetId = idsInView.find((id) => indexByKey.get(id) !== void 0);
1709
+ }
1710
+ if (dataChanged && idsInView.length > 0 && state.didContainersLayout) {
1711
+ for (let i = 0; i < idsInView.length; i++) {
1712
+ const id = idsInView[i];
1713
+ const index = indexByKey.get(id);
1714
+ if (index !== void 0) {
1715
+ const position = positions[index];
1716
+ if (position !== void 0) {
1717
+ idsInViewWithPositions.push({ id, position });
1407
1718
  }
1408
1719
  }
1409
- } else {
1410
- targetId = idsInView.find((id) => indexByKey.get(id) !== void 0);
1411
1720
  }
1412
1721
  }
1413
- if (targetId !== void 0) {
1414
- prevPosition = positions.get(targetId);
1722
+ if (targetId !== void 0 && prevPosition === void 0) {
1723
+ const targetIndex = indexByKey.get(targetId);
1724
+ if (targetIndex !== void 0) {
1725
+ prevPosition = positions[targetIndex];
1726
+ }
1415
1727
  }
1416
1728
  return () => {
1417
- let positionDiff;
1418
- if (dataChanged && targetId === void 0 && maintainVisibleContentPosition) {
1729
+ let positionDiff = 0;
1730
+ let anchorIdForLock = anchorLock == null ? void 0 : anchorLock.id;
1731
+ let anchorPositionForLock;
1732
+ let skipTargetAnchor = false;
1733
+ const data = state.props.data;
1734
+ const shouldValidateLockedAnchor = isWeb && dataChanged && mvcpData && scrollTarget === void 0 && targetId !== void 0 && (anchorLock == null ? void 0 : anchorLock.id) === targetId && shouldRestorePosition !== void 0;
1735
+ if (shouldValidateLockedAnchor && targetId !== void 0) {
1736
+ const index = indexByKey.get(targetId);
1737
+ if (index !== void 0) {
1738
+ const item = data[index];
1739
+ skipTargetAnchor = item === void 0 || !shouldRestorePosition(item, index, data);
1740
+ if (skipTargetAnchor && (anchorLock == null ? void 0 : anchorLock.id) === targetId) {
1741
+ state.mvcpAnchorLock = void 0;
1742
+ }
1743
+ }
1744
+ }
1745
+ const shouldUseFallbackVisibleAnchor = dataChanged && mvcpData && scrollTarget === void 0 && (() => {
1746
+ if (targetId === void 0 || skipTargetAnchor) {
1747
+ return true;
1748
+ }
1749
+ const targetIndex = indexByKey.get(targetId);
1750
+ return targetIndex === void 0 || positions[targetIndex] === void 0;
1751
+ })();
1752
+ if (shouldUseFallbackVisibleAnchor) {
1419
1753
  for (let i = 0; i < idsInViewWithPositions.length; i++) {
1420
1754
  const { id, position } = idsInViewWithPositions[i];
1421
- const newPosition = positions.get(id);
1755
+ const index = indexByKey.get(id);
1756
+ if (index !== void 0 && shouldRestorePosition) {
1757
+ const item = data[index];
1758
+ if (item === void 0 || !shouldRestorePosition(item, index, data)) {
1759
+ continue;
1760
+ }
1761
+ }
1762
+ const newPosition = index !== void 0 ? positions[index] : void 0;
1422
1763
  if (newPosition !== void 0) {
1423
1764
  positionDiff = newPosition - position;
1765
+ anchorIdForLock = id;
1766
+ anchorPositionForLock = newPosition;
1424
1767
  break;
1425
1768
  }
1426
1769
  }
1427
1770
  }
1428
- if (targetId !== void 0 && prevPosition !== void 0) {
1429
- const newPosition = positions.get(targetId);
1771
+ if (!skipTargetAnchor && targetId !== void 0 && prevPosition !== void 0) {
1772
+ const targetIndex = indexByKey.get(targetId);
1773
+ const newPosition = targetIndex !== void 0 ? positions[targetIndex] : void 0;
1430
1774
  if (newPosition !== void 0) {
1431
1775
  const totalSize = getContentSize(ctx);
1432
1776
  let diff = newPosition - prevPosition;
1433
- if (diff !== 0 && state.scroll + state.scrollLength > totalSize) {
1777
+ if (diff !== 0 && isEndAnchoredScrollTarget && state.scroll + state.scrollLength > totalSize) {
1434
1778
  if (diff > 0) {
1435
1779
  diff = Math.max(0, totalSize - state.scroll - state.scrollLength);
1436
1780
  } else {
@@ -1438,33 +1782,52 @@ function prepareMVCP(ctx, state, dataChanged) {
1438
1782
  }
1439
1783
  }
1440
1784
  positionDiff = diff;
1785
+ anchorIdForLock = targetId;
1786
+ anchorPositionForLock = newPosition;
1441
1787
  }
1442
1788
  }
1443
- if (positionDiff !== void 0 && Math.abs(positionDiff) > 0.1) {
1444
- requestAdjust(ctx, state, positionDiff, dataChanged && maintainVisibleContentPosition);
1789
+ if (scrollingToViewPosition && scrollingToViewPosition > 0) {
1790
+ const newSize = getItemSize(ctx, targetId, scrollTarget, state.props.data[scrollTarget]);
1791
+ const prevSize = scrollingTo == null ? void 0 : scrollingTo.itemSize;
1792
+ if (newSize !== void 0 && prevSize !== void 0 && newSize !== prevSize) {
1793
+ const diff = newSize - prevSize;
1794
+ if (diff !== 0) {
1795
+ positionDiff += diff * scrollingToViewPosition;
1796
+ scrollingTo.itemSize = newSize;
1797
+ }
1798
+ }
1799
+ }
1800
+ updateAnchorLock(state, {
1801
+ anchorId: anchorIdForLock,
1802
+ anchorPosition: anchorPositionForLock,
1803
+ dataChanged,
1804
+ now,
1805
+ positionDiff
1806
+ });
1807
+ if (Math.abs(positionDiff) > MVCP_POSITION_EPSILON) {
1808
+ requestAdjust(ctx, positionDiff, dataChanged && mvcpData);
1445
1809
  }
1446
1810
  };
1447
1811
  }
1448
1812
  }
1449
1813
 
1450
1814
  // src/core/prepareColumnStartState.ts
1451
- function prepareColumnStartState(ctx, state, startIndex, useAverageSize) {
1815
+ function prepareColumnStartState(ctx, startIndex, useAverageSize) {
1452
1816
  var _a3;
1817
+ const state = ctx.state;
1453
1818
  const numColumns = peek$(ctx, "numColumns");
1454
1819
  let rowStartIndex = startIndex;
1455
- const columnAtStart = state.columns.get(state.idCache[startIndex]);
1820
+ const columnAtStart = state.columns[startIndex];
1456
1821
  if (columnAtStart !== 1) {
1457
1822
  rowStartIndex = findRowStartIndex(state, numColumns, startIndex);
1458
1823
  }
1459
1824
  let currentRowTop = 0;
1460
- const curId = state.idCache[rowStartIndex];
1461
- const column = state.columns.get(curId);
1825
+ const column = state.columns[rowStartIndex];
1462
1826
  if (rowStartIndex > 0) {
1463
1827
  const prevIndex = rowStartIndex - 1;
1464
- const prevId = state.idCache[prevIndex];
1465
- const prevPosition = (_a3 = state.positions.get(prevId)) != null ? _a3 : 0;
1828
+ const prevPosition = (_a3 = state.positions[prevIndex]) != null ? _a3 : 0;
1466
1829
  const prevRowStart = findRowStartIndex(state, numColumns, prevIndex);
1467
- const prevRowHeight = calculateRowMaxSize(ctx, state, prevRowStart, prevIndex, useAverageSize);
1830
+ const prevRowHeight = calculateRowMaxSize(ctx, prevRowStart, prevIndex, useAverageSize);
1468
1831
  currentRowTop = prevPosition + prevRowHeight;
1469
1832
  }
1470
1833
  return {
@@ -1479,7 +1842,7 @@ function findRowStartIndex(state, numColumns, index) {
1479
1842
  }
1480
1843
  let rowStart = Math.max(0, index);
1481
1844
  while (rowStart > 0) {
1482
- const columnForIndex = state.columns.get(state.idCache[rowStart]);
1845
+ const columnForIndex = state.columns[rowStart];
1483
1846
  if (columnForIndex === 1) {
1484
1847
  break;
1485
1848
  }
@@ -1487,7 +1850,8 @@ function findRowStartIndex(state, numColumns, index) {
1487
1850
  }
1488
1851
  return rowStart;
1489
1852
  }
1490
- function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1853
+ function calculateRowMaxSize(ctx, startIndex, endIndex, useAverageSize) {
1854
+ const state = ctx.state;
1491
1855
  if (endIndex < startIndex) {
1492
1856
  return 0;
1493
1857
  }
@@ -1501,7 +1865,7 @@ function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1501
1865
  continue;
1502
1866
  }
1503
1867
  const id = state.idCache[i];
1504
- const size = getItemSize(ctx, state, id, i, data[i], useAverageSize);
1868
+ const size = getItemSize(ctx, id, i, data[i], useAverageSize);
1505
1869
  if (size > maxSize) {
1506
1870
  maxSize = size;
1507
1871
  }
@@ -1510,22 +1874,44 @@ function calculateRowMaxSize(ctx, state, startIndex, endIndex, useAverageSize) {
1510
1874
  }
1511
1875
 
1512
1876
  // src/core/updateTotalSize.ts
1513
- function updateTotalSize(ctx, state) {
1877
+ function updateTotalSize(ctx) {
1878
+ var _a3, _b;
1879
+ const state = ctx.state;
1514
1880
  const {
1515
1881
  positions,
1516
1882
  props: { data }
1517
1883
  } = state;
1884
+ const numColumns = (_a3 = peek$(ctx, "numColumns")) != null ? _a3 : 1;
1518
1885
  if (data.length === 0) {
1519
- addTotalSize(ctx, state, null, 0);
1886
+ addTotalSize(ctx, null, 0);
1520
1887
  } else {
1521
- const lastId = getId(state, data.length - 1);
1522
- if (lastId !== void 0) {
1523
- const lastPosition = positions.get(lastId);
1524
- if (lastPosition !== void 0) {
1525
- const lastSize = getItemSize(ctx, state, lastId, data.length - 1, data[data.length - 1]);
1888
+ const lastIndex = data.length - 1;
1889
+ const lastId = getId(state, lastIndex);
1890
+ const lastPosition = positions[lastIndex];
1891
+ if (lastId !== void 0 && lastPosition !== void 0) {
1892
+ if (numColumns > 1) {
1893
+ let rowStart = lastIndex;
1894
+ while (rowStart > 0) {
1895
+ const column = state.columns[rowStart];
1896
+ if (column === 1 || column === void 0) {
1897
+ break;
1898
+ }
1899
+ rowStart -= 1;
1900
+ }
1901
+ let maxSize = 0;
1902
+ for (let i = rowStart; i <= lastIndex; i++) {
1903
+ const rowId = (_b = state.idCache[i]) != null ? _b : getId(state, i);
1904
+ const size = getItemSize(ctx, rowId, i, data[i]);
1905
+ if (size > maxSize) {
1906
+ maxSize = size;
1907
+ }
1908
+ }
1909
+ addTotalSize(ctx, null, lastPosition + maxSize);
1910
+ } else {
1911
+ const lastSize = getItemSize(ctx, lastId, lastIndex, data[lastIndex]);
1526
1912
  if (lastSize !== void 0) {
1527
1913
  const totalSize = lastPosition + lastSize;
1528
- addTotalSize(ctx, state, null, totalSize);
1914
+ addTotalSize(ctx, null, totalSize);
1529
1915
  }
1530
1916
  }
1531
1917
  }
@@ -1535,90 +1921,107 @@ function updateTotalSize(ctx, state) {
1535
1921
  // src/utils/getScrollVelocity.ts
1536
1922
  var getScrollVelocity = (state) => {
1537
1923
  const { scrollHistory } = state;
1538
- let velocity = 0;
1539
- if (scrollHistory.length >= 1) {
1540
- const newest = scrollHistory[scrollHistory.length - 1];
1541
- let oldest;
1542
- let start = 0;
1543
- const now = Date.now();
1544
- for (let i = 0; i < scrollHistory.length - 1; i++) {
1545
- const entry = scrollHistory[i];
1546
- const nextEntry = scrollHistory[i + 1];
1547
- if (i > 0) {
1548
- const prevEntry = scrollHistory[i - 1];
1549
- const prevDirection = entry.scroll - prevEntry.scroll;
1550
- const currentDirection = nextEntry.scroll - entry.scroll;
1551
- if (prevDirection > 0 && currentDirection < 0 || prevDirection < 0 && currentDirection > 0) {
1552
- start = i;
1553
- break;
1554
- }
1555
- }
1924
+ const newestIndex = scrollHistory.length - 1;
1925
+ if (newestIndex < 1) {
1926
+ return 0;
1927
+ }
1928
+ const newest = scrollHistory[newestIndex];
1929
+ const now = Date.now();
1930
+ let direction = 0;
1931
+ for (let i = newestIndex; i > 0; i--) {
1932
+ const delta = scrollHistory[i].scroll - scrollHistory[i - 1].scroll;
1933
+ if (delta !== 0) {
1934
+ direction = Math.sign(delta);
1935
+ break;
1556
1936
  }
1557
- for (let i = start; i < scrollHistory.length - 1; i++) {
1558
- const entry = scrollHistory[i];
1559
- if (now - entry.time <= 1e3) {
1560
- oldest = entry;
1561
- break;
1562
- }
1937
+ }
1938
+ if (direction === 0) {
1939
+ return 0;
1940
+ }
1941
+ let oldest = newest;
1942
+ for (let i = newestIndex - 1; i >= 0; i--) {
1943
+ const current = scrollHistory[i];
1944
+ const next = scrollHistory[i + 1];
1945
+ const delta = next.scroll - current.scroll;
1946
+ const deltaSign = Math.sign(delta);
1947
+ if (deltaSign !== 0 && deltaSign !== direction) {
1948
+ break;
1563
1949
  }
1564
- if (oldest && oldest !== newest) {
1565
- const scrollDiff = newest.scroll - oldest.scroll;
1566
- const timeDiff = newest.time - oldest.time;
1567
- velocity = timeDiff > 0 ? scrollDiff / timeDiff : 0;
1950
+ if (now - current.time > 1e3) {
1951
+ break;
1568
1952
  }
1953
+ oldest = current;
1569
1954
  }
1570
- return velocity;
1955
+ const scrollDiff = newest.scroll - oldest.scroll;
1956
+ const timeDiff = newest.time - oldest.time;
1957
+ return timeDiff > 0 ? scrollDiff / timeDiff : 0;
1571
1958
  };
1572
1959
 
1573
1960
  // src/utils/updateSnapToOffsets.ts
1574
- function updateSnapToOffsets(ctx, state) {
1961
+ function updateSnapToOffsets(ctx) {
1962
+ const state = ctx.state;
1575
1963
  const {
1576
- positions,
1577
1964
  props: { snapToIndices }
1578
1965
  } = state;
1579
1966
  const snapToOffsets = Array(snapToIndices.length);
1580
1967
  for (let i = 0; i < snapToIndices.length; i++) {
1581
1968
  const idx = snapToIndices[i];
1582
- const key = getId(state, idx);
1583
- snapToOffsets[i] = positions.get(key);
1969
+ getId(state, idx);
1970
+ snapToOffsets[i] = state.positions[idx];
1584
1971
  }
1585
1972
  set$(ctx, "snapToOffsets", snapToOffsets);
1586
1973
  }
1587
1974
 
1588
1975
  // src/core/updateItemPositions.ts
1589
- function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP } = {
1976
+ function updateItemPositions(ctx, dataChanged, { startIndex, scrollBottomBuffered, forceFullUpdate = false, doMVCP } = {
1590
1977
  doMVCP: false,
1591
1978
  forceFullUpdate: false,
1592
1979
  scrollBottomBuffered: -1,
1593
1980
  startIndex: 0
1594
1981
  }) {
1595
1982
  var _a3, _b, _c, _d, _e;
1983
+ const state = ctx.state;
1984
+ const hasPositionListeners = ctx.positionListeners.size > 0;
1596
1985
  const {
1597
1986
  columns,
1987
+ columnSpans,
1598
1988
  indexByKey,
1599
1989
  positions,
1600
1990
  idCache,
1601
1991
  sizesKnown,
1602
- props: { getEstimatedItemSize, snapToIndices, enableAverages }
1992
+ props: { data, getEstimatedItemSize, overrideItemLayout, snapToIndices },
1993
+ scrollingTo
1603
1994
  } = state;
1604
- const data = state.props.data;
1605
1995
  const dataLength = data.length;
1606
- const numColumns = peek$(ctx, "numColumns");
1607
- const scrollingTo = peek$(ctx, "scrollingTo");
1996
+ const numColumns = (_a3 = peek$(ctx, "numColumns")) != null ? _a3 : 1;
1608
1997
  const hasColumns = numColumns > 1;
1609
1998
  const indexByKeyForChecking = IS_DEV ? /* @__PURE__ */ new Map() : void 0;
1610
- const shouldOptimize = !forceFullUpdate && !dataChanged && Math.abs(getScrollVelocity(state)) > 0;
1999
+ const extraData = peek$(ctx, "extraData");
2000
+ const layoutConfig = overrideItemLayout ? { span: 1 } : void 0;
2001
+ const lastScrollDelta = state.lastScrollDelta;
2002
+ const velocity = getScrollVelocity(state);
2003
+ const shouldOptimize = !forceFullUpdate && !dataChanged && (Math.abs(velocity) > 0 || Platform2.OS === "web" && state.scrollLength > 0 && lastScrollDelta > state.scrollLength);
1611
2004
  const maxVisibleArea = scrollBottomBuffered + 1e3;
1612
- const useAverageSize = enableAverages && !getEstimatedItemSize;
1613
- const preferCachedSize = !doMVCP || dataChanged || state.scrollAdjustHandler.getAdjust() !== 0 || ((_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0) !== 0;
2005
+ const useAverageSize = !getEstimatedItemSize;
2006
+ const preferCachedSize = !doMVCP || dataChanged || state.scrollAdjustHandler.getAdjust() !== 0 || ((_b = peek$(ctx, "scrollAdjustPending")) != null ? _b : 0) !== 0;
1614
2007
  let currentRowTop = 0;
1615
2008
  let column = 1;
1616
2009
  let maxSizeInRow = 0;
2010
+ if (dataChanged) {
2011
+ columnSpans.length = 0;
2012
+ }
2013
+ if (!hasColumns) {
2014
+ if (columns.length) {
2015
+ columns.length = 0;
2016
+ }
2017
+ if (columnSpans.length) {
2018
+ columnSpans.length = 0;
2019
+ }
2020
+ }
1617
2021
  if (startIndex > 0) {
1618
2022
  if (hasColumns) {
1619
2023
  const { startIndex: processedStartIndex, currentRowTop: initialRowTop } = prepareColumnStartState(
1620
2024
  ctx,
1621
- state,
1622
2025
  startIndex,
1623
2026
  useAverageSize
1624
2027
  );
@@ -1627,12 +2030,13 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1627
2030
  } else if (startIndex < dataLength) {
1628
2031
  const prevIndex = startIndex - 1;
1629
2032
  const prevId = getId(state, prevIndex);
1630
- const prevPosition = (_b = positions.get(prevId)) != null ? _b : 0;
1631
- const prevSize = (_c = sizesKnown.get(prevId)) != null ? _c : getItemSize(ctx, state, prevId, prevIndex, data[prevIndex], useAverageSize, preferCachedSize);
2033
+ const prevPosition = (_c = positions[prevIndex]) != null ? _c : 0;
2034
+ const prevSize = (_d = sizesKnown.get(prevId)) != null ? _d : getItemSize(ctx, prevId, prevIndex, data[prevIndex], useAverageSize, preferCachedSize);
1632
2035
  currentRowTop = prevPosition + prevSize;
1633
2036
  }
1634
2037
  }
1635
2038
  const needsIndexByKey = dataChanged || indexByKey.size === 0;
2039
+ const canOverrideSpan = hasColumns && !!overrideItemLayout && !!layoutConfig;
1636
2040
  let didBreakEarly = false;
1637
2041
  let breakAt;
1638
2042
  for (let i = startIndex; i < dataLength; i++) {
@@ -1644,8 +2048,23 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1644
2048
  const itemsPerRow = hasColumns ? numColumns : 1;
1645
2049
  breakAt = i + itemsPerRow + 10;
1646
2050
  }
1647
- const id = (_d = idCache[i]) != null ? _d : getId(state, i);
1648
- const size = (_e = sizesKnown.get(id)) != null ? _e : getItemSize(ctx, state, id, i, data[i], useAverageSize, preferCachedSize);
2051
+ const id = (_e = idCache[i]) != null ? _e : getId(state, i);
2052
+ let span = 1;
2053
+ if (canOverrideSpan) {
2054
+ layoutConfig.span = 1;
2055
+ overrideItemLayout(layoutConfig, data[i], i, numColumns, extraData);
2056
+ const requestedSpan = layoutConfig.span;
2057
+ if (requestedSpan !== void 0 && Number.isFinite(requestedSpan)) {
2058
+ span = Math.max(1, Math.min(numColumns, Math.round(requestedSpan)));
2059
+ }
2060
+ }
2061
+ if (hasColumns && column + span - 1 > numColumns) {
2062
+ currentRowTop += maxSizeInRow;
2063
+ column = 1;
2064
+ maxSizeInRow = 0;
2065
+ }
2066
+ const knownSize = sizesKnown.get(id);
2067
+ const size = knownSize !== void 0 ? knownSize : getItemSize(ctx, id, i, data[i], useAverageSize, preferCachedSize);
1649
2068
  if (IS_DEV && needsIndexByKey) {
1650
2069
  if (indexByKeyForChecking.has(id)) {
1651
2070
  console.error(
@@ -1654,30 +2073,36 @@ function updateItemPositions(ctx, state, dataChanged, { startIndex, scrollBottom
1654
2073
  }
1655
2074
  indexByKeyForChecking.set(id, i);
1656
2075
  }
1657
- positions.set(id, currentRowTop);
2076
+ if (currentRowTop !== positions[i]) {
2077
+ positions[i] = currentRowTop;
2078
+ if (hasPositionListeners) {
2079
+ notifyPosition$(ctx, id, currentRowTop);
2080
+ }
2081
+ }
1658
2082
  if (needsIndexByKey) {
1659
2083
  indexByKey.set(id, i);
1660
2084
  }
1661
- columns.set(id, column);
1662
- if (hasColumns) {
2085
+ if (!hasColumns) {
2086
+ currentRowTop += size;
2087
+ } else {
2088
+ columns[i] = column;
2089
+ columnSpans[i] = span;
1663
2090
  if (size > maxSizeInRow) {
1664
2091
  maxSizeInRow = size;
1665
2092
  }
1666
- column++;
2093
+ column += span;
1667
2094
  if (column > numColumns) {
1668
2095
  currentRowTop += maxSizeInRow;
1669
2096
  column = 1;
1670
2097
  maxSizeInRow = 0;
1671
2098
  }
1672
- } else {
1673
- currentRowTop += size;
1674
2099
  }
1675
2100
  }
1676
2101
  if (!didBreakEarly) {
1677
- updateTotalSize(ctx, state);
2102
+ updateTotalSize(ctx);
1678
2103
  }
1679
2104
  if (snapToIndices) {
1680
- updateSnapToOffsets(ctx, state);
2105
+ updateSnapToOffsets(ctx);
1681
2106
  }
1682
2107
  }
1683
2108
 
@@ -1755,7 +2180,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
1755
2180
  if (previousViewableItems) {
1756
2181
  for (const viewToken of previousViewableItems) {
1757
2182
  const containerId = findContainerId(ctx, viewToken.key);
1758
- if (!isViewable(
2183
+ if (!checkIsViewable(
1759
2184
  state,
1760
2185
  ctx,
1761
2186
  viewabilityConfig,
@@ -1776,7 +2201,7 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
1776
2201
  if (item) {
1777
2202
  const key = getId(state, i);
1778
2203
  const containerId = findContainerId(ctx, key);
1779
- if (isViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, i)) {
2204
+ if (checkIsViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, i)) {
1780
2205
  const viewToken = {
1781
2206
  containerId,
1782
2207
  index: i,
@@ -1822,25 +2247,49 @@ function shallowEqual(prev, next) {
1822
2247
  return true;
1823
2248
  }
1824
2249
  function computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
1825
- const { sizes, positions, scroll: scrollState } = state;
2250
+ const { sizes, scroll: scrollState } = state;
1826
2251
  const topPad = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
1827
2252
  const { itemVisiblePercentThreshold, viewAreaCoveragePercentThreshold } = viewabilityConfig;
1828
2253
  const viewAreaMode = viewAreaCoveragePercentThreshold != null;
1829
2254
  const viewablePercentThreshold = viewAreaMode ? viewAreaCoveragePercentThreshold : itemVisiblePercentThreshold;
1830
2255
  const scroll = scrollState - topPad;
1831
- const top = positions.get(key) - scroll;
2256
+ const position = state.positions[index];
1832
2257
  const size = sizes.get(key) || 0;
2258
+ if (position === void 0) {
2259
+ const value2 = {
2260
+ containerId,
2261
+ index,
2262
+ isViewable: false,
2263
+ item,
2264
+ key,
2265
+ percentOfScroller: 0,
2266
+ percentVisible: 0,
2267
+ scrollSize,
2268
+ size,
2269
+ sizeVisible: -1
2270
+ };
2271
+ const prev2 = ctx.mapViewabilityAmountValues.get(containerId);
2272
+ if (!shallowEqual(prev2, value2)) {
2273
+ ctx.mapViewabilityAmountValues.set(containerId, value2);
2274
+ const cb = ctx.mapViewabilityAmountCallbacks.get(containerId);
2275
+ if (cb) {
2276
+ cb(value2);
2277
+ }
2278
+ }
2279
+ return value2;
2280
+ }
2281
+ const top = position - scroll;
1833
2282
  const bottom = top + size;
1834
2283
  const isEntirelyVisible = top >= 0 && bottom <= scrollSize && bottom > top;
1835
2284
  const sizeVisible = isEntirelyVisible ? size : Math.min(bottom, scrollSize) - Math.max(top, 0);
1836
2285
  const percentVisible = size ? isEntirelyVisible ? 100 : 100 * (sizeVisible / size) : 0;
1837
2286
  const percentOfScroller = size ? 100 * (sizeVisible / scrollSize) : 0;
1838
2287
  const percent = isEntirelyVisible ? 100 : viewAreaMode ? percentOfScroller : percentVisible;
1839
- const isViewable2 = percent >= viewablePercentThreshold;
2288
+ const isViewable = percent >= viewablePercentThreshold;
1840
2289
  const value = {
1841
2290
  containerId,
1842
2291
  index,
1843
- isViewable: isViewable2,
2292
+ isViewable,
1844
2293
  item,
1845
2294
  key,
1846
2295
  percentOfScroller,
@@ -1859,8 +2308,11 @@ function computeViewability(state, ctx, viewabilityConfig, containerId, key, scr
1859
2308
  }
1860
2309
  return value;
1861
2310
  }
1862
- function isViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
1863
- const value = ctx.mapViewabilityAmountValues.get(containerId) || computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index);
2311
+ function checkIsViewable(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index) {
2312
+ let value = ctx.mapViewabilityAmountValues.get(containerId);
2313
+ if (!value || value.key !== key) {
2314
+ value = computeViewability(state, ctx, viewabilityConfig, containerId, key, scrollSize, item, index);
2315
+ }
1864
2316
  return value.isViewable;
1865
2317
  }
1866
2318
  function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
@@ -1869,6 +2321,8 @@ function maybeUpdateViewabilityCallback(ctx, configId, containerId, viewToken) {
1869
2321
  const cb = ctx.mapViewabilityCallbacks.get(key);
1870
2322
  cb == null ? void 0 : cb(viewToken);
1871
2323
  }
2324
+ var unstableBatchedUpdates = ReactNative__namespace.unstable_batchedUpdates;
2325
+ var batchedUpdates = typeof unstableBatchedUpdates === "function" ? unstableBatchedUpdates : (fn) => fn();
1872
2326
 
1873
2327
  // src/utils/checkAllSizesKnown.ts
1874
2328
  function isNullOrUndefined2(value) {
@@ -1888,8 +2342,9 @@ function checkAllSizesKnown(state) {
1888
2342
  }
1889
2343
 
1890
2344
  // src/utils/findAvailableContainers.ts
1891
- function findAvailableContainers(ctx, state, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers) {
2345
+ function findAvailableContainers(ctx, numNeeded, startBuffered, endBuffered, pendingRemoval, requiredItemTypes, needNewContainers) {
1892
2346
  const numContainers = peek$(ctx, "numContainers");
2347
+ const state = ctx.state;
1893
2348
  const { stickyContainerPool, containerItemTypes } = state;
1894
2349
  const result = [];
1895
2350
  const availableContainers = [];
@@ -2009,103 +2464,93 @@ function comparatorByDistance(a, b) {
2009
2464
  }
2010
2465
 
2011
2466
  // src/core/scrollToIndex.ts
2012
- function scrollToIndex(ctx, state, { index, viewOffset = 0, animated = true, viewPosition }) {
2013
- if (index >= state.props.data.length) {
2014
- index = state.props.data.length - 1;
2467
+ function scrollToIndex(ctx, { index, viewOffset = 0, animated = true, viewPosition }) {
2468
+ const state = ctx.state;
2469
+ const { data } = state.props;
2470
+ if (index >= data.length) {
2471
+ index = data.length - 1;
2015
2472
  } else if (index < 0) {
2016
2473
  index = 0;
2017
2474
  }
2018
- const firstIndexOffset = calculateOffsetForIndex(ctx, state, index);
2019
- const isLast = index === state.props.data.length - 1;
2475
+ const firstIndexOffset = calculateOffsetForIndex(ctx, index);
2476
+ const isLast = index === data.length - 1;
2020
2477
  if (isLast && viewPosition === void 0) {
2021
2478
  viewPosition = 1;
2022
2479
  }
2023
2480
  state.scrollForNextCalculateItemsInView = void 0;
2024
- scrollTo(ctx, state, {
2481
+ const targetId = getId(state, index);
2482
+ const itemSize = getItemSize(ctx, targetId, index, state.props.data[index]);
2483
+ scrollTo(ctx, {
2025
2484
  animated,
2026
2485
  index,
2027
- offset: firstIndexOffset,
2028
- viewOffset,
2029
- viewPosition: viewPosition != null ? viewPosition : 0
2030
- });
2031
- }
2032
-
2033
- // src/utils/setDidLayout.ts
2034
- function setDidLayout(ctx, state) {
2035
- const {
2036
- loadStartTime,
2037
- initialScroll,
2038
- props: { onLoad }
2039
- } = state;
2040
- state.queuedInitialLayout = true;
2041
- checkAtBottom(ctx, state);
2042
- const setIt = () => {
2043
- set$(ctx, "containersDidLayout", true);
2044
- if (onLoad) {
2045
- onLoad({ elapsedTimeInMs: Date.now() - loadStartTime });
2046
- }
2047
- };
2048
- if (Platform2.OS === "android" && initialScroll) {
2049
- if (IsNewArchitecture) {
2050
- scrollToIndex(ctx, state, { ...initialScroll, animated: false });
2051
- requestAnimationFrame(() => {
2052
- scrollToIndex(ctx, state, { ...initialScroll, animated: false });
2053
- setIt();
2054
- });
2055
- } else {
2056
- scrollToIndex(ctx, state, { ...initialScroll, animated: false });
2057
- setIt();
2058
- }
2059
- } else {
2060
- setIt();
2061
- }
2486
+ itemSize,
2487
+ offset: firstIndexOffset,
2488
+ viewOffset,
2489
+ viewPosition: viewPosition != null ? viewPosition : 0
2490
+ });
2491
+ }
2492
+
2493
+ // src/utils/setDidLayout.ts
2494
+ function setDidLayout(ctx) {
2495
+ const state = ctx.state;
2496
+ const { initialScroll } = state;
2497
+ state.queuedInitialLayout = true;
2498
+ checkAtBottom(ctx);
2499
+ if ((initialScroll == null ? void 0 : initialScroll.index) !== void 0) {
2500
+ const target = initialScroll;
2501
+ const runScroll = () => scrollToIndex(ctx, { ...target, animated: false });
2502
+ runScroll();
2503
+ requestAnimationFrame(runScroll);
2504
+ }
2505
+ setInitialRenderState(ctx, { didLayout: true });
2062
2506
  }
2063
2507
 
2064
2508
  // src/core/calculateItemsInView.ts
2065
2509
  function findCurrentStickyIndex(stickyArray, scroll, state) {
2066
- var _a3;
2067
- const idCache = state.idCache;
2068
2510
  const positions = state.positions;
2069
2511
  for (let i = stickyArray.length - 1; i >= 0; i--) {
2070
2512
  const stickyIndex = stickyArray[i];
2071
- const stickyId = (_a3 = idCache[stickyIndex]) != null ? _a3 : getId(state, stickyIndex);
2072
- const stickyPos = stickyId ? positions.get(stickyId) : void 0;
2513
+ const stickyPos = positions[stickyIndex];
2073
2514
  if (stickyPos !== void 0 && scroll >= stickyPos) {
2074
2515
  return i;
2075
2516
  }
2076
2517
  }
2077
2518
  return -1;
2078
2519
  }
2079
- function getActiveStickyIndices(ctx, state, stickyHeaderIndices) {
2520
+ function getActiveStickyIndices(ctx, stickyHeaderIndices) {
2521
+ const state = ctx.state;
2080
2522
  return new Set(
2081
2523
  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))
2082
2524
  );
2083
2525
  }
2084
- function handleStickyActivation(ctx, state, stickyHeaderIndices, stickyArray, currentStickyIdx, needNewContainers, startBuffered, endBuffered) {
2526
+ function handleStickyActivation(ctx, stickyHeaderIndices, stickyArray, currentStickyIdx, needNewContainers, needNewContainersSet, startBuffered, endBuffered) {
2085
2527
  var _a3;
2086
- const activeIndices = getActiveStickyIndices(ctx, state, stickyHeaderIndices);
2087
- state.activeStickyIndex = currentStickyIdx >= 0 ? stickyArray[currentStickyIdx] : void 0;
2528
+ const state = ctx.state;
2529
+ const activeIndices = getActiveStickyIndices(ctx, stickyHeaderIndices);
2530
+ set$(ctx, "activeStickyIndex", currentStickyIdx >= 0 ? stickyArray[currentStickyIdx] : -1);
2088
2531
  for (let offset = 0; offset <= 1; offset++) {
2089
2532
  const idx = currentStickyIdx - offset;
2090
2533
  if (idx < 0 || activeIndices.has(stickyArray[idx])) continue;
2091
2534
  const stickyIndex = stickyArray[idx];
2092
2535
  const stickyId = (_a3 = state.idCache[stickyIndex]) != null ? _a3 : getId(state, stickyIndex);
2093
- if (stickyId && !state.containerItemKeys.has(stickyId) && (stickyIndex < startBuffered || stickyIndex > endBuffered)) {
2536
+ if (stickyId && !state.containerItemKeys.has(stickyId) && (stickyIndex < startBuffered || stickyIndex > endBuffered) && !needNewContainersSet.has(stickyIndex)) {
2537
+ needNewContainersSet.add(stickyIndex);
2094
2538
  needNewContainers.push(stickyIndex);
2095
2539
  }
2096
2540
  }
2097
2541
  }
2098
- function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, currentStickyIdx, pendingRemoval) {
2099
- var _a3, _b, _c;
2542
+ function handleStickyRecycling(ctx, stickyArray, scroll, drawDistance, currentStickyIdx, pendingRemoval, alwaysRenderIndicesSet) {
2543
+ var _a3, _b;
2544
+ const state = ctx.state;
2100
2545
  for (const containerIndex of state.stickyContainerPool) {
2101
2546
  const itemKey = peek$(ctx, `containerItemKey${containerIndex}`);
2102
2547
  const itemIndex = itemKey ? state.indexByKey.get(itemKey) : void 0;
2103
2548
  if (itemIndex === void 0) continue;
2549
+ if (alwaysRenderIndicesSet.has(itemIndex)) continue;
2104
2550
  const arrayIdx = stickyArray.indexOf(itemIndex);
2105
2551
  if (arrayIdx === -1) {
2106
2552
  state.stickyContainerPool.delete(containerIndex);
2107
2553
  set$(ctx, `containerSticky${containerIndex}`, false);
2108
- set$(ctx, `containerStickyOffset${containerIndex}`, void 0);
2109
2554
  continue;
2110
2555
  }
2111
2556
  const isRecentSticky = arrayIdx >= currentStickyIdx - 1 && arrayIdx <= currentStickyIdx + 1;
@@ -2113,15 +2558,14 @@ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, cu
2113
2558
  const nextIndex = stickyArray[arrayIdx + 1];
2114
2559
  let shouldRecycle = false;
2115
2560
  if (nextIndex) {
2116
- const nextId = (_a3 = state.idCache[nextIndex]) != null ? _a3 : getId(state, nextIndex);
2117
- const nextPos = nextId ? state.positions.get(nextId) : void 0;
2118
- shouldRecycle = nextPos !== void 0 && scroll > nextPos + scrollBuffer * 2;
2561
+ const nextPos = state.positions[nextIndex];
2562
+ shouldRecycle = nextPos !== void 0 && scroll > nextPos + drawDistance * 2;
2119
2563
  } else {
2120
- const currentId = (_b = state.idCache[itemIndex]) != null ? _b : getId(state, itemIndex);
2564
+ const currentId = (_a3 = state.idCache[itemIndex]) != null ? _a3 : getId(state, itemIndex);
2121
2565
  if (currentId) {
2122
- const currentPos = state.positions.get(currentId);
2123
- const currentSize = (_c = state.sizes.get(currentId)) != null ? _c : getItemSize(ctx, state, currentId, itemIndex, state.props.data[itemIndex]);
2124
- shouldRecycle = currentPos !== void 0 && scroll > currentPos + currentSize + scrollBuffer * 3;
2566
+ const currentPos = state.positions[itemIndex];
2567
+ const currentSize = (_b = state.sizes.get(currentId)) != null ? _b : getItemSize(ctx, currentId, itemIndex, state.props.data[itemIndex]);
2568
+ shouldRecycle = currentPos !== void 0 && scroll > currentPos + currentSize + drawDistance * 3;
2125
2569
  }
2126
2570
  }
2127
2571
  if (shouldRecycle) {
@@ -2129,11 +2573,13 @@ function handleStickyRecycling(ctx, state, stickyArray, scroll, scrollBuffer, cu
2129
2573
  }
2130
2574
  }
2131
2575
  }
2132
- function calculateItemsInView(ctx, state, params = {}) {
2133
- reactNative.unstable_batchedUpdates(() => {
2134
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j;
2576
+ function calculateItemsInView(ctx, params = {}) {
2577
+ const state = ctx.state;
2578
+ batchedUpdates(() => {
2579
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
2135
2580
  const {
2136
2581
  columns,
2582
+ columnSpans,
2137
2583
  containerItemKeys,
2138
2584
  enableScrollForNextCalculateItemsInView,
2139
2585
  idCache,
@@ -2141,7 +2587,15 @@ function calculateItemsInView(ctx, state, params = {}) {
2141
2587
  initialScroll,
2142
2588
  minIndexSizeChanged,
2143
2589
  positions,
2144
- props: { getItemType, itemsAreEqual, keyExtractor, onStickyHeaderChange, scrollBuffer },
2590
+ props: {
2591
+ alwaysRenderIndicesArr,
2592
+ alwaysRenderIndicesSet,
2593
+ drawDistance,
2594
+ getItemType,
2595
+ itemsAreEqual,
2596
+ keyExtractor,
2597
+ onStickyHeaderChange
2598
+ },
2145
2599
  scrollForNextCalculateItemsInView,
2146
2600
  scrollLength,
2147
2601
  sizes,
@@ -2151,17 +2605,19 @@ function calculateItemsInView(ctx, state, params = {}) {
2151
2605
  const { data } = state.props;
2152
2606
  const stickyIndicesArr = state.props.stickyIndicesArr || [];
2153
2607
  const stickyIndicesSet = state.props.stickyIndicesSet || /* @__PURE__ */ new Set();
2608
+ const alwaysRenderArr = alwaysRenderIndicesArr || [];
2609
+ const alwaysRenderSet = alwaysRenderIndicesSet || /* @__PURE__ */ new Set();
2610
+ const { dataChanged, doMVCP, forceFullItemPositions } = params;
2154
2611
  const prevNumContainers = peek$(ctx, "numContainers");
2155
2612
  if (!data || scrollLength === 0 || !prevNumContainers) {
2156
- if (state.initialAnchor) {
2157
- ensureInitialAnchor(ctx, state);
2613
+ if (!IsNewArchitecture && state.initialAnchor) {
2614
+ ensureInitialAnchor(ctx);
2158
2615
  }
2159
2616
  return;
2160
2617
  }
2161
2618
  const totalSize = getContentSize(ctx);
2162
2619
  const topPad = peek$(ctx, "stylePaddingTop") + peek$(ctx, "headerSize");
2163
2620
  const numColumns = peek$(ctx, "numColumns");
2164
- const { dataChanged, doMVCP, forceFullItemPositions } = params;
2165
2621
  const speed = getScrollVelocity(state);
2166
2622
  const scrollExtra = 0;
2167
2623
  const { queuedInitialLayout } = state;
@@ -2169,56 +2625,58 @@ function calculateItemsInView(ctx, state, params = {}) {
2169
2625
  if (!queuedInitialLayout && initialScroll) {
2170
2626
  const updatedOffset = calculateOffsetWithOffsetPosition(
2171
2627
  ctx,
2172
- state,
2173
- calculateOffsetForIndex(ctx, state, initialScroll.index),
2628
+ calculateOffsetForIndex(ctx, initialScroll.index),
2174
2629
  initialScroll
2175
2630
  );
2176
2631
  scrollState = updatedOffset;
2177
2632
  }
2178
2633
  const scrollAdjustPending = (_a3 = peek$(ctx, "scrollAdjustPending")) != null ? _a3 : 0;
2179
2634
  const scrollAdjustPad = scrollAdjustPending - topPad;
2180
- let scroll = scrollState + scrollExtra + scrollAdjustPad;
2635
+ let scroll = Math.round(scrollState + scrollExtra + scrollAdjustPad);
2181
2636
  if (scroll + scrollLength > totalSize) {
2182
2637
  scroll = Math.max(0, totalSize - scrollLength);
2183
2638
  }
2184
- if (ENABLE_DEBUG_VIEW) {
2185
- set$(ctx, "debugRawScroll", scrollState);
2186
- set$(ctx, "debugComputedScroll", scroll);
2187
- }
2188
- const previousStickyIndex = state.activeStickyIndex;
2639
+ const previousStickyIndex = peek$(ctx, "activeStickyIndex");
2189
2640
  const currentStickyIdx = stickyIndicesArr.length > 0 ? findCurrentStickyIndex(stickyIndicesArr, scroll, state) : -1;
2190
- const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyIndicesArr[currentStickyIdx] : void 0;
2191
- state.activeStickyIndex = nextActiveStickyIndex;
2192
- set$(ctx, "activeStickyIndex", nextActiveStickyIndex);
2193
- let scrollBufferTop = scrollBuffer;
2194
- let scrollBufferBottom = scrollBuffer;
2195
- if (speed > 0 || speed === 0 && scroll < Math.max(50, scrollBuffer)) {
2196
- scrollBufferTop = scrollBuffer * 0.5;
2197
- scrollBufferBottom = scrollBuffer * 1.5;
2641
+ const nextActiveStickyIndex = currentStickyIdx >= 0 ? stickyIndicesArr[currentStickyIdx] : -1;
2642
+ if (currentStickyIdx >= 0 || previousStickyIndex >= 0) {
2643
+ set$(ctx, "activeStickyIndex", nextActiveStickyIndex);
2644
+ }
2645
+ let scrollBufferTop = drawDistance;
2646
+ let scrollBufferBottom = drawDistance;
2647
+ if (speed > 0 || speed === 0 && scroll < Math.max(50, drawDistance)) {
2648
+ scrollBufferTop = drawDistance * 0.5;
2649
+ scrollBufferBottom = drawDistance * 1.5;
2198
2650
  } else {
2199
- scrollBufferTop = scrollBuffer * 1.5;
2200
- scrollBufferBottom = scrollBuffer * 0.5;
2651
+ scrollBufferTop = drawDistance * 1.5;
2652
+ scrollBufferBottom = drawDistance * 0.5;
2201
2653
  }
2202
2654
  const scrollTopBuffered = scroll - scrollBufferTop;
2203
2655
  const scrollBottom = scroll + scrollLength + (scroll < 0 ? -scroll : 0);
2204
2656
  const scrollBottomBuffered = scrollBottom + scrollBufferBottom;
2205
- if (!dataChanged && scrollForNextCalculateItemsInView) {
2657
+ if (!dataChanged && !forceFullItemPositions && scrollForNextCalculateItemsInView) {
2206
2658
  const { top, bottom } = scrollForNextCalculateItemsInView;
2207
- if (scrollTopBuffered > top && scrollBottomBuffered < bottom) {
2208
- if (state.initialAnchor) {
2209
- ensureInitialAnchor(ctx, state);
2659
+ if (top === null && bottom === null) {
2660
+ state.scrollForNextCalculateItemsInView = void 0;
2661
+ } else if ((top === null || scrollTopBuffered > top) && (bottom === null || scrollBottomBuffered < bottom)) {
2662
+ if (!IsNewArchitecture && state.initialAnchor) {
2663
+ ensureInitialAnchor(ctx);
2664
+ }
2665
+ if (Platform2.OS !== "web" || !isInMVCPActiveMode(state)) {
2666
+ return;
2210
2667
  }
2211
- return;
2212
2668
  }
2213
2669
  }
2214
- const checkMVCP = doMVCP ? prepareMVCP(ctx, state, dataChanged) : void 0;
2670
+ const checkMVCP = doMVCP ? prepareMVCP(ctx, dataChanged) : void 0;
2215
2671
  if (dataChanged) {
2216
2672
  indexByKey.clear();
2217
2673
  idCache.length = 0;
2218
- positions.clear();
2674
+ positions.length = 0;
2675
+ columns.length = 0;
2676
+ columnSpans.length = 0;
2219
2677
  }
2220
- const startIndex = dataChanged ? 0 : (_b = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _b : 0;
2221
- updateItemPositions(ctx, state, dataChanged, {
2678
+ const startIndex = forceFullItemPositions || dataChanged ? 0 : (_b = minIndexSizeChanged != null ? minIndexSizeChanged : state.startBuffered) != null ? _b : 0;
2679
+ updateItemPositions(ctx, dataChanged, {
2222
2680
  doMVCP,
2223
2681
  forceFullUpdate: !!forceFullItemPositions,
2224
2682
  scrollBottomBuffered,
@@ -2236,18 +2694,23 @@ function calculateItemsInView(ctx, state, params = {}) {
2236
2694
  let loopStart = !dataChanged && startBufferedIdOrig ? indexByKey.get(startBufferedIdOrig) || 0 : 0;
2237
2695
  for (let i = loopStart; i >= 0; i--) {
2238
2696
  const id = (_c = idCache[i]) != null ? _c : getId(state, i);
2239
- const top = positions.get(id);
2240
- const size = (_d = sizes.get(id)) != null ? _d : getItemSize(ctx, state, id, i, data[i]);
2697
+ const top = positions[i];
2698
+ const size = (_d = sizes.get(id)) != null ? _d : getItemSize(ctx, id, i, data[i]);
2241
2699
  const bottom = top + size;
2242
- if (bottom > scroll - scrollBuffer) {
2700
+ if (bottom > scroll - scrollBufferTop) {
2243
2701
  loopStart = i;
2244
2702
  } else {
2245
2703
  break;
2246
2704
  }
2247
2705
  }
2248
- const loopStartMod = loopStart % numColumns;
2249
- if (loopStartMod > 0) {
2250
- loopStart -= loopStartMod;
2706
+ if (numColumns > 1) {
2707
+ while (loopStart > 0) {
2708
+ const loopColumn = columns[loopStart];
2709
+ if (loopColumn === 1 || loopColumn === void 0) {
2710
+ break;
2711
+ }
2712
+ loopStart -= 1;
2713
+ }
2251
2714
  }
2252
2715
  let foundEnd = false;
2253
2716
  let nextTop;
@@ -2264,8 +2727,8 @@ function calculateItemsInView(ctx, state, params = {}) {
2264
2727
  const dataLength = data.length;
2265
2728
  for (let i = Math.max(0, loopStart); i < dataLength && (!foundEnd || i <= maxIndexRendered); i++) {
2266
2729
  const id = (_e = idCache[i]) != null ? _e : getId(state, i);
2267
- const size = (_f = sizes.get(id)) != null ? _f : getItemSize(ctx, state, id, i, data[i]);
2268
- const top = positions.get(id);
2730
+ const size = (_f = sizes.get(id)) != null ? _f : getItemSize(ctx, id, i, data[i]);
2731
+ const top = positions[i];
2269
2732
  if (!foundEnd) {
2270
2733
  if (startNoBuffer === null && top + size > scroll) {
2271
2734
  startNoBuffer = i;
@@ -2276,7 +2739,11 @@ function calculateItemsInView(ctx, state, params = {}) {
2276
2739
  if (startBuffered === null && top + size > scrollTopBuffered) {
2277
2740
  startBuffered = i;
2278
2741
  startBufferedId = id;
2279
- nextTop = top;
2742
+ if (scrollTopBuffered < 0) {
2743
+ nextTop = null;
2744
+ } else {
2745
+ nextTop = top;
2746
+ }
2280
2747
  }
2281
2748
  if (startNoBuffer !== null) {
2282
2749
  if (top <= scrollBottom) {
@@ -2284,7 +2751,11 @@ function calculateItemsInView(ctx, state, params = {}) {
2284
2751
  }
2285
2752
  if (top <= scrollBottomBuffered) {
2286
2753
  endBuffered = i;
2287
- nextBottom = top + size;
2754
+ if (scrollBottomBuffered > totalSize) {
2755
+ nextBottom = null;
2756
+ } else {
2757
+ nextBottom = top + size;
2758
+ }
2288
2759
  } else {
2289
2760
  foundEnd = true;
2290
2761
  }
@@ -2306,12 +2777,12 @@ function calculateItemsInView(ctx, state, params = {}) {
2306
2777
  startNoBuffer
2307
2778
  });
2308
2779
  if (enableScrollForNextCalculateItemsInView && nextTop !== void 0 && nextBottom !== void 0) {
2309
- state.scrollForNextCalculateItemsInView = nextTop !== void 0 && nextBottom !== void 0 ? {
2780
+ state.scrollForNextCalculateItemsInView = isNullOrUndefined(nextTop) && isNullOrUndefined(nextBottom) ? void 0 : {
2310
2781
  bottom: nextBottom,
2311
2782
  top: nextTop
2312
- } : void 0;
2783
+ };
2313
2784
  }
2314
- const numContainers = peek$(ctx, "numContainers");
2785
+ let numContainers = prevNumContainers;
2315
2786
  const pendingRemoval = [];
2316
2787
  if (dataChanged) {
2317
2788
  for (let i = 0; i < numContainers; i++) {
@@ -2322,37 +2793,46 @@ function calculateItemsInView(ctx, state, params = {}) {
2322
2793
  }
2323
2794
  }
2324
2795
  if (startBuffered !== null && endBuffered !== null) {
2325
- let numContainers2 = prevNumContainers;
2326
2796
  const needNewContainers = [];
2797
+ const needNewContainersSet = /* @__PURE__ */ new Set();
2327
2798
  for (let i = startBuffered; i <= endBuffered; i++) {
2328
2799
  const id = (_h = idCache[i]) != null ? _h : getId(state, i);
2329
2800
  if (!containerItemKeys.has(id)) {
2801
+ needNewContainersSet.add(i);
2330
2802
  needNewContainers.push(i);
2331
2803
  }
2332
2804
  }
2805
+ if (alwaysRenderArr.length > 0) {
2806
+ for (const index of alwaysRenderArr) {
2807
+ if (index < 0 || index >= dataLength) continue;
2808
+ const id = (_i = idCache[index]) != null ? _i : getId(state, index);
2809
+ if (id && !containerItemKeys.has(id) && !needNewContainersSet.has(index)) {
2810
+ needNewContainersSet.add(index);
2811
+ needNewContainers.push(index);
2812
+ }
2813
+ }
2814
+ }
2333
2815
  if (stickyIndicesArr.length > 0) {
2334
2816
  handleStickyActivation(
2335
2817
  ctx,
2336
- state,
2337
2818
  stickyIndicesSet,
2338
2819
  stickyIndicesArr,
2339
2820
  currentStickyIdx,
2340
2821
  needNewContainers,
2822
+ needNewContainersSet,
2341
2823
  startBuffered,
2342
2824
  endBuffered
2343
2825
  );
2344
- } else {
2345
- state.activeStickyIndex = void 0;
2346
- set$(ctx, "activeStickyIndex", void 0);
2826
+ } else if (previousStickyIndex !== -1) {
2827
+ set$(ctx, "activeStickyIndex", -1);
2347
2828
  }
2348
2829
  if (needNewContainers.length > 0) {
2349
2830
  const requiredItemTypes = getItemType ? needNewContainers.map((i) => {
2350
2831
  const itemType = getItemType(data[i], i);
2351
- return itemType ? String(itemType) : "";
2832
+ return itemType !== void 0 ? String(itemType) : "";
2352
2833
  }) : void 0;
2353
2834
  const availableContainers = findAvailableContainers(
2354
2835
  ctx,
2355
- state,
2356
2836
  needNewContainers.length,
2357
2837
  startBuffered,
2358
2838
  endBuffered,
@@ -2363,7 +2843,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2363
2843
  for (let idx = 0; idx < needNewContainers.length; idx++) {
2364
2844
  const i = needNewContainers[idx];
2365
2845
  const containerIndex = availableContainers[idx];
2366
- const id = (_i = idCache[i]) != null ? _i : getId(state, i);
2846
+ const id = (_j = idCache[i]) != null ? _j : getId(state, i);
2367
2847
  const oldKey = peek$(ctx, `containerItemKey${containerIndex}`);
2368
2848
  if (oldKey && oldKey !== id) {
2369
2849
  containerItemKeys.delete(oldKey);
@@ -2373,30 +2853,55 @@ function calculateItemsInView(ctx, state, params = {}) {
2373
2853
  if (requiredItemTypes) {
2374
2854
  state.containerItemTypes.set(containerIndex, requiredItemTypes[idx]);
2375
2855
  }
2376
- containerItemKeys.add(id);
2377
- if (stickyIndicesSet.has(i)) {
2378
- set$(ctx, `containerSticky${containerIndex}`, true);
2379
- const topPadding = (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "headerSize") || 0);
2380
- set$(ctx, `containerStickyOffset${containerIndex}`, topPadding);
2856
+ containerItemKeys.set(id, containerIndex);
2857
+ const containerSticky = `containerSticky${containerIndex}`;
2858
+ const isSticky = stickyIndicesSet.has(i);
2859
+ const isAlwaysRender = alwaysRenderSet.has(i);
2860
+ if (isSticky) {
2861
+ set$(ctx, containerSticky, true);
2381
2862
  state.stickyContainerPool.add(containerIndex);
2382
2863
  } else {
2383
- set$(ctx, `containerSticky${containerIndex}`, false);
2384
- state.stickyContainerPool.delete(containerIndex);
2864
+ if (peek$(ctx, containerSticky)) {
2865
+ set$(ctx, containerSticky, false);
2866
+ }
2867
+ if (isAlwaysRender) {
2868
+ state.stickyContainerPool.add(containerIndex);
2869
+ } else if (state.stickyContainerPool.has(containerIndex)) {
2870
+ state.stickyContainerPool.delete(containerIndex);
2871
+ }
2385
2872
  }
2386
- if (containerIndex >= numContainers2) {
2387
- numContainers2 = containerIndex + 1;
2873
+ if (containerIndex >= numContainers) {
2874
+ numContainers = containerIndex + 1;
2388
2875
  }
2389
2876
  }
2390
- if (numContainers2 !== prevNumContainers) {
2391
- set$(ctx, "numContainers", numContainers2);
2392
- if (numContainers2 > peek$(ctx, "numContainersPooled")) {
2393
- set$(ctx, "numContainersPooled", Math.ceil(numContainers2 * 1.5));
2877
+ if (numContainers !== prevNumContainers) {
2878
+ set$(ctx, "numContainers", numContainers);
2879
+ if (numContainers > peek$(ctx, "numContainersPooled")) {
2880
+ set$(ctx, "numContainersPooled", Math.ceil(numContainers * 1.5));
2881
+ }
2882
+ }
2883
+ }
2884
+ if (alwaysRenderArr.length > 0) {
2885
+ for (const index of alwaysRenderArr) {
2886
+ if (index < 0 || index >= dataLength) continue;
2887
+ const id = (_k = idCache[index]) != null ? _k : getId(state, index);
2888
+ const containerIndex = containerItemKeys.get(id);
2889
+ if (containerIndex !== void 0) {
2890
+ state.stickyContainerPool.add(containerIndex);
2394
2891
  }
2395
2892
  }
2396
2893
  }
2397
2894
  }
2398
- if (stickyIndicesArr.length > 0) {
2399
- handleStickyRecycling(ctx, state, stickyIndicesArr, scroll, scrollBuffer, currentStickyIdx, pendingRemoval);
2895
+ if (state.stickyContainerPool.size > 0) {
2896
+ handleStickyRecycling(
2897
+ ctx,
2898
+ stickyIndicesArr,
2899
+ scroll,
2900
+ drawDistance,
2901
+ currentStickyIdx,
2902
+ pendingRemoval,
2903
+ alwaysRenderSet
2904
+ );
2400
2905
  }
2401
2906
  let didChangePositions = false;
2402
2907
  for (let i = 0; i < numContainers; i++) {
@@ -2408,26 +2913,27 @@ function calculateItemsInView(ctx, state, params = {}) {
2408
2913
  state.containerItemTypes.delete(i);
2409
2914
  if (state.stickyContainerPool.has(i)) {
2410
2915
  set$(ctx, `containerSticky${i}`, false);
2411
- set$(ctx, `containerStickyOffset${i}`, void 0);
2412
2916
  state.stickyContainerPool.delete(i);
2413
2917
  }
2414
2918
  set$(ctx, `containerItemKey${i}`, void 0);
2415
2919
  set$(ctx, `containerItemData${i}`, void 0);
2416
2920
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
2417
2921
  set$(ctx, `containerColumn${i}`, -1);
2922
+ set$(ctx, `containerSpan${i}`, 1);
2418
2923
  } else {
2419
2924
  const itemIndex = indexByKey.get(itemKey);
2420
2925
  const item = data[itemIndex];
2421
2926
  if (item !== void 0) {
2422
- const id = (_j = idCache[itemIndex]) != null ? _j : getId(state, itemIndex);
2423
- const positionValue = positions.get(id);
2927
+ const positionValue = positions[itemIndex];
2424
2928
  if (positionValue === void 0) {
2425
2929
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
2426
2930
  } else {
2427
2931
  const position = (positionValue || 0) - scrollAdjustPending;
2428
- const column = columns.get(id) || 1;
2932
+ const column = columns[itemIndex] || 1;
2933
+ const span = columnSpans[itemIndex] || 1;
2429
2934
  const prevPos = peek$(ctx, `containerPosition${i}`);
2430
2935
  const prevColumn = peek$(ctx, `containerColumn${i}`);
2936
+ const prevSpan = peek$(ctx, `containerSpan${i}`);
2431
2937
  const prevData = peek$(ctx, `containerItemData${i}`);
2432
2938
  if (position > POSITION_OUT_OF_VIEW && position !== prevPos) {
2433
2939
  set$(ctx, `containerPosition${i}`, position);
@@ -2436,6 +2942,9 @@ function calculateItemsInView(ctx, state, params = {}) {
2436
2942
  if (column >= 0 && column !== prevColumn) {
2437
2943
  set$(ctx, `containerColumn${i}`, column);
2438
2944
  }
2945
+ if (span !== prevSpan) {
2946
+ set$(ctx, `containerSpan${i}`, span);
2947
+ }
2439
2948
  if (prevData !== item && (itemsAreEqual ? !itemsAreEqual(prevData, item, itemIndex, data) : true)) {
2440
2949
  set$(ctx, `containerItemData${i}`, item);
2441
2950
  }
@@ -2448,7 +2957,7 @@ function calculateItemsInView(ctx, state, params = {}) {
2448
2957
  }
2449
2958
  if (!queuedInitialLayout && endBuffered !== null) {
2450
2959
  if (checkAllSizesKnown(state)) {
2451
- setDidLayout(ctx, state);
2960
+ setDidLayout(ctx);
2452
2961
  }
2453
2962
  }
2454
2963
  if (viewabilityConfigCallbackPairs) {
@@ -2461,8 +2970,8 @@ function calculateItemsInView(ctx, state, params = {}) {
2461
2970
  }
2462
2971
  }
2463
2972
  });
2464
- if (state.initialAnchor) {
2465
- ensureInitialAnchor(ctx, state);
2973
+ if (!IsNewArchitecture && state.initialAnchor) {
2974
+ ensureInitialAnchor(ctx);
2466
2975
  }
2467
2976
  }
2468
2977
 
@@ -2487,19 +2996,22 @@ function checkActualChange(state, dataProp, previousData) {
2487
2996
  }
2488
2997
 
2489
2998
  // src/core/doMaintainScrollAtEnd.ts
2490
- function doMaintainScrollAtEnd(ctx, state, animated) {
2999
+ function doMaintainScrollAtEnd(ctx, animated) {
3000
+ const state = ctx.state;
2491
3001
  const {
3002
+ didContainersLayout,
3003
+ isAtEnd,
2492
3004
  refScroller,
2493
3005
  props: { maintainScrollAtEnd }
2494
3006
  } = state;
2495
- if ((state == null ? void 0 : state.isAtEnd) && maintainScrollAtEnd && peek$(ctx, "containersDidLayout")) {
2496
- const paddingTop = peek$(ctx, "alignItemsPaddingTop");
2497
- if (paddingTop > 0) {
3007
+ if (isAtEnd && maintainScrollAtEnd && didContainersLayout) {
3008
+ const contentSize = getContentSize(ctx);
3009
+ if (contentSize < state.scrollLength) {
2498
3010
  state.scroll = 0;
2499
3011
  }
2500
3012
  requestAnimationFrame(() => {
2501
3013
  var _a3;
2502
- if (state == null ? void 0 : state.isAtEnd) {
3014
+ if (state.isAtEnd) {
2503
3015
  state.maintainingScrollAtEnd = true;
2504
3016
  (_a3 = refScroller.current) == null ? void 0 : _a3.scrollToEnd({
2505
3017
  animated
@@ -2570,36 +3082,37 @@ function updateAveragesOnDataChange(state, oldData, newData) {
2570
3082
  }
2571
3083
 
2572
3084
  // src/core/checkResetContainers.ts
2573
- function checkResetContainers(ctx, state, dataProp) {
3085
+ function checkResetContainers(ctx, dataProp) {
3086
+ const state = ctx.state;
2574
3087
  const { previousData } = state;
2575
3088
  if (previousData) {
2576
3089
  updateAveragesOnDataChange(state, previousData, dataProp);
2577
3090
  }
2578
3091
  const { maintainScrollAtEnd } = state.props;
2579
- calculateItemsInView(ctx, state, { dataChanged: true, doMVCP: true });
3092
+ calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
2580
3093
  const shouldMaintainScrollAtEnd = maintainScrollAtEnd === true || maintainScrollAtEnd.onDataChange;
2581
- const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, state, false);
3094
+ const didMaintainScrollAtEnd = shouldMaintainScrollAtEnd && doMaintainScrollAtEnd(ctx, false);
2582
3095
  if (!didMaintainScrollAtEnd && previousData && dataProp.length > previousData.length) {
2583
3096
  state.isEndReached = false;
2584
3097
  }
2585
3098
  if (!didMaintainScrollAtEnd) {
2586
- checkAtTop(state);
2587
- checkAtBottom(ctx, state);
3099
+ checkThresholds(ctx);
2588
3100
  }
2589
3101
  delete state.previousData;
2590
3102
  }
2591
3103
 
2592
3104
  // src/core/doInitialAllocateContainers.ts
2593
- function doInitialAllocateContainers(ctx, state, dataChanged) {
3105
+ function doInitialAllocateContainers(ctx) {
2594
3106
  var _a3, _b, _c;
3107
+ const state = ctx.state;
2595
3108
  const {
2596
3109
  scrollLength,
2597
3110
  props: {
2598
3111
  data,
3112
+ drawDistance,
2599
3113
  getEstimatedItemSize,
2600
3114
  getFixedItemSize,
2601
3115
  getItemType,
2602
- scrollBuffer,
2603
3116
  numColumns,
2604
3117
  estimatedItemSize
2605
3118
  }
@@ -2612,40 +3125,58 @@ function doInitialAllocateContainers(ctx, state, dataChanged) {
2612
3125
  const num = Math.min(20, data.length);
2613
3126
  for (let i = 0; i < num; i++) {
2614
3127
  const item = data[i];
2615
- const itemType = getItemType ? (_a3 = getItemType(item, i)) != null ? _a3 : "" : "";
2616
- totalSize += (_c = (_b = getFixedItemSize == null ? void 0 : getFixedItemSize(i, item, itemType)) != null ? _b : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(i, item, itemType)) != null ? _c : estimatedItemSize;
3128
+ if (item !== void 0) {
3129
+ const itemType = (_a3 = getItemType == null ? void 0 : getItemType(item, i)) != null ? _a3 : "";
3130
+ totalSize += (_c = (_b = getFixedItemSize == null ? void 0 : getFixedItemSize(item, i, itemType)) != null ? _b : getEstimatedItemSize == null ? void 0 : getEstimatedItemSize(item, i, itemType)) != null ? _c : estimatedItemSize;
3131
+ }
2617
3132
  }
2618
3133
  averageItemSize = totalSize / num;
2619
3134
  } else {
2620
3135
  averageItemSize = estimatedItemSize;
2621
3136
  }
2622
- const numContainers = Math.ceil((scrollLength + scrollBuffer * 2) / averageItemSize * numColumns);
3137
+ const numContainers = Math.ceil((scrollLength + drawDistance * 2) / averageItemSize * numColumns);
2623
3138
  for (let i = 0; i < numContainers; i++) {
2624
3139
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
2625
3140
  set$(ctx, `containerColumn${i}`, -1);
3141
+ set$(ctx, `containerSpan${i}`, 1);
2626
3142
  }
2627
3143
  set$(ctx, "numContainers", numContainers);
2628
3144
  set$(ctx, "numContainersPooled", numContainers * state.props.initialContainerPoolRatio);
2629
3145
  if (!IsNewArchitecture || state.lastLayout) {
2630
3146
  if (state.initialScroll) {
2631
3147
  requestAnimationFrame(() => {
2632
- calculateItemsInView(ctx, state, { dataChanged, doMVCP: true });
3148
+ calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
2633
3149
  });
2634
3150
  } else {
2635
- calculateItemsInView(ctx, state, { dataChanged, doMVCP: true });
3151
+ calculateItemsInView(ctx, { dataChanged: true, doMVCP: true });
2636
3152
  }
2637
3153
  }
2638
3154
  return true;
2639
3155
  }
2640
3156
  }
3157
+ function getWindowSize() {
3158
+ const screenSize = ReactNative.Dimensions.get("window");
3159
+ return {
3160
+ height: screenSize.height,
3161
+ width: screenSize.width
3162
+ };
3163
+ }
2641
3164
 
2642
3165
  // src/core/handleLayout.ts
2643
- function handleLayout(ctx, state, layout, setCanRender) {
2644
- const { maintainScrollAtEnd } = state.props;
2645
- const measuredLength = layout[state.props.horizontal ? "width" : "height"];
3166
+ function handleLayout(ctx, layoutParam, setCanRender) {
3167
+ const state = ctx.state;
3168
+ const { maintainScrollAtEnd, useWindowScroll } = state.props;
3169
+ const scrollAxis = state.props.horizontal ? "width" : "height";
3170
+ const otherAxis = state.props.horizontal ? "height" : "width";
3171
+ let layout = layoutParam;
3172
+ if (useWindowScroll) {
3173
+ const windowScrollAxisLength = getWindowSize()[scrollAxis];
3174
+ layout = windowScrollAxisLength > 0 ? { ...layoutParam, [scrollAxis]: windowScrollAxisLength } : layoutParam;
3175
+ }
3176
+ const measuredLength = layout[scrollAxis];
2646
3177
  const previousLength = state.scrollLength;
2647
3178
  const scrollLength = measuredLength > 0 ? measuredLength : previousLength;
2648
- const otherAxisSize = layout[state.props.horizontal ? "height" : "width"];
3179
+ const otherAxisSize = layout[otherAxis];
2649
3180
  const needsCalculate = !state.lastLayout || scrollLength > state.scrollLength || state.lastLayout.x !== layout.x || state.lastLayout.y !== layout.y;
2650
3181
  state.lastLayout = layout;
2651
3182
  const prevOtherAxisSize = state.otherAxisSize;
@@ -2656,20 +3187,18 @@ function handleLayout(ctx, state, layout, setCanRender) {
2656
3187
  state.lastBatchingAction = Date.now();
2657
3188
  state.scrollForNextCalculateItemsInView = void 0;
2658
3189
  if (scrollLength > 0) {
2659
- doInitialAllocateContainers(ctx, state);
3190
+ doInitialAllocateContainers(ctx);
2660
3191
  }
2661
3192
  if (needsCalculate) {
2662
- calculateItemsInView(ctx, state, { doMVCP: true });
3193
+ calculateItemsInView(ctx, { doMVCP: true });
2663
3194
  }
2664
3195
  if (didChange || otherAxisSize !== prevOtherAxisSize) {
2665
3196
  set$(ctx, "scrollSize", { height: layout.height, width: layout.width });
2666
3197
  }
2667
3198
  if (maintainScrollAtEnd === true || maintainScrollAtEnd.onLayout) {
2668
- doMaintainScrollAtEnd(ctx, state, false);
3199
+ doMaintainScrollAtEnd(ctx, false);
2669
3200
  }
2670
- updateAlignItemsPaddingTop(ctx, state);
2671
- checkAtBottom(ctx, state);
2672
- checkAtTop(state);
3201
+ checkThresholds(ctx);
2673
3202
  if (state) {
2674
3203
  state.needsOtherAxisSize = otherAxisSize - (state.props.stylePaddingTop || 0) < 10;
2675
3204
  }
@@ -2684,8 +3213,9 @@ function handleLayout(ctx, state, layout, setCanRender) {
2684
3213
  }
2685
3214
 
2686
3215
  // src/core/onScroll.ts
2687
- function onScroll(ctx, state, event) {
2688
- var _a3, _b, _c;
3216
+ function onScroll(ctx, event) {
3217
+ var _a3, _b, _c, _d;
3218
+ const state = ctx.state;
2689
3219
  const {
2690
3220
  scrollProcessingEnabled,
2691
3221
  props: { onScroll: onScrollProp }
@@ -2696,17 +3226,34 @@ function onScroll(ctx, state, event) {
2696
3226
  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) {
2697
3227
  return;
2698
3228
  }
3229
+ let insetChanged = false;
3230
+ if ((_d = event.nativeEvent) == null ? void 0 : _d.contentInset) {
3231
+ const { contentInset } = event.nativeEvent;
3232
+ const prevInset = state.nativeContentInset;
3233
+ if (!prevInset || prevInset.top !== contentInset.top || prevInset.bottom !== contentInset.bottom || prevInset.left !== contentInset.left || prevInset.right !== contentInset.right) {
3234
+ state.nativeContentInset = contentInset;
3235
+ insetChanged = true;
3236
+ }
3237
+ }
2699
3238
  let newScroll = event.nativeEvent.contentOffset[state.props.horizontal ? "x" : "y"];
3239
+ if (state.scrollingTo && state.scrollingTo.offset >= newScroll) {
3240
+ const maxOffset = clampScrollOffset(ctx, newScroll, state.scrollingTo);
3241
+ if (newScroll !== maxOffset && Math.abs(newScroll - maxOffset) > 1) {
3242
+ newScroll = maxOffset;
3243
+ scrollTo(ctx, {
3244
+ forceScroll: true,
3245
+ isInitialScroll: true,
3246
+ noScrollingTo: true,
3247
+ offset: newScroll
3248
+ });
3249
+ return;
3250
+ }
3251
+ }
2700
3252
  state.scrollPending = newScroll;
2701
- const maxOffset = Math.max(0, getContentSize(ctx) - state.scrollLength);
2702
- if (state.initialScroll && newScroll > maxOffset) {
2703
- newScroll = maxOffset;
2704
- scrollTo(ctx, state, {
2705
- noScrollingTo: true,
2706
- offset: newScroll
2707
- });
3253
+ updateScroll(ctx, newScroll, insetChanged);
3254
+ if (state.scrollingTo) {
3255
+ checkFinishedScroll(ctx);
2708
3256
  }
2709
- updateScroll(ctx, state, newScroll);
2710
3257
  onScrollProp == null ? void 0 : onScrollProp(event);
2711
3258
  }
2712
3259
 
@@ -2715,51 +3262,80 @@ var ScrollAdjustHandler = class {
2715
3262
  constructor(ctx) {
2716
3263
  this.appliedAdjust = 0;
2717
3264
  this.pendingAdjust = 0;
2718
- this.mounted = false;
2719
- this.context = ctx;
2720
- if (Platform2.OS === "web") {
2721
- const commitPendingAdjust = () => {
2722
- const state = this.context.internalState;
2723
- const pending = this.pendingAdjust;
2724
- if (pending !== 0) {
2725
- this.pendingAdjust = 0;
2726
- this.appliedAdjust += pending;
2727
- state.scroll += pending;
2728
- state.scrollForNextCalculateItemsInView = void 0;
2729
- set$(this.context, "scrollAdjustPending", 0);
2730
- set$(this.context, "scrollAdjust", this.appliedAdjust);
2731
- calculateItemsInView(this.context, this.context.internalState);
2732
- }
2733
- };
2734
- listen$(this.context, "scrollingTo", (value) => {
2735
- if (value === void 0) {
2736
- commitPendingAdjust();
2737
- }
2738
- });
2739
- }
3265
+ this.ctx = ctx;
2740
3266
  }
2741
3267
  requestAdjust(add) {
2742
- const scrollingTo = peek$(this.context, "scrollingTo");
2743
- if (Platform2.OS === "web" && (scrollingTo == null ? void 0 : scrollingTo.animated) && !scrollingTo.isInitialScroll) {
3268
+ const scrollingTo = this.ctx.state.scrollingTo;
3269
+ if (PlatformAdjustBreaksScroll && (scrollingTo == null ? void 0 : scrollingTo.animated) && !scrollingTo.isInitialScroll) {
2744
3270
  this.pendingAdjust += add;
2745
- set$(this.context, "scrollAdjustPending", this.pendingAdjust);
3271
+ set$(this.ctx, "scrollAdjustPending", this.pendingAdjust);
2746
3272
  } else {
2747
3273
  this.appliedAdjust += add;
2748
- set$(this.context, "scrollAdjust", this.appliedAdjust);
3274
+ set$(this.ctx, "scrollAdjust", this.appliedAdjust);
3275
+ }
3276
+ if (this.ctx.state.scrollingTo) {
3277
+ checkFinishedScroll(this.ctx);
2749
3278
  }
2750
- }
2751
- setMounted() {
2752
- this.mounted = true;
2753
3279
  }
2754
3280
  getAdjust() {
2755
3281
  return this.appliedAdjust;
2756
3282
  }
3283
+ commitPendingAdjust(scrollTarget) {
3284
+ if (PlatformAdjustBreaksScroll) {
3285
+ const state = this.ctx.state;
3286
+ const pending = this.pendingAdjust;
3287
+ this.pendingAdjust = 0;
3288
+ if (pending !== 0) {
3289
+ let targetScroll;
3290
+ if ((scrollTarget == null ? void 0 : scrollTarget.index) !== void 0) {
3291
+ const currentOffset = calculateOffsetForIndex(this.ctx, scrollTarget.index);
3292
+ targetScroll = calculateOffsetWithOffsetPosition(this.ctx, currentOffset, scrollTarget);
3293
+ targetScroll = clampScrollOffset(this.ctx, targetScroll, scrollTarget);
3294
+ } else {
3295
+ targetScroll = clampScrollOffset(this.ctx, state.scroll + pending);
3296
+ }
3297
+ const adjustment = targetScroll - state.scroll;
3298
+ if (Math.abs(adjustment) > 0.1 || Math.abs(pending) > 0.1) {
3299
+ this.appliedAdjust += adjustment;
3300
+ state.scroll = targetScroll;
3301
+ state.scrollForNextCalculateItemsInView = void 0;
3302
+ set$(this.ctx, "scrollAdjust", this.appliedAdjust);
3303
+ }
3304
+ set$(this.ctx, "scrollAdjustPending", 0);
3305
+ calculateItemsInView(this.ctx);
3306
+ }
3307
+ }
3308
+ }
2757
3309
  };
2758
3310
 
2759
3311
  // src/core/updateItemSize.ts
2760
- function updateItemSize(ctx, state, itemKey, sizeObj) {
3312
+ function runOrScheduleMVCPRecalculate(ctx) {
3313
+ const state = ctx.state;
3314
+ if (Platform2.OS === "web") {
3315
+ if (!state.mvcpAnchorLock) {
3316
+ if (state.queuedMVCPRecalculate !== void 0) {
3317
+ cancelAnimationFrame(state.queuedMVCPRecalculate);
3318
+ state.queuedMVCPRecalculate = void 0;
3319
+ }
3320
+ calculateItemsInView(ctx, { doMVCP: true });
3321
+ return;
3322
+ }
3323
+ if (state.queuedMVCPRecalculate !== void 0) {
3324
+ return;
3325
+ }
3326
+ state.queuedMVCPRecalculate = requestAnimationFrame(() => {
3327
+ state.queuedMVCPRecalculate = void 0;
3328
+ calculateItemsInView(ctx, { doMVCP: true });
3329
+ });
3330
+ } else {
3331
+ calculateItemsInView(ctx, { doMVCP: true });
3332
+ }
3333
+ }
3334
+ function updateItemSize(ctx, itemKey, sizeObj) {
2761
3335
  var _a3;
3336
+ const state = ctx.state;
2762
3337
  const {
3338
+ didContainersLayout,
2763
3339
  sizesKnown,
2764
3340
  props: {
2765
3341
  getFixedItemSize,
@@ -2782,31 +3358,24 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
2782
3358
  return;
2783
3359
  }
2784
3360
  const type = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
2785
- const size2 = getFixedItemSize(index, itemData, type);
3361
+ const size2 = getFixedItemSize(itemData, index, type);
2786
3362
  if (size2 !== void 0 && size2 === sizesKnown.get(itemKey)) {
2787
3363
  return;
2788
3364
  }
2789
3365
  }
2790
- const containersDidLayout = peek$(ctx, "containersDidLayout");
2791
- let needsRecalculate = !containersDidLayout;
3366
+ let needsRecalculate = !didContainersLayout;
2792
3367
  let shouldMaintainScrollAtEnd = false;
2793
3368
  let minIndexSizeChanged;
2794
3369
  let maxOtherAxisSize = peek$(ctx, "otherAxisSize") || 0;
2795
3370
  const prevSizeKnown = state.sizesKnown.get(itemKey);
2796
- const diff = updateOneItemSize(ctx, state, itemKey, sizeObj);
3371
+ const diff = updateOneItemSize(ctx, itemKey, sizeObj);
2797
3372
  const size = roundSize(horizontal ? sizeObj.width : sizeObj.height);
2798
3373
  if (diff !== 0) {
2799
3374
  minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
2800
3375
  const { startBuffered, endBuffered } = state;
2801
3376
  needsRecalculate || (needsRecalculate = index >= startBuffered && index <= endBuffered);
2802
- if (!needsRecalculate) {
2803
- const numContainers = ctx.values.get("numContainers");
2804
- for (let i = 0; i < numContainers; i++) {
2805
- if (peek$(ctx, `containerItemKey${i}`) === itemKey) {
2806
- needsRecalculate = true;
2807
- break;
2808
- }
2809
- }
3377
+ if (!needsRecalculate && state.containerItemKeys.has(itemKey)) {
3378
+ needsRecalculate = true;
2810
3379
  }
2811
3380
  if (state.needsOtherAxisSize) {
2812
3381
  const otherAxisSize = horizontal ? sizeObj.height : sizeObj.width;
@@ -2842,20 +3411,21 @@ function updateItemSize(ctx, state, itemKey, sizeObj) {
2842
3411
  if (!cur || maxOtherAxisSize > cur) {
2843
3412
  set$(ctx, "otherAxisSize", maxOtherAxisSize);
2844
3413
  }
2845
- if (containersDidLayout || checkAllSizesKnown(state)) {
3414
+ if (didContainersLayout || checkAllSizesKnown(state)) {
2846
3415
  if (needsRecalculate) {
2847
3416
  state.scrollForNextCalculateItemsInView = void 0;
2848
- calculateItemsInView(ctx, state, { doMVCP: true });
3417
+ runOrScheduleMVCPRecalculate(ctx);
2849
3418
  }
2850
3419
  if (shouldMaintainScrollAtEnd) {
2851
3420
  if (maintainScrollAtEnd === true || maintainScrollAtEnd.onItemLayout) {
2852
- doMaintainScrollAtEnd(ctx, state, false);
3421
+ doMaintainScrollAtEnd(ctx, false);
2853
3422
  }
2854
3423
  }
2855
3424
  }
2856
3425
  }
2857
- function updateOneItemSize(ctx, state, itemKey, sizeObj) {
3426
+ function updateOneItemSize(ctx, itemKey, sizeObj) {
2858
3427
  var _a3;
3428
+ const state = ctx.state;
2859
3429
  const {
2860
3430
  indexByKey,
2861
3431
  sizesKnown,
@@ -2864,9 +3434,10 @@ function updateOneItemSize(ctx, state, itemKey, sizeObj) {
2864
3434
  } = state;
2865
3435
  if (!data) return 0;
2866
3436
  const index = indexByKey.get(itemKey);
2867
- const prevSize = getItemSize(ctx, state, itemKey, index, data[index]);
3437
+ const prevSize = getItemSize(ctx, itemKey, index, data[index]);
2868
3438
  const rawSize = horizontal ? sizeObj.width : sizeObj.height;
2869
3439
  const size = Platform2.OS === "web" ? Math.round(rawSize) : roundSize(rawSize);
3440
+ const prevSizeKnown = sizesKnown.get(itemKey);
2870
3441
  sizesKnown.set(itemKey, size);
2871
3442
  if (!getEstimatedItemSize && !getFixedItemSize && size > 0) {
2872
3443
  const itemType = getItemType ? (_a3 = getItemType(data[index], index)) != null ? _a3 : "" : "";
@@ -2874,15 +3445,28 @@ function updateOneItemSize(ctx, state, itemKey, sizeObj) {
2874
3445
  if (!averages) {
2875
3446
  averages = averageSizes[itemType] = { avg: 0, num: 0 };
2876
3447
  }
2877
- averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
2878
- averages.num++;
3448
+ if (averages.num === 0) {
3449
+ averages.avg = size;
3450
+ averages.num++;
3451
+ } else if (prevSizeKnown !== void 0 && prevSizeKnown > 0) {
3452
+ averages.avg += (size - prevSizeKnown) / averages.num;
3453
+ } else {
3454
+ averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
3455
+ averages.num++;
3456
+ }
2879
3457
  }
2880
3458
  if (!prevSize || Math.abs(prevSize - size) > 0.1) {
2881
- setSize(ctx, state, itemKey, size);
3459
+ setSize(ctx, itemKey, size);
2882
3460
  return size - prevSize;
2883
3461
  }
2884
3462
  return 0;
2885
3463
  }
3464
+ function useWrapIfItem(fn) {
3465
+ return React2.useMemo(
3466
+ () => fn ? (arg1, arg2, arg3) => arg1 !== void 0 && arg2 !== void 0 ? fn(arg1, arg2, arg3) : void 0 : void 0,
3467
+ [fn]
3468
+ );
3469
+ }
2886
3470
  var useCombinedRef = (...refs) => {
2887
3471
  const callback = React2.useCallback((element) => {
2888
3472
  for (const ref of refs) {
@@ -2898,19 +3482,13 @@ var useCombinedRef = (...refs) => {
2898
3482
  }, refs);
2899
3483
  return callback;
2900
3484
  };
2901
- function getWindowSize() {
2902
- const screenSize = reactNative.Dimensions.get("window");
2903
- return {
2904
- height: screenSize.height,
2905
- width: screenSize.width
2906
- };
2907
- }
2908
- var StyleSheet = reactNative.StyleSheet;
3485
+ var StyleSheet = ReactNative.StyleSheet;
2909
3486
  function useStickyScrollHandler(stickyHeaderIndices, horizontal, ctx, onScroll2) {
3487
+ const shouldUseRnAnimatedEngine = !ctx.state.props.stickyPositionComponentInternal;
2910
3488
  return React2.useMemo(() => {
2911
- if (stickyHeaderIndices == null ? void 0 : stickyHeaderIndices.length) {
3489
+ if ((stickyHeaderIndices == null ? void 0 : stickyHeaderIndices.length) && shouldUseRnAnimatedEngine) {
2912
3490
  const { animatedScrollY } = ctx;
2913
- return reactNative.Animated.event(
3491
+ return ReactNative.Animated.event(
2914
3492
  [
2915
3493
  {
2916
3494
  nativeEvent: {
@@ -2925,7 +3503,7 @@ function useStickyScrollHandler(stickyHeaderIndices, horizontal, ctx, onScroll2)
2925
3503
  );
2926
3504
  }
2927
3505
  return onScroll2;
2928
- }, [stickyHeaderIndices == null ? void 0 : stickyHeaderIndices.join(","), horizontal]);
3506
+ }, [stickyHeaderIndices == null ? void 0 : stickyHeaderIndices.join(","), horizontal, shouldUseRnAnimatedEngine]);
2929
3507
  }
2930
3508
 
2931
3509
  // src/utils/createColumnWrapperStyle.ts
@@ -2943,31 +3521,118 @@ function createColumnWrapperStyle(contentContainerStyle) {
2943
3521
  }
2944
3522
  }
2945
3523
 
3524
+ // src/utils/hasActiveMVCPAnchorLock.ts
3525
+ function hasActiveMVCPAnchorLock(state) {
3526
+ const lock = state.mvcpAnchorLock;
3527
+ if (!lock) {
3528
+ return false;
3529
+ }
3530
+ if (Date.now() > lock.expiresAt) {
3531
+ state.mvcpAnchorLock = void 0;
3532
+ return false;
3533
+ }
3534
+ return true;
3535
+ }
3536
+
2946
3537
  // src/utils/createImperativeHandle.ts
2947
- function createImperativeHandle(ctx, state) {
3538
+ function createImperativeHandle(ctx) {
3539
+ const state = ctx.state;
3540
+ const IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS = 800;
3541
+ const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
3542
+ let imperativeScrollToken = 0;
3543
+ const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0 || hasActiveMVCPAnchorLock(state);
3544
+ const runWhenSettled = (token, run) => {
3545
+ const startedAt = Date.now();
3546
+ let stableFrames = 0;
3547
+ const check = () => {
3548
+ if (token !== imperativeScrollToken) {
3549
+ return;
3550
+ }
3551
+ if (isSettlingAfterDataChange()) {
3552
+ stableFrames = 0;
3553
+ } else {
3554
+ stableFrames += 1;
3555
+ }
3556
+ const timedOut = Date.now() - startedAt >= IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS;
3557
+ if (stableFrames >= IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES || timedOut) {
3558
+ run();
3559
+ return;
3560
+ }
3561
+ requestAnimationFrame(check);
3562
+ };
3563
+ requestAnimationFrame(check);
3564
+ };
3565
+ const runScrollWithPromise = (run) => new Promise((resolve) => {
3566
+ var _a3;
3567
+ const token = ++imperativeScrollToken;
3568
+ (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
3569
+ state.pendingScrollResolve = resolve;
3570
+ const runNow = () => {
3571
+ if (token !== imperativeScrollToken) {
3572
+ return;
3573
+ }
3574
+ const didStartScroll = run();
3575
+ if (!didStartScroll || !state.scrollingTo) {
3576
+ if (state.pendingScrollResolve === resolve) {
3577
+ state.pendingScrollResolve = void 0;
3578
+ }
3579
+ resolve();
3580
+ }
3581
+ };
3582
+ if (isSettlingAfterDataChange()) {
3583
+ runWhenSettled(token, runNow);
3584
+ return;
3585
+ }
3586
+ runNow();
3587
+ });
2948
3588
  const scrollIndexIntoView = (options) => {
2949
3589
  if (state) {
2950
3590
  const { index, ...rest } = options;
2951
3591
  const { startNoBuffer, endNoBuffer } = state;
2952
3592
  if (index < startNoBuffer || index > endNoBuffer) {
2953
3593
  const viewPosition = index < startNoBuffer ? 0 : 1;
2954
- scrollToIndex(ctx, state, {
3594
+ scrollToIndex(ctx, {
2955
3595
  ...rest,
2956
3596
  index,
2957
3597
  viewPosition
2958
3598
  });
3599
+ return true;
2959
3600
  }
2960
3601
  }
3602
+ return false;
2961
3603
  };
2962
3604
  const refScroller = state.refScroller;
3605
+ const clearCaches = (options) => {
3606
+ var _a3, _b;
3607
+ const mode = (_a3 = options == null ? void 0 : options.mode) != null ? _a3 : "sizes";
3608
+ state.sizes.clear();
3609
+ state.sizesKnown.clear();
3610
+ for (const key in state.averageSizes) {
3611
+ delete state.averageSizes[key];
3612
+ }
3613
+ state.minIndexSizeChanged = 0;
3614
+ state.scrollForNextCalculateItemsInView = void 0;
3615
+ state.pendingTotalSize = void 0;
3616
+ state.totalSize = 0;
3617
+ set$(ctx, "totalSize", 0);
3618
+ if (mode === "full") {
3619
+ state.indexByKey.clear();
3620
+ state.idCache.length = 0;
3621
+ state.positions.length = 0;
3622
+ state.columns.length = 0;
3623
+ state.columnSpans.length = 0;
3624
+ }
3625
+ (_b = state.triggerCalculateItemsInView) == null ? void 0 : _b.call(state, { forceFullItemPositions: true });
3626
+ };
2963
3627
  return {
3628
+ clearCaches,
2964
3629
  flashScrollIndicators: () => refScroller.current.flashScrollIndicators(),
2965
3630
  getNativeScrollRef: () => refScroller.current,
2966
3631
  getScrollableNode: () => refScroller.current.getScrollableNode(),
2967
3632
  getScrollResponder: () => refScroller.current.getScrollResponder(),
2968
3633
  getState: () => ({
2969
- activeStickyIndex: state.activeStickyIndex,
2970
- contentLength: state.totalSize,
3634
+ activeStickyIndex: peek$(ctx, "activeStickyIndex"),
3635
+ contentLength: getContentSize(ctx),
2971
3636
  data: state.props.data,
2972
3637
  elementAtIndex: (index) => {
2973
3638
  var _a3;
@@ -2977,47 +3642,71 @@ function createImperativeHandle(ctx, state) {
2977
3642
  endBuffered: state.endBuffered,
2978
3643
  isAtEnd: state.isAtEnd,
2979
3644
  isAtStart: state.isAtStart,
2980
- positionAtIndex: (index) => state.positions.get(getId(state, index)),
2981
- positions: state.positions,
3645
+ isEndReached: state.isEndReached,
3646
+ isStartReached: state.isStartReached,
3647
+ listen: (signalName, cb) => listen$(ctx, signalName, cb),
3648
+ listenToPosition: (key, cb) => listenPosition$(ctx, key, cb),
3649
+ positionAtIndex: (index) => state.positions[index],
3650
+ positionByKey: (key) => {
3651
+ const index = state.indexByKey.get(key);
3652
+ return index === void 0 ? void 0 : state.positions[index];
3653
+ },
2982
3654
  scroll: state.scroll,
2983
3655
  scrollLength: state.scrollLength,
3656
+ scrollVelocity: getScrollVelocity(state),
2984
3657
  sizeAtIndex: (index) => state.sizesKnown.get(getId(state, index)),
2985
3658
  sizes: state.sizesKnown,
2986
3659
  start: state.startNoBuffer,
2987
3660
  startBuffered: state.startBuffered
2988
3661
  }),
2989
- scrollIndexIntoView,
2990
- scrollItemIntoView: ({ item, ...props }) => {
3662
+ reportContentInset: (inset) => {
3663
+ state.contentInsetOverride = inset != null ? inset : void 0;
3664
+ updateScroll(ctx, state.scroll, true);
3665
+ },
3666
+ scrollIndexIntoView: (options) => runScrollWithPromise(() => scrollIndexIntoView(options)),
3667
+ scrollItemIntoView: ({ item, ...props }) => runScrollWithPromise(() => {
2991
3668
  const data = state.props.data;
2992
3669
  const index = data.indexOf(item);
2993
3670
  if (index !== -1) {
2994
3671
  scrollIndexIntoView({ index, ...props });
3672
+ return true;
2995
3673
  }
2996
- },
2997
- scrollToEnd: (options) => {
3674
+ return false;
3675
+ }),
3676
+ scrollToEnd: (options) => runScrollWithPromise(() => {
2998
3677
  const data = state.props.data;
2999
3678
  const stylePaddingBottom = state.props.stylePaddingBottom;
3000
3679
  const index = data.length - 1;
3001
3680
  if (index !== -1) {
3002
3681
  const paddingBottom = stylePaddingBottom || 0;
3003
3682
  const footerSize = peek$(ctx, "footerSize") || 0;
3004
- scrollToIndex(ctx, state, {
3683
+ scrollToIndex(ctx, {
3684
+ ...options,
3005
3685
  index,
3006
3686
  viewOffset: -paddingBottom - footerSize + ((options == null ? void 0 : options.viewOffset) || 0),
3007
- viewPosition: 1,
3008
- ...options
3687
+ viewPosition: 1
3009
3688
  });
3689
+ return true;
3010
3690
  }
3011
- },
3012
- scrollToIndex: (params) => scrollToIndex(ctx, state, params),
3013
- scrollToItem: ({ item, ...props }) => {
3691
+ return false;
3692
+ }),
3693
+ scrollToIndex: (params) => runScrollWithPromise(() => {
3694
+ scrollToIndex(ctx, params);
3695
+ return true;
3696
+ }),
3697
+ scrollToItem: ({ item, ...props }) => runScrollWithPromise(() => {
3014
3698
  const data = state.props.data;
3015
3699
  const index = data.indexOf(item);
3016
3700
  if (index !== -1) {
3017
- scrollToIndex(ctx, state, { index, ...props });
3701
+ scrollToIndex(ctx, { index, ...props });
3702
+ return true;
3018
3703
  }
3019
- },
3020
- scrollToOffset: (params) => scrollTo(ctx, state, params),
3704
+ return false;
3705
+ }),
3706
+ scrollToOffset: (params) => runScrollWithPromise(() => {
3707
+ scrollTo(ctx, params);
3708
+ return true;
3709
+ }),
3021
3710
  setScrollProcessingEnabled: (enabled) => {
3022
3711
  state.scrollProcessingEnabled = enabled;
3023
3712
  },
@@ -3027,8 +3716,57 @@ function createImperativeHandle(ctx, state) {
3027
3716
  }
3028
3717
  };
3029
3718
  }
3030
- function getRenderedItem(ctx, state, key) {
3719
+
3720
+ // src/utils/getAlwaysRenderIndices.ts
3721
+ var sortAsc = (a, b) => a - b;
3722
+ var toCount = (value) => typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
3723
+ var addIndex = (result, dataLength, index) => {
3724
+ if (index >= 0 && index < dataLength) {
3725
+ result.add(index);
3726
+ }
3727
+ };
3728
+ function getAlwaysRenderIndices(config, data, keyExtractor) {
3729
+ var _a3, _b;
3730
+ if (!config || data.length === 0) {
3731
+ return [];
3732
+ }
3733
+ const result = /* @__PURE__ */ new Set();
3734
+ const dataLength = data.length;
3735
+ const topCount = toCount(config.top);
3736
+ if (topCount > 0) {
3737
+ for (let i = 0; i < Math.min(topCount, dataLength); i++) {
3738
+ addIndex(result, dataLength, i);
3739
+ }
3740
+ }
3741
+ const bottomCount = toCount(config.bottom);
3742
+ if (bottomCount > 0) {
3743
+ for (let i = Math.max(0, dataLength - bottomCount); i < dataLength; i++) {
3744
+ addIndex(result, dataLength, i);
3745
+ }
3746
+ }
3747
+ if ((_a3 = config.indices) == null ? void 0 : _a3.length) {
3748
+ for (const index of config.indices) {
3749
+ if (!Number.isFinite(index)) continue;
3750
+ addIndex(result, dataLength, Math.floor(index));
3751
+ }
3752
+ }
3753
+ if ((_b = config.keys) == null ? void 0 : _b.length) {
3754
+ const keys = new Set(config.keys);
3755
+ for (let i = 0; i < dataLength && keys.size > 0; i++) {
3756
+ const key = keyExtractor(data[i], i);
3757
+ if (keys.has(key)) {
3758
+ addIndex(result, dataLength, i);
3759
+ keys.delete(key);
3760
+ }
3761
+ }
3762
+ }
3763
+ const indices = Array.from(result);
3764
+ indices.sort(sortAsc);
3765
+ return indices;
3766
+ }
3767
+ function getRenderedItem(ctx, key) {
3031
3768
  var _a3;
3769
+ const state = ctx.state;
3032
3770
  if (!state) {
3033
3771
  return null;
3034
3772
  }
@@ -3055,6 +3793,42 @@ function getRenderedItem(ctx, state, key) {
3055
3793
  }
3056
3794
  return { index, item: data[index], renderedItem };
3057
3795
  }
3796
+
3797
+ // src/utils/normalizeMaintainVisibleContentPosition.ts
3798
+ function normalizeMaintainVisibleContentPosition(value) {
3799
+ var _a3, _b;
3800
+ if (value === true) {
3801
+ return { data: true, size: true };
3802
+ }
3803
+ if (value && typeof value === "object") {
3804
+ return {
3805
+ data: (_a3 = value.data) != null ? _a3 : false,
3806
+ shouldRestorePosition: value.shouldRestorePosition,
3807
+ size: (_b = value.size) != null ? _b : true
3808
+ };
3809
+ }
3810
+ if (value === false) {
3811
+ return { data: false, size: false };
3812
+ }
3813
+ return { data: false, size: true };
3814
+ }
3815
+
3816
+ // src/utils/setPaddingTop.ts
3817
+ function setPaddingTop(ctx, { stylePaddingTop }) {
3818
+ const state = ctx.state;
3819
+ if (stylePaddingTop !== void 0) {
3820
+ const prevStylePaddingTop = peek$(ctx, "stylePaddingTop") || 0;
3821
+ if (stylePaddingTop < prevStylePaddingTop) {
3822
+ let prevTotalSize = peek$(ctx, "totalSize") || 0;
3823
+ set$(ctx, "totalSize", prevTotalSize + prevStylePaddingTop);
3824
+ state.timeoutSetPaddingTop = setTimeout(() => {
3825
+ prevTotalSize = peek$(ctx, "totalSize") || 0;
3826
+ set$(ctx, "totalSize", prevTotalSize - prevStylePaddingTop);
3827
+ }, 16);
3828
+ }
3829
+ set$(ctx, "stylePaddingTop", stylePaddingTop);
3830
+ }
3831
+ }
3058
3832
  function useThrottleDebounce(mode) {
3059
3833
  const timeoutRef = React2.useRef(null);
3060
3834
  const lastCallTimeRef = React2.useRef(0);
@@ -3102,9 +3876,8 @@ function useThrottledOnScroll(originalHandler, scrollEventThrottle) {
3102
3876
  }
3103
3877
 
3104
3878
  // src/components/LegendList.tsx
3105
- var DEFAULT_DRAW_DISTANCE = 250;
3106
- var DEFAULT_ITEM_SIZE = 100;
3107
- var LegendList = typedMemo(
3879
+ var LegendList = typedMemo2(
3880
+ // biome-ignore lint/nursery/noShadow: const function name shadowing is intentional
3108
3881
  typedForwardRef(function LegendList2(props, forwardedRef) {
3109
3882
  const { children, data: dataProp, renderItem: renderItemProp, ...restProps } = props;
3110
3883
  const isChildrenMode = children !== void 0 && dataProp === void 0;
@@ -3122,16 +3895,17 @@ var LegendList = typedMemo(
3122
3895
  })
3123
3896
  );
3124
3897
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
3125
- var _a3, _b;
3898
+ var _a3, _b, _c, _d, _e;
3126
3899
  const {
3127
3900
  alignItemsAtEnd = false,
3901
+ alwaysRender,
3128
3902
  columnWrapperStyle,
3129
3903
  contentContainerStyle: contentContainerStyleProp,
3904
+ contentInset,
3130
3905
  data: dataProp = [],
3131
3906
  dataVersion,
3132
3907
  drawDistance = 250,
3133
- enableAverages = true,
3134
- estimatedItemSize: estimatedItemSizeProp,
3908
+ estimatedItemSize = 100,
3135
3909
  estimatedListSize,
3136
3910
  extraData,
3137
3911
  getEstimatedItemSize,
@@ -3148,11 +3922,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3148
3922
  ListHeaderComponent,
3149
3923
  maintainScrollAtEnd = false,
3150
3924
  maintainScrollAtEndThreshold = 0.1,
3151
- maintainVisibleContentPosition = false,
3925
+ maintainVisibleContentPosition: maintainVisibleContentPositionProp,
3152
3926
  numColumns: numColumnsProp = 1,
3927
+ overrideItemLayout,
3153
3928
  onEndReached,
3154
3929
  onEndReachedThreshold = 0.5,
3155
3930
  onItemSizeChanged,
3931
+ onMetricsChange,
3156
3932
  onLayout: onLayoutProp,
3157
3933
  onLoad,
3158
3934
  onMomentumScrollEnd,
@@ -3167,50 +3943,102 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3167
3943
  refreshControl,
3168
3944
  refreshing,
3169
3945
  refScrollView,
3946
+ renderScrollComponent,
3170
3947
  renderItem,
3171
3948
  scrollEventThrottle,
3172
3949
  snapToIndices,
3173
3950
  stickyHeaderIndices: stickyHeaderIndicesProp,
3174
3951
  stickyIndices: stickyIndicesDeprecated,
3952
+ // TODOV3: Remove from v3 release
3175
3953
  style: styleProp,
3176
3954
  suggestEstimatedItemSize,
3955
+ useWindowScroll = false,
3177
3956
  viewabilityConfig,
3178
3957
  viewabilityConfigCallbackPairs,
3179
3958
  waitForInitialLayout = true,
3180
3959
  ...rest
3181
3960
  } = props;
3182
- const { childrenMode } = rest;
3183
- const contentContainerStyle = { ...StyleSheet.flatten(contentContainerStyleProp) };
3961
+ const animatedPropsInternal = props.animatedPropsInternal;
3962
+ const positionComponentInternal = props.positionComponentInternal;
3963
+ const stickyPositionComponentInternal = props.stickyPositionComponentInternal;
3964
+ const {
3965
+ childrenMode,
3966
+ positionComponentInternal: _positionComponentInternal,
3967
+ stickyPositionComponentInternal: _stickyPositionComponentInternal,
3968
+ ...restProps
3969
+ } = rest;
3970
+ const contentContainerStyleBase = StyleSheet.flatten(contentContainerStyleProp);
3971
+ const shouldFlexGrow = alignItemsAtEnd && (horizontal ? (contentContainerStyleBase == null ? void 0 : contentContainerStyleBase.minWidth) == null : (contentContainerStyleBase == null ? void 0 : contentContainerStyleBase.minHeight) == null);
3972
+ const contentContainerStyle = {
3973
+ ...contentContainerStyleBase,
3974
+ ...alignItemsAtEnd ? {
3975
+ display: "flex",
3976
+ flexDirection: horizontal ? "row" : "column",
3977
+ ...shouldFlexGrow ? { flexGrow: 1 } : {},
3978
+ justifyContent: "flex-end"
3979
+ } : {}
3980
+ };
3184
3981
  const style = { ...StyleSheet.flatten(styleProp) };
3185
3982
  const stylePaddingTopState = extractPadding(style, contentContainerStyle, "Top");
3186
3983
  const stylePaddingBottomState = extractPadding(style, contentContainerStyle, "Bottom");
3187
- const [renderNum, setRenderNum] = React2.useState(0);
3188
- const initialScrollProp = initialScrollAtEnd ? { index: Math.max(0, dataProp.length - 1), viewOffset: -stylePaddingBottomState } : initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? { index: initialScrollIndexProp.index || 0, viewOffset: initialScrollIndexProp.viewOffset || 0 } : { index: initialScrollIndexProp || 0, viewOffset: initialScrollOffsetProp || 0 } : void 0;
3984
+ const maintainVisibleContentPositionConfig = normalizeMaintainVisibleContentPosition(
3985
+ maintainVisibleContentPositionProp
3986
+ );
3987
+ const initialScrollProp = initialScrollAtEnd ? { index: Math.max(0, dataProp.length - 1), viewOffset: -stylePaddingBottomState, viewPosition: 1 } : initialScrollIndexProp || initialScrollOffsetProp ? typeof initialScrollIndexProp === "object" ? {
3988
+ index: initialScrollIndexProp.index || 0,
3989
+ viewOffset: initialScrollIndexProp.viewOffset || (initialScrollIndexProp.viewPosition === 1 ? -stylePaddingBottomState : 0),
3990
+ viewPosition: initialScrollIndexProp.viewPosition || 0
3991
+ } : {
3992
+ index: initialScrollIndexProp || 0,
3993
+ viewOffset: initialScrollOffsetProp || 0
3994
+ } : void 0;
3189
3995
  const [canRender, setCanRender] = React2__namespace.useState(!IsNewArchitecture);
3190
3996
  const ctx = useStateContext();
3191
3997
  ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
3192
3998
  const refScroller = React2.useRef(null);
3193
3999
  const combinedRef = useCombinedRef(refScroller, refScrollView);
3194
- const estimatedItemSize = estimatedItemSizeProp != null ? estimatedItemSizeProp : DEFAULT_ITEM_SIZE;
3195
- const scrollBuffer = (drawDistance != null ? drawDistance : DEFAULT_DRAW_DISTANCE) || 1;
3196
- const keyExtractor = keyExtractorProp != null ? keyExtractorProp : (_item, index) => index.toString();
4000
+ const keyExtractor = keyExtractorProp != null ? keyExtractorProp : ((_item, index) => index.toString());
3197
4001
  const stickyHeaderIndices = stickyHeaderIndicesProp != null ? stickyHeaderIndicesProp : stickyIndicesDeprecated;
4002
+ const alwaysRenderIndices = React2.useMemo(() => {
4003
+ const indices = getAlwaysRenderIndices(alwaysRender, dataProp, keyExtractor);
4004
+ return { arr: indices, set: new Set(indices) };
4005
+ }, [
4006
+ alwaysRender == null ? void 0 : alwaysRender.top,
4007
+ alwaysRender == null ? void 0 : alwaysRender.bottom,
4008
+ (_a3 = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _a3.join(","),
4009
+ (_b = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _b.join(","),
4010
+ dataProp,
4011
+ dataVersion,
4012
+ keyExtractor
4013
+ ]);
3198
4014
  if (IS_DEV && stickyIndicesDeprecated && !stickyHeaderIndicesProp) {
3199
4015
  warnDevOnce(
3200
4016
  "stickyIndices",
3201
4017
  "stickyIndices has been renamed to stickyHeaderIndices. Please update your props to use stickyHeaderIndices."
3202
4018
  );
3203
4019
  }
3204
- const refState = React2.useRef();
4020
+ if (IS_DEV && useWindowScroll && renderScrollComponent) {
4021
+ warnDevOnce(
4022
+ "useWindowScrollRenderScrollComponent",
4023
+ "useWindowScroll is not supported when renderScrollComponent is provided."
4024
+ );
4025
+ }
4026
+ const useWindowScrollResolved = Platform2.OS === "web" && !!useWindowScroll && !renderScrollComponent;
4027
+ const refState = React2.useRef(void 0);
4028
+ const hasOverrideItemLayout = !!overrideItemLayout;
4029
+ const prevHasOverrideItemLayout = React2.useRef(hasOverrideItemLayout);
3205
4030
  if (!refState.current) {
3206
- if (!ctx.internalState) {
4031
+ if (!ctx.state) {
3207
4032
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : IsNewArchitecture ? { height: 0, width: 0 } : getWindowSize())[horizontal ? "width" : "height"];
3208
- ctx.internalState = {
3209
- activeStickyIndex: void 0,
4033
+ ctx.state = {
4034
+ activeStickyIndex: -1,
3210
4035
  averageSizes: {},
3211
- columns: /* @__PURE__ */ new Map(),
3212
- containerItemKeys: /* @__PURE__ */ new Set(),
4036
+ columnSpans: [],
4037
+ columns: [],
4038
+ containerItemKeys: /* @__PURE__ */ new Map(),
3213
4039
  containerItemTypes: /* @__PURE__ */ new Map(),
4040
+ contentInsetOverride: void 0,
4041
+ dataChangeEpoch: 0,
3214
4042
  dataChangeNeedsScrollUpdate: false,
3215
4043
  didColumnsChange: false,
3216
4044
  didDataChange: false,
@@ -3226,24 +4054,26 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3226
4054
  attempts: 0,
3227
4055
  index: initialScrollProp.index,
3228
4056
  settledTicks: 0,
3229
- viewOffset: (_a3 = initialScrollProp.viewOffset) != null ? _a3 : 0,
4057
+ viewOffset: (_c = initialScrollProp.viewOffset) != null ? _c : 0,
3230
4058
  viewPosition: initialScrollProp.viewPosition
3231
4059
  } : void 0,
3232
4060
  initialScroll: initialScrollProp,
3233
4061
  isAtEnd: false,
3234
4062
  isAtStart: false,
3235
- isEndReached: false,
4063
+ isEndReached: null,
3236
4064
  isFirst: true,
3237
- isStartReached: false,
4065
+ isStartReached: null,
3238
4066
  lastBatchingAction: Date.now(),
3239
4067
  lastLayout: void 0,
4068
+ lastScrollDelta: 0,
3240
4069
  loadStartTime: Date.now(),
3241
4070
  minIndexSizeChanged: 0,
4071
+ nativeContentInset: void 0,
3242
4072
  nativeMarginTop: 0,
3243
- positions: /* @__PURE__ */ new Map(),
4073
+ positions: [],
3244
4074
  props: {},
3245
4075
  queuedCalculateItemsInView: 0,
3246
- refScroller: void 0,
4076
+ refScroller: { current: null },
3247
4077
  scroll: 0,
3248
4078
  scrollAdjustHandler: new ScrollAdjustHandler(ctx),
3249
4079
  scrollForNextCalculateItemsInView: void 0,
@@ -3259,6 +4089,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3259
4089
  startBuffered: -1,
3260
4090
  startNoBuffer: -1,
3261
4091
  startReachedSnapshot: void 0,
4092
+ startReachedSnapshotDataChangeEpoch: void 0,
3262
4093
  stickyContainerPool: /* @__PURE__ */ new Set(),
3263
4094
  stickyContainers: /* @__PURE__ */ new Map(),
3264
4095
  timeoutSizeMessage: 0,
@@ -3266,18 +4097,19 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3266
4097
  totalSize: 0,
3267
4098
  viewabilityConfigCallbackPairs: void 0
3268
4099
  };
3269
- const internalState = ctx.internalState;
3270
- internalState.triggerCalculateItemsInView = (params) => calculateItemsInView(ctx, internalState, params);
3271
- set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPosition);
4100
+ const internalState = ctx.state;
4101
+ internalState.triggerCalculateItemsInView = (params) => calculateItemsInView(ctx, params);
4102
+ set$(ctx, "maintainVisibleContentPosition", maintainVisibleContentPositionConfig);
3272
4103
  set$(ctx, "extraData", extraData);
3273
4104
  }
3274
- refState.current = ctx.internalState;
4105
+ refState.current = ctx.state;
3275
4106
  }
3276
4107
  const state = refState.current;
3277
4108
  const isFirstLocal = state.isFirst;
3278
4109
  state.didColumnsChange = numColumnsProp !== state.props.numColumns;
3279
4110
  const didDataChangeLocal = state.props.dataVersion !== dataVersion || state.props.data !== dataProp && checkActualChange(state, dataProp, state.props.data);
3280
4111
  if (didDataChangeLocal) {
4112
+ state.dataChangeEpoch += 1;
3281
4113
  state.dataChangeNeedsScrollUpdate = true;
3282
4114
  state.didDataChange = true;
3283
4115
  state.previousData = state.props.data;
@@ -3285,20 +4117,25 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3285
4117
  const throttleScrollFn = scrollEventThrottle && onScrollProp ? useThrottledOnScroll(onScrollProp, scrollEventThrottle) : onScrollProp;
3286
4118
  state.props = {
3287
4119
  alignItemsAtEnd,
4120
+ alwaysRender,
4121
+ alwaysRenderIndicesArr: alwaysRenderIndices.arr,
4122
+ alwaysRenderIndicesSet: alwaysRenderIndices.set,
4123
+ animatedProps: animatedPropsInternal,
4124
+ contentInset,
3288
4125
  data: dataProp,
3289
4126
  dataVersion,
3290
- enableAverages,
4127
+ drawDistance,
3291
4128
  estimatedItemSize,
3292
- getEstimatedItemSize,
3293
- getFixedItemSize,
3294
- getItemType,
4129
+ getEstimatedItemSize: useWrapIfItem(getEstimatedItemSize),
4130
+ getFixedItemSize: useWrapIfItem(getFixedItemSize),
4131
+ getItemType: useWrapIfItem(getItemType),
3295
4132
  horizontal: !!horizontal,
3296
4133
  initialContainerPoolRatio,
3297
4134
  itemsAreEqual,
3298
- keyExtractor,
4135
+ keyExtractor: useWrapIfItem(keyExtractor),
3299
4136
  maintainScrollAtEnd,
3300
4137
  maintainScrollAtEndThreshold,
3301
- maintainVisibleContentPosition,
4138
+ maintainVisibleContentPosition: maintainVisibleContentPositionConfig,
3302
4139
  numColumns: numColumnsProp,
3303
4140
  onEndReached,
3304
4141
  onEndReachedThreshold,
@@ -3308,15 +4145,18 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3308
4145
  onStartReached,
3309
4146
  onStartReachedThreshold,
3310
4147
  onStickyHeaderChange,
4148
+ overrideItemLayout,
4149
+ positionComponentInternal,
3311
4150
  recycleItems: !!recycleItems,
3312
4151
  renderItem,
3313
- scrollBuffer,
3314
4152
  snapToIndices,
3315
4153
  stickyIndicesArr: stickyHeaderIndices != null ? stickyHeaderIndices : [],
3316
4154
  stickyIndicesSet: React2.useMemo(() => new Set(stickyHeaderIndices != null ? stickyHeaderIndices : []), [stickyHeaderIndices == null ? void 0 : stickyHeaderIndices.join(",")]),
4155
+ stickyPositionComponentInternal,
3317
4156
  stylePaddingBottom: stylePaddingBottomState,
3318
4157
  stylePaddingTop: stylePaddingTopState,
3319
- suggestEstimatedItemSize: !!suggestEstimatedItemSize
4158
+ suggestEstimatedItemSize: !!suggestEstimatedItemSize,
4159
+ useWindowScroll: useWindowScrollResolved
3320
4160
  };
3321
4161
  state.refScroller = refScroller;
3322
4162
  const memoizedLastItemKeys = React2.useMemo(() => {
@@ -3326,62 +4166,65 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3326
4166
  (_, i) => getId(state, dataProp.length - 1 - i)
3327
4167
  );
3328
4168
  }, [dataProp, dataVersion, numColumnsProp]);
3329
- const initializeStateVars = () => {
4169
+ const initializeStateVars = (shouldAdjustPadding) => {
3330
4170
  set$(ctx, "lastItemKeys", memoizedLastItemKeys);
3331
4171
  set$(ctx, "numColumns", numColumnsProp);
3332
4172
  const prevPaddingTop = peek$(ctx, "stylePaddingTop");
3333
- setPaddingTop(ctx, state, { stylePaddingTop: stylePaddingTopState });
4173
+ setPaddingTop(ctx, { stylePaddingTop: stylePaddingTopState });
3334
4174
  refState.current.props.stylePaddingBottom = stylePaddingBottomState;
3335
4175
  let paddingDiff = stylePaddingTopState - prevPaddingTop;
3336
- if (paddingDiff && prevPaddingTop !== void 0 && Platform2.OS === "ios") {
4176
+ if (shouldAdjustPadding && maintainVisibleContentPositionConfig.size && paddingDiff && prevPaddingTop !== void 0 && Platform2.OS === "ios") {
3337
4177
  if (state.scroll < 0) {
3338
4178
  paddingDiff += state.scroll;
3339
4179
  }
3340
- requestAdjust(ctx, state, paddingDiff);
4180
+ requestAdjust(ctx, paddingDiff);
3341
4181
  }
3342
4182
  };
3343
4183
  if (isFirstLocal) {
3344
- initializeStateVars();
4184
+ initializeStateVars(false);
3345
4185
  updateItemPositions(
3346
4186
  ctx,
3347
- state,
3348
4187
  /*dataChanged*/
3349
4188
  true
3350
4189
  );
3351
4190
  }
4191
+ const resolveInitialScrollOffset = React2.useCallback((initialScroll) => {
4192
+ const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, initialScroll.index) : 0;
4193
+ const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, baseOffset, initialScroll);
4194
+ return clampScrollOffset(ctx, resolvedOffset, initialScroll);
4195
+ }, []);
3352
4196
  const initialContentOffset = React2.useMemo(() => {
3353
- var _a4, _b2;
3354
- const { initialScroll } = refState.current;
3355
- if (!initialScroll) {
4197
+ var _a4;
4198
+ let value;
4199
+ const { initialScroll, initialAnchor } = refState.current;
4200
+ if (initialScroll) {
4201
+ if (!IsNewArchitecture && initialScroll.index !== void 0 && (!initialAnchor || (initialAnchor == null ? void 0 : initialAnchor.index) !== initialScroll.index)) {
4202
+ refState.current.initialAnchor = {
4203
+ attempts: 0,
4204
+ index: initialScroll.index,
4205
+ settledTicks: 0,
4206
+ viewOffset: (_a4 = initialScroll.viewOffset) != null ? _a4 : 0,
4207
+ viewPosition: initialScroll.viewPosition
4208
+ };
4209
+ }
4210
+ if (initialScroll.contentOffset !== void 0) {
4211
+ value = initialScroll.contentOffset;
4212
+ } else {
4213
+ const clampedOffset = resolveInitialScrollOffset(initialScroll);
4214
+ const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
4215
+ refState.current.initialScroll = updatedInitialScroll;
4216
+ state.initialScroll = updatedInitialScroll;
4217
+ value = clampedOffset;
4218
+ }
4219
+ } else {
3356
4220
  refState.current.initialAnchor = void 0;
3357
- return 0;
4221
+ value = 0;
3358
4222
  }
3359
- if (initialScroll.index !== void 0 && (!refState.current.initialAnchor || ((_a4 = refState.current.initialAnchor) == null ? void 0 : _a4.index) !== initialScroll.index)) {
3360
- refState.current.initialAnchor = {
3361
- attempts: 0,
3362
- index: initialScroll.index,
3363
- settledTicks: 0,
3364
- viewOffset: (_b2 = initialScroll.viewOffset) != null ? _b2 : 0,
3365
- viewPosition: initialScroll.viewPosition
3366
- };
4223
+ if (!value) {
4224
+ setInitialRenderState(ctx, { didInitialScroll: true });
3367
4225
  }
3368
- if (initialScroll.contentOffset !== void 0) {
3369
- return initialScroll.contentOffset;
3370
- }
3371
- const baseOffset = initialScroll.index !== void 0 ? calculateOffsetForIndex(ctx, state, initialScroll.index) : 0;
3372
- const resolvedOffset = calculateOffsetWithOffsetPosition(ctx, state, baseOffset, initialScroll);
3373
- let clampedOffset = resolvedOffset;
3374
- if (Number.isFinite(state.scrollLength) && Number.isFinite(state.totalSize)) {
3375
- const maxOffset = Math.max(0, state.totalSize - state.scrollLength);
3376
- clampedOffset = Math.min(clampedOffset, maxOffset);
3377
- }
3378
- clampedOffset = Math.max(0, clampedOffset);
3379
- const updatedInitialScroll = { ...initialScroll, contentOffset: clampedOffset };
3380
- refState.current.initialScroll = updatedInitialScroll;
3381
- state.initialScroll = updatedInitialScroll;
3382
- refState.current.isStartReached = clampedOffset < refState.current.scrollLength * onStartReachedThreshold;
3383
- return clampedOffset;
3384
- }, [renderNum]);
4226
+ return value;
4227
+ }, []);
3385
4228
  if (isFirstLocal || didDataChangeLocal || numColumnsProp !== peek$(ctx, "numColumns")) {
3386
4229
  refState.current.lastBatchingAction = Date.now();
3387
4230
  if (!keyExtractorProp && !isFirstLocal && didDataChangeLocal) {
@@ -3390,41 +4233,53 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3390
4233
  "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."
3391
4234
  );
3392
4235
  refState.current.sizes.clear();
3393
- refState.current.positions.clear();
4236
+ refState.current.positions.length = 0;
3394
4237
  refState.current.totalSize = 0;
3395
4238
  set$(ctx, "totalSize", 0);
3396
4239
  }
3397
4240
  }
3398
- const onLayoutHeader = React2.useCallback((rect, fromLayoutEffect) => {
3399
- const { initialScroll } = refState.current;
3400
- const size = rect[horizontal ? "width" : "height"];
3401
- set$(ctx, "headerSize", size);
3402
- if ((initialScroll == null ? void 0 : initialScroll.index) !== void 0) {
3403
- if (IsNewArchitecture && Platform2.OS !== "android") {
3404
- if (fromLayoutEffect) {
3405
- setRenderNum((v) => v + 1);
3406
- }
3407
- } else {
3408
- setTimeout(doInitialScroll, 17);
3409
- }
3410
- }
3411
- }, []);
3412
4241
  const doInitialScroll = React2.useCallback(() => {
3413
- var _a4;
3414
- const initialScroll = state.initialScroll;
3415
- if (initialScroll) {
3416
- scrollTo(ctx, state, {
4242
+ const { initialScroll, didFinishInitialScroll, queuedInitialLayout, scrollingTo } = state;
4243
+ if (initialScroll && !queuedInitialLayout && !didFinishInitialScroll && !scrollingTo) {
4244
+ const offset = resolveInitialScrollOffset(initialScroll);
4245
+ const updatedInitialScroll = { ...initialScroll, contentOffset: offset };
4246
+ refState.current.initialScroll = updatedInitialScroll;
4247
+ state.initialScroll = updatedInitialScroll;
4248
+ scrollTo(ctx, {
3417
4249
  animated: false,
3418
- index: (_a4 = state.initialScroll) == null ? void 0 : _a4.index,
4250
+ index: initialScroll.index,
3419
4251
  isInitialScroll: true,
3420
- offset: initialContentOffset,
4252
+ offset,
3421
4253
  precomputedWithViewOffset: true
3422
4254
  });
3423
4255
  }
3424
- }, [initialContentOffset]);
4256
+ }, []);
4257
+ const onLayoutFooter = React2.useCallback(
4258
+ (layout) => {
4259
+ if (!initialScrollAtEnd) {
4260
+ return;
4261
+ }
4262
+ const { initialScroll } = state;
4263
+ if (!initialScroll) {
4264
+ return;
4265
+ }
4266
+ const lastIndex = Math.max(0, dataProp.length - 1);
4267
+ if (initialScroll.index !== lastIndex || initialScroll.viewPosition !== 1) {
4268
+ return;
4269
+ }
4270
+ const footerSize = layout[horizontal ? "width" : "height"];
4271
+ const viewOffset = -stylePaddingBottomState - footerSize;
4272
+ if (initialScroll.viewOffset !== viewOffset) {
4273
+ const updatedInitialScroll = { ...initialScroll, viewOffset };
4274
+ refState.current.initialScroll = updatedInitialScroll;
4275
+ state.initialScroll = updatedInitialScroll;
4276
+ }
4277
+ },
4278
+ [dataProp.length, horizontal, initialScrollAtEnd, stylePaddingBottomState]
4279
+ );
3425
4280
  const onLayoutChange = React2.useCallback((layout) => {
3426
4281
  doInitialScroll();
3427
- handleLayout(ctx, state, layout, setCanRender);
4282
+ handleLayout(ctx, layout, setCanRender);
3428
4283
  }, []);
3429
4284
  const { onLayout } = useOnLayoutSync({
3430
4285
  onLayoutChange,
@@ -3434,7 +4289,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3434
4289
  });
3435
4290
  React2.useLayoutEffect(() => {
3436
4291
  if (snapToIndices) {
3437
- updateSnapToOffsets(ctx, state);
4292
+ updateSnapToOffsets(ctx);
3438
4293
  }
3439
4294
  }, [snapToIndices]);
3440
4295
  React2.useLayoutEffect(() => {
@@ -3444,24 +4299,50 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3444
4299
  isFirst,
3445
4300
  props: { data }
3446
4301
  } = state;
3447
- const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx, state, !!(didDataChange || didColumnsChange));
4302
+ const didAllocateContainers = data.length > 0 && doInitialAllocateContainers(ctx);
3448
4303
  if (!didAllocateContainers && !isFirst && (didDataChange || didColumnsChange)) {
3449
- checkResetContainers(ctx, state, data);
4304
+ checkResetContainers(ctx, data);
3450
4305
  }
3451
4306
  state.didColumnsChange = false;
3452
4307
  state.didDataChange = false;
3453
4308
  state.isFirst = false;
3454
4309
  }, [dataProp, dataVersion, numColumnsProp]);
3455
4310
  React2.useLayoutEffect(() => {
4311
+ var _a4;
3456
4312
  set$(ctx, "extraData", extraData);
3457
- }, [extraData]);
3458
- React2.useLayoutEffect(initializeStateVars, [
3459
- dataVersion,
3460
- memoizedLastItemKeys.join(","),
3461
- numColumnsProp,
3462
- stylePaddingBottomState,
3463
- stylePaddingTopState
3464
- ]);
4313
+ const didToggleOverride = prevHasOverrideItemLayout.current !== hasOverrideItemLayout;
4314
+ prevHasOverrideItemLayout.current = hasOverrideItemLayout;
4315
+ if ((hasOverrideItemLayout || didToggleOverride) && numColumnsProp > 1) {
4316
+ (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, { forceFullItemPositions: true });
4317
+ }
4318
+ }, [extraData, hasOverrideItemLayout, numColumnsProp]);
4319
+ React2.useLayoutEffect(
4320
+ () => initializeStateVars(true),
4321
+ [dataVersion, memoizedLastItemKeys.join(","), numColumnsProp, stylePaddingBottomState, stylePaddingTopState]
4322
+ );
4323
+ React2.useEffect(() => {
4324
+ if (!onMetricsChange) {
4325
+ return;
4326
+ }
4327
+ let lastMetrics;
4328
+ const emitMetrics = () => {
4329
+ const metrics = {
4330
+ footerSize: peek$(ctx, "footerSize") || 0,
4331
+ headerSize: peek$(ctx, "headerSize") || 0
4332
+ };
4333
+ if (!lastMetrics || metrics.headerSize !== lastMetrics.headerSize || metrics.footerSize !== lastMetrics.footerSize) {
4334
+ lastMetrics = metrics;
4335
+ onMetricsChange(metrics);
4336
+ }
4337
+ };
4338
+ emitMetrics();
4339
+ const unsubscribe = [listen$(ctx, "headerSize", emitMetrics), listen$(ctx, "footerSize", emitMetrics)];
4340
+ return () => {
4341
+ for (const unsub of unsubscribe) {
4342
+ unsub();
4343
+ }
4344
+ };
4345
+ }, [ctx, onMetricsChange]);
3465
4346
  React2.useEffect(() => {
3466
4347
  const viewability = setupViewability({
3467
4348
  onViewableItemsChanged,
@@ -3471,54 +4352,53 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3471
4352
  state.viewabilityConfigCallbackPairs = viewability;
3472
4353
  state.enableScrollForNextCalculateItemsInView = !viewability;
3473
4354
  }, [viewabilityConfig, viewabilityConfigCallbackPairs, onViewableItemsChanged]);
3474
- React2.useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx, state), []);
4355
+ if (!IsNewArchitecture) {
4356
+ useInit(() => {
4357
+ doInitialAllocateContainers(ctx);
4358
+ });
4359
+ }
4360
+ React2.useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx), []);
3475
4361
  if (Platform2.OS === "web") {
3476
4362
  React2.useEffect(doInitialScroll, []);
3477
4363
  }
3478
4364
  const fns = React2.useMemo(
3479
4365
  () => ({
3480
- getRenderedItem: (key) => getRenderedItem(ctx, state, key),
3481
- onScroll: (event) => onScroll(ctx, state, event),
3482
- updateItemSize: (itemKey, sizeObj) => updateItemSize(ctx, state, itemKey, sizeObj)
4366
+ getRenderedItem: (key) => getRenderedItem(ctx, key),
4367
+ onMomentumScrollEnd: (event) => {
4368
+ checkFinishedScrollFallback(ctx);
4369
+ if (onMomentumScrollEnd) {
4370
+ onMomentumScrollEnd(event);
4371
+ }
4372
+ },
4373
+ onScroll: (event) => onScroll(ctx, event),
4374
+ updateItemSize: (itemKey, sizeObj) => updateItemSize(ctx, itemKey, sizeObj)
3483
4375
  }),
3484
4376
  []
3485
4377
  );
3486
4378
  const onScrollHandler = useStickyScrollHandler(stickyHeaderIndices, horizontal, ctx, fns.onScroll);
4379
+ const refreshControlElement = refreshControl;
3487
4380
  return /* @__PURE__ */ React2__namespace.createElement(React2__namespace.Fragment, null, /* @__PURE__ */ React2__namespace.createElement(
3488
4381
  ListComponent,
3489
4382
  {
3490
- ...rest,
4383
+ ...restProps,
3491
4384
  alignItemsAtEnd,
3492
4385
  canRender,
3493
4386
  contentContainerStyle,
4387
+ contentInset,
3494
4388
  getRenderedItem: fns.getRenderedItem,
3495
4389
  horizontal,
3496
4390
  initialContentOffset,
3497
4391
  ListEmptyComponent: dataProp.length === 0 ? ListEmptyComponent : void 0,
3498
4392
  ListHeaderComponent,
3499
- maintainVisibleContentPosition,
3500
4393
  onLayout,
3501
- onLayoutHeader,
3502
- onMomentumScrollEnd: (event) => {
3503
- if (IsNewArchitecture) {
3504
- requestAnimationFrame(() => {
3505
- finishScrollTo(ctx, refState.current);
3506
- });
3507
- } else {
3508
- setTimeout(() => {
3509
- finishScrollTo(ctx, refState.current);
3510
- }, 1e3);
3511
- }
3512
- if (onMomentumScrollEnd) {
3513
- onMomentumScrollEnd(event);
3514
- }
3515
- },
4394
+ onLayoutFooter,
4395
+ onMomentumScrollEnd: fns.onMomentumScrollEnd,
3516
4396
  onScroll: onScrollHandler,
3517
4397
  recycleItems,
3518
- refreshControl: refreshControl ? stylePaddingTopState > 0 ? React2__namespace.cloneElement(refreshControl, {
3519
- progressViewOffset: (refreshControl.props.progressViewOffset || 0) + stylePaddingTopState
3520
- }) : refreshControl : onRefresh && /* @__PURE__ */ React2__namespace.createElement(
3521
- reactNative.RefreshControl,
4398
+ refreshControl: refreshControlElement ? stylePaddingTopState > 0 ? React2__namespace.cloneElement(refreshControlElement, {
4399
+ progressViewOffset: ((_d = refreshControlElement.props.progressViewOffset) != null ? _d : 0) + stylePaddingTopState
4400
+ }) : refreshControlElement : onRefresh && /* @__PURE__ */ React2__namespace.createElement(
4401
+ ReactNative.RefreshControl,
3522
4402
  {
3523
4403
  onRefresh,
3524
4404
  progressViewOffset: (progressViewOffset || 0) + stylePaddingTopState,
@@ -3526,18 +4406,30 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
3526
4406
  }
3527
4407
  ),
3528
4408
  refScrollView: combinedRef,
3529
- scrollAdjustHandler: (_b = refState.current) == null ? void 0 : _b.scrollAdjustHandler,
3530
- scrollEventThrottle: Platform2.OS === "web" ? 16 : void 0,
4409
+ renderScrollComponent,
4410
+ scrollAdjustHandler: (_e = refState.current) == null ? void 0 : _e.scrollAdjustHandler,
4411
+ scrollEventThrottle: 0,
3531
4412
  snapToIndices,
3532
4413
  stickyHeaderIndices,
3533
4414
  style,
3534
4415
  updateItemSize: fns.updateItemSize,
4416
+ useWindowScroll: useWindowScrollResolved,
3535
4417
  waitForInitialLayout
3536
4418
  }
3537
- ), IS_DEV && ENABLE_DEBUG_VIEW && /* @__PURE__ */ React2__namespace.createElement(DebugView, { state: refState.current }));
4419
+ ), IS_DEV && ENABLE_DEBUG_VIEW);
3538
4420
  });
3539
4421
 
3540
- exports.LegendList = LegendList;
4422
+ // src/index.ts
4423
+ var LegendList3 = LegendList;
4424
+ if (IS_DEV) {
4425
+ console.warn(
4426
+ "[legend-list] Legend List 3.0 deprecates the root import (@legendapp/list) because it now supports both react and react-native. The root import is fully functional, but please switch to platform-specific imports for strict platform types:\n - React Native: @legendapp/list/react-native\n - React: @legendapp/list/react\nSee README for details."
4427
+ );
4428
+ }
4429
+
4430
+ exports.LegendList = LegendList3;
4431
+ exports.typedForwardRef = typedForwardRef;
4432
+ exports.typedMemo = typedMemo2;
3541
4433
  exports.useIsLastItem = useIsLastItem;
3542
4434
  exports.useListScrollSize = useListScrollSize;
3543
4435
  exports.useRecyclingEffect = useRecyclingEffect;