@legendapp/list 3.3.2 → 3.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/react-native.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as React2 from 'react';
2
- import { useReducer, useEffect, createContext, useRef, useState, useMemo, useCallback, useLayoutEffect, useImperativeHandle, useContext } from 'react';
2
+ import { useReducer, useEffect, createContext, useRef, useMemo, useCallback, useLayoutEffect, useImperativeHandle, useState, useContext } from 'react';
3
3
  import * as ReactNative from 'react-native';
4
4
  import { Animated, Platform as Platform$1, View as View$1, Text as Text$1, PixelRatio as PixelRatio$1, StyleSheet as StyleSheet$1, RefreshControl, Dimensions, I18nManager } from 'react-native';
5
5
  import { useSyncExternalStore } from 'use-sync-external-store/shim';
@@ -41,6 +41,12 @@ function isHorizontalRTL(state) {
41
41
  function isHorizontalRTLProps(props) {
42
42
  return !!(props == null ? void 0 : props.horizontal) && isRTLProps(props);
43
43
  }
44
+ function getStylePaddingEnd(props) {
45
+ if (!(props == null ? void 0 : props.horizontal)) {
46
+ return (props == null ? void 0 : props.stylePaddingBottom) || 0;
47
+ }
48
+ return (isHorizontalRTLProps(props) ? props.stylePaddingLeft : props.stylePaddingRight) || 0;
49
+ }
44
50
  function getLogicalHorizontalMaxOffset(state, contentWidth) {
45
51
  var _a3;
46
52
  return (_a3 = getHorizontalMaxOffset(state, contentWidth)) != null ? _a3 : 0;
@@ -131,11 +137,13 @@ function StateProvider({ children }) {
131
137
  mapViewabilityCallbacks: /* @__PURE__ */ new Map(),
132
138
  mapViewabilityConfigStates: /* @__PURE__ */ new Map(),
133
139
  mapViewabilityValues: /* @__PURE__ */ new Map(),
140
+ pendingContainerIds: void 0,
134
141
  positionListeners: /* @__PURE__ */ new Map(),
135
142
  scrollAxisGap: 0,
136
143
  state: void 0,
137
144
  values: /* @__PURE__ */ new Map([
138
145
  ["alignItemsAtEndPadding", 0],
146
+ ["containerLayoutEpoch", 0],
139
147
  ["stylePaddingTop", 0],
140
148
  ["headerSize", 0],
141
149
  ["numContainers", 0],
@@ -157,6 +165,14 @@ function useStateContext() {
157
165
  return React2.useContext(ContextState);
158
166
  }
159
167
  function createSelectorFunctionsArr(ctx, signalNames) {
168
+ if (!ctx) {
169
+ const emptyValues = [];
170
+ return {
171
+ get: () => emptyValues,
172
+ subscribe: () => () => {
173
+ }
174
+ };
175
+ }
160
176
  let lastValues = [];
161
177
  let lastSignalValues = [];
162
178
  return {
@@ -263,7 +279,7 @@ function getContentInsetEndAdjustmentEnd(adjustment) {
263
279
  return Math.max(0, adjustment != null ? adjustment : 0);
264
280
  }
265
281
  function getContentInsetEnd(ctx, contentInsetEndAdjustmentOverride) {
266
- var _a3, _b;
282
+ var _a3;
267
283
  const state = ctx.state;
268
284
  const { props } = state;
269
285
  const horizontal = props.horizontal;
@@ -274,8 +290,8 @@ function getContentInsetEnd(ctx, contentInsetEndAdjustmentOverride) {
274
290
  contentInsetEndAdjustmentOverride != null ? contentInsetEndAdjustmentOverride : props.contentInsetEndAdjustment
275
291
  );
276
292
  const anchoredEndSpaceSize = peek$(ctx, "anchoredEndSpaceSize");
277
- const anchoredEndInset = ((_a3 = props.anchoredEndSpace) == null ? void 0 : _a3.includeInEndInset) && anchoredEndSpaceSize ? anchoredEndSpaceSize : 0;
278
- const overrideInset = (_b = state.contentInsetOverride) != null ? _b : void 0;
293
+ const anchoredEndInset = props.anchoredEndSpace && anchoredEndSpaceSize ? anchoredEndSpaceSize : 0;
294
+ const overrideInset = (_a3 = state.contentInsetOverride) != null ? _a3 : void 0;
279
295
  const adjustedBaseEndInset = baseEndInset + contentInsetEndAdjustment;
280
296
  if (overrideInset) {
281
297
  const mergedInset = { bottom: 0, left: 0, right: 0, ...baseInset, ...overrideInset };
@@ -291,14 +307,15 @@ function getContentInsetEnd(ctx, contentInsetEndAdjustmentOverride) {
291
307
  function getContentSize(ctx) {
292
308
  var _a3, _b;
293
309
  const { values, state } = ctx;
294
- const stylePaddingTop = values.get("stylePaddingTop") || 0;
295
- const stylePaddingBottom = state.props.stylePaddingBottom || 0;
310
+ const stylePaddingStart = state.props.horizontal ? state.props.stylePaddingLeft || 0 : values.get("stylePaddingTop") || 0;
311
+ const stylePaddingEnd = state.props.horizontal ? state.props.stylePaddingRight || 0 : state.props.stylePaddingBottom || 0;
296
312
  const alignItemsAtEndPadding = values.get("alignItemsAtEndPadding") || 0;
297
313
  const headerSize = values.get("headerSize") || 0;
298
314
  const footerSize = values.get("footerSize") || 0;
299
- const contentInsetBottom = getContentInsetEnd(ctx);
315
+ const contentInsetEnd = getContentInsetEnd(ctx);
300
316
  const totalSize = (_b = (_a3 = state.pendingTotalSize) != null ? _a3 : state.totalSize) != null ? _b : values.get("totalSize");
301
- return headerSize + footerSize + totalSize + stylePaddingTop + alignItemsAtEndPadding + stylePaddingBottom + (contentInsetBottom || 0);
317
+ const layoutSize = Math.max(0, totalSize - (state.props.data.length > 0 ? ctx.scrollAxisGap : 0));
318
+ return headerSize + footerSize + layoutSize + stylePaddingStart + alignItemsAtEndPadding + stylePaddingEnd + (contentInsetEnd || 0);
302
319
  }
303
320
 
304
321
  // src/components/DebugView.tsx
@@ -360,28 +377,49 @@ function useInterval(callback, delay) {
360
377
  }, [delay]);
361
378
  }
362
379
 
363
- // src/components/stickyPositionUtils.ts
364
- function getStickyPushLimit(state, index, itemKey) {
365
- if (!itemKey) {
366
- return void 0;
367
- }
368
- const currentSize = state.sizes.get(itemKey);
369
- if (!(currentSize && currentSize > 0)) {
370
- return void 0;
380
+ // src/constants-platform.native.ts
381
+ var f = global.nativeFabricUIManager;
382
+ var IsNewArchitecture = f !== void 0 && f != null;
383
+
384
+ // src/core/containerItemMetadata.ts
385
+ function createContainerItemMetadata(state, itemIndex, itemData, itemType) {
386
+ return {
387
+ dataChangeEpoch: state.dataChangeEpoch,
388
+ getFixedItemSize: state.props.getFixedItemSize,
389
+ getItemType: state.props.getItemType,
390
+ itemData,
391
+ itemIndex,
392
+ itemType
393
+ };
394
+ }
395
+ function resolveContainerItemMetadata(state, containerId, itemIndex, itemData) {
396
+ var _a3, _b;
397
+ const { getFixedItemSize, getItemType } = state.props;
398
+ const previousMetadata = state.containerItemMetadata.get(containerId);
399
+ let metadata;
400
+ if ((previousMetadata == null ? void 0 : previousMetadata.dataChangeEpoch) === state.dataChangeEpoch && previousMetadata.getItemType === getItemType && previousMetadata.itemData === itemData && previousMetadata.itemIndex === itemIndex) {
401
+ metadata = previousMetadata;
402
+ } else {
403
+ const itemType = getItemType ? (_a3 = getItemType(itemData, itemIndex)) != null ? _a3 : "" : void 0;
404
+ metadata = createContainerItemMetadata(state, itemIndex, itemData, itemType);
405
+ state.containerItemMetadata.set(containerId, metadata);
371
406
  }
372
- const stickyIndexInArray = state.props.stickyHeaderIndicesArr.indexOf(index);
373
- if (stickyIndexInArray === -1) {
374
- return void 0;
407
+ if (metadata.getFixedItemSize !== getFixedItemSize) {
408
+ metadata.didResolveFixedItemSize = false;
409
+ metadata.fixedItemSize = void 0;
410
+ metadata.getFixedItemSize = getFixedItemSize;
375
411
  }
376
- const nextStickyIndex = state.props.stickyHeaderIndicesArr[stickyIndexInArray + 1];
377
- if (nextStickyIndex === void 0) {
378
- return void 0;
412
+ if (getFixedItemSize && !metadata.didResolveFixedItemSize) {
413
+ metadata.fixedItemSize = getFixedItemSize(itemData, itemIndex, (_b = metadata.itemType) != null ? _b : "");
414
+ metadata.didResolveFixedItemSize = true;
379
415
  }
380
- const nextStickyPosition = state.positions[nextStickyIndex];
381
- if (nextStickyPosition === void 0) {
382
- return void 0;
416
+ return metadata;
417
+ }
418
+ function invalidateContainerFixedItemSizes(state) {
419
+ for (const metadata of state.containerItemMetadata.values()) {
420
+ metadata.didResolveFixedItemSize = false;
421
+ metadata.fixedItemSize = void 0;
383
422
  }
384
- return nextStickyPosition - currentSize;
385
423
  }
386
424
 
387
425
  // src/utils/devEnvironment.ts
@@ -398,445 +436,73 @@ var EDGE_POSITION_EPSILON = 1;
398
436
  var ENABLE_DEVMODE = IS_DEV && false;
399
437
  var ENABLE_DEBUG_VIEW = IS_DEV && false;
400
438
 
401
- // src/constants-platform.native.ts
402
- var f = global.nativeFabricUIManager;
403
- var IsNewArchitecture = f !== void 0 && f != null;
404
- var useAnimatedValue = (initialValue) => {
405
- const [animAnimatedValue] = useState(() => new Animated.Value(initialValue));
406
- return animAnimatedValue;
407
- };
408
-
409
- // src/hooks/useValue$.ts
410
- function useValue$(key, params) {
411
- const { getValue } = params || {};
412
- const ctx = useStateContext();
413
- const getNewValue = () => {
414
- var _a3;
415
- return (_a3 = getValue ? getValue(peek$(ctx, key)) : peek$(ctx, key)) != null ? _a3 : 0;
439
+ // src/core/deferredPublicOnScroll.ts
440
+ function withResolvedContentOffset(state, event, resolvedOffset) {
441
+ return {
442
+ ...event,
443
+ nativeEvent: {
444
+ ...event.nativeEvent,
445
+ contentOffset: state.props.horizontal ? { x: resolvedOffset, y: 0 } : { x: 0, y: resolvedOffset }
446
+ }
416
447
  };
417
- const animValue = useAnimatedValue(getNewValue());
418
- useLayoutEffect(() => {
419
- const syncCurrentValue = () => {
420
- animValue.setValue(getNewValue());
421
- };
422
- const unsubscribe = listen$(ctx, key, syncCurrentValue);
423
- syncCurrentValue();
424
- return unsubscribe;
425
- }, [animValue, ctx, key]);
426
- return animValue;
427
448
  }
428
- var typedForwardRef = React2.forwardRef;
429
- var typedMemo = React2.memo;
430
- var getComponent = (Component) => {
431
- if (React2.isValidElement(Component)) {
432
- return Component;
433
- }
434
- if (Component) {
435
- return /* @__PURE__ */ React2.createElement(Component, null);
436
- }
437
- return null;
438
- };
439
-
440
- // src/components/PositionView.native.tsx
441
- var PositionViewState = typedMemo(function PositionViewState2({
442
- id,
443
- horizontal,
444
- style,
445
- refView,
446
- ...rest
447
- }) {
448
- const [position = POSITION_OUT_OF_VIEW, _itemKey] = useArr$([`containerPosition${id}`, `containerItemKey${id}`]);
449
- return /* @__PURE__ */ React2.createElement(View$1, { ref: refView, style: [style, horizontal ? { left: position } : { top: position }], ...rest });
450
- });
451
- var PositionViewAnimated = typedMemo(function PositionViewAnimated2({
452
- id,
453
- horizontal,
454
- style,
455
- refView,
456
- ...rest
457
- }) {
458
- const position$ = useValue$(`containerPosition${id}`, {
459
- getValue: (v) => v != null ? v : POSITION_OUT_OF_VIEW
460
- });
461
- const position = horizontal ? { left: position$ } : { top: position$ };
462
- return /* @__PURE__ */ React2.createElement(Animated.View, { ref: refView, style: [style, position], ...rest });
463
- });
464
- var PositionViewSticky = typedMemo(function PositionViewSticky2({
465
- id,
466
- horizontal,
467
- style,
468
- refView,
469
- animatedScrollY,
470
- index,
471
- stickyHeaderConfig,
472
- children,
473
- ...rest
474
- }) {
475
- const ctx = useStateContext();
476
- const [
477
- position = POSITION_OUT_OF_VIEW,
478
- alignItemsAtEndPadding = 0,
479
- headerSize = 0,
480
- stylePaddingTop = 0,
481
- itemKey,
482
- _totalSize = 0
483
- ] = useArr$([
484
- `containerPosition${id}`,
485
- "alignItemsAtEndPadding",
486
- "headerSize",
487
- "stylePaddingTop",
488
- `containerItemKey${id}`,
489
- "totalSize"
490
- ]);
491
- const pushLimit = React2.useMemo(
492
- () => getStickyPushLimit(ctx.state, index, itemKey),
493
- [ctx.state, index, itemKey, _totalSize]
494
- );
495
- const transform = React2.useMemo(() => {
496
- var _a3;
497
- if (animatedScrollY) {
498
- const stickyConfigOffset = (_a3 = stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset) != null ? _a3 : 0;
499
- const stickyStart = position + headerSize + stylePaddingTop + alignItemsAtEndPadding - stickyConfigOffset;
500
- let nextStickyPosition;
501
- if (pushLimit !== void 0) {
502
- if (pushLimit <= position) {
503
- nextStickyPosition = pushLimit;
504
- } else {
505
- nextStickyPosition = animatedScrollY.interpolate({
506
- extrapolateLeft: "clamp",
507
- extrapolateRight: "clamp",
508
- inputRange: [stickyStart, stickyStart + (pushLimit - position)],
509
- outputRange: [position, pushLimit]
510
- });
511
- }
512
- } else {
513
- nextStickyPosition = animatedScrollY.interpolate({
514
- extrapolateLeft: "clamp",
515
- extrapolateRight: "extend",
516
- inputRange: [stickyStart, stickyStart + 5e3],
517
- outputRange: [position, position + 5e3]
518
- });
519
- }
520
- return horizontal ? [{ translateX: nextStickyPosition }] : [{ translateY: nextStickyPosition }];
521
- }
522
- }, [
523
- alignItemsAtEndPadding,
524
- animatedScrollY,
525
- headerSize,
526
- position,
527
- pushLimit,
528
- stylePaddingTop,
529
- stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset
530
- ]);
531
- const viewStyle = React2.useMemo(() => [style, { zIndex: index + 1e3 }, { transform }], [style, transform]);
532
- const renderStickyHeaderBackdrop = React2.useMemo(() => {
533
- if (!(stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent)) {
534
- return null;
535
- }
536
- return /* @__PURE__ */ React2.createElement(
537
- View$1,
538
- {
539
- style: {
540
- inset: 0,
541
- pointerEvents: "none",
542
- position: "absolute"
543
- }
544
- },
545
- getComponent(stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent)
449
+ function releaseDeferredPublicOnScroll(ctx, resolvedOffset) {
450
+ var _a3, _b, _c, _d;
451
+ const state = ctx.state;
452
+ const deferredEvent = state.deferredPublicOnScrollEvent;
453
+ state.deferredPublicOnScrollEvent = void 0;
454
+ if (deferredEvent) {
455
+ (_d = (_c = state.props).onScroll) == null ? void 0 : _d.call(
456
+ _c,
457
+ withResolvedContentOffset(
458
+ state,
459
+ deferredEvent,
460
+ (_b = (_a3 = resolvedOffset != null ? resolvedOffset : state.scrollPending) != null ? _a3 : state.scroll) != null ? _b : 0
461
+ )
546
462
  );
547
- }, [stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent]);
548
- return /* @__PURE__ */ React2.createElement(Animated.View, { ref: refView, style: viewStyle, ...rest }, renderStickyHeaderBackdrop, children);
549
- });
550
- var PositionView = IsNewArchitecture ? PositionViewState : PositionViewAnimated;
551
- function useInit(cb) {
552
- useState(() => cb());
463
+ }
553
464
  }
554
465
 
555
- // src/utils/helpers.ts
556
- function isFunction(obj) {
557
- return typeof obj === "function";
466
+ // src/core/initialScrollSession.ts
467
+ var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
468
+ function hasInitialScrollSessionCompletion(completion) {
469
+ return !!((completion == null ? void 0 : completion.didDispatchNativeScroll) || (completion == null ? void 0 : completion.didRetrySilentInitialScroll) || (completion == null ? void 0 : completion.watchdog));
558
470
  }
559
- function isArray(obj) {
560
- return Array.isArray(obj);
471
+ function clearInitialScrollSession(state) {
472
+ state.initialScrollSession = void 0;
473
+ return void 0;
561
474
  }
562
- var warned = /* @__PURE__ */ new Set();
563
- function warnDevOnce(id, text) {
564
- if (IS_DEV && !warned.has(id)) {
565
- warned.add(id);
566
- console.warn(`[legend-list] ${text}`);
567
- }
475
+ function createInitialScrollSession(options) {
476
+ const { bootstrap, completion, kind, previousDataLength } = options;
477
+ return kind === "offset" ? {
478
+ completion,
479
+ kind,
480
+ previousDataLength
481
+ } : {
482
+ bootstrap,
483
+ completion,
484
+ kind,
485
+ previousDataLength
486
+ };
568
487
  }
569
- function roundSize(size) {
570
- return Math.floor(size * 8) / 8;
571
- }
572
- function isNullOrUndefined(value) {
573
- return value === null || value === void 0;
574
- }
575
- function getPadding(s, type) {
576
- var _a3, _b, _c;
577
- const axisPadding = type === "Left" || type === "Right" ? s.paddingHorizontal : s.paddingVertical;
578
- return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : axisPadding) != null ? _b : s.padding) != null ? _c : 0;
579
- }
580
- function extractPadding(style, contentContainerStyle, type) {
581
- return getPadding(style, type) + getPadding(contentContainerStyle, type);
582
- }
583
- function findContainerId(ctx, key) {
584
- var _a3, _b;
585
- const directMatch = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.containerItemKeys) == null ? void 0 : _b.get(key);
586
- if (directMatch !== void 0) {
587
- return directMatch;
588
- }
589
- const numContainers = peek$(ctx, "numContainers");
590
- for (let i = 0; i < numContainers; i++) {
591
- const itemKey = peek$(ctx, `containerItemKey${i}`);
592
- if (itemKey === key) {
593
- return i;
594
- }
595
- }
596
- return -1;
597
- }
598
-
599
- // src/state/ContextContainer.ts
600
- var ContextContainer = createContext(null);
601
- var NO_CONTAINER_ID = -1;
602
- function useContextContainer() {
603
- return useContext(ContextContainer);
604
- }
605
- function useContainerItemInfo(containerContext) {
606
- var _a3;
607
- const containerId = (_a3 = containerContext == null ? void 0 : containerContext.containerId) != null ? _a3 : NO_CONTAINER_ID;
608
- const [itemInfo] = useArr$([`containerItemInfo${containerId}`]);
609
- return containerContext ? itemInfo : void 0;
610
- }
611
- function useContainerItemKey(containerContext) {
612
- var _a3;
613
- const containerId = (_a3 = containerContext == null ? void 0 : containerContext.containerId) != null ? _a3 : NO_CONTAINER_ID;
614
- const [itemKey] = useArr$([`containerItemKey${containerId}`]);
615
- return containerContext ? itemKey : void 0;
616
- }
617
- function useAdaptiveRender() {
618
- const [mode] = useArr$(["adaptiveRender"]);
619
- return mode;
620
- }
621
- function useAdaptiveRenderChange(callback) {
622
- const ctx = useStateContext();
623
- const callbackRef = useRef(callback);
624
- callbackRef.current = callback;
625
- useLayoutEffect(() => {
626
- let mode = peek$(ctx, "adaptiveRender");
627
- return listen$(ctx, "adaptiveRender", (nextMode) => {
628
- if (mode !== nextMode) {
629
- mode = nextMode;
630
- callbackRef.current(nextMode);
631
- }
632
- });
633
- }, [ctx]);
634
- }
635
- function useViewability(callback, configId) {
636
- const ctx = useStateContext();
637
- const containerContext = useContextContainer();
638
- useInit(() => {
639
- if (!containerContext) {
640
- return;
641
- }
642
- const { containerId } = containerContext;
643
- const key = containerId + (configId != null ? configId : "");
644
- const value = ctx.mapViewabilityValues.get(key);
645
- if (value) {
646
- callback(value);
647
- }
648
- });
649
- useEffect(() => {
650
- if (!containerContext) {
651
- return;
652
- }
653
- const { containerId } = containerContext;
654
- const key = containerId + (configId != null ? configId : "");
655
- ctx.mapViewabilityCallbacks.set(key, callback);
656
- return () => {
657
- ctx.mapViewabilityCallbacks.delete(key);
658
- };
659
- }, [ctx, callback, configId, containerContext]);
660
- }
661
- function useViewabilityAmount(callback) {
662
- const ctx = useStateContext();
663
- const containerContext = useContextContainer();
664
- useInit(() => {
665
- if (!containerContext) {
666
- return;
667
- }
668
- const { containerId } = containerContext;
669
- const value = ctx.mapViewabilityAmountValues.get(containerId);
670
- if (value) {
671
- callback(value);
672
- }
673
- });
674
- useEffect(() => {
675
- if (!containerContext) {
676
- return;
677
- }
678
- const { containerId } = containerContext;
679
- ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
680
- return () => {
681
- ctx.mapViewabilityAmountCallbacks.delete(containerId);
682
- };
683
- }, [ctx, callback, containerContext]);
684
- }
685
- function useRecyclingEffect(effect) {
686
- const containerContext = useContextContainer();
687
- const itemInfo = useContainerItemInfo(containerContext);
688
- const prevInfo = useRef(void 0);
689
- useEffect(() => {
690
- if (!itemInfo) {
691
- return;
692
- }
693
- let ret;
694
- if (prevInfo.current) {
695
- ret = effect({
696
- index: itemInfo.index,
697
- item: itemInfo.value,
698
- prevIndex: prevInfo.current.index,
699
- prevItem: prevInfo.current.value
700
- });
701
- }
702
- prevInfo.current = itemInfo;
703
- return ret;
704
- }, [effect, itemInfo]);
705
- }
706
- function useRecyclingState(valueOrFun) {
707
- var _a3;
708
- const containerContext = useContextContainer();
709
- const itemInfo = useContainerItemInfo(containerContext);
710
- const computeValue = (info) => {
711
- if (isFunction(valueOrFun)) {
712
- const initializer = valueOrFun;
713
- return info ? initializer({
714
- index: info.index,
715
- item: info.value,
716
- prevIndex: void 0,
717
- prevItem: void 0
718
- }) : initializer();
719
- }
720
- return valueOrFun;
721
- };
722
- const [stateValue, setStateValue] = useState(() => {
723
- return computeValue(itemInfo);
724
- });
725
- const prevItemKeyRef = useRef((_a3 = itemInfo == null ? void 0 : itemInfo.itemKey) != null ? _a3 : null);
726
- if (itemInfo && prevItemKeyRef.current !== itemInfo.itemKey) {
727
- prevItemKeyRef.current = itemInfo.itemKey;
728
- setStateValue(computeValue(itemInfo));
729
- }
730
- const triggerLayout = containerContext == null ? void 0 : containerContext.triggerLayout;
731
- const setState = useCallback(
732
- (newState) => {
733
- if (!triggerLayout) {
734
- return;
735
- }
736
- setStateValue((prevValue) => {
737
- return isFunction(newState) ? newState(prevValue) : newState;
738
- });
739
- triggerLayout();
740
- },
741
- [triggerLayout]
742
- );
743
- return [stateValue, setState];
744
- }
745
- function useIsLastItem() {
746
- const containerContext = useContextContainer();
747
- const itemKey = useContainerItemKey(containerContext);
748
- const isLast = useSelector$("lastItemKeys", (lastItemKeys) => {
749
- if (containerContext && !isNullOrUndefined(itemKey)) {
750
- return (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false;
751
- }
752
- return false;
753
- });
754
- return isLast;
755
- }
756
- function useListScrollSize() {
757
- const [scrollSize] = useArr$(["scrollSize"]);
758
- return scrollSize;
759
- }
760
- var noop = () => {
761
- };
762
- function useSyncLayout() {
763
- const containerContext = useContextContainer();
764
- return IsNewArchitecture && containerContext ? containerContext.triggerLayout : noop;
765
- }
766
-
767
- // src/components/Separator.tsx
768
- function Separator({ ItemSeparatorComponent, leadingItem }) {
769
- const isLastItem = useIsLastItem();
770
- return isLastItem ? null : /* @__PURE__ */ React2.createElement(ItemSeparatorComponent, { leadingItem });
771
- }
772
-
773
- // src/core/deferredPublicOnScroll.ts
774
- function withResolvedContentOffset(state, event, resolvedOffset) {
775
- return {
776
- ...event,
777
- nativeEvent: {
778
- ...event.nativeEvent,
779
- contentOffset: state.props.horizontal ? { x: resolvedOffset, y: 0 } : { x: 0, y: resolvedOffset }
780
- }
781
- };
782
- }
783
- function releaseDeferredPublicOnScroll(ctx, resolvedOffset) {
784
- var _a3, _b, _c, _d;
785
- const state = ctx.state;
786
- const deferredEvent = state.deferredPublicOnScrollEvent;
787
- state.deferredPublicOnScrollEvent = void 0;
788
- if (deferredEvent) {
789
- (_d = (_c = state.props).onScroll) == null ? void 0 : _d.call(
790
- _c,
791
- withResolvedContentOffset(
792
- state,
793
- deferredEvent,
794
- (_b = (_a3 = resolvedOffset != null ? resolvedOffset : state.scrollPending) != null ? _a3 : state.scroll) != null ? _b : 0
795
- )
796
- );
797
- }
798
- }
799
-
800
- // src/core/initialScrollSession.ts
801
- var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
802
- function hasInitialScrollSessionCompletion(completion) {
803
- return !!((completion == null ? void 0 : completion.didDispatchNativeScroll) || (completion == null ? void 0 : completion.didRetrySilentInitialScroll) || (completion == null ? void 0 : completion.watchdog));
804
- }
805
- function clearInitialScrollSession(state) {
806
- state.initialScrollSession = void 0;
807
- return void 0;
808
- }
809
- function createInitialScrollSession(options) {
810
- const { bootstrap, completion, kind, previousDataLength } = options;
811
- return kind === "offset" ? {
812
- completion,
813
- kind,
814
- previousDataLength
815
- } : {
816
- bootstrap,
817
- completion,
818
- kind,
819
- previousDataLength
820
- };
821
- }
822
- function ensureInitialScrollSessionCompletion(state, kind = ((_b) => (_b = ((_a3) => (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind)()) != null ? _b : "bootstrap")()) {
823
- var _a4, _b2;
824
- if (!state.initialScrollSession) {
825
- state.initialScrollSession = createInitialScrollSession({
826
- completion: {},
827
- kind,
828
- previousDataLength: 0
829
- });
830
- } else if (state.initialScrollSession.kind !== kind) {
831
- state.initialScrollSession = createInitialScrollSession({
832
- bootstrap: state.initialScrollSession.kind === "bootstrap" ? state.initialScrollSession.bootstrap : void 0,
833
- completion: state.initialScrollSession.completion,
834
- kind,
835
- previousDataLength: state.initialScrollSession.previousDataLength
836
- });
837
- }
838
- (_b2 = (_a4 = state.initialScrollSession).completion) != null ? _b2 : _a4.completion = {};
839
- return state.initialScrollSession.completion;
488
+ function ensureInitialScrollSessionCompletion(state, kind = ((_b) => (_b = ((_a3) => (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind)()) != null ? _b : "bootstrap")()) {
489
+ var _a4, _b2;
490
+ if (!state.initialScrollSession) {
491
+ state.initialScrollSession = createInitialScrollSession({
492
+ completion: {},
493
+ kind,
494
+ previousDataLength: 0
495
+ });
496
+ } else if (state.initialScrollSession.kind !== kind) {
497
+ state.initialScrollSession = createInitialScrollSession({
498
+ bootstrap: state.initialScrollSession.kind === "bootstrap" ? state.initialScrollSession.bootstrap : void 0,
499
+ completion: state.initialScrollSession.completion,
500
+ kind,
501
+ previousDataLength: state.initialScrollSession.previousDataLength
502
+ });
503
+ }
504
+ (_b2 = (_a4 = state.initialScrollSession).completion) != null ? _b2 : _a4.completion = {};
505
+ return state.initialScrollSession.completion;
840
506
  }
841
507
  var initialScrollCompletion = {
842
508
  didDispatchNativeScroll(state) {
@@ -917,7 +583,11 @@ function setInitialScrollSession(state, options = {}) {
917
583
 
918
584
  // src/utils/checkThreshold.ts
919
585
  var HYSTERESIS_MULTIPLIER = 1.3;
920
- var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot, allowReentryOnChange) => {
586
+ function isOutsideThresholdHysteresis(distance, atThreshold, threshold) {
587
+ const absDistance = Math.abs(distance);
588
+ return !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
589
+ }
590
+ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot) => {
921
591
  const absDistance = Math.abs(distance);
922
592
  const within = atThreshold || threshold > 0 && absDistance <= threshold;
923
593
  const updateSnapshot = () => {
@@ -936,7 +606,7 @@ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, co
936
606
  updateSnapshot();
937
607
  return true;
938
608
  }
939
- const reset = !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
609
+ const reset = isOutsideThresholdHysteresis(distance, atThreshold, threshold);
940
610
  if (reset) {
941
611
  setSnapshot(void 0);
942
612
  return false;
@@ -944,23 +614,69 @@ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, co
944
614
  if (within) {
945
615
  const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
946
616
  if (changed) {
947
- if (allowReentryOnChange) {
948
- onReached(distance);
949
- }
950
617
  updateSnapshot();
951
618
  }
952
619
  }
953
620
  return true;
954
621
  };
955
622
 
956
- // src/utils/hasActiveInitialScroll.ts
957
- function hasActiveInitialScroll(state) {
958
- return !!(state == null ? void 0 : state.initialScroll) && !state.didFinishInitialScroll;
959
- }
960
-
961
- // src/utils/checkAtBottom.ts
962
- function checkAtBottom(ctx) {
963
- var _a3;
623
+ // src/utils/edgeReachedGate.ts
624
+ function resetEdgeLatch(ctx, edge) {
625
+ const state = ctx.state;
626
+ if (edge === "start") {
627
+ state.isStartReached = false;
628
+ state.startReachedSnapshot = void 0;
629
+ } else {
630
+ state.isEndReached = false;
631
+ state.endReachedSnapshot = void 0;
632
+ }
633
+ }
634
+ function resetSharedEdgeGateIfOutsideHysteresis(ctx) {
635
+ const state = ctx.state;
636
+ if (!state.edgeReachedGate) {
637
+ return;
638
+ }
639
+ const contentSize = getContentSize(ctx);
640
+ const endDistance = contentSize - state.scroll - state.scrollLength - getContentInsetEnd(ctx);
641
+ const isContentLess = contentSize < state.scrollLength;
642
+ const startThreshold = state.props.onStartReachedThreshold * state.scrollLength;
643
+ const endThreshold = state.props.onEndReachedThreshold * state.scrollLength;
644
+ const isOutsideStart = isOutsideThresholdHysteresis(state.scroll, false, startThreshold);
645
+ const isOutsideEnd = isOutsideThresholdHysteresis(endDistance, isContentLess, endThreshold);
646
+ if (isOutsideStart && isOutsideEnd) {
647
+ state.edgeReachedGate = void 0;
648
+ }
649
+ }
650
+ function canDispatchReachedEdge(ctx, edge, allowedEdge, allowGateCreatedInCurrentCheck) {
651
+ return !ctx.state.edgeReachedGate || allowedEdge === edge || !!allowGateCreatedInCurrentCheck;
652
+ }
653
+ function markReachedEdge(ctx) {
654
+ ctx.state.edgeReachedGate = "closed";
655
+ }
656
+ function prepareReachedEdgeForNextUserScroll(ctx) {
657
+ if (ctx.state.edgeReachedGate) {
658
+ ctx.state.edgeReachedGate = "prepared";
659
+ }
660
+ }
661
+ function beginReachedEdgeUserScroll(ctx, scrollDelta) {
662
+ const state = ctx.state;
663
+ if (state.edgeReachedGate !== "prepared") {
664
+ return void 0;
665
+ }
666
+ const allowedEdge = scrollDelta < 0 ? "start" : "end";
667
+ state.edgeReachedGate = "closed";
668
+ resetEdgeLatch(ctx, allowedEdge);
669
+ return allowedEdge;
670
+ }
671
+
672
+ // src/utils/hasActiveInitialScroll.ts
673
+ function hasActiveInitialScroll(state) {
674
+ return !!(state == null ? void 0 : state.initialScroll) && !state.didFinishInitialScroll;
675
+ }
676
+
677
+ // src/utils/checkAtBottom.ts
678
+ function checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
679
+ var _a3;
964
680
  const state = ctx.state;
965
681
  if (!state) {
966
682
  return;
@@ -973,6 +689,7 @@ function checkAtBottom(ctx) {
973
689
  props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
974
690
  } = state;
975
691
  const contentSize = getContentSize(ctx);
692
+ resetSharedEdgeGateIfOutsideHysteresis(ctx);
976
693
  if (contentSize > 0 && queuedInitialLayout) {
977
694
  const insetEnd = getContentInsetEnd(ctx);
978
695
  const distanceFromEnd = contentSize - scroll - scrollLength - insetEnd;
@@ -999,59 +716,50 @@ function checkAtBottom(ctx) {
999
716
  },
1000
717
  (distance) => {
1001
718
  var _a4, _b;
1002
- return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
719
+ if (canDispatchReachedEdge(ctx, "end", allowedEdge, allowGateCreatedInCurrentCheck)) {
720
+ markReachedEdge(ctx);
721
+ (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
722
+ }
1003
723
  },
1004
724
  (snapshot) => {
1005
725
  state.endReachedSnapshot = snapshot;
1006
- },
1007
- true
726
+ }
1008
727
  );
1009
728
  }
1010
729
  }
1011
730
  }
1012
731
 
1013
- // src/utils/isInMVCPActiveMode.native.ts
1014
- function isInMVCPActiveMode(state) {
1015
- return state.dataChangeNeedsScrollUpdate;
1016
- }
1017
-
1018
732
  // src/utils/checkAtTop.ts
1019
- function checkAtTop(ctx) {
733
+ function checkAtTop(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
1020
734
  const state = ctx == null ? void 0 : ctx.state;
1021
735
  if (!state) {
1022
736
  return;
1023
737
  }
1024
738
  const {
1025
- dataChangeEpoch,
1026
739
  isStartReached,
1027
740
  props: { data, onStartReachedThreshold },
1028
741
  scroll,
1029
742
  scrollLength,
1030
743
  startReachedSnapshot,
1031
- startReachedSnapshotDataChangeEpoch,
1032
744
  totalSize
1033
745
  } = state;
1034
746
  const dataLength = data.length;
1035
747
  const threshold = onStartReachedThreshold * scrollLength;
1036
- const dataChanged = startReachedSnapshotDataChangeEpoch !== dataChangeEpoch;
1037
- const withinThreshold = threshold > 0 && Math.abs(scroll) <= threshold;
1038
- const allowReentryOnDataChange = !!isStartReached && withinThreshold && !!dataChanged && !isInMVCPActiveMode(state);
1039
- if (isStartReached && threshold > 0 && scroll > threshold && startReachedSnapshot && (dataChanged || startReachedSnapshot.contentSize !== totalSize || startReachedSnapshot.dataLength !== dataLength)) {
748
+ resetSharedEdgeGateIfOutsideHysteresis(ctx);
749
+ if (isStartReached && threshold > 0 && scroll > threshold && startReachedSnapshot && (startReachedSnapshot.contentSize !== totalSize || startReachedSnapshot.dataLength !== dataLength)) {
1040
750
  state.isStartReached = false;
1041
751
  state.startReachedSnapshot = void 0;
1042
- state.startReachedSnapshotDataChangeEpoch = void 0;
1043
752
  }
1044
753
  set$(ctx, "isAtStart", scroll <= EDGE_POSITION_EPSILON);
1045
754
  set$(ctx, "isNearStart", scroll <= threshold);
1046
755
  const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || !!state.scrollingTo;
1047
- const shouldDeferDataChangeRefire = isStartReached && withinThreshold && dataChanged && !allowReentryOnDataChange;
1048
- if (!shouldSkipThresholdChecks && !shouldDeferDataChangeRefire) {
756
+ if (!shouldSkipThresholdChecks) {
1049
757
  state.isStartReached = checkThreshold(
1050
758
  scroll,
1051
759
  false,
1052
760
  threshold,
1053
761
  state.isStartReached,
1054
- allowReentryOnDataChange ? void 0 : startReachedSnapshot,
762
+ startReachedSnapshot,
1055
763
  {
1056
764
  contentSize: totalSize,
1057
765
  dataLength,
@@ -1059,21 +767,23 @@ function checkAtTop(ctx) {
1059
767
  },
1060
768
  (distance) => {
1061
769
  var _a3, _b;
1062
- return (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
770
+ if (canDispatchReachedEdge(ctx, "start", allowedEdge, allowGateCreatedInCurrentCheck)) {
771
+ markReachedEdge(ctx);
772
+ (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
773
+ }
1063
774
  },
1064
775
  (snapshot) => {
1065
776
  state.startReachedSnapshot = snapshot;
1066
- state.startReachedSnapshotDataChangeEpoch = snapshot ? dataChangeEpoch : void 0;
1067
- },
1068
- allowReentryOnDataChange
777
+ }
1069
778
  );
1070
779
  }
1071
780
  }
1072
781
 
1073
782
  // src/utils/checkThresholds.ts
1074
- function checkThresholds(ctx) {
1075
- checkAtBottom(ctx);
1076
- checkAtTop(ctx);
783
+ function checkThresholds(ctx, allowedEdge) {
784
+ const allowGateCreatedInCurrentCheck = !ctx.state.edgeReachedGate;
785
+ checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck);
786
+ checkAtTop(ctx, allowedEdge, allowGateCreatedInCurrentCheck);
1077
787
  }
1078
788
 
1079
789
  // src/core/recalculateSettledScroll.ts
@@ -1094,13 +804,7 @@ var DEFAULT_WEB_ADAPTIVE_RENDER_ENTER_VELOCITY = 6;
1094
804
  var DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_VELOCITY = 3;
1095
805
  var DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_DELAY = 250;
1096
806
  function clearAdaptiveRenderExitTimeout(ctx) {
1097
- const state = ctx.state;
1098
- const previousTimeout = state.timeoutAdaptiveRender;
1099
- if (previousTimeout !== void 0) {
1100
- clearTimeout(previousTimeout);
1101
- state.timeouts.delete(previousTimeout);
1102
- state.timeoutAdaptiveRender = void 0;
1103
- }
807
+ ctx.state.scheduledWork.cancel("adaptiveRender");
1104
808
  }
1105
809
  function scheduleAdaptiveRenderExit(ctx, exitDelay) {
1106
810
  const state = ctx.state;
@@ -1108,13 +812,7 @@ function scheduleAdaptiveRenderExit(ctx, exitDelay) {
1108
812
  if (exitDelay <= 0) {
1109
813
  setAdaptiveRender(ctx, "normal", "scroll");
1110
814
  } else {
1111
- const timeout = setTimeout(() => {
1112
- state.timeouts.delete(timeout);
1113
- state.timeoutAdaptiveRender = void 0;
1114
- setAdaptiveRender(ctx, "normal", "scroll");
1115
- }, exitDelay);
1116
- state.timeoutAdaptiveRender = timeout;
1117
- state.timeouts.add(timeout);
815
+ state.scheduledWork.timeout(() => setAdaptiveRender(ctx, "normal", "scroll"), exitDelay, "adaptiveRender");
1118
816
  }
1119
817
  }
1120
818
  function setAdaptiveRender(ctx, mode, reason) {
@@ -1146,7 +844,7 @@ function updateAdaptiveRender(ctx, scrollVelocity, options) {
1146
844
  const exitDelay = (_c = adaptiveRender.exitDelay) != null ? _c : isWeb ? DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_DELAY : DEFAULT_ADAPTIVE_RENDER_EXIT_DELAY;
1147
845
  const threshold = currentMode === "light" ? exitVelocity : enterVelocity;
1148
846
  const nextMode = (options == null ? void 0 : options.forceLight) || Math.abs(scrollVelocity) > threshold ? "light" : "normal";
1149
- const previousMode = state.timeoutAdaptiveRender !== void 0 ? "normal" : currentMode;
847
+ const previousMode = state.scheduledWork.has("adaptiveRender") ? "normal" : currentMode;
1150
848
  if (nextMode !== previousMode) {
1151
849
  if (nextMode === "light") {
1152
850
  setAdaptiveRender(ctx, "light", "scroll");
@@ -1173,14 +871,13 @@ function getEffectiveDrawDistance(ctx, mode) {
1173
871
  }
1174
872
  function scheduleFullDrawDistancePrewarm(ctx) {
1175
873
  const { state } = ctx;
1176
- if (state.props.drawDistance <= INITIAL_DRAW_DISTANCE || state.queuedFullDrawDistancePrewarm !== void 0) {
874
+ if (state.props.drawDistance <= INITIAL_DRAW_DISTANCE || state.scheduledWork.has("fullDrawDistancePrewarm")) {
1177
875
  return;
1178
876
  }
1179
- state.queuedFullDrawDistancePrewarm = requestAnimationFrame(() => {
877
+ state.scheduledWork.frame(() => {
1180
878
  var _a3;
1181
- state.queuedFullDrawDistancePrewarm = void 0;
1182
- (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
1183
- });
879
+ return (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
880
+ }, "fullDrawDistancePrewarm");
1184
881
  }
1185
882
 
1186
883
  // src/utils/setInitialRenderState.ts
@@ -1238,10 +935,7 @@ function syncInitialScrollOffset(state, offset) {
1238
935
  state.scrollPrev = offset;
1239
936
  }
1240
937
  function clearPreservedInitialScrollTargetTimeout(state) {
1241
- if (state.timeoutPreservedInitialScrollClear !== void 0) {
1242
- clearTimeout(state.timeoutPreservedInitialScrollClear);
1243
- state.timeoutPreservedInitialScrollClear = void 0;
1244
- }
938
+ state.scheduledWork.cancel("preservedInitialScroll");
1245
939
  }
1246
940
  function clearPreservedInitialScrollTarget(state) {
1247
941
  clearPreservedInitialScrollTargetTimeout(state);
@@ -1270,14 +964,17 @@ function finishInitialScroll(ctx, options) {
1270
964
  setInitialScrollSession(state);
1271
965
  clearPreservedInitialScrollTargetTimeout(state);
1272
966
  if (options == null ? void 0 : options.schedulePreservedTargetClear) {
1273
- state.timeoutPreservedInitialScrollClear = setTimeout(() => {
1274
- var _a5;
1275
- state.timeoutPreservedInitialScrollClear = void 0;
1276
- if (!state.didFinishInitialScroll || ((_a5 = state.scrollingTo) == null ? void 0 : _a5.isInitialScroll) || !state.initialScroll) {
1277
- return;
1278
- }
1279
- clearPreservedInitialScrollTarget(state);
1280
- }, PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS);
967
+ state.scheduledWork.timeout(
968
+ () => {
969
+ var _a5;
970
+ if (!state.didFinishInitialScroll || ((_a5 = state.scrollingTo) == null ? void 0 : _a5.isInitialScroll) || !state.initialScroll) {
971
+ return;
972
+ }
973
+ clearPreservedInitialScrollTarget(state);
974
+ },
975
+ PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS,
976
+ "preservedInitialScroll"
977
+ );
1281
978
  }
1282
979
  } else {
1283
980
  clearPreservedInitialScrollTarget(state);
@@ -1304,9 +1001,11 @@ function calculateOffsetForIndex(ctx, index) {
1304
1001
  return index !== void 0 ? state.positions[index] || 0 : 0;
1305
1002
  }
1306
1003
 
1307
- // src/core/getTopOffsetAdjustment.ts
1308
- function getTopOffsetAdjustment(ctx) {
1309
- return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "alignItemsAtEndPadding") || 0) + (peek$(ctx, "headerSize") || 0);
1004
+ // src/core/getStartOffsetAdjustment.ts
1005
+ function getStartOffsetAdjustment(ctx) {
1006
+ const { state } = ctx;
1007
+ const stylePaddingStart = state.props.horizontal ? (isHorizontalRTL(state) ? state.props.stylePaddingRight : state.props.stylePaddingLeft) || 0 : peek$(ctx, "stylePaddingTop") || 0;
1008
+ return stylePaddingStart + (peek$(ctx, "alignItemsAtEndPadding") || 0) + (peek$(ctx, "headerSize") || 0);
1310
1009
  }
1311
1010
 
1312
1011
  // src/utils/getId.ts
@@ -1382,6 +1081,50 @@ function setSize(ctx, itemKey, size, notifyTotalSize = true) {
1382
1081
  sizes.set(itemKey, size);
1383
1082
  }
1384
1083
 
1084
+ // src/utils/helpers.ts
1085
+ function isFunction(obj) {
1086
+ return typeof obj === "function";
1087
+ }
1088
+ function isArray(obj) {
1089
+ return Array.isArray(obj);
1090
+ }
1091
+ var warned = /* @__PURE__ */ new Set();
1092
+ function warnDevOnce(id, text) {
1093
+ if (IS_DEV && !warned.has(id)) {
1094
+ warned.add(id);
1095
+ console.warn(`[legend-list] ${text}`);
1096
+ }
1097
+ }
1098
+ function roundSize(size) {
1099
+ return Math.floor(size * 8) / 8;
1100
+ }
1101
+ function isNullOrUndefined(value) {
1102
+ return value === null || value === void 0;
1103
+ }
1104
+ function getPadding(s, type) {
1105
+ var _a3, _b, _c;
1106
+ const axisPadding = type === "Left" || type === "Right" ? s.paddingHorizontal : s.paddingVertical;
1107
+ return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : axisPadding) != null ? _b : s.padding) != null ? _c : 0;
1108
+ }
1109
+ function extractPadding(style, contentContainerStyle, type) {
1110
+ return getPadding(style, type) + getPadding(contentContainerStyle, type);
1111
+ }
1112
+ function findContainerId(ctx, key) {
1113
+ var _a3, _b;
1114
+ const directMatch = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.containerItemKeys) == null ? void 0 : _b.get(key);
1115
+ if (directMatch !== void 0) {
1116
+ return directMatch;
1117
+ }
1118
+ const numContainers = peek$(ctx, "numContainers");
1119
+ for (let i = 0; i < numContainers; i++) {
1120
+ const itemKey = peek$(ctx, `containerItemKey${i}`);
1121
+ if (itemKey === key) {
1122
+ return i;
1123
+ }
1124
+ }
1125
+ return -1;
1126
+ }
1127
+
1385
1128
  // src/utils/getItemSize.ts
1386
1129
  function getKnownOrFixedSize(ctx, key, index, data, resolved) {
1387
1130
  var _a3, _b;
@@ -1475,9 +1218,9 @@ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1475
1218
  offset -= viewOffset;
1476
1219
  }
1477
1220
  if (index !== void 0) {
1478
- const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
1479
- if (topOffsetAdjustment) {
1480
- offset += topOffsetAdjustment;
1221
+ const startOffsetAdjustment = getStartOffsetAdjustment(ctx);
1222
+ if (startOffsetAdjustment) {
1223
+ offset += startOffsetAdjustment;
1481
1224
  }
1482
1225
  }
1483
1226
  if (viewPosition !== void 0 && index !== void 0) {
@@ -1487,7 +1230,8 @@ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1487
1230
  }
1488
1231
  const isOutOfBounds = index < 0 || index >= dataLength;
1489
1232
  const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
1490
- const itemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1233
+ const measuredItemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1234
+ const itemSize = Math.max(0, measuredItemSize - (isOutOfBounds ? 0 : ctx.scrollAxisGap));
1491
1235
  const trailingInset = getContentInsetEnd(ctx);
1492
1236
  offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1493
1237
  if (!isOutOfBounds && index === state.props.data.length - 1) {
@@ -1514,11 +1258,35 @@ function clampScrollOffset(ctx, offset, scrollTarget) {
1514
1258
  return clampedOffset;
1515
1259
  }
1516
1260
 
1261
+ // src/core/cancelImperativeScroll.ts
1262
+ function cancelScrollCompletionChecks({ scheduledWork }) {
1263
+ scheduledWork.cancel("checkFinishedScrollFrame");
1264
+ scheduledWork.cancel("checkFinishedScrollRetryFrame");
1265
+ scheduledWork.cancel("checkFinishedScrollFallback");
1266
+ scheduledWork.cancel("platformScrollCompletion");
1267
+ }
1268
+ function settlePendingImperativeScroll(state) {
1269
+ var _a3, _b;
1270
+ const resolvePendingScroll = (_b = state.pendingScrollResolve) != null ? _b : (_a3 = state.pendingScrollToEnd) == null ? void 0 : _a3.resolve;
1271
+ state.pendingScrollResolve = void 0;
1272
+ state.pendingScrollToEnd = void 0;
1273
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1274
+ }
1275
+ function cancelImperativeScroll(state) {
1276
+ cancelScrollCompletionChecks(state);
1277
+ state.scheduledWork.cancel("imperativeScrollReady");
1278
+ state.runPendingScrollToEnd = void 0;
1279
+ state.scrollingTo = void 0;
1280
+ state.scrollTargetPinnedRange = void 0;
1281
+ settlePendingImperativeScroll(state);
1282
+ }
1283
+
1517
1284
  // src/core/finishScrollTo.ts
1518
1285
  function finishScrollTo(ctx) {
1519
1286
  var _a3, _b;
1520
1287
  const state = ctx.state;
1521
1288
  if (state == null ? void 0 : state.scrollingTo) {
1289
+ cancelScrollCompletionChecks(state);
1522
1290
  const resolvePendingScroll = state.pendingScrollResolve;
1523
1291
  state.pendingScrollResolve = void 0;
1524
1292
  const scrollingTo = state.scrollingTo;
@@ -1567,7 +1335,7 @@ function checkFinishedScroll(ctx, options) {
1567
1335
  return;
1568
1336
  }
1569
1337
  }
1570
- ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
1338
+ ctx.state.scheduledWork.frame(() => checkFinishedScrollFrame(ctx), "checkFinishedScrollFrame");
1571
1339
  }
1572
1340
  function hasScrollCompletionOwnership(state, options) {
1573
1341
  const { clampedTargetOffset, scrollingTo } = options;
@@ -1637,7 +1405,6 @@ function checkFinishedScrollFrame(ctx) {
1637
1405
  return;
1638
1406
  }
1639
1407
  const { state } = ctx;
1640
- state.animFrameCheckFinishedScroll = void 0;
1641
1408
  const completionState = getResolvedScrollCompletionState(ctx, scrollingTo);
1642
1409
  if (completionState.isAtResolvedTarget && hasScrollCompletionOwnership(state, {
1643
1410
  clampedTargetOffset: completionState.clampedTargetOffset,
@@ -1656,61 +1423,62 @@ function scrollToFallbackOffset(ctx, offset) {
1656
1423
  }
1657
1424
  function checkFinishedScrollFallback(ctx) {
1658
1425
  const state = ctx.state;
1426
+ if (state.scheduledWork.has("checkFinishedScrollFallback")) {
1427
+ return;
1428
+ }
1659
1429
  const scrollingTo = state.scrollingTo;
1660
1430
  const shouldFinishInitialZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
1661
1431
  const silentInitialDispatch = isSilentInitialDispatch(state, scrollingTo);
1662
1432
  const canFinishInitialWithoutNativeProgress = scrollingTo !== void 0 ? shouldFinishInitialScrollWithoutNativeProgress(state, scrollingTo) : false;
1663
1433
  const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && !shouldFinishInitialZeroTarget && !canFinishInitialWithoutNativeProgress || !state.didContainersLayout;
1664
1434
  const initialDelay = shouldFinishInitialZeroTarget || canFinishInitialWithoutNativeProgress ? 0 : silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : slowTimeout ? 500 : 100;
1665
- state.timeoutCheckFinishedScrollFallback = setTimeout(() => {
1666
- let numChecks = 0;
1667
- const scheduleFallbackCheck = (delay) => {
1668
- state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, delay);
1669
- };
1670
- const checkHasScrolled = () => {
1671
- var _a3, _b, _c, _d;
1672
- state.timeoutCheckFinishedScrollFallback = void 0;
1673
- const isStillScrollingTo = state.scrollingTo;
1674
- if (isStillScrollingTo) {
1675
- numChecks++;
1676
- const isNativeInitialPending = isNativeInitialNonZeroTarget(state) && !state.hasScrolled;
1677
- const maxChecks = silentInitialDispatch ? 5 : isNativeInitialPending ? INITIAL_SCROLL_MAX_FALLBACK_CHECKS : 5;
1678
- const shouldFinishZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
1679
- const canFinishInitialScrollWithoutNativeProgress = shouldFinishInitialScrollWithoutNativeProgress(
1680
- state,
1681
- isStillScrollingTo
1435
+ let numChecks = 0;
1436
+ const scheduleFallbackCheck = (delay) => {
1437
+ state.scheduledWork.timeout(checkHasScrolled, delay, "checkFinishedScrollFallback");
1438
+ };
1439
+ const checkHasScrolled = () => {
1440
+ var _a3, _b, _c, _d;
1441
+ const isStillScrollingTo = state.scrollingTo;
1442
+ if (isStillScrollingTo) {
1443
+ numChecks++;
1444
+ const isNativeInitialPending = isNativeInitialNonZeroTarget(state) && !state.hasScrolled;
1445
+ const maxChecks = silentInitialDispatch ? 5 : isNativeInitialPending ? INITIAL_SCROLL_MAX_FALLBACK_CHECKS : 5;
1446
+ const shouldFinishZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
1447
+ const canFinishInitialScrollWithoutNativeProgress = shouldFinishInitialScrollWithoutNativeProgress(
1448
+ state,
1449
+ isStillScrollingTo
1450
+ );
1451
+ const completionState = getResolvedScrollCompletionState(ctx, isStillScrollingTo);
1452
+ const canFinishAfterSilentNativeDispatch = Platform.OS === "android" && silentInitialDispatch && completionState.isAtResolvedTarget && numChecks >= 1;
1453
+ const shouldRetrySilentInitialNativeScroll = Platform.OS === "android" && canFinishAfterSilentNativeDispatch && !initialScrollCompletion.didRetrySilentInitialScroll(state);
1454
+ const shouldFinishAfterObservedScroll = state.hasScrolled && (!isStillScrollingTo.isInitialScroll || completionState.isAtResolvedTarget);
1455
+ const shouldRetryUnalignedInitialScroll = isStillScrollingTo.isInitialScroll && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
1456
+ const shouldRetryUnalignedEndScroll = Platform.OS === "ios" && !isStillScrollingTo.isInitialScroll && isEndAlignedLastItemTarget(ctx, isStillScrollingTo) && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
1457
+ if (shouldRetrySilentInitialNativeScroll) {
1458
+ const targetOffset = (_b = (_a3 = getInitialScrollWatchdogTargetOffset(state)) != null ? _a3 : isStillScrollingTo.targetOffset) != null ? _b : 0;
1459
+ const jiggleOffset = targetOffset >= SILENT_INITIAL_SCROLL_TARGET_EPSILON ? targetOffset - SILENT_INITIAL_SCROLL_TARGET_EPSILON : targetOffset + SILENT_INITIAL_SCROLL_TARGET_EPSILON;
1460
+ initialScrollCompletion.markSilentInitialScrollRetry(state);
1461
+ scrollToFallbackOffset(ctx, jiggleOffset);
1462
+ state.scheduledWork.frame(
1463
+ () => scrollToFallbackOffset(ctx, targetOffset),
1464
+ "checkFinishedScrollRetryFrame"
1682
1465
  );
1683
- const completionState = getResolvedScrollCompletionState(ctx, isStillScrollingTo);
1684
- const canFinishAfterSilentNativeDispatch = Platform.OS === "android" && silentInitialDispatch && completionState.isAtResolvedTarget && numChecks >= 1;
1685
- const shouldRetrySilentInitialNativeScroll = Platform.OS === "android" && canFinishAfterSilentNativeDispatch && !initialScrollCompletion.didRetrySilentInitialScroll(state);
1686
- const shouldFinishAfterObservedScroll = state.hasScrolled && (!isStillScrollingTo.isInitialScroll || completionState.isAtResolvedTarget);
1687
- const shouldRetryUnalignedInitialScroll = isStillScrollingTo.isInitialScroll && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
1688
- const shouldRetryUnalignedEndScroll = Platform.OS === "ios" && !isStillScrollingTo.isInitialScroll && isEndAlignedLastItemTarget(ctx, isStillScrollingTo) && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
1689
- if (shouldRetrySilentInitialNativeScroll) {
1690
- const targetOffset = (_b = (_a3 = getInitialScrollWatchdogTargetOffset(state)) != null ? _a3 : isStillScrollingTo.targetOffset) != null ? _b : 0;
1691
- const jiggleOffset = targetOffset >= SILENT_INITIAL_SCROLL_TARGET_EPSILON ? targetOffset - SILENT_INITIAL_SCROLL_TARGET_EPSILON : targetOffset + SILENT_INITIAL_SCROLL_TARGET_EPSILON;
1692
- initialScrollCompletion.markSilentInitialScrollRetry(state);
1693
- scrollToFallbackOffset(ctx, jiggleOffset);
1694
- requestAnimationFrame(() => {
1695
- scrollToFallbackOffset(ctx, targetOffset);
1696
- });
1697
- scheduleFallbackCheck(SILENT_INITIAL_SCROLL_RETRY_DELAY_MS);
1698
- } else if (shouldRetryUnalignedEndScroll) {
1699
- scrollToFallbackOffset(ctx, completionState.clampedTargetOffset);
1700
- scheduleFallbackCheck(100);
1701
- } else if (shouldFinishZeroTarget || shouldFinishAfterObservedScroll || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
1702
- finishScrollTo(ctx);
1703
- } else if ((isNativeInitialPending || shouldRetryUnalignedInitialScroll) && numChecks <= maxChecks) {
1704
- const targetOffset = (_d = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : isStillScrollingTo.targetOffset) != null ? _d : state.scrollPending;
1705
- scrollToFallbackOffset(ctx, targetOffset);
1706
- scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
1707
- } else {
1708
- scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
1709
- }
1466
+ scheduleFallbackCheck(SILENT_INITIAL_SCROLL_RETRY_DELAY_MS);
1467
+ } else if (shouldRetryUnalignedEndScroll) {
1468
+ scrollToFallbackOffset(ctx, completionState.clampedTargetOffset);
1469
+ scheduleFallbackCheck(100);
1470
+ } else if (shouldFinishZeroTarget || shouldFinishAfterObservedScroll || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
1471
+ finishScrollTo(ctx);
1472
+ } else if ((isNativeInitialPending || shouldRetryUnalignedInitialScroll) && numChecks <= maxChecks) {
1473
+ const targetOffset = (_d = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : isStillScrollingTo.targetOffset) != null ? _d : state.scrollPending;
1474
+ scrollToFallbackOffset(ctx, targetOffset);
1475
+ scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
1476
+ } else {
1477
+ scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
1710
1478
  }
1711
- };
1712
- checkHasScrolled();
1713
- }, initialDelay);
1479
+ }
1480
+ };
1481
+ scheduleFallbackCheck(initialDelay);
1714
1482
  }
1715
1483
 
1716
1484
  // src/core/doScrollTo.native.ts
@@ -1734,6 +1502,9 @@ function doScrollTo(ctx, params) {
1734
1502
  if (isInitialScroll) {
1735
1503
  initialScrollCompletion.markInitialScrollNativeDispatch(state);
1736
1504
  }
1505
+ if (isAnimated && Math.abs(state.scroll - offset) <= 1) {
1506
+ checkFinishedScroll(ctx);
1507
+ }
1737
1508
  if (!isAnimated) {
1738
1509
  state.scroll = offset;
1739
1510
  checkFinishedScrollFallback(ctx);
@@ -1764,9 +1535,12 @@ function doMaintainScrollAtEnd(ctx) {
1764
1535
  if (!state.maintainingScrollAtEnd) {
1765
1536
  const pendingState = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
1766
1537
  const activeState = maintainScrollAtEnd.animated ? "animated" : "instant";
1538
+ const scrollAtRequest = state.scroll;
1767
1539
  state.maintainingScrollAtEnd = pendingState;
1768
1540
  requestAnimationFrame(() => {
1769
- if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
1541
+ const isStillWithinThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
1542
+ const didScrollSinceRequest = state.scroll !== scrollAtRequest;
1543
+ if (isStillWithinThreshold || !didScrollSinceRequest) {
1770
1544
  state.maintainingScrollAtEnd = activeState;
1771
1545
  const scroller = refScroller.current;
1772
1546
  if (state.props.horizontal && isHorizontalRTL(state)) {
@@ -1796,6 +1570,7 @@ function doMaintainScrollAtEnd(ctx) {
1796
1570
  );
1797
1571
  } else if (state.maintainingScrollAtEnd === pendingState) {
1798
1572
  state.maintainingScrollAtEnd = void 0;
1573
+ state.pendingMaintainScrollAtEnd = false;
1799
1574
  }
1800
1575
  });
1801
1576
  } else {
@@ -1837,20 +1612,21 @@ function requestAdjust(ctx, positionDiff, dataChanged) {
1837
1612
  } else {
1838
1613
  state.ignoreScrollFromMVCP.gt = threshold;
1839
1614
  }
1840
- if (state.ignoreScrollFromMVCPTimeout) {
1841
- clearTimeout(state.ignoreScrollFromMVCPTimeout);
1842
- }
1843
1615
  const delay = needsScrollWorkaround ? 250 : 100;
1844
- state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
1845
- var _a3;
1846
- state.ignoreScrollFromMVCP = void 0;
1847
- const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
1848
- if (shouldForceUpdate) {
1849
- state.ignoreScrollFromMVCPIgnored = false;
1850
- state.scrollPending = state.scroll;
1851
- (_a3 = state.reprocessCurrentScroll) == null ? void 0 : _a3.call(state);
1852
- }
1853
- }, delay);
1616
+ state.scheduledWork.timeout(
1617
+ () => {
1618
+ var _a3;
1619
+ state.ignoreScrollFromMVCP = void 0;
1620
+ const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
1621
+ if (shouldForceUpdate) {
1622
+ state.ignoreScrollFromMVCPIgnored = false;
1623
+ state.scrollPending = state.scroll;
1624
+ (_a3 = state.reprocessCurrentScroll) == null ? void 0 : _a3.call(state);
1625
+ }
1626
+ },
1627
+ delay,
1628
+ "ignoreScrollFromMVCP"
1629
+ );
1854
1630
  }
1855
1631
  } else {
1856
1632
  state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
@@ -2206,6 +1982,11 @@ var getScrollVelocity = (state) => {
2206
1982
  return totalWeight > 0 ? weightedVelocity / totalWeight : 0;
2207
1983
  };
2208
1984
 
1985
+ // src/utils/isInMVCPActiveMode.native.ts
1986
+ function isInMVCPActiveMode(state) {
1987
+ return state.dataChangeNeedsScrollUpdate;
1988
+ }
1989
+
2209
1990
  // src/core/updateScroll.ts
2210
1991
  function updateScroll(ctx, newScroll, forceUpdate, options) {
2211
1992
  var _a3;
@@ -2243,6 +2024,8 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2243
2024
  state.scroll = newScroll;
2244
2025
  state.scrollTime = currentTime;
2245
2026
  const scrollDelta = Math.abs(newScroll - prevScroll);
2027
+ const isUserScrollEvent = !!(options == null ? void 0 : options.fromNativeScrollEvent) && scrollDelta > 0.1 && !adjustChanged && scrollingTo === void 0 && !state.pendingNativeMVCPAdjust;
2028
+ const allowedEdge = isUserScrollEvent ? beginReachedEdgeUserScroll(ctx, newScroll - prevScroll) : void 0;
2246
2029
  const didResolvePendingNativeMVCPAdjust = resolvePendingNativeMVCPAdjust(ctx, newScroll);
2247
2030
  const scrollLength = state.scrollLength;
2248
2031
  const isLargeUserScrollJump = scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength && !state.pendingNativeMVCPAdjust;
@@ -2250,7 +2033,7 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2250
2033
  updateAdaptiveRender(ctx, scrollVelocity, { forceLight: isLargeUserScrollJump });
2251
2034
  const lastCalculated = state.scrollLastCalculate;
2252
2035
  const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
2253
- const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
2036
+ const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || allowedEdge !== void 0 || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
2254
2037
  if (shouldUpdate) {
2255
2038
  state.scrollLastCalculate = state.scroll;
2256
2039
  state.ignoreScrollFromMVCPIgnored = false;
@@ -2265,16 +2048,13 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2265
2048
  calculateItemsParams.drawDistanceMode = "visible-first";
2266
2049
  }
2267
2050
  (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, calculateItemsParams);
2268
- checkThresholds(ctx);
2051
+ checkThresholds(ctx, allowedEdge);
2269
2052
  };
2270
2053
  if (isLargeUserScrollJump) {
2271
2054
  state.mvcpAnchorLock = void 0;
2272
2055
  state.pendingNativeMVCPAdjust = void 0;
2273
2056
  state.userScrollAnchorReset = { keys: /* @__PURE__ */ new Set() };
2274
- if (state.queuedMVCPRecalculate !== void 0) {
2275
- cancelAnimationFrame(state.queuedMVCPRecalculate);
2276
- state.queuedMVCPRecalculate = void 0;
2277
- }
2057
+ state.scheduledWork.cancel("mvcpRecalculate");
2278
2058
  flushSync(runCalculateItems);
2279
2059
  scheduleFullDrawDistancePrewarm(ctx);
2280
2060
  } else {
@@ -2419,12 +2199,7 @@ function scrollTo(ctx, params) {
2419
2199
  const {
2420
2200
  props: { horizontal }
2421
2201
  } = state;
2422
- if (state.animFrameCheckFinishedScroll) {
2423
- cancelAnimationFrame(ctx.state.animFrameCheckFinishedScroll);
2424
- }
2425
- if (state.timeoutCheckFinishedScrollFallback) {
2426
- clearTimeout(ctx.state.timeoutCheckFinishedScrollFallback);
2427
- }
2202
+ cancelScrollCompletionChecks(state);
2428
2203
  const requestedOffset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, scrollTargetOffset, scrollTarget);
2429
2204
  const shouldPreserveRawInitialOffsetRequest = !!isInitialScroll && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
2430
2205
  const targetOffset = clampScrollOffset(ctx, requestedOffset, scrollTarget);
@@ -2529,10 +2304,7 @@ function setInitialScrollTarget(ctx, target, options) {
2529
2304
  var _a3;
2530
2305
  const { state } = ctx;
2531
2306
  state.clearPreservedInitialScrollOnNextFinish = void 0;
2532
- if (state.timeoutPreservedInitialScrollClear !== void 0) {
2533
- clearTimeout(state.timeoutPreservedInitialScrollClear);
2534
- state.timeoutPreservedInitialScrollClear = void 0;
2535
- }
2307
+ state.scheduledWork.cancel("preservedInitialScroll");
2536
2308
  state.initialScroll = target;
2537
2309
  if (options == null ? void 0 : options.resetDidFinish) {
2538
2310
  resetInitialRenderState(ctx, { resetInitialScroll: true });
@@ -2825,13 +2597,13 @@ function rearmBootstrapInitialScroll(ctx, options) {
2825
2597
  queueBootstrapInitialScrollReevaluation(ctx.state);
2826
2598
  }
2827
2599
  function createInitialScrollAtEndTarget(options) {
2828
- const { dataLength, footerSize, preserveForFooterLayout, stylePaddingBottom } = options;
2600
+ const { dataLength, footerSize, preserveForFooterLayout, stylePaddingEnd } = options;
2829
2601
  return {
2830
2602
  contentOffset: void 0,
2831
2603
  index: Math.max(0, dataLength - 1),
2832
2604
  preserveForBottomPadding: true,
2833
2605
  preserveForFooterLayout,
2834
- viewOffset: -stylePaddingBottom - footerSize,
2606
+ viewOffset: -stylePaddingEnd - footerSize,
2835
2607
  viewPosition: 1
2836
2608
  };
2837
2609
  }
@@ -2845,7 +2617,7 @@ function isRetargetableBottomAlignedInitialScrollTarget(target) {
2845
2617
  return !!(target && target.viewPosition === 1 && (shouldPreserveInitialScrollForBottomPadding(target) || shouldPreserveInitialScrollForFooterLayout(target)));
2846
2618
  }
2847
2619
  function createRetargetedBottomAlignedInitialScroll(options) {
2848
- const { dataLength, footerSize, initialScrollAtEnd, stylePaddingBottom, target } = options;
2620
+ const { dataLength, footerSize, initialScrollAtEnd, stylePaddingEnd, target } = options;
2849
2621
  const preserveForFooterLayout = shouldPreserveInitialScrollForFooterLayout(target);
2850
2622
  return {
2851
2623
  ...target,
@@ -2853,7 +2625,7 @@ function createRetargetedBottomAlignedInitialScroll(options) {
2853
2625
  index: initialScrollAtEnd ? Math.max(0, dataLength - 1) : target.index,
2854
2626
  preserveForBottomPadding: true,
2855
2627
  preserveForFooterLayout,
2856
- viewOffset: -stylePaddingBottom - (preserveForFooterLayout ? footerSize : 0),
2628
+ viewOffset: -stylePaddingEnd - (preserveForFooterLayout ? footerSize : 0),
2857
2629
  viewPosition: 1
2858
2630
  };
2859
2631
  }
@@ -2861,7 +2633,7 @@ function areEquivalentBootstrapInitialScrollTargets(current, next) {
2861
2633
  return current.index === next.index && current.preserveForBottomPadding === next.preserveForBottomPadding && current.preserveForFooterLayout === next.preserveForFooterLayout && current.viewOffset === next.viewOffset && current.viewPosition === next.viewPosition;
2862
2634
  }
2863
2635
  function clearPendingInitialScrollFooterLayout(ctx, options) {
2864
- const { dataLength, stylePaddingBottom, target } = options;
2636
+ const { dataLength, stylePaddingEnd, target } = options;
2865
2637
  if (!shouldPreserveInitialScrollForFooterLayout(target)) {
2866
2638
  return;
2867
2639
  }
@@ -2869,7 +2641,7 @@ function clearPendingInitialScrollFooterLayout(ctx, options) {
2869
2641
  dataLength,
2870
2642
  footerSize: 0,
2871
2643
  preserveForFooterLayout: void 0,
2872
- stylePaddingBottom
2644
+ stylePaddingEnd
2873
2645
  });
2874
2646
  setInitialScrollTarget(ctx, clearedFooterTarget);
2875
2647
  }
@@ -2930,7 +2702,7 @@ function schedulePreservedEndAnchorCorrectionFrame(ctx, correction) {
2930
2702
  });
2931
2703
  }
2932
2704
  function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {
2933
- var _a3, _b;
2705
+ var _a3;
2934
2706
  const state = ctx.state;
2935
2707
  const initialScroll = state.initialScroll;
2936
2708
  if (!state.didFinishInitialScroll || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) || (initialScroll == null ? void 0 : initialScroll.viewPosition) !== 1 || state.preservedEndAnchorCorrection) {
@@ -2942,7 +2714,7 @@ function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {
2942
2714
  if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {
2943
2715
  clearPendingInitialScrollFooterLayout(ctx, {
2944
2716
  dataLength: state.props.data.length,
2945
- stylePaddingBottom: (_b = state.props.stylePaddingBottom) != null ? _b : 0,
2717
+ stylePaddingEnd: getStylePaddingEnd(state.props),
2946
2718
  target: initialScroll
2947
2719
  });
2948
2720
  } else {
@@ -2979,7 +2751,7 @@ function startBootstrapInitialScrollOnMount(ctx, options) {
2979
2751
  }
2980
2752
  }
2981
2753
  function handleBootstrapInitialScrollDataChange(ctx, options) {
2982
- const { dataLength, didDataChange, initialScrollAtEnd, previousDataLength, stylePaddingBottom } = options;
2754
+ const { dataLength, didDataChange, initialScrollAtEnd, previousDataLength, stylePaddingEnd } = options;
2983
2755
  const state = ctx.state;
2984
2756
  const initialScroll = state.initialScroll;
2985
2757
  if (isOffsetInitialScrollSession(state) || !initialScroll) {
@@ -3001,18 +2773,18 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
3001
2773
  dataLength,
3002
2774
  footerSize: peek$(ctx, "footerSize") || 0,
3003
2775
  preserveForFooterLayout: shouldPreserveInitialScrollForFooterLayout(initialScroll),
3004
- stylePaddingBottom
2776
+ stylePaddingEnd
3005
2777
  }) : createRetargetedBottomAlignedInitialScroll({
3006
2778
  dataLength,
3007
2779
  footerSize: peek$(ctx, "footerSize") || 0,
3008
2780
  initialScrollAtEnd,
3009
- stylePaddingBottom,
2781
+ stylePaddingEnd,
3010
2782
  target: initialScroll
3011
2783
  });
3012
2784
  if (!shouldResetDidFinish && didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
3013
2785
  clearPendingInitialScrollFooterLayout(ctx, {
3014
2786
  dataLength,
3015
- stylePaddingBottom,
2787
+ stylePaddingEnd,
3016
2788
  target: initialScroll
3017
2789
  });
3018
2790
  return;
@@ -3044,7 +2816,7 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
3044
2816
  }
3045
2817
  }
3046
2818
  function handleBootstrapInitialScrollFooterLayout(ctx, options) {
3047
- const { dataLength, footerSize, initialScrollAtEnd, stylePaddingBottom } = options;
2819
+ const { dataLength, footerSize, initialScrollAtEnd, stylePaddingEnd } = options;
3048
2820
  const state = ctx.state;
3049
2821
  if (!initialScrollAtEnd) {
3050
2822
  return;
@@ -3060,7 +2832,7 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
3060
2832
  if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
3061
2833
  clearPendingInitialScrollFooterLayout(ctx, {
3062
2834
  dataLength,
3063
- stylePaddingBottom,
2835
+ stylePaddingEnd,
3064
2836
  target: initialScroll
3065
2837
  });
3066
2838
  } else {
@@ -3068,13 +2840,13 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
3068
2840
  dataLength,
3069
2841
  footerSize,
3070
2842
  preserveForFooterLayout: shouldPreserveInitialScrollForFooterLayout(initialScroll),
3071
- stylePaddingBottom
2843
+ stylePaddingEnd
3072
2844
  });
3073
2845
  const didTargetChange = initialScroll.index !== updatedInitialScroll.index || initialScroll.viewPosition !== updatedInitialScroll.viewPosition || initialScroll.viewOffset !== updatedInitialScroll.viewOffset;
3074
2846
  if (!didTargetChange) {
3075
2847
  clearPendingInitialScrollFooterLayout(ctx, {
3076
2848
  dataLength,
3077
- stylePaddingBottom,
2849
+ stylePaddingEnd,
3078
2850
  target: initialScroll
3079
2851
  });
3080
2852
  } else {
@@ -3301,7 +3073,7 @@ function handleInitialScrollDataChange(ctx, options) {
3301
3073
  initialScrollAtEnd,
3302
3074
  latestInitialScroll,
3303
3075
  latestInitialScrollSessionKind,
3304
- stylePaddingBottom,
3076
+ stylePaddingEnd,
3305
3077
  useBootstrapInitialScroll
3306
3078
  } = options;
3307
3079
  const state = ctx.state;
@@ -3331,7 +3103,7 @@ function handleInitialScrollDataChange(ctx, options) {
3331
3103
  didDataChange,
3332
3104
  initialScrollAtEnd,
3333
3105
  previousDataLength: previousInitialScrollDataLength,
3334
- stylePaddingBottom
3106
+ stylePaddingEnd
3335
3107
  });
3336
3108
  return;
3337
3109
  }
@@ -3354,6 +3126,52 @@ function resetLayoutCachesForDataChange(state) {
3354
3126
  state.columnSpans.length = 0;
3355
3127
  }
3356
3128
 
3129
+ // src/core/scheduleContainerLayout.ts
3130
+ function getContainerLayoutEffectScope(ctx) {
3131
+ var _a3;
3132
+ const scheduledIds = ctx.pendingContainerIds;
3133
+ ctx.pendingContainerIds = void 0;
3134
+ if (scheduledIds === void 0) {
3135
+ return void 0;
3136
+ }
3137
+ const state = ctx.state;
3138
+ let targetContainerIds = scheduledIds;
3139
+ if (targetContainerIds && ((_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys.size)) {
3140
+ targetContainerIds = new Set(targetContainerIds);
3141
+ for (const itemKey of state.userScrollAnchorReset.keys) {
3142
+ const containerId = state.containerItemKeys.get(itemKey);
3143
+ if (containerId !== void 0) {
3144
+ targetContainerIds.add(containerId);
3145
+ }
3146
+ }
3147
+ }
3148
+ return targetContainerIds;
3149
+ }
3150
+ function scheduleContainerLayout(ctx, target) {
3151
+ var _a3;
3152
+ const isAlreadyScheduled = ctx.pendingContainerIds !== void 0;
3153
+ const previousIds = ctx.pendingContainerIds;
3154
+ if (target === void 0) {
3155
+ ctx.pendingContainerIds = null;
3156
+ } else if (previousIds !== null) {
3157
+ let nextIds = previousIds;
3158
+ if (!nextIds) {
3159
+ nextIds = typeof target === "number" ? /* @__PURE__ */ new Set([target]) : new Set(target);
3160
+ } else if (typeof target === "number") {
3161
+ nextIds.add(target);
3162
+ } else {
3163
+ for (const containerId of target) {
3164
+ nextIds.add(containerId);
3165
+ }
3166
+ }
3167
+ ctx.pendingContainerIds = nextIds;
3168
+ }
3169
+ if (!isAlreadyScheduled) {
3170
+ const nextEpoch = ((_a3 = peek$(ctx, "containerLayoutEpoch")) != null ? _a3 : 0) + 1;
3171
+ set$(ctx, "containerLayoutEpoch", nextEpoch);
3172
+ }
3173
+ }
3174
+
3357
3175
  // src/core/syncMountedContainer.ts
3358
3176
  function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3359
3177
  var _a3, _b, _c, _d, _e, _f, _g, _h, _i;
@@ -3397,14 +3215,13 @@ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3397
3215
  set$(ctx, `containerSpan${containerIndex}`, span);
3398
3216
  }
3399
3217
  }
3400
- const prevItemInfo = peek$(ctx, `containerItemInfo${containerIndex}`);
3218
+ const prevIndex = peek$(ctx, `containerItemIndex${containerIndex}`);
3219
+ if (prevIndex !== itemIndex) {
3220
+ set$(ctx, `containerItemIndex${containerIndex}`, itemIndex);
3221
+ }
3401
3222
  const prevData = peek$(ctx, `containerItemData${containerIndex}`);
3402
- let itemInfoValue = prevData;
3403
- let didChangeItemInfo = (prevItemInfo == null ? void 0 : prevItemInfo.itemKey) !== itemKey || (prevItemInfo == null ? void 0 : prevItemInfo.index) !== itemIndex || (prevItemInfo == null ? void 0 : prevItemInfo.value) !== prevData;
3404
3223
  const updateData = () => {
3405
3224
  set$(ctx, `containerItemData${containerIndex}`, item);
3406
- itemInfoValue = item;
3407
- didChangeItemInfo = true;
3408
3225
  didRefreshData = true;
3409
3226
  };
3410
3227
  if (prevData !== item) {
@@ -3439,13 +3256,6 @@ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3439
3256
  }
3440
3257
  }
3441
3258
  }
3442
- if (didChangeItemInfo) {
3443
- set$(ctx, `containerItemInfo${containerIndex}`, {
3444
- index: itemIndex,
3445
- itemKey,
3446
- value: itemInfoValue
3447
- });
3448
- }
3449
3259
  return { didChangePosition, didRefreshData };
3450
3260
  }
3451
3261
 
@@ -3763,7 +3573,6 @@ function setupViewability(props) {
3763
3573
  }
3764
3574
  function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollSize, start, end, startBuffered = start, endBuffered = end) {
3765
3575
  const {
3766
- timeouts,
3767
3576
  props: { data }
3768
3577
  } = state;
3769
3578
  for (const viewabilityConfigCallbackPair of viewabilityConfigCallbackPairs) {
@@ -3773,11 +3582,10 @@ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollS
3773
3582
  viewabilityState.startBuffered = startBuffered;
3774
3583
  viewabilityState.endBuffered = endBuffered;
3775
3584
  if (viewabilityConfigCallbackPair.viewabilityConfig.minimumViewTime) {
3776
- const timer = setTimeout(() => {
3777
- timeouts.delete(timer);
3778
- updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, state, ctx, scrollSize);
3779
- }, viewabilityConfigCallbackPair.viewabilityConfig.minimumViewTime);
3780
- timeouts.add(timer);
3585
+ state.scheduledWork.timeout(
3586
+ () => updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, state, ctx, scrollSize),
3587
+ viewabilityConfigCallbackPair.viewabilityConfig.minimumViewTime
3588
+ );
3781
3589
  } else {
3782
3590
  updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, state, ctx, scrollSize);
3783
3591
  }
@@ -3810,19 +3618,29 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
3810
3618
  if (previousViewableItems) {
3811
3619
  for (const viewToken of previousViewableItems) {
3812
3620
  previousViewableKeys.add(viewToken.key);
3621
+ const currentIndex = state.indexByKey.get(viewToken.key);
3622
+ const currentItem = currentIndex !== void 0 ? data[currentIndex] : void 0;
3813
3623
  const containerId = findContainerId(ctx, viewToken.key);
3814
- if (!checkIsViewable(
3815
- state,
3816
- ctx,
3817
- viewabilityConfig,
3818
- containerId,
3819
- viewToken.key,
3820
- scrollSize,
3821
- viewToken.item,
3822
- viewToken.index
3823
- )) {
3824
- viewToken.isViewable = false;
3825
- changed.push(viewToken);
3624
+ let isStillViewable = false;
3625
+ if (currentIndex !== void 0 && currentItem !== void 0) {
3626
+ isStillViewable = checkIsViewable(
3627
+ state,
3628
+ ctx,
3629
+ viewabilityConfig,
3630
+ containerId,
3631
+ viewToken.key,
3632
+ scrollSize,
3633
+ currentItem,
3634
+ currentIndex
3635
+ );
3636
+ }
3637
+ if (!isStillViewable) {
3638
+ changed.push({
3639
+ ...viewToken,
3640
+ index: currentIndex != null ? currentIndex : viewToken.index,
3641
+ isViewable: false,
3642
+ item: currentItem != null ? currentItem : viewToken.item
3643
+ });
3826
3644
  }
3827
3645
  }
3828
3646
  }
@@ -3979,128 +3797,102 @@ function getExpandedContainerPoolSize(dataLength, numContainers) {
3979
3797
 
3980
3798
  // src/utils/findAvailableContainers.ts
3981
3799
  function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffered, pendingRemoval, getRequiredItemType, protectedKeys) {
3800
+ var _a3;
3982
3801
  const numNeeded = needNewContainers.length;
3983
3802
  if (numNeeded === 0) {
3984
3803
  return [];
3985
3804
  }
3986
3805
  const numContainers = peek$(ctx, "numContainers");
3987
3806
  const state = ctx.state;
3988
- const { stickyContainerPool, containerItemTypes } = state;
3807
+ const { containerItemMetadata, stickyContainerPool } = state;
3989
3808
  const shouldAvoidAssignedContainerReuse = state.props.recycleItems && !!state.props.positionComponentInternal;
3990
- const allocations = [];
3991
3809
  const pendingRemovalSet = pendingRemoval.length > 0 ? new Set(pendingRemoval) : void 0;
3992
- let pendingRemovalChanged = false;
3810
+ const requests = needNewContainers.map((itemIndex, order) => ({
3811
+ isSticky: state.props.stickyHeaderIndicesSet.has(itemIndex),
3812
+ itemIndex,
3813
+ itemType: getRequiredItemType == null ? void 0 : getRequiredItemType(itemIndex),
3814
+ order
3815
+ }));
3816
+ const normalRequests = requests.filter((request) => !request.isSticky);
3817
+ const stickyRequests = requests.filter((request) => request.isSticky);
3818
+ const normalCandidates = [];
3819
+ const stickyCandidates = [];
3820
+ for (let containerIndex = 0; containerIndex < numContainers; containerIndex++) {
3821
+ const key = peek$(ctx, `containerItemKey${containerIndex}`);
3822
+ const isPendingRemoval = !!(pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(containerIndex));
3823
+ const isProtected = !!key && !!(protectedKeys == null ? void 0 : protectedKeys.has(key)) && state.indexByKey.has(key);
3824
+ if (isProtected) {
3825
+ continue;
3826
+ }
3827
+ if (stickyContainerPool.has(containerIndex)) {
3828
+ if (key === void 0 || isPendingRemoval) {
3829
+ stickyCandidates.push({ containerIndex, distance: Number.POSITIVE_INFINITY });
3830
+ }
3831
+ } else if (key === void 0 || isPendingRemoval) {
3832
+ normalCandidates.push({ containerIndex, distance: Number.POSITIVE_INFINITY });
3833
+ } else if (!shouldAvoidAssignedContainerReuse) {
3834
+ const index = state.indexByKey.get(key);
3835
+ if (index !== void 0 && (index < startBuffered || index > endBuffered)) {
3836
+ const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
3837
+ normalCandidates.push({ containerIndex, distance });
3838
+ }
3839
+ }
3840
+ }
3841
+ normalCandidates.sort(comparatorByDistance);
3842
+ const allocations = new Array(numNeeded);
3993
3843
  let nextNewContainerIndex = numContainers;
3994
- const usedContainers = /* @__PURE__ */ new Set();
3995
- let availableContainers;
3996
- const stickyHeaderIndicesSet = state.props.stickyHeaderIndicesSet;
3997
- const canReuseContainer = (containerIndex, requiredType) => {
3998
- if (!requiredType) return true;
3999
- const existingType = containerItemTypes.get(containerIndex);
4000
- if (!existingType) return true;
4001
- return existingType === requiredType;
4002
- };
4003
- const pushAllocation = (itemIndex, itemType, containerIndex) => {
4004
- allocations.push({
3844
+ let pendingRemovalChanged = false;
3845
+ const assign = (request, containerIndex) => {
3846
+ allocations[request.order] = {
4005
3847
  containerIndex,
4006
- itemIndex,
4007
- itemType
4008
- });
4009
- usedContainers.add(containerIndex);
3848
+ itemIndex: request.itemIndex,
3849
+ itemType: request.itemType
3850
+ };
4010
3851
  if (pendingRemovalSet == null ? void 0 : pendingRemovalSet.delete(containerIndex)) {
4011
3852
  pendingRemovalChanged = true;
4012
3853
  }
4013
3854
  };
4014
- const pushNewContainer = (itemIndex, itemType, isSticky) => {
4015
- const newContainerIndex = nextNewContainerIndex++;
4016
- pushAllocation(itemIndex, itemType, newContainerIndex);
4017
- if (isSticky) {
4018
- stickyContainerPool.add(newContainerIndex);
4019
- }
4020
- return newContainerIndex;
4021
- };
4022
- const canUseContainer = (containerIndex, itemType) => {
4023
- if (usedContainers.has(containerIndex) || stickyContainerPool.has(containerIndex)) {
4024
- return false;
4025
- }
4026
- const key = peek$(ctx, `containerItemKey${containerIndex}`);
4027
- const isPending = !!(pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(containerIndex));
4028
- return (key === void 0 || isPending) && canReuseContainer(containerIndex, itemType);
4029
- };
4030
- const findStickyContainer = (itemType) => {
4031
- let foundContainer;
4032
- for (const containerIndex of stickyContainerPool) {
4033
- if (!usedContainers.has(containerIndex)) {
4034
- const key = peek$(ctx, `containerItemKey${containerIndex}`);
4035
- const isPendingRemoval = !!(pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(containerIndex));
4036
- if ((key === void 0 || isPendingRemoval) && canReuseContainer(containerIndex, itemType)) {
4037
- foundContainer = containerIndex;
4038
- break;
4039
- }
4040
- }
4041
- }
4042
- return foundContainer;
4043
- };
4044
- const findUnassignedOrPendingContainer = (itemType) => {
4045
- let foundContainer;
4046
- for (let containerIndex = 0; containerIndex < numContainers && foundContainer === void 0; containerIndex++) {
4047
- if (canUseContainer(containerIndex, itemType)) {
4048
- foundContainer = containerIndex;
3855
+ const assignMatching = (pendingRequests, candidates, matches) => {
3856
+ for (const request of pendingRequests) {
3857
+ if (allocations[request.order]) {
3858
+ continue;
4049
3859
  }
4050
- }
4051
- return foundContainer;
4052
- };
4053
- const getAvailableContainers = () => {
4054
- if (!availableContainers) {
4055
- availableContainers = [];
4056
- if (!shouldAvoidAssignedContainerReuse) {
4057
- for (let containerIndex = 0; containerIndex < numContainers; containerIndex++) {
4058
- if (usedContainers.has(containerIndex) || stickyContainerPool.has(containerIndex)) {
4059
- continue;
4060
- }
4061
- const key = peek$(ctx, `containerItemKey${containerIndex}`);
4062
- if (key === void 0) continue;
4063
- if ((protectedKeys == null ? void 0 : protectedKeys.has(key)) && state.indexByKey.has(key)) continue;
4064
- const index = state.indexByKey.get(key);
4065
- const isOutOfView = index < startBuffered || index > endBuffered;
4066
- if (isOutOfView) {
4067
- const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
4068
- availableContainers.push({ distance, index: containerIndex });
4069
- }
3860
+ const candidateIndex = candidates.findIndex(
3861
+ (candidate) => {
3862
+ var _a4;
3863
+ return matches((_a4 = containerItemMetadata.get(candidate.containerIndex)) == null ? void 0 : _a4.itemType, request.itemType);
4070
3864
  }
4071
- availableContainers.sort(comparatorByDistance);
3865
+ );
3866
+ if (candidateIndex !== -1) {
3867
+ const [candidate] = candidates.splice(candidateIndex, 1);
3868
+ assign(request, candidate.containerIndex);
4072
3869
  }
4073
3870
  }
4074
- return availableContainers;
4075
3871
  };
4076
- const findAvailableContainer = (itemType) => {
4077
- const containers = getAvailableContainers();
4078
- let matchIndex = -1;
4079
- for (let i = 0; i < containers.length && matchIndex === -1; i++) {
4080
- const containerIndex = containers[i].index;
4081
- if (!usedContainers.has(containerIndex) && canReuseContainer(containerIndex, itemType)) {
4082
- matchIndex = i;
4083
- }
3872
+ const assignFromPool = (pendingRequests, candidates, allowCrossType) => {
3873
+ if (getRequiredItemType) {
3874
+ assignMatching(
3875
+ pendingRequests,
3876
+ candidates,
3877
+ (containerType, requestType) => requestType !== void 0 && containerType === requestType
3878
+ );
3879
+ }
3880
+ assignMatching(pendingRequests, candidates, (containerType) => containerType === void 0);
3881
+ if (allowCrossType) {
3882
+ assignMatching(pendingRequests, candidates, () => true);
4084
3883
  }
4085
- return matchIndex === -1 ? void 0 : containers.splice(matchIndex, 1)[0].index;
4086
3884
  };
4087
- for (const itemIndex of needNewContainers) {
4088
- const itemType = getRequiredItemType == null ? void 0 : getRequiredItemType(itemIndex);
4089
- const isSticky = stickyHeaderIndicesSet.has(itemIndex);
4090
- let containerIndex;
4091
- if (isSticky) {
4092
- containerIndex = findStickyContainer(itemType);
4093
- } else {
4094
- containerIndex = findUnassignedOrPendingContainer(itemType);
4095
- if (containerIndex === void 0) {
4096
- containerIndex = findAvailableContainer(itemType);
4097
- }
3885
+ assignFromPool(normalRequests, normalCandidates, true);
3886
+ assignFromPool(stickyRequests, stickyCandidates, false);
3887
+ for (const request of requests) {
3888
+ if (allocations[request.order]) {
3889
+ continue;
4098
3890
  }
4099
- if (containerIndex !== void 0) {
4100
- pushAllocation(itemIndex, itemType, containerIndex);
4101
- } else {
4102
- pushNewContainer(itemIndex, itemType, isSticky);
3891
+ const containerIndex = nextNewContainerIndex++;
3892
+ if (request.isSticky) {
3893
+ stickyContainerPool.add(containerIndex);
4103
3894
  }
3895
+ assign(request, containerIndex);
4104
3896
  }
4105
3897
  if (pendingRemovalChanged) {
4106
3898
  pendingRemoval.length = 0;
@@ -4110,18 +3902,21 @@ function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffe
4110
3902
  }
4111
3903
  }
4112
3904
  }
4113
- if (IS_DEV && nextNewContainerIndex > peek$(ctx, "numContainersPooled")) {
4114
- console.warn(
4115
- "[legend-list] No unused container available, so creating one on demand. This can be a minor performance issue and is likely caused by the estimatedItemSize being too large. Consider decreasing estimatedItemSize.",
4116
- {
4117
- debugInfo: {
4118
- numContainers,
4119
- numContainersPooled: peek$(ctx, "numContainersPooled"),
4120
- numNeeded,
4121
- stillNeeded: nextNewContainerIndex - numContainers
3905
+ if (IS_DEV) {
3906
+ const numContainersPooled = (_a3 = peek$(ctx, "numContainersPooled")) != null ? _a3 : Number.POSITIVE_INFINITY;
3907
+ if (nextNewContainerIndex > numContainersPooled) {
3908
+ console.warn(
3909
+ "[legend-list] No unused container available, so creating one on demand. This can be a minor performance issue and is likely caused by the estimatedItemSize being too large. Consider decreasing estimatedItemSize.",
3910
+ {
3911
+ debugInfo: {
3912
+ numContainers,
3913
+ numContainersPooled,
3914
+ numNeeded,
3915
+ stillNeeded: nextNewContainerIndex - numContainers
3916
+ }
4122
3917
  }
4123
- }
4124
- );
3918
+ );
3919
+ }
4125
3920
  }
4126
3921
  return allocations;
4127
3922
  }
@@ -4149,20 +3944,15 @@ function getProjectedBufferAdjustment(scrollVelocity, trailingBuffer) {
4149
3944
  }
4150
3945
  function scheduleRenderRangeProjectionSettle(ctx) {
4151
3946
  const state = ctx.state;
4152
- const previousTimeout = state.timeoutRenderRangeProjectionSettle;
4153
- if (previousTimeout !== void 0) {
4154
- clearTimeout(previousTimeout);
4155
- state.timeouts.delete(previousTimeout);
4156
- }
4157
- const timeout = setTimeout(() => {
4158
- var _a3;
4159
- state.timeoutRenderRangeProjectionSettle = void 0;
4160
- state.timeouts.delete(timeout);
4161
- state.scrollHistory.length = 0;
4162
- (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
4163
- }, RENDER_RANGE_PROJECTION_SETTLE_DELAY);
4164
- state.timeoutRenderRangeProjectionSettle = timeout;
4165
- state.timeouts.add(timeout);
3947
+ state.scheduledWork.timeout(
3948
+ () => {
3949
+ var _a3;
3950
+ state.scrollHistory.length = 0;
3951
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
3952
+ },
3953
+ RENDER_RANGE_PROJECTION_SETTLE_DELAY,
3954
+ "renderRangeProjection"
3955
+ );
4166
3956
  }
4167
3957
  function findCurrentStickyIndex(stickyArray, scroll, state) {
4168
3958
  const positions = state.positions;
@@ -4356,7 +4146,7 @@ function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, sc
4356
4146
  function calculateItemsInView(ctx, params = {}) {
4357
4147
  const state = ctx.state;
4358
4148
  batchedUpdates(() => {
4359
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t;
4149
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u;
4360
4150
  const {
4361
4151
  columns,
4362
4152
  containerItemKeys,
@@ -4384,6 +4174,7 @@ function calculateItemsInView(ctx, params = {}) {
4384
4174
  return;
4385
4175
  }
4386
4176
  let totalSize = getContentSize(ctx);
4177
+ let changedContainerIds;
4387
4178
  const topPad = peek$(ctx, "stylePaddingTop") + peek$(ctx, "alignItemsAtEndPadding") + peek$(ctx, "headerSize");
4388
4179
  const numColumns = peek$(ctx, "numColumns");
4389
4180
  const speed = (_b = params.scrollVelocity) != null ? _b : getScrollVelocity(state);
@@ -4509,7 +4300,7 @@ function calculateItemsInView(ctx, params = {}) {
4509
4300
  const scrollAdjustPendingBeforeMVCP = (_f = peek$(ctx, "scrollAdjustPending")) != null ? _f : 0;
4510
4301
  checkMVCP == null ? void 0 : checkMVCP();
4511
4302
  const didMVCPAdjustScroll = !!checkMVCP && (state.scroll !== scrollBeforeMVCP || ((_g = peek$(ctx, "scrollAdjustPending")) != null ? _g : 0) !== scrollAdjustPendingBeforeMVCP);
4512
- if (didMVCPAdjustScroll && (initialScroll || state.scrollingTo)) {
4303
+ if (didMVCPAdjustScroll) {
4513
4304
  updateScroll2(state.scroll);
4514
4305
  updateScrollRange();
4515
4306
  }
@@ -4689,22 +4480,20 @@ function calculateItemsInView(ctx, params = {}) {
4689
4480
  if (oldKey && oldKey !== id) {
4690
4481
  containerItemKeys.delete(oldKey);
4691
4482
  }
4483
+ if (oldKey !== id) {
4484
+ changedContainerIds != null ? changedContainerIds : changedContainerIds = /* @__PURE__ */ new Set();
4485
+ changedContainerIds.add(containerIndex);
4486
+ state.containerItemGenerations[containerIndex] = ((_p = state.containerItemGenerations[containerIndex]) != null ? _p : 0) + 1;
4487
+ }
4692
4488
  set$(ctx, `containerItemKey${containerIndex}`, id);
4489
+ set$(ctx, `containerItemIndex${containerIndex}`, i);
4693
4490
  set$(ctx, `containerItemData${containerIndex}`, data[i]);
4694
- set$(ctx, `containerItemInfo${containerIndex}`, {
4695
- index: i,
4696
- itemKey: id,
4697
- value: data[i]
4698
- });
4699
- if (allocation.itemType !== void 0) {
4700
- state.containerItemTypes.set(containerIndex, allocation.itemType);
4701
- }
4491
+ state.containerItemMetadata.set(
4492
+ containerIndex,
4493
+ createContainerItemMetadata(state, i, data[i], allocation.itemType)
4494
+ );
4702
4495
  containerItemKeys.set(id, containerIndex);
4703
- (_p = state.userScrollAnchorReset) == null ? void 0 : _p.keys.add(id);
4704
- if (IsNewArchitecture) {
4705
- (_q = state.pendingLayoutEffectMeasurements) != null ? _q : state.pendingLayoutEffectMeasurements = /* @__PURE__ */ new Set();
4706
- state.pendingLayoutEffectMeasurements.add(id);
4707
- }
4496
+ (_q = state.userScrollAnchorReset) == null ? void 0 : _q.keys.add(id);
4708
4497
  const containerSticky = `containerSticky${containerIndex}`;
4709
4498
  const isSticky = stickyHeaderIndicesSet.has(i);
4710
4499
  const isPinnedRender = isPinnedRenderIndex(i);
@@ -4754,15 +4543,18 @@ function calculateItemsInView(ctx, params = {}) {
4754
4543
  if (pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(i)) {
4755
4544
  if (itemKey !== void 0) {
4756
4545
  containerItemKeys.delete(itemKey);
4546
+ changedContainerIds != null ? changedContainerIds : changedContainerIds = /* @__PURE__ */ new Set();
4547
+ changedContainerIds.add(i);
4548
+ state.containerItemGenerations[i] = ((_t = state.containerItemGenerations[i]) != null ? _t : 0) + 1;
4757
4549
  }
4758
- state.containerItemTypes.delete(i);
4550
+ state.containerItemMetadata.delete(i);
4759
4551
  if (state.stickyContainerPool.has(i)) {
4760
4552
  set$(ctx, `containerSticky${i}`, false);
4761
4553
  state.stickyContainerPool.delete(i);
4762
4554
  }
4763
4555
  set$(ctx, `containerItemKey${i}`, void 0);
4556
+ set$(ctx, `containerItemIndex${i}`, void 0);
4764
4557
  set$(ctx, `containerItemData${i}`, void 0);
4765
- set$(ctx, `containerItemInfo${i}`, void 0);
4766
4558
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
4767
4559
  set$(ctx, `containerColumn${i}`, -1);
4768
4560
  set$(ctx, `containerSpan${i}`, 1);
@@ -4776,6 +4568,9 @@ function calculateItemsInView(ctx, params = {}) {
4776
4568
  }
4777
4569
  }
4778
4570
  }
4571
+ if (changedContainerIds && (IsNewArchitecture || Platform.OS === "web")) {
4572
+ scheduleContainerLayout(ctx, changedContainerIds);
4573
+ }
4779
4574
  if (Platform.OS === "web" && didChangePositions) {
4780
4575
  set$(ctx, "lastPositionUpdate", Date.now());
4781
4576
  }
@@ -4792,20 +4587,18 @@ function calculateItemsInView(ctx, params = {}) {
4792
4587
  }
4793
4588
  }
4794
4589
  if (viewabilityConfigCallbackPairs && visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null) {
4795
- if (!didMVCPAdjustScroll) {
4796
- updateViewableItems(
4797
- ctx.state,
4798
- ctx,
4799
- viewabilityConfigCallbackPairs,
4800
- scrollLength,
4801
- visibleRange.startNoBuffer,
4802
- visibleRange.endNoBuffer,
4803
- startBuffered != null ? startBuffered : visibleRange.startNoBuffer,
4804
- endBuffered != null ? endBuffered : visibleRange.endNoBuffer
4805
- );
4806
- }
4590
+ updateViewableItems(
4591
+ ctx.state,
4592
+ ctx,
4593
+ viewabilityConfigCallbackPairs,
4594
+ scrollLength,
4595
+ visibleRange.startNoBuffer,
4596
+ visibleRange.endNoBuffer,
4597
+ startBuffered != null ? startBuffered : visibleRange.startNoBuffer,
4598
+ endBuffered != null ? endBuffered : visibleRange.endNoBuffer
4599
+ );
4807
4600
  }
4808
- (_t = stickyState == null ? void 0 : stickyState.finishCalculateItemsInView) == null ? void 0 : _t.call(stickyState);
4601
+ (_u = stickyState == null ? void 0 : stickyState.finishCalculateItemsInView) == null ? void 0 : _u.call(stickyState);
4809
4602
  });
4810
4603
  }
4811
4604
 
@@ -4827,8 +4620,6 @@ function maybeUpdateAnchoredEndSpace(ctx) {
4827
4620
  if (anchorIndex >= 0 && anchorIndex < data.length && state.scrollLength > 0) {
4828
4621
  nextAnchorKey = getId(state, anchorIndex);
4829
4622
  let contentBelowAnchor = 0;
4830
- const footerSize = ctx.values.get("footerSize") || 0;
4831
- const stylePaddingBottom = state.props.stylePaddingBottom || 0;
4832
4623
  let hasUnknownTailSize = false;
4833
4624
  for (let index = anchorIndex; index < data.length; index++) {
4834
4625
  const size = getKnownOrFixedItemSize(ctx, index);
@@ -4840,14 +4631,16 @@ function maybeUpdateAnchoredEndSpace(ctx) {
4840
4631
  contentBelowAnchor += effectiveSize;
4841
4632
  }
4842
4633
  }
4843
- contentBelowAnchor += footerSize + stylePaddingBottom;
4634
+ contentBelowAnchor = Math.max(0, contentBelowAnchor - ctx.scrollAxisGap);
4635
+ contentBelowAnchor += (ctx.values.get("footerSize") || 0) + getStylePaddingEnd(state.props);
4844
4636
  isReady = !hasUnknownTailSize;
4845
4637
  nextSize = hasUnknownTailSize ? previousSize || 0 : Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
4846
4638
  } else if (anchorIndex >= 0) {
4847
4639
  isReady = false;
4848
4640
  }
4849
4641
  }
4850
- const didSizeChange = previousSize !== nextSize;
4642
+ const didSizeChange = previousSize !== nextSize && (previousSize !== void 0 || anchoredEndSpace !== void 0);
4643
+ const didEffectiveSizeChange = (previousSize || 0) !== nextSize;
4851
4644
  const didReadyAnchorChange = previousReadyAnchorIndex !== nextAnchorIndex || previousReadyAnchorKey !== nextAnchorKey;
4852
4645
  if (isReady && (didSizeChange || didReadyAnchorChange)) {
4853
4646
  state.anchoredEndSpaceReadyAnchorIndex = nextAnchorIndex;
@@ -4856,8 +4649,9 @@ function maybeUpdateAnchoredEndSpace(ctx) {
4856
4649
  set$(ctx, "anchoredEndSpaceSize", nextSize);
4857
4650
  (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
4858
4651
  }
4859
- if (didSizeChange && (anchoredEndSpace == null ? void 0 : anchoredEndSpace.includeInEndInset)) {
4860
- updateScroll(ctx, state.scroll, true);
4652
+ if (didEffectiveSizeChange) {
4653
+ updateContentMetricsState(ctx);
4654
+ updateScroll(ctx, state.scroll, true, { markHasScrolled: false });
4861
4655
  }
4862
4656
  (_b = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onReady) == null ? void 0 : _b.call(anchoredEndSpace, { anchorIndex: nextAnchorIndex, anchorKey: nextAnchorKey, size: nextSize });
4863
4657
  }
@@ -4886,16 +4680,10 @@ function runOrScheduleMVCPRecalculate(ctx) {
4886
4680
  }
4887
4681
  } else if (Platform.OS === "web") {
4888
4682
  if (!state.mvcpAnchorLock) {
4889
- if (state.queuedMVCPRecalculate !== void 0) {
4890
- cancelAnimationFrame(state.queuedMVCPRecalculate);
4891
- state.queuedMVCPRecalculate = void 0;
4892
- }
4683
+ state.scheduledWork.cancel("mvcpRecalculate");
4893
4684
  calculateItemsInView(ctx, { doMVCP: true });
4894
- } else if (state.queuedMVCPRecalculate === void 0) {
4895
- state.queuedMVCPRecalculate = requestAnimationFrame(() => {
4896
- state.queuedMVCPRecalculate = void 0;
4897
- calculateItemsInView(ctx, { doMVCP: true });
4898
- });
4685
+ } else if (!state.scheduledWork.has("mvcpRecalculate")) {
4686
+ state.scheduledWork.frame(() => calculateItemsInView(ctx, { doMVCP: true }), "mvcpRecalculate");
4899
4687
  }
4900
4688
  } else {
4901
4689
  calculateItemsInView(ctx, { doMVCP: true });
@@ -4911,42 +4699,28 @@ function updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal) {
4911
4699
  }
4912
4700
  }
4913
4701
  }
4702
+ var activeItemSizeBatches;
4703
+ function batchItemSizeUpdates(runUpdates) {
4704
+ const isOuterBatch = activeItemSizeBatches === void 0;
4705
+ activeItemSizeBatches != null ? activeItemSizeBatches : activeItemSizeBatches = /* @__PURE__ */ new Map();
4706
+ try {
4707
+ runUpdates();
4708
+ } finally {
4709
+ if (isOuterBatch) {
4710
+ const batches = activeItemSizeBatches;
4711
+ activeItemSizeBatches = void 0;
4712
+ for (const [ctx, measurements] of batches) {
4713
+ updateItemSizesBatch(ctx, measurements);
4714
+ }
4715
+ }
4716
+ }
4717
+ }
4914
4718
  function mergeItemSizeUpdateResult(result, next) {
4915
4719
  result.didChange || (result.didChange = next.didChange);
4916
4720
  result.didMeasureUserScrollAnchorResetItem || (result.didMeasureUserScrollAnchorResetItem = next.didMeasureUserScrollAnchorResetItem);
4917
4721
  result.needsRecalculate || (result.needsRecalculate = next.needsRecalculate);
4918
4722
  result.shouldMaintainScrollAtEnd || (result.shouldMaintainScrollAtEnd = next.shouldMaintainScrollAtEnd);
4919
4723
  }
4920
- var batchedItemSizeRecalculates = /* @__PURE__ */ new WeakMap();
4921
- function flushBatchedItemSizeRecalculate(ctx, didFallback = false, expectedResult) {
4922
- const result = batchedItemSizeRecalculates.get(ctx);
4923
- if (!result || expectedResult && result !== expectedResult) {
4924
- return;
4925
- }
4926
- batchedItemSizeRecalculates.delete(ctx);
4927
- if (didFallback) {
4928
- ctx.state.pendingLayoutEffectMeasurements = void 0;
4929
- }
4930
- flushItemSizeUpdates(ctx, result);
4931
- }
4932
- function queueItemSizeRecalculate(ctx, result) {
4933
- var _a3, _b;
4934
- const batch = batchedItemSizeRecalculates.get(ctx);
4935
- if (batch) {
4936
- mergeItemSizeUpdateResult(batch, result);
4937
- } else {
4938
- const nextBatch = { ...result };
4939
- batchedItemSizeRecalculates.set(ctx, nextBatch);
4940
- if ((_a3 = ctx.state.pendingLayoutEffectMeasurements) == null ? void 0 : _a3.size) {
4941
- requestAnimationFrame(() => {
4942
- flushBatchedItemSizeRecalculate(ctx, true, nextBatch);
4943
- });
4944
- }
4945
- }
4946
- if (!((_b = ctx.state.pendingLayoutEffectMeasurements) == null ? void 0 : _b.size)) {
4947
- flushBatchedItemSizeRecalculate(ctx);
4948
- }
4949
- }
4950
4724
  function flushItemSizeUpdates(ctx, result) {
4951
4725
  var _a3;
4952
4726
  const state = ctx.state;
@@ -4956,59 +4730,44 @@ function flushItemSizeUpdates(ctx, result) {
4956
4730
  } else if (result.didMeasureUserScrollAnchorResetItem && ((_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys.size) === 0) {
4957
4731
  state.userScrollAnchorReset = void 0;
4958
4732
  }
4733
+ if (result.didChange) {
4734
+ maybeUpdateAnchoredEndSpace(ctx);
4735
+ }
4959
4736
  if (result.didChange && result.shouldMaintainScrollAtEnd) {
4960
4737
  doMaintainScrollAtEnd(ctx);
4961
4738
  }
4962
4739
  }
4963
4740
  function updateItemSizes(ctx, measurement) {
4964
- var _a3, _b, _c, _d;
4965
- const state = ctx.state;
4966
- let didDrainLayoutEffectMeasurements = false;
4967
- if (IsNewArchitecture && measurement.fromLayoutEffect) {
4968
- const pendingLayoutEffectMeasurements = state.pendingLayoutEffectMeasurements;
4969
- if ((pendingLayoutEffectMeasurements == null ? void 0 : pendingLayoutEffectMeasurements.delete(measurement.itemKey)) && pendingLayoutEffectMeasurements.size === 0) {
4970
- state.pendingLayoutEffectMeasurements = void 0;
4971
- didDrainLayoutEffectMeasurements = true;
4972
- }
4973
- }
4974
- const pendingKeys = (_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys;
4975
- const shouldBatchPendingMeasurements = IsNewArchitecture && measurement.fromLayoutEffect && !!(pendingKeys == null ? void 0 : pendingKeys.size);
4976
- const shouldQueueRecalculate = IsNewArchitecture && !!measurement.fromLayoutEffect;
4977
- let result;
4978
- if (!shouldBatchPendingMeasurements) {
4979
- result = applyItemSize(ctx, measurement.itemKey, measurement.size);
4980
- } else {
4981
- result = {};
4982
- const updateContainerItemSize = (itemKey, containerId, size) => {
4983
- if (containerId !== void 0 && peek$(ctx, `containerItemKey${containerId}`) === itemKey) {
4984
- mergeItemSizeUpdateResult(result, applyItemSize(ctx, itemKey, size));
4985
- }
4986
- };
4987
- updateContainerItemSize(measurement.itemKey, measurement.containerId, measurement.size);
4988
- const keys = Array.from(pendingKeys);
4989
- for (const itemKey of keys) {
4990
- const containerId = state.containerItemKeys.get(itemKey);
4991
- if (containerId !== void 0) {
4992
- (_d = (_c = (_b = ctx.viewRefs.get(containerId)) == null ? void 0 : _b.current) == null ? void 0 : _c.measure) == null ? void 0 : _d.call(_c, (_x, _y, width, height) => {
4993
- if (pendingKeys.has(itemKey)) {
4994
- updateContainerItemSize(itemKey, containerId, { height, width });
4995
- }
4996
- });
4997
- }
4741
+ if (activeItemSizeBatches) {
4742
+ const measurements = activeItemSizeBatches.get(ctx);
4743
+ if (measurements) {
4744
+ measurements.push(measurement);
4745
+ } else {
4746
+ activeItemSizeBatches.set(ctx, [measurement]);
4998
4747
  }
4999
- }
5000
- if (shouldQueueRecalculate && result.needsRecalculate) {
5001
- queueItemSizeRecalculate(ctx, result);
5002
4748
  } else {
5003
- flushItemSizeUpdates(ctx, result);
5004
- }
5005
- if (didDrainLayoutEffectMeasurements) {
5006
- flushBatchedItemSizeRecalculate(ctx);
4749
+ updateItemSizesBatch(ctx, [measurement]);
5007
4750
  }
5008
4751
  }
5009
- function applyItemSize(ctx, itemKey, sizeObj) {
4752
+ function updateItemSizesBatch(ctx, measurements) {
5010
4753
  var _a3;
5011
4754
  const state = ctx.state;
4755
+ const result = {};
4756
+ for (const measurement of measurements) {
4757
+ const ownsMeasuredItem = measurement.containerId === void 0 || peek$(ctx, `containerItemKey${measurement.containerId}`) === measurement.itemKey;
4758
+ if (ownsMeasuredItem) {
4759
+ const index = state.indexByKey.get(measurement.itemKey);
4760
+ const itemData = index === void 0 ? void 0 : (_a3 = state.props.data) == null ? void 0 : _a3[index];
4761
+ const metadata = measurement.containerId !== void 0 && index !== void 0 && itemData !== void 0 ? resolveContainerItemMetadata(state, measurement.containerId, index, itemData) : void 0;
4762
+ const nextResult = applyItemSize(ctx, measurement.itemKey, measurement.size, metadata);
4763
+ mergeItemSizeUpdateResult(result, nextResult);
4764
+ }
4765
+ }
4766
+ flushItemSizeUpdates(ctx, result);
4767
+ }
4768
+ function applyItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
4769
+ var _a3, _b;
4770
+ const state = ctx.state;
5012
4771
  const userScrollAnchorReset = state.userScrollAnchorReset;
5013
4772
  const didMeasureUserScrollAnchorResetItem = !!(userScrollAnchorReset == null ? void 0 : userScrollAnchorReset.keys.delete(itemKey));
5014
4773
  const {
@@ -5018,7 +4777,6 @@ function applyItemSize(ctx, itemKey, sizeObj) {
5018
4777
  } = state;
5019
4778
  if (!data) return { didMeasureUserScrollAnchorResetItem };
5020
4779
  const index = state.indexByKey.get(itemKey);
5021
- let resolvedMeasurementItem;
5022
4780
  if (getFixedItemSize) {
5023
4781
  if (index === void 0) {
5024
4782
  return { didMeasureUserScrollAnchorResetItem };
@@ -5027,14 +4785,15 @@ function applyItemSize(ctx, itemKey, sizeObj) {
5027
4785
  if (itemData === void 0) {
5028
4786
  return { didMeasureUserScrollAnchorResetItem };
5029
4787
  }
5030
- const type = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
5031
- const size2 = getFixedItemSize(itemData, index, type);
5032
- resolvedMeasurementItem = {
5033
- didResolveFixedItemSize: true,
5034
- fixedItemSize: size2,
5035
- itemData,
5036
- itemType: type
5037
- };
4788
+ if (!(resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize)) {
4789
+ const type = (_b = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemType) != null ? _b : getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
4790
+ resolvedMeasurementItem = {
4791
+ didResolveFixedItemSize: true,
4792
+ fixedItemSize: getFixedItemSize(itemData, index, type),
4793
+ itemType: type
4794
+ };
4795
+ }
4796
+ const size2 = resolvedMeasurementItem.fixedItemSize;
5038
4797
  if (size2 !== void 0 && size2 === sizesKnown.get(itemKey)) {
5039
4798
  updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
5040
4799
  return { didMeasureUserScrollAnchorResetItem };
@@ -5063,7 +4822,6 @@ function applyItemSize(ctx, itemKey, sizeObj) {
5063
4822
  previous: size - diff,
5064
4823
  size
5065
4824
  });
5066
- maybeUpdateAnchoredEndSpace(ctx);
5067
4825
  }
5068
4826
  if (minIndexSizeChanged !== void 0) {
5069
4827
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
@@ -5084,7 +4842,7 @@ function applyItemSize(ctx, itemKey, sizeObj) {
5084
4842
  };
5085
4843
  }
5086
4844
  function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
5087
- var _a3, _b, _c;
4845
+ var _a3, _b;
5088
4846
  const state = ctx.state;
5089
4847
  const {
5090
4848
  indexByKey,
@@ -5094,11 +4852,11 @@ function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
5094
4852
  } = state;
5095
4853
  if (!data) return 0;
5096
4854
  const index = indexByKey.get(itemKey);
5097
- const itemData = (_a3 = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemData) != null ? _a3 : data[index];
4855
+ const itemData = data[index];
5098
4856
  let itemType = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemType;
5099
4857
  let fixedItemSize = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.fixedItemSize;
5100
4858
  if (getFixedItemSize && !(resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize)) {
5101
- itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
4859
+ itemType = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
5102
4860
  fixedItemSize = getFixedItemSize(itemData, index, itemType);
5103
4861
  }
5104
4862
  const resolvedItemSize = (resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize) || itemType !== void 0 || fixedItemSize !== void 0 ? {
@@ -5115,29 +4873,470 @@ function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
5115
4873
  const size = Platform.OS === "web" ? Math.round(rawSize) : roundSize(rawSize);
5116
4874
  sizesKnown.set(itemKey, size);
5117
4875
  if (fixedItemSize === void 0 && size > 0) {
5118
- itemType != null ? itemType : itemType = getItemType ? (_c = getItemType(itemData, index)) != null ? _c : "" : "";
4876
+ itemType != null ? itemType : itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
5119
4877
  let averages = averageSizes[itemType];
5120
4878
  if (!averages) {
5121
4879
  averages = averageSizes[itemType] = { avg: 0, num: 0 };
5122
4880
  }
5123
- if (averages.num === 0) {
5124
- averages.avg = size;
5125
- averages.num++;
5126
- } else if (prevSizeKnown !== void 0 && prevSizeKnown > 0) {
5127
- averages.avg += (size - prevSizeKnown) / averages.num;
5128
- } else {
5129
- averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
5130
- averages.num++;
4881
+ if (averages.num === 0) {
4882
+ averages.avg = size;
4883
+ averages.num++;
4884
+ } else if (prevSizeKnown !== void 0 && prevSizeKnown > 0) {
4885
+ averages.avg += (size - prevSizeKnown) / averages.num;
4886
+ } else {
4887
+ averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
4888
+ averages.num++;
4889
+ }
4890
+ }
4891
+ if (!prevSize || Math.abs(prevSize - size) > 0.1) {
4892
+ setSize(ctx, itemKey, size);
4893
+ return size - prevSize;
4894
+ }
4895
+ return 0;
4896
+ }
4897
+
4898
+ // src/core/measureContainersInLayoutEffect.native.ts
4899
+ function resolveFixedItemSize(ctx, containerId, itemKey) {
4900
+ var _a3;
4901
+ const state = ctx.state;
4902
+ const { data, getFixedItemSize } = state.props;
4903
+ const index = state.indexByKey.get(itemKey);
4904
+ let fixedItemSize;
4905
+ if (data && getFixedItemSize && index !== void 0) {
4906
+ const itemData = data[index];
4907
+ if (itemData !== void 0) {
4908
+ fixedItemSize = (_a3 = resolveContainerItemMetadata(state, containerId, index, itemData)) == null ? void 0 : _a3.fixedItemSize;
4909
+ }
4910
+ }
4911
+ return fixedItemSize;
4912
+ }
4913
+ function resolveSkippedAnchorReset(ctx, itemKey) {
4914
+ const state = ctx.state;
4915
+ const anchorReset = state.userScrollAnchorReset;
4916
+ if ((anchorReset == null ? void 0 : anchorReset.keys.delete(itemKey)) && anchorReset.keys.size === 0) {
4917
+ state.userScrollAnchorReset = void 0;
4918
+ }
4919
+ }
4920
+ function measureContainersInLayoutEffect(ctx, targetContainerIds = null) {
4921
+ var _a3, _b, _c;
4922
+ const state = ctx.state;
4923
+ const measurements = [];
4924
+ let isCollectingSynchronousMeasurements = true;
4925
+ const containerIds = targetContainerIds != null ? targetContainerIds : ctx.viewRefs.keys();
4926
+ for (const containerId of containerIds) {
4927
+ const viewRef = ctx.viewRefs.get(containerId);
4928
+ const itemKey = peek$(ctx, `containerItemKey${containerId}`);
4929
+ if (itemKey !== void 0) {
4930
+ const generation = ((_a3 = state.containerItemGenerations[containerId]) != null ? _a3 : 0) + 1;
4931
+ state.containerItemGenerations[containerId] = generation;
4932
+ const fixedItemSize = resolveFixedItemSize(ctx, containerId, itemKey);
4933
+ const canSkipMeasurement = !state.needsOtherAxisSize && fixedItemSize !== void 0 && state.sizesKnown.get(itemKey) === fixedItemSize + ctx.scrollAxisGap;
4934
+ if (canSkipMeasurement) {
4935
+ resolveSkippedAnchorReset(ctx, itemKey);
4936
+ } else if (viewRef) {
4937
+ (_c = (_b = viewRef.current) == null ? void 0 : _b.measure) == null ? void 0 : _c.call(_b, (_x, _y, width, height) => {
4938
+ var _a4;
4939
+ const isCurrentGeneration = ((_a4 = ctx.state.containerItemGenerations[containerId]) != null ? _a4 : 0) === generation;
4940
+ if (isCurrentGeneration) {
4941
+ const measurement = {
4942
+ containerId,
4943
+ itemKey,
4944
+ size: { height, width }
4945
+ };
4946
+ if (isCollectingSynchronousMeasurements) {
4947
+ measurements.push(measurement);
4948
+ } else {
4949
+ updateItemSizes(ctx, measurement);
4950
+ }
4951
+ }
4952
+ });
4953
+ }
4954
+ }
4955
+ }
4956
+ isCollectingSynchronousMeasurements = false;
4957
+ if (measurements.length > 0) {
4958
+ updateItemSizesBatch(ctx, measurements);
4959
+ }
4960
+ }
4961
+ var typedForwardRef = React2.forwardRef;
4962
+ var typedMemo = React2.memo;
4963
+
4964
+ // src/components/ContainerLayoutCoordinator.tsx
4965
+ var ContainerLayoutCoordinator = typedMemo(function ContainerLayoutCoordinatorComponent({
4966
+ children
4967
+ }) {
4968
+ const ctx = useStateContext();
4969
+ const [containerLayoutEpoch] = useArr$(["containerLayoutEpoch"]);
4970
+ React2.useLayoutEffect(() => {
4971
+ if (IsNewArchitecture) {
4972
+ const targetContainerIds = getContainerLayoutEffectScope(ctx);
4973
+ if (targetContainerIds !== void 0) {
4974
+ measureContainersInLayoutEffect(ctx, targetContainerIds);
4975
+ }
4976
+ }
4977
+ }, [ctx, containerLayoutEpoch]);
4978
+ return children;
4979
+ });
4980
+
4981
+ // src/components/stickyPositionUtils.ts
4982
+ function getStickyPushLimit(state, index, itemKey) {
4983
+ if (!itemKey) {
4984
+ return void 0;
4985
+ }
4986
+ const currentSize = state.sizes.get(itemKey);
4987
+ if (!(currentSize && currentSize > 0)) {
4988
+ return void 0;
4989
+ }
4990
+ const stickyIndexInArray = state.props.stickyHeaderIndicesArr.indexOf(index);
4991
+ if (stickyIndexInArray === -1) {
4992
+ return void 0;
4993
+ }
4994
+ const nextStickyIndex = state.props.stickyHeaderIndicesArr[stickyIndexInArray + 1];
4995
+ if (nextStickyIndex === void 0) {
4996
+ return void 0;
4997
+ }
4998
+ const nextStickyPosition = state.positions[nextStickyIndex];
4999
+ if (nextStickyPosition === void 0) {
5000
+ return void 0;
5001
+ }
5002
+ return nextStickyPosition - currentSize;
5003
+ }
5004
+ var useAnimatedValue = (initialValue) => {
5005
+ const [animAnimatedValue] = useState(() => new Animated.Value(initialValue));
5006
+ return animAnimatedValue;
5007
+ };
5008
+
5009
+ // src/hooks/useValue$.ts
5010
+ function useValue$(key, params) {
5011
+ const { getValue } = params || {};
5012
+ const ctx = useStateContext();
5013
+ const getNewValue = () => {
5014
+ var _a3;
5015
+ return (_a3 = getValue ? getValue(peek$(ctx, key)) : peek$(ctx, key)) != null ? _a3 : 0;
5016
+ };
5017
+ const animValue = useAnimatedValue(getNewValue());
5018
+ useLayoutEffect(() => {
5019
+ const syncCurrentValue = () => {
5020
+ animValue.setValue(getNewValue());
5021
+ };
5022
+ const unsubscribe = listen$(ctx, key, syncCurrentValue);
5023
+ syncCurrentValue();
5024
+ return unsubscribe;
5025
+ }, [animValue, ctx, key]);
5026
+ return animValue;
5027
+ }
5028
+ var getComponent = (Component) => {
5029
+ if (React2.isValidElement(Component)) {
5030
+ return Component;
5031
+ }
5032
+ if (Component) {
5033
+ return /* @__PURE__ */ React2.createElement(Component, null);
5034
+ }
5035
+ return null;
5036
+ };
5037
+
5038
+ // src/components/PositionView.native.tsx
5039
+ var PositionViewState = typedMemo(function PositionViewState2({
5040
+ id,
5041
+ horizontal,
5042
+ style,
5043
+ refView,
5044
+ ...rest
5045
+ }) {
5046
+ const [position = POSITION_OUT_OF_VIEW, _itemKey] = useArr$([`containerPosition${id}`, `containerItemKey${id}`]);
5047
+ return /* @__PURE__ */ React2.createElement(View$1, { ref: refView, style: [style, horizontal ? { left: position } : { top: position }], ...rest });
5048
+ });
5049
+ var PositionViewAnimated = typedMemo(function PositionViewAnimated2({
5050
+ id,
5051
+ horizontal,
5052
+ style,
5053
+ refView,
5054
+ ...rest
5055
+ }) {
5056
+ const position$ = useValue$(`containerPosition${id}`, {
5057
+ getValue: (v) => v != null ? v : POSITION_OUT_OF_VIEW
5058
+ });
5059
+ const position = horizontal ? { left: position$ } : { top: position$ };
5060
+ return /* @__PURE__ */ React2.createElement(Animated.View, { ref: refView, style: [style, position], ...rest });
5061
+ });
5062
+ var PositionViewSticky = typedMemo(function PositionViewSticky2({
5063
+ id,
5064
+ horizontal,
5065
+ style,
5066
+ refView,
5067
+ animatedScrollY,
5068
+ stickyHeaderConfig,
5069
+ children,
5070
+ ...rest
5071
+ }) {
5072
+ const ctx = useStateContext();
5073
+ const [
5074
+ position = POSITION_OUT_OF_VIEW,
5075
+ alignItemsAtEndPadding = 0,
5076
+ headerSize = 0,
5077
+ stylePaddingTop = 0,
5078
+ itemKey,
5079
+ itemIndex,
5080
+ _totalSize = 0
5081
+ ] = useArr$([
5082
+ `containerPosition${id}`,
5083
+ "alignItemsAtEndPadding",
5084
+ "headerSize",
5085
+ "stylePaddingTop",
5086
+ `containerItemKey${id}`,
5087
+ `containerItemIndex${id}`,
5088
+ "totalSize"
5089
+ ]);
5090
+ const pushLimit = React2.useMemo(
5091
+ () => getStickyPushLimit(ctx.state, itemIndex, itemKey),
5092
+ [ctx.state, itemIndex, itemKey, _totalSize]
5093
+ );
5094
+ const transform = React2.useMemo(() => {
5095
+ var _a3;
5096
+ if (animatedScrollY) {
5097
+ const stickyConfigOffset = (_a3 = stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset) != null ? _a3 : 0;
5098
+ const stickyStart = position + headerSize + stylePaddingTop + alignItemsAtEndPadding - stickyConfigOffset;
5099
+ let nextStickyPosition;
5100
+ if (pushLimit !== void 0) {
5101
+ if (pushLimit <= position) {
5102
+ nextStickyPosition = pushLimit;
5103
+ } else {
5104
+ nextStickyPosition = animatedScrollY.interpolate({
5105
+ extrapolateLeft: "clamp",
5106
+ extrapolateRight: "clamp",
5107
+ inputRange: [stickyStart, stickyStart + (pushLimit - position)],
5108
+ outputRange: [position, pushLimit]
5109
+ });
5110
+ }
5111
+ } else {
5112
+ nextStickyPosition = animatedScrollY.interpolate({
5113
+ extrapolateLeft: "clamp",
5114
+ extrapolateRight: "extend",
5115
+ inputRange: [stickyStart, stickyStart + 5e3],
5116
+ outputRange: [position, position + 5e3]
5117
+ });
5118
+ }
5119
+ return horizontal ? [{ translateX: nextStickyPosition }] : [{ translateY: nextStickyPosition }];
5120
+ }
5121
+ }, [
5122
+ alignItemsAtEndPadding,
5123
+ animatedScrollY,
5124
+ headerSize,
5125
+ position,
5126
+ pushLimit,
5127
+ stylePaddingTop,
5128
+ stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset
5129
+ ]);
5130
+ const viewStyle = React2.useMemo(
5131
+ () => [style, { zIndex: itemIndex + 1e3 }, { transform }],
5132
+ [style, itemIndex, transform]
5133
+ );
5134
+ const renderStickyHeaderBackdrop = React2.useMemo(() => {
5135
+ if (!(stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent)) {
5136
+ return null;
5137
+ }
5138
+ return /* @__PURE__ */ React2.createElement(
5139
+ View$1,
5140
+ {
5141
+ style: {
5142
+ inset: 0,
5143
+ pointerEvents: "none",
5144
+ position: "absolute"
5145
+ }
5146
+ },
5147
+ getComponent(stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent)
5148
+ );
5149
+ }, [stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent]);
5150
+ return /* @__PURE__ */ React2.createElement(Animated.View, { ref: refView, style: viewStyle, ...rest }, renderStickyHeaderBackdrop, children);
5151
+ });
5152
+ var PositionView = IsNewArchitecture ? PositionViewState : PositionViewAnimated;
5153
+ function useInit(cb) {
5154
+ useState(() => cb());
5155
+ }
5156
+
5157
+ // src/state/ContextContainer.ts
5158
+ var ContextContainer = createContext(null);
5159
+ var NO_CONTAINER_ID = -1;
5160
+ function useContextContainer() {
5161
+ return useContext(ContextContainer);
5162
+ }
5163
+ function useContainerItemSignals(containerContext) {
5164
+ var _a3;
5165
+ const containerId = (_a3 = containerContext == null ? void 0 : containerContext.containerId) != null ? _a3 : NO_CONTAINER_ID;
5166
+ const [itemKey, itemIndex, item] = useArr$([
5167
+ `containerItemKey${containerId}`,
5168
+ `containerItemIndex${containerId}`,
5169
+ `containerItemData${containerId}`
5170
+ ]);
5171
+ return {
5172
+ hasItemInfo: !!containerContext && itemKey !== void 0 && itemIndex !== void 0,
5173
+ item,
5174
+ itemIndex,
5175
+ itemKey
5176
+ };
5177
+ }
5178
+ function useAdaptiveRender() {
5179
+ const [mode] = useArr$(["adaptiveRender"]);
5180
+ return mode;
5181
+ }
5182
+ function useAdaptiveRenderChange(callback) {
5183
+ const ctx = useStateContext();
5184
+ const callbackRef = useRef(callback);
5185
+ callbackRef.current = callback;
5186
+ useLayoutEffect(() => {
5187
+ let mode = peek$(ctx, "adaptiveRender");
5188
+ return listen$(ctx, "adaptiveRender", (nextMode) => {
5189
+ if (mode !== nextMode) {
5190
+ mode = nextMode;
5191
+ callbackRef.current(nextMode);
5192
+ }
5193
+ });
5194
+ }, [ctx]);
5195
+ }
5196
+ function useViewability(callback, configId) {
5197
+ const ctx = useStateContext();
5198
+ const containerContext = useContextContainer();
5199
+ useInit(() => {
5200
+ if (!containerContext) {
5201
+ return;
5202
+ }
5203
+ const { containerId } = containerContext;
5204
+ const key = containerId + (configId != null ? configId : "");
5205
+ const value = ctx.mapViewabilityValues.get(key);
5206
+ if (value) {
5207
+ callback(value);
5208
+ }
5209
+ });
5210
+ useEffect(() => {
5211
+ if (!containerContext) {
5212
+ return;
5213
+ }
5214
+ const { containerId } = containerContext;
5215
+ const key = containerId + (configId != null ? configId : "");
5216
+ ctx.mapViewabilityCallbacks.set(key, callback);
5217
+ return () => {
5218
+ ctx.mapViewabilityCallbacks.delete(key);
5219
+ };
5220
+ }, [ctx, callback, configId, containerContext]);
5221
+ }
5222
+ function useViewabilityAmount(callback) {
5223
+ const ctx = useStateContext();
5224
+ const containerContext = useContextContainer();
5225
+ useInit(() => {
5226
+ if (!containerContext) {
5227
+ return;
5228
+ }
5229
+ const { containerId } = containerContext;
5230
+ const value = ctx.mapViewabilityAmountValues.get(containerId);
5231
+ if (value) {
5232
+ callback(value);
5233
+ }
5234
+ });
5235
+ useEffect(() => {
5236
+ if (!containerContext) {
5237
+ return;
5238
+ }
5239
+ const { containerId } = containerContext;
5240
+ ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
5241
+ return () => {
5242
+ ctx.mapViewabilityAmountCallbacks.delete(containerId);
5243
+ };
5244
+ }, [ctx, callback, containerContext]);
5245
+ }
5246
+ function useRecyclingEffect(effect) {
5247
+ const containerContext = useContextContainer();
5248
+ const { hasItemInfo, item, itemIndex, itemKey } = useContainerItemSignals(containerContext);
5249
+ const prevInfo = useRef(void 0);
5250
+ useEffect(() => {
5251
+ if (!hasItemInfo) {
5252
+ return;
5253
+ }
5254
+ let ret;
5255
+ if (prevInfo.current) {
5256
+ ret = effect({
5257
+ index: itemIndex,
5258
+ item,
5259
+ prevIndex: prevInfo.current.index,
5260
+ prevItem: prevInfo.current.item
5261
+ });
5131
5262
  }
5263
+ prevInfo.current = {
5264
+ index: itemIndex,
5265
+ item
5266
+ };
5267
+ return ret;
5268
+ }, [effect, hasItemInfo, itemIndex, item, itemKey]);
5269
+ }
5270
+ function useRecyclingState(valueOrFun) {
5271
+ const containerContext = useContextContainer();
5272
+ const { hasItemInfo, item, itemIndex, itemKey } = useContainerItemSignals(containerContext);
5273
+ const computeValue = () => {
5274
+ if (isFunction(valueOrFun)) {
5275
+ const initializer = valueOrFun;
5276
+ return hasItemInfo ? initializer({
5277
+ index: itemIndex,
5278
+ item,
5279
+ prevIndex: void 0,
5280
+ prevItem: void 0
5281
+ }) : initializer();
5282
+ }
5283
+ return valueOrFun;
5284
+ };
5285
+ const [stateValue, setStateValue] = useState(() => {
5286
+ return computeValue();
5287
+ });
5288
+ const prevItemKeyRef = useRef(hasItemInfo ? itemKey : null);
5289
+ if (hasItemInfo && prevItemKeyRef.current !== itemKey) {
5290
+ prevItemKeyRef.current = itemKey;
5291
+ setStateValue(computeValue());
5132
5292
  }
5133
- if (!prevSize || Math.abs(prevSize - size) > 0.1) {
5134
- setSize(ctx, itemKey, size);
5135
- return size - prevSize;
5136
- }
5137
- return 0;
5293
+ const triggerLayout = containerContext == null ? void 0 : containerContext.triggerLayout;
5294
+ const setState = useCallback(
5295
+ (newState) => {
5296
+ if (!triggerLayout) {
5297
+ return;
5298
+ }
5299
+ setStateValue((prevValue) => {
5300
+ return isFunction(newState) ? newState(prevValue) : newState;
5301
+ });
5302
+ triggerLayout();
5303
+ },
5304
+ [triggerLayout]
5305
+ );
5306
+ return [stateValue, setState];
5307
+ }
5308
+ function useIsLastItem() {
5309
+ var _a3;
5310
+ const containerContext = useContextContainer();
5311
+ const containerId = (_a3 = containerContext == null ? void 0 : containerContext.containerId) != null ? _a3 : NO_CONTAINER_ID;
5312
+ const [itemKey] = useArr$([`containerItemKey${containerId}`]);
5313
+ const isLast = useSelector$("lastItemKeys", (lastItemKeys) => {
5314
+ if (containerContext && !isNullOrUndefined(itemKey)) {
5315
+ return (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false;
5316
+ }
5317
+ return false;
5318
+ });
5319
+ return isLast;
5320
+ }
5321
+ function useListScrollSize() {
5322
+ const [scrollSize] = useArr$(["scrollSize"]);
5323
+ return scrollSize;
5324
+ }
5325
+ var noop = () => {
5326
+ };
5327
+ function useSyncLayout() {
5328
+ const containerContext = useContextContainer();
5329
+ return IsNewArchitecture && containerContext ? containerContext.triggerLayout : noop;
5330
+ }
5331
+
5332
+ // src/components/Separator.tsx
5333
+ function Separator({ ItemSeparatorComponent, leadingItem }) {
5334
+ const isLastItem = useIsLastItem();
5335
+ return isLastItem ? null : /* @__PURE__ */ React2.createElement(ItemSeparatorComponent, { leadingItem });
5138
5336
  }
5139
5337
  function useOnLayoutSync({
5140
5338
  ref,
5339
+ measureInLayoutEffect = true,
5141
5340
  onLayoutProp,
5142
5341
  onLayoutChange
5143
5342
  }, deps = []) {
@@ -5151,16 +5350,154 @@ function useOnLayoutSync({
5151
5350
  );
5152
5351
  if (IsNewArchitecture) {
5153
5352
  useLayoutEffect(() => {
5154
- if (ref.current) {
5353
+ if (measureInLayoutEffect && ref.current) {
5155
5354
  ref.current.measure((x, y, width, height) => {
5156
5355
  onLayoutChange({ height, width, x, y }, true);
5157
5356
  });
5158
5357
  }
5159
- }, deps);
5358
+ }, [measureInLayoutEffect, ...deps]);
5160
5359
  }
5161
5360
  return { onLayout };
5162
5361
  }
5163
5362
 
5363
+ // src/hooks/useContainerMeasurement.tsx
5364
+ var pendingWebShrinkMeasurements = /* @__PURE__ */ new Map();
5365
+ var pendingWebShrinkFrame;
5366
+ function cancelWebShrinkMeasurement(state) {
5367
+ pendingWebShrinkMeasurements.delete(state);
5368
+ }
5369
+ function scheduleWebShrinkMeasurement(state, confirmMeasurement) {
5370
+ pendingWebShrinkMeasurements.set(state, confirmMeasurement);
5371
+ if (pendingWebShrinkFrame === void 0) {
5372
+ pendingWebShrinkFrame = requestAnimationFrame(() => {
5373
+ const callbacks = Array.from(pendingWebShrinkMeasurements.values());
5374
+ pendingWebShrinkMeasurements.clear();
5375
+ pendingWebShrinkFrame = void 0;
5376
+ batchItemSizeUpdates(() => {
5377
+ for (const callback of callbacks) {
5378
+ callback();
5379
+ }
5380
+ });
5381
+ });
5382
+ }
5383
+ }
5384
+ function processContainerLayout({ containerId, ctx, rectangle, ref, state }) {
5385
+ var _a3, _b;
5386
+ const listState = ctx.state;
5387
+ const currentItemKey = state.itemKey;
5388
+ state.didLayout = true;
5389
+ let layout = rectangle;
5390
+ const axis = state.horizontal ? "width" : "height";
5391
+ const size = roundSize(rectangle[axis]);
5392
+ const localPreviousSize = state.lastSize ? roundSize(state.lastSize[axis]) : void 0;
5393
+ const coreKnownSize = listState.sizesKnown.get(currentItemKey);
5394
+ const previousSize = Platform.OS === "web" ? coreKnownSize : localPreviousSize;
5395
+ const applyLayout = () => {
5396
+ state.lastSize = layout;
5397
+ updateItemSizes(ctx, {
5398
+ containerId,
5399
+ itemKey: currentItemKey,
5400
+ size: layout
5401
+ });
5402
+ };
5403
+ const shouldDeferWebShrinkLayoutUpdate = Platform.OS === "web" && !isInMVCPActiveMode(listState) && previousSize !== void 0 && size + 1 < previousSize;
5404
+ if (shouldDeferWebShrinkLayoutUpdate) {
5405
+ scheduleWebShrinkMeasurement(state, () => {
5406
+ var _a4;
5407
+ if (state.itemKey === currentItemKey) {
5408
+ const element = ref.current;
5409
+ const rect = (_a4 = element == null ? void 0 : element.getBoundingClientRect) == null ? void 0 : _a4.call(element);
5410
+ if (rect) {
5411
+ layout = { height: rect.height, width: rect.width };
5412
+ }
5413
+ applyLayout();
5414
+ }
5415
+ });
5416
+ } else {
5417
+ if (Platform.OS === "web") {
5418
+ cancelWebShrinkMeasurement(state);
5419
+ }
5420
+ if (IsNewArchitecture || size > 0) {
5421
+ applyLayout();
5422
+ } else {
5423
+ (_b = (_a3 = ref.current) == null ? void 0 : _a3.measure) == null ? void 0 : _b.call(_a3, (_x, _y, width, height) => {
5424
+ layout = { height, width };
5425
+ applyLayout();
5426
+ });
5427
+ }
5428
+ }
5429
+ }
5430
+ function useContainerMeasurement({
5431
+ containerId,
5432
+ ctx,
5433
+ horizontal,
5434
+ itemKey,
5435
+ ref
5436
+ }) {
5437
+ const stateRef = useRef({
5438
+ didLayout: false,
5439
+ horizontal,
5440
+ itemKey
5441
+ });
5442
+ stateRef.current.horizontal = horizontal;
5443
+ stateRef.current.itemKey = itemKey;
5444
+ const [layoutRenderCount, forceLayoutRender] = useState(0);
5445
+ const onLayoutChange = useCallback(
5446
+ (rectangle) => {
5447
+ processContainerLayout({ containerId, ctx, rectangle, ref, state: stateRef.current });
5448
+ },
5449
+ [containerId, ctx, ref]
5450
+ );
5451
+ const triggerLayout = useCallback(() => {
5452
+ if (IsNewArchitecture) {
5453
+ scheduleContainerLayout(ctx, containerId);
5454
+ } else {
5455
+ forceLayoutRender((value) => value + 1);
5456
+ }
5457
+ }, [containerId, ctx]);
5458
+ useLayoutEffect(() => {
5459
+ ctx.containerLayoutTriggers.set(containerId, triggerLayout);
5460
+ return () => {
5461
+ cancelWebShrinkMeasurement(stateRef.current);
5462
+ if (ctx.containerLayoutTriggers.get(containerId) === triggerLayout) {
5463
+ ctx.containerLayoutTriggers.delete(containerId);
5464
+ }
5465
+ };
5466
+ }, [containerId, ctx, triggerLayout]);
5467
+ useLayoutEffect(() => {
5468
+ if (IsNewArchitecture) {
5469
+ scheduleContainerLayout(ctx, containerId);
5470
+ }
5471
+ });
5472
+ const { onLayout } = useOnLayoutSync(
5473
+ {
5474
+ measureInLayoutEffect: !IsNewArchitecture,
5475
+ onLayoutChange,
5476
+ ref},
5477
+ [itemKey, layoutRenderCount]
5478
+ );
5479
+ useEffect(() => {
5480
+ if (!IsNewArchitecture) {
5481
+ stateRef.current.didLayout = false;
5482
+ const timeout = setTimeout(() => {
5483
+ const state = stateRef.current;
5484
+ if (!state.didLayout && state.lastSize) {
5485
+ updateItemSizes(ctx, {
5486
+ containerId,
5487
+ itemKey: state.itemKey,
5488
+ size: state.lastSize
5489
+ });
5490
+ state.didLayout = true;
5491
+ }
5492
+ }, 16);
5493
+ return () => {
5494
+ clearTimeout(timeout);
5495
+ };
5496
+ }
5497
+ }, [containerId, ctx, itemKey]);
5498
+ return { onLayout, triggerLayout };
5499
+ }
5500
+
5164
5501
  // src/components/Container.tsx
5165
5502
  function getContainerPositionStyle({
5166
5503
  columnWrapperStyle,
@@ -5231,16 +5568,14 @@ var Container = typedMemo(function Container2({
5231
5568
  "extraData",
5232
5569
  `containerSticky${id}`
5233
5570
  ]);
5234
- const itemLayoutRef = useRef({
5235
- didLayout: false,
5571
+ const ref = useRef(null);
5572
+ const { onLayout, triggerLayout } = useContainerMeasurement({
5573
+ containerId: id,
5574
+ ctx,
5236
5575
  horizontal,
5237
5576
  itemKey,
5238
- pendingShrinkToken: 0
5577
+ ref
5239
5578
  });
5240
- itemLayoutRef.current.horizontal = horizontal;
5241
- itemLayoutRef.current.itemKey = itemKey;
5242
- const ref = useRef(null);
5243
- const [layoutRenderCount, forceLayoutRender] = useState(0);
5244
5579
  const resolvedColumn = column > 0 ? column : 1;
5245
5580
  const resolvedSpan = Math.min(Math.max(span || 1, 1), numColumns);
5246
5581
  const otherAxisPos = numColumns > 1 ? `${(resolvedColumn - 1) / numColumns * 100}%` : 0;
@@ -5271,63 +5606,7 @@ var Container = typedMemo(function Container2({
5271
5606
  () => itemKey !== void 0 ? getRenderedItem2(itemKey) : null,
5272
5607
  [itemKey, data, extraData]
5273
5608
  );
5274
- const { index, renderedItem } = renderedItemInfo || {};
5275
- const onLayoutChange = useCallback((rectangle, fromLayoutEffect) => {
5276
- var _a3, _b;
5277
- const {
5278
- horizontal: currentHorizontal,
5279
- itemKey: currentItemKey,
5280
- lastSize,
5281
- pendingShrinkToken
5282
- } = itemLayoutRef.current;
5283
- if (isNullOrUndefined(currentItemKey)) {
5284
- return;
5285
- }
5286
- itemLayoutRef.current.didLayout = true;
5287
- let layout = rectangle;
5288
- const axis = currentHorizontal ? "width" : "height";
5289
- const size = roundSize(rectangle[axis]);
5290
- const prevSize = lastSize ? roundSize(lastSize[axis]) : void 0;
5291
- const doUpdate = () => {
5292
- itemLayoutRef.current.lastSize = layout;
5293
- updateItemSizes(ctx, {
5294
- containerId: id,
5295
- fromLayoutEffect,
5296
- itemKey: currentItemKey,
5297
- size: layout
5298
- });
5299
- itemLayoutRef.current.didLayout = true;
5300
- };
5301
- const shouldDeferWebShrinkLayoutUpdate = Platform.OS === "web" && !isInMVCPActiveMode(ctx.state) && prevSize !== void 0 && size + 1 < prevSize;
5302
- if (shouldDeferWebShrinkLayoutUpdate) {
5303
- const token = pendingShrinkToken + 1;
5304
- itemLayoutRef.current.pendingShrinkToken = token;
5305
- requestAnimationFrame(() => {
5306
- var _a4;
5307
- if (itemLayoutRef.current.pendingShrinkToken !== token) {
5308
- return;
5309
- }
5310
- const element = ref.current;
5311
- const rect = (_a4 = element == null ? void 0 : element.getBoundingClientRect) == null ? void 0 : _a4.call(element);
5312
- if (rect) {
5313
- layout = { height: rect.height, width: rect.width };
5314
- }
5315
- doUpdate();
5316
- });
5317
- return;
5318
- }
5319
- if (IsNewArchitecture || size > 0) {
5320
- doUpdate();
5321
- } else {
5322
- (_b = (_a3 = ref.current) == null ? void 0 : _a3.measure) == null ? void 0 : _b.call(_a3, (_x, _y, width, height) => {
5323
- layout = { height, width };
5324
- doUpdate();
5325
- });
5326
- }
5327
- }, []);
5328
- const triggerLayout = useCallback(() => {
5329
- forceLayoutRender((v) => v + 1);
5330
- }, []);
5609
+ const { renderedItem } = renderedItemInfo || {};
5331
5610
  const contextValue = useMemo(() => {
5332
5611
  ctx.viewRefs.set(id, ref);
5333
5612
  return {
@@ -5335,44 +5614,6 @@ var Container = typedMemo(function Container2({
5335
5614
  triggerLayout
5336
5615
  };
5337
5616
  }, [id, triggerLayout]);
5338
- useLayoutEffect(() => {
5339
- ctx.containerLayoutTriggers.set(id, triggerLayout);
5340
- return () => {
5341
- if (ctx.containerLayoutTriggers.get(id) === triggerLayout) {
5342
- ctx.containerLayoutTriggers.delete(id);
5343
- }
5344
- };
5345
- }, [ctx, id, triggerLayout]);
5346
- const { onLayout } = useOnLayoutSync(
5347
- {
5348
- onLayoutChange,
5349
- ref},
5350
- [itemKey, layoutRenderCount]
5351
- );
5352
- if (!IsNewArchitecture) {
5353
- useEffect(() => {
5354
- if (!isNullOrUndefined(itemKey)) {
5355
- itemLayoutRef.current.didLayout = false;
5356
- const timeout = setTimeout(() => {
5357
- if (!itemLayoutRef.current.didLayout) {
5358
- const { itemKey: currentItemKey, lastSize } = itemLayoutRef.current;
5359
- if (lastSize && !isNullOrUndefined(currentItemKey)) {
5360
- updateItemSizes(ctx, {
5361
- containerId: id,
5362
- fromLayoutEffect: false,
5363
- itemKey: currentItemKey,
5364
- size: lastSize
5365
- });
5366
- itemLayoutRef.current.didLayout = true;
5367
- }
5368
- }
5369
- }, 16);
5370
- return () => {
5371
- clearTimeout(timeout);
5372
- };
5373
- }
5374
- }, [itemKey]);
5375
- }
5376
5617
  const PositionComponent = isSticky ? stickyPositionComponentInternal ? stickyPositionComponentInternal : PositionViewSticky : positionComponentInternal ? positionComponentInternal : PositionView;
5377
5618
  return /* @__PURE__ */ React2.createElement(
5378
5619
  PositionComponent,
@@ -5380,7 +5621,6 @@ var Container = typedMemo(function Container2({
5380
5621
  animatedScrollY: isSticky ? animatedScrollY : void 0,
5381
5622
  horizontal,
5382
5623
  id,
5383
- index,
5384
5624
  key: recycleItems ? void 0 : itemKey,
5385
5625
  onLayout,
5386
5626
  refView: ref,
@@ -5421,19 +5661,34 @@ function ContainerSlotBase({
5421
5661
  var ContainerSlot = typedMemo(function ContainerSlot2(props) {
5422
5662
  return /* @__PURE__ */ React2.createElement(ContainerSlotBase, { ...props });
5423
5663
  });
5664
+ function useFreshDataTransitionVisibility(readyToRender, transitionEpoch) {
5665
+ const completedTransitionEpoch = useRef(transitionEpoch);
5666
+ const isTransitionPending = completedTransitionEpoch.current !== transitionEpoch;
5667
+ useLayoutEffect(() => {
5668
+ if (!readyToRender) {
5669
+ completedTransitionEpoch.current = transitionEpoch;
5670
+ }
5671
+ }, [readyToRender, transitionEpoch]);
5672
+ return readyToRender && !isTransitionPending;
5673
+ }
5424
5674
 
5425
5675
  // src/components/Containers.native.tsx
5426
- var ContainersLayer = typedMemo(function ContainersLayer2({ children, horizontal }) {
5676
+ var ContainersLayer = typedMemo(function ContainersLayer2({
5677
+ children,
5678
+ freshDataTransitionEpoch,
5679
+ horizontal
5680
+ }) {
5427
5681
  const ctx = useStateContext();
5428
5682
  const columnWrapperStyle = ctx.columnWrapperStyle;
5429
5683
  const animSize = useValue$("totalSize");
5430
5684
  const [readyToRender, numColumns, otherAxisSize = 0] = useArr$(["readyToRender", "numColumns", "otherAxisSize"]);
5685
+ const isVisible = useFreshDataTransitionVisibility(!!readyToRender, freshDataTransitionEpoch);
5431
5686
  const style = horizontal ? {
5432
5687
  height: otherAxisSize || "100%",
5433
5688
  minHeight: otherAxisSize,
5434
- opacity: readyToRender ? 1 : 0,
5689
+ opacity: isVisible ? 1 : 0,
5435
5690
  width: animSize
5436
- } : { height: animSize, minWidth: otherAxisSize, opacity: readyToRender ? 1 : 0 };
5691
+ } : { height: animSize, minWidth: otherAxisSize, opacity: isVisible ? 1 : 0 };
5437
5692
  if (columnWrapperStyle) {
5438
5693
  const { columnGap, rowGap, gap } = columnWrapperStyle;
5439
5694
  const gapX = columnGap || gap || 0;
@@ -5454,9 +5709,10 @@ var ContainersLayer = typedMemo(function ContainersLayer2({ children, horizontal
5454
5709
  }
5455
5710
  }
5456
5711
  }
5457
- return /* @__PURE__ */ React2.createElement(Animated.View, { style }, children);
5712
+ return /* @__PURE__ */ React2.createElement(Animated.View, { pointerEvents: isVisible ? void 0 : "none", style }, /* @__PURE__ */ React2.createElement(ContainerLayoutCoordinator, null, children));
5458
5713
  });
5459
5714
  var Containers = typedMemo(function Containers2({
5715
+ freshDataTransitionEpoch,
5460
5716
  horizontal,
5461
5717
  recycleItems,
5462
5718
  ItemSeparatorComponent,
@@ -5481,7 +5737,7 @@ var Containers = typedMemo(function Containers2({
5481
5737
  )
5482
5738
  );
5483
5739
  }
5484
- return /* @__PURE__ */ React2.createElement(ContainersLayer, { horizontal }, containers);
5740
+ return /* @__PURE__ */ React2.createElement(ContainersLayer, { freshDataTransitionEpoch, horizontal }, containers);
5485
5741
  });
5486
5742
  var ListComponentScrollView = Animated.ScrollView;
5487
5743
 
@@ -5520,16 +5776,6 @@ var SnapWrapper = React2.forwardRef(function SnapWrapperInner({ ScrollComponent,
5520
5776
  const [snapToOffsets] = useArr$(["snapToOffsets"]);
5521
5777
  return /* @__PURE__ */ React2.createElement(ScrollComponent, { ...props, ref, snapToOffsets });
5522
5778
  });
5523
- function WebAnchoredEndSpace({ horizontal }) {
5524
- const ctx = useStateContext();
5525
- const [anchoredEndSpaceSize] = useArr$(["anchoredEndSpaceSize"]);
5526
- const shouldRenderAnchoredEndSpace = !!ctx.state.props.anchoredEndSpace && (anchoredEndSpaceSize || 0) > 0;
5527
- if (!shouldRenderAnchoredEndSpace) {
5528
- return null;
5529
- }
5530
- const style = horizontal ? { height: "100%", width: anchoredEndSpaceSize || 0 } : { height: anchoredEndSpaceSize || 0 };
5531
- return /* @__PURE__ */ React2.createElement("div", { style }, null);
5532
- }
5533
5779
 
5534
5780
  // src/core/updateContentMetrics.ts
5535
5781
  var SCROLL_ADJUST_EPSILON = 0.1;
@@ -5563,7 +5809,11 @@ function setHeaderSize(ctx, size) {
5563
5809
  state.didMeasureHeader = true;
5564
5810
  }
5565
5811
  function setFooterSize(ctx, size) {
5566
- return setContentLengthSignal(ctx, "footerSize", size);
5812
+ const didChange = setContentLengthSignal(ctx, "footerSize", size);
5813
+ if (didChange) {
5814
+ maybeUpdateAnchoredEndSpace(ctx);
5815
+ }
5816
+ return didChange;
5567
5817
  }
5568
5818
  function areInsetsEqual(left, right) {
5569
5819
  var _a3, _b, _c, _d, _e, _f, _g, _h;
@@ -5606,6 +5856,7 @@ var LayoutView = ({ onLayoutChange, refView, ...rest }) => {
5606
5856
  const { onLayout } = useOnLayoutSync({ onLayoutChange, ref });
5607
5857
  return /* @__PURE__ */ React2.createElement(View$1, { ...rest, onLayout, ref });
5608
5858
  };
5859
+ var StyleSheet = StyleSheet$1;
5609
5860
 
5610
5861
  // src/components/ListComponent.tsx
5611
5862
  var AlignItemsAtEndSpacer = typedMemo(function AlignItemsAtEndSpacer2({ horizontal }) {
@@ -5623,6 +5874,7 @@ var AlignItemsAtEndSpacer = typedMemo(function AlignItemsAtEndSpacer2({ horizont
5623
5874
  });
5624
5875
  var ListComponent = typedMemo(function ListComponent2({
5625
5876
  canRender,
5877
+ freshDataTransitionEpoch,
5626
5878
  style,
5627
5879
  contentContainerStyle,
5628
5880
  horizontal,
@@ -5641,6 +5893,8 @@ var ListComponent = typedMemo(function ListComponent2({
5641
5893
  refScrollView,
5642
5894
  renderScrollComponent,
5643
5895
  onLayoutFooter,
5896
+ onInternalScrollBeginDrag,
5897
+ onInternalScrollEnd,
5644
5898
  scrollAdjustHandler,
5645
5899
  snapToIndices,
5646
5900
  stickyHeaderConfig,
@@ -5650,8 +5904,17 @@ var ListComponent = typedMemo(function ListComponent2({
5650
5904
  }) {
5651
5905
  const ctx = useStateContext();
5652
5906
  const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
5653
- const [otherAxisSize = 0] = useArr$(["otherAxisSize"]);
5907
+ const [anchoredEndSpaceSize = 0, otherAxisSize = 0] = useArr$(["anchoredEndSpaceSize", "otherAxisSize"]);
5654
5908
  const shouldRenderAlignItemsAtEndSpacer = ctx.state.props.alignItemsAtEndPaddingEnabled;
5909
+ const shouldMaterializeAnchoredEndSpace = !!ctx.state.props.anchoredEndSpace && ctx.state.props.anchoredEndSpaceOwner === "list" && anchoredEndSpaceSize > 0;
5910
+ let anchoredEndSpaceStyle;
5911
+ if (shouldMaterializeAnchoredEndSpace) {
5912
+ const paddingEnd = horizontal ? isHorizontalRTL(ctx.state) ? "Left" : "Right" : "Bottom";
5913
+ const flattenedContentContainerStyle = StyleSheet.flatten(contentContainerStyle);
5914
+ anchoredEndSpaceStyle = {
5915
+ [`padding${paddingEnd}`]: extractPadding({}, flattenedContentContainerStyle || {}, paddingEnd) + anchoredEndSpaceSize
5916
+ };
5917
+ }
5655
5918
  const autoOtherAxisStyle = getAutoOtherAxisStyle({
5656
5919
  horizontal,
5657
5920
  needsOtherAxisSize: ctx.state.needsOtherAxisSize,
@@ -5702,12 +5965,12 @@ var ListComponent = typedMemo(function ListComponent2({
5702
5965
  SnapOrScroll,
5703
5966
  {
5704
5967
  ...rest,
5705
- ...ScrollComponent === ListComponentScrollView ? { useWindowScroll } : {},
5968
+ ...Platform.OS === "web" ? ScrollComponent === ListComponentScrollView ? { onInternalScrollEnd, useWindowScroll } : {} : { onScrollBeginDrag: onInternalScrollBeginDrag },
5706
5969
  contentContainerStyle: [
5707
- horizontal ? {
5708
- height: "100%"
5709
- } : {},
5710
- contentContainerStyle
5970
+ horizontal ? { height: "100%" } : {},
5971
+ contentContainerStyle,
5972
+ anchoredEndSpaceStyle,
5973
+ Platform.OS === "web" ? { boxSizing: "border-box" } : void 0
5711
5974
  ],
5712
5975
  contentOffset: initialContentOffset !== void 0 ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
5713
5976
  horizontal,
@@ -5725,6 +5988,7 @@ var ListComponent = typedMemo(function ListComponent2({
5725
5988
  canRender && !ListEmptyComponent && /* @__PURE__ */ React2.createElement(
5726
5989
  Containers,
5727
5990
  {
5991
+ freshDataTransitionEpoch,
5728
5992
  getRenderedItem: getRenderedItem2,
5729
5993
  horizontal,
5730
5994
  ItemSeparatorComponent,
@@ -5733,7 +5997,6 @@ var ListComponent = typedMemo(function ListComponent2({
5733
5997
  }
5734
5998
  ),
5735
5999
  ListFooterComponent && /* @__PURE__ */ React2.createElement(LayoutView, { onLayoutChange: onLayoutFooterInternal, style: ListFooterComponentStyle }, getComponent(ListFooterComponent)),
5736
- Platform.OS === "web" && /* @__PURE__ */ React2.createElement(WebAnchoredEndSpace, { horizontal }),
5737
6000
  IS_DEV && ENABLE_DEVMODE
5738
6001
  );
5739
6002
  });
@@ -5742,7 +6005,16 @@ var WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO = 0.9;
5742
6005
  var WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO = 0.9;
5743
6006
  function useDevChecksImpl(props) {
5744
6007
  const ctx = useStateContext();
5745
- const { childrenMode, keyExtractor, renderScrollComponent, useWindowScroll } = props;
6008
+ const { anchoredEndSpace, childrenMode, keyExtractor, numColumns, renderScrollComponent, useWindowScroll } = props;
6009
+ const hasAnchoredEndSpace = !!anchoredEndSpace;
6010
+ useEffect(() => {
6011
+ if (hasAnchoredEndSpace && (numColumns != null ? numColumns : 1) > 1) {
6012
+ warnDevOnce(
6013
+ "anchoredEndSpaceNumColumns",
6014
+ "anchoredEndSpace is only supported when numColumns is 1. Using it with multiple columns may produce incorrect anchored spacing."
6015
+ );
6016
+ }
6017
+ }, [hasAnchoredEndSpace, numColumns]);
5746
6018
  useEffect(() => {
5747
6019
  if (useWindowScroll && renderScrollComponent) {
5748
6020
  warnDevOnce(
@@ -5808,6 +6080,7 @@ function checkResetContainers(ctx, dataProp, { didColumnsChange = false } = {})
5808
6080
  if (didColumnsChange) {
5809
6081
  state.sizes.clear();
5810
6082
  state.sizesKnown.clear();
6083
+ invalidateContainerFixedItemSizes(state);
5811
6084
  for (const key in state.averageSizes) {
5812
6085
  delete state.averageSizes[key];
5813
6086
  }
@@ -6054,7 +6327,7 @@ function onScroll(ctx, event) {
6054
6327
  }
6055
6328
  }
6056
6329
  state.scrollPending = newScroll;
6057
- updateScroll(ctx, newScroll, insetChanged);
6330
+ updateScroll(ctx, newScroll, insetChanged, { fromNativeScrollEvent: true });
6058
6331
  trackInitialScrollNativeProgress(state, newScroll);
6059
6332
  clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx);
6060
6333
  if (state.scrollingTo) {
@@ -6069,6 +6342,59 @@ function onScroll(ctx, event) {
6069
6342
  }
6070
6343
  }
6071
6344
 
6345
+ // src/core/ScheduledWork.ts
6346
+ var ScheduledWork = class {
6347
+ constructor() {
6348
+ this.work = /* @__PURE__ */ new Map();
6349
+ }
6350
+ timeout(callback, delay, key) {
6351
+ if (key) {
6352
+ this.cancel(key);
6353
+ }
6354
+ const work = [void 0, clearTimeout];
6355
+ const handle = setTimeout(() => {
6356
+ const workKey = key != null ? key : handle;
6357
+ if (this.work.get(workKey) === work) {
6358
+ this.work.delete(workKey);
6359
+ callback();
6360
+ }
6361
+ }, delay);
6362
+ work[0] = handle;
6363
+ this.work.set(key != null ? key : handle, work);
6364
+ }
6365
+ frame(callback, key) {
6366
+ this.cancel(key);
6367
+ const work = [void 0, cancelAnimationFrame];
6368
+ this.work.set(key, work);
6369
+ work[0] = requestAnimationFrame(() => {
6370
+ if (this.work.get(key) === work) {
6371
+ this.work.delete(key);
6372
+ callback();
6373
+ }
6374
+ });
6375
+ }
6376
+ register(key, cancel) {
6377
+ this.cancel(key);
6378
+ this.work.set(key, [void 0, cancel]);
6379
+ }
6380
+ cancel(key) {
6381
+ const work = this.work.get(key);
6382
+ if (work) {
6383
+ this.work.delete(key);
6384
+ work[1](work[0]);
6385
+ }
6386
+ }
6387
+ has(key) {
6388
+ return this.work.has(key);
6389
+ }
6390
+ dispose() {
6391
+ for (const [handle, cancel] of this.work.values()) {
6392
+ cancel(handle);
6393
+ }
6394
+ this.work.clear();
6395
+ }
6396
+ };
6397
+
6072
6398
  // src/core/ScrollAdjustHandler.ts
6073
6399
  var ScrollAdjustHandler = class {
6074
6400
  constructor(ctx) {
@@ -6156,7 +6482,6 @@ var useCombinedRef = (...refs) => {
6156
6482
  }, refs);
6157
6483
  return callback;
6158
6484
  };
6159
- var StyleSheet = StyleSheet$1;
6160
6485
  function useStickyScrollHandler(stickyHeaderIndices, horizontal, ctx, onScroll2) {
6161
6486
  const shouldUseRnAnimatedEngine = !ctx.state.props.stickyPositionComponentInternal;
6162
6487
  return useMemo(() => {
@@ -6230,8 +6555,12 @@ function getAverageItemSizes(state) {
6230
6555
  return averageItemSizes;
6231
6556
  }
6232
6557
  function triggerMountedContainerLayouts(ctx) {
6233
- for (const triggerLayout of ctx.containerLayoutTriggers.values()) {
6234
- triggerLayout();
6558
+ if (IsNewArchitecture) {
6559
+ scheduleContainerLayout(ctx);
6560
+ } else {
6561
+ for (const triggerLayout of ctx.containerLayoutTriggers.values()) {
6562
+ triggerLayout();
6563
+ }
6235
6564
  }
6236
6565
  }
6237
6566
  function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
@@ -6239,7 +6568,7 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
6239
6568
  const IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS = 800;
6240
6569
  const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
6241
6570
  let imperativeScrollToken = 0;
6242
- const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0;
6571
+ const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.scheduledWork.has("mvcpRecalculate") || state.ignoreScrollFromMVCP !== void 0;
6243
6572
  const isScrollToIndexReady = (targetIndex, allowEmpty = false) => {
6244
6573
  var _a3;
6245
6574
  const props = state.props;
@@ -6273,9 +6602,9 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
6273
6602
  run();
6274
6603
  return;
6275
6604
  }
6276
- requestAnimationFrame(check);
6605
+ state.scheduledWork.frame(check, "imperativeScrollReady");
6277
6606
  };
6278
- requestAnimationFrame(check);
6607
+ state.scheduledWork.frame(check, "imperativeScrollReady");
6279
6608
  };
6280
6609
  const runScrollRequest = (token, resolve, run, isReady = () => true) => {
6281
6610
  const runNow = () => {
@@ -6297,10 +6626,9 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
6297
6626
  }
6298
6627
  };
6299
6628
  const startImperativeScroll = (resolve) => {
6300
- var _a3;
6629
+ state.scheduledWork.cancel("imperativeScrollReady");
6301
6630
  const token = ++imperativeScrollToken;
6302
- state.pendingScrollToEnd = void 0;
6303
- (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
6631
+ settlePendingImperativeScroll(state);
6304
6632
  state.pendingScrollResolve = resolve;
6305
6633
  return token;
6306
6634
  };
@@ -6344,6 +6672,7 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
6344
6672
  const mode = (_a3 = options == null ? void 0 : options.mode) != null ? _a3 : "sizes";
6345
6673
  state.sizes.clear();
6346
6674
  state.sizesKnown.clear();
6675
+ invalidateContainerFixedItemSizes(state);
6347
6676
  for (const key in state.averageSizes) {
6348
6677
  delete state.averageSizes[key];
6349
6678
  }
@@ -6365,6 +6694,10 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
6365
6694
  return {
6366
6695
  clearCaches,
6367
6696
  flashScrollIndicators: () => refScroller.current.flashScrollIndicators(),
6697
+ getAnimatableRef: () => {
6698
+ var _a3, _b, _c;
6699
+ return (_c = (_b = (_a3 = refScroller.current).getNativeScrollRef) == null ? void 0 : _b.call(_a3)) != null ? _c : refScroller.current;
6700
+ },
6368
6701
  getNativeScrollRef: () => refScroller.current,
6369
6702
  getScrollableNode: () => refScroller.current.getScrollableNode(),
6370
6703
  getScrollResponder: () => refScroller.current.getScrollResponder(),
@@ -6379,6 +6712,7 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
6379
6712
  end: state.endNoBuffer,
6380
6713
  endBuffered: state.endBuffered,
6381
6714
  getAverageItemSizes: () => getAverageItemSizes(state),
6715
+ indexByKey: (key) => state.indexByKey.get(key),
6382
6716
  isAtEnd: peek$(ctx, "isAtEnd"),
6383
6717
  isAtStart: peek$(ctx, "isAtStart"),
6384
6718
  isEndReached: state.isEndReached,
@@ -6682,7 +7016,7 @@ var LegendList = typedMemo(
6682
7016
  })
6683
7017
  );
6684
7018
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
6685
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
7019
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s;
6686
7020
  const noopOnScroll = useCallback((_event) => {
6687
7021
  }, []);
6688
7022
  if (props.recycleItems === void 0) {
@@ -6735,6 +7069,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6735
7069
  onMomentumScrollEnd,
6736
7070
  onRefresh,
6737
7071
  onScroll: onScrollProp,
7072
+ onScrollBeginDrag,
6738
7073
  onStartReached,
6739
7074
  onStartReachedThreshold = 0.5,
6740
7075
  onStickyHeaderChange,
@@ -6757,9 +7092,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6757
7092
  ...rest
6758
7093
  } = props;
6759
7094
  const animatedPropsInternal = props.animatedPropsInternal;
7095
+ const anchoredEndSpaceOwner = (_a3 = props.anchoredEndSpaceOwnerInternal) != null ? _a3 : "list";
6760
7096
  const positionComponentInternal = props.positionComponentInternal;
6761
7097
  const stickyPositionComponentInternal = props.stickyPositionComponentInternal;
6762
7098
  const {
7099
+ anchoredEndSpaceOwnerInternal: _anchoredEndSpaceOwnerInternal,
6763
7100
  positionComponentInternal: _positionComponentInternal,
6764
7101
  stickyPositionComponentInternal: _stickyPositionComponentInternal,
6765
7102
  ...restProps
@@ -6781,6 +7118,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6781
7118
  const stylePaddingBottomState = extractPadding(style, contentContainerStyle, "Bottom");
6782
7119
  const stylePaddingLeftState = extractPadding(style, contentContainerStyle, "Left");
6783
7120
  const stylePaddingRightState = extractPadding(style, contentContainerStyle, "Right");
7121
+ const stylePaddingEndState = getStylePaddingEnd({
7122
+ horizontal,
7123
+ rtl,
7124
+ stylePaddingBottom: stylePaddingBottomState,
7125
+ stylePaddingLeft: stylePaddingLeftState,
7126
+ stylePaddingRight: stylePaddingRightState
7127
+ });
6784
7128
  const maintainScrollAtEndConfig = normalizeMaintainScrollAtEnd(maintainScrollAtEnd);
6785
7129
  const maintainVisibleContentPositionConfig = normalizeMaintainVisibleContentPosition(
6786
7130
  maintainVisibleContentPositionProp
@@ -6793,13 +7137,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6793
7137
  const initialScrollProp = initialScrollAtEnd ? {
6794
7138
  index: Math.max(0, dataProp.length - 1),
6795
7139
  preserveForBottomPadding: true,
6796
- viewOffset: -stylePaddingBottomState,
7140
+ viewOffset: -stylePaddingEndState,
6797
7141
  viewPosition: 1
6798
7142
  } : hasInitialScrollIndex ? typeof initialScrollIndexProp === "object" ? {
6799
- index: (_a3 = initialScrollIndexProp.index) != null ? _a3 : 0,
7143
+ index: (_b = initialScrollIndexProp.index) != null ? _b : 0,
6800
7144
  preserveForBottomPadding: initialScrollIndexProp.viewOffset === void 0 && initialScrollIndexProp.viewPosition === 1 ? true : void 0,
6801
- viewOffset: (_b = initialScrollIndexProp.viewOffset) != null ? _b : initialScrollIndexProp.viewPosition === 1 ? -stylePaddingBottomState : 0,
6802
- viewPosition: (_c = initialScrollIndexProp.viewPosition) != null ? _c : 0
7145
+ viewOffset: (_c = initialScrollIndexProp.viewOffset) != null ? _c : initialScrollIndexProp.viewPosition === 1 ? -stylePaddingEndState : 0,
7146
+ viewPosition: (_d = initialScrollIndexProp.viewPosition) != null ? _d : 0
6803
7147
  } : {
6804
7148
  index: initialScrollIndexProp != null ? initialScrollIndexProp : 0,
6805
7149
  viewOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : 0
@@ -6812,7 +7156,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6812
7156
  const [, scheduleImperativeScrollCommit] = React2.useReducer((value) => value + 1, 0);
6813
7157
  const ctx = useStateContext();
6814
7158
  ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
6815
- const scrollAxisGap = horizontal ? (_f = (_d = ctx.columnWrapperStyle) == null ? void 0 : _d.columnGap) != null ? _f : (_e = ctx.columnWrapperStyle) == null ? void 0 : _e.gap : (_i = (_g = ctx.columnWrapperStyle) == null ? void 0 : _g.rowGap) != null ? _i : (_h = ctx.columnWrapperStyle) == null ? void 0 : _h.gap;
7159
+ const scrollAxisGap = horizontal ? (_g = (_e = ctx.columnWrapperStyle) == null ? void 0 : _e.columnGap) != null ? _g : (_f = ctx.columnWrapperStyle) == null ? void 0 : _f.gap : (_j = (_h = ctx.columnWrapperStyle) == null ? void 0 : _h.rowGap) != null ? _j : (_i = ctx.columnWrapperStyle) == null ? void 0 : _i.gap;
6816
7160
  const nextScrollAxisGap = typeof scrollAxisGap === "number" && Number.isFinite(scrollAxisGap) ? scrollAxisGap : 0;
6817
7161
  const refScroller = useRef(null);
6818
7162
  const combinedRef = useCombinedRef(refScroller, refScrollView);
@@ -6827,8 +7171,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6827
7171
  anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex,
6828
7172
  alwaysRender == null ? void 0 : alwaysRender.top,
6829
7173
  alwaysRender == null ? void 0 : alwaysRender.bottom,
6830
- (_j = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _j.join(","),
6831
- (_k = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _k.join(","),
7174
+ (_k = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _k.join(","),
7175
+ (_l = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _l.join(","),
6832
7176
  dataProp,
6833
7177
  dataKey,
6834
7178
  dataVersion,
@@ -6841,13 +7185,14 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6841
7185
  if (!refState.current) {
6842
7186
  if (!ctx.state) {
6843
7187
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : IsNewArchitecture ? { height: 0, width: 0 } : getWindowSize())[horizontal ? "width" : "height"];
6844
- ctx.values.set("adaptiveRender", (_l = experimental_adaptiveRender == null ? void 0 : experimental_adaptiveRender.initialMode) != null ? _l : "normal");
7188
+ ctx.values.set("adaptiveRender", (_m = experimental_adaptiveRender == null ? void 0 : experimental_adaptiveRender.initialMode) != null ? _m : "normal");
6845
7189
  ctx.state = {
6846
7190
  averageSizes: {},
6847
7191
  columnSpans: [],
6848
7192
  columns: [],
7193
+ containerItemGenerations: [],
6849
7194
  containerItemKeys: /* @__PURE__ */ new Map(),
6850
- containerItemTypes: /* @__PURE__ */ new Map(),
7195
+ containerItemMetadata: /* @__PURE__ */ new Map(),
6851
7196
  contentInsetOverride: void 0,
6852
7197
  dataChangeEpoch: 0,
6853
7198
  dataChangeNeedsScrollUpdate: false,
@@ -6859,6 +7204,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6859
7204
  endNoBuffer: -1,
6860
7205
  endReachedSnapshot: void 0,
6861
7206
  firstFullyOnScreenIndex: -1,
7207
+ freshDataTransitionEpoch: 0,
6862
7208
  hasHadNonEmptyData: dataProp.length > 0,
6863
7209
  idCache: [],
6864
7210
  idsInView: [],
@@ -6883,8 +7229,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6883
7229
  positions: [],
6884
7230
  props: {},
6885
7231
  queuedCalculateItemsInView: 0,
6886
- queuedFullDrawDistancePrewarm: void 0,
6887
7232
  refScroller: { current: null },
7233
+ scheduledWork: new ScheduledWork(),
6888
7234
  scroll: 0,
6889
7235
  scrollAdjustHandler: new ScrollAdjustHandler(ctx),
6890
7236
  scrollForNextCalculateItemsInView: void 0,
@@ -6900,11 +7246,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6900
7246
  startBuffered: -1,
6901
7247
  startNoBuffer: -1,
6902
7248
  startReachedSnapshot: void 0,
6903
- startReachedSnapshotDataChangeEpoch: void 0,
6904
7249
  stickyContainerPool: /* @__PURE__ */ new Set(),
6905
7250
  stickyContainers: /* @__PURE__ */ new Map(),
6906
- timeoutAdaptiveRender: void 0,
6907
- timeouts: /* @__PURE__ */ new Set(),
6908
7251
  totalSize: 0,
6909
7252
  viewabilityConfigCallbackPairs: void 0
6910
7253
  };
@@ -6926,13 +7269,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6926
7269
  const didScrollAxisGapChange = !isFirstLocal && ctx.scrollAxisGap !== nextScrollAxisGap;
6927
7270
  ctx.scrollAxisGap = nextScrollAxisGap;
6928
7271
  state.didColumnsChange = numColumnsProp !== previousNumColumnsProp || didScrollAxisGapChange;
6929
- const previousDataLength = (_n = (_m = state.props.data) == null ? void 0 : _m.length) != null ? _n : 0;
7272
+ const previousDataLength = (_o = (_n = state.props.data) == null ? void 0 : _n.length) != null ? _o : 0;
6930
7273
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
6931
7274
  const didDataKeyChangeLocal = state.props.dataKey !== dataKey;
6932
7275
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
6933
7276
  const didDataChangeLocal = didDataKeyChangeLocal || didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
6934
7277
  const shouldResetFreshDataLayout = !isFirstLocal && didDataChangeLocal && state.hasHadNonEmptyData && (didDataKeyChangeLocal || previousDataLength === 0) && dataProp.length > 0;
6935
- if (didDataChangeLocal && !initialScrollAtEnd && state.didFinishInitialScroll && ((_o = state.initialScroll) == null ? void 0 : _o.viewPosition) === 1 && state.props.data.length > 0) {
7278
+ if (didDataChangeLocal && !initialScrollAtEnd && state.didFinishInitialScroll && ((_p = state.initialScroll) == null ? void 0 : _p.viewPosition) === 1 && state.props.data.length > 0) {
6936
7279
  clearPreservedInitialScrollTarget(state);
6937
7280
  }
6938
7281
  if (didDataChangeLocal) {
@@ -6941,10 +7284,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6941
7284
  state.didDataChange = true;
6942
7285
  state.previousData = state.props.data;
6943
7286
  }
7287
+ if (shouldResetFreshDataLayout) {
7288
+ state.freshDataTransitionEpoch += 1;
7289
+ }
6944
7290
  const throttledOnScroll = useThrottledOnScroll(onScrollProp != null ? onScrollProp : noopOnScroll, scrollEventThrottle != null ? scrollEventThrottle : 0);
6945
7291
  const throttleScrollFn = scrollEventThrottle && onScrollProp ? throttledOnScroll : onScrollProp;
6946
- const anchoredEndSpaceResolved = Platform.OS === "web" && anchoredEndSpace ? { ...anchoredEndSpace, includeInEndInset: true } : anchoredEndSpace;
6947
- const didAnchoredEndSpaceAnchorIndexChange = !isFirstLocal && !didDataChangeLocal && ((_p = state.props.anchoredEndSpace) == null ? void 0 : _p.anchorIndex) !== (anchoredEndSpaceResolved == null ? void 0 : anchoredEndSpaceResolved.anchorIndex);
7292
+ const didAnchoredEndSpaceAnchorIndexChange = !isFirstLocal && !didDataChangeLocal && ((_q = state.props.anchoredEndSpace) == null ? void 0 : _q.anchorIndex) !== (anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex);
6948
7293
  state.props = {
6949
7294
  adaptiveRender: experimental_adaptiveRender,
6950
7295
  alignItemsAtEnd,
@@ -6952,7 +7297,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6952
7297
  alwaysRender,
6953
7298
  alwaysRenderIndicesArr: alwaysRenderIndices.arr,
6954
7299
  alwaysRenderIndicesSet: alwaysRenderIndices.set,
6955
- anchoredEndSpace: anchoredEndSpaceResolved,
7300
+ anchoredEndSpace,
7301
+ anchoredEndSpaceOwner,
6956
7302
  animatedProps: animatedPropsInternal,
6957
7303
  contentContainerAlignItems: contentContainerStyle.alignItems,
6958
7304
  contentInset,
@@ -6976,7 +7322,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6976
7322
  onFirstVisibleItemChanged,
6977
7323
  onItemSizeChanged,
6978
7324
  onLoad,
7325
+ onMomentumScrollEnd,
6979
7326
  onScroll: throttleScrollFn,
7327
+ onScrollBeginDrag,
6980
7328
  onStartReached,
6981
7329
  onStartReachedThreshold,
6982
7330
  onStickyHeaderChange,
@@ -6999,12 +7347,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6999
7347
  if (!isFirstLocal && previousAdaptiveRender && !experimental_adaptiveRender) {
7000
7348
  resetAdaptiveRender(ctx);
7001
7349
  }
7002
- if (shouldResetFreshDataLayout) {
7003
- resetInitialRenderState(ctx, {
7004
- resetInitialScroll: !!initialScrollProp,
7005
- resetLayout: true
7006
- });
7007
- }
7008
7350
  const memoizedLastItemKeys = useMemo(() => {
7009
7351
  if (!dataProp.length) return [];
7010
7352
  return Array.from(
@@ -7068,6 +7410,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7068
7410
  useDevChecks(props);
7069
7411
  }
7070
7412
  useLayoutEffect(() => {
7413
+ if (shouldResetFreshDataLayout) {
7414
+ resetInitialRenderState(ctx, {
7415
+ resetInitialScroll: !!initialScrollProp,
7416
+ resetLayout: true
7417
+ });
7418
+ }
7071
7419
  handleInitialScrollDataChange(ctx, {
7072
7420
  dataLength: dataProp.length,
7073
7421
  didDataChange: didDataChangeLocal,
@@ -7075,7 +7423,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7075
7423
  initialScrollAtEnd,
7076
7424
  latestInitialScroll: initialScrollProp,
7077
7425
  latestInitialScrollSessionKind: initialScrollUsesOffsetOnly ? "offset" : "bootstrap",
7078
- stylePaddingBottom: stylePaddingBottomState,
7426
+ stylePaddingEnd: stylePaddingEndState,
7079
7427
  useBootstrapInitialScroll: usesBootstrapInitialScroll
7080
7428
  });
7081
7429
  }, [
@@ -7084,7 +7432,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7084
7432
  didDataChangeLocal,
7085
7433
  shouldResetFreshDataLayout,
7086
7434
  initialScrollAtEnd,
7087
- stylePaddingBottomState,
7435
+ stylePaddingEndState,
7088
7436
  usesBootstrapInitialScroll
7089
7437
  ]);
7090
7438
  useLayoutEffect(() => {
@@ -7102,7 +7450,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7102
7450
  anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorMaxSize,
7103
7451
  anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorOffset,
7104
7452
  didAnchoredEndSpaceAnchorIndexChange,
7105
- numColumnsProp
7453
+ horizontal,
7454
+ numColumnsProp,
7455
+ rtl,
7456
+ stylePaddingBottomState,
7457
+ stylePaddingLeftState,
7458
+ stylePaddingRightState
7106
7459
  ]);
7107
7460
  useLayoutEffect(() => {
7108
7461
  const previousContentInsetEndAdjustment = previousContentInsetEndAdjustmentRef.current;
@@ -7118,10 +7471,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7118
7471
  dataLength: dataProp.length,
7119
7472
  footerSize: layout[horizontal ? "width" : "height"],
7120
7473
  initialScrollAtEnd,
7121
- stylePaddingBottom: stylePaddingBottomState
7474
+ stylePaddingEnd: stylePaddingEndState
7122
7475
  });
7123
7476
  },
7124
- [dataProp.length, initialScrollAtEnd, horizontal, stylePaddingBottomState, usesBootstrapInitialScroll]
7477
+ [dataProp.length, initialScrollAtEnd, horizontal, stylePaddingEndState, usesBootstrapInitialScroll]
7125
7478
  );
7126
7479
  const onLayoutChange = useCallback(
7127
7480
  (layout, fromLayoutEffect) => {
@@ -7138,7 +7491,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7138
7491
  }
7139
7492
  advanceCurrentInitialScrollSession(ctx);
7140
7493
  },
7141
- [dataProp.length, initialScrollAtEnd, stylePaddingBottomState, usesBootstrapInitialScroll]
7494
+ [dataProp.length, initialScrollAtEnd, stylePaddingEndState, usesBootstrapInitialScroll]
7142
7495
  );
7143
7496
  const { onLayout } = useOnLayoutSync({
7144
7497
  onLayoutChange,
@@ -7234,14 +7587,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7234
7587
  useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx, scheduleImperativeScrollCommit), []);
7235
7588
  useEffect(() => {
7236
7589
  return () => {
7237
- if (state.queuedFullDrawDistancePrewarm !== void 0) {
7238
- cancelAnimationFrame(state.queuedFullDrawDistancePrewarm);
7239
- state.queuedFullDrawDistancePrewarm = void 0;
7240
- }
7241
- for (const timeout of state.timeouts) {
7242
- clearTimeout(timeout);
7243
- }
7244
- state.timeouts.clear();
7590
+ cancelImperativeScroll(state);
7591
+ state.scheduledWork.dispose();
7245
7592
  };
7246
7593
  }, [state]);
7247
7594
  useLayoutEffect(() => {
@@ -7259,11 +7606,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7259
7606
  getRenderedItem: (key) => getRenderedItem(ctx, key),
7260
7607
  onMomentumScrollEnd: (event) => {
7261
7608
  checkFinishedScrollFallback(ctx);
7262
- if (onMomentumScrollEnd) {
7263
- onMomentumScrollEnd(event);
7609
+ if (state.props.onMomentumScrollEnd) {
7610
+ state.props.onMomentumScrollEnd(event);
7264
7611
  }
7265
7612
  },
7266
- onScroll: (event) => onScroll(ctx, event)
7613
+ onScroll: (event) => onScroll(ctx, event),
7614
+ onScrollBeginDrag: (event) => {
7615
+ var _a4, _b2;
7616
+ prepareReachedEdgeForNextUserScroll(ctx);
7617
+ (_b2 = (_a4 = state.props).onScrollBeginDrag) == null ? void 0 : _b2.call(_a4, event);
7618
+ },
7619
+ onScrollEnd: () => prepareReachedEdgeForNextUserScroll(ctx)
7267
7620
  }),
7268
7621
  []
7269
7622
  );
@@ -7277,6 +7630,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7277
7630
  canRender,
7278
7631
  contentContainerStyle,
7279
7632
  contentInset,
7633
+ freshDataTransitionEpoch: state.freshDataTransitionEpoch,
7280
7634
  getRenderedItem: fns.getRenderedItem,
7281
7635
  horizontal,
7282
7636
  initialContentOffset,
@@ -7284,13 +7638,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7284
7638
  ListFooterComponent,
7285
7639
  ListFooterComponentStyle,
7286
7640
  ListHeaderComponent,
7641
+ onInternalScrollBeginDrag: fns.onScrollBeginDrag,
7642
+ onInternalScrollEnd: fns.onScrollEnd,
7287
7643
  onLayout,
7288
7644
  onLayoutFooter,
7289
7645
  onMomentumScrollEnd: fns.onMomentumScrollEnd,
7290
7646
  onScroll: onScrollHandler,
7291
7647
  recycleItems,
7292
7648
  refreshControl: refreshControlElement ? stylePaddingTopState > 0 ? React2.cloneElement(refreshControlElement, {
7293
- progressViewOffset: ((_q = refreshControlElement.props.progressViewOffset) != null ? _q : 0) + stylePaddingTopState
7649
+ progressViewOffset: ((_r = refreshControlElement.props.progressViewOffset) != null ? _r : 0) + stylePaddingTopState
7294
7650
  }) : refreshControlElement : onRefresh && /* @__PURE__ */ React2.createElement(
7295
7651
  RefreshControl,
7296
7652
  {
@@ -7301,7 +7657,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7301
7657
  ),
7302
7658
  refScrollView: combinedRef,
7303
7659
  renderScrollComponent,
7304
- scrollAdjustHandler: (_r = refState.current) == null ? void 0 : _r.scrollAdjustHandler,
7660
+ scrollAdjustHandler: (_s = refState.current) == null ? void 0 : _s.scrollAdjustHandler,
7305
7661
  scrollEventThrottle: 0,
7306
7662
  snapToIndices,
7307
7663
  stickyHeaderIndices,