@legendapp/list 3.3.2 → 3.3.3

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.js CHANGED
@@ -162,11 +162,13 @@ function StateProvider({ children }) {
162
162
  mapViewabilityCallbacks: /* @__PURE__ */ new Map(),
163
163
  mapViewabilityConfigStates: /* @__PURE__ */ new Map(),
164
164
  mapViewabilityValues: /* @__PURE__ */ new Map(),
165
+ pendingContainerIds: void 0,
165
166
  positionListeners: /* @__PURE__ */ new Map(),
166
167
  scrollAxisGap: 0,
167
168
  state: void 0,
168
169
  values: /* @__PURE__ */ new Map([
169
170
  ["alignItemsAtEndPadding", 0],
171
+ ["containerLayoutEpoch", 0],
170
172
  ["stylePaddingTop", 0],
171
173
  ["headerSize", 0],
172
174
  ["numContainers", 0],
@@ -391,6 +393,18 @@ function useInterval(callback, delay) {
391
393
  }, [delay]);
392
394
  }
393
395
 
396
+ // src/constants-platform.ts
397
+ var IsNewArchitecture = true;
398
+
399
+ // src/core/containerLayoutBaseline.ts
400
+ var containerLayoutBaselines = /* @__PURE__ */ new WeakMap();
401
+ function getContainerLayoutBaseline(element) {
402
+ return containerLayoutBaselines.get(element);
403
+ }
404
+ function setContainerLayoutBaseline(element, size) {
405
+ containerLayoutBaselines.set(element, size);
406
+ }
407
+
394
408
  // src/utils/devEnvironment.ts
395
409
  var metroDev = typeof __DEV__ !== "undefined" ? __DEV__ : void 0;
396
410
  var _a;
@@ -404,528 +418,247 @@ var POSITION_OUT_OF_VIEW = -1e7;
404
418
  var EDGE_POSITION_EPSILON = 1;
405
419
  var ENABLE_DEVMODE = IS_DEV && false;
406
420
  var ENABLE_DEBUG_VIEW = IS_DEV && false;
407
- var typedForwardRef = React3__namespace.forwardRef;
408
- var typedMemo = React3__namespace.memo;
409
- var getComponent = (Component) => {
410
- if (React3__namespace.isValidElement(Component)) {
411
- return Component;
412
- }
413
- if (Component) {
414
- return /* @__PURE__ */ React3__namespace.createElement(Component, null);
415
- }
416
- return null;
417
- };
418
421
 
419
- // src/utils/helpers.ts
420
- function isFunction(obj) {
421
- return typeof obj === "function";
422
- }
423
- function isArray(obj) {
424
- return Array.isArray(obj);
422
+ // src/core/deferredPublicOnScroll.ts
423
+ function withResolvedContentOffset(state, event, resolvedOffset) {
424
+ return {
425
+ ...event,
426
+ nativeEvent: {
427
+ ...event.nativeEvent,
428
+ contentOffset: state.props.horizontal ? { x: resolvedOffset, y: 0 } : { x: 0, y: resolvedOffset }
429
+ }
430
+ };
425
431
  }
426
- var warned = /* @__PURE__ */ new Set();
427
- function warnDevOnce(id, text) {
428
- if (IS_DEV && !warned.has(id)) {
429
- warned.add(id);
430
- console.warn(`[legend-list] ${text}`);
432
+ function releaseDeferredPublicOnScroll(ctx, resolvedOffset) {
433
+ var _a3, _b, _c, _d;
434
+ const state = ctx.state;
435
+ const deferredEvent = state.deferredPublicOnScrollEvent;
436
+ state.deferredPublicOnScrollEvent = void 0;
437
+ if (deferredEvent) {
438
+ (_d = (_c = state.props).onScroll) == null ? void 0 : _d.call(
439
+ _c,
440
+ withResolvedContentOffset(
441
+ state,
442
+ deferredEvent,
443
+ (_b = (_a3 = resolvedOffset != null ? resolvedOffset : state.scrollPending) != null ? _a3 : state.scroll) != null ? _b : 0
444
+ )
445
+ );
431
446
  }
432
447
  }
433
- function roundSize(size) {
434
- return Math.floor(size * 8) / 8;
448
+
449
+ // src/core/initialScrollSession.ts
450
+ var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
451
+ function hasInitialScrollSessionCompletion(completion) {
452
+ return !!((completion == null ? void 0 : completion.didDispatchNativeScroll) || (completion == null ? void 0 : completion.didRetrySilentInitialScroll) || (completion == null ? void 0 : completion.watchdog));
435
453
  }
436
- function isNullOrUndefined(value) {
437
- return value === null || value === void 0;
454
+ function clearInitialScrollSession(state) {
455
+ state.initialScrollSession = void 0;
456
+ return void 0;
438
457
  }
439
- function getPadding(s, type) {
440
- var _a3, _b, _c;
441
- const axisPadding = type === "Left" || type === "Right" ? s.paddingHorizontal : s.paddingVertical;
442
- return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : axisPadding) != null ? _b : s.padding) != null ? _c : 0;
458
+ function createInitialScrollSession(options) {
459
+ const { bootstrap, completion, kind, previousDataLength } = options;
460
+ return kind === "offset" ? {
461
+ completion,
462
+ kind,
463
+ previousDataLength
464
+ } : {
465
+ bootstrap,
466
+ completion,
467
+ kind,
468
+ previousDataLength
469
+ };
443
470
  }
444
- function extractPadding(style, contentContainerStyle, type) {
445
- return getPadding(style, type) + getPadding(contentContainerStyle, type);
471
+ function ensureInitialScrollSessionCompletion(state, kind = ((_b) => (_b = ((_a3) => (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind)()) != null ? _b : "bootstrap")()) {
472
+ var _a4, _b2;
473
+ if (!state.initialScrollSession) {
474
+ state.initialScrollSession = createInitialScrollSession({
475
+ completion: {},
476
+ kind,
477
+ previousDataLength: 0
478
+ });
479
+ } else if (state.initialScrollSession.kind !== kind) {
480
+ state.initialScrollSession = createInitialScrollSession({
481
+ bootstrap: state.initialScrollSession.kind === "bootstrap" ? state.initialScrollSession.bootstrap : void 0,
482
+ completion: state.initialScrollSession.completion,
483
+ kind,
484
+ previousDataLength: state.initialScrollSession.previousDataLength
485
+ });
486
+ }
487
+ (_b2 = (_a4 = state.initialScrollSession).completion) != null ? _b2 : _a4.completion = {};
488
+ return state.initialScrollSession.completion;
446
489
  }
447
- function findContainerId(ctx, key) {
448
- var _a3, _b;
449
- const directMatch = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.containerItemKeys) == null ? void 0 : _b.get(key);
450
- if (directMatch !== void 0) {
451
- return directMatch;
490
+ var initialScrollCompletion = {
491
+ didDispatchNativeScroll(state) {
492
+ var _a3, _b;
493
+ return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didDispatchNativeScroll);
494
+ },
495
+ didRetrySilentInitialScroll(state) {
496
+ var _a3, _b;
497
+ return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didRetrySilentInitialScroll);
498
+ },
499
+ markInitialScrollNativeDispatch(state) {
500
+ ensureInitialScrollSessionCompletion(state).didDispatchNativeScroll = true;
501
+ },
502
+ markSilentInitialScrollRetry(state) {
503
+ ensureInitialScrollSessionCompletion(state).didRetrySilentInitialScroll = true;
504
+ },
505
+ resetFlags(state) {
506
+ if (!state.initialScrollSession) {
507
+ return;
508
+ }
509
+ const completion = ensureInitialScrollSessionCompletion(state, state.initialScrollSession.kind);
510
+ completion.didDispatchNativeScroll = void 0;
511
+ completion.didRetrySilentInitialScroll = void 0;
452
512
  }
453
- const numContainers = peek$(ctx, "numContainers");
454
- for (let i = 0; i < numContainers; i++) {
455
- const itemKey = peek$(ctx, `containerItemKey${i}`);
456
- if (itemKey === key) {
457
- return i;
513
+ };
514
+ var initialScrollWatchdog = {
515
+ clear(state) {
516
+ initialScrollWatchdog.set(state, void 0);
517
+ },
518
+ didReachTarget(newScroll, watchdog) {
519
+ const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
520
+ return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
521
+ },
522
+ get(state) {
523
+ var _a3, _b;
524
+ return (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog;
525
+ },
526
+ hasNonZeroTargetOffset(targetOffset) {
527
+ return targetOffset !== void 0 && targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
528
+ },
529
+ isAtZeroTargetOffset(targetOffset) {
530
+ return targetOffset <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
531
+ },
532
+ set(state, watchdog) {
533
+ var _a3, _b;
534
+ if (!watchdog && !((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog)) {
535
+ return;
458
536
  }
537
+ const completion = ensureInitialScrollSessionCompletion(state);
538
+ completion.watchdog = watchdog ? {
539
+ startScroll: watchdog.startScroll,
540
+ targetOffset: watchdog.targetOffset
541
+ } : void 0;
459
542
  }
460
- return -1;
543
+ };
544
+ function setInitialScrollSession(state, options = {}) {
545
+ var _a3, _b, _c, _d;
546
+ const existingSession = state.initialScrollSession;
547
+ const kind = (_a3 = options.kind) != null ? _a3 : existingSession == null ? void 0 : existingSession.kind;
548
+ const completion = existingSession == null ? void 0 : existingSession.completion;
549
+ const existingBootstrap = (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0;
550
+ const bootstrap = kind === "bootstrap" ? options.bootstrap === null ? void 0 : (_b = options.bootstrap) != null ? _b : existingBootstrap : void 0;
551
+ if (!kind) {
552
+ return clearInitialScrollSession(state);
553
+ }
554
+ if (!state.initialScroll && !bootstrap && !hasInitialScrollSessionCompletion(completion)) {
555
+ return clearInitialScrollSession(state);
556
+ }
557
+ const previousDataLength = (_d = (_c = options.previousDataLength) != null ? _c : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _d : 0;
558
+ state.initialScrollSession = createInitialScrollSession({
559
+ bootstrap,
560
+ completion,
561
+ kind,
562
+ previousDataLength
563
+ });
564
+ return state.initialScrollSession;
461
565
  }
462
566
 
463
- // src/components/PositionView.tsx
464
- var isRNWeb = typeof document !== "undefined" && !!document.getElementById("react-native-stylesheet");
465
- var baseCss = {
466
- contain: "paint layout style",
467
- ...isRNWeb ? {
468
- display: "flex",
469
- flexDirection: "column"
470
- } : {}
567
+ // src/utils/checkThreshold.ts
568
+ var HYSTERESIS_MULTIPLIER = 1.3;
569
+ function isOutsideThresholdHysteresis(distance, atThreshold, threshold) {
570
+ const absDistance = Math.abs(distance);
571
+ return !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
572
+ }
573
+ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot) => {
574
+ const absDistance = Math.abs(distance);
575
+ const within = atThreshold || threshold > 0 && absDistance <= threshold;
576
+ const updateSnapshot = () => {
577
+ setSnapshot({
578
+ atThreshold,
579
+ contentSize: context.contentSize,
580
+ dataLength: context.dataLength,
581
+ scrollPosition: context.scrollPosition
582
+ });
583
+ };
584
+ if (!wasReached) {
585
+ if (!within) {
586
+ return false;
587
+ }
588
+ onReached(distance);
589
+ updateSnapshot();
590
+ return true;
591
+ }
592
+ const reset = isOutsideThresholdHysteresis(distance, atThreshold, threshold);
593
+ if (reset) {
594
+ setSnapshot(void 0);
595
+ return false;
596
+ }
597
+ if (within) {
598
+ const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
599
+ if (changed) {
600
+ updateSnapshot();
601
+ }
602
+ }
603
+ return true;
471
604
  };
472
- var PositionViewState = typedMemo(function PositionViewState2({
473
- id,
474
- horizontal,
475
- style,
476
- refView,
477
- ...props
478
- }) {
479
- const [position = POSITION_OUT_OF_VIEW] = useArr$([`containerPosition${id}`]);
480
- const composed = isArray(style) ? Object.assign({}, ...style) : style;
481
- const combinedStyle = horizontal ? { ...baseCss, ...composed, left: position } : { ...baseCss, ...composed, top: position };
482
- const {
483
- animatedScrollY: _animatedScrollY,
484
- index,
485
- onLayout: _onLayout,
486
- onLayoutChange: _onLayoutChange,
487
- stickyHeaderConfig: _stickyHeaderConfig,
488
- ...webProps
489
- } = props;
490
- return /* @__PURE__ */ React3__namespace.createElement("div", { "data-index": index, ref: refView, ...webProps, style: combinedStyle });
491
- });
492
- var PositionViewSticky = typedMemo(function PositionViewSticky2({
493
- id,
494
- horizontal,
495
- style,
496
- refView,
497
- index,
498
- animatedScrollY: _animatedScrollY,
499
- stickyHeaderConfig,
500
- onLayout: _onLayout,
501
- onLayoutChange: _onLayoutChange,
502
- children,
503
- ...webProps
504
- }) {
505
- const [position = POSITION_OUT_OF_VIEW, activeStickyIndex] = useArr$([
506
- `containerPosition${id}`,
507
- "activeStickyIndex"
508
- ]);
509
- const composed = React3__namespace.useMemo(
510
- () => {
511
- var _a3;
512
- return (_a3 = isArray(style) ? Object.assign({}, ...style) : style) != null ? _a3 : {};
513
- },
514
- [style]
515
- );
516
- const viewStyle = React3__namespace.useMemo(() => {
517
- var _a3;
518
- const styleBase = { ...baseCss, ...composed };
519
- delete styleBase.transform;
520
- const offset = (_a3 = stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset) != null ? _a3 : 0;
521
- const isActive = activeStickyIndex === index;
522
- styleBase.position = isActive ? "sticky" : "absolute";
523
- styleBase.zIndex = index + 1e3;
524
- if (horizontal) {
525
- styleBase.left = isActive ? offset : position;
526
- } else {
527
- styleBase.top = isActive ? offset : position;
528
- }
529
- return styleBase;
530
- }, [composed, horizontal, position, index, activeStickyIndex, stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset]);
531
- const renderStickyHeaderBackdrop = React3__namespace.useMemo(
532
- () => (stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent) ? /* @__PURE__ */ React3__namespace.createElement(
533
- "div",
534
- {
535
- style: {
536
- inset: 0,
537
- pointerEvents: "none",
538
- position: "absolute"
539
- }
540
- },
541
- getComponent(stickyHeaderConfig.backdropComponent)
542
- ) : null,
543
- [stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent]
544
- );
545
- return /* @__PURE__ */ React3__namespace.createElement(
546
- "div",
547
- {
548
- "data-index": index,
549
- ref: refView,
550
- style: viewStyle,
551
- ...webProps
552
- },
553
- renderStickyHeaderBackdrop,
554
- children
555
- );
556
- });
557
- var PositionView = PositionViewState;
558
-
559
- // src/constants-platform.ts
560
- var IsNewArchitecture = true;
561
- function useInit(cb) {
562
- React3.useState(() => cb());
563
- }
564
605
 
565
- // src/state/ContextContainer.ts
566
- var ContextContainer = React3.createContext(null);
567
- var NO_CONTAINER_ID = -1;
568
- function useContextContainer() {
569
- return React3.useContext(ContextContainer);
570
- }
571
- function useContainerItemInfo(containerContext) {
572
- var _a3;
573
- const containerId = (_a3 = containerContext == null ? void 0 : containerContext.containerId) != null ? _a3 : NO_CONTAINER_ID;
574
- const [itemInfo] = useArr$([`containerItemInfo${containerId}`]);
575
- return containerContext ? itemInfo : void 0;
576
- }
577
- function useContainerItemKey(containerContext) {
578
- var _a3;
579
- const containerId = (_a3 = containerContext == null ? void 0 : containerContext.containerId) != null ? _a3 : NO_CONTAINER_ID;
580
- const [itemKey] = useArr$([`containerItemKey${containerId}`]);
581
- return containerContext ? itemKey : void 0;
582
- }
583
- function useAdaptiveRender() {
584
- const [mode] = useArr$(["adaptiveRender"]);
585
- return mode;
586
- }
587
- function useAdaptiveRenderChange(callback) {
588
- const ctx = useStateContext();
589
- const callbackRef = React3.useRef(callback);
590
- callbackRef.current = callback;
591
- React3.useLayoutEffect(() => {
592
- let mode = peek$(ctx, "adaptiveRender");
593
- return listen$(ctx, "adaptiveRender", (nextMode) => {
594
- if (mode !== nextMode) {
595
- mode = nextMode;
596
- callbackRef.current(nextMode);
597
- }
598
- });
599
- }, [ctx]);
600
- }
601
- function useViewability(callback, configId) {
602
- const ctx = useStateContext();
603
- const containerContext = useContextContainer();
604
- useInit(() => {
605
- if (!containerContext) {
606
- return;
607
- }
608
- const { containerId } = containerContext;
609
- const key = containerId + (configId != null ? configId : "");
610
- const value = ctx.mapViewabilityValues.get(key);
611
- if (value) {
612
- callback(value);
613
- }
614
- });
615
- React3.useEffect(() => {
616
- if (!containerContext) {
617
- return;
618
- }
619
- const { containerId } = containerContext;
620
- const key = containerId + (configId != null ? configId : "");
621
- ctx.mapViewabilityCallbacks.set(key, callback);
622
- return () => {
623
- ctx.mapViewabilityCallbacks.delete(key);
624
- };
625
- }, [ctx, callback, configId, containerContext]);
626
- }
627
- function useViewabilityAmount(callback) {
628
- const ctx = useStateContext();
629
- const containerContext = useContextContainer();
630
- useInit(() => {
631
- if (!containerContext) {
632
- return;
633
- }
634
- const { containerId } = containerContext;
635
- const value = ctx.mapViewabilityAmountValues.get(containerId);
636
- if (value) {
637
- callback(value);
638
- }
639
- });
640
- React3.useEffect(() => {
641
- if (!containerContext) {
642
- return;
643
- }
644
- const { containerId } = containerContext;
645
- ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
646
- return () => {
647
- ctx.mapViewabilityAmountCallbacks.delete(containerId);
648
- };
649
- }, [ctx, callback, containerContext]);
650
- }
651
- function useRecyclingEffect(effect) {
652
- const containerContext = useContextContainer();
653
- const itemInfo = useContainerItemInfo(containerContext);
654
- const prevInfo = React3.useRef(void 0);
655
- React3.useEffect(() => {
656
- if (!itemInfo) {
657
- return;
658
- }
659
- let ret;
660
- if (prevInfo.current) {
661
- ret = effect({
662
- index: itemInfo.index,
663
- item: itemInfo.value,
664
- prevIndex: prevInfo.current.index,
665
- prevItem: prevInfo.current.value
666
- });
667
- }
668
- prevInfo.current = itemInfo;
669
- return ret;
670
- }, [effect, itemInfo]);
671
- }
672
- function useRecyclingState(valueOrFun) {
673
- var _a3;
674
- const containerContext = useContextContainer();
675
- const itemInfo = useContainerItemInfo(containerContext);
676
- const computeValue = (info) => {
677
- if (isFunction(valueOrFun)) {
678
- const initializer = valueOrFun;
679
- return info ? initializer({
680
- index: info.index,
681
- item: info.value,
682
- prevIndex: void 0,
683
- prevItem: void 0
684
- }) : initializer();
685
- }
686
- return valueOrFun;
687
- };
688
- const [stateValue, setStateValue] = React3.useState(() => {
689
- return computeValue(itemInfo);
690
- });
691
- const prevItemKeyRef = React3.useRef((_a3 = itemInfo == null ? void 0 : itemInfo.itemKey) != null ? _a3 : null);
692
- if (itemInfo && prevItemKeyRef.current !== itemInfo.itemKey) {
693
- prevItemKeyRef.current = itemInfo.itemKey;
694
- setStateValue(computeValue(itemInfo));
606
+ // src/utils/edgeReachedGate.ts
607
+ function resetEdgeLatch(ctx, edge) {
608
+ const state = ctx.state;
609
+ if (edge === "start") {
610
+ state.isStartReached = false;
611
+ state.startReachedSnapshot = void 0;
612
+ } else {
613
+ state.isEndReached = false;
614
+ state.endReachedSnapshot = void 0;
695
615
  }
696
- const triggerLayout = containerContext == null ? void 0 : containerContext.triggerLayout;
697
- const setState = React3.useCallback(
698
- (newState) => {
699
- if (!triggerLayout) {
700
- return;
701
- }
702
- setStateValue((prevValue) => {
703
- return isFunction(newState) ? newState(prevValue) : newState;
704
- });
705
- triggerLayout();
706
- },
707
- [triggerLayout]
708
- );
709
- return [stateValue, setState];
710
- }
711
- function useIsLastItem() {
712
- const containerContext = useContextContainer();
713
- const itemKey = useContainerItemKey(containerContext);
714
- const isLast = useSelector$("lastItemKeys", (lastItemKeys) => {
715
- if (containerContext && !isNullOrUndefined(itemKey)) {
716
- return (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false;
717
- }
718
- return false;
719
- });
720
- return isLast;
721
- }
722
- function useListScrollSize() {
723
- const [scrollSize] = useArr$(["scrollSize"]);
724
- return scrollSize;
725
- }
726
- var noop = () => {
727
- };
728
- function useSyncLayout() {
729
- const containerContext = useContextContainer();
730
- return containerContext ? containerContext.triggerLayout : noop;
731
- }
732
-
733
- // src/components/Separator.tsx
734
- function Separator({ ItemSeparatorComponent, leadingItem }) {
735
- const isLastItem = useIsLastItem();
736
- return isLastItem ? null : /* @__PURE__ */ React3__namespace.createElement(ItemSeparatorComponent, { leadingItem });
737
- }
738
-
739
- // src/core/deferredPublicOnScroll.ts
740
- function withResolvedContentOffset(state, event, resolvedOffset) {
741
- return {
742
- ...event,
743
- nativeEvent: {
744
- ...event.nativeEvent,
745
- contentOffset: state.props.horizontal ? { x: resolvedOffset, y: 0 } : { x: 0, y: resolvedOffset }
746
- }
747
- };
748
616
  }
749
- function releaseDeferredPublicOnScroll(ctx, resolvedOffset) {
750
- var _a3, _b, _c, _d;
617
+ function resetSharedEdgeGateIfOutsideHysteresis(ctx) {
751
618
  const state = ctx.state;
752
- const deferredEvent = state.deferredPublicOnScrollEvent;
753
- state.deferredPublicOnScrollEvent = void 0;
754
- if (deferredEvent) {
755
- (_d = (_c = state.props).onScroll) == null ? void 0 : _d.call(
756
- _c,
757
- withResolvedContentOffset(
758
- state,
759
- deferredEvent,
760
- (_b = (_a3 = resolvedOffset != null ? resolvedOffset : state.scrollPending) != null ? _a3 : state.scroll) != null ? _b : 0
761
- )
762
- );
619
+ if (!state.edgeReachedGate) {
620
+ return;
621
+ }
622
+ const contentSize = getContentSize(ctx);
623
+ const endDistance = contentSize - state.scroll - state.scrollLength - getContentInsetEnd(ctx);
624
+ const isContentLess = contentSize < state.scrollLength;
625
+ const startThreshold = state.props.onStartReachedThreshold * state.scrollLength;
626
+ const endThreshold = state.props.onEndReachedThreshold * state.scrollLength;
627
+ const isOutsideStart = isOutsideThresholdHysteresis(state.scroll, false, startThreshold);
628
+ const isOutsideEnd = isOutsideThresholdHysteresis(endDistance, isContentLess, endThreshold);
629
+ if (isOutsideStart && isOutsideEnd) {
630
+ state.edgeReachedGate = void 0;
763
631
  }
764
632
  }
765
-
766
- // src/core/initialScrollSession.ts
767
- var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
768
- function hasInitialScrollSessionCompletion(completion) {
769
- return !!((completion == null ? void 0 : completion.didDispatchNativeScroll) || (completion == null ? void 0 : completion.didRetrySilentInitialScroll) || (completion == null ? void 0 : completion.watchdog));
770
- }
771
- function clearInitialScrollSession(state) {
772
- state.initialScrollSession = void 0;
773
- return void 0;
633
+ function canDispatchReachedEdge(ctx, edge, allowedEdge, allowGateCreatedInCurrentCheck) {
634
+ return !ctx.state.edgeReachedGate || allowedEdge === edge || !!allowGateCreatedInCurrentCheck;
774
635
  }
775
- function createInitialScrollSession(options) {
776
- const { bootstrap, completion, kind, previousDataLength } = options;
777
- return kind === "offset" ? {
778
- completion,
779
- kind,
780
- previousDataLength
781
- } : {
782
- bootstrap,
783
- completion,
784
- kind,
785
- previousDataLength
786
- };
636
+ function markReachedEdge(ctx) {
637
+ ctx.state.edgeReachedGate = "closed";
787
638
  }
788
- function ensureInitialScrollSessionCompletion(state, kind = ((_b) => (_b = ((_a3) => (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind)()) != null ? _b : "bootstrap")()) {
789
- var _a4, _b2;
790
- if (!state.initialScrollSession) {
791
- state.initialScrollSession = createInitialScrollSession({
792
- completion: {},
793
- kind,
794
- previousDataLength: 0
795
- });
796
- } else if (state.initialScrollSession.kind !== kind) {
797
- state.initialScrollSession = createInitialScrollSession({
798
- bootstrap: state.initialScrollSession.kind === "bootstrap" ? state.initialScrollSession.bootstrap : void 0,
799
- completion: state.initialScrollSession.completion,
800
- kind,
801
- previousDataLength: state.initialScrollSession.previousDataLength
802
- });
639
+ function prepareReachedEdgeForNextUserScroll(ctx) {
640
+ if (ctx.state.edgeReachedGate) {
641
+ ctx.state.edgeReachedGate = "prepared";
803
642
  }
804
- (_b2 = (_a4 = state.initialScrollSession).completion) != null ? _b2 : _a4.completion = {};
805
- return state.initialScrollSession.completion;
806
643
  }
807
- var initialScrollCompletion = {
808
- didDispatchNativeScroll(state) {
809
- var _a3, _b;
810
- return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didDispatchNativeScroll);
811
- },
812
- didRetrySilentInitialScroll(state) {
813
- var _a3, _b;
814
- return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didRetrySilentInitialScroll);
815
- },
816
- markInitialScrollNativeDispatch(state) {
817
- ensureInitialScrollSessionCompletion(state).didDispatchNativeScroll = true;
818
- },
819
- markSilentInitialScrollRetry(state) {
820
- ensureInitialScrollSessionCompletion(state).didRetrySilentInitialScroll = true;
821
- },
822
- resetFlags(state) {
823
- if (!state.initialScrollSession) {
824
- return;
825
- }
826
- const completion = ensureInitialScrollSessionCompletion(state, state.initialScrollSession.kind);
827
- completion.didDispatchNativeScroll = void 0;
828
- completion.didRetrySilentInitialScroll = void 0;
829
- }
830
- };
831
- var initialScrollWatchdog = {
832
- clear(state) {
833
- initialScrollWatchdog.set(state, void 0);
834
- },
835
- didReachTarget(newScroll, watchdog) {
836
- const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
837
- return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
838
- },
839
- get(state) {
840
- var _a3, _b;
841
- return (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog;
842
- },
843
- hasNonZeroTargetOffset(targetOffset) {
844
- return targetOffset !== void 0 && targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
845
- },
846
- isAtZeroTargetOffset(targetOffset) {
847
- return targetOffset <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
848
- },
849
- set(state, watchdog) {
850
- var _a3, _b;
851
- if (!watchdog && !((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog)) {
852
- return;
853
- }
854
- const completion = ensureInitialScrollSessionCompletion(state);
855
- completion.watchdog = watchdog ? {
856
- startScroll: watchdog.startScroll,
857
- targetOffset: watchdog.targetOffset
858
- } : void 0;
859
- }
860
- };
861
- function setInitialScrollSession(state, options = {}) {
862
- var _a3, _b, _c, _d;
863
- const existingSession = state.initialScrollSession;
864
- const kind = (_a3 = options.kind) != null ? _a3 : existingSession == null ? void 0 : existingSession.kind;
865
- const completion = existingSession == null ? void 0 : existingSession.completion;
866
- const existingBootstrap = (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0;
867
- const bootstrap = kind === "bootstrap" ? options.bootstrap === null ? void 0 : (_b = options.bootstrap) != null ? _b : existingBootstrap : void 0;
868
- if (!kind) {
869
- return clearInitialScrollSession(state);
870
- }
871
- if (!state.initialScroll && !bootstrap && !hasInitialScrollSessionCompletion(completion)) {
872
- return clearInitialScrollSession(state);
644
+ function beginReachedEdgeUserScroll(ctx, scrollDelta) {
645
+ const state = ctx.state;
646
+ if (state.edgeReachedGate !== "prepared") {
647
+ return void 0;
873
648
  }
874
- const previousDataLength = (_d = (_c = options.previousDataLength) != null ? _c : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _d : 0;
875
- state.initialScrollSession = createInitialScrollSession({
876
- bootstrap,
877
- completion,
878
- kind,
879
- previousDataLength
880
- });
881
- return state.initialScrollSession;
649
+ const allowedEdge = scrollDelta < 0 ? "start" : "end";
650
+ state.edgeReachedGate = "closed";
651
+ resetEdgeLatch(ctx, allowedEdge);
652
+ return allowedEdge;
882
653
  }
883
654
 
884
- // src/utils/checkThreshold.ts
885
- var HYSTERESIS_MULTIPLIER = 1.3;
886
- var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot, allowReentryOnChange) => {
887
- const absDistance = Math.abs(distance);
888
- const within = atThreshold || threshold > 0 && absDistance <= threshold;
889
- const updateSnapshot = () => {
890
- setSnapshot({
891
- atThreshold,
892
- contentSize: context.contentSize,
893
- dataLength: context.dataLength,
894
- scrollPosition: context.scrollPosition
895
- });
896
- };
897
- if (!wasReached) {
898
- if (!within) {
899
- return false;
900
- }
901
- onReached(distance);
902
- updateSnapshot();
903
- return true;
904
- }
905
- const reset = !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
906
- if (reset) {
907
- setSnapshot(void 0);
908
- return false;
909
- }
910
- if (within) {
911
- const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
912
- if (changed) {
913
- if (allowReentryOnChange) {
914
- onReached(distance);
915
- }
916
- updateSnapshot();
917
- }
918
- }
919
- return true;
920
- };
921
-
922
655
  // src/utils/hasActiveInitialScroll.ts
923
656
  function hasActiveInitialScroll(state) {
924
657
  return !!(state == null ? void 0 : state.initialScroll) && !state.didFinishInitialScroll;
925
658
  }
926
659
 
927
660
  // src/utils/checkAtBottom.ts
928
- function checkAtBottom(ctx) {
661
+ function checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
929
662
  var _a3;
930
663
  const state = ctx.state;
931
664
  if (!state) {
@@ -939,6 +672,7 @@ function checkAtBottom(ctx) {
939
672
  props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
940
673
  } = state;
941
674
  const contentSize = getContentSize(ctx);
675
+ resetSharedEdgeGateIfOutsideHysteresis(ctx);
942
676
  if (contentSize > 0 && queuedInitialLayout) {
943
677
  const insetEnd = getContentInsetEnd(ctx);
944
678
  const distanceFromEnd = contentSize - scroll - scrollLength - insetEnd;
@@ -965,72 +699,50 @@ function checkAtBottom(ctx) {
965
699
  },
966
700
  (distance) => {
967
701
  var _a4, _b;
968
- return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
702
+ if (canDispatchReachedEdge(ctx, "end", allowedEdge, allowGateCreatedInCurrentCheck)) {
703
+ markReachedEdge(ctx);
704
+ (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
705
+ }
969
706
  },
970
707
  (snapshot) => {
971
708
  state.endReachedSnapshot = snapshot;
972
- },
973
- true
709
+ }
974
710
  );
975
711
  }
976
712
  }
977
713
  }
978
714
 
979
- // src/utils/hasActiveMVCPAnchorLock.ts
980
- function hasActiveMVCPAnchorLock(state) {
981
- const lock = state.mvcpAnchorLock;
982
- if (!lock) {
983
- return false;
984
- }
985
- if (Date.now() > lock.expiresAt) {
986
- state.mvcpAnchorLock = void 0;
987
- return false;
988
- }
989
- return true;
990
- }
991
-
992
- // src/utils/isInMVCPActiveMode.ts
993
- function isInMVCPActiveMode(state) {
994
- return state.dataChangeNeedsScrollUpdate || hasActiveMVCPAnchorLock(state);
995
- }
996
-
997
715
  // src/utils/checkAtTop.ts
998
- function checkAtTop(ctx) {
716
+ function checkAtTop(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
999
717
  const state = ctx == null ? void 0 : ctx.state;
1000
718
  if (!state) {
1001
719
  return;
1002
720
  }
1003
721
  const {
1004
- dataChangeEpoch,
1005
722
  isStartReached,
1006
723
  props: { data, onStartReachedThreshold },
1007
724
  scroll,
1008
725
  scrollLength,
1009
726
  startReachedSnapshot,
1010
- startReachedSnapshotDataChangeEpoch,
1011
727
  totalSize
1012
728
  } = state;
1013
729
  const dataLength = data.length;
1014
730
  const threshold = onStartReachedThreshold * scrollLength;
1015
- const dataChanged = startReachedSnapshotDataChangeEpoch !== dataChangeEpoch;
1016
- const withinThreshold = threshold > 0 && Math.abs(scroll) <= threshold;
1017
- const allowReentryOnDataChange = !!isStartReached && withinThreshold && !!dataChanged && !isInMVCPActiveMode(state);
1018
- if (isStartReached && threshold > 0 && scroll > threshold && startReachedSnapshot && (dataChanged || startReachedSnapshot.contentSize !== totalSize || startReachedSnapshot.dataLength !== dataLength)) {
731
+ resetSharedEdgeGateIfOutsideHysteresis(ctx);
732
+ if (isStartReached && threshold > 0 && scroll > threshold && startReachedSnapshot && (startReachedSnapshot.contentSize !== totalSize || startReachedSnapshot.dataLength !== dataLength)) {
1019
733
  state.isStartReached = false;
1020
734
  state.startReachedSnapshot = void 0;
1021
- state.startReachedSnapshotDataChangeEpoch = void 0;
1022
735
  }
1023
736
  set$(ctx, "isAtStart", scroll <= EDGE_POSITION_EPSILON);
1024
737
  set$(ctx, "isNearStart", scroll <= threshold);
1025
738
  const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || !!state.scrollingTo;
1026
- const shouldDeferDataChangeRefire = isStartReached && withinThreshold && dataChanged && !allowReentryOnDataChange;
1027
- if (!shouldSkipThresholdChecks && !shouldDeferDataChangeRefire) {
739
+ if (!shouldSkipThresholdChecks) {
1028
740
  state.isStartReached = checkThreshold(
1029
741
  scroll,
1030
742
  false,
1031
743
  threshold,
1032
744
  state.isStartReached,
1033
- allowReentryOnDataChange ? void 0 : startReachedSnapshot,
745
+ startReachedSnapshot,
1034
746
  {
1035
747
  contentSize: totalSize,
1036
748
  dataLength,
@@ -1038,21 +750,23 @@ function checkAtTop(ctx) {
1038
750
  },
1039
751
  (distance) => {
1040
752
  var _a3, _b;
1041
- return (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
753
+ if (canDispatchReachedEdge(ctx, "start", allowedEdge, allowGateCreatedInCurrentCheck)) {
754
+ markReachedEdge(ctx);
755
+ (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
756
+ }
1042
757
  },
1043
758
  (snapshot) => {
1044
759
  state.startReachedSnapshot = snapshot;
1045
- state.startReachedSnapshotDataChangeEpoch = snapshot ? dataChangeEpoch : void 0;
1046
- },
1047
- allowReentryOnDataChange
760
+ }
1048
761
  );
1049
762
  }
1050
763
  }
1051
764
 
1052
765
  // src/utils/checkThresholds.ts
1053
- function checkThresholds(ctx) {
1054
- checkAtBottom(ctx);
1055
- checkAtTop(ctx);
766
+ function checkThresholds(ctx, allowedEdge) {
767
+ const allowGateCreatedInCurrentCheck = !ctx.state.edgeReachedGate;
768
+ checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck);
769
+ checkAtTop(ctx, allowedEdge, allowGateCreatedInCurrentCheck);
1056
770
  }
1057
771
 
1058
772
  // src/core/recalculateSettledScroll.ts
@@ -1353,6 +1067,50 @@ function setSize(ctx, itemKey, size, notifyTotalSize = true) {
1353
1067
  sizes.set(itemKey, size);
1354
1068
  }
1355
1069
 
1070
+ // src/utils/helpers.ts
1071
+ function isFunction(obj) {
1072
+ return typeof obj === "function";
1073
+ }
1074
+ function isArray(obj) {
1075
+ return Array.isArray(obj);
1076
+ }
1077
+ var warned = /* @__PURE__ */ new Set();
1078
+ function warnDevOnce(id, text) {
1079
+ if (IS_DEV && !warned.has(id)) {
1080
+ warned.add(id);
1081
+ console.warn(`[legend-list] ${text}`);
1082
+ }
1083
+ }
1084
+ function roundSize(size) {
1085
+ return Math.floor(size * 8) / 8;
1086
+ }
1087
+ function isNullOrUndefined(value) {
1088
+ return value === null || value === void 0;
1089
+ }
1090
+ function getPadding(s, type) {
1091
+ var _a3, _b, _c;
1092
+ const axisPadding = type === "Left" || type === "Right" ? s.paddingHorizontal : s.paddingVertical;
1093
+ return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : axisPadding) != null ? _b : s.padding) != null ? _c : 0;
1094
+ }
1095
+ function extractPadding(style, contentContainerStyle, type) {
1096
+ return getPadding(style, type) + getPadding(contentContainerStyle, type);
1097
+ }
1098
+ function findContainerId(ctx, key) {
1099
+ var _a3, _b;
1100
+ const directMatch = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.containerItemKeys) == null ? void 0 : _b.get(key);
1101
+ if (directMatch !== void 0) {
1102
+ return directMatch;
1103
+ }
1104
+ const numContainers = peek$(ctx, "numContainers");
1105
+ for (let i = 0; i < numContainers; i++) {
1106
+ const itemKey = peek$(ctx, `containerItemKey${i}`);
1107
+ if (itemKey === key) {
1108
+ return i;
1109
+ }
1110
+ }
1111
+ return -1;
1112
+ }
1113
+
1356
1114
  // src/utils/getItemSize.ts
1357
1115
  function getKnownOrFixedSize(ctx, key, index, data, resolved) {
1358
1116
  var _a3, _b;
@@ -2028,8 +1786,26 @@ var getScrollVelocity = (state) => {
2028
1786
  weightedVelocity += scrollDiff / timeDiff * weight;
2029
1787
  totalWeight += weight;
2030
1788
  }
2031
- return totalWeight > 0 ? weightedVelocity / totalWeight : 0;
2032
- };
1789
+ return totalWeight > 0 ? weightedVelocity / totalWeight : 0;
1790
+ };
1791
+
1792
+ // src/utils/hasActiveMVCPAnchorLock.ts
1793
+ function hasActiveMVCPAnchorLock(state) {
1794
+ const lock = state.mvcpAnchorLock;
1795
+ if (!lock) {
1796
+ return false;
1797
+ }
1798
+ if (Date.now() > lock.expiresAt) {
1799
+ state.mvcpAnchorLock = void 0;
1800
+ return false;
1801
+ }
1802
+ return true;
1803
+ }
1804
+
1805
+ // src/utils/isInMVCPActiveMode.ts
1806
+ function isInMVCPActiveMode(state) {
1807
+ return state.dataChangeNeedsScrollUpdate || hasActiveMVCPAnchorLock(state);
1808
+ }
2033
1809
 
2034
1810
  // src/core/updateScroll.ts
2035
1811
  function updateScroll(ctx, newScroll, forceUpdate, options) {
@@ -2068,6 +1844,8 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2068
1844
  state.scroll = newScroll;
2069
1845
  state.scrollTime = currentTime;
2070
1846
  const scrollDelta = Math.abs(newScroll - prevScroll);
1847
+ const isUserScrollEvent = !!(options == null ? void 0 : options.fromNativeScrollEvent) && scrollDelta > 0.1 && !adjustChanged && scrollingTo === void 0 && !state.pendingNativeMVCPAdjust;
1848
+ const allowedEdge = isUserScrollEvent ? beginReachedEdgeUserScroll(ctx, newScroll - prevScroll) : void 0;
2071
1849
  const didResolvePendingNativeMVCPAdjust = resolvePendingNativeMVCPAdjust(ctx, newScroll);
2072
1850
  const scrollLength = state.scrollLength;
2073
1851
  const isLargeUserScrollJump = scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength && !state.pendingNativeMVCPAdjust;
@@ -2075,7 +1853,7 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2075
1853
  updateAdaptiveRender(ctx, scrollVelocity, { forceLight: isLargeUserScrollJump });
2076
1854
  const lastCalculated = state.scrollLastCalculate;
2077
1855
  const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
2078
- const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
1856
+ const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || allowedEdge !== void 0 || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
2079
1857
  if (shouldUpdate) {
2080
1858
  state.scrollLastCalculate = state.scroll;
2081
1859
  state.ignoreScrollFromMVCPIgnored = false;
@@ -2090,7 +1868,7 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2090
1868
  calculateItemsParams.drawDistanceMode = "visible-first";
2091
1869
  }
2092
1870
  (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, calculateItemsParams);
2093
- checkThresholds(ctx);
1871
+ checkThresholds(ctx, allowedEdge);
2094
1872
  };
2095
1873
  if (isLargeUserScrollJump) {
2096
1874
  state.mvcpAnchorLock = void 0;
@@ -3051,6 +2829,47 @@ function abortBootstrapInitialScroll(ctx) {
3051
2829
  }
3052
2830
  }
3053
2831
 
2832
+ // src/core/containerItemMetadata.ts
2833
+ function createContainerItemMetadata(state, itemIndex, itemData, itemType) {
2834
+ return {
2835
+ dataChangeEpoch: state.dataChangeEpoch,
2836
+ getFixedItemSize: state.props.getFixedItemSize,
2837
+ getItemType: state.props.getItemType,
2838
+ itemData,
2839
+ itemIndex,
2840
+ itemType
2841
+ };
2842
+ }
2843
+ function resolveContainerItemMetadata(state, containerId, itemIndex, itemData) {
2844
+ var _a3, _b;
2845
+ const { getFixedItemSize, getItemType } = state.props;
2846
+ const previousMetadata = state.containerItemMetadata.get(containerId);
2847
+ let metadata;
2848
+ if ((previousMetadata == null ? void 0 : previousMetadata.dataChangeEpoch) === state.dataChangeEpoch && previousMetadata.getItemType === getItemType && previousMetadata.itemData === itemData && previousMetadata.itemIndex === itemIndex) {
2849
+ metadata = previousMetadata;
2850
+ } else {
2851
+ const itemType = getItemType ? (_a3 = getItemType(itemData, itemIndex)) != null ? _a3 : "" : void 0;
2852
+ metadata = createContainerItemMetadata(state, itemIndex, itemData, itemType);
2853
+ state.containerItemMetadata.set(containerId, metadata);
2854
+ }
2855
+ if (metadata.getFixedItemSize !== getFixedItemSize) {
2856
+ metadata.didResolveFixedItemSize = false;
2857
+ metadata.fixedItemSize = void 0;
2858
+ metadata.getFixedItemSize = getFixedItemSize;
2859
+ }
2860
+ if (getFixedItemSize && !metadata.didResolveFixedItemSize) {
2861
+ metadata.fixedItemSize = getFixedItemSize(itemData, itemIndex, (_b = metadata.itemType) != null ? _b : "");
2862
+ metadata.didResolveFixedItemSize = true;
2863
+ }
2864
+ return metadata;
2865
+ }
2866
+ function invalidateContainerFixedItemSizes(state) {
2867
+ for (const metadata of state.containerItemMetadata.values()) {
2868
+ metadata.didResolveFixedItemSize = false;
2869
+ metadata.fixedItemSize = void 0;
2870
+ }
2871
+ }
2872
+
3054
2873
  // src/core/checkFinishedScroll.ts
3055
2874
  var INITIAL_SCROLL_MAX_FALLBACK_CHECKS = 20;
3056
2875
  var INITIAL_SCROLL_COMPLETION_TARGET_EPSILON = 1;
@@ -3324,6 +3143,52 @@ function resetLayoutCachesForDataChange(state) {
3324
3143
  state.columnSpans.length = 0;
3325
3144
  }
3326
3145
 
3146
+ // src/core/scheduleContainerLayout.ts
3147
+ function getContainerLayoutEffectScope(ctx) {
3148
+ var _a3;
3149
+ const scheduledIds = ctx.pendingContainerIds;
3150
+ ctx.pendingContainerIds = void 0;
3151
+ if (scheduledIds === void 0) {
3152
+ return void 0;
3153
+ }
3154
+ const state = ctx.state;
3155
+ let targetContainerIds = scheduledIds;
3156
+ if (targetContainerIds && ((_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys.size)) {
3157
+ targetContainerIds = new Set(targetContainerIds);
3158
+ for (const itemKey of state.userScrollAnchorReset.keys) {
3159
+ const containerId = state.containerItemKeys.get(itemKey);
3160
+ if (containerId !== void 0) {
3161
+ targetContainerIds.add(containerId);
3162
+ }
3163
+ }
3164
+ }
3165
+ return targetContainerIds;
3166
+ }
3167
+ function scheduleContainerLayout(ctx, target) {
3168
+ var _a3;
3169
+ const isAlreadyScheduled = ctx.pendingContainerIds !== void 0;
3170
+ const previousIds = ctx.pendingContainerIds;
3171
+ if (target === void 0) {
3172
+ ctx.pendingContainerIds = null;
3173
+ } else if (previousIds !== null) {
3174
+ let nextIds = previousIds;
3175
+ if (!nextIds) {
3176
+ nextIds = typeof target === "number" ? /* @__PURE__ */ new Set([target]) : new Set(target);
3177
+ } else if (typeof target === "number") {
3178
+ nextIds.add(target);
3179
+ } else {
3180
+ for (const containerId of target) {
3181
+ nextIds.add(containerId);
3182
+ }
3183
+ }
3184
+ ctx.pendingContainerIds = nextIds;
3185
+ }
3186
+ if (!isAlreadyScheduled) {
3187
+ const nextEpoch = ((_a3 = peek$(ctx, "containerLayoutEpoch")) != null ? _a3 : 0) + 1;
3188
+ set$(ctx, "containerLayoutEpoch", nextEpoch);
3189
+ }
3190
+ }
3191
+
3327
3192
  // src/core/syncMountedContainer.ts
3328
3193
  function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3329
3194
  var _a3, _b, _c, _d, _e, _f, _g, _h, _i;
@@ -3367,14 +3232,13 @@ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3367
3232
  set$(ctx, `containerSpan${containerIndex}`, span);
3368
3233
  }
3369
3234
  }
3370
- const prevItemInfo = peek$(ctx, `containerItemInfo${containerIndex}`);
3235
+ const prevIndex = peek$(ctx, `containerItemIndex${containerIndex}`);
3236
+ if (prevIndex !== itemIndex) {
3237
+ set$(ctx, `containerItemIndex${containerIndex}`, itemIndex);
3238
+ }
3371
3239
  const prevData = peek$(ctx, `containerItemData${containerIndex}`);
3372
- let itemInfoValue = prevData;
3373
- let didChangeItemInfo = (prevItemInfo == null ? void 0 : prevItemInfo.itemKey) !== itemKey || (prevItemInfo == null ? void 0 : prevItemInfo.index) !== itemIndex || (prevItemInfo == null ? void 0 : prevItemInfo.value) !== prevData;
3374
3240
  const updateData = () => {
3375
3241
  set$(ctx, `containerItemData${containerIndex}`, item);
3376
- itemInfoValue = item;
3377
- didChangeItemInfo = true;
3378
3242
  didRefreshData = true;
3379
3243
  };
3380
3244
  if (prevData !== item) {
@@ -3409,13 +3273,6 @@ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3409
3273
  }
3410
3274
  }
3411
3275
  }
3412
- if (didChangeItemInfo) {
3413
- set$(ctx, `containerItemInfo${containerIndex}`, {
3414
- index: itemIndex,
3415
- itemKey,
3416
- value: itemInfoValue
3417
- });
3418
- }
3419
3276
  return { didChangePosition, didRefreshData };
3420
3277
  }
3421
3278
 
@@ -3780,19 +3637,29 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
3780
3637
  if (previousViewableItems) {
3781
3638
  for (const viewToken of previousViewableItems) {
3782
3639
  previousViewableKeys.add(viewToken.key);
3640
+ const currentIndex = state.indexByKey.get(viewToken.key);
3641
+ const currentItem = currentIndex !== void 0 ? data[currentIndex] : void 0;
3783
3642
  const containerId = findContainerId(ctx, viewToken.key);
3784
- if (!checkIsViewable(
3785
- state,
3786
- ctx,
3787
- viewabilityConfig,
3788
- containerId,
3789
- viewToken.key,
3790
- scrollSize,
3791
- viewToken.item,
3792
- viewToken.index
3793
- )) {
3794
- viewToken.isViewable = false;
3795
- changed.push(viewToken);
3643
+ let isStillViewable = false;
3644
+ if (currentIndex !== void 0 && currentItem !== void 0) {
3645
+ isStillViewable = checkIsViewable(
3646
+ state,
3647
+ ctx,
3648
+ viewabilityConfig,
3649
+ containerId,
3650
+ viewToken.key,
3651
+ scrollSize,
3652
+ currentItem,
3653
+ currentIndex
3654
+ );
3655
+ }
3656
+ if (!isStillViewable) {
3657
+ changed.push({
3658
+ ...viewToken,
3659
+ index: currentIndex != null ? currentIndex : viewToken.index,
3660
+ isViewable: false,
3661
+ item: currentItem != null ? currentItem : viewToken.item
3662
+ });
3796
3663
  }
3797
3664
  }
3798
3665
  }
@@ -3949,128 +3816,102 @@ function getExpandedContainerPoolSize(dataLength, numContainers) {
3949
3816
 
3950
3817
  // src/utils/findAvailableContainers.ts
3951
3818
  function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffered, pendingRemoval, getRequiredItemType, protectedKeys) {
3819
+ var _a3;
3952
3820
  const numNeeded = needNewContainers.length;
3953
3821
  if (numNeeded === 0) {
3954
3822
  return [];
3955
3823
  }
3956
3824
  const numContainers = peek$(ctx, "numContainers");
3957
3825
  const state = ctx.state;
3958
- const { stickyContainerPool, containerItemTypes } = state;
3826
+ const { containerItemMetadata, stickyContainerPool } = state;
3959
3827
  const shouldAvoidAssignedContainerReuse = state.props.recycleItems && !!state.props.positionComponentInternal;
3960
- const allocations = [];
3961
3828
  const pendingRemovalSet = pendingRemoval.length > 0 ? new Set(pendingRemoval) : void 0;
3962
- let pendingRemovalChanged = false;
3829
+ const requests = needNewContainers.map((itemIndex, order) => ({
3830
+ isSticky: state.props.stickyHeaderIndicesSet.has(itemIndex),
3831
+ itemIndex,
3832
+ itemType: getRequiredItemType == null ? void 0 : getRequiredItemType(itemIndex),
3833
+ order
3834
+ }));
3835
+ const normalRequests = requests.filter((request) => !request.isSticky);
3836
+ const stickyRequests = requests.filter((request) => request.isSticky);
3837
+ const normalCandidates = [];
3838
+ const stickyCandidates = [];
3839
+ for (let containerIndex = 0; containerIndex < numContainers; containerIndex++) {
3840
+ const key = peek$(ctx, `containerItemKey${containerIndex}`);
3841
+ const isPendingRemoval = !!(pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(containerIndex));
3842
+ const isProtected = !!key && !!(protectedKeys == null ? void 0 : protectedKeys.has(key)) && state.indexByKey.has(key);
3843
+ if (isProtected) {
3844
+ continue;
3845
+ }
3846
+ if (stickyContainerPool.has(containerIndex)) {
3847
+ if (key === void 0 || isPendingRemoval) {
3848
+ stickyCandidates.push({ containerIndex, distance: Number.POSITIVE_INFINITY });
3849
+ }
3850
+ } else if (key === void 0 || isPendingRemoval) {
3851
+ normalCandidates.push({ containerIndex, distance: Number.POSITIVE_INFINITY });
3852
+ } else if (!shouldAvoidAssignedContainerReuse) {
3853
+ const index = state.indexByKey.get(key);
3854
+ if (index !== void 0 && (index < startBuffered || index > endBuffered)) {
3855
+ const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
3856
+ normalCandidates.push({ containerIndex, distance });
3857
+ }
3858
+ }
3859
+ }
3860
+ normalCandidates.sort(comparatorByDistance);
3861
+ const allocations = new Array(numNeeded);
3963
3862
  let nextNewContainerIndex = numContainers;
3964
- const usedContainers = /* @__PURE__ */ new Set();
3965
- let availableContainers;
3966
- const stickyHeaderIndicesSet = state.props.stickyHeaderIndicesSet;
3967
- const canReuseContainer = (containerIndex, requiredType) => {
3968
- if (!requiredType) return true;
3969
- const existingType = containerItemTypes.get(containerIndex);
3970
- if (!existingType) return true;
3971
- return existingType === requiredType;
3972
- };
3973
- const pushAllocation = (itemIndex, itemType, containerIndex) => {
3974
- allocations.push({
3863
+ let pendingRemovalChanged = false;
3864
+ const assign = (request, containerIndex) => {
3865
+ allocations[request.order] = {
3975
3866
  containerIndex,
3976
- itemIndex,
3977
- itemType
3978
- });
3979
- usedContainers.add(containerIndex);
3867
+ itemIndex: request.itemIndex,
3868
+ itemType: request.itemType
3869
+ };
3980
3870
  if (pendingRemovalSet == null ? void 0 : pendingRemovalSet.delete(containerIndex)) {
3981
3871
  pendingRemovalChanged = true;
3982
3872
  }
3983
3873
  };
3984
- const pushNewContainer = (itemIndex, itemType, isSticky) => {
3985
- const newContainerIndex = nextNewContainerIndex++;
3986
- pushAllocation(itemIndex, itemType, newContainerIndex);
3987
- if (isSticky) {
3988
- stickyContainerPool.add(newContainerIndex);
3989
- }
3990
- return newContainerIndex;
3991
- };
3992
- const canUseContainer = (containerIndex, itemType) => {
3993
- if (usedContainers.has(containerIndex) || stickyContainerPool.has(containerIndex)) {
3994
- return false;
3995
- }
3996
- const key = peek$(ctx, `containerItemKey${containerIndex}`);
3997
- const isPending = !!(pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(containerIndex));
3998
- return (key === void 0 || isPending) && canReuseContainer(containerIndex, itemType);
3999
- };
4000
- const findStickyContainer = (itemType) => {
4001
- let foundContainer;
4002
- for (const containerIndex of stickyContainerPool) {
4003
- if (!usedContainers.has(containerIndex)) {
4004
- const key = peek$(ctx, `containerItemKey${containerIndex}`);
4005
- const isPendingRemoval = !!(pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(containerIndex));
4006
- if ((key === void 0 || isPendingRemoval) && canReuseContainer(containerIndex, itemType)) {
4007
- foundContainer = containerIndex;
4008
- break;
4009
- }
4010
- }
4011
- }
4012
- return foundContainer;
4013
- };
4014
- const findUnassignedOrPendingContainer = (itemType) => {
4015
- let foundContainer;
4016
- for (let containerIndex = 0; containerIndex < numContainers && foundContainer === void 0; containerIndex++) {
4017
- if (canUseContainer(containerIndex, itemType)) {
4018
- foundContainer = containerIndex;
3874
+ const assignMatching = (pendingRequests, candidates, matches) => {
3875
+ for (const request of pendingRequests) {
3876
+ if (allocations[request.order]) {
3877
+ continue;
4019
3878
  }
4020
- }
4021
- return foundContainer;
4022
- };
4023
- const getAvailableContainers = () => {
4024
- if (!availableContainers) {
4025
- availableContainers = [];
4026
- if (!shouldAvoidAssignedContainerReuse) {
4027
- for (let containerIndex = 0; containerIndex < numContainers; containerIndex++) {
4028
- if (usedContainers.has(containerIndex) || stickyContainerPool.has(containerIndex)) {
4029
- continue;
4030
- }
4031
- const key = peek$(ctx, `containerItemKey${containerIndex}`);
4032
- if (key === void 0) continue;
4033
- if ((protectedKeys == null ? void 0 : protectedKeys.has(key)) && state.indexByKey.has(key)) continue;
4034
- const index = state.indexByKey.get(key);
4035
- const isOutOfView = index < startBuffered || index > endBuffered;
4036
- if (isOutOfView) {
4037
- const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
4038
- availableContainers.push({ distance, index: containerIndex });
4039
- }
3879
+ const candidateIndex = candidates.findIndex(
3880
+ (candidate) => {
3881
+ var _a4;
3882
+ return matches((_a4 = containerItemMetadata.get(candidate.containerIndex)) == null ? void 0 : _a4.itemType, request.itemType);
4040
3883
  }
4041
- availableContainers.sort(comparatorByDistance);
3884
+ );
3885
+ if (candidateIndex !== -1) {
3886
+ const [candidate] = candidates.splice(candidateIndex, 1);
3887
+ assign(request, candidate.containerIndex);
4042
3888
  }
4043
3889
  }
4044
- return availableContainers;
4045
3890
  };
4046
- const findAvailableContainer = (itemType) => {
4047
- const containers = getAvailableContainers();
4048
- let matchIndex = -1;
4049
- for (let i = 0; i < containers.length && matchIndex === -1; i++) {
4050
- const containerIndex = containers[i].index;
4051
- if (!usedContainers.has(containerIndex) && canReuseContainer(containerIndex, itemType)) {
4052
- matchIndex = i;
4053
- }
3891
+ const assignFromPool = (pendingRequests, candidates, allowCrossType) => {
3892
+ if (getRequiredItemType) {
3893
+ assignMatching(
3894
+ pendingRequests,
3895
+ candidates,
3896
+ (containerType, requestType) => requestType !== void 0 && containerType === requestType
3897
+ );
3898
+ }
3899
+ assignMatching(pendingRequests, candidates, (containerType) => containerType === void 0);
3900
+ if (allowCrossType) {
3901
+ assignMatching(pendingRequests, candidates, () => true);
4054
3902
  }
4055
- return matchIndex === -1 ? void 0 : containers.splice(matchIndex, 1)[0].index;
4056
3903
  };
4057
- for (const itemIndex of needNewContainers) {
4058
- const itemType = getRequiredItemType == null ? void 0 : getRequiredItemType(itemIndex);
4059
- const isSticky = stickyHeaderIndicesSet.has(itemIndex);
4060
- let containerIndex;
4061
- if (isSticky) {
4062
- containerIndex = findStickyContainer(itemType);
4063
- } else {
4064
- containerIndex = findUnassignedOrPendingContainer(itemType);
4065
- if (containerIndex === void 0) {
4066
- containerIndex = findAvailableContainer(itemType);
4067
- }
3904
+ assignFromPool(normalRequests, normalCandidates, true);
3905
+ assignFromPool(stickyRequests, stickyCandidates, false);
3906
+ for (const request of requests) {
3907
+ if (allocations[request.order]) {
3908
+ continue;
4068
3909
  }
4069
- if (containerIndex !== void 0) {
4070
- pushAllocation(itemIndex, itemType, containerIndex);
4071
- } else {
4072
- pushNewContainer(itemIndex, itemType, isSticky);
3910
+ const containerIndex = nextNewContainerIndex++;
3911
+ if (request.isSticky) {
3912
+ stickyContainerPool.add(containerIndex);
4073
3913
  }
3914
+ assign(request, containerIndex);
4074
3915
  }
4075
3916
  if (pendingRemovalChanged) {
4076
3917
  pendingRemoval.length = 0;
@@ -4080,18 +3921,21 @@ function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffe
4080
3921
  }
4081
3922
  }
4082
3923
  }
4083
- if (IS_DEV && nextNewContainerIndex > peek$(ctx, "numContainersPooled")) {
4084
- console.warn(
4085
- "[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.",
4086
- {
4087
- debugInfo: {
4088
- numContainers,
4089
- numContainersPooled: peek$(ctx, "numContainersPooled"),
4090
- numNeeded,
4091
- stillNeeded: nextNewContainerIndex - numContainers
3924
+ if (IS_DEV) {
3925
+ const numContainersPooled = (_a3 = peek$(ctx, "numContainersPooled")) != null ? _a3 : Number.POSITIVE_INFINITY;
3926
+ if (nextNewContainerIndex > numContainersPooled) {
3927
+ console.warn(
3928
+ "[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.",
3929
+ {
3930
+ debugInfo: {
3931
+ numContainers,
3932
+ numContainersPooled,
3933
+ numNeeded,
3934
+ stillNeeded: nextNewContainerIndex - numContainers
3935
+ }
4092
3936
  }
4093
- }
4094
- );
3937
+ );
3938
+ }
4095
3939
  }
4096
3940
  return allocations;
4097
3941
  }
@@ -4326,7 +4170,7 @@ function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, sc
4326
4170
  function calculateItemsInView(ctx, params = {}) {
4327
4171
  const state = ctx.state;
4328
4172
  batchedUpdates(() => {
4329
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t;
4173
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u;
4330
4174
  const {
4331
4175
  columns,
4332
4176
  containerItemKeys,
@@ -4354,6 +4198,7 @@ function calculateItemsInView(ctx, params = {}) {
4354
4198
  return;
4355
4199
  }
4356
4200
  let totalSize = getContentSize(ctx);
4201
+ let changedContainerIds;
4357
4202
  const topPad = peek$(ctx, "stylePaddingTop") + peek$(ctx, "alignItemsAtEndPadding") + peek$(ctx, "headerSize");
4358
4203
  const numColumns = peek$(ctx, "numColumns");
4359
4204
  const speed = (_b = params.scrollVelocity) != null ? _b : getScrollVelocity(state);
@@ -4479,7 +4324,7 @@ function calculateItemsInView(ctx, params = {}) {
4479
4324
  const scrollAdjustPendingBeforeMVCP = (_f = peek$(ctx, "scrollAdjustPending")) != null ? _f : 0;
4480
4325
  checkMVCP == null ? void 0 : checkMVCP();
4481
4326
  const didMVCPAdjustScroll = !!checkMVCP && (state.scroll !== scrollBeforeMVCP || ((_g = peek$(ctx, "scrollAdjustPending")) != null ? _g : 0) !== scrollAdjustPendingBeforeMVCP);
4482
- if (didMVCPAdjustScroll && (initialScroll || state.scrollingTo)) {
4327
+ if (didMVCPAdjustScroll) {
4483
4328
  updateScroll2(state.scroll);
4484
4329
  updateScrollRange();
4485
4330
  }
@@ -4659,22 +4504,20 @@ function calculateItemsInView(ctx, params = {}) {
4659
4504
  if (oldKey && oldKey !== id) {
4660
4505
  containerItemKeys.delete(oldKey);
4661
4506
  }
4507
+ if (oldKey !== id) {
4508
+ changedContainerIds != null ? changedContainerIds : changedContainerIds = /* @__PURE__ */ new Set();
4509
+ changedContainerIds.add(containerIndex);
4510
+ state.containerItemGenerations[containerIndex] = ((_p = state.containerItemGenerations[containerIndex]) != null ? _p : 0) + 1;
4511
+ }
4662
4512
  set$(ctx, `containerItemKey${containerIndex}`, id);
4513
+ set$(ctx, `containerItemIndex${containerIndex}`, i);
4663
4514
  set$(ctx, `containerItemData${containerIndex}`, data[i]);
4664
- set$(ctx, `containerItemInfo${containerIndex}`, {
4665
- index: i,
4666
- itemKey: id,
4667
- value: data[i]
4668
- });
4669
- if (allocation.itemType !== void 0) {
4670
- state.containerItemTypes.set(containerIndex, allocation.itemType);
4671
- }
4515
+ state.containerItemMetadata.set(
4516
+ containerIndex,
4517
+ createContainerItemMetadata(state, i, data[i], allocation.itemType)
4518
+ );
4672
4519
  containerItemKeys.set(id, containerIndex);
4673
- (_p = state.userScrollAnchorReset) == null ? void 0 : _p.keys.add(id);
4674
- {
4675
- (_q = state.pendingLayoutEffectMeasurements) != null ? _q : state.pendingLayoutEffectMeasurements = /* @__PURE__ */ new Set();
4676
- state.pendingLayoutEffectMeasurements.add(id);
4677
- }
4520
+ (_q = state.userScrollAnchorReset) == null ? void 0 : _q.keys.add(id);
4678
4521
  const containerSticky = `containerSticky${containerIndex}`;
4679
4522
  const isSticky = stickyHeaderIndicesSet.has(i);
4680
4523
  const isPinnedRender = isPinnedRenderIndex(i);
@@ -4724,15 +4567,18 @@ function calculateItemsInView(ctx, params = {}) {
4724
4567
  if (pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(i)) {
4725
4568
  if (itemKey !== void 0) {
4726
4569
  containerItemKeys.delete(itemKey);
4570
+ changedContainerIds != null ? changedContainerIds : changedContainerIds = /* @__PURE__ */ new Set();
4571
+ changedContainerIds.add(i);
4572
+ state.containerItemGenerations[i] = ((_t = state.containerItemGenerations[i]) != null ? _t : 0) + 1;
4727
4573
  }
4728
- state.containerItemTypes.delete(i);
4574
+ state.containerItemMetadata.delete(i);
4729
4575
  if (state.stickyContainerPool.has(i)) {
4730
4576
  set$(ctx, `containerSticky${i}`, false);
4731
4577
  state.stickyContainerPool.delete(i);
4732
4578
  }
4733
4579
  set$(ctx, `containerItemKey${i}`, void 0);
4580
+ set$(ctx, `containerItemIndex${i}`, void 0);
4734
4581
  set$(ctx, `containerItemData${i}`, void 0);
4735
- set$(ctx, `containerItemInfo${i}`, void 0);
4736
4582
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
4737
4583
  set$(ctx, `containerColumn${i}`, -1);
4738
4584
  set$(ctx, `containerSpan${i}`, 1);
@@ -4746,6 +4592,9 @@ function calculateItemsInView(ctx, params = {}) {
4746
4592
  }
4747
4593
  }
4748
4594
  }
4595
+ if (changedContainerIds && (IsNewArchitecture)) {
4596
+ scheduleContainerLayout(ctx, changedContainerIds);
4597
+ }
4749
4598
  if (didChangePositions) {
4750
4599
  set$(ctx, "lastPositionUpdate", Date.now());
4751
4600
  }
@@ -4762,20 +4611,18 @@ function calculateItemsInView(ctx, params = {}) {
4762
4611
  }
4763
4612
  }
4764
4613
  if (viewabilityConfigCallbackPairs && visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null) {
4765
- if (!didMVCPAdjustScroll) {
4766
- updateViewableItems(
4767
- ctx.state,
4768
- ctx,
4769
- viewabilityConfigCallbackPairs,
4770
- scrollLength,
4771
- visibleRange.startNoBuffer,
4772
- visibleRange.endNoBuffer,
4773
- startBuffered != null ? startBuffered : visibleRange.startNoBuffer,
4774
- endBuffered != null ? endBuffered : visibleRange.endNoBuffer
4775
- );
4776
- }
4614
+ updateViewableItems(
4615
+ ctx.state,
4616
+ ctx,
4617
+ viewabilityConfigCallbackPairs,
4618
+ scrollLength,
4619
+ visibleRange.startNoBuffer,
4620
+ visibleRange.endNoBuffer,
4621
+ startBuffered != null ? startBuffered : visibleRange.startNoBuffer,
4622
+ endBuffered != null ? endBuffered : visibleRange.endNoBuffer
4623
+ );
4777
4624
  }
4778
- (_t = stickyState == null ? void 0 : stickyState.finishCalculateItemsInView) == null ? void 0 : _t.call(stickyState);
4625
+ (_u = stickyState == null ? void 0 : stickyState.finishCalculateItemsInView) == null ? void 0 : _u.call(stickyState);
4779
4626
  });
4780
4627
  }
4781
4628
 
@@ -4867,228 +4714,516 @@ function updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal) {
4867
4714
  set$(ctx, "otherAxisSize", otherAxisSize);
4868
4715
  }
4869
4716
  }
4870
- }
4871
- function mergeItemSizeUpdateResult(result, next) {
4872
- result.didChange || (result.didChange = next.didChange);
4873
- result.didMeasureUserScrollAnchorResetItem || (result.didMeasureUserScrollAnchorResetItem = next.didMeasureUserScrollAnchorResetItem);
4874
- result.needsRecalculate || (result.needsRecalculate = next.needsRecalculate);
4875
- result.shouldMaintainScrollAtEnd || (result.shouldMaintainScrollAtEnd = next.shouldMaintainScrollAtEnd);
4876
- }
4877
- var batchedItemSizeRecalculates = /* @__PURE__ */ new WeakMap();
4878
- function flushBatchedItemSizeRecalculate(ctx, didFallback = false, expectedResult) {
4879
- const result = batchedItemSizeRecalculates.get(ctx);
4880
- if (!result || expectedResult && result !== expectedResult) {
4881
- return;
4882
- }
4883
- batchedItemSizeRecalculates.delete(ctx);
4884
- if (didFallback) {
4885
- ctx.state.pendingLayoutEffectMeasurements = void 0;
4717
+ }
4718
+ var activeItemSizeBatches;
4719
+ function batchItemSizeUpdates(runUpdates) {
4720
+ const isOuterBatch = activeItemSizeBatches === void 0;
4721
+ activeItemSizeBatches != null ? activeItemSizeBatches : activeItemSizeBatches = /* @__PURE__ */ new Map();
4722
+ try {
4723
+ runUpdates();
4724
+ } finally {
4725
+ if (isOuterBatch) {
4726
+ const batches = activeItemSizeBatches;
4727
+ activeItemSizeBatches = void 0;
4728
+ for (const [ctx, measurements] of batches) {
4729
+ updateItemSizesBatch(ctx, measurements);
4730
+ }
4731
+ }
4732
+ }
4733
+ }
4734
+ function mergeItemSizeUpdateResult(result, next) {
4735
+ result.didChange || (result.didChange = next.didChange);
4736
+ result.didMeasureUserScrollAnchorResetItem || (result.didMeasureUserScrollAnchorResetItem = next.didMeasureUserScrollAnchorResetItem);
4737
+ result.needsRecalculate || (result.needsRecalculate = next.needsRecalculate);
4738
+ result.shouldMaintainScrollAtEnd || (result.shouldMaintainScrollAtEnd = next.shouldMaintainScrollAtEnd);
4739
+ }
4740
+ function flushItemSizeUpdates(ctx, result) {
4741
+ var _a3;
4742
+ const state = ctx.state;
4743
+ if (result.needsRecalculate) {
4744
+ state.scrollForNextCalculateItemsInView = void 0;
4745
+ runOrScheduleMVCPRecalculate(ctx);
4746
+ } else if (result.didMeasureUserScrollAnchorResetItem && ((_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys.size) === 0) {
4747
+ state.userScrollAnchorReset = void 0;
4748
+ }
4749
+ if (result.didChange && result.shouldMaintainScrollAtEnd) {
4750
+ doMaintainScrollAtEnd(ctx);
4751
+ }
4752
+ }
4753
+ function updateItemSizes(ctx, measurement) {
4754
+ if (activeItemSizeBatches) {
4755
+ const measurements = activeItemSizeBatches.get(ctx);
4756
+ if (measurements) {
4757
+ measurements.push(measurement);
4758
+ } else {
4759
+ activeItemSizeBatches.set(ctx, [measurement]);
4760
+ }
4761
+ } else {
4762
+ updateItemSizesBatch(ctx, [measurement]);
4763
+ }
4764
+ }
4765
+ function updateItemSizesBatch(ctx, measurements) {
4766
+ var _a3;
4767
+ const state = ctx.state;
4768
+ const result = {};
4769
+ for (const measurement of measurements) {
4770
+ const ownsMeasuredItem = measurement.containerId === void 0 || peek$(ctx, `containerItemKey${measurement.containerId}`) === measurement.itemKey;
4771
+ if (ownsMeasuredItem) {
4772
+ const index = state.indexByKey.get(measurement.itemKey);
4773
+ const itemData = index === void 0 ? void 0 : (_a3 = state.props.data) == null ? void 0 : _a3[index];
4774
+ const metadata = measurement.containerId !== void 0 && index !== void 0 && itemData !== void 0 ? resolveContainerItemMetadata(state, measurement.containerId, index, itemData) : void 0;
4775
+ const nextResult = applyItemSize(ctx, measurement.itemKey, measurement.size, metadata);
4776
+ mergeItemSizeUpdateResult(result, nextResult);
4777
+ }
4778
+ }
4779
+ flushItemSizeUpdates(ctx, result);
4780
+ }
4781
+ function applyItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
4782
+ var _a3, _b;
4783
+ const state = ctx.state;
4784
+ const userScrollAnchorReset = state.userScrollAnchorReset;
4785
+ const didMeasureUserScrollAnchorResetItem = !!(userScrollAnchorReset == null ? void 0 : userScrollAnchorReset.keys.delete(itemKey));
4786
+ const {
4787
+ didContainersLayout,
4788
+ sizesKnown,
4789
+ props: { getFixedItemSize, getItemType, horizontal, onItemSizeChanged, data, maintainScrollAtEnd }
4790
+ } = state;
4791
+ if (!data) return { didMeasureUserScrollAnchorResetItem };
4792
+ const index = state.indexByKey.get(itemKey);
4793
+ if (getFixedItemSize) {
4794
+ if (index === void 0) {
4795
+ return { didMeasureUserScrollAnchorResetItem };
4796
+ }
4797
+ const itemData = state.props.data[index];
4798
+ if (itemData === void 0) {
4799
+ return { didMeasureUserScrollAnchorResetItem };
4800
+ }
4801
+ if (!(resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize)) {
4802
+ const type = (_b = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemType) != null ? _b : getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
4803
+ resolvedMeasurementItem = {
4804
+ didResolveFixedItemSize: true,
4805
+ fixedItemSize: getFixedItemSize(itemData, index, type),
4806
+ itemType: type
4807
+ };
4808
+ }
4809
+ const size2 = resolvedMeasurementItem.fixedItemSize;
4810
+ if (size2 !== void 0 && size2 === sizesKnown.get(itemKey)) {
4811
+ updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
4812
+ return { didMeasureUserScrollAnchorResetItem };
4813
+ }
4814
+ }
4815
+ let needsRecalculate = !didContainersLayout;
4816
+ let shouldMaintainScrollAtEnd = false;
4817
+ let minIndexSizeChanged;
4818
+ const prevSizeKnown = state.sizesKnown.get(itemKey);
4819
+ const diff = updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem);
4820
+ const size = roundSize(horizontal ? sizeObj.width : sizeObj.height);
4821
+ if (diff !== 0) {
4822
+ minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
4823
+ const { startBuffered, endBuffered } = state;
4824
+ needsRecalculate || (needsRecalculate = index >= startBuffered && index <= endBuffered);
4825
+ if (!needsRecalculate && state.containerItemKeys.has(itemKey)) {
4826
+ needsRecalculate = true;
4827
+ }
4828
+ if (prevSizeKnown !== void 0 && Math.abs(prevSizeKnown - size) > 5) {
4829
+ shouldMaintainScrollAtEnd = true;
4830
+ }
4831
+ onItemSizeChanged == null ? void 0 : onItemSizeChanged({
4832
+ index,
4833
+ itemData: state.props.data[index],
4834
+ itemKey,
4835
+ previous: size - diff,
4836
+ size
4837
+ });
4838
+ maybeUpdateAnchoredEndSpace(ctx);
4839
+ }
4840
+ if (minIndexSizeChanged !== void 0) {
4841
+ state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
4842
+ }
4843
+ updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
4844
+ if (didContainersLayout || checkAllSizesKnown(state, state.startBuffered, state.endBuffered)) {
4845
+ const canMaintainScrollAtEnd = shouldMaintainScrollAtEnd && !!(maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onItemLayout);
4846
+ return {
4847
+ didChange: diff !== 0,
4848
+ didMeasureUserScrollAnchorResetItem,
4849
+ needsRecalculate,
4850
+ shouldMaintainScrollAtEnd: canMaintainScrollAtEnd
4851
+ };
4852
+ }
4853
+ return {
4854
+ didChange: diff !== 0,
4855
+ didMeasureUserScrollAnchorResetItem
4856
+ };
4857
+ }
4858
+ function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
4859
+ var _a3, _b;
4860
+ const state = ctx.state;
4861
+ const {
4862
+ indexByKey,
4863
+ sizesKnown,
4864
+ averageSizes,
4865
+ props: { data, horizontal, getItemType, getFixedItemSize }
4866
+ } = state;
4867
+ if (!data) return 0;
4868
+ const index = indexByKey.get(itemKey);
4869
+ const itemData = data[index];
4870
+ let itemType = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemType;
4871
+ let fixedItemSize = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.fixedItemSize;
4872
+ if (getFixedItemSize && !(resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize)) {
4873
+ itemType = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
4874
+ fixedItemSize = getFixedItemSize(itemData, index, itemType);
4875
+ }
4876
+ const resolvedItemSize = (resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize) || itemType !== void 0 || fixedItemSize !== void 0 ? {
4877
+ didResolveFixedItemSize: resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize,
4878
+ fixedItemSize,
4879
+ itemType
4880
+ } : void 0;
4881
+ const prevSize = getItemSize(ctx, itemKey, index, itemData, void 0, void 0, void 0, resolvedItemSize);
4882
+ const rawSize = horizontal ? sizeObj.width : sizeObj.height;
4883
+ const prevSizeKnown = sizesKnown.get(itemKey);
4884
+ const size = Math.round(rawSize) ;
4885
+ sizesKnown.set(itemKey, size);
4886
+ if (fixedItemSize === void 0 && size > 0) {
4887
+ itemType != null ? itemType : itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
4888
+ let averages = averageSizes[itemType];
4889
+ if (!averages) {
4890
+ averages = averageSizes[itemType] = { avg: 0, num: 0 };
4891
+ }
4892
+ if (averages.num === 0) {
4893
+ averages.avg = size;
4894
+ averages.num++;
4895
+ } else if (prevSizeKnown !== void 0 && prevSizeKnown > 0) {
4896
+ averages.avg += (size - prevSizeKnown) / averages.num;
4897
+ } else {
4898
+ averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
4899
+ averages.num++;
4900
+ }
4901
+ }
4902
+ if (!prevSize || Math.abs(prevSize - size) > 0.1) {
4903
+ setSize(ctx, itemKey, size);
4904
+ return size - prevSize;
4886
4905
  }
4887
- flushItemSizeUpdates(ctx, result);
4906
+ return 0;
4888
4907
  }
4889
- function queueItemSizeRecalculate(ctx, result) {
4890
- var _a3, _b;
4891
- const batch = batchedItemSizeRecalculates.get(ctx);
4892
- if (batch) {
4893
- mergeItemSizeUpdateResult(batch, result);
4894
- } else {
4895
- const nextBatch = { ...result };
4896
- batchedItemSizeRecalculates.set(ctx, nextBatch);
4897
- if ((_a3 = ctx.state.pendingLayoutEffectMeasurements) == null ? void 0 : _a3.size) {
4898
- requestAnimationFrame(() => {
4899
- flushBatchedItemSizeRecalculate(ctx, true, nextBatch);
4908
+
4909
+ // src/core/measureContainersInLayoutEffect.ts
4910
+ function measureContainersInLayoutEffect(ctx, targetContainerIds = null) {
4911
+ const measurements = [];
4912
+ const containerIds = targetContainerIds != null ? targetContainerIds : ctx.viewRefs.keys();
4913
+ for (const containerId of containerIds) {
4914
+ const viewRef = ctx.viewRefs.get(containerId);
4915
+ const itemKey = peek$(ctx, `containerItemKey${containerId}`);
4916
+ const element = viewRef == null ? void 0 : viewRef.current;
4917
+ if (itemKey !== void 0 && element) {
4918
+ const rect = element.getBoundingClientRect();
4919
+ setContainerLayoutBaseline(element, rect);
4920
+ measurements.push({
4921
+ containerId,
4922
+ itemKey,
4923
+ size: { height: rect.height, width: rect.width }
4900
4924
  });
4901
4925
  }
4902
4926
  }
4903
- if (!((_b = ctx.state.pendingLayoutEffectMeasurements) == null ? void 0 : _b.size)) {
4904
- flushBatchedItemSizeRecalculate(ctx);
4905
- }
4906
- }
4907
- function flushItemSizeUpdates(ctx, result) {
4908
- var _a3;
4909
- const state = ctx.state;
4910
- if (result.needsRecalculate) {
4911
- state.scrollForNextCalculateItemsInView = void 0;
4912
- runOrScheduleMVCPRecalculate(ctx);
4913
- } else if (result.didMeasureUserScrollAnchorResetItem && ((_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys.size) === 0) {
4914
- state.userScrollAnchorReset = void 0;
4915
- }
4916
- if (result.didChange && result.shouldMaintainScrollAtEnd) {
4917
- doMaintainScrollAtEnd(ctx);
4927
+ if (measurements.length > 0) {
4928
+ updateItemSizesBatch(ctx, measurements);
4918
4929
  }
4919
4930
  }
4920
- function updateItemSizes(ctx, measurement) {
4921
- var _a3, _b, _c, _d;
4922
- const state = ctx.state;
4923
- let didDrainLayoutEffectMeasurements = false;
4924
- if (measurement.fromLayoutEffect) {
4925
- const pendingLayoutEffectMeasurements = state.pendingLayoutEffectMeasurements;
4926
- if ((pendingLayoutEffectMeasurements == null ? void 0 : pendingLayoutEffectMeasurements.delete(measurement.itemKey)) && pendingLayoutEffectMeasurements.size === 0) {
4927
- state.pendingLayoutEffectMeasurements = void 0;
4928
- didDrainLayoutEffectMeasurements = true;
4929
- }
4930
- }
4931
- const pendingKeys = (_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys;
4932
- const shouldBatchPendingMeasurements = measurement.fromLayoutEffect && !!(pendingKeys == null ? void 0 : pendingKeys.size);
4933
- const shouldQueueRecalculate = !!measurement.fromLayoutEffect;
4934
- let result;
4935
- if (!shouldBatchPendingMeasurements) {
4936
- result = applyItemSize(ctx, measurement.itemKey, measurement.size);
4937
- } else {
4938
- result = {};
4939
- const updateContainerItemSize = (itemKey, containerId, size) => {
4940
- if (containerId !== void 0 && peek$(ctx, `containerItemKey${containerId}`) === itemKey) {
4941
- mergeItemSizeUpdateResult(result, applyItemSize(ctx, itemKey, size));
4942
- }
4943
- };
4944
- updateContainerItemSize(measurement.itemKey, measurement.containerId, measurement.size);
4945
- const keys = Array.from(pendingKeys);
4946
- for (const itemKey of keys) {
4947
- const containerId = state.containerItemKeys.get(itemKey);
4948
- if (containerId !== void 0) {
4949
- (_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) => {
4950
- if (pendingKeys.has(itemKey)) {
4951
- updateContainerItemSize(itemKey, containerId, { height, width });
4952
- }
4953
- });
4931
+ var typedForwardRef = React3__namespace.forwardRef;
4932
+ var typedMemo = React3__namespace.memo;
4933
+
4934
+ // src/components/ContainerLayoutCoordinator.tsx
4935
+ var ContainerLayoutCoordinator = typedMemo(function ContainerLayoutCoordinatorComponent({
4936
+ children
4937
+ }) {
4938
+ const ctx = useStateContext();
4939
+ const [containerLayoutEpoch] = useArr$(["containerLayoutEpoch"]);
4940
+ React3__namespace.useLayoutEffect(() => {
4941
+ {
4942
+ const targetContainerIds = getContainerLayoutEffectScope(ctx);
4943
+ if (targetContainerIds !== void 0) {
4944
+ measureContainersInLayoutEffect(ctx, targetContainerIds);
4954
4945
  }
4955
4946
  }
4947
+ }, [ctx, containerLayoutEpoch]);
4948
+ return children;
4949
+ });
4950
+ var getComponent = (Component) => {
4951
+ if (React3__namespace.isValidElement(Component)) {
4952
+ return Component;
4956
4953
  }
4957
- if (shouldQueueRecalculate && result.needsRecalculate) {
4958
- queueItemSizeRecalculate(ctx, result);
4959
- } else {
4960
- flushItemSizeUpdates(ctx, result);
4961
- }
4962
- if (didDrainLayoutEffectMeasurements) {
4963
- flushBatchedItemSizeRecalculate(ctx);
4954
+ if (Component) {
4955
+ return /* @__PURE__ */ React3__namespace.createElement(Component, null);
4964
4956
  }
4957
+ return null;
4958
+ };
4959
+
4960
+ // src/components/PositionView.tsx
4961
+ var isRNWeb = typeof document !== "undefined" && !!document.getElementById("react-native-stylesheet");
4962
+ var baseCss = {
4963
+ contain: "paint layout style",
4964
+ ...isRNWeb ? {
4965
+ display: "flex",
4966
+ flexDirection: "column"
4967
+ } : {}
4968
+ };
4969
+ var PositionViewState = typedMemo(function PositionViewState2({
4970
+ id,
4971
+ horizontal,
4972
+ style,
4973
+ refView,
4974
+ ...props
4975
+ }) {
4976
+ const [position = POSITION_OUT_OF_VIEW] = useArr$([`containerPosition${id}`]);
4977
+ const composed = isArray(style) ? Object.assign({}, ...style) : style;
4978
+ const combinedStyle = horizontal ? { ...baseCss, ...composed, left: position } : { ...baseCss, ...composed, top: position };
4979
+ const {
4980
+ animatedScrollY: _animatedScrollY,
4981
+ onLayout: _onLayout,
4982
+ onLayoutChange: _onLayoutChange,
4983
+ stickyHeaderConfig: _stickyHeaderConfig,
4984
+ ...webProps
4985
+ } = props;
4986
+ return /* @__PURE__ */ React3__namespace.createElement("div", { ref: refView, ...webProps, style: combinedStyle });
4987
+ });
4988
+ var PositionViewSticky = typedMemo(function PositionViewSticky2({
4989
+ id,
4990
+ horizontal,
4991
+ style,
4992
+ refView,
4993
+ animatedScrollY: _animatedScrollY,
4994
+ stickyHeaderConfig,
4995
+ onLayout: _onLayout,
4996
+ onLayoutChange: _onLayoutChange,
4997
+ children,
4998
+ ...webProps
4999
+ }) {
5000
+ const [position = POSITION_OUT_OF_VIEW, activeStickyIndex, itemIndex] = useArr$([
5001
+ `containerPosition${id}`,
5002
+ "activeStickyIndex",
5003
+ `containerItemIndex${id}`
5004
+ ]);
5005
+ const composed = React3__namespace.useMemo(
5006
+ () => {
5007
+ var _a3;
5008
+ return (_a3 = isArray(style) ? Object.assign({}, ...style) : style) != null ? _a3 : {};
5009
+ },
5010
+ [style]
5011
+ );
5012
+ const viewStyle = React3__namespace.useMemo(() => {
5013
+ var _a3;
5014
+ const styleBase = { ...baseCss, ...composed };
5015
+ delete styleBase.transform;
5016
+ const offset = (_a3 = stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset) != null ? _a3 : 0;
5017
+ const isActive = activeStickyIndex === itemIndex;
5018
+ styleBase.position = isActive ? "sticky" : "absolute";
5019
+ styleBase.zIndex = itemIndex + 1e3;
5020
+ if (horizontal) {
5021
+ styleBase.left = isActive ? offset : position;
5022
+ } else {
5023
+ styleBase.top = isActive ? offset : position;
5024
+ }
5025
+ return styleBase;
5026
+ }, [composed, horizontal, position, itemIndex, activeStickyIndex, stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset]);
5027
+ const renderStickyHeaderBackdrop = React3__namespace.useMemo(
5028
+ () => (stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent) ? /* @__PURE__ */ React3__namespace.createElement(
5029
+ "div",
5030
+ {
5031
+ style: {
5032
+ inset: 0,
5033
+ pointerEvents: "none",
5034
+ position: "absolute"
5035
+ }
5036
+ },
5037
+ getComponent(stickyHeaderConfig.backdropComponent)
5038
+ ) : null,
5039
+ [stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent]
5040
+ );
5041
+ return /* @__PURE__ */ React3__namespace.createElement("div", { ref: refView, style: viewStyle, ...webProps }, renderStickyHeaderBackdrop, children);
5042
+ });
5043
+ var PositionView = PositionViewState;
5044
+ function useInit(cb) {
5045
+ React3.useState(() => cb());
4965
5046
  }
4966
- function applyItemSize(ctx, itemKey, sizeObj) {
5047
+
5048
+ // src/state/ContextContainer.ts
5049
+ var ContextContainer = React3.createContext(null);
5050
+ var NO_CONTAINER_ID = -1;
5051
+ function useContextContainer() {
5052
+ return React3.useContext(ContextContainer);
5053
+ }
5054
+ function useContainerItemSignals(containerContext) {
4967
5055
  var _a3;
4968
- const state = ctx.state;
4969
- const userScrollAnchorReset = state.userScrollAnchorReset;
4970
- const didMeasureUserScrollAnchorResetItem = !!(userScrollAnchorReset == null ? void 0 : userScrollAnchorReset.keys.delete(itemKey));
4971
- const {
4972
- didContainersLayout,
4973
- sizesKnown,
4974
- props: { getFixedItemSize, getItemType, horizontal, onItemSizeChanged, data, maintainScrollAtEnd }
4975
- } = state;
4976
- if (!data) return { didMeasureUserScrollAnchorResetItem };
4977
- const index = state.indexByKey.get(itemKey);
4978
- let resolvedMeasurementItem;
4979
- if (getFixedItemSize) {
4980
- if (index === void 0) {
4981
- return { didMeasureUserScrollAnchorResetItem };
5056
+ const containerId = (_a3 = containerContext == null ? void 0 : containerContext.containerId) != null ? _a3 : NO_CONTAINER_ID;
5057
+ const [itemKey, itemIndex, item] = useArr$([
5058
+ `containerItemKey${containerId}`,
5059
+ `containerItemIndex${containerId}`,
5060
+ `containerItemData${containerId}`
5061
+ ]);
5062
+ return {
5063
+ hasItemInfo: !!containerContext && itemKey !== void 0 && itemIndex !== void 0,
5064
+ item,
5065
+ itemIndex,
5066
+ itemKey
5067
+ };
5068
+ }
5069
+ function useAdaptiveRender() {
5070
+ const [mode] = useArr$(["adaptiveRender"]);
5071
+ return mode;
5072
+ }
5073
+ function useAdaptiveRenderChange(callback) {
5074
+ const ctx = useStateContext();
5075
+ const callbackRef = React3.useRef(callback);
5076
+ callbackRef.current = callback;
5077
+ React3.useLayoutEffect(() => {
5078
+ let mode = peek$(ctx, "adaptiveRender");
5079
+ return listen$(ctx, "adaptiveRender", (nextMode) => {
5080
+ if (mode !== nextMode) {
5081
+ mode = nextMode;
5082
+ callbackRef.current(nextMode);
5083
+ }
5084
+ });
5085
+ }, [ctx]);
5086
+ }
5087
+ function useViewability(callback, configId) {
5088
+ const ctx = useStateContext();
5089
+ const containerContext = useContextContainer();
5090
+ useInit(() => {
5091
+ if (!containerContext) {
5092
+ return;
4982
5093
  }
4983
- const itemData = state.props.data[index];
4984
- if (itemData === void 0) {
4985
- return { didMeasureUserScrollAnchorResetItem };
5094
+ const { containerId } = containerContext;
5095
+ const key = containerId + (configId != null ? configId : "");
5096
+ const value = ctx.mapViewabilityValues.get(key);
5097
+ if (value) {
5098
+ callback(value);
5099
+ }
5100
+ });
5101
+ React3.useEffect(() => {
5102
+ if (!containerContext) {
5103
+ return;
4986
5104
  }
4987
- const type = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
4988
- const size2 = getFixedItemSize(itemData, index, type);
4989
- resolvedMeasurementItem = {
4990
- didResolveFixedItemSize: true,
4991
- fixedItemSize: size2,
4992
- itemData,
4993
- itemType: type
5105
+ const { containerId } = containerContext;
5106
+ const key = containerId + (configId != null ? configId : "");
5107
+ ctx.mapViewabilityCallbacks.set(key, callback);
5108
+ return () => {
5109
+ ctx.mapViewabilityCallbacks.delete(key);
4994
5110
  };
4995
- if (size2 !== void 0 && size2 === sizesKnown.get(itemKey)) {
4996
- updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
4997
- return { didMeasureUserScrollAnchorResetItem };
5111
+ }, [ctx, callback, configId, containerContext]);
5112
+ }
5113
+ function useViewabilityAmount(callback) {
5114
+ const ctx = useStateContext();
5115
+ const containerContext = useContextContainer();
5116
+ useInit(() => {
5117
+ if (!containerContext) {
5118
+ return;
4998
5119
  }
4999
- }
5000
- let needsRecalculate = !didContainersLayout;
5001
- let shouldMaintainScrollAtEnd = false;
5002
- let minIndexSizeChanged;
5003
- const prevSizeKnown = state.sizesKnown.get(itemKey);
5004
- const diff = updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem);
5005
- const size = roundSize(horizontal ? sizeObj.width : sizeObj.height);
5006
- if (diff !== 0) {
5007
- minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
5008
- const { startBuffered, endBuffered } = state;
5009
- needsRecalculate || (needsRecalculate = index >= startBuffered && index <= endBuffered);
5010
- if (!needsRecalculate && state.containerItemKeys.has(itemKey)) {
5011
- needsRecalculate = true;
5120
+ const { containerId } = containerContext;
5121
+ const value = ctx.mapViewabilityAmountValues.get(containerId);
5122
+ if (value) {
5123
+ callback(value);
5124
+ }
5125
+ });
5126
+ React3.useEffect(() => {
5127
+ if (!containerContext) {
5128
+ return;
5129
+ }
5130
+ const { containerId } = containerContext;
5131
+ ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
5132
+ return () => {
5133
+ ctx.mapViewabilityAmountCallbacks.delete(containerId);
5134
+ };
5135
+ }, [ctx, callback, containerContext]);
5136
+ }
5137
+ function useRecyclingEffect(effect) {
5138
+ const containerContext = useContextContainer();
5139
+ const { hasItemInfo, item, itemIndex, itemKey } = useContainerItemSignals(containerContext);
5140
+ const prevInfo = React3.useRef(void 0);
5141
+ React3.useEffect(() => {
5142
+ if (!hasItemInfo) {
5143
+ return;
5012
5144
  }
5013
- if (prevSizeKnown !== void 0 && Math.abs(prevSizeKnown - size) > 5) {
5014
- shouldMaintainScrollAtEnd = true;
5145
+ let ret;
5146
+ if (prevInfo.current) {
5147
+ ret = effect({
5148
+ index: itemIndex,
5149
+ item,
5150
+ prevIndex: prevInfo.current.index,
5151
+ prevItem: prevInfo.current.item
5152
+ });
5015
5153
  }
5016
- onItemSizeChanged == null ? void 0 : onItemSizeChanged({
5017
- index,
5018
- itemData: state.props.data[index],
5019
- itemKey,
5020
- previous: size - diff,
5021
- size
5022
- });
5023
- maybeUpdateAnchoredEndSpace(ctx);
5024
- }
5025
- if (minIndexSizeChanged !== void 0) {
5026
- state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
5027
- }
5028
- updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
5029
- if (didContainersLayout || checkAllSizesKnown(state, state.startBuffered, state.endBuffered)) {
5030
- const canMaintainScrollAtEnd = shouldMaintainScrollAtEnd && !!(maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onItemLayout);
5031
- return {
5032
- didChange: diff !== 0,
5033
- didMeasureUserScrollAnchorResetItem,
5034
- needsRecalculate,
5035
- shouldMaintainScrollAtEnd: canMaintainScrollAtEnd
5154
+ prevInfo.current = {
5155
+ index: itemIndex,
5156
+ item
5036
5157
  };
5037
- }
5038
- return {
5039
- didChange: diff !== 0,
5040
- didMeasureUserScrollAnchorResetItem
5041
- };
5158
+ return ret;
5159
+ }, [effect, hasItemInfo, itemIndex, item, itemKey]);
5042
5160
  }
5043
- function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
5044
- var _a3, _b, _c;
5045
- const state = ctx.state;
5046
- const {
5047
- indexByKey,
5048
- sizesKnown,
5049
- averageSizes,
5050
- props: { data, horizontal, getItemType, getFixedItemSize }
5051
- } = state;
5052
- if (!data) return 0;
5053
- const index = indexByKey.get(itemKey);
5054
- const itemData = (_a3 = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemData) != null ? _a3 : data[index];
5055
- let itemType = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemType;
5056
- let fixedItemSize = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.fixedItemSize;
5057
- if (getFixedItemSize && !(resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize)) {
5058
- itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
5059
- fixedItemSize = getFixedItemSize(itemData, index, itemType);
5060
- }
5061
- const resolvedItemSize = (resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize) || itemType !== void 0 || fixedItemSize !== void 0 ? {
5062
- didResolveFixedItemSize: resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize,
5063
- fixedItemSize,
5064
- itemType
5065
- } : void 0;
5066
- const prevSize = getItemSize(ctx, itemKey, index, itemData, void 0, void 0, void 0, resolvedItemSize);
5067
- const rawSize = horizontal ? sizeObj.width : sizeObj.height;
5068
- const prevSizeKnown = sizesKnown.get(itemKey);
5069
- const size = Math.round(rawSize) ;
5070
- sizesKnown.set(itemKey, size);
5071
- if (fixedItemSize === void 0 && size > 0) {
5072
- itemType != null ? itemType : itemType = getItemType ? (_c = getItemType(itemData, index)) != null ? _c : "" : "";
5073
- let averages = averageSizes[itemType];
5074
- if (!averages) {
5075
- averages = averageSizes[itemType] = { avg: 0, num: 0 };
5076
- }
5077
- if (averages.num === 0) {
5078
- averages.avg = size;
5079
- averages.num++;
5080
- } else if (prevSizeKnown !== void 0 && prevSizeKnown > 0) {
5081
- averages.avg += (size - prevSizeKnown) / averages.num;
5082
- } else {
5083
- averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
5084
- averages.num++;
5161
+ function useRecyclingState(valueOrFun) {
5162
+ const containerContext = useContextContainer();
5163
+ const { hasItemInfo, item, itemIndex, itemKey } = useContainerItemSignals(containerContext);
5164
+ const computeValue = () => {
5165
+ if (isFunction(valueOrFun)) {
5166
+ const initializer = valueOrFun;
5167
+ return hasItemInfo ? initializer({
5168
+ index: itemIndex,
5169
+ item,
5170
+ prevIndex: void 0,
5171
+ prevItem: void 0
5172
+ }) : initializer();
5085
5173
  }
5174
+ return valueOrFun;
5175
+ };
5176
+ const [stateValue, setStateValue] = React3.useState(() => {
5177
+ return computeValue();
5178
+ });
5179
+ const prevItemKeyRef = React3.useRef(hasItemInfo ? itemKey : null);
5180
+ if (hasItemInfo && prevItemKeyRef.current !== itemKey) {
5181
+ prevItemKeyRef.current = itemKey;
5182
+ setStateValue(computeValue());
5086
5183
  }
5087
- if (!prevSize || Math.abs(prevSize - size) > 0.1) {
5088
- setSize(ctx, itemKey, size);
5089
- return size - prevSize;
5090
- }
5091
- return 0;
5184
+ const triggerLayout = containerContext == null ? void 0 : containerContext.triggerLayout;
5185
+ const setState = React3.useCallback(
5186
+ (newState) => {
5187
+ if (!triggerLayout) {
5188
+ return;
5189
+ }
5190
+ setStateValue((prevValue) => {
5191
+ return isFunction(newState) ? newState(prevValue) : newState;
5192
+ });
5193
+ triggerLayout();
5194
+ },
5195
+ [triggerLayout]
5196
+ );
5197
+ return [stateValue, setState];
5198
+ }
5199
+ function useIsLastItem() {
5200
+ var _a3;
5201
+ const containerContext = useContextContainer();
5202
+ const containerId = (_a3 = containerContext == null ? void 0 : containerContext.containerId) != null ? _a3 : NO_CONTAINER_ID;
5203
+ const [itemKey] = useArr$([`containerItemKey${containerId}`]);
5204
+ const isLast = useSelector$("lastItemKeys", (lastItemKeys) => {
5205
+ if (containerContext && !isNullOrUndefined(itemKey)) {
5206
+ return (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false;
5207
+ }
5208
+ return false;
5209
+ });
5210
+ return isLast;
5211
+ }
5212
+ function useListScrollSize() {
5213
+ const [scrollSize] = useArr$(["scrollSize"]);
5214
+ return scrollSize;
5215
+ }
5216
+ var noop = () => {
5217
+ };
5218
+ function useSyncLayout() {
5219
+ const containerContext = useContextContainer();
5220
+ return containerContext ? containerContext.triggerLayout : noop;
5221
+ }
5222
+
5223
+ // src/components/Separator.tsx
5224
+ function Separator({ ItemSeparatorComponent, leadingItem }) {
5225
+ const isLastItem = useIsLastItem();
5226
+ return isLastItem ? null : /* @__PURE__ */ React3__namespace.createElement(ItemSeparatorComponent, { leadingItem });
5092
5227
  }
5093
5228
 
5094
5229
  // src/hooks/createResizeObserver.ts
@@ -5096,43 +5231,39 @@ var globalResizeObserver = null;
5096
5231
  function getGlobalResizeObserver() {
5097
5232
  if (!globalResizeObserver) {
5098
5233
  globalResizeObserver = new ResizeObserver((entries) => {
5099
- for (const entry of entries) {
5100
- const callbacks = callbackMap.get(entry.target);
5101
- if (callbacks) {
5102
- for (const callback of callbacks) {
5103
- callback(entry);
5234
+ batchItemSizeUpdates(() => {
5235
+ for (const entry of entries) {
5236
+ const callbacks = callbackMap.get(entry.target);
5237
+ if (callbacks) {
5238
+ for (const callback of callbacks) {
5239
+ callback(entry);
5240
+ }
5104
5241
  }
5105
5242
  }
5106
- }
5243
+ });
5107
5244
  });
5108
5245
  }
5109
5246
  return globalResizeObserver;
5110
5247
  }
5111
5248
  var callbackMap = /* @__PURE__ */ new WeakMap();
5112
5249
  function createResizeObserver(element, callback) {
5113
- if (typeof ResizeObserver === "undefined") {
5114
- return () => {
5115
- };
5116
- }
5117
- if (!element) {
5250
+ var _a3;
5251
+ if (typeof ResizeObserver === "undefined" || !element) {
5118
5252
  return () => {
5119
5253
  };
5120
5254
  }
5121
5255
  const observer = getGlobalResizeObserver();
5122
- let callbacks = callbackMap.get(element);
5123
- if (!callbacks) {
5124
- callbacks = /* @__PURE__ */ new Set();
5256
+ const callbacks = (_a3 = callbackMap.get(element)) != null ? _a3 : /* @__PURE__ */ new Set();
5257
+ if (callbacks.size === 0) {
5125
5258
  callbackMap.set(element, callbacks);
5126
- observer.observe(element);
5259
+ observer.observe(element, { box: "border-box" });
5127
5260
  }
5128
5261
  callbacks.add(callback);
5129
5262
  return () => {
5130
- if (callbacks) {
5131
- callbacks.delete(callback);
5132
- if (callbacks.size === 0) {
5133
- callbackMap.delete(element);
5134
- observer.unobserve(element);
5135
- }
5263
+ callbacks.delete(callback);
5264
+ if (callbacks.size === 0) {
5265
+ callbackMap.delete(element);
5266
+ observer.unobserve(element);
5136
5267
  }
5137
5268
  };
5138
5269
  }
@@ -5140,6 +5271,7 @@ function createResizeObserver(element, callback) {
5140
5271
  // src/hooks/useOnLayoutSync.tsx
5141
5272
  function useOnLayoutSync({
5142
5273
  ref,
5274
+ measureInLayoutEffect = true,
5143
5275
  onLayoutProp,
5144
5276
  onLayoutChange,
5145
5277
  webLayoutResync
@@ -5159,21 +5291,31 @@ function useOnLayoutSync({
5159
5291
  onLayoutChange(layout, fromLayoutEffect);
5160
5292
  onLayoutProp == null ? void 0 : onLayoutProp({ nativeEvent: { layout } });
5161
5293
  };
5162
- const rect = element.getBoundingClientRect();
5163
- emit(toLayout(rect), true);
5164
- let prevRect = rect;
5294
+ let prevRect;
5295
+ if (measureInLayoutEffect) {
5296
+ const rect = element.getBoundingClientRect();
5297
+ emit(toLayout(rect), true);
5298
+ prevRect = rect;
5299
+ }
5165
5300
  return createResizeObserver(element, (entry) => {
5166
5301
  var _a4;
5167
5302
  const target = entry.target instanceof HTMLElement ? entry.target : void 0;
5168
- const rectObserved = (_a4 = entry.contentRect) != null ? _a4 : target == null ? void 0 : target.getBoundingClientRect();
5169
- const didSizeChange = rectObserved.width !== prevRect.width || rectObserved.height !== prevRect.height;
5303
+ const borderBoxSize = Array.isArray(entry.borderBoxSize) ? entry.borderBoxSize[0] : entry.borderBoxSize;
5304
+ const rectObserved = borderBoxSize ? {
5305
+ height: borderBoxSize.blockSize,
5306
+ left: entry.contentRect.left,
5307
+ top: entry.contentRect.top,
5308
+ width: borderBoxSize.inlineSize
5309
+ } : (_a4 = target == null ? void 0 : target.getBoundingClientRect()) != null ? _a4 : entry.contentRect;
5310
+ const previousRect = prevRect != null ? prevRect : getContainerLayoutBaseline(element);
5311
+ const didSizeChange = previousRect === void 0 || rectObserved.width !== previousRect.width || rectObserved.height !== previousRect.height;
5312
+ prevRect = rectObserved;
5170
5313
  const shouldResyncLayout = !!(webLayoutResync == null ? void 0 : webLayoutResync());
5171
5314
  if (didSizeChange || shouldResyncLayout) {
5172
- prevRect = rectObserved;
5173
5315
  emit(toLayout(rectObserved), false);
5174
5316
  }
5175
5317
  });
5176
- }, deps || []);
5318
+ }, [measureInLayoutEffect, ...deps || []]);
5177
5319
  return {};
5178
5320
  }
5179
5321
  function toLayout(rect) {
@@ -5188,6 +5330,121 @@ function toLayout(rect) {
5188
5330
  };
5189
5331
  }
5190
5332
 
5333
+ // src/hooks/useContainerMeasurement.tsx
5334
+ var pendingWebShrinkMeasurements = /* @__PURE__ */ new Map();
5335
+ var pendingWebShrinkFrame;
5336
+ function cancelWebShrinkMeasurement(state) {
5337
+ pendingWebShrinkMeasurements.delete(state);
5338
+ }
5339
+ function scheduleWebShrinkMeasurement(state, confirmMeasurement) {
5340
+ pendingWebShrinkMeasurements.set(state, confirmMeasurement);
5341
+ if (pendingWebShrinkFrame === void 0) {
5342
+ pendingWebShrinkFrame = requestAnimationFrame(() => {
5343
+ const callbacks = Array.from(pendingWebShrinkMeasurements.values());
5344
+ pendingWebShrinkMeasurements.clear();
5345
+ pendingWebShrinkFrame = void 0;
5346
+ batchItemSizeUpdates(() => {
5347
+ for (const callback of callbacks) {
5348
+ callback();
5349
+ }
5350
+ });
5351
+ });
5352
+ }
5353
+ }
5354
+ function processContainerLayout({ containerId, ctx, rectangle, ref, state }) {
5355
+ const listState = ctx.state;
5356
+ const currentItemKey = state.itemKey;
5357
+ state.didLayout = true;
5358
+ let layout = rectangle;
5359
+ const axis = state.horizontal ? "width" : "height";
5360
+ const size = roundSize(rectangle[axis]);
5361
+ state.lastSize ? roundSize(state.lastSize[axis]) : void 0;
5362
+ const coreKnownSize = listState.sizesKnown.get(currentItemKey);
5363
+ const previousSize = coreKnownSize ;
5364
+ const applyLayout = () => {
5365
+ state.lastSize = layout;
5366
+ updateItemSizes(ctx, {
5367
+ containerId,
5368
+ itemKey: currentItemKey,
5369
+ size: layout
5370
+ });
5371
+ };
5372
+ const shouldDeferWebShrinkLayoutUpdate = !isInMVCPActiveMode(listState) && previousSize !== void 0 && size + 1 < previousSize;
5373
+ if (shouldDeferWebShrinkLayoutUpdate) {
5374
+ scheduleWebShrinkMeasurement(state, () => {
5375
+ var _a4;
5376
+ if (state.itemKey === currentItemKey) {
5377
+ const element = ref.current;
5378
+ const rect = (_a4 = element == null ? void 0 : element.getBoundingClientRect) == null ? void 0 : _a4.call(element);
5379
+ if (rect) {
5380
+ layout = { height: rect.height, width: rect.width };
5381
+ }
5382
+ applyLayout();
5383
+ }
5384
+ });
5385
+ } else {
5386
+ {
5387
+ cancelWebShrinkMeasurement(state);
5388
+ }
5389
+ {
5390
+ applyLayout();
5391
+ }
5392
+ }
5393
+ }
5394
+ function useContainerMeasurement({
5395
+ containerId,
5396
+ ctx,
5397
+ horizontal,
5398
+ itemKey,
5399
+ ref
5400
+ }) {
5401
+ const stateRef = React3.useRef({
5402
+ didLayout: false,
5403
+ horizontal,
5404
+ itemKey
5405
+ });
5406
+ stateRef.current.horizontal = horizontal;
5407
+ stateRef.current.itemKey = itemKey;
5408
+ const [layoutRenderCount, forceLayoutRender] = React3.useState(0);
5409
+ const onLayoutChange = React3.useCallback(
5410
+ (rectangle) => {
5411
+ processContainerLayout({ containerId, ctx, rectangle, ref, state: stateRef.current });
5412
+ },
5413
+ [containerId, ctx, ref]
5414
+ );
5415
+ const triggerLayout = React3.useCallback(() => {
5416
+ {
5417
+ scheduleContainerLayout(ctx, containerId);
5418
+ }
5419
+ }, [containerId, ctx]);
5420
+ React3.useLayoutEffect(() => {
5421
+ ctx.containerLayoutTriggers.set(containerId, triggerLayout);
5422
+ return () => {
5423
+ cancelWebShrinkMeasurement(stateRef.current);
5424
+ if (ctx.containerLayoutTriggers.get(containerId) === triggerLayout) {
5425
+ ctx.containerLayoutTriggers.delete(containerId);
5426
+ }
5427
+ };
5428
+ }, [containerId, ctx, triggerLayout]);
5429
+ React3.useLayoutEffect(() => {
5430
+ {
5431
+ scheduleContainerLayout(ctx, containerId);
5432
+ }
5433
+ });
5434
+ const { onLayout } = useOnLayoutSync(
5435
+ {
5436
+ measureInLayoutEffect: !IsNewArchitecture,
5437
+ onLayoutChange,
5438
+ ref,
5439
+ webLayoutResync: () => isInMVCPActiveMode(ctx.state)
5440
+ },
5441
+ [itemKey, layoutRenderCount]
5442
+ );
5443
+ React3.useEffect(() => {
5444
+ }, [containerId, ctx, itemKey]);
5445
+ return { onLayout, triggerLayout };
5446
+ }
5447
+
5191
5448
  // src/components/Container.tsx
5192
5449
  function getContainerPositionStyle({
5193
5450
  columnWrapperStyle,
@@ -5258,16 +5515,14 @@ var Container = typedMemo(function Container2({
5258
5515
  "extraData",
5259
5516
  `containerSticky${id}`
5260
5517
  ]);
5261
- const itemLayoutRef = React3.useRef({
5262
- didLayout: false,
5518
+ const ref = React3.useRef(null);
5519
+ const { onLayout, triggerLayout } = useContainerMeasurement({
5520
+ containerId: id,
5521
+ ctx,
5263
5522
  horizontal,
5264
5523
  itemKey,
5265
- pendingShrinkToken: 0
5524
+ ref
5266
5525
  });
5267
- itemLayoutRef.current.horizontal = horizontal;
5268
- itemLayoutRef.current.itemKey = itemKey;
5269
- const ref = React3.useRef(null);
5270
- const [layoutRenderCount, forceLayoutRender] = React3.useState(0);
5271
5526
  const resolvedColumn = column > 0 ? column : 1;
5272
5527
  const resolvedSpan = Math.min(Math.max(span || 1, 1), numColumns);
5273
5528
  const otherAxisPos = numColumns > 1 ? `${(resolvedColumn - 1) / numColumns * 100}%` : 0;
@@ -5298,57 +5553,7 @@ var Container = typedMemo(function Container2({
5298
5553
  () => itemKey !== void 0 ? getRenderedItem2(itemKey) : null,
5299
5554
  [itemKey, data, extraData]
5300
5555
  );
5301
- const { index, renderedItem } = renderedItemInfo || {};
5302
- const onLayoutChange = React3.useCallback((rectangle, fromLayoutEffect) => {
5303
- const {
5304
- horizontal: currentHorizontal,
5305
- itemKey: currentItemKey,
5306
- lastSize,
5307
- pendingShrinkToken
5308
- } = itemLayoutRef.current;
5309
- if (isNullOrUndefined(currentItemKey)) {
5310
- return;
5311
- }
5312
- itemLayoutRef.current.didLayout = true;
5313
- let layout = rectangle;
5314
- const axis = currentHorizontal ? "width" : "height";
5315
- const size = roundSize(rectangle[axis]);
5316
- const prevSize = lastSize ? roundSize(lastSize[axis]) : void 0;
5317
- const doUpdate = () => {
5318
- itemLayoutRef.current.lastSize = layout;
5319
- updateItemSizes(ctx, {
5320
- containerId: id,
5321
- fromLayoutEffect,
5322
- itemKey: currentItemKey,
5323
- size: layout
5324
- });
5325
- itemLayoutRef.current.didLayout = true;
5326
- };
5327
- const shouldDeferWebShrinkLayoutUpdate = !isInMVCPActiveMode(ctx.state) && prevSize !== void 0 && size + 1 < prevSize;
5328
- if (shouldDeferWebShrinkLayoutUpdate) {
5329
- const token = pendingShrinkToken + 1;
5330
- itemLayoutRef.current.pendingShrinkToken = token;
5331
- requestAnimationFrame(() => {
5332
- var _a4;
5333
- if (itemLayoutRef.current.pendingShrinkToken !== token) {
5334
- return;
5335
- }
5336
- const element = ref.current;
5337
- const rect = (_a4 = element == null ? void 0 : element.getBoundingClientRect) == null ? void 0 : _a4.call(element);
5338
- if (rect) {
5339
- layout = { height: rect.height, width: rect.width };
5340
- }
5341
- doUpdate();
5342
- });
5343
- return;
5344
- }
5345
- {
5346
- doUpdate();
5347
- }
5348
- }, []);
5349
- const triggerLayout = React3.useCallback(() => {
5350
- forceLayoutRender((v) => v + 1);
5351
- }, []);
5556
+ const { renderedItem } = renderedItemInfo || {};
5352
5557
  const contextValue = React3.useMemo(() => {
5353
5558
  ctx.viewRefs.set(id, ref);
5354
5559
  return {
@@ -5356,22 +5561,6 @@ var Container = typedMemo(function Container2({
5356
5561
  triggerLayout
5357
5562
  };
5358
5563
  }, [id, triggerLayout]);
5359
- React3.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
- webLayoutResync: () => isInMVCPActiveMode(ctx.state)
5372
- },
5373
- [itemKey, layoutRenderCount]
5374
- );
5375
5564
  const PositionComponent = isSticky ? stickyPositionComponentInternal ? stickyPositionComponentInternal : PositionViewSticky : positionComponentInternal ? positionComponentInternal : PositionView;
5376
5565
  return /* @__PURE__ */ React3__namespace.createElement(
5377
5566
  PositionComponent,
@@ -5379,7 +5568,6 @@ var Container = typedMemo(function Container2({
5379
5568
  animatedScrollY: isSticky ? animatedScrollY : void 0,
5380
5569
  horizontal,
5381
5570
  id,
5382
- index,
5383
5571
  key: recycleItems ? void 0 : itemKey,
5384
5572
  onLayout,
5385
5573
  refView: ref,
@@ -5422,18 +5610,13 @@ var ContainerSlot = typedMemo(function ContainerSlot2(props) {
5422
5610
  });
5423
5611
 
5424
5612
  // src/utils/reordering.ts
5425
- var mapFn = (element) => {
5426
- const indexStr = element.getAttribute("data-index");
5427
- if (indexStr === null) {
5428
- return [element, null];
5429
- }
5430
- const index = Number.parseInt(indexStr, 10);
5431
- return [element, Number.isNaN(index) ? null : index];
5432
- };
5433
- function sortDOMElements(container) {
5613
+ function sortDOMElements(container, indexByElement) {
5434
5614
  const elements = Array.from(container.children);
5435
5615
  if (elements.length <= 1) return elements;
5436
- const items = elements.map(mapFn);
5616
+ const items = elements.map((element) => {
5617
+ var _a3;
5618
+ return [element, (_a3 = indexByElement.get(element)) != null ? _a3 : null];
5619
+ });
5437
5620
  items.sort((a, b) => {
5438
5621
  const aKey = a[1];
5439
5622
  const bKey = b[1];
@@ -5521,7 +5704,15 @@ function useDOMOrder(ref) {
5521
5704
  debounceRef.current = setTimeout(() => {
5522
5705
  const parent = ref.current;
5523
5706
  if (parent) {
5524
- sortDOMElements(parent);
5707
+ const indexByElement = /* @__PURE__ */ new Map();
5708
+ for (const [containerId, viewRef] of ctx.viewRefs) {
5709
+ const element = viewRef.current;
5710
+ const index = peek$(ctx, `containerItemIndex${containerId}`);
5711
+ if (element && index !== void 0) {
5712
+ indexByElement.set(element, index);
5713
+ }
5714
+ }
5715
+ sortDOMElements(parent, indexByElement);
5525
5716
  }
5526
5717
  debounceRef.current = void 0;
5527
5718
  }, 500);
@@ -5545,6 +5736,7 @@ var ContainersInner = typedMemo(function ContainersInner2({ horizontal, numColum
5545
5736
  useDOMOrder(ref);
5546
5737
  const style = horizontal ? {
5547
5738
  direction: isHorizontalRTLList ? "ltr" : void 0,
5739
+ flexShrink: 0,
5548
5740
  minHeight: otherAxisSize,
5549
5741
  opacity: readyToRender ? 1 : 0,
5550
5742
  position: "relative",
@@ -5570,7 +5762,7 @@ var ContainersInner = typedMemo(function ContainersInner2({ horizontal, numColum
5570
5762
  }
5571
5763
  }
5572
5764
  }
5573
- return /* @__PURE__ */ React3__namespace.createElement("div", { ref, style }, children);
5765
+ return /* @__PURE__ */ React3__namespace.createElement("div", { ref, style }, /* @__PURE__ */ React3__namespace.createElement(ContainerLayoutCoordinator, null, children));
5574
5766
  });
5575
5767
  var Containers = typedMemo(function Containers2({
5576
5768
  horizontal,
@@ -5749,6 +5941,7 @@ function resolveWindowScrollTarget({ clampedOffset, horizontal, listPos, scroll
5749
5941
 
5750
5942
  // src/components/ListComponentScrollView.tsx
5751
5943
  var SCROLLBAR_HIDDEN_STYLE_ID = "legend-list-scrollbar-axis-hidden-style";
5944
+ var SCROLL_END_FALLBACK_MS = 200;
5752
5945
  var SCROLLBAR_HIDDEN_STYLE = `.${LEGEND_LIST_SCROLLBAR_Y_HIDDEN_CLASS}::-webkit-scrollbar:vertical{width:0;display:none;}.${LEGEND_LIST_SCROLLBAR_X_HIDDEN_CLASS}::-webkit-scrollbar:horizontal{height:0;display:none;}`;
5753
5946
  function ensureScrollbarHiddenStyle() {
5754
5947
  if (typeof document === "undefined" || document.getElementById(SCROLLBAR_HIDDEN_STYLE_ID)) {
@@ -5798,6 +5991,7 @@ var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView
5798
5991
  contentOffset,
5799
5992
  maintainVisibleContentPosition,
5800
5993
  onScroll: onScroll2,
5994
+ onInternalScrollEnd,
5801
5995
  onMomentumScrollEnd: _onMomentumScrollEnd,
5802
5996
  showsHorizontalScrollIndicator = true,
5803
5997
  showsVerticalScrollIndicator = true,
@@ -5927,6 +6121,16 @@ var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView
5927
6121
  onScroll2(scrollEvent);
5928
6122
  }, [getCurrentScrollOffset, horizontal, isWindowScroll, onScroll2]);
5929
6123
  const scrollEventCoalescer = useRafCoalescer(emitScroll);
6124
+ const scrollEndFallbackRef = React3.useRef(void 0);
6125
+ const emitScrollEnd = React3.useCallback(() => {
6126
+ const timeout = scrollEndFallbackRef.current;
6127
+ if (timeout !== void 0) {
6128
+ clearTimeout(timeout);
6129
+ scrollEndFallbackRef.current = void 0;
6130
+ }
6131
+ scrollEventCoalescer.flush();
6132
+ onInternalScrollEnd == null ? void 0 : onInternalScrollEnd();
6133
+ }, [onInternalScrollEnd, scrollEventCoalescer]);
5930
6134
  const handleScroll = React3.useCallback(
5931
6135
  (_event) => {
5932
6136
  if (!onScroll2) {
@@ -5939,18 +6143,36 @@ var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView
5939
6143
  } else {
5940
6144
  scrollEventCoalescer.schedule();
5941
6145
  }
6146
+ if (onInternalScrollEnd) {
6147
+ const timeout = scrollEndFallbackRef.current;
6148
+ if (timeout !== void 0) {
6149
+ clearTimeout(timeout);
6150
+ }
6151
+ scrollEndFallbackRef.current = setTimeout(emitScrollEnd, SCROLL_END_FALLBACK_MS);
6152
+ }
5942
6153
  },
5943
- [ctx.state, onScroll2, scrollEventCoalescer]
6154
+ [ctx.state, emitScrollEnd, onInternalScrollEnd, onScroll2, scrollEventCoalescer]
5944
6155
  );
5945
6156
  React3.useLayoutEffect(() => {
5946
6157
  const target = getScrollTarget();
5947
6158
  if (!target) return;
5948
6159
  target.addEventListener("scroll", handleScroll, { passive: true });
6160
+ if ("onscrollend" in target) {
6161
+ target.addEventListener("scrollend", emitScrollEnd);
6162
+ }
5949
6163
  return () => {
5950
6164
  target.removeEventListener("scroll", handleScroll);
6165
+ if ("onscrollend" in target) {
6166
+ target.removeEventListener("scrollend", emitScrollEnd);
6167
+ }
6168
+ const timeout = scrollEndFallbackRef.current;
6169
+ if (timeout !== void 0) {
6170
+ clearTimeout(timeout);
6171
+ scrollEndFallbackRef.current = void 0;
6172
+ }
5951
6173
  scrollEventCoalescer.cancel();
5952
6174
  };
5953
- }, [getScrollTarget, handleScroll, scrollEventCoalescer]);
6175
+ }, [emitScrollEnd, getScrollTarget, handleScroll, scrollEventCoalescer]);
5954
6176
  React3.useEffect(() => {
5955
6177
  const doScroll = () => {
5956
6178
  if (contentOffset) {
@@ -6300,6 +6522,8 @@ var ListComponent = typedMemo(function ListComponent2({
6300
6522
  refScrollView,
6301
6523
  renderScrollComponent,
6302
6524
  onLayoutFooter,
6525
+ onInternalScrollBeginDrag,
6526
+ onInternalScrollEnd,
6303
6527
  scrollAdjustHandler,
6304
6528
  snapToIndices,
6305
6529
  stickyHeaderConfig,
@@ -6361,7 +6585,7 @@ var ListComponent = typedMemo(function ListComponent2({
6361
6585
  SnapOrScroll,
6362
6586
  {
6363
6587
  ...rest,
6364
- ...ScrollComponent === ListComponentScrollView ? { useWindowScroll } : {},
6588
+ ...ScrollComponent === ListComponentScrollView ? { onInternalScrollEnd, useWindowScroll } : {} ,
6365
6589
  contentContainerStyle: [
6366
6590
  horizontal ? {
6367
6591
  height: "100%"
@@ -6467,6 +6691,7 @@ function checkResetContainers(ctx, dataProp, { didColumnsChange = false } = {})
6467
6691
  if (didColumnsChange) {
6468
6692
  state.sizes.clear();
6469
6693
  state.sizesKnown.clear();
6694
+ invalidateContainerFixedItemSizes(state);
6470
6695
  for (const key in state.averageSizes) {
6471
6696
  delete state.averageSizes[key];
6472
6697
  }
@@ -6713,7 +6938,7 @@ function onScroll(ctx, event) {
6713
6938
  }
6714
6939
  }
6715
6940
  state.scrollPending = newScroll;
6716
- updateScroll(ctx, newScroll, insetChanged);
6941
+ updateScroll(ctx, newScroll, insetChanged, { fromNativeScrollEvent: true });
6717
6942
  trackInitialScrollNativeProgress(state, newScroll);
6718
6943
  clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx);
6719
6944
  if (state.scrollingTo) {
@@ -6876,8 +7101,8 @@ function getAverageItemSizes(state) {
6876
7101
  return averageItemSizes;
6877
7102
  }
6878
7103
  function triggerMountedContainerLayouts(ctx) {
6879
- for (const triggerLayout of ctx.containerLayoutTriggers.values()) {
6880
- triggerLayout();
7104
+ {
7105
+ scheduleContainerLayout(ctx);
6881
7106
  }
6882
7107
  }
6883
7108
  function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
@@ -6990,6 +7215,7 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
6990
7215
  const mode = (_a3 = options == null ? void 0 : options.mode) != null ? _a3 : "sizes";
6991
7216
  state.sizes.clear();
6992
7217
  state.sizesKnown.clear();
7218
+ invalidateContainerFixedItemSizes(state);
6993
7219
  for (const key in state.averageSizes) {
6994
7220
  delete state.averageSizes[key];
6995
7221
  }
@@ -7011,6 +7237,10 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
7011
7237
  return {
7012
7238
  clearCaches,
7013
7239
  flashScrollIndicators: () => refScroller.current.flashScrollIndicators(),
7240
+ getAnimatableRef: () => {
7241
+ var _a3, _b, _c;
7242
+ return (_c = (_b = (_a3 = refScroller.current).getNativeScrollRef) == null ? void 0 : _b.call(_a3)) != null ? _c : refScroller.current;
7243
+ },
7014
7244
  getNativeScrollRef: () => refScroller.current,
7015
7245
  getScrollableNode: () => refScroller.current.getScrollableNode(),
7016
7246
  getScrollResponder: () => refScroller.current.getScrollResponder(),
@@ -7381,6 +7611,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7381
7611
  onMomentumScrollEnd,
7382
7612
  onRefresh,
7383
7613
  onScroll: onScrollProp,
7614
+ onScrollBeginDrag,
7384
7615
  onStartReached,
7385
7616
  onStartReachedThreshold = 0.5,
7386
7617
  onStickyHeaderChange,
@@ -7492,8 +7723,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7492
7723
  averageSizes: {},
7493
7724
  columnSpans: [],
7494
7725
  columns: [],
7726
+ containerItemGenerations: [],
7495
7727
  containerItemKeys: /* @__PURE__ */ new Map(),
7496
- containerItemTypes: /* @__PURE__ */ new Map(),
7728
+ containerItemMetadata: /* @__PURE__ */ new Map(),
7497
7729
  contentInsetOverride: void 0,
7498
7730
  dataChangeEpoch: 0,
7499
7731
  dataChangeNeedsScrollUpdate: false,
@@ -7546,7 +7778,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7546
7778
  startBuffered: -1,
7547
7779
  startNoBuffer: -1,
7548
7780
  startReachedSnapshot: void 0,
7549
- startReachedSnapshotDataChangeEpoch: void 0,
7550
7781
  stickyContainerPool: /* @__PURE__ */ new Set(),
7551
7782
  stickyContainers: /* @__PURE__ */ new Map(),
7552
7783
  timeoutAdaptiveRender: void 0,
@@ -7622,7 +7853,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7622
7853
  onFirstVisibleItemChanged,
7623
7854
  onItemSizeChanged,
7624
7855
  onLoad,
7856
+ onMomentumScrollEnd,
7625
7857
  onScroll: throttleScrollFn,
7858
+ onScrollBeginDrag,
7626
7859
  onStartReached,
7627
7860
  onStartReachedThreshold,
7628
7861
  onStickyHeaderChange,
@@ -7897,11 +8130,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7897
8130
  getRenderedItem: (key) => getRenderedItem(ctx, key),
7898
8131
  onMomentumScrollEnd: (event) => {
7899
8132
  checkFinishedScrollFallback(ctx);
7900
- if (onMomentumScrollEnd) {
7901
- onMomentumScrollEnd(event);
8133
+ if (state.props.onMomentumScrollEnd) {
8134
+ state.props.onMomentumScrollEnd(event);
7902
8135
  }
7903
8136
  },
7904
- onScroll: (event) => onScroll(ctx, event)
8137
+ onScroll: (event) => onScroll(ctx, event),
8138
+ onScrollBeginDrag: (event) => {
8139
+ var _a4, _b2;
8140
+ prepareReachedEdgeForNextUserScroll(ctx);
8141
+ (_b2 = (_a4 = state.props).onScrollBeginDrag) == null ? void 0 : _b2.call(_a4, event);
8142
+ },
8143
+ onScrollEnd: () => prepareReachedEdgeForNextUserScroll(ctx)
7905
8144
  }),
7906
8145
  []
7907
8146
  );
@@ -7922,6 +8161,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7922
8161
  ListFooterComponent,
7923
8162
  ListFooterComponentStyle,
7924
8163
  ListHeaderComponent,
8164
+ onInternalScrollBeginDrag: fns.onScrollBeginDrag,
8165
+ onInternalScrollEnd: fns.onScrollEnd,
7925
8166
  onLayout,
7926
8167
  onLayoutFooter,
7927
8168
  onMomentumScrollEnd: fns.onMomentumScrollEnd,