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