@legendapp/list 3.3.1 → 3.3.3

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