@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.js CHANGED
@@ -62,6 +62,12 @@ function isHorizontalRTL(state) {
62
62
  function isHorizontalRTLProps(props) {
63
63
  return !!(props == null ? void 0 : props.horizontal) && isRTLProps(props);
64
64
  }
65
+ function getStylePaddingEnd(props) {
66
+ if (!(props == null ? void 0 : props.horizontal)) {
67
+ return (props == null ? void 0 : props.stylePaddingBottom) || 0;
68
+ }
69
+ return (isHorizontalRTLProps(props) ? props.stylePaddingLeft : props.stylePaddingRight) || 0;
70
+ }
65
71
  function getLogicalHorizontalMaxOffset(state, contentWidth) {
66
72
  var _a3;
67
73
  return (_a3 = getHorizontalMaxOffset(state, contentWidth)) != null ? _a3 : 0;
@@ -152,11 +158,13 @@ function StateProvider({ children }) {
152
158
  mapViewabilityCallbacks: /* @__PURE__ */ new Map(),
153
159
  mapViewabilityConfigStates: /* @__PURE__ */ new Map(),
154
160
  mapViewabilityValues: /* @__PURE__ */ new Map(),
161
+ pendingContainerIds: void 0,
155
162
  positionListeners: /* @__PURE__ */ new Map(),
156
163
  scrollAxisGap: 0,
157
164
  state: void 0,
158
165
  values: /* @__PURE__ */ new Map([
159
166
  ["alignItemsAtEndPadding", 0],
167
+ ["containerLayoutEpoch", 0],
160
168
  ["stylePaddingTop", 0],
161
169
  ["headerSize", 0],
162
170
  ["numContainers", 0],
@@ -178,6 +186,14 @@ function useStateContext() {
178
186
  return React2__namespace.useContext(ContextState);
179
187
  }
180
188
  function createSelectorFunctionsArr(ctx, signalNames) {
189
+ if (!ctx) {
190
+ const emptyValues = [];
191
+ return {
192
+ get: () => emptyValues,
193
+ subscribe: () => () => {
194
+ }
195
+ };
196
+ }
181
197
  let lastValues = [];
182
198
  let lastSignalValues = [];
183
199
  return {
@@ -284,7 +300,7 @@ function getContentInsetEndAdjustmentEnd(adjustment) {
284
300
  return Math.max(0, adjustment != null ? adjustment : 0);
285
301
  }
286
302
  function getContentInsetEnd(ctx, contentInsetEndAdjustmentOverride) {
287
- var _a3, _b;
303
+ var _a3;
288
304
  const state = ctx.state;
289
305
  const { props } = state;
290
306
  const horizontal = props.horizontal;
@@ -295,8 +311,8 @@ function getContentInsetEnd(ctx, contentInsetEndAdjustmentOverride) {
295
311
  contentInsetEndAdjustmentOverride != null ? contentInsetEndAdjustmentOverride : props.contentInsetEndAdjustment
296
312
  );
297
313
  const anchoredEndSpaceSize = peek$(ctx, "anchoredEndSpaceSize");
298
- const anchoredEndInset = ((_a3 = props.anchoredEndSpace) == null ? void 0 : _a3.includeInEndInset) && anchoredEndSpaceSize ? anchoredEndSpaceSize : 0;
299
- const overrideInset = (_b = state.contentInsetOverride) != null ? _b : void 0;
314
+ const anchoredEndInset = props.anchoredEndSpace && anchoredEndSpaceSize ? anchoredEndSpaceSize : 0;
315
+ const overrideInset = (_a3 = state.contentInsetOverride) != null ? _a3 : void 0;
300
316
  const adjustedBaseEndInset = baseEndInset + contentInsetEndAdjustment;
301
317
  if (overrideInset) {
302
318
  const mergedInset = { bottom: 0, left: 0, right: 0, ...baseInset, ...overrideInset };
@@ -312,14 +328,15 @@ function getContentInsetEnd(ctx, contentInsetEndAdjustmentOverride) {
312
328
  function getContentSize(ctx) {
313
329
  var _a3, _b;
314
330
  const { values, state } = ctx;
315
- const stylePaddingTop = values.get("stylePaddingTop") || 0;
316
- const stylePaddingBottom = state.props.stylePaddingBottom || 0;
331
+ const stylePaddingStart = state.props.horizontal ? state.props.stylePaddingLeft || 0 : values.get("stylePaddingTop") || 0;
332
+ const stylePaddingEnd = state.props.horizontal ? state.props.stylePaddingRight || 0 : state.props.stylePaddingBottom || 0;
317
333
  const alignItemsAtEndPadding = values.get("alignItemsAtEndPadding") || 0;
318
334
  const headerSize = values.get("headerSize") || 0;
319
335
  const footerSize = values.get("footerSize") || 0;
320
- const contentInsetBottom = getContentInsetEnd(ctx);
336
+ const contentInsetEnd = getContentInsetEnd(ctx);
321
337
  const totalSize = (_b = (_a3 = state.pendingTotalSize) != null ? _a3 : state.totalSize) != null ? _b : values.get("totalSize");
322
- return headerSize + footerSize + totalSize + stylePaddingTop + alignItemsAtEndPadding + stylePaddingBottom + (contentInsetBottom || 0);
338
+ const layoutSize = Math.max(0, totalSize - (state.props.data.length > 0 ? ctx.scrollAxisGap : 0));
339
+ return headerSize + footerSize + layoutSize + stylePaddingStart + alignItemsAtEndPadding + stylePaddingEnd + (contentInsetEnd || 0);
323
340
  }
324
341
 
325
342
  // src/components/DebugView.tsx
@@ -381,28 +398,49 @@ function useInterval(callback, delay) {
381
398
  }, [delay]);
382
399
  }
383
400
 
384
- // src/components/stickyPositionUtils.ts
385
- function getStickyPushLimit(state, index, itemKey) {
386
- if (!itemKey) {
387
- return void 0;
388
- }
389
- const currentSize = state.sizes.get(itemKey);
390
- if (!(currentSize && currentSize > 0)) {
391
- return void 0;
401
+ // src/constants-platform.native.ts
402
+ var f = global.nativeFabricUIManager;
403
+ var IsNewArchitecture = f !== void 0 && f != null;
404
+
405
+ // src/core/containerItemMetadata.ts
406
+ function createContainerItemMetadata(state, itemIndex, itemData, itemType) {
407
+ return {
408
+ dataChangeEpoch: state.dataChangeEpoch,
409
+ getFixedItemSize: state.props.getFixedItemSize,
410
+ getItemType: state.props.getItemType,
411
+ itemData,
412
+ itemIndex,
413
+ itemType
414
+ };
415
+ }
416
+ function resolveContainerItemMetadata(state, containerId, itemIndex, itemData) {
417
+ var _a3, _b;
418
+ const { getFixedItemSize, getItemType } = state.props;
419
+ const previousMetadata = state.containerItemMetadata.get(containerId);
420
+ let metadata;
421
+ if ((previousMetadata == null ? void 0 : previousMetadata.dataChangeEpoch) === state.dataChangeEpoch && previousMetadata.getItemType === getItemType && previousMetadata.itemData === itemData && previousMetadata.itemIndex === itemIndex) {
422
+ metadata = previousMetadata;
423
+ } else {
424
+ const itemType = getItemType ? (_a3 = getItemType(itemData, itemIndex)) != null ? _a3 : "" : void 0;
425
+ metadata = createContainerItemMetadata(state, itemIndex, itemData, itemType);
426
+ state.containerItemMetadata.set(containerId, metadata);
392
427
  }
393
- const stickyIndexInArray = state.props.stickyHeaderIndicesArr.indexOf(index);
394
- if (stickyIndexInArray === -1) {
395
- return void 0;
428
+ if (metadata.getFixedItemSize !== getFixedItemSize) {
429
+ metadata.didResolveFixedItemSize = false;
430
+ metadata.fixedItemSize = void 0;
431
+ metadata.getFixedItemSize = getFixedItemSize;
396
432
  }
397
- const nextStickyIndex = state.props.stickyHeaderIndicesArr[stickyIndexInArray + 1];
398
- if (nextStickyIndex === void 0) {
399
- return void 0;
433
+ if (getFixedItemSize && !metadata.didResolveFixedItemSize) {
434
+ metadata.fixedItemSize = getFixedItemSize(itemData, itemIndex, (_b = metadata.itemType) != null ? _b : "");
435
+ metadata.didResolveFixedItemSize = true;
400
436
  }
401
- const nextStickyPosition = state.positions[nextStickyIndex];
402
- if (nextStickyPosition === void 0) {
403
- return void 0;
437
+ return metadata;
438
+ }
439
+ function invalidateContainerFixedItemSizes(state) {
440
+ for (const metadata of state.containerItemMetadata.values()) {
441
+ metadata.didResolveFixedItemSize = false;
442
+ metadata.fixedItemSize = void 0;
404
443
  }
405
- return nextStickyPosition - currentSize;
406
444
  }
407
445
 
408
446
  // src/utils/devEnvironment.ts
@@ -419,445 +457,73 @@ var EDGE_POSITION_EPSILON = 1;
419
457
  var ENABLE_DEVMODE = IS_DEV && false;
420
458
  var ENABLE_DEBUG_VIEW = IS_DEV && false;
421
459
 
422
- // src/constants-platform.native.ts
423
- var f = global.nativeFabricUIManager;
424
- var IsNewArchitecture = f !== void 0 && f != null;
425
- var useAnimatedValue = (initialValue) => {
426
- const [animAnimatedValue] = React2.useState(() => new ReactNative.Animated.Value(initialValue));
427
- return animAnimatedValue;
428
- };
429
-
430
- // src/hooks/useValue$.ts
431
- function useValue$(key, params) {
432
- const { getValue } = params || {};
433
- const ctx = useStateContext();
434
- const getNewValue = () => {
435
- var _a3;
436
- return (_a3 = getValue ? getValue(peek$(ctx, key)) : peek$(ctx, key)) != null ? _a3 : 0;
460
+ // src/core/deferredPublicOnScroll.ts
461
+ function withResolvedContentOffset(state, event, resolvedOffset) {
462
+ return {
463
+ ...event,
464
+ nativeEvent: {
465
+ ...event.nativeEvent,
466
+ contentOffset: state.props.horizontal ? { x: resolvedOffset, y: 0 } : { x: 0, y: resolvedOffset }
467
+ }
437
468
  };
438
- const animValue = useAnimatedValue(getNewValue());
439
- React2.useLayoutEffect(() => {
440
- const syncCurrentValue = () => {
441
- animValue.setValue(getNewValue());
442
- };
443
- const unsubscribe = listen$(ctx, key, syncCurrentValue);
444
- syncCurrentValue();
445
- return unsubscribe;
446
- }, [animValue, ctx, key]);
447
- return animValue;
448
469
  }
449
- var typedForwardRef = React2__namespace.forwardRef;
450
- var typedMemo = React2__namespace.memo;
451
- var getComponent = (Component) => {
452
- if (React2__namespace.isValidElement(Component)) {
453
- return Component;
454
- }
455
- if (Component) {
456
- return /* @__PURE__ */ React2__namespace.createElement(Component, null);
457
- }
458
- return null;
459
- };
460
-
461
- // src/components/PositionView.native.tsx
462
- var PositionViewState = typedMemo(function PositionViewState2({
463
- id,
464
- horizontal,
465
- style,
466
- refView,
467
- ...rest
468
- }) {
469
- const [position = POSITION_OUT_OF_VIEW, _itemKey] = useArr$([`containerPosition${id}`, `containerItemKey${id}`]);
470
- return /* @__PURE__ */ React2__namespace.createElement(ReactNative.View, { ref: refView, style: [style, horizontal ? { left: position } : { top: position }], ...rest });
471
- });
472
- var PositionViewAnimated = typedMemo(function PositionViewAnimated2({
473
- id,
474
- horizontal,
475
- style,
476
- refView,
477
- ...rest
478
- }) {
479
- const position$ = useValue$(`containerPosition${id}`, {
480
- getValue: (v) => v != null ? v : POSITION_OUT_OF_VIEW
481
- });
482
- const position = horizontal ? { left: position$ } : { top: position$ };
483
- return /* @__PURE__ */ React2__namespace.createElement(ReactNative.Animated.View, { ref: refView, style: [style, position], ...rest });
484
- });
485
- var PositionViewSticky = typedMemo(function PositionViewSticky2({
486
- id,
487
- horizontal,
488
- style,
489
- refView,
490
- animatedScrollY,
491
- index,
492
- stickyHeaderConfig,
493
- children,
494
- ...rest
495
- }) {
496
- const ctx = useStateContext();
497
- const [
498
- position = POSITION_OUT_OF_VIEW,
499
- alignItemsAtEndPadding = 0,
500
- headerSize = 0,
501
- stylePaddingTop = 0,
502
- itemKey,
503
- _totalSize = 0
504
- ] = useArr$([
505
- `containerPosition${id}`,
506
- "alignItemsAtEndPadding",
507
- "headerSize",
508
- "stylePaddingTop",
509
- `containerItemKey${id}`,
510
- "totalSize"
511
- ]);
512
- const pushLimit = React2__namespace.useMemo(
513
- () => getStickyPushLimit(ctx.state, index, itemKey),
514
- [ctx.state, index, itemKey, _totalSize]
515
- );
516
- const transform = React2__namespace.useMemo(() => {
517
- var _a3;
518
- if (animatedScrollY) {
519
- const stickyConfigOffset = (_a3 = stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset) != null ? _a3 : 0;
520
- const stickyStart = position + headerSize + stylePaddingTop + alignItemsAtEndPadding - stickyConfigOffset;
521
- let nextStickyPosition;
522
- if (pushLimit !== void 0) {
523
- if (pushLimit <= position) {
524
- nextStickyPosition = pushLimit;
525
- } else {
526
- nextStickyPosition = animatedScrollY.interpolate({
527
- extrapolateLeft: "clamp",
528
- extrapolateRight: "clamp",
529
- inputRange: [stickyStart, stickyStart + (pushLimit - position)],
530
- outputRange: [position, pushLimit]
531
- });
532
- }
533
- } else {
534
- nextStickyPosition = animatedScrollY.interpolate({
535
- extrapolateLeft: "clamp",
536
- extrapolateRight: "extend",
537
- inputRange: [stickyStart, stickyStart + 5e3],
538
- outputRange: [position, position + 5e3]
539
- });
540
- }
541
- return horizontal ? [{ translateX: nextStickyPosition }] : [{ translateY: nextStickyPosition }];
542
- }
543
- }, [
544
- alignItemsAtEndPadding,
545
- animatedScrollY,
546
- headerSize,
547
- position,
548
- pushLimit,
549
- stylePaddingTop,
550
- stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset
551
- ]);
552
- const viewStyle = React2__namespace.useMemo(() => [style, { zIndex: index + 1e3 }, { transform }], [style, transform]);
553
- const renderStickyHeaderBackdrop = React2__namespace.useMemo(() => {
554
- if (!(stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent)) {
555
- return null;
556
- }
557
- return /* @__PURE__ */ React2__namespace.createElement(
558
- ReactNative.View,
559
- {
560
- style: {
561
- inset: 0,
562
- pointerEvents: "none",
563
- position: "absolute"
564
- }
565
- },
566
- getComponent(stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent)
470
+ function releaseDeferredPublicOnScroll(ctx, resolvedOffset) {
471
+ var _a3, _b, _c, _d;
472
+ const state = ctx.state;
473
+ const deferredEvent = state.deferredPublicOnScrollEvent;
474
+ state.deferredPublicOnScrollEvent = void 0;
475
+ if (deferredEvent) {
476
+ (_d = (_c = state.props).onScroll) == null ? void 0 : _d.call(
477
+ _c,
478
+ withResolvedContentOffset(
479
+ state,
480
+ deferredEvent,
481
+ (_b = (_a3 = resolvedOffset != null ? resolvedOffset : state.scrollPending) != null ? _a3 : state.scroll) != null ? _b : 0
482
+ )
567
483
  );
568
- }, [stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent]);
569
- return /* @__PURE__ */ React2__namespace.createElement(ReactNative.Animated.View, { ref: refView, style: viewStyle, ...rest }, renderStickyHeaderBackdrop, children);
570
- });
571
- var PositionView = IsNewArchitecture ? PositionViewState : PositionViewAnimated;
572
- function useInit(cb) {
573
- React2.useState(() => cb());
484
+ }
574
485
  }
575
486
 
576
- // src/utils/helpers.ts
577
- function isFunction(obj) {
578
- return typeof obj === "function";
487
+ // src/core/initialScrollSession.ts
488
+ var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
489
+ function hasInitialScrollSessionCompletion(completion) {
490
+ return !!((completion == null ? void 0 : completion.didDispatchNativeScroll) || (completion == null ? void 0 : completion.didRetrySilentInitialScroll) || (completion == null ? void 0 : completion.watchdog));
579
491
  }
580
- function isArray(obj) {
581
- return Array.isArray(obj);
492
+ function clearInitialScrollSession(state) {
493
+ state.initialScrollSession = void 0;
494
+ return void 0;
582
495
  }
583
- var warned = /* @__PURE__ */ new Set();
584
- function warnDevOnce(id, text) {
585
- if (IS_DEV && !warned.has(id)) {
586
- warned.add(id);
587
- console.warn(`[legend-list] ${text}`);
588
- }
496
+ function createInitialScrollSession(options) {
497
+ const { bootstrap, completion, kind, previousDataLength } = options;
498
+ return kind === "offset" ? {
499
+ completion,
500
+ kind,
501
+ previousDataLength
502
+ } : {
503
+ bootstrap,
504
+ completion,
505
+ kind,
506
+ previousDataLength
507
+ };
589
508
  }
590
- function roundSize(size) {
591
- return Math.floor(size * 8) / 8;
592
- }
593
- function isNullOrUndefined(value) {
594
- return value === null || value === void 0;
595
- }
596
- function getPadding(s, type) {
597
- var _a3, _b, _c;
598
- const axisPadding = type === "Left" || type === "Right" ? s.paddingHorizontal : s.paddingVertical;
599
- return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : axisPadding) != null ? _b : s.padding) != null ? _c : 0;
600
- }
601
- function extractPadding(style, contentContainerStyle, type) {
602
- return getPadding(style, type) + getPadding(contentContainerStyle, type);
603
- }
604
- function findContainerId(ctx, key) {
605
- var _a3, _b;
606
- const directMatch = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.containerItemKeys) == null ? void 0 : _b.get(key);
607
- if (directMatch !== void 0) {
608
- return directMatch;
609
- }
610
- const numContainers = peek$(ctx, "numContainers");
611
- for (let i = 0; i < numContainers; i++) {
612
- const itemKey = peek$(ctx, `containerItemKey${i}`);
613
- if (itemKey === key) {
614
- return i;
615
- }
616
- }
617
- return -1;
618
- }
619
-
620
- // src/state/ContextContainer.ts
621
- var ContextContainer = React2.createContext(null);
622
- var NO_CONTAINER_ID = -1;
623
- function useContextContainer() {
624
- return React2.useContext(ContextContainer);
625
- }
626
- function useContainerItemInfo(containerContext) {
627
- var _a3;
628
- const containerId = (_a3 = containerContext == null ? void 0 : containerContext.containerId) != null ? _a3 : NO_CONTAINER_ID;
629
- const [itemInfo] = useArr$([`containerItemInfo${containerId}`]);
630
- return containerContext ? itemInfo : void 0;
631
- }
632
- function useContainerItemKey(containerContext) {
633
- var _a3;
634
- const containerId = (_a3 = containerContext == null ? void 0 : containerContext.containerId) != null ? _a3 : NO_CONTAINER_ID;
635
- const [itemKey] = useArr$([`containerItemKey${containerId}`]);
636
- return containerContext ? itemKey : void 0;
637
- }
638
- function useAdaptiveRender() {
639
- const [mode] = useArr$(["adaptiveRender"]);
640
- return mode;
641
- }
642
- function useAdaptiveRenderChange(callback) {
643
- const ctx = useStateContext();
644
- const callbackRef = React2.useRef(callback);
645
- callbackRef.current = callback;
646
- React2.useLayoutEffect(() => {
647
- let mode = peek$(ctx, "adaptiveRender");
648
- return listen$(ctx, "adaptiveRender", (nextMode) => {
649
- if (mode !== nextMode) {
650
- mode = nextMode;
651
- callbackRef.current(nextMode);
652
- }
653
- });
654
- }, [ctx]);
655
- }
656
- function useViewability(callback, configId) {
657
- const ctx = useStateContext();
658
- const containerContext = useContextContainer();
659
- useInit(() => {
660
- if (!containerContext) {
661
- return;
662
- }
663
- const { containerId } = containerContext;
664
- const key = containerId + (configId != null ? configId : "");
665
- const value = ctx.mapViewabilityValues.get(key);
666
- if (value) {
667
- callback(value);
668
- }
669
- });
670
- React2.useEffect(() => {
671
- if (!containerContext) {
672
- return;
673
- }
674
- const { containerId } = containerContext;
675
- const key = containerId + (configId != null ? configId : "");
676
- ctx.mapViewabilityCallbacks.set(key, callback);
677
- return () => {
678
- ctx.mapViewabilityCallbacks.delete(key);
679
- };
680
- }, [ctx, callback, configId, containerContext]);
681
- }
682
- function useViewabilityAmount(callback) {
683
- const ctx = useStateContext();
684
- const containerContext = useContextContainer();
685
- useInit(() => {
686
- if (!containerContext) {
687
- return;
688
- }
689
- const { containerId } = containerContext;
690
- const value = ctx.mapViewabilityAmountValues.get(containerId);
691
- if (value) {
692
- callback(value);
693
- }
694
- });
695
- React2.useEffect(() => {
696
- if (!containerContext) {
697
- return;
698
- }
699
- const { containerId } = containerContext;
700
- ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
701
- return () => {
702
- ctx.mapViewabilityAmountCallbacks.delete(containerId);
703
- };
704
- }, [ctx, callback, containerContext]);
705
- }
706
- function useRecyclingEffect(effect) {
707
- const containerContext = useContextContainer();
708
- const itemInfo = useContainerItemInfo(containerContext);
709
- const prevInfo = React2.useRef(void 0);
710
- React2.useEffect(() => {
711
- if (!itemInfo) {
712
- return;
713
- }
714
- let ret;
715
- if (prevInfo.current) {
716
- ret = effect({
717
- index: itemInfo.index,
718
- item: itemInfo.value,
719
- prevIndex: prevInfo.current.index,
720
- prevItem: prevInfo.current.value
721
- });
722
- }
723
- prevInfo.current = itemInfo;
724
- return ret;
725
- }, [effect, itemInfo]);
726
- }
727
- function useRecyclingState(valueOrFun) {
728
- var _a3;
729
- const containerContext = useContextContainer();
730
- const itemInfo = useContainerItemInfo(containerContext);
731
- const computeValue = (info) => {
732
- if (isFunction(valueOrFun)) {
733
- const initializer = valueOrFun;
734
- return info ? initializer({
735
- index: info.index,
736
- item: info.value,
737
- prevIndex: void 0,
738
- prevItem: void 0
739
- }) : initializer();
740
- }
741
- return valueOrFun;
742
- };
743
- const [stateValue, setStateValue] = React2.useState(() => {
744
- return computeValue(itemInfo);
745
- });
746
- const prevItemKeyRef = React2.useRef((_a3 = itemInfo == null ? void 0 : itemInfo.itemKey) != null ? _a3 : null);
747
- if (itemInfo && prevItemKeyRef.current !== itemInfo.itemKey) {
748
- prevItemKeyRef.current = itemInfo.itemKey;
749
- setStateValue(computeValue(itemInfo));
750
- }
751
- const triggerLayout = containerContext == null ? void 0 : containerContext.triggerLayout;
752
- const setState = React2.useCallback(
753
- (newState) => {
754
- if (!triggerLayout) {
755
- return;
756
- }
757
- setStateValue((prevValue) => {
758
- return isFunction(newState) ? newState(prevValue) : newState;
759
- });
760
- triggerLayout();
761
- },
762
- [triggerLayout]
763
- );
764
- return [stateValue, setState];
765
- }
766
- function useIsLastItem() {
767
- const containerContext = useContextContainer();
768
- const itemKey = useContainerItemKey(containerContext);
769
- const isLast = useSelector$("lastItemKeys", (lastItemKeys) => {
770
- if (containerContext && !isNullOrUndefined(itemKey)) {
771
- return (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false;
772
- }
773
- return false;
774
- });
775
- return isLast;
776
- }
777
- function useListScrollSize() {
778
- const [scrollSize] = useArr$(["scrollSize"]);
779
- return scrollSize;
780
- }
781
- var noop = () => {
782
- };
783
- function useSyncLayout() {
784
- const containerContext = useContextContainer();
785
- return IsNewArchitecture && containerContext ? containerContext.triggerLayout : noop;
786
- }
787
-
788
- // src/components/Separator.tsx
789
- function Separator({ ItemSeparatorComponent, leadingItem }) {
790
- const isLastItem = useIsLastItem();
791
- return isLastItem ? null : /* @__PURE__ */ React2__namespace.createElement(ItemSeparatorComponent, { leadingItem });
792
- }
793
-
794
- // src/core/deferredPublicOnScroll.ts
795
- function withResolvedContentOffset(state, event, resolvedOffset) {
796
- return {
797
- ...event,
798
- nativeEvent: {
799
- ...event.nativeEvent,
800
- contentOffset: state.props.horizontal ? { x: resolvedOffset, y: 0 } : { x: 0, y: resolvedOffset }
801
- }
802
- };
803
- }
804
- function releaseDeferredPublicOnScroll(ctx, resolvedOffset) {
805
- var _a3, _b, _c, _d;
806
- const state = ctx.state;
807
- const deferredEvent = state.deferredPublicOnScrollEvent;
808
- state.deferredPublicOnScrollEvent = void 0;
809
- if (deferredEvent) {
810
- (_d = (_c = state.props).onScroll) == null ? void 0 : _d.call(
811
- _c,
812
- withResolvedContentOffset(
813
- state,
814
- deferredEvent,
815
- (_b = (_a3 = resolvedOffset != null ? resolvedOffset : state.scrollPending) != null ? _a3 : state.scroll) != null ? _b : 0
816
- )
817
- );
818
- }
819
- }
820
-
821
- // src/core/initialScrollSession.ts
822
- var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
823
- function hasInitialScrollSessionCompletion(completion) {
824
- return !!((completion == null ? void 0 : completion.didDispatchNativeScroll) || (completion == null ? void 0 : completion.didRetrySilentInitialScroll) || (completion == null ? void 0 : completion.watchdog));
825
- }
826
- function clearInitialScrollSession(state) {
827
- state.initialScrollSession = void 0;
828
- return void 0;
829
- }
830
- function createInitialScrollSession(options) {
831
- const { bootstrap, completion, kind, previousDataLength } = options;
832
- return kind === "offset" ? {
833
- completion,
834
- kind,
835
- previousDataLength
836
- } : {
837
- bootstrap,
838
- completion,
839
- kind,
840
- previousDataLength
841
- };
842
- }
843
- function ensureInitialScrollSessionCompletion(state, kind = ((_b) => (_b = ((_a3) => (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind)()) != null ? _b : "bootstrap")()) {
844
- var _a4, _b2;
845
- if (!state.initialScrollSession) {
846
- state.initialScrollSession = createInitialScrollSession({
847
- completion: {},
848
- kind,
849
- previousDataLength: 0
850
- });
851
- } else if (state.initialScrollSession.kind !== kind) {
852
- state.initialScrollSession = createInitialScrollSession({
853
- bootstrap: state.initialScrollSession.kind === "bootstrap" ? state.initialScrollSession.bootstrap : void 0,
854
- completion: state.initialScrollSession.completion,
855
- kind,
856
- previousDataLength: state.initialScrollSession.previousDataLength
857
- });
858
- }
859
- (_b2 = (_a4 = state.initialScrollSession).completion) != null ? _b2 : _a4.completion = {};
860
- return state.initialScrollSession.completion;
509
+ function ensureInitialScrollSessionCompletion(state, kind = ((_b) => (_b = ((_a3) => (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind)()) != null ? _b : "bootstrap")()) {
510
+ var _a4, _b2;
511
+ if (!state.initialScrollSession) {
512
+ state.initialScrollSession = createInitialScrollSession({
513
+ completion: {},
514
+ kind,
515
+ previousDataLength: 0
516
+ });
517
+ } else if (state.initialScrollSession.kind !== kind) {
518
+ state.initialScrollSession = createInitialScrollSession({
519
+ bootstrap: state.initialScrollSession.kind === "bootstrap" ? state.initialScrollSession.bootstrap : void 0,
520
+ completion: state.initialScrollSession.completion,
521
+ kind,
522
+ previousDataLength: state.initialScrollSession.previousDataLength
523
+ });
524
+ }
525
+ (_b2 = (_a4 = state.initialScrollSession).completion) != null ? _b2 : _a4.completion = {};
526
+ return state.initialScrollSession.completion;
861
527
  }
862
528
  var initialScrollCompletion = {
863
529
  didDispatchNativeScroll(state) {
@@ -938,7 +604,11 @@ function setInitialScrollSession(state, options = {}) {
938
604
 
939
605
  // src/utils/checkThreshold.ts
940
606
  var HYSTERESIS_MULTIPLIER = 1.3;
941
- var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot, allowReentryOnChange) => {
607
+ function isOutsideThresholdHysteresis(distance, atThreshold, threshold) {
608
+ const absDistance = Math.abs(distance);
609
+ return !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
610
+ }
611
+ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot) => {
942
612
  const absDistance = Math.abs(distance);
943
613
  const within = atThreshold || threshold > 0 && absDistance <= threshold;
944
614
  const updateSnapshot = () => {
@@ -957,7 +627,7 @@ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, co
957
627
  updateSnapshot();
958
628
  return true;
959
629
  }
960
- const reset = !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
630
+ const reset = isOutsideThresholdHysteresis(distance, atThreshold, threshold);
961
631
  if (reset) {
962
632
  setSnapshot(void 0);
963
633
  return false;
@@ -965,23 +635,69 @@ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, co
965
635
  if (within) {
966
636
  const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
967
637
  if (changed) {
968
- if (allowReentryOnChange) {
969
- onReached(distance);
970
- }
971
638
  updateSnapshot();
972
639
  }
973
640
  }
974
641
  return true;
975
642
  };
976
643
 
977
- // src/utils/hasActiveInitialScroll.ts
978
- function hasActiveInitialScroll(state) {
979
- return !!(state == null ? void 0 : state.initialScroll) && !state.didFinishInitialScroll;
980
- }
981
-
982
- // src/utils/checkAtBottom.ts
983
- function checkAtBottom(ctx) {
984
- var _a3;
644
+ // src/utils/edgeReachedGate.ts
645
+ function resetEdgeLatch(ctx, edge) {
646
+ const state = ctx.state;
647
+ if (edge === "start") {
648
+ state.isStartReached = false;
649
+ state.startReachedSnapshot = void 0;
650
+ } else {
651
+ state.isEndReached = false;
652
+ state.endReachedSnapshot = void 0;
653
+ }
654
+ }
655
+ function resetSharedEdgeGateIfOutsideHysteresis(ctx) {
656
+ const state = ctx.state;
657
+ if (!state.edgeReachedGate) {
658
+ return;
659
+ }
660
+ const contentSize = getContentSize(ctx);
661
+ const endDistance = contentSize - state.scroll - state.scrollLength - getContentInsetEnd(ctx);
662
+ const isContentLess = contentSize < state.scrollLength;
663
+ const startThreshold = state.props.onStartReachedThreshold * state.scrollLength;
664
+ const endThreshold = state.props.onEndReachedThreshold * state.scrollLength;
665
+ const isOutsideStart = isOutsideThresholdHysteresis(state.scroll, false, startThreshold);
666
+ const isOutsideEnd = isOutsideThresholdHysteresis(endDistance, isContentLess, endThreshold);
667
+ if (isOutsideStart && isOutsideEnd) {
668
+ state.edgeReachedGate = void 0;
669
+ }
670
+ }
671
+ function canDispatchReachedEdge(ctx, edge, allowedEdge, allowGateCreatedInCurrentCheck) {
672
+ return !ctx.state.edgeReachedGate || allowedEdge === edge || !!allowGateCreatedInCurrentCheck;
673
+ }
674
+ function markReachedEdge(ctx) {
675
+ ctx.state.edgeReachedGate = "closed";
676
+ }
677
+ function prepareReachedEdgeForNextUserScroll(ctx) {
678
+ if (ctx.state.edgeReachedGate) {
679
+ ctx.state.edgeReachedGate = "prepared";
680
+ }
681
+ }
682
+ function beginReachedEdgeUserScroll(ctx, scrollDelta) {
683
+ const state = ctx.state;
684
+ if (state.edgeReachedGate !== "prepared") {
685
+ return void 0;
686
+ }
687
+ const allowedEdge = scrollDelta < 0 ? "start" : "end";
688
+ state.edgeReachedGate = "closed";
689
+ resetEdgeLatch(ctx, allowedEdge);
690
+ return allowedEdge;
691
+ }
692
+
693
+ // src/utils/hasActiveInitialScroll.ts
694
+ function hasActiveInitialScroll(state) {
695
+ return !!(state == null ? void 0 : state.initialScroll) && !state.didFinishInitialScroll;
696
+ }
697
+
698
+ // src/utils/checkAtBottom.ts
699
+ function checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
700
+ var _a3;
985
701
  const state = ctx.state;
986
702
  if (!state) {
987
703
  return;
@@ -994,6 +710,7 @@ function checkAtBottom(ctx) {
994
710
  props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
995
711
  } = state;
996
712
  const contentSize = getContentSize(ctx);
713
+ resetSharedEdgeGateIfOutsideHysteresis(ctx);
997
714
  if (contentSize > 0 && queuedInitialLayout) {
998
715
  const insetEnd = getContentInsetEnd(ctx);
999
716
  const distanceFromEnd = contentSize - scroll - scrollLength - insetEnd;
@@ -1020,59 +737,50 @@ function checkAtBottom(ctx) {
1020
737
  },
1021
738
  (distance) => {
1022
739
  var _a4, _b;
1023
- return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
740
+ if (canDispatchReachedEdge(ctx, "end", allowedEdge, allowGateCreatedInCurrentCheck)) {
741
+ markReachedEdge(ctx);
742
+ (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
743
+ }
1024
744
  },
1025
745
  (snapshot) => {
1026
746
  state.endReachedSnapshot = snapshot;
1027
- },
1028
- true
747
+ }
1029
748
  );
1030
749
  }
1031
750
  }
1032
751
  }
1033
752
 
1034
- // src/utils/isInMVCPActiveMode.native.ts
1035
- function isInMVCPActiveMode(state) {
1036
- return state.dataChangeNeedsScrollUpdate;
1037
- }
1038
-
1039
753
  // src/utils/checkAtTop.ts
1040
- function checkAtTop(ctx) {
754
+ function checkAtTop(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
1041
755
  const state = ctx == null ? void 0 : ctx.state;
1042
756
  if (!state) {
1043
757
  return;
1044
758
  }
1045
759
  const {
1046
- dataChangeEpoch,
1047
760
  isStartReached,
1048
761
  props: { data, onStartReachedThreshold },
1049
762
  scroll,
1050
763
  scrollLength,
1051
764
  startReachedSnapshot,
1052
- startReachedSnapshotDataChangeEpoch,
1053
765
  totalSize
1054
766
  } = state;
1055
767
  const dataLength = data.length;
1056
768
  const threshold = onStartReachedThreshold * scrollLength;
1057
- const dataChanged = startReachedSnapshotDataChangeEpoch !== dataChangeEpoch;
1058
- const withinThreshold = threshold > 0 && Math.abs(scroll) <= threshold;
1059
- const allowReentryOnDataChange = !!isStartReached && withinThreshold && !!dataChanged && !isInMVCPActiveMode(state);
1060
- if (isStartReached && threshold > 0 && scroll > threshold && startReachedSnapshot && (dataChanged || startReachedSnapshot.contentSize !== totalSize || startReachedSnapshot.dataLength !== dataLength)) {
769
+ resetSharedEdgeGateIfOutsideHysteresis(ctx);
770
+ if (isStartReached && threshold > 0 && scroll > threshold && startReachedSnapshot && (startReachedSnapshot.contentSize !== totalSize || startReachedSnapshot.dataLength !== dataLength)) {
1061
771
  state.isStartReached = false;
1062
772
  state.startReachedSnapshot = void 0;
1063
- state.startReachedSnapshotDataChangeEpoch = void 0;
1064
773
  }
1065
774
  set$(ctx, "isAtStart", scroll <= EDGE_POSITION_EPSILON);
1066
775
  set$(ctx, "isNearStart", scroll <= threshold);
1067
776
  const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || !!state.scrollingTo;
1068
- const shouldDeferDataChangeRefire = isStartReached && withinThreshold && dataChanged && !allowReentryOnDataChange;
1069
- if (!shouldSkipThresholdChecks && !shouldDeferDataChangeRefire) {
777
+ if (!shouldSkipThresholdChecks) {
1070
778
  state.isStartReached = checkThreshold(
1071
779
  scroll,
1072
780
  false,
1073
781
  threshold,
1074
782
  state.isStartReached,
1075
- allowReentryOnDataChange ? void 0 : startReachedSnapshot,
783
+ startReachedSnapshot,
1076
784
  {
1077
785
  contentSize: totalSize,
1078
786
  dataLength,
@@ -1080,21 +788,23 @@ function checkAtTop(ctx) {
1080
788
  },
1081
789
  (distance) => {
1082
790
  var _a3, _b;
1083
- return (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
791
+ if (canDispatchReachedEdge(ctx, "start", allowedEdge, allowGateCreatedInCurrentCheck)) {
792
+ markReachedEdge(ctx);
793
+ (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
794
+ }
1084
795
  },
1085
796
  (snapshot) => {
1086
797
  state.startReachedSnapshot = snapshot;
1087
- state.startReachedSnapshotDataChangeEpoch = snapshot ? dataChangeEpoch : void 0;
1088
- },
1089
- allowReentryOnDataChange
798
+ }
1090
799
  );
1091
800
  }
1092
801
  }
1093
802
 
1094
803
  // src/utils/checkThresholds.ts
1095
- function checkThresholds(ctx) {
1096
- checkAtBottom(ctx);
1097
- checkAtTop(ctx);
804
+ function checkThresholds(ctx, allowedEdge) {
805
+ const allowGateCreatedInCurrentCheck = !ctx.state.edgeReachedGate;
806
+ checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck);
807
+ checkAtTop(ctx, allowedEdge, allowGateCreatedInCurrentCheck);
1098
808
  }
1099
809
 
1100
810
  // src/core/recalculateSettledScroll.ts
@@ -1115,13 +825,7 @@ var DEFAULT_WEB_ADAPTIVE_RENDER_ENTER_VELOCITY = 6;
1115
825
  var DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_VELOCITY = 3;
1116
826
  var DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_DELAY = 250;
1117
827
  function clearAdaptiveRenderExitTimeout(ctx) {
1118
- const state = ctx.state;
1119
- const previousTimeout = state.timeoutAdaptiveRender;
1120
- if (previousTimeout !== void 0) {
1121
- clearTimeout(previousTimeout);
1122
- state.timeouts.delete(previousTimeout);
1123
- state.timeoutAdaptiveRender = void 0;
1124
- }
828
+ ctx.state.scheduledWork.cancel("adaptiveRender");
1125
829
  }
1126
830
  function scheduleAdaptiveRenderExit(ctx, exitDelay) {
1127
831
  const state = ctx.state;
@@ -1129,13 +833,7 @@ function scheduleAdaptiveRenderExit(ctx, exitDelay) {
1129
833
  if (exitDelay <= 0) {
1130
834
  setAdaptiveRender(ctx, "normal", "scroll");
1131
835
  } else {
1132
- const timeout = setTimeout(() => {
1133
- state.timeouts.delete(timeout);
1134
- state.timeoutAdaptiveRender = void 0;
1135
- setAdaptiveRender(ctx, "normal", "scroll");
1136
- }, exitDelay);
1137
- state.timeoutAdaptiveRender = timeout;
1138
- state.timeouts.add(timeout);
836
+ state.scheduledWork.timeout(() => setAdaptiveRender(ctx, "normal", "scroll"), exitDelay, "adaptiveRender");
1139
837
  }
1140
838
  }
1141
839
  function setAdaptiveRender(ctx, mode, reason) {
@@ -1167,7 +865,7 @@ function updateAdaptiveRender(ctx, scrollVelocity, options) {
1167
865
  const exitDelay = (_c = adaptiveRender.exitDelay) != null ? _c : isWeb ? DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_DELAY : DEFAULT_ADAPTIVE_RENDER_EXIT_DELAY;
1168
866
  const threshold = currentMode === "light" ? exitVelocity : enterVelocity;
1169
867
  const nextMode = (options == null ? void 0 : options.forceLight) || Math.abs(scrollVelocity) > threshold ? "light" : "normal";
1170
- const previousMode = state.timeoutAdaptiveRender !== void 0 ? "normal" : currentMode;
868
+ const previousMode = state.scheduledWork.has("adaptiveRender") ? "normal" : currentMode;
1171
869
  if (nextMode !== previousMode) {
1172
870
  if (nextMode === "light") {
1173
871
  setAdaptiveRender(ctx, "light", "scroll");
@@ -1194,14 +892,13 @@ function getEffectiveDrawDistance(ctx, mode) {
1194
892
  }
1195
893
  function scheduleFullDrawDistancePrewarm(ctx) {
1196
894
  const { state } = ctx;
1197
- if (state.props.drawDistance <= INITIAL_DRAW_DISTANCE || state.queuedFullDrawDistancePrewarm !== void 0) {
895
+ if (state.props.drawDistance <= INITIAL_DRAW_DISTANCE || state.scheduledWork.has("fullDrawDistancePrewarm")) {
1198
896
  return;
1199
897
  }
1200
- state.queuedFullDrawDistancePrewarm = requestAnimationFrame(() => {
898
+ state.scheduledWork.frame(() => {
1201
899
  var _a3;
1202
- state.queuedFullDrawDistancePrewarm = void 0;
1203
- (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
1204
- });
900
+ return (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
901
+ }, "fullDrawDistancePrewarm");
1205
902
  }
1206
903
 
1207
904
  // src/utils/setInitialRenderState.ts
@@ -1259,10 +956,7 @@ function syncInitialScrollOffset(state, offset) {
1259
956
  state.scrollPrev = offset;
1260
957
  }
1261
958
  function clearPreservedInitialScrollTargetTimeout(state) {
1262
- if (state.timeoutPreservedInitialScrollClear !== void 0) {
1263
- clearTimeout(state.timeoutPreservedInitialScrollClear);
1264
- state.timeoutPreservedInitialScrollClear = void 0;
1265
- }
959
+ state.scheduledWork.cancel("preservedInitialScroll");
1266
960
  }
1267
961
  function clearPreservedInitialScrollTarget(state) {
1268
962
  clearPreservedInitialScrollTargetTimeout(state);
@@ -1291,14 +985,17 @@ function finishInitialScroll(ctx, options) {
1291
985
  setInitialScrollSession(state);
1292
986
  clearPreservedInitialScrollTargetTimeout(state);
1293
987
  if (options == null ? void 0 : options.schedulePreservedTargetClear) {
1294
- state.timeoutPreservedInitialScrollClear = setTimeout(() => {
1295
- var _a5;
1296
- state.timeoutPreservedInitialScrollClear = void 0;
1297
- if (!state.didFinishInitialScroll || ((_a5 = state.scrollingTo) == null ? void 0 : _a5.isInitialScroll) || !state.initialScroll) {
1298
- return;
1299
- }
1300
- clearPreservedInitialScrollTarget(state);
1301
- }, PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS);
988
+ state.scheduledWork.timeout(
989
+ () => {
990
+ var _a5;
991
+ if (!state.didFinishInitialScroll || ((_a5 = state.scrollingTo) == null ? void 0 : _a5.isInitialScroll) || !state.initialScroll) {
992
+ return;
993
+ }
994
+ clearPreservedInitialScrollTarget(state);
995
+ },
996
+ PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS,
997
+ "preservedInitialScroll"
998
+ );
1302
999
  }
1303
1000
  } else {
1304
1001
  clearPreservedInitialScrollTarget(state);
@@ -1325,9 +1022,11 @@ function calculateOffsetForIndex(ctx, index) {
1325
1022
  return index !== void 0 ? state.positions[index] || 0 : 0;
1326
1023
  }
1327
1024
 
1328
- // src/core/getTopOffsetAdjustment.ts
1329
- function getTopOffsetAdjustment(ctx) {
1330
- return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "alignItemsAtEndPadding") || 0) + (peek$(ctx, "headerSize") || 0);
1025
+ // src/core/getStartOffsetAdjustment.ts
1026
+ function getStartOffsetAdjustment(ctx) {
1027
+ const { state } = ctx;
1028
+ const stylePaddingStart = state.props.horizontal ? (isHorizontalRTL(state) ? state.props.stylePaddingRight : state.props.stylePaddingLeft) || 0 : peek$(ctx, "stylePaddingTop") || 0;
1029
+ return stylePaddingStart + (peek$(ctx, "alignItemsAtEndPadding") || 0) + (peek$(ctx, "headerSize") || 0);
1331
1030
  }
1332
1031
 
1333
1032
  // src/utils/getId.ts
@@ -1403,6 +1102,50 @@ function setSize(ctx, itemKey, size, notifyTotalSize = true) {
1403
1102
  sizes.set(itemKey, size);
1404
1103
  }
1405
1104
 
1105
+ // src/utils/helpers.ts
1106
+ function isFunction(obj) {
1107
+ return typeof obj === "function";
1108
+ }
1109
+ function isArray(obj) {
1110
+ return Array.isArray(obj);
1111
+ }
1112
+ var warned = /* @__PURE__ */ new Set();
1113
+ function warnDevOnce(id, text) {
1114
+ if (IS_DEV && !warned.has(id)) {
1115
+ warned.add(id);
1116
+ console.warn(`[legend-list] ${text}`);
1117
+ }
1118
+ }
1119
+ function roundSize(size) {
1120
+ return Math.floor(size * 8) / 8;
1121
+ }
1122
+ function isNullOrUndefined(value) {
1123
+ return value === null || value === void 0;
1124
+ }
1125
+ function getPadding(s, type) {
1126
+ var _a3, _b, _c;
1127
+ const axisPadding = type === "Left" || type === "Right" ? s.paddingHorizontal : s.paddingVertical;
1128
+ return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : axisPadding) != null ? _b : s.padding) != null ? _c : 0;
1129
+ }
1130
+ function extractPadding(style, contentContainerStyle, type) {
1131
+ return getPadding(style, type) + getPadding(contentContainerStyle, type);
1132
+ }
1133
+ function findContainerId(ctx, key) {
1134
+ var _a3, _b;
1135
+ const directMatch = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.containerItemKeys) == null ? void 0 : _b.get(key);
1136
+ if (directMatch !== void 0) {
1137
+ return directMatch;
1138
+ }
1139
+ const numContainers = peek$(ctx, "numContainers");
1140
+ for (let i = 0; i < numContainers; i++) {
1141
+ const itemKey = peek$(ctx, `containerItemKey${i}`);
1142
+ if (itemKey === key) {
1143
+ return i;
1144
+ }
1145
+ }
1146
+ return -1;
1147
+ }
1148
+
1406
1149
  // src/utils/getItemSize.ts
1407
1150
  function getKnownOrFixedSize(ctx, key, index, data, resolved) {
1408
1151
  var _a3, _b;
@@ -1496,9 +1239,9 @@ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1496
1239
  offset -= viewOffset;
1497
1240
  }
1498
1241
  if (index !== void 0) {
1499
- const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
1500
- if (topOffsetAdjustment) {
1501
- offset += topOffsetAdjustment;
1242
+ const startOffsetAdjustment = getStartOffsetAdjustment(ctx);
1243
+ if (startOffsetAdjustment) {
1244
+ offset += startOffsetAdjustment;
1502
1245
  }
1503
1246
  }
1504
1247
  if (viewPosition !== void 0 && index !== void 0) {
@@ -1508,7 +1251,8 @@ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1508
1251
  }
1509
1252
  const isOutOfBounds = index < 0 || index >= dataLength;
1510
1253
  const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
1511
- const itemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1254
+ const measuredItemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1255
+ const itemSize = Math.max(0, measuredItemSize - (isOutOfBounds ? 0 : ctx.scrollAxisGap));
1512
1256
  const trailingInset = getContentInsetEnd(ctx);
1513
1257
  offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1514
1258
  if (!isOutOfBounds && index === state.props.data.length - 1) {
@@ -1535,11 +1279,35 @@ function clampScrollOffset(ctx, offset, scrollTarget) {
1535
1279
  return clampedOffset;
1536
1280
  }
1537
1281
 
1282
+ // src/core/cancelImperativeScroll.ts
1283
+ function cancelScrollCompletionChecks({ scheduledWork }) {
1284
+ scheduledWork.cancel("checkFinishedScrollFrame");
1285
+ scheduledWork.cancel("checkFinishedScrollRetryFrame");
1286
+ scheduledWork.cancel("checkFinishedScrollFallback");
1287
+ scheduledWork.cancel("platformScrollCompletion");
1288
+ }
1289
+ function settlePendingImperativeScroll(state) {
1290
+ var _a3, _b;
1291
+ const resolvePendingScroll = (_b = state.pendingScrollResolve) != null ? _b : (_a3 = state.pendingScrollToEnd) == null ? void 0 : _a3.resolve;
1292
+ state.pendingScrollResolve = void 0;
1293
+ state.pendingScrollToEnd = void 0;
1294
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1295
+ }
1296
+ function cancelImperativeScroll(state) {
1297
+ cancelScrollCompletionChecks(state);
1298
+ state.scheduledWork.cancel("imperativeScrollReady");
1299
+ state.runPendingScrollToEnd = void 0;
1300
+ state.scrollingTo = void 0;
1301
+ state.scrollTargetPinnedRange = void 0;
1302
+ settlePendingImperativeScroll(state);
1303
+ }
1304
+
1538
1305
  // src/core/finishScrollTo.ts
1539
1306
  function finishScrollTo(ctx) {
1540
1307
  var _a3, _b;
1541
1308
  const state = ctx.state;
1542
1309
  if (state == null ? void 0 : state.scrollingTo) {
1310
+ cancelScrollCompletionChecks(state);
1543
1311
  const resolvePendingScroll = state.pendingScrollResolve;
1544
1312
  state.pendingScrollResolve = void 0;
1545
1313
  const scrollingTo = state.scrollingTo;
@@ -1588,7 +1356,7 @@ function checkFinishedScroll(ctx, options) {
1588
1356
  return;
1589
1357
  }
1590
1358
  }
1591
- ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
1359
+ ctx.state.scheduledWork.frame(() => checkFinishedScrollFrame(ctx), "checkFinishedScrollFrame");
1592
1360
  }
1593
1361
  function hasScrollCompletionOwnership(state, options) {
1594
1362
  const { clampedTargetOffset, scrollingTo } = options;
@@ -1658,7 +1426,6 @@ function checkFinishedScrollFrame(ctx) {
1658
1426
  return;
1659
1427
  }
1660
1428
  const { state } = ctx;
1661
- state.animFrameCheckFinishedScroll = void 0;
1662
1429
  const completionState = getResolvedScrollCompletionState(ctx, scrollingTo);
1663
1430
  if (completionState.isAtResolvedTarget && hasScrollCompletionOwnership(state, {
1664
1431
  clampedTargetOffset: completionState.clampedTargetOffset,
@@ -1677,61 +1444,62 @@ function scrollToFallbackOffset(ctx, offset) {
1677
1444
  }
1678
1445
  function checkFinishedScrollFallback(ctx) {
1679
1446
  const state = ctx.state;
1447
+ if (state.scheduledWork.has("checkFinishedScrollFallback")) {
1448
+ return;
1449
+ }
1680
1450
  const scrollingTo = state.scrollingTo;
1681
1451
  const shouldFinishInitialZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
1682
1452
  const silentInitialDispatch = isSilentInitialDispatch(state, scrollingTo);
1683
1453
  const canFinishInitialWithoutNativeProgress = scrollingTo !== void 0 ? shouldFinishInitialScrollWithoutNativeProgress(state, scrollingTo) : false;
1684
1454
  const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && !shouldFinishInitialZeroTarget && !canFinishInitialWithoutNativeProgress || !state.didContainersLayout;
1685
1455
  const initialDelay = shouldFinishInitialZeroTarget || canFinishInitialWithoutNativeProgress ? 0 : silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : slowTimeout ? 500 : 100;
1686
- state.timeoutCheckFinishedScrollFallback = setTimeout(() => {
1687
- let numChecks = 0;
1688
- const scheduleFallbackCheck = (delay) => {
1689
- state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, delay);
1690
- };
1691
- const checkHasScrolled = () => {
1692
- var _a3, _b, _c, _d;
1693
- state.timeoutCheckFinishedScrollFallback = void 0;
1694
- const isStillScrollingTo = state.scrollingTo;
1695
- if (isStillScrollingTo) {
1696
- numChecks++;
1697
- const isNativeInitialPending = isNativeInitialNonZeroTarget(state) && !state.hasScrolled;
1698
- const maxChecks = silentInitialDispatch ? 5 : isNativeInitialPending ? INITIAL_SCROLL_MAX_FALLBACK_CHECKS : 5;
1699
- const shouldFinishZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
1700
- const canFinishInitialScrollWithoutNativeProgress = shouldFinishInitialScrollWithoutNativeProgress(
1701
- state,
1702
- isStillScrollingTo
1456
+ let numChecks = 0;
1457
+ const scheduleFallbackCheck = (delay) => {
1458
+ state.scheduledWork.timeout(checkHasScrolled, delay, "checkFinishedScrollFallback");
1459
+ };
1460
+ const checkHasScrolled = () => {
1461
+ var _a3, _b, _c, _d;
1462
+ const isStillScrollingTo = state.scrollingTo;
1463
+ if (isStillScrollingTo) {
1464
+ numChecks++;
1465
+ const isNativeInitialPending = isNativeInitialNonZeroTarget(state) && !state.hasScrolled;
1466
+ const maxChecks = silentInitialDispatch ? 5 : isNativeInitialPending ? INITIAL_SCROLL_MAX_FALLBACK_CHECKS : 5;
1467
+ const shouldFinishZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
1468
+ const canFinishInitialScrollWithoutNativeProgress = shouldFinishInitialScrollWithoutNativeProgress(
1469
+ state,
1470
+ isStillScrollingTo
1471
+ );
1472
+ const completionState = getResolvedScrollCompletionState(ctx, isStillScrollingTo);
1473
+ const canFinishAfterSilentNativeDispatch = Platform.OS === "android" && silentInitialDispatch && completionState.isAtResolvedTarget && numChecks >= 1;
1474
+ const shouldRetrySilentInitialNativeScroll = Platform.OS === "android" && canFinishAfterSilentNativeDispatch && !initialScrollCompletion.didRetrySilentInitialScroll(state);
1475
+ const shouldFinishAfterObservedScroll = state.hasScrolled && (!isStillScrollingTo.isInitialScroll || completionState.isAtResolvedTarget);
1476
+ const shouldRetryUnalignedInitialScroll = isStillScrollingTo.isInitialScroll && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
1477
+ const shouldRetryUnalignedEndScroll = Platform.OS === "ios" && !isStillScrollingTo.isInitialScroll && isEndAlignedLastItemTarget(ctx, isStillScrollingTo) && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
1478
+ if (shouldRetrySilentInitialNativeScroll) {
1479
+ const targetOffset = (_b = (_a3 = getInitialScrollWatchdogTargetOffset(state)) != null ? _a3 : isStillScrollingTo.targetOffset) != null ? _b : 0;
1480
+ const jiggleOffset = targetOffset >= SILENT_INITIAL_SCROLL_TARGET_EPSILON ? targetOffset - SILENT_INITIAL_SCROLL_TARGET_EPSILON : targetOffset + SILENT_INITIAL_SCROLL_TARGET_EPSILON;
1481
+ initialScrollCompletion.markSilentInitialScrollRetry(state);
1482
+ scrollToFallbackOffset(ctx, jiggleOffset);
1483
+ state.scheduledWork.frame(
1484
+ () => scrollToFallbackOffset(ctx, targetOffset),
1485
+ "checkFinishedScrollRetryFrame"
1703
1486
  );
1704
- const completionState = getResolvedScrollCompletionState(ctx, isStillScrollingTo);
1705
- const canFinishAfterSilentNativeDispatch = Platform.OS === "android" && silentInitialDispatch && completionState.isAtResolvedTarget && numChecks >= 1;
1706
- const shouldRetrySilentInitialNativeScroll = Platform.OS === "android" && canFinishAfterSilentNativeDispatch && !initialScrollCompletion.didRetrySilentInitialScroll(state);
1707
- const shouldFinishAfterObservedScroll = state.hasScrolled && (!isStillScrollingTo.isInitialScroll || completionState.isAtResolvedTarget);
1708
- const shouldRetryUnalignedInitialScroll = isStillScrollingTo.isInitialScroll && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
1709
- const shouldRetryUnalignedEndScroll = Platform.OS === "ios" && !isStillScrollingTo.isInitialScroll && isEndAlignedLastItemTarget(ctx, isStillScrollingTo) && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
1710
- if (shouldRetrySilentInitialNativeScroll) {
1711
- const targetOffset = (_b = (_a3 = getInitialScrollWatchdogTargetOffset(state)) != null ? _a3 : isStillScrollingTo.targetOffset) != null ? _b : 0;
1712
- const jiggleOffset = targetOffset >= SILENT_INITIAL_SCROLL_TARGET_EPSILON ? targetOffset - SILENT_INITIAL_SCROLL_TARGET_EPSILON : targetOffset + SILENT_INITIAL_SCROLL_TARGET_EPSILON;
1713
- initialScrollCompletion.markSilentInitialScrollRetry(state);
1714
- scrollToFallbackOffset(ctx, jiggleOffset);
1715
- requestAnimationFrame(() => {
1716
- scrollToFallbackOffset(ctx, targetOffset);
1717
- });
1718
- scheduleFallbackCheck(SILENT_INITIAL_SCROLL_RETRY_DELAY_MS);
1719
- } else if (shouldRetryUnalignedEndScroll) {
1720
- scrollToFallbackOffset(ctx, completionState.clampedTargetOffset);
1721
- scheduleFallbackCheck(100);
1722
- } else if (shouldFinishZeroTarget || shouldFinishAfterObservedScroll || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
1723
- finishScrollTo(ctx);
1724
- } else if ((isNativeInitialPending || shouldRetryUnalignedInitialScroll) && numChecks <= maxChecks) {
1725
- const targetOffset = (_d = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : isStillScrollingTo.targetOffset) != null ? _d : state.scrollPending;
1726
- scrollToFallbackOffset(ctx, targetOffset);
1727
- scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
1728
- } else {
1729
- scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
1730
- }
1487
+ scheduleFallbackCheck(SILENT_INITIAL_SCROLL_RETRY_DELAY_MS);
1488
+ } else if (shouldRetryUnalignedEndScroll) {
1489
+ scrollToFallbackOffset(ctx, completionState.clampedTargetOffset);
1490
+ scheduleFallbackCheck(100);
1491
+ } else if (shouldFinishZeroTarget || shouldFinishAfterObservedScroll || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
1492
+ finishScrollTo(ctx);
1493
+ } else if ((isNativeInitialPending || shouldRetryUnalignedInitialScroll) && numChecks <= maxChecks) {
1494
+ const targetOffset = (_d = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : isStillScrollingTo.targetOffset) != null ? _d : state.scrollPending;
1495
+ scrollToFallbackOffset(ctx, targetOffset);
1496
+ scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
1497
+ } else {
1498
+ scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
1731
1499
  }
1732
- };
1733
- checkHasScrolled();
1734
- }, initialDelay);
1500
+ }
1501
+ };
1502
+ scheduleFallbackCheck(initialDelay);
1735
1503
  }
1736
1504
 
1737
1505
  // src/core/doScrollTo.native.ts
@@ -1755,6 +1523,9 @@ function doScrollTo(ctx, params) {
1755
1523
  if (isInitialScroll) {
1756
1524
  initialScrollCompletion.markInitialScrollNativeDispatch(state);
1757
1525
  }
1526
+ if (isAnimated && Math.abs(state.scroll - offset) <= 1) {
1527
+ checkFinishedScroll(ctx);
1528
+ }
1758
1529
  if (!isAnimated) {
1759
1530
  state.scroll = offset;
1760
1531
  checkFinishedScrollFallback(ctx);
@@ -1785,9 +1556,12 @@ function doMaintainScrollAtEnd(ctx) {
1785
1556
  if (!state.maintainingScrollAtEnd) {
1786
1557
  const pendingState = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
1787
1558
  const activeState = maintainScrollAtEnd.animated ? "animated" : "instant";
1559
+ const scrollAtRequest = state.scroll;
1788
1560
  state.maintainingScrollAtEnd = pendingState;
1789
1561
  requestAnimationFrame(() => {
1790
- if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
1562
+ const isStillWithinThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
1563
+ const didScrollSinceRequest = state.scroll !== scrollAtRequest;
1564
+ if (isStillWithinThreshold || !didScrollSinceRequest) {
1791
1565
  state.maintainingScrollAtEnd = activeState;
1792
1566
  const scroller = refScroller.current;
1793
1567
  if (state.props.horizontal && isHorizontalRTL(state)) {
@@ -1817,6 +1591,7 @@ function doMaintainScrollAtEnd(ctx) {
1817
1591
  );
1818
1592
  } else if (state.maintainingScrollAtEnd === pendingState) {
1819
1593
  state.maintainingScrollAtEnd = void 0;
1594
+ state.pendingMaintainScrollAtEnd = false;
1820
1595
  }
1821
1596
  });
1822
1597
  } else {
@@ -1858,20 +1633,21 @@ function requestAdjust(ctx, positionDiff, dataChanged) {
1858
1633
  } else {
1859
1634
  state.ignoreScrollFromMVCP.gt = threshold;
1860
1635
  }
1861
- if (state.ignoreScrollFromMVCPTimeout) {
1862
- clearTimeout(state.ignoreScrollFromMVCPTimeout);
1863
- }
1864
1636
  const delay = needsScrollWorkaround ? 250 : 100;
1865
- state.ignoreScrollFromMVCPTimeout = setTimeout(() => {
1866
- var _a3;
1867
- state.ignoreScrollFromMVCP = void 0;
1868
- const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
1869
- if (shouldForceUpdate) {
1870
- state.ignoreScrollFromMVCPIgnored = false;
1871
- state.scrollPending = state.scroll;
1872
- (_a3 = state.reprocessCurrentScroll) == null ? void 0 : _a3.call(state);
1873
- }
1874
- }, delay);
1637
+ state.scheduledWork.timeout(
1638
+ () => {
1639
+ var _a3;
1640
+ state.ignoreScrollFromMVCP = void 0;
1641
+ const shouldForceUpdate = state.ignoreScrollFromMVCPIgnored && state.scrollProcessingEnabled !== false;
1642
+ if (shouldForceUpdate) {
1643
+ state.ignoreScrollFromMVCPIgnored = false;
1644
+ state.scrollPending = state.scroll;
1645
+ (_a3 = state.reprocessCurrentScroll) == null ? void 0 : _a3.call(state);
1646
+ }
1647
+ },
1648
+ delay,
1649
+ "ignoreScrollFromMVCP"
1650
+ );
1875
1651
  }
1876
1652
  } else {
1877
1653
  state.adjustingFromInitialMount = (state.adjustingFromInitialMount || 0) + 1;
@@ -2227,6 +2003,11 @@ var getScrollVelocity = (state) => {
2227
2003
  return totalWeight > 0 ? weightedVelocity / totalWeight : 0;
2228
2004
  };
2229
2005
 
2006
+ // src/utils/isInMVCPActiveMode.native.ts
2007
+ function isInMVCPActiveMode(state) {
2008
+ return state.dataChangeNeedsScrollUpdate;
2009
+ }
2010
+
2230
2011
  // src/core/updateScroll.ts
2231
2012
  function updateScroll(ctx, newScroll, forceUpdate, options) {
2232
2013
  var _a3;
@@ -2264,6 +2045,8 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2264
2045
  state.scroll = newScroll;
2265
2046
  state.scrollTime = currentTime;
2266
2047
  const scrollDelta = Math.abs(newScroll - prevScroll);
2048
+ const isUserScrollEvent = !!(options == null ? void 0 : options.fromNativeScrollEvent) && scrollDelta > 0.1 && !adjustChanged && scrollingTo === void 0 && !state.pendingNativeMVCPAdjust;
2049
+ const allowedEdge = isUserScrollEvent ? beginReachedEdgeUserScroll(ctx, newScroll - prevScroll) : void 0;
2267
2050
  const didResolvePendingNativeMVCPAdjust = resolvePendingNativeMVCPAdjust(ctx, newScroll);
2268
2051
  const scrollLength = state.scrollLength;
2269
2052
  const isLargeUserScrollJump = scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength && !state.pendingNativeMVCPAdjust;
@@ -2271,7 +2054,7 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2271
2054
  updateAdaptiveRender(ctx, scrollVelocity, { forceLight: isLargeUserScrollJump });
2272
2055
  const lastCalculated = state.scrollLastCalculate;
2273
2056
  const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
2274
- const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
2057
+ const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || allowedEdge !== void 0 || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
2275
2058
  if (shouldUpdate) {
2276
2059
  state.scrollLastCalculate = state.scroll;
2277
2060
  state.ignoreScrollFromMVCPIgnored = false;
@@ -2286,16 +2069,13 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2286
2069
  calculateItemsParams.drawDistanceMode = "visible-first";
2287
2070
  }
2288
2071
  (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, calculateItemsParams);
2289
- checkThresholds(ctx);
2072
+ checkThresholds(ctx, allowedEdge);
2290
2073
  };
2291
2074
  if (isLargeUserScrollJump) {
2292
2075
  state.mvcpAnchorLock = void 0;
2293
2076
  state.pendingNativeMVCPAdjust = void 0;
2294
2077
  state.userScrollAnchorReset = { keys: /* @__PURE__ */ new Set() };
2295
- if (state.queuedMVCPRecalculate !== void 0) {
2296
- cancelAnimationFrame(state.queuedMVCPRecalculate);
2297
- state.queuedMVCPRecalculate = void 0;
2298
- }
2078
+ state.scheduledWork.cancel("mvcpRecalculate");
2299
2079
  flushSync(runCalculateItems);
2300
2080
  scheduleFullDrawDistancePrewarm(ctx);
2301
2081
  } else {
@@ -2440,12 +2220,7 @@ function scrollTo(ctx, params) {
2440
2220
  const {
2441
2221
  props: { horizontal }
2442
2222
  } = state;
2443
- if (state.animFrameCheckFinishedScroll) {
2444
- cancelAnimationFrame(ctx.state.animFrameCheckFinishedScroll);
2445
- }
2446
- if (state.timeoutCheckFinishedScrollFallback) {
2447
- clearTimeout(ctx.state.timeoutCheckFinishedScrollFallback);
2448
- }
2223
+ cancelScrollCompletionChecks(state);
2449
2224
  const requestedOffset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, scrollTargetOffset, scrollTarget);
2450
2225
  const shouldPreserveRawInitialOffsetRequest = !!isInitialScroll && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
2451
2226
  const targetOffset = clampScrollOffset(ctx, requestedOffset, scrollTarget);
@@ -2550,10 +2325,7 @@ function setInitialScrollTarget(ctx, target, options) {
2550
2325
  var _a3;
2551
2326
  const { state } = ctx;
2552
2327
  state.clearPreservedInitialScrollOnNextFinish = void 0;
2553
- if (state.timeoutPreservedInitialScrollClear !== void 0) {
2554
- clearTimeout(state.timeoutPreservedInitialScrollClear);
2555
- state.timeoutPreservedInitialScrollClear = void 0;
2556
- }
2328
+ state.scheduledWork.cancel("preservedInitialScroll");
2557
2329
  state.initialScroll = target;
2558
2330
  if (options == null ? void 0 : options.resetDidFinish) {
2559
2331
  resetInitialRenderState(ctx, { resetInitialScroll: true });
@@ -2846,13 +2618,13 @@ function rearmBootstrapInitialScroll(ctx, options) {
2846
2618
  queueBootstrapInitialScrollReevaluation(ctx.state);
2847
2619
  }
2848
2620
  function createInitialScrollAtEndTarget(options) {
2849
- const { dataLength, footerSize, preserveForFooterLayout, stylePaddingBottom } = options;
2621
+ const { dataLength, footerSize, preserveForFooterLayout, stylePaddingEnd } = options;
2850
2622
  return {
2851
2623
  contentOffset: void 0,
2852
2624
  index: Math.max(0, dataLength - 1),
2853
2625
  preserveForBottomPadding: true,
2854
2626
  preserveForFooterLayout,
2855
- viewOffset: -stylePaddingBottom - footerSize,
2627
+ viewOffset: -stylePaddingEnd - footerSize,
2856
2628
  viewPosition: 1
2857
2629
  };
2858
2630
  }
@@ -2866,7 +2638,7 @@ function isRetargetableBottomAlignedInitialScrollTarget(target) {
2866
2638
  return !!(target && target.viewPosition === 1 && (shouldPreserveInitialScrollForBottomPadding(target) || shouldPreserveInitialScrollForFooterLayout(target)));
2867
2639
  }
2868
2640
  function createRetargetedBottomAlignedInitialScroll(options) {
2869
- const { dataLength, footerSize, initialScrollAtEnd, stylePaddingBottom, target } = options;
2641
+ const { dataLength, footerSize, initialScrollAtEnd, stylePaddingEnd, target } = options;
2870
2642
  const preserveForFooterLayout = shouldPreserveInitialScrollForFooterLayout(target);
2871
2643
  return {
2872
2644
  ...target,
@@ -2874,7 +2646,7 @@ function createRetargetedBottomAlignedInitialScroll(options) {
2874
2646
  index: initialScrollAtEnd ? Math.max(0, dataLength - 1) : target.index,
2875
2647
  preserveForBottomPadding: true,
2876
2648
  preserveForFooterLayout,
2877
- viewOffset: -stylePaddingBottom - (preserveForFooterLayout ? footerSize : 0),
2649
+ viewOffset: -stylePaddingEnd - (preserveForFooterLayout ? footerSize : 0),
2878
2650
  viewPosition: 1
2879
2651
  };
2880
2652
  }
@@ -2882,7 +2654,7 @@ function areEquivalentBootstrapInitialScrollTargets(current, next) {
2882
2654
  return current.index === next.index && current.preserveForBottomPadding === next.preserveForBottomPadding && current.preserveForFooterLayout === next.preserveForFooterLayout && current.viewOffset === next.viewOffset && current.viewPosition === next.viewPosition;
2883
2655
  }
2884
2656
  function clearPendingInitialScrollFooterLayout(ctx, options) {
2885
- const { dataLength, stylePaddingBottom, target } = options;
2657
+ const { dataLength, stylePaddingEnd, target } = options;
2886
2658
  if (!shouldPreserveInitialScrollForFooterLayout(target)) {
2887
2659
  return;
2888
2660
  }
@@ -2890,7 +2662,7 @@ function clearPendingInitialScrollFooterLayout(ctx, options) {
2890
2662
  dataLength,
2891
2663
  footerSize: 0,
2892
2664
  preserveForFooterLayout: void 0,
2893
- stylePaddingBottom
2665
+ stylePaddingEnd
2894
2666
  });
2895
2667
  setInitialScrollTarget(ctx, clearedFooterTarget);
2896
2668
  }
@@ -2951,7 +2723,7 @@ function schedulePreservedEndAnchorCorrectionFrame(ctx, correction) {
2951
2723
  });
2952
2724
  }
2953
2725
  function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {
2954
- var _a3, _b;
2726
+ var _a3;
2955
2727
  const state = ctx.state;
2956
2728
  const initialScroll = state.initialScroll;
2957
2729
  if (!state.didFinishInitialScroll || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) || (initialScroll == null ? void 0 : initialScroll.viewPosition) !== 1 || state.preservedEndAnchorCorrection) {
@@ -2963,7 +2735,7 @@ function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {
2963
2735
  if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {
2964
2736
  clearPendingInitialScrollFooterLayout(ctx, {
2965
2737
  dataLength: state.props.data.length,
2966
- stylePaddingBottom: (_b = state.props.stylePaddingBottom) != null ? _b : 0,
2738
+ stylePaddingEnd: getStylePaddingEnd(state.props),
2967
2739
  target: initialScroll
2968
2740
  });
2969
2741
  } else {
@@ -3000,7 +2772,7 @@ function startBootstrapInitialScrollOnMount(ctx, options) {
3000
2772
  }
3001
2773
  }
3002
2774
  function handleBootstrapInitialScrollDataChange(ctx, options) {
3003
- const { dataLength, didDataChange, initialScrollAtEnd, previousDataLength, stylePaddingBottom } = options;
2775
+ const { dataLength, didDataChange, initialScrollAtEnd, previousDataLength, stylePaddingEnd } = options;
3004
2776
  const state = ctx.state;
3005
2777
  const initialScroll = state.initialScroll;
3006
2778
  if (isOffsetInitialScrollSession(state) || !initialScroll) {
@@ -3022,18 +2794,18 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
3022
2794
  dataLength,
3023
2795
  footerSize: peek$(ctx, "footerSize") || 0,
3024
2796
  preserveForFooterLayout: shouldPreserveInitialScrollForFooterLayout(initialScroll),
3025
- stylePaddingBottom
2797
+ stylePaddingEnd
3026
2798
  }) : createRetargetedBottomAlignedInitialScroll({
3027
2799
  dataLength,
3028
2800
  footerSize: peek$(ctx, "footerSize") || 0,
3029
2801
  initialScrollAtEnd,
3030
- stylePaddingBottom,
2802
+ stylePaddingEnd,
3031
2803
  target: initialScroll
3032
2804
  });
3033
2805
  if (!shouldResetDidFinish && didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
3034
2806
  clearPendingInitialScrollFooterLayout(ctx, {
3035
2807
  dataLength,
3036
- stylePaddingBottom,
2808
+ stylePaddingEnd,
3037
2809
  target: initialScroll
3038
2810
  });
3039
2811
  return;
@@ -3065,7 +2837,7 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
3065
2837
  }
3066
2838
  }
3067
2839
  function handleBootstrapInitialScrollFooterLayout(ctx, options) {
3068
- const { dataLength, footerSize, initialScrollAtEnd, stylePaddingBottom } = options;
2840
+ const { dataLength, footerSize, initialScrollAtEnd, stylePaddingEnd } = options;
3069
2841
  const state = ctx.state;
3070
2842
  if (!initialScrollAtEnd) {
3071
2843
  return;
@@ -3081,7 +2853,7 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
3081
2853
  if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
3082
2854
  clearPendingInitialScrollFooterLayout(ctx, {
3083
2855
  dataLength,
3084
- stylePaddingBottom,
2856
+ stylePaddingEnd,
3085
2857
  target: initialScroll
3086
2858
  });
3087
2859
  } else {
@@ -3089,13 +2861,13 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
3089
2861
  dataLength,
3090
2862
  footerSize,
3091
2863
  preserveForFooterLayout: shouldPreserveInitialScrollForFooterLayout(initialScroll),
3092
- stylePaddingBottom
2864
+ stylePaddingEnd
3093
2865
  });
3094
2866
  const didTargetChange = initialScroll.index !== updatedInitialScroll.index || initialScroll.viewPosition !== updatedInitialScroll.viewPosition || initialScroll.viewOffset !== updatedInitialScroll.viewOffset;
3095
2867
  if (!didTargetChange) {
3096
2868
  clearPendingInitialScrollFooterLayout(ctx, {
3097
2869
  dataLength,
3098
- stylePaddingBottom,
2870
+ stylePaddingEnd,
3099
2871
  target: initialScroll
3100
2872
  });
3101
2873
  } else {
@@ -3322,7 +3094,7 @@ function handleInitialScrollDataChange(ctx, options) {
3322
3094
  initialScrollAtEnd,
3323
3095
  latestInitialScroll,
3324
3096
  latestInitialScrollSessionKind,
3325
- stylePaddingBottom,
3097
+ stylePaddingEnd,
3326
3098
  useBootstrapInitialScroll
3327
3099
  } = options;
3328
3100
  const state = ctx.state;
@@ -3352,7 +3124,7 @@ function handleInitialScrollDataChange(ctx, options) {
3352
3124
  didDataChange,
3353
3125
  initialScrollAtEnd,
3354
3126
  previousDataLength: previousInitialScrollDataLength,
3355
- stylePaddingBottom
3127
+ stylePaddingEnd
3356
3128
  });
3357
3129
  return;
3358
3130
  }
@@ -3375,6 +3147,52 @@ function resetLayoutCachesForDataChange(state) {
3375
3147
  state.columnSpans.length = 0;
3376
3148
  }
3377
3149
 
3150
+ // src/core/scheduleContainerLayout.ts
3151
+ function getContainerLayoutEffectScope(ctx) {
3152
+ var _a3;
3153
+ const scheduledIds = ctx.pendingContainerIds;
3154
+ ctx.pendingContainerIds = void 0;
3155
+ if (scheduledIds === void 0) {
3156
+ return void 0;
3157
+ }
3158
+ const state = ctx.state;
3159
+ let targetContainerIds = scheduledIds;
3160
+ if (targetContainerIds && ((_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys.size)) {
3161
+ targetContainerIds = new Set(targetContainerIds);
3162
+ for (const itemKey of state.userScrollAnchorReset.keys) {
3163
+ const containerId = state.containerItemKeys.get(itemKey);
3164
+ if (containerId !== void 0) {
3165
+ targetContainerIds.add(containerId);
3166
+ }
3167
+ }
3168
+ }
3169
+ return targetContainerIds;
3170
+ }
3171
+ function scheduleContainerLayout(ctx, target) {
3172
+ var _a3;
3173
+ const isAlreadyScheduled = ctx.pendingContainerIds !== void 0;
3174
+ const previousIds = ctx.pendingContainerIds;
3175
+ if (target === void 0) {
3176
+ ctx.pendingContainerIds = null;
3177
+ } else if (previousIds !== null) {
3178
+ let nextIds = previousIds;
3179
+ if (!nextIds) {
3180
+ nextIds = typeof target === "number" ? /* @__PURE__ */ new Set([target]) : new Set(target);
3181
+ } else if (typeof target === "number") {
3182
+ nextIds.add(target);
3183
+ } else {
3184
+ for (const containerId of target) {
3185
+ nextIds.add(containerId);
3186
+ }
3187
+ }
3188
+ ctx.pendingContainerIds = nextIds;
3189
+ }
3190
+ if (!isAlreadyScheduled) {
3191
+ const nextEpoch = ((_a3 = peek$(ctx, "containerLayoutEpoch")) != null ? _a3 : 0) + 1;
3192
+ set$(ctx, "containerLayoutEpoch", nextEpoch);
3193
+ }
3194
+ }
3195
+
3378
3196
  // src/core/syncMountedContainer.ts
3379
3197
  function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3380
3198
  var _a3, _b, _c, _d, _e, _f, _g, _h, _i;
@@ -3418,14 +3236,13 @@ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3418
3236
  set$(ctx, `containerSpan${containerIndex}`, span);
3419
3237
  }
3420
3238
  }
3421
- const prevItemInfo = peek$(ctx, `containerItemInfo${containerIndex}`);
3239
+ const prevIndex = peek$(ctx, `containerItemIndex${containerIndex}`);
3240
+ if (prevIndex !== itemIndex) {
3241
+ set$(ctx, `containerItemIndex${containerIndex}`, itemIndex);
3242
+ }
3422
3243
  const prevData = peek$(ctx, `containerItemData${containerIndex}`);
3423
- let itemInfoValue = prevData;
3424
- let didChangeItemInfo = (prevItemInfo == null ? void 0 : prevItemInfo.itemKey) !== itemKey || (prevItemInfo == null ? void 0 : prevItemInfo.index) !== itemIndex || (prevItemInfo == null ? void 0 : prevItemInfo.value) !== prevData;
3425
3244
  const updateData = () => {
3426
3245
  set$(ctx, `containerItemData${containerIndex}`, item);
3427
- itemInfoValue = item;
3428
- didChangeItemInfo = true;
3429
3246
  didRefreshData = true;
3430
3247
  };
3431
3248
  if (prevData !== item) {
@@ -3460,13 +3277,6 @@ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3460
3277
  }
3461
3278
  }
3462
3279
  }
3463
- if (didChangeItemInfo) {
3464
- set$(ctx, `containerItemInfo${containerIndex}`, {
3465
- index: itemIndex,
3466
- itemKey,
3467
- value: itemInfoValue
3468
- });
3469
- }
3470
3280
  return { didChangePosition, didRefreshData };
3471
3281
  }
3472
3282
 
@@ -3784,7 +3594,6 @@ function setupViewability(props) {
3784
3594
  }
3785
3595
  function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollSize, start, end, startBuffered = start, endBuffered = end) {
3786
3596
  const {
3787
- timeouts,
3788
3597
  props: { data }
3789
3598
  } = state;
3790
3599
  for (const viewabilityConfigCallbackPair of viewabilityConfigCallbackPairs) {
@@ -3794,11 +3603,10 @@ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollS
3794
3603
  viewabilityState.startBuffered = startBuffered;
3795
3604
  viewabilityState.endBuffered = endBuffered;
3796
3605
  if (viewabilityConfigCallbackPair.viewabilityConfig.minimumViewTime) {
3797
- const timer = setTimeout(() => {
3798
- timeouts.delete(timer);
3799
- updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, state, ctx, scrollSize);
3800
- }, viewabilityConfigCallbackPair.viewabilityConfig.minimumViewTime);
3801
- timeouts.add(timer);
3606
+ state.scheduledWork.timeout(
3607
+ () => updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, state, ctx, scrollSize),
3608
+ viewabilityConfigCallbackPair.viewabilityConfig.minimumViewTime
3609
+ );
3802
3610
  } else {
3803
3611
  updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, state, ctx, scrollSize);
3804
3612
  }
@@ -3831,19 +3639,29 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
3831
3639
  if (previousViewableItems) {
3832
3640
  for (const viewToken of previousViewableItems) {
3833
3641
  previousViewableKeys.add(viewToken.key);
3642
+ const currentIndex = state.indexByKey.get(viewToken.key);
3643
+ const currentItem = currentIndex !== void 0 ? data[currentIndex] : void 0;
3834
3644
  const containerId = findContainerId(ctx, viewToken.key);
3835
- if (!checkIsViewable(
3836
- state,
3837
- ctx,
3838
- viewabilityConfig,
3839
- containerId,
3840
- viewToken.key,
3841
- scrollSize,
3842
- viewToken.item,
3843
- viewToken.index
3844
- )) {
3845
- viewToken.isViewable = false;
3846
- changed.push(viewToken);
3645
+ let isStillViewable = false;
3646
+ if (currentIndex !== void 0 && currentItem !== void 0) {
3647
+ isStillViewable = checkIsViewable(
3648
+ state,
3649
+ ctx,
3650
+ viewabilityConfig,
3651
+ containerId,
3652
+ viewToken.key,
3653
+ scrollSize,
3654
+ currentItem,
3655
+ currentIndex
3656
+ );
3657
+ }
3658
+ if (!isStillViewable) {
3659
+ changed.push({
3660
+ ...viewToken,
3661
+ index: currentIndex != null ? currentIndex : viewToken.index,
3662
+ isViewable: false,
3663
+ item: currentItem != null ? currentItem : viewToken.item
3664
+ });
3847
3665
  }
3848
3666
  }
3849
3667
  }
@@ -4000,128 +3818,102 @@ function getExpandedContainerPoolSize(dataLength, numContainers) {
4000
3818
 
4001
3819
  // src/utils/findAvailableContainers.ts
4002
3820
  function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffered, pendingRemoval, getRequiredItemType, protectedKeys) {
3821
+ var _a3;
4003
3822
  const numNeeded = needNewContainers.length;
4004
3823
  if (numNeeded === 0) {
4005
3824
  return [];
4006
3825
  }
4007
3826
  const numContainers = peek$(ctx, "numContainers");
4008
3827
  const state = ctx.state;
4009
- const { stickyContainerPool, containerItemTypes } = state;
3828
+ const { containerItemMetadata, stickyContainerPool } = state;
4010
3829
  const shouldAvoidAssignedContainerReuse = state.props.recycleItems && !!state.props.positionComponentInternal;
4011
- const allocations = [];
4012
3830
  const pendingRemovalSet = pendingRemoval.length > 0 ? new Set(pendingRemoval) : void 0;
4013
- let pendingRemovalChanged = false;
3831
+ const requests = needNewContainers.map((itemIndex, order) => ({
3832
+ isSticky: state.props.stickyHeaderIndicesSet.has(itemIndex),
3833
+ itemIndex,
3834
+ itemType: getRequiredItemType == null ? void 0 : getRequiredItemType(itemIndex),
3835
+ order
3836
+ }));
3837
+ const normalRequests = requests.filter((request) => !request.isSticky);
3838
+ const stickyRequests = requests.filter((request) => request.isSticky);
3839
+ const normalCandidates = [];
3840
+ const stickyCandidates = [];
3841
+ for (let containerIndex = 0; containerIndex < numContainers; containerIndex++) {
3842
+ const key = peek$(ctx, `containerItemKey${containerIndex}`);
3843
+ const isPendingRemoval = !!(pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(containerIndex));
3844
+ const isProtected = !!key && !!(protectedKeys == null ? void 0 : protectedKeys.has(key)) && state.indexByKey.has(key);
3845
+ if (isProtected) {
3846
+ continue;
3847
+ }
3848
+ if (stickyContainerPool.has(containerIndex)) {
3849
+ if (key === void 0 || isPendingRemoval) {
3850
+ stickyCandidates.push({ containerIndex, distance: Number.POSITIVE_INFINITY });
3851
+ }
3852
+ } else if (key === void 0 || isPendingRemoval) {
3853
+ normalCandidates.push({ containerIndex, distance: Number.POSITIVE_INFINITY });
3854
+ } else if (!shouldAvoidAssignedContainerReuse) {
3855
+ const index = state.indexByKey.get(key);
3856
+ if (index !== void 0 && (index < startBuffered || index > endBuffered)) {
3857
+ const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
3858
+ normalCandidates.push({ containerIndex, distance });
3859
+ }
3860
+ }
3861
+ }
3862
+ normalCandidates.sort(comparatorByDistance);
3863
+ const allocations = new Array(numNeeded);
4014
3864
  let nextNewContainerIndex = numContainers;
4015
- const usedContainers = /* @__PURE__ */ new Set();
4016
- let availableContainers;
4017
- const stickyHeaderIndicesSet = state.props.stickyHeaderIndicesSet;
4018
- const canReuseContainer = (containerIndex, requiredType) => {
4019
- if (!requiredType) return true;
4020
- const existingType = containerItemTypes.get(containerIndex);
4021
- if (!existingType) return true;
4022
- return existingType === requiredType;
4023
- };
4024
- const pushAllocation = (itemIndex, itemType, containerIndex) => {
4025
- allocations.push({
3865
+ let pendingRemovalChanged = false;
3866
+ const assign = (request, containerIndex) => {
3867
+ allocations[request.order] = {
4026
3868
  containerIndex,
4027
- itemIndex,
4028
- itemType
4029
- });
4030
- usedContainers.add(containerIndex);
3869
+ itemIndex: request.itemIndex,
3870
+ itemType: request.itemType
3871
+ };
4031
3872
  if (pendingRemovalSet == null ? void 0 : pendingRemovalSet.delete(containerIndex)) {
4032
3873
  pendingRemovalChanged = true;
4033
3874
  }
4034
3875
  };
4035
- const pushNewContainer = (itemIndex, itemType, isSticky) => {
4036
- const newContainerIndex = nextNewContainerIndex++;
4037
- pushAllocation(itemIndex, itemType, newContainerIndex);
4038
- if (isSticky) {
4039
- stickyContainerPool.add(newContainerIndex);
4040
- }
4041
- return newContainerIndex;
4042
- };
4043
- const canUseContainer = (containerIndex, itemType) => {
4044
- if (usedContainers.has(containerIndex) || stickyContainerPool.has(containerIndex)) {
4045
- return false;
4046
- }
4047
- const key = peek$(ctx, `containerItemKey${containerIndex}`);
4048
- const isPending = !!(pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(containerIndex));
4049
- return (key === void 0 || isPending) && canReuseContainer(containerIndex, itemType);
4050
- };
4051
- const findStickyContainer = (itemType) => {
4052
- let foundContainer;
4053
- for (const containerIndex of stickyContainerPool) {
4054
- if (!usedContainers.has(containerIndex)) {
4055
- const key = peek$(ctx, `containerItemKey${containerIndex}`);
4056
- const isPendingRemoval = !!(pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(containerIndex));
4057
- if ((key === void 0 || isPendingRemoval) && canReuseContainer(containerIndex, itemType)) {
4058
- foundContainer = containerIndex;
4059
- break;
4060
- }
4061
- }
4062
- }
4063
- return foundContainer;
4064
- };
4065
- const findUnassignedOrPendingContainer = (itemType) => {
4066
- let foundContainer;
4067
- for (let containerIndex = 0; containerIndex < numContainers && foundContainer === void 0; containerIndex++) {
4068
- if (canUseContainer(containerIndex, itemType)) {
4069
- foundContainer = containerIndex;
3876
+ const assignMatching = (pendingRequests, candidates, matches) => {
3877
+ for (const request of pendingRequests) {
3878
+ if (allocations[request.order]) {
3879
+ continue;
4070
3880
  }
4071
- }
4072
- return foundContainer;
4073
- };
4074
- const getAvailableContainers = () => {
4075
- if (!availableContainers) {
4076
- availableContainers = [];
4077
- if (!shouldAvoidAssignedContainerReuse) {
4078
- for (let containerIndex = 0; containerIndex < numContainers; containerIndex++) {
4079
- if (usedContainers.has(containerIndex) || stickyContainerPool.has(containerIndex)) {
4080
- continue;
4081
- }
4082
- const key = peek$(ctx, `containerItemKey${containerIndex}`);
4083
- if (key === void 0) continue;
4084
- if ((protectedKeys == null ? void 0 : protectedKeys.has(key)) && state.indexByKey.has(key)) continue;
4085
- const index = state.indexByKey.get(key);
4086
- const isOutOfView = index < startBuffered || index > endBuffered;
4087
- if (isOutOfView) {
4088
- const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
4089
- availableContainers.push({ distance, index: containerIndex });
4090
- }
3881
+ const candidateIndex = candidates.findIndex(
3882
+ (candidate) => {
3883
+ var _a4;
3884
+ return matches((_a4 = containerItemMetadata.get(candidate.containerIndex)) == null ? void 0 : _a4.itemType, request.itemType);
4091
3885
  }
4092
- availableContainers.sort(comparatorByDistance);
3886
+ );
3887
+ if (candidateIndex !== -1) {
3888
+ const [candidate] = candidates.splice(candidateIndex, 1);
3889
+ assign(request, candidate.containerIndex);
4093
3890
  }
4094
3891
  }
4095
- return availableContainers;
4096
3892
  };
4097
- const findAvailableContainer = (itemType) => {
4098
- const containers = getAvailableContainers();
4099
- let matchIndex = -1;
4100
- for (let i = 0; i < containers.length && matchIndex === -1; i++) {
4101
- const containerIndex = containers[i].index;
4102
- if (!usedContainers.has(containerIndex) && canReuseContainer(containerIndex, itemType)) {
4103
- matchIndex = i;
4104
- }
3893
+ const assignFromPool = (pendingRequests, candidates, allowCrossType) => {
3894
+ if (getRequiredItemType) {
3895
+ assignMatching(
3896
+ pendingRequests,
3897
+ candidates,
3898
+ (containerType, requestType) => requestType !== void 0 && containerType === requestType
3899
+ );
3900
+ }
3901
+ assignMatching(pendingRequests, candidates, (containerType) => containerType === void 0);
3902
+ if (allowCrossType) {
3903
+ assignMatching(pendingRequests, candidates, () => true);
4105
3904
  }
4106
- return matchIndex === -1 ? void 0 : containers.splice(matchIndex, 1)[0].index;
4107
3905
  };
4108
- for (const itemIndex of needNewContainers) {
4109
- const itemType = getRequiredItemType == null ? void 0 : getRequiredItemType(itemIndex);
4110
- const isSticky = stickyHeaderIndicesSet.has(itemIndex);
4111
- let containerIndex;
4112
- if (isSticky) {
4113
- containerIndex = findStickyContainer(itemType);
4114
- } else {
4115
- containerIndex = findUnassignedOrPendingContainer(itemType);
4116
- if (containerIndex === void 0) {
4117
- containerIndex = findAvailableContainer(itemType);
4118
- }
3906
+ assignFromPool(normalRequests, normalCandidates, true);
3907
+ assignFromPool(stickyRequests, stickyCandidates, false);
3908
+ for (const request of requests) {
3909
+ if (allocations[request.order]) {
3910
+ continue;
4119
3911
  }
4120
- if (containerIndex !== void 0) {
4121
- pushAllocation(itemIndex, itemType, containerIndex);
4122
- } else {
4123
- pushNewContainer(itemIndex, itemType, isSticky);
3912
+ const containerIndex = nextNewContainerIndex++;
3913
+ if (request.isSticky) {
3914
+ stickyContainerPool.add(containerIndex);
4124
3915
  }
3916
+ assign(request, containerIndex);
4125
3917
  }
4126
3918
  if (pendingRemovalChanged) {
4127
3919
  pendingRemoval.length = 0;
@@ -4131,18 +3923,21 @@ function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffe
4131
3923
  }
4132
3924
  }
4133
3925
  }
4134
- if (IS_DEV && nextNewContainerIndex > peek$(ctx, "numContainersPooled")) {
4135
- console.warn(
4136
- "[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.",
4137
- {
4138
- debugInfo: {
4139
- numContainers,
4140
- numContainersPooled: peek$(ctx, "numContainersPooled"),
4141
- numNeeded,
4142
- stillNeeded: nextNewContainerIndex - numContainers
3926
+ if (IS_DEV) {
3927
+ const numContainersPooled = (_a3 = peek$(ctx, "numContainersPooled")) != null ? _a3 : Number.POSITIVE_INFINITY;
3928
+ if (nextNewContainerIndex > numContainersPooled) {
3929
+ console.warn(
3930
+ "[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.",
3931
+ {
3932
+ debugInfo: {
3933
+ numContainers,
3934
+ numContainersPooled,
3935
+ numNeeded,
3936
+ stillNeeded: nextNewContainerIndex - numContainers
3937
+ }
4143
3938
  }
4144
- }
4145
- );
3939
+ );
3940
+ }
4146
3941
  }
4147
3942
  return allocations;
4148
3943
  }
@@ -4170,20 +3965,15 @@ function getProjectedBufferAdjustment(scrollVelocity, trailingBuffer) {
4170
3965
  }
4171
3966
  function scheduleRenderRangeProjectionSettle(ctx) {
4172
3967
  const state = ctx.state;
4173
- const previousTimeout = state.timeoutRenderRangeProjectionSettle;
4174
- if (previousTimeout !== void 0) {
4175
- clearTimeout(previousTimeout);
4176
- state.timeouts.delete(previousTimeout);
4177
- }
4178
- const timeout = setTimeout(() => {
4179
- var _a3;
4180
- state.timeoutRenderRangeProjectionSettle = void 0;
4181
- state.timeouts.delete(timeout);
4182
- state.scrollHistory.length = 0;
4183
- (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
4184
- }, RENDER_RANGE_PROJECTION_SETTLE_DELAY);
4185
- state.timeoutRenderRangeProjectionSettle = timeout;
4186
- state.timeouts.add(timeout);
3968
+ state.scheduledWork.timeout(
3969
+ () => {
3970
+ var _a3;
3971
+ state.scrollHistory.length = 0;
3972
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
3973
+ },
3974
+ RENDER_RANGE_PROJECTION_SETTLE_DELAY,
3975
+ "renderRangeProjection"
3976
+ );
4187
3977
  }
4188
3978
  function findCurrentStickyIndex(stickyArray, scroll, state) {
4189
3979
  const positions = state.positions;
@@ -4377,7 +4167,7 @@ function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, sc
4377
4167
  function calculateItemsInView(ctx, params = {}) {
4378
4168
  const state = ctx.state;
4379
4169
  batchedUpdates(() => {
4380
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t;
4170
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u;
4381
4171
  const {
4382
4172
  columns,
4383
4173
  containerItemKeys,
@@ -4405,6 +4195,7 @@ function calculateItemsInView(ctx, params = {}) {
4405
4195
  return;
4406
4196
  }
4407
4197
  let totalSize = getContentSize(ctx);
4198
+ let changedContainerIds;
4408
4199
  const topPad = peek$(ctx, "stylePaddingTop") + peek$(ctx, "alignItemsAtEndPadding") + peek$(ctx, "headerSize");
4409
4200
  const numColumns = peek$(ctx, "numColumns");
4410
4201
  const speed = (_b = params.scrollVelocity) != null ? _b : getScrollVelocity(state);
@@ -4530,7 +4321,7 @@ function calculateItemsInView(ctx, params = {}) {
4530
4321
  const scrollAdjustPendingBeforeMVCP = (_f = peek$(ctx, "scrollAdjustPending")) != null ? _f : 0;
4531
4322
  checkMVCP == null ? void 0 : checkMVCP();
4532
4323
  const didMVCPAdjustScroll = !!checkMVCP && (state.scroll !== scrollBeforeMVCP || ((_g = peek$(ctx, "scrollAdjustPending")) != null ? _g : 0) !== scrollAdjustPendingBeforeMVCP);
4533
- if (didMVCPAdjustScroll && (initialScroll || state.scrollingTo)) {
4324
+ if (didMVCPAdjustScroll) {
4534
4325
  updateScroll2(state.scroll);
4535
4326
  updateScrollRange();
4536
4327
  }
@@ -4710,22 +4501,20 @@ function calculateItemsInView(ctx, params = {}) {
4710
4501
  if (oldKey && oldKey !== id) {
4711
4502
  containerItemKeys.delete(oldKey);
4712
4503
  }
4504
+ if (oldKey !== id) {
4505
+ changedContainerIds != null ? changedContainerIds : changedContainerIds = /* @__PURE__ */ new Set();
4506
+ changedContainerIds.add(containerIndex);
4507
+ state.containerItemGenerations[containerIndex] = ((_p = state.containerItemGenerations[containerIndex]) != null ? _p : 0) + 1;
4508
+ }
4713
4509
  set$(ctx, `containerItemKey${containerIndex}`, id);
4510
+ set$(ctx, `containerItemIndex${containerIndex}`, i);
4714
4511
  set$(ctx, `containerItemData${containerIndex}`, data[i]);
4715
- set$(ctx, `containerItemInfo${containerIndex}`, {
4716
- index: i,
4717
- itemKey: id,
4718
- value: data[i]
4719
- });
4720
- if (allocation.itemType !== void 0) {
4721
- state.containerItemTypes.set(containerIndex, allocation.itemType);
4722
- }
4512
+ state.containerItemMetadata.set(
4513
+ containerIndex,
4514
+ createContainerItemMetadata(state, i, data[i], allocation.itemType)
4515
+ );
4723
4516
  containerItemKeys.set(id, containerIndex);
4724
- (_p = state.userScrollAnchorReset) == null ? void 0 : _p.keys.add(id);
4725
- if (IsNewArchitecture) {
4726
- (_q = state.pendingLayoutEffectMeasurements) != null ? _q : state.pendingLayoutEffectMeasurements = /* @__PURE__ */ new Set();
4727
- state.pendingLayoutEffectMeasurements.add(id);
4728
- }
4517
+ (_q = state.userScrollAnchorReset) == null ? void 0 : _q.keys.add(id);
4729
4518
  const containerSticky = `containerSticky${containerIndex}`;
4730
4519
  const isSticky = stickyHeaderIndicesSet.has(i);
4731
4520
  const isPinnedRender = isPinnedRenderIndex(i);
@@ -4775,15 +4564,18 @@ function calculateItemsInView(ctx, params = {}) {
4775
4564
  if (pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(i)) {
4776
4565
  if (itemKey !== void 0) {
4777
4566
  containerItemKeys.delete(itemKey);
4567
+ changedContainerIds != null ? changedContainerIds : changedContainerIds = /* @__PURE__ */ new Set();
4568
+ changedContainerIds.add(i);
4569
+ state.containerItemGenerations[i] = ((_t = state.containerItemGenerations[i]) != null ? _t : 0) + 1;
4778
4570
  }
4779
- state.containerItemTypes.delete(i);
4571
+ state.containerItemMetadata.delete(i);
4780
4572
  if (state.stickyContainerPool.has(i)) {
4781
4573
  set$(ctx, `containerSticky${i}`, false);
4782
4574
  state.stickyContainerPool.delete(i);
4783
4575
  }
4784
4576
  set$(ctx, `containerItemKey${i}`, void 0);
4577
+ set$(ctx, `containerItemIndex${i}`, void 0);
4785
4578
  set$(ctx, `containerItemData${i}`, void 0);
4786
- set$(ctx, `containerItemInfo${i}`, void 0);
4787
4579
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
4788
4580
  set$(ctx, `containerColumn${i}`, -1);
4789
4581
  set$(ctx, `containerSpan${i}`, 1);
@@ -4797,6 +4589,9 @@ function calculateItemsInView(ctx, params = {}) {
4797
4589
  }
4798
4590
  }
4799
4591
  }
4592
+ if (changedContainerIds && (IsNewArchitecture || Platform.OS === "web")) {
4593
+ scheduleContainerLayout(ctx, changedContainerIds);
4594
+ }
4800
4595
  if (Platform.OS === "web" && didChangePositions) {
4801
4596
  set$(ctx, "lastPositionUpdate", Date.now());
4802
4597
  }
@@ -4813,20 +4608,18 @@ function calculateItemsInView(ctx, params = {}) {
4813
4608
  }
4814
4609
  }
4815
4610
  if (viewabilityConfigCallbackPairs && visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null) {
4816
- if (!didMVCPAdjustScroll) {
4817
- updateViewableItems(
4818
- ctx.state,
4819
- ctx,
4820
- viewabilityConfigCallbackPairs,
4821
- scrollLength,
4822
- visibleRange.startNoBuffer,
4823
- visibleRange.endNoBuffer,
4824
- startBuffered != null ? startBuffered : visibleRange.startNoBuffer,
4825
- endBuffered != null ? endBuffered : visibleRange.endNoBuffer
4826
- );
4827
- }
4611
+ updateViewableItems(
4612
+ ctx.state,
4613
+ ctx,
4614
+ viewabilityConfigCallbackPairs,
4615
+ scrollLength,
4616
+ visibleRange.startNoBuffer,
4617
+ visibleRange.endNoBuffer,
4618
+ startBuffered != null ? startBuffered : visibleRange.startNoBuffer,
4619
+ endBuffered != null ? endBuffered : visibleRange.endNoBuffer
4620
+ );
4828
4621
  }
4829
- (_t = stickyState == null ? void 0 : stickyState.finishCalculateItemsInView) == null ? void 0 : _t.call(stickyState);
4622
+ (_u = stickyState == null ? void 0 : stickyState.finishCalculateItemsInView) == null ? void 0 : _u.call(stickyState);
4830
4623
  });
4831
4624
  }
4832
4625
 
@@ -4848,8 +4641,6 @@ function maybeUpdateAnchoredEndSpace(ctx) {
4848
4641
  if (anchorIndex >= 0 && anchorIndex < data.length && state.scrollLength > 0) {
4849
4642
  nextAnchorKey = getId(state, anchorIndex);
4850
4643
  let contentBelowAnchor = 0;
4851
- const footerSize = ctx.values.get("footerSize") || 0;
4852
- const stylePaddingBottom = state.props.stylePaddingBottom || 0;
4853
4644
  let hasUnknownTailSize = false;
4854
4645
  for (let index = anchorIndex; index < data.length; index++) {
4855
4646
  const size = getKnownOrFixedItemSize(ctx, index);
@@ -4861,14 +4652,16 @@ function maybeUpdateAnchoredEndSpace(ctx) {
4861
4652
  contentBelowAnchor += effectiveSize;
4862
4653
  }
4863
4654
  }
4864
- contentBelowAnchor += footerSize + stylePaddingBottom;
4655
+ contentBelowAnchor = Math.max(0, contentBelowAnchor - ctx.scrollAxisGap);
4656
+ contentBelowAnchor += (ctx.values.get("footerSize") || 0) + getStylePaddingEnd(state.props);
4865
4657
  isReady = !hasUnknownTailSize;
4866
4658
  nextSize = hasUnknownTailSize ? previousSize || 0 : Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
4867
4659
  } else if (anchorIndex >= 0) {
4868
4660
  isReady = false;
4869
4661
  }
4870
4662
  }
4871
- const didSizeChange = previousSize !== nextSize;
4663
+ const didSizeChange = previousSize !== nextSize && (previousSize !== void 0 || anchoredEndSpace !== void 0);
4664
+ const didEffectiveSizeChange = (previousSize || 0) !== nextSize;
4872
4665
  const didReadyAnchorChange = previousReadyAnchorIndex !== nextAnchorIndex || previousReadyAnchorKey !== nextAnchorKey;
4873
4666
  if (isReady && (didSizeChange || didReadyAnchorChange)) {
4874
4667
  state.anchoredEndSpaceReadyAnchorIndex = nextAnchorIndex;
@@ -4877,8 +4670,9 @@ function maybeUpdateAnchoredEndSpace(ctx) {
4877
4670
  set$(ctx, "anchoredEndSpaceSize", nextSize);
4878
4671
  (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
4879
4672
  }
4880
- if (didSizeChange && (anchoredEndSpace == null ? void 0 : anchoredEndSpace.includeInEndInset)) {
4881
- updateScroll(ctx, state.scroll, true);
4673
+ if (didEffectiveSizeChange) {
4674
+ updateContentMetricsState(ctx);
4675
+ updateScroll(ctx, state.scroll, true, { markHasScrolled: false });
4882
4676
  }
4883
4677
  (_b = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onReady) == null ? void 0 : _b.call(anchoredEndSpace, { anchorIndex: nextAnchorIndex, anchorKey: nextAnchorKey, size: nextSize });
4884
4678
  }
@@ -4907,16 +4701,10 @@ function runOrScheduleMVCPRecalculate(ctx) {
4907
4701
  }
4908
4702
  } else if (Platform.OS === "web") {
4909
4703
  if (!state.mvcpAnchorLock) {
4910
- if (state.queuedMVCPRecalculate !== void 0) {
4911
- cancelAnimationFrame(state.queuedMVCPRecalculate);
4912
- state.queuedMVCPRecalculate = void 0;
4913
- }
4704
+ state.scheduledWork.cancel("mvcpRecalculate");
4914
4705
  calculateItemsInView(ctx, { doMVCP: true });
4915
- } else if (state.queuedMVCPRecalculate === void 0) {
4916
- state.queuedMVCPRecalculate = requestAnimationFrame(() => {
4917
- state.queuedMVCPRecalculate = void 0;
4918
- calculateItemsInView(ctx, { doMVCP: true });
4919
- });
4706
+ } else if (!state.scheduledWork.has("mvcpRecalculate")) {
4707
+ state.scheduledWork.frame(() => calculateItemsInView(ctx, { doMVCP: true }), "mvcpRecalculate");
4920
4708
  }
4921
4709
  } else {
4922
4710
  calculateItemsInView(ctx, { doMVCP: true });
@@ -4932,42 +4720,28 @@ function updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal) {
4932
4720
  }
4933
4721
  }
4934
4722
  }
4723
+ var activeItemSizeBatches;
4724
+ function batchItemSizeUpdates(runUpdates) {
4725
+ const isOuterBatch = activeItemSizeBatches === void 0;
4726
+ activeItemSizeBatches != null ? activeItemSizeBatches : activeItemSizeBatches = /* @__PURE__ */ new Map();
4727
+ try {
4728
+ runUpdates();
4729
+ } finally {
4730
+ if (isOuterBatch) {
4731
+ const batches = activeItemSizeBatches;
4732
+ activeItemSizeBatches = void 0;
4733
+ for (const [ctx, measurements] of batches) {
4734
+ updateItemSizesBatch(ctx, measurements);
4735
+ }
4736
+ }
4737
+ }
4738
+ }
4935
4739
  function mergeItemSizeUpdateResult(result, next) {
4936
4740
  result.didChange || (result.didChange = next.didChange);
4937
4741
  result.didMeasureUserScrollAnchorResetItem || (result.didMeasureUserScrollAnchorResetItem = next.didMeasureUserScrollAnchorResetItem);
4938
4742
  result.needsRecalculate || (result.needsRecalculate = next.needsRecalculate);
4939
4743
  result.shouldMaintainScrollAtEnd || (result.shouldMaintainScrollAtEnd = next.shouldMaintainScrollAtEnd);
4940
4744
  }
4941
- var batchedItemSizeRecalculates = /* @__PURE__ */ new WeakMap();
4942
- function flushBatchedItemSizeRecalculate(ctx, didFallback = false, expectedResult) {
4943
- const result = batchedItemSizeRecalculates.get(ctx);
4944
- if (!result || expectedResult && result !== expectedResult) {
4945
- return;
4946
- }
4947
- batchedItemSizeRecalculates.delete(ctx);
4948
- if (didFallback) {
4949
- ctx.state.pendingLayoutEffectMeasurements = void 0;
4950
- }
4951
- flushItemSizeUpdates(ctx, result);
4952
- }
4953
- function queueItemSizeRecalculate(ctx, result) {
4954
- var _a3, _b;
4955
- const batch = batchedItemSizeRecalculates.get(ctx);
4956
- if (batch) {
4957
- mergeItemSizeUpdateResult(batch, result);
4958
- } else {
4959
- const nextBatch = { ...result };
4960
- batchedItemSizeRecalculates.set(ctx, nextBatch);
4961
- if ((_a3 = ctx.state.pendingLayoutEffectMeasurements) == null ? void 0 : _a3.size) {
4962
- requestAnimationFrame(() => {
4963
- flushBatchedItemSizeRecalculate(ctx, true, nextBatch);
4964
- });
4965
- }
4966
- }
4967
- if (!((_b = ctx.state.pendingLayoutEffectMeasurements) == null ? void 0 : _b.size)) {
4968
- flushBatchedItemSizeRecalculate(ctx);
4969
- }
4970
- }
4971
4745
  function flushItemSizeUpdates(ctx, result) {
4972
4746
  var _a3;
4973
4747
  const state = ctx.state;
@@ -4977,59 +4751,44 @@ function flushItemSizeUpdates(ctx, result) {
4977
4751
  } else if (result.didMeasureUserScrollAnchorResetItem && ((_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys.size) === 0) {
4978
4752
  state.userScrollAnchorReset = void 0;
4979
4753
  }
4754
+ if (result.didChange) {
4755
+ maybeUpdateAnchoredEndSpace(ctx);
4756
+ }
4980
4757
  if (result.didChange && result.shouldMaintainScrollAtEnd) {
4981
4758
  doMaintainScrollAtEnd(ctx);
4982
4759
  }
4983
4760
  }
4984
4761
  function updateItemSizes(ctx, measurement) {
4985
- var _a3, _b, _c, _d;
4986
- const state = ctx.state;
4987
- let didDrainLayoutEffectMeasurements = false;
4988
- if (IsNewArchitecture && measurement.fromLayoutEffect) {
4989
- const pendingLayoutEffectMeasurements = state.pendingLayoutEffectMeasurements;
4990
- if ((pendingLayoutEffectMeasurements == null ? void 0 : pendingLayoutEffectMeasurements.delete(measurement.itemKey)) && pendingLayoutEffectMeasurements.size === 0) {
4991
- state.pendingLayoutEffectMeasurements = void 0;
4992
- didDrainLayoutEffectMeasurements = true;
4993
- }
4994
- }
4995
- const pendingKeys = (_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys;
4996
- const shouldBatchPendingMeasurements = IsNewArchitecture && measurement.fromLayoutEffect && !!(pendingKeys == null ? void 0 : pendingKeys.size);
4997
- const shouldQueueRecalculate = IsNewArchitecture && !!measurement.fromLayoutEffect;
4998
- let result;
4999
- if (!shouldBatchPendingMeasurements) {
5000
- result = applyItemSize(ctx, measurement.itemKey, measurement.size);
5001
- } else {
5002
- result = {};
5003
- const updateContainerItemSize = (itemKey, containerId, size) => {
5004
- if (containerId !== void 0 && peek$(ctx, `containerItemKey${containerId}`) === itemKey) {
5005
- mergeItemSizeUpdateResult(result, applyItemSize(ctx, itemKey, size));
5006
- }
5007
- };
5008
- updateContainerItemSize(measurement.itemKey, measurement.containerId, measurement.size);
5009
- const keys = Array.from(pendingKeys);
5010
- for (const itemKey of keys) {
5011
- const containerId = state.containerItemKeys.get(itemKey);
5012
- if (containerId !== void 0) {
5013
- (_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) => {
5014
- if (pendingKeys.has(itemKey)) {
5015
- updateContainerItemSize(itemKey, containerId, { height, width });
5016
- }
5017
- });
5018
- }
4762
+ if (activeItemSizeBatches) {
4763
+ const measurements = activeItemSizeBatches.get(ctx);
4764
+ if (measurements) {
4765
+ measurements.push(measurement);
4766
+ } else {
4767
+ activeItemSizeBatches.set(ctx, [measurement]);
5019
4768
  }
5020
- }
5021
- if (shouldQueueRecalculate && result.needsRecalculate) {
5022
- queueItemSizeRecalculate(ctx, result);
5023
4769
  } else {
5024
- flushItemSizeUpdates(ctx, result);
5025
- }
5026
- if (didDrainLayoutEffectMeasurements) {
5027
- flushBatchedItemSizeRecalculate(ctx);
4770
+ updateItemSizesBatch(ctx, [measurement]);
5028
4771
  }
5029
4772
  }
5030
- function applyItemSize(ctx, itemKey, sizeObj) {
4773
+ function updateItemSizesBatch(ctx, measurements) {
5031
4774
  var _a3;
5032
4775
  const state = ctx.state;
4776
+ const result = {};
4777
+ for (const measurement of measurements) {
4778
+ const ownsMeasuredItem = measurement.containerId === void 0 || peek$(ctx, `containerItemKey${measurement.containerId}`) === measurement.itemKey;
4779
+ if (ownsMeasuredItem) {
4780
+ const index = state.indexByKey.get(measurement.itemKey);
4781
+ const itemData = index === void 0 ? void 0 : (_a3 = state.props.data) == null ? void 0 : _a3[index];
4782
+ const metadata = measurement.containerId !== void 0 && index !== void 0 && itemData !== void 0 ? resolveContainerItemMetadata(state, measurement.containerId, index, itemData) : void 0;
4783
+ const nextResult = applyItemSize(ctx, measurement.itemKey, measurement.size, metadata);
4784
+ mergeItemSizeUpdateResult(result, nextResult);
4785
+ }
4786
+ }
4787
+ flushItemSizeUpdates(ctx, result);
4788
+ }
4789
+ function applyItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
4790
+ var _a3, _b;
4791
+ const state = ctx.state;
5033
4792
  const userScrollAnchorReset = state.userScrollAnchorReset;
5034
4793
  const didMeasureUserScrollAnchorResetItem = !!(userScrollAnchorReset == null ? void 0 : userScrollAnchorReset.keys.delete(itemKey));
5035
4794
  const {
@@ -5039,7 +4798,6 @@ function applyItemSize(ctx, itemKey, sizeObj) {
5039
4798
  } = state;
5040
4799
  if (!data) return { didMeasureUserScrollAnchorResetItem };
5041
4800
  const index = state.indexByKey.get(itemKey);
5042
- let resolvedMeasurementItem;
5043
4801
  if (getFixedItemSize) {
5044
4802
  if (index === void 0) {
5045
4803
  return { didMeasureUserScrollAnchorResetItem };
@@ -5048,14 +4806,15 @@ function applyItemSize(ctx, itemKey, sizeObj) {
5048
4806
  if (itemData === void 0) {
5049
4807
  return { didMeasureUserScrollAnchorResetItem };
5050
4808
  }
5051
- const type = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
5052
- const size2 = getFixedItemSize(itemData, index, type);
5053
- resolvedMeasurementItem = {
5054
- didResolveFixedItemSize: true,
5055
- fixedItemSize: size2,
5056
- itemData,
5057
- itemType: type
5058
- };
4809
+ if (!(resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize)) {
4810
+ const type = (_b = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemType) != null ? _b : getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
4811
+ resolvedMeasurementItem = {
4812
+ didResolveFixedItemSize: true,
4813
+ fixedItemSize: getFixedItemSize(itemData, index, type),
4814
+ itemType: type
4815
+ };
4816
+ }
4817
+ const size2 = resolvedMeasurementItem.fixedItemSize;
5059
4818
  if (size2 !== void 0 && size2 === sizesKnown.get(itemKey)) {
5060
4819
  updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
5061
4820
  return { didMeasureUserScrollAnchorResetItem };
@@ -5084,7 +4843,6 @@ function applyItemSize(ctx, itemKey, sizeObj) {
5084
4843
  previous: size - diff,
5085
4844
  size
5086
4845
  });
5087
- maybeUpdateAnchoredEndSpace(ctx);
5088
4846
  }
5089
4847
  if (minIndexSizeChanged !== void 0) {
5090
4848
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
@@ -5105,7 +4863,7 @@ function applyItemSize(ctx, itemKey, sizeObj) {
5105
4863
  };
5106
4864
  }
5107
4865
  function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
5108
- var _a3, _b, _c;
4866
+ var _a3, _b;
5109
4867
  const state = ctx.state;
5110
4868
  const {
5111
4869
  indexByKey,
@@ -5115,11 +4873,11 @@ function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
5115
4873
  } = state;
5116
4874
  if (!data) return 0;
5117
4875
  const index = indexByKey.get(itemKey);
5118
- const itemData = (_a3 = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemData) != null ? _a3 : data[index];
4876
+ const itemData = data[index];
5119
4877
  let itemType = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemType;
5120
4878
  let fixedItemSize = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.fixedItemSize;
5121
4879
  if (getFixedItemSize && !(resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize)) {
5122
- itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
4880
+ itemType = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
5123
4881
  fixedItemSize = getFixedItemSize(itemData, index, itemType);
5124
4882
  }
5125
4883
  const resolvedItemSize = (resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize) || itemType !== void 0 || fixedItemSize !== void 0 ? {
@@ -5136,29 +4894,470 @@ function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
5136
4894
  const size = Platform.OS === "web" ? Math.round(rawSize) : roundSize(rawSize);
5137
4895
  sizesKnown.set(itemKey, size);
5138
4896
  if (fixedItemSize === void 0 && size > 0) {
5139
- itemType != null ? itemType : itemType = getItemType ? (_c = getItemType(itemData, index)) != null ? _c : "" : "";
4897
+ itemType != null ? itemType : itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
5140
4898
  let averages = averageSizes[itemType];
5141
4899
  if (!averages) {
5142
4900
  averages = averageSizes[itemType] = { avg: 0, num: 0 };
5143
4901
  }
5144
- if (averages.num === 0) {
5145
- averages.avg = size;
5146
- averages.num++;
5147
- } else if (prevSizeKnown !== void 0 && prevSizeKnown > 0) {
5148
- averages.avg += (size - prevSizeKnown) / averages.num;
5149
- } else {
5150
- averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
5151
- averages.num++;
4902
+ if (averages.num === 0) {
4903
+ averages.avg = size;
4904
+ averages.num++;
4905
+ } else if (prevSizeKnown !== void 0 && prevSizeKnown > 0) {
4906
+ averages.avg += (size - prevSizeKnown) / averages.num;
4907
+ } else {
4908
+ averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
4909
+ averages.num++;
4910
+ }
4911
+ }
4912
+ if (!prevSize || Math.abs(prevSize - size) > 0.1) {
4913
+ setSize(ctx, itemKey, size);
4914
+ return size - prevSize;
4915
+ }
4916
+ return 0;
4917
+ }
4918
+
4919
+ // src/core/measureContainersInLayoutEffect.native.ts
4920
+ function resolveFixedItemSize(ctx, containerId, itemKey) {
4921
+ var _a3;
4922
+ const state = ctx.state;
4923
+ const { data, getFixedItemSize } = state.props;
4924
+ const index = state.indexByKey.get(itemKey);
4925
+ let fixedItemSize;
4926
+ if (data && getFixedItemSize && index !== void 0) {
4927
+ const itemData = data[index];
4928
+ if (itemData !== void 0) {
4929
+ fixedItemSize = (_a3 = resolveContainerItemMetadata(state, containerId, index, itemData)) == null ? void 0 : _a3.fixedItemSize;
4930
+ }
4931
+ }
4932
+ return fixedItemSize;
4933
+ }
4934
+ function resolveSkippedAnchorReset(ctx, itemKey) {
4935
+ const state = ctx.state;
4936
+ const anchorReset = state.userScrollAnchorReset;
4937
+ if ((anchorReset == null ? void 0 : anchorReset.keys.delete(itemKey)) && anchorReset.keys.size === 0) {
4938
+ state.userScrollAnchorReset = void 0;
4939
+ }
4940
+ }
4941
+ function measureContainersInLayoutEffect(ctx, targetContainerIds = null) {
4942
+ var _a3, _b, _c;
4943
+ const state = ctx.state;
4944
+ const measurements = [];
4945
+ let isCollectingSynchronousMeasurements = true;
4946
+ const containerIds = targetContainerIds != null ? targetContainerIds : ctx.viewRefs.keys();
4947
+ for (const containerId of containerIds) {
4948
+ const viewRef = ctx.viewRefs.get(containerId);
4949
+ const itemKey = peek$(ctx, `containerItemKey${containerId}`);
4950
+ if (itemKey !== void 0) {
4951
+ const generation = ((_a3 = state.containerItemGenerations[containerId]) != null ? _a3 : 0) + 1;
4952
+ state.containerItemGenerations[containerId] = generation;
4953
+ const fixedItemSize = resolveFixedItemSize(ctx, containerId, itemKey);
4954
+ const canSkipMeasurement = !state.needsOtherAxisSize && fixedItemSize !== void 0 && state.sizesKnown.get(itemKey) === fixedItemSize + ctx.scrollAxisGap;
4955
+ if (canSkipMeasurement) {
4956
+ resolveSkippedAnchorReset(ctx, itemKey);
4957
+ } else if (viewRef) {
4958
+ (_c = (_b = viewRef.current) == null ? void 0 : _b.measure) == null ? void 0 : _c.call(_b, (_x, _y, width, height) => {
4959
+ var _a4;
4960
+ const isCurrentGeneration = ((_a4 = ctx.state.containerItemGenerations[containerId]) != null ? _a4 : 0) === generation;
4961
+ if (isCurrentGeneration) {
4962
+ const measurement = {
4963
+ containerId,
4964
+ itemKey,
4965
+ size: { height, width }
4966
+ };
4967
+ if (isCollectingSynchronousMeasurements) {
4968
+ measurements.push(measurement);
4969
+ } else {
4970
+ updateItemSizes(ctx, measurement);
4971
+ }
4972
+ }
4973
+ });
4974
+ }
4975
+ }
4976
+ }
4977
+ isCollectingSynchronousMeasurements = false;
4978
+ if (measurements.length > 0) {
4979
+ updateItemSizesBatch(ctx, measurements);
4980
+ }
4981
+ }
4982
+ var typedForwardRef = React2__namespace.forwardRef;
4983
+ var typedMemo = React2__namespace.memo;
4984
+
4985
+ // src/components/ContainerLayoutCoordinator.tsx
4986
+ var ContainerLayoutCoordinator = typedMemo(function ContainerLayoutCoordinatorComponent({
4987
+ children
4988
+ }) {
4989
+ const ctx = useStateContext();
4990
+ const [containerLayoutEpoch] = useArr$(["containerLayoutEpoch"]);
4991
+ React2__namespace.useLayoutEffect(() => {
4992
+ if (IsNewArchitecture) {
4993
+ const targetContainerIds = getContainerLayoutEffectScope(ctx);
4994
+ if (targetContainerIds !== void 0) {
4995
+ measureContainersInLayoutEffect(ctx, targetContainerIds);
4996
+ }
4997
+ }
4998
+ }, [ctx, containerLayoutEpoch]);
4999
+ return children;
5000
+ });
5001
+
5002
+ // src/components/stickyPositionUtils.ts
5003
+ function getStickyPushLimit(state, index, itemKey) {
5004
+ if (!itemKey) {
5005
+ return void 0;
5006
+ }
5007
+ const currentSize = state.sizes.get(itemKey);
5008
+ if (!(currentSize && currentSize > 0)) {
5009
+ return void 0;
5010
+ }
5011
+ const stickyIndexInArray = state.props.stickyHeaderIndicesArr.indexOf(index);
5012
+ if (stickyIndexInArray === -1) {
5013
+ return void 0;
5014
+ }
5015
+ const nextStickyIndex = state.props.stickyHeaderIndicesArr[stickyIndexInArray + 1];
5016
+ if (nextStickyIndex === void 0) {
5017
+ return void 0;
5018
+ }
5019
+ const nextStickyPosition = state.positions[nextStickyIndex];
5020
+ if (nextStickyPosition === void 0) {
5021
+ return void 0;
5022
+ }
5023
+ return nextStickyPosition - currentSize;
5024
+ }
5025
+ var useAnimatedValue = (initialValue) => {
5026
+ const [animAnimatedValue] = React2.useState(() => new ReactNative.Animated.Value(initialValue));
5027
+ return animAnimatedValue;
5028
+ };
5029
+
5030
+ // src/hooks/useValue$.ts
5031
+ function useValue$(key, params) {
5032
+ const { getValue } = params || {};
5033
+ const ctx = useStateContext();
5034
+ const getNewValue = () => {
5035
+ var _a3;
5036
+ return (_a3 = getValue ? getValue(peek$(ctx, key)) : peek$(ctx, key)) != null ? _a3 : 0;
5037
+ };
5038
+ const animValue = useAnimatedValue(getNewValue());
5039
+ React2.useLayoutEffect(() => {
5040
+ const syncCurrentValue = () => {
5041
+ animValue.setValue(getNewValue());
5042
+ };
5043
+ const unsubscribe = listen$(ctx, key, syncCurrentValue);
5044
+ syncCurrentValue();
5045
+ return unsubscribe;
5046
+ }, [animValue, ctx, key]);
5047
+ return animValue;
5048
+ }
5049
+ var getComponent = (Component) => {
5050
+ if (React2__namespace.isValidElement(Component)) {
5051
+ return Component;
5052
+ }
5053
+ if (Component) {
5054
+ return /* @__PURE__ */ React2__namespace.createElement(Component, null);
5055
+ }
5056
+ return null;
5057
+ };
5058
+
5059
+ // src/components/PositionView.native.tsx
5060
+ var PositionViewState = typedMemo(function PositionViewState2({
5061
+ id,
5062
+ horizontal,
5063
+ style,
5064
+ refView,
5065
+ ...rest
5066
+ }) {
5067
+ const [position = POSITION_OUT_OF_VIEW, _itemKey] = useArr$([`containerPosition${id}`, `containerItemKey${id}`]);
5068
+ return /* @__PURE__ */ React2__namespace.createElement(ReactNative.View, { ref: refView, style: [style, horizontal ? { left: position } : { top: position }], ...rest });
5069
+ });
5070
+ var PositionViewAnimated = typedMemo(function PositionViewAnimated2({
5071
+ id,
5072
+ horizontal,
5073
+ style,
5074
+ refView,
5075
+ ...rest
5076
+ }) {
5077
+ const position$ = useValue$(`containerPosition${id}`, {
5078
+ getValue: (v) => v != null ? v : POSITION_OUT_OF_VIEW
5079
+ });
5080
+ const position = horizontal ? { left: position$ } : { top: position$ };
5081
+ return /* @__PURE__ */ React2__namespace.createElement(ReactNative.Animated.View, { ref: refView, style: [style, position], ...rest });
5082
+ });
5083
+ var PositionViewSticky = typedMemo(function PositionViewSticky2({
5084
+ id,
5085
+ horizontal,
5086
+ style,
5087
+ refView,
5088
+ animatedScrollY,
5089
+ stickyHeaderConfig,
5090
+ children,
5091
+ ...rest
5092
+ }) {
5093
+ const ctx = useStateContext();
5094
+ const [
5095
+ position = POSITION_OUT_OF_VIEW,
5096
+ alignItemsAtEndPadding = 0,
5097
+ headerSize = 0,
5098
+ stylePaddingTop = 0,
5099
+ itemKey,
5100
+ itemIndex,
5101
+ _totalSize = 0
5102
+ ] = useArr$([
5103
+ `containerPosition${id}`,
5104
+ "alignItemsAtEndPadding",
5105
+ "headerSize",
5106
+ "stylePaddingTop",
5107
+ `containerItemKey${id}`,
5108
+ `containerItemIndex${id}`,
5109
+ "totalSize"
5110
+ ]);
5111
+ const pushLimit = React2__namespace.useMemo(
5112
+ () => getStickyPushLimit(ctx.state, itemIndex, itemKey),
5113
+ [ctx.state, itemIndex, itemKey, _totalSize]
5114
+ );
5115
+ const transform = React2__namespace.useMemo(() => {
5116
+ var _a3;
5117
+ if (animatedScrollY) {
5118
+ const stickyConfigOffset = (_a3 = stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset) != null ? _a3 : 0;
5119
+ const stickyStart = position + headerSize + stylePaddingTop + alignItemsAtEndPadding - stickyConfigOffset;
5120
+ let nextStickyPosition;
5121
+ if (pushLimit !== void 0) {
5122
+ if (pushLimit <= position) {
5123
+ nextStickyPosition = pushLimit;
5124
+ } else {
5125
+ nextStickyPosition = animatedScrollY.interpolate({
5126
+ extrapolateLeft: "clamp",
5127
+ extrapolateRight: "clamp",
5128
+ inputRange: [stickyStart, stickyStart + (pushLimit - position)],
5129
+ outputRange: [position, pushLimit]
5130
+ });
5131
+ }
5132
+ } else {
5133
+ nextStickyPosition = animatedScrollY.interpolate({
5134
+ extrapolateLeft: "clamp",
5135
+ extrapolateRight: "extend",
5136
+ inputRange: [stickyStart, stickyStart + 5e3],
5137
+ outputRange: [position, position + 5e3]
5138
+ });
5139
+ }
5140
+ return horizontal ? [{ translateX: nextStickyPosition }] : [{ translateY: nextStickyPosition }];
5141
+ }
5142
+ }, [
5143
+ alignItemsAtEndPadding,
5144
+ animatedScrollY,
5145
+ headerSize,
5146
+ position,
5147
+ pushLimit,
5148
+ stylePaddingTop,
5149
+ stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset
5150
+ ]);
5151
+ const viewStyle = React2__namespace.useMemo(
5152
+ () => [style, { zIndex: itemIndex + 1e3 }, { transform }],
5153
+ [style, itemIndex, transform]
5154
+ );
5155
+ const renderStickyHeaderBackdrop = React2__namespace.useMemo(() => {
5156
+ if (!(stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent)) {
5157
+ return null;
5158
+ }
5159
+ return /* @__PURE__ */ React2__namespace.createElement(
5160
+ ReactNative.View,
5161
+ {
5162
+ style: {
5163
+ inset: 0,
5164
+ pointerEvents: "none",
5165
+ position: "absolute"
5166
+ }
5167
+ },
5168
+ getComponent(stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent)
5169
+ );
5170
+ }, [stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent]);
5171
+ return /* @__PURE__ */ React2__namespace.createElement(ReactNative.Animated.View, { ref: refView, style: viewStyle, ...rest }, renderStickyHeaderBackdrop, children);
5172
+ });
5173
+ var PositionView = IsNewArchitecture ? PositionViewState : PositionViewAnimated;
5174
+ function useInit(cb) {
5175
+ React2.useState(() => cb());
5176
+ }
5177
+
5178
+ // src/state/ContextContainer.ts
5179
+ var ContextContainer = React2.createContext(null);
5180
+ var NO_CONTAINER_ID = -1;
5181
+ function useContextContainer() {
5182
+ return React2.useContext(ContextContainer);
5183
+ }
5184
+ function useContainerItemSignals(containerContext) {
5185
+ var _a3;
5186
+ const containerId = (_a3 = containerContext == null ? void 0 : containerContext.containerId) != null ? _a3 : NO_CONTAINER_ID;
5187
+ const [itemKey, itemIndex, item] = useArr$([
5188
+ `containerItemKey${containerId}`,
5189
+ `containerItemIndex${containerId}`,
5190
+ `containerItemData${containerId}`
5191
+ ]);
5192
+ return {
5193
+ hasItemInfo: !!containerContext && itemKey !== void 0 && itemIndex !== void 0,
5194
+ item,
5195
+ itemIndex,
5196
+ itemKey
5197
+ };
5198
+ }
5199
+ function useAdaptiveRender() {
5200
+ const [mode] = useArr$(["adaptiveRender"]);
5201
+ return mode;
5202
+ }
5203
+ function useAdaptiveRenderChange(callback) {
5204
+ const ctx = useStateContext();
5205
+ const callbackRef = React2.useRef(callback);
5206
+ callbackRef.current = callback;
5207
+ React2.useLayoutEffect(() => {
5208
+ let mode = peek$(ctx, "adaptiveRender");
5209
+ return listen$(ctx, "adaptiveRender", (nextMode) => {
5210
+ if (mode !== nextMode) {
5211
+ mode = nextMode;
5212
+ callbackRef.current(nextMode);
5213
+ }
5214
+ });
5215
+ }, [ctx]);
5216
+ }
5217
+ function useViewability(callback, configId) {
5218
+ const ctx = useStateContext();
5219
+ const containerContext = useContextContainer();
5220
+ useInit(() => {
5221
+ if (!containerContext) {
5222
+ return;
5223
+ }
5224
+ const { containerId } = containerContext;
5225
+ const key = containerId + (configId != null ? configId : "");
5226
+ const value = ctx.mapViewabilityValues.get(key);
5227
+ if (value) {
5228
+ callback(value);
5229
+ }
5230
+ });
5231
+ React2.useEffect(() => {
5232
+ if (!containerContext) {
5233
+ return;
5234
+ }
5235
+ const { containerId } = containerContext;
5236
+ const key = containerId + (configId != null ? configId : "");
5237
+ ctx.mapViewabilityCallbacks.set(key, callback);
5238
+ return () => {
5239
+ ctx.mapViewabilityCallbacks.delete(key);
5240
+ };
5241
+ }, [ctx, callback, configId, containerContext]);
5242
+ }
5243
+ function useViewabilityAmount(callback) {
5244
+ const ctx = useStateContext();
5245
+ const containerContext = useContextContainer();
5246
+ useInit(() => {
5247
+ if (!containerContext) {
5248
+ return;
5249
+ }
5250
+ const { containerId } = containerContext;
5251
+ const value = ctx.mapViewabilityAmountValues.get(containerId);
5252
+ if (value) {
5253
+ callback(value);
5254
+ }
5255
+ });
5256
+ React2.useEffect(() => {
5257
+ if (!containerContext) {
5258
+ return;
5259
+ }
5260
+ const { containerId } = containerContext;
5261
+ ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
5262
+ return () => {
5263
+ ctx.mapViewabilityAmountCallbacks.delete(containerId);
5264
+ };
5265
+ }, [ctx, callback, containerContext]);
5266
+ }
5267
+ function useRecyclingEffect(effect) {
5268
+ const containerContext = useContextContainer();
5269
+ const { hasItemInfo, item, itemIndex, itemKey } = useContainerItemSignals(containerContext);
5270
+ const prevInfo = React2.useRef(void 0);
5271
+ React2.useEffect(() => {
5272
+ if (!hasItemInfo) {
5273
+ return;
5274
+ }
5275
+ let ret;
5276
+ if (prevInfo.current) {
5277
+ ret = effect({
5278
+ index: itemIndex,
5279
+ item,
5280
+ prevIndex: prevInfo.current.index,
5281
+ prevItem: prevInfo.current.item
5282
+ });
5152
5283
  }
5284
+ prevInfo.current = {
5285
+ index: itemIndex,
5286
+ item
5287
+ };
5288
+ return ret;
5289
+ }, [effect, hasItemInfo, itemIndex, item, itemKey]);
5290
+ }
5291
+ function useRecyclingState(valueOrFun) {
5292
+ const containerContext = useContextContainer();
5293
+ const { hasItemInfo, item, itemIndex, itemKey } = useContainerItemSignals(containerContext);
5294
+ const computeValue = () => {
5295
+ if (isFunction(valueOrFun)) {
5296
+ const initializer = valueOrFun;
5297
+ return hasItemInfo ? initializer({
5298
+ index: itemIndex,
5299
+ item,
5300
+ prevIndex: void 0,
5301
+ prevItem: void 0
5302
+ }) : initializer();
5303
+ }
5304
+ return valueOrFun;
5305
+ };
5306
+ const [stateValue, setStateValue] = React2.useState(() => {
5307
+ return computeValue();
5308
+ });
5309
+ const prevItemKeyRef = React2.useRef(hasItemInfo ? itemKey : null);
5310
+ if (hasItemInfo && prevItemKeyRef.current !== itemKey) {
5311
+ prevItemKeyRef.current = itemKey;
5312
+ setStateValue(computeValue());
5153
5313
  }
5154
- if (!prevSize || Math.abs(prevSize - size) > 0.1) {
5155
- setSize(ctx, itemKey, size);
5156
- return size - prevSize;
5157
- }
5158
- return 0;
5314
+ const triggerLayout = containerContext == null ? void 0 : containerContext.triggerLayout;
5315
+ const setState = React2.useCallback(
5316
+ (newState) => {
5317
+ if (!triggerLayout) {
5318
+ return;
5319
+ }
5320
+ setStateValue((prevValue) => {
5321
+ return isFunction(newState) ? newState(prevValue) : newState;
5322
+ });
5323
+ triggerLayout();
5324
+ },
5325
+ [triggerLayout]
5326
+ );
5327
+ return [stateValue, setState];
5328
+ }
5329
+ function useIsLastItem() {
5330
+ var _a3;
5331
+ const containerContext = useContextContainer();
5332
+ const containerId = (_a3 = containerContext == null ? void 0 : containerContext.containerId) != null ? _a3 : NO_CONTAINER_ID;
5333
+ const [itemKey] = useArr$([`containerItemKey${containerId}`]);
5334
+ const isLast = useSelector$("lastItemKeys", (lastItemKeys) => {
5335
+ if (containerContext && !isNullOrUndefined(itemKey)) {
5336
+ return (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false;
5337
+ }
5338
+ return false;
5339
+ });
5340
+ return isLast;
5341
+ }
5342
+ function useListScrollSize() {
5343
+ const [scrollSize] = useArr$(["scrollSize"]);
5344
+ return scrollSize;
5345
+ }
5346
+ var noop = () => {
5347
+ };
5348
+ function useSyncLayout() {
5349
+ const containerContext = useContextContainer();
5350
+ return IsNewArchitecture && containerContext ? containerContext.triggerLayout : noop;
5351
+ }
5352
+
5353
+ // src/components/Separator.tsx
5354
+ function Separator({ ItemSeparatorComponent, leadingItem }) {
5355
+ const isLastItem = useIsLastItem();
5356
+ return isLastItem ? null : /* @__PURE__ */ React2__namespace.createElement(ItemSeparatorComponent, { leadingItem });
5159
5357
  }
5160
5358
  function useOnLayoutSync({
5161
5359
  ref,
5360
+ measureInLayoutEffect = true,
5162
5361
  onLayoutProp,
5163
5362
  onLayoutChange
5164
5363
  }, deps = []) {
@@ -5172,16 +5371,154 @@ function useOnLayoutSync({
5172
5371
  );
5173
5372
  if (IsNewArchitecture) {
5174
5373
  React2.useLayoutEffect(() => {
5175
- if (ref.current) {
5374
+ if (measureInLayoutEffect && ref.current) {
5176
5375
  ref.current.measure((x, y, width, height) => {
5177
5376
  onLayoutChange({ height, width, x, y }, true);
5178
5377
  });
5179
5378
  }
5180
- }, deps);
5379
+ }, [measureInLayoutEffect, ...deps]);
5181
5380
  }
5182
5381
  return { onLayout };
5183
5382
  }
5184
5383
 
5384
+ // src/hooks/useContainerMeasurement.tsx
5385
+ var pendingWebShrinkMeasurements = /* @__PURE__ */ new Map();
5386
+ var pendingWebShrinkFrame;
5387
+ function cancelWebShrinkMeasurement(state) {
5388
+ pendingWebShrinkMeasurements.delete(state);
5389
+ }
5390
+ function scheduleWebShrinkMeasurement(state, confirmMeasurement) {
5391
+ pendingWebShrinkMeasurements.set(state, confirmMeasurement);
5392
+ if (pendingWebShrinkFrame === void 0) {
5393
+ pendingWebShrinkFrame = requestAnimationFrame(() => {
5394
+ const callbacks = Array.from(pendingWebShrinkMeasurements.values());
5395
+ pendingWebShrinkMeasurements.clear();
5396
+ pendingWebShrinkFrame = void 0;
5397
+ batchItemSizeUpdates(() => {
5398
+ for (const callback of callbacks) {
5399
+ callback();
5400
+ }
5401
+ });
5402
+ });
5403
+ }
5404
+ }
5405
+ function processContainerLayout({ containerId, ctx, rectangle, ref, state }) {
5406
+ var _a3, _b;
5407
+ const listState = ctx.state;
5408
+ const currentItemKey = state.itemKey;
5409
+ state.didLayout = true;
5410
+ let layout = rectangle;
5411
+ const axis = state.horizontal ? "width" : "height";
5412
+ const size = roundSize(rectangle[axis]);
5413
+ const localPreviousSize = state.lastSize ? roundSize(state.lastSize[axis]) : void 0;
5414
+ const coreKnownSize = listState.sizesKnown.get(currentItemKey);
5415
+ const previousSize = Platform.OS === "web" ? coreKnownSize : localPreviousSize;
5416
+ const applyLayout = () => {
5417
+ state.lastSize = layout;
5418
+ updateItemSizes(ctx, {
5419
+ containerId,
5420
+ itemKey: currentItemKey,
5421
+ size: layout
5422
+ });
5423
+ };
5424
+ const shouldDeferWebShrinkLayoutUpdate = Platform.OS === "web" && !isInMVCPActiveMode(listState) && previousSize !== void 0 && size + 1 < previousSize;
5425
+ if (shouldDeferWebShrinkLayoutUpdate) {
5426
+ scheduleWebShrinkMeasurement(state, () => {
5427
+ var _a4;
5428
+ if (state.itemKey === currentItemKey) {
5429
+ const element = ref.current;
5430
+ const rect = (_a4 = element == null ? void 0 : element.getBoundingClientRect) == null ? void 0 : _a4.call(element);
5431
+ if (rect) {
5432
+ layout = { height: rect.height, width: rect.width };
5433
+ }
5434
+ applyLayout();
5435
+ }
5436
+ });
5437
+ } else {
5438
+ if (Platform.OS === "web") {
5439
+ cancelWebShrinkMeasurement(state);
5440
+ }
5441
+ if (IsNewArchitecture || size > 0) {
5442
+ applyLayout();
5443
+ } else {
5444
+ (_b = (_a3 = ref.current) == null ? void 0 : _a3.measure) == null ? void 0 : _b.call(_a3, (_x, _y, width, height) => {
5445
+ layout = { height, width };
5446
+ applyLayout();
5447
+ });
5448
+ }
5449
+ }
5450
+ }
5451
+ function useContainerMeasurement({
5452
+ containerId,
5453
+ ctx,
5454
+ horizontal,
5455
+ itemKey,
5456
+ ref
5457
+ }) {
5458
+ const stateRef = React2.useRef({
5459
+ didLayout: false,
5460
+ horizontal,
5461
+ itemKey
5462
+ });
5463
+ stateRef.current.horizontal = horizontal;
5464
+ stateRef.current.itemKey = itemKey;
5465
+ const [layoutRenderCount, forceLayoutRender] = React2.useState(0);
5466
+ const onLayoutChange = React2.useCallback(
5467
+ (rectangle) => {
5468
+ processContainerLayout({ containerId, ctx, rectangle, ref, state: stateRef.current });
5469
+ },
5470
+ [containerId, ctx, ref]
5471
+ );
5472
+ const triggerLayout = React2.useCallback(() => {
5473
+ if (IsNewArchitecture) {
5474
+ scheduleContainerLayout(ctx, containerId);
5475
+ } else {
5476
+ forceLayoutRender((value) => value + 1);
5477
+ }
5478
+ }, [containerId, ctx]);
5479
+ React2.useLayoutEffect(() => {
5480
+ ctx.containerLayoutTriggers.set(containerId, triggerLayout);
5481
+ return () => {
5482
+ cancelWebShrinkMeasurement(stateRef.current);
5483
+ if (ctx.containerLayoutTriggers.get(containerId) === triggerLayout) {
5484
+ ctx.containerLayoutTriggers.delete(containerId);
5485
+ }
5486
+ };
5487
+ }, [containerId, ctx, triggerLayout]);
5488
+ React2.useLayoutEffect(() => {
5489
+ if (IsNewArchitecture) {
5490
+ scheduleContainerLayout(ctx, containerId);
5491
+ }
5492
+ });
5493
+ const { onLayout } = useOnLayoutSync(
5494
+ {
5495
+ measureInLayoutEffect: !IsNewArchitecture,
5496
+ onLayoutChange,
5497
+ ref},
5498
+ [itemKey, layoutRenderCount]
5499
+ );
5500
+ React2.useEffect(() => {
5501
+ if (!IsNewArchitecture) {
5502
+ stateRef.current.didLayout = false;
5503
+ const timeout = setTimeout(() => {
5504
+ const state = stateRef.current;
5505
+ if (!state.didLayout && state.lastSize) {
5506
+ updateItemSizes(ctx, {
5507
+ containerId,
5508
+ itemKey: state.itemKey,
5509
+ size: state.lastSize
5510
+ });
5511
+ state.didLayout = true;
5512
+ }
5513
+ }, 16);
5514
+ return () => {
5515
+ clearTimeout(timeout);
5516
+ };
5517
+ }
5518
+ }, [containerId, ctx, itemKey]);
5519
+ return { onLayout, triggerLayout };
5520
+ }
5521
+
5185
5522
  // src/components/Container.tsx
5186
5523
  function getContainerPositionStyle({
5187
5524
  columnWrapperStyle,
@@ -5252,16 +5589,14 @@ var Container = typedMemo(function Container2({
5252
5589
  "extraData",
5253
5590
  `containerSticky${id}`
5254
5591
  ]);
5255
- const itemLayoutRef = React2.useRef({
5256
- didLayout: false,
5592
+ const ref = React2.useRef(null);
5593
+ const { onLayout, triggerLayout } = useContainerMeasurement({
5594
+ containerId: id,
5595
+ ctx,
5257
5596
  horizontal,
5258
5597
  itemKey,
5259
- pendingShrinkToken: 0
5598
+ ref
5260
5599
  });
5261
- itemLayoutRef.current.horizontal = horizontal;
5262
- itemLayoutRef.current.itemKey = itemKey;
5263
- const ref = React2.useRef(null);
5264
- const [layoutRenderCount, forceLayoutRender] = React2.useState(0);
5265
5600
  const resolvedColumn = column > 0 ? column : 1;
5266
5601
  const resolvedSpan = Math.min(Math.max(span || 1, 1), numColumns);
5267
5602
  const otherAxisPos = numColumns > 1 ? `${(resolvedColumn - 1) / numColumns * 100}%` : 0;
@@ -5292,63 +5627,7 @@ var Container = typedMemo(function Container2({
5292
5627
  () => itemKey !== void 0 ? getRenderedItem2(itemKey) : null,
5293
5628
  [itemKey, data, extraData]
5294
5629
  );
5295
- const { index, renderedItem } = renderedItemInfo || {};
5296
- const onLayoutChange = React2.useCallback((rectangle, fromLayoutEffect) => {
5297
- var _a3, _b;
5298
- const {
5299
- horizontal: currentHorizontal,
5300
- itemKey: currentItemKey,
5301
- lastSize,
5302
- pendingShrinkToken
5303
- } = itemLayoutRef.current;
5304
- if (isNullOrUndefined(currentItemKey)) {
5305
- return;
5306
- }
5307
- itemLayoutRef.current.didLayout = true;
5308
- let layout = rectangle;
5309
- const axis = currentHorizontal ? "width" : "height";
5310
- const size = roundSize(rectangle[axis]);
5311
- const prevSize = lastSize ? roundSize(lastSize[axis]) : void 0;
5312
- const doUpdate = () => {
5313
- itemLayoutRef.current.lastSize = layout;
5314
- updateItemSizes(ctx, {
5315
- containerId: id,
5316
- fromLayoutEffect,
5317
- itemKey: currentItemKey,
5318
- size: layout
5319
- });
5320
- itemLayoutRef.current.didLayout = true;
5321
- };
5322
- const shouldDeferWebShrinkLayoutUpdate = Platform.OS === "web" && !isInMVCPActiveMode(ctx.state) && prevSize !== void 0 && size + 1 < prevSize;
5323
- if (shouldDeferWebShrinkLayoutUpdate) {
5324
- const token = pendingShrinkToken + 1;
5325
- itemLayoutRef.current.pendingShrinkToken = token;
5326
- requestAnimationFrame(() => {
5327
- var _a4;
5328
- if (itemLayoutRef.current.pendingShrinkToken !== token) {
5329
- return;
5330
- }
5331
- const element = ref.current;
5332
- const rect = (_a4 = element == null ? void 0 : element.getBoundingClientRect) == null ? void 0 : _a4.call(element);
5333
- if (rect) {
5334
- layout = { height: rect.height, width: rect.width };
5335
- }
5336
- doUpdate();
5337
- });
5338
- return;
5339
- }
5340
- if (IsNewArchitecture || size > 0) {
5341
- doUpdate();
5342
- } else {
5343
- (_b = (_a3 = ref.current) == null ? void 0 : _a3.measure) == null ? void 0 : _b.call(_a3, (_x, _y, width, height) => {
5344
- layout = { height, width };
5345
- doUpdate();
5346
- });
5347
- }
5348
- }, []);
5349
- const triggerLayout = React2.useCallback(() => {
5350
- forceLayoutRender((v) => v + 1);
5351
- }, []);
5630
+ const { renderedItem } = renderedItemInfo || {};
5352
5631
  const contextValue = React2.useMemo(() => {
5353
5632
  ctx.viewRefs.set(id, ref);
5354
5633
  return {
@@ -5356,44 +5635,6 @@ var Container = typedMemo(function Container2({
5356
5635
  triggerLayout
5357
5636
  };
5358
5637
  }, [id, triggerLayout]);
5359
- React2.useLayoutEffect(() => {
5360
- ctx.containerLayoutTriggers.set(id, triggerLayout);
5361
- return () => {
5362
- if (ctx.containerLayoutTriggers.get(id) === triggerLayout) {
5363
- ctx.containerLayoutTriggers.delete(id);
5364
- }
5365
- };
5366
- }, [ctx, id, triggerLayout]);
5367
- const { onLayout } = useOnLayoutSync(
5368
- {
5369
- onLayoutChange,
5370
- ref},
5371
- [itemKey, layoutRenderCount]
5372
- );
5373
- if (!IsNewArchitecture) {
5374
- React2.useEffect(() => {
5375
- if (!isNullOrUndefined(itemKey)) {
5376
- itemLayoutRef.current.didLayout = false;
5377
- const timeout = setTimeout(() => {
5378
- if (!itemLayoutRef.current.didLayout) {
5379
- const { itemKey: currentItemKey, lastSize } = itemLayoutRef.current;
5380
- if (lastSize && !isNullOrUndefined(currentItemKey)) {
5381
- updateItemSizes(ctx, {
5382
- containerId: id,
5383
- fromLayoutEffect: false,
5384
- itemKey: currentItemKey,
5385
- size: lastSize
5386
- });
5387
- itemLayoutRef.current.didLayout = true;
5388
- }
5389
- }
5390
- }, 16);
5391
- return () => {
5392
- clearTimeout(timeout);
5393
- };
5394
- }
5395
- }, [itemKey]);
5396
- }
5397
5638
  const PositionComponent = isSticky ? stickyPositionComponentInternal ? stickyPositionComponentInternal : PositionViewSticky : positionComponentInternal ? positionComponentInternal : PositionView;
5398
5639
  return /* @__PURE__ */ React2__namespace.createElement(
5399
5640
  PositionComponent,
@@ -5401,7 +5642,6 @@ var Container = typedMemo(function Container2({
5401
5642
  animatedScrollY: isSticky ? animatedScrollY : void 0,
5402
5643
  horizontal,
5403
5644
  id,
5404
- index,
5405
5645
  key: recycleItems ? void 0 : itemKey,
5406
5646
  onLayout,
5407
5647
  refView: ref,
@@ -5442,19 +5682,34 @@ function ContainerSlotBase({
5442
5682
  var ContainerSlot = typedMemo(function ContainerSlot2(props) {
5443
5683
  return /* @__PURE__ */ React2__namespace.createElement(ContainerSlotBase, { ...props });
5444
5684
  });
5685
+ function useFreshDataTransitionVisibility(readyToRender, transitionEpoch) {
5686
+ const completedTransitionEpoch = React2.useRef(transitionEpoch);
5687
+ const isTransitionPending = completedTransitionEpoch.current !== transitionEpoch;
5688
+ React2.useLayoutEffect(() => {
5689
+ if (!readyToRender) {
5690
+ completedTransitionEpoch.current = transitionEpoch;
5691
+ }
5692
+ }, [readyToRender, transitionEpoch]);
5693
+ return readyToRender && !isTransitionPending;
5694
+ }
5445
5695
 
5446
5696
  // src/components/Containers.native.tsx
5447
- var ContainersLayer = typedMemo(function ContainersLayer2({ children, horizontal }) {
5697
+ var ContainersLayer = typedMemo(function ContainersLayer2({
5698
+ children,
5699
+ freshDataTransitionEpoch,
5700
+ horizontal
5701
+ }) {
5448
5702
  const ctx = useStateContext();
5449
5703
  const columnWrapperStyle = ctx.columnWrapperStyle;
5450
5704
  const animSize = useValue$("totalSize");
5451
5705
  const [readyToRender, numColumns, otherAxisSize = 0] = useArr$(["readyToRender", "numColumns", "otherAxisSize"]);
5706
+ const isVisible = useFreshDataTransitionVisibility(!!readyToRender, freshDataTransitionEpoch);
5452
5707
  const style = horizontal ? {
5453
5708
  height: otherAxisSize || "100%",
5454
5709
  minHeight: otherAxisSize,
5455
- opacity: readyToRender ? 1 : 0,
5710
+ opacity: isVisible ? 1 : 0,
5456
5711
  width: animSize
5457
- } : { height: animSize, minWidth: otherAxisSize, opacity: readyToRender ? 1 : 0 };
5712
+ } : { height: animSize, minWidth: otherAxisSize, opacity: isVisible ? 1 : 0 };
5458
5713
  if (columnWrapperStyle) {
5459
5714
  const { columnGap, rowGap, gap } = columnWrapperStyle;
5460
5715
  const gapX = columnGap || gap || 0;
@@ -5475,9 +5730,10 @@ var ContainersLayer = typedMemo(function ContainersLayer2({ children, horizontal
5475
5730
  }
5476
5731
  }
5477
5732
  }
5478
- return /* @__PURE__ */ React2__namespace.createElement(ReactNative.Animated.View, { style }, children);
5733
+ return /* @__PURE__ */ React2__namespace.createElement(ReactNative.Animated.View, { pointerEvents: isVisible ? void 0 : "none", style }, /* @__PURE__ */ React2__namespace.createElement(ContainerLayoutCoordinator, null, children));
5479
5734
  });
5480
5735
  var Containers = typedMemo(function Containers2({
5736
+ freshDataTransitionEpoch,
5481
5737
  horizontal,
5482
5738
  recycleItems,
5483
5739
  ItemSeparatorComponent,
@@ -5502,7 +5758,7 @@ var Containers = typedMemo(function Containers2({
5502
5758
  )
5503
5759
  );
5504
5760
  }
5505
- return /* @__PURE__ */ React2__namespace.createElement(ContainersLayer, { horizontal }, containers);
5761
+ return /* @__PURE__ */ React2__namespace.createElement(ContainersLayer, { freshDataTransitionEpoch, horizontal }, containers);
5506
5762
  });
5507
5763
  var ListComponentScrollView = ReactNative.Animated.ScrollView;
5508
5764
 
@@ -5541,16 +5797,6 @@ var SnapWrapper = React2__namespace.forwardRef(function SnapWrapperInner({ Scrol
5541
5797
  const [snapToOffsets] = useArr$(["snapToOffsets"]);
5542
5798
  return /* @__PURE__ */ React2__namespace.createElement(ScrollComponent, { ...props, ref, snapToOffsets });
5543
5799
  });
5544
- function WebAnchoredEndSpace({ horizontal }) {
5545
- const ctx = useStateContext();
5546
- const [anchoredEndSpaceSize] = useArr$(["anchoredEndSpaceSize"]);
5547
- const shouldRenderAnchoredEndSpace = !!ctx.state.props.anchoredEndSpace && (anchoredEndSpaceSize || 0) > 0;
5548
- if (!shouldRenderAnchoredEndSpace) {
5549
- return null;
5550
- }
5551
- const style = horizontal ? { height: "100%", width: anchoredEndSpaceSize || 0 } : { height: anchoredEndSpaceSize || 0 };
5552
- return /* @__PURE__ */ React2__namespace.createElement("div", { style }, null);
5553
- }
5554
5800
 
5555
5801
  // src/core/updateContentMetrics.ts
5556
5802
  var SCROLL_ADJUST_EPSILON = 0.1;
@@ -5584,7 +5830,11 @@ function setHeaderSize(ctx, size) {
5584
5830
  state.didMeasureHeader = true;
5585
5831
  }
5586
5832
  function setFooterSize(ctx, size) {
5587
- return setContentLengthSignal(ctx, "footerSize", size);
5833
+ const didChange = setContentLengthSignal(ctx, "footerSize", size);
5834
+ if (didChange) {
5835
+ maybeUpdateAnchoredEndSpace(ctx);
5836
+ }
5837
+ return didChange;
5588
5838
  }
5589
5839
  function areInsetsEqual(left, right) {
5590
5840
  var _a3, _b, _c, _d, _e, _f, _g, _h;
@@ -5627,6 +5877,7 @@ var LayoutView = ({ onLayoutChange, refView, ...rest }) => {
5627
5877
  const { onLayout } = useOnLayoutSync({ onLayoutChange, ref });
5628
5878
  return /* @__PURE__ */ React2__namespace.createElement(ReactNative.View, { ...rest, onLayout, ref });
5629
5879
  };
5880
+ var StyleSheet = ReactNative.StyleSheet;
5630
5881
 
5631
5882
  // src/components/ListComponent.tsx
5632
5883
  var AlignItemsAtEndSpacer = typedMemo(function AlignItemsAtEndSpacer2({ horizontal }) {
@@ -5644,6 +5895,7 @@ var AlignItemsAtEndSpacer = typedMemo(function AlignItemsAtEndSpacer2({ horizont
5644
5895
  });
5645
5896
  var ListComponent = typedMemo(function ListComponent2({
5646
5897
  canRender,
5898
+ freshDataTransitionEpoch,
5647
5899
  style,
5648
5900
  contentContainerStyle,
5649
5901
  horizontal,
@@ -5662,6 +5914,8 @@ var ListComponent = typedMemo(function ListComponent2({
5662
5914
  refScrollView,
5663
5915
  renderScrollComponent,
5664
5916
  onLayoutFooter,
5917
+ onInternalScrollBeginDrag,
5918
+ onInternalScrollEnd,
5665
5919
  scrollAdjustHandler,
5666
5920
  snapToIndices,
5667
5921
  stickyHeaderConfig,
@@ -5671,8 +5925,17 @@ var ListComponent = typedMemo(function ListComponent2({
5671
5925
  }) {
5672
5926
  const ctx = useStateContext();
5673
5927
  const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
5674
- const [otherAxisSize = 0] = useArr$(["otherAxisSize"]);
5928
+ const [anchoredEndSpaceSize = 0, otherAxisSize = 0] = useArr$(["anchoredEndSpaceSize", "otherAxisSize"]);
5675
5929
  const shouldRenderAlignItemsAtEndSpacer = ctx.state.props.alignItemsAtEndPaddingEnabled;
5930
+ const shouldMaterializeAnchoredEndSpace = !!ctx.state.props.anchoredEndSpace && ctx.state.props.anchoredEndSpaceOwner === "list" && anchoredEndSpaceSize > 0;
5931
+ let anchoredEndSpaceStyle;
5932
+ if (shouldMaterializeAnchoredEndSpace) {
5933
+ const paddingEnd = horizontal ? isHorizontalRTL(ctx.state) ? "Left" : "Right" : "Bottom";
5934
+ const flattenedContentContainerStyle = StyleSheet.flatten(contentContainerStyle);
5935
+ anchoredEndSpaceStyle = {
5936
+ [`padding${paddingEnd}`]: extractPadding({}, flattenedContentContainerStyle || {}, paddingEnd) + anchoredEndSpaceSize
5937
+ };
5938
+ }
5676
5939
  const autoOtherAxisStyle = getAutoOtherAxisStyle({
5677
5940
  horizontal,
5678
5941
  needsOtherAxisSize: ctx.state.needsOtherAxisSize,
@@ -5723,12 +5986,12 @@ var ListComponent = typedMemo(function ListComponent2({
5723
5986
  SnapOrScroll,
5724
5987
  {
5725
5988
  ...rest,
5726
- ...ScrollComponent === ListComponentScrollView ? { useWindowScroll } : {},
5989
+ ...Platform.OS === "web" ? ScrollComponent === ListComponentScrollView ? { onInternalScrollEnd, useWindowScroll } : {} : { onScrollBeginDrag: onInternalScrollBeginDrag },
5727
5990
  contentContainerStyle: [
5728
- horizontal ? {
5729
- height: "100%"
5730
- } : {},
5731
- contentContainerStyle
5991
+ horizontal ? { height: "100%" } : {},
5992
+ contentContainerStyle,
5993
+ anchoredEndSpaceStyle,
5994
+ Platform.OS === "web" ? { boxSizing: "border-box" } : void 0
5732
5995
  ],
5733
5996
  contentOffset: initialContentOffset !== void 0 ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
5734
5997
  horizontal,
@@ -5746,6 +6009,7 @@ var ListComponent = typedMemo(function ListComponent2({
5746
6009
  canRender && !ListEmptyComponent && /* @__PURE__ */ React2__namespace.createElement(
5747
6010
  Containers,
5748
6011
  {
6012
+ freshDataTransitionEpoch,
5749
6013
  getRenderedItem: getRenderedItem2,
5750
6014
  horizontal,
5751
6015
  ItemSeparatorComponent,
@@ -5754,7 +6018,6 @@ var ListComponent = typedMemo(function ListComponent2({
5754
6018
  }
5755
6019
  ),
5756
6020
  ListFooterComponent && /* @__PURE__ */ React2__namespace.createElement(LayoutView, { onLayoutChange: onLayoutFooterInternal, style: ListFooterComponentStyle }, getComponent(ListFooterComponent)),
5757
- Platform.OS === "web" && /* @__PURE__ */ React2__namespace.createElement(WebAnchoredEndSpace, { horizontal }),
5758
6021
  IS_DEV && ENABLE_DEVMODE
5759
6022
  );
5760
6023
  });
@@ -5763,7 +6026,16 @@ var WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO = 0.9;
5763
6026
  var WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO = 0.9;
5764
6027
  function useDevChecksImpl(props) {
5765
6028
  const ctx = useStateContext();
5766
- const { childrenMode, keyExtractor, renderScrollComponent, useWindowScroll } = props;
6029
+ const { anchoredEndSpace, childrenMode, keyExtractor, numColumns, renderScrollComponent, useWindowScroll } = props;
6030
+ const hasAnchoredEndSpace = !!anchoredEndSpace;
6031
+ React2.useEffect(() => {
6032
+ if (hasAnchoredEndSpace && (numColumns != null ? numColumns : 1) > 1) {
6033
+ warnDevOnce(
6034
+ "anchoredEndSpaceNumColumns",
6035
+ "anchoredEndSpace is only supported when numColumns is 1. Using it with multiple columns may produce incorrect anchored spacing."
6036
+ );
6037
+ }
6038
+ }, [hasAnchoredEndSpace, numColumns]);
5767
6039
  React2.useEffect(() => {
5768
6040
  if (useWindowScroll && renderScrollComponent) {
5769
6041
  warnDevOnce(
@@ -5829,6 +6101,7 @@ function checkResetContainers(ctx, dataProp, { didColumnsChange = false } = {})
5829
6101
  if (didColumnsChange) {
5830
6102
  state.sizes.clear();
5831
6103
  state.sizesKnown.clear();
6104
+ invalidateContainerFixedItemSizes(state);
5832
6105
  for (const key in state.averageSizes) {
5833
6106
  delete state.averageSizes[key];
5834
6107
  }
@@ -6075,7 +6348,7 @@ function onScroll(ctx, event) {
6075
6348
  }
6076
6349
  }
6077
6350
  state.scrollPending = newScroll;
6078
- updateScroll(ctx, newScroll, insetChanged);
6351
+ updateScroll(ctx, newScroll, insetChanged, { fromNativeScrollEvent: true });
6079
6352
  trackInitialScrollNativeProgress(state, newScroll);
6080
6353
  clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx);
6081
6354
  if (state.scrollingTo) {
@@ -6090,6 +6363,59 @@ function onScroll(ctx, event) {
6090
6363
  }
6091
6364
  }
6092
6365
 
6366
+ // src/core/ScheduledWork.ts
6367
+ var ScheduledWork = class {
6368
+ constructor() {
6369
+ this.work = /* @__PURE__ */ new Map();
6370
+ }
6371
+ timeout(callback, delay, key) {
6372
+ if (key) {
6373
+ this.cancel(key);
6374
+ }
6375
+ const work = [void 0, clearTimeout];
6376
+ const handle = setTimeout(() => {
6377
+ const workKey = key != null ? key : handle;
6378
+ if (this.work.get(workKey) === work) {
6379
+ this.work.delete(workKey);
6380
+ callback();
6381
+ }
6382
+ }, delay);
6383
+ work[0] = handle;
6384
+ this.work.set(key != null ? key : handle, work);
6385
+ }
6386
+ frame(callback, key) {
6387
+ this.cancel(key);
6388
+ const work = [void 0, cancelAnimationFrame];
6389
+ this.work.set(key, work);
6390
+ work[0] = requestAnimationFrame(() => {
6391
+ if (this.work.get(key) === work) {
6392
+ this.work.delete(key);
6393
+ callback();
6394
+ }
6395
+ });
6396
+ }
6397
+ register(key, cancel) {
6398
+ this.cancel(key);
6399
+ this.work.set(key, [void 0, cancel]);
6400
+ }
6401
+ cancel(key) {
6402
+ const work = this.work.get(key);
6403
+ if (work) {
6404
+ this.work.delete(key);
6405
+ work[1](work[0]);
6406
+ }
6407
+ }
6408
+ has(key) {
6409
+ return this.work.has(key);
6410
+ }
6411
+ dispose() {
6412
+ for (const [handle, cancel] of this.work.values()) {
6413
+ cancel(handle);
6414
+ }
6415
+ this.work.clear();
6416
+ }
6417
+ };
6418
+
6093
6419
  // src/core/ScrollAdjustHandler.ts
6094
6420
  var ScrollAdjustHandler = class {
6095
6421
  constructor(ctx) {
@@ -6177,7 +6503,6 @@ var useCombinedRef = (...refs) => {
6177
6503
  }, refs);
6178
6504
  return callback;
6179
6505
  };
6180
- var StyleSheet = ReactNative.StyleSheet;
6181
6506
  function useStickyScrollHandler(stickyHeaderIndices, horizontal, ctx, onScroll2) {
6182
6507
  const shouldUseRnAnimatedEngine = !ctx.state.props.stickyPositionComponentInternal;
6183
6508
  return React2.useMemo(() => {
@@ -6251,8 +6576,12 @@ function getAverageItemSizes(state) {
6251
6576
  return averageItemSizes;
6252
6577
  }
6253
6578
  function triggerMountedContainerLayouts(ctx) {
6254
- for (const triggerLayout of ctx.containerLayoutTriggers.values()) {
6255
- triggerLayout();
6579
+ if (IsNewArchitecture) {
6580
+ scheduleContainerLayout(ctx);
6581
+ } else {
6582
+ for (const triggerLayout of ctx.containerLayoutTriggers.values()) {
6583
+ triggerLayout();
6584
+ }
6256
6585
  }
6257
6586
  }
6258
6587
  function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
@@ -6260,7 +6589,7 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
6260
6589
  const IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS = 800;
6261
6590
  const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
6262
6591
  let imperativeScrollToken = 0;
6263
- const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0;
6592
+ const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.scheduledWork.has("mvcpRecalculate") || state.ignoreScrollFromMVCP !== void 0;
6264
6593
  const isScrollToIndexReady = (targetIndex, allowEmpty = false) => {
6265
6594
  var _a3;
6266
6595
  const props = state.props;
@@ -6294,9 +6623,9 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
6294
6623
  run();
6295
6624
  return;
6296
6625
  }
6297
- requestAnimationFrame(check);
6626
+ state.scheduledWork.frame(check, "imperativeScrollReady");
6298
6627
  };
6299
- requestAnimationFrame(check);
6628
+ state.scheduledWork.frame(check, "imperativeScrollReady");
6300
6629
  };
6301
6630
  const runScrollRequest = (token, resolve, run, isReady = () => true) => {
6302
6631
  const runNow = () => {
@@ -6318,10 +6647,9 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
6318
6647
  }
6319
6648
  };
6320
6649
  const startImperativeScroll = (resolve) => {
6321
- var _a3;
6650
+ state.scheduledWork.cancel("imperativeScrollReady");
6322
6651
  const token = ++imperativeScrollToken;
6323
- state.pendingScrollToEnd = void 0;
6324
- (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
6652
+ settlePendingImperativeScroll(state);
6325
6653
  state.pendingScrollResolve = resolve;
6326
6654
  return token;
6327
6655
  };
@@ -6365,6 +6693,7 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
6365
6693
  const mode = (_a3 = options == null ? void 0 : options.mode) != null ? _a3 : "sizes";
6366
6694
  state.sizes.clear();
6367
6695
  state.sizesKnown.clear();
6696
+ invalidateContainerFixedItemSizes(state);
6368
6697
  for (const key in state.averageSizes) {
6369
6698
  delete state.averageSizes[key];
6370
6699
  }
@@ -6386,6 +6715,10 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
6386
6715
  return {
6387
6716
  clearCaches,
6388
6717
  flashScrollIndicators: () => refScroller.current.flashScrollIndicators(),
6718
+ getAnimatableRef: () => {
6719
+ var _a3, _b, _c;
6720
+ return (_c = (_b = (_a3 = refScroller.current).getNativeScrollRef) == null ? void 0 : _b.call(_a3)) != null ? _c : refScroller.current;
6721
+ },
6389
6722
  getNativeScrollRef: () => refScroller.current,
6390
6723
  getScrollableNode: () => refScroller.current.getScrollableNode(),
6391
6724
  getScrollResponder: () => refScroller.current.getScrollResponder(),
@@ -6400,6 +6733,7 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
6400
6733
  end: state.endNoBuffer,
6401
6734
  endBuffered: state.endBuffered,
6402
6735
  getAverageItemSizes: () => getAverageItemSizes(state),
6736
+ indexByKey: (key) => state.indexByKey.get(key),
6403
6737
  isAtEnd: peek$(ctx, "isAtEnd"),
6404
6738
  isAtStart: peek$(ctx, "isAtStart"),
6405
6739
  isEndReached: state.isEndReached,
@@ -6703,7 +7037,7 @@ var LegendList = typedMemo(
6703
7037
  })
6704
7038
  );
6705
7039
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
6706
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
7040
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s;
6707
7041
  const noopOnScroll = React2.useCallback((_event) => {
6708
7042
  }, []);
6709
7043
  if (props.recycleItems === void 0) {
@@ -6756,6 +7090,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6756
7090
  onMomentumScrollEnd,
6757
7091
  onRefresh,
6758
7092
  onScroll: onScrollProp,
7093
+ onScrollBeginDrag,
6759
7094
  onStartReached,
6760
7095
  onStartReachedThreshold = 0.5,
6761
7096
  onStickyHeaderChange,
@@ -6778,9 +7113,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6778
7113
  ...rest
6779
7114
  } = props;
6780
7115
  const animatedPropsInternal = props.animatedPropsInternal;
7116
+ const anchoredEndSpaceOwner = (_a3 = props.anchoredEndSpaceOwnerInternal) != null ? _a3 : "list";
6781
7117
  const positionComponentInternal = props.positionComponentInternal;
6782
7118
  const stickyPositionComponentInternal = props.stickyPositionComponentInternal;
6783
7119
  const {
7120
+ anchoredEndSpaceOwnerInternal: _anchoredEndSpaceOwnerInternal,
6784
7121
  positionComponentInternal: _positionComponentInternal,
6785
7122
  stickyPositionComponentInternal: _stickyPositionComponentInternal,
6786
7123
  ...restProps
@@ -6802,6 +7139,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6802
7139
  const stylePaddingBottomState = extractPadding(style, contentContainerStyle, "Bottom");
6803
7140
  const stylePaddingLeftState = extractPadding(style, contentContainerStyle, "Left");
6804
7141
  const stylePaddingRightState = extractPadding(style, contentContainerStyle, "Right");
7142
+ const stylePaddingEndState = getStylePaddingEnd({
7143
+ horizontal,
7144
+ rtl,
7145
+ stylePaddingBottom: stylePaddingBottomState,
7146
+ stylePaddingLeft: stylePaddingLeftState,
7147
+ stylePaddingRight: stylePaddingRightState
7148
+ });
6805
7149
  const maintainScrollAtEndConfig = normalizeMaintainScrollAtEnd(maintainScrollAtEnd);
6806
7150
  const maintainVisibleContentPositionConfig = normalizeMaintainVisibleContentPosition(
6807
7151
  maintainVisibleContentPositionProp
@@ -6814,13 +7158,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6814
7158
  const initialScrollProp = initialScrollAtEnd ? {
6815
7159
  index: Math.max(0, dataProp.length - 1),
6816
7160
  preserveForBottomPadding: true,
6817
- viewOffset: -stylePaddingBottomState,
7161
+ viewOffset: -stylePaddingEndState,
6818
7162
  viewPosition: 1
6819
7163
  } : hasInitialScrollIndex ? typeof initialScrollIndexProp === "object" ? {
6820
- index: (_a3 = initialScrollIndexProp.index) != null ? _a3 : 0,
7164
+ index: (_b = initialScrollIndexProp.index) != null ? _b : 0,
6821
7165
  preserveForBottomPadding: initialScrollIndexProp.viewOffset === void 0 && initialScrollIndexProp.viewPosition === 1 ? true : void 0,
6822
- viewOffset: (_b = initialScrollIndexProp.viewOffset) != null ? _b : initialScrollIndexProp.viewPosition === 1 ? -stylePaddingBottomState : 0,
6823
- viewPosition: (_c = initialScrollIndexProp.viewPosition) != null ? _c : 0
7166
+ viewOffset: (_c = initialScrollIndexProp.viewOffset) != null ? _c : initialScrollIndexProp.viewPosition === 1 ? -stylePaddingEndState : 0,
7167
+ viewPosition: (_d = initialScrollIndexProp.viewPosition) != null ? _d : 0
6824
7168
  } : {
6825
7169
  index: initialScrollIndexProp != null ? initialScrollIndexProp : 0,
6826
7170
  viewOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : 0
@@ -6833,7 +7177,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6833
7177
  const [, scheduleImperativeScrollCommit] = React2__namespace.useReducer((value) => value + 1, 0);
6834
7178
  const ctx = useStateContext();
6835
7179
  ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
6836
- 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;
7180
+ 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;
6837
7181
  const nextScrollAxisGap = typeof scrollAxisGap === "number" && Number.isFinite(scrollAxisGap) ? scrollAxisGap : 0;
6838
7182
  const refScroller = React2.useRef(null);
6839
7183
  const combinedRef = useCombinedRef(refScroller, refScrollView);
@@ -6848,8 +7192,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6848
7192
  anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex,
6849
7193
  alwaysRender == null ? void 0 : alwaysRender.top,
6850
7194
  alwaysRender == null ? void 0 : alwaysRender.bottom,
6851
- (_j = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _j.join(","),
6852
- (_k = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _k.join(","),
7195
+ (_k = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _k.join(","),
7196
+ (_l = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _l.join(","),
6853
7197
  dataProp,
6854
7198
  dataKey,
6855
7199
  dataVersion,
@@ -6862,13 +7206,14 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6862
7206
  if (!refState.current) {
6863
7207
  if (!ctx.state) {
6864
7208
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : IsNewArchitecture ? { height: 0, width: 0 } : getWindowSize())[horizontal ? "width" : "height"];
6865
- ctx.values.set("adaptiveRender", (_l = experimental_adaptiveRender == null ? void 0 : experimental_adaptiveRender.initialMode) != null ? _l : "normal");
7209
+ ctx.values.set("adaptiveRender", (_m = experimental_adaptiveRender == null ? void 0 : experimental_adaptiveRender.initialMode) != null ? _m : "normal");
6866
7210
  ctx.state = {
6867
7211
  averageSizes: {},
6868
7212
  columnSpans: [],
6869
7213
  columns: [],
7214
+ containerItemGenerations: [],
6870
7215
  containerItemKeys: /* @__PURE__ */ new Map(),
6871
- containerItemTypes: /* @__PURE__ */ new Map(),
7216
+ containerItemMetadata: /* @__PURE__ */ new Map(),
6872
7217
  contentInsetOverride: void 0,
6873
7218
  dataChangeEpoch: 0,
6874
7219
  dataChangeNeedsScrollUpdate: false,
@@ -6880,6 +7225,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6880
7225
  endNoBuffer: -1,
6881
7226
  endReachedSnapshot: void 0,
6882
7227
  firstFullyOnScreenIndex: -1,
7228
+ freshDataTransitionEpoch: 0,
6883
7229
  hasHadNonEmptyData: dataProp.length > 0,
6884
7230
  idCache: [],
6885
7231
  idsInView: [],
@@ -6904,8 +7250,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6904
7250
  positions: [],
6905
7251
  props: {},
6906
7252
  queuedCalculateItemsInView: 0,
6907
- queuedFullDrawDistancePrewarm: void 0,
6908
7253
  refScroller: { current: null },
7254
+ scheduledWork: new ScheduledWork(),
6909
7255
  scroll: 0,
6910
7256
  scrollAdjustHandler: new ScrollAdjustHandler(ctx),
6911
7257
  scrollForNextCalculateItemsInView: void 0,
@@ -6921,11 +7267,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6921
7267
  startBuffered: -1,
6922
7268
  startNoBuffer: -1,
6923
7269
  startReachedSnapshot: void 0,
6924
- startReachedSnapshotDataChangeEpoch: void 0,
6925
7270
  stickyContainerPool: /* @__PURE__ */ new Set(),
6926
7271
  stickyContainers: /* @__PURE__ */ new Map(),
6927
- timeoutAdaptiveRender: void 0,
6928
- timeouts: /* @__PURE__ */ new Set(),
6929
7272
  totalSize: 0,
6930
7273
  viewabilityConfigCallbackPairs: void 0
6931
7274
  };
@@ -6947,13 +7290,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6947
7290
  const didScrollAxisGapChange = !isFirstLocal && ctx.scrollAxisGap !== nextScrollAxisGap;
6948
7291
  ctx.scrollAxisGap = nextScrollAxisGap;
6949
7292
  state.didColumnsChange = numColumnsProp !== previousNumColumnsProp || didScrollAxisGapChange;
6950
- const previousDataLength = (_n = (_m = state.props.data) == null ? void 0 : _m.length) != null ? _n : 0;
7293
+ const previousDataLength = (_o = (_n = state.props.data) == null ? void 0 : _n.length) != null ? _o : 0;
6951
7294
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
6952
7295
  const didDataKeyChangeLocal = state.props.dataKey !== dataKey;
6953
7296
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
6954
7297
  const didDataChangeLocal = didDataKeyChangeLocal || didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
6955
7298
  const shouldResetFreshDataLayout = !isFirstLocal && didDataChangeLocal && state.hasHadNonEmptyData && (didDataKeyChangeLocal || previousDataLength === 0) && dataProp.length > 0;
6956
- if (didDataChangeLocal && !initialScrollAtEnd && state.didFinishInitialScroll && ((_o = state.initialScroll) == null ? void 0 : _o.viewPosition) === 1 && state.props.data.length > 0) {
7299
+ if (didDataChangeLocal && !initialScrollAtEnd && state.didFinishInitialScroll && ((_p = state.initialScroll) == null ? void 0 : _p.viewPosition) === 1 && state.props.data.length > 0) {
6957
7300
  clearPreservedInitialScrollTarget(state);
6958
7301
  }
6959
7302
  if (didDataChangeLocal) {
@@ -6962,10 +7305,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6962
7305
  state.didDataChange = true;
6963
7306
  state.previousData = state.props.data;
6964
7307
  }
7308
+ if (shouldResetFreshDataLayout) {
7309
+ state.freshDataTransitionEpoch += 1;
7310
+ }
6965
7311
  const throttledOnScroll = useThrottledOnScroll(onScrollProp != null ? onScrollProp : noopOnScroll, scrollEventThrottle != null ? scrollEventThrottle : 0);
6966
7312
  const throttleScrollFn = scrollEventThrottle && onScrollProp ? throttledOnScroll : onScrollProp;
6967
- const anchoredEndSpaceResolved = Platform.OS === "web" && anchoredEndSpace ? { ...anchoredEndSpace, includeInEndInset: true } : anchoredEndSpace;
6968
- const didAnchoredEndSpaceAnchorIndexChange = !isFirstLocal && !didDataChangeLocal && ((_p = state.props.anchoredEndSpace) == null ? void 0 : _p.anchorIndex) !== (anchoredEndSpaceResolved == null ? void 0 : anchoredEndSpaceResolved.anchorIndex);
7313
+ const didAnchoredEndSpaceAnchorIndexChange = !isFirstLocal && !didDataChangeLocal && ((_q = state.props.anchoredEndSpace) == null ? void 0 : _q.anchorIndex) !== (anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex);
6969
7314
  state.props = {
6970
7315
  adaptiveRender: experimental_adaptiveRender,
6971
7316
  alignItemsAtEnd,
@@ -6973,7 +7318,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6973
7318
  alwaysRender,
6974
7319
  alwaysRenderIndicesArr: alwaysRenderIndices.arr,
6975
7320
  alwaysRenderIndicesSet: alwaysRenderIndices.set,
6976
- anchoredEndSpace: anchoredEndSpaceResolved,
7321
+ anchoredEndSpace,
7322
+ anchoredEndSpaceOwner,
6977
7323
  animatedProps: animatedPropsInternal,
6978
7324
  contentContainerAlignItems: contentContainerStyle.alignItems,
6979
7325
  contentInset,
@@ -6997,7 +7343,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
6997
7343
  onFirstVisibleItemChanged,
6998
7344
  onItemSizeChanged,
6999
7345
  onLoad,
7346
+ onMomentumScrollEnd,
7000
7347
  onScroll: throttleScrollFn,
7348
+ onScrollBeginDrag,
7001
7349
  onStartReached,
7002
7350
  onStartReachedThreshold,
7003
7351
  onStickyHeaderChange,
@@ -7020,12 +7368,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7020
7368
  if (!isFirstLocal && previousAdaptiveRender && !experimental_adaptiveRender) {
7021
7369
  resetAdaptiveRender(ctx);
7022
7370
  }
7023
- if (shouldResetFreshDataLayout) {
7024
- resetInitialRenderState(ctx, {
7025
- resetInitialScroll: !!initialScrollProp,
7026
- resetLayout: true
7027
- });
7028
- }
7029
7371
  const memoizedLastItemKeys = React2.useMemo(() => {
7030
7372
  if (!dataProp.length) return [];
7031
7373
  return Array.from(
@@ -7089,6 +7431,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7089
7431
  useDevChecks(props);
7090
7432
  }
7091
7433
  React2.useLayoutEffect(() => {
7434
+ if (shouldResetFreshDataLayout) {
7435
+ resetInitialRenderState(ctx, {
7436
+ resetInitialScroll: !!initialScrollProp,
7437
+ resetLayout: true
7438
+ });
7439
+ }
7092
7440
  handleInitialScrollDataChange(ctx, {
7093
7441
  dataLength: dataProp.length,
7094
7442
  didDataChange: didDataChangeLocal,
@@ -7096,7 +7444,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7096
7444
  initialScrollAtEnd,
7097
7445
  latestInitialScroll: initialScrollProp,
7098
7446
  latestInitialScrollSessionKind: initialScrollUsesOffsetOnly ? "offset" : "bootstrap",
7099
- stylePaddingBottom: stylePaddingBottomState,
7447
+ stylePaddingEnd: stylePaddingEndState,
7100
7448
  useBootstrapInitialScroll: usesBootstrapInitialScroll
7101
7449
  });
7102
7450
  }, [
@@ -7105,7 +7453,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7105
7453
  didDataChangeLocal,
7106
7454
  shouldResetFreshDataLayout,
7107
7455
  initialScrollAtEnd,
7108
- stylePaddingBottomState,
7456
+ stylePaddingEndState,
7109
7457
  usesBootstrapInitialScroll
7110
7458
  ]);
7111
7459
  React2.useLayoutEffect(() => {
@@ -7123,7 +7471,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7123
7471
  anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorMaxSize,
7124
7472
  anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorOffset,
7125
7473
  didAnchoredEndSpaceAnchorIndexChange,
7126
- numColumnsProp
7474
+ horizontal,
7475
+ numColumnsProp,
7476
+ rtl,
7477
+ stylePaddingBottomState,
7478
+ stylePaddingLeftState,
7479
+ stylePaddingRightState
7127
7480
  ]);
7128
7481
  React2.useLayoutEffect(() => {
7129
7482
  const previousContentInsetEndAdjustment = previousContentInsetEndAdjustmentRef.current;
@@ -7139,10 +7492,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7139
7492
  dataLength: dataProp.length,
7140
7493
  footerSize: layout[horizontal ? "width" : "height"],
7141
7494
  initialScrollAtEnd,
7142
- stylePaddingBottom: stylePaddingBottomState
7495
+ stylePaddingEnd: stylePaddingEndState
7143
7496
  });
7144
7497
  },
7145
- [dataProp.length, initialScrollAtEnd, horizontal, stylePaddingBottomState, usesBootstrapInitialScroll]
7498
+ [dataProp.length, initialScrollAtEnd, horizontal, stylePaddingEndState, usesBootstrapInitialScroll]
7146
7499
  );
7147
7500
  const onLayoutChange = React2.useCallback(
7148
7501
  (layout, fromLayoutEffect) => {
@@ -7159,7 +7512,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7159
7512
  }
7160
7513
  advanceCurrentInitialScrollSession(ctx);
7161
7514
  },
7162
- [dataProp.length, initialScrollAtEnd, stylePaddingBottomState, usesBootstrapInitialScroll]
7515
+ [dataProp.length, initialScrollAtEnd, stylePaddingEndState, usesBootstrapInitialScroll]
7163
7516
  );
7164
7517
  const { onLayout } = useOnLayoutSync({
7165
7518
  onLayoutChange,
@@ -7255,14 +7608,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7255
7608
  React2.useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx, scheduleImperativeScrollCommit), []);
7256
7609
  React2.useEffect(() => {
7257
7610
  return () => {
7258
- if (state.queuedFullDrawDistancePrewarm !== void 0) {
7259
- cancelAnimationFrame(state.queuedFullDrawDistancePrewarm);
7260
- state.queuedFullDrawDistancePrewarm = void 0;
7261
- }
7262
- for (const timeout of state.timeouts) {
7263
- clearTimeout(timeout);
7264
- }
7265
- state.timeouts.clear();
7611
+ cancelImperativeScroll(state);
7612
+ state.scheduledWork.dispose();
7266
7613
  };
7267
7614
  }, [state]);
7268
7615
  React2.useLayoutEffect(() => {
@@ -7280,11 +7627,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7280
7627
  getRenderedItem: (key) => getRenderedItem(ctx, key),
7281
7628
  onMomentumScrollEnd: (event) => {
7282
7629
  checkFinishedScrollFallback(ctx);
7283
- if (onMomentumScrollEnd) {
7284
- onMomentumScrollEnd(event);
7630
+ if (state.props.onMomentumScrollEnd) {
7631
+ state.props.onMomentumScrollEnd(event);
7285
7632
  }
7286
7633
  },
7287
- onScroll: (event) => onScroll(ctx, event)
7634
+ onScroll: (event) => onScroll(ctx, event),
7635
+ onScrollBeginDrag: (event) => {
7636
+ var _a4, _b2;
7637
+ prepareReachedEdgeForNextUserScroll(ctx);
7638
+ (_b2 = (_a4 = state.props).onScrollBeginDrag) == null ? void 0 : _b2.call(_a4, event);
7639
+ },
7640
+ onScrollEnd: () => prepareReachedEdgeForNextUserScroll(ctx)
7288
7641
  }),
7289
7642
  []
7290
7643
  );
@@ -7298,6 +7651,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7298
7651
  canRender,
7299
7652
  contentContainerStyle,
7300
7653
  contentInset,
7654
+ freshDataTransitionEpoch: state.freshDataTransitionEpoch,
7301
7655
  getRenderedItem: fns.getRenderedItem,
7302
7656
  horizontal,
7303
7657
  initialContentOffset,
@@ -7305,13 +7659,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7305
7659
  ListFooterComponent,
7306
7660
  ListFooterComponentStyle,
7307
7661
  ListHeaderComponent,
7662
+ onInternalScrollBeginDrag: fns.onScrollBeginDrag,
7663
+ onInternalScrollEnd: fns.onScrollEnd,
7308
7664
  onLayout,
7309
7665
  onLayoutFooter,
7310
7666
  onMomentumScrollEnd: fns.onMomentumScrollEnd,
7311
7667
  onScroll: onScrollHandler,
7312
7668
  recycleItems,
7313
7669
  refreshControl: refreshControlElement ? stylePaddingTopState > 0 ? React2__namespace.cloneElement(refreshControlElement, {
7314
- progressViewOffset: ((_q = refreshControlElement.props.progressViewOffset) != null ? _q : 0) + stylePaddingTopState
7670
+ progressViewOffset: ((_r = refreshControlElement.props.progressViewOffset) != null ? _r : 0) + stylePaddingTopState
7315
7671
  }) : refreshControlElement : onRefresh && /* @__PURE__ */ React2__namespace.createElement(
7316
7672
  ReactNative.RefreshControl,
7317
7673
  {
@@ -7322,7 +7678,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7322
7678
  ),
7323
7679
  refScrollView: combinedRef,
7324
7680
  renderScrollComponent,
7325
- scrollAdjustHandler: (_r = refState.current) == null ? void 0 : _r.scrollAdjustHandler,
7681
+ scrollAdjustHandler: (_s = refState.current) == null ? void 0 : _s.scrollAdjustHandler,
7326
7682
  scrollEventThrottle: 0,
7327
7683
  snapToIndices,
7328
7684
  stickyHeaderIndices,