@legendapp/list 3.3.2 → 3.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/react.js CHANGED
@@ -70,6 +70,12 @@ function isHorizontalRTL(state) {
70
70
  function isHorizontalRTLProps(props) {
71
71
  return !!(props == null ? void 0 : props.horizontal) && isRTLProps(props);
72
72
  }
73
+ function getStylePaddingEnd(props) {
74
+ if (!(props == null ? void 0 : props.horizontal)) {
75
+ return (props == null ? void 0 : props.stylePaddingBottom) || 0;
76
+ }
77
+ return (isHorizontalRTLProps(props) ? props.stylePaddingLeft : props.stylePaddingRight) || 0;
78
+ }
73
79
  function getLogicalHorizontalMaxOffset(state, contentWidth) {
74
80
  var _a3;
75
81
  return (_a3 = getHorizontalMaxOffset(state, contentWidth)) != null ? _a3 : 0;
@@ -162,11 +168,13 @@ function StateProvider({ children }) {
162
168
  mapViewabilityCallbacks: /* @__PURE__ */ new Map(),
163
169
  mapViewabilityConfigStates: /* @__PURE__ */ new Map(),
164
170
  mapViewabilityValues: /* @__PURE__ */ new Map(),
171
+ pendingContainerIds: void 0,
165
172
  positionListeners: /* @__PURE__ */ new Map(),
166
173
  scrollAxisGap: 0,
167
174
  state: void 0,
168
175
  values: /* @__PURE__ */ new Map([
169
176
  ["alignItemsAtEndPadding", 0],
177
+ ["containerLayoutEpoch", 0],
170
178
  ["stylePaddingTop", 0],
171
179
  ["headerSize", 0],
172
180
  ["numContainers", 0],
@@ -188,6 +196,14 @@ function useStateContext() {
188
196
  return React3__namespace.useContext(ContextState);
189
197
  }
190
198
  function createSelectorFunctionsArr(ctx, signalNames) {
199
+ if (!ctx) {
200
+ const emptyValues = [];
201
+ return {
202
+ get: () => emptyValues,
203
+ subscribe: () => () => {
204
+ }
205
+ };
206
+ }
191
207
  let lastValues = [];
192
208
  let lastSignalValues = [];
193
209
  return {
@@ -294,7 +310,7 @@ function getContentInsetEndAdjustmentEnd(adjustment) {
294
310
  return Math.max(0, adjustment != null ? adjustment : 0);
295
311
  }
296
312
  function getContentInsetEnd(ctx, contentInsetEndAdjustmentOverride) {
297
- var _a3, _b;
313
+ var _a3;
298
314
  const state = ctx.state;
299
315
  const { props } = state;
300
316
  const horizontal = props.horizontal;
@@ -305,8 +321,8 @@ function getContentInsetEnd(ctx, contentInsetEndAdjustmentOverride) {
305
321
  contentInsetEndAdjustmentOverride != null ? contentInsetEndAdjustmentOverride : props.contentInsetEndAdjustment
306
322
  );
307
323
  const anchoredEndSpaceSize = peek$(ctx, "anchoredEndSpaceSize");
308
- const anchoredEndInset = ((_a3 = props.anchoredEndSpace) == null ? void 0 : _a3.includeInEndInset) && anchoredEndSpaceSize ? anchoredEndSpaceSize : 0;
309
- const overrideInset = (_b = state.contentInsetOverride) != null ? _b : void 0;
324
+ const anchoredEndInset = props.anchoredEndSpace && anchoredEndSpaceSize ? anchoredEndSpaceSize : 0;
325
+ const overrideInset = (_a3 = state.contentInsetOverride) != null ? _a3 : void 0;
310
326
  const adjustedBaseEndInset = baseEndInset + contentInsetEndAdjustment;
311
327
  if (overrideInset) {
312
328
  const mergedInset = { bottom: 0, left: 0, right: 0, ...baseInset, ...overrideInset };
@@ -322,14 +338,15 @@ function getContentInsetEnd(ctx, contentInsetEndAdjustmentOverride) {
322
338
  function getContentSize(ctx) {
323
339
  var _a3, _b;
324
340
  const { values, state } = ctx;
325
- const stylePaddingTop = values.get("stylePaddingTop") || 0;
326
- const stylePaddingBottom = state.props.stylePaddingBottom || 0;
341
+ const stylePaddingStart = state.props.horizontal ? state.props.stylePaddingLeft || 0 : values.get("stylePaddingTop") || 0;
342
+ const stylePaddingEnd = state.props.horizontal ? state.props.stylePaddingRight || 0 : state.props.stylePaddingBottom || 0;
327
343
  const alignItemsAtEndPadding = values.get("alignItemsAtEndPadding") || 0;
328
344
  const headerSize = values.get("headerSize") || 0;
329
345
  const footerSize = values.get("footerSize") || 0;
330
- const contentInsetBottom = getContentInsetEnd(ctx);
346
+ const contentInsetEnd = getContentInsetEnd(ctx);
331
347
  const totalSize = (_b = (_a3 = state.pendingTotalSize) != null ? _a3 : state.totalSize) != null ? _b : values.get("totalSize");
332
- return headerSize + footerSize + totalSize + stylePaddingTop + alignItemsAtEndPadding + stylePaddingBottom + (contentInsetBottom || 0);
348
+ const layoutSize = Math.max(0, totalSize - (state.props.data.length > 0 ? ctx.scrollAxisGap : 0));
349
+ return headerSize + footerSize + layoutSize + stylePaddingStart + alignItemsAtEndPadding + stylePaddingEnd + (contentInsetEnd || 0);
333
350
  }
334
351
 
335
352
  // src/components/DebugView.tsx
@@ -391,6 +408,18 @@ function useInterval(callback, delay) {
391
408
  }, [delay]);
392
409
  }
393
410
 
411
+ // src/constants-platform.ts
412
+ var IsNewArchitecture = true;
413
+
414
+ // src/core/containerLayoutBaseline.ts
415
+ var containerLayoutBaselines = /* @__PURE__ */ new WeakMap();
416
+ function getContainerLayoutBaseline(element) {
417
+ return containerLayoutBaselines.get(element);
418
+ }
419
+ function setContainerLayoutBaseline(element, size) {
420
+ containerLayoutBaselines.set(element, size);
421
+ }
422
+
394
423
  // src/utils/devEnvironment.ts
395
424
  var metroDev = typeof __DEV__ !== "undefined" ? __DEV__ : void 0;
396
425
  var _a;
@@ -404,520 +433,239 @@ var POSITION_OUT_OF_VIEW = -1e7;
404
433
  var EDGE_POSITION_EPSILON = 1;
405
434
  var ENABLE_DEVMODE = IS_DEV && false;
406
435
  var ENABLE_DEBUG_VIEW = IS_DEV && false;
407
- var typedForwardRef = React3__namespace.forwardRef;
408
- var typedMemo = React3__namespace.memo;
409
- var getComponent = (Component) => {
410
- if (React3__namespace.isValidElement(Component)) {
411
- return Component;
412
- }
413
- if (Component) {
414
- return /* @__PURE__ */ React3__namespace.createElement(Component, null);
415
- }
416
- return null;
417
- };
418
436
 
419
- // src/utils/helpers.ts
420
- function isFunction(obj) {
421
- return typeof obj === "function";
422
- }
423
- function isArray(obj) {
424
- return Array.isArray(obj);
437
+ // src/core/deferredPublicOnScroll.ts
438
+ function withResolvedContentOffset(state, event, resolvedOffset) {
439
+ return {
440
+ ...event,
441
+ nativeEvent: {
442
+ ...event.nativeEvent,
443
+ contentOffset: state.props.horizontal ? { x: resolvedOffset, y: 0 } : { x: 0, y: resolvedOffset }
444
+ }
445
+ };
425
446
  }
426
- var warned = /* @__PURE__ */ new Set();
427
- function warnDevOnce(id, text) {
428
- if (IS_DEV && !warned.has(id)) {
429
- warned.add(id);
430
- console.warn(`[legend-list] ${text}`);
447
+ function releaseDeferredPublicOnScroll(ctx, resolvedOffset) {
448
+ var _a3, _b, _c, _d;
449
+ const state = ctx.state;
450
+ const deferredEvent = state.deferredPublicOnScrollEvent;
451
+ state.deferredPublicOnScrollEvent = void 0;
452
+ if (deferredEvent) {
453
+ (_d = (_c = state.props).onScroll) == null ? void 0 : _d.call(
454
+ _c,
455
+ withResolvedContentOffset(
456
+ state,
457
+ deferredEvent,
458
+ (_b = (_a3 = resolvedOffset != null ? resolvedOffset : state.scrollPending) != null ? _a3 : state.scroll) != null ? _b : 0
459
+ )
460
+ );
431
461
  }
432
462
  }
433
- function roundSize(size) {
434
- return Math.floor(size * 8) / 8;
463
+
464
+ // src/core/initialScrollSession.ts
465
+ var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
466
+ function hasInitialScrollSessionCompletion(completion) {
467
+ return !!((completion == null ? void 0 : completion.didDispatchNativeScroll) || (completion == null ? void 0 : completion.didRetrySilentInitialScroll) || (completion == null ? void 0 : completion.watchdog));
435
468
  }
436
- function isNullOrUndefined(value) {
437
- return value === null || value === void 0;
469
+ function clearInitialScrollSession(state) {
470
+ state.initialScrollSession = void 0;
471
+ return void 0;
438
472
  }
439
- function getPadding(s, type) {
440
- var _a3, _b, _c;
441
- const axisPadding = type === "Left" || type === "Right" ? s.paddingHorizontal : s.paddingVertical;
442
- return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : axisPadding) != null ? _b : s.padding) != null ? _c : 0;
473
+ function createInitialScrollSession(options) {
474
+ const { bootstrap, completion, kind, previousDataLength } = options;
475
+ return kind === "offset" ? {
476
+ completion,
477
+ kind,
478
+ previousDataLength
479
+ } : {
480
+ bootstrap,
481
+ completion,
482
+ kind,
483
+ previousDataLength
484
+ };
443
485
  }
444
- function extractPadding(style, contentContainerStyle, type) {
445
- return getPadding(style, type) + getPadding(contentContainerStyle, type);
486
+ function ensureInitialScrollSessionCompletion(state, kind = ((_b) => (_b = ((_a3) => (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind)()) != null ? _b : "bootstrap")()) {
487
+ var _a4, _b2;
488
+ if (!state.initialScrollSession) {
489
+ state.initialScrollSession = createInitialScrollSession({
490
+ completion: {},
491
+ kind,
492
+ previousDataLength: 0
493
+ });
494
+ } else if (state.initialScrollSession.kind !== kind) {
495
+ state.initialScrollSession = createInitialScrollSession({
496
+ bootstrap: state.initialScrollSession.kind === "bootstrap" ? state.initialScrollSession.bootstrap : void 0,
497
+ completion: state.initialScrollSession.completion,
498
+ kind,
499
+ previousDataLength: state.initialScrollSession.previousDataLength
500
+ });
501
+ }
502
+ (_b2 = (_a4 = state.initialScrollSession).completion) != null ? _b2 : _a4.completion = {};
503
+ return state.initialScrollSession.completion;
446
504
  }
447
- function findContainerId(ctx, key) {
448
- var _a3, _b;
449
- const directMatch = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.containerItemKeys) == null ? void 0 : _b.get(key);
450
- if (directMatch !== void 0) {
451
- return directMatch;
505
+ var initialScrollCompletion = {
506
+ didDispatchNativeScroll(state) {
507
+ var _a3, _b;
508
+ return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didDispatchNativeScroll);
509
+ },
510
+ didRetrySilentInitialScroll(state) {
511
+ var _a3, _b;
512
+ return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didRetrySilentInitialScroll);
513
+ },
514
+ markInitialScrollNativeDispatch(state) {
515
+ ensureInitialScrollSessionCompletion(state).didDispatchNativeScroll = true;
516
+ },
517
+ markSilentInitialScrollRetry(state) {
518
+ ensureInitialScrollSessionCompletion(state).didRetrySilentInitialScroll = true;
519
+ },
520
+ resetFlags(state) {
521
+ if (!state.initialScrollSession) {
522
+ return;
523
+ }
524
+ const completion = ensureInitialScrollSessionCompletion(state, state.initialScrollSession.kind);
525
+ completion.didDispatchNativeScroll = void 0;
526
+ completion.didRetrySilentInitialScroll = void 0;
452
527
  }
453
- const numContainers = peek$(ctx, "numContainers");
454
- for (let i = 0; i < numContainers; i++) {
455
- const itemKey = peek$(ctx, `containerItemKey${i}`);
456
- if (itemKey === key) {
457
- return i;
528
+ };
529
+ var initialScrollWatchdog = {
530
+ clear(state) {
531
+ initialScrollWatchdog.set(state, void 0);
532
+ },
533
+ didReachTarget(newScroll, watchdog) {
534
+ const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
535
+ return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
536
+ },
537
+ get(state) {
538
+ var _a3, _b;
539
+ return (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog;
540
+ },
541
+ hasNonZeroTargetOffset(targetOffset) {
542
+ return targetOffset !== void 0 && targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
543
+ },
544
+ isAtZeroTargetOffset(targetOffset) {
545
+ return targetOffset <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
546
+ },
547
+ set(state, watchdog) {
548
+ var _a3, _b;
549
+ if (!watchdog && !((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog)) {
550
+ return;
458
551
  }
552
+ const completion = ensureInitialScrollSessionCompletion(state);
553
+ completion.watchdog = watchdog ? {
554
+ startScroll: watchdog.startScroll,
555
+ targetOffset: watchdog.targetOffset
556
+ } : void 0;
459
557
  }
460
- return -1;
558
+ };
559
+ function setInitialScrollSession(state, options = {}) {
560
+ var _a3, _b, _c, _d;
561
+ const existingSession = state.initialScrollSession;
562
+ const kind = (_a3 = options.kind) != null ? _a3 : existingSession == null ? void 0 : existingSession.kind;
563
+ const completion = existingSession == null ? void 0 : existingSession.completion;
564
+ const existingBootstrap = (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0;
565
+ const bootstrap = kind === "bootstrap" ? options.bootstrap === null ? void 0 : (_b = options.bootstrap) != null ? _b : existingBootstrap : void 0;
566
+ if (!kind) {
567
+ return clearInitialScrollSession(state);
568
+ }
569
+ if (!state.initialScroll && !bootstrap && !hasInitialScrollSessionCompletion(completion)) {
570
+ return clearInitialScrollSession(state);
571
+ }
572
+ const previousDataLength = (_d = (_c = options.previousDataLength) != null ? _c : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _d : 0;
573
+ state.initialScrollSession = createInitialScrollSession({
574
+ bootstrap,
575
+ completion,
576
+ kind,
577
+ previousDataLength
578
+ });
579
+ return state.initialScrollSession;
461
580
  }
462
581
 
463
- // src/components/PositionView.tsx
464
- var isRNWeb = typeof document !== "undefined" && !!document.getElementById("react-native-stylesheet");
465
- var baseCss = {
466
- contain: "paint layout style",
467
- ...isRNWeb ? {
468
- display: "flex",
469
- flexDirection: "column"
470
- } : {}
582
+ // src/utils/checkThreshold.ts
583
+ var HYSTERESIS_MULTIPLIER = 1.3;
584
+ function isOutsideThresholdHysteresis(distance, atThreshold, threshold) {
585
+ const absDistance = Math.abs(distance);
586
+ return !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
587
+ }
588
+ var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot) => {
589
+ const absDistance = Math.abs(distance);
590
+ const within = atThreshold || threshold > 0 && absDistance <= threshold;
591
+ const updateSnapshot = () => {
592
+ setSnapshot({
593
+ atThreshold,
594
+ contentSize: context.contentSize,
595
+ dataLength: context.dataLength,
596
+ scrollPosition: context.scrollPosition
597
+ });
598
+ };
599
+ if (!wasReached) {
600
+ if (!within) {
601
+ return false;
602
+ }
603
+ onReached(distance);
604
+ updateSnapshot();
605
+ return true;
606
+ }
607
+ const reset = isOutsideThresholdHysteresis(distance, atThreshold, threshold);
608
+ if (reset) {
609
+ setSnapshot(void 0);
610
+ return false;
611
+ }
612
+ if (within) {
613
+ const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
614
+ if (changed) {
615
+ updateSnapshot();
616
+ }
617
+ }
618
+ return true;
471
619
  };
472
- var PositionViewState = typedMemo(function PositionViewState2({
473
- id,
474
- horizontal,
475
- style,
476
- refView,
477
- ...props
478
- }) {
479
- const [position = POSITION_OUT_OF_VIEW] = useArr$([`containerPosition${id}`]);
480
- const composed = isArray(style) ? Object.assign({}, ...style) : style;
481
- const combinedStyle = horizontal ? { ...baseCss, ...composed, left: position } : { ...baseCss, ...composed, top: position };
482
- const {
483
- animatedScrollY: _animatedScrollY,
484
- index,
485
- onLayout: _onLayout,
486
- onLayoutChange: _onLayoutChange,
487
- stickyHeaderConfig: _stickyHeaderConfig,
488
- ...webProps
489
- } = props;
490
- return /* @__PURE__ */ React3__namespace.createElement("div", { "data-index": index, ref: refView, ...webProps, style: combinedStyle });
491
- });
492
- var PositionViewSticky = typedMemo(function PositionViewSticky2({
493
- id,
494
- horizontal,
495
- style,
496
- refView,
497
- index,
498
- animatedScrollY: _animatedScrollY,
499
- stickyHeaderConfig,
500
- onLayout: _onLayout,
501
- onLayoutChange: _onLayoutChange,
502
- children,
503
- ...webProps
504
- }) {
505
- const [position = POSITION_OUT_OF_VIEW, activeStickyIndex] = useArr$([
506
- `containerPosition${id}`,
507
- "activeStickyIndex"
508
- ]);
509
- const composed = React3__namespace.useMemo(
510
- () => {
511
- var _a3;
512
- return (_a3 = isArray(style) ? Object.assign({}, ...style) : style) != null ? _a3 : {};
513
- },
514
- [style]
515
- );
516
- const viewStyle = React3__namespace.useMemo(() => {
517
- var _a3;
518
- const styleBase = { ...baseCss, ...composed };
519
- delete styleBase.transform;
520
- const offset = (_a3 = stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset) != null ? _a3 : 0;
521
- const isActive = activeStickyIndex === index;
522
- styleBase.position = isActive ? "sticky" : "absolute";
523
- styleBase.zIndex = index + 1e3;
524
- if (horizontal) {
525
- styleBase.left = isActive ? offset : position;
526
- } else {
527
- styleBase.top = isActive ? offset : position;
528
- }
529
- return styleBase;
530
- }, [composed, horizontal, position, index, activeStickyIndex, stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset]);
531
- const renderStickyHeaderBackdrop = React3__namespace.useMemo(
532
- () => (stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent) ? /* @__PURE__ */ React3__namespace.createElement(
533
- "div",
534
- {
535
- style: {
536
- inset: 0,
537
- pointerEvents: "none",
538
- position: "absolute"
539
- }
540
- },
541
- getComponent(stickyHeaderConfig.backdropComponent)
542
- ) : null,
543
- [stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent]
544
- );
545
- return /* @__PURE__ */ React3__namespace.createElement(
546
- "div",
547
- {
548
- "data-index": index,
549
- ref: refView,
550
- style: viewStyle,
551
- ...webProps
552
- },
553
- renderStickyHeaderBackdrop,
554
- children
555
- );
556
- });
557
- var PositionView = PositionViewState;
558
-
559
- // src/constants-platform.ts
560
- var IsNewArchitecture = true;
561
- function useInit(cb) {
562
- React3.useState(() => cb());
563
- }
564
-
565
- // src/state/ContextContainer.ts
566
- var ContextContainer = React3.createContext(null);
567
- var NO_CONTAINER_ID = -1;
568
- function useContextContainer() {
569
- return React3.useContext(ContextContainer);
570
- }
571
- function useContainerItemInfo(containerContext) {
572
- var _a3;
573
- const containerId = (_a3 = containerContext == null ? void 0 : containerContext.containerId) != null ? _a3 : NO_CONTAINER_ID;
574
- const [itemInfo] = useArr$([`containerItemInfo${containerId}`]);
575
- return containerContext ? itemInfo : void 0;
576
- }
577
- function useContainerItemKey(containerContext) {
578
- var _a3;
579
- const containerId = (_a3 = containerContext == null ? void 0 : containerContext.containerId) != null ? _a3 : NO_CONTAINER_ID;
580
- const [itemKey] = useArr$([`containerItemKey${containerId}`]);
581
- return containerContext ? itemKey : void 0;
582
- }
583
- function useAdaptiveRender() {
584
- const [mode] = useArr$(["adaptiveRender"]);
585
- return mode;
586
- }
587
- function useAdaptiveRenderChange(callback) {
588
- const ctx = useStateContext();
589
- const callbackRef = React3.useRef(callback);
590
- callbackRef.current = callback;
591
- React3.useLayoutEffect(() => {
592
- let mode = peek$(ctx, "adaptiveRender");
593
- return listen$(ctx, "adaptiveRender", (nextMode) => {
594
- if (mode !== nextMode) {
595
- mode = nextMode;
596
- callbackRef.current(nextMode);
597
- }
598
- });
599
- }, [ctx]);
600
- }
601
- function useViewability(callback, configId) {
602
- const ctx = useStateContext();
603
- const containerContext = useContextContainer();
604
- useInit(() => {
605
- if (!containerContext) {
606
- return;
607
- }
608
- const { containerId } = containerContext;
609
- const key = containerId + (configId != null ? configId : "");
610
- const value = ctx.mapViewabilityValues.get(key);
611
- if (value) {
612
- callback(value);
613
- }
614
- });
615
- React3.useEffect(() => {
616
- if (!containerContext) {
617
- return;
618
- }
619
- const { containerId } = containerContext;
620
- const key = containerId + (configId != null ? configId : "");
621
- ctx.mapViewabilityCallbacks.set(key, callback);
622
- return () => {
623
- ctx.mapViewabilityCallbacks.delete(key);
624
- };
625
- }, [ctx, callback, configId, containerContext]);
626
- }
627
- function useViewabilityAmount(callback) {
628
- const ctx = useStateContext();
629
- const containerContext = useContextContainer();
630
- useInit(() => {
631
- if (!containerContext) {
632
- return;
633
- }
634
- const { containerId } = containerContext;
635
- const value = ctx.mapViewabilityAmountValues.get(containerId);
636
- if (value) {
637
- callback(value);
638
- }
639
- });
640
- React3.useEffect(() => {
641
- if (!containerContext) {
642
- return;
643
- }
644
- const { containerId } = containerContext;
645
- ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
646
- return () => {
647
- ctx.mapViewabilityAmountCallbacks.delete(containerId);
648
- };
649
- }, [ctx, callback, containerContext]);
650
- }
651
- function useRecyclingEffect(effect) {
652
- const containerContext = useContextContainer();
653
- const itemInfo = useContainerItemInfo(containerContext);
654
- const prevInfo = React3.useRef(void 0);
655
- React3.useEffect(() => {
656
- if (!itemInfo) {
657
- return;
658
- }
659
- let ret;
660
- if (prevInfo.current) {
661
- ret = effect({
662
- index: itemInfo.index,
663
- item: itemInfo.value,
664
- prevIndex: prevInfo.current.index,
665
- prevItem: prevInfo.current.value
666
- });
667
- }
668
- prevInfo.current = itemInfo;
669
- return ret;
670
- }, [effect, itemInfo]);
671
- }
672
- function useRecyclingState(valueOrFun) {
673
- var _a3;
674
- const containerContext = useContextContainer();
675
- const itemInfo = useContainerItemInfo(containerContext);
676
- const computeValue = (info) => {
677
- if (isFunction(valueOrFun)) {
678
- const initializer = valueOrFun;
679
- return info ? initializer({
680
- index: info.index,
681
- item: info.value,
682
- prevIndex: void 0,
683
- prevItem: void 0
684
- }) : initializer();
685
- }
686
- return valueOrFun;
687
- };
688
- const [stateValue, setStateValue] = React3.useState(() => {
689
- return computeValue(itemInfo);
690
- });
691
- const prevItemKeyRef = React3.useRef((_a3 = itemInfo == null ? void 0 : itemInfo.itemKey) != null ? _a3 : null);
692
- if (itemInfo && prevItemKeyRef.current !== itemInfo.itemKey) {
693
- prevItemKeyRef.current = itemInfo.itemKey;
694
- setStateValue(computeValue(itemInfo));
695
- }
696
- const triggerLayout = containerContext == null ? void 0 : containerContext.triggerLayout;
697
- const setState = React3.useCallback(
698
- (newState) => {
699
- if (!triggerLayout) {
700
- return;
701
- }
702
- setStateValue((prevValue) => {
703
- return isFunction(newState) ? newState(prevValue) : newState;
704
- });
705
- triggerLayout();
706
- },
707
- [triggerLayout]
708
- );
709
- return [stateValue, setState];
710
- }
711
- function useIsLastItem() {
712
- const containerContext = useContextContainer();
713
- const itemKey = useContainerItemKey(containerContext);
714
- const isLast = useSelector$("lastItemKeys", (lastItemKeys) => {
715
- if (containerContext && !isNullOrUndefined(itemKey)) {
716
- return (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false;
717
- }
718
- return false;
719
- });
720
- return isLast;
721
- }
722
- function useListScrollSize() {
723
- const [scrollSize] = useArr$(["scrollSize"]);
724
- return scrollSize;
725
- }
726
- var noop = () => {
727
- };
728
- function useSyncLayout() {
729
- const containerContext = useContextContainer();
730
- return containerContext ? containerContext.triggerLayout : noop;
731
- }
732
-
733
- // src/components/Separator.tsx
734
- function Separator({ ItemSeparatorComponent, leadingItem }) {
735
- const isLastItem = useIsLastItem();
736
- return isLastItem ? null : /* @__PURE__ */ React3__namespace.createElement(ItemSeparatorComponent, { leadingItem });
737
- }
738
620
 
739
- // src/core/deferredPublicOnScroll.ts
740
- function withResolvedContentOffset(state, event, resolvedOffset) {
741
- return {
742
- ...event,
743
- nativeEvent: {
744
- ...event.nativeEvent,
745
- contentOffset: state.props.horizontal ? { x: resolvedOffset, y: 0 } : { x: 0, y: resolvedOffset }
746
- }
747
- };
748
- }
749
- function releaseDeferredPublicOnScroll(ctx, resolvedOffset) {
750
- var _a3, _b, _c, _d;
621
+ // src/utils/edgeReachedGate.ts
622
+ function resetEdgeLatch(ctx, edge) {
751
623
  const state = ctx.state;
752
- const deferredEvent = state.deferredPublicOnScrollEvent;
753
- state.deferredPublicOnScrollEvent = void 0;
754
- if (deferredEvent) {
755
- (_d = (_c = state.props).onScroll) == null ? void 0 : _d.call(
756
- _c,
757
- withResolvedContentOffset(
758
- state,
759
- deferredEvent,
760
- (_b = (_a3 = resolvedOffset != null ? resolvedOffset : state.scrollPending) != null ? _a3 : state.scroll) != null ? _b : 0
761
- )
762
- );
763
- }
764
- }
765
-
766
- // src/core/initialScrollSession.ts
767
- var INITIAL_SCROLL_MIN_TARGET_OFFSET = 1;
768
- function hasInitialScrollSessionCompletion(completion) {
769
- return !!((completion == null ? void 0 : completion.didDispatchNativeScroll) || (completion == null ? void 0 : completion.didRetrySilentInitialScroll) || (completion == null ? void 0 : completion.watchdog));
770
- }
771
- function clearInitialScrollSession(state) {
772
- state.initialScrollSession = void 0;
773
- return void 0;
774
- }
775
- function createInitialScrollSession(options) {
776
- const { bootstrap, completion, kind, previousDataLength } = options;
777
- return kind === "offset" ? {
778
- completion,
779
- kind,
780
- previousDataLength
781
- } : {
782
- bootstrap,
783
- completion,
784
- kind,
785
- previousDataLength
786
- };
787
- }
788
- function ensureInitialScrollSessionCompletion(state, kind = ((_b) => (_b = ((_a3) => (_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind)()) != null ? _b : "bootstrap")()) {
789
- var _a4, _b2;
790
- if (!state.initialScrollSession) {
791
- state.initialScrollSession = createInitialScrollSession({
792
- completion: {},
793
- kind,
794
- previousDataLength: 0
795
- });
796
- } else if (state.initialScrollSession.kind !== kind) {
797
- state.initialScrollSession = createInitialScrollSession({
798
- bootstrap: state.initialScrollSession.kind === "bootstrap" ? state.initialScrollSession.bootstrap : void 0,
799
- completion: state.initialScrollSession.completion,
800
- kind,
801
- previousDataLength: state.initialScrollSession.previousDataLength
802
- });
803
- }
804
- (_b2 = (_a4 = state.initialScrollSession).completion) != null ? _b2 : _a4.completion = {};
805
- return state.initialScrollSession.completion;
806
- }
807
- var initialScrollCompletion = {
808
- didDispatchNativeScroll(state) {
809
- var _a3, _b;
810
- return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didDispatchNativeScroll);
811
- },
812
- didRetrySilentInitialScroll(state) {
813
- var _a3, _b;
814
- return !!((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.didRetrySilentInitialScroll);
815
- },
816
- markInitialScrollNativeDispatch(state) {
817
- ensureInitialScrollSessionCompletion(state).didDispatchNativeScroll = true;
818
- },
819
- markSilentInitialScrollRetry(state) {
820
- ensureInitialScrollSessionCompletion(state).didRetrySilentInitialScroll = true;
821
- },
822
- resetFlags(state) {
823
- if (!state.initialScrollSession) {
824
- return;
825
- }
826
- const completion = ensureInitialScrollSessionCompletion(state, state.initialScrollSession.kind);
827
- completion.didDispatchNativeScroll = void 0;
828
- completion.didRetrySilentInitialScroll = void 0;
829
- }
830
- };
831
- var initialScrollWatchdog = {
832
- clear(state) {
833
- initialScrollWatchdog.set(state, void 0);
834
- },
835
- didReachTarget(newScroll, watchdog) {
836
- const nextDistance = Math.abs(newScroll - watchdog.targetOffset);
837
- return nextDistance <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
838
- },
839
- get(state) {
840
- var _a3, _b;
841
- return (_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog;
842
- },
843
- hasNonZeroTargetOffset(targetOffset) {
844
- return targetOffset !== void 0 && targetOffset > INITIAL_SCROLL_MIN_TARGET_OFFSET;
845
- },
846
- isAtZeroTargetOffset(targetOffset) {
847
- return targetOffset <= INITIAL_SCROLL_MIN_TARGET_OFFSET;
848
- },
849
- set(state, watchdog) {
850
- var _a3, _b;
851
- if (!watchdog && !((_b = (_a3 = state.initialScrollSession) == null ? void 0 : _a3.completion) == null ? void 0 : _b.watchdog)) {
852
- return;
853
- }
854
- const completion = ensureInitialScrollSessionCompletion(state);
855
- completion.watchdog = watchdog ? {
856
- startScroll: watchdog.startScroll,
857
- targetOffset: watchdog.targetOffset
858
- } : void 0;
859
- }
860
- };
861
- function setInitialScrollSession(state, options = {}) {
862
- var _a3, _b, _c, _d;
863
- const existingSession = state.initialScrollSession;
864
- const kind = (_a3 = options.kind) != null ? _a3 : existingSession == null ? void 0 : existingSession.kind;
865
- const completion = existingSession == null ? void 0 : existingSession.completion;
866
- const existingBootstrap = (existingSession == null ? void 0 : existingSession.kind) === "bootstrap" ? existingSession.bootstrap : void 0;
867
- const bootstrap = kind === "bootstrap" ? options.bootstrap === null ? void 0 : (_b = options.bootstrap) != null ? _b : existingBootstrap : void 0;
868
- if (!kind) {
869
- return clearInitialScrollSession(state);
870
- }
871
- if (!state.initialScroll && !bootstrap && !hasInitialScrollSessionCompletion(completion)) {
872
- return clearInitialScrollSession(state);
624
+ if (edge === "start") {
625
+ state.isStartReached = false;
626
+ state.startReachedSnapshot = void 0;
627
+ } else {
628
+ state.isEndReached = false;
629
+ state.endReachedSnapshot = void 0;
873
630
  }
874
- const previousDataLength = (_d = (_c = options.previousDataLength) != null ? _c : existingSession == null ? void 0 : existingSession.previousDataLength) != null ? _d : 0;
875
- state.initialScrollSession = createInitialScrollSession({
876
- bootstrap,
877
- completion,
878
- kind,
879
- previousDataLength
880
- });
881
- return state.initialScrollSession;
882
631
  }
883
-
884
- // src/utils/checkThreshold.ts
885
- var HYSTERESIS_MULTIPLIER = 1.3;
886
- var checkThreshold = (distance, atThreshold, threshold, wasReached, snapshot, context, onReached, setSnapshot, allowReentryOnChange) => {
887
- const absDistance = Math.abs(distance);
888
- const within = atThreshold || threshold > 0 && absDistance <= threshold;
889
- const updateSnapshot = () => {
890
- setSnapshot({
891
- atThreshold,
892
- contentSize: context.contentSize,
893
- dataLength: context.dataLength,
894
- scrollPosition: context.scrollPosition
895
- });
896
- };
897
- if (!wasReached) {
898
- if (!within) {
899
- return false;
900
- }
901
- onReached(distance);
902
- updateSnapshot();
903
- return true;
904
- }
905
- const reset = !atThreshold && threshold > 0 && absDistance >= threshold * HYSTERESIS_MULTIPLIER || !atThreshold && threshold <= 0 && absDistance > 0;
906
- if (reset) {
907
- setSnapshot(void 0);
908
- return false;
632
+ function resetSharedEdgeGateIfOutsideHysteresis(ctx) {
633
+ const state = ctx.state;
634
+ if (!state.edgeReachedGate) {
635
+ return;
909
636
  }
910
- if (within) {
911
- const changed = !snapshot || snapshot.atThreshold !== atThreshold || snapshot.contentSize !== context.contentSize || snapshot.dataLength !== context.dataLength;
912
- if (changed) {
913
- if (allowReentryOnChange) {
914
- onReached(distance);
915
- }
916
- updateSnapshot();
917
- }
637
+ const contentSize = getContentSize(ctx);
638
+ const endDistance = contentSize - state.scroll - state.scrollLength - getContentInsetEnd(ctx);
639
+ const isContentLess = contentSize < state.scrollLength;
640
+ const startThreshold = state.props.onStartReachedThreshold * state.scrollLength;
641
+ const endThreshold = state.props.onEndReachedThreshold * state.scrollLength;
642
+ const isOutsideStart = isOutsideThresholdHysteresis(state.scroll, false, startThreshold);
643
+ const isOutsideEnd = isOutsideThresholdHysteresis(endDistance, isContentLess, endThreshold);
644
+ if (isOutsideStart && isOutsideEnd) {
645
+ state.edgeReachedGate = void 0;
918
646
  }
919
- return true;
920
- };
647
+ }
648
+ function canDispatchReachedEdge(ctx, edge, allowedEdge, allowGateCreatedInCurrentCheck) {
649
+ return !ctx.state.edgeReachedGate || allowedEdge === edge || !!allowGateCreatedInCurrentCheck;
650
+ }
651
+ function markReachedEdge(ctx) {
652
+ ctx.state.edgeReachedGate = "closed";
653
+ }
654
+ function prepareReachedEdgeForNextUserScroll(ctx) {
655
+ if (ctx.state.edgeReachedGate) {
656
+ ctx.state.edgeReachedGate = "prepared";
657
+ }
658
+ }
659
+ function beginReachedEdgeUserScroll(ctx, scrollDelta) {
660
+ const state = ctx.state;
661
+ if (state.edgeReachedGate !== "prepared") {
662
+ return void 0;
663
+ }
664
+ const allowedEdge = scrollDelta < 0 ? "start" : "end";
665
+ state.edgeReachedGate = "closed";
666
+ resetEdgeLatch(ctx, allowedEdge);
667
+ return allowedEdge;
668
+ }
921
669
 
922
670
  // src/utils/hasActiveInitialScroll.ts
923
671
  function hasActiveInitialScroll(state) {
@@ -925,7 +673,7 @@ function hasActiveInitialScroll(state) {
925
673
  }
926
674
 
927
675
  // src/utils/checkAtBottom.ts
928
- function checkAtBottom(ctx) {
676
+ function checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
929
677
  var _a3;
930
678
  const state = ctx.state;
931
679
  if (!state) {
@@ -939,6 +687,7 @@ function checkAtBottom(ctx) {
939
687
  props: { maintainScrollAtEndThreshold, onEndReachedThreshold }
940
688
  } = state;
941
689
  const contentSize = getContentSize(ctx);
690
+ resetSharedEdgeGateIfOutsideHysteresis(ctx);
942
691
  if (contentSize > 0 && queuedInitialLayout) {
943
692
  const insetEnd = getContentInsetEnd(ctx);
944
693
  const distanceFromEnd = contentSize - scroll - scrollLength - insetEnd;
@@ -965,72 +714,50 @@ function checkAtBottom(ctx) {
965
714
  },
966
715
  (distance) => {
967
716
  var _a4, _b;
968
- return (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
717
+ if (canDispatchReachedEdge(ctx, "end", allowedEdge, allowGateCreatedInCurrentCheck)) {
718
+ markReachedEdge(ctx);
719
+ (_b = (_a4 = state.props).onEndReached) == null ? void 0 : _b.call(_a4, { distanceFromEnd: distance });
720
+ }
969
721
  },
970
722
  (snapshot) => {
971
723
  state.endReachedSnapshot = snapshot;
972
- },
973
- true
724
+ }
974
725
  );
975
726
  }
976
727
  }
977
728
  }
978
729
 
979
- // src/utils/hasActiveMVCPAnchorLock.ts
980
- function hasActiveMVCPAnchorLock(state) {
981
- const lock = state.mvcpAnchorLock;
982
- if (!lock) {
983
- return false;
984
- }
985
- if (Date.now() > lock.expiresAt) {
986
- state.mvcpAnchorLock = void 0;
987
- return false;
988
- }
989
- return true;
990
- }
991
-
992
- // src/utils/isInMVCPActiveMode.ts
993
- function isInMVCPActiveMode(state) {
994
- return state.dataChangeNeedsScrollUpdate || hasActiveMVCPAnchorLock(state);
995
- }
996
-
997
730
  // src/utils/checkAtTop.ts
998
- function checkAtTop(ctx) {
731
+ function checkAtTop(ctx, allowedEdge, allowGateCreatedInCurrentCheck) {
999
732
  const state = ctx == null ? void 0 : ctx.state;
1000
733
  if (!state) {
1001
734
  return;
1002
735
  }
1003
736
  const {
1004
- dataChangeEpoch,
1005
737
  isStartReached,
1006
738
  props: { data, onStartReachedThreshold },
1007
739
  scroll,
1008
740
  scrollLength,
1009
741
  startReachedSnapshot,
1010
- startReachedSnapshotDataChangeEpoch,
1011
742
  totalSize
1012
743
  } = state;
1013
744
  const dataLength = data.length;
1014
745
  const threshold = onStartReachedThreshold * scrollLength;
1015
- const dataChanged = startReachedSnapshotDataChangeEpoch !== dataChangeEpoch;
1016
- const withinThreshold = threshold > 0 && Math.abs(scroll) <= threshold;
1017
- const allowReentryOnDataChange = !!isStartReached && withinThreshold && !!dataChanged && !isInMVCPActiveMode(state);
1018
- if (isStartReached && threshold > 0 && scroll > threshold && startReachedSnapshot && (dataChanged || startReachedSnapshot.contentSize !== totalSize || startReachedSnapshot.dataLength !== dataLength)) {
746
+ resetSharedEdgeGateIfOutsideHysteresis(ctx);
747
+ if (isStartReached && threshold > 0 && scroll > threshold && startReachedSnapshot && (startReachedSnapshot.contentSize !== totalSize || startReachedSnapshot.dataLength !== dataLength)) {
1019
748
  state.isStartReached = false;
1020
749
  state.startReachedSnapshot = void 0;
1021
- state.startReachedSnapshotDataChangeEpoch = void 0;
1022
750
  }
1023
751
  set$(ctx, "isAtStart", scroll <= EDGE_POSITION_EPSILON);
1024
752
  set$(ctx, "isNearStart", scroll <= threshold);
1025
753
  const shouldSkipThresholdChecks = hasActiveInitialScroll(state) || !!state.scrollingTo;
1026
- const shouldDeferDataChangeRefire = isStartReached && withinThreshold && dataChanged && !allowReentryOnDataChange;
1027
- if (!shouldSkipThresholdChecks && !shouldDeferDataChangeRefire) {
754
+ if (!shouldSkipThresholdChecks) {
1028
755
  state.isStartReached = checkThreshold(
1029
756
  scroll,
1030
757
  false,
1031
758
  threshold,
1032
759
  state.isStartReached,
1033
- allowReentryOnDataChange ? void 0 : startReachedSnapshot,
760
+ startReachedSnapshot,
1034
761
  {
1035
762
  contentSize: totalSize,
1036
763
  dataLength,
@@ -1038,21 +765,23 @@ function checkAtTop(ctx) {
1038
765
  },
1039
766
  (distance) => {
1040
767
  var _a3, _b;
1041
- return (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
768
+ if (canDispatchReachedEdge(ctx, "start", allowedEdge, allowGateCreatedInCurrentCheck)) {
769
+ markReachedEdge(ctx);
770
+ (_b = (_a3 = state.props).onStartReached) == null ? void 0 : _b.call(_a3, { distanceFromStart: distance });
771
+ }
1042
772
  },
1043
773
  (snapshot) => {
1044
774
  state.startReachedSnapshot = snapshot;
1045
- state.startReachedSnapshotDataChangeEpoch = snapshot ? dataChangeEpoch : void 0;
1046
- },
1047
- allowReentryOnDataChange
775
+ }
1048
776
  );
1049
777
  }
1050
778
  }
1051
779
 
1052
780
  // src/utils/checkThresholds.ts
1053
- function checkThresholds(ctx) {
1054
- checkAtBottom(ctx);
1055
- checkAtTop(ctx);
781
+ function checkThresholds(ctx, allowedEdge) {
782
+ const allowGateCreatedInCurrentCheck = !ctx.state.edgeReachedGate;
783
+ checkAtBottom(ctx, allowedEdge, allowGateCreatedInCurrentCheck);
784
+ checkAtTop(ctx, allowedEdge, allowGateCreatedInCurrentCheck);
1056
785
  }
1057
786
 
1058
787
  // src/core/recalculateSettledScroll.ts
@@ -1068,13 +797,7 @@ var DEFAULT_WEB_ADAPTIVE_RENDER_ENTER_VELOCITY = 6;
1068
797
  var DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_VELOCITY = 3;
1069
798
  var DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_DELAY = 250;
1070
799
  function clearAdaptiveRenderExitTimeout(ctx) {
1071
- const state = ctx.state;
1072
- const previousTimeout = state.timeoutAdaptiveRender;
1073
- if (previousTimeout !== void 0) {
1074
- clearTimeout(previousTimeout);
1075
- state.timeouts.delete(previousTimeout);
1076
- state.timeoutAdaptiveRender = void 0;
1077
- }
800
+ ctx.state.scheduledWork.cancel("adaptiveRender");
1078
801
  }
1079
802
  function scheduleAdaptiveRenderExit(ctx, exitDelay) {
1080
803
  const state = ctx.state;
@@ -1082,13 +805,7 @@ function scheduleAdaptiveRenderExit(ctx, exitDelay) {
1082
805
  if (exitDelay <= 0) {
1083
806
  setAdaptiveRender(ctx, "normal", "scroll");
1084
807
  } else {
1085
- const timeout = setTimeout(() => {
1086
- state.timeouts.delete(timeout);
1087
- state.timeoutAdaptiveRender = void 0;
1088
- setAdaptiveRender(ctx, "normal", "scroll");
1089
- }, exitDelay);
1090
- state.timeoutAdaptiveRender = timeout;
1091
- state.timeouts.add(timeout);
808
+ state.scheduledWork.timeout(() => setAdaptiveRender(ctx, "normal", "scroll"), exitDelay, "adaptiveRender");
1092
809
  }
1093
810
  }
1094
811
  function setAdaptiveRender(ctx, mode, reason) {
@@ -1119,7 +836,7 @@ function updateAdaptiveRender(ctx, scrollVelocity, options) {
1119
836
  const exitDelay = (_c = adaptiveRender.exitDelay) != null ? _c : DEFAULT_WEB_ADAPTIVE_RENDER_EXIT_DELAY ;
1120
837
  const threshold = currentMode === "light" ? exitVelocity : enterVelocity;
1121
838
  const nextMode = (options == null ? void 0 : options.forceLight) || Math.abs(scrollVelocity) > threshold ? "light" : "normal";
1122
- const previousMode = state.timeoutAdaptiveRender !== void 0 ? "normal" : currentMode;
839
+ const previousMode = state.scheduledWork.has("adaptiveRender") ? "normal" : currentMode;
1123
840
  if (nextMode !== previousMode) {
1124
841
  if (nextMode === "light") {
1125
842
  setAdaptiveRender(ctx, "light", "scroll");
@@ -1146,14 +863,13 @@ function getEffectiveDrawDistance(ctx, mode) {
1146
863
  }
1147
864
  function scheduleFullDrawDistancePrewarm(ctx) {
1148
865
  const { state } = ctx;
1149
- if (state.props.drawDistance <= INITIAL_DRAW_DISTANCE || state.queuedFullDrawDistancePrewarm !== void 0) {
866
+ if (state.props.drawDistance <= INITIAL_DRAW_DISTANCE || state.scheduledWork.has("fullDrawDistancePrewarm")) {
1150
867
  return;
1151
868
  }
1152
- state.queuedFullDrawDistancePrewarm = requestAnimationFrame(() => {
869
+ state.scheduledWork.frame(() => {
1153
870
  var _a3;
1154
- state.queuedFullDrawDistancePrewarm = void 0;
1155
- (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
1156
- });
871
+ return (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
872
+ }, "fullDrawDistancePrewarm");
1157
873
  }
1158
874
 
1159
875
  // src/utils/setInitialRenderState.ts
@@ -1211,10 +927,7 @@ function syncInitialScrollOffset(state, offset) {
1211
927
  state.scrollPrev = offset;
1212
928
  }
1213
929
  function clearPreservedInitialScrollTargetTimeout(state) {
1214
- if (state.timeoutPreservedInitialScrollClear !== void 0) {
1215
- clearTimeout(state.timeoutPreservedInitialScrollClear);
1216
- state.timeoutPreservedInitialScrollClear = void 0;
1217
- }
930
+ state.scheduledWork.cancel("preservedInitialScroll");
1218
931
  }
1219
932
  function clearPreservedInitialScrollTarget(state) {
1220
933
  clearPreservedInitialScrollTargetTimeout(state);
@@ -1243,14 +956,17 @@ function finishInitialScroll(ctx, options) {
1243
956
  setInitialScrollSession(state);
1244
957
  clearPreservedInitialScrollTargetTimeout(state);
1245
958
  if (options == null ? void 0 : options.schedulePreservedTargetClear) {
1246
- state.timeoutPreservedInitialScrollClear = setTimeout(() => {
1247
- var _a5;
1248
- state.timeoutPreservedInitialScrollClear = void 0;
1249
- if (!state.didFinishInitialScroll || ((_a5 = state.scrollingTo) == null ? void 0 : _a5.isInitialScroll) || !state.initialScroll) {
1250
- return;
1251
- }
1252
- clearPreservedInitialScrollTarget(state);
1253
- }, PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS);
959
+ state.scheduledWork.timeout(
960
+ () => {
961
+ var _a5;
962
+ if (!state.didFinishInitialScroll || ((_a5 = state.scrollingTo) == null ? void 0 : _a5.isInitialScroll) || !state.initialScroll) {
963
+ return;
964
+ }
965
+ clearPreservedInitialScrollTarget(state);
966
+ },
967
+ PRESERVED_INITIAL_SCROLL_FALLBACK_CLEAR_DELAY_MS,
968
+ "preservedInitialScroll"
969
+ );
1254
970
  }
1255
971
  } else {
1256
972
  clearPreservedInitialScrollTarget(state);
@@ -1277,9 +993,11 @@ function calculateOffsetForIndex(ctx, index) {
1277
993
  return index !== void 0 ? state.positions[index] || 0 : 0;
1278
994
  }
1279
995
 
1280
- // src/core/getTopOffsetAdjustment.ts
1281
- function getTopOffsetAdjustment(ctx) {
1282
- return (peek$(ctx, "stylePaddingTop") || 0) + (peek$(ctx, "alignItemsAtEndPadding") || 0) + (peek$(ctx, "headerSize") || 0);
996
+ // src/core/getStartOffsetAdjustment.ts
997
+ function getStartOffsetAdjustment(ctx) {
998
+ const { state } = ctx;
999
+ const stylePaddingStart = state.props.horizontal ? (isHorizontalRTL(state) ? state.props.stylePaddingRight : state.props.stylePaddingLeft) || 0 : peek$(ctx, "stylePaddingTop") || 0;
1000
+ return stylePaddingStart + (peek$(ctx, "alignItemsAtEndPadding") || 0) + (peek$(ctx, "headerSize") || 0);
1283
1001
  }
1284
1002
 
1285
1003
  // src/utils/getId.ts
@@ -1353,6 +1071,50 @@ function setSize(ctx, itemKey, size, notifyTotalSize = true) {
1353
1071
  sizes.set(itemKey, size);
1354
1072
  }
1355
1073
 
1074
+ // src/utils/helpers.ts
1075
+ function isFunction(obj) {
1076
+ return typeof obj === "function";
1077
+ }
1078
+ function isArray(obj) {
1079
+ return Array.isArray(obj);
1080
+ }
1081
+ var warned = /* @__PURE__ */ new Set();
1082
+ function warnDevOnce(id, text) {
1083
+ if (IS_DEV && !warned.has(id)) {
1084
+ warned.add(id);
1085
+ console.warn(`[legend-list] ${text}`);
1086
+ }
1087
+ }
1088
+ function roundSize(size) {
1089
+ return Math.floor(size * 8) / 8;
1090
+ }
1091
+ function isNullOrUndefined(value) {
1092
+ return value === null || value === void 0;
1093
+ }
1094
+ function getPadding(s, type) {
1095
+ var _a3, _b, _c;
1096
+ const axisPadding = type === "Left" || type === "Right" ? s.paddingHorizontal : s.paddingVertical;
1097
+ return (_c = (_b = (_a3 = s[`padding${type}`]) != null ? _a3 : axisPadding) != null ? _b : s.padding) != null ? _c : 0;
1098
+ }
1099
+ function extractPadding(style, contentContainerStyle, type) {
1100
+ return getPadding(style, type) + getPadding(contentContainerStyle, type);
1101
+ }
1102
+ function findContainerId(ctx, key) {
1103
+ var _a3, _b;
1104
+ const directMatch = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.containerItemKeys) == null ? void 0 : _b.get(key);
1105
+ if (directMatch !== void 0) {
1106
+ return directMatch;
1107
+ }
1108
+ const numContainers = peek$(ctx, "numContainers");
1109
+ for (let i = 0; i < numContainers; i++) {
1110
+ const itemKey = peek$(ctx, `containerItemKey${i}`);
1111
+ if (itemKey === key) {
1112
+ return i;
1113
+ }
1114
+ }
1115
+ return -1;
1116
+ }
1117
+
1356
1118
  // src/utils/getItemSize.ts
1357
1119
  function getKnownOrFixedSize(ctx, key, index, data, resolved) {
1358
1120
  var _a3, _b;
@@ -1446,9 +1208,9 @@ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1446
1208
  offset -= viewOffset;
1447
1209
  }
1448
1210
  if (index !== void 0) {
1449
- const topOffsetAdjustment = getTopOffsetAdjustment(ctx);
1450
- if (topOffsetAdjustment) {
1451
- offset += topOffsetAdjustment;
1211
+ const startOffsetAdjustment = getStartOffsetAdjustment(ctx);
1212
+ if (startOffsetAdjustment) {
1213
+ offset += startOffsetAdjustment;
1452
1214
  }
1453
1215
  }
1454
1216
  if (viewPosition !== void 0 && index !== void 0) {
@@ -1458,7 +1220,8 @@ function calculateOffsetWithOffsetPosition(ctx, offsetParam, params) {
1458
1220
  }
1459
1221
  const isOutOfBounds = index < 0 || index >= dataLength;
1460
1222
  const fallbackEstimatedSize = (_a3 = state.props.estimatedItemSize) != null ? _a3 : 0;
1461
- const itemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1223
+ const measuredItemSize = isOutOfBounds ? fallbackEstimatedSize : getItemSize(ctx, getId(state, index), index, state.props.data[index]);
1224
+ const itemSize = Math.max(0, measuredItemSize - (isOutOfBounds ? 0 : ctx.scrollAxisGap));
1462
1225
  const trailingInset = getContentInsetEnd(ctx);
1463
1226
  offset -= viewPosition * (state.scrollLength - trailingInset - itemSize);
1464
1227
  if (!isOutOfBounds && index === state.props.data.length - 1) {
@@ -1485,11 +1248,35 @@ function clampScrollOffset(ctx, offset, scrollTarget) {
1485
1248
  return clampedOffset;
1486
1249
  }
1487
1250
 
1251
+ // src/core/cancelImperativeScroll.ts
1252
+ function cancelScrollCompletionChecks({ scheduledWork }) {
1253
+ scheduledWork.cancel("checkFinishedScrollFrame");
1254
+ scheduledWork.cancel("checkFinishedScrollRetryFrame");
1255
+ scheduledWork.cancel("checkFinishedScrollFallback");
1256
+ scheduledWork.cancel("platformScrollCompletion");
1257
+ }
1258
+ function settlePendingImperativeScroll(state) {
1259
+ var _a3, _b;
1260
+ const resolvePendingScroll = (_b = state.pendingScrollResolve) != null ? _b : (_a3 = state.pendingScrollToEnd) == null ? void 0 : _a3.resolve;
1261
+ state.pendingScrollResolve = void 0;
1262
+ state.pendingScrollToEnd = void 0;
1263
+ resolvePendingScroll == null ? void 0 : resolvePendingScroll();
1264
+ }
1265
+ function cancelImperativeScroll(state) {
1266
+ cancelScrollCompletionChecks(state);
1267
+ state.scheduledWork.cancel("imperativeScrollReady");
1268
+ state.runPendingScrollToEnd = void 0;
1269
+ state.scrollingTo = void 0;
1270
+ state.scrollTargetPinnedRange = void 0;
1271
+ settlePendingImperativeScroll(state);
1272
+ }
1273
+
1488
1274
  // src/core/finishScrollTo.ts
1489
1275
  function finishScrollTo(ctx) {
1490
1276
  var _a3, _b;
1491
1277
  const state = ctx.state;
1492
1278
  if (state == null ? void 0 : state.scrollingTo) {
1279
+ cancelScrollCompletionChecks(state);
1493
1280
  const resolvePendingScroll = state.pendingScrollResolve;
1494
1281
  state.pendingScrollResolve = void 0;
1495
1282
  const scrollingTo = state.scrollingTo;
@@ -1531,6 +1318,7 @@ function doScrollTo(ctx, params) {
1531
1318
  var _a3, _b;
1532
1319
  const state = ctx.state;
1533
1320
  const { animated, horizontal, offset } = params;
1321
+ state.scheduledWork.cancel("platformScrollCompletion");
1534
1322
  const scroller = state.refScroller.current;
1535
1323
  const node = scroller == null ? void 0 : scroller.getScrollableNode();
1536
1324
  if (!scroller || !node) {
@@ -1551,9 +1339,16 @@ function doScrollTo(ctx, params) {
1551
1339
  });
1552
1340
  } else {
1553
1341
  state.scroll = offset;
1554
- setTimeout(() => {
1555
- finishScrollTo(ctx);
1556
- }, 100);
1342
+ const targetToken = state.scrollingTo;
1343
+ state.scheduledWork.timeout(
1344
+ () => {
1345
+ if (targetToken === state.scrollingTo) {
1346
+ finishScrollTo(ctx);
1347
+ }
1348
+ },
1349
+ 100,
1350
+ "platformScrollCompletion"
1351
+ );
1557
1352
  }
1558
1353
  }
1559
1354
  function listenForScrollEnd(ctx, params) {
@@ -1565,23 +1360,28 @@ function listenForScrollEnd(ctx, params) {
1565
1360
  const supportsScrollEnd = "onscrollend" in target;
1566
1361
  let idleTimeout;
1567
1362
  let settled = false;
1568
- const targetToken = ctx.state.scrollingTo;
1363
+ const { scheduledWork, scrollingTo: targetToken } = ctx.state;
1569
1364
  const maxTimeout = setTimeout(() => finish("max"), SCROLL_END_MAX_MS);
1570
1365
  const cleanup = () => {
1571
1366
  target.removeEventListener("scroll", onScroll2);
1572
1367
  if (supportsScrollEnd) {
1573
1368
  target.removeEventListener("scrollend", onScrollEnd);
1574
1369
  }
1575
- if (idleTimeout) {
1370
+ if (idleTimeout !== void 0) {
1576
1371
  clearTimeout(idleTimeout);
1577
1372
  }
1578
1373
  clearTimeout(maxTimeout);
1579
1374
  };
1375
+ const cancel = () => {
1376
+ if (!settled) {
1377
+ settled = true;
1378
+ cleanup();
1379
+ }
1380
+ };
1580
1381
  const finish = (reason) => {
1581
1382
  if (settled) return;
1582
1383
  if (targetToken !== ctx.state.scrollingTo) {
1583
- settled = true;
1584
- cleanup();
1384
+ scheduledWork.cancel("platformScrollCompletion");
1585
1385
  return;
1586
1386
  }
1587
1387
  const currentOffset = readOffset();
@@ -1589,12 +1389,11 @@ function listenForScrollEnd(ctx, params) {
1589
1389
  if (reason === "scrollend" && !isNearTarget) {
1590
1390
  return;
1591
1391
  }
1592
- settled = true;
1593
- cleanup();
1392
+ scheduledWork.cancel("platformScrollCompletion");
1594
1393
  finishScrollTo(ctx);
1595
1394
  };
1596
1395
  const onScroll2 = () => {
1597
- if (idleTimeout) {
1396
+ if (idleTimeout !== void 0) {
1598
1397
  clearTimeout(idleTimeout);
1599
1398
  }
1600
1399
  idleTimeout = setTimeout(() => finish("idle"), SCROLL_END_IDLE_MS);
@@ -1606,6 +1405,7 @@ function listenForScrollEnd(ctx, params) {
1606
1405
  } else {
1607
1406
  idleTimeout = setTimeout(() => finish("idle"), SMOOTH_SCROLL_DURATION_MS);
1608
1407
  }
1408
+ scheduledWork.register("platformScrollCompletion", cancel);
1609
1409
  }
1610
1410
 
1611
1411
  // src/core/doMaintainScrollAtEnd.ts
@@ -1632,9 +1432,12 @@ function doMaintainScrollAtEnd(ctx) {
1632
1432
  if (!state.maintainingScrollAtEnd) {
1633
1433
  const pendingState = maintainScrollAtEnd.animated ? "pending-animated" : "pending-instant";
1634
1434
  const activeState = maintainScrollAtEnd.animated ? "animated" : "instant";
1435
+ const scrollAtRequest = state.scroll;
1635
1436
  state.maintainingScrollAtEnd = pendingState;
1636
1437
  requestAnimationFrame(() => {
1637
- if (peek$(ctx, "isWithinMaintainScrollAtEndThreshold")) {
1438
+ const isStillWithinThreshold = peek$(ctx, "isWithinMaintainScrollAtEndThreshold");
1439
+ const didScrollSinceRequest = state.scroll !== scrollAtRequest;
1440
+ if (isStillWithinThreshold || !didScrollSinceRequest) {
1638
1441
  state.maintainingScrollAtEnd = activeState;
1639
1442
  const scroller = refScroller.current;
1640
1443
  if (state.props.horizontal && isHorizontalRTL(state)) {
@@ -1664,6 +1467,7 @@ function doMaintainScrollAtEnd(ctx) {
1664
1467
  );
1665
1468
  } else if (state.maintainingScrollAtEnd === pendingState) {
1666
1469
  state.maintainingScrollAtEnd = void 0;
1470
+ state.pendingMaintainScrollAtEnd = false;
1667
1471
  }
1668
1472
  });
1669
1473
  } else {
@@ -2031,6 +1835,24 @@ var getScrollVelocity = (state) => {
2031
1835
  return totalWeight > 0 ? weightedVelocity / totalWeight : 0;
2032
1836
  };
2033
1837
 
1838
+ // src/utils/hasActiveMVCPAnchorLock.ts
1839
+ function hasActiveMVCPAnchorLock(state) {
1840
+ const lock = state.mvcpAnchorLock;
1841
+ if (!lock) {
1842
+ return false;
1843
+ }
1844
+ if (Date.now() > lock.expiresAt) {
1845
+ state.mvcpAnchorLock = void 0;
1846
+ return false;
1847
+ }
1848
+ return true;
1849
+ }
1850
+
1851
+ // src/utils/isInMVCPActiveMode.ts
1852
+ function isInMVCPActiveMode(state) {
1853
+ return state.dataChangeNeedsScrollUpdate || hasActiveMVCPAnchorLock(state);
1854
+ }
1855
+
2034
1856
  // src/core/updateScroll.ts
2035
1857
  function updateScroll(ctx, newScroll, forceUpdate, options) {
2036
1858
  var _a3;
@@ -2068,6 +1890,8 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2068
1890
  state.scroll = newScroll;
2069
1891
  state.scrollTime = currentTime;
2070
1892
  const scrollDelta = Math.abs(newScroll - prevScroll);
1893
+ const isUserScrollEvent = !!(options == null ? void 0 : options.fromNativeScrollEvent) && scrollDelta > 0.1 && !adjustChanged && scrollingTo === void 0 && !state.pendingNativeMVCPAdjust;
1894
+ const allowedEdge = isUserScrollEvent ? beginReachedEdgeUserScroll(ctx, newScroll - prevScroll) : void 0;
2071
1895
  const didResolvePendingNativeMVCPAdjust = resolvePendingNativeMVCPAdjust(ctx, newScroll);
2072
1896
  const scrollLength = state.scrollLength;
2073
1897
  const isLargeUserScrollJump = scrollLength > 0 && scrollingTo === void 0 && scrollDelta > scrollLength && !state.pendingNativeMVCPAdjust;
@@ -2075,7 +1899,7 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2075
1899
  updateAdaptiveRender(ctx, scrollVelocity, { forceLight: isLargeUserScrollJump });
2076
1900
  const lastCalculated = state.scrollLastCalculate;
2077
1901
  const useAggressiveItemRecalculation = isInMVCPActiveMode(state);
2078
- const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
1902
+ const shouldUpdate = useAggressiveItemRecalculation || didResolvePendingNativeMVCPAdjust || allowedEdge !== void 0 || forceUpdate || lastCalculated === void 0 || Math.abs(state.scroll - lastCalculated) > 2;
2079
1903
  if (shouldUpdate) {
2080
1904
  state.scrollLastCalculate = state.scroll;
2081
1905
  state.ignoreScrollFromMVCPIgnored = false;
@@ -2090,16 +1914,13 @@ function updateScroll(ctx, newScroll, forceUpdate, options) {
2090
1914
  calculateItemsParams.drawDistanceMode = "visible-first";
2091
1915
  }
2092
1916
  (_a4 = state.triggerCalculateItemsInView) == null ? void 0 : _a4.call(state, calculateItemsParams);
2093
- checkThresholds(ctx);
1917
+ checkThresholds(ctx, allowedEdge);
2094
1918
  };
2095
1919
  if (isLargeUserScrollJump) {
2096
1920
  state.mvcpAnchorLock = void 0;
2097
1921
  state.pendingNativeMVCPAdjust = void 0;
2098
1922
  state.userScrollAnchorReset = { keys: /* @__PURE__ */ new Set() };
2099
- if (state.queuedMVCPRecalculate !== void 0) {
2100
- cancelAnimationFrame(state.queuedMVCPRecalculate);
2101
- state.queuedMVCPRecalculate = void 0;
2102
- }
1923
+ state.scheduledWork.cancel("mvcpRecalculate");
2103
1924
  ReactDOM.flushSync(runCalculateItems);
2104
1925
  scheduleFullDrawDistancePrewarm(ctx);
2105
1926
  } else {
@@ -2244,12 +2065,7 @@ function scrollTo(ctx, params) {
2244
2065
  const {
2245
2066
  props: { horizontal }
2246
2067
  } = state;
2247
- if (state.animFrameCheckFinishedScroll) {
2248
- cancelAnimationFrame(ctx.state.animFrameCheckFinishedScroll);
2249
- }
2250
- if (state.timeoutCheckFinishedScrollFallback) {
2251
- clearTimeout(ctx.state.timeoutCheckFinishedScrollFallback);
2252
- }
2068
+ cancelScrollCompletionChecks(state);
2253
2069
  const requestedOffset = precomputedWithViewOffset ? scrollTargetOffset : calculateOffsetWithOffsetPosition(ctx, scrollTargetOffset, scrollTarget);
2254
2070
  const shouldPreserveRawInitialOffsetRequest = !!isInitialScroll && ((_a3 = state.initialScrollSession) == null ? void 0 : _a3.kind) === "offset";
2255
2071
  const targetOffset = clampScrollOffset(ctx, requestedOffset, scrollTarget);
@@ -2354,10 +2170,7 @@ function setInitialScrollTarget(ctx, target, options) {
2354
2170
  var _a3;
2355
2171
  const { state } = ctx;
2356
2172
  state.clearPreservedInitialScrollOnNextFinish = void 0;
2357
- if (state.timeoutPreservedInitialScrollClear !== void 0) {
2358
- clearTimeout(state.timeoutPreservedInitialScrollClear);
2359
- state.timeoutPreservedInitialScrollClear = void 0;
2360
- }
2173
+ state.scheduledWork.cancel("preservedInitialScroll");
2361
2174
  state.initialScroll = target;
2362
2175
  if (options == null ? void 0 : options.resetDidFinish) {
2363
2176
  resetInitialRenderState(ctx, { resetInitialScroll: true });
@@ -2650,13 +2463,13 @@ function rearmBootstrapInitialScroll(ctx, options) {
2650
2463
  queueBootstrapInitialScrollReevaluation(ctx.state);
2651
2464
  }
2652
2465
  function createInitialScrollAtEndTarget(options) {
2653
- const { dataLength, footerSize, preserveForFooterLayout, stylePaddingBottom } = options;
2466
+ const { dataLength, footerSize, preserveForFooterLayout, stylePaddingEnd } = options;
2654
2467
  return {
2655
2468
  contentOffset: void 0,
2656
2469
  index: Math.max(0, dataLength - 1),
2657
2470
  preserveForBottomPadding: true,
2658
2471
  preserveForFooterLayout,
2659
- viewOffset: -stylePaddingBottom - footerSize,
2472
+ viewOffset: -stylePaddingEnd - footerSize,
2660
2473
  viewPosition: 1
2661
2474
  };
2662
2475
  }
@@ -2670,7 +2483,7 @@ function isRetargetableBottomAlignedInitialScrollTarget(target) {
2670
2483
  return !!(target && target.viewPosition === 1 && (shouldPreserveInitialScrollForBottomPadding(target) || shouldPreserveInitialScrollForFooterLayout(target)));
2671
2484
  }
2672
2485
  function createRetargetedBottomAlignedInitialScroll(options) {
2673
- const { dataLength, footerSize, initialScrollAtEnd, stylePaddingBottom, target } = options;
2486
+ const { dataLength, footerSize, initialScrollAtEnd, stylePaddingEnd, target } = options;
2674
2487
  const preserveForFooterLayout = shouldPreserveInitialScrollForFooterLayout(target);
2675
2488
  return {
2676
2489
  ...target,
@@ -2678,7 +2491,7 @@ function createRetargetedBottomAlignedInitialScroll(options) {
2678
2491
  index: initialScrollAtEnd ? Math.max(0, dataLength - 1) : target.index,
2679
2492
  preserveForBottomPadding: true,
2680
2493
  preserveForFooterLayout,
2681
- viewOffset: -stylePaddingBottom - (preserveForFooterLayout ? footerSize : 0),
2494
+ viewOffset: -stylePaddingEnd - (preserveForFooterLayout ? footerSize : 0),
2682
2495
  viewPosition: 1
2683
2496
  };
2684
2497
  }
@@ -2686,7 +2499,7 @@ function areEquivalentBootstrapInitialScrollTargets(current, next) {
2686
2499
  return current.index === next.index && current.preserveForBottomPadding === next.preserveForBottomPadding && current.preserveForFooterLayout === next.preserveForFooterLayout && current.viewOffset === next.viewOffset && current.viewPosition === next.viewPosition;
2687
2500
  }
2688
2501
  function clearPendingInitialScrollFooterLayout(ctx, options) {
2689
- const { dataLength, stylePaddingBottom, target } = options;
2502
+ const { dataLength, stylePaddingEnd, target } = options;
2690
2503
  if (!shouldPreserveInitialScrollForFooterLayout(target)) {
2691
2504
  return;
2692
2505
  }
@@ -2694,7 +2507,7 @@ function clearPendingInitialScrollFooterLayout(ctx, options) {
2694
2507
  dataLength,
2695
2508
  footerSize: 0,
2696
2509
  preserveForFooterLayout: void 0,
2697
- stylePaddingBottom
2510
+ stylePaddingEnd
2698
2511
  });
2699
2512
  setInitialScrollTarget(ctx, clearedFooterTarget);
2700
2513
  }
@@ -2755,7 +2568,7 @@ function schedulePreservedEndAnchorCorrectionFrame(ctx, correction) {
2755
2568
  });
2756
2569
  }
2757
2570
  function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {
2758
- var _a3, _b;
2571
+ var _a3;
2759
2572
  const state = ctx.state;
2760
2573
  const initialScroll = state.initialScroll;
2761
2574
  if (!state.didFinishInitialScroll || ((_a3 = state.scrollingTo) == null ? void 0 : _a3.isInitialScroll) || (initialScroll == null ? void 0 : initialScroll.viewPosition) !== 1 || state.preservedEndAnchorCorrection) {
@@ -2767,7 +2580,7 @@ function clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx) {
2767
2580
  if (shouldPreserveInitialScrollForFooterLayout(initialScroll)) {
2768
2581
  clearPendingInitialScrollFooterLayout(ctx, {
2769
2582
  dataLength: state.props.data.length,
2770
- stylePaddingBottom: (_b = state.props.stylePaddingBottom) != null ? _b : 0,
2583
+ stylePaddingEnd: getStylePaddingEnd(state.props),
2771
2584
  target: initialScroll
2772
2585
  });
2773
2586
  } else {
@@ -2804,7 +2617,7 @@ function startBootstrapInitialScrollOnMount(ctx, options) {
2804
2617
  }
2805
2618
  }
2806
2619
  function handleBootstrapInitialScrollDataChange(ctx, options) {
2807
- const { dataLength, didDataChange, initialScrollAtEnd, previousDataLength, stylePaddingBottom } = options;
2620
+ const { dataLength, didDataChange, initialScrollAtEnd, previousDataLength, stylePaddingEnd } = options;
2808
2621
  const state = ctx.state;
2809
2622
  const initialScroll = state.initialScroll;
2810
2623
  if (isOffsetInitialScrollSession(state) || !initialScroll) {
@@ -2826,18 +2639,18 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
2826
2639
  dataLength,
2827
2640
  footerSize: peek$(ctx, "footerSize") || 0,
2828
2641
  preserveForFooterLayout: shouldPreserveInitialScrollForFooterLayout(initialScroll),
2829
- stylePaddingBottom
2642
+ stylePaddingEnd
2830
2643
  }) : createRetargetedBottomAlignedInitialScroll({
2831
2644
  dataLength,
2832
2645
  footerSize: peek$(ctx, "footerSize") || 0,
2833
2646
  initialScrollAtEnd,
2834
- stylePaddingBottom,
2647
+ stylePaddingEnd,
2835
2648
  target: initialScroll
2836
2649
  });
2837
2650
  if (!shouldResetDidFinish && didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2838
2651
  clearPendingInitialScrollFooterLayout(ctx, {
2839
2652
  dataLength,
2840
- stylePaddingBottom,
2653
+ stylePaddingEnd,
2841
2654
  target: initialScroll
2842
2655
  });
2843
2656
  return;
@@ -2869,7 +2682,7 @@ function handleBootstrapInitialScrollDataChange(ctx, options) {
2869
2682
  }
2870
2683
  }
2871
2684
  function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2872
- const { dataLength, footerSize, initialScrollAtEnd, stylePaddingBottom } = options;
2685
+ const { dataLength, footerSize, initialScrollAtEnd, stylePaddingEnd } = options;
2873
2686
  const state = ctx.state;
2874
2687
  if (!initialScrollAtEnd) {
2875
2688
  return;
@@ -2885,7 +2698,7 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2885
2698
  if (didFinishedInitialScrollMoveAwayFromTarget(ctx, initialScroll)) {
2886
2699
  clearPendingInitialScrollFooterLayout(ctx, {
2887
2700
  dataLength,
2888
- stylePaddingBottom,
2701
+ stylePaddingEnd,
2889
2702
  target: initialScroll
2890
2703
  });
2891
2704
  } else {
@@ -2893,13 +2706,13 @@ function handleBootstrapInitialScrollFooterLayout(ctx, options) {
2893
2706
  dataLength,
2894
2707
  footerSize,
2895
2708
  preserveForFooterLayout: shouldPreserveInitialScrollForFooterLayout(initialScroll),
2896
- stylePaddingBottom
2709
+ stylePaddingEnd
2897
2710
  });
2898
2711
  const didTargetChange = initialScroll.index !== updatedInitialScroll.index || initialScroll.viewPosition !== updatedInitialScroll.viewPosition || initialScroll.viewOffset !== updatedInitialScroll.viewOffset;
2899
2712
  if (!didTargetChange) {
2900
2713
  clearPendingInitialScrollFooterLayout(ctx, {
2901
2714
  dataLength,
2902
- stylePaddingBottom,
2715
+ stylePaddingEnd,
2903
2716
  target: initialScroll
2904
2717
  });
2905
2718
  } else {
@@ -3051,6 +2864,47 @@ function abortBootstrapInitialScroll(ctx) {
3051
2864
  }
3052
2865
  }
3053
2866
 
2867
+ // src/core/containerItemMetadata.ts
2868
+ function createContainerItemMetadata(state, itemIndex, itemData, itemType) {
2869
+ return {
2870
+ dataChangeEpoch: state.dataChangeEpoch,
2871
+ getFixedItemSize: state.props.getFixedItemSize,
2872
+ getItemType: state.props.getItemType,
2873
+ itemData,
2874
+ itemIndex,
2875
+ itemType
2876
+ };
2877
+ }
2878
+ function resolveContainerItemMetadata(state, containerId, itemIndex, itemData) {
2879
+ var _a3, _b;
2880
+ const { getFixedItemSize, getItemType } = state.props;
2881
+ const previousMetadata = state.containerItemMetadata.get(containerId);
2882
+ let metadata;
2883
+ if ((previousMetadata == null ? void 0 : previousMetadata.dataChangeEpoch) === state.dataChangeEpoch && previousMetadata.getItemType === getItemType && previousMetadata.itemData === itemData && previousMetadata.itemIndex === itemIndex) {
2884
+ metadata = previousMetadata;
2885
+ } else {
2886
+ const itemType = getItemType ? (_a3 = getItemType(itemData, itemIndex)) != null ? _a3 : "" : void 0;
2887
+ metadata = createContainerItemMetadata(state, itemIndex, itemData, itemType);
2888
+ state.containerItemMetadata.set(containerId, metadata);
2889
+ }
2890
+ if (metadata.getFixedItemSize !== getFixedItemSize) {
2891
+ metadata.didResolveFixedItemSize = false;
2892
+ metadata.fixedItemSize = void 0;
2893
+ metadata.getFixedItemSize = getFixedItemSize;
2894
+ }
2895
+ if (getFixedItemSize && !metadata.didResolveFixedItemSize) {
2896
+ metadata.fixedItemSize = getFixedItemSize(itemData, itemIndex, (_b = metadata.itemType) != null ? _b : "");
2897
+ metadata.didResolveFixedItemSize = true;
2898
+ }
2899
+ return metadata;
2900
+ }
2901
+ function invalidateContainerFixedItemSizes(state) {
2902
+ for (const metadata of state.containerItemMetadata.values()) {
2903
+ metadata.didResolveFixedItemSize = false;
2904
+ metadata.fixedItemSize = void 0;
2905
+ }
2906
+ }
2907
+
3054
2908
  // src/core/checkFinishedScroll.ts
3055
2909
  var INITIAL_SCROLL_MAX_FALLBACK_CHECKS = 20;
3056
2910
  var INITIAL_SCROLL_COMPLETION_TARGET_EPSILON = 1;
@@ -3066,7 +2920,7 @@ function checkFinishedScroll(ctx, options) {
3066
2920
  return;
3067
2921
  }
3068
2922
  }
3069
- ctx.state.animFrameCheckFinishedScroll = requestAnimationFrame(() => checkFinishedScrollFrame(ctx));
2923
+ ctx.state.scheduledWork.frame(() => checkFinishedScrollFrame(ctx), "checkFinishedScrollFrame");
3070
2924
  }
3071
2925
  function hasScrollCompletionOwnership(state, options) {
3072
2926
  const { clampedTargetOffset, scrollingTo } = options;
@@ -3136,7 +2990,6 @@ function checkFinishedScrollFrame(ctx) {
3136
2990
  return;
3137
2991
  }
3138
2992
  const { state } = ctx;
3139
- state.animFrameCheckFinishedScroll = void 0;
3140
2993
  const completionState = getResolvedScrollCompletionState(ctx, scrollingTo);
3141
2994
  if (completionState.isAtResolvedTarget && hasScrollCompletionOwnership(state, {
3142
2995
  clampedTargetOffset: completionState.clampedTargetOffset,
@@ -3155,47 +3008,47 @@ function scrollToFallbackOffset(ctx, offset) {
3155
3008
  }
3156
3009
  function checkFinishedScrollFallback(ctx) {
3157
3010
  const state = ctx.state;
3011
+ if (state.scheduledWork.has("checkFinishedScrollFallback")) {
3012
+ return;
3013
+ }
3158
3014
  const scrollingTo = state.scrollingTo;
3159
3015
  const shouldFinishInitialZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
3160
3016
  const silentInitialDispatch = isSilentInitialDispatch(state, scrollingTo);
3161
3017
  const canFinishInitialWithoutNativeProgress = scrollingTo !== void 0 ? shouldFinishInitialScrollWithoutNativeProgress(state, scrollingTo) : false;
3162
3018
  const slowTimeout = (scrollingTo == null ? void 0 : scrollingTo.isInitialScroll) && !shouldFinishInitialZeroTarget && !canFinishInitialWithoutNativeProgress || !state.didContainersLayout;
3163
3019
  const initialDelay = shouldFinishInitialZeroTarget || canFinishInitialWithoutNativeProgress ? 0 : silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : slowTimeout ? 500 : 100;
3164
- state.timeoutCheckFinishedScrollFallback = setTimeout(() => {
3165
- let numChecks = 0;
3166
- const scheduleFallbackCheck = (delay) => {
3167
- state.timeoutCheckFinishedScrollFallback = setTimeout(checkHasScrolled, delay);
3168
- };
3169
- const checkHasScrolled = () => {
3170
- var _c, _d;
3171
- state.timeoutCheckFinishedScrollFallback = void 0;
3172
- const isStillScrollingTo = state.scrollingTo;
3173
- if (isStillScrollingTo) {
3174
- numChecks++;
3175
- const isNativeInitialPending = isNativeInitialNonZeroTarget(state) && !state.hasScrolled;
3176
- const maxChecks = silentInitialDispatch ? 5 : isNativeInitialPending ? INITIAL_SCROLL_MAX_FALLBACK_CHECKS : 5;
3177
- const shouldFinishZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
3178
- const canFinishInitialScrollWithoutNativeProgress = shouldFinishInitialScrollWithoutNativeProgress(
3179
- state,
3180
- isStillScrollingTo
3181
- );
3182
- const completionState = getResolvedScrollCompletionState(ctx, isStillScrollingTo);
3183
- const canFinishAfterSilentNativeDispatch = Platform.OS === "android";
3184
- const shouldFinishAfterObservedScroll = state.hasScrolled && (!isStillScrollingTo.isInitialScroll || completionState.isAtResolvedTarget);
3185
- const shouldRetryUnalignedInitialScroll = isStillScrollingTo.isInitialScroll && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
3186
- if (shouldFinishZeroTarget || shouldFinishAfterObservedScroll || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
3187
- finishScrollTo(ctx);
3188
- } else if ((isNativeInitialPending || shouldRetryUnalignedInitialScroll) && numChecks <= maxChecks) {
3189
- const targetOffset = (_d = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : isStillScrollingTo.targetOffset) != null ? _d : state.scrollPending;
3190
- scrollToFallbackOffset(ctx, targetOffset);
3191
- scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
3192
- } else {
3193
- scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
3194
- }
3020
+ let numChecks = 0;
3021
+ const scheduleFallbackCheck = (delay) => {
3022
+ state.scheduledWork.timeout(checkHasScrolled, delay, "checkFinishedScrollFallback");
3023
+ };
3024
+ const checkHasScrolled = () => {
3025
+ var _c, _d;
3026
+ const isStillScrollingTo = state.scrollingTo;
3027
+ if (isStillScrollingTo) {
3028
+ numChecks++;
3029
+ const isNativeInitialPending = isNativeInitialNonZeroTarget(state) && !state.hasScrolled;
3030
+ const maxChecks = silentInitialDispatch ? 5 : isNativeInitialPending ? INITIAL_SCROLL_MAX_FALLBACK_CHECKS : 5;
3031
+ const shouldFinishZeroTarget = shouldFinishInitialZeroTargetScroll(ctx);
3032
+ const canFinishInitialScrollWithoutNativeProgress = shouldFinishInitialScrollWithoutNativeProgress(
3033
+ state,
3034
+ isStillScrollingTo
3035
+ );
3036
+ const completionState = getResolvedScrollCompletionState(ctx, isStillScrollingTo);
3037
+ const canFinishAfterSilentNativeDispatch = Platform.OS === "android";
3038
+ const shouldFinishAfterObservedScroll = state.hasScrolled && (!isStillScrollingTo.isInitialScroll || completionState.isAtResolvedTarget);
3039
+ const shouldRetryUnalignedInitialScroll = isStillScrollingTo.isInitialScroll && !completionState.isAtResolvedTarget && numChecks <= maxChecks;
3040
+ if (shouldFinishZeroTarget || shouldFinishAfterObservedScroll || canFinishInitialScrollWithoutNativeProgress || canFinishAfterSilentNativeDispatch || numChecks > maxChecks) {
3041
+ finishScrollTo(ctx);
3042
+ } else if ((isNativeInitialPending || shouldRetryUnalignedInitialScroll) && numChecks <= maxChecks) {
3043
+ const targetOffset = (_d = (_c = getInitialScrollWatchdogTargetOffset(state)) != null ? _c : isStillScrollingTo.targetOffset) != null ? _d : state.scrollPending;
3044
+ scrollToFallbackOffset(ctx, targetOffset);
3045
+ scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
3046
+ } else {
3047
+ scheduleFallbackCheck(silentInitialDispatch ? SILENT_INITIAL_SCROLL_RETRY_DELAY_MS : 100);
3195
3048
  }
3196
- };
3197
- checkHasScrolled();
3198
- }, initialDelay);
3049
+ }
3050
+ };
3051
+ scheduleFallbackCheck(initialDelay);
3199
3052
  }
3200
3053
 
3201
3054
  // src/core/initialScrollLifecycle.ts
@@ -3271,7 +3124,7 @@ function handleInitialScrollDataChange(ctx, options) {
3271
3124
  initialScrollAtEnd,
3272
3125
  latestInitialScroll,
3273
3126
  latestInitialScrollSessionKind,
3274
- stylePaddingBottom,
3127
+ stylePaddingEnd,
3275
3128
  useBootstrapInitialScroll
3276
3129
  } = options;
3277
3130
  const state = ctx.state;
@@ -3301,7 +3154,7 @@ function handleInitialScrollDataChange(ctx, options) {
3301
3154
  didDataChange,
3302
3155
  initialScrollAtEnd,
3303
3156
  previousDataLength: previousInitialScrollDataLength,
3304
- stylePaddingBottom
3157
+ stylePaddingEnd
3305
3158
  });
3306
3159
  return;
3307
3160
  }
@@ -3324,6 +3177,52 @@ function resetLayoutCachesForDataChange(state) {
3324
3177
  state.columnSpans.length = 0;
3325
3178
  }
3326
3179
 
3180
+ // src/core/scheduleContainerLayout.ts
3181
+ function getContainerLayoutEffectScope(ctx) {
3182
+ var _a3;
3183
+ const scheduledIds = ctx.pendingContainerIds;
3184
+ ctx.pendingContainerIds = void 0;
3185
+ if (scheduledIds === void 0) {
3186
+ return void 0;
3187
+ }
3188
+ const state = ctx.state;
3189
+ let targetContainerIds = scheduledIds;
3190
+ if (targetContainerIds && ((_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys.size)) {
3191
+ targetContainerIds = new Set(targetContainerIds);
3192
+ for (const itemKey of state.userScrollAnchorReset.keys) {
3193
+ const containerId = state.containerItemKeys.get(itemKey);
3194
+ if (containerId !== void 0) {
3195
+ targetContainerIds.add(containerId);
3196
+ }
3197
+ }
3198
+ }
3199
+ return targetContainerIds;
3200
+ }
3201
+ function scheduleContainerLayout(ctx, target) {
3202
+ var _a3;
3203
+ const isAlreadyScheduled = ctx.pendingContainerIds !== void 0;
3204
+ const previousIds = ctx.pendingContainerIds;
3205
+ if (target === void 0) {
3206
+ ctx.pendingContainerIds = null;
3207
+ } else if (previousIds !== null) {
3208
+ let nextIds = previousIds;
3209
+ if (!nextIds) {
3210
+ nextIds = typeof target === "number" ? /* @__PURE__ */ new Set([target]) : new Set(target);
3211
+ } else if (typeof target === "number") {
3212
+ nextIds.add(target);
3213
+ } else {
3214
+ for (const containerId of target) {
3215
+ nextIds.add(containerId);
3216
+ }
3217
+ }
3218
+ ctx.pendingContainerIds = nextIds;
3219
+ }
3220
+ if (!isAlreadyScheduled) {
3221
+ const nextEpoch = ((_a3 = peek$(ctx, "containerLayoutEpoch")) != null ? _a3 : 0) + 1;
3222
+ set$(ctx, "containerLayoutEpoch", nextEpoch);
3223
+ }
3224
+ }
3225
+
3327
3226
  // src/core/syncMountedContainer.ts
3328
3227
  function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3329
3228
  var _a3, _b, _c, _d, _e, _f, _g, _h, _i;
@@ -3367,14 +3266,13 @@ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3367
3266
  set$(ctx, `containerSpan${containerIndex}`, span);
3368
3267
  }
3369
3268
  }
3370
- const prevItemInfo = peek$(ctx, `containerItemInfo${containerIndex}`);
3269
+ const prevIndex = peek$(ctx, `containerItemIndex${containerIndex}`);
3270
+ if (prevIndex !== itemIndex) {
3271
+ set$(ctx, `containerItemIndex${containerIndex}`, itemIndex);
3272
+ }
3371
3273
  const prevData = peek$(ctx, `containerItemData${containerIndex}`);
3372
- let itemInfoValue = prevData;
3373
- let didChangeItemInfo = (prevItemInfo == null ? void 0 : prevItemInfo.itemKey) !== itemKey || (prevItemInfo == null ? void 0 : prevItemInfo.index) !== itemIndex || (prevItemInfo == null ? void 0 : prevItemInfo.value) !== prevData;
3374
3274
  const updateData = () => {
3375
3275
  set$(ctx, `containerItemData${containerIndex}`, item);
3376
- itemInfoValue = item;
3377
- didChangeItemInfo = true;
3378
3276
  didRefreshData = true;
3379
3277
  };
3380
3278
  if (prevData !== item) {
@@ -3409,13 +3307,6 @@ function syncMountedContainer(ctx, containerIndex, itemIndex, options) {
3409
3307
  }
3410
3308
  }
3411
3309
  }
3412
- if (didChangeItemInfo) {
3413
- set$(ctx, `containerItemInfo${containerIndex}`, {
3414
- index: itemIndex,
3415
- itemKey,
3416
- value: itemInfoValue
3417
- });
3418
- }
3419
3310
  return { didChangePosition, didRefreshData };
3420
3311
  }
3421
3312
 
@@ -3733,7 +3624,6 @@ function setupViewability(props) {
3733
3624
  }
3734
3625
  function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollSize, start, end, startBuffered = start, endBuffered = end) {
3735
3626
  const {
3736
- timeouts,
3737
3627
  props: { data }
3738
3628
  } = state;
3739
3629
  for (const viewabilityConfigCallbackPair of viewabilityConfigCallbackPairs) {
@@ -3743,11 +3633,10 @@ function updateViewableItems(state, ctx, viewabilityConfigCallbackPairs, scrollS
3743
3633
  viewabilityState.startBuffered = startBuffered;
3744
3634
  viewabilityState.endBuffered = endBuffered;
3745
3635
  if (viewabilityConfigCallbackPair.viewabilityConfig.minimumViewTime) {
3746
- const timer = setTimeout(() => {
3747
- timeouts.delete(timer);
3748
- updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, state, ctx, scrollSize);
3749
- }, viewabilityConfigCallbackPair.viewabilityConfig.minimumViewTime);
3750
- timeouts.add(timer);
3636
+ state.scheduledWork.timeout(
3637
+ () => updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, state, ctx, scrollSize),
3638
+ viewabilityConfigCallbackPair.viewabilityConfig.minimumViewTime
3639
+ );
3751
3640
  } else {
3752
3641
  updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, state, ctx, scrollSize);
3753
3642
  }
@@ -3780,19 +3669,29 @@ function updateViewableItemsWithConfig(data, viewabilityConfigCallbackPair, stat
3780
3669
  if (previousViewableItems) {
3781
3670
  for (const viewToken of previousViewableItems) {
3782
3671
  previousViewableKeys.add(viewToken.key);
3672
+ const currentIndex = state.indexByKey.get(viewToken.key);
3673
+ const currentItem = currentIndex !== void 0 ? data[currentIndex] : void 0;
3783
3674
  const containerId = findContainerId(ctx, viewToken.key);
3784
- if (!checkIsViewable(
3785
- state,
3786
- ctx,
3787
- viewabilityConfig,
3788
- containerId,
3789
- viewToken.key,
3790
- scrollSize,
3791
- viewToken.item,
3792
- viewToken.index
3793
- )) {
3794
- viewToken.isViewable = false;
3795
- changed.push(viewToken);
3675
+ let isStillViewable = false;
3676
+ if (currentIndex !== void 0 && currentItem !== void 0) {
3677
+ isStillViewable = checkIsViewable(
3678
+ state,
3679
+ ctx,
3680
+ viewabilityConfig,
3681
+ containerId,
3682
+ viewToken.key,
3683
+ scrollSize,
3684
+ currentItem,
3685
+ currentIndex
3686
+ );
3687
+ }
3688
+ if (!isStillViewable) {
3689
+ changed.push({
3690
+ ...viewToken,
3691
+ index: currentIndex != null ? currentIndex : viewToken.index,
3692
+ isViewable: false,
3693
+ item: currentItem != null ? currentItem : viewToken.item
3694
+ });
3796
3695
  }
3797
3696
  }
3798
3697
  }
@@ -3949,128 +3848,102 @@ function getExpandedContainerPoolSize(dataLength, numContainers) {
3949
3848
 
3950
3849
  // src/utils/findAvailableContainers.ts
3951
3850
  function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffered, pendingRemoval, getRequiredItemType, protectedKeys) {
3851
+ var _a3;
3952
3852
  const numNeeded = needNewContainers.length;
3953
3853
  if (numNeeded === 0) {
3954
3854
  return [];
3955
3855
  }
3956
3856
  const numContainers = peek$(ctx, "numContainers");
3957
3857
  const state = ctx.state;
3958
- const { stickyContainerPool, containerItemTypes } = state;
3858
+ const { containerItemMetadata, stickyContainerPool } = state;
3959
3859
  const shouldAvoidAssignedContainerReuse = state.props.recycleItems && !!state.props.positionComponentInternal;
3960
- const allocations = [];
3961
3860
  const pendingRemovalSet = pendingRemoval.length > 0 ? new Set(pendingRemoval) : void 0;
3962
- let pendingRemovalChanged = false;
3861
+ const requests = needNewContainers.map((itemIndex, order) => ({
3862
+ isSticky: state.props.stickyHeaderIndicesSet.has(itemIndex),
3863
+ itemIndex,
3864
+ itemType: getRequiredItemType == null ? void 0 : getRequiredItemType(itemIndex),
3865
+ order
3866
+ }));
3867
+ const normalRequests = requests.filter((request) => !request.isSticky);
3868
+ const stickyRequests = requests.filter((request) => request.isSticky);
3869
+ const normalCandidates = [];
3870
+ const stickyCandidates = [];
3871
+ for (let containerIndex = 0; containerIndex < numContainers; containerIndex++) {
3872
+ const key = peek$(ctx, `containerItemKey${containerIndex}`);
3873
+ const isPendingRemoval = !!(pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(containerIndex));
3874
+ const isProtected = !!key && !!(protectedKeys == null ? void 0 : protectedKeys.has(key)) && state.indexByKey.has(key);
3875
+ if (isProtected) {
3876
+ continue;
3877
+ }
3878
+ if (stickyContainerPool.has(containerIndex)) {
3879
+ if (key === void 0 || isPendingRemoval) {
3880
+ stickyCandidates.push({ containerIndex, distance: Number.POSITIVE_INFINITY });
3881
+ }
3882
+ } else if (key === void 0 || isPendingRemoval) {
3883
+ normalCandidates.push({ containerIndex, distance: Number.POSITIVE_INFINITY });
3884
+ } else if (!shouldAvoidAssignedContainerReuse) {
3885
+ const index = state.indexByKey.get(key);
3886
+ if (index !== void 0 && (index < startBuffered || index > endBuffered)) {
3887
+ const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
3888
+ normalCandidates.push({ containerIndex, distance });
3889
+ }
3890
+ }
3891
+ }
3892
+ normalCandidates.sort(comparatorByDistance);
3893
+ const allocations = new Array(numNeeded);
3963
3894
  let nextNewContainerIndex = numContainers;
3964
- const usedContainers = /* @__PURE__ */ new Set();
3965
- let availableContainers;
3966
- const stickyHeaderIndicesSet = state.props.stickyHeaderIndicesSet;
3967
- const canReuseContainer = (containerIndex, requiredType) => {
3968
- if (!requiredType) return true;
3969
- const existingType = containerItemTypes.get(containerIndex);
3970
- if (!existingType) return true;
3971
- return existingType === requiredType;
3972
- };
3973
- const pushAllocation = (itemIndex, itemType, containerIndex) => {
3974
- allocations.push({
3895
+ let pendingRemovalChanged = false;
3896
+ const assign = (request, containerIndex) => {
3897
+ allocations[request.order] = {
3975
3898
  containerIndex,
3976
- itemIndex,
3977
- itemType
3978
- });
3979
- usedContainers.add(containerIndex);
3899
+ itemIndex: request.itemIndex,
3900
+ itemType: request.itemType
3901
+ };
3980
3902
  if (pendingRemovalSet == null ? void 0 : pendingRemovalSet.delete(containerIndex)) {
3981
3903
  pendingRemovalChanged = true;
3982
3904
  }
3983
3905
  };
3984
- const pushNewContainer = (itemIndex, itemType, isSticky) => {
3985
- const newContainerIndex = nextNewContainerIndex++;
3986
- pushAllocation(itemIndex, itemType, newContainerIndex);
3987
- if (isSticky) {
3988
- stickyContainerPool.add(newContainerIndex);
3989
- }
3990
- return newContainerIndex;
3991
- };
3992
- const canUseContainer = (containerIndex, itemType) => {
3993
- if (usedContainers.has(containerIndex) || stickyContainerPool.has(containerIndex)) {
3994
- return false;
3995
- }
3996
- const key = peek$(ctx, `containerItemKey${containerIndex}`);
3997
- const isPending = !!(pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(containerIndex));
3998
- return (key === void 0 || isPending) && canReuseContainer(containerIndex, itemType);
3999
- };
4000
- const findStickyContainer = (itemType) => {
4001
- let foundContainer;
4002
- for (const containerIndex of stickyContainerPool) {
4003
- if (!usedContainers.has(containerIndex)) {
4004
- const key = peek$(ctx, `containerItemKey${containerIndex}`);
4005
- const isPendingRemoval = !!(pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(containerIndex));
4006
- if ((key === void 0 || isPendingRemoval) && canReuseContainer(containerIndex, itemType)) {
4007
- foundContainer = containerIndex;
4008
- break;
4009
- }
4010
- }
4011
- }
4012
- return foundContainer;
4013
- };
4014
- const findUnassignedOrPendingContainer = (itemType) => {
4015
- let foundContainer;
4016
- for (let containerIndex = 0; containerIndex < numContainers && foundContainer === void 0; containerIndex++) {
4017
- if (canUseContainer(containerIndex, itemType)) {
4018
- foundContainer = containerIndex;
3906
+ const assignMatching = (pendingRequests, candidates, matches) => {
3907
+ for (const request of pendingRequests) {
3908
+ if (allocations[request.order]) {
3909
+ continue;
4019
3910
  }
4020
- }
4021
- return foundContainer;
4022
- };
4023
- const getAvailableContainers = () => {
4024
- if (!availableContainers) {
4025
- availableContainers = [];
4026
- if (!shouldAvoidAssignedContainerReuse) {
4027
- for (let containerIndex = 0; containerIndex < numContainers; containerIndex++) {
4028
- if (usedContainers.has(containerIndex) || stickyContainerPool.has(containerIndex)) {
4029
- continue;
4030
- }
4031
- const key = peek$(ctx, `containerItemKey${containerIndex}`);
4032
- if (key === void 0) continue;
4033
- if ((protectedKeys == null ? void 0 : protectedKeys.has(key)) && state.indexByKey.has(key)) continue;
4034
- const index = state.indexByKey.get(key);
4035
- const isOutOfView = index < startBuffered || index > endBuffered;
4036
- if (isOutOfView) {
4037
- const distance = index < startBuffered ? startBuffered - index : index - endBuffered;
4038
- availableContainers.push({ distance, index: containerIndex });
4039
- }
3911
+ const candidateIndex = candidates.findIndex(
3912
+ (candidate) => {
3913
+ var _a4;
3914
+ return matches((_a4 = containerItemMetadata.get(candidate.containerIndex)) == null ? void 0 : _a4.itemType, request.itemType);
4040
3915
  }
4041
- availableContainers.sort(comparatorByDistance);
3916
+ );
3917
+ if (candidateIndex !== -1) {
3918
+ const [candidate] = candidates.splice(candidateIndex, 1);
3919
+ assign(request, candidate.containerIndex);
4042
3920
  }
4043
3921
  }
4044
- return availableContainers;
4045
3922
  };
4046
- const findAvailableContainer = (itemType) => {
4047
- const containers = getAvailableContainers();
4048
- let matchIndex = -1;
4049
- for (let i = 0; i < containers.length && matchIndex === -1; i++) {
4050
- const containerIndex = containers[i].index;
4051
- if (!usedContainers.has(containerIndex) && canReuseContainer(containerIndex, itemType)) {
4052
- matchIndex = i;
4053
- }
3923
+ const assignFromPool = (pendingRequests, candidates, allowCrossType) => {
3924
+ if (getRequiredItemType) {
3925
+ assignMatching(
3926
+ pendingRequests,
3927
+ candidates,
3928
+ (containerType, requestType) => requestType !== void 0 && containerType === requestType
3929
+ );
3930
+ }
3931
+ assignMatching(pendingRequests, candidates, (containerType) => containerType === void 0);
3932
+ if (allowCrossType) {
3933
+ assignMatching(pendingRequests, candidates, () => true);
4054
3934
  }
4055
- return matchIndex === -1 ? void 0 : containers.splice(matchIndex, 1)[0].index;
4056
3935
  };
4057
- for (const itemIndex of needNewContainers) {
4058
- const itemType = getRequiredItemType == null ? void 0 : getRequiredItemType(itemIndex);
4059
- const isSticky = stickyHeaderIndicesSet.has(itemIndex);
4060
- let containerIndex;
4061
- if (isSticky) {
4062
- containerIndex = findStickyContainer(itemType);
4063
- } else {
4064
- containerIndex = findUnassignedOrPendingContainer(itemType);
4065
- if (containerIndex === void 0) {
4066
- containerIndex = findAvailableContainer(itemType);
4067
- }
3936
+ assignFromPool(normalRequests, normalCandidates, true);
3937
+ assignFromPool(stickyRequests, stickyCandidates, false);
3938
+ for (const request of requests) {
3939
+ if (allocations[request.order]) {
3940
+ continue;
4068
3941
  }
4069
- if (containerIndex !== void 0) {
4070
- pushAllocation(itemIndex, itemType, containerIndex);
4071
- } else {
4072
- pushNewContainer(itemIndex, itemType, isSticky);
3942
+ const containerIndex = nextNewContainerIndex++;
3943
+ if (request.isSticky) {
3944
+ stickyContainerPool.add(containerIndex);
4073
3945
  }
3946
+ assign(request, containerIndex);
4074
3947
  }
4075
3948
  if (pendingRemovalChanged) {
4076
3949
  pendingRemoval.length = 0;
@@ -4080,18 +3953,21 @@ function findAvailableContainers(ctx, needNewContainers, startBuffered, endBuffe
4080
3953
  }
4081
3954
  }
4082
3955
  }
4083
- if (IS_DEV && nextNewContainerIndex > peek$(ctx, "numContainersPooled")) {
4084
- console.warn(
4085
- "[legend-list] No unused container available, so creating one on demand. This can be a minor performance issue and is likely caused by the estimatedItemSize being too large. Consider decreasing estimatedItemSize.",
4086
- {
4087
- debugInfo: {
4088
- numContainers,
4089
- numContainersPooled: peek$(ctx, "numContainersPooled"),
4090
- numNeeded,
4091
- stillNeeded: nextNewContainerIndex - numContainers
3956
+ if (IS_DEV) {
3957
+ const numContainersPooled = (_a3 = peek$(ctx, "numContainersPooled")) != null ? _a3 : Number.POSITIVE_INFINITY;
3958
+ if (nextNewContainerIndex > numContainersPooled) {
3959
+ console.warn(
3960
+ "[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.",
3961
+ {
3962
+ debugInfo: {
3963
+ numContainers,
3964
+ numContainersPooled,
3965
+ numNeeded,
3966
+ stillNeeded: nextNewContainerIndex - numContainers
3967
+ }
4092
3968
  }
4093
- }
4094
- );
3969
+ );
3970
+ }
4095
3971
  }
4096
3972
  return allocations;
4097
3973
  }
@@ -4119,20 +3995,15 @@ function getProjectedBufferAdjustment(scrollVelocity, trailingBuffer) {
4119
3995
  }
4120
3996
  function scheduleRenderRangeProjectionSettle(ctx) {
4121
3997
  const state = ctx.state;
4122
- const previousTimeout = state.timeoutRenderRangeProjectionSettle;
4123
- if (previousTimeout !== void 0) {
4124
- clearTimeout(previousTimeout);
4125
- state.timeouts.delete(previousTimeout);
4126
- }
4127
- const timeout = setTimeout(() => {
4128
- var _a3;
4129
- state.timeoutRenderRangeProjectionSettle = void 0;
4130
- state.timeouts.delete(timeout);
4131
- state.scrollHistory.length = 0;
4132
- (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
4133
- }, RENDER_RANGE_PROJECTION_SETTLE_DELAY);
4134
- state.timeoutRenderRangeProjectionSettle = timeout;
4135
- state.timeouts.add(timeout);
3998
+ state.scheduledWork.timeout(
3999
+ () => {
4000
+ var _a3;
4001
+ state.scrollHistory.length = 0;
4002
+ (_a3 = state.triggerCalculateItemsInView) == null ? void 0 : _a3.call(state);
4003
+ },
4004
+ RENDER_RANGE_PROJECTION_SETTLE_DELAY,
4005
+ "renderRangeProjection"
4006
+ );
4136
4007
  }
4137
4008
  function findCurrentStickyIndex(stickyArray, scroll, state) {
4138
4009
  const positions = state.positions;
@@ -4326,7 +4197,7 @@ function updateViewabilityForCachedRange(ctx, viewabilityConfigCallbackPairs, sc
4326
4197
  function calculateItemsInView(ctx, params = {}) {
4327
4198
  const state = ctx.state;
4328
4199
  batchedUpdates(() => {
4329
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t;
4200
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u;
4330
4201
  const {
4331
4202
  columns,
4332
4203
  containerItemKeys,
@@ -4354,6 +4225,7 @@ function calculateItemsInView(ctx, params = {}) {
4354
4225
  return;
4355
4226
  }
4356
4227
  let totalSize = getContentSize(ctx);
4228
+ let changedContainerIds;
4357
4229
  const topPad = peek$(ctx, "stylePaddingTop") + peek$(ctx, "alignItemsAtEndPadding") + peek$(ctx, "headerSize");
4358
4230
  const numColumns = peek$(ctx, "numColumns");
4359
4231
  const speed = (_b = params.scrollVelocity) != null ? _b : getScrollVelocity(state);
@@ -4479,7 +4351,7 @@ function calculateItemsInView(ctx, params = {}) {
4479
4351
  const scrollAdjustPendingBeforeMVCP = (_f = peek$(ctx, "scrollAdjustPending")) != null ? _f : 0;
4480
4352
  checkMVCP == null ? void 0 : checkMVCP();
4481
4353
  const didMVCPAdjustScroll = !!checkMVCP && (state.scroll !== scrollBeforeMVCP || ((_g = peek$(ctx, "scrollAdjustPending")) != null ? _g : 0) !== scrollAdjustPendingBeforeMVCP);
4482
- if (didMVCPAdjustScroll && (initialScroll || state.scrollingTo)) {
4354
+ if (didMVCPAdjustScroll) {
4483
4355
  updateScroll2(state.scroll);
4484
4356
  updateScrollRange();
4485
4357
  }
@@ -4659,22 +4531,20 @@ function calculateItemsInView(ctx, params = {}) {
4659
4531
  if (oldKey && oldKey !== id) {
4660
4532
  containerItemKeys.delete(oldKey);
4661
4533
  }
4534
+ if (oldKey !== id) {
4535
+ changedContainerIds != null ? changedContainerIds : changedContainerIds = /* @__PURE__ */ new Set();
4536
+ changedContainerIds.add(containerIndex);
4537
+ state.containerItemGenerations[containerIndex] = ((_p = state.containerItemGenerations[containerIndex]) != null ? _p : 0) + 1;
4538
+ }
4662
4539
  set$(ctx, `containerItemKey${containerIndex}`, id);
4540
+ set$(ctx, `containerItemIndex${containerIndex}`, i);
4663
4541
  set$(ctx, `containerItemData${containerIndex}`, data[i]);
4664
- set$(ctx, `containerItemInfo${containerIndex}`, {
4665
- index: i,
4666
- itemKey: id,
4667
- value: data[i]
4668
- });
4669
- if (allocation.itemType !== void 0) {
4670
- state.containerItemTypes.set(containerIndex, allocation.itemType);
4671
- }
4542
+ state.containerItemMetadata.set(
4543
+ containerIndex,
4544
+ createContainerItemMetadata(state, i, data[i], allocation.itemType)
4545
+ );
4672
4546
  containerItemKeys.set(id, containerIndex);
4673
- (_p = state.userScrollAnchorReset) == null ? void 0 : _p.keys.add(id);
4674
- {
4675
- (_q = state.pendingLayoutEffectMeasurements) != null ? _q : state.pendingLayoutEffectMeasurements = /* @__PURE__ */ new Set();
4676
- state.pendingLayoutEffectMeasurements.add(id);
4677
- }
4547
+ (_q = state.userScrollAnchorReset) == null ? void 0 : _q.keys.add(id);
4678
4548
  const containerSticky = `containerSticky${containerIndex}`;
4679
4549
  const isSticky = stickyHeaderIndicesSet.has(i);
4680
4550
  const isPinnedRender = isPinnedRenderIndex(i);
@@ -4724,15 +4594,18 @@ function calculateItemsInView(ctx, params = {}) {
4724
4594
  if (pendingRemovalSet == null ? void 0 : pendingRemovalSet.has(i)) {
4725
4595
  if (itemKey !== void 0) {
4726
4596
  containerItemKeys.delete(itemKey);
4597
+ changedContainerIds != null ? changedContainerIds : changedContainerIds = /* @__PURE__ */ new Set();
4598
+ changedContainerIds.add(i);
4599
+ state.containerItemGenerations[i] = ((_t = state.containerItemGenerations[i]) != null ? _t : 0) + 1;
4727
4600
  }
4728
- state.containerItemTypes.delete(i);
4601
+ state.containerItemMetadata.delete(i);
4729
4602
  if (state.stickyContainerPool.has(i)) {
4730
4603
  set$(ctx, `containerSticky${i}`, false);
4731
4604
  state.stickyContainerPool.delete(i);
4732
4605
  }
4733
4606
  set$(ctx, `containerItemKey${i}`, void 0);
4607
+ set$(ctx, `containerItemIndex${i}`, void 0);
4734
4608
  set$(ctx, `containerItemData${i}`, void 0);
4735
- set$(ctx, `containerItemInfo${i}`, void 0);
4736
4609
  set$(ctx, `containerPosition${i}`, POSITION_OUT_OF_VIEW);
4737
4610
  set$(ctx, `containerColumn${i}`, -1);
4738
4611
  set$(ctx, `containerSpan${i}`, 1);
@@ -4746,6 +4619,9 @@ function calculateItemsInView(ctx, params = {}) {
4746
4619
  }
4747
4620
  }
4748
4621
  }
4622
+ if (changedContainerIds && (IsNewArchitecture)) {
4623
+ scheduleContainerLayout(ctx, changedContainerIds);
4624
+ }
4749
4625
  if (didChangePositions) {
4750
4626
  set$(ctx, "lastPositionUpdate", Date.now());
4751
4627
  }
@@ -4762,20 +4638,18 @@ function calculateItemsInView(ctx, params = {}) {
4762
4638
  }
4763
4639
  }
4764
4640
  if (viewabilityConfigCallbackPairs && visibleRange.startNoBuffer !== null && visibleRange.endNoBuffer !== null) {
4765
- if (!didMVCPAdjustScroll) {
4766
- updateViewableItems(
4767
- ctx.state,
4768
- ctx,
4769
- viewabilityConfigCallbackPairs,
4770
- scrollLength,
4771
- visibleRange.startNoBuffer,
4772
- visibleRange.endNoBuffer,
4773
- startBuffered != null ? startBuffered : visibleRange.startNoBuffer,
4774
- endBuffered != null ? endBuffered : visibleRange.endNoBuffer
4775
- );
4776
- }
4641
+ updateViewableItems(
4642
+ ctx.state,
4643
+ ctx,
4644
+ viewabilityConfigCallbackPairs,
4645
+ scrollLength,
4646
+ visibleRange.startNoBuffer,
4647
+ visibleRange.endNoBuffer,
4648
+ startBuffered != null ? startBuffered : visibleRange.startNoBuffer,
4649
+ endBuffered != null ? endBuffered : visibleRange.endNoBuffer
4650
+ );
4777
4651
  }
4778
- (_t = stickyState == null ? void 0 : stickyState.finishCalculateItemsInView) == null ? void 0 : _t.call(stickyState);
4652
+ (_u = stickyState == null ? void 0 : stickyState.finishCalculateItemsInView) == null ? void 0 : _u.call(stickyState);
4779
4653
  });
4780
4654
  }
4781
4655
 
@@ -4797,8 +4671,6 @@ function maybeUpdateAnchoredEndSpace(ctx) {
4797
4671
  if (anchorIndex >= 0 && anchorIndex < data.length && state.scrollLength > 0) {
4798
4672
  nextAnchorKey = getId(state, anchorIndex);
4799
4673
  let contentBelowAnchor = 0;
4800
- const footerSize = ctx.values.get("footerSize") || 0;
4801
- const stylePaddingBottom = state.props.stylePaddingBottom || 0;
4802
4674
  let hasUnknownTailSize = false;
4803
4675
  for (let index = anchorIndex; index < data.length; index++) {
4804
4676
  const size = getKnownOrFixedItemSize(ctx, index);
@@ -4810,14 +4682,16 @@ function maybeUpdateAnchoredEndSpace(ctx) {
4810
4682
  contentBelowAnchor += effectiveSize;
4811
4683
  }
4812
4684
  }
4813
- contentBelowAnchor += footerSize + stylePaddingBottom;
4685
+ contentBelowAnchor = Math.max(0, contentBelowAnchor - ctx.scrollAxisGap);
4686
+ contentBelowAnchor += (ctx.values.get("footerSize") || 0) + getStylePaddingEnd(state.props);
4814
4687
  isReady = !hasUnknownTailSize;
4815
4688
  nextSize = hasUnknownTailSize ? previousSize || 0 : Math.max(0, state.scrollLength - contentBelowAnchor - anchorOffset);
4816
4689
  } else if (anchorIndex >= 0) {
4817
4690
  isReady = false;
4818
4691
  }
4819
4692
  }
4820
- const didSizeChange = previousSize !== nextSize;
4693
+ const didSizeChange = previousSize !== nextSize && (previousSize !== void 0 || anchoredEndSpace !== void 0);
4694
+ const didEffectiveSizeChange = (previousSize || 0) !== nextSize;
4821
4695
  const didReadyAnchorChange = previousReadyAnchorIndex !== nextAnchorIndex || previousReadyAnchorKey !== nextAnchorKey;
4822
4696
  if (isReady && (didSizeChange || didReadyAnchorChange)) {
4823
4697
  state.anchoredEndSpaceReadyAnchorIndex = nextAnchorIndex;
@@ -4826,8 +4700,9 @@ function maybeUpdateAnchoredEndSpace(ctx) {
4826
4700
  set$(ctx, "anchoredEndSpaceSize", nextSize);
4827
4701
  (_a3 = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onSizeChanged) == null ? void 0 : _a3.call(anchoredEndSpace, nextSize);
4828
4702
  }
4829
- if (didSizeChange && (anchoredEndSpace == null ? void 0 : anchoredEndSpace.includeInEndInset)) {
4830
- updateScroll(ctx, state.scroll, true);
4703
+ if (didEffectiveSizeChange) {
4704
+ updateContentMetricsState(ctx);
4705
+ updateScroll(ctx, state.scroll, true, { markHasScrolled: false });
4831
4706
  }
4832
4707
  (_b = anchoredEndSpace == null ? void 0 : anchoredEndSpace.onReady) == null ? void 0 : _b.call(anchoredEndSpace, { anchorIndex: nextAnchorIndex, anchorKey: nextAnchorKey, size: nextSize });
4833
4708
  }
@@ -4845,16 +4720,10 @@ function runOrScheduleMVCPRecalculate(ctx) {
4845
4720
  }
4846
4721
  } else {
4847
4722
  if (!state.mvcpAnchorLock) {
4848
- if (state.queuedMVCPRecalculate !== void 0) {
4849
- cancelAnimationFrame(state.queuedMVCPRecalculate);
4850
- state.queuedMVCPRecalculate = void 0;
4851
- }
4723
+ state.scheduledWork.cancel("mvcpRecalculate");
4852
4724
  calculateItemsInView(ctx, { doMVCP: true });
4853
- } else if (state.queuedMVCPRecalculate === void 0) {
4854
- state.queuedMVCPRecalculate = requestAnimationFrame(() => {
4855
- state.queuedMVCPRecalculate = void 0;
4856
- calculateItemsInView(ctx, { doMVCP: true });
4857
- });
4725
+ } else if (!state.scheduledWork.has("mvcpRecalculate")) {
4726
+ state.scheduledWork.frame(() => calculateItemsInView(ctx, { doMVCP: true }), "mvcpRecalculate");
4858
4727
  }
4859
4728
  }
4860
4729
  }
@@ -4868,42 +4737,28 @@ function updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal) {
4868
4737
  }
4869
4738
  }
4870
4739
  }
4740
+ var activeItemSizeBatches;
4741
+ function batchItemSizeUpdates(runUpdates) {
4742
+ const isOuterBatch = activeItemSizeBatches === void 0;
4743
+ activeItemSizeBatches != null ? activeItemSizeBatches : activeItemSizeBatches = /* @__PURE__ */ new Map();
4744
+ try {
4745
+ runUpdates();
4746
+ } finally {
4747
+ if (isOuterBatch) {
4748
+ const batches = activeItemSizeBatches;
4749
+ activeItemSizeBatches = void 0;
4750
+ for (const [ctx, measurements] of batches) {
4751
+ updateItemSizesBatch(ctx, measurements);
4752
+ }
4753
+ }
4754
+ }
4755
+ }
4871
4756
  function mergeItemSizeUpdateResult(result, next) {
4872
4757
  result.didChange || (result.didChange = next.didChange);
4873
4758
  result.didMeasureUserScrollAnchorResetItem || (result.didMeasureUserScrollAnchorResetItem = next.didMeasureUserScrollAnchorResetItem);
4874
4759
  result.needsRecalculate || (result.needsRecalculate = next.needsRecalculate);
4875
4760
  result.shouldMaintainScrollAtEnd || (result.shouldMaintainScrollAtEnd = next.shouldMaintainScrollAtEnd);
4876
4761
  }
4877
- var batchedItemSizeRecalculates = /* @__PURE__ */ new WeakMap();
4878
- function flushBatchedItemSizeRecalculate(ctx, didFallback = false, expectedResult) {
4879
- const result = batchedItemSizeRecalculates.get(ctx);
4880
- if (!result || expectedResult && result !== expectedResult) {
4881
- return;
4882
- }
4883
- batchedItemSizeRecalculates.delete(ctx);
4884
- if (didFallback) {
4885
- ctx.state.pendingLayoutEffectMeasurements = void 0;
4886
- }
4887
- flushItemSizeUpdates(ctx, result);
4888
- }
4889
- function queueItemSizeRecalculate(ctx, result) {
4890
- var _a3, _b;
4891
- const batch = batchedItemSizeRecalculates.get(ctx);
4892
- if (batch) {
4893
- mergeItemSizeUpdateResult(batch, result);
4894
- } else {
4895
- const nextBatch = { ...result };
4896
- batchedItemSizeRecalculates.set(ctx, nextBatch);
4897
- if ((_a3 = ctx.state.pendingLayoutEffectMeasurements) == null ? void 0 : _a3.size) {
4898
- requestAnimationFrame(() => {
4899
- flushBatchedItemSizeRecalculate(ctx, true, nextBatch);
4900
- });
4901
- }
4902
- }
4903
- if (!((_b = ctx.state.pendingLayoutEffectMeasurements) == null ? void 0 : _b.size)) {
4904
- flushBatchedItemSizeRecalculate(ctx);
4905
- }
4906
- }
4907
4762
  function flushItemSizeUpdates(ctx, result) {
4908
4763
  var _a3;
4909
4764
  const state = ctx.state;
@@ -4913,59 +4768,44 @@ function flushItemSizeUpdates(ctx, result) {
4913
4768
  } else if (result.didMeasureUserScrollAnchorResetItem && ((_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys.size) === 0) {
4914
4769
  state.userScrollAnchorReset = void 0;
4915
4770
  }
4771
+ if (result.didChange) {
4772
+ maybeUpdateAnchoredEndSpace(ctx);
4773
+ }
4916
4774
  if (result.didChange && result.shouldMaintainScrollAtEnd) {
4917
4775
  doMaintainScrollAtEnd(ctx);
4918
4776
  }
4919
4777
  }
4920
4778
  function updateItemSizes(ctx, measurement) {
4921
- var _a3, _b, _c, _d;
4922
- const state = ctx.state;
4923
- let didDrainLayoutEffectMeasurements = false;
4924
- if (measurement.fromLayoutEffect) {
4925
- const pendingLayoutEffectMeasurements = state.pendingLayoutEffectMeasurements;
4926
- if ((pendingLayoutEffectMeasurements == null ? void 0 : pendingLayoutEffectMeasurements.delete(measurement.itemKey)) && pendingLayoutEffectMeasurements.size === 0) {
4927
- state.pendingLayoutEffectMeasurements = void 0;
4928
- didDrainLayoutEffectMeasurements = true;
4929
- }
4930
- }
4931
- const pendingKeys = (_a3 = state.userScrollAnchorReset) == null ? void 0 : _a3.keys;
4932
- const shouldBatchPendingMeasurements = measurement.fromLayoutEffect && !!(pendingKeys == null ? void 0 : pendingKeys.size);
4933
- const shouldQueueRecalculate = !!measurement.fromLayoutEffect;
4934
- let result;
4935
- if (!shouldBatchPendingMeasurements) {
4936
- result = applyItemSize(ctx, measurement.itemKey, measurement.size);
4937
- } else {
4938
- result = {};
4939
- const updateContainerItemSize = (itemKey, containerId, size) => {
4940
- if (containerId !== void 0 && peek$(ctx, `containerItemKey${containerId}`) === itemKey) {
4941
- mergeItemSizeUpdateResult(result, applyItemSize(ctx, itemKey, size));
4942
- }
4943
- };
4944
- updateContainerItemSize(measurement.itemKey, measurement.containerId, measurement.size);
4945
- const keys = Array.from(pendingKeys);
4946
- for (const itemKey of keys) {
4947
- const containerId = state.containerItemKeys.get(itemKey);
4948
- if (containerId !== void 0) {
4949
- (_d = (_c = (_b = ctx.viewRefs.get(containerId)) == null ? void 0 : _b.current) == null ? void 0 : _c.measure) == null ? void 0 : _d.call(_c, (_x, _y, width, height) => {
4950
- if (pendingKeys.has(itemKey)) {
4951
- updateContainerItemSize(itemKey, containerId, { height, width });
4952
- }
4953
- });
4954
- }
4779
+ if (activeItemSizeBatches) {
4780
+ const measurements = activeItemSizeBatches.get(ctx);
4781
+ if (measurements) {
4782
+ measurements.push(measurement);
4783
+ } else {
4784
+ activeItemSizeBatches.set(ctx, [measurement]);
4955
4785
  }
4956
- }
4957
- if (shouldQueueRecalculate && result.needsRecalculate) {
4958
- queueItemSizeRecalculate(ctx, result);
4959
4786
  } else {
4960
- flushItemSizeUpdates(ctx, result);
4961
- }
4962
- if (didDrainLayoutEffectMeasurements) {
4963
- flushBatchedItemSizeRecalculate(ctx);
4787
+ updateItemSizesBatch(ctx, [measurement]);
4964
4788
  }
4965
4789
  }
4966
- function applyItemSize(ctx, itemKey, sizeObj) {
4790
+ function updateItemSizesBatch(ctx, measurements) {
4967
4791
  var _a3;
4968
4792
  const state = ctx.state;
4793
+ const result = {};
4794
+ for (const measurement of measurements) {
4795
+ const ownsMeasuredItem = measurement.containerId === void 0 || peek$(ctx, `containerItemKey${measurement.containerId}`) === measurement.itemKey;
4796
+ if (ownsMeasuredItem) {
4797
+ const index = state.indexByKey.get(measurement.itemKey);
4798
+ const itemData = index === void 0 ? void 0 : (_a3 = state.props.data) == null ? void 0 : _a3[index];
4799
+ const metadata = measurement.containerId !== void 0 && index !== void 0 && itemData !== void 0 ? resolveContainerItemMetadata(state, measurement.containerId, index, itemData) : void 0;
4800
+ const nextResult = applyItemSize(ctx, measurement.itemKey, measurement.size, metadata);
4801
+ mergeItemSizeUpdateResult(result, nextResult);
4802
+ }
4803
+ }
4804
+ flushItemSizeUpdates(ctx, result);
4805
+ }
4806
+ function applyItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
4807
+ var _a3, _b;
4808
+ const state = ctx.state;
4969
4809
  const userScrollAnchorReset = state.userScrollAnchorReset;
4970
4810
  const didMeasureUserScrollAnchorResetItem = !!(userScrollAnchorReset == null ? void 0 : userScrollAnchorReset.keys.delete(itemKey));
4971
4811
  const {
@@ -4975,7 +4815,6 @@ function applyItemSize(ctx, itemKey, sizeObj) {
4975
4815
  } = state;
4976
4816
  if (!data) return { didMeasureUserScrollAnchorResetItem };
4977
4817
  const index = state.indexByKey.get(itemKey);
4978
- let resolvedMeasurementItem;
4979
4818
  if (getFixedItemSize) {
4980
4819
  if (index === void 0) {
4981
4820
  return { didMeasureUserScrollAnchorResetItem };
@@ -4984,14 +4823,15 @@ function applyItemSize(ctx, itemKey, sizeObj) {
4984
4823
  if (itemData === void 0) {
4985
4824
  return { didMeasureUserScrollAnchorResetItem };
4986
4825
  }
4987
- const type = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
4988
- const size2 = getFixedItemSize(itemData, index, type);
4989
- resolvedMeasurementItem = {
4990
- didResolveFixedItemSize: true,
4991
- fixedItemSize: size2,
4992
- itemData,
4993
- itemType: type
4994
- };
4826
+ if (!(resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize)) {
4827
+ const type = (_b = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemType) != null ? _b : getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
4828
+ resolvedMeasurementItem = {
4829
+ didResolveFixedItemSize: true,
4830
+ fixedItemSize: getFixedItemSize(itemData, index, type),
4831
+ itemType: type
4832
+ };
4833
+ }
4834
+ const size2 = resolvedMeasurementItem.fixedItemSize;
4995
4835
  if (size2 !== void 0 && size2 === sizesKnown.get(itemKey)) {
4996
4836
  updateOtherAxisSizeIfNeeded(ctx, sizeObj, horizontal);
4997
4837
  return { didMeasureUserScrollAnchorResetItem };
@@ -5020,7 +4860,6 @@ function applyItemSize(ctx, itemKey, sizeObj) {
5020
4860
  previous: size - diff,
5021
4861
  size
5022
4862
  });
5023
- maybeUpdateAnchoredEndSpace(ctx);
5024
4863
  }
5025
4864
  if (minIndexSizeChanged !== void 0) {
5026
4865
  state.minIndexSizeChanged = state.minIndexSizeChanged !== void 0 ? Math.min(state.minIndexSizeChanged, minIndexSizeChanged) : minIndexSizeChanged;
@@ -5041,7 +4880,7 @@ function applyItemSize(ctx, itemKey, sizeObj) {
5041
4880
  };
5042
4881
  }
5043
4882
  function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
5044
- var _a3, _b, _c;
4883
+ var _a3, _b;
5045
4884
  const state = ctx.state;
5046
4885
  const {
5047
4886
  indexByKey,
@@ -5051,11 +4890,11 @@ function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
5051
4890
  } = state;
5052
4891
  if (!data) return 0;
5053
4892
  const index = indexByKey.get(itemKey);
5054
- const itemData = (_a3 = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemData) != null ? _a3 : data[index];
4893
+ const itemData = data[index];
5055
4894
  let itemType = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.itemType;
5056
4895
  let fixedItemSize = resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.fixedItemSize;
5057
4896
  if (getFixedItemSize && !(resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize)) {
5058
- itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
4897
+ itemType = getItemType ? (_a3 = getItemType(itemData, index)) != null ? _a3 : "" : "";
5059
4898
  fixedItemSize = getFixedItemSize(itemData, index, itemType);
5060
4899
  }
5061
4900
  const resolvedItemSize = (resolvedMeasurementItem == null ? void 0 : resolvedMeasurementItem.didResolveFixedItemSize) || itemType !== void 0 || fixedItemSize !== void 0 ? {
@@ -5069,26 +4908,346 @@ function updateOneItemSize(ctx, itemKey, sizeObj, resolvedMeasurementItem) {
5069
4908
  const size = Math.round(rawSize) ;
5070
4909
  sizesKnown.set(itemKey, size);
5071
4910
  if (fixedItemSize === void 0 && size > 0) {
5072
- itemType != null ? itemType : itemType = getItemType ? (_c = getItemType(itemData, index)) != null ? _c : "" : "";
4911
+ itemType != null ? itemType : itemType = getItemType ? (_b = getItemType(itemData, index)) != null ? _b : "" : "";
5073
4912
  let averages = averageSizes[itemType];
5074
4913
  if (!averages) {
5075
4914
  averages = averageSizes[itemType] = { avg: 0, num: 0 };
5076
4915
  }
5077
- if (averages.num === 0) {
5078
- averages.avg = size;
5079
- averages.num++;
5080
- } else if (prevSizeKnown !== void 0 && prevSizeKnown > 0) {
5081
- averages.avg += (size - prevSizeKnown) / averages.num;
5082
- } else {
5083
- averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
5084
- averages.num++;
4916
+ if (averages.num === 0) {
4917
+ averages.avg = size;
4918
+ averages.num++;
4919
+ } else if (prevSizeKnown !== void 0 && prevSizeKnown > 0) {
4920
+ averages.avg += (size - prevSizeKnown) / averages.num;
4921
+ } else {
4922
+ averages.avg = (averages.avg * averages.num + size) / (averages.num + 1);
4923
+ averages.num++;
4924
+ }
4925
+ }
4926
+ if (!prevSize || Math.abs(prevSize - size) > 0.1) {
4927
+ setSize(ctx, itemKey, size);
4928
+ return size - prevSize;
4929
+ }
4930
+ return 0;
4931
+ }
4932
+
4933
+ // src/core/measureContainersInLayoutEffect.ts
4934
+ function measureContainersInLayoutEffect(ctx, targetContainerIds = null) {
4935
+ const measurements = [];
4936
+ const containerIds = targetContainerIds != null ? targetContainerIds : ctx.viewRefs.keys();
4937
+ for (const containerId of containerIds) {
4938
+ const viewRef = ctx.viewRefs.get(containerId);
4939
+ const itemKey = peek$(ctx, `containerItemKey${containerId}`);
4940
+ const element = viewRef == null ? void 0 : viewRef.current;
4941
+ if (itemKey !== void 0 && element) {
4942
+ const rect = element.getBoundingClientRect();
4943
+ setContainerLayoutBaseline(element, rect);
4944
+ measurements.push({
4945
+ containerId,
4946
+ itemKey,
4947
+ size: { height: rect.height, width: rect.width }
4948
+ });
4949
+ }
4950
+ }
4951
+ if (measurements.length > 0) {
4952
+ updateItemSizesBatch(ctx, measurements);
4953
+ }
4954
+ }
4955
+ var typedForwardRef = React3__namespace.forwardRef;
4956
+ var typedMemo = React3__namespace.memo;
4957
+
4958
+ // src/components/ContainerLayoutCoordinator.tsx
4959
+ var ContainerLayoutCoordinator = typedMemo(function ContainerLayoutCoordinatorComponent({
4960
+ children
4961
+ }) {
4962
+ const ctx = useStateContext();
4963
+ const [containerLayoutEpoch] = useArr$(["containerLayoutEpoch"]);
4964
+ React3__namespace.useLayoutEffect(() => {
4965
+ {
4966
+ const targetContainerIds = getContainerLayoutEffectScope(ctx);
4967
+ if (targetContainerIds !== void 0) {
4968
+ measureContainersInLayoutEffect(ctx, targetContainerIds);
4969
+ }
4970
+ }
4971
+ }, [ctx, containerLayoutEpoch]);
4972
+ return children;
4973
+ });
4974
+ var getComponent = (Component) => {
4975
+ if (React3__namespace.isValidElement(Component)) {
4976
+ return Component;
4977
+ }
4978
+ if (Component) {
4979
+ return /* @__PURE__ */ React3__namespace.createElement(Component, null);
4980
+ }
4981
+ return null;
4982
+ };
4983
+
4984
+ // src/components/PositionView.tsx
4985
+ var isRNWeb = typeof document !== "undefined" && !!document.getElementById("react-native-stylesheet");
4986
+ var baseCss = {
4987
+ contain: "paint layout style",
4988
+ ...isRNWeb ? {
4989
+ display: "flex",
4990
+ flexDirection: "column"
4991
+ } : {}
4992
+ };
4993
+ var PositionViewState = typedMemo(function PositionViewState2({
4994
+ id,
4995
+ horizontal,
4996
+ style,
4997
+ refView,
4998
+ ...props
4999
+ }) {
5000
+ const [position = POSITION_OUT_OF_VIEW] = useArr$([`containerPosition${id}`]);
5001
+ const composed = isArray(style) ? Object.assign({}, ...style) : style;
5002
+ const combinedStyle = horizontal ? { ...baseCss, ...composed, left: position } : { ...baseCss, ...composed, top: position };
5003
+ const {
5004
+ animatedScrollY: _animatedScrollY,
5005
+ onLayout: _onLayout,
5006
+ onLayoutChange: _onLayoutChange,
5007
+ stickyHeaderConfig: _stickyHeaderConfig,
5008
+ ...webProps
5009
+ } = props;
5010
+ return /* @__PURE__ */ React3__namespace.createElement("div", { ref: refView, ...webProps, style: combinedStyle });
5011
+ });
5012
+ var PositionViewSticky = typedMemo(function PositionViewSticky2({
5013
+ id,
5014
+ horizontal,
5015
+ style,
5016
+ refView,
5017
+ animatedScrollY: _animatedScrollY,
5018
+ stickyHeaderConfig,
5019
+ onLayout: _onLayout,
5020
+ onLayoutChange: _onLayoutChange,
5021
+ children,
5022
+ ...webProps
5023
+ }) {
5024
+ const [position = POSITION_OUT_OF_VIEW, activeStickyIndex, itemIndex] = useArr$([
5025
+ `containerPosition${id}`,
5026
+ "activeStickyIndex",
5027
+ `containerItemIndex${id}`
5028
+ ]);
5029
+ const composed = React3__namespace.useMemo(
5030
+ () => {
5031
+ var _a3;
5032
+ return (_a3 = isArray(style) ? Object.assign({}, ...style) : style) != null ? _a3 : {};
5033
+ },
5034
+ [style]
5035
+ );
5036
+ const viewStyle = React3__namespace.useMemo(() => {
5037
+ var _a3;
5038
+ const styleBase = { ...baseCss, ...composed };
5039
+ delete styleBase.transform;
5040
+ const offset = (_a3 = stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset) != null ? _a3 : 0;
5041
+ const isActive = activeStickyIndex === itemIndex;
5042
+ styleBase.position = isActive ? "sticky" : "absolute";
5043
+ styleBase.zIndex = itemIndex + 1e3;
5044
+ if (horizontal) {
5045
+ styleBase.left = isActive ? offset : position;
5046
+ } else {
5047
+ styleBase.top = isActive ? offset : position;
5048
+ }
5049
+ return styleBase;
5050
+ }, [composed, horizontal, position, itemIndex, activeStickyIndex, stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.offset]);
5051
+ const renderStickyHeaderBackdrop = React3__namespace.useMemo(
5052
+ () => (stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent) ? /* @__PURE__ */ React3__namespace.createElement(
5053
+ "div",
5054
+ {
5055
+ style: {
5056
+ inset: 0,
5057
+ pointerEvents: "none",
5058
+ position: "absolute"
5059
+ }
5060
+ },
5061
+ getComponent(stickyHeaderConfig.backdropComponent)
5062
+ ) : null,
5063
+ [stickyHeaderConfig == null ? void 0 : stickyHeaderConfig.backdropComponent]
5064
+ );
5065
+ return /* @__PURE__ */ React3__namespace.createElement("div", { ref: refView, style: viewStyle, ...webProps }, renderStickyHeaderBackdrop, children);
5066
+ });
5067
+ var PositionView = PositionViewState;
5068
+ function useInit(cb) {
5069
+ React3.useState(() => cb());
5070
+ }
5071
+
5072
+ // src/state/ContextContainer.ts
5073
+ var ContextContainer = React3.createContext(null);
5074
+ var NO_CONTAINER_ID = -1;
5075
+ function useContextContainer() {
5076
+ return React3.useContext(ContextContainer);
5077
+ }
5078
+ function useContainerItemSignals(containerContext) {
5079
+ var _a3;
5080
+ const containerId = (_a3 = containerContext == null ? void 0 : containerContext.containerId) != null ? _a3 : NO_CONTAINER_ID;
5081
+ const [itemKey, itemIndex, item] = useArr$([
5082
+ `containerItemKey${containerId}`,
5083
+ `containerItemIndex${containerId}`,
5084
+ `containerItemData${containerId}`
5085
+ ]);
5086
+ return {
5087
+ hasItemInfo: !!containerContext && itemKey !== void 0 && itemIndex !== void 0,
5088
+ item,
5089
+ itemIndex,
5090
+ itemKey
5091
+ };
5092
+ }
5093
+ function useAdaptiveRender() {
5094
+ const [mode] = useArr$(["adaptiveRender"]);
5095
+ return mode;
5096
+ }
5097
+ function useAdaptiveRenderChange(callback) {
5098
+ const ctx = useStateContext();
5099
+ const callbackRef = React3.useRef(callback);
5100
+ callbackRef.current = callback;
5101
+ React3.useLayoutEffect(() => {
5102
+ let mode = peek$(ctx, "adaptiveRender");
5103
+ return listen$(ctx, "adaptiveRender", (nextMode) => {
5104
+ if (mode !== nextMode) {
5105
+ mode = nextMode;
5106
+ callbackRef.current(nextMode);
5107
+ }
5108
+ });
5109
+ }, [ctx]);
5110
+ }
5111
+ function useViewability(callback, configId) {
5112
+ const ctx = useStateContext();
5113
+ const containerContext = useContextContainer();
5114
+ useInit(() => {
5115
+ if (!containerContext) {
5116
+ return;
5117
+ }
5118
+ const { containerId } = containerContext;
5119
+ const key = containerId + (configId != null ? configId : "");
5120
+ const value = ctx.mapViewabilityValues.get(key);
5121
+ if (value) {
5122
+ callback(value);
5123
+ }
5124
+ });
5125
+ React3.useEffect(() => {
5126
+ if (!containerContext) {
5127
+ return;
5128
+ }
5129
+ const { containerId } = containerContext;
5130
+ const key = containerId + (configId != null ? configId : "");
5131
+ ctx.mapViewabilityCallbacks.set(key, callback);
5132
+ return () => {
5133
+ ctx.mapViewabilityCallbacks.delete(key);
5134
+ };
5135
+ }, [ctx, callback, configId, containerContext]);
5136
+ }
5137
+ function useViewabilityAmount(callback) {
5138
+ const ctx = useStateContext();
5139
+ const containerContext = useContextContainer();
5140
+ useInit(() => {
5141
+ if (!containerContext) {
5142
+ return;
5143
+ }
5144
+ const { containerId } = containerContext;
5145
+ const value = ctx.mapViewabilityAmountValues.get(containerId);
5146
+ if (value) {
5147
+ callback(value);
5148
+ }
5149
+ });
5150
+ React3.useEffect(() => {
5151
+ if (!containerContext) {
5152
+ return;
5153
+ }
5154
+ const { containerId } = containerContext;
5155
+ ctx.mapViewabilityAmountCallbacks.set(containerId, callback);
5156
+ return () => {
5157
+ ctx.mapViewabilityAmountCallbacks.delete(containerId);
5158
+ };
5159
+ }, [ctx, callback, containerContext]);
5160
+ }
5161
+ function useRecyclingEffect(effect) {
5162
+ const containerContext = useContextContainer();
5163
+ const { hasItemInfo, item, itemIndex, itemKey } = useContainerItemSignals(containerContext);
5164
+ const prevInfo = React3.useRef(void 0);
5165
+ React3.useEffect(() => {
5166
+ if (!hasItemInfo) {
5167
+ return;
5168
+ }
5169
+ let ret;
5170
+ if (prevInfo.current) {
5171
+ ret = effect({
5172
+ index: itemIndex,
5173
+ item,
5174
+ prevIndex: prevInfo.current.index,
5175
+ prevItem: prevInfo.current.item
5176
+ });
5177
+ }
5178
+ prevInfo.current = {
5179
+ index: itemIndex,
5180
+ item
5181
+ };
5182
+ return ret;
5183
+ }, [effect, hasItemInfo, itemIndex, item, itemKey]);
5184
+ }
5185
+ function useRecyclingState(valueOrFun) {
5186
+ const containerContext = useContextContainer();
5187
+ const { hasItemInfo, item, itemIndex, itemKey } = useContainerItemSignals(containerContext);
5188
+ const computeValue = () => {
5189
+ if (isFunction(valueOrFun)) {
5190
+ const initializer = valueOrFun;
5191
+ return hasItemInfo ? initializer({
5192
+ index: itemIndex,
5193
+ item,
5194
+ prevIndex: void 0,
5195
+ prevItem: void 0
5196
+ }) : initializer();
5085
5197
  }
5198
+ return valueOrFun;
5199
+ };
5200
+ const [stateValue, setStateValue] = React3.useState(() => {
5201
+ return computeValue();
5202
+ });
5203
+ const prevItemKeyRef = React3.useRef(hasItemInfo ? itemKey : null);
5204
+ if (hasItemInfo && prevItemKeyRef.current !== itemKey) {
5205
+ prevItemKeyRef.current = itemKey;
5206
+ setStateValue(computeValue());
5086
5207
  }
5087
- if (!prevSize || Math.abs(prevSize - size) > 0.1) {
5088
- setSize(ctx, itemKey, size);
5089
- return size - prevSize;
5090
- }
5091
- return 0;
5208
+ const triggerLayout = containerContext == null ? void 0 : containerContext.triggerLayout;
5209
+ const setState = React3.useCallback(
5210
+ (newState) => {
5211
+ if (!triggerLayout) {
5212
+ return;
5213
+ }
5214
+ setStateValue((prevValue) => {
5215
+ return isFunction(newState) ? newState(prevValue) : newState;
5216
+ });
5217
+ triggerLayout();
5218
+ },
5219
+ [triggerLayout]
5220
+ );
5221
+ return [stateValue, setState];
5222
+ }
5223
+ function useIsLastItem() {
5224
+ var _a3;
5225
+ const containerContext = useContextContainer();
5226
+ const containerId = (_a3 = containerContext == null ? void 0 : containerContext.containerId) != null ? _a3 : NO_CONTAINER_ID;
5227
+ const [itemKey] = useArr$([`containerItemKey${containerId}`]);
5228
+ const isLast = useSelector$("lastItemKeys", (lastItemKeys) => {
5229
+ if (containerContext && !isNullOrUndefined(itemKey)) {
5230
+ return (lastItemKeys == null ? void 0 : lastItemKeys.includes(itemKey)) || false;
5231
+ }
5232
+ return false;
5233
+ });
5234
+ return isLast;
5235
+ }
5236
+ function useListScrollSize() {
5237
+ const [scrollSize] = useArr$(["scrollSize"]);
5238
+ return scrollSize;
5239
+ }
5240
+ var noop = () => {
5241
+ };
5242
+ function useSyncLayout() {
5243
+ const containerContext = useContextContainer();
5244
+ return containerContext ? containerContext.triggerLayout : noop;
5245
+ }
5246
+
5247
+ // src/components/Separator.tsx
5248
+ function Separator({ ItemSeparatorComponent, leadingItem }) {
5249
+ const isLastItem = useIsLastItem();
5250
+ return isLastItem ? null : /* @__PURE__ */ React3__namespace.createElement(ItemSeparatorComponent, { leadingItem });
5092
5251
  }
5093
5252
 
5094
5253
  // src/hooks/createResizeObserver.ts
@@ -5096,43 +5255,39 @@ var globalResizeObserver = null;
5096
5255
  function getGlobalResizeObserver() {
5097
5256
  if (!globalResizeObserver) {
5098
5257
  globalResizeObserver = new ResizeObserver((entries) => {
5099
- for (const entry of entries) {
5100
- const callbacks = callbackMap.get(entry.target);
5101
- if (callbacks) {
5102
- for (const callback of callbacks) {
5103
- callback(entry);
5258
+ batchItemSizeUpdates(() => {
5259
+ for (const entry of entries) {
5260
+ const callbacks = callbackMap.get(entry.target);
5261
+ if (callbacks) {
5262
+ for (const callback of callbacks) {
5263
+ callback(entry);
5264
+ }
5104
5265
  }
5105
5266
  }
5106
- }
5267
+ });
5107
5268
  });
5108
5269
  }
5109
5270
  return globalResizeObserver;
5110
5271
  }
5111
5272
  var callbackMap = /* @__PURE__ */ new WeakMap();
5112
5273
  function createResizeObserver(element, callback) {
5113
- if (typeof ResizeObserver === "undefined") {
5114
- return () => {
5115
- };
5116
- }
5117
- if (!element) {
5274
+ var _a3;
5275
+ if (typeof ResizeObserver === "undefined" || !element) {
5118
5276
  return () => {
5119
5277
  };
5120
5278
  }
5121
5279
  const observer = getGlobalResizeObserver();
5122
- let callbacks = callbackMap.get(element);
5123
- if (!callbacks) {
5124
- callbacks = /* @__PURE__ */ new Set();
5280
+ const callbacks = (_a3 = callbackMap.get(element)) != null ? _a3 : /* @__PURE__ */ new Set();
5281
+ if (callbacks.size === 0) {
5125
5282
  callbackMap.set(element, callbacks);
5126
- observer.observe(element);
5283
+ observer.observe(element, { box: "border-box" });
5127
5284
  }
5128
5285
  callbacks.add(callback);
5129
5286
  return () => {
5130
- if (callbacks) {
5131
- callbacks.delete(callback);
5132
- if (callbacks.size === 0) {
5133
- callbackMap.delete(element);
5134
- observer.unobserve(element);
5135
- }
5287
+ callbacks.delete(callback);
5288
+ if (callbacks.size === 0) {
5289
+ callbackMap.delete(element);
5290
+ observer.unobserve(element);
5136
5291
  }
5137
5292
  };
5138
5293
  }
@@ -5140,6 +5295,7 @@ function createResizeObserver(element, callback) {
5140
5295
  // src/hooks/useOnLayoutSync.tsx
5141
5296
  function useOnLayoutSync({
5142
5297
  ref,
5298
+ measureInLayoutEffect = true,
5143
5299
  onLayoutProp,
5144
5300
  onLayoutChange,
5145
5301
  webLayoutResync
@@ -5159,21 +5315,31 @@ function useOnLayoutSync({
5159
5315
  onLayoutChange(layout, fromLayoutEffect);
5160
5316
  onLayoutProp == null ? void 0 : onLayoutProp({ nativeEvent: { layout } });
5161
5317
  };
5162
- const rect = element.getBoundingClientRect();
5163
- emit(toLayout(rect), true);
5164
- let prevRect = rect;
5318
+ let prevRect;
5319
+ if (measureInLayoutEffect) {
5320
+ const rect = element.getBoundingClientRect();
5321
+ emit(toLayout(rect), true);
5322
+ prevRect = rect;
5323
+ }
5165
5324
  return createResizeObserver(element, (entry) => {
5166
5325
  var _a4;
5167
5326
  const target = entry.target instanceof HTMLElement ? entry.target : void 0;
5168
- const rectObserved = (_a4 = entry.contentRect) != null ? _a4 : target == null ? void 0 : target.getBoundingClientRect();
5169
- const didSizeChange = rectObserved.width !== prevRect.width || rectObserved.height !== prevRect.height;
5327
+ const borderBoxSize = Array.isArray(entry.borderBoxSize) ? entry.borderBoxSize[0] : entry.borderBoxSize;
5328
+ const rectObserved = borderBoxSize ? {
5329
+ height: borderBoxSize.blockSize,
5330
+ left: entry.contentRect.left,
5331
+ top: entry.contentRect.top,
5332
+ width: borderBoxSize.inlineSize
5333
+ } : (_a4 = target == null ? void 0 : target.getBoundingClientRect()) != null ? _a4 : entry.contentRect;
5334
+ const previousRect = prevRect != null ? prevRect : getContainerLayoutBaseline(element);
5335
+ const didSizeChange = previousRect === void 0 || rectObserved.width !== previousRect.width || rectObserved.height !== previousRect.height;
5336
+ prevRect = rectObserved;
5170
5337
  const shouldResyncLayout = !!(webLayoutResync == null ? void 0 : webLayoutResync());
5171
5338
  if (didSizeChange || shouldResyncLayout) {
5172
- prevRect = rectObserved;
5173
5339
  emit(toLayout(rectObserved), false);
5174
5340
  }
5175
5341
  });
5176
- }, deps || []);
5342
+ }, [measureInLayoutEffect, ...deps || []]);
5177
5343
  return {};
5178
5344
  }
5179
5345
  function toLayout(rect) {
@@ -5188,6 +5354,121 @@ function toLayout(rect) {
5188
5354
  };
5189
5355
  }
5190
5356
 
5357
+ // src/hooks/useContainerMeasurement.tsx
5358
+ var pendingWebShrinkMeasurements = /* @__PURE__ */ new Map();
5359
+ var pendingWebShrinkFrame;
5360
+ function cancelWebShrinkMeasurement(state) {
5361
+ pendingWebShrinkMeasurements.delete(state);
5362
+ }
5363
+ function scheduleWebShrinkMeasurement(state, confirmMeasurement) {
5364
+ pendingWebShrinkMeasurements.set(state, confirmMeasurement);
5365
+ if (pendingWebShrinkFrame === void 0) {
5366
+ pendingWebShrinkFrame = requestAnimationFrame(() => {
5367
+ const callbacks = Array.from(pendingWebShrinkMeasurements.values());
5368
+ pendingWebShrinkMeasurements.clear();
5369
+ pendingWebShrinkFrame = void 0;
5370
+ batchItemSizeUpdates(() => {
5371
+ for (const callback of callbacks) {
5372
+ callback();
5373
+ }
5374
+ });
5375
+ });
5376
+ }
5377
+ }
5378
+ function processContainerLayout({ containerId, ctx, rectangle, ref, state }) {
5379
+ const listState = ctx.state;
5380
+ const currentItemKey = state.itemKey;
5381
+ state.didLayout = true;
5382
+ let layout = rectangle;
5383
+ const axis = state.horizontal ? "width" : "height";
5384
+ const size = roundSize(rectangle[axis]);
5385
+ state.lastSize ? roundSize(state.lastSize[axis]) : void 0;
5386
+ const coreKnownSize = listState.sizesKnown.get(currentItemKey);
5387
+ const previousSize = coreKnownSize ;
5388
+ const applyLayout = () => {
5389
+ state.lastSize = layout;
5390
+ updateItemSizes(ctx, {
5391
+ containerId,
5392
+ itemKey: currentItemKey,
5393
+ size: layout
5394
+ });
5395
+ };
5396
+ const shouldDeferWebShrinkLayoutUpdate = !isInMVCPActiveMode(listState) && previousSize !== void 0 && size + 1 < previousSize;
5397
+ if (shouldDeferWebShrinkLayoutUpdate) {
5398
+ scheduleWebShrinkMeasurement(state, () => {
5399
+ var _a4;
5400
+ if (state.itemKey === currentItemKey) {
5401
+ const element = ref.current;
5402
+ const rect = (_a4 = element == null ? void 0 : element.getBoundingClientRect) == null ? void 0 : _a4.call(element);
5403
+ if (rect) {
5404
+ layout = { height: rect.height, width: rect.width };
5405
+ }
5406
+ applyLayout();
5407
+ }
5408
+ });
5409
+ } else {
5410
+ {
5411
+ cancelWebShrinkMeasurement(state);
5412
+ }
5413
+ {
5414
+ applyLayout();
5415
+ }
5416
+ }
5417
+ }
5418
+ function useContainerMeasurement({
5419
+ containerId,
5420
+ ctx,
5421
+ horizontal,
5422
+ itemKey,
5423
+ ref
5424
+ }) {
5425
+ const stateRef = React3.useRef({
5426
+ didLayout: false,
5427
+ horizontal,
5428
+ itemKey
5429
+ });
5430
+ stateRef.current.horizontal = horizontal;
5431
+ stateRef.current.itemKey = itemKey;
5432
+ const [layoutRenderCount, forceLayoutRender] = React3.useState(0);
5433
+ const onLayoutChange = React3.useCallback(
5434
+ (rectangle) => {
5435
+ processContainerLayout({ containerId, ctx, rectangle, ref, state: stateRef.current });
5436
+ },
5437
+ [containerId, ctx, ref]
5438
+ );
5439
+ const triggerLayout = React3.useCallback(() => {
5440
+ {
5441
+ scheduleContainerLayout(ctx, containerId);
5442
+ }
5443
+ }, [containerId, ctx]);
5444
+ React3.useLayoutEffect(() => {
5445
+ ctx.containerLayoutTriggers.set(containerId, triggerLayout);
5446
+ return () => {
5447
+ cancelWebShrinkMeasurement(stateRef.current);
5448
+ if (ctx.containerLayoutTriggers.get(containerId) === triggerLayout) {
5449
+ ctx.containerLayoutTriggers.delete(containerId);
5450
+ }
5451
+ };
5452
+ }, [containerId, ctx, triggerLayout]);
5453
+ React3.useLayoutEffect(() => {
5454
+ {
5455
+ scheduleContainerLayout(ctx, containerId);
5456
+ }
5457
+ });
5458
+ const { onLayout } = useOnLayoutSync(
5459
+ {
5460
+ measureInLayoutEffect: !IsNewArchitecture,
5461
+ onLayoutChange,
5462
+ ref,
5463
+ webLayoutResync: () => isInMVCPActiveMode(ctx.state)
5464
+ },
5465
+ [itemKey, layoutRenderCount]
5466
+ );
5467
+ React3.useEffect(() => {
5468
+ }, [containerId, ctx, itemKey]);
5469
+ return { onLayout, triggerLayout };
5470
+ }
5471
+
5191
5472
  // src/components/Container.tsx
5192
5473
  function getContainerPositionStyle({
5193
5474
  columnWrapperStyle,
@@ -5258,16 +5539,14 @@ var Container = typedMemo(function Container2({
5258
5539
  "extraData",
5259
5540
  `containerSticky${id}`
5260
5541
  ]);
5261
- const itemLayoutRef = React3.useRef({
5262
- didLayout: false,
5542
+ const ref = React3.useRef(null);
5543
+ const { onLayout, triggerLayout } = useContainerMeasurement({
5544
+ containerId: id,
5545
+ ctx,
5263
5546
  horizontal,
5264
5547
  itemKey,
5265
- pendingShrinkToken: 0
5548
+ ref
5266
5549
  });
5267
- itemLayoutRef.current.horizontal = horizontal;
5268
- itemLayoutRef.current.itemKey = itemKey;
5269
- const ref = React3.useRef(null);
5270
- const [layoutRenderCount, forceLayoutRender] = React3.useState(0);
5271
5550
  const resolvedColumn = column > 0 ? column : 1;
5272
5551
  const resolvedSpan = Math.min(Math.max(span || 1, 1), numColumns);
5273
5552
  const otherAxisPos = numColumns > 1 ? `${(resolvedColumn - 1) / numColumns * 100}%` : 0;
@@ -5298,57 +5577,7 @@ var Container = typedMemo(function Container2({
5298
5577
  () => itemKey !== void 0 ? getRenderedItem2(itemKey) : null,
5299
5578
  [itemKey, data, extraData]
5300
5579
  );
5301
- const { index, renderedItem } = renderedItemInfo || {};
5302
- const onLayoutChange = React3.useCallback((rectangle, fromLayoutEffect) => {
5303
- const {
5304
- horizontal: currentHorizontal,
5305
- itemKey: currentItemKey,
5306
- lastSize,
5307
- pendingShrinkToken
5308
- } = itemLayoutRef.current;
5309
- if (isNullOrUndefined(currentItemKey)) {
5310
- return;
5311
- }
5312
- itemLayoutRef.current.didLayout = true;
5313
- let layout = rectangle;
5314
- const axis = currentHorizontal ? "width" : "height";
5315
- const size = roundSize(rectangle[axis]);
5316
- const prevSize = lastSize ? roundSize(lastSize[axis]) : void 0;
5317
- const doUpdate = () => {
5318
- itemLayoutRef.current.lastSize = layout;
5319
- updateItemSizes(ctx, {
5320
- containerId: id,
5321
- fromLayoutEffect,
5322
- itemKey: currentItemKey,
5323
- size: layout
5324
- });
5325
- itemLayoutRef.current.didLayout = true;
5326
- };
5327
- const shouldDeferWebShrinkLayoutUpdate = !isInMVCPActiveMode(ctx.state) && prevSize !== void 0 && size + 1 < prevSize;
5328
- if (shouldDeferWebShrinkLayoutUpdate) {
5329
- const token = pendingShrinkToken + 1;
5330
- itemLayoutRef.current.pendingShrinkToken = token;
5331
- requestAnimationFrame(() => {
5332
- var _a4;
5333
- if (itemLayoutRef.current.pendingShrinkToken !== token) {
5334
- return;
5335
- }
5336
- const element = ref.current;
5337
- const rect = (_a4 = element == null ? void 0 : element.getBoundingClientRect) == null ? void 0 : _a4.call(element);
5338
- if (rect) {
5339
- layout = { height: rect.height, width: rect.width };
5340
- }
5341
- doUpdate();
5342
- });
5343
- return;
5344
- }
5345
- {
5346
- doUpdate();
5347
- }
5348
- }, []);
5349
- const triggerLayout = React3.useCallback(() => {
5350
- forceLayoutRender((v) => v + 1);
5351
- }, []);
5580
+ const { renderedItem } = renderedItemInfo || {};
5352
5581
  const contextValue = React3.useMemo(() => {
5353
5582
  ctx.viewRefs.set(id, ref);
5354
5583
  return {
@@ -5356,22 +5585,6 @@ var Container = typedMemo(function Container2({
5356
5585
  triggerLayout
5357
5586
  };
5358
5587
  }, [id, triggerLayout]);
5359
- React3.useLayoutEffect(() => {
5360
- ctx.containerLayoutTriggers.set(id, triggerLayout);
5361
- return () => {
5362
- if (ctx.containerLayoutTriggers.get(id) === triggerLayout) {
5363
- ctx.containerLayoutTriggers.delete(id);
5364
- }
5365
- };
5366
- }, [ctx, id, triggerLayout]);
5367
- const { onLayout } = useOnLayoutSync(
5368
- {
5369
- onLayoutChange,
5370
- ref,
5371
- webLayoutResync: () => isInMVCPActiveMode(ctx.state)
5372
- },
5373
- [itemKey, layoutRenderCount]
5374
- );
5375
5588
  const PositionComponent = isSticky ? stickyPositionComponentInternal ? stickyPositionComponentInternal : PositionViewSticky : positionComponentInternal ? positionComponentInternal : PositionView;
5376
5589
  return /* @__PURE__ */ React3__namespace.createElement(
5377
5590
  PositionComponent,
@@ -5379,7 +5592,6 @@ var Container = typedMemo(function Container2({
5379
5592
  animatedScrollY: isSticky ? animatedScrollY : void 0,
5380
5593
  horizontal,
5381
5594
  id,
5382
- index,
5383
5595
  key: recycleItems ? void 0 : itemKey,
5384
5596
  onLayout,
5385
5597
  refView: ref,
@@ -5422,18 +5634,13 @@ var ContainerSlot = typedMemo(function ContainerSlot2(props) {
5422
5634
  });
5423
5635
 
5424
5636
  // src/utils/reordering.ts
5425
- var mapFn = (element) => {
5426
- const indexStr = element.getAttribute("data-index");
5427
- if (indexStr === null) {
5428
- return [element, null];
5429
- }
5430
- const index = Number.parseInt(indexStr, 10);
5431
- return [element, Number.isNaN(index) ? null : index];
5432
- };
5433
- function sortDOMElements(container) {
5637
+ function sortDOMElements(container, indexByElement) {
5434
5638
  const elements = Array.from(container.children);
5435
5639
  if (elements.length <= 1) return elements;
5436
- const items = elements.map(mapFn);
5640
+ const items = elements.map((element) => {
5641
+ var _a3;
5642
+ return [element, (_a3 = indexByElement.get(element)) != null ? _a3 : null];
5643
+ });
5437
5644
  items.sort((a, b) => {
5438
5645
  const aKey = a[1];
5439
5646
  const bKey = b[1];
@@ -5521,7 +5728,15 @@ function useDOMOrder(ref) {
5521
5728
  debounceRef.current = setTimeout(() => {
5522
5729
  const parent = ref.current;
5523
5730
  if (parent) {
5524
- sortDOMElements(parent);
5731
+ const indexByElement = /* @__PURE__ */ new Map();
5732
+ for (const [containerId, viewRef] of ctx.viewRefs) {
5733
+ const element = viewRef.current;
5734
+ const index = peek$(ctx, `containerItemIndex${containerId}`);
5735
+ if (element && index !== void 0) {
5736
+ indexByElement.set(element, index);
5737
+ }
5738
+ }
5739
+ sortDOMElements(parent, indexByElement);
5525
5740
  }
5526
5741
  debounceRef.current = void 0;
5527
5742
  }, 500);
@@ -5534,23 +5749,40 @@ function useDOMOrder(ref) {
5534
5749
  };
5535
5750
  }, [ctx]);
5536
5751
  }
5752
+ function useFreshDataTransitionVisibility(readyToRender, transitionEpoch) {
5753
+ const completedTransitionEpoch = React3.useRef(transitionEpoch);
5754
+ const isTransitionPending = completedTransitionEpoch.current !== transitionEpoch;
5755
+ React3.useLayoutEffect(() => {
5756
+ if (!readyToRender) {
5757
+ completedTransitionEpoch.current = transitionEpoch;
5758
+ }
5759
+ }, [readyToRender, transitionEpoch]);
5760
+ return readyToRender && !isTransitionPending;
5761
+ }
5537
5762
 
5538
5763
  // src/components/Containers.tsx
5539
- var ContainersInner = typedMemo(function ContainersInner2({ horizontal, numColumns, children }) {
5764
+ var ContainersInner = typedMemo(function ContainersInner2({
5765
+ freshDataTransitionEpoch,
5766
+ horizontal,
5767
+ numColumns,
5768
+ children
5769
+ }) {
5540
5770
  const ref = React3.useRef(null);
5541
5771
  const ctx = useStateContext();
5542
5772
  const columnWrapperStyle = ctx.columnWrapperStyle;
5543
5773
  const isHorizontalRTLList = isHorizontalRTL(ctx.state);
5544
5774
  const [otherAxisSize, readyToRender, totalSize] = useArr$(["otherAxisSize", "readyToRender", "totalSize"]);
5775
+ const isVisible = useFreshDataTransitionVisibility(!!readyToRender, freshDataTransitionEpoch);
5545
5776
  useDOMOrder(ref);
5546
5777
  const style = horizontal ? {
5547
5778
  direction: isHorizontalRTLList ? "ltr" : void 0,
5779
+ flexShrink: 0,
5548
5780
  minHeight: otherAxisSize,
5549
- opacity: readyToRender ? 1 : 0,
5781
+ opacity: isVisible ? 1 : 0,
5550
5782
  position: "relative",
5551
5783
  width: totalSize
5552
- } : { height: totalSize, minWidth: otherAxisSize, opacity: readyToRender ? 1 : 0, position: "relative" };
5553
- if (!readyToRender) {
5784
+ } : { height: totalSize, minWidth: otherAxisSize, opacity: isVisible ? 1 : 0, position: "relative" };
5785
+ if (!isVisible) {
5554
5786
  style.pointerEvents = "none";
5555
5787
  }
5556
5788
  if (columnWrapperStyle && numColumns > 1) {
@@ -5570,9 +5802,10 @@ var ContainersInner = typedMemo(function ContainersInner2({ horizontal, numColum
5570
5802
  }
5571
5803
  }
5572
5804
  }
5573
- return /* @__PURE__ */ React3__namespace.createElement("div", { ref, style }, children);
5805
+ return /* @__PURE__ */ React3__namespace.createElement("div", { ref, style }, /* @__PURE__ */ React3__namespace.createElement(ContainerLayoutCoordinator, null, children));
5574
5806
  });
5575
5807
  var Containers = typedMemo(function Containers2({
5808
+ freshDataTransitionEpoch,
5576
5809
  horizontal,
5577
5810
  recycleItems,
5578
5811
  ItemSeparatorComponent,
@@ -5597,7 +5830,15 @@ var Containers = typedMemo(function Containers2({
5597
5830
  )
5598
5831
  );
5599
5832
  }
5600
- return /* @__PURE__ */ React3__namespace.createElement(ContainersInner, { horizontal, numColumns }, containers);
5833
+ return /* @__PURE__ */ React3__namespace.createElement(
5834
+ ContainersInner,
5835
+ {
5836
+ freshDataTransitionEpoch,
5837
+ horizontal,
5838
+ numColumns
5839
+ },
5840
+ containers
5841
+ );
5601
5842
  });
5602
5843
 
5603
5844
  // src/platform/StyleSheet.tsx
@@ -5749,6 +5990,7 @@ function resolveWindowScrollTarget({ clampedOffset, horizontal, listPos, scroll
5749
5990
 
5750
5991
  // src/components/ListComponentScrollView.tsx
5751
5992
  var SCROLLBAR_HIDDEN_STYLE_ID = "legend-list-scrollbar-axis-hidden-style";
5993
+ var SCROLL_END_FALLBACK_MS = 200;
5752
5994
  var SCROLLBAR_HIDDEN_STYLE = `.${LEGEND_LIST_SCROLLBAR_Y_HIDDEN_CLASS}::-webkit-scrollbar:vertical{width:0;display:none;}.${LEGEND_LIST_SCROLLBAR_X_HIDDEN_CLASS}::-webkit-scrollbar:horizontal{height:0;display:none;}`;
5753
5995
  function ensureScrollbarHiddenStyle() {
5754
5996
  if (typeof document === "undefined" || document.getElementById(SCROLLBAR_HIDDEN_STYLE_ID)) {
@@ -5798,6 +6040,7 @@ var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView
5798
6040
  contentOffset,
5799
6041
  maintainVisibleContentPosition,
5800
6042
  onScroll: onScroll2,
6043
+ onInternalScrollEnd,
5801
6044
  onMomentumScrollEnd: _onMomentumScrollEnd,
5802
6045
  showsHorizontalScrollIndicator = true,
5803
6046
  showsVerticalScrollIndicator = true,
@@ -5806,7 +6049,7 @@ var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView
5806
6049
  onLayout,
5807
6050
  ...props
5808
6051
  }, ref) {
5809
- var _a3, _b, _c, _d;
6052
+ var _a3, _b, _c;
5810
6053
  const ctx = useStateContext();
5811
6054
  const [anchoredEndSpaceSize] = useArr$(["anchoredEndSpaceSize"]);
5812
6055
  const scrollRef = React3.useRef(null);
@@ -5927,6 +6170,16 @@ var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView
5927
6170
  onScroll2(scrollEvent);
5928
6171
  }, [getCurrentScrollOffset, horizontal, isWindowScroll, onScroll2]);
5929
6172
  const scrollEventCoalescer = useRafCoalescer(emitScroll);
6173
+ const scrollEndFallbackRef = React3.useRef(void 0);
6174
+ const emitScrollEnd = React3.useCallback(() => {
6175
+ const timeout = scrollEndFallbackRef.current;
6176
+ if (timeout !== void 0) {
6177
+ clearTimeout(timeout);
6178
+ scrollEndFallbackRef.current = void 0;
6179
+ }
6180
+ scrollEventCoalescer.flush();
6181
+ onInternalScrollEnd == null ? void 0 : onInternalScrollEnd();
6182
+ }, [onInternalScrollEnd, scrollEventCoalescer]);
5930
6183
  const handleScroll = React3.useCallback(
5931
6184
  (_event) => {
5932
6185
  if (!onScroll2) {
@@ -5939,18 +6192,36 @@ var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView
5939
6192
  } else {
5940
6193
  scrollEventCoalescer.schedule();
5941
6194
  }
6195
+ if (onInternalScrollEnd) {
6196
+ const timeout = scrollEndFallbackRef.current;
6197
+ if (timeout !== void 0) {
6198
+ clearTimeout(timeout);
6199
+ }
6200
+ scrollEndFallbackRef.current = setTimeout(emitScrollEnd, SCROLL_END_FALLBACK_MS);
6201
+ }
5942
6202
  },
5943
- [ctx.state, onScroll2, scrollEventCoalescer]
6203
+ [ctx.state, emitScrollEnd, onInternalScrollEnd, onScroll2, scrollEventCoalescer]
5944
6204
  );
5945
6205
  React3.useLayoutEffect(() => {
5946
6206
  const target = getScrollTarget();
5947
6207
  if (!target) return;
5948
6208
  target.addEventListener("scroll", handleScroll, { passive: true });
6209
+ if ("onscrollend" in target) {
6210
+ target.addEventListener("scrollend", emitScrollEnd);
6211
+ }
5949
6212
  return () => {
5950
6213
  target.removeEventListener("scroll", handleScroll);
6214
+ if ("onscrollend" in target) {
6215
+ target.removeEventListener("scrollend", emitScrollEnd);
6216
+ }
6217
+ const timeout = scrollEndFallbackRef.current;
6218
+ if (timeout !== void 0) {
6219
+ clearTimeout(timeout);
6220
+ scrollEndFallbackRef.current = void 0;
6221
+ }
5951
6222
  scrollEventCoalescer.cancel();
5952
6223
  };
5953
- }, [getScrollTarget, handleScroll, scrollEventCoalescer]);
6224
+ }, [emitScrollEnd, getScrollTarget, handleScroll, scrollEventCoalescer]);
5954
6225
  React3.useEffect(() => {
5955
6226
  const doScroll = () => {
5956
6227
  if (contentOffset) {
@@ -6010,7 +6281,7 @@ var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView
6010
6281
  } : {}
6011
6282
  };
6012
6283
  const contentInsetEndAdjustment = getContentInsetEndAdjustmentEnd2(ctx);
6013
- const anchoredEndInset = ((_c = (_b = (_a3 = ctx.state) == null ? void 0 : _a3.props) == null ? void 0 : _b.anchoredEndSpace) == null ? void 0 : _c.includeInEndInset) && anchoredEndSpaceSize ? anchoredEndSpaceSize : 0;
6284
+ const anchoredEndInset = ((_b = (_a3 = ctx.state) == null ? void 0 : _a3.props) == null ? void 0 : _b.anchoredEndSpace) && anchoredEndSpaceSize ? anchoredEndSpaceSize : 0;
6014
6285
  const renderedContentInsetEndAdjustment = Math.max(0, contentInsetEndAdjustment - anchoredEndInset);
6015
6286
  const contentInsetEndAdjustmentSpacerStyle = renderedContentInsetEndAdjustment ? horizontal ? { flexShrink: 0, width: renderedContentInsetEndAdjustment } : { height: renderedContentInsetEndAdjustment } : void 0;
6016
6287
  const contentStyle = {
@@ -6037,7 +6308,7 @@ var ListComponentScrollView = React3.forwardRef(function ListComponentScrollView
6037
6308
  const snapOffsets = !isWindowScroll ? getFiniteSnapOffsets(snapToOffsets) : [];
6038
6309
  if (snapOffsets.length > 0) {
6039
6310
  scrollViewStyle.scrollSnapType = horizontal ? "x mandatory" : "y mandatory";
6040
- contentStyle.position = (_d = contentStyle.position) != null ? _d : "relative";
6311
+ contentStyle.position = (_c = contentStyle.position) != null ? _c : "relative";
6041
6312
  }
6042
6313
  const scrollViewClassName = hiddenScrollIndicatorClassName ? scrollViewClassNameProp ? `${scrollViewClassNameProp} ${hiddenScrollIndicatorClassName}` : hiddenScrollIndicatorClassName : scrollViewClassNameProp;
6043
6314
  if (IS_DEV) {
@@ -6186,16 +6457,6 @@ var SnapWrapper = React3__namespace.forwardRef(function SnapWrapperInner({ Scrol
6186
6457
  const [snapToOffsets] = useArr$(["snapToOffsets"]);
6187
6458
  return /* @__PURE__ */ React3__namespace.createElement(ScrollComponent, { ...props, ref, snapToOffsets });
6188
6459
  });
6189
- function WebAnchoredEndSpace({ horizontal }) {
6190
- const ctx = useStateContext();
6191
- const [anchoredEndSpaceSize] = useArr$(["anchoredEndSpaceSize"]);
6192
- const shouldRenderAnchoredEndSpace = !!ctx.state.props.anchoredEndSpace && (anchoredEndSpaceSize || 0) > 0;
6193
- if (!shouldRenderAnchoredEndSpace) {
6194
- return null;
6195
- }
6196
- const style = horizontal ? { height: "100%", width: anchoredEndSpaceSize || 0 } : { height: anchoredEndSpaceSize || 0 };
6197
- return /* @__PURE__ */ React3__namespace.createElement("div", { style }, null);
6198
- }
6199
6460
 
6200
6461
  // src/core/updateContentMetrics.ts
6201
6462
  var SCROLL_ADJUST_EPSILON = 0.1;
@@ -6229,7 +6490,11 @@ function setHeaderSize(ctx, size) {
6229
6490
  state.didMeasureHeader = true;
6230
6491
  }
6231
6492
  function setFooterSize(ctx, size) {
6232
- return setContentLengthSignal(ctx, "footerSize", size);
6493
+ const didChange = setContentLengthSignal(ctx, "footerSize", size);
6494
+ if (didChange) {
6495
+ maybeUpdateAnchoredEndSpace(ctx);
6496
+ }
6497
+ return didChange;
6233
6498
  }
6234
6499
  function areInsetsEqual(left, right) {
6235
6500
  var _a3, _b, _c, _d, _e, _f, _g, _h;
@@ -6282,6 +6547,7 @@ var AlignItemsAtEndSpacer = typedMemo(function AlignItemsAtEndSpacer2({ horizont
6282
6547
  });
6283
6548
  var ListComponent = typedMemo(function ListComponent2({
6284
6549
  canRender,
6550
+ freshDataTransitionEpoch,
6285
6551
  style,
6286
6552
  contentContainerStyle,
6287
6553
  horizontal,
@@ -6300,6 +6566,8 @@ var ListComponent = typedMemo(function ListComponent2({
6300
6566
  refScrollView,
6301
6567
  renderScrollComponent,
6302
6568
  onLayoutFooter,
6569
+ onInternalScrollBeginDrag,
6570
+ onInternalScrollEnd,
6303
6571
  scrollAdjustHandler,
6304
6572
  snapToIndices,
6305
6573
  stickyHeaderConfig,
@@ -6309,8 +6577,17 @@ var ListComponent = typedMemo(function ListComponent2({
6309
6577
  }) {
6310
6578
  const ctx = useStateContext();
6311
6579
  const maintainVisibleContentPosition = ctx.state.props.maintainVisibleContentPosition;
6312
- const [otherAxisSize = 0] = useArr$(["otherAxisSize"]);
6580
+ const [anchoredEndSpaceSize = 0, otherAxisSize = 0] = useArr$(["anchoredEndSpaceSize", "otherAxisSize"]);
6313
6581
  const shouldRenderAlignItemsAtEndSpacer = ctx.state.props.alignItemsAtEndPaddingEnabled;
6582
+ const shouldMaterializeAnchoredEndSpace = !!ctx.state.props.anchoredEndSpace && ctx.state.props.anchoredEndSpaceOwner === "list" && anchoredEndSpaceSize > 0;
6583
+ let anchoredEndSpaceStyle;
6584
+ if (shouldMaterializeAnchoredEndSpace) {
6585
+ const paddingEnd = horizontal ? isHorizontalRTL(ctx.state) ? "Left" : "Right" : "Bottom";
6586
+ const flattenedContentContainerStyle = StyleSheet.flatten(contentContainerStyle);
6587
+ anchoredEndSpaceStyle = {
6588
+ [`padding${paddingEnd}`]: extractPadding({}, flattenedContentContainerStyle || {}, paddingEnd) + anchoredEndSpaceSize
6589
+ };
6590
+ }
6314
6591
  const autoOtherAxisStyle = getAutoOtherAxisStyle({
6315
6592
  horizontal,
6316
6593
  needsOtherAxisSize: ctx.state.needsOtherAxisSize,
@@ -6361,12 +6638,12 @@ var ListComponent = typedMemo(function ListComponent2({
6361
6638
  SnapOrScroll,
6362
6639
  {
6363
6640
  ...rest,
6364
- ...ScrollComponent === ListComponentScrollView ? { useWindowScroll } : {},
6641
+ ...ScrollComponent === ListComponentScrollView ? { onInternalScrollEnd, useWindowScroll } : {} ,
6365
6642
  contentContainerStyle: [
6366
- horizontal ? {
6367
- height: "100%"
6368
- } : {},
6369
- contentContainerStyle
6643
+ horizontal ? { height: "100%" } : {},
6644
+ contentContainerStyle,
6645
+ anchoredEndSpaceStyle,
6646
+ { boxSizing: "border-box" }
6370
6647
  ],
6371
6648
  contentOffset: initialContentOffset !== void 0 ? horizontal ? { x: initialContentOffset, y: 0 } : { x: 0, y: initialContentOffset } : void 0,
6372
6649
  horizontal,
@@ -6384,6 +6661,7 @@ var ListComponent = typedMemo(function ListComponent2({
6384
6661
  canRender && !ListEmptyComponent && /* @__PURE__ */ React3__namespace.createElement(
6385
6662
  Containers,
6386
6663
  {
6664
+ freshDataTransitionEpoch,
6387
6665
  getRenderedItem: getRenderedItem2,
6388
6666
  horizontal,
6389
6667
  ItemSeparatorComponent,
@@ -6392,7 +6670,6 @@ var ListComponent = typedMemo(function ListComponent2({
6392
6670
  }
6393
6671
  ),
6394
6672
  ListFooterComponent && /* @__PURE__ */ React3__namespace.createElement(LayoutView, { onLayoutChange: onLayoutFooterInternal, style: ListFooterComponentStyle }, getComponent(ListFooterComponent)),
6395
- /* @__PURE__ */ React3__namespace.createElement(WebAnchoredEndSpace, { horizontal }),
6396
6673
  IS_DEV && ENABLE_DEVMODE
6397
6674
  );
6398
6675
  });
@@ -6401,7 +6678,16 @@ var WEB_UNBOUNDED_HEIGHT_CONTAINER_RATIO = 0.9;
6401
6678
  var WEB_UNBOUNDED_HEIGHT_VIEWPORT_RATIO = 0.9;
6402
6679
  function useDevChecksImpl(props) {
6403
6680
  const ctx = useStateContext();
6404
- const { childrenMode, keyExtractor, renderScrollComponent, useWindowScroll } = props;
6681
+ const { anchoredEndSpace, childrenMode, keyExtractor, numColumns, renderScrollComponent, useWindowScroll } = props;
6682
+ const hasAnchoredEndSpace = !!anchoredEndSpace;
6683
+ React3.useEffect(() => {
6684
+ if (hasAnchoredEndSpace && (numColumns != null ? numColumns : 1) > 1) {
6685
+ warnDevOnce(
6686
+ "anchoredEndSpaceNumColumns",
6687
+ "anchoredEndSpace is only supported when numColumns is 1. Using it with multiple columns may produce incorrect anchored spacing."
6688
+ );
6689
+ }
6690
+ }, [hasAnchoredEndSpace, numColumns]);
6405
6691
  React3.useEffect(() => {
6406
6692
  if (useWindowScroll && renderScrollComponent) {
6407
6693
  warnDevOnce(
@@ -6467,6 +6753,7 @@ function checkResetContainers(ctx, dataProp, { didColumnsChange = false } = {})
6467
6753
  if (didColumnsChange) {
6468
6754
  state.sizes.clear();
6469
6755
  state.sizesKnown.clear();
6756
+ invalidateContainerFixedItemSizes(state);
6470
6757
  for (const key in state.averageSizes) {
6471
6758
  delete state.averageSizes[key];
6472
6759
  }
@@ -6713,7 +7000,7 @@ function onScroll(ctx, event) {
6713
7000
  }
6714
7001
  }
6715
7002
  state.scrollPending = newScroll;
6716
- updateScroll(ctx, newScroll, insetChanged);
7003
+ updateScroll(ctx, newScroll, insetChanged, { fromNativeScrollEvent: true });
6717
7004
  trackInitialScrollNativeProgress(state, newScroll);
6718
7005
  clearFinishedBootstrapInitialScrollTargetIfMovedAway(ctx);
6719
7006
  if (state.scrollingTo) {
@@ -6728,6 +7015,59 @@ function onScroll(ctx, event) {
6728
7015
  }
6729
7016
  }
6730
7017
 
7018
+ // src/core/ScheduledWork.ts
7019
+ var ScheduledWork = class {
7020
+ constructor() {
7021
+ this.work = /* @__PURE__ */ new Map();
7022
+ }
7023
+ timeout(callback, delay, key) {
7024
+ if (key) {
7025
+ this.cancel(key);
7026
+ }
7027
+ const work = [void 0, clearTimeout];
7028
+ const handle = setTimeout(() => {
7029
+ const workKey = key != null ? key : handle;
7030
+ if (this.work.get(workKey) === work) {
7031
+ this.work.delete(workKey);
7032
+ callback();
7033
+ }
7034
+ }, delay);
7035
+ work[0] = handle;
7036
+ this.work.set(key != null ? key : handle, work);
7037
+ }
7038
+ frame(callback, key) {
7039
+ this.cancel(key);
7040
+ const work = [void 0, cancelAnimationFrame];
7041
+ this.work.set(key, work);
7042
+ work[0] = requestAnimationFrame(() => {
7043
+ if (this.work.get(key) === work) {
7044
+ this.work.delete(key);
7045
+ callback();
7046
+ }
7047
+ });
7048
+ }
7049
+ register(key, cancel) {
7050
+ this.cancel(key);
7051
+ this.work.set(key, [void 0, cancel]);
7052
+ }
7053
+ cancel(key) {
7054
+ const work = this.work.get(key);
7055
+ if (work) {
7056
+ this.work.delete(key);
7057
+ work[1](work[0]);
7058
+ }
7059
+ }
7060
+ has(key) {
7061
+ return this.work.has(key);
7062
+ }
7063
+ dispose() {
7064
+ for (const [handle, cancel] of this.work.values()) {
7065
+ cancel(handle);
7066
+ }
7067
+ this.work.clear();
7068
+ }
7069
+ };
7070
+
6731
7071
  // src/core/ScrollAdjustHandler.ts
6732
7072
  var ScrollAdjustHandler = class {
6733
7073
  constructor(ctx) {
@@ -6876,8 +7216,8 @@ function getAverageItemSizes(state) {
6876
7216
  return averageItemSizes;
6877
7217
  }
6878
7218
  function triggerMountedContainerLayouts(ctx) {
6879
- for (const triggerLayout of ctx.containerLayoutTriggers.values()) {
6880
- triggerLayout();
7219
+ {
7220
+ scheduleContainerLayout(ctx);
6881
7221
  }
6882
7222
  }
6883
7223
  function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
@@ -6885,7 +7225,7 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
6885
7225
  const IMPERATIVE_SCROLL_SETTLE_MAX_WAIT_MS = 800;
6886
7226
  const IMPERATIVE_SCROLL_SETTLE_STABLE_FRAMES = 2;
6887
7227
  let imperativeScrollToken = 0;
6888
- const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.queuedMVCPRecalculate !== void 0 || state.ignoreScrollFromMVCP !== void 0;
7228
+ const isSettlingAfterDataChange = () => !!state.didDataChange || !!state.didColumnsChange || state.scheduledWork.has("mvcpRecalculate") || state.ignoreScrollFromMVCP !== void 0;
6889
7229
  const isScrollToIndexReady = (targetIndex, allowEmpty = false) => {
6890
7230
  var _a3;
6891
7231
  const props = state.props;
@@ -6919,9 +7259,9 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
6919
7259
  run();
6920
7260
  return;
6921
7261
  }
6922
- requestAnimationFrame(check);
7262
+ state.scheduledWork.frame(check, "imperativeScrollReady");
6923
7263
  };
6924
- requestAnimationFrame(check);
7264
+ state.scheduledWork.frame(check, "imperativeScrollReady");
6925
7265
  };
6926
7266
  const runScrollRequest = (token, resolve, run, isReady = () => true) => {
6927
7267
  const runNow = () => {
@@ -6943,10 +7283,9 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
6943
7283
  }
6944
7284
  };
6945
7285
  const startImperativeScroll = (resolve) => {
6946
- var _a3;
7286
+ state.scheduledWork.cancel("imperativeScrollReady");
6947
7287
  const token = ++imperativeScrollToken;
6948
- state.pendingScrollToEnd = void 0;
6949
- (_a3 = state.pendingScrollResolve) == null ? void 0 : _a3.call(state);
7288
+ settlePendingImperativeScroll(state);
6950
7289
  state.pendingScrollResolve = resolve;
6951
7290
  return token;
6952
7291
  };
@@ -6990,6 +7329,7 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
6990
7329
  const mode = (_a3 = options == null ? void 0 : options.mode) != null ? _a3 : "sizes";
6991
7330
  state.sizes.clear();
6992
7331
  state.sizesKnown.clear();
7332
+ invalidateContainerFixedItemSizes(state);
6993
7333
  for (const key in state.averageSizes) {
6994
7334
  delete state.averageSizes[key];
6995
7335
  }
@@ -7011,6 +7351,10 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
7011
7351
  return {
7012
7352
  clearCaches,
7013
7353
  flashScrollIndicators: () => refScroller.current.flashScrollIndicators(),
7354
+ getAnimatableRef: () => {
7355
+ var _a3, _b, _c;
7356
+ return (_c = (_b = (_a3 = refScroller.current).getNativeScrollRef) == null ? void 0 : _b.call(_a3)) != null ? _c : refScroller.current;
7357
+ },
7014
7358
  getNativeScrollRef: () => refScroller.current,
7015
7359
  getScrollableNode: () => refScroller.current.getScrollableNode(),
7016
7360
  getScrollResponder: () => refScroller.current.getScrollResponder(),
@@ -7025,6 +7369,7 @@ function createImperativeHandle(ctx, scheduleImperativeScrollCommit) {
7025
7369
  end: state.endNoBuffer,
7026
7370
  endBuffered: state.endBuffered,
7027
7371
  getAverageItemSizes: () => getAverageItemSizes(state),
7372
+ indexByKey: (key) => state.indexByKey.get(key),
7028
7373
  isAtEnd: peek$(ctx, "isAtEnd"),
7029
7374
  isAtStart: peek$(ctx, "isAtStart"),
7030
7375
  isEndReached: state.isEndReached,
@@ -7328,7 +7673,7 @@ var LegendList = typedMemo(
7328
7673
  })
7329
7674
  );
7330
7675
  var LegendListInner = typedForwardRef(function LegendListInner2(props, forwardedRef) {
7331
- var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
7676
+ var _a3, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s;
7332
7677
  const noopOnScroll = React3.useCallback((_event) => {
7333
7678
  }, []);
7334
7679
  if (props.recycleItems === void 0) {
@@ -7381,6 +7726,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7381
7726
  onMomentumScrollEnd,
7382
7727
  onRefresh,
7383
7728
  onScroll: onScrollProp,
7729
+ onScrollBeginDrag,
7384
7730
  onStartReached,
7385
7731
  onStartReachedThreshold = 0.5,
7386
7732
  onStickyHeaderChange,
@@ -7403,9 +7749,11 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7403
7749
  ...rest
7404
7750
  } = props;
7405
7751
  const animatedPropsInternal = props.animatedPropsInternal;
7752
+ const anchoredEndSpaceOwner = (_a3 = props.anchoredEndSpaceOwnerInternal) != null ? _a3 : "list";
7406
7753
  const positionComponentInternal = props.positionComponentInternal;
7407
7754
  const stickyPositionComponentInternal = props.stickyPositionComponentInternal;
7408
7755
  const {
7756
+ anchoredEndSpaceOwnerInternal: _anchoredEndSpaceOwnerInternal,
7409
7757
  positionComponentInternal: _positionComponentInternal,
7410
7758
  stickyPositionComponentInternal: _stickyPositionComponentInternal,
7411
7759
  ...restProps
@@ -7427,6 +7775,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7427
7775
  const stylePaddingBottomState = extractPadding(style, contentContainerStyle, "Bottom");
7428
7776
  const stylePaddingLeftState = extractPadding(style, contentContainerStyle, "Left");
7429
7777
  const stylePaddingRightState = extractPadding(style, contentContainerStyle, "Right");
7778
+ const stylePaddingEndState = getStylePaddingEnd({
7779
+ horizontal,
7780
+ rtl,
7781
+ stylePaddingBottom: stylePaddingBottomState,
7782
+ stylePaddingLeft: stylePaddingLeftState,
7783
+ stylePaddingRight: stylePaddingRightState
7784
+ });
7430
7785
  const maintainScrollAtEndConfig = normalizeMaintainScrollAtEnd(maintainScrollAtEnd);
7431
7786
  const maintainVisibleContentPositionConfig = normalizeMaintainVisibleContentPosition(
7432
7787
  maintainVisibleContentPositionProp
@@ -7439,13 +7794,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7439
7794
  const initialScrollProp = initialScrollAtEnd ? {
7440
7795
  index: Math.max(0, dataProp.length - 1),
7441
7796
  preserveForBottomPadding: true,
7442
- viewOffset: -stylePaddingBottomState,
7797
+ viewOffset: -stylePaddingEndState,
7443
7798
  viewPosition: 1
7444
7799
  } : hasInitialScrollIndex ? typeof initialScrollIndexProp === "object" ? {
7445
- index: (_a3 = initialScrollIndexProp.index) != null ? _a3 : 0,
7800
+ index: (_b = initialScrollIndexProp.index) != null ? _b : 0,
7446
7801
  preserveForBottomPadding: initialScrollIndexProp.viewOffset === void 0 && initialScrollIndexProp.viewPosition === 1 ? true : void 0,
7447
- viewOffset: (_b = initialScrollIndexProp.viewOffset) != null ? _b : initialScrollIndexProp.viewPosition === 1 ? -stylePaddingBottomState : 0,
7448
- viewPosition: (_c = initialScrollIndexProp.viewPosition) != null ? _c : 0
7802
+ viewOffset: (_c = initialScrollIndexProp.viewOffset) != null ? _c : initialScrollIndexProp.viewPosition === 1 ? -stylePaddingEndState : 0,
7803
+ viewPosition: (_d = initialScrollIndexProp.viewPosition) != null ? _d : 0
7449
7804
  } : {
7450
7805
  index: initialScrollIndexProp != null ? initialScrollIndexProp : 0,
7451
7806
  viewOffset: initialScrollOffsetProp != null ? initialScrollOffsetProp : 0
@@ -7458,7 +7813,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7458
7813
  const [, scheduleImperativeScrollCommit] = React3__namespace.useReducer((value) => value + 1, 0);
7459
7814
  const ctx = useStateContext();
7460
7815
  ctx.columnWrapperStyle = columnWrapperStyle || (contentContainerStyle ? createColumnWrapperStyle(contentContainerStyle) : void 0);
7461
- const scrollAxisGap = horizontal ? (_f = (_d = ctx.columnWrapperStyle) == null ? void 0 : _d.columnGap) != null ? _f : (_e = ctx.columnWrapperStyle) == null ? void 0 : _e.gap : (_i = (_g = ctx.columnWrapperStyle) == null ? void 0 : _g.rowGap) != null ? _i : (_h = ctx.columnWrapperStyle) == null ? void 0 : _h.gap;
7816
+ const scrollAxisGap = horizontal ? (_g = (_e = ctx.columnWrapperStyle) == null ? void 0 : _e.columnGap) != null ? _g : (_f = ctx.columnWrapperStyle) == null ? void 0 : _f.gap : (_j = (_h = ctx.columnWrapperStyle) == null ? void 0 : _h.rowGap) != null ? _j : (_i = ctx.columnWrapperStyle) == null ? void 0 : _i.gap;
7462
7817
  const nextScrollAxisGap = typeof scrollAxisGap === "number" && Number.isFinite(scrollAxisGap) ? scrollAxisGap : 0;
7463
7818
  const refScroller = React3.useRef(null);
7464
7819
  const combinedRef = useCombinedRef(refScroller, refScrollView);
@@ -7473,8 +7828,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7473
7828
  anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex,
7474
7829
  alwaysRender == null ? void 0 : alwaysRender.top,
7475
7830
  alwaysRender == null ? void 0 : alwaysRender.bottom,
7476
- (_j = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _j.join(","),
7477
- (_k = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _k.join(","),
7831
+ (_k = alwaysRender == null ? void 0 : alwaysRender.indices) == null ? void 0 : _k.join(","),
7832
+ (_l = alwaysRender == null ? void 0 : alwaysRender.keys) == null ? void 0 : _l.join(","),
7478
7833
  dataProp,
7479
7834
  dataKey,
7480
7835
  dataVersion,
@@ -7487,13 +7842,14 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7487
7842
  if (!refState.current) {
7488
7843
  if (!ctx.state) {
7489
7844
  const initialScrollLength = (estimatedListSize != null ? estimatedListSize : { height: 0, width: 0 } )[horizontal ? "width" : "height"];
7490
- ctx.values.set("adaptiveRender", (_l = experimental_adaptiveRender == null ? void 0 : experimental_adaptiveRender.initialMode) != null ? _l : "normal");
7845
+ ctx.values.set("adaptiveRender", (_m = experimental_adaptiveRender == null ? void 0 : experimental_adaptiveRender.initialMode) != null ? _m : "normal");
7491
7846
  ctx.state = {
7492
7847
  averageSizes: {},
7493
7848
  columnSpans: [],
7494
7849
  columns: [],
7850
+ containerItemGenerations: [],
7495
7851
  containerItemKeys: /* @__PURE__ */ new Map(),
7496
- containerItemTypes: /* @__PURE__ */ new Map(),
7852
+ containerItemMetadata: /* @__PURE__ */ new Map(),
7497
7853
  contentInsetOverride: void 0,
7498
7854
  dataChangeEpoch: 0,
7499
7855
  dataChangeNeedsScrollUpdate: false,
@@ -7505,6 +7861,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7505
7861
  endNoBuffer: -1,
7506
7862
  endReachedSnapshot: void 0,
7507
7863
  firstFullyOnScreenIndex: -1,
7864
+ freshDataTransitionEpoch: 0,
7508
7865
  hasHadNonEmptyData: dataProp.length > 0,
7509
7866
  idCache: [],
7510
7867
  idsInView: [],
@@ -7529,8 +7886,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7529
7886
  positions: [],
7530
7887
  props: {},
7531
7888
  queuedCalculateItemsInView: 0,
7532
- queuedFullDrawDistancePrewarm: void 0,
7533
7889
  refScroller: { current: null },
7890
+ scheduledWork: new ScheduledWork(),
7534
7891
  scroll: 0,
7535
7892
  scrollAdjustHandler: new ScrollAdjustHandler(ctx),
7536
7893
  scrollForNextCalculateItemsInView: void 0,
@@ -7546,11 +7903,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7546
7903
  startBuffered: -1,
7547
7904
  startNoBuffer: -1,
7548
7905
  startReachedSnapshot: void 0,
7549
- startReachedSnapshotDataChangeEpoch: void 0,
7550
7906
  stickyContainerPool: /* @__PURE__ */ new Set(),
7551
7907
  stickyContainers: /* @__PURE__ */ new Map(),
7552
- timeoutAdaptiveRender: void 0,
7553
- timeouts: /* @__PURE__ */ new Set(),
7554
7908
  totalSize: 0,
7555
7909
  viewabilityConfigCallbackPairs: void 0
7556
7910
  };
@@ -7572,13 +7926,13 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7572
7926
  const didScrollAxisGapChange = !isFirstLocal && ctx.scrollAxisGap !== nextScrollAxisGap;
7573
7927
  ctx.scrollAxisGap = nextScrollAxisGap;
7574
7928
  state.didColumnsChange = numColumnsProp !== previousNumColumnsProp || didScrollAxisGapChange;
7575
- const previousDataLength = (_n = (_m = state.props.data) == null ? void 0 : _m.length) != null ? _n : 0;
7929
+ const previousDataLength = (_o = (_n = state.props.data) == null ? void 0 : _n.length) != null ? _o : 0;
7576
7930
  const didDataReferenceChangeLocal = state.props.data !== dataProp;
7577
7931
  const didDataKeyChangeLocal = state.props.dataKey !== dataKey;
7578
7932
  const didDataVersionChangeLocal = state.props.dataVersion !== dataVersion;
7579
7933
  const didDataChangeLocal = didDataKeyChangeLocal || didDataVersionChangeLocal || didDataReferenceChangeLocal && checkStructuralDataChange(state, dataProp, state.props.data);
7580
7934
  const shouldResetFreshDataLayout = !isFirstLocal && didDataChangeLocal && state.hasHadNonEmptyData && (didDataKeyChangeLocal || previousDataLength === 0) && dataProp.length > 0;
7581
- if (didDataChangeLocal && !initialScrollAtEnd && state.didFinishInitialScroll && ((_o = state.initialScroll) == null ? void 0 : _o.viewPosition) === 1 && state.props.data.length > 0) {
7935
+ if (didDataChangeLocal && !initialScrollAtEnd && state.didFinishInitialScroll && ((_p = state.initialScroll) == null ? void 0 : _p.viewPosition) === 1 && state.props.data.length > 0) {
7582
7936
  clearPreservedInitialScrollTarget(state);
7583
7937
  }
7584
7938
  if (didDataChangeLocal) {
@@ -7587,10 +7941,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7587
7941
  state.didDataChange = true;
7588
7942
  state.previousData = state.props.data;
7589
7943
  }
7944
+ if (shouldResetFreshDataLayout) {
7945
+ state.freshDataTransitionEpoch += 1;
7946
+ }
7590
7947
  const throttledOnScroll = useThrottledOnScroll(onScrollProp != null ? onScrollProp : noopOnScroll, scrollEventThrottle != null ? scrollEventThrottle : 0);
7591
7948
  const throttleScrollFn = scrollEventThrottle && onScrollProp ? throttledOnScroll : onScrollProp;
7592
- const anchoredEndSpaceResolved = anchoredEndSpace ? { ...anchoredEndSpace, includeInEndInset: true } : anchoredEndSpace;
7593
- const didAnchoredEndSpaceAnchorIndexChange = !isFirstLocal && !didDataChangeLocal && ((_p = state.props.anchoredEndSpace) == null ? void 0 : _p.anchorIndex) !== (anchoredEndSpaceResolved == null ? void 0 : anchoredEndSpaceResolved.anchorIndex);
7949
+ const didAnchoredEndSpaceAnchorIndexChange = !isFirstLocal && !didDataChangeLocal && ((_q = state.props.anchoredEndSpace) == null ? void 0 : _q.anchorIndex) !== (anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorIndex);
7594
7950
  state.props = {
7595
7951
  adaptiveRender: experimental_adaptiveRender,
7596
7952
  alignItemsAtEnd,
@@ -7598,7 +7954,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7598
7954
  alwaysRender,
7599
7955
  alwaysRenderIndicesArr: alwaysRenderIndices.arr,
7600
7956
  alwaysRenderIndicesSet: alwaysRenderIndices.set,
7601
- anchoredEndSpace: anchoredEndSpaceResolved,
7957
+ anchoredEndSpace,
7958
+ anchoredEndSpaceOwner,
7602
7959
  animatedProps: animatedPropsInternal,
7603
7960
  contentContainerAlignItems: contentContainerStyle.alignItems,
7604
7961
  contentInset,
@@ -7622,7 +7979,9 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7622
7979
  onFirstVisibleItemChanged,
7623
7980
  onItemSizeChanged,
7624
7981
  onLoad,
7982
+ onMomentumScrollEnd,
7625
7983
  onScroll: throttleScrollFn,
7984
+ onScrollBeginDrag,
7626
7985
  onStartReached,
7627
7986
  onStartReachedThreshold,
7628
7987
  onStickyHeaderChange,
@@ -7645,12 +8004,6 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7645
8004
  if (!isFirstLocal && previousAdaptiveRender && !experimental_adaptiveRender) {
7646
8005
  resetAdaptiveRender(ctx);
7647
8006
  }
7648
- if (shouldResetFreshDataLayout) {
7649
- resetInitialRenderState(ctx, {
7650
- resetInitialScroll: !!initialScrollProp,
7651
- resetLayout: true
7652
- });
7653
- }
7654
8007
  const memoizedLastItemKeys = React3.useMemo(() => {
7655
8008
  if (!dataProp.length) return [];
7656
8009
  return Array.from(
@@ -7709,6 +8062,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7709
8062
  useDevChecks(props);
7710
8063
  }
7711
8064
  React3.useLayoutEffect(() => {
8065
+ if (shouldResetFreshDataLayout) {
8066
+ resetInitialRenderState(ctx, {
8067
+ resetInitialScroll: !!initialScrollProp,
8068
+ resetLayout: true
8069
+ });
8070
+ }
7712
8071
  handleInitialScrollDataChange(ctx, {
7713
8072
  dataLength: dataProp.length,
7714
8073
  didDataChange: didDataChangeLocal,
@@ -7716,7 +8075,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7716
8075
  initialScrollAtEnd,
7717
8076
  latestInitialScroll: initialScrollProp,
7718
8077
  latestInitialScrollSessionKind: initialScrollUsesOffsetOnly ? "offset" : "bootstrap",
7719
- stylePaddingBottom: stylePaddingBottomState,
8078
+ stylePaddingEnd: stylePaddingEndState,
7720
8079
  useBootstrapInitialScroll: usesBootstrapInitialScroll
7721
8080
  });
7722
8081
  }, [
@@ -7725,7 +8084,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7725
8084
  didDataChangeLocal,
7726
8085
  shouldResetFreshDataLayout,
7727
8086
  initialScrollAtEnd,
7728
- stylePaddingBottomState,
8087
+ stylePaddingEndState,
7729
8088
  usesBootstrapInitialScroll
7730
8089
  ]);
7731
8090
  React3.useLayoutEffect(() => {
@@ -7743,7 +8102,12 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7743
8102
  anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorMaxSize,
7744
8103
  anchoredEndSpace == null ? void 0 : anchoredEndSpace.anchorOffset,
7745
8104
  didAnchoredEndSpaceAnchorIndexChange,
7746
- numColumnsProp
8105
+ horizontal,
8106
+ numColumnsProp,
8107
+ rtl,
8108
+ stylePaddingBottomState,
8109
+ stylePaddingLeftState,
8110
+ stylePaddingRightState
7747
8111
  ]);
7748
8112
  React3.useLayoutEffect(() => {
7749
8113
  const previousContentInsetEndAdjustment = previousContentInsetEndAdjustmentRef.current;
@@ -7759,10 +8123,10 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7759
8123
  dataLength: dataProp.length,
7760
8124
  footerSize: layout[horizontal ? "width" : "height"],
7761
8125
  initialScrollAtEnd,
7762
- stylePaddingBottom: stylePaddingBottomState
8126
+ stylePaddingEnd: stylePaddingEndState
7763
8127
  });
7764
8128
  },
7765
- [dataProp.length, initialScrollAtEnd, horizontal, stylePaddingBottomState, usesBootstrapInitialScroll]
8129
+ [dataProp.length, initialScrollAtEnd, horizontal, stylePaddingEndState, usesBootstrapInitialScroll]
7766
8130
  );
7767
8131
  const onLayoutChange = React3.useCallback(
7768
8132
  (layout, fromLayoutEffect) => {
@@ -7779,7 +8143,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7779
8143
  }
7780
8144
  advanceCurrentInitialScrollSession(ctx);
7781
8145
  },
7782
- [dataProp.length, initialScrollAtEnd, stylePaddingBottomState, usesBootstrapInitialScroll]
8146
+ [dataProp.length, initialScrollAtEnd, stylePaddingEndState, usesBootstrapInitialScroll]
7783
8147
  );
7784
8148
  const { onLayout } = useOnLayoutSync({
7785
8149
  onLayoutChange,
@@ -7872,14 +8236,8 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7872
8236
  React3.useImperativeHandle(forwardedRef, () => createImperativeHandle(ctx, scheduleImperativeScrollCommit), []);
7873
8237
  React3.useEffect(() => {
7874
8238
  return () => {
7875
- if (state.queuedFullDrawDistancePrewarm !== void 0) {
7876
- cancelAnimationFrame(state.queuedFullDrawDistancePrewarm);
7877
- state.queuedFullDrawDistancePrewarm = void 0;
7878
- }
7879
- for (const timeout of state.timeouts) {
7880
- clearTimeout(timeout);
7881
- }
7882
- state.timeouts.clear();
8239
+ cancelImperativeScroll(state);
8240
+ state.scheduledWork.dispose();
7883
8241
  };
7884
8242
  }, [state]);
7885
8243
  React3.useLayoutEffect(() => {
@@ -7897,11 +8255,17 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7897
8255
  getRenderedItem: (key) => getRenderedItem(ctx, key),
7898
8256
  onMomentumScrollEnd: (event) => {
7899
8257
  checkFinishedScrollFallback(ctx);
7900
- if (onMomentumScrollEnd) {
7901
- onMomentumScrollEnd(event);
8258
+ if (state.props.onMomentumScrollEnd) {
8259
+ state.props.onMomentumScrollEnd(event);
7902
8260
  }
7903
8261
  },
7904
- onScroll: (event) => onScroll(ctx, event)
8262
+ onScroll: (event) => onScroll(ctx, event),
8263
+ onScrollBeginDrag: (event) => {
8264
+ var _a4, _b2;
8265
+ prepareReachedEdgeForNextUserScroll(ctx);
8266
+ (_b2 = (_a4 = state.props).onScrollBeginDrag) == null ? void 0 : _b2.call(_a4, event);
8267
+ },
8268
+ onScrollEnd: () => prepareReachedEdgeForNextUserScroll(ctx)
7905
8269
  }),
7906
8270
  []
7907
8271
  );
@@ -7915,6 +8279,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7915
8279
  canRender,
7916
8280
  contentContainerStyle,
7917
8281
  contentInset,
8282
+ freshDataTransitionEpoch: state.freshDataTransitionEpoch,
7918
8283
  getRenderedItem: fns.getRenderedItem,
7919
8284
  horizontal,
7920
8285
  initialContentOffset,
@@ -7922,13 +8287,15 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7922
8287
  ListFooterComponent,
7923
8288
  ListFooterComponentStyle,
7924
8289
  ListHeaderComponent,
8290
+ onInternalScrollBeginDrag: fns.onScrollBeginDrag,
8291
+ onInternalScrollEnd: fns.onScrollEnd,
7925
8292
  onLayout,
7926
8293
  onLayoutFooter,
7927
8294
  onMomentumScrollEnd: fns.onMomentumScrollEnd,
7928
8295
  onScroll: onScrollHandler,
7929
8296
  recycleItems,
7930
8297
  refreshControl: refreshControlElement ? stylePaddingTopState > 0 ? React3__namespace.cloneElement(refreshControlElement, {
7931
- progressViewOffset: ((_q = refreshControlElement.props.progressViewOffset) != null ? _q : 0) + stylePaddingTopState
8298
+ progressViewOffset: ((_r = refreshControlElement.props.progressViewOffset) != null ? _r : 0) + stylePaddingTopState
7932
8299
  }) : refreshControlElement : onRefresh && /* @__PURE__ */ React3__namespace.createElement(
7933
8300
  RefreshControl,
7934
8301
  {
@@ -7939,7 +8306,7 @@ var LegendListInner = typedForwardRef(function LegendListInner2(props, forwarded
7939
8306
  ),
7940
8307
  refScrollView: combinedRef,
7941
8308
  renderScrollComponent,
7942
- scrollAdjustHandler: (_r = refState.current) == null ? void 0 : _r.scrollAdjustHandler,
8309
+ scrollAdjustHandler: (_s = refState.current) == null ? void 0 : _s.scrollAdjustHandler,
7943
8310
  scrollEventThrottle: 0,
7944
8311
  snapToIndices,
7945
8312
  stickyHeaderIndices,