@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.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as React3 from 'react';
2
- import { forwardRef, useReducer, useEffect, createContext, useRef, useState, useMemo, useCallback, useLayoutEffect, useImperativeHandle, useContext } from 'react';
2
+ import { forwardRef, useReducer, useEffect, createContext, useRef, useMemo, useCallback, useImperativeHandle, useLayoutEffect, useState, useContext } from 'react';
3
3
  import { useSyncExternalStore } from 'use-sync-external-store/shim';
4
4
  import * as ReactDOM from 'react-dom';
5
5
  import { flushSync } from 'react-dom';
@@ -141,11 +141,13 @@ function StateProvider({ children }) {
141
141
  mapViewabilityCallbacks: /* @__PURE__ */ new Map(),
142
142
  mapViewabilityConfigStates: /* @__PURE__ */ new Map(),
143
143
  mapViewabilityValues: /* @__PURE__ */ new Map(),
144
+ pendingContainerIds: void 0,
144
145
  positionListeners: /* @__PURE__ */ new Map(),
145
146
  scrollAxisGap: 0,
146
147
  state: void 0,
147
148
  values: /* @__PURE__ */ new Map([
148
149
  ["alignItemsAtEndPadding", 0],
150
+ ["containerLayoutEpoch", 0],
149
151
  ["stylePaddingTop", 0],
150
152
  ["headerSize", 0],
151
153
  ["numContainers", 0],
@@ -370,6 +372,18 @@ function useInterval(callback, delay) {
370
372
  }, [delay]);
371
373
  }
372
374
 
375
+ // src/constants-platform.ts
376
+ var IsNewArchitecture = true;
377
+
378
+ // src/core/containerLayoutBaseline.ts
379
+ var containerLayoutBaselines = /* @__PURE__ */ new WeakMap();
380
+ function getContainerLayoutBaseline(element) {
381
+ return containerLayoutBaselines.get(element);
382
+ }
383
+ function setContainerLayoutBaseline(element, size) {
384
+ containerLayoutBaselines.set(element, size);
385
+ }
386
+
373
387
  // src/utils/devEnvironment.ts
374
388
  var metroDev = typeof __DEV__ !== "undefined" ? __DEV__ : void 0;
375
389
  var _a;
@@ -383,528 +397,247 @@ var POSITION_OUT_OF_VIEW = -1e7;
383
397
  var EDGE_POSITION_EPSILON = 1;
384
398
  var ENABLE_DEVMODE = IS_DEV && false;
385
399
  var ENABLE_DEBUG_VIEW = IS_DEV && false;
386
- var typedForwardRef = React3.forwardRef;
387
- var typedMemo = React3.memo;
388
- var getComponent = (Component) => {
389
- if (React3.isValidElement(Component)) {
390
- return Component;
391
- }
392
- if (Component) {
393
- return /* @__PURE__ */ React3.createElement(Component, null);
394
- }
395
- return null;
396
- };
397
400
 
398
- // src/utils/helpers.ts
399
- function isFunction(obj) {
400
- return typeof obj === "function";
401
- }
402
- function isArray(obj) {
403
- return Array.isArray(obj);
401
+ // src/core/deferredPublicOnScroll.ts
402
+ function withResolvedContentOffset(state, event, resolvedOffset) {
403
+ return {
404
+ ...event,
405
+ nativeEvent: {
406
+ ...event.nativeEvent,
407
+ contentOffset: state.props.horizontal ? { x: resolvedOffset, y: 0 } : { x: 0, y: resolvedOffset }
408
+ }
409
+ };
404
410
  }
405
- var warned = /* @__PURE__ */ new Set();
406
- function warnDevOnce(id, text) {
407
- if (IS_DEV && !warned.has(id)) {
408
- warned.add(id);
409
- console.warn(`[legend-list] ${text}`);
411
+ function releaseDeferredPublicOnScroll(ctx, resolvedOffset) {
412
+ var _a3, _b, _c, _d;
413
+ const state = ctx.state;
414
+ const deferredEvent = state.deferredPublicOnScrollEvent;
415
+ state.deferredPublicOnScrollEvent = void 0;
416
+ if (deferredEvent) {
417
+ (_d = (_c = state.props).onScroll) == null ? void 0 : _d.call(
418
+ _c,
419
+ withResolvedContentOffset(
420
+ state,
421
+ deferredEvent,
422
+ (_b = (_a3 = resolvedOffset != null ? resolvedOffset : state.scrollPending) != null ? _a3 : state.scroll) != null ? _b : 0
423
+ )
424
+ );
410
425
  }
411
426
  }
412
- function roundSize(size) {
413
- return Math.floor(size * 8) / 8;
427
+
428
+ // src/core/initialScrollSession.ts
429
+ var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
430
+ function hasInitialScrollSessionCompletion(completion) {
431
+ return !!((completion == null ? void 0 : completion.didDispatchNativeScroll) || (completion == null ? void 0 : completion.didRetrySilentInitialScroll) || (completion == null ? void 0 : completion.watchdog));
414
432
  }
415
- function isNullOrUndefined(value) {
416
- return value === null || value === void 0;
433
+ function clearInitialScrollSession(state) {
434
+ state.initialScrollSession = void 0;
435
+ return void 0;
417
436
  }
418
- function getPadding(s, type) {
419
- var _a3, _b, _c;
420
- const axisPadding = type === "Left" || type === "Right" ? s.paddingHorizontal : s.paddingVertical;
421
- return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : axisPadding) != null ? _b : s.padding) != null ? _c : 0;
437
+ function createInitialScrollSession(options) {
438
+ const { bootstrap, completion, kind, previousDataLength } = options;
439
+ return kind === "offset" ? {
440
+ completion,
441
+ kind,
442
+ previousDataLength
443
+ } : {
444
+ bootstrap,
445
+ completion,
446
+ kind,
447
+ previousDataLength
448
+ };
422
449
  }
423
- function extractPadding(style, contentContainerStyle, type) {
424
- return getPadding(style, type) + getPadding(contentContainerStyle, type);
450
+ function ensureInitialScrollSessionCompletion(state, kind = ((_b) => (_b = ((_a3) => (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind)()) != null ? _b : "bootstrap")()) {
451
+ var _a4, _b2;
452
+ if (!state.initialScrollSession) {
453
+ state.initialScrollSession = createInitialScrollSession({
454
+ completion: {},
455
+ kind,
456
+ previousDataLength: 0
457
+ });
458
+ } else if (state.initialScrollSession.kind !== kind) {
459
+ state.initialScrollSession = createInitialScrollSession({
460
+ bootstrap: state.initialScrollSession.kind === "bootstrap" ? state.initialScrollSession.bootstrap : void 0,
461
+ completion: state.initialScrollSession.completion,
462
+ kind,
463
+ previousDataLength: state.initialScrollSession.previousDataLength
464
+ });
465
+ }
466
+ (_b2 = (_a4 = state.initialScrollSession).completion) != null ? _b2 : _a4.completion = {};
467
+ return state.initialScrollSession.completion;
425
468
  }
426
- function findContainerId(ctx, key) {
427
- var _a3, _b;
428
- const directMatch = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.containerItemKeys) == null ? void 0 : _b.get(key);
429
- if (directMatch !== void 0) {
430
- return directMatch;
469
+ var initialScrollCompletion = {
470
+ didDispatchNativeScroll(state) {
471
+ var _a3, _b;
472
+ return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didDispatchNativeScroll);
473
+ },
474
+ didRetrySilentInitialScroll(state) {
475
+ var _a3, _b;
476
+ return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didRetrySilentInitialScroll);
477
+ },
478
+ markInitialScrollNativeDispatch(state) {
479
+ ensureInitialScrollSessionCompletion(state).didDispatchNativeScroll = true;
480
+ },
481
+ markSilentInitialScrollRetry(state) {
482
+ ensureInitialScrollSessionCompletion(state).didRetrySilentInitialScroll = true;
483
+ },
484
+ resetFlags(state) {
485
+ if (!state.initialScrollSession) {
486
+ return;
487
+ }
488
+ const completion = ensureInitialScrollSessionCompletion(state, state.initialScrollSession.kind);
489
+ completion.didDispatchNativeScroll = void 0;
490
+ completion.didRetrySilentInitialScroll = void 0;
431
491
  }
432
- const numContainers = peek$(ctx, "numContainers");
433
- for (let i = 0; i < numContainers; i++) {
434
- const itemKey = peek$(ctx, `containerItemKey${i}`);
435
- if (itemKey === key) {
436
- return i;
492
+ };
493
+ var initialScrollWatchdog = {
494
+ clear(state) {
495
+ initialScrollWatchdog.set(state, void 0);
496
+ },
497
+ didReachTarget(newScroll, watchdog) {
498
+ const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
499
+ return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
500
+ },
501
+ get(state) {
502
+ var _a3, _b;
503
+ return (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog;
504
+ },
505
+ hasNonZeroTargetOffset(targetOffset) {
506
+ return targetOffset !== void 0 && targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
507
+ },
508
+ isAtZeroTargetOffset(targetOffset) {
509
+ return targetOffset <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
510
+ },
511
+ set(state, watchdog) {
512
+ var _a3, _b;
513
+ if (!watchdog && !((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog)) {
514
+ return;
437
515
  }
516
+ const completion = ensureInitialScrollSessionCompletion(state);
517
+ completion.watchdog = watchdog ? {
518
+ startScroll: watchdog.startScroll,
519
+ targetOffset: watchdog.targetOffset
520
+ } : void 0;
438
521
  }
439
- return -1;
522
+ };
523
+ function setInitialScrollSession(state, options = {}) {
524
+ var _a3, _b, _c, _d;
525
+ const existingSession = state.initialScrollSession;
526
+ const kind = (_a3 = options.kind) != null ? _a3 : existingSession == null ? void 0 : existingSession.kind;
527
+ const completion = existingSession == null ? void 0 : existingSession.completion;
528
+ const existingBootstrap = (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0;
529
+ const bootstrap = kind === "bootstrap" ? options.bootstrap === null ? void 0 : (_b = options.bootstrap) != null ? _b : existingBootstrap : void 0;
530
+ if (!kind) {
531
+ return clearInitialScrollSession(state);
532
+ }
533
+ if (!state.initialScroll && !bootstrap && !hasInitialScrollSessionCompletion(completion)) {
534
+ return clearInitialScrollSession(state);
535
+ }
536
+ const previousDataLength = (_d = (_c = options.previousDataLength) != null ? _c : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _d : 0;
537
+ state.initialScrollSession = createInitialScrollSession({
538
+ bootstrap,
539
+ completion,
540
+ kind,
541
+ previousDataLength
542
+ });
543
+ return state.initialScrollSession;
440
544
  }
441
545
 
442
- // src/components/PositionView.tsx
443
- var isRNWeb = typeof document !== "undefined" && !!document.getElementById("react-native-stylesheet");
444
- var baseCss = {
445
- contain: "paint layout style",
446
- ...isRNWeb ? {
447
- display: "flex",
448
- flexDirection: "column"
449
- } : {}
546
+ // src/utils/checkThreshold.ts
547
+ var HYSTERESIS_MULTIPLIER = 1.3;
548
+ function isOutsideThresholdHysteresis(distance, atThreshold, threshold) {
549
+ const absDistance = Math.abs(distance);
550
+ return !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
551
+ }
552
+ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot) => {
553
+ const absDistance = Math.abs(distance);
554
+ const within = atThreshold || threshold > 0 && absDistance <= threshold;
555
+ const updateSnapshot = () => {
556
+ setSnapshot({
557
+ atThreshold,
558
+ contentSize: context.contentSize,
559
+ dataLength: context.dataLength,
560
+ scrollPosition: context.scrollPosition
561
+ });
562
+ };
563
+ if (!wasReached) {
564
+ if (!within) {
565
+ return false;
566
+ }
567
+ onReached(distance);
568
+ updateSnapshot();
569
+ return true;
570
+ }
571
+ const reset = isOutsideThresholdHysteresis(distance, atThreshold, threshold);
572
+ if (reset) {
573
+ setSnapshot(void 0);
574
+ return false;
575
+ }
576
+ if (within) {
577
+ const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
578
+ if (changed) {
579
+ updateSnapshot();
580
+ }
581
+ }
582
+ return true;
450
583
  };
451
- var PositionViewState = typedMemo(function PositionViewState2({
452
- id,
453
- horizontal,
454
- style,
455
- refView,
456
- ...props
457
- }) {
458
- const [position = POSITION_OUT_OF_VIEW] = useArr$([`containerPosition${id}`]);
459
- const composed = isArray(style) ? Object.assign({}, ...style) : style;
460
- const combinedStyle = horizontal ? { ...baseCss, ...composed, left: position } : { ...baseCss, ...composed, top: position };
461
- const {
462
- animatedScrollY: _animatedScrollY,
463
- index,
464
- onLayout: _onLayout,
465
- onLayoutChange: _onLayoutChange,
466
- stickyHeaderConfig: _stickyHeaderConfig,
467
- ...webProps
468
- } = props;
469
- return /* @__PURE__ */ React3.createElement("div", { "data-index": index, ref: refView, ...webProps, style: combinedStyle });
470
- });
471
- var PositionViewSticky = typedMemo(function PositionViewSticky2({
472
- id,
473
- horizontal,
474
- style,
475
- refView,
476
- index,
477
- animatedScrollY: _animatedScrollY,
478
- stickyHeaderConfig,
479
- onLayout: _onLayout,
480
- onLayoutChange: _onLayoutChange,
481
- children,
482
- ...webProps
483
- }) {
484
- const [position = POSITION_OUT_OF_VIEW, activeStickyIndex] = useArr$([
485
- `containerPosition${id}`,
486
- "activeStickyIndex"
487
- ]);
488
- const composed = React3.useMemo(
489
- () => {
490
- var _a3;
491
- return (_a3 = isArray(style) ? Object.assign({}, ...style) : style) != null ? _a3 : {};
492
- },
493
- [style]
494
- );
495
- const viewStyle = React3.useMemo(() => {
496
- var _a3;
497
- const styleBase = { ...baseCss, ...composed };
498
- delete styleBase.transform;
499
- const offset = (_a3 = stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset) != null ? _a3 : 0;
500
- const isActive = activeStickyIndex === index;
501
- styleBase.position = isActive ? "sticky" : "absolute";
502
- styleBase.zIndex = index + 1e3;
503
- if (horizontal) {
504
- styleBase.left = isActive ? offset : position;
505
- } else {
506
- styleBase.top = isActive ? offset : position;
507
- }
508
- return styleBase;
509
- }, [composed, horizontal, position, index, activeStickyIndex, stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset]);
510
- const renderStickyHeaderBackdrop = React3.useMemo(
511
- () => (stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent) ? /* @__PURE__ */ React3.createElement(
512
- "div",
513
- {
514
- style: {
515
- inset: 0,
516
- pointerEvents: "none",
517
- position: "absolute"
518
- }
519
- },
520
- getComponent(stickyHeaderConfig.backdropComponent)
521
- ) : null,
522
- [stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent]
523
- );
524
- return /* @__PURE__ */ React3.createElement(
525
- "div",
526
- {
527
- "data-index": index,
528
- ref: refView,
529
- style: viewStyle,
530
- ...webProps
531
- },
532
- renderStickyHeaderBackdrop,
533
- children
534
- );
535
- });
536
- var PositionView = PositionViewState;
537
-
538
- // src/constants-platform.ts
539
- var IsNewArchitecture = true;
540
- function useInit(cb) {
541
- useState(() => cb());
542
- }
543
584
 
544
- // src/state/ContextContainer.ts
545
- var ContextContainer = createContext(null);
546
- var NO_CONTAINER_ID = -1;
547
- function useContextContainer() {
548
- return useContext(ContextContainer);
549
- }
550
- function useContainerItemInfo(containerContext) {
551
- var _a3;
552
- const containerId = (_a3 = containerContext == null ? void 0 : containerContext.containerId) != null ? _a3 : NO_CONTAINER_ID;
553
- const [itemInfo] = useArr$([`containerItemInfo${containerId}`]);
554
- return containerContext ? itemInfo : void 0;
555
- }
556
- function useContainerItemKey(containerContext) {
557
- var _a3;
558
- const containerId = (_a3 = containerContext == null ? void 0 : containerContext.containerId) != null ? _a3 : NO_CONTAINER_ID;
559
- const [itemKey] = useArr$([`containerItemKey${containerId}`]);
560
- return containerContext ? itemKey : void 0;
561
- }
562
- function useAdaptiveRender() {
563
- const [mode] = useArr$(["adaptiveRender"]);
564
- return mode;
565
- }
566
- function useAdaptiveRenderChange(callback) {
567
- const ctx = useStateContext();
568
- const callbackRef = useRef(callback);
569
- callbackRef.current = callback;
570
- useLayoutEffect(() => {
571
- let mode = peek$(ctx, "adaptiveRender");
572
- return listen$(ctx, "adaptiveRender", (nextMode) => {
573
- if (mode !== nextMode) {
574
- mode = nextMode;
575
- callbackRef.current(nextMode);
576
- }
577
- });
578
- }, [ctx]);
579
- }
580
- function useViewability(callback, configId) {
581
- const ctx = useStateContext();
582
- const containerContext = useContextContainer();
583
- useInit(() => {
584
- if (!containerContext) {
585
- return;
586
- }
587
- const { containerId } = containerContext;
588
- const key = containerId + (configId != null ? configId : "");
589
- const value = ctx.mapViewabilityValues.get(key);
590
- if (value) {
591
- callback(value);
592
- }
593
- });
594
- useEffect(() => {
595
- if (!containerContext) {
596
- return;
597
- }
598
- const { containerId } = containerContext;
599
- const key = containerId + (configId != null ? configId : "");
600
- ctx.mapViewabilityCallbacks.set(key, callback);
601
- return () => {
602
- ctx.mapViewabilityCallbacks.delete(key);
603
- };
604
- }, [ctx, callback, configId, containerContext]);
605
- }
606
- function useViewabilityAmount(callback) {
607
- const ctx = useStateContext();
608
- const containerContext = useContextContainer();
609
- useInit(() => {
610
- if (!containerContext) {
611
- return;
612
- }
613
- const { containerId } = containerContext;
614
- const value = ctx.mapViewabilityAmountValues.get(containerId);
615
- if (value) {
616
- callback(value);
617
- }
618
- });
619
- useEffect(() => {
620
- if (!containerContext) {
621
- return;
622
- }
623
- const { containerId } = containerContext;
624
- ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
625
- return () => {
626
- ctx.mapViewabilityAmountCallbacks.delete(containerId);
627
- };
628
- }, [ctx, callback, containerContext]);
629
- }
630
- function useRecyclingEffect(effect) {
631
- const containerContext = useContextContainer();
632
- const itemInfo = useContainerItemInfo(containerContext);
633
- const prevInfo = useRef(void 0);
634
- useEffect(() => {
635
- if (!itemInfo) {
636
- return;
637
- }
638
- let ret;
639
- if (prevInfo.current) {
640
- ret = effect({
641
- index: itemInfo.index,
642
- item: itemInfo.value,
643
- prevIndex: prevInfo.current.index,
644
- prevItem: prevInfo.current.value
645
- });
646
- }
647
- prevInfo.current = itemInfo;
648
- return ret;
649
- }, [effect, itemInfo]);
650
- }
651
- function useRecyclingState(valueOrFun) {
652
- var _a3;
653
- const containerContext = useContextContainer();
654
- const itemInfo = useContainerItemInfo(containerContext);
655
- const computeValue = (info) => {
656
- if (isFunction(valueOrFun)) {
657
- const initializer = valueOrFun;
658
- return info ? initializer({
659
- index: info.index,
660
- item: info.value,
661
- prevIndex: void 0,
662
- prevItem: void 0
663
- }) : initializer();
664
- }
665
- return valueOrFun;
666
- };
667
- const [stateValue, setStateValue] = useState(() => {
668
- return computeValue(itemInfo);
669
- });
670
- const prevItemKeyRef = useRef((_a3 = itemInfo == null ? void 0 : itemInfo.itemKey) != null ? _a3 : null);
671
- if (itemInfo && prevItemKeyRef.current !== itemInfo.itemKey) {
672
- prevItemKeyRef.current = itemInfo.itemKey;
673
- setStateValue(computeValue(itemInfo));
585
+ // src/utils/edgeReachedGate.ts
586
+ function resetEdgeLatch(ctx, edge) {
587
+ const state = ctx.state;
588
+ if (edge === "start") {
589
+ state.isStartReached = false;
590
+ state.startReachedSnapshot = void 0;
591
+ } else {
592
+ state.isEndReached = false;
593
+ state.endReachedSnapshot = void 0;
674
594
  }
675
- const triggerLayout = containerContext == null ? void 0 : containerContext.triggerLayout;
676
- const setState = useCallback(
677
- (newState) => {
678
- if (!triggerLayout) {
679
- return;
680
- }
681
- setStateValue((prevValue) => {
682
- return isFunction(newState) ? newState(prevValue) : newState;
683
- });
684
- triggerLayout();
685
- },
686
- [triggerLayout]
687
- );
688
- return [stateValue, setState];
689
- }
690
- function useIsLastItem() {
691
- const containerContext = useContextContainer();
692
- const itemKey = useContainerItemKey(containerContext);
693
- const isLast = useSelector$("lastItemKeys", (lastItemKeys) => {
694
- if (containerContext && !isNullOrUndefined(itemKey)) {
695
- return (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false;
696
- }
697
- return false;
698
- });
699
- return isLast;
700
- }
701
- function useListScrollSize() {
702
- const [scrollSize] = useArr$(["scrollSize"]);
703
- return scrollSize;
704
- }
705
- var noop = () => {
706
- };
707
- function useSyncLayout() {
708
- const containerContext = useContextContainer();
709
- return containerContext ? containerContext.triggerLayout : noop;
710
- }
711
-
712
- // src/components/Separator.tsx
713
- function Separator({ ItemSeparatorComponent, leadingItem }) {
714
- const isLastItem = useIsLastItem();
715
- return isLastItem ? null : /* @__PURE__ */ React3.createElement(ItemSeparatorComponent, { leadingItem });
716
- }
717
-
718
- // src/core/deferredPublicOnScroll.ts
719
- function withResolvedContentOffset(state, event, resolvedOffset) {
720
- return {
721
- ...event,
722
- nativeEvent: {
723
- ...event.nativeEvent,
724
- contentOffset: state.props.horizontal ? { x: resolvedOffset, y: 0 } : { x: 0, y: resolvedOffset }
725
- }
726
- };
727
595
  }
728
- function releaseDeferredPublicOnScroll(ctx, resolvedOffset) {
729
- var _a3, _b, _c, _d;
596
+ function resetSharedEdgeGateIfOutsideHysteresis(ctx) {
730
597
  const state = ctx.state;
731
- const deferredEvent = state.deferredPublicOnScrollEvent;
732
- state.deferredPublicOnScrollEvent = void 0;
733
- if (deferredEvent) {
734
- (_d = (_c = state.props).onScroll) == null ? void 0 : _d.call(
735
- _c,
736
- withResolvedContentOffset(
737
- state,
738
- deferredEvent,
739
- (_b = (_a3 = resolvedOffset != null ? resolvedOffset : state.scrollPending) != null ? _a3 : state.scroll) != null ? _b : 0
740
- )
741
- );
598
+ if (!state.edgeReachedGate) {
599
+ return;
600
+ }
601
+ const contentSize = getContentSize(ctx);
602
+ const endDistance = contentSize - state.scroll - state.scrollLength - getContentInsetEnd(ctx);
603
+ const isContentLess = contentSize < state.scrollLength;
604
+ const startThreshold = state.props.onStartReachedThreshold * state.scrollLength;
605
+ const endThreshold = state.props.onEndReachedThreshold * state.scrollLength;
606
+ const isOutsideStart = isOutsideThresholdHysteresis(state.scroll, false, startThreshold);
607
+ const isOutsideEnd = isOutsideThresholdHysteresis(endDistance, isContentLess, endThreshold);
608
+ if (isOutsideStart && isOutsideEnd) {
609
+ state.edgeReachedGate = void 0;
742
610
  }
743
611
  }
744
-
745
- // src/core/initialScrollSession.ts
746
- var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
747
- function hasInitialScrollSessionCompletion(completion) {
748
- return !!((completion == null ? void 0 : completion.didDispatchNativeScroll) || (completion == null ? void 0 : completion.didRetrySilentInitialScroll) || (completion == null ? void 0 : completion.watchdog));
749
- }
750
- function clearInitialScrollSession(state) {
751
- state.initialScrollSession = void 0;
752
- return void 0;
612
+ function canDispatchReachedEdge(ctx, edge, allowedEdge, allowGateCreatedInCurrentCheck) {
613
+ return !ctx.state.edgeReachedGate || allowedEdge === edge || !!allowGateCreatedInCurrentCheck;
753
614
  }
754
- function createInitialScrollSession(options) {
755
- const { bootstrap, completion, kind, previousDataLength } = options;
756
- return kind === "offset" ? {
757
- completion,
758
- kind,
759
- previousDataLength
760
- } : {
761
- bootstrap,
762
- completion,
763
- kind,
764
- previousDataLength
765
- };
615
+ function markReachedEdge(ctx) {
616
+ ctx.state.edgeReachedGate = "closed";
766
617
  }
767
- function ensureInitialScrollSessionCompletion(state, kind = ((_b) => (_b = ((_a3) => (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind)()) != null ? _b : "bootstrap")()) {
768
- var _a4, _b2;
769
- if (!state.initialScrollSession) {
770
- state.initialScrollSession = createInitialScrollSession({
771
- completion: {},
772
- kind,
773
- previousDataLength: 0
774
- });
775
- } else if (state.initialScrollSession.kind !== kind) {
776
- state.initialScrollSession = createInitialScrollSession({
777
- bootstrap: state.initialScrollSession.kind === "bootstrap" ? state.initialScrollSession.bootstrap : void 0,
778
- completion: state.initialScrollSession.completion,
779
- kind,
780
- previousDataLength: state.initialScrollSession.previousDataLength
781
- });
618
+ function prepareReachedEdgeForNextUserScroll(ctx) {
619
+ if (ctx.state.edgeReachedGate) {
620
+ ctx.state.edgeReachedGate = "prepared";
782
621
  }
783
- (_b2 = (_a4 = state.initialScrollSession).completion) != null ? _b2 : _a4.completion = {};
784
- return state.initialScrollSession.completion;
785
622
  }
786
- var initialScrollCompletion = {
787
- didDispatchNativeScroll(state) {
788
- var _a3, _b;
789
- return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didDispatchNativeScroll);
790
- },
791
- didRetrySilentInitialScroll(state) {
792
- var _a3, _b;
793
- return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didRetrySilentInitialScroll);
794
- },
795
- markInitialScrollNativeDispatch(state) {
796
- ensureInitialScrollSessionCompletion(state).didDispatchNativeScroll = true;
797
- },
798
- markSilentInitialScrollRetry(state) {
799
- ensureInitialScrollSessionCompletion(state).didRetrySilentInitialScroll = true;
800
- },
801
- resetFlags(state) {
802
- if (!state.initialScrollSession) {
803
- return;
804
- }
805
- const completion = ensureInitialScrollSessionCompletion(state, state.initialScrollSession.kind);
806
- completion.didDispatchNativeScroll = void 0;
807
- completion.didRetrySilentInitialScroll = void 0;
808
- }
809
- };
810
- var initialScrollWatchdog = {
811
- clear(state) {
812
- initialScrollWatchdog.set(state, void 0);
813
- },
814
- didReachTarget(newScroll, watchdog) {
815
- const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
816
- return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
817
- },
818
- get(state) {
819
- var _a3, _b;
820
- return (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog;
821
- },
822
- hasNonZeroTargetOffset(targetOffset) {
823
- return targetOffset !== void 0 && targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
824
- },
825
- isAtZeroTargetOffset(targetOffset) {
826
- return targetOffset <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
827
- },
828
- set(state, watchdog) {
829
- var _a3, _b;
830
- if (!watchdog && !((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog)) {
831
- return;
832
- }
833
- const completion = ensureInitialScrollSessionCompletion(state);
834
- completion.watchdog = watchdog ? {
835
- startScroll: watchdog.startScroll,
836
- targetOffset: watchdog.targetOffset
837
- } : void 0;
838
- }
839
- };
840
- function setInitialScrollSession(state, options = {}) {
841
- var _a3, _b, _c, _d;
842
- const existingSession = state.initialScrollSession;
843
- const kind = (_a3 = options.kind) != null ? _a3 : existingSession == null ? void 0 : existingSession.kind;
844
- const completion = existingSession == null ? void 0 : existingSession.completion;
845
- const existingBootstrap = (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0;
846
- const bootstrap = kind === "bootstrap" ? options.bootstrap === null ? void 0 : (_b = options.bootstrap) != null ? _b : existingBootstrap : void 0;
847
- if (!kind) {
848
- return clearInitialScrollSession(state);
849
- }
850
- if (!state.initialScroll && !bootstrap && !hasInitialScrollSessionCompletion(completion)) {
851
- return clearInitialScrollSession(state);
623
+ function beginReachedEdgeUserScroll(ctx, scrollDelta) {
624
+ const state = ctx.state;
625
+ if (state.edgeReachedGate !== "prepared") {
626
+ return void 0;
852
627
  }
853
- const previousDataLength = (_d = (_c = options.previousDataLength) != null ? _c : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _d : 0;
854
- state.initialScrollSession = createInitialScrollSession({
855
- bootstrap,
856
- completion,
857
- kind,
858
- previousDataLength
859
- });
860
- return state.initialScrollSession;
628
+ const allowedEdge = scrollDelta < 0 ? "start" : "end";
629
+ state.edgeReachedGate = "closed";
630
+ resetEdgeLatch(ctx, allowedEdge);
631
+ return allowedEdge;
861
632
  }
862
633
 
863
- // src/utils/checkThreshold.ts
864
- var HYSTERESIS_MULTIPLIER = 1.3;
865
- var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot, allowReentryOnChange) => {
866
- const absDistance = Math.abs(distance);
867
- const within = atThreshold || threshold > 0 && absDistance <= threshold;
868
- const updateSnapshot = () => {
869
- setSnapshot({
870
- atThreshold,
871
- contentSize: context.contentSize,
872
- dataLength: context.dataLength,
873
- scrollPosition: context.scrollPosition
874
- });
875
- };
876
- if (!wasReached) {
877
- if (!within) {
878
- return false;
879
- }
880
- onReached(distance);
881
- updateSnapshot();
882
- return true;
883
- }
884
- const reset = !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
885
- if (reset) {
886
- setSnapshot(void 0);
887
- return false;
888
- }
889
- if (within) {
890
- const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
891
- if (changed) {
892
- if (allowReentryOnChange) {
893
- onReached(distance);
894
- }
895
- updateSnapshot();
896
- }
897
- }
898
- return true;
899
- };
900
-
901
634
  // src/utils/hasActiveInitialScroll.ts
902
635
  function hasActiveInitialScroll(state) {
903
636
  return !!(state == null ? void 0 : state.initialScroll) && !state.didFinishInitialScroll;
904
637
  }
905
638
 
906
639
  // src/utils/checkAtBottom.ts
907
- function checkAtBottom(ctx) {
640
+ function checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
908
641
  var _a3;
909
642
  const state = ctx.state;
910
643
  if (!state) {
@@ -918,6 +651,7 @@ function checkAtBottom(ctx) {
918
651
  props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
919
652
  } = state;
920
653
  const contentSize = getContentSize(ctx);
654
+ resetSharedEdgeGateIfOutsideHysteresis(ctx);
921
655
  if (contentSize > 0 && queuedInitialLayout) {
922
656
  const insetEnd = getContentInsetEnd(ctx);
923
657
  const distanceFromEnd = contentSize - scroll - scrollLength - insetEnd;
@@ -944,72 +678,50 @@ function checkAtBottom(ctx) {
944
678
  },
945
679
  (distance) => {
946
680
  var _a4, _b;
947
- return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
681
+ if (canDispatchReachedEdge(ctx, "end", allowedEdge, allowGateCreatedInCurrentCheck)) {
682
+ markReachedEdge(ctx);
683
+ (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
684
+ }
948
685
  },
949
686
  (snapshot) => {
950
687
  state.endReachedSnapshot = snapshot;
951
- },
952
- true
688
+ }
953
689
  );
954
690
  }
955
691
  }
956
692
  }
957
693
 
958
- // src/utils/hasActiveMVCPAnchorLock.ts
959
- function hasActiveMVCPAnchorLock(state) {
960
- const lock = state.mvcpAnchorLock;
961
- if (!lock) {
962
- return false;
963
- }
964
- if (Date.now() > lock.expiresAt) {
965
- state.mvcpAnchorLock = void 0;
966
- return false;
967
- }
968
- return true;
969
- }
970
-
971
- // src/utils/isInMVCPActiveMode.ts
972
- function isInMVCPActiveMode(state) {
973
- return state.dataChangeNeedsScrollUpdate || hasActiveMVCPAnchorLock(state);
974
- }
975
-
976
694
  // src/utils/checkAtTop.ts
977
- function checkAtTop(ctx) {
695
+ function checkAtTop(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
978
696
  const state = ctx == null ? void 0 : ctx.state;
979
697
  if (!state) {
980
698
  return;
981
699
  }
982
700
  const {
983
- dataChangeEpoch,
984
701
  isStartReached,
985
702
  props: { data, onStartReachedThreshold },
986
703
  scroll,
987
704
  scrollLength,
988
705
  startReachedSnapshot,
989
- startReachedSnapshotDataChangeEpoch,
990
706
  totalSize
991
707
  } = state;
992
708
  const dataLength = data.length;
993
709
  const threshold = onStartReachedThreshold * scrollLength;
994
- const dataChanged = startReachedSnapshotDataChangeEpoch !== dataChangeEpoch;
995
- const withinThreshold = threshold > 0 && Math.abs(scroll) <= threshold;
996
- const allowReentryOnDataChange = !!isStartReached && withinThreshold && !!dataChanged && !isInMVCPActiveMode(state);
997
- if (isStartReached && threshold > 0 && scroll > threshold && startReachedSnapshot && (dataChanged || startReachedSnapshot.contentSize !== totalSize || startReachedSnapshot.dataLength !== dataLength)) {
710
+ resetSharedEdgeGateIfOutsideHysteresis(ctx);
711
+ if (isStartReached && threshold > 0 && scroll > threshold && startReachedSnapshot && (startReachedSnapshot.contentSize !== totalSize || startReachedSnapshot.dataLength !== dataLength)) {
998
712
  state.isStartReached = false;
999
713
  state.startReachedSnapshot = void 0;
1000
- state.startReachedSnapshotDataChangeEpoch = void 0;
1001
714
  }
1002
715
  set$(ctx, "isAtStart", scroll <= EDGE_POSITION_EPSILON);
1003
716
  set$(ctx, "isNearStart", scroll <= threshold);
1004
717
  const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || !!state.scrollingTo;
1005
- const shouldDeferDataChangeRefire = isStartReached && withinThreshold && dataChanged && !allowReentryOnDataChange;
1006
- if (!shouldSkipThresholdChecks && !shouldDeferDataChangeRefire) {
718
+ if (!shouldSkipThresholdChecks) {
1007
719
  state.isStartReached = checkThreshold(
1008
720
  scroll,
1009
721
  false,
1010
722
  threshold,
1011
723
  state.isStartReached,
1012
- allowReentryOnDataChange ? void 0 : startReachedSnapshot,
724
+ startReachedSnapshot,
1013
725
  {
1014
726
  contentSize: totalSize,
1015
727
  dataLength,
@@ -1017,21 +729,23 @@ function checkAtTop(ctx) {
1017
729
  },
1018
730
  (distance) => {
1019
731
  var _a3, _b;
1020
- return (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
732
+ if (canDispatchReachedEdge(ctx, "start", allowedEdge, allowGateCreatedInCurrentCheck)) {
733
+ markReachedEdge(ctx);
734
+ (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
735
+ }
1021
736
  },
1022
737
  (snapshot) => {
1023
738
  state.startReachedSnapshot = snapshot;
1024
- state.startReachedSnapshotDataChangeEpoch = snapshot ? dataChangeEpoch : void 0;
1025
- },
1026
- allowReentryOnDataChange
739
+ }
1027
740
  );
1028
741
  }
1029
742
  }
1030
743
 
1031
744
  // src/utils/checkThresholds.ts
1032
- function checkThresholds(ctx) {
1033
- checkAtBottom(ctx);
1034
- checkAtTop(ctx);
745
+ function checkThresholds(ctx, allowedEdge) {
746
+ const allowGateCreatedInCurrentCheck = !ctx.state.edgeReachedGate;
747
+ checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck);
748
+ checkAtTop(ctx, allowedEdge, allowGateCreatedInCurrentCheck);
1035
749
  }
1036
750
 
1037
751
  // src/core/recalculateSettledScroll.ts
@@ -1332,6 +1046,50 @@ function setSize(ctx, itemKey, size, notifyTotalSize = true) {
1332
1046
  sizes.set(itemKey, size);
1333
1047
  }
1334
1048
 
1049
+ // src/utils/helpers.ts
1050
+ function isFunction(obj) {
1051
+ return typeof obj === "function";
1052
+ }
1053
+ function isArray(obj) {
1054
+ return Array.isArray(obj);
1055
+ }
1056
+ var warned = /* @__PURE__ */ new Set();
1057
+ function warnDevOnce(id, text) {
1058
+ if (IS_DEV && !warned.has(id)) {
1059
+ warned.add(id);
1060
+ console.warn(`[legend-list] ${text}`);
1061
+ }
1062
+ }
1063
+ function roundSize(size) {
1064
+ return Math.floor(size * 8) / 8;
1065
+ }
1066
+ function isNullOrUndefined(value) {
1067
+ return value === null || value === void 0;
1068
+ }
1069
+ function getPadding(s, type) {
1070
+ var _a3, _b, _c;
1071
+ const axisPadding = type === "Left" || type === "Right" ? s.paddingHorizontal : s.paddingVertical;
1072
+ return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : axisPadding) != null ? _b : s.padding) != null ? _c : 0;
1073
+ }
1074
+ function extractPadding(style, contentContainerStyle, type) {
1075
+ return getPadding(style, type) + getPadding(contentContainerStyle, type);
1076
+ }
1077
+ function findContainerId(ctx, key) {
1078
+ var _a3, _b;
1079
+ const directMatch = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.containerItemKeys) == null ? void 0 : _b.get(key);
1080
+ if (directMatch !== void 0) {
1081
+ return directMatch;
1082
+ }
1083
+ const numContainers = peek$(ctx, "numContainers");
1084
+ for (let i = 0; i < numContainers; i++) {
1085
+ const itemKey = peek$(ctx, `containerItemKey${i}`);
1086
+ if (itemKey === key) {
1087
+ return i;
1088
+ }
1089
+ }
1090
+ return -1;
1091
+ }
1092
+
1335
1093
  // src/utils/getItemSize.ts
1336
1094
  function getKnownOrFixedSize(ctx, key, index, data, resolved) {
1337
1095
  var _a3, _b;
@@ -2007,8 +1765,26 @@ var getScrollVelocity = (state) => {
2007
1765
  weightedVelocity += scrollDiff / timeDiff * weight;
2008
1766
  totalWeight += weight;
2009
1767
  }
2010
- return totalWeight > 0 ? weightedVelocity / totalWeight : 0;
2011
- };
1768
+ return totalWeight > 0 ? weightedVelocity / totalWeight : 0;
1769
+ };
1770
+
1771
+ // src/utils/hasActiveMVCPAnchorLock.ts
1772
+ function hasActiveMVCPAnchorLock(state) {
1773
+ const lock = state.mvcpAnchorLock;
1774
+ if (!lock) {
1775
+ return false;
1776
+ }
1777
+ if (Date.now() > lock.expiresAt) {
1778
+ state.mvcpAnchorLock = void 0;
1779
+ return false;
1780
+ }
1781
+ return true;
1782
+ }
1783
+
1784
+ // src/utils/isInMVCPActiveMode.ts
1785
+ function isInMVCPActiveMode(state) {
1786
+ return state.dataChangeNeedsScrollUpdate || hasActiveMVCPAnchorLock(state);
1787
+ }
2012
1788
 
2013
1789
  // src/core/updateScroll.ts
2014
1790
  function updateScroll(ctx, newScroll, forceUpdate, options) {
@@ -2047,6 +1823,8 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2047
1823
  state.scroll = newScroll;
2048
1824
  state.scrollTime = currentTime;
2049
1825
  const scrollDelta = Math.abs(newScroll - prevScroll);
1826
+ const isUserScrollEvent = !!(options == null ? void 0 : options.fromNativeScrollEvent) && scrollDelta > 0.1 && !adjustChanged && scrollingTo === void 0 && !state.pendingNativeMVCPAdjust;
1827
+ const allowedEdge = isUserScrollEvent ? beginReachedEdgeUserScroll(ctx, newScroll - prevScroll) : void 0;
2050
1828
  const didResolvePendingNativeMVCPAdjust = resolvePendingNativeMVCPAdjust(ctx, newScroll);
2051
1829
  const scrollLength = state.scrollLength;
2052
1830
  const isLargeUserScrollJump = scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength && !state.pendingNativeMVCPAdjust;
@@ -2054,7 +1832,7 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2054
1832
  updateAdaptiveRender(ctx, scrollVelocity, { forceLight: isLargeUserScrollJump });
2055
1833
  const lastCalculated = state.scrollLastCalculate;
2056
1834
  const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
2057
- const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
1835
+ const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || allowedEdge !== void 0 || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
2058
1836
  if (shouldUpdate) {
2059
1837
  state.scrollLastCalculate = state.scroll;
2060
1838
  state.ignoreScrollFromMVCPIgnored = false;
@@ -2069,7 +1847,7 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2069
1847
  calculateItemsParams.drawDistanceMode = "visible-first";
2070
1848
  }
2071
1849
  (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, calculateItemsParams);
2072
- checkThresholds(ctx);
1850
+ checkThresholds(ctx, allowedEdge);
2073
1851
  };
2074
1852
  if (isLargeUserScrollJump) {
2075
1853
  state.mvcpAnchorLock = void 0;
@@ -3030,6 +2808,47 @@ function abortBootstrapInitialScroll(ctx) {
3030
2808
  }
3031
2809
  }
3032
2810
 
2811
+ // src/core/containerItemMetadata.ts
2812
+ function createContainerItemMetadata(state, itemIndex, itemData, itemType) {
2813
+ return {
2814
+ dataChangeEpoch: state.dataChangeEpoch,
2815
+ getFixedItemSize: state.props.getFixedItemSize,
2816
+ getItemType: state.props.getItemType,
2817
+ itemData,
2818
+ itemIndex,
2819
+ itemType
2820
+ };
2821
+ }
2822
+ function resolveContainerItemMetadata(state, containerId, itemIndex, itemData) {
2823
+ var _a3, _b;
2824
+ const { getFixedItemSize, getItemType } = state.props;
2825
+ const previousMetadata = state.containerItemMetadata.get(containerId);
2826
+ let metadata;
2827
+ if ((previousMetadata == null ? void 0 : previousMetadata.dataChangeEpoch) === state.dataChangeEpoch && previousMetadata.getItemType === getItemType && previousMetadata.itemData === itemData && previousMetadata.itemIndex === itemIndex) {
2828
+ metadata = previousMetadata;
2829
+ } else {
2830
+ const itemType = getItemType ? (_a3 = getItemType(itemData, itemIndex)) != null ? _a3 : "" : void 0;
2831
+ metadata = createContainerItemMetadata(state, itemIndex, itemData, itemType);
2832
+ state.containerItemMetadata.set(containerId, metadata);
2833
+ }
2834
+ if (metadata.getFixedItemSize !== getFixedItemSize) {
2835
+ metadata.didResolveFixedItemSize = false;
2836
+ metadata.fixedItemSize = void 0;
2837
+ metadata.getFixedItemSize = getFixedItemSize;
2838
+ }
2839
+ if (getFixedItemSize && !metadata.didResolveFixedItemSize) {
2840
+ metadata.fixedItemSize = getFixedItemSize(itemData, itemIndex, (_b = metadata.itemType) != null ? _b : "");
2841
+ metadata.didResolveFixedItemSize = true;
2842
+ }
2843
+ return metadata;
2844
+ }
2845
+ function invalidateContainerFixedItemSizes(state) {
2846
+ for (const metadata of state.containerItemMetadata.values()) {
2847
+ metadata.didResolveFixedItemSize = false;
2848
+ metadata.fixedItemSize = void 0;
2849
+ }
2850
+ }
2851
+
3033
2852
  // src/core/checkFinishedScroll.ts
3034
2853
  var INITIAL_SCROLL_MAX_FALLBACK_CHECKS = 20;
3035
2854
  var INITIAL_SCROLL_COMPLETION_TARGET_EPSILON = 1;
@@ -3303,6 +3122,52 @@ function resetLayoutCachesForDataChange(state) {
3303
3122
  state.columnSpans.length = 0;
3304
3123
  }
3305
3124
 
3125
+ // src/core/scheduleContainerLayout.ts
3126
+ function getContainerLayoutEffectScope(ctx) {
3127
+ var _a3;
3128
+ const scheduledIds = ctx.pendingContainerIds;
3129
+ ctx.pendingContainerIds = void 0;
3130
+ if (scheduledIds === void 0) {
3131
+ return void 0;
3132
+ }
3133
+ const state = ctx.state;
3134
+ let targetContainerIds = scheduledIds;
3135
+ if (targetContainerIds && ((_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys.size)) {
3136
+ targetContainerIds = new Set(targetContainerIds);
3137
+ for (const itemKey of state.userScrollAnchorReset.keys) {
3138
+ const containerId = state.containerItemKeys.get(itemKey);
3139
+ if (containerId !== void 0) {
3140
+ targetContainerIds.add(containerId);
3141
+ }
3142
+ }
3143
+ }
3144
+ return targetContainerIds;
3145
+ }
3146
+ function scheduleContainerLayout(ctx, target) {
3147
+ var _a3;
3148
+ const isAlreadyScheduled = ctx.pendingContainerIds !== void 0;
3149
+ const previousIds = ctx.pendingContainerIds;
3150
+ if (target === void 0) {
3151
+ ctx.pendingContainerIds = null;
3152
+ } else if (previousIds !== null) {
3153
+ let nextIds = previousIds;
3154
+ if (!nextIds) {
3155
+ nextIds = typeof target === "number" ? /* @__PURE__ */ new Set([target]) : new Set(target);
3156
+ } else if (typeof target === "number") {
3157
+ nextIds.add(target);
3158
+ } else {
3159
+ for (const containerId of target) {
3160
+ nextIds.add(containerId);
3161
+ }
3162
+ }
3163
+ ctx.pendingContainerIds = nextIds;
3164
+ }
3165
+ if (!isAlreadyScheduled) {
3166
+ const nextEpoch = ((_a3 = peek$(ctx, "containerLayoutEpoch")) != null ? _a3 : 0) + 1;
3167
+ set$(ctx, "containerLayoutEpoch", nextEpoch);
3168
+ }
3169
+ }
3170
+
3306
3171
  // src/core/syncMountedContainer.ts
3307
3172
  function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3308
3173
  var _a3, _b, _c, _d, _e, _f, _g, _h, _i;
@@ -3346,14 +3211,13 @@ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3346
3211
  set$(ctx, `containerSpan${containerIndex}`, span);
3347
3212
  }
3348
3213
  }
3349
- const prevItemInfo = peek$(ctx, `containerItemInfo${containerIndex}`);
3214
+ const prevIndex = peek$(ctx, `containerItemIndex${containerIndex}`);
3215
+ if (prevIndex !== itemIndex) {
3216
+ set$(ctx, `containerItemIndex${containerIndex}`, itemIndex);
3217
+ }
3350
3218
  const prevData = peek$(ctx, `containerItemData${containerIndex}`);
3351
- let itemInfoValue = prevData;
3352
- let didChangeItemInfo = (prevItemInfo == null ? void 0 : prevItemInfo.itemKey) !== itemKey || (prevItemInfo == null ? void 0 : prevItemInfo.index) !== itemIndex || (prevItemInfo == null ? void 0 : prevItemInfo.value) !== prevData;
3353
3219
  const updateData = () => {
3354
3220
  set$(ctx, `containerItemData${containerIndex}`, item);
3355
- itemInfoValue = item;
3356
- didChangeItemInfo = true;
3357
3221
  didRefreshData = true;
3358
3222
  };
3359
3223
  if (prevData !== item) {
@@ -3388,13 +3252,6 @@ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3388
3252
  }
3389
3253
  }
3390
3254
  }
3391
- if (didChangeItemInfo) {
3392
- set$(ctx, `containerItemInfo${containerIndex}`, {
3393
- index: itemIndex,
3394
- itemKey,
3395
- value: itemInfoValue
3396
- });
3397
- }
3398
3255
  return { didChangePosition, didRefreshData };
3399
3256
  }
3400
3257
 
@@ -3759,19 +3616,29 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
3759
3616
  if (previousViewableItems) {
3760
3617
  for (const viewToken of previousViewableItems) {
3761
3618
  previousViewableKeys.add(viewToken.key);
3619
+ const currentIndex = state.indexByKey.get(viewToken.key);
3620
+ const currentItem = currentIndex !== void 0 ? data[currentIndex] : void 0;
3762
3621
  const containerId = findContainerId(ctx, viewToken.key);
3763
- if (!checkIsViewable(
3764
- state,
3765
- ctx,
3766
- viewabilityConfig,
3767
- containerId,
3768
- viewToken.key,
3769
- scrollSize,
3770
- viewToken.item,
3771
- viewToken.index
3772
- )) {
3773
- viewToken.isViewable = false;
3774
- changed.push(viewToken);
3622
+ let isStillViewable = false;
3623
+ if (currentIndex !== void 0 && currentItem !== void 0) {
3624
+ isStillViewable = checkIsViewable(
3625
+ state,
3626
+ ctx,
3627
+ viewabilityConfig,
3628
+ containerId,
3629
+ viewToken.key,
3630
+ scrollSize,
3631
+ currentItem,
3632
+ currentIndex
3633
+ );
3634
+ }
3635
+ if (!isStillViewable) {
3636
+ changed.push({
3637
+ ...viewToken,
3638
+ index: currentIndex != null ? currentIndex : viewToken.index,
3639
+ isViewable: false,
3640
+ item: currentItem != null ? currentItem : viewToken.item
3641
+ });
3775
3642
  }
3776
3643
  }
3777
3644
  }
@@ -3928,128 +3795,102 @@ function getExpandedContainerPoolSize(dataLength, numContainers) {
3928
3795
 
3929
3796
  // src/utils/findAvailableContainers.ts
3930
3797
  function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffered, pendingRemoval, getRequiredItemType, protectedKeys) {
3798
+ var _a3;
3931
3799
  const numNeeded = needNewContainers.length;
3932
3800
  if (numNeeded === 0) {
3933
3801
  return [];
3934
3802
  }
3935
3803
  const numContainers = peek$(ctx, "numContainers");
3936
3804
  const state = ctx.state;
3937
- const { stickyContainerPool, containerItemTypes } = state;
3805
+ const { containerItemMetadata, stickyContainerPool } = state;
3938
3806
  const shouldAvoidAssignedContainerReuse = state.props.recycleItems && !!state.props.positionComponentInternal;
3939
- const allocations = [];
3940
3807
  const pendingRemovalSet = pendingRemoval.length > 0 ? new Set(pendingRemoval) : void 0;
3941
- let pendingRemovalChanged = false;
3808
+ const requests = needNewContainers.map((itemIndex, order) => ({
3809
+ isSticky: state.props.stickyHeaderIndicesSet.has(itemIndex),
3810
+ itemIndex,
3811
+ itemType: getRequiredItemType == null ? void 0 : getRequiredItemType(itemIndex),
3812
+ order
3813
+ }));
3814
+ const normalRequests = requests.filter((request) => !request.isSticky);
3815
+ const stickyRequests = requests.filter((request) => request.isSticky);
3816
+ const normalCandidates = [];
3817
+ const stickyCandidates = [];
3818
+ for (let containerIndex = 0; containerIndex < numContainers; containerIndex++) {
3819
+ const key = peek$(ctx, `containerItemKey${containerIndex}`);
3820
+ const isPendingRemoval = !!(pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(containerIndex));
3821
+ const isProtected = !!key && !!(protectedKeys == null ? void 0 : protectedKeys.has(key)) && state.indexByKey.has(key);
3822
+ if (isProtected) {
3823
+ continue;
3824
+ }
3825
+ if (stickyContainerPool.has(containerIndex)) {
3826
+ if (key === void 0 || isPendingRemoval) {
3827
+ stickyCandidates.push({ containerIndex, distance: Number.POSITIVE_INFINITY });
3828
+ }
3829
+ } else if (key === void 0 || isPendingRemoval) {
3830
+ normalCandidates.push({ containerIndex, distance: Number.POSITIVE_INFINITY });
3831
+ } else if (!shouldAvoidAssignedContainerReuse) {
3832
+ const index = state.indexByKey.get(key);
3833
+ if (index !== void 0 && (index < startBuffered || index > endBuffered)) {
3834
+ const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
3835
+ normalCandidates.push({ containerIndex, distance });
3836
+ }
3837
+ }
3838
+ }
3839
+ normalCandidates.sort(comparatorByDistance);
3840
+ const allocations = new Array(numNeeded);
3942
3841
  let nextNewContainerIndex = numContainers;
3943
- const usedContainers = /* @__PURE__ */ new Set();
3944
- let availableContainers;
3945
- const stickyHeaderIndicesSet = state.props.stickyHeaderIndicesSet;
3946
- const canReuseContainer = (containerIndex, requiredType) => {
3947
- if (!requiredType) return true;
3948
- const existingType = containerItemTypes.get(containerIndex);
3949
- if (!existingType) return true;
3950
- return existingType === requiredType;
3951
- };
3952
- const pushAllocation = (itemIndex, itemType, containerIndex) => {
3953
- allocations.push({
3842
+ let pendingRemovalChanged = false;
3843
+ const assign = (request, containerIndex) => {
3844
+ allocations[request.order] = {
3954
3845
  containerIndex,
3955
- itemIndex,
3956
- itemType
3957
- });
3958
- usedContainers.add(containerIndex);
3846
+ itemIndex: request.itemIndex,
3847
+ itemType: request.itemType
3848
+ };
3959
3849
  if (pendingRemovalSet == null ? void 0 : pendingRemovalSet.delete(containerIndex)) {
3960
3850
  pendingRemovalChanged = true;
3961
3851
  }
3962
3852
  };
3963
- const pushNewContainer = (itemIndex, itemType, isSticky) => {
3964
- const newContainerIndex = nextNewContainerIndex++;
3965
- pushAllocation(itemIndex, itemType, newContainerIndex);
3966
- if (isSticky) {
3967
- stickyContainerPool.add(newContainerIndex);
3968
- }
3969
- return newContainerIndex;
3970
- };
3971
- const canUseContainer = (containerIndex, itemType) => {
3972
- if (usedContainers.has(containerIndex) || stickyContainerPool.has(containerIndex)) {
3973
- return false;
3974
- }
3975
- const key = peek$(ctx, `containerItemKey${containerIndex}`);
3976
- const isPending = !!(pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(containerIndex));
3977
- return (key === void 0 || isPending) && canReuseContainer(containerIndex, itemType);
3978
- };
3979
- const findStickyContainer = (itemType) => {
3980
- let foundContainer;
3981
- for (const containerIndex of stickyContainerPool) {
3982
- if (!usedContainers.has(containerIndex)) {
3983
- const key = peek$(ctx, `containerItemKey${containerIndex}`);
3984
- const isPendingRemoval = !!(pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(containerIndex));
3985
- if ((key === void 0 || isPendingRemoval) && canReuseContainer(containerIndex, itemType)) {
3986
- foundContainer = containerIndex;
3987
- break;
3988
- }
3989
- }
3990
- }
3991
- return foundContainer;
3992
- };
3993
- const findUnassignedOrPendingContainer = (itemType) => {
3994
- let foundContainer;
3995
- for (let containerIndex = 0; containerIndex < numContainers && foundContainer === void 0; containerIndex++) {
3996
- if (canUseContainer(containerIndex, itemType)) {
3997
- foundContainer = containerIndex;
3853
+ const assignMatching = (pendingRequests, candidates, matches) => {
3854
+ for (const request of pendingRequests) {
3855
+ if (allocations[request.order]) {
3856
+ continue;
3998
3857
  }
3999
- }
4000
- return foundContainer;
4001
- };
4002
- const getAvailableContainers = () => {
4003
- if (!availableContainers) {
4004
- availableContainers = [];
4005
- if (!shouldAvoidAssignedContainerReuse) {
4006
- for (let containerIndex = 0; containerIndex < numContainers; containerIndex++) {
4007
- if (usedContainers.has(containerIndex) || stickyContainerPool.has(containerIndex)) {
4008
- continue;
4009
- }
4010
- const key = peek$(ctx, `containerItemKey${containerIndex}`);
4011
- if (key === void 0) continue;
4012
- if ((protectedKeys == null ? void 0 : protectedKeys.has(key)) && state.indexByKey.has(key)) continue;
4013
- const index = state.indexByKey.get(key);
4014
- const isOutOfView = index < startBuffered || index > endBuffered;
4015
- if (isOutOfView) {
4016
- const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
4017
- availableContainers.push({ distance, index: containerIndex });
4018
- }
3858
+ const candidateIndex = candidates.findIndex(
3859
+ (candidate) => {
3860
+ var _a4;
3861
+ return matches((_a4 = containerItemMetadata.get(candidate.containerIndex)) == null ? void 0 : _a4.itemType, request.itemType);
4019
3862
  }
4020
- availableContainers.sort(comparatorByDistance);
3863
+ );
3864
+ if (candidateIndex !== -1) {
3865
+ const [candidate] = candidates.splice(candidateIndex, 1);
3866
+ assign(request, candidate.containerIndex);
4021
3867
  }
4022
3868
  }
4023
- return availableContainers;
4024
3869
  };
4025
- const findAvailableContainer = (itemType) => {
4026
- const containers = getAvailableContainers();
4027
- let matchIndex = -1;
4028
- for (let i = 0; i < containers.length && matchIndex === -1; i++) {
4029
- const containerIndex = containers[i].index;
4030
- if (!usedContainers.has(containerIndex) && canReuseContainer(containerIndex, itemType)) {
4031
- matchIndex = i;
4032
- }
3870
+ const assignFromPool = (pendingRequests, candidates, allowCrossType) => {
3871
+ if (getRequiredItemType) {
3872
+ assignMatching(
3873
+ pendingRequests,
3874
+ candidates,
3875
+ (containerType, requestType) => requestType !== void 0 && containerType === requestType
3876
+ );
3877
+ }
3878
+ assignMatching(pendingRequests, candidates, (containerType) => containerType === void 0);
3879
+ if (allowCrossType) {
3880
+ assignMatching(pendingRequests, candidates, () => true);
4033
3881
  }
4034
- return matchIndex === -1 ? void 0 : containers.splice(matchIndex, 1)[0].index;
4035
3882
  };
4036
- for (const itemIndex of needNewContainers) {
4037
- const itemType = getRequiredItemType == null ? void 0 : getRequiredItemType(itemIndex);
4038
- const isSticky = stickyHeaderIndicesSet.has(itemIndex);
4039
- let containerIndex;
4040
- if (isSticky) {
4041
- containerIndex = findStickyContainer(itemType);
4042
- } else {
4043
- containerIndex = findUnassignedOrPendingContainer(itemType);
4044
- if (containerIndex === void 0) {
4045
- containerIndex = findAvailableContainer(itemType);
4046
- }
3883
+ assignFromPool(normalRequests, normalCandidates, true);
3884
+ assignFromPool(stickyRequests, stickyCandidates, false);
3885
+ for (const request of requests) {
3886
+ if (allocations[request.order]) {
3887
+ continue;
4047
3888
  }
4048
- if (containerIndex !== void 0) {
4049
- pushAllocation(itemIndex, itemType, containerIndex);
4050
- } else {
4051
- pushNewContainer(itemIndex, itemType, isSticky);
3889
+ const containerIndex = nextNewContainerIndex++;
3890
+ if (request.isSticky) {
3891
+ stickyContainerPool.add(containerIndex);
4052
3892
  }
3893
+ assign(request, containerIndex);
4053
3894
  }
4054
3895
  if (pendingRemovalChanged) {
4055
3896
  pendingRemoval.length = 0;
@@ -4059,18 +3900,21 @@ function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffe
4059
3900
  }
4060
3901
  }
4061
3902
  }
4062
- if (IS_DEV && nextNewContainerIndex > peek$(ctx, "numContainersPooled")) {
4063
- console.warn(
4064
- "[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.",
4065
- {
4066
- debugInfo: {
4067
- numContainers,
4068
- numContainersPooled: peek$(ctx, "numContainersPooled"),
4069
- numNeeded,
4070
- stillNeeded: nextNewContainerIndex - numContainers
3903
+ if (IS_DEV) {
3904
+ const numContainersPooled = (_a3 = peek$(ctx, "numContainersPooled")) != null ? _a3 : Number.POSITIVE_INFINITY;
3905
+ if (nextNewContainerIndex > numContainersPooled) {
3906
+ console.warn(
3907
+ "[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.",
3908
+ {
3909
+ debugInfo: {
3910
+ numContainers,
3911
+ numContainersPooled,
3912
+ numNeeded,
3913
+ stillNeeded: nextNewContainerIndex - numContainers
3914
+ }
4071
3915
  }
4072
- }
4073
- );
3916
+ );
3917
+ }
4074
3918
  }
4075
3919
  return allocations;
4076
3920
  }
@@ -4305,7 +4149,7 @@ function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, sc
4305
4149
  function calculateItemsInView(ctx, params = {}) {
4306
4150
  const state = ctx.state;
4307
4151
  batchedUpdates(() => {
4308
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t;
4152
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u;
4309
4153
  const {
4310
4154
  columns,
4311
4155
  containerItemKeys,
@@ -4333,6 +4177,7 @@ function calculateItemsInView(ctx, params = {}) {
4333
4177
  return;
4334
4178
  }
4335
4179
  let totalSize = getContentSize(ctx);
4180
+ let changedContainerIds;
4336
4181
  const topPad = peek$(ctx, "stylePaddingTop") + peek$(ctx, "alignItemsAtEndPadding") + peek$(ctx, "headerSize");
4337
4182
  const numColumns = peek$(ctx, "numColumns");
4338
4183
  const speed = (_b = params.scrollVelocity) != null ? _b : getScrollVelocity(state);
@@ -4458,7 +4303,7 @@ function calculateItemsInView(ctx, params = {}) {
4458
4303
  const scrollAdjustPendingBeforeMVCP = (_f = peek$(ctx, "scrollAdjustPending")) != null ? _f : 0;
4459
4304
  checkMVCP == null ? void 0 : checkMVCP();
4460
4305
  const didMVCPAdjustScroll = !!checkMVCP && (state.scroll !== scrollBeforeMVCP || ((_g = peek$(ctx, "scrollAdjustPending")) != null ? _g : 0) !== scrollAdjustPendingBeforeMVCP);
4461
- if (didMVCPAdjustScroll && (initialScroll || state.scrollingTo)) {
4306
+ if (didMVCPAdjustScroll) {
4462
4307
  updateScroll2(state.scroll);
4463
4308
  updateScrollRange();
4464
4309
  }
@@ -4638,22 +4483,20 @@ function calculateItemsInView(ctx, params = {}) {
4638
4483
  if (oldKey && oldKey !== id) {
4639
4484
  containerItemKeys.delete(oldKey);
4640
4485
  }
4486
+ if (oldKey !== id) {
4487
+ changedContainerIds != null ? changedContainerIds : changedContainerIds = /* @__PURE__ */ new Set();
4488
+ changedContainerIds.add(containerIndex);
4489
+ state.containerItemGenerations[containerIndex] = ((_p = state.containerItemGenerations[containerIndex]) != null ? _p : 0) + 1;
4490
+ }
4641
4491
  set$(ctx, `containerItemKey${containerIndex}`, id);
4492
+ set$(ctx, `containerItemIndex${containerIndex}`, i);
4642
4493
  set$(ctx, `containerItemData${containerIndex}`, data[i]);
4643
- set$(ctx, `containerItemInfo${containerIndex}`, {
4644
- index: i,
4645
- itemKey: id,
4646
- value: data[i]
4647
- });
4648
- if (allocation.itemType !== void 0) {
4649
- state.containerItemTypes.set(containerIndex, allocation.itemType);
4650
- }
4494
+ state.containerItemMetadata.set(
4495
+ containerIndex,
4496
+ createContainerItemMetadata(state, i, data[i], allocation.itemType)
4497
+ );
4651
4498
  containerItemKeys.set(id, containerIndex);
4652
- (_p = state.userScrollAnchorReset) == null ? void 0 : _p.keys.add(id);
4653
- {
4654
- (_q = state.pendingLayoutEffectMeasurements) != null ? _q : state.pendingLayoutEffectMeasurements = /* @__PURE__ */ new Set();
4655
- state.pendingLayoutEffectMeasurements.add(id);
4656
- }
4499
+ (_q = state.userScrollAnchorReset) == null ? void 0 : _q.keys.add(id);
4657
4500
  const containerSticky = `containerSticky${containerIndex}`;
4658
4501
  const isSticky = stickyHeaderIndicesSet.has(i);
4659
4502
  const isPinnedRender = isPinnedRenderIndex(i);
@@ -4703,15 +4546,18 @@ function calculateItemsInView(ctx, params = {}) {
4703
4546
  if (pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(i)) {
4704
4547
  if (itemKey !== void 0) {
4705
4548
  containerItemKeys.delete(itemKey);
4549
+ changedContainerIds != null ? changedContainerIds : changedContainerIds = /* @__PURE__ */ new Set();
4550
+ changedContainerIds.add(i);
4551
+ state.containerItemGenerations[i] = ((_t = state.containerItemGenerations[i]) != null ? _t : 0) + 1;
4706
4552
  }
4707
- state.containerItemTypes.delete(i);
4553
+ state.containerItemMetadata.delete(i);
4708
4554
  if (state.stickyContainerPool.has(i)) {
4709
4555
  set$(ctx, `containerSticky${i}`, false);
4710
4556
  state.stickyContainerPool.delete(i);
4711
4557
  }
4712
4558
  set$(ctx, `containerItemKey${i}`, void 0);
4559
+ set$(ctx, `containerItemIndex${i}`, void 0);
4713
4560
  set$(ctx, `containerItemData${i}`, void 0);
4714
- set$(ctx, `containerItemInfo${i}`, void 0);
4715
4561
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
4716
4562
  set$(ctx, `containerColumn${i}`, -1);
4717
4563
  set$(ctx, `containerSpan${i}`, 1);
@@ -4725,6 +4571,9 @@ function calculateItemsInView(ctx, params = {}) {
4725
4571
  }
4726
4572
  }
4727
4573
  }
4574
+ if (changedContainerIds && (IsNewArchitecture)) {
4575
+ scheduleContainerLayout(ctx, changedContainerIds);
4576
+ }
4728
4577
  if (didChangePositions) {
4729
4578
  set$(ctx, "lastPositionUpdate", Date.now());
4730
4579
  }
@@ -4741,20 +4590,18 @@ function calculateItemsInView(ctx, params = {}) {
4741
4590
  }
4742
4591
  }
4743
4592
  if (viewabilityConfigCallbackPairs && visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null) {
4744
- if (!didMVCPAdjustScroll) {
4745
- updateViewableItems(
4746
- ctx.state,
4747
- ctx,
4748
- viewabilityConfigCallbackPairs,
4749
- scrollLength,
4750
- visibleRange.startNoBuffer,
4751
- visibleRange.endNoBuffer,
4752
- startBuffered != null ? startBuffered : visibleRange.startNoBuffer,
4753
- endBuffered != null ? endBuffered : visibleRange.endNoBuffer
4754
- );
4755
- }
4593
+ updateViewableItems(
4594
+ ctx.state,
4595
+ ctx,
4596
+ viewabilityConfigCallbackPairs,
4597
+ scrollLength,
4598
+ visibleRange.startNoBuffer,
4599
+ visibleRange.endNoBuffer,
4600
+ startBuffered != null ? startBuffered : visibleRange.startNoBuffer,
4601
+ endBuffered != null ? endBuffered : visibleRange.endNoBuffer
4602
+ );
4756
4603
  }
4757
- (_t = stickyState == null ? void 0 : stickyState.finishCalculateItemsInView) == null ? void 0 : _t.call(stickyState);
4604
+ (_u = stickyState == null ? void 0 : stickyState.finishCalculateItemsInView) == null ? void 0 : _u.call(stickyState);
4758
4605
  });
4759
4606
  }
4760
4607
 
@@ -4846,228 +4693,516 @@ function updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal) {
4846
4693
  set$(ctx, "otherAxisSize", otherAxisSize);
4847
4694
  }
4848
4695
  }
4849
- }
4850
- function mergeItemSizeUpdateResult(result, next) {
4851
- result.didChange || (result.didChange = next.didChange);
4852
- result.didMeasureUserScrollAnchorResetItem || (result.didMeasureUserScrollAnchorResetItem = next.didMeasureUserScrollAnchorResetItem);
4853
- result.needsRecalculate || (result.needsRecalculate = next.needsRecalculate);
4854
- result.shouldMaintainScrollAtEnd || (result.shouldMaintainScrollAtEnd = next.shouldMaintainScrollAtEnd);
4855
- }
4856
- var batchedItemSizeRecalculates = /* @__PURE__ */ new WeakMap();
4857
- function flushBatchedItemSizeRecalculate(ctx, didFallback = false, expectedResult) {
4858
- const result = batchedItemSizeRecalculates.get(ctx);
4859
- if (!result || expectedResult && result !== expectedResult) {
4860
- return;
4861
- }
4862
- batchedItemSizeRecalculates.delete(ctx);
4863
- if (didFallback) {
4864
- ctx.state.pendingLayoutEffectMeasurements = void 0;
4696
+ }
4697
+ var activeItemSizeBatches;
4698
+ function batchItemSizeUpdates(runUpdates) {
4699
+ const isOuterBatch = activeItemSizeBatches === void 0;
4700
+ activeItemSizeBatches != null ? activeItemSizeBatches : activeItemSizeBatches = /* @__PURE__ */ new Map();
4701
+ try {
4702
+ runUpdates();
4703
+ } finally {
4704
+ if (isOuterBatch) {
4705
+ const batches = activeItemSizeBatches;
4706
+ activeItemSizeBatches = void 0;
4707
+ for (const [ctx, measurements] of batches) {
4708
+ updateItemSizesBatch(ctx, measurements);
4709
+ }
4710
+ }
4711
+ }
4712
+ }
4713
+ function mergeItemSizeUpdateResult(result, next) {
4714
+ result.didChange || (result.didChange = next.didChange);
4715
+ result.didMeasureUserScrollAnchorResetItem || (result.didMeasureUserScrollAnchorResetItem = next.didMeasureUserScrollAnchorResetItem);
4716
+ result.needsRecalculate || (result.needsRecalculate = next.needsRecalculate);
4717
+ result.shouldMaintainScrollAtEnd || (result.shouldMaintainScrollAtEnd = next.shouldMaintainScrollAtEnd);
4718
+ }
4719
+ function flushItemSizeUpdates(ctx, result) {
4720
+ var _a3;
4721
+ const state = ctx.state;
4722
+ if (result.needsRecalculate) {
4723
+ state.scrollForNextCalculateItemsInView = void 0;
4724
+ runOrScheduleMVCPRecalculate(ctx);
4725
+ } else if (result.didMeasureUserScrollAnchorResetItem && ((_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys.size) === 0) {
4726
+ state.userScrollAnchorReset = void 0;
4727
+ }
4728
+ if (result.didChange && result.shouldMaintainScrollAtEnd) {
4729
+ doMaintainScrollAtEnd(ctx);
4730
+ }
4731
+ }
4732
+ function updateItemSizes(ctx, measurement) {
4733
+ if (activeItemSizeBatches) {
4734
+ const measurements = activeItemSizeBatches.get(ctx);
4735
+ if (measurements) {
4736
+ measurements.push(measurement);
4737
+ } else {
4738
+ activeItemSizeBatches.set(ctx, [measurement]);
4739
+ }
4740
+ } else {
4741
+ updateItemSizesBatch(ctx, [measurement]);
4742
+ }
4743
+ }
4744
+ function updateItemSizesBatch(ctx, measurements) {
4745
+ var _a3;
4746
+ const state = ctx.state;
4747
+ const result = {};
4748
+ for (const measurement of measurements) {
4749
+ const ownsMeasuredItem = measurement.containerId === void 0 || peek$(ctx, `containerItemKey${measurement.containerId}`) === measurement.itemKey;
4750
+ if (ownsMeasuredItem) {
4751
+ const index = state.indexByKey.get(measurement.itemKey);
4752
+ const itemData = index === void 0 ? void 0 : (_a3 = state.props.data) == null ? void 0 : _a3[index];
4753
+ const metadata = measurement.containerId !== void 0 && index !== void 0 && itemData !== void 0 ? resolveContainerItemMetadata(state, measurement.containerId, index, itemData) : void 0;
4754
+ const nextResult = applyItemSize(ctx, measurement.itemKey, measurement.size, metadata);
4755
+ mergeItemSizeUpdateResult(result, nextResult);
4756
+ }
4757
+ }
4758
+ flushItemSizeUpdates(ctx, result);
4759
+ }
4760
+ function applyItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
4761
+ var _a3, _b;
4762
+ const state = ctx.state;
4763
+ const userScrollAnchorReset = state.userScrollAnchorReset;
4764
+ const didMeasureUserScrollAnchorResetItem = !!(userScrollAnchorReset == null ? void 0 : userScrollAnchorReset.keys.delete(itemKey));
4765
+ const {
4766
+ didContainersLayout,
4767
+ sizesKnown,
4768
+ props: { getFixedItemSize, getItemType, horizontal, onItemSizeChanged, data, maintainScrollAtEnd }
4769
+ } = state;
4770
+ if (!data) return { didMeasureUserScrollAnchorResetItem };
4771
+ const index = state.indexByKey.get(itemKey);
4772
+ if (getFixedItemSize) {
4773
+ if (index === void 0) {
4774
+ return { didMeasureUserScrollAnchorResetItem };
4775
+ }
4776
+ const itemData = state.props.data[index];
4777
+ if (itemData === void 0) {
4778
+ return { didMeasureUserScrollAnchorResetItem };
4779
+ }
4780
+ if (!(resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize)) {
4781
+ const type = (_b = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemType) != null ? _b : getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
4782
+ resolvedMeasurementItem = {
4783
+ didResolveFixedItemSize: true,
4784
+ fixedItemSize: getFixedItemSize(itemData, index, type),
4785
+ itemType: type
4786
+ };
4787
+ }
4788
+ const size2 = resolvedMeasurementItem.fixedItemSize;
4789
+ if (size2 !== void 0 && size2 === sizesKnown.get(itemKey)) {
4790
+ updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
4791
+ return { didMeasureUserScrollAnchorResetItem };
4792
+ }
4793
+ }
4794
+ let needsRecalculate = !didContainersLayout;
4795
+ let shouldMaintainScrollAtEnd = false;
4796
+ let minIndexSizeChanged;
4797
+ const prevSizeKnown = state.sizesKnown.get(itemKey);
4798
+ const diff = updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem);
4799
+ const size = roundSize(horizontal ? sizeObj.width : sizeObj.height);
4800
+ if (diff !== 0) {
4801
+ minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
4802
+ const { startBuffered, endBuffered } = state;
4803
+ needsRecalculate || (needsRecalculate = index >= startBuffered && index <= endBuffered);
4804
+ if (!needsRecalculate && state.containerItemKeys.has(itemKey)) {
4805
+ needsRecalculate = true;
4806
+ }
4807
+ if (prevSizeKnown !== void 0 && Math.abs(prevSizeKnown - size) > 5) {
4808
+ shouldMaintainScrollAtEnd = true;
4809
+ }
4810
+ onItemSizeChanged == null ? void 0 : onItemSizeChanged({
4811
+ index,
4812
+ itemData: state.props.data[index],
4813
+ itemKey,
4814
+ previous: size - diff,
4815
+ size
4816
+ });
4817
+ maybeUpdateAnchoredEndSpace(ctx);
4818
+ }
4819
+ if (minIndexSizeChanged !== void 0) {
4820
+ state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
4821
+ }
4822
+ updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
4823
+ if (didContainersLayout || checkAllSizesKnown(state, state.startBuffered, state.endBuffered)) {
4824
+ const canMaintainScrollAtEnd = shouldMaintainScrollAtEnd && !!(maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onItemLayout);
4825
+ return {
4826
+ didChange: diff !== 0,
4827
+ didMeasureUserScrollAnchorResetItem,
4828
+ needsRecalculate,
4829
+ shouldMaintainScrollAtEnd: canMaintainScrollAtEnd
4830
+ };
4831
+ }
4832
+ return {
4833
+ didChange: diff !== 0,
4834
+ didMeasureUserScrollAnchorResetItem
4835
+ };
4836
+ }
4837
+ function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
4838
+ var _a3, _b;
4839
+ const state = ctx.state;
4840
+ const {
4841
+ indexByKey,
4842
+ sizesKnown,
4843
+ averageSizes,
4844
+ props: { data, horizontal, getItemType, getFixedItemSize }
4845
+ } = state;
4846
+ if (!data) return 0;
4847
+ const index = indexByKey.get(itemKey);
4848
+ const itemData = data[index];
4849
+ let itemType = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemType;
4850
+ let fixedItemSize = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.fixedItemSize;
4851
+ if (getFixedItemSize && !(resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize)) {
4852
+ itemType = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
4853
+ fixedItemSize = getFixedItemSize(itemData, index, itemType);
4854
+ }
4855
+ const resolvedItemSize = (resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize) || itemType !== void 0 || fixedItemSize !== void 0 ? {
4856
+ didResolveFixedItemSize: resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize,
4857
+ fixedItemSize,
4858
+ itemType
4859
+ } : void 0;
4860
+ const prevSize = getItemSize(ctx, itemKey, index, itemData, void 0, void 0, void 0, resolvedItemSize);
4861
+ const rawSize = horizontal ? sizeObj.width : sizeObj.height;
4862
+ const prevSizeKnown = sizesKnown.get(itemKey);
4863
+ const size = Math.round(rawSize) ;
4864
+ sizesKnown.set(itemKey, size);
4865
+ if (fixedItemSize === void 0 && size > 0) {
4866
+ itemType != null ? itemType : itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
4867
+ let averages = averageSizes[itemType];
4868
+ if (!averages) {
4869
+ averages = averageSizes[itemType] = { avg: 0, num: 0 };
4870
+ }
4871
+ if (averages.num === 0) {
4872
+ averages.avg = size;
4873
+ averages.num++;
4874
+ } else if (prevSizeKnown !== void 0 && prevSizeKnown > 0) {
4875
+ averages.avg += (size - prevSizeKnown) / averages.num;
4876
+ } else {
4877
+ averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
4878
+ averages.num++;
4879
+ }
4880
+ }
4881
+ if (!prevSize || Math.abs(prevSize - size) > 0.1) {
4882
+ setSize(ctx, itemKey, size);
4883
+ return size - prevSize;
4865
4884
  }
4866
- flushItemSizeUpdates(ctx, result);
4885
+ return 0;
4867
4886
  }
4868
- function queueItemSizeRecalculate(ctx, result) {
4869
- var _a3, _b;
4870
- const batch = batchedItemSizeRecalculates.get(ctx);
4871
- if (batch) {
4872
- mergeItemSizeUpdateResult(batch, result);
4873
- } else {
4874
- const nextBatch = { ...result };
4875
- batchedItemSizeRecalculates.set(ctx, nextBatch);
4876
- if ((_a3 = ctx.state.pendingLayoutEffectMeasurements) == null ? void 0 : _a3.size) {
4877
- requestAnimationFrame(() => {
4878
- flushBatchedItemSizeRecalculate(ctx, true, nextBatch);
4887
+
4888
+ // src/core/measureContainersInLayoutEffect.ts
4889
+ function measureContainersInLayoutEffect(ctx, targetContainerIds = null) {
4890
+ const measurements = [];
4891
+ const containerIds = targetContainerIds != null ? targetContainerIds : ctx.viewRefs.keys();
4892
+ for (const containerId of containerIds) {
4893
+ const viewRef = ctx.viewRefs.get(containerId);
4894
+ const itemKey = peek$(ctx, `containerItemKey${containerId}`);
4895
+ const element = viewRef == null ? void 0 : viewRef.current;
4896
+ if (itemKey !== void 0 && element) {
4897
+ const rect = element.getBoundingClientRect();
4898
+ setContainerLayoutBaseline(element, rect);
4899
+ measurements.push({
4900
+ containerId,
4901
+ itemKey,
4902
+ size: { height: rect.height, width: rect.width }
4879
4903
  });
4880
4904
  }
4881
4905
  }
4882
- if (!((_b = ctx.state.pendingLayoutEffectMeasurements) == null ? void 0 : _b.size)) {
4883
- flushBatchedItemSizeRecalculate(ctx);
4884
- }
4885
- }
4886
- function flushItemSizeUpdates(ctx, result) {
4887
- var _a3;
4888
- const state = ctx.state;
4889
- if (result.needsRecalculate) {
4890
- state.scrollForNextCalculateItemsInView = void 0;
4891
- runOrScheduleMVCPRecalculate(ctx);
4892
- } else if (result.didMeasureUserScrollAnchorResetItem && ((_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys.size) === 0) {
4893
- state.userScrollAnchorReset = void 0;
4894
- }
4895
- if (result.didChange && result.shouldMaintainScrollAtEnd) {
4896
- doMaintainScrollAtEnd(ctx);
4906
+ if (measurements.length > 0) {
4907
+ updateItemSizesBatch(ctx, measurements);
4897
4908
  }
4898
4909
  }
4899
- function updateItemSizes(ctx, measurement) {
4900
- var _a3, _b, _c, _d;
4901
- const state = ctx.state;
4902
- let didDrainLayoutEffectMeasurements = false;
4903
- if (measurement.fromLayoutEffect) {
4904
- const pendingLayoutEffectMeasurements = state.pendingLayoutEffectMeasurements;
4905
- if ((pendingLayoutEffectMeasurements == null ? void 0 : pendingLayoutEffectMeasurements.delete(measurement.itemKey)) && pendingLayoutEffectMeasurements.size === 0) {
4906
- state.pendingLayoutEffectMeasurements = void 0;
4907
- didDrainLayoutEffectMeasurements = true;
4908
- }
4909
- }
4910
- const pendingKeys = (_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys;
4911
- const shouldBatchPendingMeasurements = measurement.fromLayoutEffect && !!(pendingKeys == null ? void 0 : pendingKeys.size);
4912
- const shouldQueueRecalculate = !!measurement.fromLayoutEffect;
4913
- let result;
4914
- if (!shouldBatchPendingMeasurements) {
4915
- result = applyItemSize(ctx, measurement.itemKey, measurement.size);
4916
- } else {
4917
- result = {};
4918
- const updateContainerItemSize = (itemKey, containerId, size) => {
4919
- if (containerId !== void 0 && peek$(ctx, `containerItemKey${containerId}`) === itemKey) {
4920
- mergeItemSizeUpdateResult(result, applyItemSize(ctx, itemKey, size));
4921
- }
4922
- };
4923
- updateContainerItemSize(measurement.itemKey, measurement.containerId, measurement.size);
4924
- const keys = Array.from(pendingKeys);
4925
- for (const itemKey of keys) {
4926
- const containerId = state.containerItemKeys.get(itemKey);
4927
- if (containerId !== void 0) {
4928
- (_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) => {
4929
- if (pendingKeys.has(itemKey)) {
4930
- updateContainerItemSize(itemKey, containerId, { height, width });
4931
- }
4932
- });
4910
+ var typedForwardRef = React3.forwardRef;
4911
+ var typedMemo = React3.memo;
4912
+
4913
+ // src/components/ContainerLayoutCoordinator.tsx
4914
+ var ContainerLayoutCoordinator = typedMemo(function ContainerLayoutCoordinatorComponent({
4915
+ children
4916
+ }) {
4917
+ const ctx = useStateContext();
4918
+ const [containerLayoutEpoch] = useArr$(["containerLayoutEpoch"]);
4919
+ React3.useLayoutEffect(() => {
4920
+ {
4921
+ const targetContainerIds = getContainerLayoutEffectScope(ctx);
4922
+ if (targetContainerIds !== void 0) {
4923
+ measureContainersInLayoutEffect(ctx, targetContainerIds);
4933
4924
  }
4934
4925
  }
4926
+ }, [ctx, containerLayoutEpoch]);
4927
+ return children;
4928
+ });
4929
+ var getComponent = (Component) => {
4930
+ if (React3.isValidElement(Component)) {
4931
+ return Component;
4935
4932
  }
4936
- if (shouldQueueRecalculate && result.needsRecalculate) {
4937
- queueItemSizeRecalculate(ctx, result);
4938
- } else {
4939
- flushItemSizeUpdates(ctx, result);
4940
- }
4941
- if (didDrainLayoutEffectMeasurements) {
4942
- flushBatchedItemSizeRecalculate(ctx);
4933
+ if (Component) {
4934
+ return /* @__PURE__ */ React3.createElement(Component, null);
4943
4935
  }
4936
+ return null;
4937
+ };
4938
+
4939
+ // src/components/PositionView.tsx
4940
+ var isRNWeb = typeof document !== "undefined" && !!document.getElementById("react-native-stylesheet");
4941
+ var baseCss = {
4942
+ contain: "paint layout style",
4943
+ ...isRNWeb ? {
4944
+ display: "flex",
4945
+ flexDirection: "column"
4946
+ } : {}
4947
+ };
4948
+ var PositionViewState = typedMemo(function PositionViewState2({
4949
+ id,
4950
+ horizontal,
4951
+ style,
4952
+ refView,
4953
+ ...props
4954
+ }) {
4955
+ const [position = POSITION_OUT_OF_VIEW] = useArr$([`containerPosition${id}`]);
4956
+ const composed = isArray(style) ? Object.assign({}, ...style) : style;
4957
+ const combinedStyle = horizontal ? { ...baseCss, ...composed, left: position } : { ...baseCss, ...composed, top: position };
4958
+ const {
4959
+ animatedScrollY: _animatedScrollY,
4960
+ onLayout: _onLayout,
4961
+ onLayoutChange: _onLayoutChange,
4962
+ stickyHeaderConfig: _stickyHeaderConfig,
4963
+ ...webProps
4964
+ } = props;
4965
+ return /* @__PURE__ */ React3.createElement("div", { ref: refView, ...webProps, style: combinedStyle });
4966
+ });
4967
+ var PositionViewSticky = typedMemo(function PositionViewSticky2({
4968
+ id,
4969
+ horizontal,
4970
+ style,
4971
+ refView,
4972
+ animatedScrollY: _animatedScrollY,
4973
+ stickyHeaderConfig,
4974
+ onLayout: _onLayout,
4975
+ onLayoutChange: _onLayoutChange,
4976
+ children,
4977
+ ...webProps
4978
+ }) {
4979
+ const [position = POSITION_OUT_OF_VIEW, activeStickyIndex, itemIndex] = useArr$([
4980
+ `containerPosition${id}`,
4981
+ "activeStickyIndex",
4982
+ `containerItemIndex${id}`
4983
+ ]);
4984
+ const composed = React3.useMemo(
4985
+ () => {
4986
+ var _a3;
4987
+ return (_a3 = isArray(style) ? Object.assign({}, ...style) : style) != null ? _a3 : {};
4988
+ },
4989
+ [style]
4990
+ );
4991
+ const viewStyle = React3.useMemo(() => {
4992
+ var _a3;
4993
+ const styleBase = { ...baseCss, ...composed };
4994
+ delete styleBase.transform;
4995
+ const offset = (_a3 = stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset) != null ? _a3 : 0;
4996
+ const isActive = activeStickyIndex === itemIndex;
4997
+ styleBase.position = isActive ? "sticky" : "absolute";
4998
+ styleBase.zIndex = itemIndex + 1e3;
4999
+ if (horizontal) {
5000
+ styleBase.left = isActive ? offset : position;
5001
+ } else {
5002
+ styleBase.top = isActive ? offset : position;
5003
+ }
5004
+ return styleBase;
5005
+ }, [composed, horizontal, position, itemIndex, activeStickyIndex, stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset]);
5006
+ const renderStickyHeaderBackdrop = React3.useMemo(
5007
+ () => (stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent) ? /* @__PURE__ */ React3.createElement(
5008
+ "div",
5009
+ {
5010
+ style: {
5011
+ inset: 0,
5012
+ pointerEvents: "none",
5013
+ position: "absolute"
5014
+ }
5015
+ },
5016
+ getComponent(stickyHeaderConfig.backdropComponent)
5017
+ ) : null,
5018
+ [stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent]
5019
+ );
5020
+ return /* @__PURE__ */ React3.createElement("div", { ref: refView, style: viewStyle, ...webProps }, renderStickyHeaderBackdrop, children);
5021
+ });
5022
+ var PositionView = PositionViewState;
5023
+ function useInit(cb) {
5024
+ useState(() => cb());
4944
5025
  }
4945
- function applyItemSize(ctx, itemKey, sizeObj) {
5026
+
5027
+ // src/state/ContextContainer.ts
5028
+ var ContextContainer = createContext(null);
5029
+ var NO_CONTAINER_ID = -1;
5030
+ function useContextContainer() {
5031
+ return useContext(ContextContainer);
5032
+ }
5033
+ function useContainerItemSignals(containerContext) {
4946
5034
  var _a3;
4947
- const state = ctx.state;
4948
- const userScrollAnchorReset = state.userScrollAnchorReset;
4949
- const didMeasureUserScrollAnchorResetItem = !!(userScrollAnchorReset == null ? void 0 : userScrollAnchorReset.keys.delete(itemKey));
4950
- const {
4951
- didContainersLayout,
4952
- sizesKnown,
4953
- props: { getFixedItemSize, getItemType, horizontal, onItemSizeChanged, data, maintainScrollAtEnd }
4954
- } = state;
4955
- if (!data) return { didMeasureUserScrollAnchorResetItem };
4956
- const index = state.indexByKey.get(itemKey);
4957
- let resolvedMeasurementItem;
4958
- if (getFixedItemSize) {
4959
- if (index === void 0) {
4960
- return { didMeasureUserScrollAnchorResetItem };
5035
+ const containerId = (_a3 = containerContext == null ? void 0 : containerContext.containerId) != null ? _a3 : NO_CONTAINER_ID;
5036
+ const [itemKey, itemIndex, item] = useArr$([
5037
+ `containerItemKey${containerId}`,
5038
+ `containerItemIndex${containerId}`,
5039
+ `containerItemData${containerId}`
5040
+ ]);
5041
+ return {
5042
+ hasItemInfo: !!containerContext && itemKey !== void 0 && itemIndex !== void 0,
5043
+ item,
5044
+ itemIndex,
5045
+ itemKey
5046
+ };
5047
+ }
5048
+ function useAdaptiveRender() {
5049
+ const [mode] = useArr$(["adaptiveRender"]);
5050
+ return mode;
5051
+ }
5052
+ function useAdaptiveRenderChange(callback) {
5053
+ const ctx = useStateContext();
5054
+ const callbackRef = useRef(callback);
5055
+ callbackRef.current = callback;
5056
+ useLayoutEffect(() => {
5057
+ let mode = peek$(ctx, "adaptiveRender");
5058
+ return listen$(ctx, "adaptiveRender", (nextMode) => {
5059
+ if (mode !== nextMode) {
5060
+ mode = nextMode;
5061
+ callbackRef.current(nextMode);
5062
+ }
5063
+ });
5064
+ }, [ctx]);
5065
+ }
5066
+ function useViewability(callback, configId) {
5067
+ const ctx = useStateContext();
5068
+ const containerContext = useContextContainer();
5069
+ useInit(() => {
5070
+ if (!containerContext) {
5071
+ return;
4961
5072
  }
4962
- const itemData = state.props.data[index];
4963
- if (itemData === void 0) {
4964
- return { didMeasureUserScrollAnchorResetItem };
5073
+ const { containerId } = containerContext;
5074
+ const key = containerId + (configId != null ? configId : "");
5075
+ const value = ctx.mapViewabilityValues.get(key);
5076
+ if (value) {
5077
+ callback(value);
5078
+ }
5079
+ });
5080
+ useEffect(() => {
5081
+ if (!containerContext) {
5082
+ return;
4965
5083
  }
4966
- const type = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
4967
- const size2 = getFixedItemSize(itemData, index, type);
4968
- resolvedMeasurementItem = {
4969
- didResolveFixedItemSize: true,
4970
- fixedItemSize: size2,
4971
- itemData,
4972
- itemType: type
5084
+ const { containerId } = containerContext;
5085
+ const key = containerId + (configId != null ? configId : "");
5086
+ ctx.mapViewabilityCallbacks.set(key, callback);
5087
+ return () => {
5088
+ ctx.mapViewabilityCallbacks.delete(key);
4973
5089
  };
4974
- if (size2 !== void 0 && size2 === sizesKnown.get(itemKey)) {
4975
- updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
4976
- return { didMeasureUserScrollAnchorResetItem };
5090
+ }, [ctx, callback, configId, containerContext]);
5091
+ }
5092
+ function useViewabilityAmount(callback) {
5093
+ const ctx = useStateContext();
5094
+ const containerContext = useContextContainer();
5095
+ useInit(() => {
5096
+ if (!containerContext) {
5097
+ return;
4977
5098
  }
4978
- }
4979
- let needsRecalculate = !didContainersLayout;
4980
- let shouldMaintainScrollAtEnd = false;
4981
- let minIndexSizeChanged;
4982
- const prevSizeKnown = state.sizesKnown.get(itemKey);
4983
- const diff = updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem);
4984
- const size = roundSize(horizontal ? sizeObj.width : sizeObj.height);
4985
- if (diff !== 0) {
4986
- minIndexSizeChanged = minIndexSizeChanged !== void 0 ? Math.min(minIndexSizeChanged, index) : index;
4987
- const { startBuffered, endBuffered } = state;
4988
- needsRecalculate || (needsRecalculate = index >= startBuffered && index <= endBuffered);
4989
- if (!needsRecalculate && state.containerItemKeys.has(itemKey)) {
4990
- needsRecalculate = true;
5099
+ const { containerId } = containerContext;
5100
+ const value = ctx.mapViewabilityAmountValues.get(containerId);
5101
+ if (value) {
5102
+ callback(value);
5103
+ }
5104
+ });
5105
+ useEffect(() => {
5106
+ if (!containerContext) {
5107
+ return;
5108
+ }
5109
+ const { containerId } = containerContext;
5110
+ ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
5111
+ return () => {
5112
+ ctx.mapViewabilityAmountCallbacks.delete(containerId);
5113
+ };
5114
+ }, [ctx, callback, containerContext]);
5115
+ }
5116
+ function useRecyclingEffect(effect) {
5117
+ const containerContext = useContextContainer();
5118
+ const { hasItemInfo, item, itemIndex, itemKey } = useContainerItemSignals(containerContext);
5119
+ const prevInfo = useRef(void 0);
5120
+ useEffect(() => {
5121
+ if (!hasItemInfo) {
5122
+ return;
4991
5123
  }
4992
- if (prevSizeKnown !== void 0 && Math.abs(prevSizeKnown - size) > 5) {
4993
- shouldMaintainScrollAtEnd = true;
5124
+ let ret;
5125
+ if (prevInfo.current) {
5126
+ ret = effect({
5127
+ index: itemIndex,
5128
+ item,
5129
+ prevIndex: prevInfo.current.index,
5130
+ prevItem: prevInfo.current.item
5131
+ });
4994
5132
  }
4995
- onItemSizeChanged == null ? void 0 : onItemSizeChanged({
4996
- index,
4997
- itemData: state.props.data[index],
4998
- itemKey,
4999
- previous: size - diff,
5000
- size
5001
- });
5002
- maybeUpdateAnchoredEndSpace(ctx);
5003
- }
5004
- if (minIndexSizeChanged !== void 0) {
5005
- state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
5006
- }
5007
- updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
5008
- if (didContainersLayout || checkAllSizesKnown(state, state.startBuffered, state.endBuffered)) {
5009
- const canMaintainScrollAtEnd = shouldMaintainScrollAtEnd && !!(maintainScrollAtEnd == null ? void 0 : maintainScrollAtEnd.onItemLayout);
5010
- return {
5011
- didChange: diff !== 0,
5012
- didMeasureUserScrollAnchorResetItem,
5013
- needsRecalculate,
5014
- shouldMaintainScrollAtEnd: canMaintainScrollAtEnd
5133
+ prevInfo.current = {
5134
+ index: itemIndex,
5135
+ item
5015
5136
  };
5016
- }
5017
- return {
5018
- didChange: diff !== 0,
5019
- didMeasureUserScrollAnchorResetItem
5020
- };
5137
+ return ret;
5138
+ }, [effect, hasItemInfo, itemIndex, item, itemKey]);
5021
5139
  }
5022
- function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
5023
- var _a3, _b, _c;
5024
- const state = ctx.state;
5025
- const {
5026
- indexByKey,
5027
- sizesKnown,
5028
- averageSizes,
5029
- props: { data, horizontal, getItemType, getFixedItemSize }
5030
- } = state;
5031
- if (!data) return 0;
5032
- const index = indexByKey.get(itemKey);
5033
- const itemData = (_a3 = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemData) != null ? _a3 : data[index];
5034
- let itemType = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemType;
5035
- let fixedItemSize = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.fixedItemSize;
5036
- if (getFixedItemSize && !(resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize)) {
5037
- itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
5038
- fixedItemSize = getFixedItemSize(itemData, index, itemType);
5039
- }
5040
- const resolvedItemSize = (resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize) || itemType !== void 0 || fixedItemSize !== void 0 ? {
5041
- didResolveFixedItemSize: resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize,
5042
- fixedItemSize,
5043
- itemType
5044
- } : void 0;
5045
- const prevSize = getItemSize(ctx, itemKey, index, itemData, void 0, void 0, void 0, resolvedItemSize);
5046
- const rawSize = horizontal ? sizeObj.width : sizeObj.height;
5047
- const prevSizeKnown = sizesKnown.get(itemKey);
5048
- const size = Math.round(rawSize) ;
5049
- sizesKnown.set(itemKey, size);
5050
- if (fixedItemSize === void 0 && size > 0) {
5051
- itemType != null ? itemType : itemType = getItemType ? (_c = getItemType(itemData, index)) != null ? _c : "" : "";
5052
- let averages = averageSizes[itemType];
5053
- if (!averages) {
5054
- averages = averageSizes[itemType] = { avg: 0, num: 0 };
5055
- }
5056
- if (averages.num === 0) {
5057
- averages.avg = size;
5058
- averages.num++;
5059
- } else if (prevSizeKnown !== void 0 && prevSizeKnown > 0) {
5060
- averages.avg += (size - prevSizeKnown) / averages.num;
5061
- } else {
5062
- averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
5063
- averages.num++;
5140
+ function useRecyclingState(valueOrFun) {
5141
+ const containerContext = useContextContainer();
5142
+ const { hasItemInfo, item, itemIndex, itemKey } = useContainerItemSignals(containerContext);
5143
+ const computeValue = () => {
5144
+ if (isFunction(valueOrFun)) {
5145
+ const initializer = valueOrFun;
5146
+ return hasItemInfo ? initializer({
5147
+ index: itemIndex,
5148
+ item,
5149
+ prevIndex: void 0,
5150
+ prevItem: void 0
5151
+ }) : initializer();
5064
5152
  }
5153
+ return valueOrFun;
5154
+ };
5155
+ const [stateValue, setStateValue] = useState(() => {
5156
+ return computeValue();
5157
+ });
5158
+ const prevItemKeyRef = useRef(hasItemInfo ? itemKey : null);
5159
+ if (hasItemInfo && prevItemKeyRef.current !== itemKey) {
5160
+ prevItemKeyRef.current = itemKey;
5161
+ setStateValue(computeValue());
5065
5162
  }
5066
- if (!prevSize || Math.abs(prevSize - size) > 0.1) {
5067
- setSize(ctx, itemKey, size);
5068
- return size - prevSize;
5069
- }
5070
- return 0;
5163
+ const triggerLayout = containerContext == null ? void 0 : containerContext.triggerLayout;
5164
+ const setState = useCallback(
5165
+ (newState) => {
5166
+ if (!triggerLayout) {
5167
+ return;
5168
+ }
5169
+ setStateValue((prevValue) => {
5170
+ return isFunction(newState) ? newState(prevValue) : newState;
5171
+ });
5172
+ triggerLayout();
5173
+ },
5174
+ [triggerLayout]
5175
+ );
5176
+ return [stateValue, setState];
5177
+ }
5178
+ function useIsLastItem() {
5179
+ var _a3;
5180
+ const containerContext = useContextContainer();
5181
+ const containerId = (_a3 = containerContext == null ? void 0 : containerContext.containerId) != null ? _a3 : NO_CONTAINER_ID;
5182
+ const [itemKey] = useArr$([`containerItemKey${containerId}`]);
5183
+ const isLast = useSelector$("lastItemKeys", (lastItemKeys) => {
5184
+ if (containerContext && !isNullOrUndefined(itemKey)) {
5185
+ return (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false;
5186
+ }
5187
+ return false;
5188
+ });
5189
+ return isLast;
5190
+ }
5191
+ function useListScrollSize() {
5192
+ const [scrollSize] = useArr$(["scrollSize"]);
5193
+ return scrollSize;
5194
+ }
5195
+ var noop = () => {
5196
+ };
5197
+ function useSyncLayout() {
5198
+ const containerContext = useContextContainer();
5199
+ return containerContext ? containerContext.triggerLayout : noop;
5200
+ }
5201
+
5202
+ // src/components/Separator.tsx
5203
+ function Separator({ ItemSeparatorComponent, leadingItem }) {
5204
+ const isLastItem = useIsLastItem();
5205
+ return isLastItem ? null : /* @__PURE__ */ React3.createElement(ItemSeparatorComponent, { leadingItem });
5071
5206
  }
5072
5207
 
5073
5208
  // src/hooks/createResizeObserver.ts
@@ -5075,43 +5210,39 @@ var globalResizeObserver = null;
5075
5210
  function getGlobalResizeObserver() {
5076
5211
  if (!globalResizeObserver) {
5077
5212
  globalResizeObserver = new ResizeObserver((entries) => {
5078
- for (const entry of entries) {
5079
- const callbacks = callbackMap.get(entry.target);
5080
- if (callbacks) {
5081
- for (const callback of callbacks) {
5082
- callback(entry);
5213
+ batchItemSizeUpdates(() => {
5214
+ for (const entry of entries) {
5215
+ const callbacks = callbackMap.get(entry.target);
5216
+ if (callbacks) {
5217
+ for (const callback of callbacks) {
5218
+ callback(entry);
5219
+ }
5083
5220
  }
5084
5221
  }
5085
- }
5222
+ });
5086
5223
  });
5087
5224
  }
5088
5225
  return globalResizeObserver;
5089
5226
  }
5090
5227
  var callbackMap = /* @__PURE__ */ new WeakMap();
5091
5228
  function createResizeObserver(element, callback) {
5092
- if (typeof ResizeObserver === "undefined") {
5093
- return () => {
5094
- };
5095
- }
5096
- if (!element) {
5229
+ var _a3;
5230
+ if (typeof ResizeObserver === "undefined" || !element) {
5097
5231
  return () => {
5098
5232
  };
5099
5233
  }
5100
5234
  const observer = getGlobalResizeObserver();
5101
- let callbacks = callbackMap.get(element);
5102
- if (!callbacks) {
5103
- callbacks = /* @__PURE__ */ new Set();
5235
+ const callbacks = (_a3 = callbackMap.get(element)) != null ? _a3 : /* @__PURE__ */ new Set();
5236
+ if (callbacks.size === 0) {
5104
5237
  callbackMap.set(element, callbacks);
5105
- observer.observe(element);
5238
+ observer.observe(element, { box: "border-box" });
5106
5239
  }
5107
5240
  callbacks.add(callback);
5108
5241
  return () => {
5109
- if (callbacks) {
5110
- callbacks.delete(callback);
5111
- if (callbacks.size === 0) {
5112
- callbackMap.delete(element);
5113
- observer.unobserve(element);
5114
- }
5242
+ callbacks.delete(callback);
5243
+ if (callbacks.size === 0) {
5244
+ callbackMap.delete(element);
5245
+ observer.unobserve(element);
5115
5246
  }
5116
5247
  };
5117
5248
  }
@@ -5119,6 +5250,7 @@ function createResizeObserver(element, callback) {
5119
5250
  // src/hooks/useOnLayoutSync.tsx
5120
5251
  function useOnLayoutSync({
5121
5252
  ref,
5253
+ measureInLayoutEffect = true,
5122
5254
  onLayoutProp,
5123
5255
  onLayoutChange,
5124
5256
  webLayoutResync
@@ -5138,21 +5270,31 @@ function useOnLayoutSync({
5138
5270
  onLayoutChange(layout, fromLayoutEffect);
5139
5271
  onLayoutProp == null ? void 0 : onLayoutProp({ nativeEvent: { layout } });
5140
5272
  };
5141
- const rect = element.getBoundingClientRect();
5142
- emit(toLayout(rect), true);
5143
- let prevRect = rect;
5273
+ let prevRect;
5274
+ if (measureInLayoutEffect) {
5275
+ const rect = element.getBoundingClientRect();
5276
+ emit(toLayout(rect), true);
5277
+ prevRect = rect;
5278
+ }
5144
5279
  return createResizeObserver(element, (entry) => {
5145
5280
  var _a4;
5146
5281
  const target = entry.target instanceof HTMLElement ? entry.target : void 0;
5147
- const rectObserved = (_a4 = entry.contentRect) != null ? _a4 : target == null ? void 0 : target.getBoundingClientRect();
5148
- const didSizeChange = rectObserved.width !== prevRect.width || rectObserved.height !== prevRect.height;
5282
+ const borderBoxSize = Array.isArray(entry.borderBoxSize) ? entry.borderBoxSize[0] : entry.borderBoxSize;
5283
+ const rectObserved = borderBoxSize ? {
5284
+ height: borderBoxSize.blockSize,
5285
+ left: entry.contentRect.left,
5286
+ top: entry.contentRect.top,
5287
+ width: borderBoxSize.inlineSize
5288
+ } : (_a4 = target == null ? void 0 : target.getBoundingClientRect()) != null ? _a4 : entry.contentRect;
5289
+ const previousRect = prevRect != null ? prevRect : getContainerLayoutBaseline(element);
5290
+ const didSizeChange = previousRect === void 0 || rectObserved.width !== previousRect.width || rectObserved.height !== previousRect.height;
5291
+ prevRect = rectObserved;
5149
5292
  const shouldResyncLayout = !!(webLayoutResync == null ? void 0 : webLayoutResync());
5150
5293
  if (didSizeChange || shouldResyncLayout) {
5151
- prevRect = rectObserved;
5152
5294
  emit(toLayout(rectObserved), false);
5153
5295
  }
5154
5296
  });
5155
- }, deps || []);
5297
+ }, [measureInLayoutEffect, ...deps || []]);
5156
5298
  return {};
5157
5299
  }
5158
5300
  function toLayout(rect) {
@@ -5167,6 +5309,121 @@ function toLayout(rect) {
5167
5309
  };
5168
5310
  }
5169
5311
 
5312
+ // src/hooks/useContainerMeasurement.tsx
5313
+ var pendingWebShrinkMeasurements = /* @__PURE__ */ new Map();
5314
+ var pendingWebShrinkFrame;
5315
+ function cancelWebShrinkMeasurement(state) {
5316
+ pendingWebShrinkMeasurements.delete(state);
5317
+ }
5318
+ function scheduleWebShrinkMeasurement(state, confirmMeasurement) {
5319
+ pendingWebShrinkMeasurements.set(state, confirmMeasurement);
5320
+ if (pendingWebShrinkFrame === void 0) {
5321
+ pendingWebShrinkFrame = requestAnimationFrame(() => {
5322
+ const callbacks = Array.from(pendingWebShrinkMeasurements.values());
5323
+ pendingWebShrinkMeasurements.clear();
5324
+ pendingWebShrinkFrame = void 0;
5325
+ batchItemSizeUpdates(() => {
5326
+ for (const callback of callbacks) {
5327
+ callback();
5328
+ }
5329
+ });
5330
+ });
5331
+ }
5332
+ }
5333
+ function processContainerLayout({ containerId, ctx, rectangle, ref, state }) {
5334
+ const listState = ctx.state;
5335
+ const currentItemKey = state.itemKey;
5336
+ state.didLayout = true;
5337
+ let layout = rectangle;
5338
+ const axis = state.horizontal ? "width" : "height";
5339
+ const size = roundSize(rectangle[axis]);
5340
+ state.lastSize ? roundSize(state.lastSize[axis]) : void 0;
5341
+ const coreKnownSize = listState.sizesKnown.get(currentItemKey);
5342
+ const previousSize = coreKnownSize ;
5343
+ const applyLayout = () => {
5344
+ state.lastSize = layout;
5345
+ updateItemSizes(ctx, {
5346
+ containerId,
5347
+ itemKey: currentItemKey,
5348
+ size: layout
5349
+ });
5350
+ };
5351
+ const shouldDeferWebShrinkLayoutUpdate = !isInMVCPActiveMode(listState) && previousSize !== void 0 && size + 1 < previousSize;
5352
+ if (shouldDeferWebShrinkLayoutUpdate) {
5353
+ scheduleWebShrinkMeasurement(state, () => {
5354
+ var _a4;
5355
+ if (state.itemKey === currentItemKey) {
5356
+ const element = ref.current;
5357
+ const rect = (_a4 = element == null ? void 0 : element.getBoundingClientRect) == null ? void 0 : _a4.call(element);
5358
+ if (rect) {
5359
+ layout = { height: rect.height, width: rect.width };
5360
+ }
5361
+ applyLayout();
5362
+ }
5363
+ });
5364
+ } else {
5365
+ {
5366
+ cancelWebShrinkMeasurement(state);
5367
+ }
5368
+ {
5369
+ applyLayout();
5370
+ }
5371
+ }
5372
+ }
5373
+ function useContainerMeasurement({
5374
+ containerId,
5375
+ ctx,
5376
+ horizontal,
5377
+ itemKey,
5378
+ ref
5379
+ }) {
5380
+ const stateRef = useRef({
5381
+ didLayout: false,
5382
+ horizontal,
5383
+ itemKey
5384
+ });
5385
+ stateRef.current.horizontal = horizontal;
5386
+ stateRef.current.itemKey = itemKey;
5387
+ const [layoutRenderCount, forceLayoutRender] = useState(0);
5388
+ const onLayoutChange = useCallback(
5389
+ (rectangle) => {
5390
+ processContainerLayout({ containerId, ctx, rectangle, ref, state: stateRef.current });
5391
+ },
5392
+ [containerId, ctx, ref]
5393
+ );
5394
+ const triggerLayout = useCallback(() => {
5395
+ {
5396
+ scheduleContainerLayout(ctx, containerId);
5397
+ }
5398
+ }, [containerId, ctx]);
5399
+ useLayoutEffect(() => {
5400
+ ctx.containerLayoutTriggers.set(containerId, triggerLayout);
5401
+ return () => {
5402
+ cancelWebShrinkMeasurement(stateRef.current);
5403
+ if (ctx.containerLayoutTriggers.get(containerId) === triggerLayout) {
5404
+ ctx.containerLayoutTriggers.delete(containerId);
5405
+ }
5406
+ };
5407
+ }, [containerId, ctx, triggerLayout]);
5408
+ useLayoutEffect(() => {
5409
+ {
5410
+ scheduleContainerLayout(ctx, containerId);
5411
+ }
5412
+ });
5413
+ const { onLayout } = useOnLayoutSync(
5414
+ {
5415
+ measureInLayoutEffect: !IsNewArchitecture,
5416
+ onLayoutChange,
5417
+ ref,
5418
+ webLayoutResync: () => isInMVCPActiveMode(ctx.state)
5419
+ },
5420
+ [itemKey, layoutRenderCount]
5421
+ );
5422
+ useEffect(() => {
5423
+ }, [containerId, ctx, itemKey]);
5424
+ return { onLayout, triggerLayout };
5425
+ }
5426
+
5170
5427
  // src/components/Container.tsx
5171
5428
  function getContainerPositionStyle({
5172
5429
  columnWrapperStyle,
@@ -5237,16 +5494,14 @@ var Container = typedMemo(function Container2({
5237
5494
  "extraData",
5238
5495
  `containerSticky${id}`
5239
5496
  ]);
5240
- const itemLayoutRef = useRef({
5241
- didLayout: false,
5497
+ const ref = useRef(null);
5498
+ const { onLayout, triggerLayout } = useContainerMeasurement({
5499
+ containerId: id,
5500
+ ctx,
5242
5501
  horizontal,
5243
5502
  itemKey,
5244
- pendingShrinkToken: 0
5503
+ ref
5245
5504
  });
5246
- itemLayoutRef.current.horizontal = horizontal;
5247
- itemLayoutRef.current.itemKey = itemKey;
5248
- const ref = useRef(null);
5249
- const [layoutRenderCount, forceLayoutRender] = useState(0);
5250
5505
  const resolvedColumn = column > 0 ? column : 1;
5251
5506
  const resolvedSpan = Math.min(Math.max(span || 1, 1), numColumns);
5252
5507
  const otherAxisPos = numColumns > 1 ? `${(resolvedColumn - 1) / numColumns * 100}%` : 0;
@@ -5277,57 +5532,7 @@ var Container = typedMemo(function Container2({
5277
5532
  () => itemKey !== void 0 ? getRenderedItem2(itemKey) : null,
5278
5533
  [itemKey, data, extraData]
5279
5534
  );
5280
- const { index, renderedItem } = renderedItemInfo || {};
5281
- const onLayoutChange = useCallback((rectangle, fromLayoutEffect) => {
5282
- const {
5283
- horizontal: currentHorizontal,
5284
- itemKey: currentItemKey,
5285
- lastSize,
5286
- pendingShrinkToken
5287
- } = itemLayoutRef.current;
5288
- if (isNullOrUndefined(currentItemKey)) {
5289
- return;
5290
- }
5291
- itemLayoutRef.current.didLayout = true;
5292
- let layout = rectangle;
5293
- const axis = currentHorizontal ? "width" : "height";
5294
- const size = roundSize(rectangle[axis]);
5295
- const prevSize = lastSize ? roundSize(lastSize[axis]) : void 0;
5296
- const doUpdate = () => {
5297
- itemLayoutRef.current.lastSize = layout;
5298
- updateItemSizes(ctx, {
5299
- containerId: id,
5300
- fromLayoutEffect,
5301
- itemKey: currentItemKey,
5302
- size: layout
5303
- });
5304
- itemLayoutRef.current.didLayout = true;
5305
- };
5306
- const shouldDeferWebShrinkLayoutUpdate = !isInMVCPActiveMode(ctx.state) && prevSize !== void 0 && size + 1 < prevSize;
5307
- if (shouldDeferWebShrinkLayoutUpdate) {
5308
- const token = pendingShrinkToken + 1;
5309
- itemLayoutRef.current.pendingShrinkToken = token;
5310
- requestAnimationFrame(() => {
5311
- var _a4;
5312
- if (itemLayoutRef.current.pendingShrinkToken !== token) {
5313
- return;
5314
- }
5315
- const element = ref.current;
5316
- const rect = (_a4 = element == null ? void 0 : element.getBoundingClientRect) == null ? void 0 : _a4.call(element);
5317
- if (rect) {
5318
- layout = { height: rect.height, width: rect.width };
5319
- }
5320
- doUpdate();
5321
- });
5322
- return;
5323
- }
5324
- {
5325
- doUpdate();
5326
- }
5327
- }, []);
5328
- const triggerLayout = useCallback(() => {
5329
- forceLayoutRender((v) => v + 1);
5330
- }, []);
5535
+ const { renderedItem } = renderedItemInfo || {};
5331
5536
  const contextValue = useMemo(() => {
5332
5537
  ctx.viewRefs.set(id, ref);
5333
5538
  return {
@@ -5335,22 +5540,6 @@ var Container = typedMemo(function Container2({
5335
5540
  triggerLayout
5336
5541
  };
5337
5542
  }, [id, triggerLayout]);
5338
- useLayoutEffect(() => {
5339
- ctx.containerLayoutTriggers.set(id, triggerLayout);
5340
- return () => {
5341
- if (ctx.containerLayoutTriggers.get(id) === triggerLayout) {
5342
- ctx.containerLayoutTriggers.delete(id);
5343
- }
5344
- };
5345
- }, [ctx, id, triggerLayout]);
5346
- const { onLayout } = useOnLayoutSync(
5347
- {
5348
- onLayoutChange,
5349
- ref,
5350
- webLayoutResync: () => isInMVCPActiveMode(ctx.state)
5351
- },
5352
- [itemKey, layoutRenderCount]
5353
- );
5354
5543
  const PositionComponent = isSticky ? stickyPositionComponentInternal ? stickyPositionComponentInternal : PositionViewSticky : positionComponentInternal ? positionComponentInternal : PositionView;
5355
5544
  return /* @__PURE__ */ React3.createElement(
5356
5545
  PositionComponent,
@@ -5358,7 +5547,6 @@ var Container = typedMemo(function Container2({
5358
5547
  animatedScrollY: isSticky ? animatedScrollY : void 0,
5359
5548
  horizontal,
5360
5549
  id,
5361
- index,
5362
5550
  key: recycleItems ? void 0 : itemKey,
5363
5551
  onLayout,
5364
5552
  refView: ref,
@@ -5401,18 +5589,13 @@ var ContainerSlot = typedMemo(function ContainerSlot2(props) {
5401
5589
  });
5402
5590
 
5403
5591
  // src/utils/reordering.ts
5404
- var mapFn = (element) => {
5405
- const indexStr = element.getAttribute("data-index");
5406
- if (indexStr === null) {
5407
- return [element, null];
5408
- }
5409
- const index = Number.parseInt(indexStr, 10);
5410
- return [element, Number.isNaN(index) ? null : index];
5411
- };
5412
- function sortDOMElements(container) {
5592
+ function sortDOMElements(container, indexByElement) {
5413
5593
  const elements = Array.from(container.children);
5414
5594
  if (elements.length <= 1) return elements;
5415
- const items = elements.map(mapFn);
5595
+ const items = elements.map((element) => {
5596
+ var _a3;
5597
+ return [element, (_a3 = indexByElement.get(element)) != null ? _a3 : null];
5598
+ });
5416
5599
  items.sort((a, b) => {
5417
5600
  const aKey = a[1];
5418
5601
  const bKey = b[1];
@@ -5500,7 +5683,15 @@ function useDOMOrder(ref) {
5500
5683
  debounceRef.current = setTimeout(() => {
5501
5684
  const parent = ref.current;
5502
5685
  if (parent) {
5503
- sortDOMElements(parent);
5686
+ const indexByElement = /* @__PURE__ */ new Map();
5687
+ for (const [containerId, viewRef] of ctx.viewRefs) {
5688
+ const element = viewRef.current;
5689
+ const index = peek$(ctx, `containerItemIndex${containerId}`);
5690
+ if (element && index !== void 0) {
5691
+ indexByElement.set(element, index);
5692
+ }
5693
+ }
5694
+ sortDOMElements(parent, indexByElement);
5504
5695
  }
5505
5696
  debounceRef.current = void 0;
5506
5697
  }, 500);
@@ -5524,6 +5715,7 @@ var ContainersInner = typedMemo(function ContainersInner2({ horizontal, numColum
5524
5715
  useDOMOrder(ref);
5525
5716
  const style = horizontal ? {
5526
5717
  direction: isHorizontalRTLList ? "ltr" : void 0,
5718
+ flexShrink: 0,
5527
5719
  minHeight: otherAxisSize,
5528
5720
  opacity: readyToRender ? 1 : 0,
5529
5721
  position: "relative",
@@ -5549,7 +5741,7 @@ var ContainersInner = typedMemo(function ContainersInner2({ horizontal, numColum
5549
5741
  }
5550
5742
  }
5551
5743
  }
5552
- return /* @__PURE__ */ React3.createElement("div", { ref, style }, children);
5744
+ return /* @__PURE__ */ React3.createElement("div", { ref, style }, /* @__PURE__ */ React3.createElement(ContainerLayoutCoordinator, null, children));
5553
5745
  });
5554
5746
  var Containers = typedMemo(function Containers2({
5555
5747
  horizontal,
@@ -5728,6 +5920,7 @@ function resolveWindowScrollTarget({ clampedOffset, horizontal, listPos, scroll
5728
5920
 
5729
5921
  // src/components/ListComponentScrollView.tsx
5730
5922
  var SCROLLBAR_HIDDEN_STYLE_ID = "legend-list-scrollbar-axis-hidden-style";
5923
+ var SCROLL_END_FALLBACK_MS = 200;
5731
5924
  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;}`;
5732
5925
  function ensureScrollbarHiddenStyle() {
5733
5926
  if (typeof document === "undefined" || document.getElementById(SCROLLBAR_HIDDEN_STYLE_ID)) {
@@ -5777,6 +5970,7 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
5777
5970
  contentOffset,
5778
5971
  maintainVisibleContentPosition,
5779
5972
  onScroll: onScroll2,
5973
+ onInternalScrollEnd,
5780
5974
  onMomentumScrollEnd: _onMomentumScrollEnd,
5781
5975
  showsHorizontalScrollIndicator = true,
5782
5976
  showsVerticalScrollIndicator = true,
@@ -5906,6 +6100,16 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
5906
6100
  onScroll2(scrollEvent);
5907
6101
  }, [getCurrentScrollOffset, horizontal, isWindowScroll, onScroll2]);
5908
6102
  const scrollEventCoalescer = useRafCoalescer(emitScroll);
6103
+ const scrollEndFallbackRef = useRef(void 0);
6104
+ const emitScrollEnd = useCallback(() => {
6105
+ const timeout = scrollEndFallbackRef.current;
6106
+ if (timeout !== void 0) {
6107
+ clearTimeout(timeout);
6108
+ scrollEndFallbackRef.current = void 0;
6109
+ }
6110
+ scrollEventCoalescer.flush();
6111
+ onInternalScrollEnd == null ? void 0 : onInternalScrollEnd();
6112
+ }, [onInternalScrollEnd, scrollEventCoalescer]);
5909
6113
  const handleScroll = useCallback(
5910
6114
  (_event) => {
5911
6115
  if (!onScroll2) {
@@ -5918,18 +6122,36 @@ var ListComponentScrollView = forwardRef(function ListComponentScrollView2({
5918
6122
  } else {
5919
6123
  scrollEventCoalescer.schedule();
5920
6124
  }
6125
+ if (onInternalScrollEnd) {
6126
+ const timeout = scrollEndFallbackRef.current;
6127
+ if (timeout !== void 0) {
6128
+ clearTimeout(timeout);
6129
+ }
6130
+ scrollEndFallbackRef.current = setTimeout(emitScrollEnd, SCROLL_END_FALLBACK_MS);
6131
+ }
5921
6132
  },
5922
- [ctx.state, onScroll2, scrollEventCoalescer]
6133
+ [ctx.state, emitScrollEnd, onInternalScrollEnd, onScroll2, scrollEventCoalescer]
5923
6134
  );
5924
6135
  useLayoutEffect(() => {
5925
6136
  const target = getScrollTarget();
5926
6137
  if (!target) return;
5927
6138
  target.addEventListener("scroll", handleScroll, { passive: true });
6139
+ if ("onscrollend" in target) {
6140
+ target.addEventListener("scrollend", emitScrollEnd);
6141
+ }
5928
6142
  return () => {
5929
6143
  target.removeEventListener("scroll", handleScroll);
6144
+ if ("onscrollend" in target) {
6145
+ target.removeEventListener("scrollend", emitScrollEnd);
6146
+ }
6147
+ const timeout = scrollEndFallbackRef.current;
6148
+ if (timeout !== void 0) {
6149
+ clearTimeout(timeout);
6150
+ scrollEndFallbackRef.current = void 0;
6151
+ }
5930
6152
  scrollEventCoalescer.cancel();
5931
6153
  };
5932
- }, [getScrollTarget, handleScroll, scrollEventCoalescer]);
6154
+ }, [emitScrollEnd, getScrollTarget, handleScroll, scrollEventCoalescer]);
5933
6155
  useEffect(() => {
5934
6156
  const doScroll = () => {
5935
6157
  if (contentOffset) {
@@ -6279,6 +6501,8 @@ var ListComponent = typedMemo(function ListComponent2({
6279
6501
  refScrollView,
6280
6502
  renderScrollComponent,
6281
6503
  onLayoutFooter,
6504
+ onInternalScrollBeginDrag,
6505
+ onInternalScrollEnd,
6282
6506
  scrollAdjustHandler,
6283
6507
  snapToIndices,
6284
6508
  stickyHeaderConfig,
@@ -6340,7 +6564,7 @@ var ListComponent = typedMemo(function ListComponent2({
6340
6564
  SnapOrScroll,
6341
6565
  {
6342
6566
  ...rest,
6343
- ...ScrollComponent === ListComponentScrollView ? { useWindowScroll } : {},
6567
+ ...ScrollComponent === ListComponentScrollView ? { onInternalScrollEnd, useWindowScroll } : {} ,
6344
6568
  contentContainerStyle: [
6345
6569
  horizontal ? {
6346
6570
  height: "100%"
@@ -6446,6 +6670,7 @@ function checkResetContainers(ctx, dataProp, { didColumnsChange = false } = {})
6446
6670
  if (didColumnsChange) {
6447
6671
  state.sizes.clear();
6448
6672
  state.sizesKnown.clear();
6673
+ invalidateContainerFixedItemSizes(state);
6449
6674
  for (const key in state.averageSizes) {
6450
6675
  delete state.averageSizes[key];
6451
6676
  }
@@ -6692,7 +6917,7 @@ function onScroll(ctx, event) {
6692
6917
  }
6693
6918
  }
6694
6919
  state.scrollPending = newScroll;
6695
- updateScroll(ctx, newScroll, insetChanged);
6920
+ updateScroll(ctx, newScroll, insetChanged, { fromNativeScrollEvent: true });
6696
6921
  trackInitialScrollNativeProgress(state, newScroll);
6697
6922
  clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx);
6698
6923
  if (state.scrollingTo) {
@@ -6855,8 +7080,8 @@ function getAverageItemSizes(state) {
6855
7080
  return averageItemSizes;
6856
7081
  }
6857
7082
  function triggerMountedContainerLayouts(ctx) {
6858
- for (const triggerLayout of ctx.containerLayoutTriggers.values()) {
6859
- triggerLayout();
7083
+ {
7084
+ scheduleContainerLayout(ctx);
6860
7085
  }
6861
7086
  }
6862
7087
  function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
@@ -6969,6 +7194,7 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
6969
7194
  const mode = (_a3 = options == null ? void 0 : options.mode) != null ? _a3 : "sizes";
6970
7195
  state.sizes.clear();
6971
7196
  state.sizesKnown.clear();
7197
+ invalidateContainerFixedItemSizes(state);
6972
7198
  for (const key in state.averageSizes) {
6973
7199
  delete state.averageSizes[key];
6974
7200
  }
@@ -6990,6 +7216,10 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
6990
7216
  return {
6991
7217
  clearCaches,
6992
7218
  flashScrollIndicators: () => refScroller.current.flashScrollIndicators(),
7219
+ getAnimatableRef: () => {
7220
+ var _a3, _b, _c;
7221
+ return (_c = (_b = (_a3 = refScroller.current).getNativeScrollRef) == null ? void 0 : _b.call(_a3)) != null ? _c : refScroller.current;
7222
+ },
6993
7223
  getNativeScrollRef: () => refScroller.current,
6994
7224
  getScrollableNode: () => refScroller.current.getScrollableNode(),
6995
7225
  getScrollResponder: () => refScroller.current.getScrollResponder(),
@@ -7360,6 +7590,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7360
7590
  onMomentumScrollEnd,
7361
7591
  onRefresh,
7362
7592
  onScroll: onScrollProp,
7593
+ onScrollBeginDrag,
7363
7594
  onStartReached,
7364
7595
  onStartReachedThreshold = 0.5,
7365
7596
  onStickyHeaderChange,
@@ -7471,8 +7702,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7471
7702
  averageSizes: {},
7472
7703
  columnSpans: [],
7473
7704
  columns: [],
7705
+ containerItemGenerations: [],
7474
7706
  containerItemKeys: /* @__PURE__ */ new Map(),
7475
- containerItemTypes: /* @__PURE__ */ new Map(),
7707
+ containerItemMetadata: /* @__PURE__ */ new Map(),
7476
7708
  contentInsetOverride: void 0,
7477
7709
  dataChangeEpoch: 0,
7478
7710
  dataChangeNeedsScrollUpdate: false,
@@ -7525,7 +7757,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7525
7757
  startBuffered: -1,
7526
7758
  startNoBuffer: -1,
7527
7759
  startReachedSnapshot: void 0,
7528
- startReachedSnapshotDataChangeEpoch: void 0,
7529
7760
  stickyContainerPool: /* @__PURE__ */ new Set(),
7530
7761
  stickyContainers: /* @__PURE__ */ new Map(),
7531
7762
  timeoutAdaptiveRender: void 0,
@@ -7601,7 +7832,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7601
7832
  onFirstVisibleItemChanged,
7602
7833
  onItemSizeChanged,
7603
7834
  onLoad,
7835
+ onMomentumScrollEnd,
7604
7836
  onScroll: throttleScrollFn,
7837
+ onScrollBeginDrag,
7605
7838
  onStartReached,
7606
7839
  onStartReachedThreshold,
7607
7840
  onStickyHeaderChange,
@@ -7876,11 +8109,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7876
8109
  getRenderedItem: (key) => getRenderedItem(ctx, key),
7877
8110
  onMomentumScrollEnd: (event) => {
7878
8111
  checkFinishedScrollFallback(ctx);
7879
- if (onMomentumScrollEnd) {
7880
- onMomentumScrollEnd(event);
8112
+ if (state.props.onMomentumScrollEnd) {
8113
+ state.props.onMomentumScrollEnd(event);
7881
8114
  }
7882
8115
  },
7883
- onScroll: (event) => onScroll(ctx, event)
8116
+ onScroll: (event) => onScroll(ctx, event),
8117
+ onScrollBeginDrag: (event) => {
8118
+ var _a4, _b2;
8119
+ prepareReachedEdgeForNextUserScroll(ctx);
8120
+ (_b2 = (_a4 = state.props).onScrollBeginDrag) == null ? void 0 : _b2.call(_a4, event);
8121
+ },
8122
+ onScrollEnd: () => prepareReachedEdgeForNextUserScroll(ctx)
7884
8123
  }),
7885
8124
  []
7886
8125
  );
@@ -7901,6 +8140,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7901
8140
  ListFooterComponent,
7902
8141
  ListFooterComponentStyle,
7903
8142
  ListHeaderComponent,
8143
+ onInternalScrollBeginDrag: fns.onScrollBeginDrag,
8144
+ onInternalScrollEnd: fns.onScrollEnd,
7904
8145
  onLayout,
7905
8146
  onLayoutFooter,
7906
8147
  onMomentumScrollEnd: fns.onMomentumScrollEnd,